1use crate::scene::base::BaseBuilder;
46use crate::scene::node::constructor::NodeConstructor;
47use crate::{
48 core::{
49 algebra::{Matrix4, UnitQuaternion, Vector3},
50 color::Color,
51 math::{aabb::AxisAlignedBoundingBox, Matrix4Ext},
52 pool::Handle,
53 reflect::prelude::*,
54 type_traits::prelude::*,
55 uuid::{uuid, Uuid},
56 variable::InheritableVariable,
57 visitor::{Visit, VisitResult, Visitor},
58 },
59 resource::texture::TextureResource,
60 scene::{
61 base::Base,
62 debug::SceneDrawingContext,
63 graph::Graph,
64 light::{BaseLight, BaseLightBuilder},
65 node::{Node, NodeTrait},
66 },
67};
68use fyrox_graph::constructor::ConstructorProvider;
69use fyrox_graph::SceneGraph;
70use std::ops::{Deref, DerefMut};
71
72#[derive(Debug, Reflect, Clone, Visit, ComponentProvider)]
74#[reflect(derived_type = "Node")]
75pub struct SpotLight {
76 #[component(include)]
77 base_light: BaseLight,
78
79 #[reflect(min_value = 0.0, max_value = 3.14159, step = 0.1)]
80 #[reflect(setter = "set_hotspot_cone_angle")]
81 hotspot_cone_angle: InheritableVariable<f32>,
82
83 #[reflect(min_value = 0.0, step = 0.1)]
84 #[reflect(setter = "set_falloff_angle_delta")]
85 falloff_angle_delta: InheritableVariable<f32>,
86
87 #[reflect(min_value = 0.0, step = 0.001)]
88 #[reflect(setter = "set_shadow_bias")]
89 shadow_bias: InheritableVariable<f32>,
90
91 #[reflect(min_value = 0.0, step = 0.1)]
92 #[reflect(setter = "set_distance")]
93 distance: InheritableVariable<f32>,
94
95 #[reflect(setter = "set_cookie_texture")]
96 cookie_texture: InheritableVariable<Option<TextureResource>>,
97}
98
99impl Deref for SpotLight {
100 type Target = Base;
101
102 fn deref(&self) -> &Self::Target {
103 &self.base_light.base
104 }
105}
106
107impl DerefMut for SpotLight {
108 fn deref_mut(&mut self) -> &mut Self::Target {
109 &mut self.base_light.base
110 }
111}
112
113impl Default for SpotLight {
114 fn default() -> Self {
115 Self {
116 base_light: Default::default(),
117 hotspot_cone_angle: InheritableVariable::new_modified(90.0f32.to_radians()),
118 falloff_angle_delta: InheritableVariable::new_modified(5.0f32.to_radians()),
119 shadow_bias: InheritableVariable::new_modified(0.00005),
120 distance: InheritableVariable::new_modified(10.0),
121 cookie_texture: InheritableVariable::new_modified(None),
122 }
123 }
124}
125
126impl TypeUuidProvider for SpotLight {
127 fn type_uuid() -> Uuid {
128 uuid!("9856a3c1-ced7-47ec-b682-4dc4dea89d8f")
129 }
130}
131
132impl SpotLight {
133 pub fn base_light_ref(&self) -> &BaseLight {
135 &self.base_light
136 }
137
138 pub fn base_light_mut(&mut self) -> &mut BaseLight {
140 &mut self.base_light
141 }
142
143 #[inline]
145 pub fn hotspot_cone_angle(&self) -> f32 {
146 *self.hotspot_cone_angle
147 }
148
149 #[inline]
151 pub fn set_hotspot_cone_angle(&mut self, cone_angle: f32) -> f32 {
152 self.hotspot_cone_angle
153 .set_value_and_mark_modified(cone_angle.abs())
154 }
155
156 #[inline]
158 pub fn set_falloff_angle_delta(&mut self, delta: f32) -> f32 {
159 self.falloff_angle_delta.set_value_and_mark_modified(delta)
160 }
161
162 #[inline]
164 pub fn falloff_angle_delta(&self) -> f32 {
165 *self.falloff_angle_delta
166 }
167
168 #[inline]
170 pub fn full_cone_angle(&self) -> f32 {
171 *self.hotspot_cone_angle + *self.falloff_angle_delta
172 }
173
174 pub fn set_shadow_bias(&mut self, bias: f32) -> f32 {
177 self.shadow_bias.set_value_and_mark_modified(bias)
178 }
179
180 pub fn shadow_bias(&self) -> f32 {
182 *self.shadow_bias
183 }
184
185 #[inline]
188 pub fn set_distance(&mut self, distance: f32) -> f32 {
189 self.distance.set_value_and_mark_modified(distance.abs())
190 }
191
192 #[inline]
194 pub fn distance(&self) -> f32 {
195 *self.distance
196 }
197
198 #[inline]
201 pub fn set_cookie_texture(
202 &mut self,
203 texture: Option<TextureResource>,
204 ) -> Option<TextureResource> {
205 self.cookie_texture.set_value_and_mark_modified(texture)
206 }
207
208 #[inline]
211 pub fn cookie_texture(&self) -> Option<TextureResource> {
212 (*self.cookie_texture).clone()
213 }
214
215 #[inline]
218 pub fn cookie_texture_ref(&self) -> Option<&TextureResource> {
219 self.cookie_texture.as_ref()
220 }
221}
222
223impl ConstructorProvider<Node, Graph> for SpotLight {
224 fn constructor() -> NodeConstructor {
225 NodeConstructor::new::<Self>()
226 .with_variant("Spot Light", |_| {
227 SpotLightBuilder::new(BaseLightBuilder::new(
228 BaseBuilder::new().with_name("SpotLight"),
229 ))
230 .with_distance(10.0)
231 .with_hotspot_cone_angle(45.0f32.to_radians())
232 .with_falloff_angle_delta(2.0f32.to_radians())
233 .build_node()
234 .into()
235 })
236 .with_group("Light")
237 }
238}
239
240impl NodeTrait for SpotLight {
241 fn local_bounding_box(&self) -> AxisAlignedBoundingBox {
242 AxisAlignedBoundingBox::from_radius(self.distance())
243 }
244
245 fn world_bounding_box(&self) -> AxisAlignedBoundingBox {
246 self.local_bounding_box()
248 .transform(&self.global_transform_without_scaling())
249 }
250
251 fn id(&self) -> Uuid {
252 Self::type_uuid()
253 }
254
255 fn debug_draw(&self, ctx: &mut SceneDrawingContext) {
256 ctx.draw_cone(
257 16,
258 (self.full_cone_angle() * 0.5).tan() * self.distance(),
259 self.distance(),
260 Matrix4::new_translation(&self.global_position())
261 * UnitQuaternion::from_matrix_eps(
262 &self.global_transform().basis(),
263 f32::EPSILON,
264 16,
265 UnitQuaternion::identity(),
266 )
267 .to_homogeneous()
268 * Matrix4::new_translation(&Vector3::new(0.0, -self.distance() * 0.5, 0.0)),
269 Color::GREEN,
270 false,
271 );
272 }
273}
274
275pub struct SpotLightBuilder {
277 base_light_builder: BaseLightBuilder,
278 hotspot_cone_angle: f32,
279 falloff_angle_delta: f32,
280 shadow_bias: f32,
281 distance: f32,
282 cookie_texture: Option<TextureResource>,
283}
284
285impl SpotLightBuilder {
286 pub fn new(base_light_builder: BaseLightBuilder) -> Self {
288 Self {
289 base_light_builder,
290 hotspot_cone_angle: 90.0f32.to_radians(),
291 falloff_angle_delta: 5.0f32.to_radians(),
292 shadow_bias: 0.00005,
293 distance: 10.0,
294 cookie_texture: None,
295 }
296 }
297
298 pub fn with_hotspot_cone_angle(mut self, hotspot_cone_angle: f32) -> Self {
300 self.hotspot_cone_angle = hotspot_cone_angle;
301 self
302 }
303
304 pub fn with_falloff_angle_delta(mut self, falloff_angle_delta: f32) -> Self {
306 self.falloff_angle_delta = falloff_angle_delta;
307 self
308 }
309
310 pub fn with_distance(mut self, distance: f32) -> Self {
312 self.distance = distance;
313 self
314 }
315
316 pub fn with_shadow_bias(mut self, bias: f32) -> Self {
318 self.shadow_bias = bias;
319 self
320 }
321
322 pub fn with_cookie_texture(mut self, texture: TextureResource) -> Self {
324 self.cookie_texture = Some(texture);
325 self
326 }
327
328 pub fn build_spot_light(self) -> SpotLight {
330 SpotLight {
331 base_light: self.base_light_builder.build(),
332 hotspot_cone_angle: self.hotspot_cone_angle.into(),
333 falloff_angle_delta: self.falloff_angle_delta.into(),
334 shadow_bias: self.shadow_bias.into(),
335 distance: self.distance.into(),
336 cookie_texture: self.cookie_texture.into(),
337 }
338 }
339
340 pub fn build_node(self) -> Node {
342 Node::new(self.build_spot_light())
343 }
344
345 pub fn build(self, graph: &mut Graph) -> Handle<SpotLight> {
347 graph.add_node(self.build_node()).to_variant()
348 }
349}