diff --git a/Assets/External/OptiTrack Unity Plugin/OptiTrack/Scripts/OptitrackSkeletonAnimator_Mingle.cs b/Assets/External/OptiTrack Unity Plugin/OptiTrack/Scripts/OptitrackSkeletonAnimator_Mingle.cs index 92a59acb8..9cab1d013 100644 --- a/Assets/External/OptiTrack Unity Plugin/OptiTrack/Scripts/OptitrackSkeletonAnimator_Mingle.cs +++ b/Assets/External/OptiTrack Unity Plugin/OptiTrack/Scripts/OptitrackSkeletonAnimator_Mingle.cs @@ -39,6 +39,11 @@ public class OptitrackSkeletonAnimator_Mingle : MonoBehaviour [HideInInspector] public FilterStrength filterStrength = FilterStrength.Medium; + [Header("어깨 증폭")] + [Tooltip("어깨 회전을 증폭합니다. 1 = 원본, 2 = 2배. 하위 체인(상완)은 자동 역보정되어 손 위치가 유지됩니다.")] + [Range(0f, 10f)] + public float shoulderAmplify = 2f; + [HideInInspector] public float filterMinCutoff = 3.0f; [HideInInspector] public float filterBeta = 1.5f; [HideInInspector] public float filterMaxCutoff = 15.0f; @@ -423,6 +428,39 @@ public class OptitrackSkeletonAnimator_Mingle : MonoBehaviour } } + // ── 어깨 증폭 + 상완 역보정 (1.0이면 스킵) ── + if (Mathf.Abs(shoulderAmplify - 1f) > 0.001f) + { + AmplifyShoulderWithCompensation("LShoulder", "LUArm"); + AmplifyShoulderWithCompensation("RShoulder", "RUArm"); + } + + } + + /// + /// 어깨 회전을 rest pose 대비 증폭하고, 상완에서 추가 회전분을 상쇄하여 손 위치를 보존합니다. + /// + private void AmplifyShoulderWithCompensation(string shoulderName, string upperArmName) + { + Transform shoulder = GetMappedTransform(shoulderName); + Transform upperArm = GetMappedTransform(upperArmName); + if (shoulder == null || upperArm == null) return; + + // rest pose 대비 델타 추출 → 증폭 + Quaternion restRot = GetRestLocalRotation(shoulderName); + Quaternion currentRot = shoulder.localRotation; + Quaternion delta = Quaternion.Inverse(restRot) * currentRot; + Quaternion amplifiedDelta = Quaternion.SlerpUnclamped(Quaternion.identity, delta, shoulderAmplify); + Quaternion amplifiedRot = restRot * amplifiedDelta; + + // 추가된 회전량 + Quaternion extraRotation = Quaternion.Inverse(currentRot) * amplifiedRot; + + // 어깨에 증폭 적용 + shoulder.localRotation = amplifiedRot; + + // 상완에서 추가분 상쇄 (손 위치 보존) + upperArm.localRotation = Quaternion.Inverse(extraRotation) * upperArm.localRotation; } /// diff --git a/Assets/Resources/StreamingleDashboard/dashboard_script.txt b/Assets/Resources/StreamingleDashboard/dashboard_script.txt index 67007f197..237d666e1 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:b8b7459084087b43af049ed7eebac07606d982a72a7aada50036e80d515e0ebf -size 72569 +oid sha256:0013b010a51681ed769b4fbfb6b9792f2b0926627cbe42adb43d6e4851194144 +size 70967 diff --git a/Assets/Scripts/KindRetargeting/CustomRetargetingScript.cs b/Assets/Scripts/KindRetargeting/CustomRetargetingScript.cs index 52d8f3bd8..bccfa7c7c 100644 --- a/Assets/Scripts/KindRetargeting/CustomRetargetingScript.cs +++ b/Assets/Scripts/KindRetargeting/CustomRetargetingScript.cs @@ -106,9 +106,6 @@ namespace KindRetargeting [HideInInspector] public Vector3 tPoseHeadForward = Vector3.forward; [HideInInspector] public Vector3 tPoseHeadUp = Vector3.up; - [Header("어깨 보정")] - [SerializeField] public ShoulderCorrectionFunction shoulderCorrection = new ShoulderCorrectionFunction(); - [Header("프랍 부착")] [SerializeField] public PropLocationController propLocation = new PropLocationController(); @@ -263,10 +260,6 @@ namespace KindRetargeting // OptiTrack 스파인 분배 초기화 InitializeOptiTrackSpineDistribution(); - // 어깨 보정 모듈 초기화 - if (targetAnimator != null) - shoulderCorrection.Initialize(targetAnimator); - // 프랍 부착 모듈 초기화 if (targetAnimator != null) propLocation.Initialize(targetAnimator); @@ -738,7 +731,6 @@ namespace KindRetargeting // 손가락은 SyncBoneRotations에서 함께 처리됨 (lastBoneIndex=55) fingerShaped.OnUpdate(); - shoulderCorrection.OnUpdate(); limbWeight.OnUpdate(); ikSolver.OnUpdate(); diff --git a/Assets/Scripts/KindRetargeting/Editor/CustomRetargetingScriptEditor.cs b/Assets/Scripts/KindRetargeting/Editor/CustomRetargetingScriptEditor.cs index 0a1f503ff..249550aa4 100644 --- a/Assets/Scripts/KindRetargeting/Editor/CustomRetargetingScriptEditor.cs +++ b/Assets/Scripts/KindRetargeting/Editor/CustomRetargetingScriptEditor.cs @@ -62,9 +62,6 @@ namespace KindRetargeting // ── 머리 회전 오프셋 ── root.Add(BuildHeadRotationSection()); - // ── 어깨 보정 (ShoulderCorrection) ── - root.Add(BuildShoulderSection()); - // ── 사지 가중치 (LimbWeight) ── root.Add(BuildLimbWeightSection()); @@ -185,33 +182,6 @@ namespace KindRetargeting return foldout; } - // ========== 어깨 보정 ========== - - private VisualElement BuildShoulderSection() - { - var foldout = new Foldout { text = "어깨 보정 (ShoulderCorrection)", value = false }; - - var strength = new Slider("블렌드 강도", 0f, 5f) { showInputField = true }; - strength.BindProperty(serializedObject.FindProperty("shoulderCorrection.blendStrength")); - foldout.Add(strength); - - var maxBlend = new Slider("최대 블렌드", 0f, 1f) { showInputField = true }; - maxBlend.BindProperty(serializedObject.FindProperty("shoulderCorrection.maxShoulderBlend")); - foldout.Add(maxBlend); - - foldout.Add(new PropertyField(serializedObject.FindProperty("shoulderCorrection.reverseLeftRotation"), "왼쪽 회전 반전")); - foldout.Add(new PropertyField(serializedObject.FindProperty("shoulderCorrection.reverseRightRotation"), "오른쪽 회전 반전")); - - foldout.Add(BuildMinMaxRange("높이 차이 범위", - serializedObject.FindProperty("shoulderCorrection.minHeightDifference"), - serializedObject.FindProperty("shoulderCorrection.maxHeightDifference"), - -0.5f, 2f)); - - foldout.Add(new PropertyField(serializedObject.FindProperty("shoulderCorrection.shoulderCorrectionCurve"), "보정 커브")); - - return foldout; - } - // ========== 사지 가중치 ========== private VisualElement BuildLimbWeightSection() diff --git a/Assets/Scripts/KindRetargeting/Editor/RetargetingControlWindow.cs b/Assets/Scripts/KindRetargeting/Editor/RetargetingControlWindow.cs index 0f1edd5a7..a141fa345 100644 --- a/Assets/Scripts/KindRetargeting/Editor/RetargetingControlWindow.cs +++ b/Assets/Scripts/KindRetargeting/Editor/RetargetingControlWindow.cs @@ -224,9 +224,6 @@ public class RetargetingControlWindow : EditorWindow footContainer.Bind(so); panel.Add(footFoldout); - // 어깨 보정 - panel.Add(BuildShoulderSection(script, so)); - // 접지 설정 // 손가락 제어 설정 @@ -729,37 +726,6 @@ public class RetargetingControlWindow : EditorWindow return box; } - // ========== Shoulder Correction ========== - - private VisualElement BuildShoulderSection(CustomRetargetingScript script, SerializedObject so) - { - var foldout = new Foldout { text = "어깨 보정", value = false }; - var container = new VisualElement(); - - var strength = new Slider("블렌드 강도", 0f, 5f) { showInputField = true }; - strength.BindProperty(so.FindProperty("shoulderCorrection.blendStrength")); - container.Add(strength); - - var maxBlend = new Slider("최대 블렌드", 0f, 1f) { showInputField = true }; - maxBlend.BindProperty(so.FindProperty("shoulderCorrection.maxShoulderBlend")); - container.Add(maxBlend); - - container.Add(new PropertyField(so.FindProperty("shoulderCorrection.reverseLeftRotation"), "왼쪽 회전 반전")); - container.Add(new PropertyField(so.FindProperty("shoulderCorrection.reverseRightRotation"), "오른쪽 회전 반전")); - - container.Add(BuildMinMaxRange("높이 차이 범위", - so.FindProperty("shoulderCorrection.minHeightDifference"), - so.FindProperty("shoulderCorrection.maxHeightDifference"), - -0.5f, 2f, so)); - - container.Add(new PropertyField(so.FindProperty("shoulderCorrection.shoulderCorrectionCurve"), "보정 커브")); - - container.Bind(so); - foldout.Add(container); - return foldout; - } - - // ========== Helpers ========== private VisualElement BuildMinMaxRange(string label, SerializedProperty minProp, SerializedProperty maxProp, float limitMin, float limitMax, SerializedObject so) diff --git a/Assets/Scripts/KindRetargeting/Remote/RetargetingRemoteController.cs b/Assets/Scripts/KindRetargeting/Remote/RetargetingRemoteController.cs index b42dc0bed..fef4d3414 100644 --- a/Assets/Scripts/KindRetargeting/Remote/RetargetingRemoteController.cs +++ b/Assets/Scripts/KindRetargeting/Remote/RetargetingRemoteController.cs @@ -280,14 +280,6 @@ namespace KindRetargeting.Remote { "enableLeftArmIK", script.limbWeight.enableLeftArmIK }, { "enableRightArmIK", script.limbWeight.enableRightArmIK }, - // ShoulderCorrection 데이터 - { "shoulderBlendStrength", script.shoulderCorrection.blendStrength }, - { "shoulderMaxBlend", script.shoulderCorrection.maxShoulderBlend }, - { "shoulderMaxHeightDiff", script.shoulderCorrection.maxHeightDifference }, - { "shoulderMinHeightDiff", script.shoulderCorrection.minHeightDifference }, - { "shoulderReverseLeft", script.shoulderCorrection.reverseLeftRotation }, - { "shoulderReverseRight", script.shoulderCorrection.reverseRightRotation }, - // FingerShapedController 데이터 { "handPoseEnabled", script.fingerShaped.enabled }, { "leftHandEnabled", script.fingerShaped.leftHandEnabled }, @@ -420,26 +412,6 @@ namespace KindRetargeting.Remote script.limbWeight.enableRightArmIK = value > 0.5f; break; - // ShoulderCorrection 속성 - case "shoulderBlendStrength": - script.shoulderCorrection.blendStrength = value; - break; - case "shoulderMaxBlend": - script.shoulderCorrection.maxShoulderBlend = value; - break; - case "shoulderMaxHeightDiff": - script.shoulderCorrection.maxHeightDifference = value; - break; - case "shoulderMinHeightDiff": - script.shoulderCorrection.minHeightDifference = value; - break; - case "shoulderReverseLeft": - script.shoulderCorrection.reverseLeftRotation = value > 0.5f; - break; - case "shoulderReverseRight": - script.shoulderCorrection.reverseRightRotation = value > 0.5f; - break; - // FingerShapedController 속성 case "handPoseEnabled": script.fingerShaped.enabled = value > 0.5f; diff --git a/Assets/Scripts/KindRetargeting/ShoulderCorrectionFunction.cs b/Assets/Scripts/KindRetargeting/ShoulderCorrectionFunction.cs deleted file mode 100644 index 434a0f70a..000000000 --- a/Assets/Scripts/KindRetargeting/ShoulderCorrectionFunction.cs +++ /dev/null @@ -1,93 +0,0 @@ -using UnityEngine; - -namespace KindRetargeting -{ - [System.Serializable] - public class ShoulderCorrectionFunction - { - [Header("설정")] - [Range(0f, 5f)] - public float blendStrength = 2f; - [Range(0f, 1f)] - public float maxShoulderBlend = 0.7f; - public bool reverseLeftRotation = false; - public bool reverseRightRotation = false; - - [Header("높이 제한 설정")] - public float maxHeightDifference = 0.8f; - public float minHeightDifference = -0.1f; - - [Header("보정 커브 설정")] - public AnimationCurve shoulderCorrectionCurve = AnimationCurve.Linear(0f, 0f, 1f, 1f); - - private float leftBlendWeight = 0f; - private float rightBlendWeight = 0f; - - private Transform leftShoulder; - private Transform rightShoulder; - private Transform leftUpperArm; - private Transform rightUpperArm; - private Transform leftLowerArm; - private Transform rightLowerArm; - - public void Initialize(Animator targetAnimator) - { - leftShoulder = targetAnimator.GetBoneTransform(HumanBodyBones.LeftShoulder); - rightShoulder = targetAnimator.GetBoneTransform(HumanBodyBones.RightShoulder); - leftUpperArm = targetAnimator.GetBoneTransform(HumanBodyBones.LeftUpperArm); - rightUpperArm = targetAnimator.GetBoneTransform(HumanBodyBones.RightUpperArm); - leftLowerArm = targetAnimator.GetBoneTransform(HumanBodyBones.LeftLowerArm); - rightLowerArm = targetAnimator.GetBoneTransform(HumanBodyBones.RightLowerArm); - } - - public void OnUpdate() - { - if (leftShoulder == null || rightShoulder == null) return; - - // 왼쪽 어깨 보정 - Vector3 leftElbowPos = leftLowerArm.position; - float leftHeightDiff = leftElbowPos.y - leftShoulder.position.y; - float leftRawBlend = Mathf.Clamp01( - Mathf.InverseLerp(minHeightDifference, maxHeightDifference, leftHeightDiff) * blendStrength - ); - leftBlendWeight = shoulderCorrectionCurve.Evaluate(leftRawBlend) * maxShoulderBlend; - - // 오른쪽 어깨 보정 - Vector3 rightElbowPos = rightLowerArm.position; - float rightHeightDiff = rightElbowPos.y - rightShoulder.position.y; - float rightRawBlend = Mathf.Clamp01( - Mathf.InverseLerp(minHeightDifference, maxHeightDifference, rightHeightDiff) * blendStrength - ); - rightBlendWeight = shoulderCorrectionCurve.Evaluate(rightRawBlend) * maxShoulderBlend; - - // 어깨와 윗팔 회전 보정 적용 - if (leftBlendWeight > 0.01f) - { - Quaternion currentWorldShoulderRot = leftShoulder.rotation; - Quaternion currentWorldArmRot = leftUpperArm.rotation; - - Vector3 shoulderToArm = (leftUpperArm.position - leftShoulder.position).normalized; - Quaternion targetRotation = Quaternion.FromToRotation(leftShoulder.forward, - reverseLeftRotation ? shoulderToArm : -shoulderToArm); - Quaternion targetWorldShoulderRot = targetRotation * currentWorldShoulderRot; - - leftShoulder.rotation = Quaternion.Lerp(currentWorldShoulderRot, targetWorldShoulderRot, leftBlendWeight); - leftUpperArm.rotation = currentWorldArmRot; - } - - if (rightBlendWeight > 0.01f) - { - Quaternion currentWorldShoulderRot = rightShoulder.rotation; - Quaternion currentWorldArmRot = rightUpperArm.rotation; - - Vector3 shoulderToArm = (rightUpperArm.position - rightShoulder.position).normalized; - Quaternion targetRotation = Quaternion.FromToRotation(rightShoulder.forward, - reverseRightRotation ? -shoulderToArm : shoulderToArm); - Quaternion targetWorldShoulderRot = targetRotation * currentWorldShoulderRot; - - rightShoulder.rotation = Quaternion.Lerp(currentWorldShoulderRot, targetWorldShoulderRot, rightBlendWeight); - rightUpperArm.rotation = currentWorldArmRot; - } - } - } -} diff --git a/Assets/Scripts/KindRetargeting/ShoulderCorrectionFunction.cs.meta b/Assets/Scripts/KindRetargeting/ShoulderCorrectionFunction.cs.meta deleted file mode 100644 index 75696218d..000000000 --- a/Assets/Scripts/KindRetargeting/ShoulderCorrectionFunction.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: d017e8fb3cc30d5448512160bfea29f5 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: