Skip to main content

pfx_geom/
mesh.rs

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}