avatar/Assets/VRCSDK/SDK3A/Editor/Components3/VRCAvatarParameterDriverEditor.cs
2022-09-27 20:47:45 -07:00

296 lines
9.4 KiB
C#

#if VRC_SDK_VRCSDK3
using UnityEngine;
using UnityEditor;
using VRC.SDK3.Avatars.Components;
using static VRC.SDKBase.VRC_AvatarParameterDriver;
using Boo.Lang;
using System;
[CustomEditor(typeof(VRCAvatarParameterDriver))]
public class AvatarParameterDriverEditor : Editor
{
string[] parameterNames;
AnimatorControllerParameterType[] parameterTypes;
int selectedParam = -1;
public void OnEnable()
{
UpdateParameters();
}
void UpdateParameters()
{
//Build parameter names
var controller = GetCurrentController();
if(controller != null)
{
//Standard
List<string> names = new List<string>();
List<AnimatorControllerParameterType> types = new List<AnimatorControllerParameterType>();
foreach(var item in controller.parameters)
{
names.Add(item.name);
types.Add(item.type);
}
parameterNames = names.ToArray();
parameterTypes = types.ToArray();
}
}
static UnityEditor.Animations.AnimatorController GetCurrentController()
{
UnityEditor.Animations.AnimatorController controller = null;
var toolType = Type.GetType("UnityEditor.Graphs.AnimatorControllerTool, UnityEditor.Graphs");
var tool = EditorWindow.GetWindow(toolType);
var controllerProperty = toolType.GetProperty("animatorController", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
if(controllerProperty != null)
{
controller = controllerProperty.GetValue(tool, null) as UnityEditor.Animations.AnimatorController;
}
else
Debug.LogError("Unable to find animator window.", tool);
return controller;
}
public override void OnInspectorGUI()
{
EditorGUI.BeginChangeCheck();
serializedObject.Update();
var driver = target as VRCAvatarParameterDriver;
//Update parameters
if(parameterNames == null)
UpdateParameters();
//Info
EditorGUILayout.HelpBox("This behaviour modifies parameters on this and all other animation controllers referenced on the avatar descriptor.\n\nKeep in mind only parameters defined in your VRCExpressionParameter object will be synced across the network.\n\nAdditionally, synced parameters are clamped between Int [0,255] and Float [-1,1]. Operations that modify these parameters will be clipped inside those bounds.", MessageType.Info);
//Data
EditorGUILayout.PropertyField(serializedObject.FindProperty("localOnly"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("debugString"));
//Local only info
bool usesAddOrRandom = false;
foreach(var param in driver.parameters)
{
if(param.type == ChangeType.Add || param.type == ChangeType.Random)
usesAddOrRandom = true;
}
if(usesAddOrRandom && !driver.localOnly)
EditorGUILayout.HelpBox("Using Add & Random may not produce the same result when run on remote instance of the avatar. When using these modes it's suggested you use a synced parameter and use the local only option.", MessageType.Warning);
//Parameters
var editable = new InspectorUtil.EditableArray();
editable.array = serializedObject.FindProperty("parameters");
editable.maxElements = int.MaxValue;
editable.onDrawElement = DrawParameter;
InspectorUtil.DrawEditableArray(this, editable, ref selectedParam);
//End
serializedObject.ApplyModifiedProperties();
if(EditorGUI.EndChangeCheck())
EditorUtility.SetDirty(this);
}
void DrawParameter(SerializedProperty parameters, int arrayIndex)
{
var param = parameters.GetArrayElementAtIndex(arrayIndex);
var name = param.FindPropertyRelative("name");
var source = param.FindPropertyRelative("source");
var changeType = param.FindPropertyRelative("type");
var value = param.FindPropertyRelative("value");
var minValue = param.FindPropertyRelative("valueMin");
var maxValue = param.FindPropertyRelative("valueMax");
var chance = param.FindPropertyRelative("chance");
//Change type
EditorGUILayout.PropertyField(changeType);
switch((ChangeType)changeType.enumValueIndex)
{
case ChangeType.Set:
{
DrawSet();
break;
}
case ChangeType.Add:
{
DrawAdd();
break;
}
case ChangeType.Random:
{
DrawRandom();
break;
}
case ChangeType.Copy:
{
DrawCopy();
break;
}
}
void DrawSet()
{
var destIndex = DrawParamaterDropdown(name, "Destination");
var valueType = destIndex >= 0 ? parameterTypes[destIndex] : AnimatorControllerParameterType.Float;
switch(valueType)
{
case AnimatorControllerParameterType.Bool:
{
value.floatValue = EditorGUILayout.Toggle("Value", value.floatValue != 0f) ? 1f : 0f;
break;
}
case AnimatorControllerParameterType.Int:
{
value.floatValue = EditorGUILayout.IntField("Value", (int)value.floatValue);
break;
}
case AnimatorControllerParameterType.Float:
{
value.floatValue = EditorGUILayout.FloatField("Value", value.floatValue);
break;
}
case AnimatorControllerParameterType.Trigger:
{
break;
}
default:
{
EditorGUILayout.HelpBox($"{valueType} parameters don't support the {changeType.enumNames[changeType.enumValueIndex]} type", MessageType.Warning);
break;
}
}
}
void DrawAdd()
{
var destIndex = DrawParamaterDropdown(name, "Destination");
var valueType = destIndex >= 0 ? parameterTypes[destIndex] : AnimatorControllerParameterType.Float;
switch(valueType)
{
case AnimatorControllerParameterType.Int:
{
value.floatValue = EditorGUILayout.IntField("Value", (int)value.floatValue);
break;
}
case AnimatorControllerParameterType.Float:
{
value.floatValue = EditorGUILayout.FloatField("Value", value.floatValue);
break;
}
default:
{
EditorGUILayout.HelpBox($"{valueType} parameters don't support the {changeType.enumNames[changeType.enumValueIndex]} type", MessageType.Warning);
break;
}
}
}
void DrawRandom()
{
var destIndex = DrawParamaterDropdown(name, "Destination");
var valueType = destIndex >= 0 ? parameterTypes[destIndex] : AnimatorControllerParameterType.Float;
switch(valueType)
{
case AnimatorControllerParameterType.Bool:
case AnimatorControllerParameterType.Trigger:
{
EditorGUILayout.PropertyField(chance);
break;
}
case AnimatorControllerParameterType.Int:
{
minValue.floatValue = EditorGUILayout.IntField("Min Value", (int)minValue.floatValue);
maxValue.floatValue = EditorGUILayout.IntField("Max Value", (int)maxValue.floatValue);
break;
}
case AnimatorControllerParameterType.Float:
{
minValue.floatValue = EditorGUILayout.FloatField("Min Value", minValue.floatValue);
maxValue.floatValue = EditorGUILayout.FloatField("Max Value", maxValue.floatValue);
break;
}
}
}
void DrawCopy()
{
var sourceIndex = DrawParamaterDropdown(source, "Source");
var sourceValueType = sourceIndex >= 0 ? parameterTypes[sourceIndex] : AnimatorControllerParameterType.Float;
var destIndex = DrawParamaterDropdown(name, "Destination");
var destValueType = destIndex >= 0 ? parameterTypes[destIndex] : AnimatorControllerParameterType.Float;
switch(destValueType)
{
case AnimatorControllerParameterType.Bool:
case AnimatorControllerParameterType.Int:
case AnimatorControllerParameterType.Float:
{
if(sourceIndex >= 0)
{
if(sourceValueType == AnimatorControllerParameterType.Trigger)
{
EditorGUILayout.HelpBox("Source parameter can't be the Trigger type", MessageType.Warning);
}
else if(sourceValueType != destValueType)
{
EditorGUILayout.HelpBox($"Value will be converted from a {sourceValueType} to a {destValueType}.", MessageType.Info);
}
}
var convertRange = param.FindPropertyRelative("convertRange");
EditorGUILayout.PropertyField(convertRange);
if(convertRange.boolValue)
{
EditorGUI.indentLevel += 1;
DrawRange("Source", "sourceMin", "sourceMax");
DrawRange("Destination", "destMin", "destMax");
EditorGUI.indentLevel -= 1;
void DrawRange(string label, string min, string max)
{
var minVal = param.FindPropertyRelative(min);
var maxVal = param.FindPropertyRelative(max);
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PrefixLabel(label);
EditorGUILayout.LabelField("Min", GUILayout.Width(64));
minVal.floatValue = EditorGUILayout.FloatField(minVal.floatValue);
EditorGUILayout.LabelField("Max", GUILayout.Width(64));
maxVal.floatValue = EditorGUILayout.FloatField(maxVal.floatValue);
EditorGUILayout.EndHorizontal();
}
}
break;
}
default:
{
EditorGUILayout.HelpBox($"{destValueType} parameters don't support the {changeType.enumNames[changeType.enumValueIndex]} type", MessageType.Warning);
break;
}
}
}
}
int DrawParamaterDropdown(SerializedProperty name, string label)
{
//Name
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PrefixLabel(label);
var index = -1;
if(parameterNames != null)
{
//Find index
EditorGUI.BeginChangeCheck();
index = Array.IndexOf(parameterNames, name.stringValue);
index = EditorGUILayout.Popup(index, parameterNames);
if(EditorGUI.EndChangeCheck() && index >= 0)
name.stringValue = parameterNames[index];
}
name.stringValue = EditorGUILayout.TextField(name.stringValue);
EditorGUILayout.EndHorizontal();
if(index < 0)
EditorGUILayout.HelpBox($"Parameter '{name.stringValue}' not found. Make sure you defined in the Animator window's Parameters tab.", MessageType.Warning);
return index;
}
}
#endif