dae_parser/fx/
texture.rs

1use crate::*;
2
3/// Declares the storage for the graphical representation of an object.
4#[derive(Clone, Debug)]
5pub struct Image {
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    /// A text string value that indicates the image format.
11    /// It describes the encoding of the image in [`ImageSource::Data`]
12    /// or the format of the image referenced by [`ImageSource::InitFrom`]
13    /// if it is in a nonstandard format that cannot be identified by its file extension.
14    /// For example, if [`ImageSource::Data`] in a COLLADA document
15    /// contains the digital contents of a JPEG file, then set this attribute to "JPG".
16    pub format: Option<String>,
17    /// An integer value that indicates the height of the image in pixels.
18    /// A value of 0 means the value is omitted.
19    pub height: u32,
20    /// An integer value that indicates the width of the image in pixels.
21    /// A value of 0 means the value is omitted.
22    pub width: u32,
23    /// An integer value that indicates the depth of the image in pixels.
24    /// A 2-D image has a depth of 1, which is the default.
25    pub depth: u32,
26    /// Asset management information about this element.
27    pub asset: Option<Box<Asset>>,
28    /// This specifies either embedded image data or an external image file.
29    pub source: ImageSource,
30    /// Provides arbitrary additional information about this element.
31    pub extra: Vec<Extra>,
32}
33
34impl Image {
35    /// Construct a new `Image` from the given source.
36    pub fn new(id: impl Into<String>, name: Option<String>, source: ImageSource) -> Self {
37        Self {
38            id: Some(id.into()),
39            name,
40            format: None,
41            height: 0,
42            width: 0,
43            depth: 1,
44            asset: None,
45            source,
46            extra: vec![],
47        }
48    }
49}
50
51impl XNode for Image {
52    const NAME: &'static str = "image";
53    fn parse(element: &Element) -> Result<Self> {
54        debug_assert_eq!(element.name(), Self::NAME);
55        let mut it = element.children().peekable();
56        Ok(Image {
57            id: element.attr("id").map(Into::into),
58            name: element.attr("name").map(Into::into),
59            format: element.attr("format").map(Into::into),
60            height: parse_attr(element.attr("height"))?.unwrap_or(0),
61            width: parse_attr(element.attr("width"))?.unwrap_or(0),
62            depth: parse_attr(element.attr("depth"))?.unwrap_or(1),
63            asset: Asset::parse_opt_box(&mut it)?,
64            source: parse_one_many(&mut it, ImageSource::parse)?,
65            extra: Extra::parse_many(it)?,
66        })
67    }
68}
69
70impl XNodeWrite for Image {
71    fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
72        let mut e = Self::elem();
73        e.opt_attr("id", &self.id);
74        e.opt_attr("name", &self.name);
75        e.opt_attr("format", &self.format);
76        e.def_print_attr("height", self.height, 0);
77        e.def_print_attr("width", self.width, 0);
78        e.def_print_attr("depth", self.depth, 1);
79        let e = e.start(w)?;
80        self.asset.write_to(w)?;
81        self.source.write_to(w)?;
82        self.extra.write_to(w)?;
83        e.end(w)
84    }
85}
86
87/// An [`Image`] or [`NewParam`] element.
88#[derive(Clone, Debug)]
89pub enum ImageParam {
90    /// A [`NewParam`] element.
91    NewParam(NewParam),
92    /// An [`Image`] element.
93    Image(Image),
94}
95
96impl From<Image> for ImageParam {
97    fn from(v: Image) -> Self {
98        Self::Image(v)
99    }
100}
101
102impl ImageParam {
103    pub(crate) fn parse_list(it: &mut ElementIter<'_>) -> Result<Vec<Self>> {
104        parse_list_many(it, |e| {
105            Ok(Some(match e.name() {
106                Image::NAME => Self::Image(Image::parse(e)?),
107                NewParam::NAME => Self::NewParam(NewParam::parse(e)?),
108                _ => return Ok(None),
109            }))
110        })
111    }
112}
113
114impl XNodeWrite for ImageParam {
115    fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
116        match self {
117            Self::NewParam(e) => e.write_to(w),
118            Self::Image(e) => e.write_to(w),
119        }
120    }
121}
122
123fn parse_hex_array(s: &str) -> Box<[u8]> {
124    let mut out = vec![];
125    let mut hi = 0;
126    let mut odd = false;
127    for c in s.bytes() {
128        let c = match c {
129            b'0'..=b'9' => c - b'0',
130            b'a'..=b'f' => c - b'a' + 10,
131            b'A'..=b'F' => c - b'A' + 10,
132            _ => continue,
133        };
134        if odd {
135            out.push(hi << 4 | c)
136        } else {
137            hi = c
138        }
139        odd = !odd
140    }
141    out.into()
142}
143
144/// A specification of the source of data for an image.
145#[derive(Clone, Debug)]
146pub enum ImageSource {
147    /// The data is provided directly as a byte buffer.
148    Data(Box<[u8]>),
149    /// A URI that specifies an external image file.
150    InitFrom(Url),
151}
152
153impl ImageSource {
154    /// Parse an [`ImageSource`] from an XML element.
155    pub fn parse(element: &Element) -> Result<Option<Self>> {
156        Ok(Some(match element.name() {
157            "data" => {
158                let s = get_text(element).ok_or("expected text element")?;
159                ImageSource::Data(parse_hex_array(s))
160            }
161            "init_from" => ImageSource::InitFrom(parse_elem(element)?),
162            _ => return Ok(None),
163        }))
164    }
165}
166
167impl XNodeWrite for ImageSource {
168    fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
169        match self {
170            Self::Data(data) => {
171                let e = ElemBuilder::new("data").start(w)?;
172                let mut indent = false;
173                for chunk in data.chunks(40) {
174                    if indent {
175                        w.write_indent()?
176                    }
177                    indent = true;
178                    for &c in chunk {
179                        write!(w.get_mut(), "{:x}", c)?
180                    }
181                }
182                e.end(w)
183            }
184            Self::InitFrom(url) => ElemBuilder::print("init_from", url, w),
185        }
186    }
187}
188
189/// Declares a two-dimensional texture sampler.
190#[derive(Clone, Debug)]
191pub struct Sampler2D {
192    /// A name, which is the `sid` of a [`Surface`].
193    /// A `Sampler*` is a definition of how a shader will resolve a
194    /// color out of a [`Surface`]. `source` identifies the [`Surface`] to read.
195    pub source: NameRef<Surface>,
196    /// Wrap mode in the first texture coordinate.
197    pub wrap_s: WrapMode,
198    /// Wrap mode in the second texture coordinate.
199    pub wrap_t: WrapMode,
200    /// Texture minimization. Applying a texture to a primitive
201    /// implies a mapping from texture image space to framebuffer image space.
202    /// In general, this mapping involves a reconstruction of the sampled texture image,
203    /// followed by a homogeneous warping implied by the mapping to framebuffer space,
204    /// then a filtering, followed finally by a resampling of the filtered, warped,
205    /// reconstructed image before applying it to a fragment.
206    pub min_filter: SamplerFilter,
207    /// Texture magnification. Enumerated type
208    /// fx_sampler_filter_common. When gamma indicates
209    /// magnification, this value determines how the texture value is
210    /// obtained.
211    pub mag_filter: SamplerFilter,
212    /// MIPmap filter.
213    pub mip_filter: SamplerFilter,
214    /// When reading past the edge of the texture address space
215    /// based on the wrap modes involving clamps, this color takes
216    /// over. Type `fx_color_common` (four floating-point numbers in RGBA order).
217    pub border_color: Option<Box<[f32; 4]>>,
218    /// The maximum number of progressive levels that the sampler will evaluate.
219    pub mipmap_max_level: u8,
220    /// Biases the gamma (level of detail parameter) that is used by the sampler
221    /// to evaluate the MIPmap chain.
222    pub mipmap_bias: f32,
223    /// Provides arbitrary additional information about this element.
224    pub extra: Vec<Extra>,
225}
226
227impl Sampler2D {
228    /// Construct a new `Sampler2D` from a source.
229    pub fn new(source: String) -> Self {
230        Self {
231            source: Ref::new(source),
232            wrap_s: Default::default(),
233            wrap_t: Default::default(),
234            min_filter: Default::default(),
235            mag_filter: Default::default(),
236            mip_filter: Default::default(),
237            border_color: None,
238            mipmap_max_level: 0,
239            mipmap_bias: 0.,
240            extra: vec![],
241        }
242    }
243}
244
245impl XNode for Sampler2D {
246    const NAME: &'static str = "sampler2D";
247    fn parse(element: &Element) -> Result<Self> {
248        debug_assert_eq!(element.name(), Self::NAME);
249        let mut it = element.children().peekable();
250        Ok(Sampler2D {
251            source: parse_one("source", &mut it, parse_elem)?,
252            wrap_s: parse_opt("wrap_s", &mut it, parse_elem)?.unwrap_or_default(),
253            wrap_t: parse_opt("wrap_t", &mut it, parse_elem)?.unwrap_or_default(),
254            min_filter: parse_opt("minfilter", &mut it, parse_elem)?.unwrap_or_default(),
255            mag_filter: parse_opt("magfilter", &mut it, parse_elem)?.unwrap_or_default(),
256            mip_filter: parse_opt("mipfilter", &mut it, parse_elem)?.unwrap_or_default(),
257            border_color: parse_opt("border_color", &mut it, parse_array_n)?,
258            mipmap_max_level: parse_opt("mipmap_maxlevel", &mut it, parse_elem)?.unwrap_or(0),
259            mipmap_bias: parse_opt("mipmap_bias", &mut it, parse_elem)?.unwrap_or(0.),
260            extra: Extra::parse_many(it)?,
261        })
262    }
263}
264
265impl XNodeWrite for Sampler2D {
266    fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
267        let e = Self::elem().start(w)?;
268        ElemBuilder::print("source", &self.source, w)?;
269        ElemBuilder::def_print("wrap_s", self.wrap_s, Default::default(), w)?;
270        ElemBuilder::def_print("wrap_t", self.wrap_t, Default::default(), w)?;
271        ElemBuilder::def_print("minfilter", self.min_filter, Default::default(), w)?;
272        ElemBuilder::def_print("magfilter", self.mag_filter, Default::default(), w)?;
273        ElemBuilder::def_print("mipfilter", self.mip_filter, Default::default(), w)?;
274        opt(&self.border_color, |e| {
275            ElemBuilder::print_arr("border_color", &**e, w)
276        })?;
277        ElemBuilder::def_print("mipmap_maxlevel", self.mipmap_max_level, 0, w)?;
278        ElemBuilder::def_print("mipmap_bias", self.mipmap_bias, 0., w)?;
279        self.extra.write_to(w)?;
280        e.end(w)
281    }
282}
283
284/// Wrap modes that affect the interpretation of `s`, `t`, and `p` texture coordinates in `Sampler*`
285/// elements.
286#[derive(Clone, Copy, Debug, PartialEq, Eq)]
287pub enum WrapMode {
288    /// OpenGL symbol `GL_REPEAT`.
289    /// Ignores the integer part of texture coordinates, using only the fractional part.
290    Wrap,
291    /// OpenGL symbol `GL_MIRRORED_REPEAT`.
292    /// First mirrors the texture coordinate.
293    /// The mirrored coordinate is then clamped as described for [`Clamp`](WrapMode::Clamp).
294    Mirror,
295    /// OpenGL symbol `GL_CLAMP_TO_EDGE`.
296    /// Clamps texture coordinates at all mipmap levels such
297    /// that the texture filter never samples a border texel.
298    /// *Note*: `GL_CLAMP` takes any texels beyond the
299    /// sampling border and substitutes those texels with
300    /// the border color. So `CLAMP_TO_EDGE` is more
301    /// appropriate. This also works much better with
302    /// OpenGL ES where the `GL_CLAMP` symbol was
303    /// removed from the OpenGL ES specification.
304    Clamp,
305    /// OpenGL symbol `GL_CLAMP_TO_BORDER`.
306    /// Clamps texture coordinates at all MIPmaps such that
307    /// the texture filter always samples border texels for
308    /// fragments whose corresponding texture coordinate
309    /// is sufficiently far outside the range [0, 1].
310    Border,
311    /// The defined behavior for `None` is consistent with
312    /// decal texturing where the border is black. Mapping
313    /// this calculation to `GL_CLAMP_TO_BORDER` is the best approximation of this.
314    None,
315}
316
317impl Default for WrapMode {
318    fn default() -> Self {
319        Self::Wrap
320    }
321}
322
323impl FromStr for WrapMode {
324    type Err = ();
325
326    fn from_str(s: &str) -> Result<Self, Self::Err> {
327        match s {
328            "WRAP" => Ok(Self::Wrap),
329            "MIRROR" => Ok(Self::Mirror),
330            "CLAMP" => Ok(Self::Clamp),
331            "BORDER" => Ok(Self::Border),
332            "NONE" => Ok(Self::None),
333            _ => Err(()),
334        }
335    }
336}
337
338impl WrapMode {
339    /// The XML name of a value in this enumeration.
340    pub fn to_str(self) -> &'static str {
341        match self {
342            Self::Wrap => "WRAP",
343            Self::Mirror => "MIRROR",
344            Self::Clamp => "CLAMP",
345            Self::Border => "BORDER",
346            Self::None => "NONE",
347        }
348    }
349}
350
351impl Display for WrapMode {
352    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
353        Display::fmt(self.to_str(), f)
354    }
355}
356
357/// (Undocumented?) Enumerated type `fx_sampler_filter_common` from COLLADA spec.
358#[derive(Clone, Copy, Debug, PartialEq, Eq)]
359#[allow(missing_docs)]
360pub enum SamplerFilter {
361    None,
362    Nearest,
363    Linear,
364    NearestMipmapNearest,
365    LinearMipmapNearest,
366    NearestMipmapLinear,
367    LinearMipmapLinear,
368}
369
370impl Default for SamplerFilter {
371    fn default() -> Self {
372        Self::None
373    }
374}
375
376impl FromStr for SamplerFilter {
377    type Err = ();
378
379    fn from_str(s: &str) -> Result<Self, Self::Err> {
380        match s {
381            "NONE" => Ok(Self::None),
382            "NEAREST" => Ok(Self::Nearest),
383            "LINEAR" => Ok(Self::Linear),
384            "NEAREST_MIPMAP_NEAREST" => Ok(Self::NearestMipmapNearest),
385            "LINEAR_MIPMAP_NEAREST" => Ok(Self::LinearMipmapNearest),
386            "NEAREST_MIPMAP_LINEAR" => Ok(Self::NearestMipmapLinear),
387            "LINEAR_MIPMAP_LINEAR" => Ok(Self::LinearMipmapLinear),
388            _ => Err(()),
389        }
390    }
391}
392
393impl SamplerFilter {
394    /// The XML name of a value in this enumeration.
395    pub fn to_str(self) -> &'static str {
396        match self {
397            Self::None => "NONE",
398            Self::Nearest => "NEAREST",
399            Self::Linear => "LINEAR",
400            Self::NearestMipmapNearest => "NEAREST_MIPMAP_NEAREST",
401            Self::LinearMipmapNearest => "LINEAR_MIPMAP_NEAREST",
402            Self::NearestMipmapLinear => "NEAREST_MIPMAP_LINEAR",
403            Self::LinearMipmapLinear => "LINEAR_MIPMAP_LINEAR",
404        }
405    }
406}
407
408impl Display for SamplerFilter {
409    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
410        Display::fmt(self.to_str(), f)
411    }
412}
413
414/// Declares a resource that can be used both as the source for
415/// texture samples and as the target of a rendering pass.
416#[derive(Clone, Debug)]
417pub struct Surface {
418    /// The type of this surface.
419    pub ty: SurfaceType,
420    /// An initialization option for this surface.
421    pub init: SurfaceInit,
422    /// Contains a string representing the texel format for this surface.
423    /// If this element is not specified or understood by the application,
424    /// then the application will attempt to use `format_hint` if it is provided;
425    /// otherwise, it should use a common format linear `R8G8B8A8`.
426    pub format: Option<String>,
427    /// An application uses `format_hint` if `format` does not exist or
428    /// is not understood by the application and `format_hint` exists.
429    /// This element describes the important features intended by the author
430    /// so that the application can pick a format that best represents what the author wanted.
431    pub format_hint: Option<Box<FormatHint>>,
432    /// Contains three integer values. If specified, the surface is
433    /// sized to these exact dimensions in texels. Surfaces of
434    /// type `1D` and `CUBE` use only the first value. Surfaces of
435    /// type `2D` and `RECT` use only the first two values,
436    /// representing width and then height. Type `3D` uses all
437    /// three values, representing width, height, and depth.
438    /// Invalid if `viewport_ratio` is used.
439    pub size: Option<Box<[u32; 3]>>,
440    /// Contains two floating-point values representing width and then height.
441    /// If specified, the surface is sized to a dimension
442    /// based on these ratios of the viewport's (backbuffer's) dimensions.
443    /// For example, `viewport_ratio = Some([0.5, 2])`
444    /// scales the surface’s width to half the viewport’s width
445    /// and its height to twice the viewport’s height.
446    /// This element is valid only for surfaces of type `2D` or `RECT`.
447    /// Invalid if `size` is used.
448    pub viewport_ratio: Option<Box<[f32; 2]>>,
449    /// Contains the number of MIP levels in the surface. A value
450    /// of 0 assumes that all MIP levels exist until a dimension
451    /// becomes 1 texel. To create a surface that has only one
452    /// level of MIP maps (`mip` = 0), set this to 1.
453    pub mip_levels: u32,
454    /// If false and not all subsurfaces are
455    /// initialized because you have not provided MIP-map
456    /// levels, the generated surface will have profile- and
457    ///
458    /// platform-specific behavior. If true, the application is
459    /// responsible for initializing the remainder of the
460    /// subsurfaces; this is typically done through a graphics API
461    /// render state or function that does this automatically, such
462    /// as `glGenerateMipmap()`.
463    pub mipmap_generate: bool,
464    /// Provides arbitrary additional information about this element.
465    pub extra: Vec<Extra>,
466}
467
468impl Surface {
469    /// Construct a new `Surface` from mandatory parameters.
470    pub fn new(ty: SurfaceType, init: SurfaceInit) -> Self {
471        Self {
472            ty,
473            init,
474            format: None,
475            format_hint: None,
476            size: None,
477            viewport_ratio: None,
478            mip_levels: 0,
479            mipmap_generate: false,
480            extra: vec![],
481        }
482    }
483}
484
485impl XNode for Surface {
486    const NAME: &'static str = "surface";
487    fn parse(element: &Element) -> Result<Self> {
488        debug_assert_eq!(element.name(), Self::NAME);
489        let mut it = element.children().peekable();
490        let res = Surface {
491            ty: parse_attr(element.attr("type"))?.ok_or("expected 'type' attr")?,
492            init: parse_one_many(&mut it, SurfaceInit::parse)?,
493            format: parse_opt("format", &mut it, parse_text)?,
494            format_hint: FormatHint::parse_opt_box(&mut it)?,
495            size: parse_opt("size", &mut it, parse_array_n)?,
496            viewport_ratio: parse_opt("viewport_ratio", &mut it, parse_array_n)?,
497            mip_levels: parse_opt("mip_levels", &mut it, parse_elem)?.unwrap_or(0),
498            mipmap_generate: parse_opt("mipmap_generate", &mut it, parse_elem)?.unwrap_or(false),
499            extra: Extra::parse_many(it)?,
500        };
501        if res.size.is_some() && res.viewport_ratio.is_some() {
502            return Err("size and viewport_ratio cannot be used together".into());
503        }
504        Ok(res)
505    }
506}
507
508impl XNodeWrite for Surface {
509    fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
510        let mut e = Self::elem();
511        e.print_attr("type", self.ty);
512        let e = e.start(w)?;
513        self.init.write_to(w)?;
514        opt(&self.format, |e| ElemBuilder::print_str("format", e, w))?;
515        self.format_hint.write_to(w)?;
516        opt(&self.size, |e| ElemBuilder::print_arr("size", &**e, w))?;
517        opt(&self.viewport_ratio, |e| {
518            ElemBuilder::print_arr("viewport_ratio", &**e, w)
519        })?;
520        ElemBuilder::def_print("mip_levels", self.mip_levels, 0, w)?;
521        ElemBuilder::def_print("mipmap_generate", self.mipmap_generate, false, w)?;
522        self.extra.write_to(w)?;
523        e.end(w)
524    }
525}
526
527/// Specifies a surface on a cube.
528#[derive(Clone, Copy, Debug, PartialEq, Eq)]
529pub enum SurfaceType {
530    /// When a surface’s type attribute is set to `Untyped`,
531    /// its type is initially unknown and established later by the context in which
532    /// it is used, such as by a texture sampler that references it.
533    /// A surface of any other type may be changed into an `Untyped` surface at
534    /// run-time, as if it were created by [`NewParam`], using [`EffectSetParam`].
535    /// If there is a type mismatch between a [`EffectSetParam`] operation and
536    /// what the run-time decides the type should be, the result is profile- and
537    /// platform-specific behavior.
538    Untyped,
539    /// A one dimensional texture.
540    _1D,
541    /// A two dimensional texture.
542    _2D,
543    /// A three dimensional texture.
544    _3D,
545    /// A RECT texture, see <http://www.opengl.org/registry/specs/ARB/texture_rectangle.txt>.
546    Rect,
547    /// A cube map.
548    Cube,
549    /// A depth map.
550    Depth,
551}
552
553impl Default for SurfaceType {
554    fn default() -> Self {
555        Self::Untyped
556    }
557}
558
559impl FromStr for SurfaceType {
560    type Err = ();
561
562    fn from_str(s: &str) -> Result<Self, Self::Err> {
563        match s {
564            "UNTYPED" => Ok(Self::Untyped),
565            "1D" => Ok(Self::_1D),
566            "2D" => Ok(Self::_2D),
567            "3D" => Ok(Self::_3D),
568            "RECT" => Ok(Self::Rect),
569            "CUBE" => Ok(Self::Cube),
570            "DEPTH" => Ok(Self::Depth),
571            _ => Err(()),
572        }
573    }
574}
575
576impl SurfaceType {
577    /// The XML name of a value in this enumeration.
578    pub fn to_str(self) -> &'static str {
579        match self {
580            Self::Untyped => "UNTYPED",
581            Self::_1D => "1D",
582            Self::_2D => "2D",
583            Self::_3D => "3D",
584            Self::Rect => "RECT",
585            Self::Cube => "CUBE",
586            Self::Depth => "DEPTH",
587        }
588    }
589}
590
591impl Display for SurfaceType {
592    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
593        Display::fmt(self.to_str(), f)
594    }
595}
596
597/// This element describes the important features intended by the author so that the
598/// application can pick a format that best represents what the author wanted.
599#[derive(Clone, Debug)]
600pub struct FormatHint {
601    /// The per-texel layout of the format.
602    pub channels: SurfaceChannels,
603    /// The range of texel channel values.
604    pub range: SurfaceRange,
605    /// The precision of the texel channel value.
606    pub precision: SurfacePrecision,
607    /// Additional hints about data relationships and other
608    /// things to help an application pick the best format.
609    pub options: Vec<SurfaceOption>,
610    /// Provides arbitrary additional information about this element.
611    pub extra: Vec<Extra>,
612}
613
614impl FormatHint {
615    /// Construct a new `FormatHint`.
616    pub fn new(
617        channels: SurfaceChannels,
618        range: SurfaceRange,
619        precision: SurfacePrecision,
620    ) -> Self {
621        Self {
622            channels,
623            range,
624            precision,
625            options: vec![],
626            extra: vec![],
627        }
628    }
629}
630
631impl XNode for FormatHint {
632    const NAME: &'static str = "format_hint";
633    fn parse(element: &Element) -> Result<Self> {
634        debug_assert_eq!(element.name(), Self::NAME);
635        let mut it = element.children().peekable();
636        Ok(FormatHint {
637            channels: parse_one("channels", &mut it, parse_elem)?,
638            range: parse_one("range", &mut it, parse_elem)?,
639            precision: parse_one("precision", &mut it, parse_elem)?,
640            options: parse_list("option", &mut it, parse_elem)?,
641            extra: Extra::parse_many(it)?,
642        })
643    }
644}
645
646impl XNodeWrite for FormatHint {
647    fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
648        let e = Self::elem().start(w)?;
649        ElemBuilder::print("channels", &self.channels, w)?;
650        ElemBuilder::print("range", &self.range, w)?;
651        ElemBuilder::print("precision", &self.precision, w)?;
652        many(&self.options, |e| ElemBuilder::print("option", e, w))?;
653        self.extra.write_to(w)?;
654        e.end(w)
655    }
656}
657
658/// A [`Surface`] initialization option, which specifies
659/// whether to initialize the surface and how to do so.
660#[derive(Clone, Debug)]
661pub enum SurfaceInit {
662    /// This surface is intended to be initialized later externally by a [`EffectSetParam`] element.
663    /// If it is used before being initialized, there is profile- and platform-specific behavior.
664    /// Most elements on the [`Surface`] element that contains this will be ignored,
665    /// including [`mip_levels`](Surface::mip_levels), [`mipmap_generate`](Surface::mipmap_generate),
666    /// [`size`](Surface::size), [`viewport_ratio`](Surface::viewport_ratio),
667    /// and [`format`](Surface::format).
668    Null,
669    /// Initializes this surface as a target for depth, stencil, or color. It does not need image
670    /// data. If this element is used, [`mipmap_generate`](Surface::mipmap_generate) is ignored.
671    Target,
672    // Cube(InitCube),
673    // Volume(InitVolume),
674    // Planar(InitPlanar),
675    /// Contains a reference to a 1D or 2D image. Initializes the surface one subsurface at
676    /// a time by specifying combinations of `mip`, `face`, and `slice` that make sense for a
677    /// particular surface type. Each subsurface is initialized by a common 1-D or 2-D
678    /// image, not a complex compound image such as DDS. If not all subsurfaces are
679    /// initialized, the surface is invalid and will result in profile- and platform-specific
680    /// behavior unless [`mipmap_generate`](Surface::mipmap_generate) is responsible for
681    /// initializing the remaining subsurfaces.
682    From {
683        /// The MIP level.
684        mip: u32,
685        /// Which 2D layer within a volume to initialize.
686        /// There are anywhere from 0 to `n` slices in a volume,
687        /// where `n` is the volume’s depth slice.
688        /// This attribute is used in combination with `mip` because a volume might have MIPmaps.
689        slice: u32,
690        /// Which surface of a cube to initialize from the specified image.
691        /// This attribute is used in combination with `mip` because a cubemap might
692        /// have MIPmaps.
693        face: SurfaceFace,
694        /// Each subsurface is initialized by a common 1-D or 2-D
695        /// image, not a complex compound image such as DDS.
696        image: NameRef<Image>,
697    },
698}
699
700impl SurfaceInit {
701    /// Construct a `SurfaceInit::From` variant with default arguments.
702    pub fn init_from(image: impl Into<String>) -> Self {
703        Self::From {
704            mip: 0,
705            slice: 0,
706            face: Default::default(),
707            image: Ref::new(image.into()),
708        }
709    }
710    /// Parse a [`SurfaceInit`] from an XML element.
711    pub fn parse(element: &Element) -> Result<Option<Self>> {
712        Ok(Some(match element.name() {
713            "init_as_null" => Self::Null,
714            "init_as_target" => Self::Target,
715            "init_cube" | "init_volume" | "init_planar" => unimplemented!(),
716            "init_from" => Self::From {
717                mip: parse_attr(element.attr("mip"))?.unwrap_or(0),
718                slice: parse_attr(element.attr("slice"))?.unwrap_or(0),
719                face: parse_attr(element.attr("face"))?.unwrap_or_default(),
720                image: parse_elem(element)?,
721            },
722            _ => return Ok(None),
723        }))
724    }
725}
726
727impl XNodeWrite for SurfaceInit {
728    fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
729        match self {
730            Self::Null => ElemBuilder::new("init_as_null").end(w),
731            Self::Target => ElemBuilder::new("init_as_target").end(w),
732            &Self::From {
733                mip,
734                slice,
735                face,
736                ref image,
737            } => {
738                let mut e = ElemBuilder::new("init_from");
739                e.def_print_attr("mip", mip, 0);
740                e.def_print_attr("slice", slice, 0);
741                e.def_print_attr("face", face, Default::default());
742                let e = e.start(w)?;
743                print_elem(image, w)?;
744                e.end(w)
745            }
746        }
747    }
748}
749
750/// Specifies a surface on a cube.
751#[derive(Clone, Copy, Debug, PartialEq, Eq)]
752pub enum SurfaceFace {
753    /// The `+x` face
754    PosX,
755    /// The `-x` face
756    NegX,
757    /// The `+y` face
758    PosY,
759    /// The `-y` face
760    NegY,
761    /// The `+z` face
762    PosZ,
763    /// The `-z` face
764    NegZ,
765}
766
767impl Default for SurfaceFace {
768    fn default() -> Self {
769        Self::PosX
770    }
771}
772
773impl FromStr for SurfaceFace {
774    type Err = ();
775
776    fn from_str(s: &str) -> Result<Self, Self::Err> {
777        match s {
778            "POSITIVE_X" => Ok(Self::PosX),
779            "NEGATIVE_X" => Ok(Self::NegX),
780            "POSITIVE_Y" => Ok(Self::PosY),
781            "NEGATIVE_Y" => Ok(Self::NegY),
782            "POSITIVE_Z" => Ok(Self::PosZ),
783            "NEGATIVE_Z" => Ok(Self::NegZ),
784            _ => Err(()),
785        }
786    }
787}
788
789impl SurfaceFace {
790    /// The XML name of a value in this enumeration.
791    pub fn to_str(self) -> &'static str {
792        match self {
793            Self::PosX => "POSITIVE_X",
794            Self::NegX => "NEGATIVE_X",
795            Self::PosY => "POSITIVE_Y",
796            Self::NegY => "NEGATIVE_Y",
797            Self::PosZ => "POSITIVE_Z",
798            Self::NegZ => "NEGATIVE_Z",
799        }
800    }
801}
802
803impl Display for SurfaceFace {
804    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
805        Display::fmt(self.to_str(), f)
806    }
807}
808
809/// The per-texel layout of the format.
810/// The length of the enumeration string indicates how many channels there are
811/// and each letter represents the name of a channel. There are typically 1 to 4 channels.
812#[derive(Clone, Copy, Debug, PartialEq, Eq)]
813pub enum SurfaceChannels {
814    /// Red/Green/Blue color map.
815    RGB,
816    /// Red/Green/Blue/Alpha map, often used for color and transparency
817    /// or other things packed into channel A, such as specular power.
818    RGBA,
819    /// Luminance map, often used for light mapping.
820    L,
821    /// Luminance/Alpha map, often used for light mapping.
822    LA,
823    /// Depth map, often used for displacement, parallax, relief, or shadow mapping.
824    D,
825    /// Typically used for normal maps or three-component displacement maps.
826    XYZ,
827    /// Typically used for normal maps, where `W` is the depth for relief or parallax mapping.
828    XYZW,
829}
830
831impl FromStr for SurfaceChannels {
832    type Err = ();
833
834    fn from_str(s: &str) -> Result<Self, Self::Err> {
835        match s {
836            "RGB" => Ok(Self::RGB),
837            "RGBA" => Ok(Self::RGBA),
838            "L" => Ok(Self::L),
839            "LA" => Ok(Self::LA),
840            "D" => Ok(Self::D),
841            "XYZ" => Ok(Self::XYZ),
842            "XYZW" => Ok(Self::XYZW),
843            _ => Err(()),
844        }
845    }
846}
847
848impl SurfaceChannels {
849    /// The XML name of a value in this enumeration.
850    pub fn to_str(self) -> &'static str {
851        match self {
852            Self::RGB => "RGB",
853            Self::RGBA => "RGBA",
854            Self::L => "L",
855            Self::LA => "LA",
856            Self::D => "D",
857            Self::XYZ => "XYZ",
858            Self::XYZW => "XYZW",
859        }
860    }
861}
862
863impl Display for SurfaceChannels {
864    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
865        Display::fmt(self.to_str(), f)
866    }
867}
868
869/// The range of texel channel values. Each channel represents a range of values.
870/// Some example ranges are signed or unsigned integers, or
871/// are within a clamped range such as 0.0f to 1.0f, or are a
872/// high dynamic range via floating point.
873#[derive(Clone, Copy, Debug, PartialEq, Eq)]
874pub enum SurfaceRange {
875    /// Format represents a decimal value that remains within the -1 to 1 range.
876    /// Implementation could be integer, fixed-point, or float.
877    SNorm,
878    /// Format represents a decimal value that remains within the 0 to 1 range.
879    /// Implementation could be integer, fixed-point, or float.
880    UNorm,
881    /// Format represents signed integer numbers;
882    /// for example, 8 bits can represent -128 to 127.
883    SInt,
884    /// Format represents unsigned integer numbers.
885    /// For example, 8 bits can represent 0 to 255.
886    UInt,
887    /// Format should support full floating-point ranges typically used for high dynamic range.
888    Float,
889}
890
891impl FromStr for SurfaceRange {
892    type Err = ();
893
894    fn from_str(s: &str) -> Result<Self, Self::Err> {
895        match s {
896            "SNORM" => Ok(Self::SNorm),
897            "UNORM" => Ok(Self::UNorm),
898            "SINT" => Ok(Self::SInt),
899            "UINT" => Ok(Self::UInt),
900            "FLOAT" => Ok(Self::Float),
901            _ => Err(()),
902        }
903    }
904}
905
906impl SurfaceRange {
907    /// The XML name of a value in this enumeration.
908    pub fn to_str(self) -> &'static str {
909        match self {
910            Self::SNorm => "SNORM",
911            Self::UNorm => "UNORM",
912            Self::SInt => "SINT",
913            Self::UInt => "UINT",
914            Self::Float => "FLOAT",
915        }
916    }
917}
918
919impl Display for SurfaceRange {
920    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
921        Display::fmt(self.to_str(), f)
922    }
923}
924
925/// The precision of the texel channel value.
926///
927/// Each channel of the texel has a precision. Typically, channels have the same precision. An
928/// exact format may lower the precision of an individual channel
929/// but applying a higher precision by linking the channels may still convey the same information.
930#[derive(Clone, Copy, Debug, PartialEq, Eq)]
931pub enum SurfacePrecision {
932    /// For integers, this typically represents 8 bits.
933    /// For floats, typically 16 bits.
934    Low,
935    /// For integers, this typically represents 8 to 24
936    /// bits. For floats, typically 16 to 32 bits.
937    Mid,
938    /// For integers, this typically represents 16 to
939    /// 32 bits. For floats, typically 24 to 32 bits.
940    High,
941}
942
943impl FromStr for SurfacePrecision {
944    type Err = ();
945
946    fn from_str(s: &str) -> Result<Self, Self::Err> {
947        match s {
948            "LOW" => Ok(Self::Low),
949            "MID" => Ok(Self::Mid),
950            "HIGH" => Ok(Self::High),
951            _ => Err(()),
952        }
953    }
954}
955
956impl SurfacePrecision {
957    /// The XML name of a value in this enumeration.
958    pub fn to_str(self) -> &'static str {
959        match self {
960            Self::Low => "LOW",
961            Self::Mid => "MID",
962            Self::High => "HIGH",
963        }
964    }
965}
966
967impl Display for SurfacePrecision {
968    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
969        Display::fmt(self.to_str(), f)
970    }
971}
972
973/// Contains additional hints about data relationships and other
974/// things to help an application pick the best format.
975#[derive(Clone, Copy, Debug, PartialEq, Eq)]
976pub enum SurfaceOption {
977    /// Colors are stored with respect
978    /// to the sRGB 2.2 gamma curve rather than linear.
979    SrgbGamma,
980    /// The texel’s XYZ/RGB should be
981    /// normalized such as in a normal map.
982    Normalized3,
983    /// The texel’s XYZW/RGBA should
984    /// be normalized such as in a normal map.
985    Normalized4,
986    /// The surface may use run-time compression.
987    /// Consider the best compression based on desired [`SurfaceChannels`],
988    /// [`SurfaceRange`], [`SurfacePrecision`], and [`SurfaceOption`]s.
989    Compressible,
990}
991
992impl FromStr for SurfaceOption {
993    type Err = ();
994
995    fn from_str(s: &str) -> Result<Self, Self::Err> {
996        match s {
997            "SRGB_GAMMA" => Ok(Self::SrgbGamma),
998            "NORMALIZED3" => Ok(Self::Normalized3),
999            "NORMALIZED4" => Ok(Self::Normalized4),
1000            "COMPRESSABLE" => Ok(Self::Compressible),
1001            _ => Err(()),
1002        }
1003    }
1004}
1005
1006impl SurfaceOption {
1007    /// The XML name of a value in this enumeration.
1008    pub fn to_str(self) -> &'static str {
1009        match self {
1010            Self::SrgbGamma => "SRGB_GAMMA",
1011            Self::Normalized3 => "NORMALIZED3",
1012            Self::Normalized4 => "NORMALIZED4",
1013            Self::Compressible => "COMPRESSABLE",
1014        }
1015    }
1016}
1017
1018impl Display for SurfaceOption {
1019    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1020        Display::fmt(self.to_str(), f)
1021    }
1022}