using System.Collections.Generic;
using UnityEngine;
namespace KindRetargeting
{
///
/// 아바타 간의 회전 오프셋을 계산하고 포즈 데이터를 복사하는 기능을 제공합니다.
///
public class OffsetTransfer : MonoBehaviour
{
[Header("아바타 설정")]
[SerializeField] public Animator sourceAnimator; // 원본 아바타의 Animator
[SerializeField] public Animator targetAnimator; // 대상 아바타의 Animator
[Header("오프셋 설정")]
[SerializeField] private bool autoCalculateOnStart = true; // 시작 시 자동 계산 여부
[SerializeField] private bool autoCopyPose = true; // 매 프레임마다 자동으로 포즈 복사 여부
// 본별 회전 오프셋을 저장하는 딕셔너리
private Dictionary rotationOffsets = new Dictionary();
#region 프로퍼티
///
/// 계산된 회전 오프셋 딕셔너리를 반환합니다.
///
public Dictionary RotationOffsets => rotationOffsets;
#endregion
#region 초기화
private void Start()
{
if (autoCalculateOnStart)
{
CalculateRotationOffsets();
}
}
#endregion
#region 업데이트
private void Update()
{
if (autoCopyPose)
{
CopyPoseToTarget();
}
}
private void LateUpdate()
{
if (autoCopyPose)
{
CopyPoseToTarget();
}
}
#endregion
#region 오프셋 계산
///
/// 원본과 대상 아바타의 각 본 간 회전 오프셋을 계산하여 저장합니다.
///
public void CalculateRotationOffsets()
{
if (sourceAnimator == null || targetAnimator == null)
{
Debug.LogError("소스 또는 타겟 Animator가 설정되지 않았습니다.");
return;
}
// Dictionary가 null이면 초기화
if (rotationOffsets == null)
{
rotationOffsets = new Dictionary();
}
// 모든 본에 대해 오프셋 계산 (기본 몸체 본 + 손가락 본 + UpperChest)
for (int i = 0; i <= 54; i++)
{
HumanBodyBones bone = (HumanBodyBones)i;
Transform sourceBone = sourceAnimator.GetBoneTransform(bone);
Transform targetBone = targetAnimator.GetBoneTransform(bone);
if (sourceBone != null && targetBone != null)
{
Quaternion offset = Quaternion.Inverse(sourceBone.rotation) * targetBone.rotation;
if (rotationOffsets.ContainsKey(bone))
{
rotationOffsets[bone] = offset;
}
else
{
rotationOffsets.Add(bone, offset);
}
}
}
Debug.Log($"회전 오프셋 계산 완료: {rotationOffsets.Count}개 본");
}
///
/// 특정 본의 회전 오프셋을 반환합니다.
///
/// 본 타입
/// 회전 오프셋 (없으면 Identity)
public Quaternion GetRotationOffset(HumanBodyBones bone)
{
if (rotationOffsets.ContainsKey(bone))
{
return rotationOffsets[bone];
}
return Quaternion.identity;
}
///
/// 특정 본에 회전 오프셋을 적용합니다.
///
/// 본 타입
/// 원본 회전값
/// 오프셋이 적용된 회전값
public Quaternion ApplyRotationOffset(HumanBodyBones bone, Quaternion originalRotation)
{
if (rotationOffsets.ContainsKey(bone))
{
return originalRotation * rotationOffsets[bone];
}
return originalRotation;
}
#endregion
#region 포즈 복사
///
/// 원본 아바타의 포즈를 대상 아바타에 복사합니다.
///
public void CopyPoseToTarget()
{
if (sourceAnimator == null || targetAnimator == null)
{
Debug.LogError("소스 또는 타겟 Animator가 설정되지 않았습니다.");
return;
}
// 모든 본에 대해 포즈 복사
for (int i = 0; i <= 54; i++)
{
HumanBodyBones bone = (HumanBodyBones)i;
Transform sourceBone = sourceAnimator.GetBoneTransform(bone);
Transform targetBone = targetAnimator.GetBoneTransform(bone);
if (sourceBone != null && targetBone != null)
{
// 회전은 오프셋 적용, 위치는 그대로 복사
Quaternion targetRotation = ApplyRotationOffset(bone, sourceBone.rotation);
targetBone.rotation = targetRotation;
targetBone.position = sourceBone.position;
}
}
}
///
/// 특정 본의 포즈를 복사합니다.
///
/// 복사할 본
public void CopyBonePose(HumanBodyBones bone)
{
if (sourceAnimator == null || targetAnimator == null)
{
Debug.LogError("소스 또는 타겟 Animator가 설정되지 않았습니다.");
return;
}
Transform sourceBone = sourceAnimator.GetBoneTransform(bone);
Transform targetBone = targetAnimator.GetBoneTransform(bone);
if (sourceBone != null && targetBone != null)
{
Quaternion targetRotation = ApplyRotationOffset(bone, sourceBone.rotation);
targetBone.rotation = targetRotation;
}
}
#endregion
}
}