use crate::BspInputMesh;
use lunar_math::Vec3;
pub fn load_gltf_meshes(path: &str) -> Result<Vec<BspInputMesh>, String> {
let (doc, buffers, _images) =
gltf::import(path).map_err(|error| format!("gltf load error for '{path}': {error}"))?;
let scene = doc
.default_scene()
.or_else(|| doc.scenes().next())
.ok_or_else(|| format!("gltf '{path}' has no scenes"))?;
let mut meshes: Vec<BspInputMesh> = Vec::new();
let identity = [
[1.0f32, 0.0, 0.0, 0.0],
[0.0, 1.0, 0.0, 0.0],
[0.0, 0.0, 1.0, 0.0],
[0.0, 0.0, 0.0, 1.0],
];
for node in scene.nodes() {
collect_node(&node, &identity, &buffers, &mut meshes);
}
Ok(meshes)
}
fn collect_node(
node: &gltf::Node,
parent_transform: &[[f32; 4]; 4],
buffers: &[gltf::buffer::Data],
meshes: &mut Vec<BspInputMesh>,
) {
let local = node.transform().matrix();
let world = mat4_mul(parent_transform, &local);
if let Some(mesh) = node.mesh() {
let area_id = parse_area_id(mesh.name().unwrap_or(""));
for prim in mesh.primitives() {
let reader = prim.reader(|buf| Some(&buffers[buf.index()]));
let positions: Vec<[f32; 3]> = match reader.read_positions() {
Some(iter) => iter.collect(),
None => continue,
};
let indices: Vec<u32> = match reader.read_indices() {
Some(iter) => iter.into_u32().collect(),
None => {
(0..positions.len() as u32).collect()
}
};
let vertices: Vec<Vec3> = positions
.iter()
.map(|p| transform_point(&world, Vec3::new(p[0], p[1], p[2])))
.collect();
if vertices.len() >= 3 && !indices.is_empty() {
meshes.push(BspInputMesh {
vertices,
indices,
area_id,
});
}
}
}
for child in node.children() {
collect_node(&child, &world, buffers, meshes);
}
}
fn parse_area_id(name: &str) -> Option<u32> {
let without_prefix = name.strip_prefix("area")?;
let end = without_prefix
.find(|c: char| !c.is_ascii_digit())
.unwrap_or(without_prefix.len());
if end == 0 {
return None;
}
without_prefix[..end].parse().ok()
}
fn transform_point(m: &[[f32; 4]; 4], p: Vec3) -> Vec3 {
let x = m[0][0] * p.x + m[1][0] * p.y + m[2][0] * p.z + m[3][0];
let y = m[0][1] * p.x + m[1][1] * p.y + m[2][1] * p.z + m[3][1];
let z = m[0][2] * p.x + m[1][2] * p.y + m[2][2] * p.z + m[3][2];
Vec3::new(x, y, z)
}
fn mat4_mul(a: &[[f32; 4]; 4], b: &[[f32; 4]; 4]) -> [[f32; 4]; 4] {
let mut out = [[0.0f32; 4]; 4];
for row in 0..4 {
for col in 0..4 {
for k in 0..4 {
out[row][col] += a[row][k] * b[k][col];
}
}
}
out
}