use crate::resources::mesh_store::MeshId;
use crate::resources::SkinWeights;
use crate::runtime::context::RuntimeStepContext;
use crate::runtime::output::{SkinnedMeshUpdate, SkinnedPoseUpdate};
use crate::runtime::plugin::{RuntimePlugin, phase};
use super::skeleton::{JointMatrices, Pose, Skeleton, apply_skin};
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum SkinningPath {
Cpu,
Gpu,
}
impl Default for SkinningPath {
fn default() -> Self {
SkinningPath::Cpu
}
}
pub struct SkeletonPlugin {
pub skeleton: Skeleton,
pub mesh_id: MeshId,
pub path: SkinningPath,
cpu_positions: Vec<[f32; 3]>,
cpu_normals: Vec<[f32; 3]>,
skin_weights: SkinWeights,
}
impl SkeletonPlugin {
pub fn new(
skeleton: Skeleton,
mesh_id: MeshId,
positions: Vec<[f32; 3]>,
normals: Vec<[f32; 3]>,
skin_weights: SkinWeights,
) -> Self {
Self {
skeleton,
mesh_id,
path: SkinningPath::default(),
cpu_positions: positions,
cpu_normals: normals,
skin_weights,
}
}
pub fn with_path(mut self, path: SkinningPath) -> Self {
self.path = path;
self
}
}
impl RuntimePlugin for SkeletonPlugin {
fn priority(&self) -> i32 {
phase::POST_SIM
}
fn step(&mut self, ctx: &mut RuntimeStepContext<'_>) {
let Some(pose) = ctx.resources.get::<Pose>() else { return };
let matrices = JointMatrices::compute(&self.skeleton, pose);
match self.path {
SkinningPath::Cpu => {
let (positions, normals) = apply_skin(
&self.cpu_positions,
&self.cpu_normals,
&self.skin_weights,
&matrices,
);
ctx.output.skinned_mesh_updates.push(SkinnedMeshUpdate {
mesh_id: self.mesh_id,
positions,
normals,
});
}
SkinningPath::Gpu => {
let joint_matrices: Vec<glam::Mat4> = matrices
.as_slice()
.iter()
.map(|m| glam::Mat4::from(*m))
.collect();
ctx.output.skinned_pose_updates.push(SkinnedPoseUpdate {
mesh_id: self.mesh_id,
instance_id: 0,
joint_matrices,
});
}
}
}
}