draco_oxide/core/mesh/
mod.rs

1pub mod builder;
2
3use super::{
4    attribute::{Attribute, AttributeType, ComponentDataType},
5    shared::{Float, Vector},
6};
7use crate::core::shared::{NdVector, PointIdx};
8use crate::utils::geom::point_to_face_distance_3d;
9
10/// Represents a 3D mesh.
11/// It consists of a list of faces, where each face is defined by three vertex indices,
12/// and a list of attributes ([Attribute]) that can be associated with the mesh.
13#[derive(Clone, Debug)]
14pub struct Mesh {
15    pub(crate) faces: Vec<[PointIdx; 3]>,
16    pub(crate) attributes: Vec<Attribute>,
17
18    // varible for glTF transcoder support
19    name: String,
20}
21
22impl Mesh {
23    pub fn get_attributes(&self) -> &[Attribute] {
24        &self.attributes
25    }
26
27    pub fn get_faces(&self) -> &[[PointIdx; 3]] {
28        &self.faces
29    }
30
31    pub fn get_attributes_mut(&mut self) -> &mut [Attribute] {
32        &mut self.attributes
33    }
34
35    pub fn get_attributes_mut_by_indices<'a>(
36        &'a mut self,
37        indices: &[usize],
38    ) -> Vec<&'a mut Attribute> {
39        let out = indices
40            .iter()
41            .map(|i| &mut self.attributes[*i] as *mut Attribute)
42            .collect::<Vec<_>>();
43
44        unsafe {
45            let out = out.iter().map(|i| *i).collect::<Vec<_>>();
46            std::mem::transmute::<Vec<*mut Attribute>, Vec<&mut Attribute>>(out)
47        }
48    }
49
50    pub fn get_name(&self) -> &str {
51        &self.name
52    }
53
54    pub fn set_name(&mut self, name: &str) {
55        self.name = name.to_owned();
56    }
57
58    pub fn new() -> Self {
59        Self {
60            faces: Vec::new(),
61            attributes: Vec::new(),
62
63            name: String::new(),
64        }
65    }
66
67    pub fn diff_l2_norm(&self, other: &Self) -> f64 {
68        let pos_att_iter = self
69            .attributes
70            .iter()
71            .enumerate()
72            .filter(|(_, att)| att.get_attribute_type() == AttributeType::Position);
73        let other_pos_att_iter = other
74            .attributes
75            .iter()
76            .enumerate()
77            .filter(|(_, att)| att.get_attribute_type() == AttributeType::Position);
78
79        let mut num_points = 0;
80        let mut sum_of_squared_dist = 0.0;
81        for ((_, pos_att), (_, other_pos_att)) in pos_att_iter.zip(other_pos_att_iter) {
82            if pos_att.get_num_components() != 3 {
83                panic!("Position attribute must have 3 components, but the first mesh has {} components", pos_att.get_num_components());
84            }
85
86            // Faces are now stored directly in the mesh
87            let faces = &self.faces;
88            let other_faces = &other.faces;
89
90            num_points += pos_att.len();
91            num_points += other_pos_att.len();
92            sum_of_squared_dist +=
93                sum_of_squared_dist_unpack_datatype(pos_att, faces, other_pos_att, other_faces);
94        }
95
96        sum_of_squared_dist.sqrt() / num_points as f64
97    }
98}
99
100fn sum_of_squared_dist_unpack_datatype(
101    position_att: &Attribute,
102    faces: &[[PointIdx; 3]],
103    other_position_att: &Attribute,
104    other_faces: &[[PointIdx; 3]],
105) -> f64 {
106    // Safety:
107    // 1. The number of components is checked to be 3.
108    // 2. The component type is checked to be f32 or f64.
109    unsafe {
110        match position_att.get_component_type() {
111            ComponentDataType::F32 => sum_of_squared_dist_impl::<f32>(
112                position_att,
113                faces,
114                other_position_att,
115                other_faces,
116            ) as f64,
117            ComponentDataType::F64 => sum_of_squared_dist_impl::<f64>(
118                position_att,
119                faces,
120                other_position_att,
121                other_faces,
122            ),
123            _ => panic!("Position Attribute is not of type f32 or f64"),
124        }
125    }
126}
127
128// # Safety: it must be safe to cast the first argument to &[Data]
129unsafe fn sum_of_squared_dist_impl<F>(
130    self_pos_att: &Attribute,
131    self_faces: &[[PointIdx; 3]],
132    other_pos_att: &Attribute,
133    other_faces: &[[PointIdx; 3]],
134) -> F
135where
136    F: Float,
137    NdVector<3, F>: Vector<3, Component = F>,
138{
139    assert!(
140        other_pos_att.get_component_type() == self_pos_att.get_component_type(),
141        "Component types must match, but the first mesh has {:?} and the second mesh has {:?}",
142        self_pos_att.get_component_type(),
143        other_pos_att.get_component_type()
144    );
145
146    if other_pos_att.get_num_components() != 3 {
147        panic!(
148            "Position attribute must have 3 components, but the second mesh has {} components",
149            other_pos_att.get_num_components()
150        );
151    }
152
153    // Safety: upheld
154    let self_pos_att = self_pos_att.unique_vals_as_slice_unchecked::<NdVector<3, F>>();
155    // Satety: Just checked
156    let other_pos_att = unsafe { other_pos_att.unique_vals_as_slice_unchecked::<NdVector<3, F>>() };
157
158    let mut sum_of_squared_dist = F::zero();
159    for pos in self_pos_att.iter() {
160        let min_dist = min_dist_point_to_faces(*pos, other_faces, other_pos_att);
161        sum_of_squared_dist += min_dist * min_dist;
162    }
163    for pos in other_pos_att.iter() {
164        let min_dist = min_dist_point_to_faces(*pos, self_faces, self_pos_att);
165        sum_of_squared_dist += min_dist * min_dist;
166    }
167
168    sum_of_squared_dist.sqrt()
169}
170
171fn min_dist_point_to_faces<F>(
172    p: NdVector<3, F>,
173    faces: &[[PointIdx; 3]],
174    pos_att: &[NdVector<3, F>],
175) -> F
176where
177    F: Float,
178{
179    let mut min_dist = F::MAX_VALUE;
180    for face in faces {
181        let v0 = pos_att[usize::from(face[0])];
182        let v1 = pos_att[usize::from(face[1])];
183        let v2 = pos_att[usize::from(face[2])];
184        let dist = point_to_face_distance_3d(p, [v0, v1, v2]);
185        if dist < min_dist {
186            min_dist = dist;
187        }
188    }
189    min_dist
190}