using UnityEngine; using UnityEditor; using System.Collections.Generic; [CustomPropertyDrawer(typeof(StreamingleFacialReceiver.BlendShapeIntensityOverride))] public class BlendShapeIntensityOverrideDrawer : PropertyDrawer { // 카테고리별로 구분된 ARKit BlendShape 이름 (Popup에 구분선 역할) private static readonly string[] ARKitBlendShapeNames = new string[] { // Eye (0-13) "EyeBlinkLeft", "EyeBlinkRight", "EyeLookDownLeft", "EyeLookDownRight", "EyeLookInLeft", "EyeLookInRight", "EyeLookOutLeft", "EyeLookOutRight", "EyeLookUpLeft", "EyeLookUpRight", "EyeSquintLeft", "EyeSquintRight", "EyeWideLeft", "EyeWideRight", // Jaw (14-17) "JawForward", "JawLeft", "JawRight", "JawOpen", // Mouth (18-37) "MouthClose", "MouthFunnel", "MouthPucker", "MouthLeft", "MouthRight", "MouthSmileLeft", "MouthSmileRight", "MouthFrownLeft", "MouthFrownRight", "MouthDimpleLeft", "MouthDimpleRight", "MouthStretchLeft", "MouthStretchRight", "MouthRollLower", "MouthRollUpper", "MouthShrugLower", "MouthShrugUpper", "MouthPressLeft", "MouthPressRight", "MouthLowerDownLeft", "MouthLowerDownRight", "MouthUpperUpLeft", "MouthUpperUpRight", // Brow (38-42) "BrowDownLeft", "BrowDownRight", "BrowInnerUp", "BrowOuterUpLeft", "BrowOuterUpRight", // Cheek/Nose (43-47) "CheekPuff", "CheekSquintLeft", "CheekSquintRight", "NoseSneerLeft", "NoseSneerRight", // Tongue (48) "TongueOut", }; // 카테고리 구분 표시용 private static readonly string[] DisplayNames; private static readonly Dictionary nameToIndex; static BlendShapeIntensityOverrideDrawer() { nameToIndex = new Dictionary(System.StringComparer.OrdinalIgnoreCase); // 카테고리 프리픽스 부여 DisplayNames = new string[ARKitBlendShapeNames.Length]; for (int i = 0; i < ARKitBlendShapeNames.Length; i++) { string name = ARKitBlendShapeNames[i]; string category; if (name.StartsWith("Eye")) category = "Eye"; else if (name.StartsWith("Jaw")) category = "Jaw"; else if (name.StartsWith("Mouth")) category = "Mouth"; else if (name.StartsWith("Brow")) category = "Brow"; else if (name.StartsWith("Cheek") || name.StartsWith("Nose")) category = "Cheek-Nose"; else if (name.StartsWith("Tongue")) category = "Tongue"; else category = ""; DisplayNames[i] = category + "/" + name; nameToIndex[name] = i; } } public override float GetPropertyHeight(SerializedProperty property, GUIContent label) { return EditorGUIUtility.singleLineHeight + 2f; } public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { EditorGUI.BeginProperty(position, label, property); position.y += 1f; position.height -= 2f; var nameProp = property.FindPropertyRelative("blendShapeName"); var intensityProp = property.FindPropertyRelative("intensity"); // 새로 추가된 항목의 기본값 보정 if (intensityProp.floatValue == 0f && string.IsNullOrEmpty(nameProp.stringValue)) { intensityProp.floatValue = 1.0f; } // 레이아웃: [드롭다운 55%] [슬라이더 35%] [값 라벨 10%] float dropW = position.width * 0.55f; float sliderW = position.width * 0.35f; float valW = position.width * 0.10f - 6f; Rect dropRect = new Rect(position.x, position.y, dropW - 2f, position.height); Rect sliderRect = new Rect(position.x + dropW + 2f, position.y, sliderW - 2f, position.height); Rect valRect = new Rect(position.x + dropW + sliderW + 4f, position.y, valW, position.height); // 현재 인덱스 int currentIndex = 0; if (!string.IsNullOrEmpty(nameProp.stringValue) && nameToIndex.TryGetValue(nameProp.stringValue, out int idx)) { currentIndex = idx; } // 드롭다운 (카테고리 구분) int newIndex = EditorGUI.Popup(dropRect, currentIndex, DisplayNames); if (newIndex != currentIndex || string.IsNullOrEmpty(nameProp.stringValue)) { nameProp.stringValue = ARKitBlendShapeNames[newIndex]; } // 슬라이더 intensityProp.floatValue = GUI.HorizontalSlider(sliderRect, intensityProp.floatValue, 0f, 3f); // 값 표시 (색상으로 강약 표현) float val = intensityProp.floatValue; Color valColor; if (val < 0.5f) valColor = new Color(1f, 0.4f, 0.4f); // 약함 = 빨강 else if (val > 1.5f) valColor = new Color(0.4f, 0.8f, 1f); // 강함 = 파랑 else valColor = new Color(0.7f, 0.7f, 0.7f); // 보통 = 회색 var valStyle = new GUIStyle(EditorStyles.miniLabel) { alignment = TextAnchor.MiddleRight, fontStyle = FontStyle.Bold, }; valStyle.normal.textColor = valColor; EditorGUI.LabelField(valRect, $"x{val:F1}", valStyle); EditorGUI.EndProperty(); } }