bevy_pbr/decal/
forward.rs1use crate::{
2 ExtendedMaterial, Material, MaterialExtension, MaterialExtensionKey, MaterialExtensionPipeline,
3 MaterialPlugin, StandardMaterial,
4};
5use bevy_app::{App, Plugin};
6use bevy_asset::{Asset, Assets, Handle};
7use bevy_ecs::{
8 component::Component, lifecycle::HookContext, resource::Resource, world::DeferredWorld,
9};
10use bevy_math::{Quat, Vec2, Vec3};
11use bevy_mesh::{Mesh, Mesh3d, MeshBuilder, MeshVertexBufferLayoutRef, Meshable};
12use bevy_reflect::{Reflect, TypePath};
13use bevy_render::{
14 render_asset::RenderAssets,
15 render_resource::{
16 AsBindGroup, AsBindGroupShaderType, CompareFunction, RenderPipelineDescriptor, ShaderType,
17 SpecializedMeshPipelineError,
18 },
19 texture::GpuImage,
20 RenderDebugFlags,
21};
22use bevy_shader::load_shader_library;
23use bevy_shape::Rectangle;
24
25pub struct ForwardDecalPlugin;
27
28impl Plugin for ForwardDecalPlugin {
29 fn build(&self, app: &mut App) {
30 load_shader_library!(app, "forward.wesl");
31
32 let mesh = app.world_mut().resource_mut::<Assets<Mesh>>().add(
33 Rectangle::from_size(Vec2::ONE)
34 .mesh()
35 .build()
36 .rotated_by(Quat::from_rotation_arc(Vec3::Z, Vec3::Y))
37 .with_generated_tangents()
38 .unwrap(),
39 );
40
41 app.insert_resource(ForwardDecalMesh(mesh));
42
43 app.add_plugins(MaterialPlugin::<ForwardDecalMaterial<StandardMaterial>> {
44 debug_flags: RenderDebugFlags::default(),
45 ..Default::default()
46 });
47 }
48}
49
50#[derive(Component, Reflect)]
63#[require(Mesh3d)]
64#[component(on_add=forward_decal_set_mesh)]
65pub struct ForwardDecal;
66
67#[expect(type_alias_bounds, reason = "Type alias generics not yet stable")]
73pub type ForwardDecalMaterial<B: Material> = ExtendedMaterial<B, ForwardDecalMaterialExt>;
74
75#[derive(Asset, AsBindGroup, TypePath, Clone, Debug)]
83#[uniform(200, ForwardDecalMaterialExtUniform)]
84pub struct ForwardDecalMaterialExt {
85 pub depth_fade_factor: f32,
96}
97
98#[derive(Clone, Default, ShaderType)]
99pub struct ForwardDecalMaterialExtUniform {
100 pub inv_depth_fade_factor: f32,
101}
102
103impl AsBindGroupShaderType<ForwardDecalMaterialExtUniform> for ForwardDecalMaterialExt {
104 fn as_bind_group_shader_type(
105 &self,
106 _images: &RenderAssets<GpuImage>,
107 ) -> ForwardDecalMaterialExtUniform {
108 ForwardDecalMaterialExtUniform {
109 inv_depth_fade_factor: 1.0 / self.depth_fade_factor.max(0.001),
110 }
111 }
112}
113
114impl MaterialExtension for ForwardDecalMaterialExt {
115 fn enable_oit() -> bool {
118 false
119 }
120
121 fn enable_prepass() -> bool {
123 false
124 }
125
126 fn enable_shadows() -> bool {
127 false
128 }
129
130 fn specialize(
131 _pipeline: &MaterialExtensionPipeline,
132 descriptor: &mut RenderPipelineDescriptor,
133 _layout: &MeshVertexBufferLayoutRef,
134 _key: MaterialExtensionKey<Self>,
135 ) -> Result<(), SpecializedMeshPipelineError> {
136 descriptor.depth_stencil.as_mut().unwrap().depth_compare = Some(CompareFunction::Always);
137
138 descriptor.vertex.shader_defs.push("FORWARD_DECAL".into());
139
140 if let Some(fragment) = &mut descriptor.fragment {
141 fragment.shader_defs.push("FORWARD_DECAL".into());
142 }
143
144 if let Some(label) = &mut descriptor.label {
145 *label = format!("forward_decal_{label}").into();
146 }
147
148 Ok(())
149 }
150}
151
152impl Default for ForwardDecalMaterialExt {
153 fn default() -> Self {
154 Self {
155 depth_fade_factor: 8.0,
156 }
157 }
158}
159
160#[derive(Resource)]
161struct ForwardDecalMesh(Handle<Mesh>);
162
163fn forward_decal_set_mesh(mut world: DeferredWorld, HookContext { entity, .. }: HookContext) {
168 let decal_mesh = world.resource::<ForwardDecalMesh>().0.clone();
169 let mut entity = world.entity_mut(entity);
170 let mut entity_mesh = entity.get_mut::<Mesh3d>().unwrap();
171 if **entity_mesh == Handle::default() {
173 entity_mesh.0 = decal_mesh;
174 }
175}