fyrox_impl/scene/decal.rs
1// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in all
11// copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19// SOFTWARE.
20
21//! Decal is an image that gets projected to a geometry of a scene.
22//!
23//! For more info see [`Decal`]
24
25use crate::{
26 core::{
27 color::Color,
28 math::aabb::AxisAlignedBoundingBox,
29 pool::Handle,
30 reflect::prelude::*,
31 type_traits::prelude::*,
32 uuid::{uuid, Uuid},
33 variable::InheritableVariable,
34 visitor::prelude::*,
35 },
36 resource::texture::TextureResource,
37 scene::node::constructor::NodeConstructor,
38 scene::{
39 base::{Base, BaseBuilder},
40 graph::Graph,
41 node::{Node, NodeTrait},
42 },
43};
44
45use fyrox_graph::constructor::ConstructorProvider;
46use fyrox_graph::SceneGraph;
47use std::ops::{Deref, DerefMut};
48
49/// Decal is an image that gets projected to a geometry of a scene. Blood splatters, bullet holes, scratches
50/// etc. are done via decals.
51///
52/// # Size and transformations
53///
54/// A decal defines a cube that projects a texture on every pixel of a scene that got into the cube. Exact cube
55/// size is defines by decal's `local scale`. For example, if you have a decal with scale (1.0, 2.0, 0.1) then
56/// the size of the cube (in local coordinates) will be `width = 1.0`, `height = 2.0`, `depth = 0.1`. The decal
57/// can be rotated as any other scene node. Its final size and orientation is defined by the chain of
58/// transformations of parent nodes.
59///
60/// # Masking
61///
62/// Often you need to ensure that decal will be applied only on desired surfaces. For example a crack on the wall
63/// should not affect any surrounding objects, this can be achieved by using decal mask. Each decal has layer index,
64/// it will be drawn only if the index matches the index of the object that inside of decal bounds.
65///
66/// # Supported maps
67///
68/// Currently, only diffuse and normal maps are supported. Diffuse and normal maps will be automatically projected
69/// on the data stored in G-Buffer.
70///
71/// # Limitations
72///
73/// Current implementation works only with Deferred render path. Custom materials that uses Forward pass should
74/// implement decals manually.
75///
76/// # Performance
77///
78/// It should be noted that decals are not cheap, keep amount (and size) of decals at reasonable values! This
79/// means that unused decals (bullet holes for example) must be removed after some time.
80///
81/// # Example
82///
83/// ```
84/// # use fyrox_impl::{
85/// # asset::manager::ResourceManager,
86/// # core::pool::Handle,
87/// # scene::{
88/// # node::Node,
89/// # graph::Graph,
90/// # decal::DecalBuilder,
91/// # base::BaseBuilder,
92/// # transform::TransformBuilder
93/// # },
94/// # core::algebra::Vector3
95/// # };
96/// # use fyrox_impl::resource::texture::Texture;
97/// # use fyrox_impl::scene::decal::Decal;
98///
99/// fn create_bullet_hole(resource_manager: ResourceManager, graph: &mut Graph) -> Handle<Decal> {
100/// DecalBuilder::new(
101/// BaseBuilder::new()
102/// .with_local_transform(
103/// TransformBuilder::new()
104/// .with_local_scale(Vector3::new(2.0, 2.0, 2.0))
105/// .build()
106/// ))
107/// .with_diffuse_texture(resource_manager.request::<Texture>("bullet_hole.png"))
108/// .build(graph)
109/// }
110/// ```
111#[derive(Debug, Visit, Default, Clone, Reflect, ComponentProvider)]
112#[reflect(derived_type = "Node")]
113pub struct Decal {
114 base: Base,
115
116 #[reflect(setter = "set_diffuse_texture")]
117 diffuse_texture: InheritableVariable<Option<TextureResource>>,
118
119 #[reflect(setter = "set_normal_texture")]
120 normal_texture: InheritableVariable<Option<TextureResource>>,
121
122 #[reflect(setter = "set_color")]
123 color: InheritableVariable<Color>,
124
125 #[reflect(min_value = 0.0)]
126 #[reflect(setter = "set_layer")]
127 layer: InheritableVariable<u8>,
128}
129
130impl Deref for Decal {
131 type Target = Base;
132
133 fn deref(&self) -> &Self::Target {
134 &self.base
135 }
136}
137
138impl DerefMut for Decal {
139 fn deref_mut(&mut self) -> &mut Self::Target {
140 &mut self.base
141 }
142}
143
144impl TypeUuidProvider for Decal {
145 fn type_uuid() -> Uuid {
146 uuid!("c4d24e48-edd1-4fb2-ad82-4b3d3ea985d8")
147 }
148}
149
150impl Decal {
151 /// Sets new diffuse texture.
152 pub fn set_diffuse_texture(
153 &mut self,
154 diffuse_texture: Option<TextureResource>,
155 ) -> Option<TextureResource> {
156 std::mem::replace(
157 self.diffuse_texture.get_value_mut_and_mark_modified(),
158 diffuse_texture,
159 )
160 }
161
162 /// Returns current diffuse texture.
163 pub fn diffuse_texture(&self) -> Option<&TextureResource> {
164 self.diffuse_texture.as_ref()
165 }
166
167 /// Returns current diffuse texture.
168 pub fn diffuse_texture_value(&self) -> Option<TextureResource> {
169 (*self.diffuse_texture).clone()
170 }
171
172 /// Sets new normal texture.
173 pub fn set_normal_texture(
174 &mut self,
175 normal_texture: Option<TextureResource>,
176 ) -> Option<TextureResource> {
177 std::mem::replace(
178 self.normal_texture.get_value_mut_and_mark_modified(),
179 normal_texture,
180 )
181 }
182
183 /// Returns current normal texture.
184 pub fn normal_texture(&self) -> Option<&TextureResource> {
185 self.normal_texture.as_ref()
186 }
187
188 /// Returns current normal texture.
189 pub fn normal_texture_value(&self) -> Option<TextureResource> {
190 (*self.normal_texture).clone()
191 }
192
193 /// Sets new color for the decal.
194 pub fn set_color(&mut self, color: Color) -> Color {
195 self.color.set_value_and_mark_modified(color)
196 }
197
198 /// Returns current color of the decal.
199 pub fn color(&self) -> Color {
200 *self.color
201 }
202
203 /// Sets layer index of the decal. Layer index allows you to apply decals only on desired
204 /// surfaces. For example, static geometry could have `index == 0` and dynamic `index == 1`.
205 /// To "filter" decals all you need to do is to set appropriate layer index to decal, for
206 /// example blood splatter decal will have `index == 0` in this case. In case of dynamic
207 /// objects (like bots, etc.) index will be 1.
208 pub fn set_layer(&mut self, layer: u8) -> u8 {
209 self.layer.set_value_and_mark_modified(layer)
210 }
211
212 /// Returns current layer index.
213 pub fn layer(&self) -> u8 {
214 *self.layer
215 }
216}
217
218impl ConstructorProvider<Node, Graph> for Decal {
219 fn constructor() -> NodeConstructor {
220 NodeConstructor::new::<Self>().with_variant("Decal", |_| {
221 DecalBuilder::new(BaseBuilder::new().with_name("Decal"))
222 .build_node()
223 .into()
224 })
225 }
226}
227
228impl NodeTrait for Decal {
229 /// Returns current **local-space** bounding box.
230 #[inline]
231 fn local_bounding_box(&self) -> AxisAlignedBoundingBox {
232 // TODO: Maybe calculate AABB using frustum corners?
233 self.base.local_bounding_box()
234 }
235
236 /// Returns current **world-space** bounding box.
237 fn world_bounding_box(&self) -> AxisAlignedBoundingBox {
238 self.base.world_bounding_box()
239 }
240
241 fn id(&self) -> Uuid {
242 Self::type_uuid()
243 }
244}
245
246/// Allows you to create a Decal in a declarative manner.
247pub struct DecalBuilder {
248 base_builder: BaseBuilder,
249 diffuse_texture: Option<TextureResource>,
250 normal_texture: Option<TextureResource>,
251 color: Color,
252 layer: u8,
253}
254
255impl DecalBuilder {
256 /// Creates a new instance of the builder.
257 pub fn new(base_builder: BaseBuilder) -> Self {
258 Self {
259 base_builder,
260 diffuse_texture: None,
261 normal_texture: None,
262 color: Color::opaque(255, 255, 255),
263 layer: 0,
264 }
265 }
266
267 /// Sets desired diffuse texture.
268 pub fn with_diffuse_texture(mut self, diffuse_texture: TextureResource) -> Self {
269 self.diffuse_texture = Some(diffuse_texture);
270 self
271 }
272
273 /// Sets desired normal texture.
274 pub fn with_normal_texture(mut self, normal_texture: TextureResource) -> Self {
275 self.normal_texture = Some(normal_texture);
276 self
277 }
278
279 /// Sets desired decal color.
280 pub fn with_color(mut self, color: Color) -> Self {
281 self.color = color;
282 self
283 }
284
285 /// Sets desired layer index.
286 pub fn with_layer(mut self, layer: u8) -> Self {
287 self.layer = layer;
288 self
289 }
290
291 /// Creates new Decal node.
292 pub fn build_decal(self) -> Decal {
293 Decal {
294 base: self.base_builder.build_base(),
295 diffuse_texture: self.diffuse_texture.into(),
296 normal_texture: self.normal_texture.into(),
297 color: self.color.into(),
298 layer: self.layer.into(),
299 }
300 }
301
302 /// Creates new Decal node.
303 pub fn build_node(self) -> Node {
304 Node::new(self.build_decal())
305 }
306
307 /// Creates new instance of Decal node and puts it in the given graph.
308 pub fn build(self, graph: &mut Graph) -> Handle<Decal> {
309 graph.add_node(self.build_node()).to_variant()
310 }
311}