MVN 모델이 애니메이션 통해 움직이는 센서들의 데이터를 VRM 모델에 전송하여 적용하는 테스트 진행

XsensJointManager로

Debugger에서 타겟 아바타로 전환하는 함수 옮김.

XsensJointManager 통해서 XsensJoint 만들고 해당 Bone계층 Transform에 넣어줌.

JointEditor 순서대로 만들고 forward up 반영해서 quaternion 적용

매 프레임 각속도, 가속도, 절대위치(world pos) 전송하는 소스코드 작성

SkinnedMEshRenderer에서 RootBone 세부 설정이 불가하기 떄문에

코드에서 직접 세부 배치(이름 동일해야 작동함)

#if UNITY_EDITOR_64
using UnityEngine;
using UnityEditor;

public class BoneRebinder : EditorWindow
{
    SkinnedMeshRenderer targetMesh;
    Transform targetRoot;

    [MenuItem("Tools/Rebind Skinned Mesh Bones")]
    static void Init()
    {
        GetWindow<BoneRebinder>("Rebind Bones");
    }

    void OnGUI()
    {
        targetMesh = (SkinnedMeshRenderer)EditorGUILayout.ObjectField("Target Mesh", targetMesh, typeof(SkinnedMeshRenderer), true);
        targetRoot = (Transform)EditorGUILayout.ObjectField("Target Root Bone", targetRoot, typeof(Transform), true);

        if (GUILayout.Button("Rebind") && targetMesh != null && targetRoot != null)
        {
            Transform[] newBones = new Transform[targetMesh.bones.Length];
            for (int i = 0; i < targetMesh.bones.Length; i++)
            {
                Debug.Log(targetMesh.bones[i].name);
                var boneName = targetMesh.bones[i].name;
                var newBone = FindChildByName(targetRoot, boneName);
                if (newBone != null)
                {
                    newBones[i] = newBone;
                }
                else
                {
                    Debug.LogWarning($"Bone not found: {boneName}");
                }
            }

            targetMesh.bones = newBones;
            Debug.Log("Bones Rebound");
        }
    }

    Transform FindChildByName(Transform parent, string name)
    {
        foreach (Transform t in parent.GetComponentsInChildren<Transform>(true))
        {
            if (t.name == name) return t;
        }

        return null;
    }
}
#endif

위 Skinned Mesh Bone Rebinder 사용 시 기존 모델에 BonePos를 지속적으로 참조하기 때문에

BonePos 리셋을 시켜주기 위함

(정상 작동하지 않을 시 Amature(모델 Root 본)에 넣고 GameObject Reset 후 Y 값만 0으로 변경 뒤 재시도)

#if UNITY_EDITOR_64
using UnityEngine;
using UnityEditor;

public class RebindPoseFixer : EditorWindow
{
    SkinnedMeshRenderer targetMesh;
    Transform targetRoot;

    [MenuItem("Tools/Fix Skinned Mesh BindPose")]
    static void Init()
    {
        GetWindow<RebindPoseFixer>("Fix BindPose");
    }

    void OnGUI()
    {
        //targetMesh
        targetMesh = (SkinnedMeshRenderer)EditorGUILayout.ObjectField("Target Mesh", targetMesh, typeof(SkinnedMeshRenderer), true);
        targetRoot = (Transform)EditorGUILayout.ObjectField("Target Root Bone", targetRoot, typeof(Transform), true);

        if (GUILayout.Button("Fix Bind Pose") && targetMesh != null && targetRoot != null)
        {
            Transform[] newBones = new Transform[targetMesh.bones.Length];
            Matrix4x4[] bindPoses = new Matrix4x4[targetMesh.bones.Length];

            for (int i = 0; i < targetMesh.bones.Length; i++)
            {
                var boneName = targetMesh.bones[i].name;
                var newBone = FindChildByName(targetRoot, boneName);
                if (newBone != null)
                {
                    newBones[i] = newBone;
                    bindPoses[i] = newBone.worldToLocalMatrix * targetMesh.transform.localToWorldMatrix;
                }
                else
                {
                    Debug.LogWarning($"Bone not found: {boneName}");
                }
            }

            targetMesh.bones = newBones;
            targetMesh.sharedMesh.bindposes = bindPoses;
            Debug.Log("Bind pose updated!");
        }
    }

    Transform FindChildByName(Transform parent, string name)
    {
        foreach (Transform t in parent.GetComponentsInChildren<Transform>(true))
        {
            if (t.name == name) return t;
        }
        return null;
    }
}
#endif