Fix : 손가락 데이터 녹화가 안되는 부분 수정

This commit is contained in:
KINDNICK 2025-07-27 23:03:15 +09:00
parent caa8ab8dbd
commit 57ff5e65a7

View File

@ -12,11 +12,11 @@ using System;
using System.IO;
using System.Reflection;
using System.Collections.Generic;
using System.Linq;
#if UNITY_EDITOR
using UnityEditor;
#endif
using EasyMotionRecorder;
using KindRetargeting;
using UniHumanoid;
namespace Entum
@ -100,7 +100,26 @@ namespace Entum
// SessionID 생성 (인스턴스 ID 제외)
SessionID = DateTime.Now.ToString("yyMMdd_HHmmss");
_poseHandler = new HumanPoseHandler(_animator.avatar, _animator.transform);
// HumanPoseHandler 안전한 초기화
if (_animator.avatar != null && _animator.isHuman)
{
try
{
_poseHandler = new HumanPoseHandler(_animator.avatar, _animator.transform);
Debug.Log($"HumanPoseHandler 초기화 완료 - 아바타: {_animator.avatar.name}");
}
catch (System.Exception e)
{
Debug.LogError($"HumanPoseHandler 초기화 실패: {e.Message}");
_poseHandler = null;
}
}
else
{
Debug.LogError($"아바타가 휴머노이드가 아니거나 Avatar가 설정되지 않았습니다. " +
$"Avatar: {_animator.avatar}, IsHuman: {_animator.isHuman}");
_poseHandler = null;
}
}
// 내부 메서드들 (SavePathManager에서 호출)
@ -147,22 +166,29 @@ namespace Entum
{
return;
}
if (FrameIndex % TargetFPS == 0)
if (FrameIndex % TargetFPS == 0 && FrameIndex > 0 && RecordedTime > 0)
{
print("Motion_FPS=" + 1 / (RecordedTime / FrameIndex));
print("Motion_FPS=" + (FrameIndex / RecordedTime));
}
}
else
{
if (Time.frameCount % Application.targetFrameRate == 0)
{
print("Motion_FPS=" + 1 / Time.deltaTime);
print("Motion_FPS=" + (1 / Time.deltaTime));
}
}
//現在のフレームのHumanoidの姿勢を取得
if (_poseHandler == null)
{
Debug.LogError("PoseHandler가 초기화되지 않았습니다. 녹화를 중단합니다.");
RecordEnd();
return;
}
_poseHandler.GetHumanPose(ref _currentPose);
//posesに取得した姿勢を書き込む
//posesに取得한姿勢를書き込む
var serializedPose = new HumanoidPoses.SerializeHumanoidPose();
switch (_rootBoneSystem)
@ -173,17 +199,51 @@ namespace Entum
break;
case MotionDataSettings.Rootbonesystem.Hipbone:
serializedPose.BodyRootPosition = _animator.GetBoneTransform(_targetRootBone).position;
serializedPose.BodyRootRotation = _animator.GetBoneTransform(_targetRootBone).rotation;
Debug.LogWarning(_animator.GetBoneTransform(_targetRootBone).position);
var hipBone = _animator.GetBoneTransform(_targetRootBone);
if (hipBone != null)
{
serializedPose.BodyRootPosition = hipBone.position;
serializedPose.BodyRootRotation = hipBone.rotation;
}
else
{
Debug.LogWarning($"타겟 루트 본 {_targetRootBone}을 찾을 수 없습니다. Object Root로 대체합니다.");
serializedPose.BodyRootPosition = _animator.transform.localPosition;
serializedPose.BodyRootRotation = _animator.transform.localRotation;
}
break;
default:
throw new ArgumentOutOfRangeException();
}
var bodyTQ = new TQ(_currentPose.bodyPosition, _currentPose.bodyRotation);
var LeftFootTQ = new TQ(_animator.GetBoneTransform(IK_LeftFootBone).position, _animator.GetBoneTransform(IK_LeftFootBone).rotation);
var RightFootTQ = new TQ(_animator.GetBoneTransform(IK_RightFootBone).position, _animator.GetBoneTransform(IK_RightFootBone).rotation);
// 발 본 안전성 검사
var leftFootBone = _animator.GetBoneTransform(IK_LeftFootBone);
var rightFootBone = _animator.GetBoneTransform(IK_RightFootBone);
TQ LeftFootTQ, RightFootTQ;
if (leftFootBone != null)
{
LeftFootTQ = new TQ(leftFootBone.position, leftFootBone.rotation);
}
else
{
Debug.LogWarning($"왼발 본 {IK_LeftFootBone}을 찾을 수 없습니다. 기본값을 사용합니다.");
LeftFootTQ = new TQ(Vector3.zero, Quaternion.identity);
}
if (rightFootBone != null)
{
RightFootTQ = new TQ(rightFootBone.position, rightFootBone.rotation);
}
else
{
Debug.LogWarning($"오른발 본 {IK_RightFootBone}을 찾을 수 없습니다. 기본값을 사용합니다.");
RightFootTQ = new TQ(Vector3.zero, Quaternion.identity);
}
serializedPose.BodyPosition = bodyTQ.t;
serializedPose.BodyRotation = bodyTQ.q;
@ -214,6 +274,13 @@ namespace Entum
return;
}
// PoseHandler 유효성 검사
if (_poseHandler == null)
{
Debug.LogError("HumanPoseHandler가 초기화되지 않았습니다. 녹화를 시작할 수 없습니다.");
return;
}
// 세션 ID 생성 (인스턴스 ID 제외)
SessionID = DateTime.Now.ToString("yyMMdd_HHmmss");
@ -223,12 +290,27 @@ namespace Entum
Poses.SessionID = SessionID;
Poses.InstanceID = instanceID; // 인스턴스 ID 설정
// T포즈 별도 저장 옵션이 활성화된 경우
if (_recordTPoseAtStart)
{
try
{
RecordTPoseAsFirstFrame();
Debug.Log("T포즈 데이터가 성공적으로 기록되었습니다.");
}
catch (System.Exception e)
{
Debug.LogError($"T포즈 녹화 중 오류 발생: {e.Message}");
// T포즈 실패해도 일반 녹화는 계속 진행
}
}
RecordedTime = 0f;
StartTime = Time.time;
FrameIndex = 0;
_recording = true;
Debug.Log($"모션 녹화 시작 - 인스턴스: {instanceID}, 세션: {SessionID}");
Debug.Log($"모션 녹화 시작 - 인스턴스: {instanceID}, 세션: {SessionID}, T포즈 옵션: {_recordTPoseAtStart}");
OnRecordStart?.Invoke();
}
@ -242,32 +324,75 @@ namespace Entum
private void SetTPose(Animator animator)
{
// T-포즈 설정을 위한 임시 애니메이션 클립 생성
var tPoseClip = new AnimationClip();
tPoseClip.name = "TPose";
// 모든 본을 T-포즈로 설정
var humanBones = animator.avatar.humanDescription.human;
foreach (var bone in humanBones)
if (animator == null || animator.avatar == null)
{
// HumanBodyBones enum으로 변환
HumanBodyBones bodyBone;
if (System.Enum.TryParse(bone.humanName, out bodyBone))
Debug.LogWarning("Animator 또는 Avatar가 null입니다. T포즈 설정을 건너뜁니다.");
return;
}
try
{
// HumanPoseClip에서 T-포즈 데이터 로드 시도
var humanPoseClip = Resources.Load<UniHumanoid.HumanPoseClip>("T-Pose.pose");
if (humanPoseClip != null)
{
var boneTransform = animator.GetBoneTransform(bodyBone);
if (boneTransform != null)
{
var curve = new AnimationCurve();
curve.AddKey(0, 0);
tPoseClip.SetCurve(boneTransform.name, typeof(Transform), "localRotation.x", curve);
tPoseClip.SetCurve(boneTransform.name, typeof(Transform), "localRotation.y", curve);
tPoseClip.SetCurve(boneTransform.name, typeof(Transform), "localRotation.z", curve);
tPoseClip.SetCurve(boneTransform.name, typeof(Transform), "localRotation.w", curve);
}
// T포즈 데이터를 적용
var pose = humanPoseClip.GetPose();
SetPoseToAnimator(animator.avatar, animator.transform, pose);
Debug.Log("T-포즈가 HumanPoseClip에서 성공적으로 로드되었습니다.");
}
else
{
// HumanPoseClip이 없는 경우 기본 T포즈 설정
Debug.LogWarning("T-Pose.pose 리소스를 찾을 수 없습니다. 기본 T포즈를 설정합니다.");
SetDefaultTPose(animator);
}
// UpperChest 본 위치 초기화 (카인드리타겟팅과 동일)
Transform upperChest = animator.GetBoneTransform(HumanBodyBones.UpperChest);
if (upperChest != null)
{
upperChest.localPosition = Vector3.zero;
}
}
catch (System.Exception e)
{
Debug.LogError($"T포즈 설정 중 오류 발생: {e.Message}");
SetDefaultTPose(animator);
}
}
/// <summary>
/// HumanPose를 Animator에 적용하는 내부 메서드
/// </summary>
private void SetPoseToAnimator(Avatar avatar, Transform transform, HumanPose pose)
{
var handler = new HumanPoseHandler(avatar, transform);
handler.SetHumanPose(ref pose);
handler.Dispose();
}
/// <summary>
/// 기본 T포즈 설정 (HumanPoseClip이 없는 경우 백업용)
/// </summary>
private void SetDefaultTPose(Animator animator)
{
Debug.Log("기본 T포즈 설정을 수행합니다.");
animator.Play("TPose");
// 기본 T포즈 생성 - Muscles를 0으로 설정
var defaultTPose = new HumanPose();
defaultTPose.bodyPosition = Vector3.zero;
defaultTPose.bodyRotation = Quaternion.identity;
// 모든 머슬을 0으로 설정 (T포즈)
defaultTPose.muscles = new float[HumanTrait.MuscleCount];
for (int i = 0; i < HumanTrait.MuscleCount; i++)
{
defaultTPose.muscles[i] = 0f;
}
SetPoseToAnimator(animator.avatar, animator.transform, defaultTPose);
}
private void RecordTPoseData()
@ -314,6 +439,7 @@ namespace Entum
{
pose.HumanoidBones = new List<HumanoidPoses.SerializeHumanoidPose.HumanoidBone>();
// 기존 avatar.humanDescription.human 본들 처리
var humanBones = animator.avatar.humanDescription.human;
foreach (var bone in humanBones)
{
@ -337,6 +463,59 @@ namespace Entum
}
}
}
// 손가락 본들 추가 처리 (누락된 것들을 위해)
AddFingerBones(animator, ref pose);
}
/// <summary>
/// 손가락 본들을 추가로 처리하는 메서드
/// </summary>
private static void AddFingerBones(Animator animator, ref HumanoidPoses.SerializeHumanoidPose pose)
{
// 이미 추가된 본들의 이름을 저장
var existingBoneNames = new HashSet<string>();
foreach (var bone in pose.HumanoidBones)
{
existingBoneNames.Add(bone.Name);
}
// 모든 손가락 본들을 정의
var fingerBones = new HumanBodyBones[]
{
// 왼손 손가락들
HumanBodyBones.LeftThumbProximal, HumanBodyBones.LeftThumbIntermediate, HumanBodyBones.LeftThumbDistal,
HumanBodyBones.LeftIndexProximal, HumanBodyBones.LeftIndexIntermediate, HumanBodyBones.LeftIndexDistal,
HumanBodyBones.LeftMiddleProximal, HumanBodyBones.LeftMiddleIntermediate, HumanBodyBones.LeftMiddleDistal,
HumanBodyBones.LeftRingProximal, HumanBodyBones.LeftRingIntermediate, HumanBodyBones.LeftRingDistal,
HumanBodyBones.LeftLittleProximal, HumanBodyBones.LeftLittleIntermediate, HumanBodyBones.LeftLittleDistal,
// 오른손 손가락들
HumanBodyBones.RightThumbProximal, HumanBodyBones.RightThumbIntermediate, HumanBodyBones.RightThumbDistal,
HumanBodyBones.RightIndexProximal, HumanBodyBones.RightIndexIntermediate, HumanBodyBones.RightIndexDistal,
HumanBodyBones.RightMiddleProximal, HumanBodyBones.RightMiddleIntermediate, HumanBodyBones.RightMiddleDistal,
HumanBodyBones.RightRingProximal, HumanBodyBones.RightRingIntermediate, HumanBodyBones.RightRingDistal,
HumanBodyBones.RightLittleProximal, HumanBodyBones.RightLittleIntermediate, HumanBodyBones.RightLittleDistal
};
// 각 손가락 본에 대해 처리
foreach (var fingerBone in fingerBones)
{
var boneTransform = animator.GetBoneTransform(fingerBone);
if (boneTransform != null)
{
var humanoidBone = new HumanoidPoses.SerializeHumanoidPose.HumanoidBone();
humanoidBone.Set(animator.transform, boneTransform);
// 중복 확인 (이미 추가되지 않은 경우만 추가)
if (!existingBoneNames.Contains(humanoidBone.Name))
{
pose.HumanoidBones.Add(humanoidBone);
existingBoneNames.Add(humanoidBone.Name);
//Debug.Log($"손가락 본 추가: {fingerBone} -> {humanoidBone.Name}");
}
}
}
}
private static bool IsElbowBone(Transform bone)
@ -654,6 +833,12 @@ namespace Entum
}
}
// 손가락 본들이 포함되었는지 로깅
var fingerBoneCount = boneCurves.Keys.Count(name =>
name.Contains("Thumb") || name.Contains("Index") || name.Contains("Middle") ||
name.Contains("Ring") || name.Contains("Little") || name.Contains("Pinky"));
Debug.Log($"제네릭 애니메이션 클립 생성 완료 - 총 {boneCurves.Count}개 본, 손가락 본 {fingerBoneCount}개 포함");
return clip;
}
#endif
@ -713,6 +898,30 @@ namespace Entum
return avatarIKGoal == AvatarIKGoal.LeftFoot ? HumanBodyBones.LeftFoot : HumanBodyBones.RightFoot;
}
}
/// <summary>
/// 컴포넌트가 파괴될 때 리소스를 정리합니다.
/// </summary>
private void OnDestroy()
{
// HumanPoseHandler 안전하게 정리
if (_poseHandler != null)
{
try
{
_poseHandler.Dispose();
Debug.Log("HumanPoseHandler가 정리되었습니다.");
}
catch (System.Exception e)
{
Debug.LogError($"HumanPoseHandler 정리 중 오류 발생: {e.Message}");
}
finally
{
_poseHandler = null;
}
}
}
}
}