Skip to main content

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}