summaryrefslogtreecommitdiff
path: root/VRCSDK3AvatarsLegacy/Assets/Resources/Lyuma/Av3Emulator/Editor
diff options
context:
space:
mode:
authortylermurphy534 <tylermurphy534@gmail.com>2022-11-06 15:12:42 -0500
committertylermurphy534 <tylermurphy534@gmail.com>2022-11-06 15:12:42 -0500
commiteb84bb298d2b95aec7b2ae12cbf25ac64f25379a (patch)
treeefd616a157df06ab661c6d56651853431ac6b08b /VRCSDK3AvatarsLegacy/Assets/Resources/Lyuma/Av3Emulator/Editor
downloadunityprojects-eb84bb298d2b95aec7b2ae12cbf25ac64f25379a.tar.gz
unityprojects-eb84bb298d2b95aec7b2ae12cbf25ac64f25379a.tar.bz2
unityprojects-eb84bb298d2b95aec7b2ae12cbf25ac64f25379a.zip
move to self host
Diffstat (limited to 'VRCSDK3AvatarsLegacy/Assets/Resources/Lyuma/Av3Emulator/Editor')
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/Resources/Lyuma/Av3Emulator/Editor/LyumaAv3EditorSupport.cs144
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/Resources/Lyuma/Av3Emulator/Editor/LyumaAv3EditorSupport.cs.meta11
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/Resources/Lyuma/Av3Emulator/Editor/LyumaAv3MenuEditor.cs354
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/Resources/Lyuma/Av3Emulator/Editor/LyumaAv3MenuEditor.cs.meta3
4 files changed, 512 insertions, 0 deletions
diff --git a/VRCSDK3AvatarsLegacy/Assets/Resources/Lyuma/Av3Emulator/Editor/LyumaAv3EditorSupport.cs b/VRCSDK3AvatarsLegacy/Assets/Resources/Lyuma/Av3Emulator/Editor/LyumaAv3EditorSupport.cs
new file mode 100644
index 00000000..a866ca68
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/Resources/Lyuma/Av3Emulator/Editor/LyumaAv3EditorSupport.cs
@@ -0,0 +1,144 @@
+/* Copyright (c) 2020 Lyuma <xn.lyuma@gmail.com>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE. */
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using UnityEditor;
+using UnityEngine.Animations;
+using UnityEditor.Animations;
+using UnityEditor.Playables;
+using UnityEngine.Playables;
+using VRC.SDK3.Avatars.Components;
+
+[InitializeOnLoadAttribute]
+public static class LyumaAv3EditorSupport
+{
+ static Dictionary<VRCAvatarDescriptor.AnimLayerType, string> animLayerToDefaultFile = new Dictionary<VRCAvatarDescriptor.AnimLayerType, string> {
+ {VRCAvatarDescriptor.AnimLayerType.TPose, "vrc_AvatarV3UtilityTPose"},
+ {VRCAvatarDescriptor.AnimLayerType.IKPose, "vrc_AvatarV3UtilityIKPose"},
+ {VRCAvatarDescriptor.AnimLayerType.Base, "vrc_AvatarV3LocomotionLayer"},
+ {VRCAvatarDescriptor.AnimLayerType.Sitting, "vrc_AvatarV3SittingLayer"},
+ {VRCAvatarDescriptor.AnimLayerType.Additive, "vrc_AvatarV3IdleLayer"},
+ {VRCAvatarDescriptor.AnimLayerType.FX, "vrc_AvatarV3FaceLayer"},
+ {VRCAvatarDescriptor.AnimLayerType.Action, "vrc_AvatarV3ActionLayer"},
+ {VRCAvatarDescriptor.AnimLayerType.Gesture, "vrc_AvatarV3HandsLayer"},
+ };
+ static Dictionary<VRCAvatarDescriptor.AnimLayerType, string> animLayerToDefaultAvaMaskFile = new Dictionary<VRCAvatarDescriptor.AnimLayerType, string>
+ {
+ {VRCAvatarDescriptor.AnimLayerType.TPose, "vrc_MusclesOnly"},
+ {VRCAvatarDescriptor.AnimLayerType.IKPose, "vrc_MusclesOnly"},
+ {VRCAvatarDescriptor.AnimLayerType.Base, null},//"LyumaFullMask"},
+ {VRCAvatarDescriptor.AnimLayerType.Sitting, null},//"LyumaFullMask"},
+ {VRCAvatarDescriptor.AnimLayerType.Additive, null},//"LyumaFullMask"},
+ {VRCAvatarDescriptor.AnimLayerType.FX, "LyumaEmptyMask"}, // TODO
+ {VRCAvatarDescriptor.AnimLayerType.Action, null},//"vrc_MusclesOnly"},
+ {VRCAvatarDescriptor.AnimLayerType.Gesture, "vrc_HandsOnly"},
+ };
+
+ static void InitDefaults() {
+ foreach (var kv in animLayerToDefaultFile) {
+ if (kv.Value == null) {
+ LyumaAv3Runtime.animLayerToDefaultController[kv.Key] = null;
+ } else
+ {
+ AnimatorController ac = AssetDatabase.LoadAssetAtPath<AnimatorController>("Assets/VRCSDK/Examples3/Animation/Controllers/" + kv.Value + ".controller");
+ if (ac == null)
+ {
+ Debug.LogWarning("Failed to resolve animator controller " + kv.Value + " for " + kv.Key);
+ ac = null;
+ }
+ LyumaAv3Runtime.animLayerToDefaultController[kv.Key] = ac;
+ }
+ }
+ foreach (var kv in animLayerToDefaultAvaMaskFile) {
+ if (kv.Value == null) {
+ LyumaAv3Runtime.animLayerToDefaultAvaMask[kv.Key] = null;
+ } else
+ {
+ AvatarMask mask = null;
+ foreach (var guid in AssetDatabase.FindAssets(kv.Value))
+ {
+ string path = AssetDatabase.GUIDToAssetPath(guid);
+ mask = AssetDatabase.LoadAssetAtPath<AvatarMask>(path);
+ }
+ if (mask == null)
+ {
+ Debug.LogWarning("Failed to resolve avatar mask " + kv.Value + " for " + kv.Key);
+ mask = new AvatarMask();
+ }
+ LyumaAv3Runtime.animLayerToDefaultAvaMask[kv.Key] = mask;
+ }
+ }
+ foreach (string guid in AssetDatabase.FindAssets("EmptyController")) {
+ LyumaAv3Emulator.EmptyController = AssetDatabase.LoadAssetAtPath<RuntimeAnimatorController>(AssetDatabase.GUIDToAssetPath(guid));
+ }
+
+ LyumaAv3Runtime.updateSelectionDelegate = (go) => {
+ if (go == null && LyumaAv3Emulator.emulatorInstance != null) {
+ Debug.Log("Resetting selected object: " + LyumaAv3Emulator.emulatorInstance);
+ go = LyumaAv3Emulator.emulatorInstance.gameObject;
+ }
+ Debug.Log("Setting selected object: " + go);
+ Selection.SetActiveObjectWithContext(go, go);
+ // Highlighter.Highlight("Inspector", "Animator To Debug");
+ };
+
+ LyumaAv3Runtime.addRuntimeDelegate = (runtime) => {
+ GameObject go = runtime.gameObject;
+ try {
+ if (PrefabUtility.IsPartOfAnyPrefab(go)) {
+ PrefabUtility.UnpackPrefabInstance(go, PrefabUnpackMode.Completely, InteractionMode.AutomatedAction);
+ }
+ } catch (System.Exception) {}
+ int moveUpCalls = go.GetComponents<Component>().Length - 2;
+ if (!PrefabUtility.IsPartOfAnyPrefab(go.GetComponents<Component>()[1])) {
+ for (int i = 0; i < moveUpCalls; i++) {
+ UnityEditorInternal.ComponentUtility.MoveComponentUp(runtime);
+ }
+ }
+ };
+ LyumaAv3Menu.addRuntimeDelegate = (menu) => {
+ GameObject go = menu.gameObject;
+ try {
+ if (PrefabUtility.IsPartOfAnyPrefab(go)) {
+ PrefabUtility.UnpackPrefabInstance(go, PrefabUnpackMode.Completely, InteractionMode.AutomatedAction);
+ }
+ } catch (System.Exception) {}
+ int moveUpCalls = go.GetComponents<Component>().Length - 2;
+ if (!PrefabUtility.IsPartOfAnyPrefab(go.GetComponents<Component>()[1])) {
+ for (int i = 0; i < moveUpCalls; i++) {
+ UnityEditorInternal.ComponentUtility.MoveComponentUp(menu);
+ }
+ }
+ };
+ }
+
+ // register an event handler when the class is initialized
+ static LyumaAv3EditorSupport()
+ {
+ InitDefaults();
+ }
+
+ [MenuItem("Tools/Enable Avatars 3.0 Emulator")]
+ public static void EnableAv3Testing() {
+ GameObject go = new GameObject("Avatars 3.0 Emulator Control");
+ go.AddComponent<LyumaAv3Emulator>();
+ }
+}
diff --git a/VRCSDK3AvatarsLegacy/Assets/Resources/Lyuma/Av3Emulator/Editor/LyumaAv3EditorSupport.cs.meta b/VRCSDK3AvatarsLegacy/Assets/Resources/Lyuma/Av3Emulator/Editor/LyumaAv3EditorSupport.cs.meta
new file mode 100644
index 00000000..79bb5d5f
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/Resources/Lyuma/Av3Emulator/Editor/LyumaAv3EditorSupport.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 77ddade7c6475a242a8e34b2b7554adc
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3AvatarsLegacy/Assets/Resources/Lyuma/Av3Emulator/Editor/LyumaAv3MenuEditor.cs b/VRCSDK3AvatarsLegacy/Assets/Resources/Lyuma/Av3Emulator/Editor/LyumaAv3MenuEditor.cs
new file mode 100644
index 00000000..293f4ef1
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/Resources/Lyuma/Av3Emulator/Editor/LyumaAv3MenuEditor.cs
@@ -0,0 +1,354 @@
+/* Copyright (c) 2020 Lyuma <xn.lyuma@gmail.com>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE. */
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using UnityEditor;
+using UnityEngine;
+using VRC.SDK3.Avatars.ScriptableObjects;
+
+[CustomEditor(typeof(LyumaAv3Menu))]
+public class LyumaAv3MenuEditor : Editor
+{
+ private readonly Dictionary<Texture2D, Texture2D> _resizedIcons = new Dictionary<Texture2D, Texture2D>();
+ private VRCExpressionsMenu _currentMenu;
+
+ public override void OnInspectorGUI()
+ {
+ var menu = (LyumaAv3Menu)target;
+ if (menu.Runtime == null) return;
+ if (menu.RootMenu == null)
+ {
+ menu.RootMenu = (VRCExpressionsMenu)EditorGUILayout.ObjectField(new GUIContent("Expressions Menu"), null, typeof(VRCExpressionsMenu), false);
+ return;
+ }
+
+ var isInRootMenu = menu.MenuStack.Count == 0;
+ GUILayout.BeginHorizontal();
+ if (GUILayout.Button(menu.IsMenuOpen ? "Close menu" : "Open menu"))
+ {
+ menu.ToggleMenu();
+ }
+
+ if (menu.gameObject.GetComponents<LyumaAv3Menu>().Length == 1)
+ {
+ if (GUILayout.Button("+", GUILayout.Width(20)))
+ {
+ OpenMenuForTwoHandedSupport(menu);
+ }
+ }
+
+ GUILayout.EndHorizontal();
+
+ GUILayout.Label(
+ (isInRootMenu ? "Expressions" : LabelizeMenu()) +
+ (menu.IsMenuOpen ? "" : " [Menu is closed]"),
+ EditorStyles.boldLabel);
+
+ if (!menu.IsMenuOpen) {
+ return;
+ }
+
+ _currentMenu = menu.MenuStack.Count == 0 ? menu.RootMenu : menu.MenuStack.Last().ExpressionsMenu;
+
+ EditorGUI.BeginDisabledGroup(true);
+ EditorGUILayout.ObjectField(_currentMenu, typeof(VRCExpressionsMenu), false);
+ EditorGUI.EndDisabledGroup();
+
+ EditorGUI.BeginDisabledGroup(isInRootMenu || menu.HasActiveControl());
+ if (GUILayout.Button("Back"))
+ {
+ menu.UserBack();
+ }
+ EditorGUI.EndDisabledGroup();
+ for (var controlIndex = 0; controlIndex < _currentMenu.controls.Count; controlIndex++)
+ {
+ var control = _currentMenu.controls[controlIndex];
+ switch (control.type)
+ {
+ case VRCExpressionsMenu.Control.ControlType.Button:
+ FromToggle(control, "Button");
+ break;
+ case VRCExpressionsMenu.Control.ControlType.Toggle:
+ FromToggle(control, "Toggle");
+ break;
+ case VRCExpressionsMenu.Control.ControlType.SubMenu:
+ FromSubMenu(control);
+ break;
+ case VRCExpressionsMenu.Control.ControlType.TwoAxisPuppet:
+ FromTwoAxis(control, controlIndex);
+ break;
+ case VRCExpressionsMenu.Control.ControlType.FourAxisPuppet:
+ FromFourAxis(control, controlIndex);
+ break;
+ case VRCExpressionsMenu.Control.ControlType.RadialPuppet:
+ FromRadial(control, controlIndex);
+ break;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+ }
+
+ if (_currentMenu.controls.Count == 0)
+ {
+ EditorGUILayout.LabelField("(This menu has no controls)");
+ }
+ }
+
+ private static void OpenMenuForTwoHandedSupport(LyumaAv3Menu menu)
+ {
+ var mainMenu = menu.Runtime.gameObject.AddComponent<LyumaAv3Menu>();
+ mainMenu.Runtime = menu.Runtime;
+ mainMenu.RootMenu = menu.RootMenu;
+ }
+
+ private string LabelizeMenu()
+ {
+ var menu = (LyumaAv3Menu)target;
+
+ var lastMenu = menu.MenuStack.Last();
+ if (lastMenu.MandatedParam == null)
+ {
+ return lastMenu.ExpressionsMenu.name;
+ }
+
+ return lastMenu.ExpressionsMenu.name + " (" + lastMenu.MandatedParam.name + " = " + lastMenu.MandatedParam.value + ")";
+ }
+
+ private void FromToggle(VRCExpressionsMenu.Control control, string labelType)
+ {
+ var menu = (LyumaAv3Menu)target;
+
+ var parameterName = control.parameter.name;
+ var controlValue = control.value;
+
+ var isActive = menu.IsVisualActive(parameterName, controlValue);
+
+ EditorGUILayout.BeginHorizontal();
+ EditorGUI.BeginDisabledGroup(menu.HasActiveControl());
+ if (GreenBackground(isActive, () => ParameterizedButton(control, parameterName, controlValue)))
+ {
+ menu.UserToggle(parameterName, controlValue);
+ }
+ EditorGUI.EndDisabledGroup();
+ LabelType(labelType);
+ EditorGUILayout.EndHorizontal();
+ }
+
+ private void FromSubMenu(VRCExpressionsMenu.Control control)
+ {
+ var menu = (LyumaAv3Menu)target;
+
+ var parameterName = control.parameter.name;
+ var wantedValue = control.value;
+
+ EditorGUILayout.BeginHorizontal();
+ EditorGUI.BeginDisabledGroup(menu.HasActiveControl());
+ if (ParameterizedButton(control, parameterName, wantedValue))
+ {
+ if (IsValidParameterName(parameterName))
+ {
+ menu.UserSubMenu(control.subMenu, parameterName, wantedValue);
+ }
+ else
+ {
+ menu.UserSubMenu(control.subMenu);
+ }
+ }
+ EditorGUI.EndDisabledGroup();
+ LabelType("SubMenu");
+ EditorGUILayout.EndHorizontal();
+ }
+
+ private void FromRadial(VRCExpressionsMenu.Control control, int controlIndex)
+ {
+ var menu = (LyumaAv3Menu)target;
+
+ SubControl(control, controlIndex, menu, "Radial");
+
+ if (menu.IsActiveControl(controlIndex))
+ {
+ if (control.subParameters.Length > 0)
+ {
+ SliderFloat(menu, control.subParameters[0], "Rotation", 0f, 1f);
+ }
+ }
+ }
+
+ private void FromTwoAxis(VRCExpressionsMenu.Control control, int controlIndex)
+ {
+ var menu = (LyumaAv3Menu)target;
+
+ SubControl(control, controlIndex, menu, "TwoAxis");
+
+ if (menu.IsActiveControl(controlIndex))
+ {
+ var sanitySubParamLength = control.subParameters.Length;
+ if (sanitySubParamLength > 0) SliderFloat(menu, control.subParameters[0], "Horizontal", -1f, 1f);
+ if (sanitySubParamLength > 1) SliderFloat(menu, control.subParameters[1], "Vertical", -1f, 1f);
+
+ var oldColor = Color.HSVToRGB(
+ 0,
+ sanitySubParamLength > 0 ? menu.FindFloat(control.subParameters[0].name) * 0.5f + 0.5f : 0,
+ sanitySubParamLength > 1 ? menu.FindFloat(control.subParameters[1].name) * 0.5f + 0.5f : 0);
+ var newColor = EditorGUILayout.ColorField(oldColor);
+ if (oldColor.r != newColor.r || oldColor.g != newColor.g || oldColor.b != newColor.b)
+ {
+ Color.RGBToHSV(newColor, out _, out var s, out var v);
+ if (sanitySubParamLength > 0) menu.UserFloat(control.subParameters[0].name, s * 2 - 1);
+ if (sanitySubParamLength > 1) menu.UserFloat(control.subParameters[1].name, v * 2 - 1);
+ }
+ }
+ }
+
+ private void FromFourAxis(VRCExpressionsMenu.Control control, int controlIndex)
+ {
+ var menu = (LyumaAv3Menu)target;
+
+ SubControl(control, controlIndex, menu, "FourAxis");
+
+ if (menu.IsActiveControl(controlIndex))
+ {
+ var sanitySubParamLength = control.subParameters.Length;
+ if (sanitySubParamLength > 0) SliderFloat(menu, control.subParameters[0], "Up", 0f, 1f);
+ if (sanitySubParamLength > 1) SliderFloat(menu, control.subParameters[1], "Right", 0f, 1f);
+ if (sanitySubParamLength > 2) SliderFloat(menu, control.subParameters[2], "Down", 0f, 1f);
+ if (sanitySubParamLength > 3) SliderFloat(menu, control.subParameters[3], "Left", 0f, 1f);
+
+ var oldColor = Color.HSVToRGB(
+ 0,
+ (sanitySubParamLength > 0 ? menu.FindFloat(control.subParameters[0].name) : 0) * 0.5f + 0.5f
+ -(sanitySubParamLength > 2 ? menu.FindFloat(control.subParameters[2].name) : 0) * 0.5f + 0.5f,
+ (sanitySubParamLength > 1 ? menu.FindFloat(control.subParameters[1].name) : 0) * 0.5f + 0.5f
+ -(sanitySubParamLength > 3 ? menu.FindFloat(control.subParameters[3].name) : 0) * 0.5f + 0.5f);
+ var newColor = EditorGUILayout.ColorField(oldColor);
+ if (oldColor.r != newColor.r || oldColor.g != newColor.g || oldColor.b != newColor.b)
+ {
+ Color.RGBToHSV(newColor, out _, out var s, out var v);
+ if (sanitySubParamLength > 0) menu.UserFloat(control.subParameters[0].name, Mathf.Clamp(v * 2 - 1, 0f, 1f));
+ if (sanitySubParamLength > 1) menu.UserFloat(control.subParameters[1].name, Mathf.Clamp(s * 2 - 1, 0f, 1f));
+ if (sanitySubParamLength > 2) menu.UserFloat(control.subParameters[2].name, -Mathf.Clamp(v * 2 - 1, -1f, 0f));
+ if (sanitySubParamLength > 3) menu.UserFloat(control.subParameters[3].name, -Mathf.Clamp(s * 2 - 1, -1f, 0f));
+ }
+ }
+ }
+
+ private void SubControl(VRCExpressionsMenu.Control control, int controlIndex, LyumaAv3Menu menu, string labelType)
+ {
+ var parameterName = control.parameter.name;
+ var intValue = (int) control.value;
+
+ var isActive = menu.IsVisualActive(parameterName, intValue);
+
+ EditorGUILayout.BeginHorizontal();
+ EditorGUI.BeginDisabledGroup(menu.HasActiveControl() && !menu.IsActiveControl(controlIndex));
+ if (GreenBackground(isActive || menu.IsActiveControl(controlIndex), () => ParameterizedButton(control, parameterName, intValue)))
+ {
+ if (!menu.IsActiveControl(controlIndex))
+ {
+ if (IsValidParameterName(parameterName))
+ {
+ menu.UserControlEnter(controlIndex, parameterName, intValue);
+ }
+ else
+ {
+ menu.UserControlEnter(controlIndex);
+ }
+ }
+ else
+ {
+ menu.UserControlExit();
+ }
+ }
+
+ EditorGUI.EndDisabledGroup();
+ LabelType(labelType);
+ EditorGUILayout.EndHorizontal();
+ }
+
+ private static void SliderFloat(LyumaAv3Menu menu, VRCExpressionsMenu.Control.Parameter subParam, string intent, float left, float right)
+ {
+ if (subParam == null || subParam.name == "")
+ {
+ EditorGUI.BeginDisabledGroup(true);
+ EditorGUILayout.Slider(intent, 0, left, right);
+ EditorGUI.EndDisabledGroup();
+ return;
+ }
+
+ menu.UserFloat(subParam.name, EditorGUILayout.Slider(intent + " (" + subParam.name + ")", menu.FindFloat(subParam.name), left, right));
+ }
+
+ private bool ParameterizedButton(VRCExpressionsMenu.Control control, string parameterName, float wantedValue)
+ {
+ var hasParameter = IsValidParameterName(parameterName);
+ return GUILayout.Button(new GUIContent(control.name + (hasParameter ? " (" + parameterName + " = " + wantedValue + ")" : ""), ResizedIcon(control.icon)));
+ }
+
+ private Texture2D ResizedIcon(Texture2D originalIcon)
+ {
+ if (_resizedIcons.ContainsKey(originalIcon))
+ {
+ return _resizedIcons[originalIcon];
+ }
+
+ var resizedIcon = GenerateResizedIcon(originalIcon, 32);
+ _resizedIcons[originalIcon] = resizedIcon;
+ return resizedIcon;
+ }
+
+ private static Texture2D GenerateResizedIcon(Texture2D originalIcon, int width)
+ {
+ var render = new RenderTexture(width, width, 24);
+ RenderTexture.active = render;
+ Graphics.Blit(originalIcon, render);
+
+ var resizedIcon = new Texture2D(width, width);
+ resizedIcon.ReadPixels(new Rect(0, 0, width, width), 0, 0);
+ resizedIcon.Apply();
+
+ return resizedIcon;
+ }
+
+ private static T GreenBackground<T>(bool isActive, Func<T> inside)
+ {
+ var col = GUI.color;
+ try
+ {
+ if (isActive) GUI.color = Color.green;
+ return inside();
+ }
+ finally
+ {
+ GUI.color = col;
+ }
+ }
+
+ private static void LabelType(string toggle)
+ {
+ EditorGUILayout.LabelField(toggle, GUILayout.Width(70), GUILayout.ExpandHeight(true));
+ }
+
+ private static bool IsValidParameterName(string parameterName)
+ {
+ return !string.IsNullOrEmpty(parameterName);
+ }
+}
diff --git a/VRCSDK3AvatarsLegacy/Assets/Resources/Lyuma/Av3Emulator/Editor/LyumaAv3MenuEditor.cs.meta b/VRCSDK3AvatarsLegacy/Assets/Resources/Lyuma/Av3Emulator/Editor/LyumaAv3MenuEditor.cs.meta
new file mode 100644
index 00000000..f613af7d
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/Resources/Lyuma/Av3Emulator/Editor/LyumaAv3MenuEditor.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 187ea10b8ccd4441a6399698c23122e3
+timeCreated: 1604152513 \ No newline at end of file