pub struct SkinnedMeshSystem { /* private fields */ }Expand description
Computes per-joint skinning matrices for skinned meshes.
This system defers to TransformSystem for cached world matrices.
For each SkinnedMeshComponent, it computes (mesh-local) skin matrices:
$$ SkinMat_j = inverse(M_meshWorld) * M_jointWorld(j) * IBM(j) $$
so the GPU can skin in mesh-local space, then apply the instance model matrix as usual.
Implementations§
Source§impl SkinnedMeshSystem
impl SkinnedMeshSystem
pub fn new() -> Self
pub fn register_skin_instance_joints( &mut self, gltf_component: ComponentId, skin_id: SkinId, joints: Vec<Option<ComponentId>>, )
Sourcepub fn instance_joints_for_skin(
&self,
gltf_component: ComponentId,
skin_id: SkinId,
) -> Option<&[Option<ComponentId>]>
pub fn instance_joints_for_skin( &self, gltf_component: ComponentId, skin_id: SkinId, ) -> Option<&[Option<ComponentId>]>
Read-only access to the resolved joint transform ComponentIds for a particular (GLTFComponent instance, SkinId) pair.
The returned slice is in the same order as VisualWorld::skin(skin_id).joint_node_indices.
Examples found in repository?
examples/vtuber-joints-example.rs (line 724)
693fn collect_joint_transforms(
694 world: &engine::ecs::World,
695 skinned_mesh: &engine::ecs::system::SkinnedMeshSystem,
696 visuals: &engine::graphics::VisualWorld,
697 gltf_component: engine::ecs::ComponentId,
698 root: engine::ecs::ComponentId,
699) -> Vec<(usize, engine::ecs::ComponentId)> {
700 fn find_first_skin_id_in_subtree(
701 world: &engine::ecs::World,
702 root: engine::ecs::ComponentId,
703 ) -> Option<engine::graphics::SkinId> {
704 let mut stack = vec![root];
705 while let Some(node) = stack.pop() {
706 if let Some(sm) = world.get_component_by_id_as::<SkinnedMeshComponent>(node) {
707 if let Some(id) = sm.skin_id {
708 return Some(id);
709 }
710 }
711 for &ch in world.children_of(node) {
712 stack.push(ch);
713 }
714 }
715 None
716 }
717
718 let Some(skin_id) = find_first_skin_id_in_subtree(world, root) else {
719 return Vec::new();
720 };
721 let Some(skin) = visuals.skin(skin_id) else {
722 return Vec::new();
723 };
724 let Some(joints) = skinned_mesh.instance_joints_for_skin(gltf_component, skin_id) else {
725 return Vec::new();
726 };
727
728 let joint_count = skin.joint_count().min(joints.len());
729 let mut out: Vec<(usize, engine::ecs::ComponentId)> = Vec::with_capacity(joint_count);
730 for i in 0..joint_count {
731 let Some(joint_tx) = joints[i] else {
732 continue;
733 };
734 let node_index = skin.joint_node_indices[i];
735 out.push((node_index, joint_tx));
736 }
737
738 out
739}Sourcepub fn transform_subtree_changed(&mut self, world: &World, root: ComponentId)
pub fn transform_subtree_changed(&mut self, world: &World, root: ComponentId)
Notify the system that a transform subtree changed.
This walks the subtree and marks any skins referencing affected joint transforms dirty.
Trait Implementations§
Source§impl Debug for SkinnedMeshSystem
impl Debug for SkinnedMeshSystem
Source§impl Default for SkinnedMeshSystem
impl Default for SkinnedMeshSystem
Source§fn default() -> SkinnedMeshSystem
fn default() -> SkinnedMeshSystem
Returns the “default value” for a type. Read more
Source§impl System for SkinnedMeshSystem
impl System for SkinnedMeshSystem
fn tick( &mut self, world: &mut World, visuals: &mut VisualWorld, _input: &InputState, _dt_sec: f32, )
Auto Trait Implementations§
impl Freeze for SkinnedMeshSystem
impl RefUnwindSafe for SkinnedMeshSystem
impl Send for SkinnedMeshSystem
impl Sync for SkinnedMeshSystem
impl Unpin for SkinnedMeshSystem
impl UnsafeUnpin for SkinnedMeshSystem
impl UnwindSafe for SkinnedMeshSystem
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Convert
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can
then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Convert
Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be
further downcast into Rc<ConcreteType> where ConcreteType implements Trait.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
Convert
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
Convert
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.