summaryrefslogtreecommitdiff
path: root/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts.meta8
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core.meta8
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/Editor.meta8
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/Editor/GmgAnimationHelper.cs38
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/Editor/GmgAnimationHelper.cs.meta11
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/Editor/GmgAnimatorControllerHelper.cs48
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/Editor/GmgAnimatorControllerHelper.cs.meta11
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/Editor/GmgLayoutHelper.cs219
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/Editor/GmgLayoutHelper.cs.meta11
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/Editor/GmgVisualElementExtensions.cs56
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/Editor/GmgVisualElementExtensions.cs.meta11
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/GmgAvatarMaskHelper.cs37
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/GmgAvatarMaskHelper.cs.meta11
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/GmgVisualMeshHelper.cs21
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/GmgVisualMeshHelper.cs.meta11
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/VisualElements.meta8
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/VisualElements/GmgCircleElement.cs145
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/VisualElements/GmgCircleElement.cs.meta11
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/VisualElements/GmgTmpRichTextElement.cs157
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/VisualElements/GmgTmpRichTextElement.cs.meta3
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor.meta8
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/GestureManagerEditor.cs351
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/GestureManagerEditor.cs.meta11
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/GestureManagerStyles.cs257
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/GestureManagerStyles.cs.meta11
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules.meta8
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/ModuleHelper.cs32
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/ModuleHelper.cs.meta11
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc2.meta8
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc2/ModuleVrc2.cs450
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc2/ModuleVrc2.cs.meta11
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3.meta8
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/AnimatorControllerWeight.cs102
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/AnimatorControllerWeight.cs.meta11
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/AvatarDynamics.meta3
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/AvatarDynamics/AnimParameterAccessAvatarGmg.cs39
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/AvatarDynamics/AnimParameterAccessAvatarGmg.cs.meta3
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/AvatarDynamics/AvatarDynamicReset.cs49
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/AvatarDynamics/AvatarDynamicReset.cs.meta3
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/AvatarTools.cs308
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/AvatarTools.cs.meta3
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/DummyModes.meta3
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/DummyModes/Vrc3DummyMode.cs55
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/DummyModes/Vrc3DummyMode.cs.meta3
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/DummyModes/Vrc3EditMode.cs37
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/DummyModes/Vrc3EditMode.cs.meta3
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/DummyModes/Vrc3TestMode.cs40
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/DummyModes/Vrc3TestMode.cs.meta3
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/ModuleVrc3.cs853
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/ModuleVrc3.cs.meta11
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/ModuleVrc3ProxyOverride.cs102
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/ModuleVrc3ProxyOverride.cs.meta11
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/ModuleVrc3Styles.cs184
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/ModuleVrc3Styles.cs.meta11
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl.meta3
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/EndpointControl.cs94
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/EndpointControl.cs.meta3
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/OscModule.cs436
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/OscModule.cs.meta3
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/OscPacket.cs384
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/OscPacket.cs.meta3
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/OscSettings.cs257
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/OscSettings.cs.meta3
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/UdpListener.cs60
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/UdpListener.cs.meta3
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/UdpSender.cs33
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/UdpSender.cs.meta3
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/VisualElements.meta3
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/VisualElements/VisualEp.cs117
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/VisualElements/VisualEp.cs.meta3
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/VisualElements/VisualEpContainer.cs33
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/VisualElements/VisualEpContainer.cs.meta3
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/VisualElements/VisualEpStyles.cs77
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/VisualElements/VisualEpStyles.cs.meta3
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Params.meta8
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Params/Vrc3Param.cs151
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Params/Vrc3Param.cs.meta11
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Params/Vrc3ParamBool.cs27
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Params/Vrc3ParamBool.cs.meta11
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Params/Vrc3ParamController.cs56
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Params/Vrc3ParamController.cs.meta11
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Params/Vrc3ParamExternal.cs39
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Params/Vrc3ParamExternal.cs.meta11
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialButtons.meta8
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialButtons/Dynamics.meta8
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialButtons/Dynamics/VisualRadialElement.cs22
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialButtons/Dynamics/VisualRadialElement.cs.meta11
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialButtons/Dynamics/VisualRunningElement.cs43
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialButtons/Dynamics/VisualRunningElement.cs.meta11
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialButtons/RadialMenuButton.cs31
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialButtons/RadialMenuButton.cs.meta11
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialButtons/RadialMenuControl.cs110
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialButtons/RadialMenuControl.cs.meta11
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialButtons/RadialMenuDynamic.cs94
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialButtons/RadialMenuDynamic.cs.meta11
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialButtons/RadialMenuItem.cs47
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialButtons/RadialMenuItem.cs.meta11
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialCursor.cs157
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialCursor.cs.meta11
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialDescription.cs43
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialDescription.cs.meta11
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialMenu.cs433
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialMenu.cs.meta11
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialMenuUtility.cs373
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialMenuUtility.cs.meta11
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialPage.cs38
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialPage.cs.meta11
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialPuppets.meta8
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialPuppets/Base.meta8
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialPuppets/Base/BaseAxisPuppet.cs47
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialPuppets/Base/BaseAxisPuppet.cs.meta11
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialPuppets/Base/BasePuppet.cs29
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialPuppets/Base/BasePuppet.cs.meta11
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialPuppets/FourAxisPuppet.cs24
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialPuppets/FourAxisPuppet.cs.meta11
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialPuppets/RadialPuppet.cs90
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialPuppets/RadialPuppet.cs.meta11
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialPuppets/TwoAxisPuppet.cs22
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialPuppets/TwoAxisPuppet.cs.meta11
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3Debug.meta3
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3Debug/Avatar.meta3
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3Debug/Avatar/Vrc3AvatarDebugWindow.cs253
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3Debug/Avatar/Vrc3AvatarDebugWindow.cs.meta3
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3Debug/Osc.meta3
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3Debug/Osc/Vrc3OscDebugWindow.cs53
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3Debug/Osc/Vrc3OscDebugWindow.cs.meta3
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3DefaultParams.cs62
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3DefaultParams.cs.meta3
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3VisualRender.cs39
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3VisualRender.cs.meta3
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3WeightController.cs57
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3WeightController.cs.meta3
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3WeightSlider.cs149
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3WeightSlider.cs.meta3
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Extra.meta8
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Extra/GestureHand.cs8
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Extra/GestureHand.cs.meta11
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Extra/ModuleBase.cs83
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Extra/ModuleBase.cs.meta11
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Extra/TransformData.cs41
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Extra/TransformData.cs.meta3
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/GestureManager.cs97
-rw-r--r--VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/GestureManager.cs.meta11
143 files changed, 8409 insertions, 0 deletions
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts.meta
new file mode 100644
index 00000000..8a974e77
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: f55d4ac6c901e7e478cbce95ff1ebefa
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core.meta
new file mode 100644
index 00000000..559f0a24
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 943ced01a0d2f7e4b8484b0c7167c019
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/Editor.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/Editor.meta
new file mode 100644
index 00000000..6dd7da46
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/Editor.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: ae3217cbe6ef6154684815eed799c212
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/Editor/GmgAnimationHelper.cs b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/Editor/GmgAnimationHelper.cs
new file mode 100644
index 00000000..cfd5ed28
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/Editor/GmgAnimationHelper.cs
@@ -0,0 +1,38 @@
+using UnityEditor;
+using UnityEngine;
+
+namespace GestureManager.Scripts.Core.Editor
+{
+ /**
+ * Hi, you're a curious one!
+ *
+ * What you're looking at are some of the methods of my Unity Libraries.
+ * They do not contains all the methods otherwise the UnityPackage would have been so much bigger.
+ *
+ * And actually the "Clone Animation Asset" is way different... but i need only to clone frames and not other stuffs, so who cares ;3
+ *
+ * P.S: Gmg stands for GestureManager~
+ */
+ public static class GmgAnimationHelper
+ {
+ public static AnimationClip CloneAnimationAsset(AnimationClip toClone)
+ {
+ var toRet = new AnimationClip
+ {
+ name = toClone.name,
+ frameRate = toClone.frameRate,
+ legacy = toClone.legacy,
+ localBounds = toClone.localBounds,
+ wrapMode = toClone.wrapMode
+ };
+
+ var curveBindings = AnimationUtility.GetCurveBindings(toClone);
+ var referBindings = AnimationUtility.GetObjectReferenceCurveBindings(toClone);
+
+ foreach (var binding in curveBindings) AnimationUtility.SetEditorCurve(toRet, binding, AnimationUtility.GetEditorCurve(toClone, binding));
+ foreach (var binding in referBindings) AnimationUtility.SetObjectReferenceCurve(toRet, binding, AnimationUtility.GetObjectReferenceCurve(toClone, binding));
+
+ return toRet;
+ }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/Editor/GmgAnimationHelper.cs.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/Editor/GmgAnimationHelper.cs.meta
new file mode 100644
index 00000000..8a1bf30c
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/Editor/GmgAnimationHelper.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: a2059bee06c9a2446a1092a900e9a725
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/Editor/GmgAnimatorControllerHelper.cs b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/Editor/GmgAnimatorControllerHelper.cs
new file mode 100644
index 00000000..99ad33dc
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/Editor/GmgAnimatorControllerHelper.cs
@@ -0,0 +1,48 @@
+using System.Collections.Generic;
+using UnityEditor.Animations;
+using UnityEngine;
+
+namespace GestureManager.Scripts.Core.Editor
+{
+ /**
+ * Hi, you're a curious one!
+ *
+ * What you're looking at are some of the methods of my Unity Libraries.
+ * They do not contains all the methods otherwise the UnityPackage would have been so much bigger.
+ *
+ * P.S: Gmg stands for GestureManager~
+ */
+ public static class GmgAnimatorControllerHelper
+ {
+ public static IEnumerable<KeyValuePair<AnimationClip, AnimationClip>> GetOverrides(AnimatorOverrideController overrideController)
+ {
+ var overrides = new List<KeyValuePair<AnimationClip, AnimationClip>>();
+ overrideController.GetOverrides(overrides);
+ return overrides;
+ }
+
+ public static AnimatorController CreateControllerWith(IEnumerable<AnimationClip> clips)
+ {
+ var controller = CreateControllerWith(new AnimationClip { name = "[SELECT YOUR ANIMATION!]" });
+ foreach (var clip in clips) AddMotion(controller, clip);
+ return controller;
+ }
+
+ public static AnimatorController CreateControllerWith(AnimationClip clip)
+ {
+ var controller = new AnimatorController { layers = new[] { new AnimatorControllerLayer { stateMachine = new AnimatorStateMachine() } } };
+ controller.AddMotion(clip);
+ return controller;
+ }
+
+ private static void AddMotion(AnimatorController controller, Motion motion) => AddMotion(controller, motion, motion.name.Replace(".", "_"));
+
+ private static void AddMotion(AnimatorController controller, Motion motion, string name)
+ {
+ var originalName = motion.name;
+ motion.name = name;
+ controller.AddMotion(motion);
+ motion.name = originalName;
+ }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/Editor/GmgAnimatorControllerHelper.cs.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/Editor/GmgAnimatorControllerHelper.cs.meta
new file mode 100644
index 00000000..0fb78162
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/Editor/GmgAnimatorControllerHelper.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 24c9e4aa6854f1948bd7e2221ba39edb
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/Editor/GmgLayoutHelper.cs b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/Editor/GmgLayoutHelper.cs
new file mode 100644
index 00000000..b7d776b8
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/Editor/GmgLayoutHelper.cs
@@ -0,0 +1,219 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using GestureManager.Scripts.Editor;
+using UnityEditor;
+using UnityEngine;
+
+namespace GestureManager.Scripts.Core.Editor
+{
+ /**
+ * Hi, you're a curious one!
+ *
+ * What you're looking at are some of the methods of my Unity Libraries.
+ * They do not contains all the methods otherwise the UnityPackage would have been so much bigger.
+ *
+ * By the way, don't pay too much attention to the UnityFieldEnterListener, I kinda rushed it~ ^-^
+ * The concept behind it it's a Field that does not change its value until the user press Enter.
+ * The overcomplexity is due to Unity not being friendly with value resets... So I kinda had to workaround it.
+ *
+ * P.S: Gmg stands for GestureManager~
+ */
+ public static class GmgLayoutHelper
+ {
+ private static object _unityFieldEnterListenerData;
+ private static string _unityFieldEnterListenerName;
+
+ public struct Toolbar
+ {
+ private GUIContent[] _contents;
+
+ public GUIContent[] Contents(IEnumerable<(string, Action)> field) => _contents ?? (_contents = field.Select(tuple => new GUIContent(tuple.Item1)).ToArray());
+ public int Selected;
+ }
+
+ public static void MyToolbar(ref Toolbar toolbar, (string, Action)[] field) => MyToolbar(ref toolbar.Selected, toolbar.Contents(field), field);
+
+ private static void MyToolbar(ref int toolbar, GUIContent[] contents, (string, Action)[] actions)
+ {
+ toolbar = GUILayout.Toolbar(toolbar, contents);
+ actions[toolbar].Item2();
+ }
+
+ public static IEnumerable<EditorWindow> GetInspectorWindows() => Resources.FindObjectsOfTypeAll<EditorWindow>().Where(window => window.titleContent.text == "Inspector");
+
+ public static void GuiLabel((Color? color, string text) tuple) => GuiLabel(tuple.color, tuple.text);
+
+ public static void GuiLabel((Color? color, string text) tuple, params GUILayoutOption[] options) => GuiLabel(tuple.color, tuple.text, null, options);
+
+ private static void GuiLabel(Color? color, string text, GUIStyle style = null, params GUILayoutOption[] options)
+ {
+ style = style ?? GUI.skin.label;
+ if (color != null)
+ {
+ var textColor = GUI.contentColor;
+ GUI.contentColor = color.Value;
+ GUILayout.Label(text, style, options);
+ GUI.contentColor = textColor;
+ }
+ else GUILayout.Label(text, style, options);
+ }
+
+ public static bool Button(string text, Color color, params GUILayoutOption[] options)
+ {
+ using (new GuiBackground(color)) return GUILayout.Button(text, options);
+ }
+
+ public static bool DebugButton(string label, GUILayoutOption width = null) => GUILayout.Button(label, GUILayout.Height(30), width ?? GUILayout.Width(150));
+
+ public static bool TitleButton(string text, string button, int width = 85)
+ {
+ using (new GUILayout.HorizontalScope())
+ {
+ GUILayout.Space(15 + width);
+ GUILayout.Label(text, GestureManagerStyles.Header);
+ return GUILayout.Button(button, GestureManagerStyles.HeaderButton, GUILayout.Width(width));
+ }
+ }
+
+ public static bool UnityFieldEnterListener<T1, T2>(T1 initialText, T2 argument, Rect rect, Func<Rect, T1, T1> field, Action<T2, T1> onEscape, string controlName)
+ {
+ if (_unityFieldEnterListenerName != controlName) _unityFieldEnterListenerData = null;
+ var isEnter = Event.current.keyCode == KeyCode.Return || Event.current.keyCode == KeyCode.KeypadEnter;
+ var isEscape = Event.current.keyCode == KeyCode.Escape;
+ _unityFieldEnterListenerName = controlName;
+ GUI.SetNextControlName(controlName);
+ GUI.FocusControl(controlName);
+ if (_unityFieldEnterListenerData == null) _unityFieldEnterListenerData = initialText;
+ var t = field(rect, _unityFieldEnterListenerData is T1 d ? d : default);
+ if (isEnter) onEscape(argument, _unityFieldEnterListenerData is T1 data ? data : default);
+ else _unityFieldEnterListenerData = t;
+ if (!isEnter && !isEscape) return false;
+ _unityFieldEnterListenerData = null;
+ GUI.SetNextControlName(controlName);
+ GUI.FocusControl(controlName);
+ EditorGUI.LabelField(Rect.zero, "");
+ return true;
+ }
+
+ /*
+ * Rect Utils!!!
+ * Rect Utils!!!
+ * Rect Utils!!!
+ */
+
+ private static Rect SubtractWidthRight(Rect rect, int width, out Rect rectR)
+ {
+ rect.width -= width;
+ rectR = rect;
+ rectR.width = width;
+ rectR.x += rect.width;
+ return rect;
+ }
+
+ /*
+ * Object Field!!!
+ * Object Field!!!
+ * Object Field!!!
+ */
+
+ private static T ObjectField<T>(string label, T unityObject) where T : UnityEngine.Object
+ {
+ if (label != null) return (T)EditorGUILayout.ObjectField(label, unityObject, typeof(T), true, null);
+ return (T)EditorGUILayout.ObjectField(unityObject, typeof(T), true, null);
+ }
+
+ public static T ObjectField<T>(string label, T unityObject, Action<T> onObjectSet) where T : UnityEngine.Object
+ {
+ if (unityObject != (unityObject = ObjectField(label, unityObject))) onObjectSet(unityObject);
+ return unityObject;
+ }
+
+ public static bool ButtonObjectField<T>(string label, T unityObject, char text, Action<T> onNewObjectDrop) where T : UnityEngine.Object
+ {
+ var rectL = SubtractWidthRight(GUILayoutUtility.GetRect(new GUIContent(), GUI.skin.label), 20, out var rectR);
+ if (unityObject != (unityObject = EditorGUI.ObjectField(rectL, label, unityObject, typeof(T), true) as T)) onNewObjectDrop?.Invoke(unityObject);
+ return GUI.Button(rectR, text.ToString());
+ }
+
+ public static Color ResetColorField(string label, Color current, Color reset)
+ {
+ var rectL = SubtractWidthRight(GUILayoutUtility.GetRect(new GUIContent(), GUI.skin.label), 20, out var rectR);
+ return GUI.Button(rectR, "X") ? reset : EditorGUI.ColorField(rectL, label, current);
+ }
+
+ public static void Divisor(int height)
+ {
+ var rect = EditorGUILayout.GetControlRect(false, height);
+ rect.height = height;
+ EditorGUI.DrawRect(rect, new Color(0.5f, 0.5f, 0.5f, 1));
+ }
+
+ public static Rect GetLastRect(ref Rect lastRect)
+ {
+ if (Event.current.type == EventType.Layout || Event.current.type == EventType.Used) return lastRect;
+ return lastRect = GUILayoutUtility.GetLastRect();
+ }
+
+ public static ulong ULongField(string label, ulong value)
+ {
+ var backULong = value;
+ return ulong.TryParse(EditorGUILayout.TextField(label, value.ToString()), out value) ? value : backULong;
+ }
+
+ public static bool ToggleRight(string label, bool active)
+ {
+ GUILayout.Label(label);
+ SubtractWidthRight(GUILayoutUtility.GetLastRect(), 15, out var rectR);
+ return GUI.Toggle(rectR, active, new GUIContent());
+ }
+
+ public static string PlaceHolderTextField(string label, string text, string placeHolder)
+ {
+ text = EditorGUILayout.TextField(label, text);
+ if(string.IsNullOrEmpty(text)) EditorGUI.LabelField(GUILayoutUtility.GetLastRect(), " ", placeHolder);
+ return text;
+ }
+
+ /*
+ * Classes...
+ * Classes...
+ * Classes...
+ */
+
+ internal class GuiBackground : IDisposable
+ {
+ private readonly Color _color;
+
+ public GuiBackground(Color color)
+ {
+ _color = GUI.backgroundColor;
+ GUI.backgroundColor = color;
+ }
+
+ public void Dispose() => GUI.backgroundColor = _color;
+ }
+
+ public static void HorizontalGrid<T1, T2>(T2 t2, float width, float sizeWidth, float sizeHeight, float divisor, IList<T1> data, Action<T2, Rect, T1> gridRect) where T1 : class
+ {
+ var intCount = (int)(width / sizeWidth);
+ if (intCount <= 0) return;
+ GUILayout.BeginHorizontal();
+ for (var i = 0; i < data.Count + (intCount - data.Count % intCount) % intCount; i++)
+ {
+ var t = i < data.Count ? data[i] : null;
+ GUILayout.FlexibleSpace();
+ var rect = GUILayoutUtility.GetRect(sizeWidth, sizeHeight);
+ if (t != null) gridRect(t2, rect, t);
+
+ if ((i + 1) % intCount != 0) continue;
+ GUILayout.FlexibleSpace();
+ GUILayout.EndHorizontal();
+ GUILayout.Space(divisor);
+ GUILayout.BeginHorizontal();
+ }
+
+ GUILayout.EndHorizontal();
+ }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/Editor/GmgLayoutHelper.cs.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/Editor/GmgLayoutHelper.cs.meta
new file mode 100644
index 00000000..768b0f48
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/Editor/GmgLayoutHelper.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 849c683df1796a3499b3d8c991ff2dc9
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/Editor/GmgVisualElementExtensions.cs b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/Editor/GmgVisualElementExtensions.cs
new file mode 100644
index 00000000..16c15011
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/Editor/GmgVisualElementExtensions.cs
@@ -0,0 +1,56 @@
+using UnityEditor;
+using UnityEditor.UIElements;
+using UnityEngine;
+using UnityEngine.UIElements;
+
+namespace GestureManager.Scripts.Core.Editor
+{
+ /**
+ * Hi, you're a curious one!
+ *
+ * What you're looking at are some of the methods of my Unity Libraries.
+ * They do not contains all the methods otherwise the UnityPackage would have been so much bigger.
+ *
+ * P.S: Gmg stands for GestureManager~
+ */
+ public static class GmgVisualElementExtensions
+ {
+ public static T MyAdd<T>(this VisualElement visualElement, T child) where T : VisualElement
+ {
+ visualElement.Add(child);
+ return child;
+ }
+
+ public static T With<T>(this T visualElement, VisualElement child) where T : VisualElement
+ {
+ visualElement.Add(child);
+ return visualElement;
+ }
+
+ public static T MyBorder<T>(this T visualElement, float width, float radius, Color color) where T : VisualElement
+ {
+ visualElement.style.borderBottomRightRadius = radius;
+ visualElement.style.borderBottomLeftRadius = radius;
+ visualElement.style.borderTopRightRadius = radius;
+ visualElement.style.borderTopLeftRadius = radius;
+ visualElement.style.borderBottomWidth = width;
+ visualElement.style.borderRightWidth = width;
+ visualElement.style.borderLeftWidth = width;
+ visualElement.style.borderTopWidth = width;
+ visualElement.style.borderBottomColor = color;
+ visualElement.style.borderRightColor = color;
+ visualElement.style.borderLeftColor = color;
+ visualElement.style.borderTopColor = color;
+ return visualElement;
+ }
+
+ public static void MySetAntiAliasing(this EditorWindow window, int antiAliasing)
+ {
+ if (!window || window.GetAntiAliasing() == antiAliasing) return;
+
+ window.SetAntiAliasing(antiAliasing);
+ // Dumb workaround method to trigger the internal MakeParentsSettingsMatchMe() method on the EditorWindow.
+ window.minSize = window.minSize;
+ }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/Editor/GmgVisualElementExtensions.cs.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/Editor/GmgVisualElementExtensions.cs.meta
new file mode 100644
index 00000000..a8606a70
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/Editor/GmgVisualElementExtensions.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 18c8d343c0c94952974882cfcbbd586b
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/GmgAvatarMaskHelper.cs b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/GmgAvatarMaskHelper.cs
new file mode 100644
index 00000000..29579b6a
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/GmgAvatarMaskHelper.cs
@@ -0,0 +1,37 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using UnityEngine;
+
+namespace GestureManager.Scripts.Core
+{
+ /**
+ * Hi, you're a curious one!
+ *
+ * What you're looking at are some of the methods of my Unity Libraries.
+ * They do not contains all the methods otherwise the UnityPackage would have been so much bigger.
+ *
+ * P.S: Gmg stands for GestureManager~
+ */
+ public static class GmgAvatarMaskHelper
+ {
+ private static readonly List<AvatarMaskBodyPart> BodyParts = Enum.GetValues(typeof(AvatarMaskBodyPart)).Cast<AvatarMaskBodyPart>().Where(part => part != AvatarMaskBodyPart.LastBodyPart).ToList();
+
+ public static AvatarMask CreateMaskWith(string name, IEnumerable<AvatarMaskBodyPart> parts)
+ {
+ var mask = new AvatarMask { name = name };
+ foreach (var part in BodyParts) mask.SetHumanoidBodyPartActive(part, false);
+ foreach (var part in parts) mask.SetHumanoidBodyPartActive(part, true);
+ return mask;
+ }
+
+ public static AvatarMask CreateMaskWithout(string name, IEnumerable<AvatarMaskBodyPart> parts)
+ {
+ var mask = new AvatarMask { name = name };
+ foreach (var part in parts) mask.SetHumanoidBodyPartActive(part, false);
+ return mask;
+ }
+
+ public static AvatarMask CreateEmptyMask(string name) => CreateMaskWith(name, Array.Empty<AvatarMaskBodyPart>());
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/GmgAvatarMaskHelper.cs.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/GmgAvatarMaskHelper.cs.meta
new file mode 100644
index 00000000..f900f602
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/GmgAvatarMaskHelper.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 1259acfa8a07401bb4eb47b8843c69e6
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/GmgVisualMeshHelper.cs b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/GmgVisualMeshHelper.cs
new file mode 100644
index 00000000..10f7944b
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/GmgVisualMeshHelper.cs
@@ -0,0 +1,21 @@
+using UnityEngine;
+using UnityEngine.UIElements;
+
+namespace GestureManager.Scripts.Core
+{
+ /**
+ * Hi, you're a curious one!
+ *
+ * What you're looking at are some of the methods of my Unity Libraries.
+ * They do not contains all the methods otherwise the UnityPackage would have been so much bigger.
+ *
+ * P.S: Gmg stands for GestureManager~
+ */
+ public static class GmgVisualMesh
+ {
+ public static class CenterRelative
+ {
+ public static Vector3 PositionOf(float x, float y, float w, float h) => new Vector3((x / 2 + 0.5f) * w, (y / 2 + 0.5f) * h, Vertex.nearZ);
+ }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/GmgVisualMeshHelper.cs.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/GmgVisualMeshHelper.cs.meta
new file mode 100644
index 00000000..4bc953ef
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/GmgVisualMeshHelper.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: e61cedaa57b749939fa3f040c3b46cc1
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/VisualElements.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/VisualElements.meta
new file mode 100644
index 00000000..5d61ac71
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/VisualElements.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 50e033fd181a4a4b936fdb78222fa4cc
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/VisualElements/GmgCircleElement.cs b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/VisualElements/GmgCircleElement.cs
new file mode 100644
index 00000000..75549d29
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/VisualElements/GmgCircleElement.cs
@@ -0,0 +1,145 @@
+using System;
+using UnityEngine;
+using UnityEngine.UIElements;
+using CR = GestureManager.Scripts.Core.GmgVisualMesh.CenterRelative;
+
+namespace GestureManager.Scripts.Core.VisualElements
+{
+ public class GmgCircleElement : VisualElement
+ {
+ private const float Tolerance = 0.00001f;
+ private float _radialProgress = Mathf.PI * 2;
+
+ private float _progress = 1f;
+
+ public float Progress
+ {
+ set
+ {
+ if (Math.Abs(_progress - value) < Tolerance) return;
+ _progress = value;
+ _radialProgress = _progress * Mathf.PI * 2;
+ MarkDirtyRepaint();
+ }
+ }
+
+ private float _borderWidth = 2f;
+
+ public float BorderWidth
+ {
+ set
+ {
+ if (Math.Abs(_borderWidth - value) < Tolerance) return;
+ _borderWidth = value;
+ MarkDirtyRepaint();
+ }
+ }
+
+ private Color _borderColor;
+
+ public Color BorderColor
+ {
+ set
+ {
+ if (_borderColor == value) return;
+ _borderColor = value;
+ MarkDirtyRepaint();
+ }
+ }
+
+ private Color _vertexColor = Color.white;
+
+ public Color VertexColor
+ {
+ set
+ {
+ if (_vertexColor == value) return;
+ _vertexColor = value;
+ MarkDirtyRepaint();
+ }
+ }
+
+ private Color _centerColor;
+
+ public Color CenterColor
+ {
+ set
+ {
+ if (_centerColor == value) return;
+ _centerColor = value;
+ MarkDirtyRepaint();
+ }
+ }
+
+ public GmgCircleElement()
+ {
+ generateVisualContent += OnGenerateVisualContent;
+ }
+
+ private void OnGenerateVisualContent(MeshGenerationContext mgc)
+ {
+ PolyMesh(100, out var vertices, out var indices);
+ var writeData = mgc.Allocate(vertices.Length, indices.Length);
+ writeData.SetAllVertices(vertices);
+ writeData.SetAllIndices(indices);
+ }
+
+ private void PolyMesh(int n, out Vertex[] vertices, out ushort[] indices)
+ {
+ if (_borderWidth != 0) BorderPolyMesh(n, out vertices, out indices);
+ else SimplePolyMesh(n, out vertices, out indices);
+ }
+
+ private void SimplePolyMesh(int n, out Vertex[] vertices, out ushort[] indices)
+ {
+ var w = contentRect.width;
+ var h = contentRect.height;
+
+ vertices = new Vertex[n + 1];
+ vertices[0] = new Vertex { position = CR.PositionOf(0, 0, w, h), tint = _centerColor };
+ for (var i = 1; i < n + 1; i++)
+ {
+ var angle = _radialProgress * (i - 1) / (n - 1);
+ vertices[i] = new Vertex { position = CR.PositionOf(Mathf.Sin(angle), -Mathf.Cos(angle), w, h), tint = _vertexColor };
+ }
+
+ indices = new ushort[n * 3];
+ for (var i = 0; i < n - 1; i++)
+ {
+ indices[i * 3 + 0] = 0;
+ indices[i * 3 + 1] = (ushort)(i + 1);
+ indices[i * 3 + 2] = (ushort)(i + 2);
+ }
+ }
+
+ private void BorderPolyMesh(int n, out Vertex[] vertices, out ushort[] indices)
+ {
+ var w = contentRect.width;
+ var h = contentRect.height;
+ var bw = contentRect.width - _borderWidth * 2;
+ var bh = contentRect.height - _borderWidth * 2;
+
+ vertices = new Vertex[n * 2 + 1];
+ vertices[0] = new Vertex { position = CR.PositionOf(0, 0, w, h), tint = _centerColor };
+
+ for (var i = 1; i < n + 1; i++)
+ {
+ var angle = _radialProgress * (i - 1) / (n - 1);
+ vertices[i] = new Vertex { position = CR.PositionOf(Mathf.Sin(angle), -Mathf.Cos(angle), w, h), tint = _borderColor };
+ vertices[n + i] = new Vertex { position = CR.PositionOf(Mathf.Sin(angle), -Mathf.Cos(angle), bw, bh) + new Vector3(_borderWidth, _borderWidth), tint = _vertexColor };
+ }
+
+ indices = new ushort[n * 6];
+ for (var i = 0; i < n - 1; i++)
+ {
+ var ixn = i + n;
+ indices[i * 3 + 0] = 0;
+ indices[i * 3 + 1] = (ushort)(i + 1);
+ indices[i * 3 + 2] = (ushort)(i + 2);
+ indices[ixn * 3 + 0] = 0;
+ indices[ixn * 3 + 1] = (ushort)(ixn + 1);
+ indices[ixn * 3 + 2] = (ushort)(ixn + 2);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/VisualElements/GmgCircleElement.cs.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/VisualElements/GmgCircleElement.cs.meta
new file mode 100644
index 00000000..8c02bb1a
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/VisualElements/GmgCircleElement.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 60459b81d0b8454ab51c09888582e36e
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/VisualElements/GmgTmpRichTextElement.cs b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/VisualElements/GmgTmpRichTextElement.cs
new file mode 100644
index 00000000..fac533e8
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/VisualElements/GmgTmpRichTextElement.cs
@@ -0,0 +1,157 @@
+using System;
+using System.Linq;
+using System.Collections.Generic;
+using System.Text.RegularExpressions;
+using UnityEngine;
+using UnityEngine.UIElements;
+using UIEPosition = UnityEngine.UIElements.Position;
+
+namespace GestureManager.Scripts.Core.VisualElements
+{
+ public class GmgTmpRichTextElement : VisualElement
+ {
+ private static string RegexTokenPattern => "<[^<>]+>";
+ private static string RegexStringPattern => "(.*?<[^<>]+>|.+)";
+
+ private const string V_OFFSET = "voffset";
+
+ private const string SUX_ATTRIBUTE = "8";
+
+ private string _text;
+
+ private Data _data;
+
+ private readonly VisualElement _textHolder;
+
+ private class Data
+ {
+ private static Dictionary<string, Color> TextMeshProColorNames => new Dictionary<string, Color>
+ {
+ { "black", Color.black },
+ { "blue", Color.blue },
+ { "green", Color.green },
+ { "orange", new Color(1f, 0.5f, 0f) },
+ { "purple", new Color(0.63f, 0.13f, 0.94f) },
+ { "red", Color.red },
+ { "white", Color.white },
+ { "yellow", new Color(1f, 0.92f, 0f) }
+ };
+
+ public FontStyle FontStyle => Italic == 0 && Bold == 0 ? FontStyle.Normal : Italic == 0 ? FontStyle.Bold : Bold == 0 ? FontStyle.Italic : FontStyle.BoldAndItalic;
+ public StyleColor ColorStyle(StyleColor fallback) => TextColor.LastOrDefault() ?? fallback;
+ public StyleLength SizeStyle(StyleLength fallback) => Size.LastOrDefault() ?? fallback;
+ public StyleColor MarkStyle => Mark.LastOrDefault() ?? Color.clear;
+
+ internal int Italic;
+ internal int Bold;
+ internal readonly List<Color?> Mark = new List<Color?>();
+ internal readonly List<Color?> TextColor = new List<Color?>();
+ internal readonly List<StyleLength?> Size = new List<StyleLength?>();
+
+ private static bool HandleList<T>(IList<T> list, bool close, string attribute, Func<IList<T>, string, bool> tryAdd)
+ {
+ if (!close) return tryAdd(list, attribute);
+ if (list.Count > 0) list.RemoveAt(list.Count - 1);
+ return true;
+ }
+
+ public static bool HandleColorList(IList<Color?> list, bool close, string attribute) => HandleList(list, close, attribute, TryAddColor);
+
+ public static bool HandleStyleLengthList(IList<StyleLength?> list, bool close, string attribute) => HandleList(list, close, attribute, TryAddStyleLength);
+
+ private static bool TryAddStyleLength(ICollection<StyleLength?> list, string attribute)
+ {
+ if (attribute == null || !int.TryParse(attribute, out var intSize)) return false;
+ var item = new StyleLength(intSize);
+ list.Add(item);
+ return true;
+ }
+
+ private static bool TryAddColor(ICollection<Color?> list, string attribute)
+ {
+ if (attribute == null || !ColorOf(attribute, out var color)) return false;
+ list.Add(color);
+ return true;
+ }
+
+ private static bool ColorOf(string attribute, out Color color) => TextMeshProColorNames.TryGetValue(attribute, out color) || ColorUtility.TryParseHtmlString(attribute, out color);
+ }
+
+ public string Text
+ {
+ set
+ {
+ if (_text == value) return;
+ _text = value;
+ SetUp(value);
+ }
+ }
+
+ public GmgTmpRichTextElement()
+ {
+ Add(new TextElement { pickingMode = PickingMode.Ignore, text = "" });
+ Add(_textHolder = new VisualElement { pickingMode = PickingMode.Ignore, style = { position = UIEPosition.Absolute } });
+ style.alignItems = Align.Center;
+ _textHolder.style.alignItems = Align.Center;
+ }
+
+ private void SetUp(string input)
+ {
+ _data = new Data();
+ foreach (var _ in Enumerable.Range(0, _textHolder.childCount)) _textHolder.RemoveAt(0);
+ foreach (var splitString in Regex.Matches(input, RegexStringPattern).OfType<Match>().Select(match => match.Value)) AddToken(splitString);
+ }
+
+ private void AddToken(string tokenInput)
+ {
+ var child = new TextElement
+ {
+ style = { unityFontStyleAndWeight = _data.FontStyle, color = _data.ColorStyle(style.color), backgroundColor = _data.MarkStyle, fontSize = _data.SizeStyle(style.fontSize) },
+ pickingMode = PickingMode.Ignore, text = Regex.Split(tokenInput, RegexTokenPattern)[0] + EvaluateToken(Regex.Match(tokenInput, RegexTokenPattern).Value)
+ };
+ if (child.text == "") return;
+ child.style.width = 100;
+ _textHolder.Add(child);
+ }
+
+ private static (string, string) GetToken(string tokenString) => GetToken(tokenString.Split(new[] { ' ', '=' }, 2));
+
+ private static (string, string) GetToken(IReadOnlyList<string> splitData) => (splitData[0], splitData.Count > 1 ? splitData[1] : null);
+
+ private string EvaluateToken(string token)
+ {
+ if (token == "") return null;
+
+ var isClose = token[1] == '/';
+ var tokenString = token.Substring(isClose ? 2 : 1, token.Length - (isClose ? 3 : 2));
+ var (tagString, attributeString) = GetToken(tokenString);
+
+ switch (tagString)
+ {
+ case "i":
+ if (!isClose) _data.Italic++;
+ else if (_data.Italic > 0) _data.Italic--;
+ return null;
+ case "b":
+ if (!isClose) _data.Bold++;
+ else if (_data.Bold > 0) _data.Bold--;
+ return null;
+ case "mark":
+ return Data.HandleColorList(_data.Mark, isClose, attributeString) ? null : token;
+ case "color":
+ return Data.HandleColorList(_data.TextColor, isClose, attributeString) ? null : token;
+ case "size":
+ return Data.HandleStyleLengthList(_data.Size, isClose, attributeString) ? null : token;
+ case "sup":
+ return Data.HandleStyleLengthList(_data.Size, isClose, SUX_ATTRIBUTE) ? null : token;
+ case "sub":
+ return Data.HandleStyleLengthList(_data.Size, isClose, SUX_ATTRIBUTE) ? null : token;
+ case V_OFFSET:
+ return null;
+ case "sprite": // No... I won't implement this perfectly... This is enough~
+ return "☻";
+ default: return token;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/VisualElements/GmgTmpRichTextElement.cs.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/VisualElements/GmgTmpRichTextElement.cs.meta
new file mode 100644
index 00000000..a6cacf76
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/VisualElements/GmgTmpRichTextElement.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 51c64d86e0e949dc8457783faef98bf1
+timeCreated: 1641297208 \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor.meta
new file mode 100644
index 00000000..75e7ca34
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 948f03d0866afcd48a004ef4e2465457
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/GestureManagerEditor.cs b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/GestureManagerEditor.cs
new file mode 100644
index 00000000..06d9ca4c
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/GestureManagerEditor.cs
@@ -0,0 +1,351 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Threading.Tasks;
+using GestureManager.Scripts.Core.Editor;
+using GestureManager.Scripts.Editor.Modules;
+using GestureManager.Scripts.Extra;
+using UnityEditor;
+using UnityEngine;
+using GmgAvatarDescriptor =
+#if VRC_SDK_VRCSDK2 || VRC_SDK_VRCSDK3
+ VRC.SDKBase.VRC_AvatarDescriptor;
+#else
+ UnityEngine.UI.GraphicRaycaster;
+#endif
+using UnityEngine.UIElements;
+
+namespace GestureManager.Scripts.Editor
+{
+ [CustomEditor(typeof(GestureManager))]
+ public class GestureManagerEditor : UnityEditor.Editor
+ {
+ private GestureManager Manager => target as GestureManager;
+ private static IEnumerable<GmgAvatarDescriptor> Descriptors => FindSceneObjectsOfTypeAll<GmgAvatarDescriptor>();
+ private IEnumerable<ModuleBase> Modules => Descriptors.Select(descriptor => ModuleHelper.GetModuleFor(Manager, descriptor)).Where(module => module != null);
+ private static bool IsValidObject(GameObject g) => g.hideFlags != HideFlags.NotEditable && g.hideFlags != HideFlags.HideAndDontSave && g.scene.name != null;
+ private static IEnumerable<T> FindSceneObjectsOfTypeAll<T>() where T : Component => Resources.FindObjectsOfTypeAll<T>().Where(t => IsValidObject(t.gameObject));
+
+ private readonly PrefUpdater _updater = new PrefUpdater();
+ private VisualElement _root;
+
+ private const int AntiAliasing = 4;
+
+ private const string Version = "3.6.0";
+ private const string Discord = "BlackStartx#6593";
+
+ private const string VersionURL = "https://raw.githubusercontent.com/BlackStartx/VRC-Gesture-Manager/master/.v3rsion";
+ private const string DiscordURL = "https://raw.githubusercontent.com/BlackStartx/VRC-Gesture-Manager/master/.discord";
+
+ [MenuItem("Tools/Gesture Manager Emulator", false, -42)]
+ public static void AddNewEmulator()
+ {
+ var asset = AssetDatabase.LoadAssetAtPath<GameObject>("Assets/GestureManager/GestureManager.prefab");
+ if (!asset) asset = AssetDatabase.LoadAssetAtPath<GameObject>(AssetDatabase.FindAssets("t:prefab GestureManager").Select(AssetDatabase.GUIDToAssetPath).FirstOrDefault());
+ AddNewEmulator(asset);
+ }
+
+ private static void AddNewEmulator(UnityEngine.Object gObject)
+ {
+ var prefab = PrefabUtility.InstantiatePrefab(gObject) as GameObject;
+ CreateAndPing(prefab ? prefab.GetComponent<GestureManager>() : null);
+ }
+
+ public static void CreateAndPing(GestureManager manager = null)
+ {
+ if (!manager) manager = new GameObject("GestureManager").AddComponent<GestureManager>();
+ Selection.activeObject = manager;
+ EditorGUIUtility.PingObject(manager);
+ }
+
+ private void Awake()
+ {
+ if (!Manager.gameObject.GetComponent<GmgAvatarDescriptor>()) return;
+ DestroyImmediate(Manager);
+ CreateAndPing();
+ }
+
+ private void OnEnable()
+ {
+ if (!Application.isPlaying || !Manager.enabled || !Manager.gameObject.activeInHierarchy || Manager.Module != null) return;
+ Manager.SetModule(GetValidDescriptor());
+ }
+
+ public override VisualElement CreateInspectorGUI()
+ {
+ _root = new VisualElement();
+ _root.Add(new IMGUIContainer(ManagerGui));
+ foreach (var inspectorWindow in GmgLayoutHelper.GetInspectorWindows()) inspectorWindow.MySetAntiAliasing(AntiAliasing);
+ return _root;
+ }
+
+ private void ManagerGui()
+ {
+ if (!Manager) return;
+ Manager.SetDrag(!Event.current.alt);
+
+ GUILayout.Label("Gesture Manager 3.6", GestureManagerStyles.TitleStyle);
+ _updater.Gui();
+
+ if (Manager.Module != null)
+ {
+ GmgLayoutHelper.ObjectField("Controlling Avatar: ", Manager.Module.Avatar, OnAvatarSwitch);
+ Manager.Module?.EditorHeader();
+ Manager.Module?.EditorContent(this, _root);
+ }
+ else SetupGui();
+
+ GestureManagerStyles.Sign();
+ }
+
+ private void SetupGui()
+ {
+ var isLoaded = Manager.gameObject.scene.isLoaded;
+ if (EditorApplication.isPlaying && isLoaded)
+ {
+ if (!Manager || !Manager.enabled || !Manager.gameObject.activeInHierarchy) GUILayout.Label("I'm disabled!", GestureManagerStyles.TextError);
+ else CheckGui();
+ }
+ else
+ {
+ GUILayout.Label(isLoaded ? "I'm an useless script if you aren't on play mode :D" : "Drag & Drop me into the scene to start testing! ♥", GestureManagerStyles.MiddleStyle);
+ using (new GUILayout.HorizontalScope())
+ {
+ GUILayout.FlexibleSpace();
+
+ GUI.enabled = !GestureManager.InWebClientRequest;
+ if (GUILayout.Button("Check For Updates", GUILayout.Width(130))) CheckForUpdates();
+
+ GUILayout.Space(20);
+
+ if (GUILayout.Button("My Discord Name", GUILayout.Width(130))) CheckDiscordName();
+ GUI.enabled = true;
+
+ GUILayout.FlexibleSpace();
+ }
+ }
+ }
+
+ private void CheckGui()
+ {
+ if (Manager.LastCheckedActiveModules.Count != 0)
+ {
+ var eligibleList = Manager.LastCheckedActiveModules.Where(module => module.Avatar && module.IsValidDesc()).ToList();
+ var nonEligibleList = Manager.LastCheckedActiveModules.Where(module => module.Avatar && !module.IsValidDesc()).ToList();
+
+ GUILayout.Label(eligibleList.Count == 0 ? "No one of your VRC_AvatarDescriptor are eligible." : "Eligible VRC_AvatarDescriptors:", GestureManagerStyles.SubHeader);
+
+ foreach (var module in eligibleList)
+ {
+ GUILayout.BeginVertical(EditorStyles.helpBox);
+ EditorGUILayout.BeginHorizontal();
+ GUILayout.Label(module.Avatar.name + ":", GUILayout.Width(Screen.width - 131));
+ if (GUILayout.Button("Set As Avatar", GUILayout.Width(100))) Manager.SetModule(module);
+ GUILayout.EndHorizontal();
+ foreach (var warning in module.GetWarnings()) GUILayout.Label(warning, GestureManagerStyles.TextWarning);
+ GUILayout.EndVertical();
+ }
+
+ if (eligibleList.Count != 0 && nonEligibleList.Count != 0) GUILayout.Label("Non-Eligible VRC_AvatarDescriptors:", GestureManagerStyles.SubHeader);
+
+ foreach (var module in nonEligibleList)
+ {
+ GUILayout.BeginVertical(GestureManagerStyles.EmoteError);
+ GUILayout.Label(module.Avatar.name + ":");
+ foreach (var error in module.GetErrors()) GUILayout.Label(error, GestureManagerStyles.TextError);
+ GUILayout.EndVertical();
+ }
+ }
+ else GUILayout.Label("There are no VRC_AvatarDescriptor on your scene. \nPlease consider adding one to your avatar before entering in PlayMode.", GestureManagerStyles.TextError);
+
+ if (GUILayout.Button("Check Again")) CheckActiveModules();
+ }
+
+ private void OnAvatarSwitch(GameObject obj)
+ {
+ if (obj)
+ {
+ var module = ModuleHelper.GetModuleFor(Manager, obj);
+ if (module != null && module.IsValidDesc()) Manager.SetModule(module);
+ }
+ else Manager.UnlinkModule();
+ }
+
+ private ModuleBase GetValidDescriptor() => CheckActiveModules().FirstOrDefault(module => module.IsPerfectDesc());
+
+ private List<ModuleBase> CheckActiveModules() => Manager.LastCheckedActiveModules = Modules.ToList();
+
+ private static void DiscordPopup(string discord)
+ {
+ if (EditorUtility.DisplayDialog("It's me!", discord, "Copy To Clipboard!", "Ok!"))
+ EditorGUIUtility.systemCopyBuffer = discord;
+ }
+
+ private static void RequestGestureDuplication(ModuleBase module, int gestureIndex)
+ {
+ var fullGestureString = module.GetFinalGestureByIndex(gestureIndex).name;
+ var nameString = "[" + fullGestureString.Substring(fullGestureString.IndexOf("]", StringComparison.Ordinal) + 2) + "]";
+ var newAnimation = GmgAnimationHelper.CloneAnimationAsset(module.GetFinalGestureByIndex(gestureIndex));
+ var pathString = EditorUtility.SaveFilePanelInProject("Creating Gesture: " + fullGestureString, nameString + ".anim", "anim", "Hi (?)");
+
+ if (pathString.Length == 0) return;
+
+ AssetDatabase.CreateAsset(newAnimation, pathString);
+ newAnimation = AssetDatabase.LoadAssetAtPath<AnimationClip>(pathString);
+ module.AddGestureToOverrideController(gestureIndex, newAnimation);
+ }
+
+ /*
+ * Layout Builders
+ */
+
+ internal static void OnCheckBoxGuiHand(ModuleBase module, GestureHand hand, int position, bool overrider)
+ {
+ for (var i = 1; i < 8; i++)
+ using (new GUILayout.HorizontalScope())
+ if (OnCheckBoxGuiHandAnimation(module, i, position, overrider, out var isOn))
+ module.OnNewHand(hand, isOn ? i : 0);
+ }
+
+ private static bool OnCheckBoxGuiHandAnimation(ModuleBase module, int i, int position, bool overrider, out bool isOn)
+ {
+ GUILayout.Label(module.GetFinalGestureByIndex(i).name);
+ GUILayout.FlexibleSpace();
+ isOn = position == i;
+ var isDifferent = isOn != (isOn = GUILayout.Toggle(isOn, ""));
+ if (!overrider) return isDifferent;
+ if (!module.HasGestureBeenOverridden(i)) OverrideButton(module, i);
+ else GUILayout.Space(35);
+ return isDifferent;
+ }
+
+ private static void OverrideButton(ModuleBase module, int i)
+ {
+ if (GUILayout.Button(GestureManagerStyles.PlusTexture, GestureManagerStyles.PlusButton, GUILayout.Width(15), GUILayout.Height(15)))
+ RequestGestureDuplication(module, i);
+ }
+
+ /*
+ * Async Calls
+ */
+
+ private static (string lastVersion, string download) GetVersionInfo(string info) => GetVersionInfo(info.Trim().Split('\n'));
+
+ private static (string lastVersion, string download) GetVersionInfo(IReadOnlyList<string> split) => (split[0], split[1]);
+
+ private static async void CheckForUpdates()
+ {
+ if (GestureManager.InWebClientRequest) return;
+
+ GestureManager.InWebClientRequest = true;
+ var versionString = await GetVersion();
+ GestureManager.InWebClientRequest = false;
+
+ const string title = "Gesture Manager Updater";
+ if (versionString != null)
+ {
+ var (lastVersionString, downloadString) = GetVersionInfo(versionString);
+ var message = $"Newer version available! ({lastVersionString})\n\nIt's recommended to delete the GestureManager folder before importing the new package.";
+ if (lastVersionString.Equals(Version)) EditorUtility.DisplayDialog(title, $"You have the latest version of the manager. ({lastVersionString})", "Good!");
+ else if (EditorUtility.DisplayDialog(title, message, "Download", "Cancel")) Application.OpenURL(downloadString);
+ }
+ else EditorUtility.DisplayDialog(title, "Unable to check for updates.", "Okay");
+ }
+
+ private static async void CheckDiscordName()
+ {
+ if (GestureManager.InWebClientRequest) return;
+
+ GestureManager.InWebClientRequest = true;
+ var discordString = await GetDiscord();
+ GestureManager.InWebClientRequest = false;
+
+ EditorUtility.ClearProgressBar();
+ DiscordPopup(discordString ?? Discord);
+ }
+
+ /*
+ * Async
+ */
+
+ private static async Task<string> Get(string uri)
+ {
+ try
+ {
+ return await new WebClient().DownloadStringTaskAsync(uri);
+ }
+ catch (WebException)
+ {
+ return null;
+ }
+ }
+
+ private static async Task<string> GetVersion() => await Get(VersionURL);
+ private static async Task<string> GetDiscord() => await Get(DiscordURL);
+
+ private class PrefUpdater
+ {
+ private static readonly Color Color = new Color(0.07f, 0.55f, 0.58f);
+
+ private const string DayKey = "GM3 Update Day";
+ private const string VerKey = "GM3 Update Ver";
+
+ private int? _day;
+ private int? _today;
+ private string _version;
+ private bool _checked;
+ private bool _higher;
+
+ private int Day
+ {
+ get => _day ?? (_day = EditorPrefs.GetInt(DayKey)).Value;
+ set => EditorPrefs.SetInt(DayKey, (_day = value).Value);
+ }
+
+ private string Ver
+ {
+ get => _version ?? (_version = EditorPrefs.GetString(VerKey, Version));
+ set => EditorPrefs.SetString(VerKey, _version = value);
+ }
+
+ private int Today => _today ?? (_today = DateTime.Now.Day).Value;
+
+ private bool Higher() => string.CompareOrdinal(Version, Ver) < 0;
+
+ public void Gui()
+ {
+ if (!_checked) Check();
+ else if (_higher) Draw(GUILayoutUtility.GetLastRect());
+ }
+
+ private void Check()
+ {
+ _checked = true;
+ _higher = Higher();
+ if (Day != Today) RunCheck();
+ }
+
+ private async void RunCheck()
+ {
+ var versionString = await GetVersion();
+ if (versionString == null) return;
+ var (lastVersionString, _) = GetVersionInfo(versionString);
+ Ver = lastVersionString;
+ Day = Today;
+ _higher = Higher();
+ }
+
+ private void Draw(Rect rect)
+ {
+ var cEvent = Event.current;
+ rect.x += rect.width - 100;
+ rect.width = 100;
+ rect.height -= 16;
+ rect.y += 8;
+ using (new GmgLayoutHelper.GuiBackground(Color)) GUI.Label(rect, $"{Ver} is out!", GestureManagerStyles.UpdateStyle);
+ if (cEvent.type == EventType.MouseDown && cEvent.button == 0 && rect.Contains(cEvent.mousePosition)) CheckForUpdates();
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/GestureManagerEditor.cs.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/GestureManagerEditor.cs.meta
new file mode 100644
index 00000000..3ed3adc4
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/GestureManagerEditor.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: dbfc15e872f2b184884048cfea11317b
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/GestureManagerStyles.cs b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/GestureManagerStyles.cs
new file mode 100644
index 00000000..6f24fc8a
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/GestureManagerStyles.cs
@@ -0,0 +1,257 @@
+using UnityEditor;
+using UnityEngine;
+
+namespace GestureManager.Scripts.Editor
+{
+ public static class GestureManagerStyles
+ {
+ private const string BsxName = "BlackStartx";
+
+ private static GUIStyle _bottomStyle;
+ private static GUIStyle _emoteError;
+ private static GUIStyle _guiGreenButton;
+ private static GUIStyle _guiHandTitle;
+ private static GUIStyle _guiDebugTitle;
+ private static GUIStyle _middleStyle;
+ private static GUIStyle _plusButton;
+ private static GUIStyle _header;
+ private static GUIStyle _toolHeader;
+ private static GUIStyle _updateStyle;
+ private static GUIStyle _headerButton;
+ private static GUIStyle _subHeader;
+ private static GUIStyle _textError;
+ private static GUIStyle _textWarningHeader;
+ private static GUIStyle _textWarning;
+ private static GUIStyle _titleStyle;
+
+ private static Texture _plusTextureLgt;
+ private static Texture _plusTexturePro;
+ private static Texture _editTextureLgt;
+ private static Texture _editTexturePro;
+
+ internal static GUIStyle UpdateStyle => _updateStyle ?? (_updateStyle = new GUIStyle(EditorStyles.helpBox)
+ {
+ fontStyle = FontStyle.Bold,
+ alignment = TextAnchor.MiddleCenter,
+ margin = new RectOffset(0, 0, 10, 10)
+ });
+
+ internal static GUIStyle TitleStyle => _titleStyle ?? (_titleStyle = new GUIStyle(GUI.skin.label)
+ {
+ fontSize = 15,
+ fontStyle = FontStyle.Bold,
+ alignment = TextAnchor.UpperCenter,
+ padding = new RectOffset(10, 10, 10, 10)
+ });
+
+ internal static GUIStyle GuiHandTitle => _guiHandTitle ?? (_guiHandTitle = new GUIStyle(GUI.skin.label)
+ {
+ fontSize = 12,
+ fontStyle = FontStyle.Bold,
+ alignment = TextAnchor.UpperCenter,
+ padding = new RectOffset(10, 10, 10, 10)
+ });
+
+ internal static GUIStyle GuiDebugTitle => _guiDebugTitle ?? (_guiDebugTitle = new GUIStyle(GUI.skin.label)
+ {
+ fontSize = 12,
+ fontStyle = FontStyle.Bold,
+ alignment = TextAnchor.UpperCenter
+ });
+
+ internal static GUIStyle MiddleStyle => _middleStyle ?? (_middleStyle = new GUIStyle(GUI.skin.label)
+ {
+ fontSize = 12,
+ fontStyle = FontStyle.Bold,
+ alignment = TextAnchor.UpperCenter,
+ padding = new RectOffset(5, 5, 5, 5)
+ });
+
+ internal static GUIStyle EmoteError => _emoteError ?? (_emoteError = new GUIStyle(EditorStyles.helpBox)
+ {
+ padding = new RectOffset(5, 5, 5, 5),
+ margin = new RectOffset(5, 5, 5, 5)
+ });
+
+ internal static GUIStyle TextError => _textError ?? (_textError = new GUIStyle(GUI.skin.label)
+ {
+ active = { textColor = Color.red },
+ normal = { textColor = Color.red },
+ fontSize = 13,
+ alignment = TextAnchor.MiddleCenter
+ });
+
+ internal static GUIStyle TextWarningHeader => _textWarningHeader ?? (_textWarningHeader = new GUIStyle(GUI.skin.label)
+ {
+ active = { textColor = Color.yellow },
+ normal = { textColor = Color.yellow },
+ alignment = TextAnchor.MiddleCenter,
+ fontSize = 14
+ });
+
+ internal static GUIStyle TextWarning => _textWarning ?? (_textWarning = new GUIStyle(GUI.skin.label)
+ {
+ active = { textColor = Color.yellow },
+ normal = { textColor = Color.yellow },
+ alignment = TextAnchor.MiddleCenter
+ });
+
+ internal static GUIStyle GuiGreenButton => _guiGreenButton ?? (_guiGreenButton = new GUIStyle(GUI.skin.button)
+ {
+ active = { textColor = Color.green },
+ normal = { textColor = Color.green },
+ hover = { textColor = Color.green },
+ fixedWidth = 100
+ });
+
+ internal static GUIStyle Header => _header ?? (_header = new GUIStyle(GUI.skin.label)
+ {
+ fontSize = 15,
+ fontStyle = FontStyle.Bold,
+ alignment = TextAnchor.MiddleCenter,
+ padding = new RectOffset(10, 10, 10, 10)
+ });
+
+ internal static GUIStyle ToolHeader => _toolHeader ?? (_toolHeader = new GUIStyle(GUI.skin.label)
+ {
+ fontSize = 15,
+ fontStyle = FontStyle.Bold,
+ alignment = TextAnchor.MiddleCenter,
+ padding = new RectOffset(10, 10, 5, 5)
+ });
+
+ public static GUIStyle HeaderButton => _headerButton ?? (_headerButton = new GUIStyle(GUI.skin.button)
+ {
+ fontStyle = FontStyle.Bold,
+ alignment = TextAnchor.MiddleCenter,
+ margin = new RectOffset(0, 10, 12, 0)
+ });
+
+ private static GUIStyle BottomStyle => _bottomStyle ?? (_bottomStyle = new GUIStyle(GUI.skin.label)
+ {
+ fontSize = 11,
+ fontStyle = FontStyle.Bold,
+ alignment = TextAnchor.UpperRight,
+ padding = new RectOffset(5, 5, 5, 5)
+ });
+
+ internal static GUIStyle SubHeader => _subHeader ?? (_subHeader = new GUIStyle(GUI.skin.label) { alignment = TextAnchor.MiddleCenter });
+ internal static GUIStyle PlusButton => _plusButton ?? (_plusButton = new GUIStyle { margin = new RectOffset(0, 20, 3, 3) });
+
+ internal static Texture PlusTexture => EditorGUIUtility.isProSkin ? PlusTexturePro : PlusTextureLgt;
+ private static Texture PlusTextureLgt => _plusTextureLgt ? _plusTextureLgt : _plusTextureLgt = Resources.Load<Texture>("Gm/BSX_GM_PlusSign");
+ private static Texture PlusTexturePro => _plusTexturePro ? _plusTexturePro : _plusTexturePro = Resources.Load<Texture>("Gm/BSX_GM_PlusSign[Pro]");
+
+ public static class Data
+ {
+ public static readonly string[] GestureNames =
+ {
+ "[GESTURE] Idle",
+ "[GESTURE] Fist",
+ "[GESTURE] Open",
+ "[GESTURE] FingerPoint",
+ "[GESTURE] Victory",
+ "[GESTURE] Rock&Roll",
+ "[GESTURE] Gun",
+ "[GESTURE] ThumbsUp"
+ };
+
+ public static readonly string[] EmoteStandingName =
+ {
+ "[EMOTE 1] Wave",
+ "[EMOTE 2] Clap",
+ "[EMOTE 3] Point",
+ "[EMOTE 4] Cheer",
+ "[EMOTE 5] Dance",
+ "[EMOTE 6] BackFlip",
+ "[EMOTE 7] Die",
+ "[EMOTE 8] Sad"
+ };
+
+ public static readonly string[] EmoteSeatedName =
+ {
+ "[EMOTE 1] Laugh",
+ "[EMOTE 2] Point",
+ "[EMOTE 3] Raise Hand",
+ "[EMOTE 4] Drum",
+ "[EMOTE 5] Clap",
+ "[EMOTE 6] Angry Fist",
+ "[EMOTE 7] Disbelief",
+ "[EMOTE 8] Disapprove"
+ };
+ }
+
+ public static class Animations
+ {
+ public static class Gesture
+ {
+ private const string Path = "Gm/Animations/Gesture/";
+
+ private static AnimationClip _fist;
+ private static AnimationClip _open;
+ private static AnimationClip _point;
+ private static AnimationClip _peace;
+ private static AnimationClip _rock;
+ private static AnimationClip _run;
+ private static AnimationClip _thumbsUp;
+
+ public static AnimationClip Fist => _fist ? _fist : _fist = Resources.Load<AnimationClip>(Path + Data.GestureNames[1]);
+ public static AnimationClip Open => _open ? _open : _open = Resources.Load<AnimationClip>(Path + Data.GestureNames[2]);
+ public static AnimationClip Point => _point ? _point : _point = Resources.Load<AnimationClip>(Path + Data.GestureNames[3]);
+ public static AnimationClip Peace => _peace ? _peace : _peace = Resources.Load<AnimationClip>(Path + Data.GestureNames[4]);
+ public static AnimationClip Rock => _rock ? _rock : _rock = Resources.Load<AnimationClip>(Path + Data.GestureNames[5]);
+ public static AnimationClip Gun => _run ? _run : _run = Resources.Load<AnimationClip>(Path + Data.GestureNames[6]);
+ public static AnimationClip ThumbsUp => _thumbsUp ? _thumbsUp : _thumbsUp = Resources.Load<AnimationClip>(Path + Data.GestureNames[7]);
+ }
+
+ public static class Emote
+ {
+ private const string Path = "Gm/Animations/Emote/";
+
+ public static class Standing
+ {
+ private static AnimationClip _wave;
+ private static AnimationClip _clap;
+ private static AnimationClip _point;
+ private static AnimationClip _cheer;
+ private static AnimationClip _dance;
+ private static AnimationClip _backFlip;
+ private static AnimationClip _die;
+ private static AnimationClip _sadKick;
+
+ public static AnimationClip Wave => _wave ? _wave : _wave = Resources.Load<AnimationClip>(Path + Data.EmoteStandingName[0]);
+ public static AnimationClip Clap => _clap ? _clap : _clap = Resources.Load<AnimationClip>(Path + Data.EmoteStandingName[1]);
+ public static AnimationClip Point => _point ? _point : _point = Resources.Load<AnimationClip>(Path + Data.EmoteStandingName[2]);
+ public static AnimationClip Cheer => _cheer ? _cheer : _cheer = Resources.Load<AnimationClip>(Path + Data.EmoteStandingName[3]);
+ public static AnimationClip Dance => _dance ? _dance : _dance = Resources.Load<AnimationClip>(Path + Data.EmoteStandingName[4]);
+ public static AnimationClip BackFlip => _backFlip ? _backFlip : _backFlip = Resources.Load<AnimationClip>(Path + Data.EmoteStandingName[5]);
+ public static AnimationClip Die => _die ? _die : _die = Resources.Load<AnimationClip>(Path + Data.EmoteStandingName[6]);
+ public static AnimationClip SadKick => _sadKick ? _sadKick : _sadKick = Resources.Load<AnimationClip>(Path + Data.EmoteStandingName[7]);
+ }
+
+ public static class Seated
+ {
+ private static AnimationClip _laugh;
+ private static AnimationClip _point;
+ private static AnimationClip _raiseHand;
+ private static AnimationClip _drum;
+ private static AnimationClip _clap;
+ private static AnimationClip _shakeFist;
+ private static AnimationClip _disbelief;
+ private static AnimationClip _disapprove;
+
+ public static AnimationClip Laugh => _laugh ? _laugh : _laugh = Resources.Load<AnimationClip>(Path + Data.EmoteSeatedName[0]);
+ public static AnimationClip Point => _point ? _point : _point = Resources.Load<AnimationClip>(Path + Data.EmoteSeatedName[1]);
+ public static AnimationClip RaiseHand => _raiseHand ? _raiseHand : _raiseHand = Resources.Load<AnimationClip>(Path + Data.EmoteSeatedName[2]);
+ public static AnimationClip Drum => _drum ? _drum : _drum = Resources.Load<AnimationClip>(Path + Data.EmoteSeatedName[3]);
+ public static AnimationClip Clap => _clap ? _clap : _clap = Resources.Load<AnimationClip>(Path + Data.EmoteSeatedName[4]);
+ public static AnimationClip ShakeFist => _shakeFist ? _shakeFist : _shakeFist = Resources.Load<AnimationClip>(Path + Data.EmoteSeatedName[5]);
+ public static AnimationClip Disbelief => _disbelief ? _disbelief : _disbelief = Resources.Load<AnimationClip>(Path + Data.EmoteSeatedName[6]);
+ public static AnimationClip Disapprove => _disapprove ? _disapprove : _disapprove = Resources.Load<AnimationClip>(Path + Data.EmoteSeatedName[7]);
+ }
+ }
+ }
+
+ public static void Sign(string category = "Script") => GUILayout.Label($"{category} made by {BsxName}", BottomStyle);
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/GestureManagerStyles.cs.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/GestureManagerStyles.cs.meta
new file mode 100644
index 00000000..7490d03d
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/GestureManagerStyles.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 9b2e0ffff243c4f4390e38d31392585f
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules.meta
new file mode 100644
index 00000000..be5cede4
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 2fb272d30fb541219104cd65ea5413b0
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/ModuleHelper.cs b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/ModuleHelper.cs
new file mode 100644
index 00000000..5513fb8c
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/ModuleHelper.cs
@@ -0,0 +1,32 @@
+using GestureManager.Scripts.Extra;
+using GmgAvatarDescriptor =
+#if VRC_SDK_VRCSDK2 || VRC_SDK_VRCSDK3
+ VRC.SDKBase.VRC_AvatarDescriptor;
+#else
+ UnityEngine.Component;
+#endif
+using UnityEngine;
+
+namespace GestureManager.Scripts.Editor.Modules
+{
+ public static class ModuleHelper
+ {
+ public static ModuleBase GetModuleFor(GestureManager manager, GameObject gameObject) => GetModuleFor(manager, gameObject.GetComponent<GmgAvatarDescriptor>());
+
+ public static ModuleBase GetModuleFor(GestureManager manager, GmgAvatarDescriptor descriptorComponent)
+ {
+ switch (descriptorComponent)
+ {
+#if VRC_SDK_VRCSDK2
+ case VRCSDK2.VRC_AvatarDescriptor descriptorV2:
+ return new Vrc2.ModuleVrc2(manager, descriptorV2);
+#endif
+#if VRC_SDK_VRCSDK3
+ case VRC.SDK3.Avatars.Components.VRCAvatarDescriptor descriptorV3:
+ return new Vrc3.ModuleVrc3(manager, descriptorV3);
+#endif
+ default: return null;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/ModuleHelper.cs.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/ModuleHelper.cs.meta
new file mode 100644
index 00000000..36b629f0
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/ModuleHelper.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: f267915a6ff445749c610c58c0a0b832
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc2.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc2.meta
new file mode 100644
index 00000000..e8c2a660
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc2.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: acbf70babfa4452499f92c62d5d1ee66
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc2/ModuleVrc2.cs b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc2/ModuleVrc2.cs
new file mode 100644
index 00000000..2a717765
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc2/ModuleVrc2.cs
@@ -0,0 +1,450 @@
+#if VRC_SDK_VRCSDK2
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+using GestureManager.Scripts.Core.Editor;
+using GestureManager.Scripts.Extra;
+using UnityEngine;
+using UnityEngine.UIElements;
+using VRCSDK2;
+using GmData = GestureManager.Scripts.Editor.GestureManagerStyles.Data;
+
+namespace GestureManager.Scripts.Editor.Modules.Vrc2
+{
+ public class ModuleVrc2 : ModuleBase
+ {
+ private readonly VRC_AvatarDescriptor _avatarDescriptor;
+
+ private readonly int _handGestureLeft = Animator.StringToHash("HandGestureLeft");
+ private readonly int _handGestureRight = Animator.StringToHash("HandGestureRight");
+ private readonly int _emoteHash = Animator.StringToHash("Emote");
+
+ private int emote;
+
+ private RuntimeAnimatorController _avatarWasUsing;
+
+ private ControllerType _usingType;
+ private ControllerType _notUsedType;
+
+ private AnimatorOverrideController _originalUsingOverrideController;
+ private AnimatorOverrideController _myRuntimeOverrideController;
+
+ private RuntimeAnimatorController _standingControllerPreset;
+ private RuntimeAnimatorController _seatedControllerPreset;
+
+ private Dictionary<string, bool> _hasBeenOverridden;
+
+ private AnimationClip _selectingCustomAnim;
+ private GmgLayoutHelper.Toolbar _toolBar;
+
+ private RuntimeAnimatorController StandingControllerPreset => _standingControllerPreset ? _standingControllerPreset : _standingControllerPreset = Resources.Load<RuntimeAnimatorController>("Vrc2/StandingEmoteTestingTemplate");
+ private RuntimeAnimatorController SeatedControllerPreset => _seatedControllerPreset ? _seatedControllerPreset : _seatedControllerPreset = Resources.Load<RuntimeAnimatorController>("Vrc2/SeatedEmoteTestingTemplate");
+
+ private Dictionary<HumanBodyBones, Quaternion> _lastBoneQuaternions;
+
+ private int _controlDelay;
+ private static IEnumerable<HumanBodyBones> Bones => Enum.GetValues(typeof(HumanBodyBones)).Cast<HumanBodyBones>();
+
+ [SuppressMessage("ReSharper", "StringLiteralTypo")]
+ private readonly AnimationBind[] _gestureBinds =
+ {
+ new AnimationBind(null, GmData.GestureNames[0]),
+ new AnimationBind("FIST", GmData.GestureNames[1]),
+ new AnimationBind("HANDOPEN", GmData.GestureNames[2]),
+ new AnimationBind("FINGERPOINT", GmData.GestureNames[3]),
+ new AnimationBind("VICTORY", GmData.GestureNames[4]),
+ new AnimationBind("ROCKNROLL", GmData.GestureNames[5]),
+ new AnimationBind("HANDGUN", GmData.GestureNames[6]),
+ new AnimationBind("THUMBSUP", GmData.GestureNames[7])
+ };
+
+ private readonly AnimationBind[] _emoteBinds =
+ {
+ new AnimationBind("EMOTE1", GmData.EmoteStandingName[0], GmData.EmoteSeatedName[0]),
+ new AnimationBind("EMOTE2", GmData.EmoteStandingName[1], GmData.EmoteSeatedName[1]),
+ new AnimationBind("EMOTE3", GmData.EmoteStandingName[2], GmData.EmoteSeatedName[2]),
+ new AnimationBind("EMOTE4", GmData.EmoteStandingName[3], GmData.EmoteSeatedName[3]),
+ new AnimationBind("EMOTE5", GmData.EmoteStandingName[4], GmData.EmoteSeatedName[4]),
+ new AnimationBind("EMOTE6", GmData.EmoteStandingName[5], GmData.EmoteSeatedName[5]),
+ new AnimationBind("EMOTE7", GmData.EmoteStandingName[6], GmData.EmoteSeatedName[6]),
+ new AnimationBind("EMOTE8", GmData.EmoteStandingName[7], GmData.EmoteSeatedName[7])
+ };
+
+ /**
+ * This dictionary is needed just because I hate the original animation names.
+ * It will just translate the name of the original animation to the my version of the name.
+ */
+ private Dictionary<string, string> _myTranslateDictionary;
+
+ private Dictionary<string, string> TranslateDictionary => _myTranslateDictionary ?? (_myTranslateDictionary = new Dictionary<string, string>
+ {
+ { _gestureBinds[1].GetOriginalName(), _gestureBinds[1].GetMyName(_usingType) },
+ { _gestureBinds[2].GetOriginalName(), _gestureBinds[2].GetMyName(_usingType) },
+ { _gestureBinds[3].GetOriginalName(), _gestureBinds[3].GetMyName(_usingType) },
+ { _gestureBinds[4].GetOriginalName(), _gestureBinds[4].GetMyName(_usingType) },
+ { _gestureBinds[5].GetOriginalName(), _gestureBinds[5].GetMyName(_usingType) },
+ { _gestureBinds[6].GetOriginalName(), _gestureBinds[6].GetMyName(_usingType) },
+ { _gestureBinds[7].GetOriginalName(), _gestureBinds[7].GetMyName(_usingType) },
+
+ { _emoteBinds[0].GetOriginalName(), _emoteBinds[0].GetMyName(_usingType) },
+ { _emoteBinds[1].GetOriginalName(), _emoteBinds[1].GetMyName(_usingType) },
+ { _emoteBinds[2].GetOriginalName(), _emoteBinds[2].GetMyName(_usingType) },
+ { _emoteBinds[3].GetOriginalName(), _emoteBinds[3].GetMyName(_usingType) },
+ { _emoteBinds[4].GetOriginalName(), _emoteBinds[4].GetMyName(_usingType) },
+ { _emoteBinds[5].GetOriginalName(), _emoteBinds[5].GetMyName(_usingType) },
+ { _emoteBinds[6].GetOriginalName(), _emoteBinds[6].GetMyName(_usingType) },
+ { _emoteBinds[7].GetOriginalName(), _emoteBinds[7].GetMyName(_usingType) }
+ });
+
+ private IEnumerable<HumanBodyBones> _whiteListedControlBones;
+ private IEnumerable<HumanBodyBones> WhiteListedControlBones => _whiteListedControlBones ?? (_whiteListedControlBones = Bones.Where(bones => !_blackListedControlBones.Contains(bones)));
+
+ private readonly List<HumanBodyBones> _blackListedControlBones = new List<HumanBodyBones>
+ {
+ // Left
+ HumanBodyBones.LeftThumbDistal,
+ HumanBodyBones.LeftThumbIntermediate,
+ HumanBodyBones.LeftThumbProximal,
+
+ HumanBodyBones.LeftIndexDistal,
+ HumanBodyBones.LeftIndexIntermediate,
+ HumanBodyBones.LeftIndexProximal,
+
+ HumanBodyBones.LeftMiddleDistal,
+ HumanBodyBones.LeftMiddleIntermediate,
+ HumanBodyBones.LeftMiddleProximal,
+
+ HumanBodyBones.LeftRingDistal,
+ HumanBodyBones.LeftRingIntermediate,
+ HumanBodyBones.LeftRingProximal,
+
+ HumanBodyBones.LeftLittleDistal,
+ HumanBodyBones.LeftLittleIntermediate,
+ HumanBodyBones.LeftLittleProximal,
+
+ // Right
+ HumanBodyBones.RightThumbDistal,
+ HumanBodyBones.RightThumbIntermediate,
+ HumanBodyBones.RightThumbProximal,
+
+ HumanBodyBones.RightIndexDistal,
+ HumanBodyBones.RightIndexIntermediate,
+ HumanBodyBones.RightIndexProximal,
+
+ HumanBodyBones.RightMiddleDistal,
+ HumanBodyBones.RightMiddleIntermediate,
+ HumanBodyBones.RightMiddleProximal,
+
+ HumanBodyBones.RightRingDistal,
+ HumanBodyBones.RightRingIntermediate,
+ HumanBodyBones.RightRingProximal,
+
+ HumanBodyBones.RightLittleDistal,
+ HumanBodyBones.RightLittleIntermediate,
+ HumanBodyBones.RightLittleProximal,
+
+ // ???
+ HumanBodyBones.LastBone
+ };
+
+ public ModuleVrc2(GestureManager manager, VRC_AvatarDescriptor avatarDescriptor) : base(manager, avatarDescriptor) => _avatarDescriptor = avatarDescriptor;
+
+ public override void Update()
+ {
+ FetchLastBoneRotation();
+ AvatarAnimator.SetInteger(_handGestureLeft, Manager.OnCustomAnimation || emote != 0 ? 8 : Left);
+ AvatarAnimator.SetInteger(_handGestureRight, Manager.OnCustomAnimation || emote != 0 ? 8 : Right);
+ AvatarAnimator.SetInteger(_emoteHash, Manager.OnCustomAnimation ? 9 : emote);
+ }
+
+ public override void LateUpdate()
+ {
+ if (emote != 0 || Manager.OnCustomAnimation) return;
+
+ foreach (var bodyBone in WhiteListedControlBones)
+ {
+ var boneTransform = AvatarAnimator.GetBoneTransform(bodyBone);
+ try
+ {
+ var boneQuaternion = _lastBoneQuaternions[bodyBone];
+ boneTransform.localRotation = new Quaternion(boneQuaternion.x, boneQuaternion.y, boneQuaternion.z, boneQuaternion.w);
+ }
+ catch (Exception)
+ {
+ // ignored
+ }
+ }
+ }
+
+ public override void InitForAvatar()
+ {
+ if (_avatarDescriptor.CustomStandingAnims) SetupOverride(ControllerType.Standing, true);
+ else if (_avatarDescriptor.CustomSittingAnims) SetupOverride(ControllerType.Seated, true);
+ }
+
+ public override void Unlink()
+ {
+ if (!AvatarAnimator) return;
+ AvatarAnimator.runtimeAnimatorController = _avatarWasUsing;
+ _avatarWasUsing = null;
+ }
+
+ public override void EditorHeader()
+ {
+ using (new GUILayout.HorizontalScope())
+ {
+ GUILayout.Label("Using Override: " + GetOverrideController().name + " [" + _usingType + "]");
+ GUI.enabled = CanSwitchController();
+ if (GUILayout.Button("Switch to " + _notUsedType.ToString().ToLower() + "!")) SwitchType();
+ GUI.enabled = true;
+ }
+ }
+
+ public override void EditorContent(object editor, VisualElement element)
+ {
+ GUILayout.Space(15);
+
+ GmgLayoutHelper.MyToolbar(ref _toolBar, new (string, Action)[]
+ {
+ ("Gestures", () =>
+ {
+ if (emote != 0 || Manager.OnCustomAnimation)
+ {
+ GUILayout.BeginHorizontal(GestureManagerStyles.EmoteError);
+ GUILayout.Label("Gesture doesn't work while you're playing an emote!");
+ if (GUILayout.Button("Stop!", GestureManagerStyles.GuiGreenButton)) StopCurrentEmote();
+
+ GUILayout.EndHorizontal();
+ }
+
+ GUILayout.BeginHorizontal();
+
+ GUILayout.BeginVertical();
+ GUILayout.Label("Left Hand", GestureManagerStyles.GuiHandTitle);
+ GestureManagerEditor.OnCheckBoxGuiHand(this, GestureHand.Left, Left, true);
+ GUILayout.EndVertical();
+
+ GUILayout.BeginVertical();
+ GUILayout.Label("Right Hand", GestureManagerStyles.GuiHandTitle);
+ GestureManagerEditor.OnCheckBoxGuiHand(this, GestureHand.Right, Right, true);
+ GUILayout.EndVertical();
+
+ GUILayout.EndHorizontal();
+ }),
+ ("Emotes", () =>
+ {
+ GUILayout.Label("Emotes", GestureManagerStyles.GuiHandTitle);
+
+ OnEmoteButton(1, OnEmoteStart, OnEmoteStop);
+ OnEmoteButton(2, OnEmoteStart, OnEmoteStop);
+ OnEmoteButton(3, OnEmoteStart, OnEmoteStop);
+ OnEmoteButton(4, OnEmoteStart, OnEmoteStop);
+ OnEmoteButton(5, OnEmoteStart, OnEmoteStop);
+ OnEmoteButton(6, OnEmoteStart, OnEmoteStop);
+ OnEmoteButton(7, OnEmoteStart, OnEmoteStop);
+ OnEmoteButton(8, OnEmoteStart, OnEmoteStop);
+ }),
+ ("Test Animation", () =>
+ {
+ GUILayout.Label("Force animation.", GestureManagerStyles.GuiHandTitle);
+
+ GUILayout.BeginHorizontal();
+ _selectingCustomAnim = GmgLayoutHelper.ObjectField("Animation: ", _selectingCustomAnim, Manager.SetCustomAnimation);
+
+ GUI.enabled = _selectingCustomAnim;
+ if (Manager.OnCustomAnimation && emote == 0)
+ {
+ if (GUILayout.Button("Stop", GestureManagerStyles.GuiGreenButton)) Manager.StopCustomAnimation();
+ }
+ else
+ {
+ if (GUILayout.Button("Play", GUILayout.Width(100)))
+ {
+ emote = 0;
+ Manager.PlayCustomAnimation(_selectingCustomAnim);
+ }
+ }
+
+ GUI.enabled = true;
+
+ GUILayout.EndHorizontal();
+ })
+ });
+ }
+
+ protected override void OnNewLeft(int left) => Left = left;
+
+ protected override void OnNewRight(int right) => Right = right;
+
+ public override AnimationClip GetFinalGestureByIndex(int gestureIndex) => _myRuntimeOverrideController[_gestureBinds[gestureIndex].GetMyName(_usingType)];
+
+ public override Animator OnCustomAnimationPlay(AnimationClip animationClip)
+ {
+ SetupOverride(_usingType, false);
+ return AvatarAnimator;
+ }
+
+ public override bool HasGestureBeenOverridden(int gestureIndex) => _hasBeenOverridden.ContainsKey(_gestureBinds[gestureIndex].GetMyName(_usingType));
+
+ public override void AddGestureToOverrideController(int gestureIndex, AnimationClip newAnimation)
+ {
+ _originalUsingOverrideController[_gestureBinds[gestureIndex].GetOriginalName()] = newAnimation;
+ SetupOverride(_usingType, false);
+ }
+
+ public override bool IsInvalid() => base.IsInvalid() || !Avatar.activeInHierarchy;
+
+ protected override List<string> CheckWarnings()
+ {
+ var warningList = base.CheckWarnings();
+ if (AvatarAnimator != null && !AvatarAnimator.isHuman) warningList.Add("- The avatar has no humanoid rig!\n(Simulation could not match in-app)");
+ return warningList;
+ }
+
+ protected override List<string> CheckErrors()
+ {
+ var errorList = base.CheckErrors();
+ if (!_avatarDescriptor.CustomSittingAnims && !_avatarDescriptor.CustomStandingAnims) errorList.Add("- The Descriptor doesn't have any kind of controller!");
+ return errorList;
+ }
+
+ private AnimationClip GetEmoteByIndex(int emoteIndex) => _myRuntimeOverrideController[_emoteBinds[emoteIndex].GetMyName(_usingType)];
+
+ private void OnEmoteButton(int current, Action<int> play, Action stop)
+ {
+ using (new GUILayout.HorizontalScope())
+ {
+ GUILayout.Label(GetEmoteByIndex(current - 1).name);
+ if (emote == current && GUILayout.Button("Stop", GestureManagerStyles.GuiGreenButton)) stop();
+ if (emote != current && GUILayout.Button("Play", GUILayout.Width(100))) play(current);
+ }
+ }
+
+ private void OnEmoteStart(int emoteIndex)
+ {
+ emote = emoteIndex;
+ Manager.PlayCustomAnimation(GetEmoteByIndex(emoteIndex - 1));
+ }
+
+ private void OnEmoteStop()
+ {
+ emote = 0;
+ Manager.StopCustomAnimation();
+ }
+
+ private void StopCurrentEmote()
+ {
+ if (emote != 0) OnEmoteStop();
+ if (Manager.OnCustomAnimation) Manager.StopCustomAnimation();
+ }
+
+ private void FetchLastBoneRotation()
+ {
+ if (emote != 0 || Manager.OnCustomAnimation)
+ {
+ _controlDelay = 5;
+ return;
+ }
+
+ if (_controlDelay > 0)
+ {
+ _controlDelay--;
+ return;
+ }
+
+ _lastBoneQuaternions = new Dictionary<HumanBodyBones, Quaternion>();
+ foreach (var bodyBone in WhiteListedControlBones)
+ {
+ var boneTransform = AvatarAnimator.GetBoneTransform(bodyBone);
+ try
+ {
+ _lastBoneQuaternions[bodyBone] = boneTransform.localRotation;
+ }
+ catch (Exception)
+ {
+ // ignored
+ }
+ }
+ }
+
+ private void SetupOverride(ControllerType controllerType, bool saveController)
+ {
+ _controlDelay = 4;
+
+ var controllerPreset = controllerType == ControllerType.Standing ? StandingControllerPreset : SeatedControllerPreset;
+ _usingType = controllerType == ControllerType.Standing ? ControllerType.Standing : ControllerType.Seated;
+ _notUsedType = controllerType == ControllerType.Standing ? ControllerType.Seated : ControllerType.Standing;
+ _originalUsingOverrideController = controllerType == ControllerType.Standing ? _avatarDescriptor.CustomStandingAnims : _avatarDescriptor.CustomSittingAnims;
+ _myRuntimeOverrideController = new AnimatorOverrideController(controllerPreset);
+
+ _myTranslateDictionary = null;
+
+ var finalOverride = new List<KeyValuePair<AnimationClip, AnimationClip>>
+ {
+ new KeyValuePair<AnimationClip, AnimationClip>(_myRuntimeOverrideController["[EXTRA] CustomAnimation"], Manager.customAnim)
+ };
+
+ var validOverrides = new List<KeyValuePair<AnimationClip, AnimationClip>>();
+ foreach (var pair in GmgAnimatorControllerHelper.GetOverrides(_originalUsingOverrideController).Where(keyValuePair => keyValuePair.Value))
+ {
+ try
+ {
+ validOverrides.Add(new KeyValuePair<AnimationClip, AnimationClip>(_myRuntimeOverrideController[TranslateDictionary[pair.Key.name]], pair.Value));
+ }
+ catch (Exception)
+ {
+ // ignored
+ }
+ }
+
+ finalOverride.AddRange(validOverrides);
+
+ _hasBeenOverridden = new Dictionary<string, bool>();
+ foreach (var valuePair in finalOverride) _hasBeenOverridden[valuePair.Key.name] = true;
+
+ _myRuntimeOverrideController.ApplyOverrides(finalOverride);
+
+ if (saveController) _avatarWasUsing = AvatarAnimator.runtimeAnimatorController;
+
+ AvatarAnimator.Rebind();
+ AvatarAnimator.runtimeAnimatorController = _myRuntimeOverrideController;
+ AvatarAnimator.runtimeAnimatorController.name = controllerPreset.name;
+ }
+
+ private AnimatorOverrideController GetOverrideController() => _originalUsingOverrideController;
+
+ private void SwitchType() => SetupOverride(_notUsedType, false);
+
+ private bool CanSwitchController() => _notUsedType == ControllerType.Seated ? _avatarDescriptor.CustomSittingAnims : _avatarDescriptor.CustomStandingAnims;
+ }
+
+ public enum ControllerType
+ {
+ Standing,
+ Seated
+ }
+
+ public class AnimationBind
+ {
+ private readonly string _originalName;
+ private readonly string _standingName;
+ private readonly string _seatedName;
+
+ public AnimationBind(string originalName, string standingName, string seatedName)
+ {
+ _originalName = originalName;
+ _standingName = standingName;
+ _seatedName = seatedName;
+ }
+
+ public AnimationBind(string originalName, string gestureName) : this(originalName, gestureName, gestureName)
+ {
+ }
+
+ public string GetMyName(ControllerType controller) => controller == ControllerType.Standing ? _standingName : _seatedName;
+
+ public string GetOriginalName() => _originalName;
+ }
+}
+#endif \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc2/ModuleVrc2.cs.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc2/ModuleVrc2.cs.meta
new file mode 100644
index 00000000..734200ed
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc2/ModuleVrc2.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 87e5d13b3425463ebd15bc75cafbed0a
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3.meta
new file mode 100644
index 00000000..718bbbe2
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 39155f6b5d481bd4d8116fc636a495c1
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/AnimatorControllerWeight.cs b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/AnimatorControllerWeight.cs
new file mode 100644
index 00000000..493da00b
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/AnimatorControllerWeight.cs
@@ -0,0 +1,102 @@
+#if VRC_SDK_VRCSDK3
+using System;
+using System.Collections.Generic;
+using UnityEngine;
+using UnityEngine.Animations;
+using UnityEngine.Playables;
+using VRC.SDKBase;
+
+namespace GestureManager.Scripts.Editor.Modules.Vrc3
+{
+ public class AnimatorControllerWeight
+ {
+ private readonly List<int> _subCompleted = new List<int>();
+ private readonly Dictionary<int, SubTimer> _subControls = new Dictionary<int, SubTimer>();
+ private readonly AnimationLayerMixerPlayable _playableMixer;
+ private readonly int _index;
+
+ private AnimatorControllerPlayable _playable;
+ private VRC_PlayableLayerControl _control;
+
+ private float _startWeight;
+ private float _startTime;
+
+ private void OnSubTimerCompleted(int index) => _subCompleted.Add(index);
+
+ public float Weight => _playableMixer.GetInputWeight(_index);
+
+ public AnimatorControllerWeight(AnimationLayerMixerPlayable playableMixer, AnimatorControllerPlayable playable, int index)
+ {
+ _playableMixer = playableMixer;
+ _playable = playable;
+ _index = index;
+ }
+
+ public void Update()
+ {
+ if (_control) Set(UpdateWeight());
+ foreach (var pair in _subControls) pair.Value.Update(_playable);
+ if (_subCompleted.Count == 0) return;
+ foreach (var idInt in _subCompleted) _subControls.Remove(idInt);
+ _subCompleted.Clear();
+ }
+
+ public void Start(VRC_PlayableLayerControl control)
+ {
+ _control = control;
+ _startWeight = Weight;
+ _startTime = Time.time;
+ }
+
+ public void Start(VRC_AnimatorLayerControl control) => _subControls[control.layer] = new SubTimer(control, _playable.GetLayerWeight(control.layer), Time.time, OnSubTimerCompleted);
+
+ public void Set(float goalWeight) => _playableMixer.SetInputWeight(_index, goalWeight);
+
+ private float UpdateWeight()
+ {
+ var time = Time.time - _startTime;
+ return time > _control.blendDuration ? Stop() : Mathf.Lerp(_startWeight, _control.goalWeight, time / _control.blendDuration);
+ }
+
+ private float Stop()
+ {
+ var weight = _control.goalWeight;
+ _control = null;
+ return weight;
+ }
+
+ private class SubTimer
+ {
+ private readonly VRC_AnimatorLayerControl _control;
+ private readonly Action<int> _onComplete;
+ private readonly float _startWeight;
+ private readonly float _startTime;
+
+ public SubTimer(VRC_AnimatorLayerControl control, float startWeight, float startTime, Action<int> onComplete)
+ {
+ _control = control;
+ _startTime = startTime;
+ _onComplete = onComplete;
+ _startWeight = startWeight;
+ }
+
+ public void Update(AnimatorControllerPlayable playable)
+ {
+ if (_control) playable.SetLayerWeight(_control.layer, SubUpdateWeight(_control, _startWeight, _startTime));
+ }
+
+ private float SubUpdateWeight(VRC_AnimatorLayerControl control, float startWeight, float startTime)
+ {
+ var time = Time.time - startTime;
+ return time > control.blendDuration ? SubStop(control) : Mathf.Lerp(startWeight, control.goalWeight, time / control.blendDuration);
+ }
+
+ private float SubStop(VRC_AnimatorLayerControl control)
+ {
+ _onComplete(control.layer);
+ return control.goalWeight;
+ }
+ }
+ }
+}
+#endif \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/AnimatorControllerWeight.cs.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/AnimatorControllerWeight.cs.meta
new file mode 100644
index 00000000..715a2925
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/AnimatorControllerWeight.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 3b85d10c576746018e8d53143a68f0d2
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/AvatarDynamics.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/AvatarDynamics.meta
new file mode 100644
index 00000000..e21eba0b
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/AvatarDynamics.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 33129c7f383b4275a4fae59c04064f6e
+timeCreated: 1651081095 \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/AvatarDynamics/AnimParameterAccessAvatarGmg.cs b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/AvatarDynamics/AnimParameterAccessAvatarGmg.cs
new file mode 100644
index 00000000..ee1c50ec
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/AvatarDynamics/AnimParameterAccessAvatarGmg.cs
@@ -0,0 +1,39 @@
+#if VRC_SDK_VRCSDK3
+using GestureManager.Scripts.Editor.Modules.Vrc3.Params;
+using VRC.SDKBase;
+
+namespace GestureManager.Scripts.Editor.Modules.Vrc3.AvatarDynamics
+{
+ internal class AnimParameterAccessAvatarGmg : IAnimParameterAccess
+ {
+ private readonly ModuleVrc3 _module;
+ private readonly string _parameter;
+
+ private Vrc3Param Param => _module.GetParam(_parameter);
+
+ public AnimParameterAccessAvatarGmg(ModuleVrc3 module, string parameter)
+ {
+ _module = module;
+ _parameter = parameter;
+ }
+
+ public bool boolVal
+ {
+ get => (Param?.Get() ?? 0f) > 0.5f;
+ set => Param?.Set(_module, value);
+ }
+
+ public int intVal
+ {
+ get => (int)(Param?.Get() ?? 0f);
+ set => Param?.Set(_module, value);
+ }
+
+ public float floatVal
+ {
+ get => Param?.Get() ?? 0f;
+ set => Param?.Set(_module, value);
+ }
+ }
+}
+#endif \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/AvatarDynamics/AnimParameterAccessAvatarGmg.cs.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/AvatarDynamics/AnimParameterAccessAvatarGmg.cs.meta
new file mode 100644
index 00000000..3a7a25a3
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/AvatarDynamics/AnimParameterAccessAvatarGmg.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: f8374584cc724986bbb351422881de38
+timeCreated: 1650035719 \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/AvatarDynamics/AvatarDynamicReset.cs b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/AvatarDynamics/AvatarDynamicReset.cs
new file mode 100644
index 00000000..077a0f62
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/AvatarDynamics/AvatarDynamicReset.cs
@@ -0,0 +1,49 @@
+#if VRC_SDK_VRCSDK3
+using UnityEngine;
+using VRC.Dynamics;
+using VRC.SDK3.Dynamics.PhysBone;
+
+namespace GestureManager.Scripts.Editor.Modules.Vrc3.AvatarDynamics
+{
+ public static class AvatarDynamicReset
+ {
+ private const string TriggerManagerName = "TriggerManager";
+ private const string PhysBoneManagerName = "PhysBoneManager";
+
+ public static void CheckSceneCollisions()
+ {
+ if (!ContactManager.Inst) ResumeContactManager();
+ if (!PhysBoneManager.Inst) ResumePhysBoneManager();
+ }
+
+ private static void RecreateComponent<T>(T original) where T : Component
+ {
+ var type = original.GetType();
+ var component = original.gameObject.AddComponent(type);
+ foreach (var field in type.GetFields()) field.SetValue(component, field.GetValue(original));
+ Object.DestroyImmediate(original);
+ }
+
+ private static void ResumeContactManager()
+ {
+ Object.DestroyImmediate(GameObject.Find(TriggerManagerName));
+ var obj = new GameObject(TriggerManagerName);
+ Object.DontDestroyOnLoad(obj);
+ obj.AddComponent<ContactManager>();
+ foreach (var contact in Resources.FindObjectsOfTypeAll<ContactBase>()) RecreateComponent(contact);
+ }
+
+ private static void ResumePhysBoneManager()
+ {
+ Object.DestroyImmediate(GameObject.Find(PhysBoneManagerName));
+ var obj = new GameObject(PhysBoneManagerName);
+ Object.DontDestroyOnLoad(obj);
+ obj.AddComponent<PhysBoneManager>();
+ PhysBoneManager.Inst.IsSDK = true;
+ PhysBoneManager.Inst.Init();
+ obj.AddComponent<PhysBoneGrabHelper>();
+ foreach (var physBone in Resources.FindObjectsOfTypeAll<VRCPhysBoneBase>()) RecreateComponent(physBone);
+ }
+ }
+}
+#endif \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/AvatarDynamics/AvatarDynamicReset.cs.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/AvatarDynamics/AvatarDynamicReset.cs.meta
new file mode 100644
index 00000000..4cc67f59
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/AvatarDynamics/AvatarDynamicReset.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 1a2e68a4eee74d729e00fd0dd861d858
+timeCreated: 1651107555 \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/AvatarTools.cs b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/AvatarTools.cs
new file mode 100644
index 00000000..108261b0
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/AvatarTools.cs
@@ -0,0 +1,308 @@
+#if VRC_SDK_VRCSDK3
+using System.Collections.Generic;
+using System.Linq;
+using GestureManager.Scripts.Core.Editor;
+using GestureManager.Scripts.Editor.Modules.Vrc3.DummyModes;
+using UnityEditor;
+using UnityEngine;
+using VRC.Dynamics;
+using VRC.Utility;
+
+namespace GestureManager.Scripts.Editor.Modules.Vrc3
+{
+ public class AvatarTools
+ {
+ private UpdateSceneCamera _sceneCamera;
+ private UpdateSceneCamera SceneCamera => _sceneCamera ?? (_sceneCamera = new UpdateSceneCamera());
+
+ private ClickableContacts _clickableContacts;
+ private ClickableContacts ContactsClickable => _clickableContacts ?? (_clickableContacts = new ClickableContacts());
+
+ private TestAnimation _testAnimation;
+ private TestAnimation AnimationTest => _testAnimation ?? (_testAnimation = new TestAnimation());
+
+ private Customization _customization;
+ private Customization CustomizationTool => _customization ?? (_customization = new Customization());
+
+ internal void Gui(ModuleVrc3 module)
+ {
+ GUILayout.Label("Gesture Manager Tools", GestureManagerStyles.Header);
+ GUILayout.Label("A collection of some small utility functions~", GestureManagerStyles.SubHeader);
+ GUILayout.Space(10);
+ SceneCamera.Display(module);
+ ContactsClickable.Display(module);
+ GUILayout.Label("Extra Tools", GestureManagerStyles.Header);
+ AnimationTest.Display(module);
+ GUILayout.Label("Customization", GestureManagerStyles.Header);
+ CustomizationTool.Display(module);
+ }
+
+ internal void OnUpdate(ModuleVrc3 module)
+ {
+ SceneCamera.OnUpdate(module);
+ AnimationTest.OnUpdate(module);
+ ContactsClickable.OnUpdate(module);
+ }
+
+ internal void OnLateUpdate(ModuleVrc3 module)
+ {
+ SceneCamera.OnLateUpdate(module);
+ AnimationTest.OnLateUpdate(module);
+ ContactsClickable.OnLateUpdate(module);
+ }
+
+ private class UpdateSceneCamera : GmgDynamicFunction
+ {
+ private static Camera _camera;
+ private readonly BoolProperty _isActive = new BoolProperty("GM3 SceneCamera");
+
+ protected override string Name => "Scene Camera";
+ protected override string Description => "This will match your game view with your scene view!\nClick the button to setup the main camera automatically~";
+ protected override bool Active => _isActive.Property;
+
+ public UpdateSceneCamera()
+ {
+ if (_isActive.Property) AutoToggle();
+ }
+
+ protected override void Update(ModuleVrc3 module)
+ {
+ var sceneView = SceneView.lastActiveSceneView;
+ if (!sceneView) return;
+ var camera = sceneView.camera;
+ if (!camera || !_camera) return;
+ var sceneTransform = camera.transform;
+ var transform = _camera.transform;
+ var positionVector = sceneTransform.position;
+ transform.rotation = sceneTransform.rotation;
+ transform.position = new Vector3(positionVector.x, positionVector.y + 0.001f, positionVector.z);
+ }
+
+ protected override void Gui(ModuleVrc3 module)
+ {
+ if (GmgLayoutHelper.ButtonObjectField("Scene Camera: ", _camera, _camera ? 'X' : 'A', camera => _camera = camera)) AutoToggle();
+ _isActive.Property = _camera != null;
+ }
+
+ private static void AutoToggle() => _camera = _camera ? null : Camera.main;
+ }
+
+ private class ClickableContacts : GmgDynamicFunction
+ {
+ private readonly BoolProperty _isActive = new BoolProperty("GM3 ClickableContacts");
+ private readonly StringProperty _tag = new StringProperty("GM3 ClickableContacts Tag");
+
+ private readonly Camera _camera = Camera.main;
+ private readonly HashSet<ContactReceiver> _activeContact = new HashSet<ContactReceiver>();
+
+ protected override string Name => "Clickable Contacts";
+ protected override string Description => "Click and trigger Avatar Contacts with your mouse!\nLike you can do with PhysBones~";
+ protected override bool Active => _isActive.Property;
+
+ protected override void Update(ModuleVrc3 module) => LateUpdate(module);
+
+ protected override void LateUpdate(ModuleVrc3 module)
+ {
+ if (Input.GetMouseButton(0)) OnClick(module);
+ if (Input.GetMouseButtonUp(0)) Disable();
+ }
+
+ private void OnClick(ModuleVrc3 module)
+ {
+ if (!_camera) return;
+ var ray = _camera.ScreenPointToRay(Input.mousePosition);
+ CheckRay(module, ray.origin, ray.origin + ray.direction * 1000f);
+ }
+
+ private void CheckRay(ModuleVrc3 module, Vector3 s, Vector3 e)
+ {
+ var manager = ContactManager.Inst;
+ if (!manager) return;
+ foreach (var receiver in module.Receivers.Where(receiver => string.IsNullOrEmpty(_tag.Property) || receiver.collisionTags.Contains(_tag.Property))) OnContactValue(receiver, ValueFor(manager, receiver, s, e));
+ }
+
+ private void OnContactValue(ContactReceiver receiver, float value)
+ {
+ if (value == 0f) Disable(receiver);
+ else Enable(receiver, value);
+ }
+
+ private static float ValueFor(ContactManager manager, ContactReceiver receiver, Vector3 s, Vector3 e)
+ {
+ var isProximity = receiver.receiverType == ContactReceiver.ReceiverType.Proximity;
+ var distance = DistanceFrom(manager, receiver, s, e, out var radius);
+ if (isProximity) distance -= radius;
+ if (isProximity) return Mathf.Clamp(-distance / radius, 0f, 1f);
+ return distance < 0 ? 1f : 0f;
+ }
+
+ private static float DistanceFrom(ContactManager manager, ContactBase receiver, Vector3 s, Vector3 e, out float radius)
+ {
+ receiver.InitShape();
+ manager.collision.UpdateShapeData(receiver.shape);
+ var shape = manager.collision.GetShapeData(receiver.shape);
+ var scaleVector = receiver.transform.lossyScale;
+ radius = receiver.radius * Mathf.Max(scaleVector.x, scaleVector.y, scaleVector.z);
+ Vector3 result0;
+ Vector3 result1;
+ if (receiver.shapeType == ContactBase.ShapeType.Sphere) PhysicsUtil.ClosestPointsBetweenLineSegments(s, e, shape.outPos0, shape.outPos0, out result0, out result1);
+ else PhysicsUtil.ClosestPointsBetweenLineSegments(s, e, shape.outPos0, shape.outPos1, out result0, out result1);
+ return (result0 - result1).magnitude - radius;
+ }
+
+ private void Enable(ContactReceiver receiver, float value)
+ {
+ if (_activeContact.Contains(receiver)) return;
+ _activeContact.Add(receiver);
+ receiver.SetParameter(value);
+ }
+
+ private void Disable(ContactReceiver receiver)
+ {
+ if (!_activeContact.Contains(receiver)) return;
+ _activeContact.Remove(receiver);
+ receiver.SetParameter(0f);
+ }
+
+ private void Disable()
+ {
+ foreach (var receiver in _activeContact) receiver.SetParameter(0f);
+ _activeContact.Clear();
+ }
+
+ protected override void Gui(ModuleVrc3 module)
+ {
+ _isActive.Property = GmgLayoutHelper.ToggleRight("Enabled: ", _isActive.Property);
+ _tag.Property = GmgLayoutHelper.PlaceHolderTextField("Tag: ", _tag.Property, " <leave blank to ignore tags> ");
+ }
+ }
+
+ private class TestAnimation : GmgDynamicFunction
+ {
+ private bool _testMode;
+ private AnimationClip _selectingCustomAnim;
+
+ protected override string Name => "Test Animation";
+ protected override string Description => "Use this tool to preview any animation in your project.\nYou can preview Emotes or Gestures.";
+ protected override bool Active => _testMode;
+
+ protected override void Gui(ModuleVrc3 module)
+ {
+ _testMode = module.Manager.OnCustomAnimation;
+
+ var isEditMode = module.DummyMode is Vrc3EditMode;
+ using (new GUILayout.HorizontalScope())
+ {
+ _selectingCustomAnim = GmgLayoutHelper.ObjectField("Animation: ", _selectingCustomAnim, module.Manager.SetCustomAnimation);
+
+ GUI.enabled = _selectingCustomAnim && !isEditMode;
+ switch (_testMode)
+ {
+ case true when GUILayout.Button("Stop", GestureManagerStyles.GuiGreenButton):
+ module.Manager.StopCustomAnimation();
+ break;
+ case false when GUILayout.Button("Play", GUILayout.Width(100)):
+ module.Manager.PlayCustomAnimation(_selectingCustomAnim);
+ break;
+ }
+
+ GUI.enabled = true;
+ }
+ }
+ }
+
+ private class Customization : GmgDynamicFunction
+ {
+ protected override string Name => "Radial Menu";
+ protected override string Description => "Customize the colors of the radial menu!";
+ protected override bool Active => false;
+
+ private Color _customMain = RadialMenuUtility.Colors.CustomMain;
+ private Color _customBorder = RadialMenuUtility.Colors.CustomBorder;
+ private Color _customSelected = RadialMenuUtility.Colors.CustomSelected;
+
+ protected override void Gui(ModuleVrc3 module)
+ {
+ _customMain = GmgLayoutHelper.ResetColorField("Main Color: ", _customMain, RadialMenuUtility.Colors.Default.Main);
+ _customBorder = GmgLayoutHelper.ResetColorField("Border Color: ", _customBorder, RadialMenuUtility.Colors.Default.Border);
+ _customSelected = GmgLayoutHelper.ResetColorField("Selected Color:", _customSelected, RadialMenuUtility.Colors.Default.Selected);
+
+ GUILayout.Space(10);
+
+ if (GUILayout.Button("Save")) Save(module);
+ }
+
+ private void Save(ModuleVrc3 module)
+ {
+ RadialMenuUtility.Colors.SaveColors(_customMain, _customBorder, _customSelected);
+ module.ReloadRadials();
+ }
+ }
+
+ private abstract class GmgDynamicFunction
+ {
+ protected abstract string Name { get; }
+ protected abstract string Description { get; }
+ protected abstract bool Active { get; }
+
+ internal void Display(ModuleVrc3 module)
+ {
+ using (new GmgLayoutHelper.GuiBackground(Active ? Color.green : GUI.backgroundColor))
+ using (new GUILayout.VerticalScope(GestureManagerStyles.EmoteError))
+ {
+ GUILayout.Label(Name, GestureManagerStyles.ToolHeader);
+ GUILayout.Label(Description, GestureManagerStyles.SubHeader);
+ GUILayout.Space(10);
+ Gui(module);
+ }
+ }
+
+ internal void OnUpdate(ModuleVrc3 module)
+ {
+ if (Active) Update(module);
+ }
+
+ internal void OnLateUpdate(ModuleVrc3 module)
+ {
+ if (Active) LateUpdate(module);
+ }
+
+ protected abstract void Gui(ModuleVrc3 module);
+
+ protected virtual void Update(ModuleVrc3 module)
+ {
+ }
+
+ protected virtual void LateUpdate(ModuleVrc3 module)
+ {
+ }
+ }
+
+ private class BoolProperty
+ {
+ private readonly string _key;
+
+ public BoolProperty(string key) => _key = key;
+
+ internal bool Property
+ {
+ get => EditorPrefs.GetBool(_key);
+ set => EditorPrefs.SetBool(_key, value);
+ }
+ }
+
+ private class StringProperty
+ {
+ private readonly string _key;
+
+ public StringProperty(string key) => _key = key;
+
+ internal string Property
+ {
+ get => EditorPrefs.GetString(_key);
+ set => EditorPrefs.SetString(_key, value);
+ }
+ }
+ }
+}
+#endif \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/AvatarTools.cs.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/AvatarTools.cs.meta
new file mode 100644
index 00000000..0b8ae509
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/AvatarTools.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 8661c1bf3ecb4fecbd64edcfa92c63d8
+timeCreated: 1651081083 \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/DummyModes.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/DummyModes.meta
new file mode 100644
index 00000000..2263904a
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/DummyModes.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 743b4bdbe55f48eb850f0b6d25d39405
+timeCreated: 1646579947 \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/DummyModes/Vrc3DummyMode.cs b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/DummyModes/Vrc3DummyMode.cs
new file mode 100644
index 00000000..522f2697
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/DummyModes/Vrc3DummyMode.cs
@@ -0,0 +1,55 @@
+#if VRC_SDK_VRCSDK3
+using UnityEditor;
+using UnityEngine;
+using Object = UnityEngine.Object;
+
+namespace GestureManager.Scripts.Editor.Modules.Vrc3.DummyModes
+{
+ public abstract class Vrc3DummyMode
+ {
+ protected readonly ModuleVrc3 Module;
+
+ internal abstract string ModeName { get; }
+
+ internal readonly GameObject Avatar;
+
+ public string ExitDummyText => "Exit " + ModeName + "-Mode";
+
+ protected Vrc3DummyMode(ModuleVrc3 module, string prefix)
+ {
+ Module = module;
+ Module.ForgetAvatar();
+ Module.Dummy.State = true;
+ Avatar = Object.Instantiate(Module.Avatar);
+ Avatar.name = Module.Avatar.name + " " + prefix;
+ Module.Avatar.SetActive(false);
+ Module.Avatar.hideFlags = HideFlags.HideInHierarchy | HideFlags.HideInInspector;
+ EditorApplication.DirtyHierarchyWindowSorting();
+ }
+
+ internal void Close()
+ {
+ if (Avatar) Object.DestroyImmediate(Avatar);
+ Module.Dummy.State = false;
+ Module.DummyMode = null;
+ Module.Avatar.SetActive(true);
+ Module.Avatar.hideFlags = HideFlags.None;
+ EditorApplication.DirtyHierarchyWindowSorting();
+ Module.AvatarAnimator.Update(1f);
+ Module.AvatarAnimator.runtimeAnimatorController = null;
+ Module.AvatarAnimator.Update(1f);
+ Module.InitForAvatar();
+ }
+
+ public void OnExecutionChange(bool state)
+ {
+ if (state || !Module.Avatar) return;
+ OnExecutionOff();
+ }
+
+ protected virtual void OnExecutionOff() => Close();
+
+ public abstract RadialDescription DummyDescription();
+ }
+}
+#endif \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/DummyModes/Vrc3DummyMode.cs.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/DummyModes/Vrc3DummyMode.cs.meta
new file mode 100644
index 00000000..f886076a
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/DummyModes/Vrc3DummyMode.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: ccefc13888cb43729d584983986142e2
+timeCreated: 1646579953 \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/DummyModes/Vrc3EditMode.cs b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/DummyModes/Vrc3EditMode.cs
new file mode 100644
index 00000000..686ca600
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/DummyModes/Vrc3EditMode.cs
@@ -0,0 +1,37 @@
+#if VRC_SDK_VRCSDK3
+using System.Collections.Generic;
+using GestureManager.Scripts.Core.Editor;
+using UnityEditor;
+using UnityEngine;
+
+namespace GestureManager.Scripts.Editor.Modules.Vrc3.DummyModes
+{
+ public class Vrc3EditMode : Vrc3DummyMode
+ {
+ public static void Enable(ModuleVrc3 module, IEnumerable<AnimationClip> originalClips)
+ {
+ module.DummyMode = new Vrc3EditMode(module, originalClips);
+ foreach (var radialMenu in module.Radials) radialMenu.MainMenuPrefab();
+ }
+
+ internal override string ModeName => "Edit";
+
+ private Vrc3EditMode(ModuleVrc3 module, IEnumerable<AnimationClip> clips) : base(module, "[Edit-Mode]")
+ {
+ Avatar.GetOrAddComponent<Animator>().runtimeAnimatorController = GmgAnimatorControllerHelper.CreateControllerWith(clips);
+ }
+
+ public override RadialDescription DummyDescription() => new RadialDescription("You're in Edit-Mode,", "select your avatar", "to directly edit your animations!", SelectAvatarAction, null);
+
+ private void SelectAvatarAction(string obj)
+ {
+ if (Avatar == null) return;
+
+ Selection.activeGameObject = Avatar;
+ // Unity is too shy and hide too much stuff in his internal scope...
+ // this is a sad and fragile work around for opening the Animation Window.
+ EditorApplication.ExecuteMenuItem("Window/Animation/Animation");
+ }
+ }
+}
+#endif \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/DummyModes/Vrc3EditMode.cs.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/DummyModes/Vrc3EditMode.cs.meta
new file mode 100644
index 00000000..09da2de8
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/DummyModes/Vrc3EditMode.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: c84d160d067f4e6e8fa536764211a8c6
+timeCreated: 1646579962 \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/DummyModes/Vrc3TestMode.cs b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/DummyModes/Vrc3TestMode.cs
new file mode 100644
index 00000000..5b1c8e57
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/DummyModes/Vrc3TestMode.cs
@@ -0,0 +1,40 @@
+#if VRC_SDK_VRCSDK3
+using GestureManager.Scripts.Core.Editor;
+using UnityEngine;
+
+namespace GestureManager.Scripts.Editor.Modules.Vrc3.DummyModes
+{
+ public class Vrc3TestMode : Vrc3DummyMode
+ {
+ public static Vrc3TestMode Enable(ModuleVrc3 module)
+ {
+ module.DummyMode = new Vrc3TestMode(module);
+ foreach (var radialMenu in module.Radials) radialMenu.MainMenuPrefab();
+ return module.DummyMode as Vrc3TestMode;
+ }
+
+ public static Animator Disable(Vrc3DummyMode dummyMode)
+ {
+ dummyMode.Close();
+ return null;
+ }
+
+ internal override string ModeName => "Test";
+
+ private Vrc3TestMode(ModuleVrc3 module) : base(module, "[Testing]")
+ {
+ }
+
+ protected override void OnExecutionOff() => Module.Manager.StopCustomAnimation();
+
+ public override RadialDescription DummyDescription() => null;
+
+ public Animator Test(AnimationClip clip)
+ {
+ var animator = Avatar.GetOrAddComponent<Animator>();
+ animator.runtimeAnimatorController = GmgAnimatorControllerHelper.CreateControllerWith(clip);
+ return animator;
+ }
+ }
+}
+#endif \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/DummyModes/Vrc3TestMode.cs.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/DummyModes/Vrc3TestMode.cs.meta
new file mode 100644
index 00000000..f56ed98e
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/DummyModes/Vrc3TestMode.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 9682e66fe4194760992fa687db275809
+timeCreated: 1646580082 \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/ModuleVrc3.cs b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/ModuleVrc3.cs
new file mode 100644
index 00000000..ff25d7cf
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/ModuleVrc3.cs
@@ -0,0 +1,853 @@
+#if VRC_SDK_VRCSDK3
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using GestureManager.Scripts.Core.Editor;
+using GestureManager.Scripts.Editor.Modules.Vrc3.AvatarDynamics;
+using GestureManager.Scripts.Editor.Modules.Vrc3.DummyModes;
+using GestureManager.Scripts.Editor.Modules.Vrc3.OpenSoundControl;
+using GestureManager.Scripts.Editor.Modules.Vrc3.OpenSoundControl.VisualElements;
+using GestureManager.Scripts.Editor.Modules.Vrc3.Params;
+using GestureManager.Scripts.Editor.Modules.Vrc3.Vrc3Debug.Avatar;
+using GestureManager.Scripts.Editor.Modules.Vrc3.Vrc3Debug.Osc;
+using GestureManager.Scripts.Extra;
+using UnityEditor;
+using UnityEditor.Animations;
+using UnityEngine;
+using UnityEngine.Animations;
+using UnityEngine.Playables;
+using UnityEngine.SceneManagement;
+using UnityEngine.UIElements;
+using VRC.Dynamics;
+using VRC.SDK3.Avatars.Components;
+using VRC.SDK3.Avatars.ScriptableObjects;
+using VRC.SDKBase;
+
+namespace GestureManager.Scripts.Editor.Modules.Vrc3
+{
+ public class ModuleVrc3 : ModuleBase
+ {
+ private readonly VRCAvatarDescriptor _avatarDescriptor;
+
+ internal readonly OscModule OscModule;
+
+ private Vrc3AvatarDebugWindow _debugAvatarWindow;
+ private readonly AvatarTools _avatarTools;
+ internal Vrc3OscDebugWindow DebugOscWindow;
+
+ internal Vrc3DummyMode DummyMode;
+
+ private PlayableGraph _playableGraph;
+ private bool _hooked;
+
+ private bool _broken;
+ private bool _ignoreWarnings;
+
+ private int _debug;
+
+ internal readonly Vrc3ParamBool Dummy;
+ internal readonly Vrc3ParamBool PoseT;
+ internal readonly Vrc3ParamBool PoseIK;
+
+ private readonly Dictionary<UnityEditor.Editor, Vrc3WeightController> _weightControllers = new Dictionary<UnityEditor.Editor, Vrc3WeightController>();
+ private readonly Dictionary<UnityEditor.Editor, VisualEpContainer> _oscContainers = new Dictionary<UnityEditor.Editor, VisualEpContainer>();
+ private readonly Dictionary<UnityEditor.Editor, RadialMenu> _radialMenus = new Dictionary<UnityEditor.Editor, RadialMenu>();
+
+ private readonly Dictionary<VRCAvatarDescriptor.AnimLayerType, LayerData> _layers = new Dictionary<VRCAvatarDescriptor.AnimLayerType, LayerData>();
+ private readonly List<VRCAvatarDescriptor.AnimLayerType> _brokenLayers = new List<VRCAvatarDescriptor.AnimLayerType>();
+ internal readonly Dictionary<string, Vrc3Param> Params = new Dictionary<string, Vrc3Param>();
+
+ internal bool LocomotionDisabled;
+ internal bool PoseSpace;
+
+ internal readonly Dictionary<string, VRC_AnimatorTrackingControl.TrackingType> TrackingControls = ModuleVrc3Styles.Data.DefaultTrackingState;
+ internal readonly HashSet<ContactReceiver> Receivers = new HashSet<ContactReceiver>();
+ private readonly HashSet<AnimationClip> _avatarClips = new HashSet<AnimationClip>();
+ private readonly HashSet<ContactSender> _senders = new HashSet<ContactSender>();
+
+ internal int DebugToolBar;
+ internal string Edit;
+
+ private IEnumerable<AnimationClip> OriginalClips => _avatarClips.Where(clip => !clip.name.StartsWith("proxy_"));
+ private VRCExpressionParameters Parameters => _avatarDescriptor.expressionParameters;
+ private VRCExpressionsMenu Menu => _avatarDescriptor.expressionsMenu;
+ internal IEnumerable<RadialMenu> Radials => _radialMenus.Values;
+ internal float ViseAmount => _avatarDescriptor.lipSync == VRC_AvatarDescriptor.LipSyncStyle.VisemeBlendShape ? 14 : 100;
+
+ public ModuleVrc3(GestureManager manager, VRCAvatarDescriptor avatarDescriptor) : base(manager, avatarDescriptor)
+ {
+ _avatarTools = new AvatarTools();
+ _avatarDescriptor = avatarDescriptor;
+ OscModule = new OscModule(this);
+
+ Dummy = new Vrc3ParamBool(OnDummyModeChange);
+ PoseIK = new Vrc3ParamBool(OnIKPoseChange);
+ PoseT = new Vrc3ParamBool(OnTPoseChange);
+ }
+
+ public override void Update()
+ {
+ if (_broken) return;
+ OscModule.Update();
+ _avatarTools.OnUpdate(this);
+ if (DummyMode == null && _layers.Any(IsBroken)) OnBrokenSimulation();
+ if (DummyMode != null && (!DummyMode.Avatar || Avatar.activeSelf)) Dummy.ShutDown();
+ foreach (var pair in _layers) pair.Value.Weight.Update();
+ }
+
+ public override void LateUpdate() => _avatarTools.OnLateUpdate(this);
+
+ public override void InitForAvatar()
+ {
+ StartVrcHooks();
+
+ AvatarAnimator.applyRootMotion = false;
+ AvatarAnimator.runtimeAnimatorController = null;
+ AvatarAnimator.updateMode = AnimatorUpdateMode.Normal;
+ AvatarAnimator.cullingMode = AnimatorCullingMode.AlwaysAnimate;
+ DestroyGraphs();
+
+ var layerList = _avatarDescriptor.baseAnimationLayers.ToList();
+ layerList.AddRange(_avatarDescriptor.specialAnimationLayers);
+ layerList.Sort(ModuleVrc3Styles.Data.LayerSort);
+
+ _playableGraph = PlayableGraph.Create("Gesture Manager 3.6");
+ var externalOutput = AnimationPlayableOutput.Create(_playableGraph, "Gesture Manager", AvatarAnimator);
+ var playableMixer = AnimationLayerMixerPlayable.Create(_playableGraph, layerList.Count + 1);
+ externalOutput.SetSourcePlayable(playableMixer);
+
+ _layers.Clear();
+ _avatarClips.Clear();
+ _brokenLayers.Clear();
+
+ _senders.Clear();
+ Receivers.Clear();
+
+ for (var i = 0; i < layerList.Count; i++)
+ {
+ var vrcAnimLayer = layerList[i];
+ var intGraph = i + 1;
+
+ var isFx = vrcAnimLayer.type == VRCAvatarDescriptor.AnimLayerType.FX;
+ var isAdd = vrcAnimLayer.type == VRCAvatarDescriptor.AnimLayerType.Additive;
+ var isPose = vrcAnimLayer.type == VRCAvatarDescriptor.AnimLayerType.IKPose || vrcAnimLayer.type == VRCAvatarDescriptor.AnimLayerType.TPose;
+ var isAction = vrcAnimLayer.type == VRCAvatarDescriptor.AnimLayerType.Sitting || vrcAnimLayer.type == VRCAvatarDescriptor.AnimLayerType.Action;
+ var isLim = isPose || isAction;
+
+ if (vrcAnimLayer.animatorController)
+ foreach (var clip in vrcAnimLayer.animatorController.animationClips)
+ _avatarClips.Add(clip);
+
+ var controller = Vrc3ProxyOverride.OverrideController(vrcAnimLayer.isDefault ? RequestBuiltInController(vrcAnimLayer.type) : vrcAnimLayer.animatorController);
+ var mask = vrcAnimLayer.isDefault || isFx ? ModuleVrc3Styles.Data.MaskOf[vrcAnimLayer.type] : vrcAnimLayer.mask;
+
+ var playable = AnimatorControllerPlayable.Create(_playableGraph, controller);
+ var weight = new AnimatorControllerWeight(playableMixer, playable, intGraph);
+ _layers[vrcAnimLayer.type] = new LayerData { Playable = playable, Weight = weight, Empty = playable.GetInput(0).IsNull() };
+
+ playableMixer.ConnectInput(intGraph, playable, 0, 1);
+
+ if (isLim) playableMixer.SetInputWeight(intGraph, 0f);
+ if (isAdd) playableMixer.SetLayerAdditive((uint)intGraph, true);
+ if (mask) playableMixer.SetLayerMaskFromAvatarMask((uint)intGraph, mask);
+ }
+
+ _playableGraph.SetTimeUpdateMode(DirectorUpdateMode.GameTime);
+ _playableGraph.Play();
+ _playableGraph.Evaluate(0f);
+
+ foreach (var menu in Radials) menu.Set(Menu);
+ InitParams(AvatarAnimator, Parameters);
+
+ GetParam(Vrc3DefaultParams.Upright)?.InternalSet(1f);
+ GetParam(Vrc3DefaultParams.Grounded)?.InternalSet(1f);
+ GetParam(Vrc3DefaultParams.TrackingType)?.InternalSet(1f);
+ GetParam(Vrc3DefaultParams.AvatarVersion)?.InternalSet(3f);
+ GetParam(Vrc3DefaultParams.GestureLeftWeight)?.InternalSet(1f);
+ GetParam(Vrc3DefaultParams.GestureRightWeight)?.InternalSet(1f);
+
+ Left = (int)(GetParam(Vrc3DefaultParams.GestureLeft)?.Get() ?? 0);
+ Right = (int)(GetParam(Vrc3DefaultParams.GestureRight)?.Get() ?? 0);
+
+ GetParam(Vrc3DefaultParams.Vise)?.SetOnChange(OnViseChange);
+ GetParam(Vrc3DefaultParams.Seated)?.SetOnChange(OnSeatedModeChange);
+ GetParam(Vrc3DefaultParams.GestureLeft)?.SetOnChange(OnGestureLeftChange);
+ GetParam(Vrc3DefaultParams.GestureRight)?.SetOnChange(OnGestureRightChange);
+
+ foreach (var physBone in AvatarComponents<VRCPhysBoneBase>()) PhysBoneBaseSetup(physBone);
+ foreach (var receiver in AvatarComponents<ContactReceiver>()) ReceiverBaseSetup(receiver);
+ foreach (var sender in AvatarComponents<ContactSender>()) SenderBaseSetup(sender);
+
+ OscModule.Resume();
+ }
+
+ public override void Unlink()
+ {
+ CloseDebugWindows();
+ if (OscModule.Enabled) OscModule.Stop();
+ if (Dummy.State) Dummy.ShutDown();
+ if (AvatarAnimator) ForgetAvatar();
+ StopVisualElements();
+ StopVrcHooks();
+ }
+
+ public override void EditorHeader()
+ {
+ if (_brokenLayers.Count == 0 || _ignoreWarnings || _broken) return;
+ using (new GmgLayoutHelper.GuiBackground(Color.yellow)) ShowWarnings();
+ }
+
+ public override void EditorContent(object editor, VisualElement element)
+ {
+ var menu = GetOrCreateRadial(editor as UnityEditor.Editor);
+ var oscContainer = GetOrCreateOscContainer(editor as UnityEditor.Editor);
+ var weightController = GetOrCreateWeightController(editor as UnityEditor.Editor);
+
+ GUI.enabled = !_broken;
+ GmgLayoutHelper.MyToolbar(ref menu.ToolBar, new (string, Action)[]
+ {
+ ("Gestures", () => EditorContentGesture(element, weightController)),
+ ("Tools", () => _avatarTools.Gui(this)),
+ ("Debug", () => EditorContentDebug(menu))
+ });
+ GUI.enabled = true;
+
+ GUILayout.Space(4);
+ GmgLayoutHelper.Divisor(1);
+
+ if (_broken) ShowError(menu);
+ else
+ switch (menu.ToolBar.Selected)
+ {
+ case 0:
+ ShowRadialMenu(menu, element);
+ break;
+ case 2:
+ ShowDebugMenu(element, oscContainer, menu);
+ break;
+ }
+
+ menu.CheckCondition(this, menu);
+ oscContainer.CheckCondition(this, menu);
+ weightController.CheckCondition(this, menu);
+ }
+
+ protected override void OnNewLeft(int left) => Params[Vrc3DefaultParams.GestureLeft].Set(this, left);
+
+ protected override void OnNewRight(int right) => Params[Vrc3DefaultParams.GestureRight].Set(this, right);
+
+ public override AnimationClip GetFinalGestureByIndex(int gestureIndex) => ModuleVrc3Styles.Data.GestureClips[gestureIndex];
+
+ public override Animator OnCustomAnimationPlay(AnimationClip clip)
+ {
+ if (!clip) return Vrc3TestMode.Disable(DummyMode);
+ if (!(DummyMode is Vrc3TestMode testMode)) testMode = Vrc3TestMode.Enable(this);
+ return testMode.Test(clip);
+ }
+
+ public override bool HasGestureBeenOverridden(int gesture) => true;
+
+ public override void AddGestureToOverrideController(int gestureIndex, AnimationClip newAnimation)
+ {
+ }
+
+ protected override List<string> CheckErrors()
+ {
+ var errorList = base.CheckErrors();
+ errorList.AddRange(RadialMenuUtility.CheckErrors(_avatarDescriptor.expressionsMenu, _avatarDescriptor.expressionParameters));
+ return errorList;
+ }
+
+ /*
+ * Editor GUI
+ */
+
+ private void EditorContentGesture(VisualElement element, Vrc3WeightController weightController)
+ {
+ using (new EditorGUILayout.HorizontalScope())
+ {
+ GUI.enabled = DummyMode == null && !_broken;
+
+ using (new GUILayout.VerticalScope())
+ {
+ GUILayout.Label("Left Hand", GestureManagerStyles.GuiHandTitle);
+ weightController.RenderLeft(element);
+ using (new GUILayout.VerticalScope()) GestureManagerEditor.OnCheckBoxGuiHand(this, GestureHand.Left, Left, false);
+ var rect = GUILayoutUtility.GetLastRect();
+ var isContained = rect.Contains(Event.current.mousePosition);
+ GestureDrag = (Event.current.type == EventType.MouseDown && isContained) || Event.current.type != EventType.MouseUp && GestureDrag;
+ if (isContained && GestureDrag && Event.current.type == EventType.MouseDrag) OnNewLeft((int)((Event.current.mousePosition.y - GUILayoutUtility.GetLastRect().y) / 19) + 1);
+ }
+
+ using (new GUILayout.VerticalScope())
+ {
+ GUILayout.Label("Right Hand", GestureManagerStyles.GuiHandTitle);
+ weightController.RenderRight(element);
+ using (new GUILayout.VerticalScope()) GestureManagerEditor.OnCheckBoxGuiHand(this, GestureHand.Right, Right, false);
+ var rect = GUILayoutUtility.GetLastRect();
+ var isContained = rect.Contains(Event.current.mousePosition);
+ GestureDrag = (Event.current.type == EventType.MouseDown && isContained) || Event.current.type != EventType.MouseUp && GestureDrag;
+ if (isContained && GestureDrag && Event.current.type == EventType.MouseDrag) OnNewRight((int)((Event.current.mousePosition.y - GUILayoutUtility.GetLastRect().y) / 19) + 1);
+ }
+
+ GUI.enabled = true;
+ }
+ }
+
+ private void EditorContentDebug(RadialMenu menu)
+ {
+ GmgLayoutHelper.MyToolbar(ref menu.DebugToolBar, new (string, Action)[]
+ {
+ ("Avatar", EditorContentDebugAvatar),
+ ("Open Sound Control", OscModule.ControlPanel)
+ });
+ }
+
+ private void EditorContentDebugAvatar()
+ {
+ GUILayout.Label("Avatar Debug", GestureManagerStyles.GuiHandTitle);
+ GUILayout.Label(_debugAvatarWindow ? Vrc3AvatarDebugWindow.Text.W.Subtitle : Vrc3AvatarDebugWindow.Text.D.Subtitle, GestureManagerStyles.SubHeader);
+
+ GUILayout.Space(_debugAvatarWindow ? 11 : 10);
+ if (_debugAvatarWindow) GUILayout.Label(Vrc3AvatarDebugWindow.Text.W.Message, GestureManagerStyles.SubHeader);
+ else DebugToolBar = Vrc3AvatarDebugWindow.Static.DebugToolbar(DebugToolBar);
+ GUILayout.Space(_debugAvatarWindow ? 11 : 10);
+
+ GUILayout.Label(_debugAvatarWindow ? Vrc3AvatarDebugWindow.Text.W.Hint : Vrc3AvatarDebugWindow.Text.D.Hint, GestureManagerStyles.SubHeader);
+ GUILayout.Space(7);
+ GUILayout.BeginHorizontal();
+ GUILayout.FlexibleSpace();
+ if (GmgLayoutHelper.DebugButton(_debugAvatarWindow ? Vrc3AvatarDebugWindow.Text.W.Button : Vrc3AvatarDebugWindow.Text.D.Button)) SwitchDebugAvatarView();
+ GUILayout.FlexibleSpace();
+ GUILayout.EndHorizontal();
+ GUILayout.Space(6);
+ }
+
+ /*
+ * Functions
+ */
+
+ private static bool IsBroken(KeyValuePair<VRCAvatarDescriptor.AnimLayerType, LayerData> pair) => !pair.Value.Empty && pair.Value.Playable.GetInput(0).IsNull();
+
+ private IEnumerable<T> AvatarComponents<T>(bool includeInactive = true) where T : Component => Avatar.GetComponentsInChildren<T>(includeInactive);
+
+ private void RemoveVise() => OnViseChange(null, 0f);
+
+ private void OnBrokenSimulation()
+ {
+ _broken = true;
+ foreach (var menu in Radials) menu.ToolBar.Selected = 0;
+ if (OscModule.Enabled) OscModule.Stop();
+ CloseDebugWindows();
+ }
+
+ private void CloseDebugWindows()
+ {
+ if (_debugAvatarWindow) _debugAvatarWindow.Close();
+ if (DebugOscWindow) DebugOscWindow.Close();
+ }
+
+ private RuntimeAnimatorController RequestBuiltInController(VRCAvatarDescriptor.AnimLayerType type)
+ {
+ var controller = ModuleVrc3Styles.Data.ControllerOf(type);
+ var restoreAsset = ModuleVrc3Styles.Data.RestoreOf(type);
+
+ if (!controller || !CheckIntegrity(new FileInfo(NameOf(controller)), new FileInfo(NameOf(restoreAsset)))) _brokenLayers.Add(type);
+ return controller ? controller : new AnimatorController();
+ }
+
+ private void StopVisualElements()
+ {
+ foreach (var container in _oscContainers.Values) container.StopRendering();
+ foreach (var weight in _weightControllers.Values) weight.StopRendering();
+ foreach (var menu in Radials) menu.StopRendering();
+ }
+
+ internal void ReloadRadials()
+ {
+ foreach (var weight in _weightControllers.Values) weight.StopRendering();
+ foreach (var menu in Radials) menu.StopRendering();
+ _weightControllers.Clear();
+ _radialMenus.Clear();
+ }
+
+ private void SwitchDebugAvatarView() => _debugAvatarWindow = _debugAvatarWindow ? Vrc3AvatarDebugWindow.Close(_debugAvatarWindow) : Vrc3AvatarDebugWindow.Create(this);
+
+ internal void SwitchDebugOscView() => DebugOscWindow = DebugOscWindow ? Vrc3OscDebugWindow.Close(DebugOscWindow) : Vrc3OscDebugWindow.Create(this);
+
+ private void ShowDebugMenu(VisualElement root, VisualEpContainer holder, RadialMenu menu)
+ {
+ switch (menu.DebugToolBar.Selected)
+ {
+ case 0 when _debugAvatarWindow:
+ case 1 when DebugOscWindow:
+ return;
+ }
+
+ DebugContext(root, holder, menu.DebugToolBar.Selected, Screen.width - 60, false);
+ GUILayout.Space(4);
+ GmgLayoutHelper.Divisor(1);
+ }
+
+ internal void DebugContext(VisualElement root, VisualEpContainer holder, int selected, float width, bool fullScreen)
+ {
+ if (DummyMode == null)
+ {
+ switch (selected)
+ {
+ case 0:
+ Vrc3AvatarDebugWindow.Static.DebugLayout(this, width, fullScreen, _layers);
+ break;
+ case 1:
+ OscModule.DebugLayout(root, holder, width);
+ break;
+ }
+ }
+ else Vrc3AvatarDebugWindow.Static.DummyLayout(DummyMode.ModeName);
+ }
+
+ private void ShowWarnings()
+ {
+ using (new GUILayout.VerticalScope(GestureManagerStyles.EmoteError))
+ {
+ GUILayout.Space(2);
+ using (new GUILayout.HorizontalScope())
+ {
+ GUILayout.Space(15);
+ GUILayout.Label("WARNING", GestureManagerStyles.TextWarningHeader);
+ if (GUILayout.Button("X", GUI.skin.label, GUILayout.Width(15))) _ignoreWarnings = true;
+ }
+
+ GUILayout.Space(5);
+ using (new GUILayout.HorizontalScope())
+ {
+ GUILayout.FlexibleSpace();
+ GUILayout.Label("Some default Animator Controllers has changed!", GestureManagerStyles.TextWarning);
+ GUILayout.FlexibleSpace();
+ if (GUILayout.Button("Restore Controllers")) RestoreDefaultControllers();
+ GUILayout.FlexibleSpace();
+ }
+ }
+ }
+
+ private void ShowError(Vrc3VisualRender menu)
+ {
+ menu.StopRendering();
+ GUILayout.Label("Radial Menu", GestureManagerStyles.GuiHandTitle);
+ using (new GmgLayoutHelper.GuiBackground(Color.red))
+ using (new GUILayout.VerticalScope(GUILayout.Height(RadialMenu.Size)))
+ {
+ GUILayout.FlexibleSpace();
+ using (new GUILayout.VerticalScope(EditorStyles.helpBox))
+ {
+ GUILayout.Space(3);
+ using (new GUILayout.VerticalScope())
+ {
+ GUILayout.FlexibleSpace();
+ GUILayout.Label("An Animator Controller changed during the simulation!", GestureManagerStyles.TextError);
+ GUILayout.FlexibleSpace();
+ using (new GUILayout.HorizontalScope())
+ {
+ GUILayout.FlexibleSpace();
+ GUI.backgroundColor = RadialMenuUtility.Colors.RestartButton;
+ if (GmgLayoutHelper.DebugButton("Click to reload scene")) ReloadScene();
+ GUILayout.FlexibleSpace();
+ }
+
+ GUILayout.FlexibleSpace();
+ }
+ }
+
+ GUILayout.FlexibleSpace();
+ }
+ }
+
+ private void ReloadScene()
+ {
+ if (Manager.Module == null) return;
+ var activeScene = SceneManager.GetActiveScene();
+ LoadScene(activeScene.buildIndex, LoadSceneMode.Single, (scene, mode) =>
+ {
+ var manager = VRC.Tools.FindSceneObjectsOfTypeAll<GestureManager>().FirstOrDefault();
+ GestureManagerEditor.CreateAndPing(manager);
+ });
+ }
+
+ private void RestoreDefaultControllers()
+ {
+ var deleted = false;
+
+ foreach (var brokenLayer in _brokenLayers)
+ {
+ var pString = AssetDatabase.GetAssetPath(ModuleVrc3Styles.Data.RestoreOf(brokenLayer));
+ var newString = AssetDatabase.GetAssetPath(ModuleVrc3Styles.Data.ControllerOf(brokenLayer));
+ if (!string.IsNullOrEmpty(pString) && !string.IsNullOrEmpty(newString)) AssetDatabase.CopyAsset(pString, newString);
+ else deleted = true;
+ }
+
+ const string message = "It seems some controllers has been moved or has been deleted!\n\nPlease, reimport the Gesture Manager to fix this.";
+ if (deleted) EditorUtility.DisplayDialog("Restore Error!", message, "Ok");
+
+ ReloadScene();
+ }
+
+ private RadialMenu GetOrCreateRadial(UnityEditor.Editor editor)
+ {
+ if (_radialMenus.ContainsKey(editor)) return _radialMenus[editor];
+
+ _radialMenus[editor] = new RadialMenu(this, true);
+ _radialMenus[editor].Set(Menu);
+ return _radialMenus[editor];
+ }
+
+ private Vrc3WeightController GetOrCreateWeightController(UnityEditor.Editor editor)
+ {
+ if (_weightControllers.ContainsKey(editor)) return _weightControllers[editor];
+
+ _weightControllers[editor] = new Vrc3WeightController(this);
+ return _weightControllers[editor];
+ }
+
+ private VisualEpContainer GetOrCreateOscContainer(UnityEditor.Editor editor)
+ {
+ if (_oscContainers.ContainsKey(editor)) return _oscContainers[editor];
+
+ _oscContainers[editor] = new VisualEpContainer();
+ return _oscContainers[editor];
+ }
+
+ private void OnDummyModeChange(bool state) => DummyMode?.OnExecutionChange(state);
+
+ internal void EnableEditMode() => Vrc3EditMode.Enable(this, OriginalClips);
+
+ public void NoExpressionRefresh()
+ {
+ if (Dummy.State) return;
+ if (Menu) ResetAvatar();
+ }
+
+ public void ResetAvatar()
+ {
+ AvatarAnimator.Rebind();
+ InitForAvatar();
+ }
+
+ internal void ForgetAvatar()
+ {
+ RemoveVise();
+ if (OscModule.Enabled) OscModule.Forget();
+ AvatarAnimator.Rebind();
+ Params.Clear();
+ DestroyGraphs();
+ }
+
+ private void DestroyGraphs()
+ {
+ if (_playableGraph.IsValid()) _playableGraph.Destroy();
+ if (AvatarAnimator.playableGraph.IsValid()) AvatarAnimator.playableGraph.Destroy();
+ }
+
+ private void OnViseChange(Vrc3Param param, float fVise)
+ {
+ var vise = (int)fVise;
+ var skinnedMesh = _avatarDescriptor.VisemeSkinnedMesh;
+
+ switch (_avatarDescriptor.lipSync)
+ {
+ case VRC_AvatarDescriptor.LipSyncStyle.JawFlapBone:
+ if (!_avatarDescriptor.lipSyncJawBone) return;
+ _avatarDescriptor.lipSyncJawBone.transform.rotation = vise == 0 ? _avatarDescriptor.lipSyncJawClosed : _avatarDescriptor.lipSyncJawOpen;
+ return;
+ case VRC_AvatarDescriptor.LipSyncStyle.JawFlapBlendShape:
+ if (!skinnedMesh) return;
+ SetBlendShapeWeight(skinnedMesh, _avatarDescriptor.MouthOpenBlendShapeName, vise);
+ return;
+ case VRC_AvatarDescriptor.LipSyncStyle.VisemeBlendShape:
+ if (_avatarDescriptor.VisemeBlendShapes == null || !skinnedMesh) return;
+ for (var i = 0; i < _avatarDescriptor.VisemeBlendShapes.Length; i++) SetBlendShapeWeight(skinnedMesh, _avatarDescriptor.VisemeBlendShapes[i], i == vise ? 100.0f : 0.0f);
+ return;
+ case VRC_AvatarDescriptor.LipSyncStyle.Default:
+ case VRC_AvatarDescriptor.LipSyncStyle.VisemeParameterOnly:
+ return;
+ default: throw new ArgumentOutOfRangeException();
+ }
+ }
+
+ private void OnSeatedModeChange(Vrc3Param param, float seated) => _layers[VRCAvatarDescriptor.AnimLayerType.Sitting].Weight.Set(seated);
+
+ private void OnTPoseChange(bool state) => _layers[VRCAvatarDescriptor.AnimLayerType.TPose].Weight.Set(state ? 1f : 0f);
+
+ private void OnIKPoseChange(bool state) => _layers[VRCAvatarDescriptor.AnimLayerType.IKPose].Weight.Set(state ? 1f : 0f);
+
+ private void OnGestureLeftChange(Vrc3Param param, float left) => Left = (int)left;
+
+ private void OnGestureRightChange(Vrc3Param param, float right) => Right = (int)right;
+
+ /*
+ * Params
+ */
+
+ private void InitParams(Animator animator, VRCExpressionParameters parameters)
+ {
+ Params.Clear();
+ foreach (var pair in _layers)
+ foreach (var parameter in RadialMenuUtility.GetParameters(pair.Value.Playable))
+ Params[parameter.name] = RadialMenuUtility.CreateParamFromController(animator, parameter, pair.Value.Playable);
+
+ if (parameters)
+ foreach (var parameter in parameters.parameters)
+ if (!Params.ContainsKey(parameter.name))
+ Params[parameter.name] = RadialMenuUtility.CreateParamFromNothing(parameter);
+
+ if (parameters)
+ foreach (var parameter in parameters.parameters)
+ Params[parameter.name].InternalSet(parameter.defaultValue);
+
+ foreach (var (nameString, type) in Vrc3DefaultParams.Parameters)
+ if (!Params.ContainsKey(nameString))
+ Params[nameString] = RadialMenuUtility.CreateParamFromNothing(nameString, type);
+ }
+
+ public Vrc3Param GetParam(string pName)
+ {
+ if (pName == null) return null;
+ Params.TryGetValue(pName, out var param);
+ return param;
+ }
+
+ private static string NameOf(UnityEngine.Object o) => Application.dataPath + AssetDatabase.GetAssetPath(o).Substring(6);
+
+ private static void LoadScene(int sceneBuildIndex, LoadSceneMode mode, Action<Scene, LoadSceneMode> onLoad)
+ {
+ void OnSceneManagerLoaded(Scene scene, LoadSceneMode m)
+ {
+ SceneManager.sceneLoaded -= OnSceneManagerLoaded;
+ onLoad(scene, m);
+ }
+
+ SceneManager.sceneLoaded += OnSceneManagerLoaded;
+ SceneManager.LoadScene(sceneBuildIndex, mode);
+ }
+
+ private static bool CheckIntegrity(FileInfo file1, FileInfo file2)
+ {
+ if (file1.Length != file2.Length) return false;
+
+ using (var stream1 = file1.OpenRead())
+ using (var stream2 = file2.OpenRead())
+ for (var i = 0; i < file1.Length; i++)
+ if (stream1.ReadByte() != stream2.ReadByte())
+ return false;
+
+ return true;
+ }
+
+ private static void ShowRadialMenu(RadialMenu menu, VisualElement element)
+ {
+ GUILayout.Label("Radial Menu", GestureManagerStyles.GuiHandTitle);
+ GUILayoutUtility.GetRect(new GUIContent(), GUIStyle.none, GUILayout.ExpandWidth(true), GUILayout.Height(RadialMenu.Size));
+ menu.Render(element, GmgLayoutHelper.GetLastRect(ref menu.Rect));
+ menu.ShowRadialDescription();
+ }
+
+ private static void SetBlendShapeWeight(SkinnedMeshRenderer skinnedMesh, string name, float weight)
+ {
+ var intIndex = skinnedMesh.sharedMesh.GetBlendShapeIndex(name);
+ if (intIndex != -1) skinnedMesh.SetBlendShapeWeight(intIndex, weight);
+ }
+
+ /*
+ * Vrc Hooks
+ */
+
+ private void StartVrcHooks()
+ {
+ if (_hooked) return;
+ AvatarDynamicReset.CheckSceneCollisions();
+
+ ContactBase.OnInitialize += ContactBaseInit;
+ VRCPhysBoneBase.OnInitialize += PhysBoneBaseInit;
+ VRC_AnimatorLayerControl.Initialize += AnimatorLayerControlInit;
+ VRC_PlayableLayerControl.Initialize += PlayableLayerControlInit;
+ VRC_AnimatorTrackingControl.Initialize += AnimatorTrackingControlInit;
+ VRC_AnimatorLocomotionControl.Initialize += AnimatorLocomotionControlInit;
+ VRC_AnimatorTemporaryPoseSpace.Initialize += AnimatorTemporaryPoseSpaceInit;
+
+ VRC_AvatarParameterDriver.OnApplySettings += AvatarParameterDriverSettings;
+
+ _hooked = true;
+ }
+
+ private void StopVrcHooks()
+ {
+ if (!_hooked) return;
+
+ ContactBase.OnInitialize -= ContactBaseInit;
+ VRCPhysBoneBase.OnInitialize -= PhysBoneBaseInit;
+ VRC_AnimatorLayerControl.Initialize -= AnimatorLayerControlInit;
+ VRC_PlayableLayerControl.Initialize -= PlayableLayerControlInit;
+ VRC_AnimatorTrackingControl.Initialize -= AnimatorTrackingControlInit;
+ VRC_AnimatorLocomotionControl.Initialize -= AnimatorLocomotionControlInit;
+ VRC_AnimatorTemporaryPoseSpace.Initialize -= AnimatorTemporaryPoseSpaceInit;
+
+ VRC_AvatarParameterDriver.OnApplySettings -= AvatarParameterDriverSettings;
+
+ _hooked = false;
+ }
+
+ /*
+ * Vrc Hooks (Static)
+ */
+
+ private void AnimatorLayerControlInit(VRC_AnimatorLayerControl animatorLayerControl) => animatorLayerControl.ApplySettings += AnimatorLayerControlSettings;
+
+ private void PlayableLayerControlInit(VRC_PlayableLayerControl playableLayerControl) => playableLayerControl.ApplySettings += PlayableLayerControlSettings;
+
+ private void AnimatorTrackingControlInit(VRC_AnimatorTrackingControl animatorTrackingControl) => animatorTrackingControl.ApplySettings += AnimatorTrackingControlSettings;
+
+ private void AnimatorLocomotionControlInit(VRC_AnimatorLocomotionControl animatorLocomotionControl) => animatorLocomotionControl.ApplySettings += AnimatorLocomotionControlSettings;
+
+ private void AnimatorTemporaryPoseSpaceInit(VRC_AnimatorTemporaryPoseSpace animatorTemporaryPoseSpace) => animatorTemporaryPoseSpace.ApplySettings += AnimatorTemporaryPoseSpaceSettings;
+
+ /*
+ * Vrc Hooks (Dynamic)
+ */
+
+ private void AnimatorLayerControlSettings(VRC_AnimatorLayerControl control, Animator animator)
+ {
+ if (!_hooked || animator != AvatarAnimator) return;
+
+ _layers[ModuleVrc3Styles.Data.AnimatorToLayer[control.playable]].Weight.Start(control);
+ }
+
+ private void PlayableLayerControlSettings(VRC_PlayableLayerControl control, Animator animator)
+ {
+ if (!_hooked || animator != AvatarAnimator) return;
+
+ _layers[ModuleVrc3Styles.Data.PlayableToLayer[control.layer]].Weight.Start(control);
+ }
+
+ private void AvatarParameterDriverSettings(VRC_AvatarParameterDriver driver, Animator animator)
+ {
+ if (!_hooked || animator != AvatarAnimator) return;
+
+ foreach (var parameter in driver.parameters)
+ {
+ var param = GetParam(parameter.name);
+ if (param == null) continue;
+
+ switch (parameter.type)
+ {
+ case VRC_AvatarParameterDriver.ChangeType.Set:
+ param.Set(this, parameter.value);
+ break;
+ case VRC_AvatarParameterDriver.ChangeType.Add:
+ param.Add(this, parameter.value);
+ break;
+ case VRC_AvatarParameterDriver.ChangeType.Copy:
+ param.Copy(this, GetParam(parameter.source)?.Get() ?? 0f, parameter.convertRange, parameter.sourceMin, parameter.sourceMax, parameter.destMin, parameter.destMax);
+ break;
+ case VRC_AvatarParameterDriver.ChangeType.Random:
+ param.Random(this, parameter.valueMin, parameter.valueMax, parameter.chance);
+ break;
+ default: throw new ArgumentOutOfRangeException();
+ }
+ }
+ }
+
+ private void AnimatorTrackingControlSettings(VRC_AnimatorTrackingControl control, Animator animator)
+ {
+ if (!_hooked || animator != AvatarAnimator) return;
+ const VRC_AnimatorTrackingControl.TrackingType noChange = VRC_AnimatorTrackingControl.TrackingType.NoChange;
+
+ if (control.trackingHip != noChange) TrackingControls["Hip"] = control.trackingHip;
+ if (control.trackingHead != noChange) TrackingControls["Head"] = control.trackingHead;
+ if (control.trackingEyes != noChange) TrackingControls["Eye & Eyelid"] = control.trackingEyes;
+ if (control.trackingMouth != noChange) TrackingControls["Mouth & Jaw"] = control.trackingMouth;
+ if (control.trackingLeftHand != noChange) TrackingControls["Left Hand"] = control.trackingLeftHand;
+ if (control.trackingLeftFoot != noChange) TrackingControls["Left Foot"] = control.trackingLeftFoot;
+ if (control.trackingRightHand != noChange) TrackingControls["Right Hand"] = control.trackingRightHand;
+ if (control.trackingRightFoot != noChange) TrackingControls["Right Foot"] = control.trackingRightFoot;
+ if (control.trackingLeftFingers != noChange) TrackingControls["Left Fingers"] = control.trackingLeftFingers;
+ if (control.trackingRightFingers != noChange) TrackingControls["Right Fingers"] = control.trackingRightFingers;
+ }
+
+ private void AnimatorLocomotionControlSettings(VRC_AnimatorLocomotionControl control, Animator animator)
+ {
+ if (!_hooked || animator != AvatarAnimator) return;
+
+ LocomotionDisabled = control.disableLocomotion;
+ }
+
+ private void AnimatorTemporaryPoseSpaceSettings(VRC_AnimatorTemporaryPoseSpace poseSpace, Animator animator)
+ {
+ if (!_hooked || animator != AvatarAnimator) return;
+
+ PoseSpace = poseSpace.enterPoseSpace;
+ }
+
+ /*
+ * Vrc Hooks (Dynamics)
+ */
+
+ private bool ContactBaseInit(ContactBase contactBase)
+ {
+ var animator = contactBase.GetComponentInParent<VRCAvatarDescriptor>()?.GetComponent<Animator>();
+ if (!_hooked || !animator || animator != AvatarAnimator) return true;
+
+ switch (contactBase)
+ {
+ case ContactReceiver receiver:
+ ReceiverBaseSetup(receiver);
+ return true;
+ case ContactSender sender:
+ SenderBaseSetup(sender);
+ return true;
+ default: return true;
+ }
+ }
+
+ private void ReceiverBaseSetup(ContactReceiver receiver)
+ {
+ if (string.IsNullOrWhiteSpace(receiver.parameter)) return;
+ Receivers.Add(receiver);
+ receiver.paramAccess = new AnimParameterAccessAvatarGmg(this, receiver.parameter);
+ if (receiver.shape != null) receiver.shape.OnEnter += shape => ContactShapeCheck(receiver, shape.component as ContactSender);
+ }
+
+ private void SenderBaseSetup(ContactSender sender) => _senders.Add(sender);
+
+ private void ContactShapeCheck(ContactReceiver receiver, ContactSender sender)
+ {
+ if (!sender || !receiver) return;
+ if (!receiver.allowSelf && _senders.Contains(sender)) receiver.shape.OnExit.Invoke(sender.shape);
+ if (!receiver.allowOthers && !_senders.Contains(sender)) receiver.shape.OnExit.Invoke(sender.shape);
+ }
+
+ private void PhysBoneBaseInit(VRCPhysBoneBase vrcPhysBoneBase)
+ {
+ if (string.IsNullOrEmpty(vrcPhysBoneBase.parameter)) return;
+
+ var animator = vrcPhysBoneBase.GetComponentInParent<VRCAvatarDescriptor>()?.GetComponent<Animator>();
+ if (!_hooked || !animator || animator != AvatarAnimator) return;
+
+ PhysBoneBaseSetup(vrcPhysBoneBase);
+ }
+
+ private void PhysBoneBaseSetup(VRCPhysBoneBase vrcPhysBoneBase)
+ {
+ vrcPhysBoneBase.param_IsGrabbed = new AnimParameterAccessAvatarGmg(this, vrcPhysBoneBase.parameter + VRCPhysBoneBase.PARAM_ISGRABBED);
+ vrcPhysBoneBase.param_Angle = new AnimParameterAccessAvatarGmg(this, vrcPhysBoneBase.parameter + VRCPhysBoneBase.PARAM_ANGLE);
+ vrcPhysBoneBase.param_Stretch = new AnimParameterAccessAvatarGmg(this, vrcPhysBoneBase.parameter + VRCPhysBoneBase.PARAM_STRETCH);
+ }
+
+ internal struct LayerData
+ {
+ internal AnimatorControllerPlayable Playable;
+ internal AnimatorControllerWeight Weight;
+ internal bool Empty;
+ }
+ }
+}
+#endif \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/ModuleVrc3.cs.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/ModuleVrc3.cs.meta
new file mode 100644
index 00000000..729df3f8
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/ModuleVrc3.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: d787d32d1f44402f8ae7a4af4b0f0b18
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/ModuleVrc3ProxyOverride.cs b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/ModuleVrc3ProxyOverride.cs
new file mode 100644
index 00000000..b712686f
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/ModuleVrc3ProxyOverride.cs
@@ -0,0 +1,102 @@
+#if VRC_SDK_VRCSDK3
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+using UnityEngine;
+
+namespace GestureManager.Scripts.Editor.Modules.Vrc3
+{
+ public static class Vrc3ProxyOverride
+ {
+ public static RuntimeAnimatorController OverrideController(RuntimeAnimatorController controller)
+ {
+ if (!controller) return controller;
+ var overrideController = new AnimatorOverrideController(controller);
+ overrideController.ApplyOverrides(controller.animationClips.Select(OverrideClip).Where(pair => pair.Value).ToList());
+ return overrideController;
+ }
+
+ private static KeyValuePair<AnimationClip, AnimationClip> OverrideClip(AnimationClip clip)
+ {
+ OverrideOf.TryGetValue(clip.name, out var oClip);
+ return new KeyValuePair<AnimationClip, AnimationClip>(clip, oClip);
+ }
+
+ private static Dictionary<string, AnimationClip> _overrideOf;
+
+ [SuppressMessage("ReSharper", "StringLiteralTypo")]
+ private static Dictionary<string, AnimationClip> OverrideOf => _overrideOf ?? (_overrideOf = new Dictionary<string, AnimationClip>
+ {
+ // Locomotion
+ { "proxy_low_crawl_still", null },
+ { "proxy_low_crawl_forward", null },
+ { "proxy_low_crawl_right", null },
+ { "proxy_sprint_forward", null },
+ { "proxy_run_forward", null },
+ { "proxy_walk_forward", null },
+ { "proxy_stand_still", null },
+ { "proxy_walk_backward", null },
+ { "proxy_run_backward", null },
+ { "proxy_strafe_right", null },
+ { "proxy_strafe_right_45", null },
+ { "proxy_strafe_right_135", null },
+ { "proxy_run_strafe_right", null },
+ { "proxy_run_strafe_right_45", null },
+ { "proxy_run_strafe_right_135", null },
+ { "proxy_crouch_still", null },
+ { "proxy_crouch_walk_forward", null },
+ { "proxy_crouch_walk_right", null },
+ { "proxy_crouch_walk_right_45", null },
+ { "proxy_crouch_walk_right_135", null },
+ // Fall & Landing
+ { "proxy_fall_short", null },
+ { "proxy_landing", null },
+ { "proxy_fall_long", null },
+ { "proxy_land_quick", null },
+ { "proxy_idle", null },
+ // Gesture
+ { "proxy_hands_idle", null },
+ { "proxy_hands_fist", GestureManagerStyles.Animations.Gesture.Fist },
+ { "proxy_hands_open", GestureManagerStyles.Animations.Gesture.Open },
+ { "proxy_hands_point", GestureManagerStyles.Animations.Gesture.Point },
+ { "proxy_hands_peace", GestureManagerStyles.Animations.Gesture.Peace },
+ { "proxy_hands_rock", GestureManagerStyles.Animations.Gesture.Rock },
+ { "proxy_hands_gun", GestureManagerStyles.Animations.Gesture.Gun },
+ { "proxy_hands_thumbs_up", GestureManagerStyles.Animations.Gesture.ThumbsUp },
+ // Emotes [Standing]
+ { "proxy_stand_wave", GestureManagerStyles.Animations.Emote.Standing.Wave },
+ { "proxy_stand_clap", GestureManagerStyles.Animations.Emote.Standing.Clap },
+ { "proxy_stand_point", GestureManagerStyles.Animations.Emote.Standing.Point },
+ { "proxy_stand_cheer", GestureManagerStyles.Animations.Emote.Standing.Cheer },
+ { "proxy_dance", GestureManagerStyles.Animations.Emote.Standing.Dance },
+ { "proxy_backflip", GestureManagerStyles.Animations.Emote.Standing.BackFlip },
+ { "proxy_die", GestureManagerStyles.Animations.Emote.Standing.Die },
+ { "proxy_stand_sadkick", GestureManagerStyles.Animations.Emote.Standing.SadKick },
+ // Emotes [Seated]
+ { "proxy_seated_laugh", GestureManagerStyles.Animations.Emote.Seated.Laugh },
+ { "proxy_seated_point", GestureManagerStyles.Animations.Emote.Seated.Point },
+ { "proxy_seated_raise_hand", GestureManagerStyles.Animations.Emote.Seated.RaiseHand },
+ { "proxy_seated_drum", GestureManagerStyles.Animations.Emote.Seated.Drum },
+ { "proxy_seated_clap", GestureManagerStyles.Animations.Emote.Seated.Clap },
+ { "proxy_seated_shake_fist", GestureManagerStyles.Animations.Emote.Seated.ShakeFist },
+ { "proxy_seated_disbelief", GestureManagerStyles.Animations.Emote.Seated.Disbelief },
+ { "proxy_seated_disapprove", GestureManagerStyles.Animations.Emote.Seated.Disapprove },
+ // Cool Animations
+ { "proxy_afk", null },
+ { "proxy_sit", null },
+ { "proxy_tpose", null },
+ { "proxy_ikpose", null },
+ // Extra
+ { "proxy_supine_getup", null },
+ { "proxy_eyes_die", null },
+ { "proxy_eyes_open", null },
+ { "proxy_eyes_shut", null },
+ { "proxy_mood_neutral", null },
+ { "proxy_mood_happy", null },
+ { "proxy_mood_surprised", null },
+ { "proxy_mood_sad", null },
+ { "proxy_mood_angry", null }
+ });
+ }
+}
+#endif \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/ModuleVrc3ProxyOverride.cs.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/ModuleVrc3ProxyOverride.cs.meta
new file mode 100644
index 00000000..6278a4f5
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/ModuleVrc3ProxyOverride.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: f6c4ee2694df43359f8f7008f15db059
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/ModuleVrc3Styles.cs b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/ModuleVrc3Styles.cs
new file mode 100644
index 00000000..c984f5fc
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/ModuleVrc3Styles.cs
@@ -0,0 +1,184 @@
+#if VRC_SDK_VRCSDK3
+using System.Collections.Generic;
+using GestureManager.Scripts.Core;
+using UnityEditor.Animations;
+using UnityEngine;
+using VRC.SDK3.Avatars.Components;
+using ValueType = VRC.SDK3.Avatars.ScriptableObjects.VRCExpressionParameters.ValueType;
+using AnimLayerType = VRC.SDK3.Avatars.Components.VRCAvatarDescriptor.AnimLayerType;
+using BlendablePlayableLayer = VRC.SDKBase.VRC_PlayableLayerControl.BlendableLayer;
+using BlendableAnimatorLayer = VRC.SDKBase.VRC_AnimatorLayerControl.BlendableLayer;
+using TrackingType = VRC.SDKBase.VRC_AnimatorTrackingControl.TrackingType;
+
+namespace GestureManager.Scripts.Editor.Modules.Vrc3
+{
+ public static class ModuleVrc3Styles
+ {
+ private static GUIStyle _url;
+ private static GUIStyle _urlPro;
+
+ private static Texture2D _emojis;
+ private static Texture2D _option;
+ private static Texture2D _expressions;
+ private static Texture2D _noExpressions;
+ private static Texture2D _back;
+ private static Texture2D _backHome;
+ private static Texture2D _default;
+ private static Texture2D _reset;
+ private static Texture2D _twoAxis;
+ private static Texture2D _fourAxis;
+ private static Texture2D _radial;
+ private static Texture2D _toggle;
+ private static Texture2D _runningParam;
+ private static Texture2D _axisUp;
+ private static Texture2D _axisRight;
+ private static Texture2D _axisDown;
+ private static Texture2D _axisLeft;
+ private static Texture2D _supportLike;
+ private static Texture2D _supportHeart;
+
+ internal static GUIStyle Url => _url ?? (_url = new GUIStyle(GUI.skin.label) { padding = new RectOffset(-3, -3, 1, 0), normal = { textColor = Color.blue } });
+ internal static GUIStyle UrlPro => _urlPro ?? (_urlPro = new GUIStyle(GUI.skin.label) { padding = new RectOffset(-3, -3, 1, 0), normal = { textColor = Color.cyan } });
+
+ internal static Texture2D Emojis => _emojis ? _emojis : _emojis = Resources.Load<Texture2D>("Vrc3/BSX_GM_Emojis");
+ internal static Texture2D Option => _option ? _option : _option = Resources.Load<Texture2D>("Vrc3/BSX_GM_Option");
+ internal static Texture2D Expressions => _expressions ? _expressions : _expressions = Resources.Load<Texture2D>("Vrc3/BSX_GM_Expressions");
+ internal static Texture2D NoExpressions => _noExpressions ? _noExpressions : _noExpressions = Resources.Load<Texture2D>("Vrc3/BSX_GM_No_Expressions");
+ internal static Texture2D Back => _back ? _back : _back = Resources.Load<Texture2D>("Vrc3/BSX_GM_Back");
+ internal static Texture2D BackHome => _backHome ? _backHome : _backHome = Resources.Load<Texture2D>("Vrc3/BSX_GM_BackHome");
+ internal static Texture2D Default => _default ? _default : _default = Resources.Load<Texture2D>("Vrc3/BSX_GM_Default");
+ internal static Texture2D Reset => _reset ? _reset : _reset = Resources.Load<Texture2D>("Vrc3/BSX_GM_Reset");
+ internal static Texture2D TwoAxis => _twoAxis ? _twoAxis : _twoAxis = Resources.Load<Texture2D>("Vrc3/BSX_GM_2_Axis");
+ internal static Texture2D FourAxis => _fourAxis ? _fourAxis : _fourAxis = Resources.Load<Texture2D>("Vrc3/BSX_GM_4_Axis");
+ internal static Texture2D Radial => _radial ? _radial : _radial = Resources.Load<Texture2D>("Vrc3/BSX_GM_Radial");
+ internal static Texture2D Toggle => _toggle ? _toggle : _toggle = Resources.Load<Texture2D>("Vrc3/BSX_GM_Toggle");
+ internal static Texture2D RunningParam => _runningParam ? _runningParam : _runningParam = Resources.Load<Texture2D>("Vrc3/BSX_GM_Running_Param");
+ internal static Texture2D AxisUp => _axisUp ? _axisUp : _axisUp = Resources.Load<Texture2D>("Vrc3/BSX_GM_Axis_Up");
+ internal static Texture2D AxisRight => _axisRight ? _axisRight : _axisRight = Resources.Load<Texture2D>("Vrc3/BSX_GM_Axis_Right");
+ internal static Texture2D AxisDown => _axisDown ? _axisDown : _axisDown = Resources.Load<Texture2D>("Vrc3/BSX_GM_Axis_Down");
+ internal static Texture2D AxisLeft => _axisLeft ? _axisLeft : _axisLeft = Resources.Load<Texture2D>("Vrc3/BSX_GM_Axis_Left");
+ internal static Texture2D SupportLike => _supportLike ? _supportLike : _supportLike = Resources.Load<Texture2D>("Vrc3/BSX_GM_Support_Like");
+ internal static Texture2D SupportHeart => _supportHeart ? _supportHeart : _supportHeart = Resources.Load<Texture2D>("Vrc3/BSX_GM_Support_Heart");
+
+ public static class Data
+ {
+ private const string VrcSdk3ControllerPath = "Vrc3/Controllers/";
+ private const string VrcSdk3RestorePath = "Vrc3/Controllers/Restore/";
+
+ private static AnimatorController ControllerOfPath(string path) => Resources.Load<AnimatorController>(VrcSdk3ControllerPath + path);
+
+ private static TextAsset RestoreOfPath(string path) => Resources.Load<TextAsset>(VrcSdk3RestorePath + path);
+
+ internal static TextAsset RestoreOf(AnimLayerType type) => RestoreOfPath(NameOf[type]);
+
+ internal static AnimatorController ControllerOf(AnimLayerType type) => ControllerOfPath(NameOf[type]);
+
+ public static int LayerSort(VRCAvatarDescriptor.CustomAnimLayer x, VRCAvatarDescriptor.CustomAnimLayer y) => SortValue[x.type] - SortValue[y.type];
+
+ private static readonly Dictionary<AnimLayerType, string> NameOf = new Dictionary<AnimLayerType, string>
+ {
+ { AnimLayerType.FX, "GmgFxLayer" },
+ { AnimLayerType.Base, "GmgBaseLayer" },
+ { AnimLayerType.TPose, "GmgUtilityTPose" },
+ { AnimLayerType.Action, "GmgActionLayer" },
+ { AnimLayerType.IKPose, "GmgUtilityIKPose" },
+ { AnimLayerType.Gesture, "GmgGestureLayer" },
+ { AnimLayerType.Sitting, "GmgSittingLayer" },
+ { AnimLayerType.Additive, "GmgAdditiveLayer" }
+ };
+
+ internal static readonly Dictionary<BlendableAnimatorLayer, AnimLayerType> AnimatorToLayer = new Dictionary<BlendableAnimatorLayer, AnimLayerType>
+ {
+ { BlendableAnimatorLayer.FX, AnimLayerType.FX },
+ { BlendableAnimatorLayer.Action, AnimLayerType.Action },
+ { BlendableAnimatorLayer.Gesture, AnimLayerType.Gesture },
+ { BlendableAnimatorLayer.Additive, AnimLayerType.Additive }
+ };
+
+ internal static readonly Dictionary<BlendablePlayableLayer, AnimLayerType> PlayableToLayer = new Dictionary<BlendablePlayableLayer, AnimLayerType>
+ {
+ { BlendablePlayableLayer.FX, AnimLayerType.FX },
+ { BlendablePlayableLayer.Action, AnimLayerType.Action },
+ { BlendablePlayableLayer.Gesture, AnimLayerType.Gesture },
+ { BlendablePlayableLayer.Additive, AnimLayerType.Additive }
+ };
+
+ internal static readonly Dictionary<AnimLayerType, int> SortValue = new Dictionary<AnimLayerType, int>
+ {
+ { AnimLayerType.Base, 0 },
+ { AnimLayerType.Additive, 1 },
+ { AnimLayerType.Sitting, 2 },
+ { AnimLayerType.TPose, 3 },
+ { AnimLayerType.IKPose, 4 },
+ { AnimLayerType.Gesture, 5 },
+ { AnimLayerType.Action, 6 },
+ { AnimLayerType.FX, 7 }
+ };
+
+ internal static readonly Dictionary<AnimLayerType, AvatarMask> MaskOf = new Dictionary<AnimLayerType, AvatarMask>
+ {
+ { AnimLayerType.Base, null },
+ { AnimLayerType.Action, null },
+ { AnimLayerType.Sitting, null },
+ { AnimLayerType.Additive, null },
+ { AnimLayerType.FX, Masks.Empty },
+ { AnimLayerType.TPose, Masks.MuscleOnly },
+ { AnimLayerType.IKPose, Masks.MuscleOnly },
+ { AnimLayerType.Gesture, Masks.HandsOnly }
+ };
+
+ internal static readonly Dictionary<ValueType, AnimatorControllerParameterType> TypeOf = new Dictionary<ValueType, AnimatorControllerParameterType>
+ {
+ { ValueType.Int, AnimatorControllerParameterType.Int },
+ { ValueType.Bool, AnimatorControllerParameterType.Bool },
+ { ValueType.Float, AnimatorControllerParameterType.Float }
+ };
+
+ public static readonly AnimationClip[] GestureClips =
+ {
+ new AnimationClip { name = GestureManagerStyles.Data.GestureNames[0] },
+ new AnimationClip { name = GestureManagerStyles.Data.GestureNames[1] },
+ new AnimationClip { name = GestureManagerStyles.Data.GestureNames[2] },
+ new AnimationClip { name = GestureManagerStyles.Data.GestureNames[3] },
+ new AnimationClip { name = GestureManagerStyles.Data.GestureNames[4] },
+ new AnimationClip { name = GestureManagerStyles.Data.GestureNames[5] },
+ new AnimationClip { name = GestureManagerStyles.Data.GestureNames[6] },
+ new AnimationClip { name = GestureManagerStyles.Data.GestureNames[7] }
+ };
+
+ public static Dictionary<string, TrackingType> DefaultTrackingState => new Dictionary<string, TrackingType>
+ {
+ { "Head", TrackingType.Tracking },
+ { "Left Hand", TrackingType.Tracking },
+ { "Right Hand", TrackingType.Tracking },
+ { "Hip", TrackingType.Tracking },
+ { "Left Foot", TrackingType.Tracking },
+ { "Right Foot", TrackingType.Tracking },
+ { "Left Fingers", TrackingType.Tracking },
+ { "Right Fingers", TrackingType.Tracking },
+ { "Eye & Eyelid", TrackingType.Tracking },
+ { "Mouth & Jaw", TrackingType.Tracking }
+ };
+ }
+
+ private static class Masks
+ {
+ internal static AvatarMask Empty => GmgAvatarMaskHelper.CreateEmptyMask("Empty");
+
+ internal static AvatarMask MuscleOnly => GmgAvatarMaskHelper.CreateMaskWithout("MuscleOnly", new[]
+ {
+ AvatarMaskBodyPart.LeftFootIK,
+ AvatarMaskBodyPart.LeftHandIK,
+ AvatarMaskBodyPart.RightFootIK,
+ AvatarMaskBodyPart.RightHandIK
+ });
+
+ internal static AvatarMask HandsOnly => GmgAvatarMaskHelper.CreateMaskWith("HandsOnly", new[]
+ {
+ AvatarMaskBodyPart.LeftFingers,
+ AvatarMaskBodyPart.RightFingers
+ });
+ }
+ }
+}
+#endif \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/ModuleVrc3Styles.cs.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/ModuleVrc3Styles.cs.meta
new file mode 100644
index 00000000..905b8fbf
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/ModuleVrc3Styles.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 04abd51958a4483f82935267b5fbc77d
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl.meta
new file mode 100644
index 00000000..dff7240a
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 5e9a9bfa17894eefada1888bcf965456
+timeCreated: 1645206902 \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/EndpointControl.cs b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/EndpointControl.cs
new file mode 100644
index 00000000..6fbf2323
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/EndpointControl.cs
@@ -0,0 +1,94 @@
+#if VRC_SDK_VRCSDK3
+using System.Collections.Generic;
+using GestureManager.Scripts.Editor.Modules.Vrc3.OpenSoundControl.VisualElements;
+using UnityEngine;
+
+namespace GestureManager.Scripts.Editor.Modules.Vrc3.OpenSoundControl
+{
+ public class EndpointControl
+ {
+ internal readonly string HorizontalEndpoint;
+ internal readonly List<(float? min, object value, float? max)> Values = new List<(float? min, object value, float? max)>();
+
+ private readonly List<VisualEp> _visualElements;
+
+ public EndpointControl(string horizontalEndpoint, IList<EndpointControl> chronological)
+ {
+ chronological.Insert(0, this);
+
+ _visualElements = new List<VisualEp>();
+ HorizontalEndpoint = horizontalEndpoint;
+ }
+
+ internal static bool? BoolValue(object value)
+ {
+ switch (value)
+ {
+ case bool b: return b;
+ case float f: return f > 0.5f;
+ case int i: return i == 1;
+ default: return null;
+ }
+ }
+
+ internal static int? IntValue(object value)
+ {
+ switch (value)
+ {
+ case bool b: return b ? 1 : 0;
+ case float f: return (int)f;
+ case int i: return i;
+ default: return null;
+ }
+ }
+
+ internal static float? FloatValue(object value)
+ {
+ switch (value)
+ {
+ case bool b: return b ? 1 : 0;
+ case float f: return f;
+ case int i: return i;
+ default: return null;
+ }
+ }
+
+ public void OnMessage(OscPacket.Message message)
+ {
+ for (var i = 0; i < message.Arguments.Count; i++)
+ {
+ if (i < Values.Count) UpdateValue(i, message.Arguments[i]);
+ else Values.Add((null, message.Arguments[i], null));
+ }
+
+ foreach (var visual in _visualElements) visual.OnMessage(this);
+ }
+
+ private void UpdateValue(int i, object argument)
+ {
+ var vFloat = FloatValue(argument);
+ if (vFloat.HasValue) UpdateValue(i, vFloat.Value, argument);
+ else Values[i] = (Values[i].min, argument, Values[i].max);
+ }
+
+ private void UpdateValue(int i, float fValue, object value)
+ {
+ var vFloat = FloatValue(Values[i].value);
+ if (vFloat.HasValue && RadialMenuUtility.Is(vFloat.Value, fValue)) return;
+ Values[i] = (Mathf.Min(Values[i].min ?? vFloat ?? fValue, fValue, 0), value, Mathf.Max(Values[i].max ?? vFloat ?? fValue, fValue, 0));
+ }
+
+ public VisualEp New()
+ {
+ var visual = new VisualEp(this);
+ _visualElements.Add(visual);
+ return visual;
+ }
+
+ public void Clear()
+ {
+ foreach (var visual in _visualElements) visual.RemoveFromHierarchy();
+ }
+ }
+}
+#endif \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/EndpointControl.cs.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/EndpointControl.cs.meta
new file mode 100644
index 00000000..839fc86c
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/EndpointControl.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 2d960d661de24a9f8250f46e821ecd86
+timeCreated: 1646060105 \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/OscModule.cs b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/OscModule.cs
new file mode 100644
index 00000000..39a248dc
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/OscModule.cs
@@ -0,0 +1,436 @@
+#if VRC_SDK_VRCSDK3
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using GestureManager.Scripts.Core.Editor;
+using GestureManager.Scripts.Editor.Modules.Vrc3.OpenSoundControl.VisualElements;
+using GestureManager.Scripts.Editor.Modules.Vrc3.Params;
+using GestureManager.Scripts.Editor.Modules.Vrc3.Vrc3Debug.Avatar;
+using UnityEditor;
+using UnityEngine;
+using UnityEngine.UIElements;
+
+namespace GestureManager.Scripts.Editor.Modules.Vrc3.OpenSoundControl
+{
+ public class OscModule
+ {
+ private readonly ModuleVrc3 _module;
+ private const int SquareSize = VisualEpStyles.SquareSize;
+
+ private const string VrcReceiverAddress = "127.0.0.1";
+ private const int VrcListenerPort = 9000;
+ private const int VrcSenderPort = 9001;
+
+ internal GmgLayoutHelper.Toolbar ToolBar;
+ private readonly OscSettings _settings;
+
+ private UdpSender _sender;
+ private UdpListener _listener;
+ private readonly List<byte[]> _queue = new List<byte[]>();
+
+ private bool _useLocalConfiguration;
+
+ private bool _customSelection;
+ private string _customAddress = VrcReceiverAddress;
+ private int _customListener = VrcSenderPort;
+ private int _customSender = VrcListenerPort;
+ private (string address, int listenerPort, int senderPort)? _forgotData;
+
+ private IEnumerable<OscPacket.Message> Messages => _messages.Select(tuple => new OscPacket.Message(tuple.address, tuple.data));
+ private readonly List<(string address, List<object> data)> _messages = new List<(string address, List<object> data)>();
+ private bool _sendingBundle;
+ private int _addIndex = -1;
+ private ulong _timeTag = 1;
+ private string _dateTag = OscPacket.TimeTagToDateTime(1).ToString("F");
+
+ private static string VrcReceiverPortUsed => $"The VRChat default port {VrcListenerPort} is already in use.";
+ internal bool Enabled => _listener != null && _sender != null;
+
+ private readonly Dictionary<string, EndpointControl> _dataDictionary = new Dictionary<string, EndpointControl>();
+ private readonly List<EndpointControl> _chronological = new List<EndpointControl>();
+
+ public OscModule(ModuleVrc3 module)
+ {
+ _module = module;
+ _settings = new OscSettings(_module);
+ }
+
+ public void Update()
+ {
+ lock (_queue)
+ {
+ if (_queue.Count == 0) return;
+ foreach (var bytes in _queue) OnListenerBytes(bytes);
+ _queue.Clear();
+ }
+ }
+
+ public void ControlPanel()
+ {
+ if ((_listener != null || _sender != null) && !Enabled) Stop();
+
+ GUILayout.Label("Osc Debug", GestureManagerStyles.GuiHandTitle);
+ if (_module.DummyMode != null)
+ {
+ GUILayout.Space(28);
+ GUILayout.Label($"Osc Debug is not available while you are in {_module.DummyMode.ModeName}-Mode!", GestureManagerStyles.SubHeader);
+ GUILayout.Space(28);
+ }
+ else if (!Enabled)
+ {
+ GUILayout.Label("Osc Debug is disabled, start it with the button bellows!", GestureManagerStyles.SubHeader);
+
+ GUILayout.Space(15);
+ var isCustomMode = _customSelection && _settings.Stable;
+
+ using (new GUILayout.HorizontalScope())
+ {
+ if (isCustomMode)
+ {
+ GUILayout.FlexibleSpace();
+ using (new GUILayout.VerticalScope(EditorStyles.helpBox))
+ {
+ GUILayout.Label("Listening", GestureManagerStyles.GuiDebugTitle);
+ _customListener = EditorGUILayout.IntField(_customListener);
+ }
+
+ GUILayout.FlexibleSpace();
+ using (new GUILayout.VerticalScope(EditorStyles.helpBox))
+ {
+ GUILayout.Label("Sending", GestureManagerStyles.GuiDebugTitle);
+ using (new GUILayout.HorizontalScope())
+ {
+ _customAddress = EditorGUILayout.TextField(_customAddress);
+ _customSender = EditorGUILayout.IntField(_customSender);
+ }
+ }
+
+ GUILayout.FlexibleSpace();
+ using (new GUILayout.VerticalScope())
+ {
+ if (GUILayout.Button("Back!", GUILayout.Height(20))) _customSelection = false;
+ GUI.enabled = _customListener >= 0 && _customSender >= 0;
+ if (GmgLayoutHelper.Button("Start!", Color.green, GUILayout.Height(20))) Start(_customListener, _customAddress, _customSender);
+ GUI.enabled = true;
+ }
+
+ GUILayout.FlexibleSpace();
+ }
+ else
+ {
+ GUI.enabled = _settings.Stable;
+ GUILayout.FlexibleSpace();
+ if (GmgLayoutHelper.DebugButton("Start on VRChat ports")) Start(VrcListenerPort, VrcReceiverAddress, VrcSenderPort);
+ GUILayout.FlexibleSpace();
+ if (GmgLayoutHelper.DebugButton("Start on custom ports")) _customSelection = true;
+ GUILayout.FlexibleSpace();
+ GUI.enabled = true;
+ }
+ }
+
+ GUILayout.Space(isCustomMode ? -2 : 11);
+ }
+ else
+ {
+ GUILayout.Label("Osc Debug is running, you can stop it with the orange button!", GestureManagerStyles.SubHeader);
+
+ GUILayout.Space(4);
+ using (new GUILayout.HorizontalScope())
+ {
+ GUILayout.FlexibleSpace();
+ using (new GUILayout.VerticalScope(EditorStyles.helpBox))
+ {
+ GUILayout.Label("Listening To", GestureManagerStyles.GuiDebugTitle);
+ GUILayout.Label($"{_listener?.Port}", GestureManagerStyles.SubHeader);
+ }
+
+ GUILayout.FlexibleSpace();
+
+ using (new GUILayout.VerticalScope())
+ {
+ GUILayout.Space(8);
+ if (GmgLayoutHelper.Button("Stop", RadialMenuUtility.Colors.RestartButton, GUILayout.Height(30), GUILayout.Width(60))) Stop();
+ }
+
+ GUILayout.FlexibleSpace();
+
+ using (new GUILayout.VerticalScope(EditorStyles.helpBox))
+ {
+ GUILayout.Label("Sending To", GestureManagerStyles.GuiDebugTitle);
+ GUILayout.Label($"{_sender?.Port}", GestureManagerStyles.SubHeader);
+ }
+
+ GUILayout.FlexibleSpace();
+ }
+
+ GUILayout.Space(10);
+ }
+
+ GUILayout.Space(9);
+
+ using (new GUILayout.HorizontalScope())
+ {
+ GUILayout.FlexibleSpace();
+ if (GmgLayoutHelper.DebugButton(_module.DebugOscWindow ? Vrc3AvatarDebugWindow.Text.W.Button : Vrc3AvatarDebugWindow.Text.D.Button)) _module.SwitchDebugOscView();
+ GUILayout.FlexibleSpace();
+ }
+
+ GUILayout.Space(6);
+ }
+
+ private void Start(int listenerPort, string sendAddress, int senderPort)
+ {
+ if (!_settings.Setup()) return;
+
+ try
+ {
+ _listener = new UdpListener(listenerPort, AddQueue);
+ _sender = new UdpSender(sendAddress, senderPort);
+ _customSelection = false;
+ }
+ catch (Exception)
+ {
+ var messageString = listenerPort == VrcListenerPort ? VrcReceiverPortUsed : $"The port {listenerPort} is already in use.";
+ EditorUtility.DisplayDialog("Gesture Manager", messageString, ":c");
+ }
+ }
+
+ internal void Stop(bool clear = true)
+ {
+ _listener?.Close();
+ _listener = null;
+ _sender?.Close();
+ _sender = null;
+ if (!clear) return;
+ lock (_queue) _queue.Clear();
+ ClearElements();
+ }
+
+ private void ClearElements()
+ {
+ foreach (var endpointControl in _chronological) endpointControl.Clear();
+ _dataDictionary.Clear();
+ _chronological.Clear();
+ }
+
+ public void Forget()
+ {
+ _forgotData = (_sender.Address, _listener.Port, _sender.Port);
+ Stop(false);
+ }
+
+ public void Resume()
+ {
+ if (_forgotData == null) return;
+ Start(_forgotData.Value.listenerPort, _forgotData.Value.address, _forgotData.Value.senderPort);
+ _forgotData = null;
+ }
+
+ private void AddQueue(byte[] bytes)
+ {
+ lock (_queue) _queue.Add(bytes);
+ }
+
+ private void OnListenerBytes(byte[] bytes)
+ {
+ foreach (var message in OscPacket.GetMessages(bytes)) OnMessage(message);
+ }
+
+ public void DebugLayout(VisualElement root, VisualEpContainer holder, float width)
+ {
+ if (_listener == null || _sender == null) SettingsLayout();
+ else Layout(root, holder, width);
+ }
+
+ private void SettingsLayout()
+ {
+ if (!_settings.Loaded)
+ {
+ GUILayout.Label("Settings", GestureManagerStyles.Header);
+ GUILayout.Space(5);
+ GUILayout.Label("You can start the OSC debug right now and it will use wide settings!", GestureManagerStyles.SubHeader);
+ GUILayout.Space(10);
+ GUILayout.Label("Or you can load your OSC settings!", GestureManagerStyles.SubHeader);
+ GUILayout.Space(30);
+ using (new GUILayout.HorizontalScope())
+ {
+ GUILayout.FlexibleSpace();
+ if (GmgLayoutHelper.DebugButton("Load Settings")) _settings.Load();
+ GUILayout.FlexibleSpace();
+ }
+
+ GUILayout.Space(20);
+ }
+ else if (_settings.Layout()) _settings.Clean();
+ }
+
+ private void Layout(VisualElement root, VisualEpContainer holder, float width)
+ {
+ GmgLayoutHelper.MyToolbar(ref ToolBar, new (string, Action)[]
+ {
+ ("Receive", () => ReceiveLayout(root, holder, width)),
+ ("Send", SendLayout)
+ });
+ }
+
+ private void ReceiveLayout(VisualElement root, VisualEpContainer holder, float width)
+ {
+ holder.Render(root);
+ if (holder.parent != root) root.Add(holder);
+ GUILayout.Label("Received Osc Packet Messages", GestureManagerStyles.Header);
+
+ if (_chronological.Count != 0) GmgLayoutHelper.HorizontalGrid(holder, width, SquareSize, SquareSize, 5, _chronological, ShowData);
+ else NoDataLayout();
+ }
+
+ private static void ShowData(VisualEpContainer holder, Rect rect, EndpointControl element)
+ {
+ if (Event.current.type == EventType.Layout || Event.current.type == EventType.Used) return;
+
+ holder.RenderMessage(rect, element);
+ }
+
+ private void SendLayout()
+ {
+ if (GmgLayoutHelper.TitleButton($"Send Custom {(_sendingBundle ? "Bundle" : "Message")}", _sendingBundle ? "Message" : "Bundle")) _sendingBundle = !_sendingBundle;
+
+ if (_messages.Count == 0) AddMessage();
+ if (_sendingBundle) BundleLayout();
+ else SingleMessageLayout();
+
+ GUILayout.Space(15);
+ using (new GUILayout.HorizontalScope())
+ {
+ GUILayout.FlexibleSpace();
+ if (GmgLayoutHelper.DebugButton("Send")) _sender.Send(_sendingBundle ? new OscPacket(_timeTag, Messages.ToList()).GetBytes() : Messages.First().GetBytes());
+ GUILayout.FlexibleSpace();
+ }
+ }
+
+ private void AddMessage() => _messages.Add(("/avatar/parameters/", new List<object>()));
+
+ private void SingleMessageLayout()
+ {
+ using (new GUILayout.VerticalScope(EditorStyles.helpBox)) MessageLayout(0);
+ }
+
+ private void BundleLayout()
+ {
+ if (_timeTag != (_timeTag = GmgLayoutHelper.ULongField("TimeTag: ", _timeTag))) OnNewTimeTag();
+ GUILayout.Label(_dateTag, EditorStyles.helpBox);
+ for (var i = 0; i < _messages.Count; i++) MessageBundleLayout(ref i);
+ using (new GUILayout.VerticalScope(EditorStyles.helpBox))
+ if (GUILayout.Button("New Osc Message"))
+ AddMessage();
+ }
+
+ private void OnNewTimeTag() => _dateTag = OscPacket.TimeTagToDateTime(_timeTag).ToString("F");
+
+ private void MessageBundleLayout(ref int i)
+ {
+ using (new GUILayout.VerticalScope(EditorStyles.helpBox))
+ {
+ if (GmgLayoutHelper.TitleButton($"Osc Message [{i}]", "-", 20))
+ {
+ _messages.RemoveAt(i);
+ i--;
+ }
+ else MessageLayout(i);
+ }
+ }
+
+ private void MessageLayout(int mId)
+ {
+ var (address, data) = _messages[mId];
+
+ address = EditorGUILayout.TextField("Address: ", address);
+ for (var i = 0; i < data.Count; i++)
+ {
+ using (new GUILayout.HorizontalScope())
+ {
+ switch (data[i])
+ {
+ case bool boolObject:
+ data[i] = EditorGUILayout.Toggle("Bool: ", boolObject);
+ break;
+ case float floatObject:
+ data[i] = EditorGUILayout.FloatField("Float: ", floatObject);
+ break;
+ case char charObject:
+ data[i] = (char)EditorGUILayout.IntField("Char: ", charObject);
+ GUILayout.Label(charObject.ToString(), GUILayout.Width(50));
+ break;
+ case string stringObject:
+ data[i] = EditorGUILayout.TextField("String: ", stringObject);
+ break;
+ case int intObject:
+ data[i] = EditorGUILayout.IntField("Int: ", intObject);
+ break;
+ default:
+ data.RemoveAt(i);
+ i--;
+ continue;
+ }
+
+ if (!GUILayout.Button("-", GUILayout.Width(20))) continue;
+ data.RemoveAt(i);
+ i--;
+ }
+ }
+
+ using (new GUILayout.HorizontalScope())
+ {
+ if (_addIndex != mId)
+ {
+ EditorGUILayout.LabelField("", "");
+ if (GUILayout.Button("+", GUILayout.Width(20))) _addIndex = mId;
+ }
+ else
+ {
+ if (GUILayout.Button("Add Boolean")) Add(data, false);
+ if (GUILayout.Button("Add Integer")) Add(data, new int());
+ if (GUILayout.Button("Add Float")) Add(data, new float());
+ if (GUILayout.Button("Add String")) Add(data, "");
+ if (GUILayout.Button("Add Char")) Add(data, 'a');
+ if (GUILayout.Button("x", GUILayout.Width(20))) _addIndex = -1;
+ }
+ }
+
+ _messages[mId] = (address, data);
+ }
+
+ private void Add(ICollection<object> data, object value)
+ {
+ data.Add(value);
+ _addIndex = -1;
+ }
+
+ private void NoDataLayout() => ErrorLayout("No data received yet!", $"Is your application sending on port {_listener.Port}?");
+
+ private static void ErrorLayout(string red, string white)
+ {
+ GUILayout.FlexibleSpace();
+ GUILayout.Space(100);
+ GUILayout.Label(red, GestureManagerStyles.TextError);
+ GUILayout.Space(20);
+ GUILayout.Label(white, GestureManagerStyles.SubHeader);
+ GUILayout.Space(100);
+ GUILayout.FlexibleSpace();
+ }
+
+ private void OnMessage(OscPacket.Message message)
+ {
+ _settings.OnMessage(message);
+ if (!_dataDictionary.TryGetValue(message.Address, out var control)) _dataDictionary[message.Address] = control = new EndpointControl(message.Address, _chronological);
+ control.OnMessage(message);
+ }
+
+ public void OnParameterChange(Vrc3Param param, float value)
+ {
+ if (_sender == null) return;
+ var message = _settings.OnParameterChange(param, value);
+ if (message != null) _sender.Send(message.GetBytes());
+ }
+ }
+}
+#endif \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/OscModule.cs.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/OscModule.cs.meta
new file mode 100644
index 00000000..083e90e2
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/OscModule.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: ab4e312e7012410d974a788d091f9b55
+timeCreated: 1645206911 \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/OscPacket.cs b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/OscPacket.cs
new file mode 100644
index 00000000..39fcb089
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/OscPacket.cs
@@ -0,0 +1,384 @@
+#if VRC_SDK_VRCSDK3
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace GestureManager.Scripts.Editor.Modules.Vrc3.OpenSoundControl
+{
+ public class OscPacket
+ {
+ public static IEnumerable<Message> GetMessages(byte[] oscData) => oscData[0] == '#' ? ParseBundle(oscData)._messages : new[] { Message.ParseMessage(oscData) };
+
+ private readonly ulong _timeTag;
+ private readonly IList<Message> _messages;
+
+ internal OscPacket(ulong timeTag, IList<Message> messages)
+ {
+ _timeTag = timeTag;
+ _messages = messages;
+ }
+
+ public byte[] GetBytes()
+ {
+ const string bundle = "#bundle";
+ var intTagLen = AlignedStringLength(bundle);
+ var tag = SetULong(_timeTag);
+
+ var msgList = _messages.Select(message => message.GetBytes()).ToList();
+
+ var i = 0;
+ var output = new byte[intTagLen + tag.Length + msgList.Sum(x => x.Length + 4)];
+ Encoding.ASCII.GetBytes(bundle).CopyTo(output, i);
+ i += intTagLen;
+ tag.CopyTo(output, i);
+ i += tag.Length;
+
+ foreach (var msg in msgList)
+ {
+ var size = SetInt(msg.Length);
+ size.CopyTo(output, i);
+ i += size.Length;
+ msg.CopyTo(output, i);
+ i += msg.Length;
+ }
+
+ return output;
+ }
+
+ private static OscPacket ParseBundle(byte[] bundle)
+ {
+ var messages = new List<Message>();
+ var index = 0;
+ var bundleTagString = Encoding.ASCII.GetString(SubArray(bundle, ref index, 8));
+ var timeULong = GetULong(bundle, ref index);
+ if (bundleTagString != "#bundle\0") throw new Exception("Not a bundle");
+ while (index < bundle.Length) messages.Add(Message.ParseMessage(SubArray(bundle, ref index, GetInt(bundle, ref index))));
+ return new OscPacket(timeULong, messages);
+ }
+
+ private static string GetAddress(byte[] msg, ref int index)
+ {
+ int i;
+ for (i = index; i < msg.Length; i += 4)
+ {
+ if (msg[i] != ',') continue;
+ if (i == 0) return "";
+ break;
+ }
+
+ if (i >= msg.Length) throw new Exception("No comma found in OSC packet!");
+ var addressString = Encoding.ASCII.GetString(SubArray(msg, ref index, i));
+ return addressString.Replace("\0", "");
+ }
+
+ private static IEnumerable<char> GetTypes(byte[] msg, ref int index)
+ {
+ int i;
+ for (i = index + 4; i < msg.Length; i += 4)
+ if (msg[i - 1] == 0)
+ break;
+ return Encoding.ASCII.GetChars(SubArray(msg, ref index, i - index));
+ }
+
+ private static string GetString(byte[] msg, ref int index)
+ {
+ int i;
+ for (i = index + 4; i - 1 < msg.Length; i += 4)
+ if (msg[i - 1] == 0)
+ break;
+ return Encoding.ASCII.GetString(SubArray(msg, ref index, i - index)).Replace("\0", "");
+ }
+
+ private static int GetInt(IReadOnlyList<byte> msg, ref int index) => GetInternalInt(msg, index += 4);
+
+ private static int GetInternalInt(IReadOnlyList<byte> msg, int index) => (msg[index - 4] << 24) + (msg[index - 3] << 16) + (msg[index - 2] << 8) + (msg[index - 1] << 0);
+
+ private static byte[] GetBlob(byte[] msg, ref int index) => SubArray(msg, ref index, GetInt(msg, ref index));
+
+ private static ulong GetULong(IReadOnlyList<byte> msg, ref int index) => GetInternalULong(msg, index += 8);
+
+ private static ulong GetInternalULong(IReadOnlyList<byte> msg, int index) => ((ulong)msg[index - 8] << 56) + ((ulong)msg[index - 7] << 48) + ((ulong)msg[index - 6] << 40) + ((ulong)msg[index - 5] << 32) + ((ulong)msg[index - 4] << 24) + ((ulong)msg[index - 3] << 16) + ((ulong)msg[index - 2] << 8) + ((ulong)msg[index - 1] << 0);
+
+ private static float GetFloat(IReadOnlyList<byte> msg, ref int index)
+ {
+ var reversed = new byte[4];
+ reversed[3] = msg[index];
+ reversed[2] = msg[index + 1];
+ reversed[1] = msg[index + 2];
+ reversed[0] = msg[index + 3];
+ index += 4;
+ return BitConverter.ToSingle(reversed, 0);
+ }
+
+ private static long GetLong(IReadOnlyList<byte> msg, ref int index)
+ {
+ var var = new byte[8];
+ var[7] = msg[index];
+ var[6] = msg[index + 1];
+ var[5] = msg[index + 2];
+ var[4] = msg[index + 3];
+ var[3] = msg[index + 4];
+ var[2] = msg[index + 5];
+ var[1] = msg[index + 6];
+ var[0] = msg[index + 7];
+ index += 8;
+ return BitConverter.ToInt64(var, 0);
+ }
+
+ private static double GetDouble(IReadOnlyList<byte> msg, ref int index)
+ {
+ var var = new byte[8];
+ var[7] = msg[index];
+ var[6] = msg[index + 1];
+ var[5] = msg[index + 2];
+ var[4] = msg[index + 3];
+ var[3] = msg[index + 4];
+ var[2] = msg[index + 5];
+ var[1] = msg[index + 6];
+ var[0] = msg[index + 7];
+ index += 8;
+ return BitConverter.ToDouble(var, 0);
+ }
+
+ private static char GetChar(IReadOnlyList<byte> msg, ref int index) => (char)msg[(index += 4) - 1];
+
+ private static byte[] SetInt(int value) => BitConverter.GetBytes(value).Reverse().ToArray();
+
+ private static byte[] SetLong(long value) => BitConverter.GetBytes(value).Reverse().ToArray();
+
+ private static byte[] SetFloat(float value) => BitConverter.GetBytes(value).Reverse().ToArray();
+
+ private static byte[] SetULong(ulong value) => BitConverter.GetBytes(value).Reverse().ToArray();
+
+ private static byte[] SetDouble(double value) => BitConverter.GetBytes(value).Reverse().ToArray();
+
+ private static byte[] SetString(string value)
+ {
+ var intLen = value.Length + (4 - value.Length % 4);
+ if (intLen <= value.Length) intLen += 4;
+ var msg = new byte[intLen];
+ Encoding.ASCII.GetBytes(value).CopyTo(msg, 0);
+ return msg;
+ }
+
+ private static byte[] SetBlob(byte[] value)
+ {
+ var msg = new byte[value.Length + 4 + (4 - (value.Length + 4) % 4)];
+ SetInt(value.Length).CopyTo(msg, 0);
+ value.CopyTo(msg, 4);
+ return msg;
+ }
+
+ private static byte[] SetChar(char value)
+ {
+ var output = new byte[4];
+ output[0] = 0;
+ output[1] = 0;
+ output[2] = 0;
+ output[3] = (byte)value;
+ return output;
+ }
+
+ private static T[] SubArray<T>(T[] sourceArray, ref int sourceIndex, int length)
+ {
+ var destinationArray = new T[length];
+ Array.Copy(sourceArray, sourceIndex, destinationArray, 0, length);
+ sourceIndex += length;
+ return destinationArray;
+ }
+
+ private static int AlignedStringLength(string val)
+ {
+ var intLen = val.Length + (4 - val.Length % 4);
+ return intLen <= val.Length ? intLen + 4 : intLen;
+ }
+
+ public static DateTime TimeTagToDateTime(ulong val) => val == 1 ? DateTime.Now : DateTime.Parse("1900-01-01 00:00:00").AddSeconds((uint)(val >> 32)).AddSeconds(TimeTagToFraction(val));
+
+ private static double TimeTagToFraction(ulong val) => val == 1 ? 0.0 : (double)(uint)(val & 0x00000000FFFFFFFF) / 0xFFFFFFFF;
+
+ public class Message
+ {
+ public readonly string Address;
+ public readonly IList<object> Arguments;
+
+ public Message(string address, IList<object> arguments)
+ {
+ Address = address;
+ Arguments = arguments;
+ }
+
+ public Message(string address, object arg) : this(address, new[] { arg })
+ {
+ }
+
+ public byte[] GetBytes()
+ {
+ var lists = new List<(IList<object>, int)>();
+
+ var parts = new List<byte[]>();
+ var currentList = Arguments;
+
+ var typeString = ",";
+ var i = 0;
+ while (i < currentList.Count)
+ {
+ switch (currentList[i])
+ {
+ case null:
+ typeString += "N";
+ break;
+ case int intArg:
+ typeString += "i";
+ parts.Add(SetInt(intArg));
+ break;
+ case string stringArg:
+ typeString += "s";
+ parts.Add(SetString(stringArg));
+ break;
+ case byte[] byteArg:
+ typeString += "b";
+ parts.Add(SetBlob(byteArg));
+ break;
+ case long longArg:
+ typeString += "h";
+ parts.Add(SetLong(longArg));
+ break;
+ case ulong ulongArg:
+ typeString += "t";
+ parts.Add(SetULong(ulongArg));
+ break;
+ case char charArg:
+ typeString += "c";
+ parts.Add(SetChar(charArg));
+ break;
+ case bool boolArg:
+ typeString += boolArg ? "T" : "F";
+ break;
+ case float floatArg:
+ var isFloatInfinity = float.IsPositiveInfinity(floatArg);
+ if (!isFloatInfinity) parts.Add(SetFloat(floatArg));
+ typeString += !isFloatInfinity ? "f" : "I";
+ break;
+ case double doubleArg:
+ var isDoubleInfinity = double.IsPositiveInfinity(doubleArg);
+ if (!isDoubleInfinity) parts.Add(SetDouble(doubleArg));
+ typeString += !isDoubleInfinity ? "d" : "I";
+ break;
+ case IList<object> objectArray:
+ typeString += "[";
+ lists.Add((currentList, i));
+ currentList = objectArray;
+ i = 0;
+ continue;
+ default: throw new Exception("Unable to transmit values of type " + currentList[i].GetType());
+ }
+
+ i++;
+ if (ReferenceEquals(currentList, Arguments) || i != currentList.Count) continue;
+
+ var intIndex = lists.Count - 1;
+ typeString += "]";
+ currentList = lists[intIndex].Item1;
+ i = lists[intIndex].Item2 + 1;
+ lists.RemoveAt(intIndex);
+ }
+
+ var addressLen = string.IsNullOrEmpty(Address) ? 0 : AlignedStringLength(Address);
+ var typeLen = AlignedStringLength(typeString);
+ var output = new byte[addressLen + typeLen + parts.Sum(x => x.Length)];
+
+ i = 0;
+ Encoding.ASCII.GetBytes(Address).CopyTo(output, i);
+ i += addressLen;
+ Encoding.ASCII.GetBytes(typeString).CopyTo(output, i);
+ i += typeLen;
+
+ foreach (var part in parts)
+ {
+ part.CopyTo(output, i);
+ i += part.Length;
+ }
+
+ return output;
+ }
+
+ public static Message ParseMessage(byte[] msg)
+ {
+ var lists = new List<List<object>>();
+ var arguments = new List<object>();
+
+ var index = 0;
+ var addressString = GetAddress(msg, ref index);
+ var charEnumerable = GetTypes(msg, ref index);
+
+ var commaParsed = false;
+
+ foreach (var charType in charEnumerable)
+ {
+ if (charType == ',' && !commaParsed)
+ {
+ commaParsed = true;
+ continue;
+ }
+
+ switch (charType)
+ {
+ case '\0':
+ break;
+ case 'i':
+ arguments.Add(GetInt(msg, ref index));
+ break;
+ case 'f':
+ arguments.Add(GetFloat(msg, ref index));
+ break;
+ case 's':
+ arguments.Add(GetString(msg, ref index));
+ break;
+ case 'b':
+ arguments.Add(GetBlob(msg, ref index));
+ while (index % 4 != 0) index++;
+ break;
+ case 'h':
+ arguments.Add(GetLong(msg, ref index));
+ break;
+ case 'd':
+ arguments.Add(GetDouble(msg, ref index));
+ break;
+ case 'c':
+ arguments.Add(GetChar(msg, ref index));
+ break;
+ case 'T':
+ arguments.Add(true);
+ break;
+ case 'F':
+ arguments.Add(false);
+ break;
+ case 'N':
+ arguments.Add(null);
+ break;
+ case 'I':
+ arguments.Add(double.PositiveInfinity);
+ break;
+ case '[':
+ lists.Add(arguments);
+ arguments = new List<object>();
+ break;
+ case ']':
+ var intIndex = lists.Count - 1;
+ var upList = lists[intIndex];
+ upList.Add(arguments);
+ lists.RemoveAt(intIndex);
+ arguments = upList;
+ break;
+ default: throw new Exception($"OSC type tag '{charType}' is unknown.");
+ }
+ }
+
+ return new Message(addressString, arguments);
+ }
+ }
+ }
+}
+#endif \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/OscPacket.cs.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/OscPacket.cs.meta
new file mode 100644
index 00000000..9a25d3e6
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/OscPacket.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: ac9a23bafff44f2b817791681400b379
+timeCreated: 1645208413 \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/OscSettings.cs b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/OscSettings.cs
new file mode 100644
index 00000000..a32ec741
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/OscSettings.cs
@@ -0,0 +1,257 @@
+#if VRC_SDK_VRCSDK3
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using GestureManager.Scripts.Core.Editor;
+using GestureManager.Scripts.Editor.Modules.Vrc3.Params;
+using UnityEditor;
+using UnityEngine;
+using VRC.Core;
+
+namespace GestureManager.Scripts.Editor.Modules.Vrc3.OpenSoundControl
+{
+ public class OscSettings
+ {
+ private readonly ModuleVrc3 _module;
+
+ private PipelineManager Pipeline => _module.Avatar.GetComponent<PipelineManager>();
+ private static string LocalLow => Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "Low";
+ private static string VrcDirectory => Path.Combine(LocalLow, "VRChat", "VRChat");
+ private static string OscDirectory => Path.Combine(VrcDirectory, "OSC");
+
+ public bool Stable => !Loaded || _fileProblem == null && _userProblem == null && _fileExist;
+
+ public bool Loaded;
+ private InputOutputData _data;
+
+ private string _filePathString;
+ private string _fileNameString;
+
+ private string[] _users;
+ private int _selectedUser;
+
+ private string _userProblem;
+ private string _fileProblem;
+
+ private bool _fileExist;
+
+ public OscSettings(ModuleVrc3 module)
+ {
+ _module = module;
+ }
+
+ public void Load()
+ {
+ Loaded = true;
+ _userProblem = TryLoadUsers();
+ if (_userProblem == null) LoadUser();
+ }
+
+ private void LoadUser() => _fileProblem = TryLoadFile();
+
+ public void Clean() => Loaded = false;
+
+ private string TryLoadUsers()
+ {
+ if (string.IsNullOrEmpty(Pipeline.blueprintId)) return "The current avatar doesn't have a blueprint ID.";
+ if (!Directory.Exists(VrcDirectory)) return "VRChat's directory cannot be found.";
+ if (!Directory.Exists(OscDirectory)) return "VRChat's OSC directory cannot be found.";
+ _users = Directory.GetDirectories(OscDirectory).Select(Path.GetFileName).ToArray();
+ return _users.Length == 0 ? "No users found in VRChat's OSC Directory." : null;
+ }
+
+ private string TryLoadFile()
+ {
+ _fileNameString = Pipeline.blueprintId + ".json";
+ _filePathString = Path.Combine(OscDirectory, _users[_selectedUser], "Avatars", _fileNameString);
+ return !File.Exists(_filePathString) ? "OSC settings for the avatar cannot be found in this user folder." : null;
+ }
+
+ public bool Layout()
+ {
+ if (GmgLayoutHelper.TitleButton("File Settings", "X", 20)) return true;
+ if (_userProblem != null) UserProblemLayout();
+ else UserLayout();
+ return false;
+ }
+
+ private void UserLayout()
+ {
+ if (_selectedUser != (_selectedUser = EditorGUILayout.Popup("User: ", _selectedUser, _users))) LoadUser();
+ if (_fileProblem != null) FileProblemLayout();
+ else IntegrityLayout();
+ }
+
+ private void IntegrityLayout()
+ {
+ _fileExist = File.Exists(_filePathString);
+ if (_fileExist) FileLayout();
+ else UserProblemLayout(30, "The file has been deleted...", 10);
+ }
+
+ private void FileLayout()
+ {
+ GUILayout.Label("Loaded File", GestureManagerStyles.Header);
+ using (new GUILayout.VerticalScope(EditorStyles.helpBox))
+ {
+ GUILayout.Space(10);
+ GUILayout.Label($"{_fileNameString}", GestureManagerStyles.SubHeader);
+ GUILayout.Space(10);
+ using (new GUILayout.HorizontalScope())
+ {
+ GUILayout.FlexibleSpace();
+ if (GmgLayoutHelper.DebugButton("Open in text editor!")) EditorUtility.OpenWithDefaultApp(_filePathString);
+ GUILayout.FlexibleSpace();
+ if (GmgLayoutHelper.DebugButton("Show in Explorer")) EditorUtility.RevealInFinder(_filePathString);
+ GUILayout.FlexibleSpace();
+ }
+
+ GUILayout.Space(15);
+ }
+ }
+
+ private void UserProblemLayout() => UserProblemLayout(20, _userProblem, 20);
+
+ private void FileProblemLayout() => UserProblemLayout(30, _fileProblem, 10);
+
+ private static void UserProblemLayout(int pixelLeft, string text, int pixelRight)
+ {
+ GUILayout.Space(pixelLeft);
+ GUILayout.Label(text, GestureManagerStyles.TextError);
+ GUILayout.Space(pixelRight);
+ }
+
+ /*
+ * Input & Outputs
+ */
+
+ public void OnMessage(OscPacket.Message message)
+ {
+ if (_data.Input.TryGetValue(message.Address, out var tuple)) tuple.execute(message.Arguments);
+ }
+
+ public OscPacket.Message OnParameterChange(Vrc3Param param, float value)
+ {
+ return !_data.Output.TryGetValue(param.Name, out var tuple) ? null : Message(tuple.address, tuple.type, value);
+ }
+
+ /*
+ * Configuration
+ */
+
+ public bool Setup()
+ {
+ _data = new InputOutputData();
+ return Loaded ? CustomSetup() : DefaultSetup();
+ }
+
+ private bool DefaultSetup()
+ {
+ foreach (var pair in _module.Params) _data.Input[$"/avatar/parameters/{pair.Key}"] = (pair.Value.Type, dataList => Execute(pair.Value, pair.Value.Type, dataList[0]));
+ foreach (var pair in _module.Params) _data.Output[pair.Key] = ($"/avatar/parameters/{pair.Key}", pair.Value.Type);
+ return true;
+ }
+
+ private bool CustomSetup()
+ {
+ try
+ {
+ return CustomSetup(JsonUtility.FromJson<OscFile>(File.ReadAllText(_filePathString)));
+ }
+ catch
+ {
+ const string message = "Failed to parse Json data.\n\nCheck if your file contains invalid Json data.";
+ EditorUtility.DisplayDialog("Json Problem", message, "Ok");
+ return false;
+ }
+ }
+
+ private bool CustomSetup(OscFile file)
+ {
+ foreach (var parameter in file.InputParameters) _data.Input[parameter.input.address] = (parameter.input.Type, dataList => Execute(_module.GetParam(parameter.name), parameter.input.Type, dataList[0]));
+ foreach (var parameter in file.OutputParameters) _data.Output[parameter.name] = (parameter.output.address, parameter.output.Type);
+ return true;
+ }
+
+ private void Execute(Vrc3Param param, AnimatorControllerParameterType type, object value)
+ {
+ if (param == null) return;
+
+ switch (type)
+ {
+ case AnimatorControllerParameterType.Float:
+ param.Set(_module, EndpointControl.FloatValue(value));
+ break;
+ case AnimatorControllerParameterType.Int:
+ param.Set(_module, EndpointControl.IntValue(value));
+ break;
+ case AnimatorControllerParameterType.Bool:
+ case AnimatorControllerParameterType.Trigger:
+ param.Set(_module, EndpointControl.BoolValue(value));
+ break;
+ default: throw new ArgumentOutOfRangeException(nameof(type), type, null);
+ }
+ }
+
+ private static OscPacket.Message Message(string address, AnimatorControllerParameterType type, float value)
+ {
+ switch (type)
+ {
+ case AnimatorControllerParameterType.Float:
+ return new OscPacket.Message(address, value);
+ case AnimatorControllerParameterType.Int:
+ return new OscPacket.Message(address, (int)value);
+ case AnimatorControllerParameterType.Bool:
+ case AnimatorControllerParameterType.Trigger:
+ return new OscPacket.Message(address, value > 0.5);
+ default: throw new ArgumentOutOfRangeException();
+ }
+ }
+
+ private class InputOutputData
+ {
+ internal readonly Dictionary<string, (AnimatorControllerParameterType type, Action<IList<object>> execute)> Input = new Dictionary<string, (AnimatorControllerParameterType type, Action<IList<object>> execute)>();
+ internal readonly Dictionary<string, (string address, AnimatorControllerParameterType type)> Output = new Dictionary<string, (string address, AnimatorControllerParameterType type)>();
+ }
+
+ [Serializable]
+ public class OscFile
+ {
+ public Parameter[] parameters;
+
+ public IEnumerable<Parameter> InputParameters => parameters.Where(parameter => parameter.input.address != null).ToList();
+ public IEnumerable<Parameter> OutputParameters => parameters.Where(parameter => parameter.output.address != null).ToList();
+ }
+
+ [Serializable]
+ public struct Parameter
+ {
+ public string name;
+ public Data input;
+ public Data output;
+ }
+
+ [Serializable]
+ public struct Data
+ {
+ public string address;
+ public string type;
+
+ private AnimatorControllerParameterType? _type;
+ internal AnimatorControllerParameterType Type => (_type ?? (_type = FetchType())).Value;
+
+ public AnimatorControllerParameterType FetchType()
+ {
+ switch (type)
+ {
+ case "Int": return AnimatorControllerParameterType.Int;
+ case "Bool": return AnimatorControllerParameterType.Bool;
+ case "Float": return AnimatorControllerParameterType.Float;
+ default: return AnimatorControllerParameterType.Float;
+ }
+ }
+ }
+ }
+}
+#endif \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/OscSettings.cs.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/OscSettings.cs.meta
new file mode 100644
index 00000000..670af1ea
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/OscSettings.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: fdd4be90de4841ac964884d31dbda0b7
+timeCreated: 1646680507 \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/UdpListener.cs b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/UdpListener.cs
new file mode 100644
index 00000000..a89aa716
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/UdpListener.cs
@@ -0,0 +1,60 @@
+#if VRC_SDK_VRCSDK3
+using System;
+using System.Net;
+using System.Net.Sockets;
+
+namespace GestureManager.Scripts.Editor.Modules.Vrc3.OpenSoundControl
+{
+ public class UdpListener
+ {
+ internal int Port { get; }
+
+ private readonly UdpClient _listener;
+ private IPEndPoint _remoteIpEndPoint;
+
+ private bool _closing;
+
+ private readonly Action<byte[]> _onData;
+
+ public UdpListener(int port, Action<byte[]> callback)
+ {
+ Port = port;
+ _onData = callback;
+ _listener = new UdpClient(port);
+ _remoteIpEndPoint = new IPEndPoint(IPAddress.Any, port);
+ _listener?.BeginReceive(ReceiveCallback, null);
+ }
+
+ private void ReceiveCallback(IAsyncResult result)
+ {
+ try
+ {
+ OnData(_listener.EndReceive(result, ref _remoteIpEndPoint));
+ }
+ catch (ObjectDisposedException)
+ {
+ }
+
+ if (!_closing) _listener.BeginReceive(ReceiveCallback, null);
+ }
+
+ private void OnData(byte[] endReceive)
+ {
+ try
+ {
+ _onData(endReceive);
+ }
+ catch (Exception)
+ {
+ // ignored
+ }
+ }
+
+ public void Close()
+ {
+ _closing = true;
+ _listener.Close();
+ }
+ }
+}
+#endif \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/UdpListener.cs.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/UdpListener.cs.meta
new file mode 100644
index 00000000..717cda93
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/UdpListener.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: a785367341e043ae97a0388cbdd4217d
+timeCreated: 1645209912 \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/UdpSender.cs b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/UdpSender.cs
new file mode 100644
index 00000000..22e64738
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/UdpSender.cs
@@ -0,0 +1,33 @@
+#if VRC_SDK_VRCSDK3
+using System.Linq;
+using System.Net;
+using System.Net.Sockets;
+
+namespace GestureManager.Scripts.Editor.Modules.Vrc3.OpenSoundControl
+{
+ public class UdpSender
+ {
+ public string Address { get; }
+ public int Port { get; }
+
+ private readonly IPEndPoint _remoteIpEndPoint;
+ private readonly Socket _socket;
+
+ public UdpSender(string address, int port) : this(Dns.GetHostAddresses(address).FirstOrDefault(), port)
+ {
+ Address = address;
+ }
+
+ private UdpSender(IPAddress addressDns, int port)
+ {
+ Port = port;
+ _socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
+ _remoteIpEndPoint = new IPEndPoint(addressDns, port);
+ }
+
+ public void Send(byte[] bytes) => _socket.SendTo(bytes, _remoteIpEndPoint);
+
+ public void Close() => _socket.Close();
+ }
+}
+#endif \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/UdpSender.cs.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/UdpSender.cs.meta
new file mode 100644
index 00000000..46b5a3d3
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/UdpSender.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 823597fbe2764ef28b10155a77b4ce93
+timeCreated: 1645208066 \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/VisualElements.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/VisualElements.meta
new file mode 100644
index 00000000..aac43f31
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/VisualElements.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 848c47a64a084f428ace470c3f2d4b58
+timeCreated: 1646318352 \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/VisualElements/VisualEp.cs b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/VisualElements/VisualEp.cs
new file mode 100644
index 00000000..ec060b2b
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/VisualElements/VisualEp.cs
@@ -0,0 +1,117 @@
+#if VRC_SDK_VRCSDK3
+using UnityEngine;
+using UnityEngine.UIElements;
+using GestureManager.Scripts.Core.Editor;
+using UIEPosition = UnityEngine.UIElements.Position;
+
+namespace GestureManager.Scripts.Editor.Modules.Vrc3.OpenSoundControl.VisualElements
+{
+ public class VisualEp : VisualElement
+ {
+ private readonly VisualElement _dataHolder;
+ private readonly VisualElement _dataBorder;
+
+ internal VisualEp(EndpointControl endpoint)
+ {
+ VisualElement center;
+ TextElement horizontal;
+
+ style.position = UIEPosition.Absolute;
+ pickingMode = PickingMode.Ignore;
+
+ Add(center = VisualEpStyles.Center);
+ center.Add(_dataHolder = VisualEpStyles.Holder);
+ center.Add(_dataBorder = VisualEpStyles.Outline);
+
+ Add(VisualEpStyles.VerticalText);
+ Add(horizontal = VisualEpStyles.HorizontalText);
+ horizontal.text = endpoint.HorizontalEndpoint;
+ OnMessage(endpoint);
+ }
+
+ public void OnMessage(EndpointControl control)
+ {
+ _dataBorder.experimental.animation.Start(1f, 0.5f, 500, Animation);
+ if (control.Values.Count > _dataHolder.childCount) SetValueCount(control.Values.Count);
+ for (var i = 0; i < control.Values.Count; i++) (_dataHolder[i] as Data)?.SetValue(control.Values[i]);
+ }
+
+ private void SetValueCount(int count)
+ {
+ var childHeight = (float)VisualEpStyles.InnerSize / count;
+ for (var intLeft = _dataHolder.childCount; intLeft < count; intLeft++) _dataHolder.Add(new Data());
+ foreach (var element in _dataHolder.Children()) (element as Data)?.SetHeight(childHeight);
+ }
+
+ private static void Animation(VisualElement e, float val)
+ {
+ var color = new Color(val, val, val, 1f);
+ var width = 2 * val;
+ e.style.borderBottomColor = e.style.borderLeftColor = e.style.borderRightColor = e.style.borderTopColor = color;
+ e.style.borderBottomWidth = e.style.borderLeftWidth = e.style.borderRightWidth = e.style.borderTopWidth = width;
+ }
+
+ private class Data : VisualElement
+ {
+ private readonly VisualElement _bar;
+
+ private readonly TextElement _min;
+ private readonly TextElement _value;
+ private readonly TextElement _max;
+
+ internal Data()
+ {
+ style.width = VisualEpStyles.InnerSize;
+
+ Add(_bar = new VisualElement { style = { backgroundColor = VisualEpStyles.BgColor } }.MyBorder(1, VisualEpStyles.Radius, Color.clear));
+ Add(_min = Create(TextAnchor.MiddleLeft));
+ Add(_value = Create(TextAnchor.MiddleCenter));
+ Add(_max = Create(TextAnchor.MiddleRight));
+ }
+
+ private static TextElement Create(TextAnchor anchor) => new TextElement
+ {
+ style = { color = Color.white, position = UIEPosition.Absolute, fontSize = 10, width = VisualEpStyles.InnerSize - 8, marginLeft = 3, unityTextAlign = anchor }
+ };
+
+ public void SetHeight(float childHeight)
+ {
+ style.height = childHeight;
+ _min.style.height = childHeight;
+ _max.style.height = childHeight;
+ _bar.style.height = childHeight;
+ _value.style.height = childHeight;
+ }
+
+ public void SetValue((float? min, object value, float? max) value) => SetValue(value.min, value.value, value.max, EndpointControl.FloatValue(value.value));
+
+ private void SetValue(float? min, object value, float? max, float? vFloat)
+ {
+ _min.visible = min.HasValue;
+ _max.visible = max.HasValue;
+ _bar.visible = vFloat.HasValue;
+ if (min.HasValue) _min.text = min.Value.ToString("F");
+ if (max.HasValue) _max.text = max.Value.ToString("F");
+ _value.text = $"{value}";
+ if (!vFloat.HasValue) return;
+ if (min.HasValue && max.HasValue && !RadialMenuUtility.Is(min.Value, max.Value)) SetValue(min.Value, vFloat.Value, max.Value);
+ else SetValue(vFloat.Value > 0.5);
+ }
+
+ private void SetValue(float min, float value, float max)
+ {
+ var dis = max - min;
+ var width = Mathf.Abs(value) / dis * VisualEpStyles.InnerSize;
+ _bar.style.width = width;
+ _bar.style.right = min / dis * VisualEpStyles.InnerSize;
+ if (value < 0f) _bar.style.right = _bar.style.right.value.value + width;
+ }
+
+ private void SetValue(bool value)
+ {
+ _bar.style.width = value ? VisualEpStyles.InnerSize : 0f;
+ }
+ }
+ }
+}
+#endif \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/VisualElements/VisualEp.cs.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/VisualElements/VisualEp.cs.meta
new file mode 100644
index 00000000..3352822f
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/VisualElements/VisualEp.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 8e3ab87bce6f4cf5bd34e914cb699062
+timeCreated: 1646318374 \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/VisualElements/VisualEpContainer.cs b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/VisualElements/VisualEpContainer.cs
new file mode 100644
index 00000000..7edba739
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/VisualElements/VisualEpContainer.cs
@@ -0,0 +1,33 @@
+#if VRC_SDK_VRCSDK3
+using UnityEngine;
+using UnityEngine.UIElements;
+using System.Collections.Generic;
+using UIEPosition = UnityEngine.UIElements.Position;
+
+namespace GestureManager.Scripts.Editor.Modules.Vrc3.OpenSoundControl.VisualElements
+{
+ public class VisualEpContainer : Vrc3VisualRender
+ {
+ private readonly Dictionary<EndpointControl, VisualElement> _elements = new Dictionary<EndpointControl, VisualElement>();
+
+ public VisualEpContainer() => style.position = UIEPosition.Absolute;
+
+ protected override bool RenderCondition(ModuleVrc3 module, RadialMenu menu)
+ {
+ return menu.ToolBar.Selected == 2 && menu.DebugToolBar.Selected == 1 && module.OscModule.Enabled && !module.DebugOscWindow && module.OscModule.ToolBar.Selected == 0;
+ }
+
+ public void Render(VisualElement root) => Render(root, new Rect(0, style.top.value.value, 0, 0));
+
+ public void RenderMessage(Rect rect, EndpointControl endpoint)
+ {
+ if (!_elements.TryGetValue(endpoint, out var messageElement)) messageElement = _elements[endpoint] = endpoint.New();
+ if (messageElement.parent != this) Add(messageElement);
+ messageElement.style.height = rect.height;
+ messageElement.style.width = rect.width;
+ messageElement.style.left = rect.x;
+ messageElement.style.top = rect.y;
+ }
+ }
+}
+#endif \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/VisualElements/VisualEpContainer.cs.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/VisualElements/VisualEpContainer.cs.meta
new file mode 100644
index 00000000..d7c4761e
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/VisualElements/VisualEpContainer.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 1b96b92dd3414287893946471bb8f830
+timeCreated: 1645988829 \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/VisualElements/VisualEpStyles.cs b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/VisualElements/VisualEpStyles.cs
new file mode 100644
index 00000000..124ffe35
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/VisualElements/VisualEpStyles.cs
@@ -0,0 +1,77 @@
+#if VRC_SDK_VRCSDK3
+using UnityEngine;
+using UnityEngine.UIElements;
+using GestureManager.Scripts.Core.Editor;
+using UIEPosition = UnityEngine.UIElements.Position;
+
+namespace GestureManager.Scripts.Editor.Modules.Vrc3.OpenSoundControl.VisualElements
+{
+ public static class VisualEpStyles
+ {
+ private const int TextHeight = 20;
+ private const int TextMargin = 5;
+
+ internal const int Radius = 5;
+ internal const int SquareSize = 200;
+ internal const int InnerSize = SquareSize - TextHeight - TextHeight - TextMargin - TextMargin;
+
+ public static StyleColor BgColor = new Color(0.21f, 0.52f, 0.57f);
+
+ internal static VisualElement Center => new VisualElement
+ {
+ pickingMode = PickingMode.Ignore,
+ style = { height = InnerSize, width = InnerSize, left = TextHeight + TextMargin }
+ };
+
+ public static VisualElement Holder => new VisualElement
+ {
+ style =
+ {
+ width = InnerSize,
+ height = InnerSize,
+ backgroundColor = Color.black
+ }
+ }.MyBorder(0, Radius, Color.clear);
+
+ public static VisualElement Outline => new VisualElement
+ {
+ style =
+ {
+ position = UIEPosition.Absolute,
+ width = InnerSize,
+ height = InnerSize
+ }
+ }.MyBorder(1, Radius, Color.gray);
+
+ internal static TextElement HorizontalText => new TextElement
+ {
+ pickingMode = PickingMode.Ignore,
+ style =
+ {
+ color = Color.white,
+ unityTextAlign = TextAnchor.MiddleLeft,
+ right = -TextHeight - TextMargin,
+ top = -TextHeight + TextMargin,
+ backgroundColor = Color.black,
+ overflow = Overflow.Hidden,
+ height = TextHeight,
+ width = InnerSize,
+ fontSize = 10
+ }
+ }.MyBorder(1, 5, Color.black);
+
+ internal static TextElement VerticalText
+ {
+ get
+ {
+ var element = HorizontalText;
+ element.transform.rotation = Quaternion.Euler(0, 0, 90);
+ element.style.right = -TextHeight;
+ element.style.top = -InnerSize;
+ element.visible = false;
+ return element;
+ }
+ }
+ }
+}
+#endif \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/VisualElements/VisualEpStyles.cs.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/VisualElements/VisualEpStyles.cs.meta
new file mode 100644
index 00000000..74b15b46
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/VisualElements/VisualEpStyles.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 3c1322d4fccd4107adee3eca43e78cf1
+timeCreated: 1646318397 \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Params.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Params.meta
new file mode 100644
index 00000000..415be404
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Params.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: d240f43ce6d44ca58038545fc8dbc76e
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Params/Vrc3Param.cs b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Params/Vrc3Param.cs
new file mode 100644
index 00000000..aaf93684
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Params/Vrc3Param.cs
@@ -0,0 +1,151 @@
+#if VRC_SDK_VRCSDK3
+using System;
+using GestureManager.Scripts.Core.Editor;
+using UnityEditor;
+using UnityEngine;
+
+namespace GestureManager.Scripts.Editor.Modules.Vrc3.Params
+{
+ public abstract class Vrc3Param
+ {
+ public readonly AnimatorControllerParameterType Type;
+ private readonly Func<float, float> _converted;
+ protected readonly int HashId;
+ public readonly string Name;
+
+ internal Action<Vrc3Param, float> OnChange;
+
+ protected Vrc3Param(string name, AnimatorControllerParameterType type)
+ {
+ Name = name;
+ Type = type;
+ HashId = Animator.StringToHash(Name);
+ _converted = GenerateConverter();
+ }
+
+ public void Set(ModuleVrc3 module, float value)
+ {
+ if (Is(value = _converted(value))) return;
+ module.OscModule.OnParameterChange(this, value);
+ InternalSet(value);
+ OnChange?.Invoke(this, value);
+ foreach (var menu in module.Radials) menu.UpdateValue(Name, value);
+ }
+
+ private void Set(ModuleVrc3 module, bool value) => Set(module, value ? 1f : 0f);
+
+ private void Set(ModuleVrc3 module, int value) => Set(module, (float)value);
+
+ public void Set(ModuleVrc3 module, float? value)
+ {
+ if (value.HasValue) Set(module, value.Value);
+ }
+
+ public void Set(ModuleVrc3 module, bool? value)
+ {
+ if (value.HasValue) Set(module, value.Value);
+ }
+
+ public void SetOnChange(Action<Vrc3Param, float> onChange) => OnChange = onChange;
+
+ private bool Is(float value) => RadialMenuUtility.Is(Get(), value);
+
+ public abstract float Get();
+
+ protected internal abstract void InternalSet(float value);
+
+ public void Add(ModuleVrc3 module, float value) => Set(module, Get() + value);
+
+ public void Copy(ModuleVrc3 module, float get, bool range, float sourceMin, float sourceMax, float destMin, float destMax)
+ {
+ if (range) get = RangeOf(get, sourceMin, sourceMax, destMin, destMax);
+ switch (Type)
+ {
+ case AnimatorControllerParameterType.Float:
+ Set(module, get);
+ break;
+ case AnimatorControllerParameterType.Int:
+ Set(module, (int)get);
+ break;
+ case AnimatorControllerParameterType.Bool:
+ case AnimatorControllerParameterType.Trigger:
+ Set(module, !RadialMenuUtility.Is(get, 0));
+ break;
+ default: throw new ArgumentOutOfRangeException();
+ }
+ }
+
+ private static float RangeOf(float get, float sourceMin, float sourceMax, float destMin, float destMax) => RangeOf(get - sourceMin, sourceMax - sourceMin, destMin, destMax - destMin);
+
+ private static float RangeOf(float offset, float sourceLen, float destMin, float destLen) => destMin + destLen * (sourceLen != 0 ? Mathf.Clamp01(offset / sourceLen) : 0.0f);
+
+ public void Random(ModuleVrc3 module, float min, float max, float chance)
+ {
+ switch (Type)
+ {
+ case AnimatorControllerParameterType.Float:
+ Set(module, UnityEngine.Random.Range(min, max));
+ break;
+ case AnimatorControllerParameterType.Int:
+ Set(module, UnityEngine.Random.Range((int)min, (int)max + 1));
+ break;
+ case AnimatorControllerParameterType.Bool:
+ case AnimatorControllerParameterType.Trigger:
+ Set(module, UnityEngine.Random.Range(0.0f, 1.0f) < chance);
+ break;
+ default: throw new ArgumentOutOfRangeException();
+ }
+ }
+
+ public (Color? color, string text) LabelTuple()
+ {
+ switch (Type)
+ {
+ case AnimatorControllerParameterType.Float:
+ return (null, Get().ToString("0.00"));
+ case AnimatorControllerParameterType.Int:
+ return (null, ((int)Get()).ToString());
+ case AnimatorControllerParameterType.Bool:
+ case AnimatorControllerParameterType.Trigger:
+ return Get() > 0.5f ? (Color.green, "True") : (Color.red, "False");
+ default: throw new ArgumentOutOfRangeException();
+ }
+ }
+
+ public void FieldTuple(ModuleVrc3 module)
+ {
+ var rect = GUILayoutUtility.GetRect(new GUIContent(), GUI.skin.label);
+ switch (Type)
+ {
+ case AnimatorControllerParameterType.Float:
+ if (GmgLayoutHelper.UnityFieldEnterListener(Get(), module, rect, EditorGUI.FloatField, Set, module.Edit)) module.Edit = null;
+ break;
+ case AnimatorControllerParameterType.Int:
+ if (GmgLayoutHelper.UnityFieldEnterListener((int)Get(), module, rect, EditorGUI.IntField, Set, module.Edit)) module.Edit = null;
+ break;
+ case AnimatorControllerParameterType.Bool:
+ case AnimatorControllerParameterType.Trigger:
+ Set(module, Get() < 0.5f);
+ module.Edit = null;
+ break;
+ default: throw new ArgumentOutOfRangeException();
+ }
+ }
+
+ private Func<float, float> GenerateConverter()
+ {
+ switch (Type)
+ {
+ case AnimatorControllerParameterType.Float:
+ return value => value;
+ case AnimatorControllerParameterType.Int:
+ return value => (int)value;
+ case AnimatorControllerParameterType.Bool:
+ case AnimatorControllerParameterType.Trigger:
+ return value => value > 0.5f ? 1f : 0f;
+ default: throw new ArgumentOutOfRangeException();
+ }
+ }
+ }
+}
+#endif \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Params/Vrc3Param.cs.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Params/Vrc3Param.cs.meta
new file mode 100644
index 00000000..e4a038b1
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Params/Vrc3Param.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 55a6b9fce6f54a81b80ebbe074f8437a
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Params/Vrc3ParamBool.cs b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Params/Vrc3ParamBool.cs
new file mode 100644
index 00000000..e478c0d8
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Params/Vrc3ParamBool.cs
@@ -0,0 +1,27 @@
+#if VRC_SDK_VRCSDK3
+using System;
+using UnityEngine;
+
+namespace GestureManager.Scripts.Editor.Modules.Vrc3.Params
+{
+ public class Vrc3ParamBool : Vrc3Param
+ {
+ public bool State;
+
+ public Vrc3ParamBool(Action<bool> onChange = null) : base(null, AnimatorControllerParameterType.Bool)
+ {
+ if (onChange != null) SetOnChange((param, f) => onChange(f > 0.5f));
+ }
+
+ public override float Get() => State ? 1f : 0f;
+
+ public void ShutDown()
+ {
+ State = false;
+ OnChange(this, 0f);
+ }
+
+ protected internal override void InternalSet(float value) => State = value > 0.5f;
+ }
+}
+#endif \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Params/Vrc3ParamBool.cs.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Params/Vrc3ParamBool.cs.meta
new file mode 100644
index 00000000..695ba6ac
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Params/Vrc3ParamBool.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: df12737e26f946fd92b17f35b3b274be
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Params/Vrc3ParamController.cs b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Params/Vrc3ParamController.cs
new file mode 100644
index 00000000..a4664b88
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Params/Vrc3ParamController.cs
@@ -0,0 +1,56 @@
+#if VRC_SDK_VRCSDK3
+using System;
+using UnityEngine;
+using UnityEngine.Animations;
+using UnityEngine.Playables;
+
+namespace GestureManager.Scripts.Editor.Modules.Vrc3.Params
+{
+ public class Vrc3ParamController : Vrc3Param
+ {
+ private AnimatorControllerPlayable _controller;
+
+ private readonly Animator _animator;
+
+ public Vrc3ParamController(AnimatorControllerParameterType type, string name, AnimatorControllerPlayable controller, Animator animator) : base(name, type)
+ {
+ _animator = animator;
+ _controller = controller;
+ }
+
+ public override float Get()
+ {
+ if (!_controller.IsValid()) return 0;
+ switch (Type)
+ {
+ case AnimatorControllerParameterType.Float:
+ return _controller.GetFloat(Name);
+ case AnimatorControllerParameterType.Int:
+ return _controller.GetInteger(Name);
+ case AnimatorControllerParameterType.Trigger:
+ case AnimatorControllerParameterType.Bool:
+ return _controller.GetBool(Name) ? 1f : 0f;
+ default: throw new ArgumentOutOfRangeException();
+ }
+ }
+
+ protected internal override void InternalSet(float value)
+ {
+ switch (Type)
+ {
+ case AnimatorControllerParameterType.Float:
+ _animator.SetFloat(HashId, value);
+ break;
+ case AnimatorControllerParameterType.Int:
+ _animator.SetInteger(HashId, (int)value);
+ break;
+ case AnimatorControllerParameterType.Trigger:
+ case AnimatorControllerParameterType.Bool:
+ _animator.SetBool(HashId, value > 0.5f);
+ break;
+ default: throw new ArgumentOutOfRangeException();
+ }
+ }
+ }
+}
+#endif \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Params/Vrc3ParamController.cs.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Params/Vrc3ParamController.cs.meta
new file mode 100644
index 00000000..33c800f6
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Params/Vrc3ParamController.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 7c09918f51634b6197238c2f7d17fb0d
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Params/Vrc3ParamExternal.cs b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Params/Vrc3ParamExternal.cs
new file mode 100644
index 00000000..3455b8a8
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Params/Vrc3ParamExternal.cs
@@ -0,0 +1,39 @@
+#if VRC_SDK_VRCSDK3
+using System;
+using UnityEngine;
+
+namespace GestureManager.Scripts.Editor.Modules.Vrc3.Params
+{
+ public class Vrc3ParamExternal : Vrc3Param
+ {
+ private float _value;
+
+ public Vrc3ParamExternal(string name, AnimatorControllerParameterType type) : base(name, type)
+ {
+ }
+
+ public override float Get()
+ {
+ return _value;
+ }
+
+ protected internal override void InternalSet(float value)
+ {
+ switch (Type)
+ {
+ case AnimatorControllerParameterType.Float:
+ _value = value;
+ break;
+ case AnimatorControllerParameterType.Int:
+ _value = (int)value;
+ break;
+ case AnimatorControllerParameterType.Bool:
+ case AnimatorControllerParameterType.Trigger:
+ _value = value > 0.5f ? 1f : 0f;
+ break;
+ default: throw new ArgumentOutOfRangeException();
+ }
+ }
+ }
+}
+#endif \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Params/Vrc3ParamExternal.cs.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Params/Vrc3ParamExternal.cs.meta
new file mode 100644
index 00000000..ecd885ed
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Params/Vrc3ParamExternal.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: c05efb5946774ec3a313331d530468ff
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialButtons.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialButtons.meta
new file mode 100644
index 00000000..82b79f86
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialButtons.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: e8b392f04a934a859bfbb8f2e26c81c0
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialButtons/Dynamics.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialButtons/Dynamics.meta
new file mode 100644
index 00000000..2bd9621e
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialButtons/Dynamics.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 6ef22cb636bc434aa7a916b690b4784d
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialButtons/Dynamics/VisualRadialElement.cs b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialButtons/Dynamics/VisualRadialElement.cs
new file mode 100644
index 00000000..19a7e944
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialButtons/Dynamics/VisualRadialElement.cs
@@ -0,0 +1,22 @@
+#if VRC_SDK_VRCSDK3
+using UnityEngine.UIElements;
+
+namespace GestureManager.Scripts.Editor.Modules.Vrc3.RadialButtons.Dynamics
+{
+ public class VisualRadialElement : VisualElement
+ {
+ private readonly TextElement _text;
+
+ public float Value
+ {
+ set => _text.text = RadialMenuUtility.RadialPercentage(value) + "%";
+ }
+
+ public VisualRadialElement(float value)
+ {
+ RadialMenuUtility.Prefabs.SetRadialText(this, out _text, 15);
+ Value = value;
+ }
+ }
+}
+#endif \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialButtons/Dynamics/VisualRadialElement.cs.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialButtons/Dynamics/VisualRadialElement.cs.meta
new file mode 100644
index 00000000..37b4892a
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialButtons/Dynamics/VisualRadialElement.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: dbee3333ac874b50aef06e21d0f002b5
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialButtons/Dynamics/VisualRunningElement.cs b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialButtons/Dynamics/VisualRunningElement.cs
new file mode 100644
index 00000000..f56bb5b0
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialButtons/Dynamics/VisualRunningElement.cs
@@ -0,0 +1,43 @@
+#if VRC_SDK_VRCSDK3
+using UnityEngine;
+using UnityEngine.UIElements;
+using UIEPosition = UnityEngine.UIElements.Position;
+
+namespace GestureManager.Scripts.Editor.Modules.Vrc3.RadialButtons.Dynamics
+{
+ public class VisualRunningElement : VisualElement
+ {
+ public VisualRunningElement(bool active)
+ {
+ pickingMode = PickingMode.Ignore;
+ style.left = 25;
+ style.top = 25;
+
+ Add(new VisualElement
+ {
+ pickingMode = PickingMode.Ignore,
+ style =
+ {
+ width = 50,
+ height = 50,
+ left = -25,
+ top = -25,
+ position = UIEPosition.Absolute,
+ backgroundImage = ModuleVrc3Styles.RunningParam
+ }
+ });
+
+ visible = active;
+
+ experimental.animation.Start(25f, 200f, int.MaxValue, Animation);
+ }
+
+ private void Animation(VisualElement element, float val)
+ {
+ var eulerVector = transform.rotation.eulerAngles;
+ eulerVector.z += 2f;
+ transform.rotation = Quaternion.Euler(eulerVector);
+ }
+ }
+}
+#endif \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialButtons/Dynamics/VisualRunningElement.cs.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialButtons/Dynamics/VisualRunningElement.cs.meta
new file mode 100644
index 00000000..59797959
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialButtons/Dynamics/VisualRunningElement.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 1d6da016592f2654491ceda1f05951d1
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialButtons/RadialMenuButton.cs b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialButtons/RadialMenuButton.cs
new file mode 100644
index 00000000..c91112bd
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialButtons/RadialMenuButton.cs
@@ -0,0 +1,31 @@
+#if VRC_SDK_VRCSDK3
+using System;
+using UnityEngine;
+
+namespace GestureManager.Scripts.Editor.Modules.Vrc3.RadialButtons
+{
+ public class RadialMenuButton : RadialMenuItem
+ {
+ private readonly Action _onClick;
+
+ public RadialMenuButton(Action onClick, string text, Texture2D icon, Color? color = null) : base(text, icon, null)
+ {
+ _onClick = onClick ?? (() => { });
+ TextColor = color ?? Color.white;
+ }
+
+ protected override void CreateExtra()
+ {
+ }
+
+ public override void OnClickStart()
+ {
+ }
+
+ public override void OnClickEnd()
+ {
+ _onClick();
+ }
+ }
+}
+#endif \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialButtons/RadialMenuButton.cs.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialButtons/RadialMenuButton.cs.meta
new file mode 100644
index 00000000..b63032ca
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialButtons/RadialMenuButton.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 63f4c7c2f8ea4bba90b9c1ef52b24718
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialButtons/RadialMenuControl.cs b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialButtons/RadialMenuControl.cs
new file mode 100644
index 00000000..029f40b4
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialButtons/RadialMenuControl.cs
@@ -0,0 +1,110 @@
+#if VRC_SDK_VRCSDK3
+using System;
+using System.Linq;
+using GestureManager.Scripts.Editor.Modules.Vrc3.Params;
+using GestureManager.Scripts.Editor.Modules.Vrc3.RadialPuppets;
+using UnityEngine;
+using VRC.SDK3.Avatars.ScriptableObjects;
+using ControlType = VRC.SDK3.Avatars.ScriptableObjects.VRCExpressionsMenu.Control.ControlType;
+
+namespace GestureManager.Scripts.Editor.Modules.Vrc3.RadialButtons
+{
+ public class RadialMenuControl : RadialMenuDynamic
+ {
+ private readonly float? _amplify;
+ private readonly RadialMenu _menu;
+ private readonly Vrc3Param _parameter;
+ private readonly Vrc3Param[] _subParameters;
+ private readonly VRCExpressionsMenu _subMenu;
+ private readonly VRCExpressionsMenu.Control.Label[] _subLabels;
+
+ public RadialMenuControl(RadialMenu menu, VRCExpressionsMenu.Control control) : base(control.name, control.icon, control.type, control.value)
+ {
+ _menu = menu;
+ _subMenu = control.subMenu;
+ _subLabels = control.labels;
+ _parameter = menu.GetParam(control.parameter.name);
+ _subParameters = control.subParameters == null ? Array.Empty<Vrc3Param>() : control.subParameters.Select(parameter => menu.GetParam(parameter.name)).ToArray();
+ }
+
+ public RadialMenuControl(RadialMenu menu, string name, Texture2D icon, ControlType type, float activeValue, Vrc3Param param, Vrc3Param[] subParams, VRCExpressionsMenu subMenu, VRCExpressionsMenu.Control.Label[] subLabels, float? amplify = null) : base(name, icon, type, activeValue)
+ {
+ _menu = menu;
+ _amplify = amplify;
+ _subMenu = subMenu;
+ _parameter = param;
+ _subLabels = subLabels;
+ _subParameters = subParams;
+ if (_parameter == null && _subParameters.All(vrc3Param => vrc3Param == null)) TextColor = Color.gray;
+ }
+
+ public override void OnClickStart()
+ {
+ switch (Type)
+ {
+ case ControlType.Button:
+ _menu.PressingButton?.OnClickEnd();
+ _menu.PressingButton = this;
+ SetControlValue();
+ break;
+ case ControlType.Toggle:
+ case ControlType.SubMenu:
+ case ControlType.TwoAxisPuppet:
+ case ControlType.FourAxisPuppet:
+ case ControlType.RadialPuppet:
+ break;
+ default: throw new ArgumentOutOfRangeException();
+ }
+ }
+
+ public override void OnClickEnd()
+ {
+ switch (Type)
+ {
+ case ControlType.Button:
+ if (_menu.PressingButton == this) _menu.PressingButton = null;
+ else return;
+ SetValue(0);
+ break;
+ case ControlType.Toggle:
+ if (RadialMenuUtility.Is(GetValue(), ActiveValue)) SetValue(0);
+ else SetControlValue();
+ break;
+ case ControlType.SubMenu:
+ if (!_subMenu) break;
+ SetControlValue();
+ _menu.OpenMenu(_subMenu, _parameter, ActiveValue);
+ break;
+ case ControlType.TwoAxisPuppet:
+ _menu.OpenPuppet(new TwoAxisPuppet(this));
+ break;
+ case ControlType.FourAxisPuppet:
+ _menu.OpenPuppet(new FourAxisPuppet(this));
+ break;
+ case ControlType.RadialPuppet:
+ _menu.OpenPuppet(new RadialPuppet(this));
+ break;
+ default: throw new ArgumentOutOfRangeException();
+ }
+ }
+
+ private float AmplifiedValue(float value) => _amplify.HasValue ? value * _amplify.Value : value;
+
+ internal float NonAmplifiedValue(float value) => _amplify.HasValue ? value / _amplify.Value : value;
+
+ internal void SetControlValue() => SetValue(ActiveValue);
+
+ internal void SetValue(float value) => _parameter?.Set(_menu.Module, AmplifiedValue(value));
+
+ internal void SetSubValue(int index, float value) => _subParameters[index]?.Set(_menu.Module, AmplifiedValue(value));
+
+ public VRCExpressionsMenu.Control.Label[] GetSubLabels() => _subLabels;
+
+ internal string GetSubParameterName(int index) => _subParameters[index].Name;
+
+ protected override float GetValue() => NonAmplifiedValue(_parameter?.Get() ?? 0);
+
+ internal override float GetSubValue(int index) => NonAmplifiedValue(_subParameters[index]?.Get() ?? 0);
+ }
+}
+#endif \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialButtons/RadialMenuControl.cs.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialButtons/RadialMenuControl.cs.meta
new file mode 100644
index 00000000..3e7bb79f
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialButtons/RadialMenuControl.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 9f78dde7d35c4e31b9ed6093664f067a
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialButtons/RadialMenuDynamic.cs b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialButtons/RadialMenuDynamic.cs
new file mode 100644
index 00000000..9846fc9b
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialButtons/RadialMenuDynamic.cs
@@ -0,0 +1,94 @@
+#if VRC_SDK_VRCSDK3
+using System;
+using GestureManager.Scripts.Core.Editor;
+using GestureManager.Scripts.Editor.Modules.Vrc3.RadialButtons.Dynamics;
+using UnityEngine;
+using UnityEngine.UIElements;
+using ControlType = VRC.SDK3.Avatars.ScriptableObjects.VRCExpressionsMenu.Control.ControlType;
+
+namespace GestureManager.Scripts.Editor.Modules.Vrc3.RadialButtons
+{
+ public abstract class RadialMenuDynamic : RadialMenuItem
+ {
+ internal readonly ControlType Type;
+ internal readonly float ActiveValue;
+
+ private readonly bool _instance;
+ private VisualElement _dynamicElement;
+ private readonly DynamicType _dynamicType;
+
+ private bool RunCheck => RadialMenuUtility.Is(GetValue(), ActiveValue);
+
+ internal RadialMenuDynamic(string name, Texture2D icon, ControlType type, float activeValue) : base(name, icon, RadialMenuUtility.GetSubIcon(type))
+ {
+ Type = type;
+ ActiveValue = activeValue;
+ _dynamicType = GetDynamicType();
+ _instance = _dynamicType != DynamicType.None;
+ }
+
+ private DynamicType GetDynamicType()
+ {
+ switch (Type)
+ {
+ case ControlType.Button:
+ case ControlType.Toggle:
+ return DynamicType.Running;
+ case ControlType.RadialPuppet:
+ return DynamicType.Radial;
+ case ControlType.SubMenu:
+ case ControlType.TwoAxisPuppet:
+ case ControlType.FourAxisPuppet:
+ return DynamicType.None;
+ default: throw new ArgumentOutOfRangeException(nameof(Type), Type, null);
+ }
+ }
+
+ protected override void CreateExtra()
+ {
+ if (!_instance) return;
+ _dynamicElement = InstanceElement();
+ }
+
+ internal void CheckRunningUpdate()
+ {
+ if (!_instance) return;
+
+ switch (_dynamicElement)
+ {
+ case VisualRunningElement runningElement:
+ runningElement.visible = RunCheck;
+ break;
+ case VisualRadialElement sliderElement:
+ sliderElement.Value = GetSubValue(0);
+ break;
+ }
+ }
+
+ private VisualElement InstanceElement()
+ {
+ switch (_dynamicType)
+ {
+ case DynamicType.None:
+ return null;
+ case DynamicType.Running:
+ return Texture.MyAdd(new VisualRunningElement(RunCheck));
+ case DynamicType.Radial:
+ return Texture.MyAdd(new VisualRadialElement(GetSubValue(0)));
+ default: throw new ArgumentOutOfRangeException();
+ }
+ }
+
+ protected abstract float GetValue();
+
+ internal abstract float GetSubValue(int index);
+
+ private enum DynamicType
+ {
+ None,
+ Running,
+ Radial
+ }
+ }
+}
+#endif \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialButtons/RadialMenuDynamic.cs.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialButtons/RadialMenuDynamic.cs.meta
new file mode 100644
index 00000000..6d371c20
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialButtons/RadialMenuDynamic.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 63a6f7e24bc744fdac27cd0b3094e3c7
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialButtons/RadialMenuItem.cs b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialButtons/RadialMenuItem.cs
new file mode 100644
index 00000000..1d968010
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialButtons/RadialMenuItem.cs
@@ -0,0 +1,47 @@
+#if VRC_SDK_VRCSDK3
+using GestureManager.Scripts.Core.Editor;
+using GestureManager.Scripts.Core.VisualElements;
+using UnityEngine;
+using UnityEngine.UIElements;
+using UIEPosition = UnityEngine.UIElements.Position;
+
+namespace GestureManager.Scripts.Editor.Modules.Vrc3.RadialButtons
+{
+ public abstract class RadialMenuItem
+ {
+ internal Color TextColor;
+ internal Color SelectedBorderColor = RadialMenuUtility.Colors.CustomSelected;
+ internal Color SelectedCenterColor = RadialMenuUtility.Colors.RadialSelColor;
+
+ public VisualElement DataHolder;
+ protected VisualElement Texture;
+
+ private readonly string _text;
+ private readonly Texture2D _texture;
+ private readonly Texture2D _subIcon;
+
+ protected RadialMenuItem(string text, Texture2D icon, Texture2D subIcon)
+ {
+ _text = text;
+ _texture = icon ? icon : ModuleVrc3Styles.Default;
+ _subIcon = subIcon;
+ TextColor = Color.white;
+ }
+
+ public void Create()
+ {
+ DataHolder = RadialMenuUtility.Prefabs.NewData(100, 100);
+ Texture = DataHolder.MyAdd(new VisualElement { pickingMode = PickingMode.Ignore, style = { width = 50, height = 50, backgroundImage = _texture } });
+ if (_subIcon) DataHolder.Add(RadialMenuUtility.Prefabs.NewSubIcon(_subIcon));
+ DataHolder.MyAdd(new GmgTmpRichTextElement { pickingMode = PickingMode.Ignore, Text = _text, style = { color = TextColor, unityTextAlign = TextAnchor.MiddleCenter } });
+ CreateExtra();
+ }
+
+ protected abstract void CreateExtra();
+
+ public abstract void OnClickStart();
+
+ public abstract void OnClickEnd();
+ }
+}
+#endif \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialButtons/RadialMenuItem.cs.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialButtons/RadialMenuItem.cs.meta
new file mode 100644
index 00000000..e6db8766
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialButtons/RadialMenuItem.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: e13f4e3520704fb1b69c4971a2c2ed6e
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialCursor.cs b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialCursor.cs
new file mode 100644
index 00000000..c1617467
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialCursor.cs
@@ -0,0 +1,157 @@
+#if VRC_SDK_VRCSDK3
+using System.Collections.Generic;
+using GestureManager.Scripts.Core.Editor;
+using GestureManager.Scripts.Core.VisualElements;
+using UnityEngine;
+using UnityEngine.UIElements;
+
+namespace GestureManager.Scripts.Editor.Modules.Vrc3
+{
+ public class RadialCursor : GmgCircleElement
+ {
+ private const int CursorSize = 50;
+
+ private float _clampReset;
+ private float _clamp;
+ private float _min;
+ private float _max;
+
+ private Vector2 _position;
+ internal int Selection = -1;
+
+ public RadialCursor()
+ {
+ RadialMenuUtility.Prefabs.SetCircle(this, CursorSize, RadialMenuUtility.Colors.Cursor, RadialMenuUtility.Colors.CursorBorder)
+ .MyAdd(RadialMenuUtility.Prefabs.NewCircleBorder((int)(CursorSize / 1.5f), RadialMenuUtility.Colors.CursorBorder))
+ .Add(RadialMenuUtility.Prefabs.NewCircleBorder((int)(CursorSize / 4f), RadialMenuUtility.Colors.CursorBorder));
+ }
+
+ /*
+ * Data
+ */
+
+ private void SetParent(VisualElement referenceLayout)
+ {
+ parent.Remove(this);
+ referenceLayout.Add(this);
+ }
+
+ public void SetData(float clamp, float clampReset, float min, float max, VisualElement referenceLayout)
+ {
+ _min = min;
+ _max = max;
+ _clamp = clamp;
+ _clampReset = clampReset;
+ if (referenceLayout != null && referenceLayout != parent) SetParent(referenceLayout);
+ }
+
+ public void Update(Vector2 mouse, IList<GmgButton> selectionTuple, bool puppet)
+ {
+ if (mouse.magnitude < _clampReset) SetCursorPosition(mouse);
+ else SetCursorPosition(0, 0);
+ if (selectionTuple == null) return;
+ var intSelection = GetChoice(selectionTuple.Count, puppet);
+ if (Selection != intSelection) UpdateSelection(selectionTuple, intSelection);
+ }
+
+ private void UpdateSelection(IList<GmgButton> selectionTuple, int selection)
+ {
+ if (Selection != -1) Des(selectionTuple[Selection]);
+ if (selection != -1) Sel(selectionTuple[selection]);
+ Selection = selection;
+ }
+
+ private static void Des(GmgButton oldElement)
+ {
+ oldElement.Data.experimental.animation.Scale(1f, 100);
+ oldElement.Data.experimental.animation.TopLeft(RadialMenuUtility.DataVector, 100);
+ oldElement.CircleElement.CenterColor = RadialMenuUtility.Colors.RadialCenter;
+ oldElement.CircleElement.VertexColor = RadialMenuUtility.Colors.CustomMain;
+ }
+
+ internal static void Sel(GmgButton newElement, bool instant = false, float scale = 0.10f)
+ {
+ var topLeftVector = RadialMenuUtility.DataVector + RadialMenuUtility.DataVector * scale;
+
+ newElement.Data.experimental.animation.Scale(1f + scale, 100);
+ newElement.Data.experimental.animation.TopLeft(topLeftVector, instant ? 0 : 100);
+ newElement.CircleElement.CenterColor = newElement.Button.SelectedCenterColor;
+ newElement.CircleElement.VertexColor = newElement.Button.SelectedBorderColor;
+ }
+
+ /*
+ * Static
+ */
+
+ private static float GetAngle(Vector2 mouse)
+ {
+ return -Mathf.Atan2(mouse.x, mouse.y) * 180f / Mathf.PI + 180f;
+ }
+
+ private static bool Get2Axis(Vector2 mouse, float range, out Vector2 axis)
+ {
+ axis = Vector2.zero;
+ if (Event.current.type == EventType.Layout) return false;
+
+ axis = Vector2.ClampMagnitude(new Vector2(mouse.x / range, -mouse.y / range), 1);
+ return true;
+ }
+
+ private static bool Get4Axis(Vector2 mouse, float range, out Vector4 axis)
+ {
+ axis = Vector4.zero;
+ if (!Get2Axis(mouse, range, out var twoAxis)) return false;
+
+ axis.x = Mathf.Max(twoAxis.y, 0);
+ axis.y = Mathf.Max(twoAxis.x, 0);
+ axis.z = Mathf.Max(-twoAxis.y, 0);
+ axis.w = Mathf.Max(-twoAxis.x, 0);
+
+ return true;
+ }
+
+ private static bool GetRadial(Vector2 mouse, float min, out float radial)
+ {
+ radial = -1;
+ if (Event.current.type == EventType.Layout) return false;
+
+ if (mouse.magnitude < min) return false;
+
+ radial = GetAngle(mouse) / 360f;
+ return true;
+ }
+
+ private static int GetChoice(Vector2 mouse, float min, float max, int elements)
+ {
+ if (mouse.magnitude < min || mouse.magnitude > max) return -1;
+ return (int)((GetAngle(mouse) + 180f / elements) % 360 / (360f / elements));
+ }
+
+ /*
+ * Listeners
+ */
+
+ internal int GetChoice(int elements, bool puppet) => puppet ? -1 : GetChoice(_position, _min, _max, elements);
+
+ internal bool GetRadial(float min, out float radial) => GetRadial(_position, min, out radial);
+
+ internal bool Get2Axis(float range, out Vector2 axis) => Get2Axis(_position, range, out axis);
+
+ internal bool Get4Axis(float range, out Vector4 axis) => Get4Axis(_position, range, out axis);
+
+ /*
+ * Cursor Position
+ */
+
+ private void SetCursorPosition(float x, float y) => SetCursorPosition(new Vector2(x, y));
+
+ private void SetCursorPosition(Vector2 position)
+ {
+ position = Vector2.ClampMagnitude(position, _clamp);
+ style.left = position.x;
+ style.top = position.y;
+ _position = new Vector2(style.left.value.value, style.top.value.value);
+ }
+ }
+}
+#endif \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialCursor.cs.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialCursor.cs.meta
new file mode 100644
index 00000000..779398b9
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialCursor.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: f58770c8daee40e5bdf71181897d8e89
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialDescription.cs b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialDescription.cs
new file mode 100644
index 00000000..f1192ec5
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialDescription.cs
@@ -0,0 +1,43 @@
+#if VRC_SDK_VRCSDK3
+using System;
+using UnityEditor;
+using UnityEngine;
+
+namespace GestureManager.Scripts.Editor.Modules.Vrc3
+{
+ public class RadialDescription
+ {
+ private readonly Action<string> _action;
+
+ private readonly string _text;
+ private readonly string _link;
+ private readonly string _url;
+ private readonly string _tail;
+
+ public RadialDescription(string text, string link, string tail, Action<string> action, string url)
+ {
+ _text = text;
+ _link = link;
+ _action = action;
+ _url = url;
+ _tail = tail;
+ }
+
+ public void Show()
+ {
+ GUILayout.Space(10);
+ GUILayout.BeginHorizontal(GestureManagerStyles.EmoteError);
+ GUILayout.FlexibleSpace();
+ GUILayout.Label(_text);
+
+ var guiStyle = EditorGUIUtility.isProSkin ? ModuleVrc3Styles.UrlPro : ModuleVrc3Styles.Url;
+ if (GUILayout.Button(_link, guiStyle)) _action(_url);
+ EditorGUIUtility.AddCursorRect(GUILayoutUtility.GetLastRect(), MouseCursor.Link);
+ GUILayout.Label(_tail);
+ GUILayout.FlexibleSpace();
+
+ GUILayout.EndHorizontal();
+ }
+ }
+}
+#endif \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialDescription.cs.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialDescription.cs.meta
new file mode 100644
index 00000000..cbfd7721
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialDescription.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 843edca2e7354bf2a1d82588c62dc671
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialMenu.cs b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialMenu.cs
new file mode 100644
index 00000000..58d9dba1
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialMenu.cs
@@ -0,0 +1,433 @@
+#if VRC_SDK_VRCSDK3
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+using UnityEngine;
+using UnityEngine.UIElements;
+using GestureManager.Scripts.Core.Editor;
+using GestureManager.Scripts.Core.VisualElements;
+using GestureManager.Scripts.Editor.Modules.Vrc3.Params;
+using GestureManager.Scripts.Editor.Modules.Vrc3.RadialButtons;
+using GestureManager.Scripts.Editor.Modules.Vrc3.RadialPuppets.Base;
+using VRC.SDK3.Avatars.ScriptableObjects;
+using UIEPosition = UnityEngine.UIElements.Position;
+
+namespace GestureManager.Scripts.Editor.Modules.Vrc3
+{
+ public class RadialMenu : Vrc3VisualRender
+ {
+ private readonly RadialCursor _cursor;
+ internal readonly ModuleVrc3 Module;
+
+ public const float Size = 300;
+
+ private const float InnerSize = Size / 3;
+ private const float Clamp = Size / 3;
+ private const float ClampReset = Size / 1.7f;
+
+ private const string TrackingDocumentationUrl = "https://docs.vrchat.com/docs/animator-parameters#trackingtype-parameter";
+
+ [SuppressMessage("ReSharper", "StringLiteralTypo")]
+ private readonly string[] _supporters =
+ {
+ "Stack_",
+ "GaNyan",
+ "Nayu",
+ "Ahri~",
+ "Hiro N.",
+ "lindesu"
+ };
+
+ private Vector2 ExternalPosition()
+ {
+ var vector2 = Event.current.mousePosition - Rect.center;
+ if (_puppet != null) vector2 -= new Vector2(_puppet.style.left.value.value, _puppet.style.top.value.value);
+ return vector2;
+ }
+
+ private readonly List<RadialPage> _menuPath = new List<RadialPage>();
+ private readonly List<GmgButton> _selectionTuple = new List<GmgButton>();
+
+ private VRCExpressionsMenu _menu;
+
+ internal GmgLayoutHelper.Toolbar ToolBar;
+ internal GmgLayoutHelper.Toolbar DebugToolBar;
+
+ private bool Puppet => _puppet != null;
+ private BasePuppet _puppet;
+ private RadialDescription _radialDescription;
+
+
+ private VisualElement _borderHolder;
+ private VisualElement _sliceHolder;
+ private VisualElement _dataHolder;
+
+ private VisualElement _puppetHolder;
+ private VisualElement _radial;
+
+ internal RadialMenuItem PressingButton;
+ private RadialMenuItem[] _buttons;
+
+ private readonly bool _official;
+
+ public RadialMenu(ModuleVrc3 module, bool official = false)
+ {
+ Module = module;
+ _official = official;
+ _cursor = new RadialCursor();
+ CreateRadial();
+ }
+
+ /*
+ * Events
+ */
+
+ private void OnClickStart()
+ {
+ if (_puppet != null) return;
+ if (_cursor.Selection != -1) _buttons[_cursor.Selection].OnClickStart();
+ }
+
+ private void OnClickEnd()
+ {
+ if (_puppet == null)
+ {
+ if (_cursor.Selection != -1) _buttons[_cursor.Selection].OnClickEnd();
+ PressingButton?.OnClickEnd();
+ }
+ else ClosePuppet();
+ }
+
+ /*
+ * Core
+ */
+
+ public override void Render(VisualElement root, Rect rect)
+ {
+ if (Event.current.type == EventType.MouseDown) OnClickStart();
+ base.Render(root, rect);
+ if (Event.current.type == EventType.MouseUp) OnClickEnd();
+ HandleExternalInput();
+ if (!_official) GestureManagerStyles.Sign("UI");
+ }
+
+ protected override bool RenderCondition(ModuleVrc3 module, RadialMenu menu) => menu.ToolBar.Selected == 0;
+
+ /*
+ * Puppets
+ */
+
+ internal void OpenPuppet(BasePuppet puppet)
+ {
+ if (_puppet != null) return;
+
+ _puppet = puppet;
+ _puppet.OnOpen();
+ _puppetHolder.Add(_puppet);
+ _puppet.style.left = _cursor.style.left;
+ _puppet.style.top = _cursor.style.top;
+ _cursor.SetData(puppet.Clamp, float.MaxValue, (int)(InnerSize / 2f), (int)(Size / 2f), _puppet);
+ _cursor.Update(ExternalPosition(), _selectionTuple, Puppet);
+ _puppet.AfterCursor();
+ }
+
+ private void ClosePuppet()
+ {
+ if (_puppet == null) return;
+
+ _puppetHolder.Remove(_puppet);
+ _cursor.SetData(Clamp, ClampReset, (int)(InnerSize / 2f), (int)(Size / 2f), _radial);
+ _puppet.OnClose();
+ _puppet = null;
+
+ _cursor.Update(Event.current.mousePosition, _selectionTuple, Puppet);
+ }
+
+ /*
+ * Menu Prefabs
+ */
+
+ internal void MainMenuPrefab()
+ {
+ _menuPath.Clear();
+ var buttons = new RadialMenuItem[3];
+ if (Module.DummyMode == null) buttons[0] = new RadialMenuButton(OptionMainMenuPrefab, "Options", ModuleVrc3Styles.Option);
+ else buttons[0] = RadialMenuUtility.Buttons.ToggleFromParam(this, Module.DummyMode.ExitDummyText, Module.Dummy);
+ if (Module.DummyMode != null || !_menu) buttons[1] = new RadialMenuButton(Module.NoExpressionRefresh, "Expressions", ModuleVrc3Styles.NoExpressions, Color.gray);
+ else buttons[1] = new RadialMenuButton(ExpressionsMenu, "Expressions", ModuleVrc3Styles.Expressions);
+ buttons[2] = new RadialMenuButton(SupporterMenuPrefab, "Thanks to...", ModuleVrc3Styles.Emojis);
+ SetButtons(buttons);
+ _radialDescription = Module.DummyMode?.DummyDescription();
+ }
+
+ private void OptionMainMenuPrefab()
+ {
+ OpenCustom(new RadialMenuItem[]
+ {
+ new RadialMenuButton(OptionExtraMenuPrefab, "Extra", ModuleVrc3Styles.Option),
+ new RadialMenuButton(OptionTrackingMenuPrefab, "Tracking", ModuleVrc3Styles.Option),
+ new RadialMenuButton(Module.EnableEditMode, "Edit-Mode", ModuleVrc3Styles.Default),
+ new RadialMenuButton(OptionStatesMenuPrefab, "States", ModuleVrc3Styles.Option),
+ new RadialMenuButton(OptionLocomotionMenuPrefab, "Locomotion", ModuleVrc3Styles.Option)
+ });
+ }
+
+ private void OptionLocomotionMenuPrefab()
+ {
+ OpenCustom(new[]
+ {
+ RadialMenuUtility.Buttons.ToggleFromParam(this, "Grounded", GetParam(Vrc3DefaultParams.Grounded)),
+ RadialMenuUtility.Buttons.RadialFromParam(this, "Falling Speed", GetParam(Vrc3DefaultParams.VelocityY), -22f),
+ RadialMenuUtility.Buttons.RadialFromParam(this, "Upright", GetParam(Vrc3DefaultParams.Upright)),
+ RadialMenuUtility.Buttons.AxisFromParams(this, "Velocity", GetParam(Vrc3DefaultParams.VelocityX), GetParam(Vrc3DefaultParams.VelocityZ), 7f)
+ });
+ }
+
+ private void OptionTrackingMenuPrefab()
+ {
+ var param = GetParam(Vrc3DefaultParams.TrackingType);
+ OpenCustom(new RadialMenuItem[]
+ {
+ RadialMenuUtility.Buttons.ParamStateToggle(this, "Uninitialized", param, 0f),
+ RadialMenuUtility.Buttons.ParamStateToggle(this, "Generic", param, 1f),
+ RadialMenuUtility.Buttons.ParamStateToggle(this, "Hands-only", param, 2f),
+ RadialMenuUtility.Buttons.ToggleFromParam(this, "VRMode", GetParam(Vrc3DefaultParams.VRMode)),
+ RadialMenuUtility.Buttons.ParamStateToggle(this, "Head And Hands", param, 3f),
+ RadialMenuUtility.Buttons.ParamStateToggle(this, "4-Point VR", param, 4f),
+ RadialMenuUtility.Buttons.ParamStateToggle(this, "Full Body", param, 6f)
+ });
+ _radialDescription = new RadialDescription("If you don't know what those are you can check the", "documentation!", "", Application.OpenURL, TrackingDocumentationUrl);
+ }
+
+ private void OptionStatesMenuPrefab()
+ {
+ OpenCustom(new[]
+ {
+ RadialMenuUtility.Buttons.ToggleFromParam(this, "T Pose", Module.PoseT),
+ RadialMenuUtility.Buttons.ToggleFromParam(this, "AFK", GetParam(Vrc3DefaultParams.Afk)),
+ RadialMenuUtility.Buttons.RadialFromParam(this, Vrc3DefaultParams.Vise, GetParam(Vrc3DefaultParams.Vise), Module.ViseAmount),
+ RadialMenuUtility.Buttons.ToggleFromParam(this, "Seated", GetParam(Vrc3DefaultParams.Seated)),
+ RadialMenuUtility.Buttons.ToggleFromParam(this, "IK Pose", Module.PoseIK)
+ });
+ }
+
+ private void OptionExtraMenuPrefab()
+ {
+ OpenCustom(new[]
+ {
+ RadialMenuUtility.Buttons.ToggleFromParam(this, "IsLocal", GetParam(Vrc3DefaultParams.IsLocal)),
+ RadialMenuUtility.Buttons.RadialFromParam(this, "Gesture\nRight Weight", GetParam(Vrc3DefaultParams.GestureRightWeight)),
+ RadialMenuUtility.Buttons.ToggleFromParam(this, "MuteSelf", GetParam(Vrc3DefaultParams.MuteSelf)),
+ RadialMenuUtility.Buttons.RadialFromParam(this, "Gesture\nLeft Weight", GetParam(Vrc3DefaultParams.GestureLeftWeight)),
+ RadialMenuUtility.Buttons.ToggleFromParam(this, "InStation", GetParam(Vrc3DefaultParams.InStation))
+ });
+ }
+
+ private void SupporterMenuPrefab()
+ {
+ OpenCustom(new[]
+ {
+ new RadialMenuButton(null, _supporters[0], ModuleVrc3Styles.SupportLike),
+ new RadialMenuButton(null, _supporters[1], ModuleVrc3Styles.SupportLike),
+ new RadialMenuButton(null, _supporters[2], ModuleVrc3Styles.SupportLike),
+ new RadialMenuButton(null, "You!", ModuleVrc3Styles.SupportHeart) { SelectedCenterColor = Color.red },
+ new RadialMenuButton(null, _supporters[3], ModuleVrc3Styles.SupportLike),
+ new RadialMenuButton(null, _supporters[4], ModuleVrc3Styles.SupportLike) { SelectedCenterColor = Color.blue },
+ new RadialMenuButton(null, _supporters[5], ModuleVrc3Styles.SupportLike)
+ });
+ }
+
+ private void ExpressionsMenu() => OpenMenu(_menu, null, 0f);
+
+ /*
+ * Menu
+ */
+
+ internal void OpenMenu(VRCExpressionsMenu menu, Vrc3Param param, float value)
+ {
+ _menuPath.Add(new RadialPage(this, menu, param, value));
+ SetMenu(menu);
+ }
+
+ internal void SetMenu(VRCExpressionsMenu menu)
+ {
+ var isMain = menu == _menu;
+ var defaultButtonsInt = isMain ? 2 : 1;
+ var intCount = menu.controls.Count + defaultButtonsInt;
+ var intMax = defaultButtonsInt + 8;
+
+ SetButtons(intCount, intMax, intCurrent =>
+ {
+ switch (intCurrent)
+ {
+ case 0: return new RadialMenuButton(GoBack, "Back", isMain ? ModuleVrc3Styles.BackHome : ModuleVrc3Styles.Back);
+ case 1 when isMain: return new RadialMenuButton(Module.ResetAvatar, "Reset Avatar", ModuleVrc3Styles.Reset);
+ default: return new RadialMenuControl(this, menu.controls[intCurrent - defaultButtonsInt]);
+ }
+ });
+ }
+
+ /*
+ * Custom Menu
+ */
+
+ private void OpenCustom(IReadOnlyList<RadialMenuItem> controls)
+ {
+ _menuPath.Add(new RadialPage(this, controls));
+ SetCustom(controls);
+ }
+
+ internal void SetCustom(IReadOnlyList<RadialMenuItem> controls)
+ {
+ var isMain = _menuPath.Count == 1;
+ SetButtons(controls.Count + 1, i =>
+ {
+ switch (i)
+ {
+ case 0: return new RadialMenuButton(GoBack, "Back", isMain ? ModuleVrc3Styles.BackHome : ModuleVrc3Styles.Back);
+ default: return controls[i - 1];
+ }
+ });
+ }
+
+ /*
+ * Menu Actions
+ */
+
+ private void GoBack()
+ {
+ RemoveMenu(_menuPath.Count - 1);
+ if (_menuPath.Count == 0) MainMenuPrefab();
+ else _menuPath[_menuPath.Count - 1].Open();
+ }
+
+ private void RemoveMenu(int index)
+ {
+ var param = _menuPath[index].Param;
+ _menuPath.RemoveAt(index);
+ param?.Set(Module, 0);
+ }
+
+ /*
+ * Radial Stuff
+ */
+
+ public Vrc3Param GetParam(string pName) => Module.GetParam(pName);
+
+ public void ShowRadialDescription() => _radialDescription?.Show();
+
+ public void Set(VRCExpressionsMenu menu)
+ {
+ _menu = menu;
+ MainMenuPrefab();
+ }
+
+ private void CreateRadial()
+ {
+ style.alignItems = Align.Center;
+ style.justifyContent = Justify.Center;
+ style.position = UIEPosition.Absolute;
+ pickingMode = PickingMode.Ignore;
+
+ _radial = this.MyAdd(new VisualElement { pickingMode = PickingMode.Ignore, style = { justifyContent = Justify.Center, position = UIEPosition.Absolute, alignItems = Align.Center } });
+
+ _sliceHolder = _radial.MyAdd(new VisualElement { pickingMode = PickingMode.Ignore, style = { position = UIEPosition.Absolute } });
+ _borderHolder = _radial.MyAdd(new VisualElement { pickingMode = PickingMode.Ignore, style = { position = UIEPosition.Absolute } });
+ _radial.MyAdd(RadialMenuUtility.Prefabs.NewCircle((int)InnerSize, RadialMenuUtility.Colors.RadialInner, RadialMenuUtility.Colors.CustomBorder, UIEPosition.Absolute));
+
+ _dataHolder = _radial.MyAdd(new VisualElement { pickingMode = PickingMode.Ignore, style = { position = UIEPosition.Absolute } });
+ _puppetHolder = _radial.MyAdd(new VisualElement { pickingMode = PickingMode.Ignore, style = { position = UIEPosition.Absolute } });
+ _radial.MyAdd(_cursor);
+
+ _cursor.SetData(Clamp, ClampReset, (int)(InnerSize / 2f), (int)(Size / 2f), _radial);
+ }
+
+ private void SetButtons(int count, Func<int, RadialMenuItem> create) => SetButtons(count, 10, create);
+
+ private void SetButtons(int count, int max, Func<int, RadialMenuItem> create) => SetButtons((from iInt in Enumerable.Range(0, Math.Min(count, max)) select create(iInt)).ToArray());
+
+ private void SetButtons(RadialMenuItem[] buttons)
+ {
+ _radialDescription = null;
+ _buttons = buttons;
+
+ _selectionTuple.Clear();
+ _borderHolder.Clear();
+ _sliceHolder.Clear();
+ _dataHolder.Clear();
+
+ var step = 360f / _buttons.Length;
+ var current = -step / 2;
+ var progress = 1f / _buttons.Length;
+
+ var rStep = Mathf.PI * 2 / _buttons.Length;
+ var rCurrent = Mathf.PI;
+
+ foreach (var item in _buttons)
+ {
+ item.Create();
+
+ var circleHolder = new VisualElement();
+ var circle = circleHolder.MyAdd(RadialMenuUtility.Prefabs.NewSlice(Size, RadialMenuUtility.Colors.RadialCenter, RadialMenuUtility.Colors.CustomMain, RadialMenuUtility.Colors.CustomBorder));
+ circle.Progress = progress;
+
+ _sliceHolder.MyAdd(circleHolder).transform.rotation = Quaternion.Euler(0, 0, current);
+ _borderHolder.MyAdd(RadialMenuUtility.Prefabs.NewBorder(Size / 2)).transform.rotation = Quaternion.Euler(0, 0, current - 90);
+
+ item.DataHolder.transform.position = new Vector3(Mathf.Sin(rCurrent) * Size / 3, Mathf.Cos(rCurrent) * Size / 3, 0);
+
+ _dataHolder.MyAdd(item.DataHolder);
+ _selectionTuple.Add(new GmgButton { Button = item, Data = item.DataHolder, CircleElement = circle });
+
+ current += step;
+ rCurrent -= rStep;
+ }
+
+ _cursor.Selection = _cursor.GetChoice(buttons.Length, Puppet);
+ if (_cursor.Selection != -1) RadialCursor.Sel(_selectionTuple[_cursor.Selection], true);
+ }
+
+ private void HandleExternalInput()
+ {
+ if (Event.current.type == EventType.Layout) return;
+
+ var mouseVector = ExternalPosition();
+ _cursor.Update(mouseVector, _selectionTuple, Puppet);
+ _puppet?.Update(_cursor);
+ }
+
+ internal void UpdateValue(string pName, float value)
+ {
+ _puppet?.UpdateValue(pName, value);
+ if (!UpdateMenus(pName, value)) UpdateRunning();
+ }
+
+ private bool UpdateMenus(string pName, float value)
+ {
+ if (string.IsNullOrEmpty(pName)) return false;
+
+ var list = _menuPath.TakeWhile(page => page.Param?.Name != pName || RadialMenuUtility.Is(page.Value, value)).ToList();
+ if (list.Count == _menuPath.Count) return false;
+
+ for (var i = list.Count; i < _menuPath.Count; i++) RemoveMenu(list.Count);
+ _menuPath[_menuPath.Count - 1].Open();
+ return true;
+ }
+
+ private void UpdateRunning()
+ {
+ foreach (var item in _buttons)
+ if (item is RadialMenuDynamic dynamic)
+ dynamic.CheckRunningUpdate();
+ }
+ }
+
+ public class GmgButton
+ {
+ internal GmgCircleElement CircleElement;
+ internal RadialMenuItem Button;
+ internal VisualElement Data;
+ }
+}
+#endif \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialMenu.cs.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialMenu.cs.meta
new file mode 100644
index 00000000..c827521a
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialMenu.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 437f6c8a9c304863886322a63496db31
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialMenuUtility.cs b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialMenuUtility.cs
new file mode 100644
index 00000000..be088154
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialMenuUtility.cs
@@ -0,0 +1,373 @@
+#if VRC_SDK_VRCSDK3
+using System;
+using System.Linq;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using GestureManager.Scripts.Core.Editor;
+using GestureManager.Scripts.Core.VisualElements;
+using GestureManager.Scripts.Editor.Modules.Vrc3.Params;
+using GestureManager.Scripts.Editor.Modules.Vrc3.RadialButtons;
+using UnityEditor;
+using UnityEngine;
+using UnityEngine.Animations;
+using UnityEngine.UIElements;
+using UnityEngine.Playables;
+using VRC.SDK3.Avatars.ScriptableObjects;
+using UIEPosition = UnityEngine.UIElements.Position;
+using ControlType = VRC.SDK3.Avatars.ScriptableObjects.VRCExpressionsMenu.Control.ControlType;
+
+namespace GestureManager.Scripts.Editor.Modules.Vrc3
+{
+ public static class RadialMenuUtility
+ {
+ internal static Vector2 DataVector = new Vector2(-50, -60);
+
+ public static class Colors
+ {
+ internal static class Default
+ {
+ internal static readonly Color Main = new Color(0.14f, 0.18f, 0.2f);
+ internal static readonly Color Border = new Color(0.1f, 0.35f, 0.38f);
+ internal static readonly Color Selected = new Color(0.07f, 0.55f, 0.58f);
+ }
+
+ private const string MainKeyName = "GM3 Main Color";
+ private const string BorderKeyName = "GM3 Border Color";
+ private const string SelectedKeyName = "GM3 Selected Color";
+
+ private static Color PrefColor(string name, Color defaultColor) => ColorUtility.TryParseHtmlString(EditorPrefs.GetString(name), out var color) ? color : defaultColor;
+
+ internal static readonly Color RadialTextBackground = new Color(0.11f, 0.11f, 0.11f, 0.49f);
+ internal static readonly Color RadialSelColor = new Color(0.06f, 0.2f, 0.22f);
+ internal static readonly Color RadialCenter = new Color(0.06f, 0.27f, 0.29f);
+ internal static readonly Color RadialInner = new Color(0.21f, 0.24f, 0.27f);
+ internal static readonly Color RestartButton = new Color(1f, 0.72f, 0.41f);
+ internal static readonly Color SubIcon = new Color(0.22f, 0.24f, 0.27f);
+
+ internal static Color Cursor => new Color(CustomMain.r, CustomMain.g, CustomMain.b, CustomMain.a - 0.3f);
+ internal static Color CursorBorder => new Color(CustomBorder.r, CustomBorder.g, CustomBorder.b, CustomBorder.a - 0.5f);
+ internal static Color ProgressBorder => CustomSelected * 1.5f;
+
+ internal static Color CustomMain = PrefColor(MainKeyName, Default.Main);
+ internal static Color CustomBorder = PrefColor(BorderKeyName, Default.Border);
+ internal static Color CustomSelected = PrefColor(SelectedKeyName, Default.Selected);
+
+ public static void SaveColors(Color main, Color border, Color selected)
+ {
+ CustomMain = main;
+ CustomBorder = border;
+ CustomSelected = selected;
+ EditorPrefs.SetString(MainKeyName, $"#{ColorUtility.ToHtmlStringRGBA(main)}");
+ EditorPrefs.SetString(BorderKeyName, $"#{ColorUtility.ToHtmlStringRGBA(border)}");
+ EditorPrefs.SetString(SelectedKeyName, $"#{ColorUtility.ToHtmlStringRGBA(selected)}");
+ }
+ }
+
+ public static class Prefabs
+ {
+ internal static VisualElement NewBorder(float size)
+ {
+ return new VisualElement
+ {
+ pickingMode = PickingMode.Ignore,
+ style =
+ {
+ width = size,
+ height = 2,
+ backgroundColor = Colors.CustomBorder,
+ position = UIEPosition.Absolute
+ }
+ };
+ }
+
+ internal static VisualElement NewBorder(float size, float euler)
+ {
+ var element = NewBorder(size);
+ element.transform.rotation = Quaternion.Euler(0, 0, euler);
+ return element;
+ }
+
+ internal static VisualElement NewCircleBorder(float size, Color border, UIEPosition position = default) => SetCircleBorder(new VisualElement(), size, border, position);
+
+ private static VisualElement SetCircleBorder(VisualElement element, float size, Color border, UIEPosition position = default)
+ {
+ element.MyBorder(2, size, border);
+ element.pickingMode = PickingMode.Ignore;
+ element.style.position = position;
+ element.style.alignItems = Align.Center;
+ element.style.width = element.style.height = size;
+ element.style.justifyContent = Justify.Center;
+ return element;
+ }
+
+ internal static VisualElement NewIconText(float x, float y, float size, Texture2D icon, string text) => NewIconText(x, y, size, size / 2, icon, text);
+
+ private static VisualElement NewIconText(float x, float y, float size, float hSize, Texture2D icon, string text)
+ {
+ return new VisualElement
+ {
+ pickingMode = PickingMode.Ignore,
+ style =
+ {
+ width = size,
+ height = size,
+ left = x - hSize,
+ top = y - hSize,
+ backgroundImage = icon,
+ position = UIEPosition.Absolute
+ }
+ }.With(new TextElement
+ {
+ text = text,
+ pickingMode = PickingMode.Ignore,
+ style =
+ {
+ color = Color.white,
+ fontSize = 8,
+ unityTextAlign = TextAnchor.MiddleCenter,
+ top = size
+ }
+ });
+ }
+
+ internal static VisualElement NewData(float width, float height)
+ {
+ return new VisualElement
+ {
+ pickingMode = PickingMode.Ignore,
+ style =
+ {
+ width = width,
+ height = height,
+ left = -width / 2,
+ top = -height / 2 - 10,
+ backgroundColor = Color.clear,
+ position = UIEPosition.Absolute,
+ alignItems = Align.Center,
+ justifyContent = Justify.Center
+ }
+ };
+ }
+
+ internal static GmgCircleElement NewSlice(float size, Color centerColor, Color color, Color border, UIEPosition position = UIEPosition.Absolute) => SetSlice(new GmgCircleElement(), size, centerColor, color, border, position);
+
+ private static GmgCircleElement SetSlice(GmgCircleElement element, float size, Color centerColor, Color color, Color border, UIEPosition position = default)
+ {
+ element.BorderWidth = 2;
+ element.VertexColor = color;
+ element.BorderColor = border;
+ element.CenterColor = centerColor;
+ element.pickingMode = PickingMode.Ignore;
+ element.style.position = position;
+ element.style.alignItems = Align.Center;
+ element.style.width = element.style.height = size;
+ element.style.justifyContent = Justify.Center;
+ element.style.left = element.style.top = -size / 2;
+ return element;
+ }
+
+ internal static GmgCircleElement NewCircle(float size, Color color, Color border, UIEPosition position = default) => SetCircle(new GmgCircleElement(), size, color, color, border, position);
+
+ internal static GmgCircleElement NewCircle(float size, Color centerColor, Color color, Color border, UIEPosition position = default) => SetCircle(new GmgCircleElement(), size, centerColor, color, border, position);
+
+ internal static GmgCircleElement SetCircle(GmgCircleElement element, float size, Color color, Color border, UIEPosition position = default) => SetCircle(element, size, color, color, border, position);
+
+ private static GmgCircleElement SetCircle(GmgCircleElement element, float size, Color centerColor, Color color, Color border, UIEPosition position = default)
+ {
+ element.BorderWidth = 2;
+ element.VertexColor = color;
+ element.BorderColor = border;
+ element.CenterColor = centerColor;
+ element.pickingMode = PickingMode.Ignore;
+ element.style.position = position;
+ element.style.alignItems = Align.Center;
+ element.style.width = element.style.height = size;
+ element.style.justifyContent = Justify.Center;
+ return element;
+ }
+
+ internal static VisualElement NewRadialText(out TextElement text, int top, UIEPosition position = default)
+ {
+ return SetRadialText(new VisualElement(), out text, top, position);
+ }
+
+ internal static VisualElement SetRadialText(VisualElement element, out TextElement text, int top, UIEPosition position = default)
+ {
+ element.style.width = 50;
+ element.style.height = 20;
+ element.pickingMode = PickingMode.Ignore;
+ element.style.backgroundColor = Colors.RadialTextBackground;
+ element.style.borderTopLeftRadius = 10;
+ element.style.borderTopRightRadius = 10;
+ element.style.borderBottomLeftRadius = 10;
+ element.style.borderBottomRightRadius = 10;
+ element.style.position = position;
+ if (top != 0) element.style.top = top;
+ text = element.MyAdd(new TextElement { pickingMode = PickingMode.Ignore, style = { height = 20, unityTextAlign = TextAnchor.MiddleCenter, color = Color.white, fontSize = 14 } });
+ return element;
+ }
+
+ internal static VisualElement NewSubIcon(Texture2D texture)
+ {
+ return new VisualElement
+ {
+ pickingMode = PickingMode.Ignore,
+ style =
+ {
+ width = 25,
+ height = 25,
+ borderTopLeftRadius = 15,
+ borderTopRightRadius = 15,
+ borderBottomLeftRadius = 15,
+ borderBottomRightRadius = 15,
+ left = 60,
+ top = 50,
+ backgroundColor = Colors.SubIcon,
+ position = UIEPosition.Absolute,
+ justifyContent = Justify.Center,
+ alignItems = Align.Center
+ }
+ }.With(new VisualElement
+ {
+ pickingMode = PickingMode.Ignore,
+ style =
+ {
+ width = 20,
+ height = 20,
+ backgroundImage = texture
+ }
+ });
+ }
+ }
+
+ public static class Buttons
+ {
+ public static RadialMenuControl ToggleFromParam(RadialMenu menu, string name, Vrc3Param param)
+ {
+ return ParamStateToggle(menu, name, param, 1f);
+ }
+
+ public static RadialMenuControl ParamStateToggle(RadialMenu menu, string name, Vrc3Param param, float activeValue)
+ {
+ return new RadialMenuControl(menu, name, null, ControlType.Toggle, activeValue, param, Array.Empty<Vrc3Param>(), null, null);
+ }
+
+ public static RadialMenuItem RadialFromParam(RadialMenu menu, string name, Vrc3Param param, float amplify = 1f)
+ {
+ return new RadialMenuControl(menu, name, null, ControlType.RadialPuppet, 1f, null, new[] { param }, null, null, amplify);
+ }
+
+ public static RadialMenuControl AxisFromParams(RadialMenu menu, string name, Vrc3Param xParam, Vrc3Param yParam, float amplify = 1f)
+ {
+ var subLabels = new VRCExpressionsMenu.Control.Label[4];
+ return new RadialMenuControl(menu, name, null, ControlType.TwoAxisPuppet, 1f, null, new[] { xParam, yParam }, null, subLabels, amplify);
+ }
+ }
+
+ public static Texture2D GetSubIcon(ControlType type)
+ {
+ switch (type)
+ {
+ case ControlType.Button:
+ return null;
+ case ControlType.Toggle:
+ return ModuleVrc3Styles.Toggle;
+ case ControlType.SubMenu:
+ return ModuleVrc3Styles.Option;
+ case ControlType.TwoAxisPuppet:
+ return ModuleVrc3Styles.TwoAxis;
+ case ControlType.FourAxisPuppet:
+ return ModuleVrc3Styles.FourAxis;
+ case ControlType.RadialPuppet:
+ return ModuleVrc3Styles.Radial;
+ default: throw new ArgumentOutOfRangeException(nameof(type), type, null);
+ }
+ }
+
+ public static IEnumerable<AnimatorControllerParameter> GetParameters(AnimatorControllerPlayable playable)
+ {
+ if (playable.IsNull()) yield break;
+ for (var i = 0; i < playable.GetParameterCount(); i++)
+ yield return playable.GetParameter(i);
+ }
+
+ public static Vrc3ParamController CreateParamFromController(Animator animator, AnimatorControllerParameter parameter, AnimatorControllerPlayable controller)
+ {
+ return new Vrc3ParamController(parameter.type, parameter.name, controller, animator);
+ }
+
+ public static Vrc3Param CreateParamFromNothing(VRCExpressionParameters.Parameter parameter)
+ {
+ return new Vrc3ParamExternal(parameter.name, ModuleVrc3Styles.Data.TypeOf[parameter.valueType]);
+ }
+
+ public static Vrc3Param CreateParamFromNothing(string name, AnimatorControllerParameterType type)
+ {
+ return new Vrc3ParamExternal(name, type);
+ }
+
+ public static int RadialPercentage(float value)
+ {
+ return RadialPercentage(value, out _);
+ }
+
+ public static int RadialPercentage(float value, out float clamp)
+ {
+ clamp = Mathf.Clamp(value, 0f, 1f);
+ return (int)(clamp * 100);
+ }
+
+ private static void AppendMenus(VRCExpressionsMenu menu, ICollection<VRCExpressionsMenu> menus)
+ {
+ if (!menu || menus.Contains(menu)) return;
+ menus.Add(menu);
+ foreach (var control in menu.controls) AppendMenus(control.subMenu, menus);
+ }
+
+ private static IEnumerable<VRCExpressionsMenu> GetMenus(VRCExpressionsMenu menu)
+ {
+ var menus = new List<VRCExpressionsMenu>();
+ AppendMenus(menu, menus);
+ return menus;
+ }
+
+ private static IEnumerable<string> GetParams(IEnumerable<VRCExpressionsMenu> menus)
+ {
+ var paramList = new HashSet<string>();
+ foreach (var menu in menus)
+ foreach (var control in menu.controls)
+ {
+ paramList.Add(control.name);
+ if (control.subParameters == null) continue;
+ foreach (var subParameter in control.subParameters)
+ paramList.Add(subParameter.name);
+ }
+
+ return paramList;
+ }
+
+ public static IEnumerable<string> CheckErrors(VRCExpressionsMenu menu, VRCExpressionParameters param)
+ {
+ var errors = new List<string>();
+ if (!menu && !param) return errors;
+ if (!menu || !param) errors.Add(!menu ? "- No menu defined when parameters are defined!" : "- No parameters defined when menu is defined!");
+ return errors;
+ }
+
+ [SuppressMessage("ReSharper", "UnusedMember.Global")]
+ public static IEnumerable<string> CheckWarnings(VRCExpressionsMenu menu, VRCExpressionParameters param)
+ {
+ return from paramName in GetParams(GetMenus(menu))
+ let isDefined = string.IsNullOrEmpty(paramName) || param.FindParameter(paramName) != null
+ where !isDefined
+ select "Menu uses a parameter that is not defined: " + paramName;
+ }
+
+ [SuppressMessage("ReSharper", "CompareOfFloatsByEqualityOperator")]
+ public static bool Is(float get, float value)
+ {
+ return get == value;
+ }
+ }
+}
+#endif \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialMenuUtility.cs.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialMenuUtility.cs.meta
new file mode 100644
index 00000000..c5b992f6
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialMenuUtility.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 16ef3510dde44e3a9c0ea2ff8fef8f07
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialPage.cs b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialPage.cs
new file mode 100644
index 00000000..11473460
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialPage.cs
@@ -0,0 +1,38 @@
+#if VRC_SDK_VRCSDK3
+using System.Collections.Generic;
+using GestureManager.Scripts.Editor.Modules.Vrc3.Params;
+using GestureManager.Scripts.Editor.Modules.Vrc3.RadialButtons;
+using VRC.SDK3.Avatars.ScriptableObjects;
+
+namespace GestureManager.Scripts.Editor.Modules.Vrc3
+{
+ public class RadialPage
+ {
+ private readonly IReadOnlyList<RadialMenuItem> _controls;
+ private readonly VRCExpressionsMenu _menu;
+ private readonly RadialMenu _radialMenu;
+ public readonly Vrc3Param Param;
+ public readonly float Value;
+
+ public RadialPage(RadialMenu radialMenu, VRCExpressionsMenu menu, Vrc3Param param, float value)
+ {
+ _radialMenu = radialMenu;
+ _menu = menu;
+ Param = param;
+ Value = value;
+ }
+
+ public RadialPage(RadialMenu radialMenu, IReadOnlyList<RadialMenuItem> controls)
+ {
+ _radialMenu = radialMenu;
+ _controls = controls;
+ }
+
+ public void Open()
+ {
+ if (_menu) _radialMenu.SetMenu(_menu);
+ else _radialMenu.SetCustom(_controls);
+ }
+ }
+}
+#endif \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialPage.cs.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialPage.cs.meta
new file mode 100644
index 00000000..20765820
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialPage.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: d247fbb475b344cdbdc4056ed7c2c0d4
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialPuppets.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialPuppets.meta
new file mode 100644
index 00000000..c8e0f834
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialPuppets.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 07e98662aa494527b3fbd309a3458dc8
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialPuppets/Base.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialPuppets/Base.meta
new file mode 100644
index 00000000..8fa37527
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialPuppets/Base.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 9c99504918ed4a98b9013781e14ba338
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialPuppets/Base/BaseAxisPuppet.cs b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialPuppets/Base/BaseAxisPuppet.cs
new file mode 100644
index 00000000..def1c2ca
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialPuppets/Base/BaseAxisPuppet.cs
@@ -0,0 +1,47 @@
+#if VRC_SDK_VRCSDK3
+using GestureManager.Scripts.Core.Editor;
+using GestureManager.Scripts.Editor.Modules.Vrc3.RadialButtons;
+using UnityEngine;
+using UnityEngine.UIElements;
+using VRC.SDK3.Avatars.ScriptableObjects;
+using UIEPosition = UnityEngine.UIElements.Position;
+
+namespace GestureManager.Scripts.Editor.Modules.Vrc3.RadialPuppets.Base
+{
+ public abstract class BaseAxisPuppet : BasePuppet
+ {
+ internal override float Clamp => 60F;
+
+ private readonly VRCExpressionsMenu.Control.Label[] _labels;
+
+ protected BaseAxisPuppet(RadialMenuControl control) : base(140, control)
+ {
+ var holder = this.MyAdd(new VisualElement { pickingMode = PickingMode.Ignore, style = { position = UIEPosition.Absolute } });
+ holder.Add(RadialMenuUtility.Prefabs.NewBorder(70, 45));
+ holder.Add(RadialMenuUtility.Prefabs.NewBorder(70, 135));
+ holder.Add(RadialMenuUtility.Prefabs.NewBorder(70, 225));
+ holder.Add(RadialMenuUtility.Prefabs.NewBorder(70, 315));
+ Add(RadialMenuUtility.Prefabs.NewCircle(65, RadialMenuUtility.Colors.RadialInner, RadialMenuUtility.Colors.CustomBorder, UIEPosition.Absolute));
+ _labels = control.GetSubLabels();
+ }
+
+ public override void AfterCursor()
+ {
+ const int v = 50;
+ var holder = this.MyAdd(new VisualElement { pickingMode = PickingMode.Ignore, style = { position = UIEPosition.Absolute } });
+ holder.Add(RadialMenuUtility.Prefabs.NewIconText(0, -v, 24, LabelIcon(0, ModuleVrc3Styles.AxisUp), LabelText(0)));
+ holder.Add(RadialMenuUtility.Prefabs.NewIconText(v, 0, 24, LabelIcon(1, ModuleVrc3Styles.AxisRight), LabelText(1)));
+ holder.Add(RadialMenuUtility.Prefabs.NewIconText(0, v, 24, LabelIcon(2, ModuleVrc3Styles.AxisDown), LabelText(2)));
+ holder.Add(RadialMenuUtility.Prefabs.NewIconText(-v, 0, 24, LabelIcon(3, ModuleVrc3Styles.AxisLeft), LabelText(3)));
+ }
+
+ private Texture2D LabelIcon(int index, Texture2D def) => _labels == null || _labels.Length <= index || !_labels[index].icon ? def : _labels[index].icon;
+
+ private string LabelText(int index) => _labels == null || _labels.Length <= index ? null : _labels[index].name;
+
+ public override void UpdateValue(string pName, float value)
+ {
+ }
+ }
+}
+#endif \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialPuppets/Base/BaseAxisPuppet.cs.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialPuppets/Base/BaseAxisPuppet.cs.meta
new file mode 100644
index 00000000..fc1faeb4
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialPuppets/Base/BaseAxisPuppet.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 88a7eed5c2624331bb48626240d3d2aa
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialPuppets/Base/BasePuppet.cs b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialPuppets/Base/BasePuppet.cs
new file mode 100644
index 00000000..b410115c
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialPuppets/Base/BasePuppet.cs
@@ -0,0 +1,29 @@
+#if VRC_SDK_VRCSDK3
+using GestureManager.Scripts.Core.VisualElements;
+using GestureManager.Scripts.Editor.Modules.Vrc3.RadialButtons;
+
+namespace GestureManager.Scripts.Editor.Modules.Vrc3.RadialPuppets.Base
+{
+ public abstract class BasePuppet : GmgCircleElement
+ {
+ internal readonly RadialMenuControl Control;
+ internal abstract float Clamp { get; }
+
+ protected BasePuppet(float size, RadialMenuControl control)
+ {
+ Control = control;
+ RadialMenuUtility.Prefabs.SetCircle(this, size, RadialMenuUtility.Colors.CustomMain, RadialMenuUtility.Colors.CustomBorder);
+ }
+
+ public void OnOpen() => Control.SetControlValue();
+
+ public void OnClose() => Control.SetValue(0);
+
+ public abstract void UpdateValue(string pName, float value);
+
+ public abstract void Update(RadialCursor cursor);
+
+ public abstract void AfterCursor();
+ }
+}
+#endif \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialPuppets/Base/BasePuppet.cs.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialPuppets/Base/BasePuppet.cs.meta
new file mode 100644
index 00000000..1976a0e0
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialPuppets/Base/BasePuppet.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: df0d7ad7f8294b9e979d581a0cd0b083
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialPuppets/FourAxisPuppet.cs b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialPuppets/FourAxisPuppet.cs
new file mode 100644
index 00000000..3cfffef7
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialPuppets/FourAxisPuppet.cs
@@ -0,0 +1,24 @@
+#if VRC_SDK_VRCSDK3
+using GestureManager.Scripts.Editor.Modules.Vrc3.RadialButtons;
+using GestureManager.Scripts.Editor.Modules.Vrc3.RadialPuppets.Base;
+
+namespace GestureManager.Scripts.Editor.Modules.Vrc3.RadialPuppets
+{
+ public class FourAxisPuppet : BaseAxisPuppet
+ {
+ public FourAxisPuppet(RadialMenuControl control) : base(control)
+ {
+ }
+
+ public override void Update(RadialCursor cursor)
+ {
+ if (!cursor.Get4Axis(Clamp, out var axis)) return;
+
+ Control.SetSubValue(0, axis.x);
+ Control.SetSubValue(1, axis.y);
+ Control.SetSubValue(2, axis.z);
+ Control.SetSubValue(3, axis.w);
+ }
+ }
+}
+#endif \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialPuppets/FourAxisPuppet.cs.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialPuppets/FourAxisPuppet.cs.meta
new file mode 100644
index 00000000..755e22fc
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialPuppets/FourAxisPuppet.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: afcf0b0c3810421cb8da8776b35b0849
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialPuppets/RadialPuppet.cs b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialPuppets/RadialPuppet.cs
new file mode 100644
index 00000000..60388f98
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialPuppets/RadialPuppet.cs
@@ -0,0 +1,90 @@
+#if VRC_SDK_VRCSDK3
+using UnityEngine;
+using UnityEngine.UIElements;
+using GestureManager.Scripts.Core.Editor;
+using GestureManager.Scripts.Core.VisualElements;
+using GestureManager.Scripts.Editor.Modules.Vrc3.RadialButtons;
+using GestureManager.Scripts.Editor.Modules.Vrc3.RadialPuppets.Base;
+using UIEPosition = UnityEngine.UIElements.Position;
+
+namespace GestureManager.Scripts.Editor.Modules.Vrc3.RadialPuppets
+{
+ public class RadialPuppet : BasePuppet
+ {
+ internal override float Clamp => 20F;
+
+ private readonly TextElement _text;
+ private readonly VisualElement _arrow;
+ private readonly GmgCircleElement _progress;
+
+ private float Get => Control.GetSubValue(0);
+
+ public RadialPuppet(RadialMenuControl control) : base(100, control)
+ {
+ _progress = this.MyAdd(RadialMenuUtility.Prefabs.NewCircle(96, RadialMenuUtility.Colors.CustomSelected, RadialMenuUtility.Colors.CustomSelected, UIEPosition.Absolute));
+ Add(RadialMenuUtility.Prefabs.NewCircle(65, RadialMenuUtility.Colors.RadialInner, RadialMenuUtility.Colors.CustomBorder, UIEPosition.Absolute));
+ Add(RadialMenuUtility.Prefabs.NewRadialText(out _text, 0, UIEPosition.Absolute));
+ _arrow = this.MyAdd(GenerateArrow());
+
+ ShowValue(Get);
+ }
+
+ private void ShowValue(float value)
+ {
+ var intValue = RadialMenuUtility.RadialPercentage(value, out var cValue);
+ _progress.Progress = value;
+ _text.text = intValue + "%";
+ _arrow.transform.rotation = Quaternion.Euler(0, 0, cValue * 360f);
+ }
+
+ public override void UpdateValue(string pName, float value)
+ {
+ if (Control.GetSubParameterName(0) == pName) ShowValue(Control.NonAmplifiedValue(value));
+ }
+
+ public override void Update(RadialCursor cursor)
+ {
+ if (cursor.GetRadial(Clamp, out var radial)) TrySetValue(Get, radial);
+ }
+
+ private void TrySetValue(float from, float to)
+ {
+ var pDistance = to - from;
+ var mDistance = from - to;
+
+ if (mDistance > 0.5f) to = 1f;
+ if (pDistance > 0.5f) to = 0f;
+
+ Control.SetSubValue(0, to);
+ }
+
+ public override void AfterCursor()
+ {
+ _text.parent.BringToFront();
+ }
+
+ /*
+ * Static
+ */
+
+ private static VisualElement GenerateArrow()
+ {
+ var container = new VisualElement { pickingMode = PickingMode.Ignore, style = { position = UIEPosition.Absolute } };
+ var element = container.MyAdd(new VisualElement
+ {
+ pickingMode = PickingMode.Ignore,
+ style =
+ {
+ width = 20,
+ height = 20,
+ backgroundColor = RadialMenuUtility.Colors.CustomSelected,
+ top = -65,
+ position = UIEPosition.Absolute
+ }
+ }).MyBorder(2f, 0f, RadialMenuUtility.Colors.ProgressBorder);
+ element.transform.rotation = Quaternion.Euler(0, 0, 45);
+ return container;
+ }
+ }
+}
+#endif \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialPuppets/RadialPuppet.cs.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialPuppets/RadialPuppet.cs.meta
new file mode 100644
index 00000000..c3c1dfeb
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialPuppets/RadialPuppet.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: e6c28fa4c7304629b8e965f4d14e4a92
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialPuppets/TwoAxisPuppet.cs b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialPuppets/TwoAxisPuppet.cs
new file mode 100644
index 00000000..6f338a31
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialPuppets/TwoAxisPuppet.cs
@@ -0,0 +1,22 @@
+#if VRC_SDK_VRCSDK3
+using GestureManager.Scripts.Editor.Modules.Vrc3.RadialButtons;
+using GestureManager.Scripts.Editor.Modules.Vrc3.RadialPuppets.Base;
+
+namespace GestureManager.Scripts.Editor.Modules.Vrc3.RadialPuppets
+{
+ public class TwoAxisPuppet : BaseAxisPuppet
+ {
+ public TwoAxisPuppet(RadialMenuControl control) : base(control)
+ {
+ }
+
+ public override void Update(RadialCursor cursor)
+ {
+ if (!cursor.Get2Axis(Clamp, out var axis)) return;
+
+ Control.SetSubValue(0, axis.x);
+ Control.SetSubValue(1, axis.y);
+ }
+ }
+}
+#endif \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialPuppets/TwoAxisPuppet.cs.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialPuppets/TwoAxisPuppet.cs.meta
new file mode 100644
index 00000000..df5d917e
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialPuppets/TwoAxisPuppet.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 30db5172fa85424891ddb5c212d3bd7c
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3Debug.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3Debug.meta
new file mode 100644
index 00000000..7dd4e3f7
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3Debug.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: eac71ce5ebf145e288b0816c2961f328
+timeCreated: 1638146335 \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3Debug/Avatar.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3Debug/Avatar.meta
new file mode 100644
index 00000000..96a45fdb
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3Debug/Avatar.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 2613059600464066a57337432bdb4ceb
+timeCreated: 1645381180 \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3Debug/Avatar/Vrc3AvatarDebugWindow.cs b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3Debug/Avatar/Vrc3AvatarDebugWindow.cs
new file mode 100644
index 00000000..b70a2676
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3Debug/Avatar/Vrc3AvatarDebugWindow.cs
@@ -0,0 +1,253 @@
+#if VRC_SDK_VRCSDK3
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using GestureManager.Scripts.Core.Editor;
+using GestureManager.Scripts.Editor.Modules.Vrc3.Params;
+using UnityEditor;
+using UnityEngine;
+using VRC.SDK3.Avatars.Components;
+using VRC.SDKBase;
+
+namespace GestureManager.Scripts.Editor.Modules.Vrc3.Vrc3Debug.Avatar
+{
+ internal class Vrc3AvatarDebugWindow : EditorWindow
+ {
+ private ModuleVrc3 _source;
+ private Vector2 _scroll;
+
+ internal static Vrc3AvatarDebugWindow Create(ModuleVrc3 source)
+ {
+ var instance = CreateInstance<Vrc3AvatarDebugWindow>();
+ instance.titleContent = new GUIContent("[Debug Window] Gesture Manager");
+ instance._source = source;
+ instance.Show();
+ return instance;
+ }
+
+ internal static Vrc3AvatarDebugWindow Close(Vrc3AvatarDebugWindow source)
+ {
+ source.Close();
+ return null;
+ }
+
+ public void OnInspectorUpdate() => Repaint();
+
+ private void OnGUI()
+ {
+ if (_source == null) Close();
+ else DebugGUI();
+ }
+
+ private void DebugGUI()
+ {
+ GUILayout.Label("Gesture Manager - Avatar Debug Window", GestureManagerStyles.Header);
+ var fullScreen = Screen.width > 1279;
+ if (!fullScreen) _source.DebugToolBar = Static.DebugToolbar(_source.DebugToolBar);
+ _scroll = GUILayout.BeginScrollView(_scroll);
+ _source.DebugContext(rootVisualElement, null, 0, Screen.width - 60, fullScreen);
+ GUILayout.EndScrollView();
+ }
+
+ internal static class Static
+ {
+ internal static int DebugToolbar(int toolbar) => GUILayout.Toolbar(toolbar, new[] { "Parameters", "Tracking Control", "Animator States" });
+
+ internal static void DebugLayout(ModuleVrc3 module, float width, bool fullscreen, Dictionary<VRCAvatarDescriptor.AnimLayerType, ModuleVrc3.LayerData> data)
+ {
+ if (fullscreen) DebugLayoutFullScreen(module, width, data);
+ else DebugLayoutCompact(module, width, data);
+ }
+
+ private static void DebugLayoutCompact(ModuleVrc3 module, float width, Dictionary<VRCAvatarDescriptor.AnimLayerType, ModuleVrc3.LayerData> data)
+ {
+ switch (module.DebugToolBar)
+ {
+ case 0:
+ ParametersLayout(module, width);
+ break;
+ case 1:
+ TrackingControlLayout(width, data, module.TrackingControls, module.LocomotionDisabled, module.PoseSpace);
+ break;
+ case 2:
+ AnimatorsLayout(width, data);
+ break;
+ }
+ }
+
+ private static void DebugLayoutFullScreen(ModuleVrc3 module, float width, Dictionary<VRCAvatarDescriptor.AnimLayerType, ModuleVrc3.LayerData> data)
+ {
+ width /= 3;
+ GUILayout.BeginHorizontal();
+ ParametersLayout(module, width);
+ TrackingControlLayout(width, data, module.TrackingControls, module.LocomotionDisabled, module.PoseSpace);
+ AnimatorsLayout(width, data);
+ GUILayout.EndHorizontal();
+ }
+
+ internal static void DummyLayout(string mode)
+ {
+ GUILayout.FlexibleSpace();
+ GUILayout.Space(100);
+ GUILayout.Label($"Debug mode is disabled in {mode}-Mode!", GestureManagerStyles.TextError);
+ GUILayout.Space(20);
+ GUILayout.Label($"Exit {mode}-Mode to show the debug information of your avatar!", GestureManagerStyles.SubHeader);
+ GUILayout.Space(100);
+ GUILayout.FlexibleSpace();
+ }
+
+ private static void ParametersLayout(ModuleVrc3 module, float width)
+ {
+ var inWidth = width / 3;
+ GUILayout.BeginVertical(GUILayout.Width(width));
+ GUILayout.Label("Parameters", GestureManagerStyles.GuiHandTitle, GUILayout.Width(width));
+ GUILayout.BeginHorizontal();
+ GUILayout.BeginVertical();
+ GUILayout.Label("Parameter", GestureManagerStyles.GuiDebugTitle, GUILayout.Width(inWidth));
+ foreach (var paramPair in module.Params) GUILayout.Label(paramPair.Key);
+ GUILayout.EndVertical();
+
+ GUILayout.BeginVertical();
+ GUILayout.Label("Type", GestureManagerStyles.GuiDebugTitle, GUILayout.Width(inWidth));
+ foreach (var paramPair in module.Params) GUILayout.Label(paramPair.Value.Type.ToString());
+ GUILayout.EndVertical();
+
+ GUILayout.BeginVertical();
+ GUILayout.Label("Value", GestureManagerStyles.GuiDebugTitle, GUILayout.Width(inWidth));
+ foreach (var paramPair in module.Params) ParametersLayoutValue(module, paramPair);
+ GUILayout.EndVertical();
+ GUILayout.EndHorizontal();
+ GUILayout.EndVertical();
+ }
+
+ private static void ParametersLayoutValue(ModuleVrc3 module, KeyValuePair<string, Vrc3Param> paramPair)
+ {
+ if (module.Edit != paramPair.Key)
+ {
+ GmgLayoutHelper.GuiLabel(paramPair.Value.LabelTuple());
+ var rect = GUILayoutUtility.GetLastRect();
+ rect.x += rect.width - 15;
+ if (GUI.Toggle(rect, false, "")) module.Edit = paramPair.Key;
+ }
+ else paramPair.Value.FieldTuple(module);
+ }
+
+ private static void TrackingControlLayout(float width, Dictionary<VRCAvatarDescriptor.AnimLayerType, ModuleVrc3.LayerData> data, Dictionary<string, VRC_AnimatorTrackingControl.TrackingType> trackingControls, bool locomotionDisabled, bool poseSpace)
+ {
+ var inWidth = width / 2;
+ GUILayout.BeginVertical(GUILayout.Width(width));
+
+ GUILayout.Label("Tracking Control", GestureManagerStyles.GuiHandTitle, GUILayout.Width(width));
+ GUILayout.BeginHorizontal();
+ GUILayout.BeginVertical();
+ GUILayout.Label("Name", GestureManagerStyles.GuiDebugTitle, GUILayout.Width(inWidth));
+ foreach (var trackingPair in trackingControls) GUILayout.Label(trackingPair.Key, GUILayout.Width(inWidth));
+ GUILayout.EndVertical();
+
+ GUILayout.BeginVertical();
+ GUILayout.Label("Value", GestureManagerStyles.GuiDebugTitle, GUILayout.Width(inWidth));
+ foreach (var trackingPair in trackingControls) GmgLayoutHelper.GuiLabel(TrackingTuple(trackingPair.Value), GUILayout.Width(inWidth));
+ GUILayout.EndVertical();
+ GUILayout.EndHorizontal();
+
+ GUILayout.Label("Animation Controllers", GestureManagerStyles.GuiHandTitle, GUILayout.Width(width));
+ GUILayout.BeginHorizontal();
+ GUILayout.BeginVertical();
+ GUILayout.Label("Name", GestureManagerStyles.GuiDebugTitle, GUILayout.Width(inWidth));
+ foreach (var controllerPair in data) GUILayout.Label(controllerPair.Key.ToString(), GUILayout.Width(inWidth));
+ GUILayout.EndVertical();
+
+ GUILayout.BeginVertical();
+ GUILayout.Label("Weight", GestureManagerStyles.GuiDebugTitle, GUILayout.Width(inWidth));
+ foreach (var controllerPair in data) GUILayout.Label(controllerPair.Value.Weight.Weight.ToString("0.00"), GUILayout.Width(inWidth));
+ GUILayout.EndVertical();
+ GUILayout.EndHorizontal();
+
+ GUILayout.Label("Miscellaneous", GestureManagerStyles.GuiHandTitle, GUILayout.Width(width));
+ GUILayout.BeginHorizontal();
+ GUILayout.BeginVertical();
+ GUILayout.Label("Locomotion", GUILayout.Width(inWidth));
+ GUILayout.Label("Pose Space", GUILayout.Width(inWidth));
+ GUILayout.EndVertical();
+
+ GUILayout.BeginVertical();
+ GmgLayoutHelper.GuiLabel(LocomotionTuple(!locomotionDisabled), GUILayout.Width(inWidth));
+ GmgLayoutHelper.GuiLabel(PoseSpaceTuple(poseSpace), GUILayout.Width(inWidth));
+ GUILayout.EndVertical();
+ GUILayout.EndHorizontal();
+ GUILayout.EndVertical();
+ }
+
+ private static void AnimatorsLayout(float width, IReadOnlyDictionary<VRCAvatarDescriptor.AnimLayerType, ModuleVrc3.LayerData> data)
+ {
+ var inWidth = width / 3;
+ GUILayout.BeginVertical(GUILayout.Width(width));
+ GUILayout.Label("Animator States", GestureManagerStyles.GuiHandTitle, GUILayout.Width(width));
+ foreach (var sortPair in ModuleVrc3Styles.Data.SortValue.Where(sortPair => data.ContainsKey(sortPair.Key)))
+ {
+ GUILayout.Label(sortPair.Key.ToString(), GestureManagerStyles.GuiDebugTitle);
+ var animator = data[sortPair.Key].Playable;
+ var layerList = Enumerable.Range(0, animator.GetLayerCount()).ToList();
+
+ GUILayout.BeginHorizontal(EditorStyles.helpBox);
+ GUILayout.BeginVertical();
+ GUILayout.Label("Name", GestureManagerStyles.SubHeader, GUILayout.Width(inWidth));
+ foreach (var intLayer in layerList) GUILayout.Label(animator.GetLayerName(intLayer), GUILayout.Width(inWidth));
+ GUILayout.EndVertical();
+
+ GUILayout.BeginVertical();
+ GUILayout.Label("Weight", GestureManagerStyles.SubHeader, GUILayout.Width(inWidth));
+ foreach (var intLayer in layerList) GUILayout.Label(animator.GetLayerWeight(intLayer).ToString("0.00"), GUILayout.Width(inWidth));
+ GUILayout.EndVertical();
+
+ GUILayout.BeginVertical();
+ GUILayout.Label("State", GestureManagerStyles.SubHeader, GUILayout.Width(inWidth));
+ foreach (var infos in layerList.Select(intLayer => animator.GetCurrentAnimatorClipInfo(intLayer)))
+ GUILayout.Label(infos.Length == 0 ? "[UNKNOWN]" : infos[0].clip.name, GUILayout.Width(inWidth));
+
+ GUILayout.EndVertical();
+ GUILayout.EndHorizontal();
+ }
+
+ GUILayout.EndVertical();
+ }
+
+ private static (Color? color, string text) TrackingTuple(VRC_AnimatorTrackingControl.TrackingType trackingType)
+ {
+ switch (trackingType)
+ {
+ case VRC_AnimatorTrackingControl.TrackingType.NoChange:
+ return (Color.yellow, "No Change");
+ case VRC_AnimatorTrackingControl.TrackingType.Tracking:
+ return (Color.green, "Tracking");
+ case VRC_AnimatorTrackingControl.TrackingType.Animation:
+ return (Color.red, "Animation");
+ default: throw new ArgumentOutOfRangeException(nameof(trackingType), trackingType, null);
+ }
+ }
+
+ private static (Color? color, string text) LocomotionTuple(bool locomotion) => locomotion ? (Color.green, "Enabled") : (Color.red, "Disabled");
+
+ private static (Color? color, string text) PoseSpaceTuple(bool poseSpace) => poseSpace ? (Color.green, "Pose") : (Color.white, "Default");
+ }
+
+ internal static class Text
+ {
+ public static class W
+ {
+ public const string Subtitle = "The debug view is now floating,";
+ public const string Message = "♥ you can switch to other tabs now ♥";
+ public const string Hint = "or you can dock the window back in by using the button bellow!";
+ public const string Button = "Dock Debug Window";
+ }
+
+ public static class D
+ {
+ public const string Subtitle = "Select a category from the toolbar bellow.";
+ public const string Hint = "For an easier experience you can undock the window with the button bellow!";
+ public const string Button = "Undock Debug Window";
+ }
+ }
+ }
+}
+#endif \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3Debug/Avatar/Vrc3AvatarDebugWindow.cs.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3Debug/Avatar/Vrc3AvatarDebugWindow.cs.meta
new file mode 100644
index 00000000..1c10b610
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3Debug/Avatar/Vrc3AvatarDebugWindow.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 7a27b57b5d3b494d8581b9c988b4f507
+timeCreated: 1638146388 \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3Debug/Osc.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3Debug/Osc.meta
new file mode 100644
index 00000000..503414e3
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3Debug/Osc.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 09f426de76454ad591fd6ce4a8c73c67
+timeCreated: 1645742459 \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3Debug/Osc/Vrc3OscDebugWindow.cs b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3Debug/Osc/Vrc3OscDebugWindow.cs
new file mode 100644
index 00000000..d759685f
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3Debug/Osc/Vrc3OscDebugWindow.cs
@@ -0,0 +1,53 @@
+#if VRC_SDK_VRCSDK3
+using GestureManager.Scripts.Editor.Modules.Vrc3.OpenSoundControl.VisualElements;
+using UnityEditor;
+using UnityEngine;
+using UnityEngine.UIElements;
+
+namespace GestureManager.Scripts.Editor.Modules.Vrc3.Vrc3Debug.Osc
+{
+ public class Vrc3OscDebugWindow : EditorWindow
+ {
+ private ModuleVrc3 _source;
+ private Vector2 _scroll;
+
+ private VisualEpContainer _holder;
+ private VisualEpContainer Holder => _holder ?? (_holder = new VisualEpContainer());
+
+ internal static Vrc3OscDebugWindow Create(ModuleVrc3 source)
+ {
+ var instance = CreateInstance<Vrc3OscDebugWindow>();
+ instance.titleContent = new GUIContent("[Debug Window] Gesture Manager");
+ instance._source = source;
+ instance.Show();
+ return instance;
+ }
+
+ internal static Vrc3OscDebugWindow Close(Vrc3OscDebugWindow source)
+ {
+ source.Close();
+ return null;
+ }
+
+ private void OnGUI()
+ {
+ if (_source == null) Close();
+ else DebugGUI();
+ }
+
+ private void DebugGUI()
+ {
+ var isFullScreen = Screen.width > 1279;
+
+ _scroll = GUILayout.BeginScrollView(_scroll);
+ GUILayout.Label("Gesture Manager - Osc Debug Window", GestureManagerStyles.Header);
+ _source.DebugContext(rootVisualElement, Holder, 1, Screen.width - 60, isFullScreen);
+ GUILayout.Space(25);
+ GUILayout.EndScrollView();
+
+ Holder.style.top = new StyleLength(43 - _scroll.y);
+ if (_source.OscModule.ToolBar.Selected != 0) Holder.StopRendering();
+ }
+ }
+}
+#endif \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3Debug/Osc/Vrc3OscDebugWindow.cs.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3Debug/Osc/Vrc3OscDebugWindow.cs.meta
new file mode 100644
index 00000000..695bfcba
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3Debug/Osc/Vrc3OscDebugWindow.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 4a9b14d0c5194ceca5bd3158628d012d
+timeCreated: 1645742466 \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3DefaultParams.cs b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3DefaultParams.cs
new file mode 100644
index 00000000..43bba81f
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3DefaultParams.cs
@@ -0,0 +1,62 @@
+#if VRC_SDK_VRCSDK3
+using System.Collections.Generic;
+using UnityEngine;
+
+namespace GestureManager.Scripts.Editor.Modules.Vrc3
+{
+ public static class Vrc3DefaultParams
+ {
+ internal const string GestureRightWeight = "GestureRightWeight";
+ internal const string GestureLeftWeight = "GestureLeftWeight";
+ internal const string AvatarVersion = "AvatarVersion";
+ internal const string TrackingType = "TrackingType";
+ internal const string GestureRight = "GestureRight";
+ internal const string GestureLeft = "GestureLeft";
+ internal const string VelocityX = "VelocityX";
+ internal const string VelocityY = "VelocityY";
+ internal const string VelocityZ = "VelocityZ";
+ internal const string InStation = "InStation";
+ internal const string Grounded = "Grounded";
+ internal const string MuteSelf = "MuteSelf";
+ internal const string Upright = "Upright";
+ internal const string IsLocal = "IsLocal";
+ internal const string Seated = "Seated";
+ internal const string VRMode = "VRMode";
+ internal const string Vise = "Viseme";
+ internal const string Afk = "AFK";
+
+ private const string VrcFaceBlendH = "VrcFaceBlendH";
+ private const string VrcFaceBlendV = "VrcFaceBlendV";
+ private const string VrcEmote = "VRCEmote";
+ private const string AngularY = "AngularY";
+ private const string Voice = "Voice";
+
+ public static IEnumerable<(string name, AnimatorControllerParameterType type)> Parameters => new[]
+ {
+ (GestureRightWeight, AnimatorControllerParameterType.Float),
+ (GestureLeftWeight, AnimatorControllerParameterType.Float),
+ (VrcFaceBlendH, AnimatorControllerParameterType.Float),
+ (VrcFaceBlendV, AnimatorControllerParameterType.Float),
+ (AvatarVersion, AnimatorControllerParameterType.Int),
+ (TrackingType, AnimatorControllerParameterType.Int),
+ (GestureRight, AnimatorControllerParameterType.Int),
+ (GestureLeft, AnimatorControllerParameterType.Int),
+ (VelocityX, AnimatorControllerParameterType.Float),
+ (VelocityY, AnimatorControllerParameterType.Float),
+ (VelocityZ, AnimatorControllerParameterType.Float),
+ (InStation, AnimatorControllerParameterType.Bool),
+ (AngularY, AnimatorControllerParameterType.Float),
+ (Grounded, AnimatorControllerParameterType.Bool),
+ (MuteSelf, AnimatorControllerParameterType.Bool),
+ (VrcEmote, AnimatorControllerParameterType.Int),
+ (Upright, AnimatorControllerParameterType.Float),
+ (IsLocal, AnimatorControllerParameterType.Bool),
+ (Seated, AnimatorControllerParameterType.Bool),
+ (VRMode, AnimatorControllerParameterType.Int),
+ (Voice, AnimatorControllerParameterType.Float),
+ (Vise, AnimatorControllerParameterType.Int),
+ (Afk, AnimatorControllerParameterType.Bool)
+ };
+ }
+}
+#endif \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3DefaultParams.cs.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3DefaultParams.cs.meta
new file mode 100644
index 00000000..88f2b843
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3DefaultParams.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 0b5ba6e3f8dd4ec1aa8299657ef6b9f6
+timeCreated: 1646514550 \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3VisualRender.cs b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3VisualRender.cs
new file mode 100644
index 00000000..c2cadb92
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3VisualRender.cs
@@ -0,0 +1,39 @@
+#if VRC_SDK_VRCSDK3
+using UnityEngine;
+using UnityEngine.UIElements;
+
+namespace GestureManager.Scripts.Editor.Modules.Vrc3
+{
+ public abstract class Vrc3VisualRender : VisualElement
+ {
+ private bool _rendering;
+ internal Rect Rect;
+
+ public virtual void Render(VisualElement root, Rect rect)
+ {
+ if (Event.current.type != EventType.Layout && !root.Contains(this)) root.Add(this);
+ _rendering = true;
+
+ style.height = rect.height;
+ style.width = rect.width;
+ style.left = rect.x;
+ style.top = rect.y;
+ }
+
+ internal void StopRendering()
+ {
+ if (!_rendering) return;
+ _rendering = false;
+ parent?.Remove(this);
+ }
+
+ protected abstract bool RenderCondition(ModuleVrc3 module, RadialMenu meu);
+
+ public void CheckCondition(ModuleVrc3 module, RadialMenu menu)
+ {
+ if (!_rendering) return;
+ if (!RenderCondition(module, menu)) StopRendering();
+ }
+ }
+}
+#endif \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3VisualRender.cs.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3VisualRender.cs.meta
new file mode 100644
index 00000000..510d5bf9
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3VisualRender.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: f7766a4b7ab24fcc92542c9c201f022c
+timeCreated: 1635862973 \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3WeightController.cs b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3WeightController.cs
new file mode 100644
index 00000000..b3f7d781
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3WeightController.cs
@@ -0,0 +1,57 @@
+#if VRC_SDK_VRCSDK3
+using GestureManager.Scripts.Core.Editor;
+using UnityEngine;
+using UnityEngine.UIElements;
+
+namespace GestureManager.Scripts.Editor.Modules.Vrc3
+{
+ public class Vrc3WeightController
+ {
+ internal readonly ModuleVrc3 Module;
+
+ private readonly Vrc3WeightSlider _lWeight;
+ private readonly Vrc3WeightSlider _rWeight;
+ internal bool Dragging => _lWeight.Drag || _rWeight.Drag;
+ internal bool Active => _lWeight.Active || _rWeight.Active;
+
+ public Vrc3WeightController(ModuleVrc3 module)
+ {
+ Module = module;
+ _lWeight = new Vrc3WeightSlider(this, Vrc3DefaultParams.GestureLeftWeight);
+ _rWeight = new Vrc3WeightSlider(this, Vrc3DefaultParams.GestureRightWeight);
+ }
+
+ public void RenderLeft(VisualElement root)
+ {
+ GUILayoutUtility.GetRect(new GUIContent(), GUIStyle.none, GUILayout.ExpandWidth(true), GUILayout.Height(10));
+ _lWeight.Render(root, GmgLayoutHelper.GetLastRect(ref _lWeight.Rect));
+ _lWeight.ShowWeight();
+ }
+
+ public void RenderRight(VisualElement root)
+ {
+ GUILayoutUtility.GetRect(new GUIContent(), GUIStyle.none, GUILayout.ExpandWidth(true), GUILayout.Height(10));
+ _rWeight.Render(root, GmgLayoutHelper.GetLastRect(ref _rWeight.Rect));
+ _rWeight.ShowWeight();
+ }
+
+ public void CheckCondition(ModuleVrc3 module, RadialMenu menu)
+ {
+ _lWeight.CheckCondition(module, menu);
+ _rWeight.CheckCondition(module, menu);
+ }
+
+ public void StopRendering()
+ {
+ _lWeight.StopRendering();
+ _rWeight.StopRendering();
+ }
+
+ public void DisableDrag()
+ {
+ _lWeight.Drag = false;
+ _rWeight.Drag = false;
+ }
+ }
+}
+#endif \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3WeightController.cs.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3WeightController.cs.meta
new file mode 100644
index 00000000..dd4b5568
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3WeightController.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: ccfa11fc577343daa7ee84239d5eae41
+timeCreated: 1636076818 \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3WeightSlider.cs b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3WeightSlider.cs
new file mode 100644
index 00000000..7829730d
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3WeightSlider.cs
@@ -0,0 +1,149 @@
+#if VRC_SDK_VRCSDK3
+using UnityEngine;
+using UnityEngine.UIElements;
+using GestureManager.Scripts.Core.Editor;
+using UIEPosition = UnityEngine.UIElements.Position;
+
+namespace GestureManager.Scripts.Editor.Modules.Vrc3
+{
+ public class Vrc3WeightSlider : Vrc3VisualRender
+ {
+ private const float FadeSpeed = 0.02f;
+
+ private readonly Vrc3WeightController _controller;
+ private readonly string _target;
+
+ private VisualElement _slider;
+ private VisualElement _right;
+ private VisualElement _left;
+ private VisualElement _dot;
+
+ private VisualElement _textHolder;
+ private TextElement _textWeight;
+
+ internal bool Drag;
+ internal bool Active;
+
+ private int _afk;
+ private bool _out;
+ private float _weight = 1f;
+
+ private bool Xin(Vector2 p) => Rect.xMin < p.x && Rect.xMax > p.x;
+
+ public Vrc3WeightSlider(Vrc3WeightController controller, string target)
+ {
+ _controller = controller;
+ _target = target;
+
+ style.justifyContent = Justify.Center;
+ style.position = UIEPosition.Absolute;
+ pickingMode = PickingMode.Ignore;
+
+ CreateWeightController();
+ }
+
+ private void CreateWeightController()
+ {
+ _slider = this.MyAdd(new VisualElement { pickingMode = PickingMode.Ignore, style = { opacity = 0f, position = UIEPosition.Absolute, justifyContent = Justify.Center } });
+ _right = _slider.MyAdd(new VisualElement { pickingMode = PickingMode.Ignore, style = { backgroundColor = Color.gray, height = 10, position = UIEPosition.Absolute, right = 0 } }).MyBorder(1, 5, Color.black);
+ _left = _slider.MyAdd(new VisualElement { pickingMode = PickingMode.Ignore, style = { backgroundColor = RadialMenuUtility.Colors.CustomSelected, height = 10, width = 10, position = UIEPosition.Absolute, left = 0 } }).MyBorder(1, 5, Color.black);
+ _dot = _slider.MyAdd(RadialMenuUtility.Prefabs.NewCircle(16, RadialMenuUtility.Colors.RadialCenter, RadialMenuUtility.Colors.CustomMain, RadialMenuUtility.Colors.CustomBorder, UIEPosition.Absolute));
+
+ _textHolder = this.MyAdd(new VisualElement { pickingMode = PickingMode.Ignore, style = { opacity = 1f, position = UIEPosition.Absolute, justifyContent = Justify.Center, unityTextAlign = TextAnchor.MiddleCenter, alignItems = Align.Center, flexDirection = FlexDirection.Row } });
+ _textHolder.MyAdd(new TextElement { pickingMode = PickingMode.Ignore, text = "Weight: ", style = { fontSize = 12, height = 15 } });
+ _textWeight = _textHolder.MyAdd(new TextElement { pickingMode = PickingMode.Ignore, text = "100%", style = { fontSize = 15, color = RadialMenuUtility.Colors.CustomSelected, height = 15 } });
+ }
+
+ public override void Render(VisualElement root, Rect rect)
+ {
+ base.Render(root, rect);
+ if (Event.current.type == EventType.MouseUp) Drag = false;
+ style.width = style.width.value.value - 80;
+ style.left = rect.xMin + 40;
+ style.top = rect.y - 5;
+
+ _slider.style.width = style.width;
+ _textHolder.style.width = style.width;
+
+ HandleFade(Event.current.mousePosition);
+ if (!GUI.enabled) return;
+ if (Drag) Update(Event.current.mousePosition);
+ SetWeight(_controller.Module.GetParam(_target)?.Get() ?? 0f);
+
+ rect.center += new Vector2((rect.width - _slider.style.width.value.value) / 2 - 10, -10);
+ rect.width = _slider.style.width.value.value + 20f;
+ rect.height += 10f;
+ if (Event.current.type == EventType.MouseDown) CheckDragStart(rect);
+ }
+
+ private void CheckDragStart(Rect rect)
+ {
+ if (!rect.Contains(Event.current.mousePosition)) return;
+ _controller.DisableDrag();
+ Drag = true;
+ UpdatePosition(Event.current.mousePosition, false);
+ }
+
+ protected override bool RenderCondition(ModuleVrc3 module, RadialMenu menu) => menu.ToolBar.Selected == 0;
+
+ private void Update(Vector2 mousePosition)
+ {
+ if (Event.current.type == EventType.MouseDrag) UpdatePosition(mousePosition, false);
+ else if (!Xin(mousePosition)) UpdatePosition(mousePosition, true);
+ else if (_out && --_afk == 0) Drag = false;
+ }
+
+ private void UpdatePosition(Vector2 mousePosition, bool outer)
+ {
+ _out = outer;
+ var position = mousePosition.x - layout.x;
+ var weight = Mathf.Clamp(position / layout.width, 0f, 1f);
+ _controller.Module.GetParam(_target)?.Set(_controller.Module, weight);
+ _afk = 5;
+ }
+
+ private void HandleFade(Vector2 mousePosition)
+ {
+ if (GUI.enabled)
+ {
+ Active = _controller.Dragging || Mathf.Abs(mousePosition.y - Rect.y) < 16 && Xin(mousePosition);
+ if (_controller.Active) FadeOn();
+ else FadeOff();
+ }
+ else FadeDisable();
+ }
+
+ private void FadeDisable()
+ {
+ _slider.style.opacity = 0f;
+ _textHolder.style.opacity = 0.5f;
+ }
+
+ private void FadeOff()
+ {
+ _slider.style.opacity = Mathf.Clamp(_slider.style.opacity.value - FadeSpeed, 0f, 1f);
+ _textHolder.style.opacity = Mathf.Clamp(_textHolder.style.opacity.value + FadeSpeed, 0f, 1f);
+ }
+
+ private void FadeOn()
+ {
+ _slider.style.opacity = Mathf.Clamp(_slider.style.opacity.value + FadeSpeed, 0f, 1f);
+ _textHolder.style.opacity = Mathf.Clamp(_textHolder.style.opacity.value - FadeSpeed, 0f, 1f);
+ }
+
+ private void SetWeight(float weight)
+ {
+ _weight = weight;
+ ShowWeight();
+ }
+
+ internal void ShowWeight()
+ {
+ _left.style.width = _weight * style.width.value.value;
+ _right.style.width = style.width.value.value - _left.style.width.value.value;
+ _dot.style.left = _weight * style.width.value.value - 8;
+ _textWeight.text = (int)(_weight * 100f) + "%";
+ }
+ }
+}
+#endif \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3WeightSlider.cs.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3WeightSlider.cs.meta
new file mode 100644
index 00000000..cef105e0
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3WeightSlider.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: e9ef20b60613435eb111668a12dbdedf
+timeCreated: 1635863550 \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Extra.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Extra.meta
new file mode 100644
index 00000000..89e7b71a
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Extra.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: bb62ec31ea8d451aba8a723b5f51b222
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Extra/GestureHand.cs b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Extra/GestureHand.cs
new file mode 100644
index 00000000..9ece2b70
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Extra/GestureHand.cs
@@ -0,0 +1,8 @@
+namespace GestureManager.Scripts.Extra
+{
+ public enum GestureHand
+ {
+ Left,
+ Right
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Extra/GestureHand.cs.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Extra/GestureHand.cs.meta
new file mode 100644
index 00000000..bef78a32
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Extra/GestureHand.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: a64c1dae578847e7ac5944f19a1089d2
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Extra/ModuleBase.cs b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Extra/ModuleBase.cs
new file mode 100644
index 00000000..bb4aa59f
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Extra/ModuleBase.cs
@@ -0,0 +1,83 @@
+using System.Collections.Generic;
+using GmgAvatarDescriptor =
+#if VRC_SDK_VRCSDK2 || VRC_SDK_VRCSDK3
+ VRC.SDKBase.VRC_AvatarDescriptor;
+#else
+ UnityEngine.Component;
+#endif
+using UnityEngine;
+using UnityEngine.UIElements;
+
+namespace GestureManager.Scripts.Extra
+{
+ public abstract class ModuleBase
+ {
+ private readonly GmgAvatarDescriptor _avatarDescriptor;
+
+ private List<string> _errorList = new List<string>();
+ private List<string> _warningList = new List<string>();
+
+ public readonly GameObject Avatar;
+ public readonly Animator AvatarAnimator;
+ public readonly GestureManager Manager;
+
+ protected int Right, Left;
+ protected bool GestureDrag;
+
+ protected ModuleBase(GestureManager manager, GmgAvatarDescriptor avatarDescriptor)
+ {
+ _avatarDescriptor = avatarDescriptor;
+
+ Manager = manager;
+ Avatar = avatarDescriptor.gameObject;
+ AvatarAnimator = Avatar.GetComponent<Animator>();
+ }
+
+ public abstract void Update();
+ public abstract void LateUpdate();
+ public abstract void InitForAvatar();
+ public abstract void Unlink();
+ public abstract void EditorHeader();
+ public abstract void EditorContent(object editor, VisualElement element);
+ protected abstract void OnNewLeft(int left);
+ protected abstract void OnNewRight(int right);
+ public abstract AnimationClip GetFinalGestureByIndex(int gestureIndex);
+ public abstract Animator OnCustomAnimationPlay(AnimationClip clip);
+ public abstract bool HasGestureBeenOverridden(int gesture);
+ public abstract void AddGestureToOverrideController(int gestureIndex, AnimationClip newAnimation);
+
+ public virtual bool IsInvalid() => !Avatar || !AvatarAnimator || !_avatarDescriptor;
+
+ protected virtual List<string> CheckWarnings() => new List<string>();
+
+ protected virtual List<string> CheckErrors()
+ {
+ var errors = new List<string>();
+ if (GestureManager.ControlledAvatars.ContainsKey(Avatar)) errors.Add("- The avatar is already controlled by another Gesture Manager!");
+ if (!Avatar) errors.Add("- The GameObject has been deleted!");
+ else if (!Avatar.activeInHierarchy) errors.Add("- The GameObject is disabled!");
+ if (!AvatarAnimator) errors.Add("- The model doesn't have any animator!");
+ if (!_avatarDescriptor) errors.Add("- The VRC_AvatarDescriptor has been deleted!");
+ return errors;
+ }
+
+ public bool IsValidDesc()
+ {
+ _errorList = CheckErrors();
+ _warningList = CheckWarnings();
+ return _errorList.Count == 0;
+ }
+
+ public void OnNewHand(GestureHand hand, int i)
+ {
+ if (hand == GestureHand.Left) OnNewLeft(i);
+ else OnNewRight(i);
+ }
+
+ public bool IsPerfectDesc() => IsValidDesc() && _warningList.Count == 0;
+
+ public IEnumerable<string> GetErrors() => _errorList;
+
+ public IEnumerable<string> GetWarnings() => _warningList;
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Extra/ModuleBase.cs.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Extra/ModuleBase.cs.meta
new file mode 100644
index 00000000..c4f28840
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Extra/ModuleBase.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: aa66d7c17f2245a1b5b7a0f73b113582
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Extra/TransformData.cs b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Extra/TransformData.cs
new file mode 100644
index 00000000..58d59bb8
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Extra/TransformData.cs
@@ -0,0 +1,41 @@
+using UnityEngine;
+
+namespace GestureManager.Scripts.Extra
+{
+ public class TransformData
+ {
+ private readonly Vector3 _position;
+ private readonly Quaternion _rotation;
+ private readonly Vector3 _localScale;
+
+ private TransformData(Vector3 p, Quaternion r, Vector3 s)
+ {
+ _position = p;
+ _rotation = r;
+ _localScale = s;
+ }
+
+ public TransformData(Transform t) : this(t.position, t.rotation, t.localScale)
+ {
+ }
+
+ public void AddTo(Transform t)
+ {
+ t.position += _position;
+ t.rotation = _rotation * t.rotation;
+ t.localScale += _localScale;
+ }
+
+ public TransformData Difference(Transform t) => new TransformData(t.position - _position, t.rotation * Quaternion.Inverse(_rotation), t.localScale - _localScale);
+ }
+
+ public static class Extensions
+ {
+ public static void ApplyTo(this Transform s, Transform t)
+ {
+ t.position = s.position;
+ t.rotation = s.rotation;
+ t.localScale = s.lossyScale;
+ }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Extra/TransformData.cs.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Extra/TransformData.cs.meta
new file mode 100644
index 00000000..64a62110
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Extra/TransformData.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 290bf9bf376c43a38989c6eacea15bcd
+timeCreated: 1653852193 \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/GestureManager.cs b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/GestureManager.cs
new file mode 100644
index 00000000..a62ba95a
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/GestureManager.cs
@@ -0,0 +1,97 @@
+using System.Collections.Generic;
+using GestureManager.Scripts.Extra;
+using UnityEngine;
+
+namespace GestureManager.Scripts
+{
+ public class GestureManager : MonoBehaviour
+ {
+ public static readonly Dictionary<GameObject, ModuleBase> ControlledAvatars = new Dictionary<GameObject, ModuleBase>();
+ public static bool InWebClientRequest;
+
+ private TransformData _managerTransform;
+ private Animator _customAnimator;
+ private Transform _beforeEmote;
+ private bool _drag;
+
+ public List<ModuleBase> LastCheckedActiveModules = new List<ModuleBase>();
+ public ModuleBase Module;
+
+ public bool OnCustomAnimation { get; private set; }
+ public AnimationClip customAnim;
+
+ private void OnDisable() => UnlinkModule();
+
+ private void Update()
+ {
+ if (Module == null) return;
+ if (Module.IsInvalid()) UnlinkModule();
+ else ModuleUpdate();
+ }
+
+ private void ModuleUpdate()
+ {
+ if (_drag) _managerTransform.Difference(transform).AddTo(Module.Avatar.transform);
+ _managerTransform = new TransformData(transform);
+ Module.Update();
+ }
+
+ private void LateUpdate()
+ {
+ _managerTransform = new TransformData(transform);
+ Module?.LateUpdate();
+ }
+
+ public void SetDrag(bool drag) => _drag = drag;
+
+ public void UnlinkModule() => SetModule(null);
+
+ public void SetModule(ModuleBase module)
+ {
+ Module?.Unlink();
+ if (Module != null) ControlledAvatars.Remove(Module.Avatar);
+
+ Module = module;
+ Module?.Avatar.transform.ApplyTo(transform);
+ if (Module == null) return;
+
+ Module.InitForAvatar();
+ ControlledAvatars[module.Avatar] = module;
+ }
+
+ private void SaveCurrentStartEmotePosition() => _beforeEmote = _customAnimator.gameObject.transform;
+
+ private void RevertToEmotePosition() => _beforeEmote.ApplyTo(_customAnimator.gameObject.transform);
+
+ public void SetCustomAnimation(AnimationClip clip)
+ {
+ if (!OnCustomAnimation) customAnim = clip;
+ else if (!clip) StopCustomAnimation();
+ else PlayCustomAnimation(clip);
+ }
+
+ /*
+ * Events
+ */
+
+ public void PlayCustomAnimation(AnimationClip clip)
+ {
+ if (OnCustomAnimation) RevertToEmotePosition();
+ customAnim = clip;
+ OnCustomAnimation = true;
+ _customAnimator = Module.OnCustomAnimationPlay(customAnim);
+ SaveCurrentStartEmotePosition();
+ _customAnimator.applyRootMotion = true;
+ }
+
+ public void StopCustomAnimation()
+ {
+ OnCustomAnimation = false;
+ SetCustomAnimation(null);
+ _customAnimator = Module.OnCustomAnimationPlay(null);
+ if (!_customAnimator) return;
+ RevertToEmotePosition();
+ _customAnimator.applyRootMotion = false;
+ }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/GestureManager.cs.meta b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/GestureManager.cs.meta
new file mode 100644
index 00000000..cb0933c9
--- /dev/null
+++ b/VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/GestureManager.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 2398979b1d0d84349abc5ee9f0571350
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant: