dae_parser/fx/
render.rs

1use crate::*;
2
3/// Describes one effect pass to evaluate a scene.
4#[derive(Clone, Debug)]
5pub struct Render {
6    /// Refers to a node that contains a camera describing the viewpoint
7    /// from which to render this compositing step.
8    pub camera_node: UrlRef<Node>,
9    /// Specifies which layer or layers to render in this compositing step
10    /// while evaluating the scene.
11    pub layers: Vec<String>,
12    /// Instantiates a COLLADA material resource. See [`InstanceEffectData`]
13    /// for the additional instance effect data.
14    pub instance_effect: Option<Instance<Effect>>,
15}
16
17impl Render {
18    /// Construct a new render pass.
19    pub fn new(camera_node: Url, layers: Vec<String>, instance_effect: Url) -> Self {
20        Self {
21            camera_node: Ref::new(camera_node),
22            layers,
23            instance_effect: Some(Instance::new(instance_effect)),
24        }
25    }
26}
27
28impl XNode for Render {
29    const NAME: &'static str = "render";
30    fn parse(element: &Element) -> Result<Self> {
31        debug_assert_eq!(element.name(), Self::NAME);
32        let mut it = element.children().peekable();
33        let res = Render {
34            camera_node: parse_attr(element.attr("camera_node"))?
35                .ok_or("missing camera_node attr")?,
36            layers: parse_list("layer", &mut it, parse_text)?,
37            instance_effect: Instance::parse_opt(&mut it)?,
38        };
39        finish(res, it)
40    }
41}
42
43impl XNodeWrite for Render {
44    fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
45        let mut e = Self::elem();
46        e.print_attr("camera_node", &self.camera_node);
47        let e = e.start(w)?;
48        many(&self.layers, |e| ElemBuilder::print_str("layer", e, w))?;
49        self.instance_effect.write_to(w)?;
50        e.end(w)
51    }
52}
53
54/// A shader element.
55#[derive(Clone, Debug)]
56pub enum Shader {
57    /// Produces a specularly shaded surface with a Blinn BRDF approximation.
58    Blinn(Blinn),
59    /// Produces a constantly shaded surface that is independent of lighting.
60    Constant(ConstantFx),
61    /// Produces a diffuse shaded surface that is independent of lighting.
62    Lambert(Lambert),
63    /// Produces a specularly shaded surface where the specular reflection is shaded
64    /// according the Phong BRDF approximation.
65    Phong(Phong),
66}
67
68impl From<Blinn> for Shader {
69    fn from(v: Blinn) -> Self {
70        Self::Blinn(v)
71    }
72}
73
74impl From<ConstantFx> for Shader {
75    fn from(v: ConstantFx) -> Self {
76        Self::Constant(v)
77    }
78}
79
80impl From<Lambert> for Shader {
81    fn from(v: Lambert) -> Self {
82        Self::Lambert(v)
83    }
84}
85
86impl From<Phong> for Shader {
87    fn from(v: Phong) -> Self {
88        Self::Phong(v)
89    }
90}
91
92impl Shader {
93    /// Parse a [`Shader`] from an XML element.
94    pub fn parse(e: &Element) -> Result<Option<Self>> {
95        Ok(Some(match e.name() {
96            Blinn::NAME => Self::Blinn(Blinn::parse(e)?),
97            ConstantFx::NAME => Self::Constant(ConstantFx::parse(e)?),
98            Lambert::NAME => Self::Lambert(Lambert::parse(e)?),
99            Phong::NAME => Self::Phong(Phong::parse(e)?),
100            _ => return Ok(None),
101        }))
102    }
103
104    /// Run the function `f` on all arguments of type [`Texture`] in the parameters to this shader.
105    pub fn on_textures<'a, E>(
106        &'a self,
107        f: &mut impl FnMut(&'a Texture) -> Result<(), E>,
108    ) -> Result<(), E> {
109        match self {
110            Self::Blinn(s) => s.on_textures(f),
111            Self::Constant(s) => s.on_textures(f),
112            Self::Lambert(s) => s.on_textures(f),
113            Self::Phong(s) => s.on_textures(f),
114        }
115    }
116}
117
118impl XNodeWrite for Shader {
119    fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
120        match self {
121            Self::Blinn(e) => e.write_to(w),
122            Self::Constant(e) => e.write_to(w),
123            Self::Lambert(e) => e.write_to(w),
124            Self::Phong(e) => e.write_to(w),
125        }
126    }
127}
128
129/// Produces a specularly shaded surface with a Blinn BRDF approximation.
130#[derive(Clone, Default, Debug)]
131pub struct Blinn {
132    /// Declares the amount of light emitted from the surface of this object.
133    pub emission: Option<WithSid<ColorParam>>,
134    /// Declares the amount of ambient light emitted from the surface of this object.
135    pub ambient: Option<WithSid<ColorParam>>,
136    /// Declares the amount of light diffusely reflected from the surface of this object.
137    pub diffuse: Option<WithSid<ColorParam>>,
138    /// Declares the color of light specularly reflected from the surface of this object.
139    pub specular: Option<WithSid<ColorParam>>,
140    /// Declares the specularity or roughness of the specular reflection lobe.
141    pub shininess: Option<WithSid<FloatParam>>,
142    /// Declares the color of a perfect mirror reflection.
143    pub reflective: Option<WithSid<ColorParam>>,
144    /// Declares the amount of perfect mirror reflection to be added
145    /// to the reflected light as a value between 0.0 and 1.0.
146    pub reflectivity: Option<WithSid<FloatParam>>,
147    /// Declares the color of perfectly refracted light.
148    pub transparent: Option<WithSid<ColorParam>>,
149    /// Declares the amount of perfectly refracted light added
150    /// to the reflected color as a scalar value between 0.0 and 1.0.
151    pub transparency: Option<WithSid<FloatParam>>,
152    /// Declares the index of refraction for perfectly refracted light
153    /// as a single scalar index.
154    pub index_of_refraction: Option<WithSid<FloatParam>>,
155}
156
157impl XNode for Blinn {
158    const NAME: &'static str = "blinn";
159    fn parse(element: &Element) -> Result<Self> {
160        debug_assert_eq!(element.name(), Self::NAME);
161        let mut it = element.children().peekable();
162        Ok(Blinn {
163            emission: parse_opt("emission", &mut it, WithSid::parse)?,
164            ambient: parse_opt("ambient", &mut it, WithSid::parse)?,
165            diffuse: parse_opt("diffuse", &mut it, WithSid::parse)?,
166            specular: parse_opt("specular", &mut it, WithSid::parse)?,
167            shininess: parse_opt("shininess", &mut it, WithSid::parse)?,
168            reflective: parse_opt("reflective", &mut it, WithSid::parse)?,
169            reflectivity: parse_opt("reflectivity", &mut it, WithSid::parse)?,
170            transparent: parse_opt("transparent", &mut it, WithSid::parse)?,
171            transparency: parse_opt("transparency", &mut it, WithSid::parse)?,
172            index_of_refraction: parse_opt("index_of_refraction", &mut it, WithSid::parse)?,
173        })
174    }
175}
176
177impl XNodeWrite for Blinn {
178    fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
179        let e = Self::elem().start(w)?;
180        WithSid::write_opt(&self.emission, "emission", w)?;
181        WithSid::write_opt(&self.ambient, "ambient", w)?;
182        WithSid::write_opt(&self.diffuse, "diffuse", w)?;
183        WithSid::write_opt(&self.specular, "specular", w)?;
184        WithSid::write_opt(&self.shininess, "shininess", w)?;
185        WithSid::write_opt(&self.reflective, "reflective", w)?;
186        WithSid::write_opt(&self.reflectivity, "reflectivity", w)?;
187        WithSid::write_opt(&self.transparent, "transparent", w)?;
188        WithSid::write_opt(&self.transparency, "transparency", w)?;
189        WithSid::write_opt(&self.index_of_refraction, "index_of_refraction", w)?;
190        e.end(w)
191    }
192}
193
194impl Blinn {
195    /// Run the function `f` on all arguments of type [`Texture`] in the parameters to this shader.
196    pub fn on_textures<'a, E>(
197        &'a self,
198        f: &mut impl FnMut(&'a Texture) -> Result<(), E>,
199    ) -> Result<(), E> {
200        on_color_as_texture(&self.emission, f)?;
201        on_color_as_texture(&self.ambient, f)?;
202        on_color_as_texture(&self.diffuse, f)?;
203        on_color_as_texture(&self.specular, f)?;
204        on_color_as_texture(&self.reflective, f)?;
205        on_color_as_texture(&self.transparent, f)
206    }
207}
208
209/// Produces a constantly shaded surface that is independent of lighting.
210#[derive(Clone, Default, Debug)]
211pub struct ConstantFx {
212    /// Declares the amount of light emitted from the surface of this object.
213    pub emission: Option<WithSid<ColorParam>>,
214    /// Declares the color of a perfect mirror reflection.
215    pub reflective: Option<WithSid<ColorParam>>,
216    /// Declares the amount of perfect mirror reflection to be added
217    /// to the reflected light as a value between 0.0 and 1.0.
218    pub reflectivity: Option<WithSid<FloatParam>>,
219    /// Declares the color of perfectly refracted light.
220    pub transparent: Option<WithSid<ColorParam>>,
221    /// Declares the amount of perfectly refracted light added
222    /// to the reflected color as a scalar value between 0.0 and 1.0.
223    pub transparency: Option<WithSid<FloatParam>>,
224    /// Declares the index of refraction for perfectly refracted light
225    /// as a single scalar index.
226    pub index_of_refraction: Option<WithSid<FloatParam>>,
227}
228
229impl XNode for ConstantFx {
230    const NAME: &'static str = "constant";
231    fn parse(element: &Element) -> Result<Self> {
232        debug_assert_eq!(element.name(), Self::NAME);
233        let mut it = element.children().peekable();
234        Ok(ConstantFx {
235            emission: parse_opt("emission", &mut it, WithSid::parse)?,
236            reflective: parse_opt("reflective", &mut it, WithSid::parse)?,
237            reflectivity: parse_opt("reflectivity", &mut it, WithSid::parse)?,
238            transparent: parse_opt("transparent", &mut it, WithSid::parse)?,
239            transparency: parse_opt("transparency", &mut it, WithSid::parse)?,
240            index_of_refraction: parse_opt("index_of_refraction", &mut it, WithSid::parse)?,
241        })
242    }
243}
244
245impl XNodeWrite for ConstantFx {
246    fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
247        let e = Self::elem().start(w)?;
248        WithSid::write_opt(&self.emission, "emission", w)?;
249        WithSid::write_opt(&self.reflective, "reflective", w)?;
250        WithSid::write_opt(&self.reflectivity, "reflectivity", w)?;
251        WithSid::write_opt(&self.transparent, "transparent", w)?;
252        WithSid::write_opt(&self.transparency, "transparency", w)?;
253        WithSid::write_opt(&self.index_of_refraction, "index_of_refraction", w)?;
254        e.end(w)
255    }
256}
257
258impl ConstantFx {
259    /// Run the function `f` on all arguments of type [`Texture`] in the parameters to this shader.
260    pub fn on_textures<'a, E>(
261        &'a self,
262        f: &mut impl FnMut(&'a Texture) -> Result<(), E>,
263    ) -> Result<(), E> {
264        on_color_as_texture(&self.emission, f)?;
265        on_color_as_texture(&self.reflective, f)?;
266        on_color_as_texture(&self.transparent, f)
267    }
268}
269
270/// Produces a diffuse shaded surface that is independent of lighting.
271#[derive(Clone, Default, Debug)]
272pub struct Lambert {
273    /// Declares the amount of light emitted from the surface of this object.
274    pub emission: Option<WithSid<ColorParam>>,
275    /// Declares the amount of ambient light emitted from the surface of this object.
276    pub ambient: Option<WithSid<ColorParam>>,
277    /// Declares the amount of light diffusely reflected from the surface of this object.
278    pub diffuse: Option<WithSid<ColorParam>>,
279    /// Declares the color of a perfect mirror reflection.
280    pub reflective: Option<WithSid<ColorParam>>,
281    /// Declares the amount of perfect mirror reflection to be added
282    /// to the reflected light as a value between 0.0 and 1.0.
283    pub reflectivity: Option<WithSid<FloatParam>>,
284    /// Declares the color of perfectly refracted light.
285    pub transparent: Option<WithSid<ColorParam>>,
286    /// Declares the amount of perfectly refracted light added
287    /// to the reflected color as a scalar value between 0.0 and 1.0.
288    pub transparency: Option<WithSid<FloatParam>>,
289    /// Declares the index of refraction for perfectly refracted light
290    /// as a single scalar index.
291    pub index_of_refraction: Option<WithSid<FloatParam>>,
292}
293
294impl XNode for Lambert {
295    const NAME: &'static str = "lambert";
296    fn parse(element: &Element) -> Result<Self> {
297        debug_assert_eq!(element.name(), Self::NAME);
298        let mut it = element.children().peekable();
299        Ok(Lambert {
300            emission: parse_opt("emission", &mut it, WithSid::parse)?,
301            ambient: parse_opt("ambient", &mut it, WithSid::parse)?,
302            diffuse: parse_opt("diffuse", &mut it, WithSid::parse)?,
303            reflective: parse_opt("reflective", &mut it, WithSid::parse)?,
304            reflectivity: parse_opt("reflectivity", &mut it, WithSid::parse)?,
305            transparent: parse_opt("transparent", &mut it, WithSid::parse)?,
306            transparency: parse_opt("transparency", &mut it, WithSid::parse)?,
307            index_of_refraction: parse_opt("index_of_refraction", &mut it, WithSid::parse)?,
308        })
309    }
310}
311
312impl XNodeWrite for Lambert {
313    fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
314        let e = Self::elem().start(w)?;
315        WithSid::write_opt(&self.emission, "emission", w)?;
316        WithSid::write_opt(&self.ambient, "ambient", w)?;
317        WithSid::write_opt(&self.diffuse, "diffuse", w)?;
318        WithSid::write_opt(&self.reflective, "reflective", w)?;
319        WithSid::write_opt(&self.reflectivity, "reflectivity", w)?;
320        WithSid::write_opt(&self.transparent, "transparent", w)?;
321        WithSid::write_opt(&self.transparency, "transparency", w)?;
322        WithSid::write_opt(&self.index_of_refraction, "index_of_refraction", w)?;
323        e.end(w)
324    }
325}
326
327impl Lambert {
328    /// Run the function `f` on all arguments of type [`Texture`] in the parameters to this shader.
329    pub fn on_textures<'a, E>(
330        &'a self,
331        f: &mut impl FnMut(&'a Texture) -> Result<(), E>,
332    ) -> Result<(), E> {
333        on_color_as_texture(&self.emission, f)?;
334        on_color_as_texture(&self.ambient, f)?;
335        on_color_as_texture(&self.diffuse, f)?;
336        on_color_as_texture(&self.reflective, f)?;
337        on_color_as_texture(&self.transparent, f)
338    }
339}
340
341/// Produces a specularly shaded surface where the specular reflection is shaded
342/// according the Phong BRDF approximation.
343#[derive(Clone, Default, Debug)]
344pub struct Phong {
345    /// Declares the amount of light emitted from the surface of this object.
346    pub emission: Option<WithSid<ColorParam>>,
347    /// Declares the amount of ambient light emitted from the surface of this object.
348    pub ambient: Option<WithSid<ColorParam>>,
349    /// Declares the amount of light diffusely reflected from the surface of this object.
350    pub diffuse: Option<WithSid<ColorParam>>,
351    /// the surface of this object.  the surface of this object.
352    pub specular: Option<WithSid<ColorParam>>,
353    /// reflection lobe.reflection lobe.
354    pub shininess: Option<WithSid<FloatParam>>,
355    /// Declares the color of a perfect mirror reflection.
356    pub reflective: Option<WithSid<ColorParam>>,
357    /// Declares the amount of perfect mirror reflection to be added
358    /// to the reflected light as a value between 0.0 and 1.0.
359    pub reflectivity: Option<WithSid<FloatParam>>,
360    /// Declares the color of perfectly refracted light.
361    pub transparent: Option<WithSid<ColorParam>>,
362    /// Declares the amount of perfectly refracted light added
363    /// to the reflected color as a scalar value between 0.0 and 1.0.
364    pub transparency: Option<WithSid<FloatParam>>,
365    /// Declares the index of refraction for perfectly refracted light
366    /// as a single scalar index.
367    pub index_of_refraction: Option<WithSid<FloatParam>>,
368}
369
370impl XNode for Phong {
371    const NAME: &'static str = "phong";
372    fn parse(element: &Element) -> Result<Self> {
373        debug_assert_eq!(element.name(), Self::NAME);
374        let mut it = element.children().peekable();
375        Ok(Phong {
376            emission: parse_opt("emission", &mut it, WithSid::parse)?,
377            ambient: parse_opt("ambient", &mut it, WithSid::parse)?,
378            diffuse: parse_opt("diffuse", &mut it, WithSid::parse)?,
379            specular: parse_opt("specular", &mut it, WithSid::parse)?,
380            shininess: parse_opt("shininess", &mut it, WithSid::parse)?,
381            reflective: parse_opt("reflective", &mut it, WithSid::parse)?,
382            reflectivity: parse_opt("reflectivity", &mut it, WithSid::parse)?,
383            transparent: parse_opt("transparent", &mut it, WithSid::parse)?,
384            transparency: parse_opt("transparency", &mut it, WithSid::parse)?,
385            index_of_refraction: parse_opt("index_of_refraction", &mut it, WithSid::parse)?,
386        })
387    }
388}
389
390impl XNodeWrite for Phong {
391    fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
392        let e = Self::elem().start(w)?;
393        WithSid::write_opt(&self.emission, "emission", w)?;
394        WithSid::write_opt(&self.ambient, "ambient", w)?;
395        WithSid::write_opt(&self.diffuse, "diffuse", w)?;
396        WithSid::write_opt(&self.specular, "specular", w)?;
397        WithSid::write_opt(&self.shininess, "shininess", w)?;
398        WithSid::write_opt(&self.reflective, "reflective", w)?;
399        WithSid::write_opt(&self.reflectivity, "reflectivity", w)?;
400        WithSid::write_opt(&self.transparent, "transparent", w)?;
401        WithSid::write_opt(&self.transparency, "transparency", w)?;
402        WithSid::write_opt(&self.index_of_refraction, "index_of_refraction", w)?;
403        e.end(w)
404    }
405}
406
407impl Phong {
408    /// Run the function `f` on all arguments of type [`Texture`] in the parameters to this shader.
409    pub fn on_textures<'a, E>(
410        &'a self,
411        f: &mut impl FnMut(&'a Texture) -> Result<(), E>,
412    ) -> Result<(), E> {
413        on_color_as_texture(&self.emission, f)?;
414        on_color_as_texture(&self.ambient, f)?;
415        on_color_as_texture(&self.diffuse, f)?;
416        on_color_as_texture(&self.specular, f)?;
417        on_color_as_texture(&self.reflective, f)?;
418        on_color_as_texture(&self.transparent, f)
419    }
420}
421
422/// A struct that attaches an optional SID to a shader parameter.
423#[derive(Clone, Default, Debug)]
424pub struct WithSid<T> {
425    sid: Option<String>,
426    data: T,
427}
428
429impl<T> Deref for WithSid<T> {
430    type Target = T;
431
432    fn deref(&self) -> &Self::Target {
433        &self.data
434    }
435}
436
437pub(crate) use private::CanWithSid;
438pub(crate) mod private {
439    use super::*;
440    pub trait CanWithSid: XNodeWrite + Sized {
441        fn parse(element: &Element) -> Result<Option<Self>>;
442
443        fn write_with_sid<W: Write>(&self, sid: &Option<String>, w: &mut XWriter<W>) -> Result<()>;
444    }
445}
446
447impl<T> From<T> for WithSid<T> {
448    fn from(data: T) -> Self {
449        Self::new(data)
450    }
451}
452
453impl<T> WithSid<T> {
454    /// Construct a new `WithSid` with no sid.
455    pub fn new(data: T) -> Self {
456        Self { sid: None, data }
457    }
458
459    /// Construct a new `WithSid` with a sid.
460    #[allow(clippy::self_named_constructors)]
461    pub fn with_sid(sid: impl Into<String>, data: T) -> Self {
462        Self {
463            sid: Some(sid.into()),
464            data,
465        }
466    }
467}
468
469impl<T: CanWithSid> WithSid<T> {
470    /// Parse a [`WithSid<T>`] from an XML element.
471    pub fn parse(element: &Element) -> Result<Self> {
472        let mut it = element.children().peekable();
473        parse_one_many(&mut it, |e| {
474            Ok(T::parse(e)?.map(|data| Self {
475                sid: e.attr("sid").map(Into::into),
476                data,
477            }))
478        })
479    }
480
481    fn write_opt(this: &Option<Self>, name: &str, w: &mut XWriter<impl Write>) -> Result<()> {
482        opt(this, |this| {
483            let elem = ElemBuilder::new(name).start(w)?;
484            this.write_to(w)?;
485            elem.end(w)
486        })
487    }
488}
489
490impl<T: CanWithSid> XNodeWrite for WithSid<T> {
491    fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
492        self.data.write_with_sid(&self.sid, w)
493    }
494}
495
496/// A type that describes color attributes of fixed-function shader elements inside
497/// [`ProfileCommon`] effects.
498#[derive(Clone, Debug)]
499pub enum ColorParam {
500    /// The value is a literal color, specified by four floating-point numbers in RGBA order.
501    Color(Box<[f32; 4]>),
502    /// The value is specified by a reference to a previously defined parameter
503    /// in the current scope that can be cast directly to a `float4`.
504    Param(Box<str>),
505    /// The value is specified by a reference to a previously defined `sampler2D` object.
506    Texture(Box<Texture>),
507}
508
509impl From<[f32; 4]> for ColorParam {
510    fn from(rgba: [f32; 4]) -> Self {
511        Self::color(rgba)
512    }
513}
514
515impl From<[f32; 4]> for WithSid<ColorParam> {
516    fn from(rgba: [f32; 4]) -> Self {
517        WithSid::new(rgba.into())
518    }
519}
520
521impl From<Texture> for ColorParam {
522    fn from(tex: Texture) -> Self {
523        Self::Texture(Box::new(tex))
524    }
525}
526
527impl From<Texture> for WithSid<ColorParam> {
528    fn from(tex: Texture) -> Self {
529        WithSid::new(tex.into())
530    }
531}
532
533impl ColorParam {
534    /// Construct a new `ColorParam` from a color.
535    pub fn color(rgba: [f32; 4]) -> Self {
536        Self::Color(Box::new(rgba))
537    }
538}
539
540impl CanWithSid for ColorParam {
541    fn parse(e: &Element) -> Result<Option<Self>> {
542        Ok(Some(match e.name() {
543            "color" => Self::Color(parse_array_n(e)?),
544            Param::NAME => Self::Param(e.attr("ref").ok_or("expected ref attr")?.into()),
545            Texture::NAME => Self::Texture(Texture::parse_box(e)?),
546            _ => return Ok(None),
547        }))
548    }
549
550    fn write_with_sid<W: Write>(&self, sid: &Option<String>, w: &mut XWriter<W>) -> Result<()> {
551        match self {
552            Self::Color(arr) => {
553                let mut e = ElemBuilder::new("color");
554                e.opt_attr("sid", sid);
555                let e = e.start(w)?;
556                print_arr(&**arr, w)?;
557                e.end(w)
558            }
559            Self::Param(ref_) => {
560                let mut e = ElemBuilder::new(Param::NAME);
561                e.opt_attr("sid", sid);
562                e.attr("ref", ref_);
563                e.end(w)
564            }
565            Self::Texture(e) => e.write_to(w),
566        }
567    }
568}
569
570impl XNodeWrite for ColorParam {
571    fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
572        self.write_with_sid(&None, w)
573    }
574}
575
576impl ColorParam {
577    /// Convert this parameter to a texture reference, if it is one.
578    pub fn as_texture(&self) -> Option<&Texture> {
579        match self {
580            ColorParam::Texture(tex) => Some(tex),
581            _ => None,
582        }
583    }
584
585    /// Get the color literal of this parameter, if it is a literal.
586    pub fn as_color(&self) -> Option<&[f32; 4]> {
587        match self {
588            ColorParam::Color(c) => Some(c),
589            _ => None,
590        }
591    }
592}
593
594/// A type that describes the scalar attributes of fixed-function shader elements inside
595/// [`ProfileCommon`] effects.
596#[derive(Clone, Debug)]
597pub enum FloatParam {
598    /// The value is represented by a literal floating-point scalar.
599    Float(f32),
600    /// The value is represented by a reference to a previously
601    /// defined parameter that can be directly cast to a floating-point scalar.
602    Param(Box<str>),
603}
604
605impl From<f32> for FloatParam {
606    fn from(val: f32) -> Self {
607        Self::Float(val)
608    }
609}
610
611impl From<f32> for WithSid<FloatParam> {
612    fn from(val: f32) -> Self {
613        WithSid::new(val.into())
614    }
615}
616
617impl CanWithSid for FloatParam {
618    fn parse(e: &Element) -> Result<Option<Self>> {
619        Ok(Some(match e.name() {
620            "float" => Self::Float(parse_elem(e)?),
621            Param::NAME => Self::Param(e.attr("ref").ok_or("expected ref attr")?.into()),
622            _ => return Ok(None),
623        }))
624    }
625
626    fn write_with_sid<W: Write>(&self, sid: &Option<String>, w: &mut XWriter<W>) -> Result<()> {
627        match self {
628            Self::Float(val) => {
629                let mut e = ElemBuilder::new("float");
630                e.opt_attr("sid", sid);
631                let e = e.start(w)?;
632                print_elem(val, w)?;
633                e.end(w)
634            }
635            Self::Param(ref_) => {
636                let mut e = ElemBuilder::new(Param::NAME);
637                e.opt_attr("sid", sid);
638                e.attr("ref", ref_);
639                e.end(w)
640            }
641        }
642    }
643}
644
645impl XNodeWrite for FloatParam {
646    fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
647        self.write_with_sid(&None, w)
648    }
649}
650
651/// A color parameter referencing a texture.
652#[derive(Clone, Debug)]
653pub struct Texture {
654    /// The texture to reference.
655    pub texture: String,
656    /// A semantic token, which will be referenced within
657    /// [`BindMaterial`] to bind an array of texcoords from a
658    /// [`Geometry`] instance to the `TextureUnit`.
659    pub texcoord: String,
660    /// Provides arbitrary additional information about this element.
661    pub extra: Option<Box<Extra>>,
662}
663
664impl Texture {
665    /// Construct a new `Texture` from the mandatory data.
666    pub fn new(texture: impl Into<String>, texcoord: impl Into<String>) -> Self {
667        Self {
668            texture: texture.into(),
669            texcoord: texcoord.into(),
670            extra: None,
671        }
672    }
673
674    fn write_with_sid<W: Write>(&self, sid: &Option<String>, w: &mut XWriter<W>) -> Result<()> {
675        let mut e = Self::elem();
676        e.opt_attr("sid", sid);
677        e.attr("texture", &self.texture);
678        e.attr("texcoord", &self.texcoord);
679        if let Some(extra) = &self.extra {
680            let e = e.start(w)?;
681            extra.write_to(w)?;
682            e.end(w)
683        } else {
684            e.end(w)
685        }
686    }
687}
688
689impl XNode for Texture {
690    const NAME: &'static str = "texture";
691    fn parse(e: &Element) -> Result<Self> {
692        let mut it = e.children().peekable();
693        let res = Texture {
694            texture: e.attr("texture").ok_or("expected texture attr")?.into(),
695            texcoord: e.attr("texcoord").ok_or("expected texcoord attr")?.into(),
696            extra: Extra::parse_opt_box(&mut it)?,
697        };
698        finish(res, it)
699    }
700}
701
702impl XNodeWrite for Texture {
703    fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
704        self.write_with_sid(&None, w)
705    }
706}
707
708fn on_color_as_texture<'a, E>(
709    opt: &'a Option<WithSid<ColorParam>>,
710    f: &mut impl FnMut(&'a Texture) -> Result<(), E>,
711) -> Result<(), E> {
712    if let Some(WithSid {
713        data: ColorParam::Texture(tex),
714        ..
715    }) = opt
716    {
717        f(tex)?
718    }
719    Ok(())
720}