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]
name = "bevy_mod_outline"
version = "0.3.5"
version = "0.4.0"
edition = "2021"
license = "MIT OR Apache-2.0"
description = "A mesh outlining plugin for Bevy."
@ -11,7 +11,7 @@ keywords = ["gamedev", "bevy", "outline"]
categories = ["game-engines", "rendering"]
[dependencies]
bevy = { version = "0.9", default-features = false, features = [
bevy = { version = "0.10", default-features = false, features = [
"bevy_asset",
"bevy_render",
"bevy_pbr",
@ -22,7 +22,7 @@ interpolation = "0.2"
thiserror = "1.0"
[dev-dependencies]
bevy = { version = "0.9", default-features = false, features = [
bevy = { version = "0.10", default-features = false, features = [
"bevy_winit",
"x11",
] }

View File

@ -1,6 +1,10 @@
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::{
AutoGenerateOutlineNormalsPlugin, OutlineBundle, OutlinePlugin, OutlineVolume,
};
@ -41,7 +45,10 @@ fn setup(
// Plane
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()),
..default()
});

View File

@ -2,7 +2,7 @@ use std::f32::consts::TAU;
use bevy::{
prelude::{
shape::{Capsule, Torus, UVSphere},
shape::{Capsule, Plane, Torus, UVSphere},
*,
},
window::close_on_esc,
@ -13,7 +13,7 @@ use bevy_mod_outline::*;
#[bevy_main]
fn main() {
App::new()
.insert_resource(Msaa { samples: 4 })
.insert_resource(Msaa::Sample4)
.insert_resource(ClearColor(Color::BLACK))
.add_plugins(DefaultPlugins)
.add_plugin(OutlinePlugin)
@ -101,7 +101,10 @@ fn setup(
// Add plane, light source, and camera
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()),
..default()
});

View File

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

View File

@ -2,7 +2,7 @@ use std::f32::consts::{PI, TAU};
use bevy::{
prelude::{
shape::{Cube, Torus},
shape::{Cube, Plane, Torus},
*,
},
window::close_on_esc,
@ -13,7 +13,7 @@ use bevy_mod_outline::*;
#[bevy_main]
fn main() {
App::new()
.insert_resource(Msaa { samples: 4 })
.insert_resource(Msaa::Sample4)
.insert_resource(ClearColor(Color::BLACK))
.add_plugins(DefaultPlugins)
.add_plugin(OutlinePlugin)
@ -81,7 +81,10 @@ fn setup(
// Add plane, light source, and camera
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()),
..default()
});

View File

@ -29,7 +29,7 @@ pub(crate) fn queue_outline_stencil_mesh(
stencil_pipeline: Res<OutlinePipeline>,
msaa: Res<Msaa>,
mut pipelines: ResMut<SpecializedMeshPipelines<OutlinePipeline>>,
mut pipeline_cache: ResMut<PipelineCache>,
pipeline_cache: Res<PipelineCache>,
render_meshes: Res<RenderAssets<Mesh>>,
material_meshes: Query<(
Entity,
@ -50,7 +50,7 @@ pub(crate) fn queue_outline_stencil_mesh(
.unwrap();
let base_key = PipelineKey::new()
.with_msaa_samples(msaa.samples)
.with_msaa(*msaa)
.with_pass_type(PassType::Stencil);
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_offset_zero(stencil_uniform.offset == 0.0);
let pipeline = pipelines
.specialize(&mut pipeline_cache, &stencil_pipeline, key, &mesh.layout)
.specialize(&pipeline_cache, &stencil_pipeline, key, &mesh.layout)
.unwrap();
let distance =
rangefinder.distance(&Mat4::from_translation(stencil_uniform.origin));
@ -99,7 +99,7 @@ pub(crate) fn queue_outline_volume_mesh(
outline_pipeline: Res<OutlinePipeline>,
msaa: Res<Msaa>,
mut pipelines: ResMut<SpecializedMeshPipelines<OutlinePipeline>>,
mut pipeline_cache: ResMut<PipelineCache>,
pipeline_cache: Res<PipelineCache>,
render_meshes: Res<RenderAssets<Mesh>>,
material_meshes: Query<(
Entity,
@ -125,7 +125,7 @@ pub(crate) fn queue_outline_volume_mesh(
.get_id::<DrawOutline>()
.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() {
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_hdr_format(view.hdr);
let pipeline = pipelines
.specialize(&mut pipeline_cache, &outline_pipeline, key, &mesh.layout)
.specialize(&pipeline_cache, &outline_pipeline, key, &mesh.layout)
.unwrap();
let distance = rangefinder.distance(&Mat4::from_translation(volume_uniform.origin));
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_resource::{SpecializedMeshPipelines, VertexFormat};
use bevy::render::view::RenderLayers;
use bevy::render::{RenderApp, RenderStage};
use bevy::render::{RenderApp, RenderSet};
use bevy::transform::TransformSystem;
use interpolation::Lerp;
@ -82,9 +82,10 @@ pub struct OutlineStencil {
impl ExtractComponent for OutlineStencil {
type Query = &'static OutlineStencil;
type Filter = ();
type Out = Self;
fn extract_component(item: QueryItem<Self::Query>) -> Self {
item.clone()
fn extract_component(item: QueryItem<Self::Query>) -> Option<Self> {
Some(item.clone())
}
}
@ -143,14 +144,17 @@ impl ExtractComponent for OutlineRenderLayers {
Option<&'static RenderLayers>,
);
type Filter = Or<(With<OutlineVolume>, With<OutlineStencil>)>;
type Out = Self;
fn extract_component(
(outline_mask, object_mask): (Option<&OutlineRenderLayers>, Option<&RenderLayers>),
) -> Self {
outline_mask
.copied()
.or_else(|| object_mask.copied().map(OutlineRenderLayers))
.unwrap_or_default()
) -> Option<Self> {
Some(
outline_mask
.copied()
.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::<OutlineFragmentUniform>::default())
.add_plugin(UniformComponentPlugin::<OutlineViewUniform>::default())
.add_system_to_stage(
CoreStage::PostUpdate,
compute_outline_depth.after(TransformSystem::TransformPropagate),
.add_system(
compute_outline_depth
.in_base_set(CoreSet::PostUpdate)
.after(TransformSystem::TransformPropagate),
)
.sub_app_mut(RenderApp)
.init_resource::<DrawFunctions<StencilOutline>>()
@ -206,20 +211,17 @@ impl Plugin for OutlinePlugin {
.add_render_command::<StencilOutline, DrawStencil>()
.add_render_command::<OpaqueOutline, DrawOutline>()
.add_render_command::<TransparentOutline, DrawOutline>()
.add_system_to_stage(RenderStage::Extract, extract_outline_view_uniforms)
.add_system_to_stage(RenderStage::Extract, extract_outline_stencil_uniforms)
.add_system_to_stage(RenderStage::Extract, extract_outline_volume_uniforms)
.add_system_to_stage(RenderStage::PhaseSort, sort_phase_system::<StencilOutline>)
.add_system_to_stage(RenderStage::PhaseSort, sort_phase_system::<OpaqueOutline>)
.add_system_to_stage(
RenderStage::PhaseSort,
sort_phase_system::<TransparentOutline>,
)
.add_system_to_stage(RenderStage::Queue, queue_outline_view_bind_group)
.add_system_to_stage(RenderStage::Queue, queue_outline_stencil_bind_group)
.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);
.add_system(extract_outline_view_uniforms.in_schedule(ExtractSchedule))
.add_system(extract_outline_stencil_uniforms.in_schedule(ExtractSchedule))
.add_system(extract_outline_volume_uniforms.in_schedule(ExtractSchedule))
.add_system(sort_phase_system::<StencilOutline>.in_set(RenderSet::PhaseSort))
.add_system(sort_phase_system::<OpaqueOutline>.in_set(RenderSet::PhaseSort))
.add_system(sort_phase_system::<TransparentOutline>.in_set(RenderSet::PhaseSort))
.add_system(queue_outline_view_bind_group.in_set(RenderSet::Queue))
.add_system(queue_outline_stencil_bind_group.in_set(RenderSet::Queue))
.add_system(queue_outline_volume_bind_group.in_set(RenderSet::Queue))
.add_system(queue_outline_stencil_mesh.in_set(RenderSet::Queue))
.add_system(queue_outline_volume_mesh.in_set(RenderSet::Queue));
let world = &mut app.sub_app_mut(RenderApp).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)
.unwrap();
draw_3d_graph.add_node(OUTLINE_PASS_NODE_NAME, node);
draw_3d_graph
.add_slot_edge(
draw_3d_graph.input_node().unwrap().id,
bevy::core_pipeline::core_3d::graph::input::VIEW_ENTITY,
OUTLINE_PASS_NODE_NAME,
OutlineNode::IN_VIEW,
)
.unwrap();
draw_3d_graph.add_slot_edge(
draw_3d_graph.input_node().id,
bevy::core_pipeline::core_3d::graph::input::VIEW_ENTITY,
OUTLINE_PASS_NODE_NAME,
OutlineNode::IN_VIEW,
);
// Run after main 3D pass, but before UI psss
draw_3d_graph
.add_node_edge(
bevy::core_pipeline::core_3d::graph::node::MAIN_PASS,
OUTLINE_PASS_NODE_NAME,
)
.unwrap();
draw_3d_graph.add_node_edge(
bevy::core_pipeline::core_3d::graph::node::MAIN_PASS,
OUTLINE_PASS_NODE_NAME,
);
#[cfg(feature = "bevy_ui")]
draw_3d_graph
.add_node_edge(
OUTLINE_PASS_NODE_NAME,
bevy::ui::draw_ui_graph::node::UI_PASS,
)
.unwrap();
draw_3d_graph.add_node_edge(
OUTLINE_PASS_NODE_NAME,
bevy::ui::draw_ui_graph::node::UI_PASS,
);
}
}

View File

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

View File

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

View File

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