use bevy::asset::load_internal_asset;
use bevy::core_pipeline::core_3d::graph::{Core3d, Node3d};
use bevy::prelude::*;
use bevy::render::batching::{batch_and_prepare_render_phase, write_batched_instance_buffer};
use bevy::render::extract_component::{
ExtractComponent, ExtractComponentPlugin, UniformComponentPlugin,
};
use bevy::render::mesh::MeshVertexAttribute;
use bevy::render::render_graph::{RenderGraphApp, RenderLabel};
use bevy::render::render_phase::{sort_phase_system, AddRenderCommand, DrawFunctions};
use bevy::render::render_resource::{SpecializedMeshPipelines, VertexFormat};
use bevy::render::view::{RenderLayers, VisibilitySystems};
use bevy::render::{Render, RenderApp, RenderSet};
use bevy::transform::TransformSystem;
use interpolation::Lerp;
use crate::draw::{
queue_outline_stencil_mesh, queue_outline_volume_mesh, DrawOutline, DrawStencil,
};
use crate::node::{OpaqueOutline, OutlineNode, StencilOutline, TransparentOutline};
use crate::pipeline::{OutlinePipeline, FRAGMENT_SHADER_HANDLE, OUTLINE_SHADER_HANDLE};
use crate::uniforms::{
extract_outline_uniforms, prepare_outline_stencil_bind_group,
prepare_outline_volume_bind_group, set_outline_visibility, OutlineFragmentUniform,
OutlineStencilUniform, OutlineVolumeUniform,
};
use crate::view_uniforms::{
extract_outline_view_uniforms, prepare_outline_view_bind_group, OutlineViewUniform,
};
mod computed;
mod draw;
mod generate;
mod node;
mod pipeline;
mod scene;
mod uniforms;
mod view_uniforms;
pub use computed::*;
pub use generate::*;
pub use scene::*;
pub const ATTRIBUTE_OUTLINE_NORMAL: MeshVertexAttribute =
MeshVertexAttribute::new("Outline_Normal", 1585570526, VertexFormat::Float32x3);
#[derive(Copy, Clone, Debug, RenderLabel, Hash, PartialEq, Eq)]
pub enum NodeOutline {
OutlinePass,
}
#[derive(Clone, Component)]
pub struct OutlineStencil {
pub enabled: bool,
pub offset: f32,
}
impl Default for OutlineStencil {
fn default() -> Self {
OutlineStencil {
enabled: true,
offset: 0.0,
}
}
}
fn lerp_bool(this: bool, other: bool, scalar: f32) -> bool {
if scalar <= 0.0 {
this
} else if scalar >= 1.0 {
other
} else {
this | other
}
}
impl Lerp for OutlineStencil {
type Scalar = f32;
fn lerp(&self, other: &Self, scalar: &Self::Scalar) -> Self {
OutlineStencil {
enabled: lerp_bool(self.enabled, other.enabled, *scalar),
offset: self.offset.lerp(other.offset, *scalar),
}
}
}
#[derive(Clone, Component, Default)]
pub struct OutlineVolume {
pub visible: bool,
pub width: f32,
pub colour: Color,
}
impl Lerp for OutlineVolume {
type Scalar = f32;
fn lerp(&self, other: &Self, scalar: &Self::Scalar) -> Self {
OutlineVolume {
visible: lerp_bool(self.visible, other.visible, *scalar),
width: self.width.lerp(other.width, *scalar),
colour: {
let [r, g, b, a] = self
.colour
.as_linear_rgba_f32()
.lerp(&other.colour.as_linear_rgba_f32(), scalar);
Color::rgba_linear(r, g, b, a)
},
}
}
}
#[derive(Component, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Deref, DerefMut, Default)]
pub struct OutlineRenderLayers(pub RenderLayers);
impl ExtractComponent for OutlineRenderLayers {
type QueryData = (
Option<&'static OutlineRenderLayers>,
Option<&'static RenderLayers>,
);
type QueryFilter = With<ComputedOutline>;
type Out = Self;
fn extract_component(
(outline_mask, object_mask): (Option<&OutlineRenderLayers>, Option<&RenderLayers>),
) -> Option<Self> {
Some(
outline_mask
.copied()
.or_else(|| object_mask.copied().map(OutlineRenderLayers))
.unwrap_or_default(),
)
}
}
#[derive(Clone, Component)]
#[non_exhaustive]
pub enum OutlineMode {
FlatVertex { model_origin: Vec3 },
RealVertex,
}
impl Default for OutlineMode {
fn default() -> Self {
OutlineMode::FlatVertex {
model_origin: Vec3::ZERO,
}
}
}
#[derive(Clone, Component, Default)]
pub struct InheritOutline;
#[derive(Bundle, Clone, Default)]
pub struct OutlineBundle {
pub outline: OutlineVolume,
pub stencil: OutlineStencil,
pub mode: OutlineMode,
pub computed: ComputedOutline,
}
#[derive(Bundle, Clone, Default)]
pub struct OutlineStencilBundle {
pub stencil: OutlineStencil,
pub mode: OutlineMode,
pub computed: ComputedOutline,
}
#[derive(Bundle, Clone, Default)]
pub struct InheritOutlineBundle {
pub inherit: InheritOutline,
pub computed: ComputedOutline,
}
pub struct OutlinePlugin;
impl Plugin for OutlinePlugin {
fn build(&self, app: &mut App) {
load_internal_asset!(
app,
OUTLINE_SHADER_HANDLE,
"outline.wgsl",
Shader::from_wgsl
);
load_internal_asset!(
app,
FRAGMENT_SHADER_HANDLE,
"fragment.wgsl",
Shader::from_wgsl
);
app.add_plugins((
ExtractComponentPlugin::<OutlineRenderLayers>::default(),
UniformComponentPlugin::<OutlineStencilUniform>::default(),
UniformComponentPlugin::<OutlineVolumeUniform>::default(),
UniformComponentPlugin::<OutlineFragmentUniform>::default(),
UniformComponentPlugin::<OutlineViewUniform>::default(),
))
.add_systems(
PostUpdate,
(
compute_outline
.after(TransformSystem::TransformPropagate)
.after(VisibilitySystems::VisibilityPropagate),
set_outline_visibility.in_set(VisibilitySystems::CheckVisibility),
),
)
.sub_app_mut(RenderApp)
.init_resource::<DrawFunctions<StencilOutline>>()
.init_resource::<DrawFunctions<OpaqueOutline>>()
.init_resource::<DrawFunctions<TransparentOutline>>()
.init_resource::<SpecializedMeshPipelines<OutlinePipeline>>()
.add_render_command::<StencilOutline, DrawStencil>()
.add_render_command::<OpaqueOutline, DrawOutline>()
.add_render_command::<TransparentOutline, DrawOutline>()
.add_systems(
ExtractSchedule,
(extract_outline_uniforms, extract_outline_view_uniforms),
)
.add_systems(
Render,
(
prepare_outline_view_bind_group,
prepare_outline_stencil_bind_group,
prepare_outline_volume_bind_group,
)
.in_set(RenderSet::PrepareBindGroups),
)
.add_systems(
Render,
(queue_outline_stencil_mesh, queue_outline_volume_mesh).in_set(RenderSet::QueueMeshes),
)
.add_systems(
Render,
(
sort_phase_system::<StencilOutline>,
sort_phase_system::<OpaqueOutline>,
sort_phase_system::<TransparentOutline>,
)
.in_set(RenderSet::PhaseSort),
)
.add_systems(
Render,
(
batch_and_prepare_render_phase::<StencilOutline, OutlinePipeline>,
batch_and_prepare_render_phase::<OpaqueOutline, OutlinePipeline>,
batch_and_prepare_render_phase::<TransparentOutline, OutlinePipeline>,
)
.in_set(RenderSet::PrepareResources),
)
.add_systems(
Render,
write_batched_instance_buffer::<OutlinePipeline>
.in_set(RenderSet::PrepareResourcesFlush),
)
.add_render_graph_node::<OutlineNode>(Core3d, NodeOutline::OutlinePass)
.add_render_graph_edges(
Core3d,
(
Node3d::Tonemapping,
NodeOutline::OutlinePass,
Node3d::Fxaa,
Node3d::EndMainPassPostProcessing,
),
);
}
fn finish(&self, app: &mut App) {
app.sub_app_mut(RenderApp)
.init_resource::<OutlinePipeline>();
}
}