feat: Refactor code structure and enhance functionality across multiple crates
This commit is contained in:
@@ -7,4 +7,5 @@ edition = "2021"
|
||||
msh-core = { path = "../msh-core" }
|
||||
|
||||
[dev-dependencies]
|
||||
common = { path = "../common" }
|
||||
nres = { path = "../nres" }
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use msh_core::Model;
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub const DEFAULT_UV_SCALE: f32 = 1024.0;
|
||||
|
||||
@@ -11,21 +12,24 @@ pub struct RenderVertex {
|
||||
#[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.vertices.len() / 3
|
||||
self.indices.len() / 3
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds an expanded triangle list for a specific LOD/group pair.
|
||||
///
|
||||
/// The output is suitable for simple `glDrawArrays(GL_TRIANGLES, ...)` paths.
|
||||
/// 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 {
|
||||
@@ -49,36 +53,62 @@ pub fn build_render_mesh(model: &Model, lod: usize, group: usize) -> RenderMesh
|
||||
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 {
|
||||
continue;
|
||||
batch_valid = false;
|
||||
break;
|
||||
};
|
||||
let Some(pos) = model.positions.get(final_idx) else {
|
||||
continue;
|
||||
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,
|
||||
});
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,23 +1,10 @@
|
||||
use super::*;
|
||||
use common::collect_files_recursive;
|
||||
use msh_core::parse_model_payload;
|
||||
use nres::Archive;
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
fn collect_files_recursive(root: &Path, out: &mut Vec<PathBuf>) {
|
||||
let Ok(entries) = fs::read_dir(root) else {
|
||||
return;
|
||||
};
|
||||
for entry in entries.flatten() {
|
||||
let path = entry.path();
|
||||
if path.is_dir() {
|
||||
collect_files_recursive(&path, out);
|
||||
} else if path.is_file() {
|
||||
out.push(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn nres_test_files() -> Vec<PathBuf> {
|
||||
let root = Path::new(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("..")
|
||||
@@ -71,12 +58,20 @@ fn build_render_mesh_for_real_models() {
|
||||
)
|
||||
});
|
||||
let mesh = build_render_mesh(&model, 0, 0);
|
||||
if !mesh.vertices.is_empty() {
|
||||
if !mesh.indices.is_empty() {
|
||||
meshes_non_empty += 1;
|
||||
}
|
||||
if compute_bounds_for_mesh(&mesh.vertices).is_some() {
|
||||
bounds_non_empty += 1;
|
||||
}
|
||||
for &index in &mesh.indices {
|
||||
assert!(
|
||||
usize::from(index) < mesh.vertices.len(),
|
||||
"index out of bounds for '{}' in {}",
|
||||
entry.meta.name,
|
||||
archive_path.display()
|
||||
);
|
||||
}
|
||||
for vertex in &mesh.vertices {
|
||||
assert!(
|
||||
vertex.uv0[0].is_finite() && vertex.uv0[1].is_finite(),
|
||||
@@ -189,6 +184,7 @@ fn build_render_mesh_handles_empty_slot_model() {
|
||||
|
||||
let mesh = build_render_mesh(&model, 0, 0);
|
||||
assert!(mesh.vertices.is_empty());
|
||||
assert!(mesh.indices.is_empty());
|
||||
assert_eq!(mesh.batch_count, 0);
|
||||
assert_eq!(mesh.triangle_count(), 0);
|
||||
}
|
||||
@@ -225,9 +221,36 @@ fn build_render_mesh_supports_multi_node_and_uv_scaling() {
|
||||
let mesh = build_render_mesh(&model, 0, 0);
|
||||
assert_eq!(mesh.batch_count, 2);
|
||||
assert_eq!(mesh.vertices.len(), 6);
|
||||
assert_eq!(mesh.indices, vec![0, 1, 2, 3, 4, 5]);
|
||||
assert_eq!(mesh.triangle_count(), 2);
|
||||
assert_eq!(mesh.vertices[0].uv0, [1.0, -1.0]);
|
||||
assert_eq!(mesh.vertices[1].uv0, [0.5, 0.25]);
|
||||
assert_eq!(mesh.vertices[2].uv0, [0.0, 0.0]);
|
||||
assert_eq!(mesh.vertices[3].uv0, [1.0, 1.0]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_render_mesh_deduplicates_shared_vertices() {
|
||||
let model = msh_core::Model {
|
||||
node_stride: 38,
|
||||
node_count: 1,
|
||||
nodes_raw: nodes_with_slot_refs(&[Some(0)]),
|
||||
slots: vec![slot(0, 1)],
|
||||
positions: vec![
|
||||
[0.0, 0.0, 0.0],
|
||||
[1.0, 0.0, 0.0],
|
||||
[0.0, 1.0, 0.0],
|
||||
[1.0, 1.0, 0.0],
|
||||
],
|
||||
normals: None,
|
||||
uv0: None,
|
||||
indices: vec![0, 1, 2, 2, 1, 3],
|
||||
batches: vec![batch(0, 6, 0)],
|
||||
node_names: None,
|
||||
};
|
||||
|
||||
let mesh = build_render_mesh(&model, 0, 0);
|
||||
assert_eq!(mesh.vertices.len(), 4);
|
||||
assert_eq!(mesh.indices, vec![0, 1, 2, 2, 1, 3]);
|
||||
assert_eq!(mesh.triangle_count(), 2);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user