re_types/archetypes/
mesh3d_ext.rs

1use arrow::array::{self, Array as _};
2
3use crate::{
4    archetypes,
5    components::{self},
6};
7
8use super::Mesh3D;
9
10#[derive(thiserror::Error, Debug)]
11pub enum Mesh3DError {
12    #[error(
13        "No indices were specified, so the number of positions must be divisible by 9 [(xyz xyz xyz), …], got {0}"
14    )]
15    PositionsAreNotTriangles(usize),
16
17    #[error("Index out of bounds: got index={index} with {num_vertices} vertices")]
18    IndexOutOfBounds { index: u32, num_vertices: usize },
19
20    #[error(
21        "Positions & normals array must have the same length, \
22        got positions={0} vs. normals={1}"
23    )]
24    MismatchedPositionsNormals(usize, usize),
25}
26
27impl Mesh3D {
28    /// Use this image as the albedo texture.
29    #[inline]
30    pub fn with_albedo_texture_image(mut self, image: impl Into<archetypes::Image>) -> Self {
31        let image = image.into();
32
33        self.albedo_texture_format = image
34            .format
35            .map(|batch| batch.with_descriptor_override(Self::descriptor_albedo_texture_format()));
36        self.albedo_texture_buffer = image
37            .buffer
38            .map(|batch| batch.with_descriptor_override(Self::descriptor_albedo_texture_buffer()));
39        self
40    }
41
42    /// Use this image as the albedo texture.
43    #[inline]
44    pub fn with_albedo_texture(
45        self,
46        image_format: impl Into<components::ImageFormat>,
47        image_buffer: impl Into<components::ImageBuffer>,
48    ) -> Self {
49        self.with_albedo_texture_format(image_format)
50            .with_albedo_texture_buffer(image_buffer)
51    }
52
53    /// Check that this is a valid mesh, e.g. that the vertex indices are within bounds
54    /// and that we have the same number of positions and normals (if any).
55    ///
56    /// Only use this when logging a whole new mesh. Not meaningful for field updates!
57    #[track_caller]
58    pub fn sanity_check(&self) -> Result<(), Mesh3DError> {
59        let num_vertices = self.num_vertices();
60
61        let index_data = self.triangle_indices.as_ref().map(|indices| {
62            array::as_fixed_size_list_array(&indices.array)
63                .values()
64                .to_data()
65        });
66
67        if let Some(index_data) = index_data {
68            for index in index_data.buffer::<u32>(0) {
69                if num_vertices <= *index as usize {
70                    return Err(Mesh3DError::IndexOutOfBounds {
71                        index: *index,
72                        num_vertices,
73                    });
74                }
75            }
76        } else if !num_vertices.is_multiple_of(9) {
77            return Err(Mesh3DError::PositionsAreNotTriangles(num_vertices));
78        }
79
80        if let Some(normals) = &self.vertex_normals {
81            if normals.array.len() != num_vertices {
82                return Err(Mesh3DError::MismatchedPositionsNormals(
83                    num_vertices,
84                    normals.array.len(),
85                ));
86            }
87        }
88
89        Ok(())
90    }
91
92    /// The total number of vertices.
93    #[inline]
94    pub fn num_vertices(&self) -> usize {
95        self.vertex_positions
96            .as_ref()
97            .map_or(0, |positions| positions.array.len())
98    }
99
100    /// The total number of triangles.
101    #[inline]
102    pub fn num_triangles(&self) -> usize {
103        if let Some(triangle_indices) = self.triangle_indices.as_ref() {
104            triangle_indices.array.len()
105        } else {
106            self.num_vertices() / 3
107        }
108    }
109}