dae_parser/fx/
material.rs

1use crate::*;
2
3/// Describes the visual appearance of a geometric object.
4#[derive(Clone, Debug)]
5pub struct Material {
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    /// Instantiates a COLLADA material resource. See [`InstanceEffectData`]
13    /// for the additional instance effect data.
14    pub instance_effect: Instance<Effect>,
15    /// Provides arbitrary additional information about this element.
16    pub extra: Vec<Extra>,
17}
18
19impl Material {
20    /// Construct a new `Material` from an instance effect.
21    pub fn new(id: impl Into<String>, name: impl Into<String>, instance_effect: Url) -> Self {
22        Self {
23            id: Some(id.into()),
24            name: Some(name.into()),
25            asset: None,
26            instance_effect: Instance::new(instance_effect),
27            extra: vec![],
28        }
29    }
30}
31
32impl XNode for Material {
33    const NAME: &'static str = "material";
34    fn parse(element: &Element) -> Result<Self> {
35        debug_assert_eq!(element.name(), Self::NAME);
36        let mut it = element.children().peekable();
37        Ok(Material {
38            id: element.attr("id").map(Into::into),
39            name: element.attr("name").map(Into::into),
40            asset: Asset::parse_opt_box(&mut it)?,
41            instance_effect: Instance::parse_one(&mut it)?,
42            extra: Extra::parse_many(it)?,
43        })
44    }
45}
46
47impl XNodeWrite for Material {
48    fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
49        let mut e = Self::elem();
50        e.opt_attr("id", &self.id);
51        e.opt_attr("name", &self.name);
52        let e = e.start(w)?;
53        self.asset.write_to(w)?;
54        self.instance_effect.write_to(w)?;
55        self.extra.write_to(w)?;
56        e.end(w)
57    }
58}
59
60/// Instantiates a COLLADA material resource.
61#[derive(Clone, Debug)]
62pub struct InstanceMaterial {
63    /// A text string value containing the subidentifier of this element.
64    /// This value must be unique within the scope of the parent element.
65    pub sid: Option<String>,
66    /// The text string name of this element.
67    pub name: Option<String>,
68    /// Which symbol defined from within the geometry this material binds to.
69    pub symbol: String,
70    /// The URI of the location of the [`Material`] element to instantiate.
71    /// Can refer to a local instance or external reference.
72    /// For a local instance, this is a relative URI fragment identifier
73    /// that begins with the `"#"` character.
74    /// The fragment identifier is an XPointer shorthand pointer that
75    /// consists of the ID of the element to instantiate.
76    /// For an external reference, this is an absolute or relative URL.
77    pub target: UrlRef<Material>,
78    /// Connects a parameter in the material’s effect by semantic
79    /// to a target in the scene.
80    pub bind: Vec<BindM>,
81    /// Binds vertex inputs to effect parameters upon instantiation.
82    pub bind_vertex_input: Vec<BindVertexInput>,
83    /// Provides arbitrary additional information about this element.
84    pub extra: Vec<Extra>,
85}
86
87impl InstanceMaterial {
88    /// Construct a new `InstanceMaterial` with the given bindings.
89    pub fn new(
90        symbol: impl Into<String>,
91        target: Url,
92        bind_vertex_input: Vec<BindVertexInput>,
93    ) -> Self {
94        Self {
95            sid: None,
96            name: None,
97            symbol: symbol.into(),
98            target: Ref::new(target),
99            bind: vec![],
100            bind_vertex_input,
101            extra: vec![],
102        }
103    }
104}
105
106impl XNode for InstanceMaterial {
107    const NAME: &'static str = "instance_material";
108    fn parse(element: &Element) -> Result<Self> {
109        debug_assert_eq!(element.name(), Self::NAME);
110        let symbol = element.attr("symbol").ok_or("expecting symbol attr")?;
111        let mut it = element.children().peekable();
112        Ok(InstanceMaterial {
113            sid: element.attr("sid").map(Into::into),
114            name: element.attr("name").map(Into::into),
115            symbol: symbol.into(),
116            target: parse_attr(element.attr("target"))?.ok_or("missing target attribute")?,
117            bind: BindM::parse_list(&mut it)?,
118            bind_vertex_input: BindVertexInput::parse_list(&mut it)?,
119            extra: Extra::parse_many(it)?,
120        })
121    }
122}
123
124impl XNodeWrite for InstanceMaterial {
125    fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
126        let mut e = Self::elem();
127        e.opt_attr("sid", &self.sid);
128        e.opt_attr("name", &self.name);
129        e.attr("symbol", &self.symbol);
130        e.print_attr("target", &self.target);
131        let e = e.start(w)?;
132        self.bind.write_to(w)?;
133        self.bind_vertex_input.write_to(w)?;
134        self.extra.write_to(w)?;
135        e.end(w)
136    }
137}
138
139/// Binds a specific material to a piece of geometry,
140/// binding varying and uniform parameters at the same time.
141#[derive(Clone, Debug)]
142pub struct BindMaterial {
143    /// In [`BindMaterial`] these are added to be targets for animation.
144    /// These objects can then be bound to input parameters in the normal manner
145    /// without requiring the animation targeting system to parse the internal
146    /// layout of an [`Effect`].
147    pub param: Vec<Param>,
148    /// The common profile data is list of [`InstanceMaterial`]s.
149    pub instance_material: Vec<InstanceMaterial>,
150    /// Declares the information used to process some portion of the content. (optional)
151    pub technique: Vec<Technique>,
152    /// Provides arbitrary additional information about this element.
153    pub extra: Vec<Extra>,
154}
155
156impl BindMaterial {
157    /// Construct a `BindMaterial` with the given instances.
158    pub fn new(instance_material: Vec<InstanceMaterial>) -> Self {
159        assert!(!instance_material.is_empty());
160        Self {
161            param: vec![],
162            instance_material,
163            technique: vec![],
164            extra: vec![],
165        }
166    }
167}
168
169impl XNode for BindMaterial {
170    const NAME: &'static str = "bind_material";
171    fn parse(element: &Element) -> Result<Self> {
172        debug_assert_eq!(element.name(), Self::NAME);
173        let mut it = element.children().peekable();
174        Ok(BindMaterial {
175            param: Param::parse_list(&mut it)?,
176            instance_material: parse_one(Technique::COMMON, &mut it, |e| {
177                let mut it = e.children().peekable();
178                finish(InstanceMaterial::parse_list_n::<1>(&mut it)?, it)
179            })?,
180            technique: Technique::parse_list(&mut it)?,
181            extra: Extra::parse_many(it)?,
182        })
183    }
184}
185
186impl XNodeWrite for BindMaterial {
187    fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
188        let e = Self::elem().start(w)?;
189        self.param.write_to(w)?;
190        let common = ElemBuilder::new(Technique::COMMON).start(w)?;
191        self.instance_material.write_to(w)?;
192        common.end(w)?;
193        self.technique.write_to(w)?;
194        self.extra.write_to(w)?;
195        e.end(w)
196    }
197}
198
199/// Binds values to uniform inputs of a shader or binds values to effect
200/// parameters upon instantiation.
201/// In the COLLADA spec, this element is called "`<bind>` (material)".
202#[derive(Clone, Debug)]
203pub struct BindM {
204    /// Which effect parameter to bind.
205    pub semantic: Option<String>,
206    /// The location of the value to bind to the specified semantic.
207    pub target: Address,
208}
209
210impl BindM {
211    /// Construct a `BindM` with the given semantic and target.
212    pub fn new(semantic: impl Into<String>, target: impl Into<String>) -> Self {
213        Self {
214            semantic: Some(semantic.into()),
215            target: Address(target.into()),
216        }
217    }
218}
219
220impl XNode for BindM {
221    const NAME: &'static str = "bind";
222    fn parse(element: &Element) -> Result<Self> {
223        debug_assert_eq!(element.name(), Self::NAME);
224        let target = element.attr("target").ok_or("missing target attribute")?;
225        Ok(BindM {
226            semantic: element.attr("semantic").map(Into::into),
227            target: Address(target.into()),
228        })
229    }
230}
231
232impl XNodeWrite for BindM {
233    fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
234        let mut e = Self::elem();
235        e.opt_attr("semantic", &self.semantic);
236        e.print_attr("target", &self.target);
237        e.end(w)
238    }
239}