Add ComputedOutlinePlane and an example.
This commit is contained in:
parent
6781476bf0
commit
e1c845c434
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "bevy_mod_outline"
|
name = "bevy_mod_outline"
|
||||||
version = "0.2.4"
|
version = "0.3.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."
|
||||||
|
@ -26,14 +26,17 @@ bevy = { version = "0.8", default-features = false, features = [
|
||||||
] }
|
] }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["align16", "bevy_ui"]
|
default = ["bevy_ui"]
|
||||||
align16 = []
|
|
||||||
bevy_ui = ["bevy/bevy_ui", "bevy/bevy_sprite", "bevy/bevy_text"]
|
bevy_ui = ["bevy/bevy_ui", "bevy/bevy_sprite", "bevy/bevy_text"]
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "shapes"
|
name = "shapes"
|
||||||
path = "examples/shapes.rs"
|
path = "examples/shapes.rs"
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "pieces"
|
||||||
|
path = "examples/pieces.rs"
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "animated_fox"
|
name = "animated_fox"
|
||||||
path = "examples/animated_fox.rs"
|
path = "examples/animated_fox.rs"
|
||||||
|
|
|
@ -0,0 +1,139 @@
|
||||||
|
use std::f32::consts::TAU;
|
||||||
|
|
||||||
|
use bevy::{
|
||||||
|
prelude::{
|
||||||
|
shape::{Capsule, Torus, UVSphere},
|
||||||
|
*,
|
||||||
|
},
|
||||||
|
window::close_on_esc,
|
||||||
|
};
|
||||||
|
|
||||||
|
use bevy_mod_outline::*;
|
||||||
|
|
||||||
|
#[bevy_main]
|
||||||
|
fn main() {
|
||||||
|
App::new()
|
||||||
|
.insert_resource(Msaa { samples: 4 })
|
||||||
|
.insert_resource(ClearColor(Color::BLACK))
|
||||||
|
.add_plugins(DefaultPlugins)
|
||||||
|
.add_plugin(OutlinePlugin)
|
||||||
|
.add_startup_system(setup)
|
||||||
|
.add_system(close_on_esc)
|
||||||
|
.add_system(rotates)
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
|
struct Rotates;
|
||||||
|
|
||||||
|
fn setup(
|
||||||
|
mut commands: Commands,
|
||||||
|
mut meshes: ResMut<Assets<Mesh>>,
|
||||||
|
mut materials: ResMut<Assets<StandardMaterial>>,
|
||||||
|
) {
|
||||||
|
// Add sphere with child meshes sticking out of it
|
||||||
|
commands
|
||||||
|
.spawn_bundle(PbrBundle {
|
||||||
|
mesh: meshes.add(
|
||||||
|
UVSphere {
|
||||||
|
radius: 0.75,
|
||||||
|
sectors: 30,
|
||||||
|
stacks: 30,
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
),
|
||||||
|
material: materials.add(Color::rgb(0.9, 0.1, 0.1).into()),
|
||||||
|
transform: Transform::from_translation(Vec3::new(0.0, 1.0, 0.0)),
|
||||||
|
|
||||||
|
..default()
|
||||||
|
})
|
||||||
|
.insert_bundle(OutlineBundle {
|
||||||
|
outline: Outline {
|
||||||
|
visible: true,
|
||||||
|
colour: Color::WHITE,
|
||||||
|
width: 10.0,
|
||||||
|
},
|
||||||
|
..default()
|
||||||
|
})
|
||||||
|
.insert(Rotates)
|
||||||
|
.with_children(|parent| {
|
||||||
|
parent
|
||||||
|
.spawn_bundle(PbrBundle {
|
||||||
|
mesh: meshes.add(
|
||||||
|
Capsule {
|
||||||
|
radius: 0.2,
|
||||||
|
rings: 15,
|
||||||
|
depth: 1.0,
|
||||||
|
latitudes: 15,
|
||||||
|
longitudes: 15,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
),
|
||||||
|
material: materials.add(Color::rgb(0.1, 0.1, 0.9).into()),
|
||||||
|
transform: Transform::from_rotation(Quat::from_axis_angle(Vec3::X, TAU / 4.0))
|
||||||
|
.with_translation(Vec3::new(0.0, 0.0, 0.75)),
|
||||||
|
..default()
|
||||||
|
})
|
||||||
|
.insert_bundle(OutlineBundle {
|
||||||
|
outline: Outline {
|
||||||
|
visible: true,
|
||||||
|
colour: Color::WHITE,
|
||||||
|
width: 10.0,
|
||||||
|
},
|
||||||
|
..default()
|
||||||
|
})
|
||||||
|
.insert(InheritOutlinePlane);
|
||||||
|
parent
|
||||||
|
.spawn_bundle(PbrBundle {
|
||||||
|
mesh: meshes.add(
|
||||||
|
Torus {
|
||||||
|
radius: 0.5,
|
||||||
|
ring_radius: 0.1,
|
||||||
|
subdivisions_segments: 30,
|
||||||
|
subdivisions_sides: 15,
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
),
|
||||||
|
material: materials.add(Color::rgb(0.1, 0.1, 0.9).into()),
|
||||||
|
transform: Transform::from_rotation(Quat::from_axis_angle(Vec3::Z, TAU / 4.0))
|
||||||
|
.with_translation(Vec3::new(0.0, 0.0, -0.75)),
|
||||||
|
..default()
|
||||||
|
})
|
||||||
|
.insert_bundle(OutlineBundle {
|
||||||
|
outline: Outline {
|
||||||
|
visible: true,
|
||||||
|
colour: Color::WHITE,
|
||||||
|
width: 10.0,
|
||||||
|
},
|
||||||
|
..default()
|
||||||
|
})
|
||||||
|
.insert(InheritOutlinePlane);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add plane, light source, and camera
|
||||||
|
commands.spawn_bundle(PbrBundle {
|
||||||
|
mesh: meshes.add(Mesh::from(bevy::prelude::shape::Plane { size: 5.0 })),
|
||||||
|
material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()),
|
||||||
|
..default()
|
||||||
|
});
|
||||||
|
commands.spawn_bundle(PointLightBundle {
|
||||||
|
point_light: PointLight {
|
||||||
|
intensity: 1500.0,
|
||||||
|
shadows_enabled: true,
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
transform: Transform::from_xyz(4.0, 8.0, 4.0),
|
||||||
|
..default()
|
||||||
|
});
|
||||||
|
commands.spawn_bundle(Camera3dBundle {
|
||||||
|
transform: Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
|
||||||
|
..default()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rotates(mut query: Query<&mut Transform, With<Rotates>>, timer: Res<Time>) {
|
||||||
|
for mut transform in query.iter_mut() {
|
||||||
|
transform.rotate_axis(Vec3::Y, 0.75 * timer.delta_seconds());
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,17 +5,10 @@
|
||||||
@group(1) @binding(0)
|
@group(1) @binding(0)
|
||||||
var<uniform> mesh: Mesh;
|
var<uniform> mesh: Mesh;
|
||||||
|
|
||||||
#ifdef SKINNED
|
fn model_origin_z(plane: vec3<f32>, view_proj: mat4x4<f32>) -> f32 {
|
||||||
@group(1) @binding(1)
|
|
||||||
var<uniform> joint_matrices: SkinnedMesh;
|
|
||||||
#import bevy_pbr::skinning
|
|
||||||
#endif
|
|
||||||
|
|
||||||
fn model_origin_z(model: mat4x4<f32>, view_proj: mat4x4<f32>) -> f32 {
|
|
||||||
var origin = model[3];
|
|
||||||
var proj_zw = mat4x2<f32>(
|
var proj_zw = mat4x2<f32>(
|
||||||
view_proj[0].zw, view_proj[1].zw,
|
view_proj[0].zw, view_proj[1].zw,
|
||||||
view_proj[2].zw, view_proj[3].zw);
|
view_proj[2].zw, view_proj[3].zw);
|
||||||
var zw = proj_zw * origin;
|
var zw = proj_zw * vec4<f32>(plane, 1.0);
|
||||||
return zw.x / zw.y;
|
return zw.x / zw.y;
|
||||||
}
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
use bevy::prelude::*;
|
||||||
|
|
||||||
|
/// A component for storing the computed plane on which the outline lies.
|
||||||
|
#[derive(Clone, Component, Default)]
|
||||||
|
pub struct ComputedOutlinePlane {
|
||||||
|
pub(crate) plane: Vec3,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A component which specifies that this entity lies on the same plane as its parent.
|
||||||
|
#[derive(Clone, Component, Default)]
|
||||||
|
pub struct InheritOutlinePlane;
|
||||||
|
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
|
pub(crate) fn compute_outline_plane(
|
||||||
|
mut root_query: Query<
|
||||||
|
(
|
||||||
|
&mut ComputedOutlinePlane,
|
||||||
|
&GlobalTransform,
|
||||||
|
Changed<GlobalTransform>,
|
||||||
|
Option<(&Children, Changed<Children>)>,
|
||||||
|
),
|
||||||
|
Without<InheritOutlinePlane>,
|
||||||
|
>,
|
||||||
|
mut computed_query: Query<(&mut ComputedOutlinePlane, Changed<InheritOutlinePlane>)>,
|
||||||
|
child_query: Query<(&Children, Changed<Children>)>,
|
||||||
|
) {
|
||||||
|
for (mut computed, transform, changed_transform, children) in root_query.iter_mut() {
|
||||||
|
if changed_transform {
|
||||||
|
let matrix = transform.compute_matrix();
|
||||||
|
computed.plane = matrix.project_point3(Vec3::ZERO);
|
||||||
|
}
|
||||||
|
if let Some((cs, changed_children)) = children {
|
||||||
|
let changed2 = changed_children || changed_transform;
|
||||||
|
for child in cs.iter() {
|
||||||
|
propagate_outline_planes(
|
||||||
|
&computed,
|
||||||
|
changed2,
|
||||||
|
*child,
|
||||||
|
&mut computed_query,
|
||||||
|
&child_query,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn propagate_outline_planes(
|
||||||
|
root_computed: &ComputedOutlinePlane,
|
||||||
|
changed: bool,
|
||||||
|
entity: Entity,
|
||||||
|
computed_query: &mut Query<(&mut ComputedOutlinePlane, Changed<InheritOutlinePlane>)>,
|
||||||
|
child_query: &Query<(&Children, Changed<Children>)>,
|
||||||
|
) {
|
||||||
|
if let Ok((mut computed, changed_inherit)) = computed_query.get_mut(entity) {
|
||||||
|
if changed_inherit || changed {
|
||||||
|
*computed = root_computed.clone();
|
||||||
|
}
|
||||||
|
if let Ok((cs, changed_children)) = child_query.get(entity) {
|
||||||
|
let changed2 = changed_children || changed_inherit || changed;
|
||||||
|
for child in cs.iter() {
|
||||||
|
propagate_outline_planes(
|
||||||
|
root_computed,
|
||||||
|
changed2,
|
||||||
|
*child,
|
||||||
|
computed_query,
|
||||||
|
child_query,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,7 +7,7 @@ use bevy::render::view::ExtractedView;
|
||||||
|
|
||||||
use crate::node::{OpaqueOutline, StencilOutline, TransparentOutline};
|
use crate::node::{OpaqueOutline, StencilOutline, TransparentOutline};
|
||||||
use crate::pipeline::{OutlinePipeline, PassType};
|
use crate::pipeline::{OutlinePipeline, PassType};
|
||||||
use crate::uniforms::{OutlineFragmentUniform, SetOutlineBindGroup};
|
use crate::uniforms::{OutlineFragmentUniform, SetOutlineBindGroup, SetOutlineStencilBindGroup};
|
||||||
use crate::view_uniforms::SetOutlineViewBindGroup;
|
use crate::view_uniforms::SetOutlineViewBindGroup;
|
||||||
use crate::OutlineStencil;
|
use crate::OutlineStencil;
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@ pub type DrawStencil = (
|
||||||
SetItemPipeline,
|
SetItemPipeline,
|
||||||
SetMeshViewBindGroup<0>,
|
SetMeshViewBindGroup<0>,
|
||||||
SetMeshBindGroup<1>,
|
SetMeshBindGroup<1>,
|
||||||
|
SetOutlineStencilBindGroup<2>,
|
||||||
DrawMesh,
|
DrawMesh,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
21
src/lib.rs
21
src/lib.rs
|
@ -26,6 +26,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::{RenderApp, RenderStage};
|
use bevy::render::{RenderApp, RenderStage};
|
||||||
|
use bevy::transform::TransformSystem;
|
||||||
|
|
||||||
use crate::draw::{queue_outline_mesh, queue_outline_stencil_mesh, DrawOutline, DrawStencil};
|
use crate::draw::{queue_outline_mesh, queue_outline_stencil_mesh, DrawOutline, DrawStencil};
|
||||||
use crate::node::{OpaqueOutline, OutlineNode, StencilOutline, TransparentOutline};
|
use crate::node::{OpaqueOutline, OutlineNode, StencilOutline, TransparentOutline};
|
||||||
|
@ -33,13 +34,15 @@ use crate::pipeline::{
|
||||||
OutlinePipeline, COMMON_SHADER_HANDLE, OUTLINE_SHADER_HANDLE, STENCIL_SHADER_HANDLE,
|
OutlinePipeline, COMMON_SHADER_HANDLE, OUTLINE_SHADER_HANDLE, STENCIL_SHADER_HANDLE,
|
||||||
};
|
};
|
||||||
use crate::uniforms::{
|
use crate::uniforms::{
|
||||||
extract_outline_uniforms, queue_outline_bind_group, OutlineFragmentUniform,
|
extract_outline_stencil_uniforms, extract_outline_uniforms, queue_outline_bind_group,
|
||||||
|
queue_outline_stencil_bind_group, OutlineFragmentUniform, OutlineStencilUniform,
|
||||||
OutlineVertexUniform,
|
OutlineVertexUniform,
|
||||||
};
|
};
|
||||||
use crate::view_uniforms::{
|
use crate::view_uniforms::{
|
||||||
extract_outline_view_uniforms, queue_outline_view_bind_group, OutlineViewUniform,
|
extract_outline_view_uniforms, queue_outline_view_bind_group, OutlineViewUniform,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
mod computed;
|
||||||
mod draw;
|
mod draw;
|
||||||
mod generate;
|
mod generate;
|
||||||
mod node;
|
mod node;
|
||||||
|
@ -47,6 +50,7 @@ mod pipeline;
|
||||||
mod uniforms;
|
mod uniforms;
|
||||||
mod view_uniforms;
|
mod view_uniforms;
|
||||||
|
|
||||||
|
pub use computed::*;
|
||||||
pub use generate::*;
|
pub use generate::*;
|
||||||
|
|
||||||
// See https://alexanderameye.github.io/notes/rendering-outlines/
|
// See https://alexanderameye.github.io/notes/rendering-outlines/
|
||||||
|
@ -90,6 +94,14 @@ pub struct Outline {
|
||||||
pub struct OutlineBundle {
|
pub struct OutlineBundle {
|
||||||
pub outline: Outline,
|
pub outline: Outline,
|
||||||
pub stencil: OutlineStencil,
|
pub stencil: OutlineStencil,
|
||||||
|
pub plane: ComputedOutlinePlane,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A bundle for stenciling meshes in the outlining pass.
|
||||||
|
#[derive(Bundle, Clone, Default)]
|
||||||
|
pub struct OutlineStencilBundle {
|
||||||
|
pub stencil: OutlineStencil,
|
||||||
|
pub plane: ComputedOutlinePlane,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds support for rendering outlines.
|
/// Adds support for rendering outlines.
|
||||||
|
@ -112,9 +124,14 @@ impl Plugin for OutlinePlugin {
|
||||||
);
|
);
|
||||||
|
|
||||||
app.add_plugin(ExtractComponentPlugin::<OutlineStencil>::extract_visible())
|
app.add_plugin(ExtractComponentPlugin::<OutlineStencil>::extract_visible())
|
||||||
|
.add_plugin(UniformComponentPlugin::<OutlineStencilUniform>::default())
|
||||||
.add_plugin(UniformComponentPlugin::<OutlineVertexUniform>::default())
|
.add_plugin(UniformComponentPlugin::<OutlineVertexUniform>::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(
|
||||||
|
CoreStage::PostUpdate,
|
||||||
|
compute_outline_plane.after(TransformSystem::TransformPropagate),
|
||||||
|
)
|
||||||
.sub_app_mut(RenderApp)
|
.sub_app_mut(RenderApp)
|
||||||
.init_resource::<DrawFunctions<StencilOutline>>()
|
.init_resource::<DrawFunctions<StencilOutline>>()
|
||||||
.init_resource::<DrawFunctions<OpaqueOutline>>()
|
.init_resource::<DrawFunctions<OpaqueOutline>>()
|
||||||
|
@ -125,6 +142,7 @@ impl Plugin for OutlinePlugin {
|
||||||
.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_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_uniforms)
|
.add_system_to_stage(RenderStage::Extract, extract_outline_uniforms)
|
||||||
.add_system_to_stage(RenderStage::PhaseSort, sort_phase_system::<StencilOutline>)
|
.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::<OpaqueOutline>)
|
||||||
|
@ -133,6 +151,7 @@ impl Plugin for OutlinePlugin {
|
||||||
sort_phase_system::<TransparentOutline>,
|
sort_phase_system::<TransparentOutline>,
|
||||||
)
|
)
|
||||||
.add_system_to_stage(RenderStage::Queue, queue_outline_view_bind_group)
|
.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_bind_group)
|
.add_system_to_stage(RenderStage::Queue, queue_outline_bind_group)
|
||||||
.add_system_to_stage(RenderStage::Queue, queue_outline_stencil_mesh)
|
.add_system_to_stage(RenderStage::Queue, queue_outline_stencil_mesh)
|
||||||
.add_system_to_stage(RenderStage::Queue, queue_outline_mesh);
|
.add_system_to_stage(RenderStage::Queue, queue_outline_mesh);
|
||||||
|
|
|
@ -17,16 +17,13 @@ struct OutlineViewUniform {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct OutlineVertexUniform {
|
struct OutlineVertexUniform {
|
||||||
#ifdef ALIGN_16
|
|
||||||
@align(16)
|
@align(16)
|
||||||
#endif
|
plane: vec3<f32>,
|
||||||
width: f32,
|
width: f32,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct OutlineFragmentUniform {
|
struct OutlineFragmentUniform {
|
||||||
#ifdef ALIGN_16
|
|
||||||
@align(16)
|
@align(16)
|
||||||
#endif
|
|
||||||
colour: vec4<f32>,
|
colour: vec4<f32>,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -56,7 +53,7 @@ fn vertex(vertex: VertexInput) -> @builtin(position) vec4<f32> {
|
||||||
var clip_norm = mat4to3(view.view_proj) * (mat4to3(model) * vertex.normal);
|
var clip_norm = mat4to3(view.view_proj) * (mat4to3(model) * vertex.normal);
|
||||||
var ndc_pos = clip_pos.xy / clip_pos.w;
|
var ndc_pos = clip_pos.xy / clip_pos.w;
|
||||||
var ndc_delta = vstage.width * normalize(clip_norm.xy) * view_uniform.scale;
|
var ndc_delta = vstage.width * normalize(clip_norm.xy) * view_uniform.scale;
|
||||||
return vec4<f32>(ndc_pos + ndc_delta, model_origin_z(mesh.model, view.view_proj), 1.0);
|
return vec4<f32>(ndc_pos + ndc_delta, model_origin_z(vstage.plane, view.view_proj), 1.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@fragment
|
@fragment
|
||||||
|
|
|
@ -20,7 +20,7 @@ use bevy::{
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::uniforms::{OutlineFragmentUniform, OutlineVertexUniform};
|
use crate::uniforms::{OutlineFragmentUniform, OutlineStencilUniform, OutlineVertexUniform};
|
||||||
use crate::view_uniforms::OutlineViewUniform;
|
use crate::view_uniforms::OutlineViewUniform;
|
||||||
use crate::ATTRIBUTE_OUTLINE_NORMAL;
|
use crate::ATTRIBUTE_OUTLINE_NORMAL;
|
||||||
|
|
||||||
|
@ -43,6 +43,7 @@ pub enum PassType {
|
||||||
pub struct OutlinePipeline {
|
pub struct OutlinePipeline {
|
||||||
mesh_pipeline: MeshPipeline,
|
mesh_pipeline: MeshPipeline,
|
||||||
pub outline_view_bind_group_layout: BindGroupLayout,
|
pub outline_view_bind_group_layout: BindGroupLayout,
|
||||||
|
pub outline_stencil_bind_group_layout: BindGroupLayout,
|
||||||
pub outline_bind_group_layout: BindGroupLayout,
|
pub outline_bind_group_layout: BindGroupLayout,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,9 +96,24 @@ impl FromWorld for OutlinePipeline {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
let outline_stencil_bind_group_layout =
|
||||||
|
render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
|
||||||
|
label: Some("outline_stencil_bind_group_layout"),
|
||||||
|
entries: &[BindGroupLayoutEntry {
|
||||||
|
binding: 0,
|
||||||
|
visibility: ShaderStages::VERTEX,
|
||||||
|
ty: BindingType::Buffer {
|
||||||
|
ty: BufferBindingType::Uniform,
|
||||||
|
has_dynamic_offset: true,
|
||||||
|
min_binding_size: BufferSize::new(OutlineStencilUniform::SHADER_SIZE.get()),
|
||||||
|
},
|
||||||
|
count: None,
|
||||||
|
}],
|
||||||
|
});
|
||||||
OutlinePipeline {
|
OutlinePipeline {
|
||||||
mesh_pipeline,
|
mesh_pipeline,
|
||||||
outline_view_bind_group_layout,
|
outline_view_bind_group_layout,
|
||||||
|
outline_stencil_bind_group_layout,
|
||||||
outline_bind_group_layout,
|
outline_bind_group_layout,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -114,11 +130,7 @@ impl SpecializedMeshPipeline for OutlinePipeline {
|
||||||
let mut targets = vec![];
|
let mut targets = vec![];
|
||||||
let mut bind_layouts = vec![self.mesh_pipeline.view_layout.clone()];
|
let mut bind_layouts = vec![self.mesh_pipeline.view_layout.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 shader_defs = if cfg!(feature = "align16") {
|
let mut shader_defs = vec![];
|
||||||
vec!["ALIGN_16".to_string()]
|
|
||||||
} else {
|
|
||||||
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)
|
||||||
|
@ -135,6 +147,7 @@ impl SpecializedMeshPipeline for OutlinePipeline {
|
||||||
match pass_type {
|
match pass_type {
|
||||||
PassType::Stencil => {
|
PassType::Stencil => {
|
||||||
shader = STENCIL_SHADER_HANDLE;
|
shader = STENCIL_SHADER_HANDLE;
|
||||||
|
bind_layouts.push(self.outline_stencil_bind_group_layout.clone());
|
||||||
}
|
}
|
||||||
PassType::Opaque | PassType::Transparent => {
|
PassType::Opaque | PassType::Transparent => {
|
||||||
shader = OUTLINE_SHADER_HANDLE;
|
shader = OUTLINE_SHADER_HANDLE;
|
||||||
|
|
|
@ -8,6 +8,14 @@ struct VertexInput {
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct OutlineStencilUniform {
|
||||||
|
@align(16)
|
||||||
|
plane: vec3<f32>,
|
||||||
|
};
|
||||||
|
|
||||||
|
@group(2) @binding(0)
|
||||||
|
var<uniform> vstage: OutlineStencilUniform;
|
||||||
|
|
||||||
@vertex
|
@vertex
|
||||||
fn vertex(vertex: VertexInput) -> @builtin(position) vec4<f32> {
|
fn vertex(vertex: VertexInput) -> @builtin(position) vec4<f32> {
|
||||||
#ifdef SKINNED
|
#ifdef SKINNED
|
||||||
|
@ -17,7 +25,7 @@ fn vertex(vertex: VertexInput) -> @builtin(position) vec4<f32> {
|
||||||
#endif
|
#endif
|
||||||
var clip_pos = view.view_proj * (model * vec4<f32>(vertex.position, 1.0));
|
var clip_pos = view.view_proj * (model * vec4<f32>(vertex.position, 1.0));
|
||||||
var ndc_pos = clip_pos.xy / clip_pos.w;
|
var ndc_pos = clip_pos.xy / clip_pos.w;
|
||||||
return vec4<f32>(ndc_pos, model_origin_z(mesh.model, view.view_proj), 1.0);
|
return vec4<f32>(ndc_pos, model_origin_z(vstage.plane, view.view_proj), 1.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@fragment
|
@fragment
|
||||||
|
|
|
@ -13,11 +13,18 @@ use bevy::{
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{pipeline::OutlinePipeline, Outline};
|
use crate::{pipeline::OutlinePipeline, ComputedOutlinePlane, Outline, OutlineStencil};
|
||||||
|
|
||||||
|
#[derive(Clone, Component, ShaderType)]
|
||||||
|
pub struct OutlineStencilUniform {
|
||||||
|
#[cfg_attr(feature = "align16", align(16))]
|
||||||
|
pub plane: Vec3,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Component, ShaderType)]
|
#[derive(Clone, Component, ShaderType)]
|
||||||
pub struct OutlineVertexUniform {
|
pub struct OutlineVertexUniform {
|
||||||
#[cfg_attr(feature = "align16", align(16))]
|
#[align(16)]
|
||||||
|
pub plane: Vec3,
|
||||||
pub width: f32,
|
pub width: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,12 +34,30 @@ pub struct OutlineFragmentUniform {
|
||||||
pub colour: Vec4,
|
pub colour: Vec4,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct OutlineStencilBindGroup {
|
||||||
|
pub bind_group: BindGroup,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct OutlineBindGroup {
|
pub struct OutlineBindGroup {
|
||||||
pub bind_group: BindGroup,
|
pub bind_group: BindGroup,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn extract_outline_uniforms(mut commands: Commands, query: Extract<Query<(Entity, &Outline)>>) {
|
pub fn extract_outline_stencil_uniforms(
|
||||||
for (entity, outline) in query.iter() {
|
mut commands: Commands,
|
||||||
|
query: Extract<Query<(Entity, &ComputedOutlinePlane), With<OutlineStencil>>>,
|
||||||
|
) {
|
||||||
|
for (entity, computed) in query.iter() {
|
||||||
|
commands.get_or_spawn(entity).insert(OutlineStencilUniform {
|
||||||
|
plane: computed.plane,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn extract_outline_uniforms(
|
||||||
|
mut commands: Commands,
|
||||||
|
query: Extract<Query<(Entity, &Outline, &ComputedOutlinePlane)>>,
|
||||||
|
) {
|
||||||
|
for (entity, outline, computed) in query.iter() {
|
||||||
if !outline.visible || outline.colour.a() == 0.0 {
|
if !outline.visible || outline.colour.a() == 0.0 {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -40,6 +65,7 @@ pub fn extract_outline_uniforms(mut commands: Commands, query: Extract<Query<(En
|
||||||
.get_or_spawn(entity)
|
.get_or_spawn(entity)
|
||||||
.insert(OutlineVertexUniform {
|
.insert(OutlineVertexUniform {
|
||||||
width: outline.width,
|
width: outline.width,
|
||||||
|
plane: computed.plane,
|
||||||
})
|
})
|
||||||
.insert(OutlineFragmentUniform {
|
.insert(OutlineFragmentUniform {
|
||||||
colour: outline.colour.as_linear_rgba_f32().into(),
|
colour: outline.colour.as_linear_rgba_f32().into(),
|
||||||
|
@ -47,6 +73,25 @@ pub fn extract_outline_uniforms(mut commands: Commands, query: Extract<Query<(En
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn queue_outline_stencil_bind_group(
|
||||||
|
mut commands: Commands,
|
||||||
|
render_device: Res<RenderDevice>,
|
||||||
|
outline_pipeline: Res<OutlinePipeline>,
|
||||||
|
vertex: Res<ComponentUniforms<OutlineStencilUniform>>,
|
||||||
|
) {
|
||||||
|
if let Some(vertex_binding) = vertex.binding() {
|
||||||
|
let bind_group = render_device.create_bind_group(&BindGroupDescriptor {
|
||||||
|
entries: &[BindGroupEntry {
|
||||||
|
binding: 0,
|
||||||
|
resource: vertex_binding.clone(),
|
||||||
|
}],
|
||||||
|
label: Some("outline_stencil_bind_group"),
|
||||||
|
layout: &outline_pipeline.outline_stencil_bind_group_layout,
|
||||||
|
});
|
||||||
|
commands.insert_resource(OutlineStencilBindGroup { bind_group });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn queue_outline_bind_group(
|
pub fn queue_outline_bind_group(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
render_device: Res<RenderDevice>,
|
render_device: Res<RenderDevice>,
|
||||||
|
@ -73,6 +118,25 @@ pub fn queue_outline_bind_group(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct SetOutlineStencilBindGroup<const I: usize>();
|
||||||
|
|
||||||
|
impl<const I: usize> EntityRenderCommand for SetOutlineStencilBindGroup<I> {
|
||||||
|
type Param = (
|
||||||
|
SRes<OutlineStencilBindGroup>,
|
||||||
|
SQuery<Read<DynamicUniformIndex<OutlineStencilUniform>>>,
|
||||||
|
);
|
||||||
|
fn render<'w>(
|
||||||
|
_view: Entity,
|
||||||
|
item: Entity,
|
||||||
|
(bind_group, query): 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()]);
|
||||||
|
RenderCommandResult::Success
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct SetOutlineBindGroup<const I: usize>();
|
pub struct SetOutlineBindGroup<const I: usize>();
|
||||||
|
|
||||||
impl<const I: usize> EntityRenderCommand for SetOutlineBindGroup<I> {
|
impl<const I: usize> EntityRenderCommand for SetOutlineBindGroup<I> {
|
||||||
|
|
|
@ -15,7 +15,7 @@ use crate::pipeline::OutlinePipeline;
|
||||||
|
|
||||||
#[derive(Clone, Component, ShaderType)]
|
#[derive(Clone, Component, ShaderType)]
|
||||||
pub struct OutlineViewUniform {
|
pub struct OutlineViewUniform {
|
||||||
#[cfg_attr(feature = "align16", align(16))]
|
#[align(16)]
|
||||||
scale: Vec2,
|
scale: Vec2,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue