diff --git a/Assets/External/OptiTrack Unity Plugin/OptiTrack/BaseAvatar 5bone/BaseAvatar - OptiTrack 5 bone.prefab b/Assets/External/OptiTrack Unity Plugin/OptiTrack/BaseAvatar 5bone/BaseAvatar - OptiTrack 5 bone.prefab index c11a77082..9283d285b 100644 --- a/Assets/External/OptiTrack Unity Plugin/OptiTrack/BaseAvatar 5bone/BaseAvatar - OptiTrack 5 bone.prefab +++ b/Assets/External/OptiTrack Unity Plugin/OptiTrack/BaseAvatar 5bone/BaseAvatar - OptiTrack 5 bone.prefab @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f9f72404d4a8e80ae82777cad20b119ac1e0bce3b4ee9e986486dc425d05f0b8 -size 105286 +oid sha256:c13cae3de49bfc0a64f05e61878961f07930f4d960c619a98a1aea461019bbe1 +size 105269 diff --git a/Assets/Resources/StreamingleDashboard/dashboard_script.txt b/Assets/Resources/StreamingleDashboard/dashboard_script.txt index 00a1e5513..67007f197 100644 --- a/Assets/Resources/StreamingleDashboard/dashboard_script.txt +++ b/Assets/Resources/StreamingleDashboard/dashboard_script.txt @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e607c9dd85af7c506d321d5d8bda08aadbea09fcbf59cd882bb67f6ca78e567f -size 76254 +oid sha256:b8b7459084087b43af049ed7eebac07606d982a72a7aada50036e80d515e0ebf +size 72569 diff --git a/Assets/ResourcesData/Project/260219_구슬요방송/260219_구슬요방송.unity b/Assets/ResourcesData/Project/260219_구슬요방송/260219_구슬요방송.unity index dfbb0d43e..e79d5b2e0 100644 --- a/Assets/ResourcesData/Project/260219_구슬요방송/260219_구슬요방송.unity +++ b/Assets/ResourcesData/Project/260219_구슬요방송/260219_구슬요방송.unity @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b3832d539ad41cd948005b8765895034414c5031430f332f911d79a58364527f -size 933520 +oid sha256:d16bd36a59d821e13b32301bfd5caed5f60f822966a5010cc7dd8819b75f6065 +size 906961 diff --git a/Assets/Scripts/KindRetargeting/CustomRetargetingScript.cs b/Assets/Scripts/KindRetargeting/CustomRetargetingScript.cs index 437dcea5f..fc57731ef 100644 --- a/Assets/Scripts/KindRetargeting/CustomRetargetingScript.cs +++ b/Assets/Scripts/KindRetargeting/CustomRetargetingScript.cs @@ -92,9 +92,6 @@ namespace KindRetargeting [Header("바닥 높이 조정")] [SerializeField, Range(-1f, 1f)] public float floorHeight = 0f; // 바닥 높이 조정값 - [Header("발목 높이 설정")] - [SerializeField] public float minimumAnkleHeight = 0.2f; // 수동 설정용 최소 발목 높이 - [Header("머리 회전 오프셋")] [SerializeField, Range(-180f, 180f)] public float headRotationOffsetX = 0f; // 머리 좌우 기울기 (Roll) [SerializeField, Range(-180f, 180f)] public float headRotationOffsetY = 0f; // 머리 좌우 회전 (Yaw) @@ -112,9 +109,6 @@ namespace KindRetargeting [Header("어깨 보정")] [SerializeField] public ShoulderCorrectionFunction shoulderCorrection = new ShoulderCorrectionFunction(); - [Header("발 접지")] - [SerializeField] public FootGroundingController footGrounding = new FootGroundingController(); - [Header("프랍 부착")] [SerializeField] public PropLocationController propLocation = new PropLocationController(); @@ -273,9 +267,6 @@ namespace KindRetargeting if (targetAnimator != null) shoulderCorrection.Initialize(targetAnimator); - // 발 접지 모듈 초기화 - footGrounding.Initialize(ikSolver, targetAnimator); - // 프랍 부착 모듈 초기화 if (targetAnimator != null) propLocation.Initialize(targetAnimator); @@ -749,7 +740,6 @@ namespace KindRetargeting fingerShaped.OnUpdate(); shoulderCorrection.OnUpdate(); limbWeight.OnUpdate(); - footGrounding.OnUpdate(); ikSolver.OnUpdate(); if (!Mathf.Approximately(previousScale, avatarScale)) @@ -764,7 +754,6 @@ namespace KindRetargeting /// void LateUpdate() { - footGrounding.OnLateUpdate(); ApplyHeadRotationOffset(); ApplyHeadScale(); } @@ -1186,7 +1175,7 @@ namespace KindRetargeting GameObject rightArmGoal = CreateIKTargetObject("RightArmGoal", targetAnimator.GetBoneTransform(HumanBodyBones.RightLowerArm), ikTargetsParent); ikSolver.rightArm.bendGoal = rightArmGoal.transform; - // 다리 타겟 (Foot + BendGoal) — 2-bone IK는 Foot까지만 풂, Toes는 FootGrounding에서 처리 + // 다리 타겟 (Foot + BendGoal) GameObject leftFoot = CreateIKTargetObject("LeftFoot", targetAnimator.GetBoneTransform(HumanBodyBones.LeftFoot), ikTargetsParent); ikSolver.leftLeg.target = leftFoot.transform; GameObject rightFoot = CreateIKTargetObject("RightFoot", targetAnimator.GetBoneTransform(HumanBodyBones.RightFoot), ikTargetsParent); @@ -1234,7 +1223,7 @@ namespace KindRetargeting { if (targetAnimator == null) return; - // 손과 발 타겟 업데이트 (다리는 Foot 타겟, FootGrounding이 Toes 처리) + // 손과 발 타겟 업데이트 UpdateEndTarget(ikSolver.leftArm.target, HumanBodyBones.LeftHand); UpdateEndTarget(ikSolver.rightArm.target, HumanBodyBones.RightHand); UpdateEndTarget(ikSolver.leftLeg.target, HumanBodyBones.LeftFoot); diff --git a/Assets/Scripts/KindRetargeting/Editor/CustomRetargetingScriptEditor.cs b/Assets/Scripts/KindRetargeting/Editor/CustomRetargetingScriptEditor.cs index 41227cd6c..0a1f503ff 100644 --- a/Assets/Scripts/KindRetargeting/Editor/CustomRetargetingScriptEditor.cs +++ b/Assets/Scripts/KindRetargeting/Editor/CustomRetargetingScriptEditor.cs @@ -57,7 +57,6 @@ namespace KindRetargeting // ── 바닥 높이 ── var floorFoldout = new Foldout { text = "바닥 높이 조정", value = false }; floorFoldout.Add(new PropertyField(serializedObject.FindProperty("floorHeight"), "바닥 높이 (-1 ~ 1)")); - floorFoldout.Add(new PropertyField(serializedObject.FindProperty("minimumAnkleHeight"), "최소 발목 높이")); root.Add(floorFoldout); // ── 머리 회전 오프셋 ── @@ -69,14 +68,6 @@ namespace KindRetargeting // ── 사지 가중치 (LimbWeight) ── root.Add(BuildLimbWeightSection()); - // ── 접지 설정 (FootGrounding) ── - root.Add(BuildGroundingSection()); - - // ── 손가락 복제 설정 ── - var fingerCopyFoldout = new Foldout { text = "손가락 복제 설정", value = false }; - fingerCopyFoldout.Add(new PropertyField(serializedObject.FindProperty("fingerCopyMode"), "복제 방식")); - root.Add(fingerCopyFoldout); - // ── 손가락 셰이핑 (FingerShaped) ── root.Add(BuildFingerShapedSection()); @@ -258,34 +249,6 @@ namespace KindRetargeting return foldout; } - // ========== 접지 설정 ========== - - private VisualElement BuildGroundingSection() - { - var foldout = new Foldout { text = "접지 설정 (FootGrounding)", value = false }; - - foldout.Add(new HelpBox( - "HIK 스타일 2-Pass 접지 시스템:\n" + - "• Pre-IK: 발이 바닥을 뚫지 않도록 IK 타겟 보정\n" + - "• Post-IK: Foot 회전으로 Toes 접지 미세 보정", - HelpBoxMessageType.Info)); - - foldout.Add(new PropertyField(serializedObject.FindProperty("footGrounding.groundHeight"), "바닥 높이")); - - var weightSlider = new Slider("접지 강도", 0f, 1f) { showInputField = true }; - weightSlider.BindProperty(serializedObject.FindProperty("footGrounding.groundingWeight")); - foldout.Add(weightSlider); - - foldout.Add(new PropertyField(serializedObject.FindProperty("footGrounding.activationHeight"), "활성화 높이") { tooltip = "발목이 이 높이 이상이면 보정 비활성화" }); - foldout.Add(new PropertyField(serializedObject.FindProperty("footGrounding.plantThreshold"), "접지 판정 범위")); - - var smoothField = new Slider("스무딩 속도", 1f, 30f) { showInputField = true }; - smoothField.BindProperty(serializedObject.FindProperty("footGrounding.smoothSpeed")); - foldout.Add(smoothField); - - return foldout; - } - // ========== 손가락 셰이핑 ========== private VisualElement BuildFingerShapedSection() diff --git a/Assets/Scripts/KindRetargeting/Editor/RetargetingControlWindow.cs b/Assets/Scripts/KindRetargeting/Editor/RetargetingControlWindow.cs index 644e23988..0f1edd5a7 100644 --- a/Assets/Scripts/KindRetargeting/Editor/RetargetingControlWindow.cs +++ b/Assets/Scripts/KindRetargeting/Editor/RetargetingControlWindow.cs @@ -228,19 +228,14 @@ public class RetargetingControlWindow : EditorWindow panel.Add(BuildShoulderSection(script, so)); // 접지 설정 - panel.Add(BuildGroundingSection(script, so)); // 손가락 제어 설정 panel.Add(BuildFingerControlSection(script, so)); - // 손가락 복제 설정 - panel.Add(BuildFingerCopySection(script, so)); - // 바닥 높이 설정 var floorFoldout = new Foldout { text = "바닥 높이 설정", value = false }; var floorContainer = new VisualElement(); floorContainer.Add(new PropertyField(so.FindProperty("floorHeight"), "바닥 높이 (-1 ~ 1)")); - floorContainer.Add(new PropertyField(so.FindProperty("minimumAnkleHeight"), "최소 발목 높이")); floorContainer.Bind(so); floorFoldout.Add(floorContainer); panel.Add(floorFoldout); @@ -416,6 +411,7 @@ public class RetargetingControlWindow : EditorWindow // 프리셋 container.Add(BuildFingerPresets(script, script.fingerShaped)); + container.Bind(so); foldout.Add(container); return foldout; } @@ -512,21 +508,6 @@ public class RetargetingControlWindow : EditorWindow return container; } - // ========== Finger Copy ========== - - private VisualElement BuildFingerCopySection(CustomRetargetingScript script, SerializedObject so) - { - var foldout = new Foldout { text = "손가락 복제 설정", value = false }; - var container = new VisualElement(); - - var copyModeProp = so.FindProperty("fingerCopyMode"); - container.Add(new PropertyField(copyModeProp, "복제 방식")); - - container.Bind(so); - foldout.Add(container); - return foldout; - } - // ========== Head Rotation ========== private VisualElement BuildHeadRotationSection(CustomRetargetingScript script, SerializedObject so) @@ -778,30 +759,6 @@ public class RetargetingControlWindow : EditorWindow return foldout; } - // ========== Foot Grounding ========== - - private VisualElement BuildGroundingSection(CustomRetargetingScript script, SerializedObject so) - { - var foldout = new Foldout { text = "접지 설정", value = false }; - var container = new VisualElement(); - - container.Add(new PropertyField(so.FindProperty("footGrounding.groundHeight"), "바닥 높이")); - - var weightSlider = new Slider("접지 강도", 0f, 1f) { showInputField = true }; - weightSlider.BindProperty(so.FindProperty("footGrounding.groundingWeight")); - container.Add(weightSlider); - - container.Add(new PropertyField(so.FindProperty("footGrounding.activationHeight"), "활성화 높이") { tooltip = "발목이 이 높이 이상이면 보정 비활성화" }); - container.Add(new PropertyField(so.FindProperty("footGrounding.plantThreshold"), "접지 판정 범위")); - - var smoothField = new Slider("스무딩 속도", 1f, 30f) { showInputField = true }; - smoothField.BindProperty(so.FindProperty("footGrounding.smoothSpeed")); - container.Add(smoothField); - - container.Bind(so); - foldout.Add(container); - return foldout; - } // ========== Helpers ========== diff --git a/Assets/Scripts/KindRetargeting/FootGroundingController.cs b/Assets/Scripts/KindRetargeting/FootGroundingController.cs deleted file mode 100644 index 58010ea88..000000000 --- a/Assets/Scripts/KindRetargeting/FootGroundingController.cs +++ /dev/null @@ -1,238 +0,0 @@ -using UnityEngine; - -namespace KindRetargeting -{ - /// - /// HIK 스타일 2-Pass 접지 시스템. - /// - /// Pass 1 (OnUpdate, Order 5 → IK 전): - /// IK 타겟 위치를 수정하여 Toes가 바닥을 뚫지 않도록 보정. - /// - /// Pass 2 (OnLateUpdate → IK 후): - /// IK 결과의 잔차를 Foot 회전으로 미세 보정. - /// - [System.Serializable] - public class FootGroundingController - { - [Header("접지 설정")] - [Tooltip("바닥 Y 좌표 (월드 공간)")] - public float groundHeight = 0f; - - [Tooltip("접지 보정 강도")] - [Range(0f, 1f)] - public float groundingWeight = 1f; - - [Tooltip("이 높이 이상이면 AIRBORNE (보정 안 함)")] - public float activationHeight = 0.5f; - - [Tooltip("Toes가 이 범위 안이면 접지 중으로 판정")] - public float plantThreshold = 0.02f; - - [Header("스무딩")] - [Tooltip("보정량 변화 속도 (높을수록 빠른 반응)")] - [Range(1f, 30f)] - public float smoothSpeed = 10f; - - private TwoBoneIKSolver ikSolver; - private Animator animator; - - private Transform leftFoot; - private Transform rightFoot; - private Transform leftToes; - private Transform rightToes; - - private Vector3 leftLocalToesOffset; - private Vector3 rightLocalToesOffset; - - private float leftFootHeight; - private float rightFootHeight; - - private bool leftHasToes; - private bool rightHasToes; - - private float leftPrevAdj; - private float rightPrevAdj; - - private bool isInitialized; - - public void Initialize(TwoBoneIKSolver ikSolver, Animator animator) - { - this.ikSolver = ikSolver; - this.animator = animator; - - if (animator == null || !animator.isHuman || ikSolver == null) return; - - leftFoot = animator.GetBoneTransform(HumanBodyBones.LeftFoot); - rightFoot = animator.GetBoneTransform(HumanBodyBones.RightFoot); - leftToes = animator.GetBoneTransform(HumanBodyBones.LeftToes); - rightToes = animator.GetBoneTransform(HumanBodyBones.RightToes); - - if (leftFoot == null || rightFoot == null) return; - - leftHasToes = leftToes != null; - rightHasToes = rightToes != null; - - if (leftHasToes) - { - leftLocalToesOffset = leftFoot.InverseTransformPoint(leftToes.position); - leftFootHeight = Mathf.Abs(leftFoot.position.y - leftToes.position.y); - } - else - { - leftFootHeight = 0.05f; - } - - if (rightHasToes) - { - rightLocalToesOffset = rightFoot.InverseTransformPoint(rightToes.position); - rightFootHeight = Mathf.Abs(rightFoot.position.y - rightToes.position.y); - } - else - { - rightFootHeight = 0.05f; - } - - isInitialized = true; - } - - /// - /// Pass 1: Pre-IK — IK 타겟 위치를 수정합니다. - /// - public void OnUpdate() - { - if (!isInitialized || groundingWeight < 0.001f) return; - - float leftAdj = AdjustFootTarget( - ikSolver.leftLeg, leftLocalToesOffset, leftFootHeight, - leftHasToes, ikSolver.leftLeg.positionWeight); - float rightAdj = AdjustFootTarget( - ikSolver.rightLeg, rightLocalToesOffset, rightFootHeight, - rightHasToes, ikSolver.rightLeg.positionWeight); - - float dt = Time.deltaTime * smoothSpeed; - leftPrevAdj = Mathf.Lerp(leftPrevAdj, leftAdj, Mathf.Clamp01(dt)); - rightPrevAdj = Mathf.Lerp(rightPrevAdj, rightAdj, Mathf.Clamp01(dt)); - } - - private float AdjustFootTarget(TwoBoneIKSolver.LimbIK limb, Vector3 localToesOffset, - float footHeight, bool hasToes, float ikWeight) - { - if (limb.target == null || ikWeight < 0.01f) return 0f; - - Transform ankleTarget = limb.target; - Vector3 anklePos = ankleTarget.position; - float ankleY = anklePos.y; - - if (ankleY - groundHeight > activationHeight) return 0f; - - float weight = groundingWeight * ikWeight; - - if (!hasToes) - { - float minAnkleY = groundHeight + footHeight; - if (ankleY < minAnkleY) - { - float adj = (minAnkleY - ankleY) * weight; - anklePos.y += adj; - ankleTarget.position = anklePos; - return adj; - } - return 0f; - } - - Vector3 predictedToesWorld = anklePos + ankleTarget.rotation * localToesOffset; - float predictedToesY = predictedToesWorld.y; - - float adjustment = 0f; - - if (predictedToesY < groundHeight + plantThreshold) - { - float toesError = groundHeight - predictedToesY; - - if (ankleY < groundHeight + footHeight + plantThreshold) - { - float minAnkleY = groundHeight + footHeight; - if (ankleY < minAnkleY) - { - adjustment = (minAnkleY - ankleY) * weight; - anklePos.y += adjustment; - } - - if (toesError > 0f) - { - float extra = toesError * weight; - anklePos.y += extra; - adjustment += extra; - } - } - else - { - if (toesError > 0f) - { - adjustment = toesError * weight; - anklePos.y += adjustment; - } - } - - ankleTarget.position = anklePos; - } - else - { - float minAnkleY = groundHeight + footHeight; - if (ankleY < minAnkleY) - { - adjustment = (minAnkleY - ankleY) * weight; - anklePos.y += adjustment; - ankleTarget.position = anklePos; - } - } - - return adjustment; - } - - /// - /// Pass 2: Post-IK — 잔차를 Foot 회전으로 미세 보정합니다. - /// - public void OnLateUpdate() - { - if (!isInitialized || groundingWeight < 0.001f) return; - - if (leftHasToes) - AlignFootToGround(leftFoot, leftToes, ikSolver.leftLeg.positionWeight); - if (rightHasToes) - AlignFootToGround(rightFoot, rightToes, ikSolver.rightLeg.positionWeight); - } - - private void AlignFootToGround(Transform foot, Transform toes, float ikWeight) - { - if (foot == null || toes == null) return; - if (ikWeight < 0.01f) return; - - if (foot.position.y - groundHeight > activationHeight) return; - - float weight = groundingWeight * ikWeight; - - float actualToesY = toes.position.y; - float error = actualToesY - groundHeight; - - if (Mathf.Abs(error) < 0.001f) return; - - if (error > plantThreshold) return; - - Vector3 footToToes = toes.position - foot.position; - float horizontalDist = new Vector2(footToToes.x, footToToes.z).magnitude; - - if (horizontalDist < 0.001f) return; - - float pitchAngle = Mathf.Atan2(error, horizontalDist) * Mathf.Rad2Deg; - - Vector3 footForwardFlat = new Vector3(footToToes.x, 0f, footToToes.z).normalized; - Vector3 pitchAxis = Vector3.Cross(Vector3.up, footForwardFlat); - if (pitchAxis.sqrMagnitude < 0.001f) return; - pitchAxis.Normalize(); - - Quaternion correction = Quaternion.AngleAxis(-pitchAngle, pitchAxis); - foot.rotation = Quaternion.Slerp(foot.rotation, correction * foot.rotation, weight); - } - } -} diff --git a/Assets/Scripts/KindRetargeting/FootGroundingController.cs.meta b/Assets/Scripts/KindRetargeting/FootGroundingController.cs.meta deleted file mode 100644 index 7f32b94d4..000000000 --- a/Assets/Scripts/KindRetargeting/FootGroundingController.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 1752f1fe3cee0b24e95ae0d5a0468865 \ No newline at end of file diff --git a/Assets/Scripts/KindRetargeting/README.md b/Assets/Scripts/KindRetargeting/README.md index 0ead25e2f..131cb3bd5 100644 --- a/Assets/Scripts/KindRetargeting/README.md +++ b/Assets/Scripts/KindRetargeting/README.md @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a468d775910756cb5761bd2c4202e8ddf3a65f215153d753fc91c9711d4d868c -size 14396 +oid sha256:567b19df1dd4fa2f6eea49fcc8e5c6287318a3caf51cdeb8a692a4e2c2733019 +size 13245 diff --git a/Assets/Scripts/KindRetargeting/Remote/RetargetingRemoteController.cs b/Assets/Scripts/KindRetargeting/Remote/RetargetingRemoteController.cs index 9887a4b77..b42dc0bed 100644 --- a/Assets/Scripts/KindRetargeting/Remote/RetargetingRemoteController.cs +++ b/Assets/Scripts/KindRetargeting/Remote/RetargetingRemoteController.cs @@ -288,13 +288,6 @@ namespace KindRetargeting.Remote { "shoulderReverseLeft", script.shoulderCorrection.reverseLeftRotation }, { "shoulderReverseRight", script.shoulderCorrection.reverseRightRotation }, - // FootGrounding 데이터 - { "groundingWeight", script.footGrounding.groundingWeight }, - { "groundingGroundHeight", script.footGrounding.groundHeight }, - { "groundingActivationHeight", script.footGrounding.activationHeight }, - { "groundingPlantThreshold", script.footGrounding.plantThreshold }, - { "groundingSmoothSpeed", script.footGrounding.smoothSpeed }, - // FingerShapedController 데이터 { "handPoseEnabled", script.fingerShaped.enabled }, { "leftHandEnabled", script.fingerShaped.leftHandEnabled }, @@ -312,8 +305,6 @@ namespace KindRetargeting.Remote { "rightPinkyCurl", script.fingerShaped.rightPinkyCurl }, { "rightSpreadFingers", script.fingerShaped.rightSpreadFingers }, - // 최소 발목 높이 - { "minimumAnkleHeight", GetPrivateField(script, "minimumAnkleHeight") }, }; var response = new @@ -449,23 +440,6 @@ namespace KindRetargeting.Remote script.shoulderCorrection.reverseRightRotation = value > 0.5f; break; - // FootGrounding 속성 - case "groundingWeight": - script.footGrounding.groundingWeight = value; - break; - case "groundingGroundHeight": - script.footGrounding.groundHeight = value; - break; - case "groundingActivationHeight": - script.footGrounding.activationHeight = value; - break; - case "groundingPlantThreshold": - script.footGrounding.plantThreshold = value; - break; - case "groundingSmoothSpeed": - script.footGrounding.smoothSpeed = value; - break; - // FingerShapedController 속성 case "handPoseEnabled": script.fingerShaped.enabled = value > 0.5f; @@ -519,11 +493,6 @@ namespace KindRetargeting.Remote script.fingerShaped.rightSpreadFingers = value; break; - // 최소 발목 높이 - case "minimumAnkleHeight": - SetPrivateField(script, "minimumAnkleHeight", value); - break; - default: Debug.LogWarning($"[RetargetingRemote] 알 수 없는 속성: {property}"); break;