libs/render (Renderer, SceneDraws, preview play) plus the Arcade sim/math it still draws through. Gameplay crates stay out.
102 lines
3.2 KiB
Rust
102 lines
3.2 KiB
Rust
//! Lossless SkinTokens augmentation -> engine parser/deformation gate.
|
|
|
|
use makepad_render::skin::{PoseBuffer, SkinnedModel, SKIN_VERTEX_FLOATS};
|
|
use makepad_gltf::{
|
|
augment_glb_skin, replace_glb_node_animations, write_glb_mesh, GlbAnimPath,
|
|
GlbNodeAnimChannel, GlbNodeAnimClip, GlbPrimitiveSkin, GlbSkinJoint,
|
|
};
|
|
|
|
fn positions(model: &SkinnedModel, clip: Option<(usize, f32)>) -> Vec<[f32; 3]> {
|
|
let pose = match clip {
|
|
Some((clip, time)) => {
|
|
let mut pose = PoseBuffer::new();
|
|
model.sample_clip(clip, time, &mut pose);
|
|
pose
|
|
}
|
|
None => model.rest_pose(),
|
|
};
|
|
let mut palette = Vec::new();
|
|
model.palette(&pose, &mut palette);
|
|
let mut packed = Vec::new();
|
|
model.skin_to_packed(&palette, &mut packed);
|
|
packed
|
|
.chunks_exact(SKIN_VERTEX_FLOATS)
|
|
.map(|vertex| [vertex[0], vertex[1], vertex[2]])
|
|
.collect()
|
|
}
|
|
|
|
fn assert_point(actual: [f32; 3], expected: [f32; 3]) {
|
|
for axis in 0..3 {
|
|
assert!(
|
|
(actual[axis] - expected[axis]).abs() < 2.0e-5,
|
|
"actual={actual:?}, expected={expected:?}"
|
|
);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn augmented_mesh_is_identity_at_bind_and_deforms_about_child_head() {
|
|
let source_positions = [
|
|
[0.0f32, 0.0, 0.0],
|
|
[1.0, 0.0, 0.0],
|
|
[0.0, 1.0, 0.0],
|
|
[0.0, 2.0, 0.0],
|
|
];
|
|
let source = write_glb_mesh(&source_positions, &[0, 1, 2, 1, 3, 2]);
|
|
let joints = [
|
|
GlbSkinJoint {
|
|
name: "root".into(),
|
|
parent: None,
|
|
global_translation: [0.0, 0.0, 0.0],
|
|
},
|
|
GlbSkinJoint {
|
|
name: "child".into(),
|
|
parent: Some(0),
|
|
global_translation: [0.0, 1.0, 0.0],
|
|
},
|
|
];
|
|
let rigged = augment_glb_skin(
|
|
&source,
|
|
&joints,
|
|
&[GlbPrimitiveSkin {
|
|
node: 0,
|
|
primitive: 0,
|
|
weights: vec![
|
|
1.0, 0.0, // v0 root
|
|
1.0, 0.0, // v1 root
|
|
0.0, 1.0, // v2 child
|
|
0.0, 1.0, // v3 child
|
|
],
|
|
}],
|
|
)
|
|
.unwrap();
|
|
let child_node = 2;
|
|
let animated = replace_glb_node_animations(
|
|
&rigged,
|
|
&[GlbNodeAnimClip {
|
|
name: "walk".into(),
|
|
channels: vec![GlbNodeAnimChannel {
|
|
node: child_node,
|
|
path: GlbAnimPath::Rotation,
|
|
times: vec![0.0, 1.0, 2.0],
|
|
values: vec![
|
|
0.0, 0.0, 0.0, 1.0,
|
|
0.0, 0.0, std::f32::consts::FRAC_1_SQRT_2,
|
|
std::f32::consts::FRAC_1_SQRT_2,
|
|
0.0, 0.0, 0.0, 1.0,
|
|
],
|
|
}],
|
|
}],
|
|
)
|
|
.unwrap();
|
|
let model = SkinnedModel::parse_glb(&animated).expect("engine accepts augmented character");
|
|
assert_eq!(model.joint_count(), 2);
|
|
assert_eq!(model.vertex_count(), 4);
|
|
for (actual, expected) in positions(&model, None).into_iter().zip(source_positions) {
|
|
assert_point(actual, expected);
|
|
}
|
|
let posed = positions(&model, Some((0, 1.0)));
|
|
assert_point(posed[0], [0.0, 0.0, 0.0]);
|
|
assert_point(posed[2], [0.0, 1.0, 0.0]);
|
|
assert_point(posed[3], [-1.0, 1.0, 0.0]);
|
|
}
|