diff --git a/Assets/Scripts/KindRetargeting/CustomRetargetingScript.cs b/Assets/Scripts/KindRetargeting/CustomRetargetingScript.cs index 274d82d7e..9ec19621e 100644 --- a/Assets/Scripts/KindRetargeting/CustomRetargetingScript.cs +++ b/Assets/Scripts/KindRetargeting/CustomRetargetingScript.cs @@ -19,7 +19,7 @@ namespace KindRetargeting [HideInInspector] public Animator targetAnimator; // 대상 아바타의 Animator // IK 컴포넌트 참조 - private TwoBoneIKSolver ikSolver; + [SerializeField] public TwoBoneIKSolver ikSolver = new TwoBoneIKSolver(); [Header("힙 위치 보정 (로컬 좌표계 기반)")] [SerializeField, Range(-1, 1)] @@ -269,8 +269,7 @@ namespace KindRetargeting // 설정 로드 LoadSettings(); - // IK 컴포넌트 참조 가져오기 - ikSolver = GetComponent(); + // IK 모듈은 InitializeIKJoints에서 초기화 // IK 타겟 생성 (무릎 시각화 오브젝트 포함) CreateIKTargets(); @@ -832,6 +831,9 @@ namespace KindRetargeting // 어깨 보정 (기존 ExecutionOrder 3) shoulderCorrection.OnUpdate(); + // IK 솔버 (기존 ExecutionOrder 6) + ikSolver.OnUpdate(); + // 스케일 변경 확인 및 적용 if (!Mathf.Approximately(previousScale, avatarScale)) { @@ -1158,11 +1160,6 @@ namespace KindRetargeting /// private void CreateIKTargets() { - // IK 컴포넌트 가져오기 또는 새로 추가 - ikSolver = GetComponent(); - if (ikSolver == null) - ikSolver = gameObject.AddComponent(); - ikSolver.animator = targetAnimator; // IK 타겟들을 담을 부모 오브젝트 생성 @@ -1194,7 +1191,7 @@ namespace KindRetargeting ikSolver.rightLeg.bendGoal = rightLegGoal.transform; // TwoBoneIKSolver 본 캐싱 초기화 - ikSolver.Initialize(); + ikSolver.Initialize(targetAnimator); } /// diff --git a/Assets/Scripts/KindRetargeting/FootGroundingController.cs b/Assets/Scripts/KindRetargeting/FootGroundingController.cs index a5fdd89b9..8a5908da2 100644 --- a/Assets/Scripts/KindRetargeting/FootGroundingController.cs +++ b/Assets/Scripts/KindRetargeting/FootGroundingController.cs @@ -67,7 +67,8 @@ namespace KindRetargeting private void Start() { - ikSolver = GetComponent(); + var crs = GetComponent(); + if (crs != null) ikSolver = crs.ikSolver; animator = GetComponent(); if (animator == null || !animator.isHuman || ikSolver == null) return; diff --git a/Assets/Scripts/KindRetargeting/LimbWeightController.cs b/Assets/Scripts/KindRetargeting/LimbWeightController.cs index c5ce81e6b..977b12a2e 100644 --- a/Assets/Scripts/KindRetargeting/LimbWeightController.cs +++ b/Assets/Scripts/KindRetargeting/LimbWeightController.cs @@ -96,9 +96,8 @@ namespace KindRetargeting void Start() { - ikSolver = GetComponent(); - crs = GetComponent(); + if (crs != null) ikSolver = crs.ikSolver; InitWeightLayers(); diff --git a/Assets/Scripts/KindRetargeting/TwoBoneIKSolver.cs b/Assets/Scripts/KindRetargeting/TwoBoneIKSolver.cs index 832f66b9c..13b5e9953 100644 --- a/Assets/Scripts/KindRetargeting/TwoBoneIKSolver.cs +++ b/Assets/Scripts/KindRetargeting/TwoBoneIKSolver.cs @@ -7,8 +7,8 @@ namespace KindRetargeting /// FinalIK IKSolverTrigonometric.Solve()를 사용하는 IK 래퍼. /// 4개 사지(양팔, 양다리)에 대해 FinalIK의 검증된 코사인 법칙 솔버를 호출합니다. /// - [DefaultExecutionOrder(6)] - public class TwoBoneIKSolver : MonoBehaviour + [System.Serializable] + public class TwoBoneIKSolver { [System.Serializable] public class LimbIK @@ -27,7 +27,6 @@ namespace KindRetargeting [HideInInspector] public float upperLength; [HideInInspector] public float lowerLength; - // 초기 벤드 법선 (upper 본 로컬 공간 — FinalIK 방식) [HideInInspector] public Vector3 localBendNormal; } @@ -43,15 +42,9 @@ namespace KindRetargeting private bool isInitialized; - private void Start() + public void Initialize(Animator targetAnimator) { - Initialize(); - } - - public void Initialize() - { - if (animator == null) - animator = GetComponent(); + animator = targetAnimator; if (animator == null || !animator.isHuman) return; CacheLimb(leftArm, HumanBodyBones.LeftUpperArm, HumanBodyBones.LeftLowerArm, HumanBodyBones.LeftHand); @@ -73,8 +66,6 @@ namespace KindRetargeting limb.upperLength = Vector3.Distance(limb.upper.position, limb.lower.position); limb.lowerLength = Vector3.Distance(limb.lower.position, limb.end.position); - // 초기 벤드 법선을 upper 본의 로컬 공간에 캐싱 (FinalIK TrigonometricBone 방식) - // 런타임에 upper.rotation * localBendNormal로 안정적인 월드 법선 획득 Vector3 ab = limb.lower.position - limb.upper.position; Vector3 bc = limb.end.position - limb.lower.position; Vector3 bendNormal = Vector3.Cross(ab, bc); @@ -90,7 +81,7 @@ namespace KindRetargeting limb.localBendNormal = Quaternion.Inverse(limb.upper.rotation) * bendNormal; } - private void Update() + public void OnUpdate() { if (!isInitialized) return; @@ -105,10 +96,8 @@ namespace KindRetargeting if (!limb.enabled || limb.target == null || limb.positionWeight < 0.001f) return; if (limb.upper == null || limb.lower == null || limb.end == null) return; - // 벤드 법선 계산 Vector3 bendNormal = GetBendNormal(limb); - // bendGoal 적용 if (limb.bendGoal != null && limb.bendGoalWeight > 0.001f) { Vector3 goalNormal = Vector3.Cross( @@ -121,7 +110,6 @@ namespace KindRetargeting } } - // FinalIK 정적 솔버 호출 IKSolverTrigonometric.Solve( limb.upper, limb.lower, @@ -131,7 +119,6 @@ namespace KindRetargeting limb.positionWeight ); - // 끝단 회전 if (limb.rotationWeight > 0.001f) { limb.end.rotation = Quaternion.Slerp( @@ -142,19 +129,11 @@ namespace KindRetargeting } } - /// - /// 벤드 법선을 upper 본의 회전에서 유도합니다 (FinalIK 방식). - /// 위치 기반 Cross(ab, bc)는 직선 근처에서 불안정하지만, - /// 회전 기반은 본의 회전을 그대로 따르므로 안정적입니다. - /// private Vector3 GetBendNormal(LimbIK limb) { return limb.upper.rotation * limb.localBendNormal; } - /// - /// 히프 높이 자동 보정값을 계산합니다. - /// public float CalculateAutoFloorHeight(float comfortRatio = 0.98f) { if (animator == null || leftLeg.upper == null || leftLeg.lower == null || leftLeg.end == null) return 0f;