Skip to main content

ferroui_base/rendering/composition/
composition_light.rs

1//! The WinUI lights: `CompositionLight` and its subclasses `AmbientLight`,
2//! `DistantLight`, `PointLight` and `SpotLight`, and the
3//! `VisualUnorderedCollection` of the visuals a light targets and excludes.
4//!
5//! As for the clips, every light is one [`CompositionLight`] behind an `Rc`,
6//! which holds what the concrete class adds in a kind; the concrete classes
7//! are typed handles around the same `Rc` ([`AmbientLight`],
8//! [`DistantLight`], [`PointLight`], [`SpotLight`]).
9//!
10//! A light lights the visuals of its [`targets`](CompositionLight::targets)
11//! and their subtrees, except the visuals of its
12//! [`exclusions_from_targets`](CompositionLight::exclusions_from_targets)
13//! and their subtrees: the diffuse lighting of flat visuals facing the
14//! viewer, see `server::LitContent` for how it is drawn.
15
16use super::composition_object_impls::composition_object_impls;
17use super::generated::{
18    AmbientLightHooks, AmbientLightProps, CompositionLightHooks, CompositionLightProps, DistantLightHooks,
19    DistantLightProps, PointLightHooks, PointLightProps, ServerAmbientLightProps, ServerDistantLightProps,
20    ServerPointLightProps, ServerSpotLightProps, SpotLightHooks, SpotLightProps, VisualUnorderedCollectionHooks,
21    VisualUnorderedCollectionProps,
22};
23use super::server::{
24    IServerObject, ServerAmbientLight, ServerCompositor, ServerDistantLight, ServerObjectId, ServerPointLight,
25    ServerSpotLight, ServerVisualUnorderedCollection,
26};
27use super::{CompositionObject, CompositionVisual, Compositor, ICompositionObject};
28use crate::media::Color;
29use crate::Vector3D;
30use std::cell::RefCell;
31use std::ops::Deref;
32use std::rc::{Rc, Weak};
33
34// --- the visual collection -------------------------------------------------------------
35
36/// A set of visuals (WinUI
37/// `Microsoft.UI.Composition.VisualUnorderedCollection`): the targets or the
38/// exclusions of a light.
39pub struct VisualUnorderedCollection {
40    this: Weak<VisualUnorderedCollection>,
41    object: CompositionObject,
42    props: VisualUnorderedCollectionProps<CompositionVisual>,
43}
44
45impl VisualUnorderedCollection {
46    pub(crate) fn new(compositor: &Rc<Compositor>) -> Rc<VisualUnorderedCollection> {
47        let server =
48            compositor.create_server_object(|compositor, _| ServerVisualUnorderedCollection::new(compositor));
49        let collection = Rc::new_cyclic(|this: &Weak<VisualUnorderedCollection>| VisualUnorderedCollection {
50            this: this.clone(),
51            object: CompositionObject::new(compositor, Some(server)),
52            props: VisualUnorderedCollectionProps::new(),
53        });
54        collection.props.initialize_defaults(&*collection);
55        collection
56    }
57
58    /// The id of the server-side collection.
59    pub fn server(&self) -> ServerObjectId {
60        self.object.required_server()
61    }
62
63    /// A snapshot of the visuals, for enumeration (in the order they were
64    /// added, which has no meaning).
65    pub fn items(&self) -> Vec<Rc<CompositionVisual>> {
66        self.props.items()
67    }
68
69    /// `Add`: adds a visual; a visual that is in the collection already is
70    /// not added again.
71    pub fn add(&self, visual: Rc<CompositionVisual>) {
72        if !self.props.contains(&visual) {
73            self.props.add(self, visual);
74        }
75    }
76
77    /// `Remove`: removes a visual; `false` when it was not in the
78    /// collection.
79    pub fn remove(&self, visual: &Rc<CompositionVisual>) -> bool {
80        self.props.remove(self, visual)
81    }
82
83    /// `RemoveAll`.
84    pub fn remove_all(&self) {
85        self.props.clear(self);
86    }
87
88    /// `Count`.
89    pub fn count(&self) -> usize {
90        self.props.count()
91    }
92
93    /// Whether the visual is in the collection (compared by reference).
94    pub fn contains(&self, visual: &Rc<CompositionVisual>) -> bool {
95        self.props.contains(visual)
96    }
97
98    pub fn is_disposed(&self) -> bool {
99        self.object.is_disposed()
100    }
101
102    pub fn dispose(&self) {
103        self.object.dispose();
104    }
105}
106
107impl VisualUnorderedCollectionHooks<CompositionVisual> for VisualUnorderedCollection {}
108
109composition_object_impls!(
110    VisualUnorderedCollection, "VisualUnorderedCollection",
111    serialize: |this, writer| this.props.serialize_changes_core(this, writer),
112    start_animation: |_this, _name, _animation, _final_value| false,
113    composition_property: |_this, _name| None,
114);
115
116// --- the lights ----------------------------------------------------------------------
117
118/// What the concrete class of a light adds to [`CompositionLight`].
119pub(crate) enum CompositionLightKind {
120    Ambient(AmbientLightProps),
121    Distant(DistantLightProps),
122    Point(PointLightProps),
123    Spot(SpotLightProps),
124}
125
126/// A light (WinUI `Microsoft.UI.Composition.CompositionLight`): it lights
127/// the visuals of [`targets`](Self::targets) and their subtrees, minus the
128/// visuals of [`exclusions_from_targets`](Self::exclusions_from_targets)
129/// and their subtrees, while it [`is_enabled`](Self::is_enabled). A visual
130/// that some enabled light reaches shows its content multiplied by the sum
131/// of the lights that reach it, so it is dark where none of them shines.
132///
133/// Deviation (winui-composition.md): the lighting is a light map drawn over
134/// the content of each lit visual, see `server::LitContent`.
135pub struct CompositionLight {
136    this: Weak<CompositionLight>,
137    object: CompositionObject,
138    kind: CompositionLightKind,
139    targets: Rc<VisualUnorderedCollection>,
140    exclusions: Rc<VisualUnorderedCollection>,
141    /// The XAML light the light belongs to (winui-materials.md): its id and
142    /// the visual of the element that holds it, and whether they changed
143    /// since the last batch.
144    xaml_binding: RefCell<(Option<String>, Option<Rc<CompositionVisual>>, bool)>,
145}
146
147impl CompositionLight {
148    fn create(
149        compositor: &Rc<Compositor>,
150        kind: CompositionLightKind,
151        server: impl FnOnce(&Rc<ServerCompositor>) -> Rc<dyn IServerObject> + Send + 'static,
152    ) -> Rc<CompositionLight> {
153        // The collections first: the light refers to their server objects.
154        let targets = VisualUnorderedCollection::new(compositor);
155        let exclusions = VisualUnorderedCollection::new(compositor);
156        let server = compositor.create_server_object(move |compositor, _| server(compositor));
157        let light = Rc::new_cyclic(|this: &Weak<CompositionLight>| CompositionLight {
158            this: this.clone(),
159            object: CompositionObject::new(compositor, Some(server)),
160            kind,
161            targets,
162            exclusions,
163            xaml_binding: RefCell::new((None, None, false)),
164        });
165        match &light.kind {
166            CompositionLightKind::Ambient(props) => props.initialize_defaults(&*light),
167            CompositionLightKind::Distant(props) => props.initialize_defaults(&*light),
168            CompositionLightKind::Point(props) => props.initialize_defaults(&*light),
169            CompositionLightKind::Spot(props) => props.initialize_defaults(&*light),
170        }
171        let targets: Rc<dyn ICompositionObject> = light.targets.clone();
172        light.light_props().set_targets(&*light, Some(targets));
173        let exclusions: Rc<dyn ICompositionObject> = light.exclusions.clone();
174        light.light_props().set_exclusions_from_targets(&*light, Some(exclusions));
175        light
176    }
177
178    /// The generated properties of `CompositionLight`.
179    pub fn light_props(&self) -> &CompositionLightProps {
180        match &self.kind {
181            CompositionLightKind::Ambient(props) => props.base(),
182            CompositionLightKind::Distant(props) => props.base(),
183            CompositionLightKind::Point(props) => props.base(),
184            CompositionLightKind::Spot(props) => props.base(),
185        }
186    }
187
188    pub fn compositor(&self) -> &Rc<Compositor> {
189        self.object.compositor()
190    }
191
192    /// The embedded `CompositionObject`.
193    pub fn object(&self) -> &CompositionObject {
194        &self.object
195    }
196
197    /// The id of the server-side light.
198    pub fn server(&self) -> ServerObjectId {
199        self.object.required_server()
200    }
201
202    pub fn is_disposed(&self) -> bool {
203        self.object.is_disposed()
204    }
205
206    /// Disposes the light and its two collections: the visuals it lit are
207    /// drawn without it from the next frame.
208    pub fn dispose(&self) {
209        self.object.dispose();
210        self.targets.dispose();
211        self.exclusions.dispose();
212    }
213
214    /// The name of the concrete class, for messages.
215    pub fn type_name(&self) -> &'static str {
216        match &self.kind {
217            CompositionLightKind::Ambient(_) => "AmbientLight",
218            CompositionLightKind::Distant(_) => "DistantLight",
219            CompositionLightKind::Point(_) => "PointLight",
220            CompositionLightKind::Spot(_) => "SpotLight",
221        }
222    }
223
224    /// `Targets`: the visuals the light lights, with their subtrees.
225    pub fn targets(&self) -> &Rc<VisualUnorderedCollection> {
226        &self.targets
227    }
228
229    /// `ExclusionsFromTargets`: the visuals, with their subtrees, the light
230    /// does not light although they are in the subtree of a target.
231    pub fn exclusions_from_targets(&self) -> &Rc<VisualUnorderedCollection> {
232        &self.exclusions
233    }
234
235    /// Binds the light to a XAML light (`XamlLight.CompositionLight`): the
236    /// light then also lights the XAML brushes that target the id `id`
237    /// (`XamlLight.AddTargetBrush`) where they are drawn in the subtree of
238    /// `scope`, the visual of the element whose `Lights` holds the XAML
239    /// light. `None` unbinds it. Called by `XamlLight`.
240    pub fn set_xaml_binding(&self, id: Option<String>, scope: Option<Rc<CompositionVisual>>) {
241        {
242            let mut binding = self.xaml_binding.borrow_mut();
243            let same_scope = match (&binding.1, &scope) {
244                (Some(a), Some(b)) => Rc::ptr_eq(a, b),
245                (None, None) => true,
246                _ => false,
247            };
248            if binding.0 == id && same_scope {
249                return;
250            }
251            *binding = (id, scope, true);
252        }
253        self.object
254            .register_for_serialization(|| self.this.upgrade().map(|this| this as Rc<dyn crate::rendering::composition::ICompositorSerializable>));
255    }
256
257    /// The id of the XAML light the light is bound to, if any.
258    pub fn xaml_id(&self) -> Option<String> {
259        self.xaml_binding.borrow().0.clone()
260    }
261
262    /// Writes the XAML binding when it changed (after the properties).
263    fn serialize_xaml_binding(&self, writer: &mut crate::rendering::composition::transport::BatchStreamWriter<'_>) {
264        let mut binding = self.xaml_binding.borrow_mut();
265        let changes = binding.2.then(|| crate::rendering::composition::server::XamlLightChanges {
266            id: binding.0.clone(),
267            scope: binding.1.as_ref().and_then(|scope| scope.object().server()),
268        });
269        binding.2 = false;
270        writer.write_value(changes);
271    }
272
273    /// `IsEnabled` (default `true`): a disabled light lights nothing, and
274    /// the visuals it targets are drawn as if it did not exist.
275    pub fn is_enabled(&self) -> bool {
276        self.light_props().is_enabled()
277    }
278
279    pub fn set_is_enabled(&self, value: bool) {
280        self.light_props().set_is_enabled(self, value)
281    }
282}
283
284impl CompositionLightHooks for CompositionLight {}
285
286impl AmbientLightHooks for CompositionLight {}
287
288impl DistantLightHooks for CompositionLight {}
289
290impl PointLightHooks for CompositionLight {}
291
292impl SpotLightHooks for CompositionLight {}
293
294composition_object_impls!(
295    CompositionLight, "CompositionLight",
296    serialize: |this, writer| {
297        match &this.kind {
298            CompositionLightKind::Ambient(props) => props.serialize_changes_core(this, writer),
299            CompositionLightKind::Distant(props) => props.serialize_changes_core(this, writer),
300            CompositionLightKind::Point(props) => props.serialize_changes_core(this, writer),
301            CompositionLightKind::Spot(props) => props.serialize_changes_core(this, writer),
302        }
303        this.serialize_xaml_binding(writer);
304    },
305    start_animation: |this, name, animation, final_value| match &this.kind {
306        CompositionLightKind::Ambient(props) => props.start_animation(this, name, animation, final_value),
307        CompositionLightKind::Distant(props) => props.start_animation(this, name, animation, final_value),
308        CompositionLightKind::Point(props) => props.start_animation(this, name, animation, final_value),
309        CompositionLightKind::Spot(props) => props.start_animation(this, name, animation, final_value),
310    },
311    composition_property: |this, name| match &this.kind {
312        CompositionLightKind::Ambient(_) => ServerAmbientLightProps::get_composition_property(name),
313        CompositionLightKind::Distant(_) => ServerDistantLightProps::get_composition_property(name),
314        CompositionLightKind::Point(_) => ServerPointLightProps::get_composition_property(name),
315        CompositionLightKind::Spot(_) => ServerSpotLightProps::get_composition_property(name),
316    },
317);
318
319/// The visual of an object property, as a visual.
320fn as_visual(object: Option<Rc<dyn ICompositionObject>>) -> Option<Rc<CompositionVisual>> {
321    object.and_then(|visual| visual.into_any_rc().downcast::<CompositionVisual>().ok())
322}
323
324/// A visual for an object property.
325fn as_object(visual: Option<Rc<CompositionVisual>>) -> Option<Rc<dyn ICompositionObject>> {
326    visual.map(|visual| visual as Rc<dyn ICompositionObject>)
327}
328
329/// Declares the typed handle of a light class: the struct, `Deref` to the
330/// light, and `from_light`.
331macro_rules! light_handle {
332    ($(#[$meta:meta])* $name:ident, $kind:ident, $props:ident) => {
333        $(#[$meta])*
334        #[derive(Clone)]
335        pub struct $name(Rc<CompositionLight>);
336
337        impl Deref for $name {
338            type Target = Rc<CompositionLight>;
339
340            fn deref(&self) -> &Rc<CompositionLight> {
341                &self.0
342            }
343        }
344
345        impl $name {
346            /// The handle of `light` as this class, if it is one.
347            pub fn from_light(light: &Rc<CompositionLight>) -> Option<$name> {
348                matches!(light.kind, CompositionLightKind::$kind(_)).then(|| $name(light.clone()))
349            }
350
351            /// The generated properties of the class.
352            pub fn props(&self) -> &$props {
353                match &self.0.kind {
354                    CompositionLightKind::$kind(props) => props,
355                    _ => unreachable!("a light handle wraps a light of its class"),
356                }
357            }
358        }
359    };
360}
361
362light_handle!(
363    /// A light that lights every point of its targets alike (WinUI
364    /// `Microsoft.UI.Composition.AmbientLight`): `Color` times `Intensity`,
365    /// both animatable.
366    AmbientLight,
367    Ambient,
368    AmbientLightProps
369);
370
371impl AmbientLight {
372    pub(crate) fn new(compositor: &Rc<Compositor>) -> AmbientLight {
373        AmbientLight(CompositionLight::create(
374            compositor,
375            CompositionLightKind::Ambient(AmbientLightProps::new()),
376            |c| ServerAmbientLight::new(c),
377        ))
378    }
379
380    /// The colour of the light (default white).
381    pub fn color(&self) -> Color {
382        self.props().color()
383    }
384
385    pub fn set_color(&self, value: Color) {
386        self.props().set_color(&*self.0, value)
387    }
388
389    /// The intensity of the light (default 1).
390    pub fn intensity(&self) -> f32 {
391        self.props().intensity()
392    }
393
394    pub fn set_intensity(&self, value: f32) {
395        self.props().set_intensity(&*self.0, value)
396    }
397}
398
399light_handle!(
400    /// A light from far away in one direction (WinUI
401    /// `Microsoft.UI.Composition.DistantLight`): every point of a flat
402    /// visual is lit alike, by the cosine of the angle between the direction
403    /// and the visual. `Color`, `Direction` and `Intensity` are animatable.
404    DistantLight,
405    Distant,
406    DistantLightProps
407);
408
409impl DistantLight {
410    pub(crate) fn new(compositor: &Rc<Compositor>) -> DistantLight {
411        DistantLight(CompositionLight::create(
412            compositor,
413            CompositionLightKind::Distant(DistantLightProps::new()),
414            |c| ServerDistantLight::new(c),
415        ))
416    }
417
418    /// The colour of the light (default white).
419    pub fn color(&self) -> Color {
420        self.props().color()
421    }
422
423    pub fn set_color(&self, value: Color) {
424        self.props().set_color(&*self.0, value)
425    }
426
427    /// The visual whose coordinates `Direction` is in; `None` for the
428    /// targeted visual.
429    pub fn coordinate_space(&self) -> Option<Rc<CompositionVisual>> {
430        as_visual(self.props().coordinate_space())
431    }
432
433    pub fn set_coordinate_space(&self, value: Option<Rc<CompositionVisual>>) {
434        self.props().set_coordinate_space(&*self.0, as_object(value))
435    }
436
437    /// The direction the light shines in (default `(0, 0, -1)`: straight at
438    /// a visual, which faces +Z).
439    pub fn direction(&self) -> Vector3D {
440        self.props().direction()
441    }
442
443    pub fn set_direction(&self, value: Vector3D) {
444        self.props().set_direction(&*self.0, value)
445    }
446
447    /// The intensity of the light (default 1).
448    pub fn intensity(&self) -> f32 {
449        self.props().intensity()
450    }
451
452    pub fn set_intensity(&self, value: f32) {
453        self.props().set_intensity(&*self.0, value)
454    }
455}
456
457light_handle!(
458    /// A light from a point (WinUI `Microsoft.UI.Composition.PointLight`):
459    /// a point of a visual is lit by the cosine of the angle of the light to
460    /// the visual, weakened by the attenuation of the distance
461    /// `1 / (constant + linear * d + quadratic * d^2)` between the minimum and
462    /// the maximum cutoff. Every member but `CoordinateSpace` is animatable.
463    PointLight,
464    Point,
465    PointLightProps
466);
467
468impl PointLight {
469    pub(crate) fn new(compositor: &Rc<Compositor>) -> PointLight {
470        PointLight(CompositionLight::create(
471            compositor,
472            CompositionLightKind::Point(PointLightProps::new()),
473            |c| ServerPointLight::new(c),
474        ))
475    }
476
477    /// The colour of the light (default white).
478    pub fn color(&self) -> Color {
479        self.props().color()
480    }
481
482    pub fn set_color(&self, value: Color) {
483        self.props().set_color(&*self.0, value)
484    }
485
486    /// The constant term of the attenuation (default 1).
487    pub fn constant_attenuation(&self) -> f32 {
488        self.props().constant_attenuation()
489    }
490
491    pub fn set_constant_attenuation(&self, value: f32) {
492        self.props().set_constant_attenuation(&*self.0, value)
493    }
494
495    /// The term of the attenuation proportional to the distance (default 0).
496    pub fn linear_attenuation(&self) -> f32 {
497        self.props().linear_attenuation()
498    }
499
500    pub fn set_linear_attenuation(&self, value: f32) {
501        self.props().set_linear_attenuation(&*self.0, value)
502    }
503
504    /// The term of the attenuation proportional to the square of the
505    /// distance (default 0).
506    pub fn quadratic_attenuation(&self) -> f32 {
507        self.props().quadratic_attenuation()
508    }
509
510    pub fn set_quadratic_attenuation(&self, value: f32) {
511        self.props().set_quadratic_attenuation(&*self.0, value)
512    }
513
514    /// The visual whose coordinates `Offset` is in; `None` for the targeted
515    /// visual.
516    pub fn coordinate_space(&self) -> Option<Rc<CompositionVisual>> {
517        as_visual(self.props().coordinate_space())
518    }
519
520    pub fn set_coordinate_space(&self, value: Option<Rc<CompositionVisual>>) {
521        self.props().set_coordinate_space(&*self.0, as_object(value))
522    }
523
524    /// The position of the light in its coordinate space; Z is the height
525    /// above the visuals.
526    pub fn offset(&self) -> Vector3D {
527        self.props().offset()
528    }
529
530    pub fn set_offset(&self, value: Vector3D) {
531        self.props().set_offset(&*self.0, value)
532    }
533
534    /// The intensity of the light (default 1).
535    pub fn intensity(&self) -> f32 {
536        self.props().intensity()
537    }
538
539    pub fn set_intensity(&self, value: f32) {
540        self.props().set_intensity(&*self.0, value)
541    }
542
543    /// The distance up to which the light is not attenuated (default 0).
544    pub fn min_attenuation_cutoff(&self) -> f32 {
545        self.props().min_attenuation_cutoff()
546    }
547
548    pub fn set_min_attenuation_cutoff(&self, value: f32) {
549        self.props().set_min_attenuation_cutoff(&*self.0, value)
550    }
551
552    /// The distance beyond which the light lights nothing (default
553    /// `f32::MAX`).
554    pub fn max_attenuation_cutoff(&self) -> f32 {
555        self.props().max_attenuation_cutoff()
556    }
557
558    pub fn set_max_attenuation_cutoff(&self, value: f32) {
559        self.props().set_max_attenuation_cutoff(&*self.0, value)
560    }
561}
562
563light_handle!(
564    /// A light from a point into a cone (WinUI
565    /// `Microsoft.UI.Composition.SpotLight`): a point light whose light is
566    /// the inner cone colour inside the inner cone, falls off to the outer
567    /// cone colour and to nothing at the edge of the outer cone, and is
568    /// nothing outside it. The cone angles are measured from the direction
569    /// to the edge of the cone, in radians. Every member but
570    /// `CoordinateSpace` is animatable.
571    ///
572    /// Deviation (winui-composition.md): `InnerConeAngleInDegrees` and
573    /// `OuterConeAngleInDegrees` are views of the radians and cannot be the
574    /// target of an animation.
575    SpotLight,
576    Spot,
577    SpotLightProps
578);
579
580impl SpotLight {
581    pub(crate) fn new(compositor: &Rc<Compositor>) -> SpotLight {
582        SpotLight(CompositionLight::create(
583            compositor,
584            CompositionLightKind::Spot(SpotLightProps::new()),
585            |c| ServerSpotLight::new(c),
586        ))
587    }
588
589    /// The constant term of the attenuation (default 1).
590    pub fn constant_attenuation(&self) -> f32 {
591        self.props().constant_attenuation()
592    }
593
594    pub fn set_constant_attenuation(&self, value: f32) {
595        self.props().set_constant_attenuation(&*self.0, value)
596    }
597
598    /// The term of the attenuation proportional to the distance (default 0).
599    pub fn linear_attenuation(&self) -> f32 {
600        self.props().linear_attenuation()
601    }
602
603    pub fn set_linear_attenuation(&self, value: f32) {
604        self.props().set_linear_attenuation(&*self.0, value)
605    }
606
607    /// The term of the attenuation proportional to the square of the
608    /// distance (default 0).
609    pub fn quadratic_attenuation(&self) -> f32 {
610        self.props().quadratic_attenuation()
611    }
612
613    pub fn set_quadratic_attenuation(&self, value: f32) {
614        self.props().set_quadratic_attenuation(&*self.0, value)
615    }
616
617    /// The visual whose coordinates `Offset` and `Direction` are in; `None`
618    /// for the targeted visual.
619    pub fn coordinate_space(&self) -> Option<Rc<CompositionVisual>> {
620        as_visual(self.props().coordinate_space())
621    }
622
623    pub fn set_coordinate_space(&self, value: Option<Rc<CompositionVisual>>) {
624        self.props().set_coordinate_space(&*self.0, as_object(value))
625    }
626
627    /// The direction the light shines in (default `(0, 0, -1)`).
628    pub fn direction(&self) -> Vector3D {
629        self.props().direction()
630    }
631
632    pub fn set_direction(&self, value: Vector3D) {
633        self.props().set_direction(&*self.0, value)
634    }
635
636    /// The angle from the direction to the edge of the inner cone, in
637    /// radians (default 0).
638    pub fn inner_cone_angle(&self) -> f32 {
639        self.props().inner_cone_angle()
640    }
641
642    pub fn set_inner_cone_angle(&self, value: f32) {
643        self.props().set_inner_cone_angle(&*self.0, value)
644    }
645
646    /// The inner cone angle in degrees: a view of
647    /// [`inner_cone_angle`](Self::inner_cone_angle).
648    pub fn inner_cone_angle_in_degrees(&self) -> f32 {
649        self.inner_cone_angle().to_degrees()
650    }
651
652    pub fn set_inner_cone_angle_in_degrees(&self, value: f32) {
653        self.set_inner_cone_angle(value.to_radians())
654    }
655
656    /// The colour of the light inside the inner cone (default white).
657    pub fn inner_cone_color(&self) -> Color {
658        self.props().inner_cone_color()
659    }
660
661    pub fn set_inner_cone_color(&self, value: Color) {
662        self.props().set_inner_cone_color(&*self.0, value)
663    }
664
665    /// The intensity of the light inside the inner cone (default 1).
666    pub fn inner_cone_intensity(&self) -> f32 {
667        self.props().inner_cone_intensity()
668    }
669
670    pub fn set_inner_cone_intensity(&self, value: f32) {
671        self.props().set_inner_cone_intensity(&*self.0, value)
672    }
673
674    /// The angle from the direction to the edge of the outer cone, in
675    /// radians (default pi / 4).
676    pub fn outer_cone_angle(&self) -> f32 {
677        self.props().outer_cone_angle()
678    }
679
680    pub fn set_outer_cone_angle(&self, value: f32) {
681        self.props().set_outer_cone_angle(&*self.0, value)
682    }
683
684    /// The outer cone angle in degrees: a view of
685    /// [`outer_cone_angle`](Self::outer_cone_angle).
686    pub fn outer_cone_angle_in_degrees(&self) -> f32 {
687        self.outer_cone_angle().to_degrees()
688    }
689
690    pub fn set_outer_cone_angle_in_degrees(&self, value: f32) {
691        self.set_outer_cone_angle(value.to_radians())
692    }
693
694    /// The colour of the light between the inner and the outer cone
695    /// (default white).
696    pub fn outer_cone_color(&self) -> Color {
697        self.props().outer_cone_color()
698    }
699
700    pub fn set_outer_cone_color(&self, value: Color) {
701        self.props().set_outer_cone_color(&*self.0, value)
702    }
703
704    /// The intensity of the light between the inner and the outer cone
705    /// (default 1).
706    pub fn outer_cone_intensity(&self) -> f32 {
707        self.props().outer_cone_intensity()
708    }
709
710    pub fn set_outer_cone_intensity(&self, value: f32) {
711        self.props().set_outer_cone_intensity(&*self.0, value)
712    }
713
714    /// The position of the light in its coordinate space; Z is the height
715    /// above the visuals.
716    pub fn offset(&self) -> Vector3D {
717        self.props().offset()
718    }
719
720    pub fn set_offset(&self, value: Vector3D) {
721        self.props().set_offset(&*self.0, value)
722    }
723
724    /// The distance up to which the light is not attenuated (default 0).
725    pub fn min_attenuation_cutoff(&self) -> f32 {
726        self.props().min_attenuation_cutoff()
727    }
728
729    pub fn set_min_attenuation_cutoff(&self, value: f32) {
730        self.props().set_min_attenuation_cutoff(&*self.0, value)
731    }
732
733    /// The distance beyond which the light lights nothing (default
734    /// `f32::MAX`).
735    pub fn max_attenuation_cutoff(&self) -> f32 {
736        self.props().max_attenuation_cutoff()
737    }
738
739    pub fn set_max_attenuation_cutoff(&self, value: f32) {
740        self.props().set_max_attenuation_cutoff(&*self.0, value)
741    }
742}
743
744impl Compositor {
745    /// `CreateAmbientLight`.
746    pub fn create_ambient_light(&self) -> AmbientLight {
747        AmbientLight::new(&self.this_handle())
748    }
749
750    /// `CreateDistantLight`.
751    pub fn create_distant_light(&self) -> DistantLight {
752        DistantLight::new(&self.this_handle())
753    }
754
755    /// `CreatePointLight`.
756    pub fn create_point_light(&self) -> PointLight {
757        PointLight::new(&self.this_handle())
758    }
759
760    /// `CreateSpotLight`.
761    pub fn create_spot_light(&self) -> SpotLight {
762        SpotLight::new(&self.this_handle())
763    }
764}