Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_pbr/src/decal/forward.rs
9418 views
1
use crate::{
2
ExtendedMaterial, Material, MaterialExtension, MaterialExtensionKey, MaterialExtensionPipeline,
3
MaterialPlugin, StandardMaterial,
4
};
5
use bevy_app::{App, Plugin};
6
use bevy_asset::{Asset, Assets, Handle};
7
use bevy_ecs::{
8
component::Component, lifecycle::HookContext, resource::Resource, world::DeferredWorld,
9
};
10
use bevy_material::AlphaMode;
11
use bevy_math::{prelude::Rectangle, Quat, Vec2, Vec3};
12
use bevy_mesh::{Mesh, Mesh3d, MeshBuilder, MeshVertexBufferLayoutRef, Meshable};
13
use bevy_reflect::{Reflect, TypePath};
14
use bevy_render::{
15
render_asset::RenderAssets,
16
render_resource::{
17
AsBindGroup, AsBindGroupShaderType, CompareFunction, RenderPipelineDescriptor, ShaderType,
18
SpecializedMeshPipelineError,
19
},
20
texture::GpuImage,
21
RenderDebugFlags,
22
};
23
use bevy_shader::load_shader_library;
24
25
/// Plugin to render [`ForwardDecal`]s.
26
pub struct ForwardDecalPlugin;
27
28
impl Plugin for ForwardDecalPlugin {
29
fn build(&self, app: &mut App) {
30
load_shader_library!(app, "forward_decal.wgsl");
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
/// A decal that renders via a 1x1 transparent quad mesh, smoothly alpha-blending with the underlying
51
/// geometry towards the edges.
52
///
53
/// Because forward decals are meshes, you can use arbitrary materials to control their appearance.
54
///
55
/// # Usage Notes
56
///
57
/// * Spawn this component on an entity with a [`crate::MeshMaterial3d`] component holding a [`ForwardDecalMaterial`].
58
/// * Any camera rendering a forward decal must have the [`bevy_core_pipeline::prepass::DepthPrepass`] component.
59
/// * Looking at forward decals at a steep angle can cause distortion. This can be mitigated by padding your decal's
60
/// texture with extra transparent pixels on the edges.
61
/// * On Wasm, requires using WebGPU and disabling `Msaa` on your camera.
62
#[derive(Component, Reflect)]
63
#[require(Mesh3d)]
64
#[component(on_add=forward_decal_set_mesh)]
65
pub struct ForwardDecal;
66
67
/// Type alias for an extended material with a [`ForwardDecalMaterialExt`] extension.
68
///
69
/// Make sure to register the [`MaterialPlugin`] for this material in your app setup.
70
///
71
/// [`StandardMaterial`] comes with out of the box support for forward decals.
72
#[expect(type_alias_bounds, reason = "Type alias generics not yet stable")]
73
pub type ForwardDecalMaterial<B: Material> = ExtendedMaterial<B, ForwardDecalMaterialExt>;
74
75
/// Material extension for a [`ForwardDecal`].
76
///
77
/// In addition to wrapping your material type with this extension, your shader must use
78
/// the `bevy_pbr::decal::forward::get_forward_decal_info` function.
79
///
80
/// The `FORWARD_DECAL` shader define will be made available to your shader so that you can gate
81
/// the forward decal code behind an ifdef.
82
#[derive(Asset, AsBindGroup, TypePath, Clone, Debug)]
83
#[uniform(200, ForwardDecalMaterialExtUniform)]
84
pub struct ForwardDecalMaterialExt {
85
/// Controls the distance threshold for decal blending with surfaces.
86
///
87
/// This parameter determines how far away a surface can be before the decal no longer blends
88
/// with it and instead renders with full opacity.
89
///
90
/// Lower values cause the decal to only blend with close surfaces, while higher values allow
91
/// blending with more distant surfaces.
92
///
93
/// Units are in meters.
94
pub depth_fade_factor: f32,
95
}
96
97
#[derive(Clone, Default, ShaderType)]
98
pub struct ForwardDecalMaterialExtUniform {
99
pub inv_depth_fade_factor: f32,
100
}
101
102
impl AsBindGroupShaderType<ForwardDecalMaterialExtUniform> for ForwardDecalMaterialExt {
103
fn as_bind_group_shader_type(
104
&self,
105
_images: &RenderAssets<GpuImage>,
106
) -> ForwardDecalMaterialExtUniform {
107
ForwardDecalMaterialExtUniform {
108
inv_depth_fade_factor: 1.0 / self.depth_fade_factor.max(0.001),
109
}
110
}
111
}
112
113
impl MaterialExtension for ForwardDecalMaterialExt {
114
fn alpha_mode() -> Option<AlphaMode> {
115
Some(AlphaMode::Blend)
116
}
117
118
fn enable_shadows() -> bool {
119
false
120
}
121
122
fn specialize(
123
_pipeline: &MaterialExtensionPipeline,
124
descriptor: &mut RenderPipelineDescriptor,
125
_layout: &MeshVertexBufferLayoutRef,
126
_key: MaterialExtensionKey<Self>,
127
) -> Result<(), SpecializedMeshPipelineError> {
128
descriptor.depth_stencil.as_mut().unwrap().depth_compare = CompareFunction::Always;
129
130
descriptor.vertex.shader_defs.push("FORWARD_DECAL".into());
131
132
if let Some(fragment) = &mut descriptor.fragment {
133
fragment.shader_defs.push("FORWARD_DECAL".into());
134
}
135
136
if let Some(label) = &mut descriptor.label {
137
*label = format!("forward_decal_{label}").into();
138
}
139
140
Ok(())
141
}
142
}
143
144
impl Default for ForwardDecalMaterialExt {
145
fn default() -> Self {
146
Self {
147
depth_fade_factor: 8.0,
148
}
149
}
150
}
151
152
#[derive(Resource)]
153
struct ForwardDecalMesh(Handle<Mesh>);
154
155
// Note: We need to use a hook here instead of required components since we cannot access resources
156
// with required components, and we can't otherwise get a handle to the asset from a required
157
// component constructor, since the constructor must be a function pointer, and we intentionally do
158
// not want to use `uuid_handle!`.
159
fn forward_decal_set_mesh(mut world: DeferredWorld, HookContext { entity, .. }: HookContext) {
160
let decal_mesh = world.resource::<ForwardDecalMesh>().0.clone();
161
let mut entity = world.entity_mut(entity);
162
let mut entity_mesh = entity.get_mut::<Mesh3d>().unwrap();
163
// Only replace the mesh handle if the mesh handle is defaulted.
164
if **entity_mesh == Handle::default() {
165
entity_mesh.0 = decal_mesh;
166
}
167
}
168
169