blender_mesh/vertex_attributes/single_indexed/
interleave.rs

1use crate::vertex_attributes::VertexAttribute;
2use crate::SingleIndexedVertexAttributes;
3
4/// An error while interleaving vertex data
5#[derive(Debug, thiserror::Error)]
6pub enum InterleaveError {
7    /// Likely a mistake when creating the slice of vertex attributes
8    #[error("Only {num_provided} buffers were provided so there is nothing to interleave.")]
9    RequiresAtLeastTwoBuffers { num_provided: usize },
10    /// If, say, positions have an attribute size of 3 and uvs have a size of 2 then
11    /// positions.len() / 3 should equal uvs.len() / 2
12    #[error("The length of each attribute should correspond to the same number of vertices")]
13    MismatchedLengths,
14}
15
16impl SingleIndexedVertexAttributes {
17    /// Combine anu number of vertex attributes into a single buffer of vertex data.
18    ///
19    /// Say you have
20    ///   positions: [0., 1., 2., 10., 11., 12.] with attribute size 3
21    ///   uvs      : [0., 1., 1., 1.]
22    ///
23    /// This would get stitched together as
24    ///              [0., 1., 2., 0., 1., 10., 11., 12., 1., 1.]
25    ///
26    /// More generally, say you have attributes P with size 3, U with size 2, N with size 3.
27    ///
28    /// They'll get interleaved as
29    ///         [
30    ///             P0, P0, P0, U0, U0, N0, N0,
31    ///             P1, P1, P1, U1, U1, N1, N1, ...
32    ///         ],
33    pub fn interleave<T: Copy>(attribs: &[&VertexAttribute<T>]) -> Result<Vec<T>, InterleaveError> {
34        if attribs.len() < 2 {
35            return Err(InterleaveError::RequiresAtLeastTwoBuffers {
36                num_provided: attribs.len(),
37            });
38        }
39
40        let vertex_count = attribs[0].data.len() as f32 / attribs[0].attribute_size as f32;
41
42        if !attribs
43            .iter()
44            .all(|attrib| attrib.len() as f32 / attrib.attribute_size as f32 == vertex_count)
45        {
46            return Err(InterleaveError::MismatchedLengths);
47        }
48
49        let vertex_count = vertex_count as usize;
50
51        // TODO: We can by setting the vector to the correct capacity and length with uninitialized
52        // memory and just iterate through each attribute and put the data in the right place with
53        // the right stride
54
55        let mut interleaved = vec![];
56
57        for vertex in 0..vertex_count {
58            for attrib in attribs {
59                let attribuze_size = attrib.attribute_size as usize;
60                let index = vertex * attribuze_size;
61                for idx in index..index + attribuze_size {
62                    interleaved.push(attrib.data[idx]);
63                }
64            }
65        }
66
67        Ok(interleaved)
68    }
69}
70
71#[cfg(test)]
72mod tests {
73    use super::*;
74
75    /// Interleave two vertex attributes
76    #[test]
77    fn combine_two_attributes() {
78        let positions = VertexAttribute::new(vec![0., 1., 2., 3., 4., 5.], 3).unwrap();
79        let uvs = VertexAttribute::new(vec![50., 51., 52., 53.], 2).unwrap();
80
81        let combined = SingleIndexedVertexAttributes::interleave(&[&positions, &uvs]).unwrap();
82
83        assert_eq!(combined, vec![0., 1., 2., 50., 51., 3., 4., 5., 52., 53.]);
84    }
85
86    /// Trying to interleave one buffer is likely a mistake
87    #[test]
88    fn only_one_buffer_provided() {
89        let positions = VertexAttribute::new(vec![0., 1., 2., 3., 4., 5.], 3).unwrap();
90        match SingleIndexedVertexAttributes::interleave(&[&positions]) {
91            Err(InterleaveError::RequiresAtLeastTwoBuffers { num_provided: 1 }) => {}
92            _ => unreachable!(),
93        };
94    }
95
96    /// The lengths of all of the attributes should correspond to the same number of vertices
97    #[test]
98    fn error_if_incompatible_lengths() {
99        let positions = VertexAttribute::new(vec![0.], 3).unwrap();
100        let uvs = VertexAttribute::new(vec![50., 51., 52., 53.], 2).unwrap();
101
102        match SingleIndexedVertexAttributes::interleave(&[&positions, &uvs]) {
103            Err(InterleaveError::MismatchedLengths {}) => {}
104            _ => unreachable!(),
105        };
106    }
107}