From eb84bb298d2b95aec7b2ae12cbf25ac64f25379a Mon Sep 17 00:00:00 2001 From: tylermurphy534 Date: Sun, 6 Nov 2022 15:12:42 -0500 Subject: move to self host --- .../Resources/GestureManager/Scripts/Core.meta | 8 + .../GestureManager/Scripts/Core/Editor.meta | 8 + .../Scripts/Core/Editor/GmgAnimationHelper.cs | 38 + .../Scripts/Core/Editor/GmgAnimationHelper.cs.meta | 11 + .../Core/Editor/GmgAnimatorControllerHelper.cs | 48 ++ .../Editor/GmgAnimatorControllerHelper.cs.meta | 11 + .../Scripts/Core/Editor/GmgLayoutHelper.cs | 219 ++++++ .../Scripts/Core/Editor/GmgLayoutHelper.cs.meta | 11 + .../Core/Editor/GmgVisualElementExtensions.cs | 56 ++ .../Core/Editor/GmgVisualElementExtensions.cs.meta | 11 + .../Scripts/Core/GmgAvatarMaskHelper.cs | 37 + .../Scripts/Core/GmgAvatarMaskHelper.cs.meta | 11 + .../Scripts/Core/GmgVisualMeshHelper.cs | 21 + .../Scripts/Core/GmgVisualMeshHelper.cs.meta | 11 + .../Scripts/Core/VisualElements.meta | 8 + .../Core/VisualElements/GmgCircleElement.cs | 145 ++++ .../Core/VisualElements/GmgCircleElement.cs.meta | 11 + .../Core/VisualElements/GmgTmpRichTextElement.cs | 157 ++++ .../VisualElements/GmgTmpRichTextElement.cs.meta | 3 + .../Resources/GestureManager/Scripts/Editor.meta | 8 + .../Scripts/Editor/GestureManagerEditor.cs | 351 +++++++++ .../Scripts/Editor/GestureManagerEditor.cs.meta | 11 + .../Scripts/Editor/GestureManagerStyles.cs | 257 +++++++ .../Scripts/Editor/GestureManagerStyles.cs.meta | 11 + .../GestureManager/Scripts/Editor/Modules.meta | 8 + .../Scripts/Editor/Modules/ModuleHelper.cs | 32 + .../Scripts/Editor/Modules/ModuleHelper.cs.meta | 11 + .../Scripts/Editor/Modules/Vrc2.meta | 8 + .../Scripts/Editor/Modules/Vrc2/ModuleVrc2.cs | 450 +++++++++++ .../Scripts/Editor/Modules/Vrc2/ModuleVrc2.cs.meta | 11 + .../Scripts/Editor/Modules/Vrc3.meta | 8 + .../Modules/Vrc3/AnimatorControllerWeight.cs | 102 +++ .../Modules/Vrc3/AnimatorControllerWeight.cs.meta | 11 + .../Editor/Modules/Vrc3/AvatarDynamics.meta | 3 + .../AvatarDynamics/AnimParameterAccessAvatarGmg.cs | 39 + .../AnimParameterAccessAvatarGmg.cs.meta | 3 + .../Vrc3/AvatarDynamics/AvatarDynamicReset.cs | 49 ++ .../Vrc3/AvatarDynamics/AvatarDynamicReset.cs.meta | 3 + .../Scripts/Editor/Modules/Vrc3/AvatarTools.cs | 308 ++++++++ .../Editor/Modules/Vrc3/AvatarTools.cs.meta | 3 + .../Scripts/Editor/Modules/Vrc3/DummyModes.meta | 3 + .../Modules/Vrc3/DummyModes/Vrc3DummyMode.cs | 55 ++ .../Modules/Vrc3/DummyModes/Vrc3DummyMode.cs.meta | 3 + .../Editor/Modules/Vrc3/DummyModes/Vrc3EditMode.cs | 37 + .../Modules/Vrc3/DummyModes/Vrc3EditMode.cs.meta | 3 + .../Editor/Modules/Vrc3/DummyModes/Vrc3TestMode.cs | 40 + .../Modules/Vrc3/DummyModes/Vrc3TestMode.cs.meta | 3 + .../Scripts/Editor/Modules/Vrc3/ModuleVrc3.cs | 853 +++++++++++++++++++++ .../Scripts/Editor/Modules/Vrc3/ModuleVrc3.cs.meta | 11 + .../Editor/Modules/Vrc3/ModuleVrc3ProxyOverride.cs | 102 +++ .../Modules/Vrc3/ModuleVrc3ProxyOverride.cs.meta | 11 + .../Editor/Modules/Vrc3/ModuleVrc3Styles.cs | 184 +++++ .../Editor/Modules/Vrc3/ModuleVrc3Styles.cs.meta | 11 + .../Editor/Modules/Vrc3/OpenSoundControl.meta | 3 + .../Vrc3/OpenSoundControl/EndpointControl.cs | 94 +++ .../Vrc3/OpenSoundControl/EndpointControl.cs.meta | 3 + .../Modules/Vrc3/OpenSoundControl/OscModule.cs | 436 +++++++++++ .../Vrc3/OpenSoundControl/OscModule.cs.meta | 3 + .../Modules/Vrc3/OpenSoundControl/OscPacket.cs | 384 ++++++++++ .../Vrc3/OpenSoundControl/OscPacket.cs.meta | 3 + .../Modules/Vrc3/OpenSoundControl/OscSettings.cs | 257 +++++++ .../Vrc3/OpenSoundControl/OscSettings.cs.meta | 3 + .../Modules/Vrc3/OpenSoundControl/UdpListener.cs | 60 ++ .../Vrc3/OpenSoundControl/UdpListener.cs.meta | 3 + .../Modules/Vrc3/OpenSoundControl/UdpSender.cs | 33 + .../Vrc3/OpenSoundControl/UdpSender.cs.meta | 3 + .../Vrc3/OpenSoundControl/VisualElements.meta | 3 + .../OpenSoundControl/VisualElements/VisualEp.cs | 117 +++ .../VisualElements/VisualEp.cs.meta | 3 + .../VisualElements/VisualEpContainer.cs | 33 + .../VisualElements/VisualEpContainer.cs.meta | 3 + .../VisualElements/VisualEpStyles.cs | 77 ++ .../VisualElements/VisualEpStyles.cs.meta | 3 + .../Scripts/Editor/Modules/Vrc3/Params.meta | 8 + .../Editor/Modules/Vrc3/Params/Vrc3Param.cs | 151 ++++ .../Editor/Modules/Vrc3/Params/Vrc3Param.cs.meta | 11 + .../Editor/Modules/Vrc3/Params/Vrc3ParamBool.cs | 27 + .../Modules/Vrc3/Params/Vrc3ParamBool.cs.meta | 11 + .../Modules/Vrc3/Params/Vrc3ParamController.cs | 56 ++ .../Vrc3/Params/Vrc3ParamController.cs.meta | 11 + .../Modules/Vrc3/Params/Vrc3ParamExternal.cs | 39 + .../Modules/Vrc3/Params/Vrc3ParamExternal.cs.meta | 11 + .../Scripts/Editor/Modules/Vrc3/RadialButtons.meta | 8 + .../Modules/Vrc3/RadialButtons/Dynamics.meta | 8 + .../RadialButtons/Dynamics/VisualRadialElement.cs | 22 + .../Dynamics/VisualRadialElement.cs.meta | 11 + .../RadialButtons/Dynamics/VisualRunningElement.cs | 43 ++ .../Dynamics/VisualRunningElement.cs.meta | 11 + .../Modules/Vrc3/RadialButtons/RadialMenuButton.cs | 31 + .../Vrc3/RadialButtons/RadialMenuButton.cs.meta | 11 + .../Vrc3/RadialButtons/RadialMenuControl.cs | 110 +++ .../Vrc3/RadialButtons/RadialMenuControl.cs.meta | 11 + .../Vrc3/RadialButtons/RadialMenuDynamic.cs | 94 +++ .../Vrc3/RadialButtons/RadialMenuDynamic.cs.meta | 11 + .../Modules/Vrc3/RadialButtons/RadialMenuItem.cs | 47 ++ .../Vrc3/RadialButtons/RadialMenuItem.cs.meta | 11 + .../Scripts/Editor/Modules/Vrc3/RadialCursor.cs | 157 ++++ .../Editor/Modules/Vrc3/RadialCursor.cs.meta | 11 + .../Editor/Modules/Vrc3/RadialDescription.cs | 43 ++ .../Editor/Modules/Vrc3/RadialDescription.cs.meta | 11 + .../Scripts/Editor/Modules/Vrc3/RadialMenu.cs | 433 +++++++++++ .../Scripts/Editor/Modules/Vrc3/RadialMenu.cs.meta | 11 + .../Editor/Modules/Vrc3/RadialMenuUtility.cs | 373 +++++++++ .../Editor/Modules/Vrc3/RadialMenuUtility.cs.meta | 11 + .../Scripts/Editor/Modules/Vrc3/RadialPage.cs | 38 + .../Scripts/Editor/Modules/Vrc3/RadialPage.cs.meta | 11 + .../Scripts/Editor/Modules/Vrc3/RadialPuppets.meta | 8 + .../Editor/Modules/Vrc3/RadialPuppets/Base.meta | 8 + .../Vrc3/RadialPuppets/Base/BaseAxisPuppet.cs | 47 ++ .../Vrc3/RadialPuppets/Base/BaseAxisPuppet.cs.meta | 11 + .../Modules/Vrc3/RadialPuppets/Base/BasePuppet.cs | 29 + .../Vrc3/RadialPuppets/Base/BasePuppet.cs.meta | 11 + .../Modules/Vrc3/RadialPuppets/FourAxisPuppet.cs | 24 + .../Vrc3/RadialPuppets/FourAxisPuppet.cs.meta | 11 + .../Modules/Vrc3/RadialPuppets/RadialPuppet.cs | 90 +++ .../Vrc3/RadialPuppets/RadialPuppet.cs.meta | 11 + .../Modules/Vrc3/RadialPuppets/TwoAxisPuppet.cs | 22 + .../Vrc3/RadialPuppets/TwoAxisPuppet.cs.meta | 11 + .../Scripts/Editor/Modules/Vrc3/Vrc3Debug.meta | 3 + .../Editor/Modules/Vrc3/Vrc3Debug/Avatar.meta | 3 + .../Vrc3/Vrc3Debug/Avatar/Vrc3AvatarDebugWindow.cs | 253 ++++++ .../Vrc3Debug/Avatar/Vrc3AvatarDebugWindow.cs.meta | 3 + .../Scripts/Editor/Modules/Vrc3/Vrc3Debug/Osc.meta | 3 + .../Vrc3/Vrc3Debug/Osc/Vrc3OscDebugWindow.cs | 53 ++ .../Vrc3/Vrc3Debug/Osc/Vrc3OscDebugWindow.cs.meta | 3 + .../Editor/Modules/Vrc3/Vrc3DefaultParams.cs | 62 ++ .../Editor/Modules/Vrc3/Vrc3DefaultParams.cs.meta | 3 + .../Editor/Modules/Vrc3/Vrc3VisualRender.cs | 39 + .../Editor/Modules/Vrc3/Vrc3VisualRender.cs.meta | 3 + .../Editor/Modules/Vrc3/Vrc3WeightController.cs | 57 ++ .../Modules/Vrc3/Vrc3WeightController.cs.meta | 3 + .../Editor/Modules/Vrc3/Vrc3WeightSlider.cs | 149 ++++ .../Editor/Modules/Vrc3/Vrc3WeightSlider.cs.meta | 3 + .../Resources/GestureManager/Scripts/Extra.meta | 8 + .../GestureManager/Scripts/Extra/GestureHand.cs | 8 + .../Scripts/Extra/GestureHand.cs.meta | 11 + .../GestureManager/Scripts/Extra/ModuleBase.cs | 83 ++ .../Scripts/Extra/ModuleBase.cs.meta | 11 + .../GestureManager/Scripts/Extra/TransformData.cs | 41 + .../Scripts/Extra/TransformData.cs.meta | 3 + .../GestureManager/Scripts/GestureManager.cs | 97 +++ .../GestureManager/Scripts/GestureManager.cs.meta | 11 + 142 files changed, 8401 insertions(+) create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/Editor.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/Editor/GmgAnimationHelper.cs create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/Editor/GmgAnimationHelper.cs.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/Editor/GmgAnimatorControllerHelper.cs create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/Editor/GmgAnimatorControllerHelper.cs.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/Editor/GmgLayoutHelper.cs create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/Editor/GmgLayoutHelper.cs.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/Editor/GmgVisualElementExtensions.cs create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/Editor/GmgVisualElementExtensions.cs.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/GmgAvatarMaskHelper.cs create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/GmgAvatarMaskHelper.cs.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/GmgVisualMeshHelper.cs create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/GmgVisualMeshHelper.cs.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/VisualElements.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/VisualElements/GmgCircleElement.cs create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/VisualElements/GmgCircleElement.cs.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/VisualElements/GmgTmpRichTextElement.cs create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Core/VisualElements/GmgTmpRichTextElement.cs.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/GestureManagerEditor.cs create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/GestureManagerEditor.cs.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/GestureManagerStyles.cs create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/GestureManagerStyles.cs.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/ModuleHelper.cs create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/ModuleHelper.cs.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc2.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc2/ModuleVrc2.cs create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc2/ModuleVrc2.cs.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/AnimatorControllerWeight.cs create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/AnimatorControllerWeight.cs.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/AvatarDynamics.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/AvatarDynamics/AnimParameterAccessAvatarGmg.cs create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/AvatarDynamics/AnimParameterAccessAvatarGmg.cs.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/AvatarDynamics/AvatarDynamicReset.cs create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/AvatarDynamics/AvatarDynamicReset.cs.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/AvatarTools.cs create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/AvatarTools.cs.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/DummyModes.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/DummyModes/Vrc3DummyMode.cs create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/DummyModes/Vrc3DummyMode.cs.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/DummyModes/Vrc3EditMode.cs create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/DummyModes/Vrc3EditMode.cs.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/DummyModes/Vrc3TestMode.cs create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/DummyModes/Vrc3TestMode.cs.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/ModuleVrc3.cs create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/ModuleVrc3.cs.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/ModuleVrc3ProxyOverride.cs create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/ModuleVrc3ProxyOverride.cs.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/ModuleVrc3Styles.cs create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/ModuleVrc3Styles.cs.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/EndpointControl.cs create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/EndpointControl.cs.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/OscModule.cs create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/OscModule.cs.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/OscPacket.cs create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/OscPacket.cs.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/OscSettings.cs create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/OscSettings.cs.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/UdpListener.cs create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/UdpListener.cs.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/UdpSender.cs create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/UdpSender.cs.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/VisualElements.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/VisualElements/VisualEp.cs create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/VisualElements/VisualEp.cs.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/VisualElements/VisualEpContainer.cs create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/VisualElements/VisualEpContainer.cs.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/VisualElements/VisualEpStyles.cs create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/OpenSoundControl/VisualElements/VisualEpStyles.cs.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Params.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Params/Vrc3Param.cs create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Params/Vrc3Param.cs.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Params/Vrc3ParamBool.cs create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Params/Vrc3ParamBool.cs.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Params/Vrc3ParamController.cs create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Params/Vrc3ParamController.cs.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Params/Vrc3ParamExternal.cs create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Params/Vrc3ParamExternal.cs.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialButtons.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialButtons/Dynamics.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialButtons/Dynamics/VisualRadialElement.cs create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialButtons/Dynamics/VisualRadialElement.cs.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialButtons/Dynamics/VisualRunningElement.cs create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialButtons/Dynamics/VisualRunningElement.cs.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialButtons/RadialMenuButton.cs create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialButtons/RadialMenuButton.cs.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialButtons/RadialMenuControl.cs create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialButtons/RadialMenuControl.cs.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialButtons/RadialMenuDynamic.cs create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialButtons/RadialMenuDynamic.cs.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialButtons/RadialMenuItem.cs create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialButtons/RadialMenuItem.cs.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialCursor.cs create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialCursor.cs.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialDescription.cs create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialDescription.cs.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialMenu.cs create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialMenu.cs.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialMenuUtility.cs create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialMenuUtility.cs.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialPage.cs create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialPage.cs.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialPuppets.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialPuppets/Base.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialPuppets/Base/BaseAxisPuppet.cs create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialPuppets/Base/BaseAxisPuppet.cs.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialPuppets/Base/BasePuppet.cs create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialPuppets/Base/BasePuppet.cs.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialPuppets/FourAxisPuppet.cs create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialPuppets/FourAxisPuppet.cs.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialPuppets/RadialPuppet.cs create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialPuppets/RadialPuppet.cs.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialPuppets/TwoAxisPuppet.cs create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/RadialPuppets/TwoAxisPuppet.cs.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3Debug.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3Debug/Avatar.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3Debug/Avatar/Vrc3AvatarDebugWindow.cs create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3Debug/Avatar/Vrc3AvatarDebugWindow.cs.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3Debug/Osc.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3Debug/Osc/Vrc3OscDebugWindow.cs create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3Debug/Osc/Vrc3OscDebugWindow.cs.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3DefaultParams.cs create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3DefaultParams.cs.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3VisualRender.cs create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3VisualRender.cs.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3WeightController.cs create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3WeightController.cs.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3WeightSlider.cs create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Editor/Modules/Vrc3/Vrc3WeightSlider.cs.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Extra.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Extra/GestureHand.cs create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Extra/GestureHand.cs.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Extra/ModuleBase.cs create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Extra/ModuleBase.cs.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Extra/TransformData.cs create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/Extra/TransformData.cs.meta create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/GestureManager.cs create mode 100644 VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts/GestureManager.cs.meta (limited to 'VRCSDK3AvatarsQuest/Assets/Resources/GestureManager/Scripts') 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> GetOverrides(AnimatorOverrideController overrideController) + { + var overrides = new List>(); + overrideController.GetOverrides(overrides); + return overrides; + } + + public static AnimatorController CreateControllerWith(IEnumerable 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 GetInspectorWindows() => Resources.FindObjectsOfTypeAll().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 initialText, T2 argument, Rect rect, Func field, Action 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(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(string label, T unityObject, Action onObjectSet) where T : UnityEngine.Object + { + if (unityObject != (unityObject = ObjectField(label, unityObject))) onObjectSet(unityObject); + return unityObject; + } + + public static bool ButtonObjectField(string label, T unityObject, char text, Action 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(T2 t2, float width, float sizeWidth, float sizeHeight, float divisor, IList data, Action 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(this VisualElement visualElement, T child) where T : VisualElement + { + visualElement.Add(child); + return child; + } + + public static T With(this T visualElement, VisualElement child) where T : VisualElement + { + visualElement.Add(child); + return visualElement; + } + + public static T MyBorder(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 BodyParts = Enum.GetValues(typeof(AvatarMaskBodyPart)).Cast().Where(part => part != AvatarMaskBodyPart.LastBodyPart).ToList(); + + public static AvatarMask CreateMaskWith(string name, IEnumerable 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 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()); + } +} \ 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 TextMeshProColorNames => new Dictionary + { + { "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 Mark = new List(); + internal readonly List TextColor = new List(); + internal readonly List Size = new List(); + + private static bool HandleList(IList list, bool close, string attribute, Func, 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 list, bool close, string attribute) => HandleList(list, close, attribute, TryAddColor); + + public static bool HandleStyleLengthList(IList list, bool close, string attribute) => HandleList(list, close, attribute, TryAddStyleLength); + + private static bool TryAddStyleLength(ICollection 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 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().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 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 Descriptors => FindSceneObjectsOfTypeAll(); + private IEnumerable 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 FindSceneObjectsOfTypeAll() where T : Component => Resources.FindObjectsOfTypeAll().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("Assets/GestureManager/GestureManager.prefab"); + if (!asset) asset = AssetDatabase.LoadAssetAtPath(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() : null); + } + + public static void CreateAndPing(GestureManager manager = null) + { + if (!manager) manager = new GameObject("GestureManager").AddComponent(); + Selection.activeObject = manager; + EditorGUIUtility.PingObject(manager); + } + + private void Awake() + { + if (!Manager.gameObject.GetComponent()) 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 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(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 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 Get(string uri) + { + try + { + return await new WebClient().DownloadStringTaskAsync(uri); + } + catch (WebException) + { + return null; + } + } + + private static async Task GetVersion() => await Get(VersionURL); + private static async Task 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("Gm/BSX_GM_PlusSign"); + private static Texture PlusTexturePro => _plusTexturePro ? _plusTexturePro : _plusTexturePro = Resources.Load("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(Path + Data.GestureNames[1]); + public static AnimationClip Open => _open ? _open : _open = Resources.Load(Path + Data.GestureNames[2]); + public static AnimationClip Point => _point ? _point : _point = Resources.Load(Path + Data.GestureNames[3]); + public static AnimationClip Peace => _peace ? _peace : _peace = Resources.Load(Path + Data.GestureNames[4]); + public static AnimationClip Rock => _rock ? _rock : _rock = Resources.Load(Path + Data.GestureNames[5]); + public static AnimationClip Gun => _run ? _run : _run = Resources.Load(Path + Data.GestureNames[6]); + public static AnimationClip ThumbsUp => _thumbsUp ? _thumbsUp : _thumbsUp = Resources.Load(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(Path + Data.EmoteStandingName[0]); + public static AnimationClip Clap => _clap ? _clap : _clap = Resources.Load(Path + Data.EmoteStandingName[1]); + public static AnimationClip Point => _point ? _point : _point = Resources.Load(Path + Data.EmoteStandingName[2]); + public static AnimationClip Cheer => _cheer ? _cheer : _cheer = Resources.Load(Path + Data.EmoteStandingName[3]); + public static AnimationClip Dance => _dance ? _dance : _dance = Resources.Load(Path + Data.EmoteStandingName[4]); + public static AnimationClip BackFlip => _backFlip ? _backFlip : _backFlip = Resources.Load(Path + Data.EmoteStandingName[5]); + public static AnimationClip Die => _die ? _die : _die = Resources.Load(Path + Data.EmoteStandingName[6]); + public static AnimationClip SadKick => _sadKick ? _sadKick : _sadKick = Resources.Load(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(Path + Data.EmoteSeatedName[0]); + public static AnimationClip Point => _point ? _point : _point = Resources.Load(Path + Data.EmoteSeatedName[1]); + public static AnimationClip RaiseHand => _raiseHand ? _raiseHand : _raiseHand = Resources.Load(Path + Data.EmoteSeatedName[2]); + public static AnimationClip Drum => _drum ? _drum : _drum = Resources.Load(Path + Data.EmoteSeatedName[3]); + public static AnimationClip Clap => _clap ? _clap : _clap = Resources.Load(Path + Data.EmoteSeatedName[4]); + public static AnimationClip ShakeFist => _shakeFist ? _shakeFist : _shakeFist = Resources.Load(Path + Data.EmoteSeatedName[5]); + public static AnimationClip Disbelief => _disbelief ? _disbelief : _disbelief = Resources.Load(Path + Data.EmoteSeatedName[6]); + public static AnimationClip Disapprove => _disapprove ? _disapprove : _disapprove = Resources.Load(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()); + + 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 _hasBeenOverridden; + + private AnimationClip _selectingCustomAnim; + private GmgLayoutHelper.Toolbar _toolBar; + + private RuntimeAnimatorController StandingControllerPreset => _standingControllerPreset ? _standingControllerPreset : _standingControllerPreset = Resources.Load("Vrc2/StandingEmoteTestingTemplate"); + private RuntimeAnimatorController SeatedControllerPreset => _seatedControllerPreset ? _seatedControllerPreset : _seatedControllerPreset = Resources.Load("Vrc2/SeatedEmoteTestingTemplate"); + + private Dictionary _lastBoneQuaternions; + + private int _controlDelay; + private static IEnumerable Bones => Enum.GetValues(typeof(HumanBodyBones)).Cast(); + + [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 _myTranslateDictionary; + + private Dictionary TranslateDictionary => _myTranslateDictionary ?? (_myTranslateDictionary = new Dictionary + { + { _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 _whiteListedControlBones; + private IEnumerable WhiteListedControlBones => _whiteListedControlBones ?? (_whiteListedControlBones = Bones.Where(bones => !_blackListedControlBones.Contains(bones))); + + private readonly List _blackListedControlBones = new List + { + // 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 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 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 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(); + 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> + { + new KeyValuePair(_myRuntimeOverrideController["[EXTRA] CustomAnimation"], Manager.customAnim) + }; + + var validOverrides = new List>(); + foreach (var pair in GmgAnimatorControllerHelper.GetOverrides(_originalUsingOverrideController).Where(keyValuePair => keyValuePair.Value)) + { + try + { + validOverrides.Add(new KeyValuePair(_myRuntimeOverrideController[TranslateDictionary[pair.Key.name]], pair.Value)); + } + catch (Exception) + { + // ignored + } + } + + finalOverride.AddRange(validOverrides); + + _hasBeenOverridden = new Dictionary(); + 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 _subCompleted = new List(); + private readonly Dictionary _subControls = new Dictionary(); + 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 _onComplete; + private readonly float _startWeight; + private readonly float _startTime; + + public SubTimer(VRC_AnimatorLayerControl control, float startWeight, float startTime, Action 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 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(); + foreach (var contact in Resources.FindObjectsOfTypeAll()) RecreateComponent(contact); + } + + private static void ResumePhysBoneManager() + { + Object.DestroyImmediate(GameObject.Find(PhysBoneManagerName)); + var obj = new GameObject(PhysBoneManagerName); + Object.DontDestroyOnLoad(obj); + obj.AddComponent(); + PhysBoneManager.Inst.IsSDK = true; + PhysBoneManager.Inst.Init(); + obj.AddComponent(); + foreach (var physBone in Resources.FindObjectsOfTypeAll()) 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 _activeContact = new HashSet(); + + 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, " "); + } + } + + 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 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 clips) : base(module, "[Edit-Mode]") + { + Avatar.GetOrAddComponent().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.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 _weightControllers = new Dictionary(); + private readonly Dictionary _oscContainers = new Dictionary(); + private readonly Dictionary _radialMenus = new Dictionary(); + + private readonly Dictionary _layers = new Dictionary(); + private readonly List _brokenLayers = new List(); + internal readonly Dictionary Params = new Dictionary(); + + internal bool LocomotionDisabled; + internal bool PoseSpace; + + internal readonly Dictionary TrackingControls = ModuleVrc3Styles.Data.DefaultTrackingState; + internal readonly HashSet Receivers = new HashSet(); + private readonly HashSet _avatarClips = new HashSet(); + private readonly HashSet _senders = new HashSet(); + + internal int DebugToolBar; + internal string Edit; + + private IEnumerable OriginalClips => _avatarClips.Where(clip => !clip.name.StartsWith("proxy_")); + private VRCExpressionParameters Parameters => _avatarDescriptor.expressionParameters; + private VRCExpressionsMenu Menu => _avatarDescriptor.expressionsMenu; + internal IEnumerable 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()) PhysBoneBaseSetup(physBone); + foreach (var receiver in AvatarComponents()) ReceiverBaseSetup(receiver); + foreach (var sender in AvatarComponents()) 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 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 pair) => !pair.Value.Empty && pair.Value.Playable.GetInput(0).IsNull(); + + private IEnumerable AvatarComponents(bool includeInactive = true) where T : Component => Avatar.GetComponentsInChildren(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().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 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()?.GetComponent(); + 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()?.GetComponent(); + 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 OverrideClip(AnimationClip clip) + { + OverrideOf.TryGetValue(clip.name, out var oClip); + return new KeyValuePair(clip, oClip); + } + + private static Dictionary _overrideOf; + + [SuppressMessage("ReSharper", "StringLiteralTypo")] + private static Dictionary OverrideOf => _overrideOf ?? (_overrideOf = new Dictionary + { + // 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("Vrc3/BSX_GM_Emojis"); + internal static Texture2D Option => _option ? _option : _option = Resources.Load("Vrc3/BSX_GM_Option"); + internal static Texture2D Expressions => _expressions ? _expressions : _expressions = Resources.Load("Vrc3/BSX_GM_Expressions"); + internal static Texture2D NoExpressions => _noExpressions ? _noExpressions : _noExpressions = Resources.Load("Vrc3/BSX_GM_No_Expressions"); + internal static Texture2D Back => _back ? _back : _back = Resources.Load("Vrc3/BSX_GM_Back"); + internal static Texture2D BackHome => _backHome ? _backHome : _backHome = Resources.Load("Vrc3/BSX_GM_BackHome"); + internal static Texture2D Default => _default ? _default : _default = Resources.Load("Vrc3/BSX_GM_Default"); + internal static Texture2D Reset => _reset ? _reset : _reset = Resources.Load("Vrc3/BSX_GM_Reset"); + internal static Texture2D TwoAxis => _twoAxis ? _twoAxis : _twoAxis = Resources.Load("Vrc3/BSX_GM_2_Axis"); + internal static Texture2D FourAxis => _fourAxis ? _fourAxis : _fourAxis = Resources.Load("Vrc3/BSX_GM_4_Axis"); + internal static Texture2D Radial => _radial ? _radial : _radial = Resources.Load("Vrc3/BSX_GM_Radial"); + internal static Texture2D Toggle => _toggle ? _toggle : _toggle = Resources.Load("Vrc3/BSX_GM_Toggle"); + internal static Texture2D RunningParam => _runningParam ? _runningParam : _runningParam = Resources.Load("Vrc3/BSX_GM_Running_Param"); + internal static Texture2D AxisUp => _axisUp ? _axisUp : _axisUp = Resources.Load("Vrc3/BSX_GM_Axis_Up"); + internal static Texture2D AxisRight => _axisRight ? _axisRight : _axisRight = Resources.Load("Vrc3/BSX_GM_Axis_Right"); + internal static Texture2D AxisDown => _axisDown ? _axisDown : _axisDown = Resources.Load("Vrc3/BSX_GM_Axis_Down"); + internal static Texture2D AxisLeft => _axisLeft ? _axisLeft : _axisLeft = Resources.Load("Vrc3/BSX_GM_Axis_Left"); + internal static Texture2D SupportLike => _supportLike ? _supportLike : _supportLike = Resources.Load("Vrc3/BSX_GM_Support_Like"); + internal static Texture2D SupportHeart => _supportHeart ? _supportHeart : _supportHeart = Resources.Load("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(VrcSdk3ControllerPath + path); + + private static TextAsset RestoreOfPath(string path) => Resources.Load(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 NameOf = new Dictionary + { + { 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 AnimatorToLayer = new Dictionary + { + { BlendableAnimatorLayer.FX, AnimLayerType.FX }, + { BlendableAnimatorLayer.Action, AnimLayerType.Action }, + { BlendableAnimatorLayer.Gesture, AnimLayerType.Gesture }, + { BlendableAnimatorLayer.Additive, AnimLayerType.Additive } + }; + + internal static readonly Dictionary PlayableToLayer = new Dictionary + { + { BlendablePlayableLayer.FX, AnimLayerType.FX }, + { BlendablePlayableLayer.Action, AnimLayerType.Action }, + { BlendablePlayableLayer.Gesture, AnimLayerType.Gesture }, + { BlendablePlayableLayer.Additive, AnimLayerType.Additive } + }; + + internal static readonly Dictionary SortValue = new Dictionary + { + { 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 MaskOf = new Dictionary + { + { 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 TypeOf = new Dictionary + { + { 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 DefaultTrackingState => new Dictionary + { + { "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 _visualElements; + + public EndpointControl(string horizontalEndpoint, IList chronological) + { + chronological.Insert(0, this); + + _visualElements = new List(); + 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 _queue = new List(); + + 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 Messages => _messages.Select(tuple => new OscPacket.Message(tuple.address, tuple.data)); + private readonly List<(string address, List data)> _messages = new List<(string address, List 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 _dataDictionary = new Dictionary(); + private readonly List _chronological = new List(); + + 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())); + + 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 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 GetMessages(byte[] oscData) => oscData[0] == '#' ? ParseBundle(oscData)._messages : new[] { Message.ParseMessage(oscData) }; + + private readonly ulong _timeTag; + private readonly IList _messages; + + internal OscPacket(ulong timeTag, IList 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(); + 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 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 msg, ref int index) => GetInternalInt(msg, index += 4); + + private static int GetInternalInt(IReadOnlyList 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 msg, ref int index) => GetInternalULong(msg, index += 8); + + private static ulong GetInternalULong(IReadOnlyList 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 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 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 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 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[] 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 Arguments; + + public Message(string address, IList arguments) + { + Address = address; + Arguments = arguments; + } + + public Message(string address, object arg) : this(address, new[] { arg }) + { + } + + public byte[] GetBytes() + { + var lists = new List<(IList, int)>(); + + var parts = new List(); + 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 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>(); + var arguments = new List(); + + 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(); + 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(); + 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(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> execute)> Input = new Dictionary> execute)>(); + internal readonly Dictionary Output = new Dictionary(); + } + + [Serializable] + public class OscFile + { + public Parameter[] parameters; + + public IEnumerable InputParameters => parameters.Where(parameter => parameter.input.address != null).ToList(); + public IEnumerable 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 _onData; + + public UdpListener(int port, Action 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 _elements = new Dictionary(); + + 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 _converted; + protected readonly int HashId; + public readonly string Name; + + internal Action 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 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 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 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() : 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 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 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 _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 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 _menuPath = new List(); + private readonly List _selectionTuple = new List(); + + 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 controls) + { + _menuPath.Add(new RadialPage(this, controls)); + SetCustom(controls); + } + + internal void SetCustom(IReadOnlyList 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 create) => SetButtons(count, 10, create); + + private void SetButtons(int count, int max, Func 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(), 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 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 menus) + { + if (!menu || menus.Contains(menu)) return; + menus.Add(menu); + foreach (var control in menu.controls) AppendMenus(control.subMenu, menus); + } + + private static IEnumerable GetMenus(VRCExpressionsMenu menu) + { + var menus = new List(); + AppendMenus(menu, menus); + return menus; + } + + private static IEnumerable GetParams(IEnumerable menus) + { + var paramList = new HashSet(); + 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 CheckErrors(VRCExpressionsMenu menu, VRCExpressionParameters param) + { + var errors = new List(); + 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 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 _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 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(); + 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 data) + { + if (fullscreen) DebugLayoutFullScreen(module, width, data); + else DebugLayoutCompact(module, width, data); + } + + private static void DebugLayoutCompact(ModuleVrc3 module, float width, Dictionary 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 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 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 data, Dictionary 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 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(); + 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 _errorList = new List(); + private List _warningList = new List(); + + 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(); + } + + 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 CheckWarnings() => new List(); + + protected virtual List CheckErrors() + { + var errors = new List(); + 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 GetErrors() => _errorList; + + public IEnumerable 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 ControlledAvatars = new Dictionary(); + public static bool InWebClientRequest; + + private TransformData _managerTransform; + private Animator _customAnimator; + private Transform _beforeEmote; + private bool _drag; + + public List LastCheckedActiveModules = new List(); + 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: -- cgit v1.2.3-freya