fyrox_impl/scene/decal.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278
//! Decal is an image that gets projected to a geometry of a scene.
//!
//! For more info see [`Decal`]
use crate::{
core::{
color::Color,
math::aabb::AxisAlignedBoundingBox,
pool::Handle,
reflect::prelude::*,
uuid::{uuid, Uuid},
variable::InheritableVariable,
visitor::prelude::*,
TypeUuidProvider,
},
resource::texture::TextureResource,
scene::{
base::{Base, BaseBuilder},
graph::Graph,
node::{Node, NodeTrait},
},
};
use fyrox_graph::BaseSceneGraph;
use std::ops::{Deref, DerefMut};
/// Decal is an image that gets projected to a geometry of a scene. Blood splatters, bullet holes, scratches
/// etc. are done via decals.
///
/// # Size and transformations
///
/// A decal defines a cube that projects a texture on every pixel of a scene that got into the cube. Exact cube
/// size is defines by decal's `local scale`. For example, if you have a decal with scale (1.0, 2.0, 0.1) then
/// the size of the cube (in local coordinates) will be `width = 1.0`, `height = 2.0`, `depth = 0.1`. The decal
/// can be rotated as any other scene node. Its final size and orientation is defined by the chain of
/// transformations of parent nodes.
///
/// # Masking
///
/// Often you need to ensure that decal will be applied only on desired surfaces. For example a crack on the wall
/// should not affect any surrounding objects, this can be achieved by using decal mask. Each decal has layer index,
/// it will be drawn only if the index matches the index of the object that inside of decal bounds.
///
/// # Supported maps
///
/// Currently, only diffuse and normal maps are supported. Diffuse and normal maps will be automatically projected
/// on the data stored in G-Buffer.
///
/// # Limitations
///
/// Current implementation works only with Deferred render path. Custom materials that uses Forward pass should
/// implement decals manually.
///
/// # Performance
///
/// It should be noted that decals are not cheap, keep amount (and size) of decals at reasonable values! This
/// means that unused decals (bullet holes for example) must be removed after some time.
///
/// # Example
///
/// ```
/// # use fyrox_impl::{
/// # asset::manager::ResourceManager,
/// # core::pool::Handle,
/// # scene::{
/// # node::Node,
/// # graph::Graph,
/// # decal::DecalBuilder,
/// # base::BaseBuilder,
/// # transform::TransformBuilder
/// # },
/// # core::algebra::Vector3
/// # };
/// # use fyrox_impl::resource::texture::Texture;
///
/// fn create_bullet_hole(resource_manager: ResourceManager, graph: &mut Graph) -> Handle<Node> {
/// DecalBuilder::new(
/// BaseBuilder::new()
/// .with_local_transform(
/// TransformBuilder::new()
/// .with_local_scale(Vector3::new(2.0, 2.0, 2.0))
/// .build()
/// ))
/// .with_diffuse_texture(resource_manager.request::<Texture>("bullet_hole.png"))
/// .build(graph)
/// }
/// ```
#[derive(Debug, Visit, Default, Clone, Reflect)]
pub struct Decal {
base: Base,
#[reflect(setter = "set_diffuse_texture")]
diffuse_texture: InheritableVariable<Option<TextureResource>>,
#[reflect(setter = "set_normal_texture")]
normal_texture: InheritableVariable<Option<TextureResource>>,
#[reflect(setter = "set_color")]
color: InheritableVariable<Color>,
#[reflect(min_value = 0.0)]
#[reflect(setter = "set_layer")]
layer: InheritableVariable<u8>,
}
impl Deref for Decal {
type Target = Base;
fn deref(&self) -> &Self::Target {
&self.base
}
}
impl DerefMut for Decal {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.base
}
}
impl TypeUuidProvider for Decal {
fn type_uuid() -> Uuid {
uuid!("c4d24e48-edd1-4fb2-ad82-4b3d3ea985d8")
}
}
impl Decal {
/// Sets new diffuse texture.
pub fn set_diffuse_texture(
&mut self,
diffuse_texture: Option<TextureResource>,
) -> Option<TextureResource> {
std::mem::replace(
self.diffuse_texture.get_value_mut_and_mark_modified(),
diffuse_texture,
)
}
/// Returns current diffuse texture.
pub fn diffuse_texture(&self) -> Option<&TextureResource> {
self.diffuse_texture.as_ref()
}
/// Returns current diffuse texture.
pub fn diffuse_texture_value(&self) -> Option<TextureResource> {
(*self.diffuse_texture).clone()
}
/// Sets new normal texture.
pub fn set_normal_texture(
&mut self,
normal_texture: Option<TextureResource>,
) -> Option<TextureResource> {
std::mem::replace(
self.normal_texture.get_value_mut_and_mark_modified(),
normal_texture,
)
}
/// Returns current normal texture.
pub fn normal_texture(&self) -> Option<&TextureResource> {
self.normal_texture.as_ref()
}
/// Returns current normal texture.
pub fn normal_texture_value(&self) -> Option<TextureResource> {
(*self.normal_texture).clone()
}
/// Sets new color for the decal.
pub fn set_color(&mut self, color: Color) -> Color {
self.color.set_value_and_mark_modified(color)
}
/// Returns current color of the decal.
pub fn color(&self) -> Color {
*self.color
}
/// Sets layer index of the decal. Layer index allows you to apply decals only on desired
/// surfaces. For example, static geometry could have `index == 0` and dynamic `index == 1`.
/// To "filter" decals all you need to do is to set appropriate layer index to decal, for
/// example blood splatter decal will have `index == 0` in this case. In case of dynamic
/// objects (like bots, etc.) index will be 1.
pub fn set_layer(&mut self, layer: u8) -> u8 {
self.layer.set_value_and_mark_modified(layer)
}
/// Returns current layer index.
pub fn layer(&self) -> u8 {
*self.layer
}
}
impl NodeTrait for Decal {
crate::impl_query_component!();
/// Returns current **local-space** bounding box.
#[inline]
fn local_bounding_box(&self) -> AxisAlignedBoundingBox {
// TODO: Maybe calculate AABB using frustum corners?
self.base.local_bounding_box()
}
/// Returns current **world-space** bounding box.
fn world_bounding_box(&self) -> AxisAlignedBoundingBox {
self.base.world_bounding_box()
}
fn id(&self) -> Uuid {
Self::type_uuid()
}
}
/// Allows you to create a Decal in a declarative manner.
pub struct DecalBuilder {
base_builder: BaseBuilder,
diffuse_texture: Option<TextureResource>,
normal_texture: Option<TextureResource>,
color: Color,
layer: u8,
}
impl DecalBuilder {
/// Creates a new instance of the builder.
pub fn new(base_builder: BaseBuilder) -> Self {
Self {
base_builder,
diffuse_texture: None,
normal_texture: None,
color: Color::opaque(255, 255, 255),
layer: 0,
}
}
/// Sets desired diffuse texture.
pub fn with_diffuse_texture(mut self, diffuse_texture: TextureResource) -> Self {
self.diffuse_texture = Some(diffuse_texture);
self
}
/// Sets desired normal texture.
pub fn with_normal_texture(mut self, normal_texture: TextureResource) -> Self {
self.normal_texture = Some(normal_texture);
self
}
/// Sets desired decal color.
pub fn with_color(mut self, color: Color) -> Self {
self.color = color;
self
}
/// Sets desired layer index.
pub fn with_layer(mut self, layer: u8) -> Self {
self.layer = layer;
self
}
/// Creates new Decal node.
pub fn build_decal(self) -> Decal {
Decal {
base: self.base_builder.build_base(),
diffuse_texture: self.diffuse_texture.into(),
normal_texture: self.normal_texture.into(),
color: self.color.into(),
layer: self.layer.into(),
}
}
/// Creates new Decal node.
pub fn build_node(self) -> Node {
Node::new(self.build_decal())
}
/// Creates new instance of Decal node and puts it in the given graph.
pub fn build(self, graph: &mut Graph) -> Handle<Node> {
graph.add_node(self.build_node())
}
}