1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
|
use msh_core::Model;
use std::collections::HashMap;
pub const DEFAULT_UV_SCALE: f32 = 1024.0;
#[derive(Clone, Debug)]
pub struct RenderVertex {
pub position: [f32; 3],
pub uv0: [f32; 2],
}
#[derive(Clone, Debug)]
pub struct RenderMesh {
pub vertices: Vec<RenderVertex>,
pub indices: Vec<u16>,
pub batch_count: usize,
pub index_overflow: bool,
}
impl RenderMesh {
pub fn triangle_count(&self) -> usize {
self.indices.len() / 3
}
}
/// Builds an indexed triangle mesh for a specific LOD/group pair.
pub fn build_render_mesh(model: &Model, lod: usize, group: usize) -> RenderMesh {
let mut vertices = Vec::new();
let mut indices = Vec::new();
let mut index_remap: HashMap<usize, u16> = HashMap::new();
let mut batch_count = 0usize;
let mut index_overflow = false;
let uv0 = model.uv0.as_ref();
for node_index in 0..model.node_count {
let Some(slot_idx) = model.slot_index(node_index, lod, group) else {
continue;
};
let Some(slot) = model.slots.get(slot_idx) else {
continue;
};
let batch_start = usize::from(slot.batch_start);
let batch_end = batch_start.saturating_add(usize::from(slot.batch_count));
if batch_end > model.batches.len() {
continue;
}
for batch in &model.batches[batch_start..batch_end] {
let index_start = usize::try_from(batch.index_start).unwrap_or(usize::MAX);
let index_count = usize::from(batch.index_count);
let index_end = index_start.saturating_add(index_count);
if index_end > model.indices.len() || index_count < 3 {
continue;
}
let batch_out_start = indices.len();
let mut batch_valid = true;
for &idx in &model.indices[index_start..index_end] {
let final_idx_u64 = u64::from(batch.base_vertex).saturating_add(u64::from(idx));
let Ok(final_idx) = usize::try_from(final_idx_u64) else {
batch_valid = false;
break;
};
let Some(pos) = model.positions.get(final_idx) else {
batch_valid = false;
break;
};
let local_index = if let Some(&mapped) = index_remap.get(&final_idx) {
mapped
} else {
let Ok(mapped) = u16::try_from(vertices.len()) else {
index_overflow = true;
batch_valid = false;
break;
};
let uv = uv0
.and_then(|uvs| uvs.get(final_idx))
.copied()
.map(|packed| {
[
packed[0] as f32 / DEFAULT_UV_SCALE,
packed[1] as f32 / DEFAULT_UV_SCALE,
]
})
.unwrap_or([0.0, 0.0]);
vertices.push(RenderVertex {
position: *pos,
uv0: uv,
});
index_remap.insert(final_idx, mapped);
mapped
};
indices.push(local_index);
}
if !batch_valid {
indices.truncate(batch_out_start);
continue;
}
batch_count += 1;
}
}
RenderMesh {
vertices,
indices,
batch_count,
index_overflow,
}
}
pub fn compute_bounds(vertices: &[[f32; 3]]) -> Option<([f32; 3], [f32; 3])> {
compute_bounds_impl(vertices.iter().copied())
}
pub fn compute_bounds_for_mesh(vertices: &[RenderVertex]) -> Option<([f32; 3], [f32; 3])> {
compute_bounds_impl(vertices.iter().map(|v| v.position))
}
fn compute_bounds_impl<I>(mut positions: I) -> Option<([f32; 3], [f32; 3])>
where
I: Iterator<Item = [f32; 3]>,
{
let first = positions.next()?;
let mut min_v = first;
let mut max_v = first;
for pos in positions {
for i in 0..3 {
if pos[i] < min_v[i] {
min_v[i] = pos[i];
}
if pos[i] > max_v[i] {
max_v[i] = pos[i];
}
}
}
Some((min_v, max_v))
}
#[cfg(test)]
mod tests;
|