Port to Bevy 0.10.

This commit is contained in:
Robin KAY 2023-03-10 18:43:13 +00:00
parent 2bb0ba2b20
commit d84e476c78
11 changed files with 190 additions and 178 deletions

View File

@ -1,6 +1,6 @@
[package] [package]
name = "bevy_mod_outline" name = "bevy_mod_outline"
version = "0.3.5" version = "0.4.0"
edition = "2021" edition = "2021"
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
description = "A mesh outlining plugin for Bevy." description = "A mesh outlining plugin for Bevy."
@ -11,7 +11,7 @@ keywords = ["gamedev", "bevy", "outline"]
categories = ["game-engines", "rendering"] categories = ["game-engines", "rendering"]
[dependencies] [dependencies]
bevy = { version = "0.9", default-features = false, features = [ bevy = { version = "0.10", default-features = false, features = [
"bevy_asset", "bevy_asset",
"bevy_render", "bevy_render",
"bevy_pbr", "bevy_pbr",
@ -22,7 +22,7 @@ interpolation = "0.2"
thiserror = "1.0" thiserror = "1.0"
[dev-dependencies] [dev-dependencies]
bevy = { version = "0.9", default-features = false, features = [ bevy = { version = "0.10", default-features = false, features = [
"bevy_winit", "bevy_winit",
"x11", "x11",
] } ] }
@ -48,4 +48,4 @@ name = "animated_fox"
path = "examples/animated_fox.rs" path = "examples/animated_fox.rs"
required-features = [ required-features = [
"bevy/animation", "bevy/bevy_gltf", "bevy/png", "bevy/bevy_scene" "bevy/animation", "bevy/bevy_gltf", "bevy/png", "bevy/bevy_scene"
] ]

View File

@ -1,6 +1,10 @@
use std::f32::consts::PI; use std::f32::consts::PI;
use bevy::{prelude::*, scene::SceneInstance, window::close_on_esc}; use bevy::{
prelude::{shape::Plane, *},
scene::SceneInstance,
window::close_on_esc,
};
use bevy_mod_outline::{ use bevy_mod_outline::{
AutoGenerateOutlineNormalsPlugin, OutlineBundle, OutlinePlugin, OutlineVolume, AutoGenerateOutlineNormalsPlugin, OutlineBundle, OutlinePlugin, OutlineVolume,
}; };
@ -41,7 +45,10 @@ fn setup(
// Plane // Plane
commands.spawn(PbrBundle { commands.spawn(PbrBundle {
mesh: meshes.add(Mesh::from(shape::Plane { size: 500000.0 })), mesh: meshes.add(Mesh::from(Plane {
size: 500000.0,
subdivisions: 0,
})),
material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()), material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()),
..default() ..default()
}); });

View File

@ -2,7 +2,7 @@ use std::f32::consts::TAU;
use bevy::{ use bevy::{
prelude::{ prelude::{
shape::{Capsule, Torus, UVSphere}, shape::{Capsule, Plane, Torus, UVSphere},
*, *,
}, },
window::close_on_esc, window::close_on_esc,
@ -13,7 +13,7 @@ use bevy_mod_outline::*;
#[bevy_main] #[bevy_main]
fn main() { fn main() {
App::new() App::new()
.insert_resource(Msaa { samples: 4 }) .insert_resource(Msaa::Sample4)
.insert_resource(ClearColor(Color::BLACK)) .insert_resource(ClearColor(Color::BLACK))
.add_plugins(DefaultPlugins) .add_plugins(DefaultPlugins)
.add_plugin(OutlinePlugin) .add_plugin(OutlinePlugin)
@ -101,7 +101,10 @@ fn setup(
// Add plane, light source, and camera // Add plane, light source, and camera
commands.spawn(PbrBundle { commands.spawn(PbrBundle {
mesh: meshes.add(Mesh::from(bevy::prelude::shape::Plane { size: 5.0 })), mesh: meshes.add(Mesh::from(Plane {
size: 5.0,
subdivisions: 0,
})),
material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()), material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()),
..default() ..default()
}); });

View File

@ -2,16 +2,19 @@ use std::f32::consts::PI;
use bevy::{ use bevy::{
core_pipeline::clear_color::ClearColorConfig, core_pipeline::clear_color::ClearColorConfig,
prelude::{shape::Torus, *}, prelude::{
shape::{Plane, Torus},
*,
},
render::{camera::Viewport, view::RenderLayers}, render::{camera::Viewport, view::RenderLayers},
window::close_on_esc, window::{close_on_esc, PrimaryWindow},
}; };
use bevy_mod_outline::{OutlineBundle, OutlinePlugin, OutlineRenderLayers, OutlineVolume}; use bevy_mod_outline::{OutlineBundle, OutlinePlugin, OutlineRenderLayers, OutlineVolume};
#[bevy_main] #[bevy_main]
fn main() { fn main() {
App::new() App::new()
.insert_resource(Msaa { samples: 4 }) .insert_resource(Msaa::Sample4)
.insert_resource(ClearColor(Color::BLACK)) .insert_resource(ClearColor(Color::BLACK))
.add_plugins(DefaultPlugins) .add_plugins(DefaultPlugins)
.add_plugin(OutlinePlugin) .add_plugin(OutlinePlugin)
@ -62,7 +65,10 @@ fn setup(
// Add plane and light source // Add plane and light source
commands.spawn(PbrBundle { commands.spawn(PbrBundle {
mesh: meshes.add(Mesh::from(bevy::prelude::shape::Plane { size: 5.0 })), mesh: meshes.add(Mesh::from(Plane {
size: 5.0,
subdivisions: 0,
})),
material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()), material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()),
..default() ..default()
}); });
@ -90,7 +96,7 @@ fn setup(
commands commands
.spawn(Camera3dBundle { .spawn(Camera3dBundle {
camera: Camera { camera: Camera {
priority: i, order: i,
..default() ..default()
}, },
camera_3d: Camera3d { camera_3d: Camera3d {
@ -112,10 +118,13 @@ fn setup(
} }
} }
fn set_camera_viewports(windows: Res<Windows>, mut query: Query<(&mut Camera, &CameraMode)>) { fn set_camera_viewports(
if windows.is_changed() { win_query: Query<(&Window, Changed<Window>), With<PrimaryWindow>>,
mut query: Query<(&mut Camera, &CameraMode)>,
) {
let (win, win_changed) = win_query.get_single().unwrap();
if win_changed {
// Divide window into quadrants // Divide window into quadrants
let win = windows.primary();
let size = UVec2::new(win.physical_width() / 2, win.physical_height() / 2); let size = UVec2::new(win.physical_width() / 2, win.physical_height() / 2);
for (mut camera, mode) in query.iter_mut() { for (mut camera, mode) in query.iter_mut() {
let offset = UVec2::new( let offset = UVec2::new(

View File

@ -2,7 +2,7 @@ use std::f32::consts::{PI, TAU};
use bevy::{ use bevy::{
prelude::{ prelude::{
shape::{Cube, Torus}, shape::{Cube, Plane, Torus},
*, *,
}, },
window::close_on_esc, window::close_on_esc,
@ -13,7 +13,7 @@ use bevy_mod_outline::*;
#[bevy_main] #[bevy_main]
fn main() { fn main() {
App::new() App::new()
.insert_resource(Msaa { samples: 4 }) .insert_resource(Msaa::Sample4)
.insert_resource(ClearColor(Color::BLACK)) .insert_resource(ClearColor(Color::BLACK))
.add_plugins(DefaultPlugins) .add_plugins(DefaultPlugins)
.add_plugin(OutlinePlugin) .add_plugin(OutlinePlugin)
@ -81,7 +81,10 @@ fn setup(
// Add plane, light source, and camera // Add plane, light source, and camera
commands.spawn(PbrBundle { commands.spawn(PbrBundle {
mesh: meshes.add(Mesh::from(bevy::prelude::shape::Plane { size: 5.0 })), mesh: meshes.add(Mesh::from(Plane {
size: 5.0,
subdivisions: 0,
})),
material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()), material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()),
..default() ..default()
}); });

View File

@ -29,7 +29,7 @@ pub(crate) fn queue_outline_stencil_mesh(
stencil_pipeline: Res<OutlinePipeline>, stencil_pipeline: Res<OutlinePipeline>,
msaa: Res<Msaa>, msaa: Res<Msaa>,
mut pipelines: ResMut<SpecializedMeshPipelines<OutlinePipeline>>, mut pipelines: ResMut<SpecializedMeshPipelines<OutlinePipeline>>,
mut pipeline_cache: ResMut<PipelineCache>, pipeline_cache: Res<PipelineCache>,
render_meshes: Res<RenderAssets<Mesh>>, render_meshes: Res<RenderAssets<Mesh>>,
material_meshes: Query<( material_meshes: Query<(
Entity, Entity,
@ -50,7 +50,7 @@ pub(crate) fn queue_outline_stencil_mesh(
.unwrap(); .unwrap();
let base_key = PipelineKey::new() let base_key = PipelineKey::new()
.with_msaa_samples(msaa.samples) .with_msaa(*msaa)
.with_pass_type(PassType::Stencil); .with_pass_type(PassType::Stencil);
for (view, mut stencil_phase, view_mask) in views.iter_mut() { for (view, mut stencil_phase, view_mask) in views.iter_mut() {
@ -68,7 +68,7 @@ pub(crate) fn queue_outline_stencil_mesh(
.with_depth_mode(stencil_flags.depth_mode) .with_depth_mode(stencil_flags.depth_mode)
.with_offset_zero(stencil_uniform.offset == 0.0); .with_offset_zero(stencil_uniform.offset == 0.0);
let pipeline = pipelines let pipeline = pipelines
.specialize(&mut pipeline_cache, &stencil_pipeline, key, &mesh.layout) .specialize(&pipeline_cache, &stencil_pipeline, key, &mesh.layout)
.unwrap(); .unwrap();
let distance = let distance =
rangefinder.distance(&Mat4::from_translation(stencil_uniform.origin)); rangefinder.distance(&Mat4::from_translation(stencil_uniform.origin));
@ -99,7 +99,7 @@ pub(crate) fn queue_outline_volume_mesh(
outline_pipeline: Res<OutlinePipeline>, outline_pipeline: Res<OutlinePipeline>,
msaa: Res<Msaa>, msaa: Res<Msaa>,
mut pipelines: ResMut<SpecializedMeshPipelines<OutlinePipeline>>, mut pipelines: ResMut<SpecializedMeshPipelines<OutlinePipeline>>,
mut pipeline_cache: ResMut<PipelineCache>, pipeline_cache: Res<PipelineCache>,
render_meshes: Res<RenderAssets<Mesh>>, render_meshes: Res<RenderAssets<Mesh>>,
material_meshes: Query<( material_meshes: Query<(
Entity, Entity,
@ -125,7 +125,7 @@ pub(crate) fn queue_outline_volume_mesh(
.get_id::<DrawOutline>() .get_id::<DrawOutline>()
.unwrap(); .unwrap();
let base_key = PipelineKey::new().with_msaa_samples(msaa.samples); let base_key = PipelineKey::new().with_msaa(*msaa);
for (view, mut opaque_phase, mut transparent_phase, view_mask) in views.iter_mut() { for (view, mut opaque_phase, mut transparent_phase, view_mask) in views.iter_mut() {
let view_mask = view_mask.copied().unwrap_or_default(); let view_mask = view_mask.copied().unwrap_or_default();
@ -149,7 +149,7 @@ pub(crate) fn queue_outline_volume_mesh(
.with_offset_zero(volume_uniform.offset == 0.0) .with_offset_zero(volume_uniform.offset == 0.0)
.with_hdr_format(view.hdr); .with_hdr_format(view.hdr);
let pipeline = pipelines let pipeline = pipelines
.specialize(&mut pipeline_cache, &outline_pipeline, key, &mesh.layout) .specialize(&pipeline_cache, &outline_pipeline, key, &mesh.layout)
.unwrap(); .unwrap();
let distance = rangefinder.distance(&Mat4::from_translation(volume_uniform.origin)); let distance = rangefinder.distance(&Mat4::from_translation(volume_uniform.origin));
if transparent { if transparent {

View File

@ -31,7 +31,7 @@ use bevy::render::render_graph::RenderGraph;
use bevy::render::render_phase::{sort_phase_system, AddRenderCommand, DrawFunctions}; use bevy::render::render_phase::{sort_phase_system, AddRenderCommand, DrawFunctions};
use bevy::render::render_resource::{SpecializedMeshPipelines, VertexFormat}; use bevy::render::render_resource::{SpecializedMeshPipelines, VertexFormat};
use bevy::render::view::RenderLayers; use bevy::render::view::RenderLayers;
use bevy::render::{RenderApp, RenderStage}; use bevy::render::{RenderApp, RenderSet};
use bevy::transform::TransformSystem; use bevy::transform::TransformSystem;
use interpolation::Lerp; use interpolation::Lerp;
@ -82,9 +82,10 @@ pub struct OutlineStencil {
impl ExtractComponent for OutlineStencil { impl ExtractComponent for OutlineStencil {
type Query = &'static OutlineStencil; type Query = &'static OutlineStencil;
type Filter = (); type Filter = ();
type Out = Self;
fn extract_component(item: QueryItem<Self::Query>) -> Self { fn extract_component(item: QueryItem<Self::Query>) -> Option<Self> {
item.clone() Some(item.clone())
} }
} }
@ -143,14 +144,17 @@ impl ExtractComponent for OutlineRenderLayers {
Option<&'static RenderLayers>, Option<&'static RenderLayers>,
); );
type Filter = Or<(With<OutlineVolume>, With<OutlineStencil>)>; type Filter = Or<(With<OutlineVolume>, With<OutlineStencil>)>;
type Out = Self;
fn extract_component( fn extract_component(
(outline_mask, object_mask): (Option<&OutlineRenderLayers>, Option<&RenderLayers>), (outline_mask, object_mask): (Option<&OutlineRenderLayers>, Option<&RenderLayers>),
) -> Self { ) -> Option<Self> {
outline_mask Some(
.copied() outline_mask
.or_else(|| object_mask.copied().map(OutlineRenderLayers)) .copied()
.unwrap_or_default() .or_else(|| object_mask.copied().map(OutlineRenderLayers))
.unwrap_or_default(),
)
} }
} }
@ -193,9 +197,10 @@ impl Plugin for OutlinePlugin {
.add_plugin(UniformComponentPlugin::<OutlineVolumeUniform>::default()) .add_plugin(UniformComponentPlugin::<OutlineVolumeUniform>::default())
.add_plugin(UniformComponentPlugin::<OutlineFragmentUniform>::default()) .add_plugin(UniformComponentPlugin::<OutlineFragmentUniform>::default())
.add_plugin(UniformComponentPlugin::<OutlineViewUniform>::default()) .add_plugin(UniformComponentPlugin::<OutlineViewUniform>::default())
.add_system_to_stage( .add_system(
CoreStage::PostUpdate, compute_outline_depth
compute_outline_depth.after(TransformSystem::TransformPropagate), .in_base_set(CoreSet::PostUpdate)
.after(TransformSystem::TransformPropagate),
) )
.sub_app_mut(RenderApp) .sub_app_mut(RenderApp)
.init_resource::<DrawFunctions<StencilOutline>>() .init_resource::<DrawFunctions<StencilOutline>>()
@ -206,20 +211,17 @@ impl Plugin for OutlinePlugin {
.add_render_command::<StencilOutline, DrawStencil>() .add_render_command::<StencilOutline, DrawStencil>()
.add_render_command::<OpaqueOutline, DrawOutline>() .add_render_command::<OpaqueOutline, DrawOutline>()
.add_render_command::<TransparentOutline, DrawOutline>() .add_render_command::<TransparentOutline, DrawOutline>()
.add_system_to_stage(RenderStage::Extract, extract_outline_view_uniforms) .add_system(extract_outline_view_uniforms.in_schedule(ExtractSchedule))
.add_system_to_stage(RenderStage::Extract, extract_outline_stencil_uniforms) .add_system(extract_outline_stencil_uniforms.in_schedule(ExtractSchedule))
.add_system_to_stage(RenderStage::Extract, extract_outline_volume_uniforms) .add_system(extract_outline_volume_uniforms.in_schedule(ExtractSchedule))
.add_system_to_stage(RenderStage::PhaseSort, sort_phase_system::<StencilOutline>) .add_system(sort_phase_system::<StencilOutline>.in_set(RenderSet::PhaseSort))
.add_system_to_stage(RenderStage::PhaseSort, sort_phase_system::<OpaqueOutline>) .add_system(sort_phase_system::<OpaqueOutline>.in_set(RenderSet::PhaseSort))
.add_system_to_stage( .add_system(sort_phase_system::<TransparentOutline>.in_set(RenderSet::PhaseSort))
RenderStage::PhaseSort, .add_system(queue_outline_view_bind_group.in_set(RenderSet::Queue))
sort_phase_system::<TransparentOutline>, .add_system(queue_outline_stencil_bind_group.in_set(RenderSet::Queue))
) .add_system(queue_outline_volume_bind_group.in_set(RenderSet::Queue))
.add_system_to_stage(RenderStage::Queue, queue_outline_view_bind_group) .add_system(queue_outline_stencil_mesh.in_set(RenderSet::Queue))
.add_system_to_stage(RenderStage::Queue, queue_outline_stencil_bind_group) .add_system(queue_outline_volume_mesh.in_set(RenderSet::Queue));
.add_system_to_stage(RenderStage::Queue, queue_outline_volume_bind_group)
.add_system_to_stage(RenderStage::Queue, queue_outline_stencil_mesh)
.add_system_to_stage(RenderStage::Queue, queue_outline_volume_mesh);
let world = &mut app.sub_app_mut(RenderApp).world; let world = &mut app.sub_app_mut(RenderApp).world;
let node = OutlineNode::new(world); let node = OutlineNode::new(world);
@ -230,28 +232,22 @@ impl Plugin for OutlinePlugin {
.get_sub_graph_mut(bevy::core_pipeline::core_3d::graph::NAME) .get_sub_graph_mut(bevy::core_pipeline::core_3d::graph::NAME)
.unwrap(); .unwrap();
draw_3d_graph.add_node(OUTLINE_PASS_NODE_NAME, node); draw_3d_graph.add_node(OUTLINE_PASS_NODE_NAME, node);
draw_3d_graph draw_3d_graph.add_slot_edge(
.add_slot_edge( draw_3d_graph.input_node().id,
draw_3d_graph.input_node().unwrap().id, bevy::core_pipeline::core_3d::graph::input::VIEW_ENTITY,
bevy::core_pipeline::core_3d::graph::input::VIEW_ENTITY, OUTLINE_PASS_NODE_NAME,
OUTLINE_PASS_NODE_NAME, OutlineNode::IN_VIEW,
OutlineNode::IN_VIEW, );
)
.unwrap();
// Run after main 3D pass, but before UI psss // Run after main 3D pass, but before UI psss
draw_3d_graph draw_3d_graph.add_node_edge(
.add_node_edge( bevy::core_pipeline::core_3d::graph::node::MAIN_PASS,
bevy::core_pipeline::core_3d::graph::node::MAIN_PASS, OUTLINE_PASS_NODE_NAME,
OUTLINE_PASS_NODE_NAME, );
)
.unwrap();
#[cfg(feature = "bevy_ui")] #[cfg(feature = "bevy_ui")]
draw_3d_graph draw_3d_graph.add_node_edge(
.add_node_edge( OUTLINE_PASS_NODE_NAME,
OUTLINE_PASS_NODE_NAME, bevy::ui::draw_ui_graph::node::UI_PASS,
bevy::ui::draw_ui_graph::node::UI_PASS, );
)
.unwrap();
} }
} }

View File

@ -5,8 +5,7 @@ use bevy::prelude::*;
use bevy::render::camera::ExtractedCamera; use bevy::render::camera::ExtractedCamera;
use bevy::render::render_graph::{NodeRunError, SlotInfo, SlotType}; use bevy::render::render_graph::{NodeRunError, SlotInfo, SlotType};
use bevy::render::render_phase::{ use bevy::render::render_phase::{
CachedRenderPipelinePhaseItem, DrawFunctionId, DrawFunctions, EntityPhaseItem, PhaseItem, CachedRenderPipelinePhaseItem, DrawFunctionId, PhaseItem, RenderPhase,
RenderPhase, TrackedRenderPass,
}; };
use bevy::render::render_resource::{ use bevy::render::render_resource::{
CachedRenderPipelineId, LoadOp, Operations, RenderPassDepthStencilAttachment, CachedRenderPipelineId, LoadOp, Operations, RenderPassDepthStencilAttachment,
@ -29,6 +28,11 @@ pub(crate) struct StencilOutline {
impl PhaseItem for StencilOutline { impl PhaseItem for StencilOutline {
type SortKey = Reverse<FloatOrd>; type SortKey = Reverse<FloatOrd>;
#[inline]
fn entity(&self) -> Entity {
self.entity
}
fn sort_key(&self) -> Self::SortKey { fn sort_key(&self) -> Self::SortKey {
Reverse(FloatOrd(self.distance)) Reverse(FloatOrd(self.distance))
} }
@ -38,13 +42,6 @@ impl PhaseItem for StencilOutline {
} }
} }
impl EntityPhaseItem for StencilOutline {
#[inline]
fn entity(&self) -> Entity {
self.entity
}
}
impl CachedRenderPipelinePhaseItem for StencilOutline { impl CachedRenderPipelinePhaseItem for StencilOutline {
#[inline] #[inline]
fn cached_pipeline(&self) -> CachedRenderPipelineId { fn cached_pipeline(&self) -> CachedRenderPipelineId {
@ -62,6 +59,11 @@ pub(crate) struct OpaqueOutline {
impl PhaseItem for OpaqueOutline { impl PhaseItem for OpaqueOutline {
type SortKey = Reverse<FloatOrd>; type SortKey = Reverse<FloatOrd>;
#[inline]
fn entity(&self) -> Entity {
self.entity
}
fn sort_key(&self) -> Self::SortKey { fn sort_key(&self) -> Self::SortKey {
Reverse(FloatOrd(self.distance)) Reverse(FloatOrd(self.distance))
} }
@ -71,13 +73,6 @@ impl PhaseItem for OpaqueOutline {
} }
} }
impl EntityPhaseItem for OpaqueOutline {
#[inline]
fn entity(&self) -> Entity {
self.entity
}
}
impl CachedRenderPipelinePhaseItem for OpaqueOutline { impl CachedRenderPipelinePhaseItem for OpaqueOutline {
#[inline] #[inline]
fn cached_pipeline(&self) -> CachedRenderPipelineId { fn cached_pipeline(&self) -> CachedRenderPipelineId {
@ -95,6 +90,11 @@ pub(crate) struct TransparentOutline {
impl PhaseItem for TransparentOutline { impl PhaseItem for TransparentOutline {
type SortKey = FloatOrd; type SortKey = FloatOrd;
#[inline]
fn entity(&self) -> Entity {
self.entity
}
fn sort_key(&self) -> Self::SortKey { fn sort_key(&self) -> Self::SortKey {
FloatOrd(self.distance) FloatOrd(self.distance)
} }
@ -104,13 +104,6 @@ impl PhaseItem for TransparentOutline {
} }
} }
impl EntityPhaseItem for TransparentOutline {
#[inline]
fn entity(&self) -> Entity {
self.entity
}
}
impl CachedRenderPipelinePhaseItem for TransparentOutline { impl CachedRenderPipelinePhaseItem for TransparentOutline {
#[inline] #[inline]
fn cached_pipeline(&self) -> CachedRenderPipelineId { fn cached_pipeline(&self) -> CachedRenderPipelineId {
@ -182,19 +175,11 @@ impl Node for OutlineNode {
stencil_ops: None, stencil_ops: None,
}), }),
}; };
let draw_functions = world.resource::<DrawFunctions<StencilOutline>>(); let mut tracked_pass = render_context.begin_tracked_render_pass(pass_descriptor);
let render_pass = render_context
.command_encoder
.begin_render_pass(&pass_descriptor);
let mut draw_functions = draw_functions.write();
let mut tracked_pass = TrackedRenderPass::new(render_pass);
if let Some(viewport) = camera.viewport.as_ref() { if let Some(viewport) = camera.viewport.as_ref() {
tracked_pass.set_camera_viewport(viewport); tracked_pass.set_camera_viewport(viewport);
} }
for item in &stencil_phase.items { stencil_phase.render(&mut tracked_pass, world, view_entity);
let draw_function = draw_functions.get_mut(item.draw_function).unwrap();
draw_function.draw(world, &mut tracked_pass, view_entity, item);
}
} }
if !opaque_phase.items.is_empty() { if !opaque_phase.items.is_empty() {
@ -213,19 +198,11 @@ impl Node for OutlineNode {
stencil_ops: None, stencil_ops: None,
}), }),
}; };
let draw_functions = world.resource::<DrawFunctions<OpaqueOutline>>(); let mut tracked_pass = render_context.begin_tracked_render_pass(pass_descriptor);
let render_pass = render_context
.command_encoder
.begin_render_pass(&pass_descriptor);
let mut draw_functions = draw_functions.write();
let mut tracked_pass = TrackedRenderPass::new(render_pass);
if let Some(viewport) = camera.viewport.as_ref() { if let Some(viewport) = camera.viewport.as_ref() {
tracked_pass.set_camera_viewport(viewport); tracked_pass.set_camera_viewport(viewport);
} }
for item in &opaque_phase.items { opaque_phase.render(&mut tracked_pass, world, view_entity);
let draw_function = draw_functions.get_mut(item.draw_function).unwrap();
draw_function.draw(world, &mut tracked_pass, view_entity, item);
}
} }
if !transparent_phase.items.is_empty() { if !transparent_phase.items.is_empty() {
@ -244,19 +221,11 @@ impl Node for OutlineNode {
stencil_ops: None, stencil_ops: None,
}), }),
}; };
let draw_functions = world.resource::<DrawFunctions<TransparentOutline>>(); let mut tracked_pass = render_context.begin_tracked_render_pass(pass_descriptor);
let render_pass = render_context
.command_encoder
.begin_render_pass(&pass_descriptor);
let mut draw_functions = draw_functions.write();
let mut tracked_pass = TrackedRenderPass::new(render_pass);
if let Some(viewport) = camera.viewport.as_ref() { if let Some(viewport) = camera.viewport.as_ref() {
tracked_pass.set_camera_viewport(viewport); tracked_pass.set_camera_viewport(viewport);
} }
for item in &transparent_phase.items { transparent_phase.render(&mut tracked_pass, world, view_entity);
let draw_function = draw_functions.get_mut(item.draw_function).unwrap();
draw_function.draw(world, &mut tracked_pass, view_entity, item);
}
} }
Ok(()) Ok(())

View File

@ -1,13 +1,14 @@
use std::borrow::Cow; use std::borrow::Cow;
use bevy::pbr::{MAX_CASCADES_PER_LIGHT, MAX_DIRECTIONAL_LIGHTS};
use bevy::prelude::*; use bevy::prelude::*;
use bevy::reflect::TypeUuid; use bevy::reflect::TypeUuid;
use bevy::render::render_resource::{ use bevy::render::render_resource::{
BindGroupLayout, BindGroupLayoutDescriptor, BindGroupLayoutEntry, BindingType, BlendState, BindGroupLayout, BindGroupLayoutDescriptor, BindGroupLayoutEntry, BindingType, BlendState,
BufferBindingType, BufferSize, ColorTargetState, ColorWrites, CompareFunction, DepthBiasState, BufferBindingType, BufferSize, ColorTargetState, ColorWrites, CompareFunction, DepthBiasState,
DepthStencilState, Face, FragmentState, FrontFace, MultisampleState, PolygonMode, DepthStencilState, Face, FragmentState, FrontFace, MultisampleState, PolygonMode,
PrimitiveState, PrimitiveTopology, ShaderSize, ShaderStages, StencilState, TextureFormat, PrimitiveState, PrimitiveTopology, ShaderDefVal, ShaderSize, ShaderStages, StencilState,
VertexState, TextureFormat, VertexState,
}; };
use bevy::render::renderer::RenderDevice; use bevy::render::renderer::RenderDevice;
use bevy::render::texture::BevyDefault; use bevy::render::texture::BevyDefault;
@ -61,13 +62,19 @@ impl PipelineKey {
PipelineKey(0) PipelineKey(0)
} }
pub(crate) fn with_msaa_samples(mut self, msaa_samples: u32) -> Self { pub(crate) fn with_msaa(mut self, msaa: Msaa) -> Self {
self.set_msaa_samples_minus_one(msaa_samples - 1); self.set_msaa_samples_minus_one(msaa as u32 - 1);
self self
} }
pub(crate) fn msaa_samples(&self) -> u32 { pub(crate) fn msaa(&self) -> Msaa {
self.msaa_samples_minus_one() + 1 match self.msaa_samples_minus_one() + 1 {
x if x == Msaa::Off as u32 => Msaa::Off,
x if x == Msaa::Sample2 as u32 => Msaa::Sample2,
x if x == Msaa::Sample4 as u32 => Msaa::Sample4,
x if x == Msaa::Sample8 as u32 => Msaa::Sample8,
x => panic!("Invalid value for Msaa: {}", x),
}
} }
pub(crate) fn with_primitive_topology(mut self, primitive_topology: PrimitiveTopology) -> Self { pub(crate) fn with_primitive_topology(mut self, primitive_topology: PrimitiveTopology) -> Self {
@ -213,15 +220,28 @@ impl SpecializedMeshPipeline for OutlinePipeline {
mesh_layout: &MeshVertexBufferLayout, mesh_layout: &MeshVertexBufferLayout,
) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError> { ) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError> {
let mut targets = vec![]; let mut targets = vec![];
let mut bind_layouts = vec![self.mesh_pipeline.view_layout.clone()]; let mut bind_layouts = vec![if key.msaa() == Msaa::Off {
self.mesh_pipeline.view_layout.clone()
} else {
self.mesh_pipeline.view_layout_multisampled.clone()
}];
let mut buffer_attrs = vec![Mesh::ATTRIBUTE_POSITION.at_shader_location(0)]; let mut buffer_attrs = vec![Mesh::ATTRIBUTE_POSITION.at_shader_location(0)];
let mut vertex_defs = vec![]; let mut vertex_defs = vec![
ShaderDefVal::Int(
"MAX_CASCADES_PER_LIGHT".to_string(),
MAX_CASCADES_PER_LIGHT as i32,
),
ShaderDefVal::Int(
"MAX_DIRECTIONAL_LIGHTS".to_string(),
MAX_DIRECTIONAL_LIGHTS as i32,
),
];
let mut fragment_defs = vec![]; let mut fragment_defs = vec![];
bind_layouts.push( bind_layouts.push(
if mesh_layout.contains(Mesh::ATTRIBUTE_JOINT_INDEX) if mesh_layout.contains(Mesh::ATTRIBUTE_JOINT_INDEX)
&& mesh_layout.contains(Mesh::ATTRIBUTE_JOINT_WEIGHT) && mesh_layout.contains(Mesh::ATTRIBUTE_JOINT_WEIGHT)
{ {
vertex_defs.push("SKINNED".to_string()); vertex_defs.push(ShaderDefVal::from("SKINNED"));
buffer_attrs.push(Mesh::ATTRIBUTE_JOINT_INDEX.at_shader_location(2)); buffer_attrs.push(Mesh::ATTRIBUTE_JOINT_INDEX.at_shader_location(2));
buffer_attrs.push(Mesh::ATTRIBUTE_JOINT_WEIGHT.at_shader_location(3)); buffer_attrs.push(Mesh::ATTRIBUTE_JOINT_WEIGHT.at_shader_location(3));
self.mesh_pipeline.skinned_mesh_layout.clone() self.mesh_pipeline.skinned_mesh_layout.clone()
@ -232,7 +252,7 @@ impl SpecializedMeshPipeline for OutlinePipeline {
bind_layouts.push(self.outline_view_bind_group_layout.clone()); bind_layouts.push(self.outline_view_bind_group_layout.clone());
let cull_mode; let cull_mode;
if key.depth_mode() == DepthMode::Flat { if key.depth_mode() == DepthMode::Flat {
vertex_defs.push("FLAT_DEPTH".to_string()); vertex_defs.push(ShaderDefVal::from("FLAT_DEPTH"));
cull_mode = Some(Face::Back); cull_mode = Some(Face::Back);
} else if key.pass_type() == PassType::Stencil { } else if key.pass_type() == PassType::Stencil {
cull_mode = Some(Face::Back); cull_mode = Some(Face::Back);
@ -240,7 +260,7 @@ impl SpecializedMeshPipeline for OutlinePipeline {
cull_mode = Some(Face::Front); cull_mode = Some(Face::Front);
} }
if key.offset_zero() { if key.offset_zero() {
vertex_defs.push("OFFSET_ZERO".to_string()); vertex_defs.push(ShaderDefVal::from("OFFSET_ZERO"));
} else { } else {
buffer_attrs.push( buffer_attrs.push(
if mesh_layout.contains(ATTRIBUTE_OUTLINE_NORMAL) { if mesh_layout.contains(ATTRIBUTE_OUTLINE_NORMAL) {
@ -256,7 +276,7 @@ impl SpecializedMeshPipeline for OutlinePipeline {
bind_layouts.push(self.outline_stencil_bind_group_layout.clone()); bind_layouts.push(self.outline_stencil_bind_group_layout.clone());
} }
PassType::Opaque | PassType::Transparent => { PassType::Opaque | PassType::Transparent => {
fragment_defs.push("VOLUME".to_string()); fragment_defs.push(ShaderDefVal::from("VOLUME"));
targets.push(Some(ColorTargetState { targets.push(Some(ColorTargetState {
format: if key.hdr_format() { format: if key.hdr_format() {
ViewTarget::TEXTURE_FORMAT_HDR ViewTarget::TEXTURE_FORMAT_HDR
@ -288,7 +308,7 @@ impl SpecializedMeshPipeline for OutlinePipeline {
entry_point: "fragment".into(), entry_point: "fragment".into(),
targets, targets,
}), }),
layout: Some(bind_layouts), layout: bind_layouts,
primitive: PrimitiveState { primitive: PrimitiveState {
front_face: FrontFace::Ccw, front_face: FrontFace::Ccw,
cull_mode, cull_mode,
@ -315,10 +335,11 @@ impl SpecializedMeshPipeline for OutlinePipeline {
}, },
}), }),
multisample: MultisampleState { multisample: MultisampleState {
count: key.msaa_samples(), count: key.msaa().samples(),
mask: !0, mask: !0,
alpha_to_coverage_enabled: false, alpha_to_coverage_enabled: false,
}, },
push_constant_ranges: default(),
label: Some(Cow::Borrowed("outline_pipeline")), label: Some(Cow::Borrowed("outline_pipeline")),
}) })
} }

View File

@ -1,19 +1,22 @@
use bevy::{ use bevy::{
ecs::system::{ ecs::system::{
lifetimeless::{Read, SQuery, SRes}, lifetimeless::{Read, SRes},
SystemParamItem, SystemParamItem,
}, },
prelude::*, prelude::*,
render::{ render::{
extract_component::{ComponentUniforms, DynamicUniformIndex}, extract_component::{ComponentUniforms, DynamicUniformIndex},
render_phase::{EntityRenderCommand, RenderCommandResult, TrackedRenderPass}, render_phase::{PhaseItem, RenderCommand, RenderCommandResult, TrackedRenderPass},
render_resource::{BindGroup, BindGroupDescriptor, BindGroupEntry, ShaderType}, render_resource::{BindGroup, BindGroupDescriptor, BindGroupEntry, ShaderType},
renderer::RenderDevice, renderer::RenderDevice,
Extract, Extract,
}, },
}; };
use crate::{pipeline::OutlinePipeline, ComputedOutlineDepth, OutlineStencil, OutlineVolume}; use crate::{
node::StencilOutline, pipeline::OutlinePipeline, ComputedOutlineDepth, OutlineStencil,
OutlineVolume,
};
#[derive(Clone, Component, ShaderType)] #[derive(Clone, Component, ShaderType)]
pub(crate) struct OutlineStencilUniform { pub(crate) struct OutlineStencilUniform {
@ -150,40 +153,46 @@ pub(crate) fn queue_outline_volume_bind_group(
pub(crate) struct SetOutlineStencilBindGroup<const I: usize>(); pub(crate) struct SetOutlineStencilBindGroup<const I: usize>();
impl<const I: usize> EntityRenderCommand for SetOutlineStencilBindGroup<I> { impl<const I: usize> RenderCommand<StencilOutline> for SetOutlineStencilBindGroup<I> {
type Param = ( type ViewWorldQuery = ();
SRes<OutlineStencilBindGroup>, type ItemWorldQuery = Read<DynamicUniformIndex<OutlineStencilUniform>>;
SQuery<Read<DynamicUniformIndex<OutlineStencilUniform>>>, type Param = SRes<OutlineStencilBindGroup>;
);
fn render<'w>( fn render<'w>(
_view: Entity, _item: &StencilOutline,
item: Entity, _view_data: (),
(bind_group, query): SystemParamItem<'w, '_, Self::Param>, entity_data: &DynamicUniformIndex<OutlineStencilUniform>,
bind_group: SystemParamItem<'w, '_, Self::Param>,
pass: &mut TrackedRenderPass<'w>, pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult { ) -> RenderCommandResult {
let vertex = query.get(item).unwrap(); pass.set_bind_group(
pass.set_bind_group(I, &bind_group.into_inner().bind_group, &[vertex.index()]); I,
&bind_group.into_inner().bind_group,
&[entity_data.index()],
);
RenderCommandResult::Success RenderCommandResult::Success
} }
} }
pub(crate) struct SetOutlineVolumeBindGroup<const I: usize>(); pub(crate) struct SetOutlineVolumeBindGroup<const I: usize>();
impl<const I: usize> EntityRenderCommand for SetOutlineVolumeBindGroup<I> { impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetOutlineVolumeBindGroup<I> {
type Param = ( type ViewWorldQuery = ();
SRes<OutlineVolumeBindGroup>, type ItemWorldQuery = (
SQuery<( Read<DynamicUniformIndex<OutlineVolumeUniform>>,
Read<DynamicUniformIndex<OutlineVolumeUniform>>, Read<DynamicUniformIndex<OutlineFragmentUniform>>,
Read<DynamicUniformIndex<OutlineFragmentUniform>>,
)>,
); );
type Param = SRes<OutlineVolumeBindGroup>;
fn render<'w>( fn render<'w>(
_view: Entity, _item: &P,
item: Entity, _view_data: (),
(bind_group, query): SystemParamItem<'w, '_, Self::Param>, entity_data: (
&DynamicUniformIndex<OutlineVolumeUniform>,
&DynamicUniformIndex<OutlineFragmentUniform>,
),
bind_group: SystemParamItem<'w, '_, Self::Param>,
pass: &mut TrackedRenderPass<'w>, pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult { ) -> RenderCommandResult {
let (vertex, fragment) = query.get(item).unwrap(); let (vertex, fragment) = entity_data;
pass.set_bind_group( pass.set_bind_group(
I, I,
&bind_group.into_inner().bind_group, &bind_group.into_inner().bind_group,

View File

@ -1,9 +1,9 @@
use bevy::ecs::system::lifetimeless::{Read, SQuery, SRes}; use bevy::ecs::system::lifetimeless::{Read, SRes};
use bevy::ecs::system::SystemParamItem; use bevy::ecs::system::SystemParamItem;
use bevy::prelude::*; use bevy::prelude::*;
use bevy::render::extract_component::{ComponentUniforms, DynamicUniformIndex}; use bevy::render::extract_component::{ComponentUniforms, DynamicUniformIndex};
use bevy::render::render_phase::{ use bevy::render::render_phase::{
EntityRenderCommand, RenderCommandResult, RenderPhase, TrackedRenderPass, PhaseItem, RenderCommand, RenderCommandResult, RenderPhase, TrackedRenderPass,
}; };
use bevy::render::render_resource::ShaderType; use bevy::render::render_resource::ShaderType;
use bevy::render::render_resource::{BindGroup, BindGroupDescriptor, BindGroupEntry}; use bevy::render::render_resource::{BindGroup, BindGroupDescriptor, BindGroupEntry};
@ -70,23 +70,18 @@ pub(crate) fn queue_outline_view_bind_group(
pub(crate) struct SetOutlineViewBindGroup<const I: usize>(); pub(crate) struct SetOutlineViewBindGroup<const I: usize>();
impl<const I: usize> EntityRenderCommand for SetOutlineViewBindGroup<I> { impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetOutlineViewBindGroup<I> {
type Param = ( type ViewWorldQuery = Read<DynamicUniformIndex<OutlineViewUniform>>;
SRes<OutlineViewBindGroup>, type ItemWorldQuery = ();
SQuery<Read<DynamicUniformIndex<OutlineViewUniform>>>, type Param = SRes<OutlineViewBindGroup>;
);
fn render<'w>( fn render<'w>(
view: Entity, _item: &P,
_item: Entity, view_data: &DynamicUniformIndex<OutlineViewUniform>,
(bind_group, query): SystemParamItem<'w, '_, Self::Param>, _entity_data: (),
bind_group: SystemParamItem<'w, '_, Self::Param>,
pass: &mut TrackedRenderPass<'w>, pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult { ) -> RenderCommandResult {
let view_index = query.get(view).unwrap(); pass.set_bind_group(I, &bind_group.into_inner().bind_group, &[view_data.index()]);
pass.set_bind_group(
I,
&bind_group.into_inner().bind_group,
&[view_index.index()],
);
RenderCommandResult::Success RenderCommandResult::Success
} }
} }