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}