Fix : 모캡 접지력 향상 업데이트

This commit is contained in:
user 2026-03-28 19:55:40 +09:00
parent 2a7c8b8043
commit 3929be8974
2 changed files with 91 additions and 20 deletions

View File

@ -67,6 +67,20 @@ public class OptitrackSkeletonAnimator_Mingle : MonoBehaviour
// OptiTrack 본 이름 → Transform 빠른 캐시 (GetMappedTransform O(n) → O(1))
private Dictionary<string, Transform> m_optiNameTransformCache = new Dictionary<string, Transform>();
// 필터 적용 전 raw 월드 위치 (IK 타겟용 — 접지력 보존)
private Dictionary<HumanBodyBones, Vector3> m_rawWorldPositions = new Dictionary<HumanBodyBones, Vector3>();
// raw 위치를 캡처할 IK 포인트 본 목록
private static readonly HumanBodyBones[] k_IKPointBones = new HumanBodyBones[]
{
HumanBodyBones.LeftFoot,
HumanBodyBones.RightFoot,
HumanBodyBones.LeftHand,
HumanBodyBones.RightHand,
HumanBodyBones.LeftToes,
HumanBodyBones.RightToes,
};
// 스파인/넥 체인 Transform 캐시 (GetSpineChainTransforms 매 호출 List 할당 방지)
private List<Transform> m_spineChainCache = new List<Transform>();
@ -261,38 +275,79 @@ public class OptitrackSkeletonAnimator_Mingle : MonoBehaviour
m_lastFrameTimestamp = frameTs;
m_hasLastFrameTimestamp = true;
// ── 각 본 업데이트 ───────────────────────────────────────────────────────
// ── Pass 1: Raw 데이터 적용 → IK 포인트 월드 위치 캡처 ──────────────────
// 필터가 활성화되어 있을 때만 two-pass, 비활성이면 single-pass
if (enableBoneFilter)
{
// Raw 데이터로 모든 본 업데이트 (필터 없이)
foreach (var bone in m_skeletonDef.Bones)
{
if (!m_boneIdToMappingIndex.TryGetValue(bone.Id, out int mappingIdx))
continue;
var mapping = boneMappings[mappingIdx];
if (!mapping.isMapped || mapping.cachedTransform == null)
continue;
if (mapping.applyRotation)
{
if (!m_snapshotOrientations.TryGetValue(bone.Id, out Quaternion finalOri))
continue;
if (enableBoneFilter)
finalOri = ApplyOneEuroOri(bone.Id, finalOri, m_natNetDt);
mapping.cachedTransform.localRotation = finalOri;
if (mapping.applyRotation && m_snapshotOrientations.TryGetValue(bone.Id, out Quaternion rawOri))
mapping.cachedTransform.localRotation = rawOri;
if (mapping.applyPosition && m_snapshotPositions.TryGetValue(bone.Id, out Vector3 rawPos))
mapping.cachedTransform.localPosition = rawPos;
}
if (mapping.applyPosition)
// Raw 상태에서 IK 포인트 월드 위치 캡처
foreach (var ikBone in k_IKPointBones)
{
if (!m_snapshotPositions.TryGetValue(bone.Id, out Vector3 finalPos))
Transform t = GetBoneTransform(ikBone);
if (t != null)
m_rawWorldPositions[ikBone] = t.position;
}
// Pass 2: 필터 적용된 데이터로 덮어쓰기
foreach (var bone in m_skeletonDef.Bones)
{
if (!m_boneIdToMappingIndex.TryGetValue(bone.Id, out int mappingIdx))
continue;
var mapping = boneMappings[mappingIdx];
if (!mapping.isMapped || mapping.cachedTransform == null)
continue;
if (enableBoneFilter)
if (mapping.applyRotation && m_snapshotOrientations.TryGetValue(bone.Id, out Quaternion finalOri))
{
finalOri = ApplyOneEuroOri(bone.Id, finalOri, m_natNetDt);
mapping.cachedTransform.localRotation = finalOri;
}
if (mapping.applyPosition && m_snapshotPositions.TryGetValue(bone.Id, out Vector3 finalPos))
{
finalPos = ApplyOneEuroPos(bone.Id, finalPos, m_natNetDt);
mapping.cachedTransform.localPosition = finalPos;
}
}
}
else
{
// 필터 비활성: single-pass, raw = filtered
foreach (var bone in m_skeletonDef.Bones)
{
if (!m_boneIdToMappingIndex.TryGetValue(bone.Id, out int mappingIdx))
continue;
var mapping = boneMappings[mappingIdx];
if (!mapping.isMapped || mapping.cachedTransform == null)
continue;
if (mapping.applyRotation && m_snapshotOrientations.TryGetValue(bone.Id, out Quaternion finalOri))
mapping.cachedTransform.localRotation = finalOri;
if (mapping.applyPosition && m_snapshotPositions.TryGetValue(bone.Id, out Vector3 finalPos))
mapping.cachedTransform.localPosition = finalPos;
}
// 필터 없으면 현재 Transform 위치가 곧 raw
foreach (var ikBone in k_IKPointBones)
{
Transform t = GetBoneTransform(ikBone);
if (t != null)
m_rawWorldPositions[ikBone] = t.position;
}
}
}
@ -571,6 +626,16 @@ public class OptitrackSkeletonAnimator_Mingle : MonoBehaviour
{ HumanBodyBones.RightLittleDistal, "RPinky3" },
};
/// <summary>
/// 1€ 필터 적용 전의 raw 월드 위치를 반환합니다.
/// IK 타겟(발 접지 등)에 사용하면 필터 스무딩으로 인한 접지력 저하를 방지합니다.
/// 지원 본: LeftFoot, RightFoot, LeftHand, RightHand, LeftToes, RightToes
/// </summary>
public bool TryGetRawWorldPosition(HumanBodyBones bone, out Vector3 position)
{
return m_rawWorldPositions.TryGetValue(bone, out position);
}
/// <summary>
/// HumanBodyBones enum으로 OptiTrack 매핑된 Transform 반환
/// (Humanoid 호환 레이어 — sourceAnimator.GetBoneTransform() 대체)

View File

@ -1268,7 +1268,13 @@ namespace KindRetargeting
if (sourceBone != null && targetBone != null)
{
Vector3 targetPosition = sourceBone.position;
// 1€ 필터 적용 전 raw 위치 사용 (접지력 보존)
// raw가 없으면 필터된 Transform.position fallback
Vector3 targetPosition;
if (optitrackSource != null && optitrackSource.TryGetRawWorldPosition(endBone, out Vector3 rawPos))
targetPosition = rawPos;
else
targetPosition = sourceBone.position;
Quaternion targetRotation = targetBone.rotation;
// 발 본인 경우 오프셋 적용