bevy_vrm1 0.2.1

Allows you to use VRM and VRMA in Bevy
Documentation
//! This module handles humanoid bones.
//! Refer to [here](https://docs.unity3d.com/ja/2019.4/ScriptReference/HumanBodyBones.html) for the list of humanoid bones.
//!
//! After the VRM(A) is loaded, marker components are inserted for each bone.
//! For example, the entity of the hips bone will have [`Hips`] inserted.
//! Additionally, a component that holds the entity will be inserted into the VRM(A) entity.
//!
//! The setup of these is done after all bones have been spawned, so there may be a slight delay.

mod bones;

use crate::prelude::*;
use crate::system_param::child_searcher::ChildSearcher;
use crate::vrm::gltf::extensions::VrmNode;
use crate::vrm::humanoid_bone::bones::BonesPlugin;
use crate::vrm::{BoneRestGlobalTransform, BoneRestTransform, VrmBone};
use bevy::app::{App, Plugin, Update};
use bevy::asset::{Assets, Handle};
use bevy::gltf::GltfNode;
use bevy::platform::collections::HashMap;
use bevy::prelude::*;
use serde::{Deserialize, Serialize};

pub mod prelude {
    pub use crate::vrm::humanoid_bone::bones::*;
}

#[derive(Component, Deref, Reflect, Default)]
pub(crate) struct HumanoidBoneRegistry(HashMap<VrmBone, Name>);

impl HumanoidBoneRegistry {
    pub fn new(
        bones: &HashMap<String, VrmNode>,
        node_assets: &Assets<GltfNode>,
        nodes: &[Handle<GltfNode>],
    ) -> Self {
        Self(
            bones
                .iter()
                .filter_map(|(name, target_node)| {
                    let node_handle = nodes.get(target_node.node)?;
                    let node = node_assets.get(node_handle)?;
                    Some((VrmBone(name.clone()), Name::new(node.name.clone())))
                })
                .collect(),
        )
    }
}

pub(super) struct VrmHumanoidBonePlugin;

impl Plugin for VrmHumanoidBonePlugin {
    fn build(
        &self,
        app: &mut App,
    ) {
        app.register_type::<HumanoidBonesAttached>()
            .register_type::<HumanoidBoneRegistry>()
            .add_plugins(BonesPlugin)
            .add_systems(Update, setup_bones);
    }
}

#[derive(Component, Reflect, Serialize, Deserialize)]
pub struct HumanoidBonesAttached;

macro_rules! insert_bone {
    (
        $commands: expr,
        $vrm_entity: expr,
        $bone_entity: expr,
        $bone_name: expr,
        $($bone: ident),+$(,)?
    ) => {

        match $bone_name.0.to_uppercase(){
            $(
                x if x == stringify!($bone).to_uppercase() => {
                    paste::paste!{
                        $commands.entity($vrm_entity).insert([<$bone BoneEntity>]($bone_entity));
                    }
                    $commands.entity($bone_entity).insert($bone);
                }
            )+
            _ => {

            }
        }
    };
}

fn setup_bones(
    mut commands: Commands,
    searcher: ChildSearcher,
    vrm: Query<(Entity, &HumanoidBoneRegistry), Without<HumanoidBonesAttached>>,
    transforms: Query<(&Transform, &GlobalTransform)>,
) {
    for (vrm_entity, humanoid_bones) in vrm.iter() {
        if !searcher.has_been_spawned_all_bones(vrm_entity, humanoid_bones) {
            continue;
        }

        for (bone, name) in humanoid_bones.iter() {
            let Some(bone_entity) = searcher.find_from_name(vrm_entity, name.as_str()) else {
                continue;
            };
            let Ok((tf, gtf)) = transforms.get(bone_entity) else {
                continue;
            };
            commands.entity(bone_entity).insert((
                bone.clone(),
                BoneRestTransform(*tf),
                BoneRestGlobalTransform(*gtf),
            ));
            insert_bone!(
                commands,
                vrm_entity,
                bone_entity,
                bone,
                Hips,
                RightRingProximal,
                RightThumbDistal,
                RightRingIntermediate,
                RightUpperArm,
                LeftIndexProximal,
                LeftUpperLeg,
                LeftFoot,
                LeftIndexDistal,
                LeftThumbMetacarpal,
                RightLowerArm,
                LeftMiddleDistal,
                RightUpperLeg,
                LeftToes,
                LeftThumbDistal,
                RightShoulder,
                RightThumbMetacarpal,
                Spine,
                LeftLowerLeg,
                LeftShoulder,
                LeftUpperArm,
                UpperChest,
                RightToes,
                RightIndexDistal,
                LeftMiddleProximal,
                LeftRingProximal,
                LeftRingDistal,
                LeftThumbProximal,
                LeftIndexIntermediate,
                LeftLittleProximal,
                LeftLittleDistal,
                RightHand,
                RightLittleProximal,
                LeftRingIntermediate,
                RightIndexIntermediate,
                Chest,
                LeftHand,
                RightLittleIntermediate,
                RightFoot,
                RightLowerLeg,
                LeftLittleIntermediate,
                LeftLowerArm,
                RightLittleDistal,
                RightMiddleIntermediate,
                RightMiddleProximal,
                RightThumbProximal,
                Neck,
                Jaw,
                Head,
                LeftEye,
                RightEye,
                LeftMiddleIntermediate,
                RightRingDistal,
                RightIndexProximal,
                RightMiddleDistal,
            );
            commands.entity(vrm_entity).insert(HumanoidBonesAttached);
        }
    }
}