Skip to main content

fyrox_impl/scene/
probe.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//! Reflection probe is an object that allows "capturing" a scene content in a cube texture, that
22//! can later be used to render reflections and be used as a source of ambient lighting for a scene.
23//! See [`ReflectionProbe`] docs for more info.
24
25use crate::{
26    core::{
27        algebra::Vector3,
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    graph::{constructor::ConstructorProvider, SceneGraph},
37    scene::EnvironmentLightingSource,
38    scene::{
39        base::{Base, BaseBuilder},
40        graph::Graph,
41        node::{constructor::NodeConstructor, Node, NodeTrait, UpdateContext},
42    },
43};
44use fyrox_core::color::Color;
45use fyrox_texture::{TextureResource, TextureResourceExtension};
46use std::{
47    cell::Cell,
48    ops::{Deref, DerefMut},
49};
50use strum_macros::{AsRefStr, EnumString, VariantNames};
51
52const DEFAULT_RESOLUTION: usize = 512;
53
54/// Update mode of reflection probes.
55#[derive(
56    Clone,
57    Reflect,
58    PartialEq,
59    Default,
60    Debug,
61    Visit,
62    TypeUuidProvider,
63    AsRefStr,
64    EnumString,
65    VariantNames,
66)]
67#[type_uuid(id = "66450303-4f6c-4456-bde5-a7309f25b7ce")]
68pub enum UpdateMode {
69    /// The probe will be updated once it is created and its content won't change until the
70    /// [`ReflectionProbe::force_update`] call.
71    #[default]
72    Once,
73
74    /// The probe will be updated each frame. This option may lead to performance issues and
75    /// should be used with caution.
76    EachFrame,
77}
78
79/// Reflection probe is an object that allows "capturing" a scene content in a cube texture, that
80/// can later be used to render reflections and be used as a source of ambient lighting for a scene.
81///
82/// ## Update Mode
83///
84/// Reflection probe can be updated either once or every frame. The default mode is [`UpdateMode::Once`].
85/// If you need dynamic reflections, then use [`UpdateMode::EachFrame`] mode. However, it may lead
86/// to performance issues.
87///
88/// ## Performance
89///
90/// Reflection probe renders the scene six times which is quite slow. In most cases, it does not matter
91/// because most of the probes can be updated just once (static probes). Such probes can have
92/// increased resolution.
93///
94/// Dynamic probes are the heaviest and require careful performance tweaking. There should be a balance
95/// between the resolution and the speed. Reflection probes does frustum culling, so some part of
96/// the scene geometry can be excluded. This functionality can be tweaked by setting the far clipping
97/// plane distance to lower values to prevent the probe to render distant objects.
98///
99/// ## Interaction With Cameras
100///
101/// When rendering, the engine will automatically pick a reflection probe for a camera. It is done
102/// by a simple point-box intersection test. This reflection probe will then be used for rendering
103/// using the camera.
104///
105/// ## Example
106///
107/// The following example creates a new reflection probe 20 units wide in all directions, centered
108/// at (0.0, 10.0, 0.0) point with a rendering position offset by 10 units along X axis.
109///
110/// ```rust
111/// # use fyrox_impl::{
112/// #     core::{algebra::Vector3, pool::Handle},
113/// #     scene::{
114/// #         base::BaseBuilder,
115/// #         graph::Graph,
116/// #         node::Node,
117/// #         probe::{ReflectionProbeBuilder, UpdateMode},
118/// #         transform::TransformBuilder,
119/// #     },
120/// # };
121/// # use fyrox_impl::scene::probe::ReflectionProbe;
122/// fn create_probe(graph: &mut Graph) -> Handle<ReflectionProbe> {
123///     ReflectionProbeBuilder::new(
124///         BaseBuilder::new().with_local_transform(
125///             TransformBuilder::new()
126///                 // The center of the probe's bounding box is located 10 units above the ground.
127///                 .with_local_position(Vector3::new(0.0, 10.0, 0.0))
128///                 // The size of the probe's bounding box is 20 units.
129///                 .with_local_scale(Vector3::repeat(20.0))
130///                 .build(),
131///         ),
132///     )
133///     // Set resolution of the probe.
134///     .with_resolution(256)
135///     // The probe will capture the scene once it is created.
136///     .with_update_mode(UpdateMode::Once)
137///     // Set the capture point slightly off-center. The probe will capture the scene at
138///     // (10.0, 10.0, 0.0) point.
139///     .with_rendering_local_position(Vector3::new(10.0, 0.0, 0.0))
140///     .build(graph)
141/// }
142/// ```
143#[derive(Clone, Reflect, Debug, Visit, ComponentProvider, TypeUuidProvider)]
144#[type_uuid(id = "7e0c138f-e371-4045-bd2c-ff5b165c7ee6")]
145#[reflect(derived_type = "Node")]
146#[visit(optional, post_visit_method = "on_visited")]
147pub struct ReflectionProbe {
148    base: Base,
149
150    /// Defines rendering position in local coordinate space of the probe. The scene will be captured
151    /// from the point ignoring the orientation of the probe.
152    pub rendering_position: InheritableVariable<Vector3<f32>>,
153
154    /// Resolution of the probe. It defines the size of the cube map face and thus the overall
155    /// quality of the image. The larger the value, the more detailed reflections will be. Large
156    /// values may slow down rendering of the probe.
157    #[reflect(max_value = 2048.0, min_value = 16.0, setter = "set_resolution")]
158    pub resolution: InheritableVariable<usize>,
159
160    /// Position of the near clipping plane.
161    #[reflect(min_value = 0.0)]
162    pub z_near: InheritableVariable<f32>,
163
164    /// Position of the far clipping plane. This parameter can be decreased to improve performance.
165    #[reflect(min_value = 0.0)]
166    pub z_far: InheritableVariable<f32>,
167
168    /// Update mode of the probe. See [`UpdateMode`] docs for more info.
169    pub update_mode: InheritableVariable<UpdateMode>,
170
171    /// Ambient lighting of the reflection probe. This value is used only if the `environment` is
172    /// set to [`EnvironmentLightingSource::AmbientColor`].
173    pub ambient_lighting_color: InheritableVariable<Color>,
174
175    /// Environment lighting source of the reflection probe.
176    pub environment_lighting_source: InheritableVariable<EnvironmentLightingSource>,
177
178    /// A flag, that defines whether the probe should be updated or not.
179    #[reflect(hidden)]
180    #[visit(skip)]
181    pub need_update: bool,
182
183    #[reflect(hidden)]
184    #[visit(skip)]
185    pub(crate) updated: Cell<bool>,
186    #[reflect(hidden)]
187    render_target: TextureResource,
188}
189
190impl Default for ReflectionProbe {
191    fn default() -> Self {
192        Self {
193            base: Default::default(),
194            rendering_position: Default::default(),
195            resolution: DEFAULT_RESOLUTION.into(),
196            z_near: 0.001.into(),
197            z_far: 128.0.into(),
198            update_mode: Default::default(),
199            ambient_lighting_color: Color::repeat_opaque(120).into(),
200            environment_lighting_source: Default::default(),
201            need_update: true,
202            updated: Cell::new(false),
203            render_target: TextureResource::new_cube_render_target(DEFAULT_RESOLUTION as u32),
204        }
205    }
206}
207
208impl ReflectionProbe {
209    /// Sets the desired resolution of the reflection probe.
210    pub fn set_resolution(&mut self, resolution: usize) -> usize {
211        let old = self.resolution.set_value_and_mark_modified(resolution);
212        self.recreate_render_target();
213        old
214    }
215
216    /// Returns current resolution of the reflection probe.
217    pub fn resolution(&self) -> usize {
218        *self.resolution
219    }
220
221    /// Returns current render target of the reflection probe.
222    pub fn render_target(&self) -> &TextureResource {
223        &self.render_target
224    }
225
226    /// Schedules update of the reflection probe for the next frame.
227    pub fn force_update(&mut self) {
228        self.need_update = true;
229        self.updated.set(false);
230    }
231
232    /// Calculates position of the rendering point in global coordinates.
233    pub fn global_rendering_position(&self) -> Vector3<f32> {
234        self.global_position() + *self.rendering_position
235    }
236
237    fn recreate_render_target(&mut self) {
238        self.render_target = TextureResource::new_cube_render_target(*self.resolution as u32);
239        self.force_update();
240    }
241
242    fn on_visited(&mut self, visitor: &mut Visitor) {
243        if visitor.is_reading() {
244            self.recreate_render_target();
245        }
246    }
247}
248
249impl Deref for ReflectionProbe {
250    type Target = Base;
251
252    fn deref(&self) -> &Self::Target {
253        &self.base
254    }
255}
256
257impl DerefMut for ReflectionProbe {
258    fn deref_mut(&mut self) -> &mut Self::Target {
259        &mut self.base
260    }
261}
262
263impl ConstructorProvider<Node, Graph> for ReflectionProbe {
264    fn constructor() -> NodeConstructor {
265        NodeConstructor::new::<Self>()
266            .with_group("Light")
267            .with_variant("Reflection Probe", |_| {
268                ReflectionProbeBuilder::new(BaseBuilder::new().with_name("Reflection Probe"))
269                    .build_node()
270                    .into()
271            })
272    }
273}
274
275impl NodeTrait for ReflectionProbe {
276    fn local_bounding_box(&self) -> AxisAlignedBoundingBox {
277        self.base.local_bounding_box()
278    }
279
280    fn world_bounding_box(&self) -> AxisAlignedBoundingBox {
281        self.base.world_bounding_box()
282    }
283
284    fn id(&self) -> Uuid {
285        Self::type_uuid()
286    }
287
288    fn update(&mut self, _context: &mut UpdateContext) {
289        match *self.update_mode {
290            UpdateMode::Once => {
291                if self.need_update {
292                    self.updated.set(false);
293                    self.need_update = false;
294                }
295            }
296            UpdateMode::EachFrame => {
297                self.updated.set(false);
298            }
299        }
300    }
301}
302
303/// Allows you to create a reflection probe node declaratively.
304pub struct ReflectionProbeBuilder {
305    base_builder: BaseBuilder,
306    offset: Vector3<f32>,
307    z_near: f32,
308    z_far: f32,
309    resolution: usize,
310    update_mode: UpdateMode,
311    ambient_lighting_color: Color,
312    environment_lighting_source: EnvironmentLightingSource,
313}
314
315impl ReflectionProbeBuilder {
316    /// Creates a new reflection probe builder.
317    pub fn new(base_builder: BaseBuilder) -> Self {
318        Self {
319            base_builder,
320            offset: Default::default(),
321            z_near: 0.1,
322            z_far: 32.0,
323            resolution: DEFAULT_RESOLUTION,
324            update_mode: Default::default(),
325            ambient_lighting_color: Color::repeat_opaque(120),
326            environment_lighting_source: Default::default(),
327        }
328    }
329
330    /// Sets the desired offset of the reflection probe.
331    pub fn with_rendering_local_position(mut self, offset: Vector3<f32>) -> Self {
332        self.offset = offset;
333        self
334    }
335
336    /// Sets the desired position of the near clipping plane.
337    pub fn with_z_near(mut self, z_near: f32) -> Self {
338        self.z_near = z_near;
339        self
340    }
341
342    /// Sets the desired position of the far clipping plane.
343    pub fn with_z_far(mut self, z_far: f32) -> Self {
344        self.z_far = z_far;
345        self
346    }
347
348    /// Sets the desired resolution of the probe. See [`ReflectionProbe`] docs for more info.
349    pub fn with_resolution(mut self, resolution: usize) -> Self {
350        self.resolution = resolution;
351        self
352    }
353
354    /// Sets the desired update mode of the probe. See [`UpdateMode`] docs for more info.
355    pub fn with_update_mode(mut self, mode: UpdateMode) -> Self {
356        self.update_mode = mode;
357        self
358    }
359
360    /// Environment lighting source of the reflection probe.
361    pub fn with_environment(
362        mut self,
363        environment_lighting_source: EnvironmentLightingSource,
364    ) -> Self {
365        self.environment_lighting_source = environment_lighting_source;
366        self
367    }
368
369    /// Sets the ambient lighting color of the reflection probe.
370    pub fn with_ambient_lighting_color(mut self, ambient_lighting_color: Color) -> Self {
371        self.ambient_lighting_color = ambient_lighting_color;
372        self
373    }
374
375    /// Creates a new reflection probe node.
376    pub fn build_node(self) -> Node {
377        Node::new(ReflectionProbe {
378            base: self.base_builder.build_base(),
379            rendering_position: self.offset.into(),
380            resolution: self.resolution.into(),
381            z_near: self.z_near.into(),
382            z_far: self.z_far.into(),
383            update_mode: self.update_mode.into(),
384            ambient_lighting_color: self.ambient_lighting_color.into(),
385            environment_lighting_source: self.environment_lighting_source.into(),
386            need_update: true,
387            updated: Cell::new(false),
388            render_target: TextureResource::new_cube_render_target(self.resolution as u32),
389        })
390    }
391
392    /// Creates a new reflection probe node and adds it to the graph.
393    pub fn build(self, graph: &mut Graph) -> Handle<ReflectionProbe> {
394        graph.add_node(self.build_node()).to_variant()
395    }
396}