dae_parser/api/
geom.rs

1//! Contains functions for reading vertex and other data out of the geometry types, like [`Mesh`].
2
3use super::*;
4use crate::source::{InputKind, SourceReader, ST, XYZ};
5
6/// This trait abstracts over types that can be used to resolve URL references.
7/// It is implemented for [`LocalMap`] and [`LocalMaps`], and user types can also implement
8/// this trait to provide external URL resolution.
9pub trait UrlResolver<'a, T> {
10    /// The error type of the resolver.
11    type Error;
12
13    /// Resolve an individual URL.
14    fn resolve(&self, url: &UrlRef<T>) -> Result<&'a T, Self::Error>;
15}
16
17impl<'a, T> UrlResolver<'a, T> for LocalMap<'a, T> {
18    type Error = ();
19    fn resolve(&self, url: &UrlRef<T>) -> Result<&'a T, Self::Error> {
20        self.get(url).ok_or(())
21    }
22}
23
24impl<'a, T: HasId> UrlResolver<'a, T> for LocalMaps<'a> {
25    type Error = ();
26    fn resolve(&self, url: &UrlRef<T>) -> Result<&'a T, Self::Error> {
27        self.get(url).ok_or(())
28    }
29}
30
31/// The result of reading a [`Vertices`] and resolving the references in it,
32/// to produce an object that can be used to translate primitive lists (see [`Importer`]).
33#[derive(Clone, Debug, Default)]
34pub struct VertexImporter<'a> {
35    position: Option<SourceReader<'a, XYZ>>,
36    normal: Option<SourceReader<'a, XYZ>>,
37    texcoord: Option<SourceReader<'a, ST>>,
38}
39
40impl<'a> VertexImporter<'a> {
41    /// Get the importer for position data.
42    pub fn position_importer(&self) -> Option<&SourceReader<'a, XYZ>> {
43        self.position.as_ref()
44    }
45
46    /// Get the importer for normal data.
47    pub fn normal_importer(&self) -> Option<&SourceReader<'a, XYZ>> {
48        self.normal.as_ref()
49    }
50
51    /// Get the importer for texture coordinate data.
52    pub fn texcoord_importer(&self) -> Option<&SourceReader<'a, ST>> {
53        self.texcoord.as_ref()
54    }
55}
56
57fn load<'a, K: InputKind + Default, R: UrlResolver<'a, Source>>(
58    res: &R,
59    input: &Input,
60) -> Result<SourceReader<'a, K>, R::Error> {
61    let src = res.resolve(input.source_as_source())?;
62    Ok(src.reader(K::default()).unwrap())
63}
64
65impl Vertices {
66    /// Construct a [`VertexImporter`] from the [`Vertices`] data. It requires a [`UrlResolver`]
67    /// to find the source arrays (which are usually but not always stored in the `Mesh` itself).
68    pub fn importer<'a, R: UrlResolver<'a, Source>>(
69        &'a self,
70        res: &R,
71    ) -> Result<VertexImporter<'a>, R::Error> {
72        let mut imp = VertexImporter::default();
73        for input in &self.inputs {
74            match input.semantic {
75                Semantic::Position => imp.position = Some(load(res, input)?),
76                Semantic::Normal => imp.normal = Some(load(res, input)?),
77                Semantic::TexCoord => imp.texcoord = Some(load(res, input)?),
78                _ => unimplemented!(),
79            }
80        }
81        Ok(imp)
82    }
83}
84
85#[derive(Clone, Debug)]
86enum Instruction<'a> {
87    Normal(SourceReader<'a, XYZ>),
88    TexCoord(SourceReader<'a, ST>, Option<u32>),
89    // TODO: implement more kinds of data
90}
91
92/// A completed impporter, which is used to translate primitive lists.
93/// Created with [`Geom::importer`],
94#[derive(Clone, Debug, Default)]
95pub struct Importer<'a> {
96    vimp: VertexImporter<'a>,
97    vtx_offset: usize,
98    stride: usize,
99    insts: Vec<(usize, Instruction<'a>)>,
100}
101
102impl<T> Geom<T> {
103    /// Construct an [`Importer`] from a [`VertexImporter`] and a [`UrlResolver`].
104    pub fn importer<'a, R: UrlResolver<'a, Source>>(
105        &'a self,
106        res: &R,
107        vimp: VertexImporter<'a>,
108    ) -> Result<Importer<'a>, R::Error> {
109        let mut insts = vec![];
110        let vtx_offset = self
111            .inputs
112            .iter()
113            .find(|p| p.semantic == Semantic::Vertex)
114            .expect("no VERTEX input found")
115            .offset as usize;
116        for i in &*self.inputs {
117            match i.semantic {
118                Semantic::Normal => {
119                    insts.push((i.offset as usize, Instruction::Normal(load(res, i)?)))
120                }
121                Semantic::TexCoord => insts.push((
122                    i.offset as usize,
123                    Instruction::TexCoord(load(res, i)?, i.set),
124                )),
125                _ => {}
126            }
127        }
128        Ok(Importer {
129            vimp,
130            vtx_offset,
131            stride: self.inputs.stride,
132            insts,
133        })
134    }
135}
136
137/// A trait, to be implemented by user types, to describe how to construct a vertex object
138/// from the stored data.
139/// The context `C` is an arbitrary type that can be used for getting
140/// (or modifying, with interior mutability) additional data to create the object.
141pub trait VertexLoad<'a, C: ?Sized = ()>: Clone {
142    /// Construct a new vertex using only position data.
143    ///
144    /// The position `index` is provided as is, but the intended use is to call `reader.get(i)`
145    /// and use the resulting `[f32; 3]` as the position.
146    fn position(ctx: &C, reader: &SourceReader<'a, XYZ>, index: u32) -> Self;
147
148    /// Add normal data to a vertex.
149    ///
150    /// The position `index` is provided as is, but the intended use is to call `reader.get(i)`
151    /// and use the resulting `[f32; 3]` as the normal.
152    fn add_normal(&mut self, ctx: &C, reader: &SourceReader<'a, XYZ>, index: u32);
153
154    /// Add texture coordinate data to a vertex.
155    ///
156    /// The position `index` is provided as is, but the intended use is to call `reader.get(i)`
157    /// and use the resulting `[f32; 2]` as the u-v data.
158    fn add_texcoord(
159        &mut self,
160        ctx: &C,
161        reader: &SourceReader<'a, ST>,
162        index: u32,
163        set: Option<u32>,
164    );
165}
166
167impl<'a> Importer<'a> {
168    /// Construct a new vertex from a slice of the index data.
169    /// This is a low level function; prefer [`read`](Self::read)
170    /// and the functions on the [`ArrayIter`] type.
171    pub fn build_vertex<C: ?Sized, V: VertexLoad<'a, C>>(&self, ctx: &C, data: &[u32]) -> V {
172        let vtx_data = data[self.vtx_offset];
173        let mut vtx = V::position(ctx, self.vimp.position.as_ref().unwrap(), vtx_data);
174        if let Some(ref reader) = self.vimp.normal {
175            vtx.add_normal(ctx, reader, vtx_data)
176        }
177        if let Some(ref reader) = self.vimp.texcoord {
178            vtx.add_texcoord(ctx, reader, vtx_data, None)
179        }
180        for inst in &self.insts {
181            match *inst {
182                (off, Instruction::Normal(ref reader)) => vtx.add_normal(ctx, reader, data[off]),
183                (off, Instruction::TexCoord(ref reader, set)) => {
184                    vtx.add_texcoord(ctx, reader, data[off], set)
185                }
186            }
187        }
188        vtx
189    }
190
191    /// Get the importer for normal data.
192    pub fn normal_importer(&self) -> Option<&SourceReader<'a, XYZ>> {
193        let mut importer = self.vimp.normal_importer();
194        for inst in &self.insts {
195            if let Instruction::Normal(imp) = &inst.1 {
196                importer = Some(imp)
197            }
198        }
199        importer
200    }
201
202    /// Get the importer for texture coordinate data.
203    pub fn texcoord_importer(&self, set: u32) -> Option<&SourceReader<'a, ST>> {
204        let mut importer = self.vimp.texcoord_importer();
205        for inst in &self.insts {
206            if let Instruction::TexCoord(imp, i) = &inst.1 {
207                if i.map_or(true, |i| i == set) {
208                    importer = Some(imp)
209                }
210            }
211        }
212        importer
213    }
214
215    /// Construct a new vertex iterator from some user context `C` (can be `()`),
216    /// and an array coming from one of the `prim` fields:
217    /// * [`LineGeom::prim`]
218    /// * the elements of [`LineStripGeom::prim`]
219    /// * [`PolygonHole::verts`] and the elements of [`PolygonHole::hole`]
220    /// * [`PolyListGeom::prim`]
221    /// * [`TriangleGeom::prim`]
222    /// * the elements of [`TriFanGeom::prim`]
223    /// * the elements of [`TriStripGeom::prim`]
224    pub fn read<'b, C: ?Sized, V: VertexLoad<'a, C>>(
225        &'b self,
226        ctx: &'b C,
227        array: &'a [u32],
228    ) -> ArrayIter<'a, 'b, C, V> {
229        assert!(self.stride != 0);
230        let len = array.len() / self.stride;
231        assert!(len * self.stride == array.len());
232        ArrayIter {
233            imp: self,
234            ctx,
235            len,
236            array,
237            _mark: PhantomData,
238        }
239    }
240}
241
242/// An iterator / accessor for retrieving vertex objects from a stored `u32` array.
243#[derive(Clone, Debug)]
244pub struct ArrayIter<'a, 'b, C: ?Sized, V> {
245    imp: &'b Importer<'a>,
246    ctx: &'b C,
247    len: usize,
248    array: &'a [u32],
249    _mark: PhantomData<V>,
250}
251
252impl<'a, 'b, C: ?Sized, V: VertexLoad<'a, C>> ArrayIter<'a, 'b, C, V> {
253    /// Get the index slice at a vertex index.
254    /// This is a low level function, prefer [`get`](Self::get), but this can be used as input to
255    /// [`Importer::build_vertex`].
256    pub fn slice(&self, i: usize) -> &'a [u32] {
257        &self.array[i * self.imp.stride..][..self.imp.stride]
258    }
259
260    /// Return a vertex object by index.
261    pub fn get(&self, i: usize) -> V {
262        self.imp.build_vertex(self.ctx, self.slice(i))
263    }
264
265    /// Advances the iterator by `n` elements. See [`Iterator::advance_by`].
266    pub fn advance_by(&mut self, n: usize) -> Result<(), usize> {
267        if n <= self.len {
268            self.array = &self.array[n * self.imp.stride..];
269            Ok(())
270        } else {
271            Err(self.len)
272        }
273    }
274
275    /// Advances the iterator from the back by `n` elements.
276    /// See [`DoubleEndedIterator::advance_back_by`].
277    pub fn advance_back_by(&mut self, n: usize) -> Result<(), usize> {
278        if n <= self.len {
279            self.array = &self.array[..self.array.len() - n * self.imp.stride];
280            Ok(())
281        } else {
282            Err(self.len)
283        }
284    }
285}
286
287impl<'a, 'b, C: ?Sized, V: VertexLoad<'a, C>> ExactSizeIterator for ArrayIter<'a, 'b, C, V> {
288    fn len(&self) -> usize {
289        self.len
290    }
291}
292
293impl<'a, 'b, C: ?Sized, V: VertexLoad<'a, C>> DoubleEndedIterator for ArrayIter<'a, 'b, C, V> {
294    fn next_back(&mut self) -> Option<Self::Item> {
295        self.len = self.len.checked_sub(1)?;
296        let (left, right) = self.array.split_at(self.len);
297        self.array = left;
298        Some(self.imp.build_vertex(self.ctx, right))
299    }
300
301    fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
302        self.advance_back_by(n).ok()?;
303        self.next_back()
304    }
305}
306
307impl<'a, 'b, C: ?Sized, V: VertexLoad<'a, C>> Iterator for ArrayIter<'a, 'b, C, V> {
308    type Item = V;
309
310    fn next(&mut self) -> Option<Self::Item> {
311        self.len = self.len.checked_sub(1)?;
312        let (left, right) = self.array.split_at(self.imp.stride);
313        self.array = right;
314        Some(self.imp.build_vertex(self.ctx, left))
315    }
316
317    fn size_hint(&self) -> (usize, Option<usize>) {
318        (self.len, Some(self.len))
319    }
320
321    fn count(self) -> usize {
322        self.len
323    }
324
325    fn last(mut self) -> Option<Self::Item> {
326        self.next_back()
327    }
328
329    fn nth(&mut self, n: usize) -> Option<Self::Item> {
330        self.advance_by(n).ok()?;
331        self.next()
332    }
333}