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}