fyrox_impl/scene/light/
directional.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//! Directional light is a light source with parallel rays, it has excellent example in real life - Sun.
22//! It does not have position, only direction which defined by parent light scene node.
23//!
24//! ## Shadows
25//!
26//! Directional light sources uses cascaded shadow maps for shadows. Each cascade has different position
27//! on the viewing frustum and overall split options can be changed by using [`FrustumSplitOptions`].
28
29use crate::{
30    core::{
31        algebra::{UnitQuaternion, Vector3},
32        color::Color,
33        math::aabb::AxisAlignedBoundingBox,
34        pool::Handle,
35        reflect::prelude::*,
36        type_traits::prelude::*,
37        uuid::{uuid, Uuid},
38        uuid_provider,
39        variable::InheritableVariable,
40        visitor::{Visit, VisitResult, Visitor},
41    },
42    scene::{
43        base::{Base, BaseBuilder},
44        debug::SceneDrawingContext,
45        graph::Graph,
46        light::{BaseLight, BaseLightBuilder},
47        node::constructor::NodeConstructor,
48        node::{Node, NodeTrait},
49    },
50};
51use fyrox_graph::constructor::ConstructorProvider;
52use fyrox_graph::BaseSceneGraph;
53use std::ops::{Deref, DerefMut};
54use strum_macros::{AsRefStr, EnumString, VariantNames};
55
56/// Maximum amount of cascades.
57pub const CSM_NUM_CASCADES: usize = 3;
58
59/// Frustum split options defines how to split camera's frustum to generate cascades.
60#[derive(Reflect, Clone, Visit, Debug, PartialEq, AsRefStr, EnumString, VariantNames)]
61pub enum FrustumSplitOptions {
62    /// Camera frustum will be split into a [`CSM_NUM_CASCADES`] splits where each sub-frustum
63    /// will have fixed far plane location.
64    ///
65    /// This option allows you to set far planes very precisely, thus allowing you to set desired
66    /// quality of each cascade.
67    ///
68    /// This is default option.
69    Absolute {
70        /// A fixed set of distances, where each distance sets the location of far plane of
71        /// of sub-frustum. If far plane exceeds far plane of current camera, then cascade will
72        /// be discarded and won't be used for rendering.
73        far_planes: [f32; CSM_NUM_CASCADES],
74    },
75    /// Camera frustum will be split into a [`CSM_NUM_CASCADES`] splits using provided fractions.
76    ///
77    /// This option might give lesser quality results with camera that have large far plane, however
78    /// it does not require any precise tweaking.
79    Relative {
80        /// A fixed set of fractions in `[0; 1]` range which defines how far the far plane of
81        /// sub-frustum will be relative to camera's frustum.
82        fractions: [f32; CSM_NUM_CASCADES],
83    },
84}
85
86uuid_provider!(FrustumSplitOptions = "b2ed128a-b7da-4d34-b027-a0af19c2f563");
87
88impl Default for FrustumSplitOptions {
89    fn default() -> Self {
90        Self::Absolute {
91            far_planes: [5.0, 25.0, 64.0],
92        }
93    }
94}
95
96/// Cascade Shadow Mapping (CSM) options.
97#[derive(Reflect, Clone, Visit, PartialEq, Debug)]
98pub struct CsmOptions {
99    /// See [`FrustumSplitOptions`].
100    pub split_options: FrustumSplitOptions,
101
102    #[reflect(min_value = 0.0, step = 0.000025)]
103    shadow_bias: f32,
104}
105
106impl Default for CsmOptions {
107    fn default() -> Self {
108        Self {
109            split_options: Default::default(),
110            shadow_bias: 0.00025,
111        }
112    }
113}
114
115impl CsmOptions {
116    /// Sets new shadow bias value. Shadow bias allows you to prevent "shadow-acne" effect by
117    /// shifting values fetched from shadow map by a certain value. "Shadow acne" occur due to
118    /// insufficient precision.
119    pub fn set_shadow_bias(&mut self, bias: f32) {
120        self.shadow_bias = bias.max(0.0);
121    }
122
123    /// Returns current shadow bias value.
124    pub fn shadow_bias(&self) -> f32 {
125        self.shadow_bias
126    }
127}
128
129/// See module docs.
130#[derive(Default, Debug, Visit, Reflect, Clone, ComponentProvider)]
131pub struct DirectionalLight {
132    #[component(include)]
133    base_light: BaseLight,
134    /// See [`CsmOptions`].
135    pub csm_options: InheritableVariable<CsmOptions>,
136}
137
138impl From<BaseLight> for DirectionalLight {
139    fn from(base_light: BaseLight) -> Self {
140        Self {
141            base_light,
142            csm_options: Default::default(),
143        }
144    }
145}
146
147impl Deref for DirectionalLight {
148    type Target = Base;
149
150    fn deref(&self) -> &Self::Target {
151        &self.base_light.base
152    }
153}
154
155impl DerefMut for DirectionalLight {
156    fn deref_mut(&mut self) -> &mut Self::Target {
157        &mut self.base_light.base
158    }
159}
160
161impl TypeUuidProvider for DirectionalLight {
162    fn type_uuid() -> Uuid {
163        uuid!("8b8248e1-1cdf-42a3-9abe-0691de82c519")
164    }
165}
166
167impl DirectionalLight {
168    /// Returns a reference to base light.
169    pub fn base_light_ref(&self) -> &BaseLight {
170        &self.base_light
171    }
172
173    /// Returns a reference to base light.
174    pub fn base_light_mut(&mut self) -> &mut BaseLight {
175        &mut self.base_light
176    }
177}
178
179impl ConstructorProvider<Node, Graph> for DirectionalLight {
180    fn constructor() -> NodeConstructor {
181        NodeConstructor::new::<Self>()
182            .with_variant("Directional Light", |_| {
183                DirectionalLightBuilder::new(BaseLightBuilder::new(
184                    BaseBuilder::new().with_name("DirectionalLight"),
185                ))
186                .build_node()
187                .into()
188            })
189            .with_group("Light")
190    }
191}
192
193impl NodeTrait for DirectionalLight {
194    fn local_bounding_box(&self) -> AxisAlignedBoundingBox {
195        AxisAlignedBoundingBox::default()
196    }
197
198    fn world_bounding_box(&self) -> AxisAlignedBoundingBox {
199        self.local_bounding_box()
200            .transform(&self.global_transform())
201    }
202
203    fn id(&self) -> Uuid {
204        Self::type_uuid()
205    }
206
207    fn debug_draw(&self, ctx: &mut SceneDrawingContext) {
208        ctx.draw_arrow(
209            16,
210            Color::GREEN,
211            1.0,
212            0.2,
213            self.global_transform()
214                * UnitQuaternion::from_axis_angle(&Vector3::x_axis(), 180.0f32.to_radians())
215                    .to_homogeneous(),
216        );
217    }
218}
219
220/// Allows you to build directional light in declarative manner.
221pub struct DirectionalLightBuilder {
222    base_light_builder: BaseLightBuilder,
223    csm_options: CsmOptions,
224}
225
226impl DirectionalLightBuilder {
227    /// Creates new builder instance.
228    pub fn new(base_light_builder: BaseLightBuilder) -> Self {
229        Self {
230            base_light_builder,
231            csm_options: Default::default(),
232        }
233    }
234
235    /// Creates new instance of directional light.
236    pub fn build_directional_light(self) -> DirectionalLight {
237        DirectionalLight {
238            base_light: self.base_light_builder.build(),
239            csm_options: self.csm_options.into(),
240        }
241    }
242
243    /// Sets desired options for cascaded shadow maps.
244    pub fn with_csm_options(mut self, csm_options: CsmOptions) -> Self {
245        self.csm_options = csm_options;
246        self
247    }
248
249    /// Creates new instance of directional light node.
250    pub fn build_node(self) -> Node {
251        Node::new(self.build_directional_light())
252    }
253
254    /// Creates new instance of directional light and adds it to the graph.
255    pub fn build(self, graph: &mut Graph) -> Handle<Node> {
256        graph.add_node(self.build_node())
257    }
258}