- 모든 컨트롤러 에디터를 IMGUI → UI Toolkit(UXML/USS)으로 전환 (Camera, Item, Event, Avatar, System, StreamDeck, OptiTrack, Facial) - StreamingleCommon.uss 공통 테마 + 개별 에디터 USS 스타일시트 - SystemController 서브매니저 분리 (OptiTrack, Facial, Recording, Screenshot 등) - 런타임 컨트롤 패널 (ESC 토글, 좌측 오버레이, 150% 스케일) - 웹 대시보드 서버 (StreamingleDashboardServer) + 리타게팅 통합 - 설정 도구(StreamingleControllerSetupTool) UXML 재작성 + 원클릭 설정 - SimplePoseTransfer UXML 에디터 추가 - 전체 UXML 한글화 + NanumGothic 폰트 적용 - Streamingle.Debug → Streamingle.Debugging 네임스페이스 변경 (Debug.Log 충돌 해결) - 불필요 코드 제거 (rawkey.cs, RetargetingHTTPServer, OptitrackSkeletonAnimator 등) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
165 lines
6.2 KiB
C#
165 lines
6.2 KiB
C#
using UnityEngine;
|
|
using UnityEditor;
|
|
using UnityEngine.UIElements;
|
|
using UnityEditor.UIElements;
|
|
using System.Collections.Generic;
|
|
|
|
[CustomPropertyDrawer(typeof(StreamingleFacialReceiver.BlendShapeIntensityOverride))]
|
|
public class BlendShapeIntensityOverrideDrawer : PropertyDrawer
|
|
{
|
|
private static readonly string[] ARKitBlendShapeNames = new string[]
|
|
{
|
|
// Eye
|
|
"EyeBlinkLeft", "EyeBlinkRight",
|
|
"EyeLookDownLeft", "EyeLookDownRight",
|
|
"EyeLookInLeft", "EyeLookInRight",
|
|
"EyeLookOutLeft", "EyeLookOutRight",
|
|
"EyeLookUpLeft", "EyeLookUpRight",
|
|
"EyeSquintLeft", "EyeSquintRight",
|
|
"EyeWideLeft", "EyeWideRight",
|
|
// Jaw
|
|
"JawForward", "JawLeft", "JawRight", "JawOpen",
|
|
// Mouth
|
|
"MouthClose", "MouthFunnel", "MouthPucker",
|
|
"MouthLeft", "MouthRight",
|
|
"MouthSmileLeft", "MouthSmileRight",
|
|
"MouthFrownLeft", "MouthFrownRight",
|
|
"MouthDimpleLeft", "MouthDimpleRight",
|
|
"MouthStretchLeft", "MouthStretchRight",
|
|
"MouthRollLower", "MouthRollUpper",
|
|
"MouthShrugLower", "MouthShrugUpper",
|
|
"MouthPressLeft", "MouthPressRight",
|
|
"MouthLowerDownLeft", "MouthLowerDownRight",
|
|
"MouthUpperUpLeft", "MouthUpperUpRight",
|
|
// Brow
|
|
"BrowDownLeft", "BrowDownRight",
|
|
"BrowInnerUp",
|
|
"BrowOuterUpLeft", "BrowOuterUpRight",
|
|
// Cheek/Nose
|
|
"CheekPuff", "CheekSquintLeft", "CheekSquintRight",
|
|
"NoseSneerLeft", "NoseSneerRight",
|
|
// Tongue
|
|
"TongueOut",
|
|
};
|
|
|
|
private static readonly List<string> DisplayNames;
|
|
private static readonly Dictionary<string, int> NameToIndex;
|
|
|
|
static BlendShapeIntensityOverrideDrawer()
|
|
{
|
|
NameToIndex = new Dictionary<string, int>(System.StringComparer.OrdinalIgnoreCase);
|
|
DisplayNames = new List<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.Add(category + "/" + name);
|
|
NameToIndex[name] = i;
|
|
}
|
|
}
|
|
|
|
private const string UssPath = "Assets/External/StreamingleFacial/Editor/UXML/StreamingleFacialReceiverEditor.uss";
|
|
|
|
public override VisualElement CreatePropertyGUI(SerializedProperty property)
|
|
{
|
|
var row = new VisualElement();
|
|
row.AddToClassList("blendshape-override-row");
|
|
|
|
var uss = AssetDatabase.LoadAssetAtPath<StyleSheet>(UssPath);
|
|
if (uss != null) row.styleSheets.Add(uss);
|
|
|
|
var nameProp = property.FindPropertyRelative("blendShapeName");
|
|
var intensityProp = property.FindPropertyRelative("intensity");
|
|
|
|
// Default value for new entries
|
|
if (intensityProp.floatValue == 0f && string.IsNullOrEmpty(nameProp.stringValue))
|
|
{
|
|
intensityProp.floatValue = 1.0f;
|
|
nameProp.stringValue = ARKitBlendShapeNames[0];
|
|
property.serializedObject.ApplyModifiedPropertiesWithoutUndo();
|
|
}
|
|
|
|
// Dropdown for ARKit blendshape selection
|
|
int currentIndex = 0;
|
|
if (!string.IsNullOrEmpty(nameProp.stringValue) && NameToIndex.TryGetValue(nameProp.stringValue, out int idx))
|
|
currentIndex = idx;
|
|
|
|
var dropdown = new PopupField<string>(DisplayNames, currentIndex);
|
|
dropdown.AddToClassList("blendshape-override-dropdown");
|
|
dropdown.RegisterValueChangedCallback(evt =>
|
|
{
|
|
int newIdx = DisplayNames.IndexOf(evt.newValue);
|
|
if (newIdx >= 0 && newIdx < ARKitBlendShapeNames.Length)
|
|
{
|
|
nameProp.stringValue = ARKitBlendShapeNames[newIdx];
|
|
nameProp.serializedObject.ApplyModifiedProperties();
|
|
}
|
|
});
|
|
row.Add(dropdown);
|
|
|
|
// Slider for intensity
|
|
var slider = new Slider(0f, 3f);
|
|
slider.value = intensityProp.floatValue;
|
|
slider.AddToClassList("blendshape-override-slider");
|
|
row.Add(slider);
|
|
|
|
// Value label with color coding
|
|
var valueLabel = new Label($"x{intensityProp.floatValue:F1}");
|
|
valueLabel.AddToClassList("blendshape-override-value");
|
|
UpdateValueLabelStyle(valueLabel, intensityProp.floatValue);
|
|
row.Add(valueLabel);
|
|
|
|
// Bind slider <-> property
|
|
slider.RegisterValueChangedCallback(evt =>
|
|
{
|
|
intensityProp.floatValue = evt.newValue;
|
|
intensityProp.serializedObject.ApplyModifiedProperties();
|
|
valueLabel.text = $"x{evt.newValue:F1}";
|
|
UpdateValueLabelStyle(valueLabel, evt.newValue);
|
|
});
|
|
|
|
// Track external changes to intensity
|
|
row.TrackPropertyValue(intensityProp, prop =>
|
|
{
|
|
slider.SetValueWithoutNotify(prop.floatValue);
|
|
valueLabel.text = $"x{prop.floatValue:F1}";
|
|
UpdateValueLabelStyle(valueLabel, prop.floatValue);
|
|
});
|
|
|
|
// Track external changes to blendShapeName
|
|
row.TrackPropertyValue(nameProp, prop =>
|
|
{
|
|
if (!string.IsNullOrEmpty(prop.stringValue) && NameToIndex.TryGetValue(prop.stringValue, out int newIdx))
|
|
{
|
|
dropdown.SetValueWithoutNotify(DisplayNames[newIdx]);
|
|
}
|
|
});
|
|
|
|
return row;
|
|
}
|
|
|
|
private void UpdateValueLabelStyle(Label label, float value)
|
|
{
|
|
label.RemoveFromClassList("blendshape-value--low");
|
|
label.RemoveFromClassList("blendshape-value--normal");
|
|
label.RemoveFromClassList("blendshape-value--high");
|
|
|
|
if (value < 0.5f)
|
|
label.AddToClassList("blendshape-value--low");
|
|
else if (value > 1.5f)
|
|
label.AddToClassList("blendshape-value--high");
|
|
else
|
|
label.AddToClassList("blendshape-value--normal");
|
|
}
|
|
}
|