1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
use gltf_derive::Validate;
use serde_derive::{Deserialize, Serialize};
#[cfg(feature = "extensions")]
use serde_json::{Map, Value};

/// A node in the node hierarchy.  When the node contains `skin`, all
/// `mesh.primitives` must contain `JOINTS_0` and `WEIGHTS_0` attributes.
/// A node can have either a `matrix` or any combination of
/// `translation`/`rotation`/`scale` (TRS) properties. TRS properties are converted
/// to matrices and postmultiplied in the `T * R * S` order to compose the
/// transformation matrix; first the scale is applied to the vertices, then the
/// rotation, and then the translation. If none are provided, the transform is the
/// identity. When a node is targeted for animation (referenced by an
/// animation.channel.target), only TRS properties may be present; `matrix` will not
/// be present.
#[derive(Clone, Debug, Default, Deserialize, Serialize, Validate)]
pub struct Node {
    #[cfg(feature = "KHR_lights_punctual")]
    #[serde(
        default,
        rename = "KHR_lights_punctual",
        skip_serializing_if = "Option::is_none"
    )]
    pub khr_lights_punctual: Option<khr_lights_punctual::KhrLightsPunctual>,

    #[cfg(feature = "extensions")]
    #[serde(default, flatten)]
    pub others: Map<String, Value>,
}

#[cfg(feature = "KHR_lights_punctual")]
pub mod khr_lights_punctual {
    use crate::validation::{Checked, Error};
    use crate::{Extras, Index, Path, Root};
    use gltf_derive::Validate;
    use serde::{de, ser};
    use serde_derive::{Deserialize, Serialize};
    use std::fmt;

    /// All valid light types.
    pub const VALID_TYPES: &[&str] = &["directional", "point", "spot"];

    #[derive(Clone, Debug, Deserialize, Serialize, Validate)]
    pub struct KhrLightsPunctual {
        pub light: Index<Light>,
    }

    /// Specifies the light type.
    #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
    pub enum Type {
        /// Directional lights act as though they are infinitely far away and emit light in
        /// the direction of the local -z axis. This light type inherits the orientation of
        /// the node that it belongs to; position and scale are ignored except for their
        /// effect on the inherited node orientation. Because it is at an infinite distance,
        /// the light is not attenuated. Its intensity is defined in lumens per metre squared,
        /// or lux (lm/m^2).
        Directional = 1,

        /// Point lights emit light in all directions from their position in space; rotation
        /// and scale are ignored except for their effect on the inherited node position. The
        /// brightness of the light attenuates in a physically correct manner as distance
        /// increases from the light's position (i.e. brightness goes like the inverse square
        /// of the distance). Point light intensity is defined in candela, which is lumens per
        /// square radian (lm/sr)."
        Point,

        /// Spot lights emit light in a cone in the direction of the local -z axis. The angle
        /// and falloff of the cone is defined using two numbers, the innerConeAngle and outer
        /// ConeAngle. As with point lights, the brightness also attenuates in a physically
        /// correct manner as distance increases from the light's position (i.e. brightness
        /// goes like the inverse square of the distance). Spot light intensity refers to the
        /// brightness inside the innerConeAngle (and at the location of the light) and is
        /// defined in candela, which is lumens per square radian (lm/sr). Engines that don't
        /// support two angles for spotlights should use outerConeAngle as the spotlight angle
        /// (leaving innerConeAngle to implicitly be 0).
        Spot,
    }

    #[derive(Clone, Debug, Deserialize, Serialize, Validate)]
    #[gltf(validate_hook = "light_validate_hook")]
    pub struct Light {
        /// Color of the light source.
        #[serde(default = "color_default")]
        pub color: [f32; 3],

        /// Extension specific data.
        #[serde(default, skip_serializing_if = "Option::is_none")]
        pub extensions: Option<std::boxed::Box<serde_json::value::RawValue>>,

        /// Optional application specific data.
        #[serde(default)]
        #[cfg_attr(feature = "extras", serde(skip_serializing_if = "Option::is_none"))]
        #[cfg_attr(not(feature = "extras"), serde(skip_serializing))]
        pub extras: Extras,

        /// Intensity of the light source. `point` and `spot` lights use luminous intensity
        /// in candela (lm/sr) while `directional` lights use illuminance in lux (lm/m^2).
        #[serde(default = "intensity_default")]
        pub intensity: f32,

        /// Optional user-defined name for this object.
        #[cfg(feature = "names")]
        #[cfg_attr(feature = "names", serde(skip_serializing_if = "Option::is_none"))]
        pub name: Option<String>,

        /// A distance cutoff at which the light's intensity may be considered to have reached
        /// zero.
        #[serde(default, skip_serializing_if = "Option::is_none")]
        pub range: Option<f32>,

        /// Spot light parameters.
        #[serde(default, skip_serializing_if = "Option::is_none")]
        pub spot: Option<Spot>,

        /// Specifies the light type.
        #[serde(rename = "type")]
        pub type_: Checked<Type>,
    }

    fn light_validate_hook<P, R>(light: &Light, _root: &Root, path: P, report: &mut R)
    where
        P: Fn() -> Path,
        R: FnMut(&dyn Fn() -> Path, Error),
    {
        if let Checked::Valid(ty) = light.type_.as_ref() {
            if *ty == Type::Spot && light.spot.is_none() {
                report(&|| path().field("spot"), Error::Missing);
            }
        }
    }

    fn color_default() -> [f32; 3] {
        [1.0, 1.0, 1.0]
    }

    fn intensity_default() -> f32 {
        1.0
    }

    /// Spot light parameters.
    #[derive(Clone, Debug, Default, Deserialize, Serialize, Validate)]
    #[serde(rename_all = "camelCase")]
    pub struct Spot {
        /// Angle in radians from centre of spotlight where falloff begins.
        #[serde(default)]
        pub inner_cone_angle: f32,

        /// Angle in radians from centre of spotlight where falloff ends.
        #[serde(default = "outer_cone_angle_default")]
        pub outer_cone_angle: f32,
    }

    fn outer_cone_angle_default() -> f32 {
        std::f32::consts::FRAC_PI_4
    }

    impl<'de> de::Deserialize<'de> for Checked<Type> {
        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
        where
            D: de::Deserializer<'de>,
        {
            struct Visitor;
            impl<'de> de::Visitor<'de> for Visitor {
                type Value = Checked<Type>;

                fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
                    write!(f, "any of: {:?}", VALID_TYPES)
                }

                fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
                where
                    E: de::Error,
                {
                    use self::Type::*;
                    use crate::validation::Checked::*;
                    Ok(match value {
                        "directional" => Valid(Directional),
                        "point" => Valid(Point),
                        "spot" => Valid(Spot),
                        _ => Invalid,
                    })
                }
            }
            deserializer.deserialize_str(Visitor)
        }
    }

    impl ser::Serialize for Type {
        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
        where
            S: ser::Serializer,
        {
            serializer.serialize_str(match *self {
                Type::Directional => "directional",
                Type::Point => "point",
                Type::Spot => "spot",
            })
        }
    }
}

#[cfg(feature = "KHR_materials_variants")]
pub mod khr_materials_variants {
    use crate::validation::{Error, Validate};
    use crate::{Path, Root};
    use serde_derive::{Deserialize, Serialize};

    #[derive(Clone, Debug, Deserialize, Serialize)]
    pub struct Variant {
        pub name: String,
    }

    impl Validate for Variant {
        fn validate<P, R>(&self, root: &Root, path: P, report: &mut R)
        where
            P: Fn() -> Path,
            R: FnMut(&dyn Fn() -> Path, Error),
        {
            self.name.validate(root, || path().field("name"), report);
        }
    }
}

/// The root `Node`s of a scene.
#[derive(Clone, Debug, Default, Deserialize, Serialize, Validate)]
pub struct Scene {
    #[cfg(feature = "extensions")]
    #[serde(default, flatten)]
    pub others: Map<String, Value>,
}