Fix : 리타겟팅 버그 업데이트 프랍 데이터베이스 추가

This commit is contained in:
user 2026-03-29 19:16:22 +09:00
parent 4b59344a3e
commit 4aa22756e5
3 changed files with 57 additions and 11 deletions

View File

@ -109,8 +109,9 @@ public class OptitrackSkeletonAnimator_Mingle : MonoBehaviour
// OptiTrack 본 이름 → Transform 빠른 캐시 (GetMappedTransform O(n) → O(1))
private Dictionary<string, Transform> m_optiNameTransformCache = new Dictionary<string, Transform>();
// 필터 적용 전 raw 월드 위치 (IK 타겟용 — 접지력 보존)
// 필터 적용 전 raw 월드 위치/회전 (IK 타겟용 — 접지력 보존)
private Dictionary<HumanBodyBones, Vector3> m_rawWorldPositions = new Dictionary<HumanBodyBones, Vector3>();
private Dictionary<HumanBodyBones, Quaternion> m_rawWorldRotations = new Dictionary<HumanBodyBones, Quaternion>();
// raw 위치를 캡처할 IK 포인트 본 목록
private static readonly HumanBodyBones[] k_IKPointBones = new HumanBodyBones[]
@ -373,12 +374,15 @@ public class OptitrackSkeletonAnimator_Mingle : MonoBehaviour
mapping.cachedTransform.localPosition = rawPos;
}
// Raw 상태에서 IK 포인트 월드 위치 캡처
// Raw 상태에서 IK 포인트 월드 위치/회전 캡처
foreach (var ikBone in k_IKPointBones)
{
Transform t = GetBoneTransform(ikBone);
if (t != null)
{
m_rawWorldPositions[ikBone] = t.position;
m_rawWorldRotations[ikBone] = t.rotation;
}
}
// Pass 2: 필터 적용된 데이터로 덮어쓰기
@ -419,20 +423,23 @@ public class OptitrackSkeletonAnimator_Mingle : MonoBehaviour
mapping.cachedTransform.localPosition = finalPos;
}
// 필터 없으면 현재 Transform 위치 곧 raw
// 필터 없으면 현재 Transform 위치/회전이 곧 raw
foreach (var ikBone in k_IKPointBones)
{
Transform t = GetBoneTransform(ikBone);
if (t != null)
{
m_rawWorldPositions[ikBone] = t.position;
m_rawWorldRotations[ikBone] = t.rotation;
}
}
}
// ── 어깨 증폭 + 상완 역보정 (1.0이면 스킵) ──
if (Mathf.Abs(shoulderAmplify - 1f) > 0.001f)
{
AmplifyShoulderWithCompensation("LShoulder", "LUArm");
AmplifyShoulderWithCompensation("RShoulder", "RUArm");
AmplifyShoulderWithCompensation("LShoulder", "LUArm", true);
AmplifyShoulderWithCompensation("RShoulder", "RUArm", false);
}
}
@ -440,17 +447,35 @@ public class OptitrackSkeletonAnimator_Mingle : MonoBehaviour
/// <summary>
/// 어깨 회전을 rest pose 대비 증폭하고, 상완에서 추가 회전분을 상쇄하여 손 위치를 보존합니다.
/// </summary>
private void AmplifyShoulderWithCompensation(string shoulderName, string upperArmName)
private float m_leftShoulderBlend = 0f;
private float m_rightShoulderBlend = 0f;
/// <param name="isLeft">true = 왼쪽(Z &lt; 0일 때 증폭), false = 오른쪽(Z &gt; 0일 때 증폭)</param>
private void AmplifyShoulderWithCompensation(string shoulderName, string upperArmName, bool isLeft)
{
Transform shoulder = GetMappedTransform(shoulderName);
Transform upperArm = GetMappedTransform(upperArmName);
if (shoulder == null || upperArm == null) return;
// rest pose 대비 델타 추출 → 증폭
// rest pose 대비 델타 추출
Quaternion restRot = GetRestLocalRotation(shoulderName);
Quaternion currentRot = shoulder.localRotation;
Quaternion delta = Quaternion.Inverse(restRot) * currentRot;
Quaternion amplifiedDelta = Quaternion.SlerpUnclamped(Quaternion.identity, delta, shoulderAmplify);
// 어깨가 올라가는 방향일 때만 증폭 (왼쪽: Z < 0, 오른쪽: Z > 0)
Vector3 deltaEuler = delta.eulerAngles;
float z = deltaEuler.z > 180f ? deltaEuler.z - 360f : deltaEuler.z;
float targetBlend = isLeft ? Mathf.Clamp01(-z / 5f) : Mathf.Clamp01(z / 5f);
// 부드러운 블렌딩 (급격한 on/off 방지)
ref float blend = ref (isLeft ? ref m_leftShoulderBlend : ref m_rightShoulderBlend);
blend = Mathf.Lerp(blend, targetBlend, 10f * Time.deltaTime);
if (blend < 0.001f) return;
// 증폭량: 1(원본) ~ shoulderAmplify 사이를 blend로 보간
float effectiveAmplify = Mathf.Lerp(1f, shoulderAmplify, blend);
Quaternion amplifiedDelta = Quaternion.SlerpUnclamped(Quaternion.identity, delta, effectiveAmplify);
Quaternion amplifiedRot = restRot * amplifiedDelta;
// 추가된 회전량
@ -748,6 +773,14 @@ public class OptitrackSkeletonAnimator_Mingle : MonoBehaviour
return m_rawWorldPositions.TryGetValue(bone, out position);
}
/// <summary>
/// 1€ 필터 적용 전의 raw 월드 회전을 반환합니다.
/// </summary>
public bool TryGetRawWorldRotation(HumanBodyBones bone, out Quaternion rotation)
{
return m_rawWorldRotations.TryGetValue(bone, out rotation);
}
/// <summary>
/// HumanBodyBones enum으로 OptiTrack 매핑된 Transform 반환
/// (Humanoid 호환 레이어 — sourceAnimator.GetBoneTransform() 대체)

Binary file not shown.

View File

@ -1257,12 +1257,25 @@ namespace KindRetargeting
{
// 1€ 필터 적용 전 raw 위치 사용 (접지력 보존)
// raw가 없으면 필터된 Transform.position fallback
// 1€ 필터 적용 전 raw 위치/회전 사용 (접지력 보존)
Vector3 targetPosition;
if (optitrackSource != null && optitrackSource.TryGetRawWorldPosition(endBone, out Vector3 rawPos))
targetPosition = rawPos;
else
targetPosition = sourceBone.position;
Quaternion targetRotation = targetBone.rotation;
// raw 회전 + 리타게팅 오프셋 적용 (필터 스무딩 없는 회전)
Quaternion targetRotation;
if (optitrackSource != null
&& optitrackSource.TryGetRawWorldRotation(endBone, out Quaternion rawRot)
&& rotationOffsets.TryGetValue(endBone, out Quaternion endOffset))
{
targetRotation = rawRot * endOffset;
}
else
{
targetRotation = targetBone.rotation;
}
// 발 본인 경우 오프셋 적용
if (endBone == HumanBodyBones.LeftFoot || endBone == HumanBodyBones.RightFoot)