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::BaseSceneGraph;
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///
98/// fn create_bullet_hole(resource_manager: ResourceManager, graph: &mut Graph) -> Handle<Node> {
99/// DecalBuilder::new(
100/// BaseBuilder::new()
101/// .with_local_transform(
102/// TransformBuilder::new()
103/// .with_local_scale(Vector3::new(2.0, 2.0, 2.0))
104/// .build()
105/// ))
106/// .with_diffuse_texture(resource_manager.request::<Texture>("bullet_hole.png"))
107/// .build(graph)
108/// }
109/// ```
110#[derive(Debug, Visit, Default, Clone, Reflect, ComponentProvider)]
111#[reflect(derived_type = "Node")]
112pub struct Decal {
113 base: Base,
114
115 #[reflect(setter = "set_diffuse_texture")]
116 diffuse_texture: InheritableVariable<Option<TextureResource>>,
117
118 #[reflect(setter = "set_normal_texture")]
119 normal_texture: InheritableVariable<Option<TextureResource>>,
120
121 #[reflect(setter = "set_color")]
122 color: InheritableVariable<Color>,
123
124 #[reflect(min_value = 0.0)]
125 #[reflect(setter = "set_layer")]
126 layer: InheritableVariable<u8>,
127}
128
129impl Deref for Decal {
130 type Target = Base;
131
132 fn deref(&self) -> &Self::Target {
133 &self.base
134 }
135}
136
137impl DerefMut for Decal {
138 fn deref_mut(&mut self) -> &mut Self::Target {
139 &mut self.base
140 }
141}
142
143impl TypeUuidProvider for Decal {
144 fn type_uuid() -> Uuid {
145 uuid!("c4d24e48-edd1-4fb2-ad82-4b3d3ea985d8")
146 }
147}
148
149impl Decal {
150 /// Sets new diffuse texture.
151 pub fn set_diffuse_texture(
152 &mut self,
153 diffuse_texture: Option<TextureResource>,
154 ) -> Option<TextureResource> {
155 std::mem::replace(
156 self.diffuse_texture.get_value_mut_and_mark_modified(),
157 diffuse_texture,
158 )
159 }
160
161 /// Returns current diffuse texture.
162 pub fn diffuse_texture(&self) -> Option<&TextureResource> {
163 self.diffuse_texture.as_ref()
164 }
165
166 /// Returns current diffuse texture.
167 pub fn diffuse_texture_value(&self) -> Option<TextureResource> {
168 (*self.diffuse_texture).clone()
169 }
170
171 /// Sets new normal texture.
172 pub fn set_normal_texture(
173 &mut self,
174 normal_texture: Option<TextureResource>,
175 ) -> Option<TextureResource> {
176 std::mem::replace(
177 self.normal_texture.get_value_mut_and_mark_modified(),
178 normal_texture,
179 )
180 }
181
182 /// Returns current normal texture.
183 pub fn normal_texture(&self) -> Option<&TextureResource> {
184 self.normal_texture.as_ref()
185 }
186
187 /// Returns current normal texture.
188 pub fn normal_texture_value(&self) -> Option<TextureResource> {
189 (*self.normal_texture).clone()
190 }
191
192 /// Sets new color for the decal.
193 pub fn set_color(&mut self, color: Color) -> Color {
194 self.color.set_value_and_mark_modified(color)
195 }
196
197 /// Returns current color of the decal.
198 pub fn color(&self) -> Color {
199 *self.color
200 }
201
202 /// Sets layer index of the decal. Layer index allows you to apply decals only on desired
203 /// surfaces. For example, static geometry could have `index == 0` and dynamic `index == 1`.
204 /// To "filter" decals all you need to do is to set appropriate layer index to decal, for
205 /// example blood splatter decal will have `index == 0` in this case. In case of dynamic
206 /// objects (like bots, etc.) index will be 1.
207 pub fn set_layer(&mut self, layer: u8) -> u8 {
208 self.layer.set_value_and_mark_modified(layer)
209 }
210
211 /// Returns current layer index.
212 pub fn layer(&self) -> u8 {
213 *self.layer
214 }
215}
216
217impl ConstructorProvider<Node, Graph> for Decal {
218 fn constructor() -> NodeConstructor {
219 NodeConstructor::new::<Self>().with_variant("Decal", |_| {
220 DecalBuilder::new(BaseBuilder::new().with_name("Decal"))
221 .build_node()
222 .into()
223 })
224 }
225}
226
227impl NodeTrait for Decal {
228 /// Returns current **local-space** bounding box.
229 #[inline]
230 fn local_bounding_box(&self) -> AxisAlignedBoundingBox {
231 // TODO: Maybe calculate AABB using frustum corners?
232 self.base.local_bounding_box()
233 }
234
235 /// Returns current **world-space** bounding box.
236 fn world_bounding_box(&self) -> AxisAlignedBoundingBox {
237 self.base.world_bounding_box()
238 }
239
240 fn id(&self) -> Uuid {
241 Self::type_uuid()
242 }
243}
244
245/// Allows you to create a Decal in a declarative manner.
246pub struct DecalBuilder {
247 base_builder: BaseBuilder,
248 diffuse_texture: Option<TextureResource>,
249 normal_texture: Option<TextureResource>,
250 color: Color,
251 layer: u8,
252}
253
254impl DecalBuilder {
255 /// Creates a new instance of the builder.
256 pub fn new(base_builder: BaseBuilder) -> Self {
257 Self {
258 base_builder,
259 diffuse_texture: None,
260 normal_texture: None,
261 color: Color::opaque(255, 255, 255),
262 layer: 0,
263 }
264 }
265
266 /// Sets desired diffuse texture.
267 pub fn with_diffuse_texture(mut self, diffuse_texture: TextureResource) -> Self {
268 self.diffuse_texture = Some(diffuse_texture);
269 self
270 }
271
272 /// Sets desired normal texture.
273 pub fn with_normal_texture(mut self, normal_texture: TextureResource) -> Self {
274 self.normal_texture = Some(normal_texture);
275 self
276 }
277
278 /// Sets desired decal color.
279 pub fn with_color(mut self, color: Color) -> Self {
280 self.color = color;
281 self
282 }
283
284 /// Sets desired layer index.
285 pub fn with_layer(mut self, layer: u8) -> Self {
286 self.layer = layer;
287 self
288 }
289
290 /// Creates new Decal node.
291 pub fn build_decal(self) -> Decal {
292 Decal {
293 base: self.base_builder.build_base(),
294 diffuse_texture: self.diffuse_texture.into(),
295 normal_texture: self.normal_texture.into(),
296 color: self.color.into(),
297 layer: self.layer.into(),
298 }
299 }
300
301 /// Creates new Decal node.
302 pub fn build_node(self) -> Node {
303 Node::new(self.build_decal())
304 }
305
306 /// Creates new instance of Decal node and puts it in the given graph.
307 pub fn build(self, graph: &mut Graph) -> Handle<Node> {
308 graph.add_node(self.build_node())
309 }
310}