Skip to main content

pfx_geom/
lod.rs

1use crate::mesh::Mesh;
2use meshopt::{SimplifyOptions, VertexDataAdapter, simplify_with_attributes_and_locks};
3
4pub const NORMAL_WEIGHT: f32 = 0.5;
5pub const UV_WEIGHT: f32 = 1.0;
6pub const MIN_TRIANGLES: usize = 32;
7pub const MAX_LEVELS: usize = 8;
8
9#[derive(Clone, Debug)]
10pub struct Level {
11    pub mesh: Mesh,
12    pub error: f32,
13}
14
15pub fn chain(mesh: &Mesh) -> Vec<Level> {
16    let mut levels = vec![Level {
17        mesh: mesh.clone(),
18        error: 0.0,
19    }];
20    if mesh.indices.is_empty() {
21        return levels;
22    }
23    let positions: Vec<u8> = mesh
24        .positions
25        .iter()
26        .flat_map(|p| p.iter().flat_map(|v| v.to_le_bytes()))
27        .collect();
28    let Ok(adapter) = VertexDataAdapter::new(&positions, 12, 0) else {
29        return levels;
30    };
31    let attributes: Vec<f32> = mesh
32        .normals
33        .iter()
34        .zip(mesh.uvs.iter())
35        .flat_map(|(n, uv)| [n[0], n[1], n[2], uv[0], uv[1]])
36        .collect();
37    let weights = [
38        NORMAL_WEIGHT,
39        NORMAL_WEIGHT,
40        NORMAL_WEIGHT,
41        UV_WEIGHT,
42        UV_WEIGHT,
43    ];
44    let locks = vec![false; mesh.positions.len()];
45    let mut previous = mesh.triangle_count();
46    let mut target = previous / 2;
47    while levels.len() < MAX_LEVELS && target >= MIN_TRIANGLES {
48        let mut error = 0.0f32;
49        let indices = simplify_with_attributes_and_locks(
50            &mesh.indices,
51            &adapter,
52            &attributes,
53            &weights,
54            5 * std::mem::size_of::<f32>(),
55            &locks,
56            target * 3,
57            f32::MAX,
58            SimplifyOptions::ErrorAbsolute | SimplifyOptions::LockBorder,
59            Some(&mut error),
60        );
61        let triangles = indices.len() / 3;
62        if triangles == 0 || triangles * 10 > previous * 9 {
63            break;
64        }
65        let floor = levels.last().map_or(0.0, |level| level.error);
66        levels.push(Level {
67            mesh: compact(mesh, &indices),
68            error: error.max(floor),
69        });
70        previous = triangles;
71        target = triangles / 2;
72    }
73    levels
74}
75
76pub fn errors(levels: &[Level]) -> Vec<f32> {
77    levels.iter().map(|level| level.error).collect()
78}
79
80fn compact(mesh: &Mesh, indices: &[u32]) -> Mesh {
81    let mut remap = vec![u32::MAX; mesh.positions.len()];
82    let mut positions = Vec::new();
83    let mut normals = Vec::new();
84    let mut tangents = Vec::new();
85    let mut uvs = Vec::new();
86    let mut out = Vec::with_capacity(indices.len());
87    for &index in indices {
88        let slot = &mut remap[index as usize];
89        if *slot == u32::MAX {
90            *slot = positions.len() as u32;
91            positions.push(mesh.positions[index as usize]);
92            normals.push(mesh.normals[index as usize]);
93            tangents.push(mesh.tangents[index as usize]);
94            uvs.push(mesh.uvs[index as usize]);
95        }
96        out.push(*slot);
97    }
98    Mesh::new(positions, normals, tangents, uvs, out)
99}