247 lines
8.1 KiB
C#

using UnityEngine;
using UnityEditor;
using System.IO;
using System.Collections.Generic;
using System.Linq;
public class StreamingleAvatarExporter : EditorWindow
{
private GameObject targetAvatar;
private Vector2 scrollPosition;
private Dictionary<SkinnedMeshRenderer, string> meshTags = new Dictionary<SkinnedMeshRenderer, string>();
private string[] tagOptions = new string[] { "None", "Clothes", "Hair", "Face", "Body", "Other" };
private Color[] tagColors = new Color[] {
Color.gray, // None
Color.blue, // Clothes
Color.yellow, // Hair
Color.green, // Face
Color.red, // Body
Color.magenta // Other
};
[MenuItem("Tools/Streamingle/Avatar Exporter")]
public static void ShowWindow()
{
GetWindow<StreamingleAvatarExporter>("Streamingle Exporter");
}
private void OnGUI()
{
GUILayout.Label("Streamingle Avatar Exporter", EditorStyles.boldLabel);
EditorGUI.BeginChangeCheck();
GameObject newTarget = (GameObject)EditorGUILayout.ObjectField("Avatar Object", targetAvatar, typeof(GameObject), true);
if (newTarget != targetAvatar)
{
targetAvatar = newTarget;
if (targetAvatar != null)
{
InitializeMeshList();
}
else
{
meshTags.Clear();
}
}
if (targetAvatar != null)
{
GUILayout.Space(10);
EditorGUILayout.LabelField("메쉬 태그 설정", EditorStyles.boldLabel);
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
foreach (var pair in meshTags.ToList())
{
SkinnedMeshRenderer renderer = pair.Key;
if (renderer == null) continue;
EditorGUILayout.BeginVertical("box");
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField($"메쉬: {renderer.name}", EditorStyles.boldLabel);
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField($"버텍스: {renderer.sharedMesh.vertexCount}", GUILayout.Width(120));
EditorGUILayout.LabelField($"블렌드쉐입: {renderer.sharedMesh.blendShapeCount}", GUILayout.Width(120));
EditorGUILayout.EndHorizontal();
int currentIndex = System.Array.IndexOf(tagOptions, pair.Value);
int newIndex = EditorGUILayout.Popup("태그", currentIndex, tagOptions);
if (newIndex != currentIndex)
{
meshTags[renderer] = tagOptions[newIndex];
}
EditorGUI.DrawRect(EditorGUILayout.GetControlRect(false, 3), tagColors[newIndex]);
EditorGUILayout.EndVertical();
GUILayout.Space(5);
}
EditorGUILayout.EndScrollView();
if (GUILayout.Button("Export Avatar"))
{
ExportAvatar();
}
}
}
private void InitializeMeshList()
{
meshTags.Clear();
var renderers = targetAvatar.GetComponentsInChildren<SkinnedMeshRenderer>(true);
foreach (var renderer in renderers)
{
meshTags[renderer] = "None";
}
}
private void ExportAvatar()
{
if (targetAvatar == null)
{
EditorUtility.DisplayDialog("Error", "Please select an avatar first!", "OK");
return;
}
string fullpath = EditorUtility.SaveFilePanel(
"Export Streamingle Avatar",
".",
targetAvatar.name,
"streamingle"
);
if (string.IsNullOrEmpty(fullpath))
return;
string filename = Path.GetFileName(fullpath);
string prefabPath = "Assets/StreamingleAvatarTemp.prefab";
try
{
// 컴파일 에러 체크
if (!string.IsNullOrEmpty(AssetDatabase.GetAssetPath(targetAvatar)))
{
AssetDatabase.ImportAsset(AssetDatabase.GetAssetPath(targetAvatar), ImportAssetOptions.ForceUpdate);
}
// 프리팹 생성 전 임시 폴더 정리
string tempPath = Path.Combine(Application.dataPath, "../Temp/AssetBundles");
if (Directory.Exists(tempPath))
{
Directory.Delete(tempPath, true);
}
Directory.CreateDirectory(tempPath);
// 프리팹 생성
bool succeededPack = false;
GameObject prefabAsset = PrefabUtility.SaveAsPrefabAsset(targetAvatar, prefabPath, out succeededPack);
if (!succeededPack || prefabAsset == null)
{
throw new System.Exception("프리팹 생성 실패");
}
// AssetDatabase 새로고침 및 대기
AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate);
System.Threading.Thread.Sleep(1000); // 새로고침 대기
// AssetBundle 설정
AssetBundleBuild bundleBuild = new AssetBundleBuild
{
assetBundleName = filename,
assetNames = new string[] { prefabPath }
};
// 빌드 옵션 설정
BuildAssetBundleOptions options = BuildAssetBundleOptions.None;
// AssetBundle 빌드
BuildPipeline.BuildAssetBundles(
tempPath,
new AssetBundleBuild[] { bundleBuild },
options,
EditorUserBuildSettings.activeBuildTarget
);
// 결과 파일 이동
string bundlePath = Path.Combine(tempPath, filename);
if (File.Exists(bundlePath))
{
if (File.Exists(fullpath))
File.Delete(fullpath);
File.Copy(bundlePath, fullpath, true);
}
else
{
throw new System.Exception($"AssetBundle 파일을 찾을 수 없습니다. 경로: {bundlePath}");
}
EditorUtility.DisplayDialog("Export", "아바타 출력 완료!", "OK");
}
catch (System.Exception e)
{
Debug.LogError($"Export failed: {e.Message}");
EditorUtility.DisplayDialog("Error", "아바타 출력 실패! 콘솔을 확인해주세요.", "OK");
}
finally
{
AssetDatabase.DeleteAsset(prefabPath);
if (File.Exists(prefabPath))
File.Delete(prefabPath);
}
}
}
// 태그 설정을 위한 별도의 윈도우
public class TagSetupWindow : EditorWindow
{
private GameObject targetAvatar;
private Vector2 scrollPosition;
private string[] availableTags = new string[] { "Clothes", "Hair", "Face", "Body", "Other" };
public void Initialize(GameObject avatar)
{
targetAvatar = avatar;
titleContent = new GUIContent("태그 설정");
}
private void OnGUI()
{
if (targetAvatar == null) return;
GUILayout.Label("아바타 파츠 태그 설정", EditorStyles.boldLabel);
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
Transform[] children = targetAvatar.GetComponentsInChildren<Transform>(true);
foreach (Transform child in children)
{
if (child == targetAvatar.transform) continue;
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField(child.name);
string currentTag = child.tag;
int tagIndex = System.Array.IndexOf(availableTags, currentTag);
int newTagIndex = EditorGUILayout.Popup(tagIndex, availableTags);
if (newTagIndex != tagIndex)
{
child.tag = availableTags[newTagIndex];
EditorUtility.SetDirty(child.gameObject);
}
EditorGUILayout.EndHorizontal();
}
EditorGUILayout.EndScrollView();
}
}