315 lines
14 KiB
C#
315 lines
14 KiB
C#
using UnityEditor;
|
|
using UnityEngine;
|
|
|
|
namespace KindRetargeting
|
|
{
|
|
/// <summary>
|
|
/// CustomRetargetingScript의 인스펙터를 커스터마이징하여 프로퍼티들을 접었다 펼칠 수 있는 섹션으로 그룹화합니다.
|
|
/// </summary>
|
|
[CustomEditor(typeof(CustomRetargetingScript))]
|
|
public class CustomRetargetingScriptEditor : BaseRetargetingEditor
|
|
{
|
|
// Foldout 상태를 저장할 변수들
|
|
private bool showHipsSettings = false;
|
|
private bool showFingerCopySettings = false;
|
|
private bool showMotionFilterSettings = false;
|
|
private bool showRoughMotionSettings = false;
|
|
private bool showKneeSettings = true;
|
|
private bool showFootSettings = true;
|
|
private bool hasCachedData = false;
|
|
private bool showFloorSettings = false;
|
|
private bool showScaleSettings = true;
|
|
|
|
// SerializedProperty 변수들
|
|
private SerializedProperty sourceAnimatorProp;
|
|
private SerializedProperty targetAnimatorProp;
|
|
private SerializedProperty hipsOffsetXProp;
|
|
private SerializedProperty hipsOffsetYProp;
|
|
private SerializedProperty hipsOffsetZProp;
|
|
private SerializedProperty debugAxisNormalizerProp;
|
|
private SerializedProperty fingerCopyModeProp;
|
|
private SerializedProperty useMotionFilterProp;
|
|
private SerializedProperty filterBufferSizeProp;
|
|
private SerializedProperty useBodyRoughMotionProp;
|
|
private SerializedProperty useFingerRoughMotionProp;
|
|
private SerializedProperty bodyRoughnessProp;
|
|
private SerializedProperty fingerRoughnessProp;
|
|
private SerializedProperty kneeInOutWeightProp;
|
|
private SerializedProperty kneeFrontBackWeightProp;
|
|
private SerializedProperty footFrontBackOffsetProp;
|
|
private SerializedProperty footInOutOffsetProp;
|
|
private SerializedProperty floorHeightProp;
|
|
private SerializedProperty avatarScaleProp;
|
|
|
|
protected override void OnEnable()
|
|
{
|
|
base.OnEnable();
|
|
// SerializedProperty 초기화
|
|
sourceAnimatorProp = serializedObject.FindProperty("sourceAnimator");
|
|
targetAnimatorProp = serializedObject.FindProperty("targetAnimator");
|
|
hipsOffsetXProp = serializedObject.FindProperty("hipsOffsetX");
|
|
hipsOffsetYProp = serializedObject.FindProperty("hipsOffsetY");
|
|
hipsOffsetZProp = serializedObject.FindProperty("hipsOffsetZ");
|
|
debugAxisNormalizerProp = serializedObject.FindProperty("debugAxisNormalizer");
|
|
fingerCopyModeProp = serializedObject.FindProperty("fingerCopyMode");
|
|
useMotionFilterProp = serializedObject.FindProperty("useMotionFilter");
|
|
filterBufferSizeProp = serializedObject.FindProperty("filterBufferSize");
|
|
useBodyRoughMotionProp = serializedObject.FindProperty("useBodyRoughMotion");
|
|
useFingerRoughMotionProp = serializedObject.FindProperty("useFingerRoughMotion");
|
|
bodyRoughnessProp = serializedObject.FindProperty("bodyRoughness");
|
|
fingerRoughnessProp = serializedObject.FindProperty("fingerRoughness");
|
|
kneeInOutWeightProp = serializedObject.FindProperty("kneeInOutWeight");
|
|
kneeFrontBackWeightProp = serializedObject.FindProperty("kneeFrontBackWeight");
|
|
footFrontBackOffsetProp = serializedObject.FindProperty("footFrontBackOffset");
|
|
footInOutOffsetProp = serializedObject.FindProperty("footInOutOffset");
|
|
floorHeightProp = serializedObject.FindProperty("floorHeight");
|
|
avatarScaleProp = serializedObject.FindProperty("avatarScale");
|
|
}
|
|
|
|
private void CheckCacheStatus()
|
|
{
|
|
var script = (CustomRetargetingScript)target;
|
|
hasCachedData = script.HasCachedSettings();
|
|
}
|
|
|
|
public override void OnInspectorGUI()
|
|
{
|
|
serializedObject.Update();
|
|
|
|
EditorGUI.BeginChangeCheck();
|
|
|
|
GUILayout.Space(10);
|
|
|
|
// sourceAnimator를 한 번만 표시
|
|
EditorGUILayout.PropertyField(sourceAnimatorProp, new GUIContent("원본 Animator"));
|
|
|
|
GUILayout.Space(10);
|
|
|
|
// 아바타 크기 조정 섹션 추가
|
|
showScaleSettings = EditorGUILayout.Foldout(showScaleSettings, "아바타 크기 설정", true);
|
|
if (showScaleSettings)
|
|
{
|
|
EditorGUI.indentLevel++;
|
|
EditorGUILayout.PropertyField(avatarScaleProp, new GUIContent("아바타 크기"));
|
|
EditorGUI.indentLevel--;
|
|
}
|
|
|
|
GUILayout.Space(5);
|
|
|
|
// 힙 위치 보정 Foldout
|
|
showHipsSettings = EditorGUILayout.Foldout(showHipsSettings, "힙 위치 보정 (로컬 좌표계)");
|
|
if (showHipsSettings)
|
|
{
|
|
EditorGUI.indentLevel++;
|
|
|
|
// 축 매핑 정보 표시
|
|
if (debugAxisNormalizerProp != null)
|
|
{
|
|
Vector3 axisMapping = debugAxisNormalizerProp.vector3Value;
|
|
|
|
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
|
EditorGUILayout.LabelField("축 매핑 정보 (T-포즈 기준)", EditorStyles.boldLabel);
|
|
|
|
// 플레이 모드에서만 정확한 정보 표시
|
|
if (Application.isPlaying && axisMapping != Vector3.one)
|
|
{
|
|
// axisMapping: 1=X, 2=Y, 3=Z, 부호는 방향
|
|
string GetAxisName(float value)
|
|
{
|
|
int axis = Mathf.RoundToInt(Mathf.Abs(value));
|
|
string sign = value > 0 ? "+" : "-";
|
|
return axis switch
|
|
{
|
|
1 => $"{sign}X",
|
|
2 => $"{sign}Y",
|
|
3 => $"{sign}Z",
|
|
_ => "?"
|
|
};
|
|
}
|
|
|
|
EditorGUILayout.HelpBox(
|
|
"T-포즈에서 분석된 축 매핑:\n" +
|
|
$" 좌우 오프셋 → 로컬 {GetAxisName(axisMapping.x)} 축\n" +
|
|
$" 상하 오프셋 → 로컬 {GetAxisName(axisMapping.y)} 축\n" +
|
|
$" 앞뒤 오프셋 → 로컬 {GetAxisName(axisMapping.z)} 축\n\n" +
|
|
"이 매핑 덕분에 모든 아바타에서 동일하게 작동합니다.",
|
|
MessageType.Info);
|
|
}
|
|
else
|
|
{
|
|
EditorGUILayout.HelpBox(
|
|
"플레이 모드에서 T-포즈 분석 후 축 매핑 정보가 표시됩니다.\n" +
|
|
"이 매핑은 각 아바타의 힙 로컬 축 방향에 맞춰 자동 계산됩니다.",
|
|
MessageType.Info);
|
|
}
|
|
EditorGUILayout.EndVertical();
|
|
|
|
GUILayout.Space(5);
|
|
}
|
|
|
|
EditorGUILayout.PropertyField(hipsOffsetXProp,
|
|
new GUIContent("좌우 오프셋 (←-/+→)", "캐릭터 기준 왼쪽(-) / 오른쪽(+)"));
|
|
EditorGUILayout.PropertyField(hipsOffsetYProp,
|
|
new GUIContent("상하 오프셋 (↓-/+↑)", "캐릭터 기준 아래(-) / 위(+)"));
|
|
EditorGUILayout.PropertyField(hipsOffsetZProp,
|
|
new GUIContent("앞뒤 오프셋 (←-/+→)", "캐릭터 기준 뒤(-) / 앞(+)"));
|
|
|
|
EditorGUILayout.HelpBox(
|
|
"로컬 좌표계 기반: 캐릭터의 회전 상태와 관계없이 항상 캐릭터 기준으로 이동합니다.",
|
|
MessageType.Info);
|
|
|
|
EditorGUI.indentLevel--;
|
|
}
|
|
|
|
GUILayout.Space(5);
|
|
|
|
// 무릎 위치 조정 Foldout
|
|
showKneeSettings = EditorGUILayout.Foldout(showKneeSettings, "무릎 위치 조정", true);
|
|
if (showKneeSettings)
|
|
{
|
|
EditorGUI.indentLevel++;
|
|
|
|
// 무릎 안/밖 조정
|
|
EditorGUILayout.Slider(kneeFrontBackWeightProp, -1f, 1f,
|
|
new GUIContent("무릎 앞/뒤 가중치", "음수: 뒤로, 양수: 앞으로"));
|
|
EditorGUILayout.Slider(kneeInOutWeightProp, -1f, 1f,
|
|
new GUIContent("무릎 안/밖 가중치", "음수: 안쪽, 양수: 바깥쪽"));
|
|
|
|
EditorGUI.indentLevel--;
|
|
}
|
|
|
|
GUILayout.Space(5);
|
|
|
|
// 발 IK 위치 조정 Foldout
|
|
showFootSettings = EditorGUILayout.Foldout(showFootSettings, "발 IK 위치 조정", true);
|
|
if (showFootSettings)
|
|
{
|
|
EditorGUI.indentLevel++;
|
|
|
|
EditorGUILayout.Slider(footFrontBackOffsetProp, -1f, 1f,
|
|
new GUIContent("발 앞/뒤 오프셋", "+: 앞으로, -: 뒤로"));
|
|
EditorGUILayout.Slider(footInOutOffsetProp, -1f, 1f,
|
|
new GUIContent("발 벌리기/모으기", "+: 벌리기, -: 모으기"));
|
|
|
|
EditorGUI.indentLevel--;
|
|
}
|
|
|
|
GUILayout.Space(5);
|
|
|
|
// 손가락 복제 설정 Foldout
|
|
showFingerCopySettings = EditorGUILayout.Foldout(showFingerCopySettings, "손가락 복제 설정");
|
|
if (showFingerCopySettings)
|
|
{
|
|
EditorGUI.indentLevel++;
|
|
EditorGUILayout.PropertyField(fingerCopyModeProp,
|
|
new GUIContent("복제 방식", "손가락 포즈를 복제하는 방식을 선택합니다."));
|
|
EditorGUI.indentLevel--;
|
|
}
|
|
|
|
GUILayout.Space(5);
|
|
|
|
// 모션 필터링 설정 Foldout
|
|
showMotionFilterSettings = EditorGUILayout.Foldout(showMotionFilterSettings, "모션 필터링 설정");
|
|
if (showMotionFilterSettings)
|
|
{
|
|
EditorGUI.indentLevel++;
|
|
EditorGUILayout.PropertyField(useMotionFilterProp,
|
|
new GUIContent("모션 필터 사용", "모션 필터링을 적용할지 여부를 설정합니다."));
|
|
if (useMotionFilterProp.boolValue)
|
|
{
|
|
EditorGUILayout.PropertyField(filterBufferSizeProp,
|
|
new GUIContent("필터 버퍼 크기", "모션 필터링에 사용할 버퍼의 크기를 설정합니다. (2-10)"));
|
|
}
|
|
EditorGUI.indentLevel--;
|
|
}
|
|
|
|
GUILayout.Space(5);
|
|
|
|
// 러프 모션 설정 Foldout
|
|
showRoughMotionSettings = EditorGUILayout.Foldout(showRoughMotionSettings, "러프 모션 설정");
|
|
if (showRoughMotionSettings)
|
|
{
|
|
EditorGUI.indentLevel++;
|
|
|
|
// 몸 러프 모션 설정
|
|
EditorGUILayout.PropertyField(useBodyRoughMotionProp,
|
|
new GUIContent("몸 러프 모션 사용", "몸의 러프한 움직임을 적용할지 여부를 설정합니다."));
|
|
if (useBodyRoughMotionProp.boolValue)
|
|
{
|
|
EditorGUILayout.PropertyField(bodyRoughnessProp,
|
|
new GUIContent("몸 러프니스", "몸 전체의 러프한 정도를 설정합니다 (0: 없음, 1: 최대)"));
|
|
}
|
|
|
|
// 손가락 러프 모션 설정
|
|
EditorGUILayout.PropertyField(useFingerRoughMotionProp,
|
|
new GUIContent("손가락 러프 모션 사용", "손가락의 러프한 움직임을 적용할지 여부를 설정합니다."));
|
|
if (useFingerRoughMotionProp.boolValue)
|
|
{
|
|
EditorGUILayout.PropertyField(fingerRoughnessProp,
|
|
new GUIContent("손가락 러프니스", "손가락의 러프한 정도를 설정합니다 (0: 없음, 1: 최대)"));
|
|
}
|
|
|
|
EditorGUI.indentLevel--;
|
|
}
|
|
|
|
GUILayout.Space(5);
|
|
|
|
// 바닥 높이 조정 Foldout
|
|
showFloorSettings = EditorGUILayout.Foldout(showFloorSettings, "바닥 높이 조정", true);
|
|
if (showFloorSettings)
|
|
{
|
|
EditorGUI.indentLevel++;
|
|
EditorGUILayout.PropertyField(floorHeightProp, new GUIContent("바닥 높이 (-1 ~ 1)"));
|
|
EditorGUI.indentLevel--;
|
|
}
|
|
|
|
// 캐시 상태를 텍스트로 표시
|
|
EditorGUILayout.LabelField(hasCachedData ?
|
|
"캘리브레이션 데이터가 저장되어 있습니다." :
|
|
"저장된 캘리브레이션 데이터가 없습니다.",
|
|
EditorStyles.boldLabel);
|
|
|
|
// 버튼들을 수평으로 배치
|
|
EditorGUILayout.BeginHorizontal();
|
|
|
|
// I-포즈 캘리브레이션 버튼
|
|
if (GUILayout.Button("I-포즈 캘리브레이션"))
|
|
{
|
|
var script = (CustomRetargetingScript)target;
|
|
script.I_PoseCalibration();
|
|
CheckCacheStatus();
|
|
Repaint();
|
|
}
|
|
|
|
// 캐시 삭제 버튼 (캐시가 있을 때만 표시)
|
|
if (hasCachedData)
|
|
{
|
|
if (GUILayout.Button("캐시 데이터 삭제"))
|
|
{
|
|
var script = (CustomRetargetingScript)target;
|
|
script.ResetPoseAndCache();
|
|
CheckCacheStatus();
|
|
Repaint();
|
|
}
|
|
}
|
|
|
|
EditorGUILayout.EndHorizontal();
|
|
|
|
GUILayout.Space(10);
|
|
|
|
if (EditorGUI.EndChangeCheck())
|
|
{
|
|
serializedObject.ApplyModifiedProperties();
|
|
var script = (CustomRetargetingScript)target;
|
|
if (script.targetAnimator != null) // targetAnimator가 설정되어 있을 때만 저장
|
|
{
|
|
script.SaveSettings();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
serializedObject.ApplyModifiedProperties();
|
|
}
|
|
}
|
|
}
|
|
} |