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