Skip to main content

ferroui_base/rendering/composition/generated/
distant_light.rs

1// @generated by scripts/generate_composition.py — do not edit
2
3//! `DistantLight`.
4//!
5//! Defined by the FerroUI overlay (scripts/composition-schema-winui.xml).
6
7use 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    /// The fields of `DistantLight` that changed since its last serialization.
40    #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
41    pub struct DistantLightChangedFields: u8 {
42        const COLOR = 1;
43        const COLOR_ANIMATED = 2;
44        const COORDINATE_SPACE = 4;
45        const DIRECTION = 8;
46        const DIRECTION_ANIMATED = 16;
47        const INTENSITY = 32;
48        const INTENSITY_ANIMATED = 64;
49    }
50}
51
52impl BatchValue for DistantLightChangedFields {
53    #[inline]
54    fn write_to(&self, out: &mut Vec<u8>) {
55        self.bits().write_to(out);
56    }
57
58    #[inline]
59    fn read_from(input: &mut BatchValueReader<'_>) -> Self {
60        DistantLightChangedFields::from_bits_retain(u8::read_from(input))
61    }
62}
63
64/// The partial methods of `DistantLight`: implemented by the class that embeds
65/// [`DistantLightProps`]. Every hook defaults to doing nothing.
66pub trait DistantLightHooks: CompositionLightHooks {
67    fn initialize_defaults_extra(&self) {}
68
69    fn validate_color_change(&self, _old_value: Color, _new_value: Color) {}
70    fn on_color_changed(&self) {}
71    fn on_color_changing(&self) {}
72
73    fn validate_coordinate_space_change(&self, _old_value: &Option<Rc<dyn ICompositionObject>>, _new_value: &Option<Rc<dyn ICompositionObject>>) {}
74    fn on_coordinate_space_changed(&self) {}
75    fn on_coordinate_space_changing(&self) {}
76
77    fn validate_direction_change(&self, _old_value: Vector3D, _new_value: Vector3D) {}
78    fn on_direction_changed(&self) {}
79    fn on_direction_changing(&self) {}
80
81    fn validate_intensity_change(&self, _old_value: f32, _new_value: f32) {}
82    fn on_intensity_changed(&self) {}
83    fn on_intensity_changing(&self) {}
84}
85
86/// The UI-thread properties of `DistantLight`.
87pub struct DistantLightProps {
88    base: CompositionLightProps,
89    changed_fields_of_distant_light: Cell<DistantLightChangedFields>,
90    color: Cell<Color>,
91    coordinate_space: RefCell<Option<Rc<dyn ICompositionObject>>>,
92    direction: Cell<Vector3D>,
93    intensity: Cell<f32>,
94}
95
96impl Default for DistantLightProps {
97    fn default() -> Self {
98        Self::new()
99    }
100}
101
102impl DistantLightProps {
103    /// Creates the properties with every field at the default of its type.
104    /// The defaults of the schema are applied by
105    /// [`initialize_defaults`](Self::initialize_defaults).
106    pub fn new() -> Self {
107        Self {
108            base: CompositionLightProps::new(),
109            changed_fields_of_distant_light: Cell::new(DistantLightChangedFields::empty()),
110            color: Cell::new(Default::default()),
111            coordinate_space: RefCell::new(None),
112            direction: Cell::new(Default::default()),
113            intensity: Cell::new(Default::default()),
114        }
115    }
116
117    /// The properties of the base class.
118    pub fn base(&self) -> &CompositionLightProps {
119        &self.base
120    }
121
122    /// The `InitializeDefaults` calls of the constructor chain: the base
123    /// classes first, then this class.
124    pub fn initialize_defaults(&self, host: &dyn DistantLightHooks) {
125        self.base.initialize_defaults(host);
126        self.initialize_own_defaults(host);
127    }
128
129    /// `InitializeDefaults` of this class alone.
130    pub fn initialize_own_defaults(&self, host: &dyn DistantLightHooks) {
131        DistantLightHooks::initialize_defaults_extra(host);
132        self.set_color(host, crate::media::Colors::WHITE);
133        self.set_direction(host, Vector3D::new(0.0, 0.0, -1.0));
134        self.set_intensity(host, 1.0);
135    }
136
137    /// `Color` (animated, default `crate::media::Colors::WHITE`, FerroUI overlay).
138    pub fn color(&self) -> Color {
139        self.color.get()
140    }
141
142    /// Sets `Color` (animated, default `crate::media::Colors::WHITE`, FerroUI overlay).
143    pub fn set_color(&self, host: &dyn DistantLightHooks, value: Color) {
144        let mut changed = false;
145        let old_value = self.color.get();
146        if old_value.differs(&value) {
147            DistantLightHooks::validate_color_change(host, old_value, value);
148            DistantLightHooks::on_color_changing(host);
149            changed = true;
150            // Update the backing value
151            self.color.set(value);
152            // Register object for serialization in the next batch
153            self.changed_fields_of_distant_light.set(self.changed_fields_of_distant_light.get() | DistantLightChangedFields::COLOR);
154            host.register_for_serialization();
155            // Reset previous animation if any
156            host.pending_animations().remove(ServerDistantLightProps::id_of_color_property().base());
157            self.changed_fields_of_distant_light.set(self.changed_fields_of_distant_light.get() & !DistantLightChangedFields::COLOR_ANIMATED);
158            // Check for implicit animations
159            if let Some(animation) = host.implicit_animation("Color") {
160                // Animation affects only current property
161                if let Some(a) = animation.as_composition_animation() {
162                    self.changed_fields_of_distant_light.set(self.changed_fields_of_distant_light.get() | DistantLightChangedFields::COLOR_ANIMATED);
163                    host.pending_animations().set(
164                        ServerDistantLightProps::id_of_color_property().base(),
165                        a.create_instance(host.server(), Some(ExpressionVariant::create(value))),
166                    );
167                }
168                // Animation is triggered by the current field, but does not necessary affects it
169                host.start_animation_group(&animation, "Color", ExpressionVariant::create(value));
170            }
171        }
172        self.color.set(value);
173        if changed {
174            DistantLightHooks::on_color_changed(host);
175        }
176    }
177
178    /// `CoordinateSpace` (FerroUI overlay).
179    pub fn coordinate_space(&self) -> Option<Rc<dyn ICompositionObject>> {
180        self.coordinate_space.borrow().clone()
181    }
182
183    /// Sets `CoordinateSpace` (FerroUI overlay).
184    pub fn set_coordinate_space(&self, host: &dyn DistantLightHooks, value: Option<Rc<dyn ICompositionObject>>) {
185        let mut changed = false;
186        let old_value = self.coordinate_space.borrow().clone();
187        if old_value.differs(&value) {
188            DistantLightHooks::validate_coordinate_space_change(host, &old_value, &value);
189            DistantLightHooks::on_coordinate_space_changing(host);
190            changed = true;
191            // Update the backing value
192            *self.coordinate_space.borrow_mut() = value.clone();
193            // Register object for serialization in the next batch
194            self.changed_fields_of_distant_light.set(self.changed_fields_of_distant_light.get() | DistantLightChangedFields::COORDINATE_SPACE);
195            host.register_for_serialization();
196        }
197        *self.coordinate_space.borrow_mut() = value;
198        if changed {
199            DistantLightHooks::on_coordinate_space_changed(host);
200        }
201    }
202
203    /// `Direction` (animated, default `Vector3D::new(0.0, 0.0, -1.0)`, FerroUI overlay).
204    pub fn direction(&self) -> Vector3D {
205        self.direction.get()
206    }
207
208    /// Sets `Direction` (animated, default `Vector3D::new(0.0, 0.0, -1.0)`, FerroUI overlay).
209    pub fn set_direction(&self, host: &dyn DistantLightHooks, value: Vector3D) {
210        let mut changed = false;
211        let old_value = self.direction.get();
212        if old_value.differs(&value) {
213            DistantLightHooks::validate_direction_change(host, old_value, value);
214            DistantLightHooks::on_direction_changing(host);
215            changed = true;
216            // Update the backing value
217            self.direction.set(value);
218            // Register object for serialization in the next batch
219            self.changed_fields_of_distant_light.set(self.changed_fields_of_distant_light.get() | DistantLightChangedFields::DIRECTION);
220            host.register_for_serialization();
221            // Reset previous animation if any
222            host.pending_animations().remove(ServerDistantLightProps::id_of_direction_property().base());
223            self.changed_fields_of_distant_light.set(self.changed_fields_of_distant_light.get() & !DistantLightChangedFields::DIRECTION_ANIMATED);
224            // Check for implicit animations
225            if let Some(animation) = host.implicit_animation("Direction") {
226                // Animation affects only current property
227                if let Some(a) = animation.as_composition_animation() {
228                    self.changed_fields_of_distant_light.set(self.changed_fields_of_distant_light.get() | DistantLightChangedFields::DIRECTION_ANIMATED);
229                    host.pending_animations().set(
230                        ServerDistantLightProps::id_of_direction_property().base(),
231                        a.create_instance(host.server(), Some(ExpressionVariant::create(value))),
232                    );
233                }
234                // Animation is triggered by the current field, but does not necessary affects it
235                host.start_animation_group(&animation, "Direction", ExpressionVariant::create(value));
236            }
237        }
238        self.direction.set(value);
239        if changed {
240            DistantLightHooks::on_direction_changed(host);
241        }
242    }
243
244    /// `Intensity` (animated, default `1.0`, FerroUI overlay).
245    pub fn intensity(&self) -> f32 {
246        self.intensity.get()
247    }
248
249    /// Sets `Intensity` (animated, default `1.0`, FerroUI overlay).
250    pub fn set_intensity(&self, host: &dyn DistantLightHooks, value: f32) {
251        let mut changed = false;
252        let old_value = self.intensity.get();
253        if old_value.differs(&value) {
254            DistantLightHooks::validate_intensity_change(host, old_value, value);
255            DistantLightHooks::on_intensity_changing(host);
256            changed = true;
257            // Update the backing value
258            self.intensity.set(value);
259            // Register object for serialization in the next batch
260            self.changed_fields_of_distant_light.set(self.changed_fields_of_distant_light.get() | DistantLightChangedFields::INTENSITY);
261            host.register_for_serialization();
262            // Reset previous animation if any
263            host.pending_animations().remove(ServerDistantLightProps::id_of_intensity_property().base());
264            self.changed_fields_of_distant_light.set(self.changed_fields_of_distant_light.get() & !DistantLightChangedFields::INTENSITY_ANIMATED);
265            // Check for implicit animations
266            if let Some(animation) = host.implicit_animation("Intensity") {
267                // Animation affects only current property
268                if let Some(a) = animation.as_composition_animation() {
269                    self.changed_fields_of_distant_light.set(self.changed_fields_of_distant_light.get() | DistantLightChangedFields::INTENSITY_ANIMATED);
270                    host.pending_animations().set(
271                        ServerDistantLightProps::id_of_intensity_property().base(),
272                        a.create_instance(host.server(), Some(ExpressionVariant::create(value))),
273                    );
274                }
275                // Animation is triggered by the current field, but does not necessary affects it
276                host.start_animation_group(&animation, "Intensity", ExpressionVariant::create(value));
277            }
278        }
279        self.intensity.set(value);
280        if changed {
281            DistantLightHooks::on_intensity_changed(host);
282        }
283    }
284
285    /// Connects an animation with the named property. Returns `false` when
286    /// neither this class nor its generated bases have an animated property
287    /// of that name: the caller continues with its hand-written base.
288    pub fn start_animation(
289        &self,
290        host: &dyn DistantLightHooks,
291        property_name: &str,
292        animation: &dyn ICompositionAnimation,
293        final_value: Option<ExpressionVariant>,
294    ) -> bool {
295        if property_name == "Color" {
296            let server = animation.create_instance(host.server(), final_value);
297            host.pending_animations().set(ServerDistantLightProps::id_of_color_property().base(), server);
298            self.changed_fields_of_distant_light.set(self.changed_fields_of_distant_light.get() | DistantLightChangedFields::COLOR_ANIMATED);
299            host.register_for_serialization();
300            return true;
301        }
302        if property_name == "Direction" {
303            let server = animation.create_instance(host.server(), final_value);
304            host.pending_animations().set(ServerDistantLightProps::id_of_direction_property().base(), server);
305            self.changed_fields_of_distant_light.set(self.changed_fields_of_distant_light.get() | DistantLightChangedFields::DIRECTION_ANIMATED);
306            host.register_for_serialization();
307            return true;
308        }
309        if property_name == "Intensity" {
310            let server = animation.create_instance(host.server(), final_value);
311            host.pending_animations().set(ServerDistantLightProps::id_of_intensity_property().base(), server);
312            self.changed_fields_of_distant_light.set(self.changed_fields_of_distant_light.get() | DistantLightChangedFields::INTENSITY_ANIMATED);
313            host.register_for_serialization();
314            return true;
315        }
316        false
317    }
318
319    /// Writes the changes of the object: the base class first, then the
320    /// changed-fields mask and the changed members of this class.
321    pub fn serialize_changes_core(&self, host: &dyn DistantLightHooks, writer: &mut BatchStreamWriter<'_>) {
322        self.base.serialize_changes_core(host, writer);
323        let changed = self.changed_fields_of_distant_light.get();
324        writer.write(changed);
325        if changed.contains(DistantLightChangedFields::COLOR_ANIMATED) {
326            writer.write_value(host.pending_animations().get_and_remove(ServerDistantLightProps::id_of_color_property().base()));
327        } else if changed.contains(DistantLightChangedFields::COLOR) {
328            writer.write(self.color.get());
329        }
330        if changed.contains(DistantLightChangedFields::COORDINATE_SPACE) {
331            writer.write_server_object(self.coordinate_space.borrow().clone().map(|o| o.server()));
332        }
333        if changed.contains(DistantLightChangedFields::DIRECTION_ANIMATED) {
334            writer.write_value(host.pending_animations().get_and_remove(ServerDistantLightProps::id_of_direction_property().base()));
335        } else if changed.contains(DistantLightChangedFields::DIRECTION) {
336            writer.write(self.direction.get());
337        }
338        if changed.contains(DistantLightChangedFields::INTENSITY_ANIMATED) {
339            writer.write_value(host.pending_animations().get_and_remove(ServerDistantLightProps::id_of_intensity_property().base()));
340        } else if changed.contains(DistantLightChangedFields::INTENSITY) {
341            writer.write(self.intensity.get());
342        }
343        self.changed_fields_of_distant_light.set(DistantLightChangedFields::empty());
344    }
345}
346
347/// The partial methods of `ServerDistantLight`: implemented by the class that embeds
348/// [`ServerDistantLightProps`]. Every hook defaults to doing nothing.
349pub trait ServerDistantLightHooks: ServerCompositionLightHooks {
350    fn deserialize_changes_extra(&self, _reader: &mut BatchStreamReader<'_>) {}
351    fn on_fields_deserialized(&self, _changed: DistantLightChangedFields) {}
352
353    fn on_coordinate_space_changed(&self) {}
354    fn on_coordinate_space_changing(&self) {}
355}
356
357/// The server-side properties of `ServerDistantLight`.
358pub struct ServerDistantLightProps {
359    base: ServerCompositionLightProps,
360    color: Cell<Color>,
361    coordinate_space: RefCell<Option<Rc<dyn IServerObject>>>,
362    direction: Cell<Vector3D>,
363    intensity: Cell<f32>,
364}
365
366impl Default for ServerDistantLightProps {
367    fn default() -> Self {
368        Self::new()
369    }
370}
371
372impl ServerDistantLightProps {
373    /// Creates the properties with every field at the default of its type; the
374    /// values of the UI thread arrive with the first batch.
375    pub fn new() -> Self {
376        Self {
377            base: ServerCompositionLightProps::new(),
378            color: Cell::new(Default::default()),
379            coordinate_space: RefCell::new(None),
380            direction: Cell::new(Default::default()),
381            intensity: Cell::new(Default::default()),
382        }
383    }
384
385    /// The properties of the base class.
386    pub fn base(&self) -> &ServerCompositionLightProps {
387        &self.base
388    }
389
390    /// This block or the block of a base class, by type: what
391    /// `IServerObject::get_props` of the embedding class returns.
392    pub fn find_props(&self, type_id: TypeId) -> Option<&dyn Any> {
393        if type_id == TypeId::of::<Self>() {
394            return Some(self);
395        }
396        self.base.find_props(type_id)
397    }
398
399    /// The composition property of `Color`.
400    pub fn id_of_color_property() -> &'static CompositionPropertyOf<Color> {
401        static PROPERTY: OnceLock<CompositionPropertyOf<Color>> = OnceLock::new();
402        PROPERTY.get_or_init(|| {
403            CompositionProperty::register::<ServerDistantLightProps, Color>(
404                "Color",
405                "ServerDistantLight",
406                |obj| props_of::<ServerDistantLightProps>(obj).color.get(),
407                |obj, v| props_of::<ServerDistantLightProps>(obj).color.set(v),
408                Some(|obj| ExpressionVariant::create(props_of::<ServerDistantLightProps>(obj).color.get())),
409                Some(|obj, v: ExpressionVariant| props_of::<ServerDistantLightProps>(obj).color.set(v.cast_or_default::<Color>())),
410            )
411        })
412    }
413
414    /// `Color` (animated).
415    pub fn color(&self) -> Color {
416        self.color.get()
417    }
418
419    pub fn set_color(&self, host: &dyn ServerDistantLightHooks, value: Color) {
420        self.color.set(value);
421        host.remove_animation_for_property(Self::id_of_color_property().base());
422    }
423
424    /// The composition property of `CoordinateSpace`.
425    pub fn id_of_coordinate_space_property() -> &'static CompositionPropertyOf<Option<Rc<dyn IServerObject>>> {
426        static PROPERTY: OnceLock<CompositionPropertyOf<Option<Rc<dyn IServerObject>>>> = OnceLock::new();
427        PROPERTY.get_or_init(|| {
428            CompositionProperty::register::<ServerDistantLightProps, Option<Rc<dyn IServerObject>>>(
429                "CoordinateSpace",
430                "ServerDistantLight",
431                |obj| props_of::<ServerDistantLightProps>(obj).coordinate_space.borrow().clone(),
432                |obj, v| *props_of::<ServerDistantLightProps>(obj).coordinate_space.borrow_mut() = v,
433                None,
434                None,
435            )
436        })
437    }
438
439    /// `CoordinateSpace`.
440    pub fn coordinate_space(&self) -> Option<Rc<dyn IServerObject>> {
441        self.coordinate_space.borrow().clone()
442    }
443
444    pub fn set_coordinate_space(&self, host: &dyn ServerDistantLightHooks, value: Option<Rc<dyn IServerObject>>) {
445        let old_value = self.coordinate_space.borrow().clone();
446        let mut changed = false;
447        if old_value.differs(&value) {
448            ServerDistantLightHooks::on_coordinate_space_changing(host);
449            changed = true;
450        }
451        host.set_value(
452            Self::id_of_coordinate_space_property().base(),
453            ServerValueChange {
454                equal: old_value.default_equals(&value),
455                old_resource: old_value.as_server_render_resource(),
456                new_resource: value.as_server_render_resource(),
457                assign: &mut || *self.coordinate_space.borrow_mut() = value.clone(),
458            },
459        );
460        if changed {
461            ServerDistantLightHooks::on_coordinate_space_changed(host);
462        }
463    }
464
465    /// The composition property of `Direction`.
466    pub fn id_of_direction_property() -> &'static CompositionPropertyOf<Vector3D> {
467        static PROPERTY: OnceLock<CompositionPropertyOf<Vector3D>> = OnceLock::new();
468        PROPERTY.get_or_init(|| {
469            CompositionProperty::register::<ServerDistantLightProps, Vector3D>(
470                "Direction",
471                "ServerDistantLight",
472                |obj| props_of::<ServerDistantLightProps>(obj).direction.get(),
473                |obj, v| props_of::<ServerDistantLightProps>(obj).direction.set(v),
474                Some(|obj| ExpressionVariant::create(props_of::<ServerDistantLightProps>(obj).direction.get())),
475                Some(|obj, v: ExpressionVariant| props_of::<ServerDistantLightProps>(obj).direction.set(v.cast_or_default::<Vector3D>())),
476            )
477        })
478    }
479
480    /// `Direction` (animated).
481    pub fn direction(&self) -> Vector3D {
482        self.direction.get()
483    }
484
485    pub fn set_direction(&self, host: &dyn ServerDistantLightHooks, value: Vector3D) {
486        self.direction.set(value);
487        host.remove_animation_for_property(Self::id_of_direction_property().base());
488    }
489
490    /// The composition property of `Intensity`.
491    pub fn id_of_intensity_property() -> &'static CompositionPropertyOf<f32> {
492        static PROPERTY: OnceLock<CompositionPropertyOf<f32>> = OnceLock::new();
493        PROPERTY.get_or_init(|| {
494            CompositionProperty::register::<ServerDistantLightProps, f32>(
495                "Intensity",
496                "ServerDistantLight",
497                |obj| props_of::<ServerDistantLightProps>(obj).intensity.get(),
498                |obj, v| props_of::<ServerDistantLightProps>(obj).intensity.set(v),
499                Some(|obj| ExpressionVariant::create(props_of::<ServerDistantLightProps>(obj).intensity.get())),
500                Some(|obj, v: ExpressionVariant| props_of::<ServerDistantLightProps>(obj).intensity.set(v.cast_or_default::<f32>())),
501            )
502        })
503    }
504
505    /// `Intensity` (animated).
506    pub fn intensity(&self) -> f32 {
507        self.intensity.get()
508    }
509
510    pub fn set_intensity(&self, host: &dyn ServerDistantLightHooks, value: f32) {
511        self.intensity.set(value);
512        host.remove_animation_for_property(Self::id_of_intensity_property().base());
513    }
514
515    /// Reads the changes of the object: the base class first, then the
516    /// changed-fields mask and the changed members of this class.
517    pub fn deserialize_changes_core(
518        &self,
519        host: &dyn ServerDistantLightHooks,
520        reader: &mut BatchStreamReader<'_>,
521        committed_at: Duration,
522    ) {
523        let compositor = host.server_compositor();
524        self.base.deserialize_changes_core(host, reader, committed_at);
525        ServerDistantLightHooks::deserialize_changes_extra(host, reader);
526        let changed = reader.read::<DistantLightChangedFields>();
527        if changed.contains(DistantLightChangedFields::COLOR_ANIMATED) {
528            let Some(animation) = reader.read_value::<AnimationInstanceFactory>().map(AnimationInstanceFactory::create) else {
529                panic!("a batch starts an animation of `Color` without an animation instance");
530            };
531            host.set_animated_value(
532                Self::id_of_color_property().base(),
533                ExpressionVariant::create(self.color.get()),
534                committed_at,
535                animation,
536            );
537        } else if changed.contains(DistantLightChangedFields::COLOR) {
538            let value = reader.read::<Color>();
539            self.set_color(host, value);
540        }
541        if changed.contains(DistantLightChangedFields::COORDINATE_SPACE) {
542            let value = read_server_object(reader, compositor.as_ref());
543            self.set_coordinate_space(host, value);
544        }
545        if changed.contains(DistantLightChangedFields::DIRECTION_ANIMATED) {
546            let Some(animation) = reader.read_value::<AnimationInstanceFactory>().map(AnimationInstanceFactory::create) else {
547                panic!("a batch starts an animation of `Direction` without an animation instance");
548            };
549            host.set_animated_value(
550                Self::id_of_direction_property().base(),
551                ExpressionVariant::create(self.direction.get()),
552                committed_at,
553                animation,
554            );
555        } else if changed.contains(DistantLightChangedFields::DIRECTION) {
556            let value = reader.read::<Vector3D>();
557            self.set_direction(host, value);
558        }
559        if changed.contains(DistantLightChangedFields::INTENSITY_ANIMATED) {
560            let Some(animation) = reader.read_value::<AnimationInstanceFactory>().map(AnimationInstanceFactory::create) else {
561                panic!("a batch starts an animation of `Intensity` without an animation instance");
562            };
563            host.set_animated_value(
564                Self::id_of_intensity_property().base(),
565                ExpressionVariant::create(self.intensity.get()),
566                committed_at,
567                animation,
568            );
569        } else if changed.contains(DistantLightChangedFields::INTENSITY) {
570            let value = reader.read::<f32>();
571            self.set_intensity(host, value);
572        }
573        ServerDistantLightHooks::on_fields_deserialized(host, changed);
574    }
575
576    /// The composition property with the given name, among the properties
577    /// of this class and of its generated bases that have a variant form.
578    pub fn get_composition_property(name: &str) -> Option<&'static CompositionProperty> {
579        if name == "Color" {
580            return Some(Self::id_of_color_property().base());
581        }
582        if name == "Direction" {
583            return Some(Self::id_of_direction_property().base());
584        }
585        if name == "Intensity" {
586            return Some(Self::id_of_intensity_property().base());
587        }
588        ServerCompositionLightProps::get_composition_property(name)
589    }
590}