dae_parser/core/
light.rs

1use crate::*;
2
3/// Declares a light source that illuminates a scene.
4#[derive(Clone, Debug)]
5pub struct Light {
6    /// A text string containing the unique identifier of the element.
7    pub id: Option<String>,
8    /// The text string name of this element.
9    pub name: Option<String>,
10    /// Asset management information about this element.
11    pub asset: Option<Box<Asset>>,
12    /// The kind of light being described.
13    pub kind: LightKind,
14    /// Declares the information used to process some portion of the content. (optional)
15    pub technique: Vec<Technique>,
16    /// Provides arbitrary additional information about this element.
17    pub extra: Vec<Extra>,
18}
19
20impl Light {
21    /// Construct a new `Light` with the given name and kind.
22    pub fn new(id: impl Into<String>, name: Option<String>, kind: impl Into<LightKind>) -> Self {
23        Self {
24            id: Some(id.into()),
25            name,
26            asset: None,
27            kind: kind.into(),
28            technique: vec![],
29            extra: vec![],
30        }
31    }
32}
33
34impl XNode for Light {
35    const NAME: &'static str = "light";
36    fn parse(element: &Element) -> Result<Self> {
37        debug_assert_eq!(element.name(), Self::NAME);
38        let mut it = element.children().peekable();
39        Ok(Light {
40            id: element.attr("id").map(Into::into),
41            name: element.attr("name").map(Into::into),
42            asset: Asset::parse_opt_box(&mut it)?,
43            kind: parse_one(Technique::COMMON, &mut it, |e| {
44                let mut it = e.children().peekable();
45                finish(parse_one_many(&mut it, LightKind::parse)?, it)
46            })?,
47            technique: Technique::parse_list(&mut it)?,
48            extra: Extra::parse_many(it)?,
49        })
50    }
51}
52
53impl XNodeWrite for Light {
54    fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
55        let mut e = Self::elem();
56        e.opt_attr("id", &self.id);
57        e.opt_attr("name", &self.name);
58        let e = e.start(w)?;
59        self.asset.write_to(w)?;
60        let common = ElemBuilder::new(Technique::COMMON).start(w)?;
61        self.kind.write_to(w)?;
62        common.end(w)?;
63        self.technique.write_to(w)?;
64        self.extra.write_to(w)?;
65        e.end(w)
66    }
67}
68
69/// The kind of light being described.
70#[derive(Clone, Debug)]
71pub enum LightKind {
72    /// Describes an ambient light source.
73    Ambient(AmbientLight),
74    /// Describes a directional light source.
75    Directional(DirectionalLight),
76    /// Describes a point light source.
77    Point(Box<PointLight>),
78    /// Describes a spot light source.
79    Spot(Box<SpotLight>),
80}
81
82impl From<SpotLight> for LightKind {
83    fn from(v: SpotLight) -> Self {
84        Self::Spot(Box::new(v))
85    }
86}
87
88impl From<PointLight> for LightKind {
89    fn from(v: PointLight) -> Self {
90        Self::Point(Box::new(v))
91    }
92}
93
94impl From<DirectionalLight> for LightKind {
95    fn from(v: DirectionalLight) -> Self {
96        Self::Directional(v)
97    }
98}
99
100impl From<AmbientLight> for LightKind {
101    fn from(v: AmbientLight) -> Self {
102        Self::Ambient(v)
103    }
104}
105
106impl LightKind {
107    /// Parse a [`LightKind`] from an XML element.
108    pub fn parse(e: &Element) -> Result<Option<Self>> {
109        Ok(Some(match e.name() {
110            AmbientLight::NAME => Self::Ambient(AmbientLight::parse(e)?),
111            DirectionalLight::NAME => Self::Directional(DirectionalLight::parse(e)?),
112            PointLight::NAME => Self::Point(PointLight::parse_box(e)?),
113            SpotLight::NAME => Self::Spot(SpotLight::parse_box(e)?),
114            _ => return Ok(None),
115        }))
116    }
117}
118
119impl XNodeWrite for LightKind {
120    fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
121        match self {
122            LightKind::Ambient(e) => e.write_to(w),
123            LightKind::Directional(e) => e.write_to(w),
124            LightKind::Point(e) => e.write_to(w),
125            LightKind::Spot(e) => e.write_to(w),
126        }
127    }
128}
129
130/// Describes an ambient light source.
131#[derive(Clone, Debug)]
132pub struct AmbientLight {
133    /// Contains three floating-point numbers specifying the color of the light.
134    pub color: Box<[f32; 3]>,
135}
136
137impl AmbientLight {
138    /// Create a new `AmbientLight` with the given color.
139    pub fn new(color: [f32; 3]) -> Self {
140        Self {
141            color: Box::new(color),
142        }
143    }
144}
145
146impl XNode for AmbientLight {
147    const NAME: &'static str = "ambient";
148    fn parse(element: &Element) -> Result<Self> {
149        debug_assert_eq!(element.name(), Self::NAME);
150        let mut it = element.children().peekable();
151        let color = parse_one("color", &mut it, parse_array_n)?;
152        finish(AmbientLight { color }, it)
153    }
154}
155
156impl XNodeWrite for AmbientLight {
157    fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
158        let e = Self::elem().start(w)?;
159        ElemBuilder::print_arr("color", &*self.color, w)?;
160        e.end(w)
161    }
162}
163
164/// Describes a directional light source.
165///
166/// The light’s default direction vector in local coordinates is [0,0,-1],
167/// pointing down the negative z axis.
168/// The actual direction of the light is defined by the transform of the node
169/// where the light is instantiated.
170#[derive(Clone, Debug)]
171pub struct DirectionalLight {
172    /// Contains three floating-point numbers specifying the color of the light.
173    pub color: Box<[f32; 3]>,
174}
175
176impl DirectionalLight {
177    /// Create a new `DirectionalLight` with the given color.
178    pub fn new(color: [f32; 3]) -> Self {
179        Self {
180            color: Box::new(color),
181        }
182    }
183}
184
185impl XNode for DirectionalLight {
186    const NAME: &'static str = "directional";
187    fn parse(element: &Element) -> Result<Self> {
188        debug_assert_eq!(element.name(), Self::NAME);
189        let mut it = element.children().peekable();
190        let color = parse_one("color", &mut it, parse_array_n)?;
191        finish(DirectionalLight { color }, it)
192    }
193}
194
195impl XNodeWrite for DirectionalLight {
196    fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
197        let e = Self::elem().start(w)?;
198        ElemBuilder::print_arr("color", &*self.color, w)?;
199        e.end(w)
200    }
201}
202
203/// Describes a point light source.
204///
205/// The position of the light is defined by the transform of the node in which it is instantiated.
206///
207/// The `constant_attenuation`, `linear_attenuation`, and `quadratic_attenuation` are
208/// used to calculate the total attenuation of this light given a distance.
209/// The equation used is:
210/// ```text
211/// A = constant_attenuation + Dist * linear_attenuation + Dist^2 * quadratic_attenuation
212/// ```
213#[derive(Clone, Debug)]
214pub struct PointLight {
215    /// Contains three floating-point numbers specifying the color of the light.
216    pub color: Box<[f32; 3]>,
217    /// The constant term in the attentuation equation, see [`PointLight`].
218    pub constant_attenuation: f32,
219    /// The linear term in the attentuation equation, see [`PointLight`].
220    pub linear_attenuation: f32,
221    /// The quadratic term in the attentuation equation, see [`PointLight`].
222    pub quadratic_attenuation: f32,
223}
224
225impl PointLight {
226    /// Create a new `PointLight` with the given color.
227    pub fn new(color: [f32; 3]) -> Self {
228        Self {
229            color: Box::new(color),
230            constant_attenuation: 0.,
231            linear_attenuation: 0.,
232            quadratic_attenuation: 0.,
233        }
234    }
235}
236
237impl XNode for PointLight {
238    const NAME: &'static str = "point";
239    fn parse(element: &Element) -> Result<Self> {
240        debug_assert_eq!(element.name(), Self::NAME);
241        let mut it = element.children().peekable();
242        let res = PointLight {
243            color: parse_one("color", &mut it, parse_array_n)?,
244            constant_attenuation: parse_opt("constant_attenuation", &mut it, parse_elem)?
245                .unwrap_or(0.),
246            linear_attenuation: parse_opt("linear_attenuation", &mut it, parse_elem)?.unwrap_or(0.),
247            quadratic_attenuation: parse_opt("quadratic_attenuation", &mut it, parse_elem)?
248                .unwrap_or(0.),
249        };
250        finish(res, it)
251    }
252}
253
254impl XNodeWrite for PointLight {
255    fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
256        let e = Self::elem().start(w)?;
257        ElemBuilder::print_arr("color", &*self.color, w)?;
258        ElemBuilder::def_print("constant_attenuation", self.constant_attenuation, 0., w)?;
259        ElemBuilder::def_print("linear_attenuation", self.linear_attenuation, 0., w)?;
260        ElemBuilder::def_print("quadratic_attenuation", self.quadratic_attenuation, 0., w)?;
261        e.end(w)
262    }
263}
264
265/// Describes a spot light source.
266///
267/// The light’s default direction vector in local coordinates is [0,0,-1],
268/// pointing down the negative z axis.
269/// The actual direction of the light is defined by the transform of the node
270/// where the light is instantiated.
271///
272/// The `constant_attenuation`, `linear_attenuation`, and `quadratic_attenuation` are
273/// used to calculate the total attenuation of this light given a distance.
274/// The equation used is:
275/// ```text
276/// A = constant_attenuation + Dist * linear_attenuation + Dist^2 * quadratic_attenuation
277/// ```
278///
279/// The `falloff_angle` and `falloff_exponent` are used to specify
280/// the amount of attenuation based on the direction of the light.
281#[derive(Clone, Debug)]
282pub struct SpotLight {
283    /// Contains three floating-point numbers specifying the color of the light.
284    pub color: Box<[f32; 3]>,
285    /// The constant term in the attentuation equation, see [`SpotLight`].
286    pub constant_attenuation: f32,
287    /// The linear term in the attentuation equation, see [`SpotLight`].
288    pub linear_attenuation: f32,
289    /// The quadratic term in the attentuation equation, see [`SpotLight`].
290    pub quadratic_attenuation: f32,
291    /// The directional attenuation of the light.
292    pub falloff_angle: f32,
293    /// A term in the directional attenuation equation of the light.
294    pub falloff_exponent: f32,
295}
296
297impl SpotLight {
298    /// Create a new `SpotLight` with the given color.
299    pub fn new(color: [f32; 3]) -> Self {
300        Self {
301            color: Box::new(color),
302            constant_attenuation: 0.,
303            linear_attenuation: 0.,
304            quadratic_attenuation: 0.,
305            falloff_angle: 180.,
306            falloff_exponent: 0.,
307        }
308    }
309}
310
311impl XNode for SpotLight {
312    const NAME: &'static str = "spot";
313    fn parse(element: &Element) -> Result<Self> {
314        debug_assert_eq!(element.name(), Self::NAME);
315        let mut it = element.children().peekable();
316        let res = SpotLight {
317            color: parse_one("color", &mut it, parse_array_n)?,
318            constant_attenuation: parse_opt("constant_attenuation", &mut it, parse_elem)?
319                .unwrap_or(0.),
320            linear_attenuation: parse_opt("linear_attenuation", &mut it, parse_elem)?.unwrap_or(0.),
321            quadratic_attenuation: parse_opt("quadratic_attenuation", &mut it, parse_elem)?
322                .unwrap_or(0.),
323            falloff_angle: parse_opt("falloff_angle", &mut it, parse_elem)?.unwrap_or(180.),
324            falloff_exponent: parse_opt("falloff_exponent", &mut it, parse_elem)?.unwrap_or(0.),
325        };
326        finish(res, it)
327    }
328}
329
330impl XNodeWrite for SpotLight {
331    fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
332        let e = Self::elem().start(w)?;
333        ElemBuilder::print_arr("color", &*self.color, w)?;
334        ElemBuilder::def_print("constant_attenuation", self.constant_attenuation, 0., w)?;
335        ElemBuilder::def_print("linear_attenuation", self.linear_attenuation, 0., w)?;
336        ElemBuilder::def_print("quadratic_attenuation", self.quadratic_attenuation, 0., w)?;
337        ElemBuilder::def_print("falloff_angle", self.falloff_angle, 180., w)?;
338        ElemBuilder::def_print("falloff_exponent", self.falloff_exponent, 0., w)?;
339        e.end(w)
340    }
341}