use crate::compile::geometry::{
SkinnedLods, compile_mesh_payload, compile_skinned_mesh_payload_with_lods,
payload_joints_to_defs,
};
use concinnity_core::gfx::mesh_payload::{deserialise_skinned, deserialise_with_lods};
type LodAlternates = Vec<(f32, Vec<u16>)>;
type SkinnedImport = (
Vec<concinnity_core::gfx::mesh_payload::SkinnedVertex>,
Vec<u16>,
Vec<concinnity_core::components::SkeletonJoint>,
);
pub fn decode_mesh_from_parsed_glb(
doc: &crate::import::gltf_source::GltfDoc,
source: &str,
primitive_index: u32,
lod_levels: u32,
lod_distances: &[f32],
) -> Result<
(
Vec<concinnity_core::gfx::mesh_payload::Vertex>,
Vec<u16>,
LodAlternates,
),
String,
> {
let (vertex_data, indices) =
crate::import::glb::import_static_glb_primitive_from_doc(doc, source, primitive_index)?;
let args = serde_json::json!({
"vertices": vertex_data,
"indices": indices,
"lod_levels": lod_levels,
"lod_distances": lod_distances,
});
let payload = compile_mesh_payload(&args)?;
deserialise_with_lods(&payload)
}
pub struct InlineSkinnedImport {
pub vertices: Vec<crate::components::SkinnedVertexData>,
pub indices: Vec<u16>,
pub skeleton: Vec<concinnity_core::components::SkeletonJoint>,
pub morph_target_names: Vec<String>,
pub morph_deltas: Vec<crate::components::MorphDelta>,
}
pub fn decode_skinned_inline_from_parsed_glb(
doc: &crate::import::gltf_source::GltfDoc,
source: &str,
skin_index: u32,
) -> Result<InlineSkinnedImport, String> {
let imported = crate::import::glb::import_skinned_from_doc(doc, source, skin_index)?;
Ok(InlineSkinnedImport {
vertices: imported.vertices,
indices: imported.indices,
skeleton: imported.skeleton,
morph_target_names: imported.morph_target_names,
morph_deltas: imported.morph_deltas,
})
}
pub fn decode_skinned_from_parsed_glb(
doc: &crate::import::gltf_source::GltfDoc,
source: &str,
skin_index: u32,
) -> Result<SkinnedImport, String> {
let imported = crate::import::glb::import_skinned_from_doc(doc, source, skin_index)?;
let payload = compile_skinned_mesh_payload_with_lods(
&imported.vertices,
&imported.indices,
&imported.skeleton,
&imported.morph_target_names,
&imported.morph_deltas,
&SkinnedLods {
levels: 1,
distances: &[],
},
)?;
let (verts, idxs, payload_joints) = deserialise_skinned(&payload)?;
let skeleton = payload_joints_to_defs(payload_joints);
Ok((verts, idxs, skeleton))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::import::glb::test_fixtures::{
make_glb, parse, skinned_bin, skinned_glb, skinned_json, static_triangle_glb,
};
#[test]
fn decode_static_mesh_round_trips_a_triangle() {
let doc = parse(&static_triangle_glb());
let (vertices, indices, lods) =
decode_mesh_from_parsed_glb(&doc, "t.glb", 0, 1, &[]).expect("decode");
assert_eq!(vertices.len(), 3);
assert_eq!(indices.len(), 3);
assert!(lods.is_empty());
}
#[test]
fn decode_static_mesh_rejects_out_of_range_primitive() {
let doc = parse(&static_triangle_glb());
let err = decode_mesh_from_parsed_glb(&doc, "t.glb", 3, 1, &[]).unwrap_err();
assert!(err.contains("out of range"), "got: {err}");
}
#[test]
fn decode_skinned_mesh_round_trips_vertices_and_the_bind_skeleton() {
let doc = parse(&skinned_glb());
let (vertices, indices, skeleton) =
decode_skinned_from_parsed_glb(&doc, "s.glb", 0).expect("decode");
assert_eq!(vertices.len(), 3);
assert_eq!(indices, vec![0u16, 1, 2]);
assert_eq!(skeleton.len(), 2);
assert_eq!(skeleton[0].name, "root");
assert_eq!(skeleton[0].parent, -1);
assert_eq!(skeleton[1].name, "tip");
assert_eq!(skeleton[1].parent, 0);
assert_eq!(skeleton[1].translation, [0.0, 0.5, 0.0]);
assert_eq!(vertices[0].joints, [1, 1, 1, 1]);
assert_eq!(vertices[0].weights, [1.0, 0.0, 0.0, 0.0]);
assert_eq!(vertices[0].normal, [0.0, 0.0, 1.0]);
}
#[test]
fn decode_skinned_inline_keeps_the_authored_form() {
let doc = parse(&skinned_glb());
let m = decode_skinned_inline_from_parsed_glb(&doc, "s.glb", 0).expect("decode");
assert_eq!(m.vertices.len(), 3);
assert_eq!(m.indices, vec![0, 1, 2]);
assert_eq!(m.skeleton[0].name, "root");
assert_eq!(m.vertices[0].joints, [1, 1, 1, 1]);
assert!(m.morph_target_names.is_empty() && m.morph_deltas.is_empty());
}
#[test]
fn decode_skinned_mesh_surfaces_a_payload_compile_failure() {
let mut bin = skinned_bin();
bin[40..42].copy_from_slice(&9u16.to_le_bytes());
let doc = parse(&make_glb(&skinned_json(true, true, false), Some(&bin)));
let err = decode_skinned_from_parsed_glb(&doc, "s.glb", 0).unwrap_err();
assert_eq!(err, "SkinnedMesh index out of range in triangle 0");
}
#[test]
fn decode_skinned_mesh_rejects_a_file_with_no_skinned_node() {
let doc = parse(&static_triangle_glb());
let err = decode_skinned_from_parsed_glb(&doc, "t.glb", 0).unwrap_err();
assert!(
err.contains("no node with both a mesh and a skin"),
"got: {err}"
);
}
}