1use bitflags::bitflags;
8use crate::Vector3D;
9use crate::media::Color;
10use crate::rendering::composition::ICompositionObject;
11use crate::rendering::composition::animations::AnimationInstanceFactory;
12use crate::rendering::composition::animations::ICompositionAnimation;
13use crate::rendering::composition::expressions::ExpressionVariant;
14use crate::rendering::composition::server::AsServerRenderResource;
15use crate::rendering::composition::server::CompositionProperty;
16use crate::rendering::composition::server::CompositionPropertyOf;
17use crate::rendering::composition::server::IServerObject;
18use crate::rendering::composition::server::ServerPropertyValue;
19use crate::rendering::composition::server::ServerValueChange;
20use crate::rendering::composition::server::props_of;
21use crate::rendering::composition::server::read_server_object;
22use crate::rendering::composition::transport::BatchStreamReader;
23use crate::rendering::composition::transport::BatchStreamWriter;
24use crate::rendering::composition::transport::BatchValue;
25use crate::rendering::composition::transport::BatchValueReader;
26use std::any::Any;
27use std::any::TypeId;
28use std::cell::Cell;
29use std::cell::RefCell;
30use std::rc::Rc;
31use std::sync::OnceLock;
32use std::time::Duration;
33use super::CompositionLightHooks;
34use super::CompositionLightProps;
35use super::ServerCompositionLightHooks;
36use super::ServerCompositionLightProps;
37
38bitflags! {
39 #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
41 pub struct SpotLightChangedFields: u32 {
42 const CONSTANT_ATTENUATION = 1;
43 const CONSTANT_ATTENUATION_ANIMATED = 2;
44 const LINEAR_ATTENUATION = 4;
45 const LINEAR_ATTENUATION_ANIMATED = 8;
46 const QUADRATIC_ATTENUATION = 16;
47 const QUADRATIC_ATTENUATION_ANIMATED = 32;
48 const COORDINATE_SPACE = 64;
49 const DIRECTION = 128;
50 const DIRECTION_ANIMATED = 256;
51 const INNER_CONE_ANGLE = 512;
52 const INNER_CONE_ANGLE_ANIMATED = 1024;
53 const INNER_CONE_COLOR = 2048;
54 const INNER_CONE_COLOR_ANIMATED = 4096;
55 const INNER_CONE_INTENSITY = 8192;
56 const INNER_CONE_INTENSITY_ANIMATED = 16384;
57 const OUTER_CONE_ANGLE = 32768;
58 const OUTER_CONE_ANGLE_ANIMATED = 65536;
59 const OUTER_CONE_COLOR = 131072;
60 const OUTER_CONE_COLOR_ANIMATED = 262144;
61 const OUTER_CONE_INTENSITY = 524288;
62 const OUTER_CONE_INTENSITY_ANIMATED = 1048576;
63 const OFFSET = 2097152;
64 const OFFSET_ANIMATED = 4194304;
65 const MIN_ATTENUATION_CUTOFF = 8388608;
66 const MIN_ATTENUATION_CUTOFF_ANIMATED = 16777216;
67 const MAX_ATTENUATION_CUTOFF = 33554432;
68 const MAX_ATTENUATION_CUTOFF_ANIMATED = 67108864;
69 }
70}
71
72impl BatchValue for SpotLightChangedFields {
73 #[inline]
74 fn write_to(&self, out: &mut Vec<u8>) {
75 self.bits().write_to(out);
76 }
77
78 #[inline]
79 fn read_from(input: &mut BatchValueReader<'_>) -> Self {
80 SpotLightChangedFields::from_bits_retain(u32::read_from(input))
81 }
82}
83
84pub trait SpotLightHooks: CompositionLightHooks {
87 fn initialize_defaults_extra(&self) {}
88
89 fn validate_constant_attenuation_change(&self, _old_value: f32, _new_value: f32) {}
90 fn on_constant_attenuation_changed(&self) {}
91 fn on_constant_attenuation_changing(&self) {}
92
93 fn validate_linear_attenuation_change(&self, _old_value: f32, _new_value: f32) {}
94 fn on_linear_attenuation_changed(&self) {}
95 fn on_linear_attenuation_changing(&self) {}
96
97 fn validate_quadratic_attenuation_change(&self, _old_value: f32, _new_value: f32) {}
98 fn on_quadratic_attenuation_changed(&self) {}
99 fn on_quadratic_attenuation_changing(&self) {}
100
101 fn validate_coordinate_space_change(&self, _old_value: &Option<Rc<dyn ICompositionObject>>, _new_value: &Option<Rc<dyn ICompositionObject>>) {}
102 fn on_coordinate_space_changed(&self) {}
103 fn on_coordinate_space_changing(&self) {}
104
105 fn validate_direction_change(&self, _old_value: Vector3D, _new_value: Vector3D) {}
106 fn on_direction_changed(&self) {}
107 fn on_direction_changing(&self) {}
108
109 fn validate_inner_cone_angle_change(&self, _old_value: f32, _new_value: f32) {}
110 fn on_inner_cone_angle_changed(&self) {}
111 fn on_inner_cone_angle_changing(&self) {}
112
113 fn validate_inner_cone_color_change(&self, _old_value: Color, _new_value: Color) {}
114 fn on_inner_cone_color_changed(&self) {}
115 fn on_inner_cone_color_changing(&self) {}
116
117 fn validate_inner_cone_intensity_change(&self, _old_value: f32, _new_value: f32) {}
118 fn on_inner_cone_intensity_changed(&self) {}
119 fn on_inner_cone_intensity_changing(&self) {}
120
121 fn validate_outer_cone_angle_change(&self, _old_value: f32, _new_value: f32) {}
122 fn on_outer_cone_angle_changed(&self) {}
123 fn on_outer_cone_angle_changing(&self) {}
124
125 fn validate_outer_cone_color_change(&self, _old_value: Color, _new_value: Color) {}
126 fn on_outer_cone_color_changed(&self) {}
127 fn on_outer_cone_color_changing(&self) {}
128
129 fn validate_outer_cone_intensity_change(&self, _old_value: f32, _new_value: f32) {}
130 fn on_outer_cone_intensity_changed(&self) {}
131 fn on_outer_cone_intensity_changing(&self) {}
132
133 fn validate_offset_change(&self, _old_value: Vector3D, _new_value: Vector3D) {}
134 fn on_offset_changed(&self) {}
135 fn on_offset_changing(&self) {}
136
137 fn validate_min_attenuation_cutoff_change(&self, _old_value: f32, _new_value: f32) {}
138 fn on_min_attenuation_cutoff_changed(&self) {}
139 fn on_min_attenuation_cutoff_changing(&self) {}
140
141 fn validate_max_attenuation_cutoff_change(&self, _old_value: f32, _new_value: f32) {}
142 fn on_max_attenuation_cutoff_changed(&self) {}
143 fn on_max_attenuation_cutoff_changing(&self) {}
144}
145
146pub struct SpotLightProps {
148 base: CompositionLightProps,
149 changed_fields_of_spot_light: Cell<SpotLightChangedFields>,
150 constant_attenuation: Cell<f32>,
151 linear_attenuation: Cell<f32>,
152 quadratic_attenuation: Cell<f32>,
153 coordinate_space: RefCell<Option<Rc<dyn ICompositionObject>>>,
154 direction: Cell<Vector3D>,
155 inner_cone_angle: Cell<f32>,
156 inner_cone_color: Cell<Color>,
157 inner_cone_intensity: Cell<f32>,
158 outer_cone_angle: Cell<f32>,
159 outer_cone_color: Cell<Color>,
160 outer_cone_intensity: Cell<f32>,
161 offset: Cell<Vector3D>,
162 min_attenuation_cutoff: Cell<f32>,
163 max_attenuation_cutoff: Cell<f32>,
164}
165
166impl Default for SpotLightProps {
167 fn default() -> Self {
168 Self::new()
169 }
170}
171
172impl SpotLightProps {
173 pub fn new() -> Self {
177 Self {
178 base: CompositionLightProps::new(),
179 changed_fields_of_spot_light: Cell::new(SpotLightChangedFields::empty()),
180 constant_attenuation: Cell::new(Default::default()),
181 linear_attenuation: Cell::new(Default::default()),
182 quadratic_attenuation: Cell::new(Default::default()),
183 coordinate_space: RefCell::new(None),
184 direction: Cell::new(Default::default()),
185 inner_cone_angle: Cell::new(Default::default()),
186 inner_cone_color: Cell::new(Default::default()),
187 inner_cone_intensity: Cell::new(Default::default()),
188 outer_cone_angle: Cell::new(Default::default()),
189 outer_cone_color: Cell::new(Default::default()),
190 outer_cone_intensity: Cell::new(Default::default()),
191 offset: Cell::new(Default::default()),
192 min_attenuation_cutoff: Cell::new(Default::default()),
193 max_attenuation_cutoff: Cell::new(Default::default()),
194 }
195 }
196
197 pub fn base(&self) -> &CompositionLightProps {
199 &self.base
200 }
201
202 pub fn initialize_defaults(&self, host: &dyn SpotLightHooks) {
205 self.base.initialize_defaults(host);
206 self.initialize_own_defaults(host);
207 }
208
209 pub fn initialize_own_defaults(&self, host: &dyn SpotLightHooks) {
211 SpotLightHooks::initialize_defaults_extra(host);
212 self.set_constant_attenuation(host, 1.0);
213 self.set_direction(host, Vector3D::new(0.0, 0.0, -1.0));
214 self.set_inner_cone_color(host, crate::media::Colors::WHITE);
215 self.set_inner_cone_intensity(host, 1.0);
216 self.set_outer_cone_angle(host, std::f32::consts::FRAC_PI_4);
217 self.set_outer_cone_color(host, crate::media::Colors::WHITE);
218 self.set_outer_cone_intensity(host, 1.0);
219 self.set_max_attenuation_cutoff(host, f32::MAX);
220 }
221
222 pub fn constant_attenuation(&self) -> f32 {
224 self.constant_attenuation.get()
225 }
226
227 pub fn set_constant_attenuation(&self, host: &dyn SpotLightHooks, value: f32) {
229 let mut changed = false;
230 let old_value = self.constant_attenuation.get();
231 if old_value.differs(&value) {
232 SpotLightHooks::validate_constant_attenuation_change(host, old_value, value);
233 SpotLightHooks::on_constant_attenuation_changing(host);
234 changed = true;
235 self.constant_attenuation.set(value);
237 self.changed_fields_of_spot_light.set(self.changed_fields_of_spot_light.get() | SpotLightChangedFields::CONSTANT_ATTENUATION);
239 host.register_for_serialization();
240 host.pending_animations().remove(ServerSpotLightProps::id_of_constant_attenuation_property().base());
242 self.changed_fields_of_spot_light.set(self.changed_fields_of_spot_light.get() & !SpotLightChangedFields::CONSTANT_ATTENUATION_ANIMATED);
243 if let Some(animation) = host.implicit_animation("ConstantAttenuation") {
245 if let Some(a) = animation.as_composition_animation() {
247 self.changed_fields_of_spot_light.set(self.changed_fields_of_spot_light.get() | SpotLightChangedFields::CONSTANT_ATTENUATION_ANIMATED);
248 host.pending_animations().set(
249 ServerSpotLightProps::id_of_constant_attenuation_property().base(),
250 a.create_instance(host.server(), Some(ExpressionVariant::create(value))),
251 );
252 }
253 host.start_animation_group(&animation, "ConstantAttenuation", ExpressionVariant::create(value));
255 }
256 }
257 self.constant_attenuation.set(value);
258 if changed {
259 SpotLightHooks::on_constant_attenuation_changed(host);
260 }
261 }
262
263 pub fn linear_attenuation(&self) -> f32 {
265 self.linear_attenuation.get()
266 }
267
268 pub fn set_linear_attenuation(&self, host: &dyn SpotLightHooks, value: f32) {
270 let mut changed = false;
271 let old_value = self.linear_attenuation.get();
272 if old_value.differs(&value) {
273 SpotLightHooks::validate_linear_attenuation_change(host, old_value, value);
274 SpotLightHooks::on_linear_attenuation_changing(host);
275 changed = true;
276 self.linear_attenuation.set(value);
278 self.changed_fields_of_spot_light.set(self.changed_fields_of_spot_light.get() | SpotLightChangedFields::LINEAR_ATTENUATION);
280 host.register_for_serialization();
281 host.pending_animations().remove(ServerSpotLightProps::id_of_linear_attenuation_property().base());
283 self.changed_fields_of_spot_light.set(self.changed_fields_of_spot_light.get() & !SpotLightChangedFields::LINEAR_ATTENUATION_ANIMATED);
284 if let Some(animation) = host.implicit_animation("LinearAttenuation") {
286 if let Some(a) = animation.as_composition_animation() {
288 self.changed_fields_of_spot_light.set(self.changed_fields_of_spot_light.get() | SpotLightChangedFields::LINEAR_ATTENUATION_ANIMATED);
289 host.pending_animations().set(
290 ServerSpotLightProps::id_of_linear_attenuation_property().base(),
291 a.create_instance(host.server(), Some(ExpressionVariant::create(value))),
292 );
293 }
294 host.start_animation_group(&animation, "LinearAttenuation", ExpressionVariant::create(value));
296 }
297 }
298 self.linear_attenuation.set(value);
299 if changed {
300 SpotLightHooks::on_linear_attenuation_changed(host);
301 }
302 }
303
304 pub fn quadratic_attenuation(&self) -> f32 {
306 self.quadratic_attenuation.get()
307 }
308
309 pub fn set_quadratic_attenuation(&self, host: &dyn SpotLightHooks, value: f32) {
311 let mut changed = false;
312 let old_value = self.quadratic_attenuation.get();
313 if old_value.differs(&value) {
314 SpotLightHooks::validate_quadratic_attenuation_change(host, old_value, value);
315 SpotLightHooks::on_quadratic_attenuation_changing(host);
316 changed = true;
317 self.quadratic_attenuation.set(value);
319 self.changed_fields_of_spot_light.set(self.changed_fields_of_spot_light.get() | SpotLightChangedFields::QUADRATIC_ATTENUATION);
321 host.register_for_serialization();
322 host.pending_animations().remove(ServerSpotLightProps::id_of_quadratic_attenuation_property().base());
324 self.changed_fields_of_spot_light.set(self.changed_fields_of_spot_light.get() & !SpotLightChangedFields::QUADRATIC_ATTENUATION_ANIMATED);
325 if let Some(animation) = host.implicit_animation("QuadraticAttenuation") {
327 if let Some(a) = animation.as_composition_animation() {
329 self.changed_fields_of_spot_light.set(self.changed_fields_of_spot_light.get() | SpotLightChangedFields::QUADRATIC_ATTENUATION_ANIMATED);
330 host.pending_animations().set(
331 ServerSpotLightProps::id_of_quadratic_attenuation_property().base(),
332 a.create_instance(host.server(), Some(ExpressionVariant::create(value))),
333 );
334 }
335 host.start_animation_group(&animation, "QuadraticAttenuation", ExpressionVariant::create(value));
337 }
338 }
339 self.quadratic_attenuation.set(value);
340 if changed {
341 SpotLightHooks::on_quadratic_attenuation_changed(host);
342 }
343 }
344
345 pub fn coordinate_space(&self) -> Option<Rc<dyn ICompositionObject>> {
347 self.coordinate_space.borrow().clone()
348 }
349
350 pub fn set_coordinate_space(&self, host: &dyn SpotLightHooks, value: Option<Rc<dyn ICompositionObject>>) {
352 let mut changed = false;
353 let old_value = self.coordinate_space.borrow().clone();
354 if old_value.differs(&value) {
355 SpotLightHooks::validate_coordinate_space_change(host, &old_value, &value);
356 SpotLightHooks::on_coordinate_space_changing(host);
357 changed = true;
358 *self.coordinate_space.borrow_mut() = value.clone();
360 self.changed_fields_of_spot_light.set(self.changed_fields_of_spot_light.get() | SpotLightChangedFields::COORDINATE_SPACE);
362 host.register_for_serialization();
363 }
364 *self.coordinate_space.borrow_mut() = value;
365 if changed {
366 SpotLightHooks::on_coordinate_space_changed(host);
367 }
368 }
369
370 pub fn direction(&self) -> Vector3D {
372 self.direction.get()
373 }
374
375 pub fn set_direction(&self, host: &dyn SpotLightHooks, value: Vector3D) {
377 let mut changed = false;
378 let old_value = self.direction.get();
379 if old_value.differs(&value) {
380 SpotLightHooks::validate_direction_change(host, old_value, value);
381 SpotLightHooks::on_direction_changing(host);
382 changed = true;
383 self.direction.set(value);
385 self.changed_fields_of_spot_light.set(self.changed_fields_of_spot_light.get() | SpotLightChangedFields::DIRECTION);
387 host.register_for_serialization();
388 host.pending_animations().remove(ServerSpotLightProps::id_of_direction_property().base());
390 self.changed_fields_of_spot_light.set(self.changed_fields_of_spot_light.get() & !SpotLightChangedFields::DIRECTION_ANIMATED);
391 if let Some(animation) = host.implicit_animation("Direction") {
393 if let Some(a) = animation.as_composition_animation() {
395 self.changed_fields_of_spot_light.set(self.changed_fields_of_spot_light.get() | SpotLightChangedFields::DIRECTION_ANIMATED);
396 host.pending_animations().set(
397 ServerSpotLightProps::id_of_direction_property().base(),
398 a.create_instance(host.server(), Some(ExpressionVariant::create(value))),
399 );
400 }
401 host.start_animation_group(&animation, "Direction", ExpressionVariant::create(value));
403 }
404 }
405 self.direction.set(value);
406 if changed {
407 SpotLightHooks::on_direction_changed(host);
408 }
409 }
410
411 pub fn inner_cone_angle(&self) -> f32 {
413 self.inner_cone_angle.get()
414 }
415
416 pub fn set_inner_cone_angle(&self, host: &dyn SpotLightHooks, value: f32) {
418 let mut changed = false;
419 let old_value = self.inner_cone_angle.get();
420 if old_value.differs(&value) {
421 SpotLightHooks::validate_inner_cone_angle_change(host, old_value, value);
422 SpotLightHooks::on_inner_cone_angle_changing(host);
423 changed = true;
424 self.inner_cone_angle.set(value);
426 self.changed_fields_of_spot_light.set(self.changed_fields_of_spot_light.get() | SpotLightChangedFields::INNER_CONE_ANGLE);
428 host.register_for_serialization();
429 host.pending_animations().remove(ServerSpotLightProps::id_of_inner_cone_angle_property().base());
431 self.changed_fields_of_spot_light.set(self.changed_fields_of_spot_light.get() & !SpotLightChangedFields::INNER_CONE_ANGLE_ANIMATED);
432 if let Some(animation) = host.implicit_animation("InnerConeAngle") {
434 if let Some(a) = animation.as_composition_animation() {
436 self.changed_fields_of_spot_light.set(self.changed_fields_of_spot_light.get() | SpotLightChangedFields::INNER_CONE_ANGLE_ANIMATED);
437 host.pending_animations().set(
438 ServerSpotLightProps::id_of_inner_cone_angle_property().base(),
439 a.create_instance(host.server(), Some(ExpressionVariant::create(value))),
440 );
441 }
442 host.start_animation_group(&animation, "InnerConeAngle", ExpressionVariant::create(value));
444 }
445 }
446 self.inner_cone_angle.set(value);
447 if changed {
448 SpotLightHooks::on_inner_cone_angle_changed(host);
449 }
450 }
451
452 pub fn inner_cone_color(&self) -> Color {
454 self.inner_cone_color.get()
455 }
456
457 pub fn set_inner_cone_color(&self, host: &dyn SpotLightHooks, value: Color) {
459 let mut changed = false;
460 let old_value = self.inner_cone_color.get();
461 if old_value.differs(&value) {
462 SpotLightHooks::validate_inner_cone_color_change(host, old_value, value);
463 SpotLightHooks::on_inner_cone_color_changing(host);
464 changed = true;
465 self.inner_cone_color.set(value);
467 self.changed_fields_of_spot_light.set(self.changed_fields_of_spot_light.get() | SpotLightChangedFields::INNER_CONE_COLOR);
469 host.register_for_serialization();
470 host.pending_animations().remove(ServerSpotLightProps::id_of_inner_cone_color_property().base());
472 self.changed_fields_of_spot_light.set(self.changed_fields_of_spot_light.get() & !SpotLightChangedFields::INNER_CONE_COLOR_ANIMATED);
473 if let Some(animation) = host.implicit_animation("InnerConeColor") {
475 if let Some(a) = animation.as_composition_animation() {
477 self.changed_fields_of_spot_light.set(self.changed_fields_of_spot_light.get() | SpotLightChangedFields::INNER_CONE_COLOR_ANIMATED);
478 host.pending_animations().set(
479 ServerSpotLightProps::id_of_inner_cone_color_property().base(),
480 a.create_instance(host.server(), Some(ExpressionVariant::create(value))),
481 );
482 }
483 host.start_animation_group(&animation, "InnerConeColor", ExpressionVariant::create(value));
485 }
486 }
487 self.inner_cone_color.set(value);
488 if changed {
489 SpotLightHooks::on_inner_cone_color_changed(host);
490 }
491 }
492
493 pub fn inner_cone_intensity(&self) -> f32 {
495 self.inner_cone_intensity.get()
496 }
497
498 pub fn set_inner_cone_intensity(&self, host: &dyn SpotLightHooks, value: f32) {
500 let mut changed = false;
501 let old_value = self.inner_cone_intensity.get();
502 if old_value.differs(&value) {
503 SpotLightHooks::validate_inner_cone_intensity_change(host, old_value, value);
504 SpotLightHooks::on_inner_cone_intensity_changing(host);
505 changed = true;
506 self.inner_cone_intensity.set(value);
508 self.changed_fields_of_spot_light.set(self.changed_fields_of_spot_light.get() | SpotLightChangedFields::INNER_CONE_INTENSITY);
510 host.register_for_serialization();
511 host.pending_animations().remove(ServerSpotLightProps::id_of_inner_cone_intensity_property().base());
513 self.changed_fields_of_spot_light.set(self.changed_fields_of_spot_light.get() & !SpotLightChangedFields::INNER_CONE_INTENSITY_ANIMATED);
514 if let Some(animation) = host.implicit_animation("InnerConeIntensity") {
516 if let Some(a) = animation.as_composition_animation() {
518 self.changed_fields_of_spot_light.set(self.changed_fields_of_spot_light.get() | SpotLightChangedFields::INNER_CONE_INTENSITY_ANIMATED);
519 host.pending_animations().set(
520 ServerSpotLightProps::id_of_inner_cone_intensity_property().base(),
521 a.create_instance(host.server(), Some(ExpressionVariant::create(value))),
522 );
523 }
524 host.start_animation_group(&animation, "InnerConeIntensity", ExpressionVariant::create(value));
526 }
527 }
528 self.inner_cone_intensity.set(value);
529 if changed {
530 SpotLightHooks::on_inner_cone_intensity_changed(host);
531 }
532 }
533
534 pub fn outer_cone_angle(&self) -> f32 {
536 self.outer_cone_angle.get()
537 }
538
539 pub fn set_outer_cone_angle(&self, host: &dyn SpotLightHooks, value: f32) {
541 let mut changed = false;
542 let old_value = self.outer_cone_angle.get();
543 if old_value.differs(&value) {
544 SpotLightHooks::validate_outer_cone_angle_change(host, old_value, value);
545 SpotLightHooks::on_outer_cone_angle_changing(host);
546 changed = true;
547 self.outer_cone_angle.set(value);
549 self.changed_fields_of_spot_light.set(self.changed_fields_of_spot_light.get() | SpotLightChangedFields::OUTER_CONE_ANGLE);
551 host.register_for_serialization();
552 host.pending_animations().remove(ServerSpotLightProps::id_of_outer_cone_angle_property().base());
554 self.changed_fields_of_spot_light.set(self.changed_fields_of_spot_light.get() & !SpotLightChangedFields::OUTER_CONE_ANGLE_ANIMATED);
555 if let Some(animation) = host.implicit_animation("OuterConeAngle") {
557 if let Some(a) = animation.as_composition_animation() {
559 self.changed_fields_of_spot_light.set(self.changed_fields_of_spot_light.get() | SpotLightChangedFields::OUTER_CONE_ANGLE_ANIMATED);
560 host.pending_animations().set(
561 ServerSpotLightProps::id_of_outer_cone_angle_property().base(),
562 a.create_instance(host.server(), Some(ExpressionVariant::create(value))),
563 );
564 }
565 host.start_animation_group(&animation, "OuterConeAngle", ExpressionVariant::create(value));
567 }
568 }
569 self.outer_cone_angle.set(value);
570 if changed {
571 SpotLightHooks::on_outer_cone_angle_changed(host);
572 }
573 }
574
575 pub fn outer_cone_color(&self) -> Color {
577 self.outer_cone_color.get()
578 }
579
580 pub fn set_outer_cone_color(&self, host: &dyn SpotLightHooks, value: Color) {
582 let mut changed = false;
583 let old_value = self.outer_cone_color.get();
584 if old_value.differs(&value) {
585 SpotLightHooks::validate_outer_cone_color_change(host, old_value, value);
586 SpotLightHooks::on_outer_cone_color_changing(host);
587 changed = true;
588 self.outer_cone_color.set(value);
590 self.changed_fields_of_spot_light.set(self.changed_fields_of_spot_light.get() | SpotLightChangedFields::OUTER_CONE_COLOR);
592 host.register_for_serialization();
593 host.pending_animations().remove(ServerSpotLightProps::id_of_outer_cone_color_property().base());
595 self.changed_fields_of_spot_light.set(self.changed_fields_of_spot_light.get() & !SpotLightChangedFields::OUTER_CONE_COLOR_ANIMATED);
596 if let Some(animation) = host.implicit_animation("OuterConeColor") {
598 if let Some(a) = animation.as_composition_animation() {
600 self.changed_fields_of_spot_light.set(self.changed_fields_of_spot_light.get() | SpotLightChangedFields::OUTER_CONE_COLOR_ANIMATED);
601 host.pending_animations().set(
602 ServerSpotLightProps::id_of_outer_cone_color_property().base(),
603 a.create_instance(host.server(), Some(ExpressionVariant::create(value))),
604 );
605 }
606 host.start_animation_group(&animation, "OuterConeColor", ExpressionVariant::create(value));
608 }
609 }
610 self.outer_cone_color.set(value);
611 if changed {
612 SpotLightHooks::on_outer_cone_color_changed(host);
613 }
614 }
615
616 pub fn outer_cone_intensity(&self) -> f32 {
618 self.outer_cone_intensity.get()
619 }
620
621 pub fn set_outer_cone_intensity(&self, host: &dyn SpotLightHooks, value: f32) {
623 let mut changed = false;
624 let old_value = self.outer_cone_intensity.get();
625 if old_value.differs(&value) {
626 SpotLightHooks::validate_outer_cone_intensity_change(host, old_value, value);
627 SpotLightHooks::on_outer_cone_intensity_changing(host);
628 changed = true;
629 self.outer_cone_intensity.set(value);
631 self.changed_fields_of_spot_light.set(self.changed_fields_of_spot_light.get() | SpotLightChangedFields::OUTER_CONE_INTENSITY);
633 host.register_for_serialization();
634 host.pending_animations().remove(ServerSpotLightProps::id_of_outer_cone_intensity_property().base());
636 self.changed_fields_of_spot_light.set(self.changed_fields_of_spot_light.get() & !SpotLightChangedFields::OUTER_CONE_INTENSITY_ANIMATED);
637 if let Some(animation) = host.implicit_animation("OuterConeIntensity") {
639 if let Some(a) = animation.as_composition_animation() {
641 self.changed_fields_of_spot_light.set(self.changed_fields_of_spot_light.get() | SpotLightChangedFields::OUTER_CONE_INTENSITY_ANIMATED);
642 host.pending_animations().set(
643 ServerSpotLightProps::id_of_outer_cone_intensity_property().base(),
644 a.create_instance(host.server(), Some(ExpressionVariant::create(value))),
645 );
646 }
647 host.start_animation_group(&animation, "OuterConeIntensity", ExpressionVariant::create(value));
649 }
650 }
651 self.outer_cone_intensity.set(value);
652 if changed {
653 SpotLightHooks::on_outer_cone_intensity_changed(host);
654 }
655 }
656
657 pub fn offset(&self) -> Vector3D {
659 self.offset.get()
660 }
661
662 pub fn set_offset(&self, host: &dyn SpotLightHooks, value: Vector3D) {
664 let mut changed = false;
665 let old_value = self.offset.get();
666 if old_value.differs(&value) {
667 SpotLightHooks::validate_offset_change(host, old_value, value);
668 SpotLightHooks::on_offset_changing(host);
669 changed = true;
670 self.offset.set(value);
672 self.changed_fields_of_spot_light.set(self.changed_fields_of_spot_light.get() | SpotLightChangedFields::OFFSET);
674 host.register_for_serialization();
675 host.pending_animations().remove(ServerSpotLightProps::id_of_offset_property().base());
677 self.changed_fields_of_spot_light.set(self.changed_fields_of_spot_light.get() & !SpotLightChangedFields::OFFSET_ANIMATED);
678 if let Some(animation) = host.implicit_animation("Offset") {
680 if let Some(a) = animation.as_composition_animation() {
682 self.changed_fields_of_spot_light.set(self.changed_fields_of_spot_light.get() | SpotLightChangedFields::OFFSET_ANIMATED);
683 host.pending_animations().set(
684 ServerSpotLightProps::id_of_offset_property().base(),
685 a.create_instance(host.server(), Some(ExpressionVariant::create(value))),
686 );
687 }
688 host.start_animation_group(&animation, "Offset", ExpressionVariant::create(value));
690 }
691 }
692 self.offset.set(value);
693 if changed {
694 SpotLightHooks::on_offset_changed(host);
695 }
696 }
697
698 pub fn min_attenuation_cutoff(&self) -> f32 {
700 self.min_attenuation_cutoff.get()
701 }
702
703 pub fn set_min_attenuation_cutoff(&self, host: &dyn SpotLightHooks, value: f32) {
705 let mut changed = false;
706 let old_value = self.min_attenuation_cutoff.get();
707 if old_value.differs(&value) {
708 SpotLightHooks::validate_min_attenuation_cutoff_change(host, old_value, value);
709 SpotLightHooks::on_min_attenuation_cutoff_changing(host);
710 changed = true;
711 self.min_attenuation_cutoff.set(value);
713 self.changed_fields_of_spot_light.set(self.changed_fields_of_spot_light.get() | SpotLightChangedFields::MIN_ATTENUATION_CUTOFF);
715 host.register_for_serialization();
716 host.pending_animations().remove(ServerSpotLightProps::id_of_min_attenuation_cutoff_property().base());
718 self.changed_fields_of_spot_light.set(self.changed_fields_of_spot_light.get() & !SpotLightChangedFields::MIN_ATTENUATION_CUTOFF_ANIMATED);
719 if let Some(animation) = host.implicit_animation("MinAttenuationCutoff") {
721 if let Some(a) = animation.as_composition_animation() {
723 self.changed_fields_of_spot_light.set(self.changed_fields_of_spot_light.get() | SpotLightChangedFields::MIN_ATTENUATION_CUTOFF_ANIMATED);
724 host.pending_animations().set(
725 ServerSpotLightProps::id_of_min_attenuation_cutoff_property().base(),
726 a.create_instance(host.server(), Some(ExpressionVariant::create(value))),
727 );
728 }
729 host.start_animation_group(&animation, "MinAttenuationCutoff", ExpressionVariant::create(value));
731 }
732 }
733 self.min_attenuation_cutoff.set(value);
734 if changed {
735 SpotLightHooks::on_min_attenuation_cutoff_changed(host);
736 }
737 }
738
739 pub fn max_attenuation_cutoff(&self) -> f32 {
741 self.max_attenuation_cutoff.get()
742 }
743
744 pub fn set_max_attenuation_cutoff(&self, host: &dyn SpotLightHooks, value: f32) {
746 let mut changed = false;
747 let old_value = self.max_attenuation_cutoff.get();
748 if old_value.differs(&value) {
749 SpotLightHooks::validate_max_attenuation_cutoff_change(host, old_value, value);
750 SpotLightHooks::on_max_attenuation_cutoff_changing(host);
751 changed = true;
752 self.max_attenuation_cutoff.set(value);
754 self.changed_fields_of_spot_light.set(self.changed_fields_of_spot_light.get() | SpotLightChangedFields::MAX_ATTENUATION_CUTOFF);
756 host.register_for_serialization();
757 host.pending_animations().remove(ServerSpotLightProps::id_of_max_attenuation_cutoff_property().base());
759 self.changed_fields_of_spot_light.set(self.changed_fields_of_spot_light.get() & !SpotLightChangedFields::MAX_ATTENUATION_CUTOFF_ANIMATED);
760 if let Some(animation) = host.implicit_animation("MaxAttenuationCutoff") {
762 if let Some(a) = animation.as_composition_animation() {
764 self.changed_fields_of_spot_light.set(self.changed_fields_of_spot_light.get() | SpotLightChangedFields::MAX_ATTENUATION_CUTOFF_ANIMATED);
765 host.pending_animations().set(
766 ServerSpotLightProps::id_of_max_attenuation_cutoff_property().base(),
767 a.create_instance(host.server(), Some(ExpressionVariant::create(value))),
768 );
769 }
770 host.start_animation_group(&animation, "MaxAttenuationCutoff", ExpressionVariant::create(value));
772 }
773 }
774 self.max_attenuation_cutoff.set(value);
775 if changed {
776 SpotLightHooks::on_max_attenuation_cutoff_changed(host);
777 }
778 }
779
780 pub fn start_animation(
784 &self,
785 host: &dyn SpotLightHooks,
786 property_name: &str,
787 animation: &dyn ICompositionAnimation,
788 final_value: Option<ExpressionVariant>,
789 ) -> bool {
790 if property_name == "ConstantAttenuation" {
791 let server = animation.create_instance(host.server(), final_value);
792 host.pending_animations().set(ServerSpotLightProps::id_of_constant_attenuation_property().base(), server);
793 self.changed_fields_of_spot_light.set(self.changed_fields_of_spot_light.get() | SpotLightChangedFields::CONSTANT_ATTENUATION_ANIMATED);
794 host.register_for_serialization();
795 return true;
796 }
797 if property_name == "LinearAttenuation" {
798 let server = animation.create_instance(host.server(), final_value);
799 host.pending_animations().set(ServerSpotLightProps::id_of_linear_attenuation_property().base(), server);
800 self.changed_fields_of_spot_light.set(self.changed_fields_of_spot_light.get() | SpotLightChangedFields::LINEAR_ATTENUATION_ANIMATED);
801 host.register_for_serialization();
802 return true;
803 }
804 if property_name == "QuadraticAttenuation" {
805 let server = animation.create_instance(host.server(), final_value);
806 host.pending_animations().set(ServerSpotLightProps::id_of_quadratic_attenuation_property().base(), server);
807 self.changed_fields_of_spot_light.set(self.changed_fields_of_spot_light.get() | SpotLightChangedFields::QUADRATIC_ATTENUATION_ANIMATED);
808 host.register_for_serialization();
809 return true;
810 }
811 if property_name == "Direction" {
812 let server = animation.create_instance(host.server(), final_value);
813 host.pending_animations().set(ServerSpotLightProps::id_of_direction_property().base(), server);
814 self.changed_fields_of_spot_light.set(self.changed_fields_of_spot_light.get() | SpotLightChangedFields::DIRECTION_ANIMATED);
815 host.register_for_serialization();
816 return true;
817 }
818 if property_name == "InnerConeAngle" {
819 let server = animation.create_instance(host.server(), final_value);
820 host.pending_animations().set(ServerSpotLightProps::id_of_inner_cone_angle_property().base(), server);
821 self.changed_fields_of_spot_light.set(self.changed_fields_of_spot_light.get() | SpotLightChangedFields::INNER_CONE_ANGLE_ANIMATED);
822 host.register_for_serialization();
823 return true;
824 }
825 if property_name == "InnerConeColor" {
826 let server = animation.create_instance(host.server(), final_value);
827 host.pending_animations().set(ServerSpotLightProps::id_of_inner_cone_color_property().base(), server);
828 self.changed_fields_of_spot_light.set(self.changed_fields_of_spot_light.get() | SpotLightChangedFields::INNER_CONE_COLOR_ANIMATED);
829 host.register_for_serialization();
830 return true;
831 }
832 if property_name == "InnerConeIntensity" {
833 let server = animation.create_instance(host.server(), final_value);
834 host.pending_animations().set(ServerSpotLightProps::id_of_inner_cone_intensity_property().base(), server);
835 self.changed_fields_of_spot_light.set(self.changed_fields_of_spot_light.get() | SpotLightChangedFields::INNER_CONE_INTENSITY_ANIMATED);
836 host.register_for_serialization();
837 return true;
838 }
839 if property_name == "OuterConeAngle" {
840 let server = animation.create_instance(host.server(), final_value);
841 host.pending_animations().set(ServerSpotLightProps::id_of_outer_cone_angle_property().base(), server);
842 self.changed_fields_of_spot_light.set(self.changed_fields_of_spot_light.get() | SpotLightChangedFields::OUTER_CONE_ANGLE_ANIMATED);
843 host.register_for_serialization();
844 return true;
845 }
846 if property_name == "OuterConeColor" {
847 let server = animation.create_instance(host.server(), final_value);
848 host.pending_animations().set(ServerSpotLightProps::id_of_outer_cone_color_property().base(), server);
849 self.changed_fields_of_spot_light.set(self.changed_fields_of_spot_light.get() | SpotLightChangedFields::OUTER_CONE_COLOR_ANIMATED);
850 host.register_for_serialization();
851 return true;
852 }
853 if property_name == "OuterConeIntensity" {
854 let server = animation.create_instance(host.server(), final_value);
855 host.pending_animations().set(ServerSpotLightProps::id_of_outer_cone_intensity_property().base(), server);
856 self.changed_fields_of_spot_light.set(self.changed_fields_of_spot_light.get() | SpotLightChangedFields::OUTER_CONE_INTENSITY_ANIMATED);
857 host.register_for_serialization();
858 return true;
859 }
860 if property_name == "Offset" {
861 let server = animation.create_instance(host.server(), final_value);
862 host.pending_animations().set(ServerSpotLightProps::id_of_offset_property().base(), server);
863 self.changed_fields_of_spot_light.set(self.changed_fields_of_spot_light.get() | SpotLightChangedFields::OFFSET_ANIMATED);
864 host.register_for_serialization();
865 return true;
866 }
867 if property_name == "MinAttenuationCutoff" {
868 let server = animation.create_instance(host.server(), final_value);
869 host.pending_animations().set(ServerSpotLightProps::id_of_min_attenuation_cutoff_property().base(), server);
870 self.changed_fields_of_spot_light.set(self.changed_fields_of_spot_light.get() | SpotLightChangedFields::MIN_ATTENUATION_CUTOFF_ANIMATED);
871 host.register_for_serialization();
872 return true;
873 }
874 if property_name == "MaxAttenuationCutoff" {
875 let server = animation.create_instance(host.server(), final_value);
876 host.pending_animations().set(ServerSpotLightProps::id_of_max_attenuation_cutoff_property().base(), server);
877 self.changed_fields_of_spot_light.set(self.changed_fields_of_spot_light.get() | SpotLightChangedFields::MAX_ATTENUATION_CUTOFF_ANIMATED);
878 host.register_for_serialization();
879 return true;
880 }
881 false
882 }
883
884 pub fn serialize_changes_core(&self, host: &dyn SpotLightHooks, writer: &mut BatchStreamWriter<'_>) {
887 self.base.serialize_changes_core(host, writer);
888 let changed = self.changed_fields_of_spot_light.get();
889 writer.write(changed);
890 if changed.contains(SpotLightChangedFields::CONSTANT_ATTENUATION_ANIMATED) {
891 writer.write_value(host.pending_animations().get_and_remove(ServerSpotLightProps::id_of_constant_attenuation_property().base()));
892 } else if changed.contains(SpotLightChangedFields::CONSTANT_ATTENUATION) {
893 writer.write(self.constant_attenuation.get());
894 }
895 if changed.contains(SpotLightChangedFields::LINEAR_ATTENUATION_ANIMATED) {
896 writer.write_value(host.pending_animations().get_and_remove(ServerSpotLightProps::id_of_linear_attenuation_property().base()));
897 } else if changed.contains(SpotLightChangedFields::LINEAR_ATTENUATION) {
898 writer.write(self.linear_attenuation.get());
899 }
900 if changed.contains(SpotLightChangedFields::QUADRATIC_ATTENUATION_ANIMATED) {
901 writer.write_value(host.pending_animations().get_and_remove(ServerSpotLightProps::id_of_quadratic_attenuation_property().base()));
902 } else if changed.contains(SpotLightChangedFields::QUADRATIC_ATTENUATION) {
903 writer.write(self.quadratic_attenuation.get());
904 }
905 if changed.contains(SpotLightChangedFields::COORDINATE_SPACE) {
906 writer.write_server_object(self.coordinate_space.borrow().clone().map(|o| o.server()));
907 }
908 if changed.contains(SpotLightChangedFields::DIRECTION_ANIMATED) {
909 writer.write_value(host.pending_animations().get_and_remove(ServerSpotLightProps::id_of_direction_property().base()));
910 } else if changed.contains(SpotLightChangedFields::DIRECTION) {
911 writer.write(self.direction.get());
912 }
913 if changed.contains(SpotLightChangedFields::INNER_CONE_ANGLE_ANIMATED) {
914 writer.write_value(host.pending_animations().get_and_remove(ServerSpotLightProps::id_of_inner_cone_angle_property().base()));
915 } else if changed.contains(SpotLightChangedFields::INNER_CONE_ANGLE) {
916 writer.write(self.inner_cone_angle.get());
917 }
918 if changed.contains(SpotLightChangedFields::INNER_CONE_COLOR_ANIMATED) {
919 writer.write_value(host.pending_animations().get_and_remove(ServerSpotLightProps::id_of_inner_cone_color_property().base()));
920 } else if changed.contains(SpotLightChangedFields::INNER_CONE_COLOR) {
921 writer.write(self.inner_cone_color.get());
922 }
923 if changed.contains(SpotLightChangedFields::INNER_CONE_INTENSITY_ANIMATED) {
924 writer.write_value(host.pending_animations().get_and_remove(ServerSpotLightProps::id_of_inner_cone_intensity_property().base()));
925 } else if changed.contains(SpotLightChangedFields::INNER_CONE_INTENSITY) {
926 writer.write(self.inner_cone_intensity.get());
927 }
928 if changed.contains(SpotLightChangedFields::OUTER_CONE_ANGLE_ANIMATED) {
929 writer.write_value(host.pending_animations().get_and_remove(ServerSpotLightProps::id_of_outer_cone_angle_property().base()));
930 } else if changed.contains(SpotLightChangedFields::OUTER_CONE_ANGLE) {
931 writer.write(self.outer_cone_angle.get());
932 }
933 if changed.contains(SpotLightChangedFields::OUTER_CONE_COLOR_ANIMATED) {
934 writer.write_value(host.pending_animations().get_and_remove(ServerSpotLightProps::id_of_outer_cone_color_property().base()));
935 } else if changed.contains(SpotLightChangedFields::OUTER_CONE_COLOR) {
936 writer.write(self.outer_cone_color.get());
937 }
938 if changed.contains(SpotLightChangedFields::OUTER_CONE_INTENSITY_ANIMATED) {
939 writer.write_value(host.pending_animations().get_and_remove(ServerSpotLightProps::id_of_outer_cone_intensity_property().base()));
940 } else if changed.contains(SpotLightChangedFields::OUTER_CONE_INTENSITY) {
941 writer.write(self.outer_cone_intensity.get());
942 }
943 if changed.contains(SpotLightChangedFields::OFFSET_ANIMATED) {
944 writer.write_value(host.pending_animations().get_and_remove(ServerSpotLightProps::id_of_offset_property().base()));
945 } else if changed.contains(SpotLightChangedFields::OFFSET) {
946 writer.write(self.offset.get());
947 }
948 if changed.contains(SpotLightChangedFields::MIN_ATTENUATION_CUTOFF_ANIMATED) {
949 writer.write_value(host.pending_animations().get_and_remove(ServerSpotLightProps::id_of_min_attenuation_cutoff_property().base()));
950 } else if changed.contains(SpotLightChangedFields::MIN_ATTENUATION_CUTOFF) {
951 writer.write(self.min_attenuation_cutoff.get());
952 }
953 if changed.contains(SpotLightChangedFields::MAX_ATTENUATION_CUTOFF_ANIMATED) {
954 writer.write_value(host.pending_animations().get_and_remove(ServerSpotLightProps::id_of_max_attenuation_cutoff_property().base()));
955 } else if changed.contains(SpotLightChangedFields::MAX_ATTENUATION_CUTOFF) {
956 writer.write(self.max_attenuation_cutoff.get());
957 }
958 self.changed_fields_of_spot_light.set(SpotLightChangedFields::empty());
959 }
960}
961
962pub trait ServerSpotLightHooks: ServerCompositionLightHooks {
965 fn deserialize_changes_extra(&self, _reader: &mut BatchStreamReader<'_>) {}
966 fn on_fields_deserialized(&self, _changed: SpotLightChangedFields) {}
967
968 fn on_coordinate_space_changed(&self) {}
969 fn on_coordinate_space_changing(&self) {}
970}
971
972pub struct ServerSpotLightProps {
974 base: ServerCompositionLightProps,
975 constant_attenuation: Cell<f32>,
976 linear_attenuation: Cell<f32>,
977 quadratic_attenuation: Cell<f32>,
978 coordinate_space: RefCell<Option<Rc<dyn IServerObject>>>,
979 direction: Cell<Vector3D>,
980 inner_cone_angle: Cell<f32>,
981 inner_cone_color: Cell<Color>,
982 inner_cone_intensity: Cell<f32>,
983 outer_cone_angle: Cell<f32>,
984 outer_cone_color: Cell<Color>,
985 outer_cone_intensity: Cell<f32>,
986 offset: Cell<Vector3D>,
987 min_attenuation_cutoff: Cell<f32>,
988 max_attenuation_cutoff: Cell<f32>,
989}
990
991impl Default for ServerSpotLightProps {
992 fn default() -> Self {
993 Self::new()
994 }
995}
996
997impl ServerSpotLightProps {
998 pub fn new() -> Self {
1001 Self {
1002 base: ServerCompositionLightProps::new(),
1003 constant_attenuation: Cell::new(Default::default()),
1004 linear_attenuation: Cell::new(Default::default()),
1005 quadratic_attenuation: Cell::new(Default::default()),
1006 coordinate_space: RefCell::new(None),
1007 direction: Cell::new(Default::default()),
1008 inner_cone_angle: Cell::new(Default::default()),
1009 inner_cone_color: Cell::new(Default::default()),
1010 inner_cone_intensity: Cell::new(Default::default()),
1011 outer_cone_angle: Cell::new(Default::default()),
1012 outer_cone_color: Cell::new(Default::default()),
1013 outer_cone_intensity: Cell::new(Default::default()),
1014 offset: Cell::new(Default::default()),
1015 min_attenuation_cutoff: Cell::new(Default::default()),
1016 max_attenuation_cutoff: Cell::new(Default::default()),
1017 }
1018 }
1019
1020 pub fn base(&self) -> &ServerCompositionLightProps {
1022 &self.base
1023 }
1024
1025 pub fn find_props(&self, type_id: TypeId) -> Option<&dyn Any> {
1028 if type_id == TypeId::of::<Self>() {
1029 return Some(self);
1030 }
1031 self.base.find_props(type_id)
1032 }
1033
1034 pub fn id_of_constant_attenuation_property() -> &'static CompositionPropertyOf<f32> {
1036 static PROPERTY: OnceLock<CompositionPropertyOf<f32>> = OnceLock::new();
1037 PROPERTY.get_or_init(|| {
1038 CompositionProperty::register::<ServerSpotLightProps, f32>(
1039 "ConstantAttenuation",
1040 "ServerSpotLight",
1041 |obj| props_of::<ServerSpotLightProps>(obj).constant_attenuation.get(),
1042 |obj, v| props_of::<ServerSpotLightProps>(obj).constant_attenuation.set(v),
1043 Some(|obj| ExpressionVariant::create(props_of::<ServerSpotLightProps>(obj).constant_attenuation.get())),
1044 Some(|obj, v: ExpressionVariant| props_of::<ServerSpotLightProps>(obj).constant_attenuation.set(v.cast_or_default::<f32>())),
1045 )
1046 })
1047 }
1048
1049 pub fn constant_attenuation(&self) -> f32 {
1051 self.constant_attenuation.get()
1052 }
1053
1054 pub fn set_constant_attenuation(&self, host: &dyn ServerSpotLightHooks, value: f32) {
1055 self.constant_attenuation.set(value);
1056 host.remove_animation_for_property(Self::id_of_constant_attenuation_property().base());
1057 }
1058
1059 pub fn id_of_linear_attenuation_property() -> &'static CompositionPropertyOf<f32> {
1061 static PROPERTY: OnceLock<CompositionPropertyOf<f32>> = OnceLock::new();
1062 PROPERTY.get_or_init(|| {
1063 CompositionProperty::register::<ServerSpotLightProps, f32>(
1064 "LinearAttenuation",
1065 "ServerSpotLight",
1066 |obj| props_of::<ServerSpotLightProps>(obj).linear_attenuation.get(),
1067 |obj, v| props_of::<ServerSpotLightProps>(obj).linear_attenuation.set(v),
1068 Some(|obj| ExpressionVariant::create(props_of::<ServerSpotLightProps>(obj).linear_attenuation.get())),
1069 Some(|obj, v: ExpressionVariant| props_of::<ServerSpotLightProps>(obj).linear_attenuation.set(v.cast_or_default::<f32>())),
1070 )
1071 })
1072 }
1073
1074 pub fn linear_attenuation(&self) -> f32 {
1076 self.linear_attenuation.get()
1077 }
1078
1079 pub fn set_linear_attenuation(&self, host: &dyn ServerSpotLightHooks, value: f32) {
1080 self.linear_attenuation.set(value);
1081 host.remove_animation_for_property(Self::id_of_linear_attenuation_property().base());
1082 }
1083
1084 pub fn id_of_quadratic_attenuation_property() -> &'static CompositionPropertyOf<f32> {
1086 static PROPERTY: OnceLock<CompositionPropertyOf<f32>> = OnceLock::new();
1087 PROPERTY.get_or_init(|| {
1088 CompositionProperty::register::<ServerSpotLightProps, f32>(
1089 "QuadraticAttenuation",
1090 "ServerSpotLight",
1091 |obj| props_of::<ServerSpotLightProps>(obj).quadratic_attenuation.get(),
1092 |obj, v| props_of::<ServerSpotLightProps>(obj).quadratic_attenuation.set(v),
1093 Some(|obj| ExpressionVariant::create(props_of::<ServerSpotLightProps>(obj).quadratic_attenuation.get())),
1094 Some(|obj, v: ExpressionVariant| props_of::<ServerSpotLightProps>(obj).quadratic_attenuation.set(v.cast_or_default::<f32>())),
1095 )
1096 })
1097 }
1098
1099 pub fn quadratic_attenuation(&self) -> f32 {
1101 self.quadratic_attenuation.get()
1102 }
1103
1104 pub fn set_quadratic_attenuation(&self, host: &dyn ServerSpotLightHooks, value: f32) {
1105 self.quadratic_attenuation.set(value);
1106 host.remove_animation_for_property(Self::id_of_quadratic_attenuation_property().base());
1107 }
1108
1109 pub fn id_of_coordinate_space_property() -> &'static CompositionPropertyOf<Option<Rc<dyn IServerObject>>> {
1111 static PROPERTY: OnceLock<CompositionPropertyOf<Option<Rc<dyn IServerObject>>>> = OnceLock::new();
1112 PROPERTY.get_or_init(|| {
1113 CompositionProperty::register::<ServerSpotLightProps, Option<Rc<dyn IServerObject>>>(
1114 "CoordinateSpace",
1115 "ServerSpotLight",
1116 |obj| props_of::<ServerSpotLightProps>(obj).coordinate_space.borrow().clone(),
1117 |obj, v| *props_of::<ServerSpotLightProps>(obj).coordinate_space.borrow_mut() = v,
1118 None,
1119 None,
1120 )
1121 })
1122 }
1123
1124 pub fn coordinate_space(&self) -> Option<Rc<dyn IServerObject>> {
1126 self.coordinate_space.borrow().clone()
1127 }
1128
1129 pub fn set_coordinate_space(&self, host: &dyn ServerSpotLightHooks, value: Option<Rc<dyn IServerObject>>) {
1130 let old_value = self.coordinate_space.borrow().clone();
1131 let mut changed = false;
1132 if old_value.differs(&value) {
1133 ServerSpotLightHooks::on_coordinate_space_changing(host);
1134 changed = true;
1135 }
1136 host.set_value(
1137 Self::id_of_coordinate_space_property().base(),
1138 ServerValueChange {
1139 equal: old_value.default_equals(&value),
1140 old_resource: old_value.as_server_render_resource(),
1141 new_resource: value.as_server_render_resource(),
1142 assign: &mut || *self.coordinate_space.borrow_mut() = value.clone(),
1143 },
1144 );
1145 if changed {
1146 ServerSpotLightHooks::on_coordinate_space_changed(host);
1147 }
1148 }
1149
1150 pub fn id_of_direction_property() -> &'static CompositionPropertyOf<Vector3D> {
1152 static PROPERTY: OnceLock<CompositionPropertyOf<Vector3D>> = OnceLock::new();
1153 PROPERTY.get_or_init(|| {
1154 CompositionProperty::register::<ServerSpotLightProps, Vector3D>(
1155 "Direction",
1156 "ServerSpotLight",
1157 |obj| props_of::<ServerSpotLightProps>(obj).direction.get(),
1158 |obj, v| props_of::<ServerSpotLightProps>(obj).direction.set(v),
1159 Some(|obj| ExpressionVariant::create(props_of::<ServerSpotLightProps>(obj).direction.get())),
1160 Some(|obj, v: ExpressionVariant| props_of::<ServerSpotLightProps>(obj).direction.set(v.cast_or_default::<Vector3D>())),
1161 )
1162 })
1163 }
1164
1165 pub fn direction(&self) -> Vector3D {
1167 self.direction.get()
1168 }
1169
1170 pub fn set_direction(&self, host: &dyn ServerSpotLightHooks, value: Vector3D) {
1171 self.direction.set(value);
1172 host.remove_animation_for_property(Self::id_of_direction_property().base());
1173 }
1174
1175 pub fn id_of_inner_cone_angle_property() -> &'static CompositionPropertyOf<f32> {
1177 static PROPERTY: OnceLock<CompositionPropertyOf<f32>> = OnceLock::new();
1178 PROPERTY.get_or_init(|| {
1179 CompositionProperty::register::<ServerSpotLightProps, f32>(
1180 "InnerConeAngle",
1181 "ServerSpotLight",
1182 |obj| props_of::<ServerSpotLightProps>(obj).inner_cone_angle.get(),
1183 |obj, v| props_of::<ServerSpotLightProps>(obj).inner_cone_angle.set(v),
1184 Some(|obj| ExpressionVariant::create(props_of::<ServerSpotLightProps>(obj).inner_cone_angle.get())),
1185 Some(|obj, v: ExpressionVariant| props_of::<ServerSpotLightProps>(obj).inner_cone_angle.set(v.cast_or_default::<f32>())),
1186 )
1187 })
1188 }
1189
1190 pub fn inner_cone_angle(&self) -> f32 {
1192 self.inner_cone_angle.get()
1193 }
1194
1195 pub fn set_inner_cone_angle(&self, host: &dyn ServerSpotLightHooks, value: f32) {
1196 self.inner_cone_angle.set(value);
1197 host.remove_animation_for_property(Self::id_of_inner_cone_angle_property().base());
1198 }
1199
1200 pub fn id_of_inner_cone_color_property() -> &'static CompositionPropertyOf<Color> {
1202 static PROPERTY: OnceLock<CompositionPropertyOf<Color>> = OnceLock::new();
1203 PROPERTY.get_or_init(|| {
1204 CompositionProperty::register::<ServerSpotLightProps, Color>(
1205 "InnerConeColor",
1206 "ServerSpotLight",
1207 |obj| props_of::<ServerSpotLightProps>(obj).inner_cone_color.get(),
1208 |obj, v| props_of::<ServerSpotLightProps>(obj).inner_cone_color.set(v),
1209 Some(|obj| ExpressionVariant::create(props_of::<ServerSpotLightProps>(obj).inner_cone_color.get())),
1210 Some(|obj, v: ExpressionVariant| props_of::<ServerSpotLightProps>(obj).inner_cone_color.set(v.cast_or_default::<Color>())),
1211 )
1212 })
1213 }
1214
1215 pub fn inner_cone_color(&self) -> Color {
1217 self.inner_cone_color.get()
1218 }
1219
1220 pub fn set_inner_cone_color(&self, host: &dyn ServerSpotLightHooks, value: Color) {
1221 self.inner_cone_color.set(value);
1222 host.remove_animation_for_property(Self::id_of_inner_cone_color_property().base());
1223 }
1224
1225 pub fn id_of_inner_cone_intensity_property() -> &'static CompositionPropertyOf<f32> {
1227 static PROPERTY: OnceLock<CompositionPropertyOf<f32>> = OnceLock::new();
1228 PROPERTY.get_or_init(|| {
1229 CompositionProperty::register::<ServerSpotLightProps, f32>(
1230 "InnerConeIntensity",
1231 "ServerSpotLight",
1232 |obj| props_of::<ServerSpotLightProps>(obj).inner_cone_intensity.get(),
1233 |obj, v| props_of::<ServerSpotLightProps>(obj).inner_cone_intensity.set(v),
1234 Some(|obj| ExpressionVariant::create(props_of::<ServerSpotLightProps>(obj).inner_cone_intensity.get())),
1235 Some(|obj, v: ExpressionVariant| props_of::<ServerSpotLightProps>(obj).inner_cone_intensity.set(v.cast_or_default::<f32>())),
1236 )
1237 })
1238 }
1239
1240 pub fn inner_cone_intensity(&self) -> f32 {
1242 self.inner_cone_intensity.get()
1243 }
1244
1245 pub fn set_inner_cone_intensity(&self, host: &dyn ServerSpotLightHooks, value: f32) {
1246 self.inner_cone_intensity.set(value);
1247 host.remove_animation_for_property(Self::id_of_inner_cone_intensity_property().base());
1248 }
1249
1250 pub fn id_of_outer_cone_angle_property() -> &'static CompositionPropertyOf<f32> {
1252 static PROPERTY: OnceLock<CompositionPropertyOf<f32>> = OnceLock::new();
1253 PROPERTY.get_or_init(|| {
1254 CompositionProperty::register::<ServerSpotLightProps, f32>(
1255 "OuterConeAngle",
1256 "ServerSpotLight",
1257 |obj| props_of::<ServerSpotLightProps>(obj).outer_cone_angle.get(),
1258 |obj, v| props_of::<ServerSpotLightProps>(obj).outer_cone_angle.set(v),
1259 Some(|obj| ExpressionVariant::create(props_of::<ServerSpotLightProps>(obj).outer_cone_angle.get())),
1260 Some(|obj, v: ExpressionVariant| props_of::<ServerSpotLightProps>(obj).outer_cone_angle.set(v.cast_or_default::<f32>())),
1261 )
1262 })
1263 }
1264
1265 pub fn outer_cone_angle(&self) -> f32 {
1267 self.outer_cone_angle.get()
1268 }
1269
1270 pub fn set_outer_cone_angle(&self, host: &dyn ServerSpotLightHooks, value: f32) {
1271 self.outer_cone_angle.set(value);
1272 host.remove_animation_for_property(Self::id_of_outer_cone_angle_property().base());
1273 }
1274
1275 pub fn id_of_outer_cone_color_property() -> &'static CompositionPropertyOf<Color> {
1277 static PROPERTY: OnceLock<CompositionPropertyOf<Color>> = OnceLock::new();
1278 PROPERTY.get_or_init(|| {
1279 CompositionProperty::register::<ServerSpotLightProps, Color>(
1280 "OuterConeColor",
1281 "ServerSpotLight",
1282 |obj| props_of::<ServerSpotLightProps>(obj).outer_cone_color.get(),
1283 |obj, v| props_of::<ServerSpotLightProps>(obj).outer_cone_color.set(v),
1284 Some(|obj| ExpressionVariant::create(props_of::<ServerSpotLightProps>(obj).outer_cone_color.get())),
1285 Some(|obj, v: ExpressionVariant| props_of::<ServerSpotLightProps>(obj).outer_cone_color.set(v.cast_or_default::<Color>())),
1286 )
1287 })
1288 }
1289
1290 pub fn outer_cone_color(&self) -> Color {
1292 self.outer_cone_color.get()
1293 }
1294
1295 pub fn set_outer_cone_color(&self, host: &dyn ServerSpotLightHooks, value: Color) {
1296 self.outer_cone_color.set(value);
1297 host.remove_animation_for_property(Self::id_of_outer_cone_color_property().base());
1298 }
1299
1300 pub fn id_of_outer_cone_intensity_property() -> &'static CompositionPropertyOf<f32> {
1302 static PROPERTY: OnceLock<CompositionPropertyOf<f32>> = OnceLock::new();
1303 PROPERTY.get_or_init(|| {
1304 CompositionProperty::register::<ServerSpotLightProps, f32>(
1305 "OuterConeIntensity",
1306 "ServerSpotLight",
1307 |obj| props_of::<ServerSpotLightProps>(obj).outer_cone_intensity.get(),
1308 |obj, v| props_of::<ServerSpotLightProps>(obj).outer_cone_intensity.set(v),
1309 Some(|obj| ExpressionVariant::create(props_of::<ServerSpotLightProps>(obj).outer_cone_intensity.get())),
1310 Some(|obj, v: ExpressionVariant| props_of::<ServerSpotLightProps>(obj).outer_cone_intensity.set(v.cast_or_default::<f32>())),
1311 )
1312 })
1313 }
1314
1315 pub fn outer_cone_intensity(&self) -> f32 {
1317 self.outer_cone_intensity.get()
1318 }
1319
1320 pub fn set_outer_cone_intensity(&self, host: &dyn ServerSpotLightHooks, value: f32) {
1321 self.outer_cone_intensity.set(value);
1322 host.remove_animation_for_property(Self::id_of_outer_cone_intensity_property().base());
1323 }
1324
1325 pub fn id_of_offset_property() -> &'static CompositionPropertyOf<Vector3D> {
1327 static PROPERTY: OnceLock<CompositionPropertyOf<Vector3D>> = OnceLock::new();
1328 PROPERTY.get_or_init(|| {
1329 CompositionProperty::register::<ServerSpotLightProps, Vector3D>(
1330 "Offset",
1331 "ServerSpotLight",
1332 |obj| props_of::<ServerSpotLightProps>(obj).offset.get(),
1333 |obj, v| props_of::<ServerSpotLightProps>(obj).offset.set(v),
1334 Some(|obj| ExpressionVariant::create(props_of::<ServerSpotLightProps>(obj).offset.get())),
1335 Some(|obj, v: ExpressionVariant| props_of::<ServerSpotLightProps>(obj).offset.set(v.cast_or_default::<Vector3D>())),
1336 )
1337 })
1338 }
1339
1340 pub fn offset(&self) -> Vector3D {
1342 self.offset.get()
1343 }
1344
1345 pub fn set_offset(&self, host: &dyn ServerSpotLightHooks, value: Vector3D) {
1346 self.offset.set(value);
1347 host.remove_animation_for_property(Self::id_of_offset_property().base());
1348 }
1349
1350 pub fn id_of_min_attenuation_cutoff_property() -> &'static CompositionPropertyOf<f32> {
1352 static PROPERTY: OnceLock<CompositionPropertyOf<f32>> = OnceLock::new();
1353 PROPERTY.get_or_init(|| {
1354 CompositionProperty::register::<ServerSpotLightProps, f32>(
1355 "MinAttenuationCutoff",
1356 "ServerSpotLight",
1357 |obj| props_of::<ServerSpotLightProps>(obj).min_attenuation_cutoff.get(),
1358 |obj, v| props_of::<ServerSpotLightProps>(obj).min_attenuation_cutoff.set(v),
1359 Some(|obj| ExpressionVariant::create(props_of::<ServerSpotLightProps>(obj).min_attenuation_cutoff.get())),
1360 Some(|obj, v: ExpressionVariant| props_of::<ServerSpotLightProps>(obj).min_attenuation_cutoff.set(v.cast_or_default::<f32>())),
1361 )
1362 })
1363 }
1364
1365 pub fn min_attenuation_cutoff(&self) -> f32 {
1367 self.min_attenuation_cutoff.get()
1368 }
1369
1370 pub fn set_min_attenuation_cutoff(&self, host: &dyn ServerSpotLightHooks, value: f32) {
1371 self.min_attenuation_cutoff.set(value);
1372 host.remove_animation_for_property(Self::id_of_min_attenuation_cutoff_property().base());
1373 }
1374
1375 pub fn id_of_max_attenuation_cutoff_property() -> &'static CompositionPropertyOf<f32> {
1377 static PROPERTY: OnceLock<CompositionPropertyOf<f32>> = OnceLock::new();
1378 PROPERTY.get_or_init(|| {
1379 CompositionProperty::register::<ServerSpotLightProps, f32>(
1380 "MaxAttenuationCutoff",
1381 "ServerSpotLight",
1382 |obj| props_of::<ServerSpotLightProps>(obj).max_attenuation_cutoff.get(),
1383 |obj, v| props_of::<ServerSpotLightProps>(obj).max_attenuation_cutoff.set(v),
1384 Some(|obj| ExpressionVariant::create(props_of::<ServerSpotLightProps>(obj).max_attenuation_cutoff.get())),
1385 Some(|obj, v: ExpressionVariant| props_of::<ServerSpotLightProps>(obj).max_attenuation_cutoff.set(v.cast_or_default::<f32>())),
1386 )
1387 })
1388 }
1389
1390 pub fn max_attenuation_cutoff(&self) -> f32 {
1392 self.max_attenuation_cutoff.get()
1393 }
1394
1395 pub fn set_max_attenuation_cutoff(&self, host: &dyn ServerSpotLightHooks, value: f32) {
1396 self.max_attenuation_cutoff.set(value);
1397 host.remove_animation_for_property(Self::id_of_max_attenuation_cutoff_property().base());
1398 }
1399
1400 pub fn deserialize_changes_core(
1403 &self,
1404 host: &dyn ServerSpotLightHooks,
1405 reader: &mut BatchStreamReader<'_>,
1406 committed_at: Duration,
1407 ) {
1408 let compositor = host.server_compositor();
1409 self.base.deserialize_changes_core(host, reader, committed_at);
1410 ServerSpotLightHooks::deserialize_changes_extra(host, reader);
1411 let changed = reader.read::<SpotLightChangedFields>();
1412 if changed.contains(SpotLightChangedFields::CONSTANT_ATTENUATION_ANIMATED) {
1413 let Some(animation) = reader.read_value::<AnimationInstanceFactory>().map(AnimationInstanceFactory::create) else {
1414 panic!("a batch starts an animation of `ConstantAttenuation` without an animation instance");
1415 };
1416 host.set_animated_value(
1417 Self::id_of_constant_attenuation_property().base(),
1418 ExpressionVariant::create(self.constant_attenuation.get()),
1419 committed_at,
1420 animation,
1421 );
1422 } else if changed.contains(SpotLightChangedFields::CONSTANT_ATTENUATION) {
1423 let value = reader.read::<f32>();
1424 self.set_constant_attenuation(host, value);
1425 }
1426 if changed.contains(SpotLightChangedFields::LINEAR_ATTENUATION_ANIMATED) {
1427 let Some(animation) = reader.read_value::<AnimationInstanceFactory>().map(AnimationInstanceFactory::create) else {
1428 panic!("a batch starts an animation of `LinearAttenuation` without an animation instance");
1429 };
1430 host.set_animated_value(
1431 Self::id_of_linear_attenuation_property().base(),
1432 ExpressionVariant::create(self.linear_attenuation.get()),
1433 committed_at,
1434 animation,
1435 );
1436 } else if changed.contains(SpotLightChangedFields::LINEAR_ATTENUATION) {
1437 let value = reader.read::<f32>();
1438 self.set_linear_attenuation(host, value);
1439 }
1440 if changed.contains(SpotLightChangedFields::QUADRATIC_ATTENUATION_ANIMATED) {
1441 let Some(animation) = reader.read_value::<AnimationInstanceFactory>().map(AnimationInstanceFactory::create) else {
1442 panic!("a batch starts an animation of `QuadraticAttenuation` without an animation instance");
1443 };
1444 host.set_animated_value(
1445 Self::id_of_quadratic_attenuation_property().base(),
1446 ExpressionVariant::create(self.quadratic_attenuation.get()),
1447 committed_at,
1448 animation,
1449 );
1450 } else if changed.contains(SpotLightChangedFields::QUADRATIC_ATTENUATION) {
1451 let value = reader.read::<f32>();
1452 self.set_quadratic_attenuation(host, value);
1453 }
1454 if changed.contains(SpotLightChangedFields::COORDINATE_SPACE) {
1455 let value = read_server_object(reader, compositor.as_ref());
1456 self.set_coordinate_space(host, value);
1457 }
1458 if changed.contains(SpotLightChangedFields::DIRECTION_ANIMATED) {
1459 let Some(animation) = reader.read_value::<AnimationInstanceFactory>().map(AnimationInstanceFactory::create) else {
1460 panic!("a batch starts an animation of `Direction` without an animation instance");
1461 };
1462 host.set_animated_value(
1463 Self::id_of_direction_property().base(),
1464 ExpressionVariant::create(self.direction.get()),
1465 committed_at,
1466 animation,
1467 );
1468 } else if changed.contains(SpotLightChangedFields::DIRECTION) {
1469 let value = reader.read::<Vector3D>();
1470 self.set_direction(host, value);
1471 }
1472 if changed.contains(SpotLightChangedFields::INNER_CONE_ANGLE_ANIMATED) {
1473 let Some(animation) = reader.read_value::<AnimationInstanceFactory>().map(AnimationInstanceFactory::create) else {
1474 panic!("a batch starts an animation of `InnerConeAngle` without an animation instance");
1475 };
1476 host.set_animated_value(
1477 Self::id_of_inner_cone_angle_property().base(),
1478 ExpressionVariant::create(self.inner_cone_angle.get()),
1479 committed_at,
1480 animation,
1481 );
1482 } else if changed.contains(SpotLightChangedFields::INNER_CONE_ANGLE) {
1483 let value = reader.read::<f32>();
1484 self.set_inner_cone_angle(host, value);
1485 }
1486 if changed.contains(SpotLightChangedFields::INNER_CONE_COLOR_ANIMATED) {
1487 let Some(animation) = reader.read_value::<AnimationInstanceFactory>().map(AnimationInstanceFactory::create) else {
1488 panic!("a batch starts an animation of `InnerConeColor` without an animation instance");
1489 };
1490 host.set_animated_value(
1491 Self::id_of_inner_cone_color_property().base(),
1492 ExpressionVariant::create(self.inner_cone_color.get()),
1493 committed_at,
1494 animation,
1495 );
1496 } else if changed.contains(SpotLightChangedFields::INNER_CONE_COLOR) {
1497 let value = reader.read::<Color>();
1498 self.set_inner_cone_color(host, value);
1499 }
1500 if changed.contains(SpotLightChangedFields::INNER_CONE_INTENSITY_ANIMATED) {
1501 let Some(animation) = reader.read_value::<AnimationInstanceFactory>().map(AnimationInstanceFactory::create) else {
1502 panic!("a batch starts an animation of `InnerConeIntensity` without an animation instance");
1503 };
1504 host.set_animated_value(
1505 Self::id_of_inner_cone_intensity_property().base(),
1506 ExpressionVariant::create(self.inner_cone_intensity.get()),
1507 committed_at,
1508 animation,
1509 );
1510 } else if changed.contains(SpotLightChangedFields::INNER_CONE_INTENSITY) {
1511 let value = reader.read::<f32>();
1512 self.set_inner_cone_intensity(host, value);
1513 }
1514 if changed.contains(SpotLightChangedFields::OUTER_CONE_ANGLE_ANIMATED) {
1515 let Some(animation) = reader.read_value::<AnimationInstanceFactory>().map(AnimationInstanceFactory::create) else {
1516 panic!("a batch starts an animation of `OuterConeAngle` without an animation instance");
1517 };
1518 host.set_animated_value(
1519 Self::id_of_outer_cone_angle_property().base(),
1520 ExpressionVariant::create(self.outer_cone_angle.get()),
1521 committed_at,
1522 animation,
1523 );
1524 } else if changed.contains(SpotLightChangedFields::OUTER_CONE_ANGLE) {
1525 let value = reader.read::<f32>();
1526 self.set_outer_cone_angle(host, value);
1527 }
1528 if changed.contains(SpotLightChangedFields::OUTER_CONE_COLOR_ANIMATED) {
1529 let Some(animation) = reader.read_value::<AnimationInstanceFactory>().map(AnimationInstanceFactory::create) else {
1530 panic!("a batch starts an animation of `OuterConeColor` without an animation instance");
1531 };
1532 host.set_animated_value(
1533 Self::id_of_outer_cone_color_property().base(),
1534 ExpressionVariant::create(self.outer_cone_color.get()),
1535 committed_at,
1536 animation,
1537 );
1538 } else if changed.contains(SpotLightChangedFields::OUTER_CONE_COLOR) {
1539 let value = reader.read::<Color>();
1540 self.set_outer_cone_color(host, value);
1541 }
1542 if changed.contains(SpotLightChangedFields::OUTER_CONE_INTENSITY_ANIMATED) {
1543 let Some(animation) = reader.read_value::<AnimationInstanceFactory>().map(AnimationInstanceFactory::create) else {
1544 panic!("a batch starts an animation of `OuterConeIntensity` without an animation instance");
1545 };
1546 host.set_animated_value(
1547 Self::id_of_outer_cone_intensity_property().base(),
1548 ExpressionVariant::create(self.outer_cone_intensity.get()),
1549 committed_at,
1550 animation,
1551 );
1552 } else if changed.contains(SpotLightChangedFields::OUTER_CONE_INTENSITY) {
1553 let value = reader.read::<f32>();
1554 self.set_outer_cone_intensity(host, value);
1555 }
1556 if changed.contains(SpotLightChangedFields::OFFSET_ANIMATED) {
1557 let Some(animation) = reader.read_value::<AnimationInstanceFactory>().map(AnimationInstanceFactory::create) else {
1558 panic!("a batch starts an animation of `Offset` without an animation instance");
1559 };
1560 host.set_animated_value(
1561 Self::id_of_offset_property().base(),
1562 ExpressionVariant::create(self.offset.get()),
1563 committed_at,
1564 animation,
1565 );
1566 } else if changed.contains(SpotLightChangedFields::OFFSET) {
1567 let value = reader.read::<Vector3D>();
1568 self.set_offset(host, value);
1569 }
1570 if changed.contains(SpotLightChangedFields::MIN_ATTENUATION_CUTOFF_ANIMATED) {
1571 let Some(animation) = reader.read_value::<AnimationInstanceFactory>().map(AnimationInstanceFactory::create) else {
1572 panic!("a batch starts an animation of `MinAttenuationCutoff` without an animation instance");
1573 };
1574 host.set_animated_value(
1575 Self::id_of_min_attenuation_cutoff_property().base(),
1576 ExpressionVariant::create(self.min_attenuation_cutoff.get()),
1577 committed_at,
1578 animation,
1579 );
1580 } else if changed.contains(SpotLightChangedFields::MIN_ATTENUATION_CUTOFF) {
1581 let value = reader.read::<f32>();
1582 self.set_min_attenuation_cutoff(host, value);
1583 }
1584 if changed.contains(SpotLightChangedFields::MAX_ATTENUATION_CUTOFF_ANIMATED) {
1585 let Some(animation) = reader.read_value::<AnimationInstanceFactory>().map(AnimationInstanceFactory::create) else {
1586 panic!("a batch starts an animation of `MaxAttenuationCutoff` without an animation instance");
1587 };
1588 host.set_animated_value(
1589 Self::id_of_max_attenuation_cutoff_property().base(),
1590 ExpressionVariant::create(self.max_attenuation_cutoff.get()),
1591 committed_at,
1592 animation,
1593 );
1594 } else if changed.contains(SpotLightChangedFields::MAX_ATTENUATION_CUTOFF) {
1595 let value = reader.read::<f32>();
1596 self.set_max_attenuation_cutoff(host, value);
1597 }
1598 ServerSpotLightHooks::on_fields_deserialized(host, changed);
1599 }
1600
1601 pub fn get_composition_property(name: &str) -> Option<&'static CompositionProperty> {
1604 if name == "ConstantAttenuation" {
1605 return Some(Self::id_of_constant_attenuation_property().base());
1606 }
1607 if name == "LinearAttenuation" {
1608 return Some(Self::id_of_linear_attenuation_property().base());
1609 }
1610 if name == "QuadraticAttenuation" {
1611 return Some(Self::id_of_quadratic_attenuation_property().base());
1612 }
1613 if name == "Direction" {
1614 return Some(Self::id_of_direction_property().base());
1615 }
1616 if name == "InnerConeAngle" {
1617 return Some(Self::id_of_inner_cone_angle_property().base());
1618 }
1619 if name == "InnerConeColor" {
1620 return Some(Self::id_of_inner_cone_color_property().base());
1621 }
1622 if name == "InnerConeIntensity" {
1623 return Some(Self::id_of_inner_cone_intensity_property().base());
1624 }
1625 if name == "OuterConeAngle" {
1626 return Some(Self::id_of_outer_cone_angle_property().base());
1627 }
1628 if name == "OuterConeColor" {
1629 return Some(Self::id_of_outer_cone_color_property().base());
1630 }
1631 if name == "OuterConeIntensity" {
1632 return Some(Self::id_of_outer_cone_intensity_property().base());
1633 }
1634 if name == "Offset" {
1635 return Some(Self::id_of_offset_property().base());
1636 }
1637 if name == "MinAttenuationCutoff" {
1638 return Some(Self::id_of_min_attenuation_cutoff_property().base());
1639 }
1640 if name == "MaxAttenuationCutoff" {
1641 return Some(Self::id_of_max_attenuation_cutoff_property().base());
1642 }
1643 ServerCompositionLightProps::get_composition_property(name)
1644 }
1645}