2025-06-08 00:55:10 +09:00

297 lines
11 KiB
C#

#if UNITY_EDITOR
using UnityEngine;
using UnityEditor;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using VRM;
using UniGLTF;
namespace Bitd
{
public class ARKitAdder : EditorWindow
{
string[] blendShapeNames = new string[]
{
"eyeBlinkLeft", "eyeBlinkRight","eyeLookDownLeft", "eyeLookDownRight",
"eyeLookInLeft", "eyeLookInRight", "eyeLookOutLeft", "eyeLookOutRight",
"eyeLookUpLeft", "eyeLookUpRight", "eyeSquintLeft", "eyeSquintRight", "eyeWideLeft", "eyeWideRight",
"browDownLeft", "browDownRight", "browInnerUp", "browOuterUpLeft", "browOuterUpRight",
"mouthClose", "mouthFunnel", "mouthPucker", "mouthLeft", "mouthRight", "mouthSmileLeft", "mouthSmileRight", "mouthFrownLeft", "mouthFrownRight",
"mouthDimpleLeft", "mouthDimpleRight", "mouthStretchLeft", "mouthStretchRight", "mouthRollLower", "mouthRollUpper",
"mouthShrugLower", "mouthShrugUpper", "mouthPressLeft", "mouthPressRight", "mouthLowerDownLeft", "mouthLowerDownRight",
"mouthUpperUpLeft", "mouthUpperUpRight", "cheekPuff", "cheekSquintLeft", "cheekSquintRight", "noseSneerLeft", "noseSneerRight",
"jawOpen", "jawForward", "jawLeft", "jawRight", "tongueOut"
};
VRMBlendShapeProxy proxy;
[MenuItem("Bitd/ARKit 생성기", false, 301)]
public static void ShowWindow()
{
var window = GetWindow<ARKitAdder>("ARKit 생성기");
}
void OnGUI()
{
GUILayout.Label("파일 생성기", EditorStyles.boldLabel);
// 선택된 오브젝트 표시
proxy = (VRMBlendShapeProxy)EditorGUILayout.ObjectField("타겟 아바타", proxy, typeof(VRMBlendShapeProxy), true);
if (proxy == null)
{
EditorGUILayout.HelpBox("아바타를 넣어주세요.", MessageType.Warning);
}
else
{
// 버튼: 파일 생성 시작
if (GUILayout.Button("ARKit 생성"))
{
GenerateFiles();
}
}
}
private void GenerateFiles()
{
// 파일 경로 선택
string path = EditorUtility.SaveFolderPanel("파일 저장 경로 선택", Application.dataPath, "");
if (string.IsNullOrEmpty(path))
{
Debug.LogWarning("경로 선택이 취소되었습니다.");
return;
}
// Unity 경로로 변환
if (path.StartsWith(Application.dataPath))
{
path = "Assets" + path.Substring(Application.dataPath.Length);
}
else
{
Debug.LogError("경로는 반드시 Unity 프로젝트 내부여야 합니다.");
return;
}
Debug.Log($"파일 생성 경로: {path}");
// 파일 생성 함수 호출
CreateFilesAtPath(path);
// 에디터 윈도우 닫기
this.Close();
// 에디터 리컴파일 강제
AssetDatabase.Refresh();
}
private void CreateFilesAtPath(string path)
{
// 파일 생성 로직을 여기에 구현
Debug.Log($"'{proxy.name}'에 있는 블렌드쉐이프 값을 찾아서 '{path}'에 클립들을 생성합니다!");
SkinnedMeshRenderer[] targetObjs = proxy.GetComponentsInChildren<SkinnedMeshRenderer>();
for (int i = 0; i < targetObjs.Length; i++)
{
for (int j = 0; j < blendShapeNames.Length; j++)
{
CreateClipByName(path, blendShapeNames[j], targetObjs[i]);
//CreateClipByName(path, CapitalizeFirstLetter(blendShapeNames[j]), targetObjs[i]);
}
}
}
private void CreateClipByName(string path, string clipName, SkinnedMeshRenderer body)
{
BlendShapeClip bsClip;
bool alreadyIn = false;
int targetIndex = -1;
string lowerFirstName = LowercaseFirstLetter(clipName); // 앞글자 소문자 들어옴
string upperFirstName = CapitalizeFirstLetter(clipName); // 앞글자 대문자 들어옴
// 블쉪클립을 찾기 시작함
// 앞글자가 소문자인 경우 체크
for (int i = 0; i < proxy.BlendShapeAvatar.Clips.Count; i++)
{
if (proxy.BlendShapeAvatar.Clips[i].BlendShapeName.Contains(clipName))
{
alreadyIn = true;
targetIndex = i;
break;
}
}
// 소문자로 체크되지 않을 경우 앞글자가 대문자인 경우 체크
if (!alreadyIn)
{
for (int i = 0; i < proxy.BlendShapeAvatar.Clips.Count; i++)
{
if (proxy.BlendShapeAvatar.Clips[i].BlendShapeName.Contains(upperFirstName))
{
alreadyIn = true;
targetIndex = i;
break;
}
}
}
// 블쉪클립 유무를 체크 완료함 (클립 네임을 찾았던 거임)
// 아래는 쉐이프키 네임을 찾음
// 소문자로 쭉 찾아봄
int blendshapeIndex = body.sharedMesh.GetBlendShapeIndex(lowerFirstName);
// 해당하는 블랜드쉐이프가 없을 경우 대문자로 한번 찾아봄
if (blendshapeIndex < 0)
{
blendshapeIndex = body.sharedMesh.GetBlendShapeIndex(upperFirstName);
// 아직도 없으면 반환
if (blendshapeIndex < 0)
{
return;
}
}
// 블랜드쉐이프키가 존재할 경우 로직을 수행함
if (alreadyIn)
{
// 블쉪클립이 이미 존재한다면 그걸로 등록
bsClip = proxy.BlendShapeAvatar.Clips[targetIndex];
}
else
{
// 블쉪클립이 존재하지 않는다면 새로 생성
string fileName = $"{path}/{clipName}.asset";
fileName = AssetDatabase.GenerateUniqueAssetPath(fileName);
// Create new BlendShapeClip
bsClip = BlendShapeAvatar.CreateBlendShapeClip(fileName);
bsClip.BlendShapeName = lowerFirstName;
bsClip.Preset = BlendShapePreset.Unknown;
proxy.BlendShapeAvatar.Clips.Add(bsClip);
}
// Add BlendShapeBinding
var bsb = new BlendShapeBinding
{
RelativePath = body.name,
Index = blendshapeIndex,
Weight = 100
};
if (!IsBindingAlreadySet(bsClip, bsb))
{
Array.Resize(ref bsClip.Values, bsClip.Values.Length + 1);
bsClip.Values[bsClip.Values.Length - 1] = bsb;
}
// Save changes
EditorUtility.SetDirty(bsClip);
EditorUtility.SetDirty(proxy.BlendShapeAvatar);
AssetDatabase.SaveAssets();
Debug.Log($"Successfully created BlendShapeClip: {clipName}");
}
//private void CreateClipByName(string path, string clipName, SkinnedMeshRenderer body)
//{
// Debug.Log($"Creating BlendShapeClip: {clipName} / {body.name}");
// // Lowercase/Uppercase 처리
// clipName = LowercaseFirstLetter(clipName);
// string upperFirstName = CapitalizeFirstLetter(clipName);
// // Check if BlendShapeClip already exists
// BlendShapeClip bsClip = proxy.BlendShapeAvatar.Clips.Find(
// clip => clip.BlendShapeName.Equals(clipName, StringComparison.OrdinalIgnoreCase) ||
// clip.BlendShapeName.Equals(upperFirstName, StringComparison.OrdinalIgnoreCase)
// );
// // Find BlendShape index
// string blendshapeName = bsClip != null && bsClip.BlendShapeName.Equals(upperFirstName) ? upperFirstName : clipName;
// int blendshapeIndex = body.sharedMesh.GetBlendShapeIndex(blendshapeName);
// if (blendshapeIndex < 0)
// {
// Debug.LogWarning($"BlendShape '{blendshapeName}' not found in SkinnedMeshRenderer '{body.name}'.");
// return;
// }
// if (bsClip == null)
// {
// // Generate unique path for the new BlendShapeClip
// string fileName = $"{path}/{clipName}.asset";
// fileName = AssetDatabase.GenerateUniqueAssetPath(fileName);
// // Create new BlendShapeClip
// bsClip = BlendShapeAvatar.CreateBlendShapeClip(fileName);
// if (bsClip == null)
// {
// Debug.LogError($"Failed to create BlendShapeClip: {fileName}");
// return;
// }
// bsClip.BlendShapeName = clipName;
// proxy.BlendShapeAvatar.Clips.Add(bsClip);
// }
// // Add BlendShapeBinding
// var bsb = new BlendShapeBinding
// {
// RelativePath = body.name,
// Index = blendshapeIndex,
// Weight = 100
// };
// if (!IsBindingAlreadySet(bsClip, bsb))
// {
// Array.Resize(ref bsClip.Values, bsClip.Values.Length + 1);
// bsClip.Values[bsClip.Values.Length - 1] = bsb;
// }
// // Save changes
// EditorUtility.SetDirty(proxy.BlendShapeAvatar);
// AssetDatabase.SaveAssets();
// Debug.Log($"Successfully created BlendShapeClip: {clipName}");
//}
private bool IsBindingAlreadySet(BlendShapeClip clip, BlendShapeBinding binding)
{
foreach (var value in clip.Values)
{
if (value.RelativePath == binding.RelativePath && value.Index == binding.Index)
{
return true;
}
}
return false;
}
private string CapitalizeFirstLetter(string input)
{
if (string.IsNullOrEmpty(input))
return input;
return char.ToUpper(input[0]) + input.Substring(1);
}
private string LowercaseFirstLetter(string input)
{
if (string.IsNullOrEmpty(input))
return input;
return char.ToLower(input[0]) + input.Substring(1);
}
}
}
#endif