use bytemuck::{Pod, Zeroable};
use crate::math::{Mat4, Quat, Vec3};
use crate::mesh::Mixed;
#[derive(Clone, Debug, Default, PartialEq)]
pub(crate) struct Rig {
joints: Vec<Joint>,
weights: Vec<Weighted>,
}
impl Rig {
pub(crate) fn new(joints: Vec<Joint>, weights: Vec<Weighted>) -> Self {
Self { joints, weights }
}
pub(crate) fn joints(&self) -> &[Joint] {
&self.joints
}
pub(crate) fn weights(&self) -> &[Weighted] {
&self.weights
}
pub(crate) fn skins(&self) -> bool {
!self.joints.is_empty() && !self.weights.is_empty()
}
pub(crate) fn bytes(&self) -> usize {
size_of_val(self.joints()) + size_of_val(self.weights())
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub(crate) struct Joint {
pub(crate) placed: Placed,
pub(crate) rest: Local,
pub(crate) bind: Mat4,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub(crate) enum Placed {
Under(u32),
Within(Mat4),
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub(crate) struct Local {
pub(crate) position: Vec3,
pub(crate) turn: Quat,
pub(crate) scale: Vec3,
}
impl Local {
pub(crate) fn new(position: Vec3, turn: Quat, scale: Vec3) -> Self {
Self {
position,
turn,
scale,
}
}
pub(crate) fn matrix(self) -> Mat4 {
Mat4::from_scale_rotation_translation(self.scale, self.turn, self.position)
}
pub(crate) fn mixed(self, other: Self, amount: f32) -> Self {
Self {
position: self.position.mixed(other.position, amount),
turn: self.turn.mixed(other.turn, amount),
scale: self.scale.mixed(other.scale, amount),
}
}
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)]
pub(crate) struct Weighted {
pub(crate) joints: [u32; 4],
pub(crate) weights: [f32; 4],
}
impl Weighted {
pub(crate) fn skinned(self, position: Vec3, palette: &[Mat4]) -> Vec3 {
let blend = self
.joints
.into_iter()
.zip(self.weights)
.fold(Mat4::ZERO, |blend, (joint, weight)| {
blend + palette[joint as usize] * weight
});
blend.transform_point3(position)
}
pub(crate) fn whole(joint: u32) -> Self {
Self {
joints: [joint, 0, 0, 0],
weights: [1.0, 0.0, 0.0, 0.0],
}
}
pub(crate) fn scaled(joints: [u32; 4], weights: [f32; 4]) -> Self {
let mut taken = Self { joints, weights };
let sum: f32 = taken.weights.iter().sum();
taken.weights = match sum > 0.0 {
true => taken.weights.map(|weight| weight / sum),
false => [1.0, 0.0, 0.0, 0.0],
};
taken
}
}