1use crate::math::{add, cross, dot, normalize, scale, sub};
2
3#[derive(Clone, Copy, Debug)]
4pub struct Bounds {
5 pub min: [f32; 3],
6 pub max: [f32; 3],
7}
8
9impl Bounds {
10 pub fn empty() -> Self {
11 Self {
12 min: [f32::INFINITY; 3],
13 max: [f32::NEG_INFINITY; 3],
14 }
15 }
16
17 pub fn point(p: [f32; 3]) -> Self {
18 Self { min: p, max: p }
19 }
20
21 pub fn include(&mut self, p: [f32; 3]) {
22 for ((low, high), value) in self.min.iter_mut().zip(self.max.iter_mut()).zip(p) {
23 *low = low.min(value);
24 *high = high.max(value);
25 }
26 }
27
28 pub fn from_points(points: &[[f32; 3]]) -> Self {
29 let mut bounds = Self::empty();
30 for p in points {
31 bounds.include(*p);
32 }
33 bounds
34 }
35
36 pub fn center(self) -> [f32; 3] {
37 if !self.min[0].is_finite() {
38 return [0.0; 3];
39 }
40 scale(add(self.min, self.max), 0.5)
41 }
42
43 pub fn radius(self) -> f32 {
44 if !self.min[0].is_finite() {
45 return 0.0;
46 }
47 let half = scale(sub(self.max, self.min), 0.5);
48 crate::math::length(half)
49 }
50
51 pub fn extent(self) -> f32 {
52 if !self.min[0].is_finite() {
53 return 0.0;
54 }
55 let span = sub(self.max, self.min);
56 span[0].max(span[1]).max(span[2])
57 }
58}
59
60#[derive(Clone, Debug)]
61pub struct Mesh {
62 pub positions: Vec<[f32; 3]>,
63 pub normals: Vec<[f32; 3]>,
64 pub tangents: Vec<[f32; 4]>,
65 pub uvs: Vec<[f32; 2]>,
66 pub indices: Vec<u32>,
67 pub bounds: Bounds,
68}
69
70impl Mesh {
71 pub fn new(
72 positions: Vec<[f32; 3]>,
73 mut normals: Vec<[f32; 3]>,
74 mut tangents: Vec<[f32; 4]>,
75 mut uvs: Vec<[f32; 2]>,
76 indices: Vec<u32>,
77 ) -> Self {
78 let count = positions.len();
79 normals.resize(count, [0.0, 0.0, 1.0]);
80 uvs.resize(count, [0.0, 0.0]);
81 let bounds = Bounds::from_points(&positions);
82 let indices = indices
83 .chunks_exact(3)
84 .filter(|tri| tri.iter().all(|index| (*index as usize) < count))
85 .flatten()
86 .copied()
87 .collect::<Vec<_>>();
88 if tangents.len() != count {
89 tangents = tangents_from(&positions, &normals, &uvs, &indices);
90 }
91 Self {
92 positions,
93 normals,
94 tangents,
95 uvs,
96 indices,
97 bounds,
98 }
99 }
100
101 pub fn triangle_count(&self) -> usize {
102 self.indices.len() / 3
103 }
104
105 pub fn bytes(&self) -> Vec<u8> {
106 let mut out = Vec::new();
107 push_u32(&mut out, self.positions.len() as u32);
108 push_u32(&mut out, self.indices.len() as u32);
109 for p in &self.positions {
110 push_f32s(&mut out, p);
111 }
112 for n in &self.normals {
113 push_f32s(&mut out, n);
114 }
115 for t in &self.tangents {
116 push_f32s(&mut out, t);
117 }
118 for uv in &self.uvs {
119 push_f32s(&mut out, uv);
120 }
121 for index in &self.indices {
122 push_u32(&mut out, *index);
123 }
124 out
125 }
126}
127
128fn push_u32(out: &mut Vec<u8>, value: u32) {
129 out.extend_from_slice(&value.to_le_bytes());
130}
131
132fn push_f32s(out: &mut Vec<u8>, values: &[f32]) {
133 for value in values {
134 out.extend_from_slice(&value.to_le_bytes());
135 }
136}
137
138pub(crate) struct Builder {
139 positions: Vec<[f32; 3]>,
140 normals: Vec<[f32; 3]>,
141 uvs: Vec<[f32; 2]>,
142 indices: Vec<u32>,
143}
144
145impl Builder {
146 pub(crate) fn new() -> Self {
147 Self {
148 positions: Vec::new(),
149 normals: Vec::new(),
150 uvs: Vec::new(),
151 indices: Vec::new(),
152 }
153 }
154
155 pub(crate) fn vertex(&mut self, position: [f32; 3], normal: [f32; 3], uv: [f32; 2]) -> u32 {
156 let index = self.positions.len() as u32;
157 self.positions.push(position);
158 self.normals.push(normalize(normal));
159 self.uvs.push(uv);
160 index
161 }
162
163 pub(crate) fn tri(&mut self, a: u32, b: u32, c: u32) {
164 if a == b || b == c || c == a {
165 return;
166 }
167 let pa = self.positions[a as usize];
168 let pb = self.positions[b as usize];
169 let pc = self.positions[c as usize];
170 let face = cross(sub(pb, pa), sub(pc, pa));
171 if crate::math::length(face) <= 1e-12 {
172 return;
173 }
174 let average = normalize(add(
175 add(self.normals[a as usize], self.normals[b as usize]),
176 self.normals[c as usize],
177 ));
178 if dot(face, average) < 0.0 {
179 self.indices.extend([a, c, b]);
180 } else {
181 self.indices.extend([a, b, c]);
182 }
183 }
184
185 pub(crate) fn build(self) -> Mesh {
186 Mesh::new(
187 self.positions,
188 self.normals,
189 Vec::new(),
190 self.uvs,
191 self.indices,
192 )
193 }
194}
195
196pub(crate) fn weld(mesh: Mesh, quantum: f32) -> Mesh {
197 let quantum = quantum.max(1e-7);
198 let mut key_of: Vec<(i32, i32, i32)> = Vec::with_capacity(mesh.positions.len());
199 let mut map: std::collections::BTreeMap<(i32, i32, i32), u32> =
200 std::collections::BTreeMap::new();
201 let mut remap = vec![0u32; mesh.positions.len()];
202 let mut positions = Vec::new();
203 let mut normals = Vec::new();
204 let mut uvs = Vec::new();
205 for (index, position) in mesh.positions.iter().enumerate() {
206 let key = (
207 (position[0] / quantum).round() as i32,
208 (position[1] / quantum).round() as i32,
209 (position[2] / quantum).round() as i32,
210 );
211 key_of.push(key);
212 if let Some(kept) = map.get(&key) {
213 remap[index] = *kept;
214 } else {
215 let kept = positions.len() as u32;
216 map.insert(key, kept);
217 remap[index] = kept;
218 positions.push(*position);
219 normals.push(mesh.normals[index]);
220 uvs.push(mesh.uvs[index]);
221 }
222 }
223 let _ = key_of;
224 let indices = mesh
225 .indices
226 .chunks_exact(3)
227 .filter_map(|tri| {
228 let a = remap[tri[0] as usize];
229 let b = remap[tri[1] as usize];
230 let c = remap[tri[2] as usize];
231 (a != b && b != c && c != a).then_some([a, b, c])
232 })
233 .flatten()
234 .collect::<Vec<_>>();
235 Mesh::new(positions, normals, Vec::new(), uvs, indices)
236}
237
238fn tangents_from(
239 positions: &[[f32; 3]],
240 normals: &[[f32; 3]],
241 uvs: &[[f32; 2]],
242 indices: &[u32],
243) -> Vec<[f32; 4]> {
244 let mut along_u = vec![[0.0; 3]; positions.len()];
245 let mut along_v = vec![[0.0; 3]; positions.len()];
246 for tri in indices.chunks_exact(3) {
247 let (i0, i1, i2) = (tri[0] as usize, tri[1] as usize, tri[2] as usize);
248 let (p0, p1, p2) = (positions[i0], positions[i1], positions[i2]);
249 let (w0, w1, w2) = (uvs[i0], uvs[i1], uvs[i2]);
250 let edge1 = sub(p1, p0);
251 let edge2 = sub(p2, p0);
252 let du1 = w1[0] - w0[0];
253 let du2 = w2[0] - w0[0];
254 let dv1 = w1[1] - w0[1];
255 let dv2 = w2[1] - w0[1];
256 let denom = du1 * dv2 - du2 * dv1;
257 let factor = if denom.abs() < 1e-12 {
258 0.0
259 } else {
260 1.0 / denom
261 };
262 let sdir = scale(
263 [
264 dv2 * edge1[0] - dv1 * edge2[0],
265 dv2 * edge1[1] - dv1 * edge2[1],
266 dv2 * edge1[2] - dv1 * edge2[2],
267 ],
268 factor,
269 );
270 let tdir = scale(
271 [
272 du1 * edge2[0] - du2 * edge1[0],
273 du1 * edge2[1] - du2 * edge1[1],
274 du1 * edge2[2] - du2 * edge1[2],
275 ],
276 factor,
277 );
278 for index in [i0, i1, i2] {
279 along_u[index] = add(along_u[index], sdir);
280 along_v[index] = add(along_v[index], tdir);
281 }
282 }
283 along_u
284 .iter()
285 .zip(along_v.iter())
286 .zip(normals.iter())
287 .map(|((sdir, tdir), normal)| {
288 let normal = normalize(*normal);
289 let mut tangent = sub(*sdir, scale(normal, dot(normal, *sdir)));
290 if crate::math::length(tangent) <= 1e-8 {
291 let axis = if normal[0].abs() < 0.9 {
292 [1.0, 0.0, 0.0]
293 } else {
294 [0.0, 1.0, 0.0]
295 };
296 tangent = sub(axis, scale(normal, dot(normal, axis)));
297 }
298 let tangent = normalize(tangent);
299 let handed = if dot(cross(normal, tangent), *tdir) < 0.0 {
300 -1.0
301 } else {
302 1.0
303 };
304 [tangent[0], tangent[1], tangent[2], handed]
305 })
306 .collect()
307}