use glam::{Mat4, Vec3};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BoneTransform {
pub inv_bind_pose: [f32; 16],
pub current_pose: [f32; 16],
pub weight: f32,
}
#[inline(always)]
pub fn sdf_skinning(p: Vec3, bones: &[BoneTransform]) -> (Vec3, f32) {
if bones.is_empty() {
return (p, 1.0);
}
let mut result = Vec3::ZERO;
let mut total_weight = 0.0;
for bone in bones {
let w = bone.weight;
if w < 1e-6 {
continue;
}
let inv_current = mat4_from_array(&bone.current_pose);
let bind = mat4_from_array(&bone.inv_bind_pose);
let skinned = inv_current.transform_point3(p);
let rest_p = bind.transform_point3(skinned);
result += rest_p * w;
total_weight += w;
}
if total_weight > 1e-6 {
result /= total_weight;
} else {
result = p;
}
(result, 1.0)
}
const fn mat4_from_array(m: &[f32; 16]) -> Mat4 {
Mat4::from_cols_array(m)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_identity_skinning() {
let identity = [
1.0, 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,
];
let bone = BoneTransform {
inv_bind_pose: identity,
current_pose: identity,
weight: 1.0,
};
let p = Vec3::new(1.0, 2.0, 3.0);
let (q, _) = sdf_skinning(p, &[bone]);
assert!((q - p).length() < 1e-5);
}
}