Port to Bevy 0.8.

This commit is contained in:
Robin KAY 2022-08-01 22:10:53 +01:00
parent e06fb383a3
commit 4627eaa093
4 changed files with 75 additions and 142 deletions

View File

@ -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"

View File

@ -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()
}); });

View File

@ -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,

View File

@ -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;
} }