dae_parser/fx/
effect.rs

1use crate::*;
2
3/// Provides a self-contained description of a COLLADA effect.
4#[derive(Clone, Debug)]
5pub struct Effect {
6    /// Global identifier for this object.
7    pub id: 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    /// A list of strongly typed annotation remarks.
13    pub annotate: Vec<Annotate>,
14    /// Declares a standard COLLADA image resource.
15    pub image: Vec<Image>,
16    /// Creates a new parameter from a constrained set of
17    /// types recognizable by all platforms, see [`ParamType`].
18    pub new_param: Vec<NewParam>,
19    /// At least one profile must appear.
20    pub profile: Vec<Profile>,
21    /// Provides arbitrary additional information about this element.
22    pub extra: Vec<Extra>,
23}
24
25impl XNode for Effect {
26    const NAME: &'static str = "effect";
27    fn parse(element: &Element) -> Result<Self> {
28        debug_assert_eq!(element.name(), Self::NAME);
29        let mut it = element.children().peekable();
30        let res = Effect {
31            id: element.attr("id").ok_or("expected id attr")?.into(),
32            name: element.attr("name").map(Into::into),
33            asset: Asset::parse_opt_box(&mut it)?,
34            annotate: Annotate::parse_list(&mut it)?,
35            image: Image::parse_list(&mut it)?,
36            new_param: NewParam::parse_list(&mut it)?,
37            profile: parse_list_many(&mut it, Profile::parse)?,
38            extra: Extra::parse_many(it)?,
39        };
40        if res.profile.is_empty() {
41            return Err("expected at least one profile".into());
42        }
43        Ok(res)
44    }
45}
46
47impl XNodeWrite for Effect {
48    fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
49        let mut e = Self::elem();
50        e.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.annotate.write_to(w)?;
55        self.image.write_to(w)?;
56        self.new_param.write_to(w)?;
57        self.profile.write_to(w)?;
58        self.extra.write_to(w)?;
59        e.end(w)
60    }
61}
62
63impl Effect {
64    /// Construct a new `Effect` from common profile data.
65    pub fn new(id: impl Into<String>, technique: TechniqueFx<CommonData>) -> Self {
66        Self {
67            id: id.into(),
68            name: None,
69            asset: None,
70            annotate: vec![],
71            image: vec![],
72            new_param: vec![],
73            profile: vec![ProfileCommon::new(technique).into()],
74            extra: vec![],
75        }
76    }
77
78    /// Construct a simple `Effect` with one shader.
79    pub fn shader(id: impl Into<String>, shader: impl Into<Shader>) -> Self {
80        Self::new(id, TechniqueFx::new("common", CommonData::shader(shader)))
81    }
82
83    /// Get the first [`ProfileCommon`] in this effect.
84    pub fn get_common_profile(&self) -> Option<&ProfileCommon> {
85        self.profile.iter().find_map(Profile::as_common)
86    }
87
88    /// Get a parameter of the effect by name.
89    pub fn get_param(&self, sid: &str) -> Option<&NewParam> {
90        self.new_param.iter().rev().find(|p| p.sid == sid)
91    }
92}
93
94/// Extra data associated to [`Instance`]<[`Effect`]>.
95#[derive(Clone, Debug, Default)]
96pub struct InstanceEffectData {
97    /// [`TechniqueHint`]s indicate the desired or last-used technique
98    /// inside an effect profile.
99    pub technique_hint: Vec<TechniqueHint>,
100    /// [`EffectSetParam`]s assign values to specific effect and profile
101    /// parameters that will be unique to the instance.
102    pub set_param: Vec<EffectSetParam>,
103}
104
105impl XNodeWrite for InstanceEffectData {
106    fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
107        self.technique_hint.write_to(w)?;
108        self.set_param.write_to(w)
109    }
110}
111
112impl Instantiate for Effect {
113    const INSTANCE: &'static str = "instance_effect";
114    type Data = InstanceEffectData;
115    fn parse_data(_: &Element, it: &mut ElementIter<'_>) -> Result<Self::Data> {
116        Ok(InstanceEffectData {
117            technique_hint: TechniqueHint::parse_list(it)?,
118            set_param: EffectSetParam::parse_list(it)?,
119        })
120    }
121    fn is_empty(data: &Self::Data) -> bool {
122        data.technique_hint.is_empty() && data.set_param.is_empty()
123    }
124}
125
126/// Binds geometry vertex inputs to effect vertex inputs upon instantiation.
127#[derive(Clone, Debug)]
128pub struct BindVertexInput {
129    /// Which effect parameter to bind.
130    pub semantic: String,
131    /// Which input semantic to bind.
132    pub input_semantic: String,
133    /// Which input set to bind.
134    pub input_set: Option<u32>,
135}
136
137impl BindVertexInput {
138    /// Construct a `BindVertexInput` from the input data.
139    pub fn new(
140        semantic: impl Into<String>,
141        input_semantic: impl Into<String>,
142        input_set: Option<u32>,
143    ) -> Self {
144        Self {
145            semantic: semantic.into(),
146            input_semantic: input_semantic.into(),
147            input_set,
148        }
149    }
150}
151
152impl XNode for BindVertexInput {
153    const NAME: &'static str = "bind_vertex_input";
154    fn parse(element: &Element) -> Result<Self> {
155        debug_assert_eq!(element.name(), Self::NAME);
156        let semantic = element.attr("semantic");
157        let input_semantic = element.attr("input_semantic");
158        Ok(BindVertexInput {
159            semantic: semantic.ok_or("missing semantic attribute")?.into(),
160            input_semantic: input_semantic.ok_or("missing input semantic")?.into(),
161            input_set: parse_attr(element.attr("input_set"))?,
162        })
163    }
164}
165
166impl XNodeWrite for BindVertexInput {
167    fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
168        let mut e = Self::elem();
169        e.attr("semantic", &self.semantic);
170        e.attr("input_semantic", &self.input_semantic);
171        e.opt_print_attr("input_set", &self.input_set);
172        e.end(w)
173    }
174}
175
176/// A trait for the types that are legal to go in a [`TechniqueFx<T>`].
177pub trait ProfileData: XNodeWrite + Sized {
178    /// Parse the embedded data from a subsequence of children in the `<technique>` node.
179    fn parse(it: &mut ElementIter<'_>) -> Result<Self>;
180}
181
182/// Holds a description of the textures, samplers, shaders, parameters,
183/// and passes necessary for rendering this effect using one method.
184///
185/// It is parameterized on additional data determined by the parent of this element.
186#[derive(Clone, Debug)]
187pub struct TechniqueFx<T> {
188    /// A text string containing the unique identifier of the element.
189    pub id: Option<String>,
190    /// A text string value containing the subidentifier of this element.
191    /// This value must be unique within the scope of the parent element.
192    pub sid: String,
193    /// Asset management information about this element.
194    pub asset: Option<Box<Asset>>,
195    /// The profile-specific child data.
196    pub data: T,
197    /// Provides arbitrary additional information about this element.
198    pub extra: Vec<Extra>,
199}
200
201impl<T> TechniqueFx<T> {
202    /// Construct a new `TechniqueFx` given the profile-specific data.
203    pub fn new(sid: impl Into<String>, data: T) -> Self {
204        Self {
205            id: None,
206            sid: sid.into(),
207            asset: None,
208            data,
209            extra: vec![],
210        }
211    }
212
213    /// Construct a new `TechniqueFx` with default data.
214    pub fn default(sid: impl Into<String>) -> Self
215    where
216        T: Default,
217    {
218        Self::new(sid, T::default())
219    }
220}
221
222impl<T: ProfileData> XNode for TechniqueFx<T> {
223    const NAME: &'static str = "technique";
224    fn parse(element: &Element) -> Result<Self> {
225        debug_assert_eq!(element.name(), Self::NAME);
226        let mut it = element.children().peekable();
227        Ok(TechniqueFx {
228            id: element.attr("id").map(Into::into),
229            sid: element.attr("sid").ok_or("expecting sid attr")?.into(),
230            asset: Asset::parse_opt_box(&mut it)?,
231            data: T::parse(&mut it)?,
232            extra: Extra::parse_many(it)?,
233        })
234    }
235}
236
237impl<T: ProfileData> XNodeWrite for TechniqueFx<T> {
238    fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
239        let mut e = Self::elem();
240        e.opt_attr("id", &self.id);
241        e.attr("sid", &self.sid);
242        let e = e.start(w)?;
243        self.asset.write_to(w)?;
244        self.data.write_to(w)?;
245        self.extra.write_to(w)?;
246        e.end(w)
247    }
248}
249
250/// Adds a hint for a platform of which technique to use in this effect.
251#[derive(Clone, Debug)]
252pub struct TechniqueHint {
253    /// Defines a string that specifies for which platform this hint is intended.
254    pub platform: Option<String>,
255    /// A reference to the name of the platform.
256    pub ref_: String,
257    /// A string that specifies for which API profile this hint is intended.
258    /// It is the name of the profile within the effect that contains the technique.
259    /// Profiles are constructed by appending this attribute’s value to "Profile".
260    /// For example, to select [`ProfileCG`], specify `profile="CG"`.
261    pub profile: Option<String>,
262}
263
264impl TechniqueHint {
265    /// Construct a new `TechniqueHint`.
266    pub fn new(
267        platform: impl Into<String>,
268        ref_: impl Into<String>,
269        profile: impl Into<String>,
270    ) -> Self {
271        Self {
272            platform: Some(platform.into()),
273            ref_: ref_.into(),
274            profile: Some(profile.into()),
275        }
276    }
277}
278
279impl XNode for TechniqueHint {
280    const NAME: &'static str = "technique_hint";
281    fn parse(element: &Element) -> Result<Self> {
282        debug_assert_eq!(element.name(), Self::NAME);
283        Ok(TechniqueHint {
284            platform: element.attr("platform").map(Into::into),
285            ref_: element.attr("ref").ok_or("expected 'ref' attr")?.into(),
286            profile: element.attr("profile").map(Into::into),
287        })
288    }
289}
290
291impl XNodeWrite for TechniqueHint {
292    fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
293        let mut e = Self::elem();
294        e.opt_attr("platform", &self.platform);
295        e.attr("ref", &self.ref_);
296        e.opt_attr("profile", &self.profile);
297        e.end(w)
298    }
299}