Change outline and stencil properties to be inheritable.

This commit is contained in:
Robin KAY 2023-11-08 21:40:19 +00:00
parent 65c7c3356f
commit 600f58ba73
8 changed files with 341 additions and 232 deletions

View File

@ -6,7 +6,8 @@ use bevy::{
window::close_on_esc,
};
use bevy_mod_outline::{
AutoGenerateOutlineNormalsPlugin, OutlineBundle, OutlinePlugin, OutlineVolume,
AutoGenerateOutlineNormalsPlugin, InheritOutlineBundle, OutlineBundle, OutlinePlugin,
OutlineVolume,
};
#[derive(Resource)]
@ -65,9 +66,18 @@ fn setup(
});
// Fox
commands.spawn(SceneBundle {
commands
.spawn(SceneBundle {
scene: asset_server.load("Fox.glb#Scene0"),
..default()
})
.insert(OutlineBundle {
outline: OutlineVolume {
visible: true,
width: 3.0,
colour: Color::RED,
},
..default()
});
}
@ -86,14 +96,9 @@ fn setup_scene_once_loaded(
{
if scene_manager.instance_is_ready(**scene) {
for entity in scene_manager.iter_instance_entities(**scene) {
commands.entity(entity).insert(OutlineBundle {
outline: OutlineVolume {
visible: true,
width: 3.0,
colour: Color::RED,
},
..default()
});
commands
.entity(entity)
.insert(InheritOutlineBundle::default());
}
player.play(animation.0.clone_weak()).repeat();
*done = true;

View File

@ -56,7 +56,18 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
..default()
})
.insert(Rotates)
.insert(ComputedOutlineDepth::default());
.insert(OutlineBundle {
outline: OutlineVolume {
visible: true,
width: 7.5,
colour: Color::BLUE,
},
stencil: OutlineStencil {
enabled: true,
offset: 0.0,
},
..default()
});
}
// Once the scene is loaded, start the animation and add an outline
@ -73,19 +84,7 @@ fn setup_scene_once_loaded(
for entity in scene_manager.iter_instance_entities(**scene) {
commands
.entity(entity)
.insert(OutlineBundle {
outline: OutlineVolume {
visible: true,
width: 7.5,
colour: Color::BLUE,
},
stencil: OutlineStencil {
enabled: true,
offset: 0.0,
},
..default()
})
.insert(InheritOutlineDepth);
.insert(InheritOutlineBundle::default());
if let Ok(name) = name_query.get(entity) {
if name.as_str() == "inside" {
commands.entity(entity).insert(RotatesHue);

View File

@ -9,7 +9,8 @@
use bevy::{prelude::*, scene::SceneInstance};
use bevy_mod_outline::{
AutoGenerateOutlineNormalsPlugin, OutlineBundle, OutlinePlugin, OutlineVolume,
AutoGenerateOutlineNormalsPlugin, InheritOutlineBundle, OutlineBundle, OutlinePlugin,
OutlineVolume,
};
use std::f32::consts::PI;
@ -46,9 +47,18 @@ fn setup(asset_server: Res<AssetServer>, mut commands: Commands) {
the_wave: asset_server.load("MorphStressTest.gltf#Animation2"),
mesh: asset_server.load("MorphStressTest.gltf#Mesh0/Primitive0"),
});
commands.spawn(SceneBundle {
commands
.spawn(SceneBundle {
scene: asset_server.load("MorphStressTest.gltf#Scene0"),
..default()
})
.insert(OutlineBundle {
outline: OutlineVolume {
visible: true,
width: 3.0,
colour: Color::RED,
},
..default()
});
commands.spawn(DirectionalLightBundle {
directional_light: DirectionalLight {
@ -78,14 +88,9 @@ fn setup_outlines(
if let Ok(scene) = scene_query.get_single() {
if scene_manager.instance_is_ready(**scene) {
for entity in scene_manager.iter_instance_entities(**scene) {
commands.entity(entity).insert(OutlineBundle {
outline: OutlineVolume {
visible: true,
width: 3.0,
colour: Color::RED,
},
..default()
});
commands
.entity(entity)
.insert(InheritOutlineBundle::default());
*has_setup = true;
}
}

View File

@ -29,19 +29,6 @@ fn setup(
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
) {
let outline = OutlineBundle {
outline: OutlineVolume {
visible: true,
colour: Color::WHITE,
width: 10.0,
},
stencil: OutlineStencil {
offset: 5.0,
..default()
},
..default()
};
// Add sphere with child meshes sticking out of it
commands
.spawn(PbrBundle {
@ -57,7 +44,18 @@ fn setup(
transform: Transform::from_translation(Vec3::new(0.0, 1.0, 0.0)),
..default()
})
.insert(outline.clone())
.insert(OutlineBundle {
outline: OutlineVolume {
visible: true,
colour: Color::WHITE,
width: 10.0,
},
stencil: OutlineStencil {
offset: 5.0,
..default()
},
..default()
})
.insert(Rotates)
.with_children(|parent| {
parent
@ -78,8 +76,7 @@ fn setup(
.with_translation(Vec3::new(0.0, 0.0, 0.75)),
..default()
})
.insert(outline.clone())
.insert(InheritOutlineDepth);
.insert(InheritOutlineBundle::default());
parent
.spawn(PbrBundle {
mesh: meshes.add(
@ -96,8 +93,7 @@ fn setup(
.with_translation(Vec3::new(0.0, 0.0, -0.75)),
..default()
})
.insert(outline.clone())
.insert(InheritOutlineDepth);
.insert(InheritOutlineBundle::default());
});
// Add plane, light source, and camera

View File

@ -1,76 +1,113 @@
use bevy::prelude::*;
use bevy::{ecs::query::QueryItem, prelude::*};
use crate::uniforms::DepthMode;
use crate::{uniforms::DepthMode, InheritOutline, OutlineMode, OutlineStencil, OutlineVolume};
#[derive(Clone, Default)]
pub(crate) struct ComputedVolume {
pub(crate) enabled: bool,
pub(crate) offset: f32,
pub(crate) colour: Vec4,
}
#[derive(Clone, Default)]
pub(crate) struct ComputedStencil {
pub(crate) enabled: bool,
pub(crate) offset: f32,
}
#[derive(Clone)]
pub(crate) struct ComputedMode {
pub(crate) world_origin: Vec3,
pub(crate) depth_mode: DepthMode,
}
impl Default for ComputedMode {
fn default() -> Self {
Self {
world_origin: Vec3::NAN,
depth_mode: DepthMode::Real,
}
}
}
#[derive(Copy, Clone, Default)]
pub(crate) enum Source {
#[default]
Set,
Inherited,
}
#[derive(Clone, Default)]
pub(crate) struct Sourced<T: Clone + Default> {
pub(crate) value: T,
pub(crate) source: Source,
}
impl<T: Clone + Default> Sourced<T> {
pub fn inherit(value: &T) -> Self {
Sourced {
value: value.clone(),
source: Source::Inherited,
}
}
pub fn set(value: T) -> Self {
Sourced {
value,
source: Source::Set,
}
}
pub fn is_changed<U: Component>(&self, tuple: Option<(&U, bool)>) -> bool {
tuple.is_some() != matches!(self.source, Source::Set)
|| if let Some((_, c)) = tuple { c } else { false }
}
}
#[derive(Clone, Default)]
pub(crate) struct ComputedInternal {
pub(crate) inherited_from: Option<Entity>,
pub(crate) volume: Sourced<ComputedVolume>,
pub(crate) stencil: Sourced<ComputedStencil>,
pub(crate) mode: Sourced<ComputedMode>,
}
/// A component for storing the computed depth at which the outline lies.
#[derive(Clone, Component, Default)]
pub struct ComputedOutlineDepth {
pub(crate) world_origin: Vec3,
pub(crate) depth_mode: DepthMode,
pub(crate) inherited: Option<Entity>,
}
pub struct ComputedOutline(pub(crate) Option<ComputedInternal>);
/// A component which specifies how the outline depth should be computed.
#[derive(Clone, Component)]
#[non_exhaustive]
pub enum SetOutlineDepth {
/// A flat plane facing the camera and intersecting the specified point in model-space.
Flat { model_origin: Vec3 },
/// Real model-space.
Real,
}
/// A component which specifies that this outline lies at the same depth as its parent.
#[derive(Clone, Component, Default)]
pub struct InheritOutlineDepth;
type OutlineComponents<'a> = (
(&'a GlobalTransform, Changed<GlobalTransform>),
Option<(&'a OutlineVolume, Changed<OutlineVolume>)>,
Option<(&'a OutlineStencil, Changed<OutlineStencil>)>,
Option<(&'a OutlineMode, Changed<OutlineMode>)>,
);
#[allow(clippy::type_complexity)]
pub(crate) fn compute_outline_depth(
pub(crate) fn compute_outline(
mut root_query: Query<
(
Entity,
&mut ComputedOutlineDepth,
&GlobalTransform,
Changed<GlobalTransform>,
Option<(&SetOutlineDepth, Changed<SetOutlineDepth>)>,
&mut ComputedOutline,
OutlineComponents,
Option<&Children>,
),
Without<InheritOutlineDepth>,
Without<InheritOutline>,
>,
mut computed_query: Query<&mut ComputedOutlineDepth, With<InheritOutlineDepth>>,
mut child_query_mut: Query<(&mut ComputedOutline, OutlineComponents), With<InheritOutline>>,
child_query: Query<&Children>,
) {
for (entity, mut computed, transform, changed_transform, set_depth, children) in
root_query.iter_mut()
{
let changed = !computed.depth_mode.is_valid()
|| computed.inherited.is_some()
|| changed_transform
|| set_depth.filter(|(_, c)| *c).is_some();
if changed {
let (origin, depth_mode) = if let Some((sd, _)) = set_depth {
match sd {
SetOutlineDepth::Flat {
model_origin: origin,
} => (*origin, DepthMode::Flat),
SetOutlineDepth::Real => (Vec3::NAN, DepthMode::Real),
}
} else {
(Vec3::ZERO, DepthMode::Flat)
};
let matrix = transform.compute_matrix();
computed.world_origin = matrix.project_point3(origin);
computed.depth_mode = depth_mode;
computed.inherited = None;
}
for (entity, mut computed, components, children) in root_query.iter_mut() {
let changed = update_computed_outline(&mut computed, components, &default(), None, false);
if let Some(cs) = children {
let parent_computed = &computed.0.as_ref().unwrap().clone();
for child in cs.iter() {
propagate_outline_depth(
&computed,
propagate_computed_outline(
parent_computed,
changed,
entity,
*child,
&mut computed_query,
&mut child_query_mut,
&child_query,
);
}
@ -78,31 +115,92 @@ pub(crate) fn compute_outline_depth(
}
}
fn propagate_outline_depth(
root_computed: &ComputedOutlineDepth,
mut changed: bool,
parent: Entity,
fn propagate_computed_outline(
parent_computed: &ComputedInternal,
parent_changed: bool,
parent_entity: Entity,
entity: Entity,
computed_query: &mut Query<&mut ComputedOutlineDepth, With<InheritOutlineDepth>>,
child_query_mut: &mut Query<(&mut ComputedOutline, OutlineComponents), With<InheritOutline>>,
child_query: &Query<&Children>,
) {
if let Ok(mut computed) = computed_query.get_mut(entity) {
changed |= !computed.depth_mode.is_valid() | (computed.inherited != Some(parent));
if changed {
*computed = root_computed.clone();
computed.inherited = Some(parent);
}
if let Ok((mut computed, components)) = child_query_mut.get_mut(entity) {
let changed = update_computed_outline(
&mut computed,
components,
parent_computed,
Some(parent_entity),
parent_changed,
);
if let Ok(cs) = child_query.get(entity) {
let parent_computed = &computed.0.as_ref().unwrap().clone();
for child in cs.iter() {
propagate_outline_depth(
root_computed,
propagate_computed_outline(
parent_computed,
changed,
entity,
*child,
computed_query,
child_query_mut,
child_query,
);
}
}
}
}
fn update_computed_outline(
computed: &mut ComputedOutline,
((transform, changed_transform), volume, stencil, mode): QueryItem<'_, OutlineComponents>,
parent_computed: &ComputedInternal,
parent_entity: Option<Entity>,
force_update: bool,
) -> bool {
let changed = force_update
|| if let ComputedOutline(Some(computed)) = computed {
computed.inherited_from != parent_entity
|| (changed_transform && matches!(mode, Some((OutlineMode::FlatVertex { .. }, _))))
|| computed.volume.is_changed(volume)
|| computed.stencil.is_changed(stencil)
|| computed.mode.is_changed(mode)
} else {
true
};
if changed {
*computed = ComputedOutline(Some(ComputedInternal {
inherited_from: parent_entity,
volume: if let Some((vol, _)) = volume {
Sourced::set(ComputedVolume {
enabled: vol.visible && vol.colour.a() != 0.0,
offset: vol.width,
colour: vol.colour.into(),
})
} else {
Sourced::inherit(&parent_computed.volume.value)
},
stencil: if let Some((sten, _)) = stencil {
Sourced::set(ComputedStencil {
enabled: sten.enabled,
offset: sten.offset,
})
} else {
Sourced::inherit(&parent_computed.stencil.value)
},
mode: if let Some((m, _)) = mode {
Sourced::set(match m {
OutlineMode::FlatVertex {
model_origin: origin,
} => ComputedMode {
world_origin: transform.compute_matrix().project_point3(*origin),
depth_mode: DepthMode::Flat,
},
OutlineMode::RealVertex => ComputedMode {
world_origin: Vec3::NAN,
depth_mode: DepthMode::Real,
},
})
} else {
Sourced::inherit(&parent_computed.mode.value)
},
}));
}
changed
}

View File

@ -8,9 +8,8 @@ use bevy::render::view::{ExtractedView, RenderLayers};
use crate::node::{OpaqueOutline, StencilOutline, TransparentOutline};
use crate::pipeline::{OutlinePipeline, PassType, PipelineKey};
use crate::uniforms::{
DepthMode, OutlineFragmentUniform, OutlineStencilFlags, OutlineStencilUniform,
OutlineVolumeFlags, OutlineVolumeUniform, SetOutlineStencilBindGroup,
SetOutlineVolumeBindGroup,
ExtractedOutline, OutlineFragmentUniform, OutlineStencilUniform, OutlineVolumeUniform,
SetOutlineStencilBindGroup, SetOutlineVolumeBindGroup,
};
use crate::view_uniforms::SetOutlineViewBindGroup;
use crate::OutlineRenderLayers;
@ -34,9 +33,8 @@ pub(crate) fn queue_outline_stencil_mesh(
render_meshes: Res<RenderAssets<Mesh>>,
material_meshes: Query<(
Entity,
&Handle<Mesh>,
&OutlineStencilUniform,
&OutlineStencilFlags,
&ExtractedOutline,
&OutlineRenderLayers,
)>,
mut views: Query<(
@ -57,21 +55,16 @@ pub(crate) fn queue_outline_stencil_mesh(
for (view, mut stencil_phase, view_mask) in views.iter_mut() {
let rangefinder = view.rangefinder3d();
let view_mask = view_mask.copied().unwrap_or_default();
for (entity, mesh_handle, stencil_uniform, stencil_flags, outline_mask) in
material_meshes.iter()
{
for (entity, stencil_uniform, outline, outline_mask) in material_meshes.iter() {
if !view_mask.intersects(outline_mask) {
continue; // Layer not enabled
}
if stencil_flags.depth_mode == DepthMode::Invalid {
continue; // DepthMode not propagated
}
let Some(mesh) = render_meshes.get(mesh_handle) else {
let Some(mesh) = render_meshes.get(&outline.mesh) else {
continue; // No mesh
};
let key = base_key
.with_primitive_topology(mesh.primitive_topology)
.with_depth_mode(stencil_flags.depth_mode)
.with_depth_mode(outline.depth_mode)
.with_offset_zero(stencil_uniform.offset == 0.0)
.with_morph_targets(mesh.morph_targets.is_some());
let Ok(pipeline) =
@ -110,9 +103,8 @@ pub(crate) fn queue_outline_volume_mesh(
render_meshes: Res<RenderAssets<Mesh>>,
material_meshes: Query<(
Entity,
&Handle<Mesh>,
&OutlineVolumeUniform,
&OutlineVolumeFlags,
&ExtractedOutline,
&OutlineFragmentUniform,
&OutlineRenderLayers,
)>,
@ -137,16 +129,13 @@ pub(crate) fn queue_outline_volume_mesh(
for (view, mut opaque_phase, mut transparent_phase, view_mask) in views.iter_mut() {
let view_mask = view_mask.copied().unwrap_or_default();
let rangefinder = view.rangefinder3d();
for (entity, mesh_handle, volume_uniform, volume_flags, fragment_uniform, outline_mask) in
for (entity, volume_uniform, outline, fragment_uniform, outline_mask) in
material_meshes.iter()
{
if !view_mask.intersects(outline_mask) {
continue; // Layer not enabled
}
if volume_flags.depth_mode == DepthMode::Invalid {
continue; // DepthMode not propagated
}
let Some(mesh) = render_meshes.get(mesh_handle) else {
let Some(mesh) = render_meshes.get(&outline.mesh) else {
continue; // No mesh
};
let transparent = fragment_uniform.colour[3] < 1.0;
@ -157,7 +146,7 @@ pub(crate) fn queue_outline_volume_mesh(
} else {
PassType::Opaque
})
.with_depth_mode(volume_flags.depth_mode)
.with_depth_mode(outline.depth_mode)
.with_offset_zero(volume_uniform.offset == 0.0)
.with_hdr_format(view.hdr)
.with_morph_targets(mesh.morph_targets.is_some());

View File

@ -3,17 +3,19 @@
//!
//! Outlines are rendered in a seperate pass following the main 3D pass. The effect of this
//! pass is to present the outlines in depth sorted order according to the model translation
//! of each mesh. This ensures that outlines are not clipped by other geometry.
//! of each mesh. This ensures that outlines are not clipped by non-outline geometry.
//!
//! The [`OutlineVolume`] component will, by itself, cover the original object entirely with
//! the outline colour. The [`OutlineStencil`] component must also be added to prevent the body
//! of an object from being filled it. This must be added to any entity which needs to appear on
//! top of an outline.
//!
//! The [`OutlineMode`] component specifies the rendering method. Outlines may be flattened into
//! a plane in order to further avoid clipping, or left in real space.
//!
//! The [`OutlineBundle`] and [`OutlineStencilBundle`] bundles can be used to add the right
//! components, including the required [`ComputedOutlineDepth`] component. Optionally, the
//! [`SetOutlineDepth`] and [`InheritOutlineDepth`] components may also be added to control the
//! depth ordering of outlines.
//! components, including the required [`ComputedOutline`] component. Outlines can be inherited
//! from the parent via the [`InheritOutline`] component and [`InheritOutlineBundle`].
//!
//! Vertex extrusion works best with meshes that have smooth surfaces. To avoid visual
//! artefacts when outlining meshes with hard edges, see the
@ -29,7 +31,7 @@ use bevy::render::mesh::MeshVertexAttribute;
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::view::{RenderLayers, VisibilitySystems};
use bevy::render::{Render, RenderApp, RenderSet};
use bevy::transform::TransformSystem;
use interpolation::Lerp;
@ -40,9 +42,8 @@ use crate::draw::{
use crate::node::{OpaqueOutline, OutlineNode, StencilOutline, TransparentOutline};
use crate::pipeline::{OutlinePipeline, FRAGMENT_SHADER_HANDLE, OUTLINE_SHADER_HANDLE};
use crate::uniforms::{
extract_outline_stencil_uniforms, extract_outline_volume_uniforms,
queue_outline_stencil_bind_group, queue_outline_volume_bind_group, OutlineFragmentUniform,
OutlineStencilUniform, OutlineVolumeUniform,
extract_outline_uniforms, queue_outline_stencil_bind_group, queue_outline_volume_bind_group,
set_outline_visibility, OutlineFragmentUniform, OutlineStencilUniform, OutlineVolumeUniform,
};
use crate::view_uniforms::{
extract_outline_view_uniforms, queue_outline_view_bind_group, OutlineViewUniform,
@ -148,7 +149,7 @@ impl ExtractComponent for OutlineRenderLayers {
Option<&'static OutlineRenderLayers>,
Option<&'static RenderLayers>,
);
type Filter = Or<(With<OutlineVolume>, With<OutlineStencil>)>;
type Filter = With<ComputedOutline>;
type Out = Self;
fn extract_component(
@ -163,19 +164,51 @@ impl ExtractComponent for OutlineRenderLayers {
}
}
/// A component which specifies how the outline should be rendered.
#[derive(Clone, Component)]
#[non_exhaustive]
pub enum OutlineMode {
/// Vertex extrusion flattened into a plane facing the camera and intersecting the specified
/// point in model-space.
FlatVertex { model_origin: Vec3 },
/// Vertex extrusion in real model-space.
RealVertex,
}
impl Default for OutlineMode {
fn default() -> Self {
OutlineMode::FlatVertex {
model_origin: Vec3::ZERO,
}
}
}
/// A component for inheriting outlines from the parent entity.
#[derive(Clone, Component, Default)]
pub struct InheritOutline;
/// A bundle for rendering stenciled outlines around meshes.
#[derive(Bundle, Clone, Default)]
pub struct OutlineBundle {
pub outline: OutlineVolume,
pub stencil: OutlineStencil,
pub plane: ComputedOutlineDepth,
pub mode: OutlineMode,
pub computed: ComputedOutline,
}
/// A bundle for stenciling meshes in the outlining pass.
#[derive(Bundle, Clone, Default)]
pub struct OutlineStencilBundle {
pub stencil: OutlineStencil,
pub plane: ComputedOutlineDepth,
pub mode: OutlineMode,
pub computed: ComputedOutline,
}
/// A bundle for inheriting outlines from the parent entity.
#[derive(Bundle, Clone, Default)]
pub struct InheritOutlineBundle {
pub inherit: InheritOutline,
pub computed: ComputedOutline,
}
/// Adds support for rendering outlines.
@ -205,7 +238,10 @@ impl Plugin for OutlinePlugin {
))
.add_systems(
PostUpdate,
compute_outline_depth.after(TransformSystem::TransformPropagate),
(
compute_outline.after(TransformSystem::TransformPropagate),
set_outline_visibility.in_set(VisibilitySystems::CheckVisibility),
),
)
.sub_app_mut(RenderApp)
.init_resource::<DrawFunctions<StencilOutline>>()
@ -215,35 +251,31 @@ impl Plugin for OutlinePlugin {
.add_render_command::<StencilOutline, DrawStencil>()
.add_render_command::<OpaqueOutline, DrawOutline>()
.add_render_command::<TransparentOutline, DrawOutline>()
.add_systems(ExtractSchedule, extract_outline_view_uniforms)
.add_systems(ExtractSchedule, extract_outline_stencil_uniforms)
.add_systems(ExtractSchedule, extract_outline_volume_uniforms)
.add_systems(
Render,
sort_phase_system::<StencilOutline>.in_set(RenderSet::PhaseSort),
ExtractSchedule,
(extract_outline_uniforms, extract_outline_view_uniforms),
)
.add_systems(
Render,
sort_phase_system::<OpaqueOutline>.in_set(RenderSet::PhaseSort),
(
sort_phase_system::<OpaqueOutline>,
sort_phase_system::<TransparentOutline>,
)
.in_set(RenderSet::PhaseSort),
)
.add_systems(
Render,
sort_phase_system::<TransparentOutline>.in_set(RenderSet::PhaseSort),
(
queue_outline_view_bind_group,
queue_outline_stencil_bind_group,
queue_outline_volume_bind_group,
)
.in_set(RenderSet::Queue),
)
.add_systems(
Render,
queue_outline_view_bind_group.in_set(RenderSet::Queue),
)
.add_systems(
Render,
queue_outline_stencil_bind_group.in_set(RenderSet::Queue),
)
.add_systems(
Render,
queue_outline_volume_bind_group.in_set(RenderSet::Queue),
)
.add_systems(Render, queue_outline_stencil_mesh.in_set(RenderSet::Queue))
.add_systems(Render, queue_outline_volume_mesh.in_set(RenderSet::Queue));
(queue_outline_stencil_mesh, queue_outline_volume_mesh).in_set(RenderSet::Queue),
);
let world = &mut app.sub_app_mut(RenderApp).world;
let node = OutlineNode::new(world);

View File

@ -13,10 +13,13 @@ use bevy::{
},
};
use crate::{
node::StencilOutline, pipeline::OutlinePipeline, ComputedOutlineDepth, OutlineStencil,
OutlineVolume,
};
use crate::{node::StencilOutline, pipeline::OutlinePipeline, ComputedOutline};
#[derive(Component)]
pub(crate) struct ExtractedOutline {
pub depth_mode: DepthMode,
pub mesh: Handle<Mesh>,
}
#[derive(Clone, Component, ShaderType)]
pub(crate) struct OutlineStencilUniform {
@ -38,30 +41,12 @@ pub(crate) struct OutlineFragmentUniform {
pub colour: Vec4,
}
#[derive(Clone, Copy, Default, PartialEq)]
#[derive(Clone, Copy, PartialEq)]
pub(crate) enum DepthMode {
#[default]
Invalid = 0,
Flat = 1,
Real = 2,
}
impl DepthMode {
pub fn is_valid(&self) -> bool {
*self != DepthMode::Invalid
}
}
#[derive(Component)]
pub(crate) struct OutlineStencilFlags {
pub depth_mode: DepthMode,
}
#[derive(Component)]
pub(crate) struct OutlineVolumeFlags {
pub depth_mode: DepthMode,
}
#[derive(Resource)]
pub(crate) struct OutlineStencilBindGroup {
pub bind_group: BindGroup,
@ -72,47 +57,47 @@ pub(crate) struct OutlineVolumeBindGroup {
pub bind_group: BindGroup,
}
pub(crate) fn extract_outline_stencil_uniforms(
mut commands: Commands,
query: Extract<Query<(Entity, &OutlineStencil, &ComputedOutlineDepth)>>,
pub(crate) fn set_outline_visibility(
mut query: Query<(&mut ComputedVisibility, &ComputedOutline)>,
) {
for (entity, stencil, computed) in query.iter() {
if !stencil.enabled {
continue;
for (mut visibility, computed) in query.iter_mut() {
if let ComputedOutline(Some(computed)) = computed {
if computed.volume.value.enabled || computed.stencil.value.enabled {
visibility.set_visible_in_view();
}
}
commands
.get_or_spawn(entity)
.insert(OutlineStencilUniform {
origin: computed.world_origin,
offset: stencil.offset,
})
.insert(OutlineStencilFlags {
depth_mode: computed.depth_mode,
});
}
}
pub(crate) fn extract_outline_volume_uniforms(
#[allow(clippy::type_complexity)]
pub(crate) fn extract_outline_uniforms(
mut commands: Commands,
query: Extract<Query<(Entity, &OutlineVolume, &ComputedOutlineDepth)>>,
query: Extract<Query<(Entity, &ComputedOutline, &Handle<Mesh>)>>,
) {
for (entity, outline, computed) in query.iter() {
if !outline.visible || outline.colour.a() == 0.0 {
continue;
}
commands
.get_or_spawn(entity)
.insert(OutlineVolumeUniform {
origin: computed.world_origin,
offset: outline.width,
for (entity, computed, mesh) in query.iter() {
let cmds = &mut commands.get_or_spawn(entity);
if let ComputedOutline(Some(computed)) = computed {
cmds.insert(ExtractedOutline {
depth_mode: computed.mode.value.depth_mode,
mesh: mesh.clone_weak(),
});
if computed.volume.value.enabled {
cmds.insert(OutlineVolumeUniform {
origin: computed.mode.value.world_origin,
offset: computed.volume.value.offset,
})
.insert(OutlineFragmentUniform {
colour: outline.colour.as_linear_rgba_f32().into(),
})
.insert(OutlineVolumeFlags {
depth_mode: computed.depth_mode,
colour: computed.volume.value.colour,
});
}
if computed.stencil.value.enabled {
cmds.insert(OutlineStencilUniform {
origin: computed.mode.value.world_origin,
offset: computed.stencil.value.offset,
});
}
}
}
}
pub(crate) fn queue_outline_stencil_bind_group(