1use axiolid_core::{Aabb, Point3, Vec3};
4
5use crate::attribute::AttributeChannel;
6use crate::MeshValidationError;
7
8#[derive(Debug, Clone, Default, PartialEq)]
10#[cfg_attr(
11 feature = "serde",
12 derive(serde::Serialize, serde::Deserialize),
13 serde(deny_unknown_fields)
14)]
15pub struct NormalAttribute {
16 pub values: Vec<Vec3>,
18 pub indices: Option<Vec<u32>>,
20}
21
22#[derive(Debug, Clone, Default, PartialEq)]
27#[cfg_attr(
28 feature = "serde",
29 derive(serde::Serialize, serde::Deserialize),
30 serde(deny_unknown_fields)
31)]
32pub struct TriMesh {
33 pub positions: Vec<Point3>,
35 pub indices: Vec<u32>,
37 pub normals: Option<NormalAttribute>,
39 pub attributes: Vec<AttributeChannel>,
43}
44
45impl TriMesh {
46 pub fn new(positions: Vec<Point3>, indices: Vec<u32>) -> Self {
48 Self {
49 positions,
50 indices,
51 normals: None,
52 attributes: Vec::new(),
53 }
54 }
55
56 pub fn triangle_count(&self) -> usize {
58 self.indices.len() / 3
59 }
60
61 pub fn bounds(&self) -> Aabb {
63 let mut bounds = Aabb::default();
64 for &position in &self.positions {
65 bounds.extend(position);
66 }
67 bounds
68 }
69
70 pub fn validate_structure(&self) -> Result<(), MeshValidationError> {
72 if !self.indices.len().is_multiple_of(3) {
73 return Err(MeshValidationError::IncompleteTriangle {
74 index_count: self.indices.len(),
75 });
76 }
77 if let Some(&index) = self
78 .indices
79 .iter()
80 .find(|&&index| index as usize >= self.positions.len())
81 {
82 return Err(MeshValidationError::PositionIndexOutOfRange {
83 index,
84 position_count: self.positions.len(),
85 });
86 }
87 if let Some(normals) = &self.normals {
88 if let Some(indices) = &normals.indices {
89 if indices.len() != self.indices.len() {
90 return Err(MeshValidationError::NormalIndexCount {
91 expected: self.indices.len(),
92 actual: indices.len(),
93 });
94 }
95 if let Some(&index) = indices
96 .iter()
97 .find(|&&index| index as usize >= normals.values.len())
98 {
99 return Err(MeshValidationError::NormalIndexOutOfRange {
100 index,
101 normal_count: normals.values.len(),
102 });
103 }
104 } else if normals.values.len() != self.positions.len() {
105 return Err(MeshValidationError::NormalCount {
106 expected: self.positions.len(),
107 actual: normals.values.len(),
108 });
109 }
110 }
111 for (position, channel) in self.attributes.iter().enumerate() {
112 if channel.width == 0 {
113 return Err(MeshValidationError::AttributeZeroWidth {
114 name: channel.name.clone(),
115 });
116 }
117 if self.attributes[..position]
120 .iter()
121 .any(|earlier| earlier.name == channel.name)
122 {
123 return Err(MeshValidationError::AttributeDuplicateName {
124 name: channel.name.clone(),
125 });
126 }
127 if let Some(corners) = &channel.corner_indices {
128 validate_corner_channel(channel, corners, self.indices.len())?;
129 } else if channel.vertex_count() != self.positions.len()
130 || channel.values.len() % channel.width != 0
131 {
132 return Err(MeshValidationError::AttributeCount {
133 name: channel.name.clone(),
134 expected: self.positions.len(),
135 actual: channel.vertex_count(),
136 });
137 }
138 }
139 Ok(())
140 }
141
142 pub fn is_structurally_valid(&self) -> bool {
144 self.validate_structure().is_ok()
145 }
146
147 pub fn triangles(&self) -> impl ExactSizeIterator<Item = [u32; 3]> + '_ {
149 self.indices
150 .as_chunks::<3>()
151 .0
152 .iter()
153 .map(|triangle| [triangle[0], triangle[1], triangle[2]])
154 }
155}
156
157fn validate_corner_channel(
164 channel: &AttributeChannel,
165 corners: &[u32],
166 corner_count: usize,
167) -> Result<(), MeshValidationError> {
168 if !channel.values.len().is_multiple_of(channel.width) {
169 return Err(MeshValidationError::AttributeRaggedValues {
170 name: channel.name.clone(),
171 values: channel.values.len(),
172 width: channel.width,
173 });
174 }
175 if corners.len() != corner_count {
176 return Err(MeshValidationError::AttributeCornerCount {
177 name: channel.name.clone(),
178 expected: corner_count,
179 actual: corners.len(),
180 });
181 }
182 let value_count = channel.value_count();
183 if let Some(&index) = corners
184 .iter()
185 .find(|&&index| index != AttributeChannel::UNMAPPED && index as usize >= value_count)
186 {
187 return Err(MeshValidationError::AttributeCornerIndexOutOfRange {
188 name: channel.name.clone(),
189 index,
190 value_count,
191 });
192 }
193 for (triangle, entries) in corners.as_chunks::<3>().0.iter().enumerate() {
194 let unmapped = entries
195 .iter()
196 .filter(|&&index| index == AttributeChannel::UNMAPPED)
197 .count();
198 if unmapped != 0 && unmapped != 3 {
199 return Err(MeshValidationError::AttributePartiallyMapped {
200 name: channel.name.clone(),
201 triangle,
202 });
203 }
204 }
205 Ok(())
206}