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::BaseSceneGraph;
70use std::ops::{Deref, DerefMut};
71
72#[derive(Debug, Reflect, Clone, Visit, ComponentProvider)]
74pub struct SpotLight {
75 #[component(include)]
76 base_light: BaseLight,
77
78 #[reflect(min_value = 0.0, max_value = 3.14159, step = 0.1)]
79 #[reflect(setter = "set_hotspot_cone_angle")]
80 hotspot_cone_angle: InheritableVariable<f32>,
81
82 #[reflect(min_value = 0.0, step = 0.1)]
83 #[reflect(setter = "set_falloff_angle_delta")]
84 falloff_angle_delta: InheritableVariable<f32>,
85
86 #[reflect(min_value = 0.0, step = 0.001)]
87 #[reflect(setter = "set_shadow_bias")]
88 shadow_bias: InheritableVariable<f32>,
89
90 #[reflect(min_value = 0.0, step = 0.1)]
91 #[reflect(setter = "set_distance")]
92 distance: InheritableVariable<f32>,
93
94 #[reflect(setter = "set_cookie_texture")]
95 cookie_texture: InheritableVariable<Option<TextureResource>>,
96}
97
98impl Deref for SpotLight {
99 type Target = Base;
100
101 fn deref(&self) -> &Self::Target {
102 &self.base_light.base
103 }
104}
105
106impl DerefMut for SpotLight {
107 fn deref_mut(&mut self) -> &mut Self::Target {
108 &mut self.base_light.base
109 }
110}
111
112impl Default for SpotLight {
113 fn default() -> Self {
114 Self {
115 base_light: Default::default(),
116 hotspot_cone_angle: InheritableVariable::new_modified(90.0f32.to_radians()),
117 falloff_angle_delta: InheritableVariable::new_modified(5.0f32.to_radians()),
118 shadow_bias: InheritableVariable::new_modified(0.00005),
119 distance: InheritableVariable::new_modified(10.0),
120 cookie_texture: InheritableVariable::new_modified(None),
121 }
122 }
123}
124
125impl TypeUuidProvider for SpotLight {
126 fn type_uuid() -> Uuid {
127 uuid!("9856a3c1-ced7-47ec-b682-4dc4dea89d8f")
128 }
129}
130
131impl SpotLight {
132 pub fn base_light_ref(&self) -> &BaseLight {
134 &self.base_light
135 }
136
137 pub fn base_light_mut(&mut self) -> &mut BaseLight {
139 &mut self.base_light
140 }
141
142 #[inline]
144 pub fn hotspot_cone_angle(&self) -> f32 {
145 *self.hotspot_cone_angle
146 }
147
148 #[inline]
150 pub fn set_hotspot_cone_angle(&mut self, cone_angle: f32) -> f32 {
151 self.hotspot_cone_angle
152 .set_value_and_mark_modified(cone_angle.abs())
153 }
154
155 #[inline]
157 pub fn set_falloff_angle_delta(&mut self, delta: f32) -> f32 {
158 self.falloff_angle_delta.set_value_and_mark_modified(delta)
159 }
160
161 #[inline]
163 pub fn falloff_angle_delta(&self) -> f32 {
164 *self.falloff_angle_delta
165 }
166
167 #[inline]
169 pub fn full_cone_angle(&self) -> f32 {
170 *self.hotspot_cone_angle + *self.falloff_angle_delta
171 }
172
173 pub fn set_shadow_bias(&mut self, bias: f32) -> f32 {
176 self.shadow_bias.set_value_and_mark_modified(bias)
177 }
178
179 pub fn shadow_bias(&self) -> f32 {
181 *self.shadow_bias
182 }
183
184 #[inline]
187 pub fn set_distance(&mut self, distance: f32) -> f32 {
188 self.distance.set_value_and_mark_modified(distance.abs())
189 }
190
191 #[inline]
193 pub fn distance(&self) -> f32 {
194 *self.distance
195 }
196
197 #[inline]
200 pub fn set_cookie_texture(
201 &mut self,
202 texture: Option<TextureResource>,
203 ) -> Option<TextureResource> {
204 self.cookie_texture.set_value_and_mark_modified(texture)
205 }
206
207 #[inline]
210 pub fn cookie_texture(&self) -> Option<TextureResource> {
211 (*self.cookie_texture).clone()
212 }
213
214 #[inline]
217 pub fn cookie_texture_ref(&self) -> Option<&TextureResource> {
218 self.cookie_texture.as_ref()
219 }
220}
221
222impl ConstructorProvider<Node, Graph> for SpotLight {
223 fn constructor() -> NodeConstructor {
224 NodeConstructor::new::<Self>()
225 .with_variant("Spot Light", |_| {
226 SpotLightBuilder::new(BaseLightBuilder::new(
227 BaseBuilder::new().with_name("SpotLight"),
228 ))
229 .with_distance(10.0)
230 .with_hotspot_cone_angle(45.0f32.to_radians())
231 .with_falloff_angle_delta(2.0f32.to_radians())
232 .build_node()
233 .into()
234 })
235 .with_group("Light")
236 }
237}
238
239impl NodeTrait for SpotLight {
240 fn local_bounding_box(&self) -> AxisAlignedBoundingBox {
241 AxisAlignedBoundingBox::from_radius(self.distance())
242 }
243
244 fn world_bounding_box(&self) -> AxisAlignedBoundingBox {
245 self.local_bounding_box()
247 .transform(&self.global_transform_without_scaling())
248 }
249
250 fn id(&self) -> Uuid {
251 Self::type_uuid()
252 }
253
254 fn debug_draw(&self, ctx: &mut SceneDrawingContext) {
255 ctx.draw_cone(
256 16,
257 (self.full_cone_angle() * 0.5).tan() * self.distance(),
258 self.distance(),
259 Matrix4::new_translation(&self.global_position())
260 * UnitQuaternion::from_matrix_eps(
261 &self.global_transform().basis(),
262 f32::EPSILON,
263 16,
264 UnitQuaternion::identity(),
265 )
266 .to_homogeneous()
267 * Matrix4::new_translation(&Vector3::new(0.0, -self.distance() * 0.5, 0.0)),
268 Color::GREEN,
269 false,
270 );
271 }
272}
273
274pub struct SpotLightBuilder {
276 base_light_builder: BaseLightBuilder,
277 hotspot_cone_angle: f32,
278 falloff_angle_delta: f32,
279 shadow_bias: f32,
280 distance: f32,
281 cookie_texture: Option<TextureResource>,
282}
283
284impl SpotLightBuilder {
285 pub fn new(base_light_builder: BaseLightBuilder) -> Self {
287 Self {
288 base_light_builder,
289 hotspot_cone_angle: 90.0f32.to_radians(),
290 falloff_angle_delta: 5.0f32.to_radians(),
291 shadow_bias: 0.00005,
292 distance: 10.0,
293 cookie_texture: None,
294 }
295 }
296
297 pub fn with_hotspot_cone_angle(mut self, hotspot_cone_angle: f32) -> Self {
299 self.hotspot_cone_angle = hotspot_cone_angle;
300 self
301 }
302
303 pub fn with_falloff_angle_delta(mut self, falloff_angle_delta: f32) -> Self {
305 self.falloff_angle_delta = falloff_angle_delta;
306 self
307 }
308
309 pub fn with_distance(mut self, distance: f32) -> Self {
311 self.distance = distance;
312 self
313 }
314
315 pub fn with_shadow_bias(mut self, bias: f32) -> Self {
317 self.shadow_bias = bias;
318 self
319 }
320
321 pub fn with_cookie_texture(mut self, texture: TextureResource) -> Self {
323 self.cookie_texture = Some(texture);
324 self
325 }
326
327 pub fn build_spot_light(self) -> SpotLight {
329 SpotLight {
330 base_light: self.base_light_builder.build(),
331 hotspot_cone_angle: self.hotspot_cone_angle.into(),
332 falloff_angle_delta: self.falloff_angle_delta.into(),
333 shadow_bias: self.shadow_bias.into(),
334 distance: self.distance.into(),
335 cookie_texture: self.cookie_texture.into(),
336 }
337 }
338
339 pub fn build_node(self) -> Node {
341 Node::new(self.build_spot_light())
342 }
343
344 pub fn build(self, graph: &mut Graph) -> Handle<Node> {
346 graph.add_node(self.build_node())
347 }
348}