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 PointLightChangedFields: u32 {
42 const COLOR = 1;
43 const COLOR_ANIMATED = 2;
44 const CONSTANT_ATTENUATION = 4;
45 const CONSTANT_ATTENUATION_ANIMATED = 8;
46 const LINEAR_ATTENUATION = 16;
47 const LINEAR_ATTENUATION_ANIMATED = 32;
48 const QUADRATIC_ATTENUATION = 64;
49 const QUADRATIC_ATTENUATION_ANIMATED = 128;
50 const COORDINATE_SPACE = 256;
51 const OFFSET = 512;
52 const OFFSET_ANIMATED = 1024;
53 const INTENSITY = 2048;
54 const INTENSITY_ANIMATED = 4096;
55 const MIN_ATTENUATION_CUTOFF = 8192;
56 const MIN_ATTENUATION_CUTOFF_ANIMATED = 16384;
57 const MAX_ATTENUATION_CUTOFF = 32768;
58 const MAX_ATTENUATION_CUTOFF_ANIMATED = 65536;
59 }
60}
61
62impl BatchValue for PointLightChangedFields {
63 #[inline]
64 fn write_to(&self, out: &mut Vec<u8>) {
65 self.bits().write_to(out);
66 }
67
68 #[inline]
69 fn read_from(input: &mut BatchValueReader<'_>) -> Self {
70 PointLightChangedFields::from_bits_retain(u32::read_from(input))
71 }
72}
73
74pub trait PointLightHooks: CompositionLightHooks {
77 fn initialize_defaults_extra(&self) {}
78
79 fn validate_color_change(&self, _old_value: Color, _new_value: Color) {}
80 fn on_color_changed(&self) {}
81 fn on_color_changing(&self) {}
82
83 fn validate_constant_attenuation_change(&self, _old_value: f32, _new_value: f32) {}
84 fn on_constant_attenuation_changed(&self) {}
85 fn on_constant_attenuation_changing(&self) {}
86
87 fn validate_linear_attenuation_change(&self, _old_value: f32, _new_value: f32) {}
88 fn on_linear_attenuation_changed(&self) {}
89 fn on_linear_attenuation_changing(&self) {}
90
91 fn validate_quadratic_attenuation_change(&self, _old_value: f32, _new_value: f32) {}
92 fn on_quadratic_attenuation_changed(&self) {}
93 fn on_quadratic_attenuation_changing(&self) {}
94
95 fn validate_coordinate_space_change(&self, _old_value: &Option<Rc<dyn ICompositionObject>>, _new_value: &Option<Rc<dyn ICompositionObject>>) {}
96 fn on_coordinate_space_changed(&self) {}
97 fn on_coordinate_space_changing(&self) {}
98
99 fn validate_offset_change(&self, _old_value: Vector3D, _new_value: Vector3D) {}
100 fn on_offset_changed(&self) {}
101 fn on_offset_changing(&self) {}
102
103 fn validate_intensity_change(&self, _old_value: f32, _new_value: f32) {}
104 fn on_intensity_changed(&self) {}
105 fn on_intensity_changing(&self) {}
106
107 fn validate_min_attenuation_cutoff_change(&self, _old_value: f32, _new_value: f32) {}
108 fn on_min_attenuation_cutoff_changed(&self) {}
109 fn on_min_attenuation_cutoff_changing(&self) {}
110
111 fn validate_max_attenuation_cutoff_change(&self, _old_value: f32, _new_value: f32) {}
112 fn on_max_attenuation_cutoff_changed(&self) {}
113 fn on_max_attenuation_cutoff_changing(&self) {}
114}
115
116pub struct PointLightProps {
118 base: CompositionLightProps,
119 changed_fields_of_point_light: Cell<PointLightChangedFields>,
120 color: Cell<Color>,
121 constant_attenuation: Cell<f32>,
122 linear_attenuation: Cell<f32>,
123 quadratic_attenuation: Cell<f32>,
124 coordinate_space: RefCell<Option<Rc<dyn ICompositionObject>>>,
125 offset: Cell<Vector3D>,
126 intensity: Cell<f32>,
127 min_attenuation_cutoff: Cell<f32>,
128 max_attenuation_cutoff: Cell<f32>,
129}
130
131impl Default for PointLightProps {
132 fn default() -> Self {
133 Self::new()
134 }
135}
136
137impl PointLightProps {
138 pub fn new() -> Self {
142 Self {
143 base: CompositionLightProps::new(),
144 changed_fields_of_point_light: Cell::new(PointLightChangedFields::empty()),
145 color: Cell::new(Default::default()),
146 constant_attenuation: Cell::new(Default::default()),
147 linear_attenuation: Cell::new(Default::default()),
148 quadratic_attenuation: Cell::new(Default::default()),
149 coordinate_space: RefCell::new(None),
150 offset: Cell::new(Default::default()),
151 intensity: Cell::new(Default::default()),
152 min_attenuation_cutoff: Cell::new(Default::default()),
153 max_attenuation_cutoff: Cell::new(Default::default()),
154 }
155 }
156
157 pub fn base(&self) -> &CompositionLightProps {
159 &self.base
160 }
161
162 pub fn initialize_defaults(&self, host: &dyn PointLightHooks) {
165 self.base.initialize_defaults(host);
166 self.initialize_own_defaults(host);
167 }
168
169 pub fn initialize_own_defaults(&self, host: &dyn PointLightHooks) {
171 PointLightHooks::initialize_defaults_extra(host);
172 self.set_color(host, crate::media::Colors::WHITE);
173 self.set_constant_attenuation(host, 1.0);
174 self.set_intensity(host, 1.0);
175 self.set_max_attenuation_cutoff(host, f32::MAX);
176 }
177
178 pub fn color(&self) -> Color {
180 self.color.get()
181 }
182
183 pub fn set_color(&self, host: &dyn PointLightHooks, value: Color) {
185 let mut changed = false;
186 let old_value = self.color.get();
187 if old_value.differs(&value) {
188 PointLightHooks::validate_color_change(host, old_value, value);
189 PointLightHooks::on_color_changing(host);
190 changed = true;
191 self.color.set(value);
193 self.changed_fields_of_point_light.set(self.changed_fields_of_point_light.get() | PointLightChangedFields::COLOR);
195 host.register_for_serialization();
196 host.pending_animations().remove(ServerPointLightProps::id_of_color_property().base());
198 self.changed_fields_of_point_light.set(self.changed_fields_of_point_light.get() & !PointLightChangedFields::COLOR_ANIMATED);
199 if let Some(animation) = host.implicit_animation("Color") {
201 if let Some(a) = animation.as_composition_animation() {
203 self.changed_fields_of_point_light.set(self.changed_fields_of_point_light.get() | PointLightChangedFields::COLOR_ANIMATED);
204 host.pending_animations().set(
205 ServerPointLightProps::id_of_color_property().base(),
206 a.create_instance(host.server(), Some(ExpressionVariant::create(value))),
207 );
208 }
209 host.start_animation_group(&animation, "Color", ExpressionVariant::create(value));
211 }
212 }
213 self.color.set(value);
214 if changed {
215 PointLightHooks::on_color_changed(host);
216 }
217 }
218
219 pub fn constant_attenuation(&self) -> f32 {
221 self.constant_attenuation.get()
222 }
223
224 pub fn set_constant_attenuation(&self, host: &dyn PointLightHooks, value: f32) {
226 let mut changed = false;
227 let old_value = self.constant_attenuation.get();
228 if old_value.differs(&value) {
229 PointLightHooks::validate_constant_attenuation_change(host, old_value, value);
230 PointLightHooks::on_constant_attenuation_changing(host);
231 changed = true;
232 self.constant_attenuation.set(value);
234 self.changed_fields_of_point_light.set(self.changed_fields_of_point_light.get() | PointLightChangedFields::CONSTANT_ATTENUATION);
236 host.register_for_serialization();
237 host.pending_animations().remove(ServerPointLightProps::id_of_constant_attenuation_property().base());
239 self.changed_fields_of_point_light.set(self.changed_fields_of_point_light.get() & !PointLightChangedFields::CONSTANT_ATTENUATION_ANIMATED);
240 if let Some(animation) = host.implicit_animation("ConstantAttenuation") {
242 if let Some(a) = animation.as_composition_animation() {
244 self.changed_fields_of_point_light.set(self.changed_fields_of_point_light.get() | PointLightChangedFields::CONSTANT_ATTENUATION_ANIMATED);
245 host.pending_animations().set(
246 ServerPointLightProps::id_of_constant_attenuation_property().base(),
247 a.create_instance(host.server(), Some(ExpressionVariant::create(value))),
248 );
249 }
250 host.start_animation_group(&animation, "ConstantAttenuation", ExpressionVariant::create(value));
252 }
253 }
254 self.constant_attenuation.set(value);
255 if changed {
256 PointLightHooks::on_constant_attenuation_changed(host);
257 }
258 }
259
260 pub fn linear_attenuation(&self) -> f32 {
262 self.linear_attenuation.get()
263 }
264
265 pub fn set_linear_attenuation(&self, host: &dyn PointLightHooks, value: f32) {
267 let mut changed = false;
268 let old_value = self.linear_attenuation.get();
269 if old_value.differs(&value) {
270 PointLightHooks::validate_linear_attenuation_change(host, old_value, value);
271 PointLightHooks::on_linear_attenuation_changing(host);
272 changed = true;
273 self.linear_attenuation.set(value);
275 self.changed_fields_of_point_light.set(self.changed_fields_of_point_light.get() | PointLightChangedFields::LINEAR_ATTENUATION);
277 host.register_for_serialization();
278 host.pending_animations().remove(ServerPointLightProps::id_of_linear_attenuation_property().base());
280 self.changed_fields_of_point_light.set(self.changed_fields_of_point_light.get() & !PointLightChangedFields::LINEAR_ATTENUATION_ANIMATED);
281 if let Some(animation) = host.implicit_animation("LinearAttenuation") {
283 if let Some(a) = animation.as_composition_animation() {
285 self.changed_fields_of_point_light.set(self.changed_fields_of_point_light.get() | PointLightChangedFields::LINEAR_ATTENUATION_ANIMATED);
286 host.pending_animations().set(
287 ServerPointLightProps::id_of_linear_attenuation_property().base(),
288 a.create_instance(host.server(), Some(ExpressionVariant::create(value))),
289 );
290 }
291 host.start_animation_group(&animation, "LinearAttenuation", ExpressionVariant::create(value));
293 }
294 }
295 self.linear_attenuation.set(value);
296 if changed {
297 PointLightHooks::on_linear_attenuation_changed(host);
298 }
299 }
300
301 pub fn quadratic_attenuation(&self) -> f32 {
303 self.quadratic_attenuation.get()
304 }
305
306 pub fn set_quadratic_attenuation(&self, host: &dyn PointLightHooks, value: f32) {
308 let mut changed = false;
309 let old_value = self.quadratic_attenuation.get();
310 if old_value.differs(&value) {
311 PointLightHooks::validate_quadratic_attenuation_change(host, old_value, value);
312 PointLightHooks::on_quadratic_attenuation_changing(host);
313 changed = true;
314 self.quadratic_attenuation.set(value);
316 self.changed_fields_of_point_light.set(self.changed_fields_of_point_light.get() | PointLightChangedFields::QUADRATIC_ATTENUATION);
318 host.register_for_serialization();
319 host.pending_animations().remove(ServerPointLightProps::id_of_quadratic_attenuation_property().base());
321 self.changed_fields_of_point_light.set(self.changed_fields_of_point_light.get() & !PointLightChangedFields::QUADRATIC_ATTENUATION_ANIMATED);
322 if let Some(animation) = host.implicit_animation("QuadraticAttenuation") {
324 if let Some(a) = animation.as_composition_animation() {
326 self.changed_fields_of_point_light.set(self.changed_fields_of_point_light.get() | PointLightChangedFields::QUADRATIC_ATTENUATION_ANIMATED);
327 host.pending_animations().set(
328 ServerPointLightProps::id_of_quadratic_attenuation_property().base(),
329 a.create_instance(host.server(), Some(ExpressionVariant::create(value))),
330 );
331 }
332 host.start_animation_group(&animation, "QuadraticAttenuation", ExpressionVariant::create(value));
334 }
335 }
336 self.quadratic_attenuation.set(value);
337 if changed {
338 PointLightHooks::on_quadratic_attenuation_changed(host);
339 }
340 }
341
342 pub fn coordinate_space(&self) -> Option<Rc<dyn ICompositionObject>> {
344 self.coordinate_space.borrow().clone()
345 }
346
347 pub fn set_coordinate_space(&self, host: &dyn PointLightHooks, value: Option<Rc<dyn ICompositionObject>>) {
349 let mut changed = false;
350 let old_value = self.coordinate_space.borrow().clone();
351 if old_value.differs(&value) {
352 PointLightHooks::validate_coordinate_space_change(host, &old_value, &value);
353 PointLightHooks::on_coordinate_space_changing(host);
354 changed = true;
355 *self.coordinate_space.borrow_mut() = value.clone();
357 self.changed_fields_of_point_light.set(self.changed_fields_of_point_light.get() | PointLightChangedFields::COORDINATE_SPACE);
359 host.register_for_serialization();
360 }
361 *self.coordinate_space.borrow_mut() = value;
362 if changed {
363 PointLightHooks::on_coordinate_space_changed(host);
364 }
365 }
366
367 pub fn offset(&self) -> Vector3D {
369 self.offset.get()
370 }
371
372 pub fn set_offset(&self, host: &dyn PointLightHooks, value: Vector3D) {
374 let mut changed = false;
375 let old_value = self.offset.get();
376 if old_value.differs(&value) {
377 PointLightHooks::validate_offset_change(host, old_value, value);
378 PointLightHooks::on_offset_changing(host);
379 changed = true;
380 self.offset.set(value);
382 self.changed_fields_of_point_light.set(self.changed_fields_of_point_light.get() | PointLightChangedFields::OFFSET);
384 host.register_for_serialization();
385 host.pending_animations().remove(ServerPointLightProps::id_of_offset_property().base());
387 self.changed_fields_of_point_light.set(self.changed_fields_of_point_light.get() & !PointLightChangedFields::OFFSET_ANIMATED);
388 if let Some(animation) = host.implicit_animation("Offset") {
390 if let Some(a) = animation.as_composition_animation() {
392 self.changed_fields_of_point_light.set(self.changed_fields_of_point_light.get() | PointLightChangedFields::OFFSET_ANIMATED);
393 host.pending_animations().set(
394 ServerPointLightProps::id_of_offset_property().base(),
395 a.create_instance(host.server(), Some(ExpressionVariant::create(value))),
396 );
397 }
398 host.start_animation_group(&animation, "Offset", ExpressionVariant::create(value));
400 }
401 }
402 self.offset.set(value);
403 if changed {
404 PointLightHooks::on_offset_changed(host);
405 }
406 }
407
408 pub fn intensity(&self) -> f32 {
410 self.intensity.get()
411 }
412
413 pub fn set_intensity(&self, host: &dyn PointLightHooks, value: f32) {
415 let mut changed = false;
416 let old_value = self.intensity.get();
417 if old_value.differs(&value) {
418 PointLightHooks::validate_intensity_change(host, old_value, value);
419 PointLightHooks::on_intensity_changing(host);
420 changed = true;
421 self.intensity.set(value);
423 self.changed_fields_of_point_light.set(self.changed_fields_of_point_light.get() | PointLightChangedFields::INTENSITY);
425 host.register_for_serialization();
426 host.pending_animations().remove(ServerPointLightProps::id_of_intensity_property().base());
428 self.changed_fields_of_point_light.set(self.changed_fields_of_point_light.get() & !PointLightChangedFields::INTENSITY_ANIMATED);
429 if let Some(animation) = host.implicit_animation("Intensity") {
431 if let Some(a) = animation.as_composition_animation() {
433 self.changed_fields_of_point_light.set(self.changed_fields_of_point_light.get() | PointLightChangedFields::INTENSITY_ANIMATED);
434 host.pending_animations().set(
435 ServerPointLightProps::id_of_intensity_property().base(),
436 a.create_instance(host.server(), Some(ExpressionVariant::create(value))),
437 );
438 }
439 host.start_animation_group(&animation, "Intensity", ExpressionVariant::create(value));
441 }
442 }
443 self.intensity.set(value);
444 if changed {
445 PointLightHooks::on_intensity_changed(host);
446 }
447 }
448
449 pub fn min_attenuation_cutoff(&self) -> f32 {
451 self.min_attenuation_cutoff.get()
452 }
453
454 pub fn set_min_attenuation_cutoff(&self, host: &dyn PointLightHooks, value: f32) {
456 let mut changed = false;
457 let old_value = self.min_attenuation_cutoff.get();
458 if old_value.differs(&value) {
459 PointLightHooks::validate_min_attenuation_cutoff_change(host, old_value, value);
460 PointLightHooks::on_min_attenuation_cutoff_changing(host);
461 changed = true;
462 self.min_attenuation_cutoff.set(value);
464 self.changed_fields_of_point_light.set(self.changed_fields_of_point_light.get() | PointLightChangedFields::MIN_ATTENUATION_CUTOFF);
466 host.register_for_serialization();
467 host.pending_animations().remove(ServerPointLightProps::id_of_min_attenuation_cutoff_property().base());
469 self.changed_fields_of_point_light.set(self.changed_fields_of_point_light.get() & !PointLightChangedFields::MIN_ATTENUATION_CUTOFF_ANIMATED);
470 if let Some(animation) = host.implicit_animation("MinAttenuationCutoff") {
472 if let Some(a) = animation.as_composition_animation() {
474 self.changed_fields_of_point_light.set(self.changed_fields_of_point_light.get() | PointLightChangedFields::MIN_ATTENUATION_CUTOFF_ANIMATED);
475 host.pending_animations().set(
476 ServerPointLightProps::id_of_min_attenuation_cutoff_property().base(),
477 a.create_instance(host.server(), Some(ExpressionVariant::create(value))),
478 );
479 }
480 host.start_animation_group(&animation, "MinAttenuationCutoff", ExpressionVariant::create(value));
482 }
483 }
484 self.min_attenuation_cutoff.set(value);
485 if changed {
486 PointLightHooks::on_min_attenuation_cutoff_changed(host);
487 }
488 }
489
490 pub fn max_attenuation_cutoff(&self) -> f32 {
492 self.max_attenuation_cutoff.get()
493 }
494
495 pub fn set_max_attenuation_cutoff(&self, host: &dyn PointLightHooks, value: f32) {
497 let mut changed = false;
498 let old_value = self.max_attenuation_cutoff.get();
499 if old_value.differs(&value) {
500 PointLightHooks::validate_max_attenuation_cutoff_change(host, old_value, value);
501 PointLightHooks::on_max_attenuation_cutoff_changing(host);
502 changed = true;
503 self.max_attenuation_cutoff.set(value);
505 self.changed_fields_of_point_light.set(self.changed_fields_of_point_light.get() | PointLightChangedFields::MAX_ATTENUATION_CUTOFF);
507 host.register_for_serialization();
508 host.pending_animations().remove(ServerPointLightProps::id_of_max_attenuation_cutoff_property().base());
510 self.changed_fields_of_point_light.set(self.changed_fields_of_point_light.get() & !PointLightChangedFields::MAX_ATTENUATION_CUTOFF_ANIMATED);
511 if let Some(animation) = host.implicit_animation("MaxAttenuationCutoff") {
513 if let Some(a) = animation.as_composition_animation() {
515 self.changed_fields_of_point_light.set(self.changed_fields_of_point_light.get() | PointLightChangedFields::MAX_ATTENUATION_CUTOFF_ANIMATED);
516 host.pending_animations().set(
517 ServerPointLightProps::id_of_max_attenuation_cutoff_property().base(),
518 a.create_instance(host.server(), Some(ExpressionVariant::create(value))),
519 );
520 }
521 host.start_animation_group(&animation, "MaxAttenuationCutoff", ExpressionVariant::create(value));
523 }
524 }
525 self.max_attenuation_cutoff.set(value);
526 if changed {
527 PointLightHooks::on_max_attenuation_cutoff_changed(host);
528 }
529 }
530
531 pub fn start_animation(
535 &self,
536 host: &dyn PointLightHooks,
537 property_name: &str,
538 animation: &dyn ICompositionAnimation,
539 final_value: Option<ExpressionVariant>,
540 ) -> bool {
541 if property_name == "Color" {
542 let server = animation.create_instance(host.server(), final_value);
543 host.pending_animations().set(ServerPointLightProps::id_of_color_property().base(), server);
544 self.changed_fields_of_point_light.set(self.changed_fields_of_point_light.get() | PointLightChangedFields::COLOR_ANIMATED);
545 host.register_for_serialization();
546 return true;
547 }
548 if property_name == "ConstantAttenuation" {
549 let server = animation.create_instance(host.server(), final_value);
550 host.pending_animations().set(ServerPointLightProps::id_of_constant_attenuation_property().base(), server);
551 self.changed_fields_of_point_light.set(self.changed_fields_of_point_light.get() | PointLightChangedFields::CONSTANT_ATTENUATION_ANIMATED);
552 host.register_for_serialization();
553 return true;
554 }
555 if property_name == "LinearAttenuation" {
556 let server = animation.create_instance(host.server(), final_value);
557 host.pending_animations().set(ServerPointLightProps::id_of_linear_attenuation_property().base(), server);
558 self.changed_fields_of_point_light.set(self.changed_fields_of_point_light.get() | PointLightChangedFields::LINEAR_ATTENUATION_ANIMATED);
559 host.register_for_serialization();
560 return true;
561 }
562 if property_name == "QuadraticAttenuation" {
563 let server = animation.create_instance(host.server(), final_value);
564 host.pending_animations().set(ServerPointLightProps::id_of_quadratic_attenuation_property().base(), server);
565 self.changed_fields_of_point_light.set(self.changed_fields_of_point_light.get() | PointLightChangedFields::QUADRATIC_ATTENUATION_ANIMATED);
566 host.register_for_serialization();
567 return true;
568 }
569 if property_name == "Offset" {
570 let server = animation.create_instance(host.server(), final_value);
571 host.pending_animations().set(ServerPointLightProps::id_of_offset_property().base(), server);
572 self.changed_fields_of_point_light.set(self.changed_fields_of_point_light.get() | PointLightChangedFields::OFFSET_ANIMATED);
573 host.register_for_serialization();
574 return true;
575 }
576 if property_name == "Intensity" {
577 let server = animation.create_instance(host.server(), final_value);
578 host.pending_animations().set(ServerPointLightProps::id_of_intensity_property().base(), server);
579 self.changed_fields_of_point_light.set(self.changed_fields_of_point_light.get() | PointLightChangedFields::INTENSITY_ANIMATED);
580 host.register_for_serialization();
581 return true;
582 }
583 if property_name == "MinAttenuationCutoff" {
584 let server = animation.create_instance(host.server(), final_value);
585 host.pending_animations().set(ServerPointLightProps::id_of_min_attenuation_cutoff_property().base(), server);
586 self.changed_fields_of_point_light.set(self.changed_fields_of_point_light.get() | PointLightChangedFields::MIN_ATTENUATION_CUTOFF_ANIMATED);
587 host.register_for_serialization();
588 return true;
589 }
590 if property_name == "MaxAttenuationCutoff" {
591 let server = animation.create_instance(host.server(), final_value);
592 host.pending_animations().set(ServerPointLightProps::id_of_max_attenuation_cutoff_property().base(), server);
593 self.changed_fields_of_point_light.set(self.changed_fields_of_point_light.get() | PointLightChangedFields::MAX_ATTENUATION_CUTOFF_ANIMATED);
594 host.register_for_serialization();
595 return true;
596 }
597 false
598 }
599
600 pub fn serialize_changes_core(&self, host: &dyn PointLightHooks, writer: &mut BatchStreamWriter<'_>) {
603 self.base.serialize_changes_core(host, writer);
604 let changed = self.changed_fields_of_point_light.get();
605 writer.write(changed);
606 if changed.contains(PointLightChangedFields::COLOR_ANIMATED) {
607 writer.write_value(host.pending_animations().get_and_remove(ServerPointLightProps::id_of_color_property().base()));
608 } else if changed.contains(PointLightChangedFields::COLOR) {
609 writer.write(self.color.get());
610 }
611 if changed.contains(PointLightChangedFields::CONSTANT_ATTENUATION_ANIMATED) {
612 writer.write_value(host.pending_animations().get_and_remove(ServerPointLightProps::id_of_constant_attenuation_property().base()));
613 } else if changed.contains(PointLightChangedFields::CONSTANT_ATTENUATION) {
614 writer.write(self.constant_attenuation.get());
615 }
616 if changed.contains(PointLightChangedFields::LINEAR_ATTENUATION_ANIMATED) {
617 writer.write_value(host.pending_animations().get_and_remove(ServerPointLightProps::id_of_linear_attenuation_property().base()));
618 } else if changed.contains(PointLightChangedFields::LINEAR_ATTENUATION) {
619 writer.write(self.linear_attenuation.get());
620 }
621 if changed.contains(PointLightChangedFields::QUADRATIC_ATTENUATION_ANIMATED) {
622 writer.write_value(host.pending_animations().get_and_remove(ServerPointLightProps::id_of_quadratic_attenuation_property().base()));
623 } else if changed.contains(PointLightChangedFields::QUADRATIC_ATTENUATION) {
624 writer.write(self.quadratic_attenuation.get());
625 }
626 if changed.contains(PointLightChangedFields::COORDINATE_SPACE) {
627 writer.write_server_object(self.coordinate_space.borrow().clone().map(|o| o.server()));
628 }
629 if changed.contains(PointLightChangedFields::OFFSET_ANIMATED) {
630 writer.write_value(host.pending_animations().get_and_remove(ServerPointLightProps::id_of_offset_property().base()));
631 } else if changed.contains(PointLightChangedFields::OFFSET) {
632 writer.write(self.offset.get());
633 }
634 if changed.contains(PointLightChangedFields::INTENSITY_ANIMATED) {
635 writer.write_value(host.pending_animations().get_and_remove(ServerPointLightProps::id_of_intensity_property().base()));
636 } else if changed.contains(PointLightChangedFields::INTENSITY) {
637 writer.write(self.intensity.get());
638 }
639 if changed.contains(PointLightChangedFields::MIN_ATTENUATION_CUTOFF_ANIMATED) {
640 writer.write_value(host.pending_animations().get_and_remove(ServerPointLightProps::id_of_min_attenuation_cutoff_property().base()));
641 } else if changed.contains(PointLightChangedFields::MIN_ATTENUATION_CUTOFF) {
642 writer.write(self.min_attenuation_cutoff.get());
643 }
644 if changed.contains(PointLightChangedFields::MAX_ATTENUATION_CUTOFF_ANIMATED) {
645 writer.write_value(host.pending_animations().get_and_remove(ServerPointLightProps::id_of_max_attenuation_cutoff_property().base()));
646 } else if changed.contains(PointLightChangedFields::MAX_ATTENUATION_CUTOFF) {
647 writer.write(self.max_attenuation_cutoff.get());
648 }
649 self.changed_fields_of_point_light.set(PointLightChangedFields::empty());
650 }
651}
652
653pub trait ServerPointLightHooks: ServerCompositionLightHooks {
656 fn deserialize_changes_extra(&self, _reader: &mut BatchStreamReader<'_>) {}
657 fn on_fields_deserialized(&self, _changed: PointLightChangedFields) {}
658
659 fn on_coordinate_space_changed(&self) {}
660 fn on_coordinate_space_changing(&self) {}
661}
662
663pub struct ServerPointLightProps {
665 base: ServerCompositionLightProps,
666 color: Cell<Color>,
667 constant_attenuation: Cell<f32>,
668 linear_attenuation: Cell<f32>,
669 quadratic_attenuation: Cell<f32>,
670 coordinate_space: RefCell<Option<Rc<dyn IServerObject>>>,
671 offset: Cell<Vector3D>,
672 intensity: Cell<f32>,
673 min_attenuation_cutoff: Cell<f32>,
674 max_attenuation_cutoff: Cell<f32>,
675}
676
677impl Default for ServerPointLightProps {
678 fn default() -> Self {
679 Self::new()
680 }
681}
682
683impl ServerPointLightProps {
684 pub fn new() -> Self {
687 Self {
688 base: ServerCompositionLightProps::new(),
689 color: Cell::new(Default::default()),
690 constant_attenuation: Cell::new(Default::default()),
691 linear_attenuation: Cell::new(Default::default()),
692 quadratic_attenuation: Cell::new(Default::default()),
693 coordinate_space: RefCell::new(None),
694 offset: Cell::new(Default::default()),
695 intensity: Cell::new(Default::default()),
696 min_attenuation_cutoff: Cell::new(Default::default()),
697 max_attenuation_cutoff: Cell::new(Default::default()),
698 }
699 }
700
701 pub fn base(&self) -> &ServerCompositionLightProps {
703 &self.base
704 }
705
706 pub fn find_props(&self, type_id: TypeId) -> Option<&dyn Any> {
709 if type_id == TypeId::of::<Self>() {
710 return Some(self);
711 }
712 self.base.find_props(type_id)
713 }
714
715 pub fn id_of_color_property() -> &'static CompositionPropertyOf<Color> {
717 static PROPERTY: OnceLock<CompositionPropertyOf<Color>> = OnceLock::new();
718 PROPERTY.get_or_init(|| {
719 CompositionProperty::register::<ServerPointLightProps, Color>(
720 "Color",
721 "ServerPointLight",
722 |obj| props_of::<ServerPointLightProps>(obj).color.get(),
723 |obj, v| props_of::<ServerPointLightProps>(obj).color.set(v),
724 Some(|obj| ExpressionVariant::create(props_of::<ServerPointLightProps>(obj).color.get())),
725 Some(|obj, v: ExpressionVariant| props_of::<ServerPointLightProps>(obj).color.set(v.cast_or_default::<Color>())),
726 )
727 })
728 }
729
730 pub fn color(&self) -> Color {
732 self.color.get()
733 }
734
735 pub fn set_color(&self, host: &dyn ServerPointLightHooks, value: Color) {
736 self.color.set(value);
737 host.remove_animation_for_property(Self::id_of_color_property().base());
738 }
739
740 pub fn id_of_constant_attenuation_property() -> &'static CompositionPropertyOf<f32> {
742 static PROPERTY: OnceLock<CompositionPropertyOf<f32>> = OnceLock::new();
743 PROPERTY.get_or_init(|| {
744 CompositionProperty::register::<ServerPointLightProps, f32>(
745 "ConstantAttenuation",
746 "ServerPointLight",
747 |obj| props_of::<ServerPointLightProps>(obj).constant_attenuation.get(),
748 |obj, v| props_of::<ServerPointLightProps>(obj).constant_attenuation.set(v),
749 Some(|obj| ExpressionVariant::create(props_of::<ServerPointLightProps>(obj).constant_attenuation.get())),
750 Some(|obj, v: ExpressionVariant| props_of::<ServerPointLightProps>(obj).constant_attenuation.set(v.cast_or_default::<f32>())),
751 )
752 })
753 }
754
755 pub fn constant_attenuation(&self) -> f32 {
757 self.constant_attenuation.get()
758 }
759
760 pub fn set_constant_attenuation(&self, host: &dyn ServerPointLightHooks, value: f32) {
761 self.constant_attenuation.set(value);
762 host.remove_animation_for_property(Self::id_of_constant_attenuation_property().base());
763 }
764
765 pub fn id_of_linear_attenuation_property() -> &'static CompositionPropertyOf<f32> {
767 static PROPERTY: OnceLock<CompositionPropertyOf<f32>> = OnceLock::new();
768 PROPERTY.get_or_init(|| {
769 CompositionProperty::register::<ServerPointLightProps, f32>(
770 "LinearAttenuation",
771 "ServerPointLight",
772 |obj| props_of::<ServerPointLightProps>(obj).linear_attenuation.get(),
773 |obj, v| props_of::<ServerPointLightProps>(obj).linear_attenuation.set(v),
774 Some(|obj| ExpressionVariant::create(props_of::<ServerPointLightProps>(obj).linear_attenuation.get())),
775 Some(|obj, v: ExpressionVariant| props_of::<ServerPointLightProps>(obj).linear_attenuation.set(v.cast_or_default::<f32>())),
776 )
777 })
778 }
779
780 pub fn linear_attenuation(&self) -> f32 {
782 self.linear_attenuation.get()
783 }
784
785 pub fn set_linear_attenuation(&self, host: &dyn ServerPointLightHooks, value: f32) {
786 self.linear_attenuation.set(value);
787 host.remove_animation_for_property(Self::id_of_linear_attenuation_property().base());
788 }
789
790 pub fn id_of_quadratic_attenuation_property() -> &'static CompositionPropertyOf<f32> {
792 static PROPERTY: OnceLock<CompositionPropertyOf<f32>> = OnceLock::new();
793 PROPERTY.get_or_init(|| {
794 CompositionProperty::register::<ServerPointLightProps, f32>(
795 "QuadraticAttenuation",
796 "ServerPointLight",
797 |obj| props_of::<ServerPointLightProps>(obj).quadratic_attenuation.get(),
798 |obj, v| props_of::<ServerPointLightProps>(obj).quadratic_attenuation.set(v),
799 Some(|obj| ExpressionVariant::create(props_of::<ServerPointLightProps>(obj).quadratic_attenuation.get())),
800 Some(|obj, v: ExpressionVariant| props_of::<ServerPointLightProps>(obj).quadratic_attenuation.set(v.cast_or_default::<f32>())),
801 )
802 })
803 }
804
805 pub fn quadratic_attenuation(&self) -> f32 {
807 self.quadratic_attenuation.get()
808 }
809
810 pub fn set_quadratic_attenuation(&self, host: &dyn ServerPointLightHooks, value: f32) {
811 self.quadratic_attenuation.set(value);
812 host.remove_animation_for_property(Self::id_of_quadratic_attenuation_property().base());
813 }
814
815 pub fn id_of_coordinate_space_property() -> &'static CompositionPropertyOf<Option<Rc<dyn IServerObject>>> {
817 static PROPERTY: OnceLock<CompositionPropertyOf<Option<Rc<dyn IServerObject>>>> = OnceLock::new();
818 PROPERTY.get_or_init(|| {
819 CompositionProperty::register::<ServerPointLightProps, Option<Rc<dyn IServerObject>>>(
820 "CoordinateSpace",
821 "ServerPointLight",
822 |obj| props_of::<ServerPointLightProps>(obj).coordinate_space.borrow().clone(),
823 |obj, v| *props_of::<ServerPointLightProps>(obj).coordinate_space.borrow_mut() = v,
824 None,
825 None,
826 )
827 })
828 }
829
830 pub fn coordinate_space(&self) -> Option<Rc<dyn IServerObject>> {
832 self.coordinate_space.borrow().clone()
833 }
834
835 pub fn set_coordinate_space(&self, host: &dyn ServerPointLightHooks, value: Option<Rc<dyn IServerObject>>) {
836 let old_value = self.coordinate_space.borrow().clone();
837 let mut changed = false;
838 if old_value.differs(&value) {
839 ServerPointLightHooks::on_coordinate_space_changing(host);
840 changed = true;
841 }
842 host.set_value(
843 Self::id_of_coordinate_space_property().base(),
844 ServerValueChange {
845 equal: old_value.default_equals(&value),
846 old_resource: old_value.as_server_render_resource(),
847 new_resource: value.as_server_render_resource(),
848 assign: &mut || *self.coordinate_space.borrow_mut() = value.clone(),
849 },
850 );
851 if changed {
852 ServerPointLightHooks::on_coordinate_space_changed(host);
853 }
854 }
855
856 pub fn id_of_offset_property() -> &'static CompositionPropertyOf<Vector3D> {
858 static PROPERTY: OnceLock<CompositionPropertyOf<Vector3D>> = OnceLock::new();
859 PROPERTY.get_or_init(|| {
860 CompositionProperty::register::<ServerPointLightProps, Vector3D>(
861 "Offset",
862 "ServerPointLight",
863 |obj| props_of::<ServerPointLightProps>(obj).offset.get(),
864 |obj, v| props_of::<ServerPointLightProps>(obj).offset.set(v),
865 Some(|obj| ExpressionVariant::create(props_of::<ServerPointLightProps>(obj).offset.get())),
866 Some(|obj, v: ExpressionVariant| props_of::<ServerPointLightProps>(obj).offset.set(v.cast_or_default::<Vector3D>())),
867 )
868 })
869 }
870
871 pub fn offset(&self) -> Vector3D {
873 self.offset.get()
874 }
875
876 pub fn set_offset(&self, host: &dyn ServerPointLightHooks, value: Vector3D) {
877 self.offset.set(value);
878 host.remove_animation_for_property(Self::id_of_offset_property().base());
879 }
880
881 pub fn id_of_intensity_property() -> &'static CompositionPropertyOf<f32> {
883 static PROPERTY: OnceLock<CompositionPropertyOf<f32>> = OnceLock::new();
884 PROPERTY.get_or_init(|| {
885 CompositionProperty::register::<ServerPointLightProps, f32>(
886 "Intensity",
887 "ServerPointLight",
888 |obj| props_of::<ServerPointLightProps>(obj).intensity.get(),
889 |obj, v| props_of::<ServerPointLightProps>(obj).intensity.set(v),
890 Some(|obj| ExpressionVariant::create(props_of::<ServerPointLightProps>(obj).intensity.get())),
891 Some(|obj, v: ExpressionVariant| props_of::<ServerPointLightProps>(obj).intensity.set(v.cast_or_default::<f32>())),
892 )
893 })
894 }
895
896 pub fn intensity(&self) -> f32 {
898 self.intensity.get()
899 }
900
901 pub fn set_intensity(&self, host: &dyn ServerPointLightHooks, value: f32) {
902 self.intensity.set(value);
903 host.remove_animation_for_property(Self::id_of_intensity_property().base());
904 }
905
906 pub fn id_of_min_attenuation_cutoff_property() -> &'static CompositionPropertyOf<f32> {
908 static PROPERTY: OnceLock<CompositionPropertyOf<f32>> = OnceLock::new();
909 PROPERTY.get_or_init(|| {
910 CompositionProperty::register::<ServerPointLightProps, f32>(
911 "MinAttenuationCutoff",
912 "ServerPointLight",
913 |obj| props_of::<ServerPointLightProps>(obj).min_attenuation_cutoff.get(),
914 |obj, v| props_of::<ServerPointLightProps>(obj).min_attenuation_cutoff.set(v),
915 Some(|obj| ExpressionVariant::create(props_of::<ServerPointLightProps>(obj).min_attenuation_cutoff.get())),
916 Some(|obj, v: ExpressionVariant| props_of::<ServerPointLightProps>(obj).min_attenuation_cutoff.set(v.cast_or_default::<f32>())),
917 )
918 })
919 }
920
921 pub fn min_attenuation_cutoff(&self) -> f32 {
923 self.min_attenuation_cutoff.get()
924 }
925
926 pub fn set_min_attenuation_cutoff(&self, host: &dyn ServerPointLightHooks, value: f32) {
927 self.min_attenuation_cutoff.set(value);
928 host.remove_animation_for_property(Self::id_of_min_attenuation_cutoff_property().base());
929 }
930
931 pub fn id_of_max_attenuation_cutoff_property() -> &'static CompositionPropertyOf<f32> {
933 static PROPERTY: OnceLock<CompositionPropertyOf<f32>> = OnceLock::new();
934 PROPERTY.get_or_init(|| {
935 CompositionProperty::register::<ServerPointLightProps, f32>(
936 "MaxAttenuationCutoff",
937 "ServerPointLight",
938 |obj| props_of::<ServerPointLightProps>(obj).max_attenuation_cutoff.get(),
939 |obj, v| props_of::<ServerPointLightProps>(obj).max_attenuation_cutoff.set(v),
940 Some(|obj| ExpressionVariant::create(props_of::<ServerPointLightProps>(obj).max_attenuation_cutoff.get())),
941 Some(|obj, v: ExpressionVariant| props_of::<ServerPointLightProps>(obj).max_attenuation_cutoff.set(v.cast_or_default::<f32>())),
942 )
943 })
944 }
945
946 pub fn max_attenuation_cutoff(&self) -> f32 {
948 self.max_attenuation_cutoff.get()
949 }
950
951 pub fn set_max_attenuation_cutoff(&self, host: &dyn ServerPointLightHooks, value: f32) {
952 self.max_attenuation_cutoff.set(value);
953 host.remove_animation_for_property(Self::id_of_max_attenuation_cutoff_property().base());
954 }
955
956 pub fn deserialize_changes_core(
959 &self,
960 host: &dyn ServerPointLightHooks,
961 reader: &mut BatchStreamReader<'_>,
962 committed_at: Duration,
963 ) {
964 let compositor = host.server_compositor();
965 self.base.deserialize_changes_core(host, reader, committed_at);
966 ServerPointLightHooks::deserialize_changes_extra(host, reader);
967 let changed = reader.read::<PointLightChangedFields>();
968 if changed.contains(PointLightChangedFields::COLOR_ANIMATED) {
969 let Some(animation) = reader.read_value::<AnimationInstanceFactory>().map(AnimationInstanceFactory::create) else {
970 panic!("a batch starts an animation of `Color` without an animation instance");
971 };
972 host.set_animated_value(
973 Self::id_of_color_property().base(),
974 ExpressionVariant::create(self.color.get()),
975 committed_at,
976 animation,
977 );
978 } else if changed.contains(PointLightChangedFields::COLOR) {
979 let value = reader.read::<Color>();
980 self.set_color(host, value);
981 }
982 if changed.contains(PointLightChangedFields::CONSTANT_ATTENUATION_ANIMATED) {
983 let Some(animation) = reader.read_value::<AnimationInstanceFactory>().map(AnimationInstanceFactory::create) else {
984 panic!("a batch starts an animation of `ConstantAttenuation` without an animation instance");
985 };
986 host.set_animated_value(
987 Self::id_of_constant_attenuation_property().base(),
988 ExpressionVariant::create(self.constant_attenuation.get()),
989 committed_at,
990 animation,
991 );
992 } else if changed.contains(PointLightChangedFields::CONSTANT_ATTENUATION) {
993 let value = reader.read::<f32>();
994 self.set_constant_attenuation(host, value);
995 }
996 if changed.contains(PointLightChangedFields::LINEAR_ATTENUATION_ANIMATED) {
997 let Some(animation) = reader.read_value::<AnimationInstanceFactory>().map(AnimationInstanceFactory::create) else {
998 panic!("a batch starts an animation of `LinearAttenuation` without an animation instance");
999 };
1000 host.set_animated_value(
1001 Self::id_of_linear_attenuation_property().base(),
1002 ExpressionVariant::create(self.linear_attenuation.get()),
1003 committed_at,
1004 animation,
1005 );
1006 } else if changed.contains(PointLightChangedFields::LINEAR_ATTENUATION) {
1007 let value = reader.read::<f32>();
1008 self.set_linear_attenuation(host, value);
1009 }
1010 if changed.contains(PointLightChangedFields::QUADRATIC_ATTENUATION_ANIMATED) {
1011 let Some(animation) = reader.read_value::<AnimationInstanceFactory>().map(AnimationInstanceFactory::create) else {
1012 panic!("a batch starts an animation of `QuadraticAttenuation` without an animation instance");
1013 };
1014 host.set_animated_value(
1015 Self::id_of_quadratic_attenuation_property().base(),
1016 ExpressionVariant::create(self.quadratic_attenuation.get()),
1017 committed_at,
1018 animation,
1019 );
1020 } else if changed.contains(PointLightChangedFields::QUADRATIC_ATTENUATION) {
1021 let value = reader.read::<f32>();
1022 self.set_quadratic_attenuation(host, value);
1023 }
1024 if changed.contains(PointLightChangedFields::COORDINATE_SPACE) {
1025 let value = read_server_object(reader, compositor.as_ref());
1026 self.set_coordinate_space(host, value);
1027 }
1028 if changed.contains(PointLightChangedFields::OFFSET_ANIMATED) {
1029 let Some(animation) = reader.read_value::<AnimationInstanceFactory>().map(AnimationInstanceFactory::create) else {
1030 panic!("a batch starts an animation of `Offset` without an animation instance");
1031 };
1032 host.set_animated_value(
1033 Self::id_of_offset_property().base(),
1034 ExpressionVariant::create(self.offset.get()),
1035 committed_at,
1036 animation,
1037 );
1038 } else if changed.contains(PointLightChangedFields::OFFSET) {
1039 let value = reader.read::<Vector3D>();
1040 self.set_offset(host, value);
1041 }
1042 if changed.contains(PointLightChangedFields::INTENSITY_ANIMATED) {
1043 let Some(animation) = reader.read_value::<AnimationInstanceFactory>().map(AnimationInstanceFactory::create) else {
1044 panic!("a batch starts an animation of `Intensity` without an animation instance");
1045 };
1046 host.set_animated_value(
1047 Self::id_of_intensity_property().base(),
1048 ExpressionVariant::create(self.intensity.get()),
1049 committed_at,
1050 animation,
1051 );
1052 } else if changed.contains(PointLightChangedFields::INTENSITY) {
1053 let value = reader.read::<f32>();
1054 self.set_intensity(host, value);
1055 }
1056 if changed.contains(PointLightChangedFields::MIN_ATTENUATION_CUTOFF_ANIMATED) {
1057 let Some(animation) = reader.read_value::<AnimationInstanceFactory>().map(AnimationInstanceFactory::create) else {
1058 panic!("a batch starts an animation of `MinAttenuationCutoff` without an animation instance");
1059 };
1060 host.set_animated_value(
1061 Self::id_of_min_attenuation_cutoff_property().base(),
1062 ExpressionVariant::create(self.min_attenuation_cutoff.get()),
1063 committed_at,
1064 animation,
1065 );
1066 } else if changed.contains(PointLightChangedFields::MIN_ATTENUATION_CUTOFF) {
1067 let value = reader.read::<f32>();
1068 self.set_min_attenuation_cutoff(host, value);
1069 }
1070 if changed.contains(PointLightChangedFields::MAX_ATTENUATION_CUTOFF_ANIMATED) {
1071 let Some(animation) = reader.read_value::<AnimationInstanceFactory>().map(AnimationInstanceFactory::create) else {
1072 panic!("a batch starts an animation of `MaxAttenuationCutoff` without an animation instance");
1073 };
1074 host.set_animated_value(
1075 Self::id_of_max_attenuation_cutoff_property().base(),
1076 ExpressionVariant::create(self.max_attenuation_cutoff.get()),
1077 committed_at,
1078 animation,
1079 );
1080 } else if changed.contains(PointLightChangedFields::MAX_ATTENUATION_CUTOFF) {
1081 let value = reader.read::<f32>();
1082 self.set_max_attenuation_cutoff(host, value);
1083 }
1084 ServerPointLightHooks::on_fields_deserialized(host, changed);
1085 }
1086
1087 pub fn get_composition_property(name: &str) -> Option<&'static CompositionProperty> {
1090 if name == "Color" {
1091 return Some(Self::id_of_color_property().base());
1092 }
1093 if name == "ConstantAttenuation" {
1094 return Some(Self::id_of_constant_attenuation_property().base());
1095 }
1096 if name == "LinearAttenuation" {
1097 return Some(Self::id_of_linear_attenuation_property().base());
1098 }
1099 if name == "QuadraticAttenuation" {
1100 return Some(Self::id_of_quadratic_attenuation_property().base());
1101 }
1102 if name == "Offset" {
1103 return Some(Self::id_of_offset_property().base());
1104 }
1105 if name == "Intensity" {
1106 return Some(Self::id_of_intensity_property().base());
1107 }
1108 if name == "MinAttenuationCutoff" {
1109 return Some(Self::id_of_min_attenuation_cutoff_property().base());
1110 }
1111 if name == "MaxAttenuationCutoff" {
1112 return Some(Self::id_of_max_attenuation_cutoff_property().base());
1113 }
1114 ServerCompositionLightProps::get_composition_property(name)
1115 }
1116}