Fix : 전반적인 스크립트 리스트 정리, 모션 에셋파일 눌러도 랙 안걸리게 패치, 모캡 ok파일 리네이머 추가
This commit is contained in:
parent
1ed9aa919c
commit
86cd3e9671
BIN
.claude/settings.local.json
(Stored with Git LFS)
BIN
.claude/settings.local.json
(Stored with Git LFS)
Binary file not shown.
@ -14,11 +14,20 @@ namespace Entum
|
||||
private bool _showBoneData = false;
|
||||
private bool _showMuscleData = false;
|
||||
private bool _showIKData = false;
|
||||
|
||||
|
||||
// 프레임 탐색
|
||||
private int _currentFrameIndex = 0;
|
||||
private Vector2 _scrollPosition;
|
||||
|
||||
|
||||
// 성능 최적화: 에디터 초기화 플래그
|
||||
private bool _isInitialized = false;
|
||||
|
||||
// 캐싱된 데이터 (매번 접근하지 않도록)
|
||||
private int _cachedPoseCount = -1;
|
||||
private float _cachedTotalTime = -1f;
|
||||
private int _cachedBoneCount = -1;
|
||||
private int _cachedMuscleCount = -1;
|
||||
|
||||
// UI 스타일
|
||||
private GUIStyle _cardStyle;
|
||||
private GUIStyle _headerStyle;
|
||||
@ -35,36 +44,45 @@ namespace Entum
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
Repaint();
|
||||
// 캐시 초기화
|
||||
_cachedPoseCount = -1;
|
||||
_cachedTotalTime = -1f;
|
||||
_cachedBoneCount = -1;
|
||||
_cachedMuscleCount = -1;
|
||||
_isInitialized = false;
|
||||
}
|
||||
|
||||
private void InitializeStyles()
|
||||
{
|
||||
if (_isInitialized) return; // 이미 초기화되었으면 스킵
|
||||
|
||||
// 카드 스타일
|
||||
_cardStyle = new GUIStyle();
|
||||
_cardStyle.normal.background = CreateTexture(2, 2, new Color(0.15f, 0.15f, 0.15f, 1f));
|
||||
_cardStyle.padding = new RectOffset(15, 15, 10, 10);
|
||||
_cardStyle.margin = new RectOffset(0, 0, 5, 5);
|
||||
|
||||
|
||||
// 헤더 스타일
|
||||
_headerStyle = new GUIStyle(EditorStyles.boldLabel);
|
||||
_headerStyle.fontSize = 14;
|
||||
_headerStyle.normal.textColor = Color.white;
|
||||
_headerStyle.margin = new RectOffset(0, 0, 5, 10);
|
||||
|
||||
|
||||
// 정보 스타일
|
||||
_infoStyle = new GUIStyle(EditorStyles.label);
|
||||
_infoStyle.normal.textColor = new Color(0.8f, 0.8f, 0.8f);
|
||||
_infoStyle.fontSize = 11;
|
||||
|
||||
|
||||
// 버튼 스타일
|
||||
_buttonStyle = new GUIStyle(EditorStyles.miniButton);
|
||||
_buttonStyle.fontSize = 11;
|
||||
_buttonStyle.padding = new RectOffset(8, 8, 4, 4);
|
||||
|
||||
|
||||
// 섹션 스타일
|
||||
_sectionStyle = new GUIStyle();
|
||||
_sectionStyle.margin = new RectOffset(0, 0, 8, 8);
|
||||
|
||||
_isInitialized = true;
|
||||
}
|
||||
|
||||
private Texture2D CreateTexture(int width, int height, Color color)
|
||||
@ -127,13 +145,18 @@ namespace Entum
|
||||
private void DrawDataStatusCard(HumanoidPoses humanoidPoses)
|
||||
{
|
||||
EditorGUILayout.BeginVertical(_cardStyle);
|
||||
|
||||
|
||||
EditorGUILayout.LabelField("📊 데이터 상태", _headerStyle);
|
||||
|
||||
if (humanoidPoses.Poses != null && humanoidPoses.Poses.Count > 0)
|
||||
|
||||
// Summary 데이터 우선 사용
|
||||
int poseCount = (humanoidPoses.Summary != null && humanoidPoses.Summary.TotalPoses > 0)
|
||||
? humanoidPoses.Summary.TotalPoses
|
||||
: (humanoidPoses.Poses != null ? humanoidPoses.Poses.Count : 0);
|
||||
|
||||
if (poseCount > 0)
|
||||
{
|
||||
EditorGUILayout.LabelField($"✅ {humanoidPoses.Poses.Count}개의 포즈 데이터 로드됨", _infoStyle);
|
||||
|
||||
EditorGUILayout.LabelField($"✅ {poseCount}개의 포즈 데이터 로드됨", _infoStyle);
|
||||
|
||||
// T-포즈 상태 표시
|
||||
EditorGUILayout.Space(3);
|
||||
if (humanoidPoses.HasTPoseData)
|
||||
@ -144,28 +167,28 @@ namespace Entum
|
||||
{
|
||||
EditorGUILayout.LabelField($"🎯 T-포즈: ❌ 없음", _infoStyle);
|
||||
}
|
||||
|
||||
|
||||
EditorGUILayout.Space(5);
|
||||
|
||||
|
||||
// 명확한 토글 버튼
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.LabelField("데이터 탐색:", _infoStyle, GUILayout.Width(80));
|
||||
|
||||
|
||||
var oldColor = GUI.backgroundColor;
|
||||
GUI.backgroundColor = _showData ? _successColor : _secondaryColor;
|
||||
|
||||
|
||||
if (GUILayout.Button(_showData ? "🔽 숨기기" : "🔼 보기", GUILayout.Width(80)))
|
||||
{
|
||||
_showData = !_showData;
|
||||
}
|
||||
|
||||
|
||||
GUI.backgroundColor = oldColor;
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.LabelField("❌ 데이터가 없습니다", _infoStyle);
|
||||
|
||||
|
||||
// T-포즈 상태 표시 (데이터가 없어도)
|
||||
EditorGUILayout.Space(3);
|
||||
if (humanoidPoses.HasTPoseData)
|
||||
@ -177,43 +200,61 @@ namespace Entum
|
||||
EditorGUILayout.LabelField($"🎯 T-포즈: ❌ 없음", _infoStyle);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
EditorGUILayout.EndVertical();
|
||||
}
|
||||
|
||||
private void DrawBasicInfoCard(HumanoidPoses humanoidPoses)
|
||||
{
|
||||
EditorGUILayout.BeginVertical(_cardStyle);
|
||||
|
||||
|
||||
EditorGUILayout.LabelField("📈 기본 정보", _headerStyle);
|
||||
|
||||
if (humanoidPoses.Poses != null && humanoidPoses.Poses.Count > 0)
|
||||
|
||||
// Summary 데이터가 있으면 우선 사용 (성능 최적화)
|
||||
if (humanoidPoses.Summary != null && humanoidPoses.Summary.TotalPoses > 0)
|
||||
{
|
||||
DrawInfoRow("🎭 총 포즈 수", humanoidPoses.Summary.TotalPoses.ToString());
|
||||
DrawInfoRow("⏱️ 총 시간", $"{humanoidPoses.Summary.TotalTime:F2}초");
|
||||
DrawInfoRow("🦴 본 수", humanoidPoses.Summary.TotalBones.ToString());
|
||||
DrawInfoRow("💪 근육 수", humanoidPoses.Summary.TotalMuscles.ToString());
|
||||
DrawInfoRow("🎬 평균 FPS", $"{humanoidPoses.Summary.AverageFPS:F1}");
|
||||
|
||||
float fileSize = EstimateFileSizeFromSummary(humanoidPoses.Summary);
|
||||
DrawInfoRow("💾 예상 크기", $"{fileSize:F1}KB");
|
||||
|
||||
// T-포즈 정보 추가
|
||||
DrawInfoRow("🎯 T-포즈", humanoidPoses.HasTPoseData ? "✅ 포함" : "❌ 없음");
|
||||
}
|
||||
else if (humanoidPoses.Poses != null && humanoidPoses.Poses.Count > 0)
|
||||
{
|
||||
// Summary가 없으면 직접 계산 (느림 - 경고 표시)
|
||||
EditorGUILayout.HelpBox("⚠️ Summary 데이터가 없어서 직접 계산 중입니다. 성능이 느릴 수 있습니다.", MessageType.Warning);
|
||||
|
||||
var firstPose = humanoidPoses.Poses[0];
|
||||
var lastPose = humanoidPoses.Poses[humanoidPoses.Poses.Count - 1];
|
||||
|
||||
|
||||
DrawInfoRow("🎭 총 포즈 수", humanoidPoses.Poses.Count.ToString());
|
||||
DrawInfoRow("⏱️ 총 시간", $"{lastPose.Time:F2}초");
|
||||
DrawInfoRow("🦴 본 수", firstPose.HumanoidBones.Count.ToString());
|
||||
DrawInfoRow("💪 근육 수", firstPose.Muscles.Length.ToString());
|
||||
|
||||
|
||||
float avgFPS = humanoidPoses.Poses.Count / lastPose.Time;
|
||||
DrawInfoRow("🎬 평균 FPS", $"{avgFPS:F1}");
|
||||
|
||||
|
||||
float fileSize = EstimateFileSize(humanoidPoses);
|
||||
DrawInfoRow("💾 예상 크기", $"{fileSize:F1}KB");
|
||||
|
||||
|
||||
// T-포즈 정보 추가
|
||||
DrawInfoRow("🎯 T-포즈", humanoidPoses.HasTPoseData ? "✅ 포함" : "❌ 없음");
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.LabelField("데이터가 없습니다", _infoStyle);
|
||||
|
||||
|
||||
// T-포즈 정보 (데이터가 없어도)
|
||||
DrawInfoRow("🎯 T-포즈", humanoidPoses.HasTPoseData ? "✅ 포함" : "❌ 없음");
|
||||
}
|
||||
|
||||
|
||||
EditorGUILayout.EndVertical();
|
||||
}
|
||||
|
||||
@ -567,15 +608,26 @@ namespace Entum
|
||||
private float EstimateFileSize(HumanoidPoses humanoidPoses)
|
||||
{
|
||||
if (humanoidPoses.Poses == null || humanoidPoses.Poses.Count == 0) return 0;
|
||||
|
||||
|
||||
var firstPose = humanoidPoses.Poses[0];
|
||||
int poseSize = 4 * 3 + 4 * 4 + 4 * 3 + 4 * 4 + 4 * 3 + 4 * 4 + 4 + 4 + 4;
|
||||
int boneSize = (4 * 3 + 4 * 4 + 50) * firstPose.HumanoidBones.Count;
|
||||
int muscleSize = 4 * firstPose.Muscles.Length;
|
||||
|
||||
|
||||
return (poseSize + boneSize + muscleSize) * humanoidPoses.Poses.Count / 1024f;
|
||||
}
|
||||
|
||||
private float EstimateFileSizeFromSummary(HumanoidPoses.SummaryInfo summary)
|
||||
{
|
||||
if (summary == null || summary.TotalPoses == 0) return 0;
|
||||
|
||||
int poseSize = 4 * 3 + 4 * 4 + 4 * 3 + 4 * 4 + 4 * 3 + 4 * 4 + 4 + 4 + 4;
|
||||
int boneSize = (4 * 3 + 4 * 4 + 50) * summary.TotalBones;
|
||||
int muscleSize = 4 * summary.TotalMuscles;
|
||||
|
||||
return (poseSize + boneSize + muscleSize) * summary.TotalPoses / 1024f;
|
||||
}
|
||||
|
||||
private long EstimateMemoryUsage(HumanoidPoses humanoidPoses)
|
||||
{
|
||||
if (humanoidPoses.Poses == null || humanoidPoses.Poses.Count == 0) return 0;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3a53342f3b4a81240ada88d8ca085116
|
||||
guid: 20ac0b0b555cf8240a87cc7062d409b1
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
@ -19,7 +19,7 @@ public class HumanPoseClipApplier : EditorWindow
|
||||
private string jsonPath;
|
||||
private bool useJsonFile = false;
|
||||
|
||||
[MenuItem("Tools/Animation/Apply Human Pose Clip")]
|
||||
[MenuItem("Tools/Animation Tools/Human Pose Clip Applier")]
|
||||
public static void ShowWindow()
|
||||
{
|
||||
GetWindow<HumanPoseClipApplier>("Human Pose Clip Applier");
|
||||
@ -9,7 +9,7 @@ public class HumanPoseClipCreator : EditorWindow
|
||||
private string assetName = "NewPoseClip";
|
||||
private string savePath = "Assets/Resources";
|
||||
|
||||
[MenuItem("Tools/Animation/Create Human Pose Clip")]
|
||||
[MenuItem("Tools/Animation Tools/Human Pose Clip Creator")]
|
||||
public static void ShowWindow()
|
||||
{
|
||||
GetWindow<HumanPoseClipCreator>("Human Pose Clip Creator");
|
||||
@ -75,7 +75,7 @@ namespace Streamingle.Editor
|
||||
HumanBodyBones.RightLittleDistal
|
||||
};
|
||||
|
||||
[MenuItem("Tools/Pose Recorder")]
|
||||
[MenuItem("Tools/Animation Tools/Pose Recorder")]
|
||||
public static void ShowWindow()
|
||||
{
|
||||
GetWindow<PoseRecorderWindow>("포즈 기록기");
|
||||
@ -22,7 +22,7 @@ namespace Streamingle.Editor
|
||||
// 미리보기 옵션
|
||||
private bool showBoneInfo = true;
|
||||
|
||||
[MenuItem("Tools/Pose Rotation Baker")]
|
||||
[MenuItem("Tools/Animation Tools/Pose Rotation Baker")]
|
||||
public static void ShowWindow()
|
||||
{
|
||||
GetWindow<PoseRotationBaker>("포즈 로테이션 베이커");
|
||||
@ -1,5 +1,6 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7a76ec1c32272a242936808ac20449ad
|
||||
guid: 5e26c5e61de10ef428871a3d3fdb4eb9
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
@ -6,7 +6,7 @@ public class FindUnusedBones : EditorWindow
|
||||
{
|
||||
private List<string> excludeStrings = new List<string>(); // 제외할 문자열 리스트
|
||||
|
||||
[MenuItem("Tools/Find Unused Bones")]
|
||||
[MenuItem("Tools/Bone Tools/Find Unused Bones")]
|
||||
public static void ShowWindow()
|
||||
{
|
||||
GetWindow<FindUnusedBones>("Find Unused Bones");
|
||||
@ -83,7 +83,7 @@ public class HumanBoneRenamer : EditorWindow
|
||||
{ HumanBodyBones.RightLittleDistal, "RightLittleDistal" }
|
||||
};
|
||||
|
||||
[MenuItem("Tools/Human Bone Renamer")]
|
||||
[MenuItem("Tools/Bone Tools/Human Bone Renamer")]
|
||||
public static void ShowWindow()
|
||||
{
|
||||
GetWindow<HumanBoneRenamer>("Human Bone Renamer");
|
||||
@ -1,691 +0,0 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System;
|
||||
|
||||
/// <summary>
|
||||
/// 모션 캡쳐 파일 네이밍 편의성 툴
|
||||
/// - 선택한 파일들에 일괄적으로 접미사/접두사 추가
|
||||
/// - 번호 매기기, 날짜 스탬프 등 다양한 네이밍 패턴 지원
|
||||
/// </summary>
|
||||
public class MotionCaptureFileRenamer : EditorWindow
|
||||
{
|
||||
#region Nested Classes
|
||||
[Serializable]
|
||||
public class NamingPreset
|
||||
{
|
||||
public string presetName = "New Preset";
|
||||
public NamingMode mode = NamingMode.Suffix;
|
||||
public string customText = "";
|
||||
public int startNumber = 1;
|
||||
public int numberPadding = 3;
|
||||
public bool includeDate = false;
|
||||
public bool includeTime = false;
|
||||
public string dateFormat = "yyyyMMdd";
|
||||
public string timeFormat = "HHmmss";
|
||||
public string separator = "_";
|
||||
}
|
||||
|
||||
public enum NamingMode
|
||||
{
|
||||
Suffix, // 파일명 뒤에 추가
|
||||
Prefix, // 파일명 앞에 추가
|
||||
Replace, // 전체 이름 변경
|
||||
Sequential // 순차 번호
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
private List<UnityEngine.Object> selectedFiles = new List<UnityEngine.Object>();
|
||||
private Vector2 scrollPosition;
|
||||
|
||||
// Naming settings
|
||||
private NamingMode namingMode = NamingMode.Suffix;
|
||||
private string customText = "_ok";
|
||||
private int startNumber = 1;
|
||||
private int numberPadding = 3;
|
||||
private bool includeDate = false;
|
||||
private bool includeTime = false;
|
||||
private string dateFormat = "yyyyMMdd";
|
||||
private string timeFormat = "HHmmss";
|
||||
private string separator = "_";
|
||||
|
||||
// Preview
|
||||
private bool showPreview = true;
|
||||
private Dictionary<string, string> previewNames = new Dictionary<string, string>();
|
||||
|
||||
// Presets
|
||||
private List<NamingPreset> presets = new List<NamingPreset>();
|
||||
private int selectedPresetIndex = -1;
|
||||
private bool showPresetSection = false;
|
||||
private string newPresetName = "My Preset";
|
||||
|
||||
// UI Style
|
||||
private GUIStyle headerStyle;
|
||||
private GUIStyle boxStyle;
|
||||
private Color successColor = new Color(0.5f, 1f, 0.5f, 0.3f);
|
||||
private Color warningColor = new Color(1f, 1f, 0.5f, 0.3f);
|
||||
private Color errorColor = new Color(1f, 0.5f, 0.5f, 0.3f);
|
||||
#endregion
|
||||
|
||||
#region Window Initialization
|
||||
[MenuItem("Tools/Motion Capture/File Renamer")]
|
||||
public static void ShowWindow()
|
||||
{
|
||||
var window = GetWindow<MotionCaptureFileRenamer>("MoCap File Renamer");
|
||||
window.minSize = new Vector2(450, 500);
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
LoadPresets();
|
||||
RefreshSelectedFiles();
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
SavePresets();
|
||||
}
|
||||
|
||||
private void InitializeStyles()
|
||||
{
|
||||
if (headerStyle == null)
|
||||
{
|
||||
headerStyle = new GUIStyle(EditorStyles.boldLabel)
|
||||
{
|
||||
fontSize = 14,
|
||||
alignment = TextAnchor.MiddleLeft
|
||||
};
|
||||
}
|
||||
|
||||
if (boxStyle == null)
|
||||
{
|
||||
boxStyle = new GUIStyle(EditorStyles.helpBox)
|
||||
{
|
||||
padding = new RectOffset(10, 10, 10, 10)
|
||||
};
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region GUI
|
||||
private void OnGUI()
|
||||
{
|
||||
InitializeStyles();
|
||||
|
||||
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
|
||||
|
||||
DrawHeader();
|
||||
DrawFileSelectionSection();
|
||||
DrawNamingOptionsSection();
|
||||
DrawPreviewSection();
|
||||
DrawPresetsSection();
|
||||
DrawActionButtons();
|
||||
|
||||
EditorGUILayout.EndScrollView();
|
||||
}
|
||||
|
||||
private void DrawHeader()
|
||||
{
|
||||
EditorGUILayout.Space(10);
|
||||
EditorGUILayout.LabelField("Motion Capture File Renamer", headerStyle);
|
||||
EditorGUILayout.LabelField("모션 캡쳐 파일 일괄 네이밍 도구", EditorStyles.miniLabel);
|
||||
EditorGUILayout.Space(10);
|
||||
EditorGUILayout.HelpBox("프로젝트 뷰에서 파일을 선택하거나 아래 영역에 드래그 앤 드롭하세요.", MessageType.Info);
|
||||
EditorGUILayout.Space(5);
|
||||
}
|
||||
|
||||
private void DrawFileSelectionSection()
|
||||
{
|
||||
EditorGUILayout.BeginVertical(boxStyle);
|
||||
EditorGUILayout.LabelField("📁 File Selection", EditorStyles.boldLabel);
|
||||
EditorGUILayout.Space(5);
|
||||
|
||||
// Drag and drop area
|
||||
Rect dropArea = GUILayoutUtility.GetRect(0f, 50f, GUILayout.ExpandWidth(true));
|
||||
GUI.Box(dropArea, "Drag & Drop Files Here\n또는 'Load Selected Files' 버튼 클릭", EditorStyles.helpBox);
|
||||
|
||||
HandleDragAndDrop(dropArea);
|
||||
|
||||
EditorGUILayout.Space(5);
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
if (GUILayout.Button("Load Selected Files", GUILayout.Height(30)))
|
||||
{
|
||||
RefreshSelectedFiles();
|
||||
}
|
||||
if (GUILayout.Button("Clear", GUILayout.Width(80), GUILayout.Height(30)))
|
||||
{
|
||||
selectedFiles.Clear();
|
||||
previewNames.Clear();
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
EditorGUILayout.Space(5);
|
||||
EditorGUILayout.LabelField($"Selected Files: {selectedFiles.Count}", EditorStyles.miniLabel);
|
||||
|
||||
if (selectedFiles.Count > 0)
|
||||
{
|
||||
EditorGUILayout.Space(5);
|
||||
EditorGUILayout.LabelField("Current Files:", EditorStyles.miniBoldLabel);
|
||||
|
||||
var rect = EditorGUILayout.BeginVertical(GUILayout.MaxHeight(100));
|
||||
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition, GUILayout.MaxHeight(100));
|
||||
|
||||
for (int i = 0; i < selectedFiles.Count; i++)
|
||||
{
|
||||
if (selectedFiles[i] != null)
|
||||
{
|
||||
EditorGUILayout.LabelField($"{i + 1}. {selectedFiles[i].name}", EditorStyles.miniLabel);
|
||||
}
|
||||
}
|
||||
|
||||
EditorGUILayout.EndScrollView();
|
||||
EditorGUILayout.EndVertical();
|
||||
}
|
||||
|
||||
EditorGUILayout.EndVertical();
|
||||
EditorGUILayout.Space(10);
|
||||
}
|
||||
|
||||
private void DrawNamingOptionsSection()
|
||||
{
|
||||
EditorGUILayout.BeginVertical(boxStyle);
|
||||
EditorGUILayout.LabelField("⚙️ Naming Options", EditorStyles.boldLabel);
|
||||
EditorGUILayout.Space(5);
|
||||
|
||||
// Naming mode
|
||||
namingMode = (NamingMode)EditorGUILayout.EnumPopup("Naming Mode", namingMode);
|
||||
EditorGUILayout.Space(5);
|
||||
|
||||
switch (namingMode)
|
||||
{
|
||||
case NamingMode.Suffix:
|
||||
DrawSuffixOptions();
|
||||
break;
|
||||
case NamingMode.Prefix:
|
||||
DrawPrefixOptions();
|
||||
break;
|
||||
case NamingMode.Replace:
|
||||
DrawReplaceOptions();
|
||||
break;
|
||||
case NamingMode.Sequential:
|
||||
DrawSequentialOptions();
|
||||
break;
|
||||
}
|
||||
|
||||
EditorGUILayout.Space(5);
|
||||
|
||||
// Common options
|
||||
EditorGUILayout.LabelField("Additional Options", EditorStyles.miniBoldLabel);
|
||||
separator = EditorGUILayout.TextField("Separator", separator);
|
||||
includeDate = EditorGUILayout.Toggle("Include Date", includeDate);
|
||||
if (includeDate)
|
||||
{
|
||||
EditorGUI.indentLevel++;
|
||||
dateFormat = EditorGUILayout.TextField("Date Format", dateFormat);
|
||||
EditorGUILayout.LabelField($"Example: {DateTime.Now.ToString(dateFormat)}", EditorStyles.miniLabel);
|
||||
EditorGUI.indentLevel--;
|
||||
}
|
||||
|
||||
includeTime = EditorGUILayout.Toggle("Include Time", includeTime);
|
||||
if (includeTime)
|
||||
{
|
||||
EditorGUI.indentLevel++;
|
||||
timeFormat = EditorGUILayout.TextField("Time Format", timeFormat);
|
||||
EditorGUILayout.LabelField($"Example: {DateTime.Now.ToString(timeFormat)}", EditorStyles.miniLabel);
|
||||
EditorGUI.indentLevel--;
|
||||
}
|
||||
|
||||
EditorGUILayout.EndVertical();
|
||||
EditorGUILayout.Space(10);
|
||||
}
|
||||
|
||||
private void DrawSuffixOptions()
|
||||
{
|
||||
EditorGUILayout.HelpBox("파일명 뒤에 텍스트를 추가합니다.\n예: Animation.anim → Animation_ok.anim", MessageType.None);
|
||||
customText = EditorGUILayout.TextField("Suffix Text", customText);
|
||||
|
||||
EditorGUILayout.Space(5);
|
||||
EditorGUILayout.LabelField("Quick Presets:", EditorStyles.miniBoldLabel);
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
if (GUILayout.Button("_ok")) customText = "_ok";
|
||||
if (GUILayout.Button("_final")) customText = "_final";
|
||||
if (GUILayout.Button("_test")) customText = "_test";
|
||||
if (GUILayout.Button("_backup")) customText = "_backup";
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
private void DrawPrefixOptions()
|
||||
{
|
||||
EditorGUILayout.HelpBox("파일명 앞에 텍스트를 추가합니다.\n예: Animation.anim → Take001_Animation.anim", MessageType.None);
|
||||
customText = EditorGUILayout.TextField("Prefix Text", customText);
|
||||
|
||||
EditorGUILayout.Space(5);
|
||||
EditorGUILayout.LabelField("Quick Presets:", EditorStyles.miniBoldLabel);
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
if (GUILayout.Button("Take001")) customText = "Take001";
|
||||
if (GUILayout.Button("MoCap")) customText = "MoCap";
|
||||
if (GUILayout.Button("Test")) customText = "Test";
|
||||
if (GUILayout.Button("WIP")) customText = "WIP";
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
private void DrawReplaceOptions()
|
||||
{
|
||||
EditorGUILayout.HelpBox("파일명을 완전히 새로운 이름으로 변경합니다.\n(확장자는 유지됩니다)", MessageType.Warning);
|
||||
customText = EditorGUILayout.TextField("New Base Name", customText);
|
||||
|
||||
EditorGUILayout.Space(5);
|
||||
bool autoNumber = EditorGUILayout.Toggle("Auto Number", true);
|
||||
if (autoNumber)
|
||||
{
|
||||
EditorGUI.indentLevel++;
|
||||
startNumber = EditorGUILayout.IntField("Start Number", startNumber);
|
||||
numberPadding = EditorGUILayout.IntSlider("Number Padding", numberPadding, 1, 5);
|
||||
EditorGUI.indentLevel--;
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawSequentialOptions()
|
||||
{
|
||||
EditorGUILayout.HelpBox("선택한 파일들에 순차적으로 번호를 부여합니다.\n예: Take001, Take002, Take003...", MessageType.None);
|
||||
|
||||
customText = EditorGUILayout.TextField("Base Name", customText);
|
||||
startNumber = EditorGUILayout.IntField("Start Number", startNumber);
|
||||
numberPadding = EditorGUILayout.IntSlider("Number Padding", numberPadding, 1, 5);
|
||||
|
||||
EditorGUILayout.Space(5);
|
||||
EditorGUILayout.LabelField($"Example: {customText}{startNumber.ToString().PadLeft(numberPadding, '0')}", EditorStyles.miniLabel);
|
||||
}
|
||||
|
||||
private void DrawPreviewSection()
|
||||
{
|
||||
showPreview = EditorGUILayout.Foldout(showPreview, "🔍 Preview", true, EditorStyles.foldoutHeader);
|
||||
|
||||
if (showPreview)
|
||||
{
|
||||
EditorGUILayout.BeginVertical(boxStyle);
|
||||
|
||||
if (selectedFiles.Count == 0)
|
||||
{
|
||||
EditorGUILayout.HelpBox("파일을 먼저 선택하세요.", MessageType.Info);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (GUILayout.Button("Generate Preview", GUILayout.Height(25)))
|
||||
{
|
||||
GeneratePreview();
|
||||
}
|
||||
|
||||
if (previewNames.Count > 0)
|
||||
{
|
||||
EditorGUILayout.Space(5);
|
||||
EditorGUILayout.LabelField("변경 예정:", EditorStyles.miniBoldLabel);
|
||||
|
||||
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
||||
Vector2 previewScroll = EditorGUILayout.BeginScrollView(scrollPosition, GUILayout.MaxHeight(150));
|
||||
|
||||
foreach (var kvp in previewNames)
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.LabelField(kvp.Key, GUILayout.Width(150));
|
||||
EditorGUILayout.LabelField("→", GUILayout.Width(20));
|
||||
EditorGUILayout.LabelField(kvp.Value, EditorStyles.boldLabel);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
EditorGUILayout.EndScrollView();
|
||||
EditorGUILayout.EndVertical();
|
||||
}
|
||||
}
|
||||
|
||||
EditorGUILayout.EndVertical();
|
||||
}
|
||||
|
||||
EditorGUILayout.Space(10);
|
||||
}
|
||||
|
||||
private void DrawPresetsSection()
|
||||
{
|
||||
showPresetSection = EditorGUILayout.Foldout(showPresetSection, "💾 Presets", true, EditorStyles.foldoutHeader);
|
||||
|
||||
if (showPresetSection)
|
||||
{
|
||||
EditorGUILayout.BeginVertical(boxStyle);
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
newPresetName = EditorGUILayout.TextField("Preset Name", newPresetName);
|
||||
if (GUILayout.Button("Save Current", GUILayout.Width(100)))
|
||||
{
|
||||
SaveCurrentAsPreset();
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
EditorGUILayout.Space(5);
|
||||
|
||||
if (presets.Count > 0)
|
||||
{
|
||||
EditorGUILayout.LabelField("Saved Presets:", EditorStyles.miniBoldLabel);
|
||||
|
||||
for (int i = 0; i < presets.Count; i++)
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
|
||||
if (GUILayout.Button(presets[i].presetName, GUILayout.Height(25)))
|
||||
{
|
||||
LoadPreset(i);
|
||||
}
|
||||
|
||||
if (GUILayout.Button("×", GUILayout.Width(25), GUILayout.Height(25)))
|
||||
{
|
||||
if (EditorUtility.DisplayDialog("Delete Preset",
|
||||
$"'{presets[i].presetName}' 프리셋을 삭제하시겠습니까?", "Delete", "Cancel"))
|
||||
{
|
||||
presets.RemoveAt(i);
|
||||
SavePresets();
|
||||
}
|
||||
}
|
||||
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.HelpBox("저장된 프리셋이 없습니다.", MessageType.Info);
|
||||
}
|
||||
|
||||
EditorGUILayout.EndVertical();
|
||||
}
|
||||
|
||||
EditorGUILayout.Space(10);
|
||||
}
|
||||
|
||||
private void DrawActionButtons()
|
||||
{
|
||||
EditorGUILayout.BeginVertical(boxStyle);
|
||||
|
||||
GUI.enabled = selectedFiles.Count > 0;
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
|
||||
if (GUILayout.Button("Apply Rename", GUILayout.Height(40)))
|
||||
{
|
||||
if (EditorUtility.DisplayDialog("Confirm Rename",
|
||||
$"{selectedFiles.Count}개의 파일 이름을 변경하시겠습니까?\n\n이 작업은 Undo로 되돌릴 수 있습니다.",
|
||||
"Rename", "Cancel"))
|
||||
{
|
||||
ApplyRename();
|
||||
}
|
||||
}
|
||||
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
GUI.enabled = true;
|
||||
|
||||
EditorGUILayout.EndVertical();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region File Selection
|
||||
private void RefreshSelectedFiles()
|
||||
{
|
||||
selectedFiles.Clear();
|
||||
|
||||
var selected = Selection.objects;
|
||||
foreach (var obj in selected)
|
||||
{
|
||||
string path = AssetDatabase.GetAssetPath(obj);
|
||||
if (!string.IsNullOrEmpty(path) && !AssetDatabase.IsValidFolder(path))
|
||||
{
|
||||
selectedFiles.Add(obj);
|
||||
}
|
||||
}
|
||||
|
||||
previewNames.Clear();
|
||||
Debug.Log($"Loaded {selectedFiles.Count} files");
|
||||
}
|
||||
|
||||
private void HandleDragAndDrop(Rect dropArea)
|
||||
{
|
||||
Event evt = Event.current;
|
||||
|
||||
if (dropArea.Contains(evt.mousePosition))
|
||||
{
|
||||
if (evt.type == EventType.DragUpdated)
|
||||
{
|
||||
DragAndDrop.visualMode = DragAndDropVisualMode.Copy;
|
||||
evt.Use();
|
||||
}
|
||||
else if (evt.type == EventType.DragPerform)
|
||||
{
|
||||
DragAndDrop.AcceptDrag();
|
||||
|
||||
foreach (var obj in DragAndDrop.objectReferences)
|
||||
{
|
||||
string path = AssetDatabase.GetAssetPath(obj);
|
||||
if (!string.IsNullOrEmpty(path) && !AssetDatabase.IsValidFolder(path))
|
||||
{
|
||||
if (!selectedFiles.Contains(obj))
|
||||
{
|
||||
selectedFiles.Add(obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
evt.Use();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Naming Logic
|
||||
private void GeneratePreview()
|
||||
{
|
||||
previewNames.Clear();
|
||||
|
||||
for (int i = 0; i < selectedFiles.Count; i++)
|
||||
{
|
||||
var file = selectedFiles[i];
|
||||
if (file == null) continue;
|
||||
|
||||
string originalName = file.name;
|
||||
string newName = GenerateNewName(originalName, i);
|
||||
|
||||
previewNames[originalName] = newName;
|
||||
}
|
||||
}
|
||||
|
||||
private string GenerateNewName(string originalName, int index)
|
||||
{
|
||||
string baseName = originalName;
|
||||
string result = "";
|
||||
|
||||
// Build timestamp if needed
|
||||
string timestamp = "";
|
||||
if (includeDate)
|
||||
{
|
||||
timestamp += DateTime.Now.ToString(dateFormat);
|
||||
}
|
||||
if (includeTime)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(timestamp))
|
||||
timestamp += separator;
|
||||
timestamp += DateTime.Now.ToString(timeFormat);
|
||||
}
|
||||
|
||||
switch (namingMode)
|
||||
{
|
||||
case NamingMode.Suffix:
|
||||
result = baseName + separator + customText;
|
||||
if (!string.IsNullOrEmpty(timestamp))
|
||||
result += separator + timestamp;
|
||||
break;
|
||||
|
||||
case NamingMode.Prefix:
|
||||
result = customText + separator + baseName;
|
||||
if (!string.IsNullOrEmpty(timestamp))
|
||||
result += separator + timestamp;
|
||||
break;
|
||||
|
||||
case NamingMode.Replace:
|
||||
string number = (startNumber + index).ToString().PadLeft(numberPadding, '0');
|
||||
result = customText + separator + number;
|
||||
if (!string.IsNullOrEmpty(timestamp))
|
||||
result += separator + timestamp;
|
||||
break;
|
||||
|
||||
case NamingMode.Sequential:
|
||||
string seqNumber = (startNumber + index).ToString().PadLeft(numberPadding, '0');
|
||||
result = customText + seqNumber;
|
||||
if (!string.IsNullOrEmpty(timestamp))
|
||||
result += separator + timestamp;
|
||||
break;
|
||||
}
|
||||
|
||||
// Remove invalid characters
|
||||
result = string.Join("_", result.Split(Path.GetInvalidFileNameChars()));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void ApplyRename()
|
||||
{
|
||||
if (selectedFiles.Count == 0)
|
||||
{
|
||||
EditorUtility.DisplayDialog("Error", "선택된 파일이 없습니다.", "OK");
|
||||
return;
|
||||
}
|
||||
|
||||
int successCount = 0;
|
||||
int failCount = 0;
|
||||
|
||||
AssetDatabase.StartAssetEditing();
|
||||
|
||||
try
|
||||
{
|
||||
for (int i = 0; i < selectedFiles.Count; i++)
|
||||
{
|
||||
var file = selectedFiles[i];
|
||||
if (file == null) continue;
|
||||
|
||||
string assetPath = AssetDatabase.GetAssetPath(file);
|
||||
string directory = Path.GetDirectoryName(assetPath);
|
||||
string extension = Path.GetExtension(assetPath);
|
||||
string oldName = file.name;
|
||||
string newName = GenerateNewName(oldName, i);
|
||||
string newPath = Path.Combine(directory, newName + extension);
|
||||
|
||||
// Check if file already exists
|
||||
if (File.Exists(newPath) && assetPath != newPath)
|
||||
{
|
||||
Debug.LogWarning($"File already exists: {newPath}. Skipping {oldName}.");
|
||||
failCount++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Rename asset
|
||||
string error = AssetDatabase.RenameAsset(assetPath, newName);
|
||||
|
||||
if (string.IsNullOrEmpty(error))
|
||||
{
|
||||
Debug.Log($"Renamed: {oldName} → {newName}");
|
||||
successCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"Failed to rename {oldName}: {error}");
|
||||
failCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
AssetDatabase.StopAssetEditing();
|
||||
AssetDatabase.SaveAssets();
|
||||
AssetDatabase.Refresh();
|
||||
}
|
||||
|
||||
string message = $"Rename completed!\n\nSuccess: {successCount}\nFailed: {failCount}";
|
||||
EditorUtility.DisplayDialog("Rename Result", message, "OK");
|
||||
|
||||
// Clear selection and preview
|
||||
selectedFiles.Clear();
|
||||
previewNames.Clear();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Presets
|
||||
private void SaveCurrentAsPreset()
|
||||
{
|
||||
var preset = new NamingPreset
|
||||
{
|
||||
presetName = newPresetName,
|
||||
mode = namingMode,
|
||||
customText = customText,
|
||||
startNumber = startNumber,
|
||||
numberPadding = numberPadding,
|
||||
includeDate = includeDate,
|
||||
includeTime = includeTime,
|
||||
dateFormat = dateFormat,
|
||||
timeFormat = timeFormat,
|
||||
separator = separator
|
||||
};
|
||||
|
||||
presets.Add(preset);
|
||||
SavePresets();
|
||||
|
||||
Debug.Log($"Preset saved: {newPresetName}");
|
||||
EditorUtility.DisplayDialog("Success", $"프리셋 '{newPresetName}'이(가) 저장되었습니다.", "OK");
|
||||
}
|
||||
|
||||
private void LoadPreset(int index)
|
||||
{
|
||||
if (index < 0 || index >= presets.Count) return;
|
||||
|
||||
var preset = presets[index];
|
||||
namingMode = preset.mode;
|
||||
customText = preset.customText;
|
||||
startNumber = preset.startNumber;
|
||||
numberPadding = preset.numberPadding;
|
||||
includeDate = preset.includeDate;
|
||||
includeTime = preset.includeTime;
|
||||
dateFormat = preset.dateFormat;
|
||||
timeFormat = preset.timeFormat;
|
||||
separator = preset.separator;
|
||||
|
||||
Debug.Log($"Preset loaded: {preset.presetName}");
|
||||
}
|
||||
|
||||
private void SavePresets()
|
||||
{
|
||||
string json = JsonUtility.ToJson(new PresetList { presets = presets }, true);
|
||||
EditorPrefs.SetString("MotionCaptureFileRenamer_Presets", json);
|
||||
}
|
||||
|
||||
private void LoadPresets()
|
||||
{
|
||||
string json = EditorPrefs.GetString("MotionCaptureFileRenamer_Presets", "");
|
||||
if (!string.IsNullOrEmpty(json))
|
||||
{
|
||||
try
|
||||
{
|
||||
var presetList = JsonUtility.FromJson<PresetList>(json);
|
||||
presets = presetList.presets ?? new List<NamingPreset>();
|
||||
}
|
||||
catch
|
||||
{
|
||||
presets = new List<NamingPreset>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
private class PresetList
|
||||
{
|
||||
public List<NamingPreset> presets = new List<NamingPreset>();
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
@ -1,8 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0085861f604b4b243ba02d059d2dd867
|
||||
NativeFormatImporter:
|
||||
guid: 9bb84e74216a1f54cbeceefb27c95899
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 2100000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -20,7 +20,7 @@ public class StreamingleAvatarExporter : EditorWindow
|
||||
Color.magenta // Other
|
||||
};
|
||||
|
||||
[MenuItem("Tools/Streamingle Avatar Exporter")]
|
||||
[MenuItem("Tools/Streamingle/Avatar Exporter")]
|
||||
public static void ShowWindow()
|
||||
{
|
||||
GetWindow<StreamingleAvatarExporter>("Streamingle Exporter");
|
||||
8
Assets/Scripts/Editor/TimelineTools.meta
Normal file
8
Assets/Scripts/Editor/TimelineTools.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bbab7799f67203d4c86443651fd45fc3
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -25,7 +25,7 @@ namespace Streamingle.Editor
|
||||
|
||||
private List<TrackMap> previewMappings = new List<TrackMap>();
|
||||
|
||||
[MenuItem("Tools/Timeline/Transfer Bindings")]
|
||||
[MenuItem("Tools/Timeline Tools/Transfer Bindings")]
|
||||
public static void ShowWindow()
|
||||
{
|
||||
GetWindow<TimelineBindingTransferWindow>("Timeline Binding Transfer");
|
||||
8
Assets/Scripts/Editor/Utilities.meta
Normal file
8
Assets/Scripts/Editor/Utilities.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b8decd084ae90044ca3e5777d7ef5013
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -17,7 +17,7 @@ public class MaterialAndTextureTool : EditorWindow
|
||||
private HashSet<Texture> collectedTextures = new HashSet<Texture>();
|
||||
private Vector2 scroll;
|
||||
|
||||
[MenuItem("Tools/Material & Texture Tool")]
|
||||
[MenuItem("Tools/Utilities/Material & Texture Tool")]
|
||||
public static void ShowWindow()
|
||||
{
|
||||
var window = GetWindow<MaterialAndTextureTool>("MatTex Tool");
|
||||
@ -5,7 +5,7 @@ using System.Linq;
|
||||
|
||||
public class MaterialObjectSelector : EditorWindow
|
||||
{
|
||||
[MenuItem("Tools/Select Objects Using Material")]
|
||||
[MenuItem("Tools/Utilities/Select Objects Using Material")]
|
||||
public static void ShowWindow()
|
||||
{
|
||||
GetWindow<MaterialObjectSelector>("Material Object Selector");
|
||||
466
Assets/Scripts/Editor/Utilities/MotionCaptureFileRenamer.cs
Normal file
466
Assets/Scripts/Editor/Utilities/MotionCaptureFileRenamer.cs
Normal file
@ -0,0 +1,466 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System;
|
||||
|
||||
/// <summary>
|
||||
/// 모션 캡쳐 파일 네이밍 편의성 툴
|
||||
/// - 선택한 파일들에 일괄적으로 접미사/접두사 추가
|
||||
/// - 번호 매기기, 날짜 스탬프 등 다양한 네이밍 패턴 지원
|
||||
/// </summary>
|
||||
public class MotionCaptureFileRenamer : EditorWindow
|
||||
{
|
||||
#region Nested Classes
|
||||
[Serializable]
|
||||
public class NamingPreset
|
||||
{
|
||||
public string presetName = "New Preset";
|
||||
public NamingMode mode = NamingMode.Suffix;
|
||||
public string customText = "";
|
||||
public int startNumber = 1;
|
||||
public int numberPadding = 3;
|
||||
public string separator = "_";
|
||||
}
|
||||
|
||||
public enum NamingMode
|
||||
{
|
||||
Suffix, // 파일명 뒤에 추가
|
||||
Prefix, // 파일명 앞에 추가
|
||||
Replace, // 전체 이름 변경
|
||||
Sequential // 순차 번호
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
private List<UnityEngine.Object> selectedFiles = new List<UnityEngine.Object>();
|
||||
private Vector2 scrollPosition;
|
||||
private Vector2 fileListScrollPosition;
|
||||
private Vector2 previewScrollPosition;
|
||||
|
||||
// Naming settings
|
||||
private NamingMode namingMode = NamingMode.Suffix;
|
||||
private string customText = "_ok";
|
||||
private int startNumber = 1;
|
||||
private int numberPadding = 3;
|
||||
private string separator = "_";
|
||||
|
||||
// Presets
|
||||
private List<NamingPreset> presets = new List<NamingPreset>();
|
||||
private int selectedPresetIndex = -1;
|
||||
private bool showPresetSection = false;
|
||||
private string newPresetName = "My Preset";
|
||||
|
||||
// UI Style
|
||||
private GUIStyle headerStyle;
|
||||
private GUIStyle boxStyle;
|
||||
private Color successColor = new Color(0.5f, 1f, 0.5f, 0.3f);
|
||||
private Color warningColor = new Color(1f, 1f, 0.5f, 0.3f);
|
||||
private Color errorColor = new Color(1f, 0.5f, 0.5f, 0.3f);
|
||||
#endregion
|
||||
|
||||
#region Window Initialization
|
||||
[MenuItem("Tools/Motion Capture Tools/File Renamer")]
|
||||
public static void ShowWindow()
|
||||
{
|
||||
var window = GetWindow<MotionCaptureFileRenamer>("MoCap File Renamer");
|
||||
window.minSize = new Vector2(450, 500);
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
LoadPresets();
|
||||
RefreshSelectedFiles();
|
||||
Selection.selectionChanged += OnSelectionChanged;
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
SavePresets();
|
||||
Selection.selectionChanged -= OnSelectionChanged;
|
||||
}
|
||||
|
||||
private void OnSelectionChanged()
|
||||
{
|
||||
RefreshSelectedFiles();
|
||||
Repaint();
|
||||
}
|
||||
|
||||
private void InitializeStyles()
|
||||
{
|
||||
if (headerStyle == null)
|
||||
{
|
||||
headerStyle = new GUIStyle(EditorStyles.boldLabel)
|
||||
{
|
||||
fontSize = 14,
|
||||
alignment = TextAnchor.MiddleLeft
|
||||
};
|
||||
}
|
||||
|
||||
if (boxStyle == null)
|
||||
{
|
||||
boxStyle = new GUIStyle(EditorStyles.helpBox)
|
||||
{
|
||||
padding = new RectOffset(10, 10, 10, 10)
|
||||
};
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region GUI
|
||||
private void OnGUI()
|
||||
{
|
||||
InitializeStyles();
|
||||
|
||||
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
|
||||
|
||||
DrawHeader();
|
||||
DrawFileSelectionSection();
|
||||
DrawNamingOptionsSection();
|
||||
DrawPresetsSection();
|
||||
DrawActionButtons();
|
||||
|
||||
EditorGUILayout.EndScrollView();
|
||||
}
|
||||
|
||||
private void DrawHeader()
|
||||
{
|
||||
EditorGUILayout.Space(5);
|
||||
EditorGUILayout.LabelField("🎬 MoCap File Renamer", headerStyle);
|
||||
EditorGUILayout.Space(5);
|
||||
}
|
||||
|
||||
private void DrawFileSelectionSection()
|
||||
{
|
||||
EditorGUILayout.BeginVertical(boxStyle);
|
||||
|
||||
// 선택된 파일 수 표시
|
||||
GUIStyle countStyle = new GUIStyle(EditorStyles.boldLabel);
|
||||
countStyle.fontSize = 13;
|
||||
|
||||
if (selectedFiles.Count > 0)
|
||||
{
|
||||
countStyle.normal.textColor = new Color(0.3f, 0.8f, 0.3f);
|
||||
EditorGUILayout.LabelField($"📁 {selectedFiles.Count} files selected", countStyle);
|
||||
}
|
||||
else
|
||||
{
|
||||
countStyle.normal.textColor = Color.gray;
|
||||
EditorGUILayout.LabelField($"📁 No files selected", countStyle);
|
||||
}
|
||||
|
||||
EditorGUILayout.EndVertical();
|
||||
EditorGUILayout.Space(5);
|
||||
}
|
||||
|
||||
private void DrawNamingOptionsSection()
|
||||
{
|
||||
EditorGUILayout.BeginVertical(boxStyle);
|
||||
EditorGUILayout.LabelField("⚙️ Naming Options", EditorStyles.boldLabel);
|
||||
EditorGUILayout.Space(5);
|
||||
|
||||
// Naming mode
|
||||
namingMode = (NamingMode)EditorGUILayout.EnumPopup("Mode", namingMode);
|
||||
|
||||
switch (namingMode)
|
||||
{
|
||||
case NamingMode.Suffix:
|
||||
customText = EditorGUILayout.TextField("Suffix", customText);
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
if (GUILayout.Button("_ok")) customText = "_ok";
|
||||
if (GUILayout.Button("_final")) customText = "_final";
|
||||
if (GUILayout.Button("_test")) customText = "_test";
|
||||
EditorGUILayout.EndHorizontal();
|
||||
break;
|
||||
case NamingMode.Prefix:
|
||||
customText = EditorGUILayout.TextField("Prefix", customText);
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
if (GUILayout.Button("Take001")) customText = "Take001";
|
||||
if (GUILayout.Button("MoCap")) customText = "MoCap";
|
||||
if (GUILayout.Button("Test")) customText = "Test";
|
||||
EditorGUILayout.EndHorizontal();
|
||||
break;
|
||||
case NamingMode.Replace:
|
||||
customText = EditorGUILayout.TextField("New Name", customText);
|
||||
startNumber = EditorGUILayout.IntField("Start Number", startNumber);
|
||||
numberPadding = EditorGUILayout.IntSlider("Padding", numberPadding, 1, 5);
|
||||
break;
|
||||
case NamingMode.Sequential:
|
||||
customText = EditorGUILayout.TextField("Base Name", customText);
|
||||
startNumber = EditorGUILayout.IntField("Start Number", startNumber);
|
||||
numberPadding = EditorGUILayout.IntSlider("Padding", numberPadding, 1, 5);
|
||||
EditorGUILayout.LabelField($"Ex: {customText}{startNumber.ToString().PadLeft(numberPadding, '0')}", EditorStyles.miniLabel);
|
||||
break;
|
||||
}
|
||||
|
||||
EditorGUILayout.EndVertical();
|
||||
EditorGUILayout.Space(5);
|
||||
}
|
||||
|
||||
|
||||
private void DrawPresetsSection()
|
||||
{
|
||||
showPresetSection = EditorGUILayout.Foldout(showPresetSection, "💾 Presets", true, EditorStyles.foldoutHeader);
|
||||
|
||||
if (showPresetSection)
|
||||
{
|
||||
EditorGUILayout.BeginVertical(boxStyle);
|
||||
|
||||
if (presets.Count > 0)
|
||||
{
|
||||
for (int i = 0; i < presets.Count; i++)
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
|
||||
if (GUILayout.Button(presets[i].presetName, GUILayout.Height(22)))
|
||||
{
|
||||
LoadPreset(i);
|
||||
}
|
||||
|
||||
if (GUILayout.Button("×", GUILayout.Width(25), GUILayout.Height(22)))
|
||||
{
|
||||
presets.RemoveAt(i);
|
||||
SavePresets();
|
||||
}
|
||||
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
EditorGUILayout.Space(3);
|
||||
}
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
newPresetName = EditorGUILayout.TextField(newPresetName);
|
||||
if (GUILayout.Button("Save", GUILayout.Width(60)))
|
||||
{
|
||||
SaveCurrentAsPreset();
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
EditorGUILayout.EndVertical();
|
||||
}
|
||||
|
||||
EditorGUILayout.Space(5);
|
||||
}
|
||||
|
||||
private void DrawActionButtons()
|
||||
{
|
||||
GUI.enabled = selectedFiles.Count > 0;
|
||||
|
||||
GUI.backgroundColor = new Color(0.3f, 0.8f, 0.3f);
|
||||
if (GUILayout.Button("✓ Apply Rename", GUILayout.Height(50)))
|
||||
{
|
||||
ApplyRename();
|
||||
}
|
||||
GUI.backgroundColor = Color.white;
|
||||
|
||||
GUI.enabled = true;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region File Selection
|
||||
private void RefreshSelectedFiles()
|
||||
{
|
||||
selectedFiles.Clear();
|
||||
|
||||
var selected = Selection.objects;
|
||||
foreach (var obj in selected)
|
||||
{
|
||||
string path = AssetDatabase.GetAssetPath(obj);
|
||||
if (!string.IsNullOrEmpty(path) && !AssetDatabase.IsValidFolder(path))
|
||||
{
|
||||
selectedFiles.Add(obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleDragAndDrop(Rect dropArea)
|
||||
{
|
||||
Event evt = Event.current;
|
||||
|
||||
if (dropArea.Contains(evt.mousePosition))
|
||||
{
|
||||
if (evt.type == EventType.DragUpdated)
|
||||
{
|
||||
DragAndDrop.visualMode = DragAndDropVisualMode.Copy;
|
||||
evt.Use();
|
||||
}
|
||||
else if (evt.type == EventType.DragPerform)
|
||||
{
|
||||
DragAndDrop.AcceptDrag();
|
||||
|
||||
foreach (var obj in DragAndDrop.objectReferences)
|
||||
{
|
||||
string path = AssetDatabase.GetAssetPath(obj);
|
||||
if (!string.IsNullOrEmpty(path) && !AssetDatabase.IsValidFolder(path))
|
||||
{
|
||||
if (!selectedFiles.Contains(obj))
|
||||
{
|
||||
selectedFiles.Add(obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
evt.Use();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Naming Logic
|
||||
private string GenerateNewName(string originalName, int index)
|
||||
{
|
||||
string baseName = originalName;
|
||||
string result = "";
|
||||
|
||||
switch (namingMode)
|
||||
{
|
||||
case NamingMode.Suffix:
|
||||
result = baseName + separator + customText;
|
||||
break;
|
||||
|
||||
case NamingMode.Prefix:
|
||||
result = customText + separator + baseName;
|
||||
break;
|
||||
|
||||
case NamingMode.Replace:
|
||||
string number = (startNumber + index).ToString().PadLeft(numberPadding, '0');
|
||||
result = customText + separator + number;
|
||||
break;
|
||||
|
||||
case NamingMode.Sequential:
|
||||
string seqNumber = (startNumber + index).ToString().PadLeft(numberPadding, '0');
|
||||
result = customText + seqNumber;
|
||||
break;
|
||||
}
|
||||
|
||||
// Remove invalid characters
|
||||
result = string.Join("_", result.Split(Path.GetInvalidFileNameChars()));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void ApplyRename()
|
||||
{
|
||||
if (selectedFiles.Count == 0) return;
|
||||
|
||||
int successCount = 0;
|
||||
int failCount = 0;
|
||||
|
||||
AssetDatabase.StartAssetEditing();
|
||||
|
||||
try
|
||||
{
|
||||
for (int i = 0; i < selectedFiles.Count; i++)
|
||||
{
|
||||
var file = selectedFiles[i];
|
||||
if (file == null) continue;
|
||||
|
||||
string assetPath = AssetDatabase.GetAssetPath(file);
|
||||
string directory = Path.GetDirectoryName(assetPath);
|
||||
string extension = Path.GetExtension(assetPath);
|
||||
string oldName = file.name;
|
||||
string newName = GenerateNewName(oldName, i);
|
||||
string newPath = Path.Combine(directory, newName + extension);
|
||||
|
||||
// Check if file already exists
|
||||
if (File.Exists(newPath) && assetPath != newPath)
|
||||
{
|
||||
Debug.LogWarning($"Skipped (exists): {oldName}");
|
||||
failCount++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Rename asset
|
||||
string error = AssetDatabase.RenameAsset(assetPath, newName);
|
||||
|
||||
if (string.IsNullOrEmpty(error))
|
||||
{
|
||||
Debug.Log($"✓ {oldName} → {newName}");
|
||||
successCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"✗ {oldName}: {error}");
|
||||
failCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
AssetDatabase.StopAssetEditing();
|
||||
AssetDatabase.SaveAssets();
|
||||
AssetDatabase.Refresh();
|
||||
}
|
||||
|
||||
Debug.Log($"<b>Rename Complete:</b> {successCount} success, {failCount} failed");
|
||||
|
||||
// Clear selection
|
||||
selectedFiles.Clear();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Presets
|
||||
private void SaveCurrentAsPreset()
|
||||
{
|
||||
var preset = new NamingPreset
|
||||
{
|
||||
presetName = newPresetName,
|
||||
mode = namingMode,
|
||||
customText = customText,
|
||||
startNumber = startNumber,
|
||||
numberPadding = numberPadding,
|
||||
separator = separator
|
||||
};
|
||||
|
||||
presets.Add(preset);
|
||||
SavePresets();
|
||||
|
||||
Debug.Log($"Preset saved: {newPresetName}");
|
||||
}
|
||||
|
||||
private void LoadPreset(int index)
|
||||
{
|
||||
if (index < 0 || index >= presets.Count) return;
|
||||
|
||||
var preset = presets[index];
|
||||
namingMode = preset.mode;
|
||||
customText = preset.customText;
|
||||
startNumber = preset.startNumber;
|
||||
numberPadding = preset.numberPadding;
|
||||
separator = preset.separator;
|
||||
|
||||
Debug.Log($"Preset loaded: {preset.presetName}");
|
||||
}
|
||||
|
||||
private void SavePresets()
|
||||
{
|
||||
string json = JsonUtility.ToJson(new PresetList { presets = presets }, true);
|
||||
EditorPrefs.SetString("MotionCaptureFileRenamer_Presets", json);
|
||||
}
|
||||
|
||||
private void LoadPresets()
|
||||
{
|
||||
string json = EditorPrefs.GetString("MotionCaptureFileRenamer_Presets", "");
|
||||
if (!string.IsNullOrEmpty(json))
|
||||
{
|
||||
try
|
||||
{
|
||||
var presetList = JsonUtility.FromJson<PresetList>(json);
|
||||
presets = presetList.presets ?? new List<NamingPreset>();
|
||||
}
|
||||
catch
|
||||
{
|
||||
presets = new List<NamingPreset>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
private class PresetList
|
||||
{
|
||||
public List<NamingPreset> presets = new List<NamingPreset>();
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
@ -42,7 +42,7 @@ namespace Bitd
|
||||
|
||||
private Vector2 scrollPosObjects; // 오브젝트 목록 스크롤 위치
|
||||
|
||||
[MenuItem("Bitd/닐로툰 매트캡 자동 인식기", false, 153)]
|
||||
[MenuItem("Tools/Utilities/Nilotoon Material Matcap Setter")]
|
||||
public static void ShowWindow()
|
||||
{
|
||||
GetWindow<NiloMaterialMatcapSetter>("닐로툰 매트캡 자동 인식기");
|
||||
@ -2,7 +2,7 @@ using UnityEditor;
|
||||
|
||||
public class RecompileScript
|
||||
{
|
||||
[MenuItem("Tools/Recompile")]
|
||||
[MenuItem("Tools/Utilities/Recompile")]
|
||||
public static void Recompile()
|
||||
{
|
||||
// 현재의 에디터 애플리케이션의 상태를 강제로 리컴파일합니다.
|
||||
@ -1,64 +0,0 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using UniHumanoid;
|
||||
|
||||
public class HumanPoseClipEditor : EditorWindow
|
||||
{
|
||||
private Animator sourceAnimator;
|
||||
private string savePath = "Assets/NewPose.pose";
|
||||
|
||||
[MenuItem("Tools/Human Pose Clip Creator")]
|
||||
static void ShowWindow()
|
||||
{
|
||||
var window = GetWindow<HumanPoseClipEditor>("포즈 생성기");
|
||||
window.minSize = new Vector2(300, 100);
|
||||
}
|
||||
|
||||
void OnGUI()
|
||||
{
|
||||
EditorGUILayout.Space(10);
|
||||
EditorGUILayout.LabelField("휴머노이드 포즈 생성", EditorStyles.boldLabel);
|
||||
EditorGUILayout.Space(10);
|
||||
|
||||
sourceAnimator = EditorGUILayout.ObjectField("소스 애니메이터",
|
||||
sourceAnimator, typeof(Animator), true) as Animator;
|
||||
|
||||
savePath = EditorGUILayout.TextField("저장 경로", savePath);
|
||||
|
||||
EditorGUILayout.Space(10);
|
||||
|
||||
GUI.enabled = sourceAnimator != null;
|
||||
if (GUILayout.Button("현재 포즈로 에셋 생성"))
|
||||
{
|
||||
CreatePoseClip();
|
||||
}
|
||||
GUI.enabled = true;
|
||||
}
|
||||
|
||||
void CreatePoseClip()
|
||||
{
|
||||
if (!sourceAnimator.isHuman)
|
||||
{
|
||||
EditorUtility.DisplayDialog("오류",
|
||||
"선택된 애니메이터가 휴머노이드가 아닙니다.", "확인");
|
||||
return;
|
||||
}
|
||||
|
||||
var clip = ScriptableObject.CreateInstance<HumanPoseClip>();
|
||||
var handler = new HumanPoseHandler(sourceAnimator.avatar, sourceAnimator.transform);
|
||||
var pose = new HumanPose();
|
||||
|
||||
handler.GetHumanPose(ref pose);
|
||||
clip.ApplyPose(ref pose);
|
||||
|
||||
// 에셋 저장
|
||||
AssetDatabase.CreateAsset(clip, savePath + ".asset");
|
||||
AssetDatabase.SaveAssets();
|
||||
|
||||
EditorUtility.DisplayDialog("완료",
|
||||
"포즈 클립이 생성되었습니다.", "확인");
|
||||
|
||||
// 생성된 에셋 선택
|
||||
Selection.activeObject = clip;
|
||||
}
|
||||
}
|
||||
@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2cf677b8ffaf4aa45a26179e80a8ac23
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
BIN
Assets/Scripts/YAMO_Scripts/Camera Composition/Camera Composition.zip
(Stored with Git LFS)
BIN
Assets/Scripts/YAMO_Scripts/Camera Composition/Camera Composition.zip
(Stored with Git LFS)
Binary file not shown.
BIN
Assets/Scripts/YAMO_Scripts/Camera Composition/CameraComposition.pdf
(Stored with Git LFS)
BIN
Assets/Scripts/YAMO_Scripts/Camera Composition/CameraComposition.pdf
(Stored with Git LFS)
Binary file not shown.
@ -1,14 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 403187bd32b8502488efe4583bc51110
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 202816
|
||||
packageName: Camera Composition - Grid Overlay Tool
|
||||
packageVersion: 1.0.2
|
||||
assetPath: Assets/Camera Composition/CameraComposition.pdf
|
||||
uploadId: 471102
|
||||
BIN
Assets/Scripts/YAMO_Scripts/Camera Composition/Changelog.txt
(Stored with Git LFS)
BIN
Assets/Scripts/YAMO_Scripts/Camera Composition/Changelog.txt
(Stored with Git LFS)
Binary file not shown.
@ -1,14 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1c03e5c3741df0b408c8e81db15780ef
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 202816
|
||||
packageName: Camera Composition - Grid Overlay Tool
|
||||
packageVersion: 1.0.2
|
||||
assetPath: Assets/Camera Composition/Changelog.txt
|
||||
uploadId: 471102
|
||||
BIN
Assets/Scripts/YAMO_Scripts/Camera Composition/README.txt
(Stored with Git LFS)
BIN
Assets/Scripts/YAMO_Scripts/Camera Composition/README.txt
(Stored with Git LFS)
Binary file not shown.
@ -1,14 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 583d2b0c7a3fb5146baec6c15b419b8b
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 202816
|
||||
packageName: Camera Composition - Grid Overlay Tool
|
||||
packageVersion: 1.0.2
|
||||
assetPath: Assets/Camera Composition/README.txt
|
||||
uploadId: 471102
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d90295d3945afb04dbf6709ab5c26a3e
|
||||
Loading…
x
Reference in New Issue
Block a user