Streamingle_URP/Assets/Scripts/Editor/PoseRecorderWindow.cs

373 lines
14 KiB
C#

using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
using System.IO;
using System;
namespace Streamingle.Editor
{
public class PoseRecorderWindow : EditorWindow
{
private GameObject selectedAvatar;
private Animator avatarAnimator;
private string poseName = "NewPose";
private string savePath = "Assets/Recordings/";
// 휴먼본 본들 (Animator HumanBodyBones 사용)
private readonly HumanBodyBones[] humanBodyBones = {
HumanBodyBones.Hips,
HumanBodyBones.Spine,
HumanBodyBones.Chest,
HumanBodyBones.UpperChest,
HumanBodyBones.Neck,
HumanBodyBones.Head,
HumanBodyBones.LeftShoulder,
HumanBodyBones.LeftUpperArm,
HumanBodyBones.LeftLowerArm,
HumanBodyBones.LeftHand,
HumanBodyBones.RightShoulder,
HumanBodyBones.RightUpperArm,
HumanBodyBones.RightLowerArm,
HumanBodyBones.RightHand,
HumanBodyBones.LeftUpperLeg,
HumanBodyBones.LeftLowerLeg,
HumanBodyBones.LeftFoot,
HumanBodyBones.LeftToes,
HumanBodyBones.RightUpperLeg,
HumanBodyBones.RightLowerLeg,
HumanBodyBones.RightFoot,
HumanBodyBones.RightToes
};
// 손가락 본들
private readonly HumanBodyBones[] fingerBones = {
// 왼손 손가락
HumanBodyBones.LeftThumbProximal,
HumanBodyBones.LeftThumbIntermediate,
HumanBodyBones.LeftThumbDistal,
HumanBodyBones.LeftIndexProximal,
HumanBodyBones.LeftIndexIntermediate,
HumanBodyBones.LeftIndexDistal,
HumanBodyBones.LeftMiddleProximal,
HumanBodyBones.LeftMiddleIntermediate,
HumanBodyBones.LeftMiddleDistal,
HumanBodyBones.LeftRingProximal,
HumanBodyBones.LeftRingIntermediate,
HumanBodyBones.LeftRingDistal,
HumanBodyBones.LeftLittleProximal,
HumanBodyBones.LeftLittleIntermediate,
HumanBodyBones.LeftLittleDistal,
// 오른손 손가락
HumanBodyBones.RightThumbProximal,
HumanBodyBones.RightThumbIntermediate,
HumanBodyBones.RightThumbDistal,
HumanBodyBones.RightIndexProximal,
HumanBodyBones.RightIndexIntermediate,
HumanBodyBones.RightIndexDistal,
HumanBodyBones.RightMiddleProximal,
HumanBodyBones.RightMiddleIntermediate,
HumanBodyBones.RightMiddleDistal,
HumanBodyBones.RightRingProximal,
HumanBodyBones.RightRingIntermediate,
HumanBodyBones.RightRingDistal,
HumanBodyBones.RightLittleProximal,
HumanBodyBones.RightLittleIntermediate,
HumanBodyBones.RightLittleDistal
};
[MenuItem("Tools/Pose Recorder")]
public static void ShowWindow()
{
GetWindow<PoseRecorderWindow>("포즈 기록기");
}
private void OnEnable()
{
// 선택된 GameObject가 아바타인지 확인
if (Selection.activeGameObject != null)
{
selectedAvatar = Selection.activeGameObject;
avatarAnimator = selectedAvatar.GetComponent<Animator>();
if (avatarAnimator == null)
{
avatarAnimator = selectedAvatar.GetComponentInChildren<Animator>();
}
}
}
private Vector2 scrollPosition;
private void OnGUI()
{
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
GUILayout.Label("아바타 포즈 기록기", EditorStyles.boldLabel);
EditorGUILayout.Space();
// 아바타 선택
EditorGUILayout.BeginHorizontal();
GUILayout.Label("아바타:", GUILayout.Width(60));
selectedAvatar = (GameObject)EditorGUILayout.ObjectField(selectedAvatar, typeof(GameObject), true);
EditorGUILayout.EndHorizontal();
if (selectedAvatar != null)
{
avatarAnimator = selectedAvatar.GetComponent<Animator>();
if (avatarAnimator == null)
{
avatarAnimator = selectedAvatar.GetComponentInChildren<Animator>();
}
if (avatarAnimator == null)
{
EditorGUILayout.HelpBox("선택된 오브젝트에 Animator가 없습니다!", MessageType.Error);
EditorGUILayout.EndScrollView();
return;
}
EditorGUILayout.HelpBox($"아바타: {selectedAvatar.name}", MessageType.Info);
}
else
{
EditorGUILayout.HelpBox("아바타를 선택해주세요.", MessageType.Warning);
}
EditorGUILayout.Space();
// 포즈 이름 입력
EditorGUILayout.BeginHorizontal();
GUILayout.Label("포즈 이름:", GUILayout.Width(60));
poseName = EditorGUILayout.TextField(poseName);
EditorGUILayout.EndHorizontal();
EditorGUILayout.Space();
// 저장 경로 설정
EditorGUILayout.BeginHorizontal();
GUILayout.Label("저장 경로:", GUILayout.Width(60));
savePath = EditorGUILayout.TextField(savePath);
if (GUILayout.Button("폴더 선택", GUILayout.Width(80)))
{
string newPath = EditorUtility.OpenFolderPanel("저장 폴더 선택", savePath, "");
if (!string.IsNullOrEmpty(newPath))
{
// Assets 폴더 내부로 경로 변환
if (newPath.StartsWith(Application.dataPath))
{
savePath = "Assets" + newPath.Substring(Application.dataPath.Length) + "/";
}
else
{
savePath = "Assets/Recordings/";
}
}
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.Space();
// 버튼들
EditorGUI.BeginDisabledGroup(selectedAvatar == null || avatarAnimator == null);
if (GUILayout.Button("현재 포즈 기록 (전체)", GUILayout.Height(30)))
{
RecordCurrentPose(true);
}
if (GUILayout.Button("현재 포즈 기록 (본체만)", GUILayout.Height(30)))
{
RecordCurrentPose(false);
}
EditorGUILayout.Space();
if (GUILayout.Button("T-Pose 기록", GUILayout.Height(30)))
{
RecordTPose();
}
if (GUILayout.Button("A-Pose 기록", GUILayout.Height(30)))
{
RecordAPose();
}
EditorGUI.EndDisabledGroup();
EditorGUILayout.Space();
// 정보 표시
if (selectedAvatar != null && avatarAnimator != null)
{
EditorGUILayout.LabelField("기록 가능한 본들:", EditorStyles.boldLabel);
// 본체 본들
EditorGUILayout.LabelField("본체 본들:", EditorStyles.boldLabel);
EditorGUILayout.BeginVertical("box");
int foundBodyBones = 0;
foreach (HumanBodyBones humanBone in humanBodyBones)
{
Transform bone = avatarAnimator.GetBoneTransform(humanBone);
if (bone != null)
{
EditorGUILayout.LabelField($"✓ {humanBone}");
foundBodyBones++;
}
}
EditorGUILayout.EndVertical();
EditorGUILayout.LabelField($"본체 본: {foundBodyBones}개");
EditorGUILayout.Space();
// 손가락 본들
EditorGUILayout.LabelField("손가락 본들:", EditorStyles.boldLabel);
EditorGUILayout.BeginVertical("box");
int foundFingerBones = 0;
foreach (HumanBodyBones fingerBone in fingerBones)
{
Transform bone = avatarAnimator.GetBoneTransform(fingerBone);
if (bone != null)
{
EditorGUILayout.LabelField($"✓ {fingerBone}");
foundFingerBones++;
}
}
EditorGUILayout.EndVertical();
EditorGUILayout.LabelField($"손가락 본: {foundFingerBones}개");
EditorGUILayout.Space();
EditorGUILayout.LabelField($"총 {foundBodyBones + foundFingerBones}개 본 발견", EditorStyles.boldLabel);
}
EditorGUILayout.EndScrollView();
}
private void RecordCurrentPose(bool includeFingers = true)
{
if (selectedAvatar == null || avatarAnimator == null)
{
EditorUtility.DisplayDialog("오류", "아바타를 선택해주세요.", "확인");
return;
}
PoseData poseData = new PoseData();
poseData.avatarName = selectedAvatar.name;
poseData.poseName = poseName;
poseData.recordedTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
int recordedBones = 0;
// 본체 본들 기록
foreach (HumanBodyBones humanBone in humanBodyBones)
{
Transform bone = avatarAnimator.GetBoneTransform(humanBone);
if (bone != null)
{
BonePoseData boneData = new BonePoseData();
boneData.boneName = humanBone.ToString();
boneData.localPosition = bone.localPosition;
boneData.localRotation = bone.localEulerAngles;
boneData.localScale = bone.localScale;
poseData.bonePoses.Add(boneData);
recordedBones++;
}
}
// 손가락 본들 기록 (옵션)
if (includeFingers)
{
int fingerBonesCount = 0;
foreach (HumanBodyBones fingerBone in fingerBones)
{
Transform bone = avatarAnimator.GetBoneTransform(fingerBone);
if (bone != null)
{
BonePoseData boneData = new BonePoseData();
boneData.boneName = fingerBone.ToString();
boneData.localPosition = bone.localPosition;
boneData.localRotation = bone.localEulerAngles;
boneData.localScale = bone.localScale;
poseData.bonePoses.Add(boneData);
recordedBones++;
fingerBonesCount++;
}
}
if (fingerBonesCount > 0)
{
UnityEngine.Debug.Log($"손가락 본 {fingerBonesCount}개 기록됨");
}
}
if (recordedBones > 0)
{
SavePoseData(poseData);
string message = includeFingers ?
$"포즈가 기록되었습니다!\n본체 본: {recordedBones - fingerBones.Length}개\n손가락 본: {fingerBones.Length}개\n총 {recordedBones}개 본" :
$"포즈가 기록되었습니다!\n본체 본: {recordedBones}개";
EditorUtility.DisplayDialog("성공", message, "확인");
}
else
{
EditorUtility.DisplayDialog("오류", "기록할 본을 찾을 수 없습니다.", "확인");
}
}
private void RecordTPose()
{
poseName = "T-Pose";
RecordCurrentPose(true); // 손가락 포함
}
private void RecordAPose()
{
poseName = "A-Pose";
RecordCurrentPose(true); // 손가락 포함
}
private void SavePoseData(PoseData poseData)
{
// 저장 폴더 생성
if (!Directory.Exists(savePath))
{
Directory.CreateDirectory(savePath);
}
// 파일명 생성
string fileName = $"{poseData.poseName}_{DateTime.Now:yyyyMMdd_HHmmss}.json";
string filePath = Path.Combine(savePath, fileName);
// JSON으로 저장
string json = JsonUtility.ToJson(poseData, true);
File.WriteAllText(filePath, json);
// Unity 에디터에서 파일 새로고침
AssetDatabase.Refresh();
UnityEngine.Debug.Log($"포즈가 저장되었습니다: {filePath}");
}
}
[System.Serializable]
public class BonePoseData
{
public string boneName;
public Vector3 localPosition;
public Vector3 localRotation;
public Vector3 localScale;
}
[System.Serializable]
public class PoseData
{
public string avatarName;
public string poseName;
public string recordedTime;
public List<BonePoseData> bonePoses = new List<BonePoseData>();
}
}