316 lines
12 KiB
C#

using System;
using System.Collections.Generic;
using Unity.Mathematics;
using UnityEngine;
using System.Collections;
[System.Serializable]
public enum MotionApplicationScope
{
All, // 전신 적용
ExcludeFingersOnly, // 손가락만 제외
ExcludeHandsAndFingers // 손목 + 손가락 제외
}
public class OptitrackSkeletonAnimator_Mingle : MonoBehaviour
{
[Header("OptiTrack 설정")]
[Tooltip("OptiTrackStreamingClient가 포함된 오브젝트")]
public OptitrackStreamingClient StreamingClient;
[Tooltip("Motive의 스켈레톤 에셋 이름")]
public string SkeletonAssetName = "Skeleton1";
private Animator TargetAnimator;
[Header("모션 적용 범위")]
[Tooltip("모션 캡처 데이터를 적용할 범위 선택")]
public MotionApplicationScope motionScope = MotionApplicationScope.All;
private OptitrackSkeletonDefinition m_skeletonDef;
private Dictionary<string, HumanBodyBones> m_optitrackToHumanBoneMap;
private string previousSkeletonName;
[HideInInspector]
public bool isSkeletonFound = false;
private float updateInterval = 0.1f;
void Start()
{
TargetAnimator = GetComponent<Animator>();
if (TargetAnimator == null)
{
//Debug.LogError("이 게임오브젝트에서 Animator 컴포넌트를 찾을 수 없습니다.", this);
enabled = false;
return;
}
InitializeStreamingClient();
// StreamingClient 등록 추가
if (StreamingClient != null)
{
StreamingClient.RegisterSkeleton(this, this.SkeletonAssetName);
//Debug.Log($"[OptiTrack] 스켈레톤 '{SkeletonAssetName}'이(가) 등록되었습니다.");
}
InitializeBoneMapping();
// 주기적으로 스켈레톤 연결 상태를 확인하는 코루틴 시작
StartCoroutine(CheckSkeletonConnectionPeriodically());
}
void Update()
{
if (TargetAnimator == null)
return;
// StreamingClient 체크
if (StreamingClient == null)
{
InitializeStreamingClient();
return;
}
// 스켈레톤 이름이 변경되었을 때
if (previousSkeletonName != SkeletonAssetName)
{
// 새 스켈레톤 등록
StreamingClient.RegisterSkeleton(this, SkeletonAssetName);
//Debug.Log($"[OptiTrack] 새 스켈레톤 '{SkeletonAssetName}' 등록");
// 스켈레톤 정의 새로 가져오기
m_skeletonDef = StreamingClient.GetSkeletonDefinitionByName(SkeletonAssetName);
if (m_skeletonDef == null)
{
//Debug.LogWarning($"[OptiTrack] 스켈레톤 '{SkeletonAssetName}'을(를) 찾을 수 없습니다. Motive에서 올바른 스켈레톤 이름을 확인해주세요.", this);
previousSkeletonName = SkeletonAssetName; // 이름 업데이트
return;
}
//Debug.Log($"[OptiTrack] 스켈레톤 '{SkeletonAssetName}'을(를) 성공적으로 찾았습니다.", this);
previousSkeletonName = SkeletonAssetName; // 이름 업데이트
}
// 스켈레톤 정의가 없는 경우 체크
if (m_skeletonDef == null)
{
m_skeletonDef = StreamingClient.GetSkeletonDefinitionByName(SkeletonAssetName);
if (m_skeletonDef == null)
{
return;
}
}
// 최신 스켈레톤 상태 가져오기
OptitrackSkeletonState skelState = StreamingClient.GetLatestSkeletonState(m_skeletonDef.Id);
if (skelState == null)
{
//Debug.LogWarning($"[OptiTrack] 스켈레톤 '{SkeletonAssetName}'의 상태가 null입니다. Motive에 마커가 제대로 트래킹되고 있는지 확인해주세요.", this);
return;
}
// 각 본 업데이트
foreach (var bone in m_skeletonDef.Bones)
{
string boneName = bone.Name;
string optitrackBoneName = boneName.Contains("_") ? boneName.Substring(boneName.IndexOf('_') + 1) : boneName;
if (m_optitrackToHumanBoneMap.TryGetValue(optitrackBoneName, out HumanBodyBones humanBone))
{
// 모션 스코프에 따른 본 필터링
if (!ShouldApplyMotionToBone(optitrackBoneName))
continue;
Transform boneTransform = TargetAnimator.GetBoneTransform(humanBone);
if (boneTransform != null)
{
if (skelState.BonePoses.TryGetValue(bone.Id, out OptitrackPose bonePose))
{
// 위치는 항상 업데이트 (Hip 등 루트 본의 경우)
boneTransform.localPosition = bonePose.Position;
// 회전 업데이트
boneTransform.localRotation = bonePose.Orientation;
}
}
}
}
}
private void InitializeStreamingClient()
{
if (StreamingClient == null)
{
// 씬에서 OptitrackStreamingClient 찾기
StreamingClient = FindObjectOfType<OptitrackStreamingClient>();
if (StreamingClient == null)
{
//Debug.LogWarning("씬에서 OptiTrack Streaming Client를 찾을 수 없습니다. 다음 프레임에서 다시 시도합니다.", this);
}
else
{
Debug.Log("OptiTrack Streaming Client를 찾았습니다.", this);
}
}
}
private void InitializeBoneMapping()
{
m_optitrackToHumanBoneMap = new Dictionary<string, HumanBodyBones>();
if (TargetAnimator == null || !TargetAnimator.isHuman)
{
Debug.LogError("휴머노이드 아바타가 설정되지 않았습니다.", this);
return;
}
// OptiTrack 본 이름을 HumanBodyBones enum과 매핑
// 스켈레톤 에셋 이름을 제외한 기본 매핑 설정
SetupBoneNameMapping();
}
private void SetupBoneNameMapping()
{
// 기본 본 매핑 (스켈레톤 에셋 이름 없이)
m_optitrackToHumanBoneMap.Add("Hip", HumanBodyBones.Hips);
m_optitrackToHumanBoneMap.Add("Ab", HumanBodyBones.Spine);
m_optitrackToHumanBoneMap.Add("Chest", HumanBodyBones.Chest);
m_optitrackToHumanBoneMap.Add("Neck", HumanBodyBones.Neck);
m_optitrackToHumanBoneMap.Add("Head", HumanBodyBones.Head);
// 왼쪽 팔
m_optitrackToHumanBoneMap.Add("LShoulder", HumanBodyBones.LeftShoulder);
m_optitrackToHumanBoneMap.Add("LUArm", HumanBodyBones.LeftUpperArm);
m_optitrackToHumanBoneMap.Add("LFArm", HumanBodyBones.LeftLowerArm);
m_optitrackToHumanBoneMap.Add("LHand", HumanBodyBones.LeftHand);
// 오른쪽 팔
m_optitrackToHumanBoneMap.Add("RShoulder", HumanBodyBones.RightShoulder);
m_optitrackToHumanBoneMap.Add("RUArm", HumanBodyBones.RightUpperArm);
m_optitrackToHumanBoneMap.Add("RFArm", HumanBodyBones.RightLowerArm);
m_optitrackToHumanBoneMap.Add("RHand", HumanBodyBones.RightHand);
// 왼쪽 다리
m_optitrackToHumanBoneMap.Add("LThigh", HumanBodyBones.LeftUpperLeg);
m_optitrackToHumanBoneMap.Add("LShin", HumanBodyBones.LeftLowerLeg);
m_optitrackToHumanBoneMap.Add("LFoot", HumanBodyBones.LeftFoot);
m_optitrackToHumanBoneMap.Add("LToe", HumanBodyBones.LeftToes);
// 오른쪽 다리
m_optitrackToHumanBoneMap.Add("RThigh", HumanBodyBones.RightUpperLeg);
m_optitrackToHumanBoneMap.Add("RShin", HumanBodyBones.RightLowerLeg);
m_optitrackToHumanBoneMap.Add("RFoot", HumanBodyBones.RightFoot);
m_optitrackToHumanBoneMap.Add("RToe", HumanBodyBones.RightToes);
// 왼쪽 손가락들
m_optitrackToHumanBoneMap.Add("LThumb1", HumanBodyBones.LeftThumbProximal);
m_optitrackToHumanBoneMap.Add("LThumb2", HumanBodyBones.LeftThumbIntermediate);
m_optitrackToHumanBoneMap.Add("LThumb3", HumanBodyBones.LeftThumbDistal);
m_optitrackToHumanBoneMap.Add("LIndex1", HumanBodyBones.LeftIndexProximal);
m_optitrackToHumanBoneMap.Add("LIndex2", HumanBodyBones.LeftIndexIntermediate);
m_optitrackToHumanBoneMap.Add("LIndex3", HumanBodyBones.LeftIndexDistal);
m_optitrackToHumanBoneMap.Add("LMiddle1", HumanBodyBones.LeftMiddleProximal);
m_optitrackToHumanBoneMap.Add("LMiddle2", HumanBodyBones.LeftMiddleIntermediate);
m_optitrackToHumanBoneMap.Add("LMiddle3", HumanBodyBones.LeftMiddleDistal);
m_optitrackToHumanBoneMap.Add("LRing1", HumanBodyBones.LeftRingProximal);
m_optitrackToHumanBoneMap.Add("LRing2", HumanBodyBones.LeftRingIntermediate);
m_optitrackToHumanBoneMap.Add("LRing3", HumanBodyBones.LeftRingDistal);
m_optitrackToHumanBoneMap.Add("LPinky1", HumanBodyBones.LeftLittleProximal);
m_optitrackToHumanBoneMap.Add("LPinky2", HumanBodyBones.LeftLittleIntermediate);
m_optitrackToHumanBoneMap.Add("LPinky3", HumanBodyBones.LeftLittleDistal);
// 오른쪽 손가락들
m_optitrackToHumanBoneMap.Add("RThumb1", HumanBodyBones.RightThumbProximal);
m_optitrackToHumanBoneMap.Add("RThumb2", HumanBodyBones.RightThumbIntermediate);
m_optitrackToHumanBoneMap.Add("RThumb3", HumanBodyBones.RightThumbDistal);
m_optitrackToHumanBoneMap.Add("RIndex1", HumanBodyBones.RightIndexProximal);
m_optitrackToHumanBoneMap.Add("RIndex2", HumanBodyBones.RightIndexIntermediate);
m_optitrackToHumanBoneMap.Add("RIndex3", HumanBodyBones.RightIndexDistal);
m_optitrackToHumanBoneMap.Add("RMiddle1", HumanBodyBones.RightMiddleProximal);
m_optitrackToHumanBoneMap.Add("RMiddle2", HumanBodyBones.RightMiddleIntermediate);
m_optitrackToHumanBoneMap.Add("RMiddle3", HumanBodyBones.RightMiddleDistal);
m_optitrackToHumanBoneMap.Add("RRing1", HumanBodyBones.RightRingProximal);
m_optitrackToHumanBoneMap.Add("RRing2", HumanBodyBones.RightRingIntermediate);
m_optitrackToHumanBoneMap.Add("RRing3", HumanBodyBones.RightRingDistal);
m_optitrackToHumanBoneMap.Add("RPinky1", HumanBodyBones.RightLittleProximal);
m_optitrackToHumanBoneMap.Add("RPinky2", HumanBodyBones.RightLittleIntermediate);
m_optitrackToHumanBoneMap.Add("RPinky3", HumanBodyBones.RightLittleDistal);
}
private IEnumerator CheckSkeletonConnectionPeriodically()
{
while (true)
{
if (StreamingClient != null)
{
m_skeletonDef = StreamingClient.GetSkeletonDefinitionByName(SkeletonAssetName);
if (m_skeletonDef != null)
{
OptitrackSkeletonState skelState = StreamingClient.GetLatestSkeletonState(m_skeletonDef.Id);
isSkeletonFound = (skelState != null);
if (isSkeletonFound && previousSkeletonName != SkeletonAssetName)
{
StreamingClient.RegisterSkeleton(this, SkeletonAssetName);
previousSkeletonName = SkeletonAssetName;
Debug.Log($"[OptiTrack] 스켈레톤 '{SkeletonAssetName}' 연결 성공");
}
}
else
{
isSkeletonFound = false;
}
}
yield return new WaitForSeconds(updateInterval);
}
}
private bool IsFingerBone(string boneName)
{
// 손가락 관련 본들 확인 (양손 모두)
return boneName.Contains("Thumb") ||
boneName.Contains("Index") ||
boneName.Contains("Middle") ||
boneName.Contains("Ring") ||
boneName.Contains("Pinky");
}
private bool IsHandBone(string boneName)
{
// 손 관련 본들 확인 (양손 모두)
return boneName.Contains("LHand") ||
boneName.Contains("RHand");
}
private bool ShouldApplyMotionToBone(string boneName)
{
switch (motionScope)
{
case MotionApplicationScope.All:
return true;
case MotionApplicationScope.ExcludeFingersOnly:
return !IsFingerBone(boneName);
case MotionApplicationScope.ExcludeHandsAndFingers:
return !IsFingerBone(boneName) && !IsHandBone(boneName);
default:
return true;
}
}
}