Port to Bevy 0.8.
This commit is contained in:
parent
e06fb383a3
commit
4627eaa093
13
Cargo.toml
13
Cargo.toml
@ -10,22 +10,19 @@ repository = "https://github.com/komadori/bevy_mod_outline/"
|
|||||||
keywords = ["gamedev", "bevy", "outline"]
|
keywords = ["gamedev", "bevy", "outline"]
|
||||||
categories = ["game-engines", "rendering"]
|
categories = ["game-engines", "rendering"]
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bevy = { version = "0.7", default-features = false, features = [
|
bevy = { version = "0.8", default-features = false, features = [
|
||||||
|
"bevy_asset",
|
||||||
"render",
|
"render",
|
||||||
] }
|
] }
|
||||||
bevy_asset = { version = "0.7", default-features = false }
|
|
||||||
libm = "0.2"
|
libm = "0.2"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
bevy = { version = "0.7", default-features = false, features = [
|
bevy = { version = "0.8", default-features = false, features = [
|
||||||
"bevy_winit",
|
"bevy_winit",
|
||||||
"x11",
|
"x11",
|
||||||
] }
|
] }
|
||||||
bevy_mod_rounded_box = "0.1"
|
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "cube"
|
name = "torus"
|
||||||
path = "examples/cube.rs"
|
path = "examples/torus.rs"
|
@ -1,9 +1,8 @@
|
|||||||
use std::f32::consts::TAU;
|
use std::f32::consts::TAU;
|
||||||
|
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::{shape::Torus, *};
|
||||||
|
|
||||||
use bevy_mod_outline::*;
|
use bevy_mod_outline::*;
|
||||||
use bevy_mod_rounded_box::*;
|
|
||||||
|
|
||||||
#[bevy_main]
|
#[bevy_main]
|
||||||
fn main() {
|
fn main() {
|
||||||
@ -34,10 +33,11 @@ fn setup(
|
|||||||
});
|
});
|
||||||
commands
|
commands
|
||||||
.spawn_bundle(PbrBundle {
|
.spawn_bundle(PbrBundle {
|
||||||
mesh: meshes.add(Mesh::from(RoundedBox {
|
mesh: meshes.add(Mesh::from(Torus {
|
||||||
size: Vec3::new(1., 1., 1.),
|
radius: 0.6,
|
||||||
radius: 0.3,
|
ring_radius: 0.2,
|
||||||
subdivisions: 5,
|
subdivisions_segments: 20,
|
||||||
|
subdivisions_sides: 10,
|
||||||
})),
|
})),
|
||||||
material: materials.add(Color::rgb(0.1, 0.1, 0.9).into()),
|
material: materials.add(Color::rgb(0.1, 0.1, 0.9).into()),
|
||||||
transform: Transform::from_xyz(0.0, 1.0, 0.0),
|
transform: Transform::from_xyz(0.0, 1.0, 0.0),
|
||||||
@ -57,7 +57,7 @@ fn setup(
|
|||||||
transform: Transform::from_xyz(4.0, 8.0, 4.0),
|
transform: Transform::from_xyz(4.0, 8.0, 4.0),
|
||||||
..default()
|
..default()
|
||||||
});
|
});
|
||||||
commands.spawn_bundle(PerspectiveCameraBundle {
|
commands.spawn_bundle(Camera3dBundle {
|
||||||
transform: Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
|
transform: Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
|
||||||
..default()
|
..default()
|
||||||
});
|
});
|
146
src/lib.rs
146
src/lib.rs
@ -1,5 +1,5 @@
|
|||||||
use bevy::asset::load_internal_asset;
|
use bevy::asset::load_internal_asset;
|
||||||
use bevy::core_pipeline::{Opaque3d, Transparent3d};
|
use bevy::core_pipeline::core_3d::{Opaque3d, Transparent3d};
|
||||||
use bevy::ecs::system::lifetimeless::{Read, SQuery, SRes};
|
use bevy::ecs::system::lifetimeless::{Read, SQuery, SRes};
|
||||||
use bevy::ecs::system::SystemParamItem;
|
use bevy::ecs::system::SystemParamItem;
|
||||||
use bevy::pbr::{
|
use bevy::pbr::{
|
||||||
@ -7,24 +7,24 @@ use bevy::pbr::{
|
|||||||
};
|
};
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use bevy::reflect::TypeUuid;
|
use bevy::reflect::TypeUuid;
|
||||||
use bevy::render::camera::{ActiveCamera, Camera3d};
|
use bevy::render::extract_component::ExtractComponentPlugin;
|
||||||
use bevy::render::mesh::{MeshVertexBufferLayout, PrimitiveTopology};
|
use bevy::render::mesh::{MeshVertexBufferLayout, PrimitiveTopology};
|
||||||
use bevy::render::render_asset::{RenderAsset, RenderAssetPlugin, RenderAssets};
|
use bevy::render::render_asset::{PrepareAssetError, RenderAsset, RenderAssetPlugin, RenderAssets};
|
||||||
use bevy::render::render_component::ExtractComponentPlugin;
|
|
||||||
use bevy::render::render_phase::{
|
use bevy::render::render_phase::{
|
||||||
AddRenderCommand, DrawFunctions, EntityRenderCommand, RenderCommandResult, RenderPhase,
|
AddRenderCommand, DrawFunctions, EntityRenderCommand, RenderCommandResult, RenderPhase,
|
||||||
SetItemPipeline, TrackedRenderPass,
|
SetItemPipeline, TrackedRenderPass,
|
||||||
};
|
};
|
||||||
use bevy::render::render_resource::std140::{AsStd140, Std140};
|
|
||||||
use bevy::render::render_resource::{
|
use bevy::render::render_resource::{
|
||||||
BindGroup, BindGroupDescriptor, BindGroupEntry, BindGroupLayout, BindGroupLayoutDescriptor,
|
AsBindGroup, BindGroup, BindGroupDescriptor, BindGroupEntry, BindGroupLayout,
|
||||||
BindGroupLayoutEntry, BindingType, Buffer, BufferBindingType, BufferInitDescriptor, BufferSize,
|
BindGroupLayoutDescriptor, BindGroupLayoutEntry, BindingType, BufferBindingType, BufferSize,
|
||||||
BufferUsages, DynamicUniformVec, Face, PipelineCache, RenderPipelineDescriptor, ShaderStages,
|
DynamicUniformBuffer, Face, PipelineCache, PreparedBindGroup, RenderPipelineDescriptor,
|
||||||
SpecializedMeshPipeline, SpecializedMeshPipelineError, SpecializedMeshPipelines,
|
ShaderSize, ShaderStages, ShaderType, SpecializedMeshPipeline, SpecializedMeshPipelineError,
|
||||||
|
SpecializedMeshPipelines,
|
||||||
};
|
};
|
||||||
use bevy::render::renderer::{RenderDevice, RenderQueue};
|
use bevy::render::renderer::{RenderDevice, RenderQueue};
|
||||||
|
use bevy::render::texture::FallbackImage;
|
||||||
use bevy::render::view::ExtractedView;
|
use bevy::render::view::ExtractedView;
|
||||||
use bevy::render::{RenderApp, RenderStage};
|
use bevy::render::{Extract, RenderApp, RenderStage};
|
||||||
use libm::nextafterf;
|
use libm::nextafterf;
|
||||||
|
|
||||||
// See https://alexanderameye.github.io/notes/rendering-outlines/
|
// See https://alexanderameye.github.io/notes/rendering-outlines/
|
||||||
@ -33,12 +33,14 @@ const OUTLINE_SHADER_HANDLE: HandleUntyped =
|
|||||||
HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 2101625026478770097);
|
HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 2101625026478770097);
|
||||||
|
|
||||||
/// An asset for rendering outlines around meshes.
|
/// An asset for rendering outlines around meshes.
|
||||||
#[derive(Clone, TypeUuid)]
|
#[derive(Clone, TypeUuid, AsBindGroup)]
|
||||||
#[uuid = "552e416b-2766-4e6a-9ee5-9ebd0e8c0230"]
|
#[uuid = "552e416b-2766-4e6a-9ee5-9ebd0e8c0230"]
|
||||||
pub struct Outline {
|
pub struct Outline {
|
||||||
/// Colour of the outline
|
/// Colour of the outline
|
||||||
|
#[uniform(1, visibility(fragment))]
|
||||||
pub colour: Color,
|
pub colour: Color,
|
||||||
/// Width of the outline in logical pixels
|
/// Width of the outline in logical pixels
|
||||||
|
#[uniform(0, visibility(vertex))]
|
||||||
pub width: f32,
|
pub width: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,7 +49,12 @@ impl RenderAsset for Outline {
|
|||||||
|
|
||||||
type PreparedAsset = GpuOutline;
|
type PreparedAsset = GpuOutline;
|
||||||
|
|
||||||
type Param = (SRes<RenderDevice>, SRes<OutlinePipeline>);
|
type Param = (
|
||||||
|
SRes<RenderDevice>,
|
||||||
|
SRes<OutlinePipeline>,
|
||||||
|
SRes<RenderAssets<Image>>,
|
||||||
|
SRes<FallbackImage>,
|
||||||
|
);
|
||||||
|
|
||||||
fn extract_asset(&self) -> Self::ExtractedAsset {
|
fn extract_asset(&self) -> Self::ExtractedAsset {
|
||||||
self.clone()
|
self.clone()
|
||||||
@ -55,57 +62,35 @@ impl RenderAsset for Outline {
|
|||||||
|
|
||||||
fn prepare_asset(
|
fn prepare_asset(
|
||||||
outline: Self::ExtractedAsset,
|
outline: Self::ExtractedAsset,
|
||||||
(render_device, outline_pipeline): &mut bevy::ecs::system::SystemParamItem<Self::Param>,
|
(render_device, outline_pipeline, images, fallback_image): &mut bevy::ecs::system::SystemParamItem<Self::Param>,
|
||||||
) -> Result<
|
) -> Result<
|
||||||
Self::PreparedAsset,
|
Self::PreparedAsset,
|
||||||
bevy::render::render_asset::PrepareAssetError<Self::ExtractedAsset>,
|
bevy::render::render_asset::PrepareAssetError<Self::ExtractedAsset>,
|
||||||
> {
|
> {
|
||||||
let colour = outline.colour.as_linear_rgba_f32().into();
|
if let Ok(pbg) = outline.as_bind_group(
|
||||||
let vbuffer = render_device.create_buffer_with_data(&BufferInitDescriptor {
|
&outline_pipeline.outline_bind_group_layout,
|
||||||
label: Some("outline_vertex_stage_uniform_buffer"),
|
render_device,
|
||||||
usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST,
|
images,
|
||||||
contents: VertexStageData {
|
fallback_image,
|
||||||
width: outline.width,
|
) {
|
||||||
}
|
|
||||||
.as_std140()
|
|
||||||
.as_bytes(),
|
|
||||||
});
|
|
||||||
let fbuffer = render_device.create_buffer_with_data(&BufferInitDescriptor {
|
|
||||||
label: Some("outline_fragment_stage_uniform_buffer"),
|
|
||||||
usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST,
|
|
||||||
contents: FragmentStageData { colour }.as_std140().as_bytes(),
|
|
||||||
});
|
|
||||||
let bind_group = render_device.create_bind_group(&BindGroupDescriptor {
|
|
||||||
label: Some("outline_bind_group"),
|
|
||||||
layout: &outline_pipeline.outline_bind_group_layout,
|
|
||||||
entries: &[
|
|
||||||
BindGroupEntry {
|
|
||||||
binding: 0,
|
|
||||||
resource: vbuffer.as_entire_binding(),
|
|
||||||
},
|
|
||||||
BindGroupEntry {
|
|
||||||
binding: 1,
|
|
||||||
resource: fbuffer.as_entire_binding(),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
Ok(GpuOutline {
|
Ok(GpuOutline {
|
||||||
_vertex_stage_buffer: vbuffer,
|
bind_group: pbg,
|
||||||
_fragment_stage_buffer: fbuffer,
|
transparent: outline.colour.a() < 1.0,
|
||||||
bind_group,
|
|
||||||
transparent: colour.w < 1.0,
|
|
||||||
})
|
})
|
||||||
|
} else {
|
||||||
|
Err(PrepareAssetError::RetryNextUpdate(outline))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Component, AsStd140)]
|
#[derive(Clone, Component, ShaderType)]
|
||||||
struct ViewSizeUniform {
|
struct ViewSizeUniform {
|
||||||
logical_size: Vec2,
|
logical_size: Vec2,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct ViewSizeUniforms {
|
struct ViewSizeUniforms {
|
||||||
pub uniforms: DynamicUniformVec<ViewSizeUniform>,
|
pub uniforms: DynamicUniformBuffer<ViewSizeUniform>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
@ -117,20 +102,8 @@ struct GpuViewSize {
|
|||||||
bind_group: BindGroup,
|
bind_group: BindGroup,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, AsStd140)]
|
|
||||||
struct VertexStageData {
|
|
||||||
width: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, AsStd140)]
|
|
||||||
struct FragmentStageData {
|
|
||||||
colour: Vec4,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct GpuOutline {
|
pub struct GpuOutline {
|
||||||
_vertex_stage_buffer: Buffer,
|
bind_group: PreparedBindGroup<Outline>,
|
||||||
_fragment_stage_buffer: Buffer,
|
|
||||||
bind_group: BindGroup,
|
|
||||||
transparent: bool,
|
transparent: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -203,28 +176,26 @@ impl<const I: usize> EntityRenderCommand for SetOutlineBindGroup<I> {
|
|||||||
) -> RenderCommandResult {
|
) -> RenderCommandResult {
|
||||||
let outline_handle = query.get(item).unwrap();
|
let outline_handle = query.get(item).unwrap();
|
||||||
let outline = outlines.into_inner().get(outline_handle).unwrap();
|
let outline = outlines.into_inner().get(outline_handle).unwrap();
|
||||||
pass.set_bind_group(I, &outline.bind_group, &[]);
|
pass.set_bind_group(I, &outline.bind_group.bind_group, &[]);
|
||||||
RenderCommandResult::Success
|
RenderCommandResult::Success
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extract_view_size_uniforms(
|
fn extract_view_size_uniforms(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
windows: Res<Windows>,
|
query: Extract<Query<(Entity, &Camera), With<Camera3d>>>,
|
||||||
images: Res<Assets<Image>>,
|
|
||||||
active_camera: Res<ActiveCamera<Camera3d>>,
|
|
||||||
query: Query<&Camera, With<Camera3d>>,
|
|
||||||
) {
|
) {
|
||||||
if let Some(entity) = active_camera.get() {
|
for (entity, camera) in query.iter() {
|
||||||
if let Ok(camera) = query.get(entity) {
|
if !camera.is_active {
|
||||||
if let Some(size) = camera.target.get_logical_size(&windows, &images) {
|
continue;
|
||||||
|
}
|
||||||
|
if let Some(size) = camera.logical_viewport_size() {
|
||||||
commands
|
commands
|
||||||
.get_or_spawn(entity)
|
.get_or_spawn(entity)
|
||||||
.insert(ViewSizeUniform { logical_size: size });
|
.insert(ViewSizeUniform { logical_size: size });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fn prepare_view_size_uniforms(
|
fn prepare_view_size_uniforms(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
@ -348,43 +319,12 @@ impl FromWorld for OutlinePipeline {
|
|||||||
ty: BindingType::Buffer {
|
ty: BindingType::Buffer {
|
||||||
ty: BufferBindingType::Uniform,
|
ty: BufferBindingType::Uniform,
|
||||||
has_dynamic_offset: true,
|
has_dynamic_offset: true,
|
||||||
min_binding_size: BufferSize::new(
|
min_binding_size: BufferSize::new(ViewSizeUniform::SHADER_SIZE.get()),
|
||||||
ViewSizeUniform::std140_size_static() as u64
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
count: None,
|
count: None,
|
||||||
}],
|
}],
|
||||||
});
|
});
|
||||||
let outline_bind_group_layout =
|
let outline_bind_group_layout = Outline::bind_group_layout(&render_device);
|
||||||
render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
|
|
||||||
label: Some("outline_bind_group_layout"),
|
|
||||||
entries: &[
|
|
||||||
BindGroupLayoutEntry {
|
|
||||||
binding: 0,
|
|
||||||
visibility: ShaderStages::VERTEX,
|
|
||||||
ty: BindingType::Buffer {
|
|
||||||
ty: BufferBindingType::Uniform,
|
|
||||||
has_dynamic_offset: false,
|
|
||||||
min_binding_size: BufferSize::new(
|
|
||||||
VertexStageData::std140_size_static() as u64,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
count: None,
|
|
||||||
},
|
|
||||||
BindGroupLayoutEntry {
|
|
||||||
binding: 1,
|
|
||||||
visibility: ShaderStages::FRAGMENT,
|
|
||||||
ty: BindingType::Buffer {
|
|
||||||
ty: BufferBindingType::Uniform,
|
|
||||||
has_dynamic_offset: false,
|
|
||||||
min_binding_size: BufferSize::new(
|
|
||||||
FragmentStageData::std140_size_static() as u64,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
count: None,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
OutlinePipeline {
|
OutlinePipeline {
|
||||||
mesh_pipeline,
|
mesh_pipeline,
|
||||||
view_size_bind_group_layout,
|
view_size_bind_group_layout,
|
||||||
|
@ -1,37 +1,37 @@
|
|||||||
#import bevy_pbr::mesh_view_bind_group
|
#import bevy_pbr::mesh_types
|
||||||
#import bevy_pbr::mesh_struct
|
#import bevy_pbr::mesh_view_bindings
|
||||||
|
|
||||||
struct Vertex {
|
struct Vertex {
|
||||||
[[location(0)]] position: vec3<f32>;
|
@location(0) position: vec3<f32>,
|
||||||
[[location(1)]] normal: vec3<f32>;
|
@location(1) normal: vec3<f32>,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct VertexOutput {
|
struct VertexOutput {
|
||||||
[[builtin(position)]] clip_position: vec4<f32>;
|
@builtin(position) clip_position: vec4<f32>,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ViewSizeUniforms {
|
struct ViewSizeUniforms {
|
||||||
logical_size: vec2<f32>;
|
logical_size: vec2<f32>,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct VertexStageData {
|
struct VertexStageData {
|
||||||
width: f32;
|
width: f32,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FragmentStageData {
|
struct FragmentStageData {
|
||||||
colour: vec4<f32>;
|
colour: vec4<f32>,
|
||||||
};
|
};
|
||||||
|
|
||||||
[[group(1), binding(0)]]
|
@group(1) @binding(0)
|
||||||
var<uniform> mesh: Mesh;
|
var<uniform> mesh: Mesh;
|
||||||
|
|
||||||
[[group(2), binding(0)]]
|
@group(2) @binding(0)
|
||||||
var<uniform> view_size: ViewSizeUniforms;
|
var<uniform> view_size: ViewSizeUniforms;
|
||||||
|
|
||||||
[[group(3), binding(0)]]
|
@group(3) @binding(0)
|
||||||
var<uniform> vstage: VertexStageData;
|
var<uniform> vstage: VertexStageData;
|
||||||
|
|
||||||
[[group(3), binding(1)]]
|
@group(3) @binding(1)
|
||||||
var<uniform> fstage: FragmentStageData;
|
var<uniform> fstage: FragmentStageData;
|
||||||
|
|
||||||
fn mat4to3(m: mat4x4<f32>) -> mat3x3<f32> {
|
fn mat4to3(m: mat4x4<f32>) -> mat3x3<f32> {
|
||||||
@ -40,7 +40,7 @@ fn mat4to3(m: mat4x4<f32>) -> mat3x3<f32> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
[[stage(vertex)]]
|
@vertex
|
||||||
fn vertex(vertex: Vertex) -> VertexOutput {
|
fn vertex(vertex: Vertex) -> VertexOutput {
|
||||||
var out: VertexOutput;
|
var out: VertexOutput;
|
||||||
var clip_pos = view.view_proj * (mesh.model * vec4<f32>(vertex.position, 1.0));
|
var clip_pos = view.view_proj * (mesh.model * vec4<f32>(vertex.position, 1.0));
|
||||||
@ -50,11 +50,7 @@ fn vertex(vertex: Vertex) -> VertexOutput {
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct FragmentInput {
|
@fragment
|
||||||
[[builtin(front_facing)]] is_front: bool;
|
fn fragment() -> @location(0) vec4<f32> {
|
||||||
};
|
|
||||||
|
|
||||||
[[stage(fragment)]]
|
|
||||||
fn fragment(in: FragmentInput) -> [[location(0)]] vec4<f32> {
|
|
||||||
return fstage.colour;
|
return fstage.colour;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user