gltf_v1_json/extensions/
light.rs

1use std::fmt;
2
3use gltf_v1_derive::Validate;
4use map::IndexMap;
5use serde::{de, ser};
6use serde_derive::{Deserialize, Serialize};
7
8use crate::{Path, Root, StringIndex, gltf::Get, validation::Checked};
9
10#[derive(Clone, Debug, PartialEq, Eq, Copy, Default)]
11pub enum Type {
12    #[default]
13    Ambient,
14    Directional,
15    Point,
16    Spot,
17}
18
19impl Type {
20    pub const VALID_TYPES: &[&str] = &["ambient", "directional", "point", "spot"];
21}
22impl TryFrom<&str> for Type {
23    type Error = ();
24
25    fn try_from(value: &str) -> Result<Self, Self::Error> {
26        match value {
27            "ambient" => Ok(Type::Ambient),
28            "directional" => Ok(Type::Directional),
29            "point" => Ok(Type::Point),
30            "spot" => Ok(Type::Spot),
31            _ => Err(()),
32        }
33    }
34}
35
36impl From<Type> for &str {
37    fn from(value: Type) -> Self {
38        match value {
39            Type::Ambient => "ambient",
40            Type::Directional => "directional",
41            Type::Point => "point",
42            Type::Spot => "spot",
43        }
44    }
45}
46
47impl ser::Serialize for Type {
48    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
49    where
50        S: ser::Serializer,
51    {
52        serializer.serialize_str(Into::into(*self))
53    }
54}
55
56impl<'de> de::Deserialize<'de> for Checked<Type> {
57    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
58    where
59        D: de::Deserializer<'de>,
60    {
61        struct Visitor;
62        impl de::Visitor<'_> for Visitor {
63            type Value = Checked<Type>;
64
65            fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
66                write!(f, "any of: {:?}", Type::VALID_TYPES)
67            }
68
69            fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
70            where
71                E: de::Error,
72            {
73                Ok(TryInto::try_into(value)
74                    .map(Checked::Valid)
75                    .unwrap_or(Checked::Invalid))
76            }
77        }
78        deserializer.deserialize_str(Visitor)
79    }
80}
81
82fn f32vec4_is_default(value: &[f32; 4]) -> bool {
83    value[0] == 0.0 && value[1] == 0.0 && value[2] == 0.0 && value[3] == 1.0
84}
85
86fn default_f32vec4() -> [f32; 4] {
87    [0.0, 0.0, 0.0, 1.0]
88}
89
90fn f320_default() -> f32 {
91    0.0
92}
93fn f321_default() -> f32 {
94    1.0
95}
96fn half_pi_default() -> f32 {
97    std::f32::consts::PI / 2.0
98}
99
100#[derive(Clone, Debug, Default, Deserialize, Serialize, Validate)]
101pub struct DirectionalLight {
102    #[serde(
103        skip_serializing_if = "f32vec4_is_default",
104        default = "default_f32vec4"
105    )]
106    pub color: [f32; 4],
107}
108
109#[derive(Clone, Debug, Default, Deserialize, Serialize, Validate)]
110pub struct AmbientLight {
111    #[serde(
112        skip_serializing_if = "f32vec4_is_default",
113        default = "default_f32vec4"
114    )]
115    pub color: [f32; 4],
116}
117#[derive(Clone, Debug, Default, Deserialize, Serialize, Validate)]
118pub struct PointLight {
119    #[serde(
120        skip_serializing_if = "f32vec4_is_default",
121        default = "default_f32vec4"
122    )]
123    pub color: [f32; 4],
124    #[serde(rename = "constantAttenuation", default = "f320_default")]
125    pub constant_attenuation: f32,
126    #[serde(rename = "linearAttenuation", default = "f321_default")]
127    pub linear_attenuation: f32,
128    #[serde(rename = "quadraticAttenuation", default = "f321_default")]
129    pub quadratic_attenuation: f32,
130    #[serde(default = "f320_default")]
131    pub distance: f32,
132}
133
134#[derive(Clone, Debug, Default, Deserialize, Serialize, Validate)]
135pub struct SpotLight {
136    #[serde(
137        skip_serializing_if = "f32vec4_is_default",
138        default = "default_f32vec4"
139    )]
140    pub color: [f32; 4],
141    #[serde(rename = "constantAttenuation", default = "f320_default")]
142    pub constant_attenuation: f32,
143    #[serde(rename = "linearAttenuation", default = "f321_default")]
144    pub linear_attenuation: f32,
145    #[serde(rename = "quadraticAttenuation", default = "f321_default")]
146    pub quadratic_attenuation: f32,
147    #[serde(default = "f320_default")]
148    pub distance: f32,
149    #[serde(rename = "falloffAngle", default = "half_pi_default")]
150    pub falloff_angle: f32,
151    #[serde(rename = "falloffExponent", default = "f320_default")]
152    pub falloff_exponent: f32,
153}
154
155#[derive(Clone, Debug, Default, Deserialize, Serialize, Validate)]
156#[gltf(validate_hook = "light_validate_light")]
157pub struct Light {
158    #[serde(skip_serializing_if = "Option::is_none")]
159    pub name: Option<String>,
160    #[serde(skip_serializing_if = "Option::is_none")]
161    pub ambient: Option<AmbientLight>,
162    #[serde(skip_serializing_if = "Option::is_none")]
163    pub directional: Option<DirectionalLight>,
164    #[serde(skip_serializing_if = "Option::is_none")]
165    pub point: Option<PointLight>,
166    #[serde(skip_serializing_if = "Option::is_none")]
167    pub spot: Option<SpotLight>,
168    #[serde(rename = "type")]
169    pub type_: Checked<Type>,
170}
171
172#[derive(Clone, Debug, Default, Deserialize, Serialize, Validate)]
173pub struct Lights {
174    #[serde(default)]
175    #[serde(rename = "lights")]
176    #[serde(skip_serializing_if = "IndexMap::is_empty")]
177    pub lights: IndexMap<String, Light>,
178}
179
180#[cfg(feature = "KHR_materials_common")]
181impl Get<Light> for Root {
182    fn get(&self, index: StringIndex<Light>) -> Option<&Light> {
183        self.extensions
184            .as_ref()?
185            .ktr_materials_common
186            .as_ref()?
187            .lights
188            .get(index.value())
189    }
190}
191
192fn light_validate_light<P, R>(light: &Light, _root: &Root, path: P, report: &mut R)
193where
194    P: Fn() -> Path,
195    R: FnMut(&dyn Fn() -> Path, crate::validation::Error),
196{
197    match light.type_ {
198        Checked::Valid(Type::Spot) if light.spot.is_none() => {
199            report(&path, crate::validation::Error::Missing)
200        }
201        Checked::Valid(Type::Ambient) if light.ambient.is_none() => {
202            report(&path, crate::validation::Error::Missing)
203        }
204        Checked::Valid(Type::Point) if light.point.is_none() => {
205            report(&path, crate::validation::Error::Missing)
206        }
207        Checked::Valid(Type::Directional) if light.directional.is_none() => {
208            report(&path, crate::validation::Error::Missing)
209        }
210        _ => {}
211    }
212}