182 lines
6.2 KiB
C#
182 lines
6.2 KiB
C#
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
|
|
namespace KindRetargeting
|
|
{
|
|
/// <summary>
|
|
/// 아바타 간의 회전 오프셋을 계산하고 포즈 데이터를 복사하는 기능을 제공합니다.
|
|
/// </summary>
|
|
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<HumanBodyBones, Quaternion> rotationOffsets = new Dictionary<HumanBodyBones, Quaternion>();
|
|
|
|
#region 프로퍼티
|
|
|
|
/// <summary>
|
|
/// 계산된 회전 오프셋 딕셔너리를 반환합니다.
|
|
/// </summary>
|
|
public Dictionary<HumanBodyBones, Quaternion> 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 오프셋 계산
|
|
|
|
/// <summary>
|
|
/// 원본과 대상 아바타의 각 본 간 회전 오프셋을 계산하여 저장합니다.
|
|
/// </summary>
|
|
public void CalculateRotationOffsets()
|
|
{
|
|
if (sourceAnimator == null || targetAnimator == null)
|
|
{
|
|
Debug.LogError("소스 또는 타겟 Animator가 설정되지 않았습니다.");
|
|
return;
|
|
}
|
|
|
|
// Dictionary가 null이면 초기화
|
|
if (rotationOffsets == null)
|
|
{
|
|
rotationOffsets = new Dictionary<HumanBodyBones, Quaternion>();
|
|
}
|
|
|
|
// 모든 본에 대해 오프셋 계산 (기본 몸체 본 + 손가락 본 + 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;
|
|
// 최적화: ContainsKey + 인덱서 이중 조회 대신 인덱서 직접 사용
|
|
rotationOffsets[bone] = offset;
|
|
}
|
|
}
|
|
|
|
Debug.Log($"회전 오프셋 계산 완료: {rotationOffsets.Count}개 본");
|
|
}
|
|
|
|
/// <summary>
|
|
/// 특정 본의 회전 오프셋을 반환합니다.
|
|
/// </summary>
|
|
/// <param name="bone">본 타입</param>
|
|
/// <returns>회전 오프셋 (없으면 Identity)</returns>
|
|
public Quaternion GetRotationOffset(HumanBodyBones bone)
|
|
{
|
|
// 최적화: TryGetValue로 단일 조회
|
|
return rotationOffsets.TryGetValue(bone, out Quaternion offset) ? offset : Quaternion.identity;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 특정 본에 회전 오프셋을 적용합니다.
|
|
/// </summary>
|
|
/// <param name="bone">본 타입</param>
|
|
/// <param name="originalRotation">원본 회전값</param>
|
|
/// <returns>오프셋이 적용된 회전값</returns>
|
|
public Quaternion ApplyRotationOffset(HumanBodyBones bone, Quaternion originalRotation)
|
|
{
|
|
// 최적화: TryGetValue로 단일 조회
|
|
return rotationOffsets.TryGetValue(bone, out Quaternion offset)
|
|
? originalRotation * offset
|
|
: originalRotation;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region 포즈 복사
|
|
|
|
/// <summary>
|
|
/// 원본 아바타의 포즈를 대상 아바타에 복사합니다.
|
|
/// </summary>
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 특정 본의 포즈를 복사합니다.
|
|
/// </summary>
|
|
/// <param name="bone">복사할 본</param>
|
|
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
|
|
}
|
|
}
|