Fix : 머리 회전 기능 업데이트

This commit is contained in:
KINDNICK 2025-12-20 23:14:07 +09:00
parent 580b64894c
commit 98676d49db
2 changed files with 91 additions and 0 deletions

View File

@ -108,6 +108,11 @@ namespace KindRetargeting
[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)
[SerializeField, Range(-180f, 180f)] public float headRotationOffsetZ = 0f; // 머리 상하 회전 (Pitch)
[Header("설정 저장/로드")]
[SerializeField] private string settingsFolderName = "RetargetingSettings";
@ -171,6 +176,10 @@ namespace KindRetargeting
public List<MuscleCalibrationData> sourceMuscleCalibrationCache;
// 의자 앉기 높이 오프셋 (LimbWeightController)
public float chairSeatHeightOffset;
// 머리 회전 오프셋
public float headRotationOffsetX;
public float headRotationOffsetY;
public float headRotationOffsetZ;
}
[System.Serializable]
@ -685,6 +694,9 @@ namespace KindRetargeting
fingerCloseRotationsCache = fingerCloseCache,
sourceMuscleCalibrationCache = muscleCalibrationCache,
chairSeatHeightOffset = chairOffset,
headRotationOffsetX = headRotationOffsetX,
headRotationOffsetY = headRotationOffsetY,
headRotationOffsetZ = headRotationOffsetZ,
};
string json = JsonUtility.ToJson(settings, true);
@ -785,6 +797,11 @@ namespace KindRetargeting
limbController.chairSeatHeightOffset = settings.chairSeatHeightOffset;
}
// 머리 회전 오프셋 로드
headRotationOffsetX = settings.headRotationOffsetX;
headRotationOffsetY = settings.headRotationOffsetY;
headRotationOffsetZ = settings.headRotationOffsetZ;
//너무 자주 출력되어서 주석처리
//Debug.Log($"설정을 로드했습니다: {filePath}");
}
@ -1356,6 +1373,36 @@ namespace KindRetargeting
previousScale = avatarScale;
}
}
/// <summary>
/// LateUpdate에서 머리 회전 오프셋을 적용합니다 (IK 이후 실행).
/// </summary>
void LateUpdate()
{
ApplyHeadRotationOffset();
}
/// <summary>
/// 머리 본에 회전 오프셋을 적용합니다.
/// </summary>
private void ApplyHeadRotationOffset()
{
if (targetAnimator == null) return;
// 머리 본 가져오기
Transform headBone = targetAnimator.GetBoneTransform(HumanBodyBones.Head);
if (headBone == null) return;
// 오프셋이 모두 0이면 스킵
if (Mathf.Approximately(headRotationOffsetX, 0f) &&
Mathf.Approximately(headRotationOffsetY, 0f) &&
Mathf.Approximately(headRotationOffsetZ, 0f))
return;
// 오프셋 적용 (로컬 회전에 오일러 각도 추가)
Quaternion offsetRotation = Quaternion.Euler(headRotationOffsetX, headRotationOffsetY, headRotationOffsetZ);
headBone.localRotation *= offsetRotation;
}
/// <summary>
/// 머슬 데이터를 사용하여 손가락 포즈를 복제합니다.
/// SetHumanPose가 모든 본에 영향을 미치므로, 손가락을 제외한 모든 본의 Transform을 저장하고 복원합니다.

View File

@ -27,6 +27,7 @@ public class RetargetingControlWindow : EditorWindow
private Dictionary<int, bool> rightHandFoldouts = new Dictionary<int, bool>();
private Dictionary<int, bool> headFoldouts = new Dictionary<int, bool>();
private Dictionary<int, bool> propControlFoldouts = new Dictionary<int, bool>();
private Dictionary<int, bool> headRotationFoldouts = new Dictionary<int, bool>();
private bool isDirty = false;
private double lastUpdateTime;
@ -233,6 +234,7 @@ public class RetargetingControlWindow : EditorWindow
if (!rightHandFoldouts.ContainsKey(instanceID)) rightHandFoldouts[instanceID] = false;
if (!headFoldouts.ContainsKey(instanceID)) headFoldouts[instanceID] = false;
if (!propControlFoldouts.ContainsKey(instanceID)) propControlFoldouts[instanceID] = false;
if (!headRotationFoldouts.ContainsKey(instanceID)) headRotationFoldouts[instanceID] = false;
SerializedObject serializedObject = new SerializedObject(script);
@ -387,6 +389,15 @@ public class RetargetingControlWindow : EditorWindow
EditorGUI.indentLevel--;
}
// 머리 회전 오프셋 설정 섹션
headRotationFoldouts[instanceID] = EditorGUILayout.Foldout(headRotationFoldouts[instanceID], "머리 회전 오프셋", true);
if (headRotationFoldouts[instanceID])
{
EditorGUI.indentLevel++;
DrawHeadRotationSettings(serializedObject);
EditorGUI.indentLevel--;
}
// 프랍 컨트롤 설정
propControlFoldouts[instanceID] = EditorGUILayout.Foldout(propControlFoldouts[instanceID], "프랍 설정", true);
if (propControlFoldouts[instanceID])
@ -1114,6 +1125,39 @@ public class RetargetingControlWindow : EditorWindow
}
}
// 머리 회전 오프셋 UI 그리기 함수
private void DrawHeadRotationSettings(SerializedObject serializedObject)
{
EditorGUI.BeginChangeCheck();
var headRotationXProp = serializedObject.FindProperty("headRotationOffsetX");
var headRotationYProp = serializedObject.FindProperty("headRotationOffsetY");
var headRotationZProp = serializedObject.FindProperty("headRotationOffsetZ");
EditorGUILayout.Slider(headRotationXProp, -180f, 180f,
new GUIContent("X (Roll) - 좌우 기울기", "머리를 좌우로 기울입니다"));
EditorGUILayout.Slider(headRotationYProp, -180f, 180f,
new GUIContent("Y (Yaw) - 좌우 회전", "머리를 좌우로 회전합니다"));
EditorGUILayout.Slider(headRotationZProp, -180f, 180f,
new GUIContent("Z (Pitch) - 상하 회전", "머리를 상하로 회전합니다"));
// 초기화 버튼
EditorGUILayout.Space(5);
if (GUILayout.Button("회전 초기화", GUILayout.Height(25)))
{
headRotationXProp.floatValue = 0f;
headRotationYProp.floatValue = 0f;
headRotationZProp.floatValue = 0f;
serializedObject.ApplyModifiedProperties();
EditorUtility.SetDirty(serializedObject.targetObject);
}
if (EditorGUI.EndChangeCheck())
{
serializedObject.ApplyModifiedProperties();
}
}
// 창이 포커스를 얻거나 잃을 때 리페인트
private void OnFocus()
{