diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..454d664 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 SDraw + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 6a75221..5c860ea 100644 --- a/README.md +++ b/README.md @@ -3,22 +3,23 @@ Merged set of MelonLoader mods for ChilloutVR. **Table for game build 2023r173:** | Full name | Short name | Latest version | Available in [CVRMA](https://github.com/knah/CVRMelonAssistant) | |:---------:|:----------:|:--------------:| :----------------------------------------------------------------| -| [Avatar Motion Tweaker](/ml_amt/README.md) | ml_amt | 1.3.6 [:arrow_down:](../../releases/latest/download/ml_amt.dll)| ✔ Yes:hourglass: Update review | -| [Avatar Synced Look](/ml_asl/README.md) | ml_asl | 1.0.0 [:arrow_down:](../../releases/latest/download/ml_asl.dll)| ✔ Yes | -| [Desktop Head Tracking](/ml_dht/README.md) | ml_dht | 1.2.0 [:arrow_down:](../../releases/latest/download/ml_dht.dll) | ✔ Yes (`Retired` group):hourglass: Update review | -| [Leap Motion Extension](/ml_lme/README.md)| ml_lme | 1.4.5 [:arrow_down:](../../releases/latest/download/ml_lme.dll)| ✔ Yes | -| [Pickup Arm Movement](/ml_pam/README.md)| ml_pam | 1.0.9 [:arrow_down:](../../releases/latest/download/ml_pam.dll)| ✔ Yes | -| [Player Movement Copycat](/ml_pmc/README.md)| ml_pmc | 1.0.4 [:arrow_down:](../../releases/latest/download/ml_pmc.dll)| ✔ Yes | -| [Player Ragdoll Mod](/ml_prm/README.md) | ml_prm | 1.1.2 [:arrow_down:](../../releases/latest/download/ml_prm.dll)| ✔ Yes:hourglass: Update review | -| [Players Instance Notifier](/ml_pin/README.md) | ml_pin | 1.0.1 [:arrow_down:](../../releases/latest/download/ml_ml_pin.dll)| ✔ Yes:hourglass: Update review | -| [Vive Extended Input](/ml_vei/README.md) | ml_vei | 1.0.0 [:arrow_down:](../../releases/latest/download/ml_vei.dll)| ✔ Yes | +| [Avatar Motion Tweaker](/ml_amt/README.md) | ml_amt | 1.3.7 [:arrow_down:](../../releases/latest/download/ml_amt.dll)| Yes | +| [Avatar Synced Look](/ml_asl/README.md) | ml_asl | 1.0.1 [:arrow_down:](../../releases/latest/download/ml_asl.dll)| Yes | +| [Better Fingers Tracking](/ml_bft/README.md) | ml_bft | 1.0.0 [:arrow_down:](../../releases/latest/download/ml_bft.dll)| YesUpdate review | +| [Desktop Head Tracking](/ml_dht/README.md) | ml_dht | 1.2.1 [:arrow_down:](../../releases/latest/download/ml_dht.dll) | Yes | +| [Leap Motion Extension](/ml_lme/README.md)| ml_lme | 1.4.7 [:arrow_down:](../../releases/latest/download/ml_lme.dll)| YesUpdate review | +| [Pickup Arm Movement](/ml_pam/README.md)| ml_pam | 1.1.0 [:arrow_down:](../../releases/latest/download/ml_pam.dll)| Yes | +| [Player Movement Copycat](/ml_pmc/README.md)| ml_pmc | 1.0.5 [:arrow_down:](../../releases/latest/download/ml_pmc.dll)| Yes | +| [Player Ragdoll Mod](/ml_prm/README.md) | ml_prm | 1.1.3 [:arrow_down:](../../releases/latest/download/ml_prm.dll)| Yes | +| [Players Instance Notifier](/ml_pin/README.md) | ml_pin | 1.0.2 [:arrow_down:](../../releases/latest/download/ml_ml_pin.dll)| Yes | +| [Vive Extended Input](/ml_vei/README.md) | ml_vei | 1.0.1 [:arrow_down:](../../releases/latest/download/ml_vei.dll)| Yes | **Archived mods:** | Full name | Short name | Notes | |:---------:|:----------:|-------| | Avatar Change Info | ml_aci | Superseded by `Extended Game Notifications` | | Desktop Reticle Switch | ml_drs | Boring functionality | -| Extended Game Notifications | ml_egn | In-game feature sine 2023r172 update | +| Extended Game Notifications | ml_egn | In-game feature since 2023r172 update | | Four Point Tracking | ml_fpt | In-game feature since 2022r170 update | -| Game Main Fixes | ml_gmf | In-game feature sine 2023r172 update | +| Game Main Fixes | ml_gmf | In-game feature since 2023r172 update | | Server Connection Info | ml_sci | Superseded by `Extended Game Notifications` diff --git a/ml_amt/Main.cs b/ml_amt/Main.cs index ff51ec3..57e99e2 100644 --- a/ml_amt/Main.cs +++ b/ml_amt/Main.cs @@ -1,6 +1,5 @@ using ABI_RC.Core.Player; using ABI_RC.Systems.IK; -using ABI_RC.Systems.IK.SubSystems; using System; using System.Collections; using System.Reflection; @@ -98,7 +97,7 @@ namespace ml_amt if(m_localTweaker != null) m_localTweaker.OnAvatarReinitialize(); } - catch(System.Exception e) + catch(Exception e) { MelonLoader.MelonLogger.Error(e); } diff --git a/ml_amt/MotionTweaker.cs b/ml_amt/MotionTweaker.cs index 2ac9cee..c4469e8 100644 --- a/ml_amt/MotionTweaker.cs +++ b/ml_amt/MotionTweaker.cs @@ -1,5 +1,4 @@ using ABI_RC.Core.Player; -using ABI_RC.Systems.IK; using ABI_RC.Systems.IK.SubSystems; using ABI_RC.Systems.Movement; using RootMotion.FinalIK; @@ -125,6 +124,8 @@ namespace ml_amt internal void OnSetupAvatar() { + Utils.SetAvatarTPose(); + m_vrIk = PlayerSetup.Instance._avatar.GetComponent(); m_locomotionLayer = PlayerSetup.Instance._animator.GetLayerIndex("Locomotion/Emotes"); m_avatarScale = Mathf.Abs(PlayerSetup.Instance._avatar.transform.localScale.y); @@ -179,6 +180,8 @@ namespace ml_amt internal void OnAvatarReinitialize() { // Old VRIK is destroyed by game + Utils.SetAvatarTPose(); + m_vrIk = PlayerSetup.Instance._animator.GetComponent(); if(m_vrIk != null) { @@ -242,25 +245,25 @@ namespace ml_amt } // Settings - internal void SetCrouchLimit(float p_value) + void SetCrouchLimit(float p_value) { if(m_ikLimits == null) BetterBetterCharacterController.Instance.avatarCrouchLimit = Mathf.Clamp01(p_value); } - internal void SetProneLimit(float p_value) + void SetProneLimit(float p_value) { if(m_ikLimits == null) BetterBetterCharacterController.Instance.avatarProneLimit = Mathf.Clamp01(p_value); } - internal void SetIKOverrideFly(bool p_state) + void SetIKOverrideFly(bool p_state) { m_ikOverrideFly = p_state; } - internal void SetIKOverrideJump(bool p_state) + void SetIKOverrideJump(bool p_state) { m_ikOverrideJump = p_state; } - internal void SetDetectEmotes(bool p_state) + void SetDetectEmotes(bool p_state) { m_detectEmotes = p_state; } diff --git a/ml_amt/Properties/AssemblyInfo.cs b/ml_amt/Properties/AssemblyInfo.cs index 4273301..c0ff1dd 100644 --- a/ml_amt/Properties/AssemblyInfo.cs +++ b/ml_amt/Properties/AssemblyInfo.cs @@ -1,4 +1,4 @@ -[assembly: MelonLoader.MelonInfo(typeof(ml_amt.AvatarMotionTweaker), "AvatarMotionTweaker", "1.3.7-ex", "SDraw", "https://github.com/SDraw/ml_mods_cvr")] +[assembly: MelonLoader.MelonInfo(typeof(ml_amt.AvatarMotionTweaker), "AvatarMotionTweaker", "1.3.7", "SDraw", "https://github.com/SDraw/ml_mods_cvr")] [assembly: MelonLoader.MelonGame(null, "ChilloutVR")] [assembly: MelonLoader.MelonPlatform(MelonLoader.MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)] [assembly: MelonLoader.MelonPlatformDomain(MelonLoader.MelonPlatformDomainAttribute.CompatibleDomains.MONO)] diff --git a/ml_amt/Settings.cs b/ml_amt/Settings.cs index 0253865..b4c3404 100644 --- a/ml_amt/Settings.cs +++ b/ml_amt/Settings.cs @@ -26,12 +26,12 @@ namespace ml_amt static MelonLoader.MelonPreferences_Category ms_category = null; static List ms_entries = null; - static public event Action CrouchLimitChange; - static public event Action ProneLimitChange; - static public event Action IKOverrideFlyChange; - static public event Action IKOverrideJumpChange; - static public event Action DetectEmotesChange; - static public event Action MassCenterChange; + public static event Action CrouchLimitChange; + public static event Action ProneLimitChange; + public static event Action IKOverrideFlyChange; + public static event Action IKOverrideJumpChange; + public static event Action DetectEmotesChange; + public static event Action MassCenterChange; internal static void Init() { diff --git a/ml_amt/Utils.cs b/ml_amt/Utils.cs index e9aca84..2e892da 100644 --- a/ml_amt/Utils.cs +++ b/ml_amt/Utils.cs @@ -1,6 +1,8 @@ using ABI.CCK.Components; +using ABI_RC.Core.Player; using ABI_RC.Core.Savior; using ABI_RC.Core.UI; +using ABI_RC.Systems.IK; using RootMotion.FinalIK; using System.Reflection; using UnityEngine; @@ -30,7 +32,14 @@ namespace ml_amt return l_result; } - static public void ExecuteScript(this CohtmlControlledViewWrapper p_instance, string p_script) => ((cohtml.Net.View)ms_view.GetValue(p_instance)).ExecuteScript(p_script); + public static void ExecuteScript(this CohtmlControlledViewWrapper p_instance, string p_script) => ((cohtml.Net.View)ms_view.GetValue(p_instance)).ExecuteScript(p_script); + + public static void SetAvatarTPose() + { + IKSystem.Instance.SetAvatarPose(IKSystem.AvatarPose.TPose); + PlayerSetup.Instance._avatar.transform.localPosition = Vector3.zero; + PlayerSetup.Instance._avatar.transform.localRotation = Quaternion.identity; + } // Engine extensions public static Matrix4x4 GetMatrix(this Transform p_transform, bool p_pos = true, bool p_rot = true, bool p_scl = false) diff --git a/ml_amt/ml_amt.csproj b/ml_amt/ml_amt.csproj index 2d26d8b..195a72c 100644 --- a/ml_amt/ml_amt.csproj +++ b/ml_amt/ml_amt.csproj @@ -14,8 +14,8 @@ x64 4 - none - false + embedded + true false TRACE diff --git a/ml_asl/Main.cs b/ml_asl/Main.cs index 968e458..8f7e414 100644 --- a/ml_asl/Main.cs +++ b/ml_asl/Main.cs @@ -1,25 +1,25 @@ -using ABI_RC.Core.Player; -using System.Reflection; - -namespace ml_asl -{ - public class AvatarSyncedLook : MelonLoader.MelonMod - { - public override void OnInitializeMelon() - { - Settings.Init(); - - HarmonyInstance.Patch( - typeof(PlayerSetup).GetMethod("UpdatePlayerAvatarMovementData", BindingFlags.NonPublic | BindingFlags.Instance), - null, - new HarmonyLib.HarmonyMethod(typeof(AvatarSyncedLook).GetMethod(nameof(OnPlayerAvatarMovementDataUpdate_Postfix), BindingFlags.NonPublic | BindingFlags.Static)) - ); - } - - static void OnPlayerAvatarMovementDataUpdate_Postfix(ref PlayerSetup __instance, PlayerAvatarMovementData ____playerAvatarMovementData) - { - if(Settings.Enabled && (__instance.EyeMovementController != null)) - ____playerAvatarMovementData.EyeTrackingOverride = true; - } - } -} +using ABI_RC.Core.Player; +using System.Reflection; + +namespace ml_asl +{ + public class AvatarSyncedLook : MelonLoader.MelonMod + { + public override void OnInitializeMelon() + { + Settings.Init(); + + HarmonyInstance.Patch( + typeof(PlayerSetup).GetMethod("UpdatePlayerAvatarMovementData", BindingFlags.NonPublic | BindingFlags.Instance), + null, + new HarmonyLib.HarmonyMethod(typeof(AvatarSyncedLook).GetMethod(nameof(OnPlayerAvatarMovementDataUpdate_Postfix), BindingFlags.NonPublic | BindingFlags.Static)) + ); + } + + static void OnPlayerAvatarMovementDataUpdate_Postfix(ref PlayerSetup __instance, PlayerAvatarMovementData ____playerAvatarMovementData) + { + if(Settings.Enabled && (__instance.EyeMovementController != null)) + ____playerAvatarMovementData.EyeTrackingOverride = true; + } + } +} diff --git a/ml_asl/Properties/AssemblyInfo.cs b/ml_asl/Properties/AssemblyInfo.cs index fcbf161..dcc9c32 100644 --- a/ml_asl/Properties/AssemblyInfo.cs +++ b/ml_asl/Properties/AssemblyInfo.cs @@ -1,4 +1,4 @@ -[assembly: MelonLoader.MelonInfo(typeof(ml_asl.AvatarSyncedLook), "AvatarSyncedLook", "1.0.1-ex", "SDraw", "https://github.com/SDraw/ml_mods_cvr")] -[assembly: MelonLoader.MelonGame(null, "ChilloutVR")] -[assembly: MelonLoader.MelonPlatform(MelonLoader.MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)] -[assembly: MelonLoader.MelonPlatformDomain(MelonLoader.MelonPlatformDomainAttribute.CompatibleDomains.MONO)] +[assembly: MelonLoader.MelonInfo(typeof(ml_asl.AvatarSyncedLook), "AvatarSyncedLook", "1.0.1", "SDraw", "https://github.com/SDraw/ml_mods_cvr")] +[assembly: MelonLoader.MelonGame(null, "ChilloutVR")] +[assembly: MelonLoader.MelonPlatform(MelonLoader.MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)] +[assembly: MelonLoader.MelonPlatformDomain(MelonLoader.MelonPlatformDomainAttribute.CompatibleDomains.MONO)] diff --git a/ml_asl/Settings.cs b/ml_asl/Settings.cs index 251d8ae..52ba124 100644 --- a/ml_asl/Settings.cs +++ b/ml_asl/Settings.cs @@ -16,7 +16,7 @@ namespace ml_asl static MelonLoader.MelonPreferences_Category ms_category = null; static List ms_entries = null; - static public event Action EnabledChange; + public static event Action EnabledChange; internal static void Init() { diff --git a/ml_asl/Utils.cs b/ml_asl/Utils.cs index 2266e47..fe7a1a8 100644 --- a/ml_asl/Utils.cs +++ b/ml_asl/Utils.cs @@ -7,6 +7,6 @@ namespace ml_asl { static readonly FieldInfo ms_view = typeof(CohtmlControlledViewWrapper).GetField("_view", BindingFlags.NonPublic | BindingFlags.Instance); - static public void ExecuteScript(this CohtmlControlledViewWrapper p_instance, string p_script) => ((cohtml.Net.View)ms_view.GetValue(p_instance)).ExecuteScript(p_script); + public static void ExecuteScript(this CohtmlControlledViewWrapper p_instance, string p_script) => ((cohtml.Net.View)ms_view.GetValue(p_instance)).ExecuteScript(p_script); } } diff --git a/ml_asl/ml_asl.csproj b/ml_asl/ml_asl.csproj index 2e5e075..ceeab33 100644 --- a/ml_asl/ml_asl.csproj +++ b/ml_asl/ml_asl.csproj @@ -1,64 +1,69 @@ - - - - netstandard2.1 - x64 - AvatarSyncedLook - SDraw - None - AvatarSyncedLook - 1.0.1 - - - - - - - - - D:\Games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\0Harmony.dll - false - false - - - D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Assembly-CSharp.dll - false - false - - - D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Assembly-CSharp-firstpass.dll - false - false - - - D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\cohtml.Net.dll - false - false - - - D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Cohtml.Runtime.dll - false - false - - - D:\Games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\MelonLoader.dll - false - false - - - D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.CoreModule.dll - false - false - - - - - - - - - - - - - + + + + netstandard2.1 + x64 + AvatarSyncedLook + SDraw + None + AvatarSyncedLook + 1.0.1 + + + + embedded + true + + + + + + + + + D:\Games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\0Harmony.dll + false + false + + + D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Assembly-CSharp.dll + false + false + + + D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Assembly-CSharp-firstpass.dll + false + false + + + D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\cohtml.Net.dll + false + false + + + D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Cohtml.Runtime.dll + false + false + + + D:\Games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\MelonLoader.dll + false + false + + + D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.CoreModule.dll + false + false + + + + + + + + + + + + + diff --git a/ml_bft/.github/img_01.png b/ml_bft/.github/img_01.png new file mode 100644 index 0000000..078a176 Binary files /dev/null and b/ml_bft/.github/img_01.png differ diff --git a/ml_bft/AssetsHandler.cs b/ml_bft/AssetsHandler.cs new file mode 100644 index 0000000..87e74d7 --- /dev/null +++ b/ml_bft/AssetsHandler.cs @@ -0,0 +1,89 @@ +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using UnityEngine; + +namespace ml_bft +{ + static class AssetsHandler + { + static readonly List ms_assets = new List() + { + "ovr_fingers.asset", + "oxr_fingers.asset" + }; + + static readonly Dictionary ms_loadedAssets = new Dictionary(); + static readonly Dictionary ms_loadedObjects = new Dictionary(); + + public static void Load() + { + Assembly l_assembly = Assembly.GetExecutingAssembly(); + string l_assemblyName = l_assembly.GetName().Name; + + foreach(string l_assetName in ms_assets) + { + try + { + Stream l_assetStream = l_assembly.GetManifestResourceStream(l_assemblyName + ".resources." + l_assetName); + if(l_assetStream != null) + { + MemoryStream l_memorySteam = new MemoryStream((int)l_assetStream.Length); + l_assetStream.CopyTo(l_memorySteam); + AssetBundle l_assetBundle = AssetBundle.LoadFromMemory(l_memorySteam.ToArray(), 0); + if(l_assetBundle != null) + { + l_assetBundle.hideFlags |= HideFlags.DontUnloadUnusedAsset; + ms_loadedAssets.Add(l_assetName, l_assetBundle); + } + else + MelonLoader.MelonLogger.Warning("Unable to load bundled '" + l_assetName + "' asset"); + } + else + MelonLoader.MelonLogger.Warning("Unable to get bundled '" + l_assetName + "' asset stream"); + } + catch(System.Exception e) + { + MelonLoader.MelonLogger.Warning("Unable to load bundled '" + l_assetName + "' asset, reason: " + e.Message); + } + } + } + + public static GameObject GetAsset(string p_name) + { + GameObject l_result = null; + if(ms_loadedObjects.ContainsKey(p_name)) + { + l_result = Object.Instantiate(ms_loadedObjects[p_name]); + l_result.SetActive(true); + l_result.hideFlags |= HideFlags.DontUnloadUnusedAsset; + } + else + { + foreach(var l_pair in ms_loadedAssets) + { + if(l_pair.Value.Contains(p_name)) + { + GameObject l_bundledObject = (GameObject)l_pair.Value.LoadAsset(p_name, typeof(GameObject)); + if(l_bundledObject != null) + { + ms_loadedObjects.Add(p_name, l_bundledObject); + l_result = Object.Instantiate(l_bundledObject); + l_result.SetActive(true); + l_result.hideFlags |= HideFlags.DontUnloadUnusedAsset; + } + break; + } + } + } + return l_result; + } + + public static void Unload() + { + foreach(var l_pair in ms_loadedAssets) + Object.Destroy(l_pair.Value); + ms_loadedAssets.Clear(); + } + } +} diff --git a/ml_bft/FingerSystem.cs b/ml_bft/FingerSystem.cs new file mode 100644 index 0000000..9bc903c --- /dev/null +++ b/ml_bft/FingerSystem.cs @@ -0,0 +1,297 @@ +using ABI_RC.Core.Player; +using ABI_RC.Core.Savior; +using ABI_RC.Systems.IK; +using ABI_RC.Systems.InputManagement; +using System.Collections.Generic; +using UnityEngine; + +namespace ml_bft +{ + class FingerSystem + { + enum PlaneType + { + OXZ, + OYX + } + + struct RotationOffset + { + public Transform m_target; + public Transform m_source; + public Quaternion m_offset; + + public void Reset() + { + m_source = null; + m_target = null; + m_offset = Quaternion.identity; + } + } + + static readonly HumanBodyBones[] ms_leftFingerBones = + { + HumanBodyBones.LeftThumbProximal, HumanBodyBones.LeftThumbIntermediate, HumanBodyBones.LeftThumbDistal, + HumanBodyBones.LeftIndexProximal, HumanBodyBones.LeftIndexIntermediate, HumanBodyBones.LeftIndexDistal, + HumanBodyBones.LeftMiddleProximal, HumanBodyBones.LeftMiddleIntermediate, HumanBodyBones.LeftMiddleDistal, + HumanBodyBones.LeftRingProximal, HumanBodyBones.LeftRingIntermediate, HumanBodyBones.LeftRingDistal, + HumanBodyBones.LeftLittleProximal, HumanBodyBones.LeftLittleIntermediate, HumanBodyBones.LeftLittleDistal + }; + static readonly HumanBodyBones[] ms_rightFingerBones = + { + HumanBodyBones.RightThumbProximal, HumanBodyBones.RightThumbIntermediate, HumanBodyBones.RightThumbDistal, + HumanBodyBones.RightIndexProximal, HumanBodyBones.RightIndexIntermediate, HumanBodyBones.RightIndexDistal, + HumanBodyBones.RightMiddleProximal, HumanBodyBones.RightMiddleIntermediate, HumanBodyBones.RightMiddleDistal, + HumanBodyBones.RightRingProximal, HumanBodyBones.RightRingIntermediate, HumanBodyBones.RightRingDistal, + HumanBodyBones.RightLittleProximal, HumanBodyBones.RightLittleIntermediate, HumanBodyBones.RightLittleDistal + }; + static readonly (HumanBodyBones, HumanBodyBones, bool)[] ms_rotationFixChains = + { + (HumanBodyBones.LeftThumbProximal,HumanBodyBones.LeftThumbIntermediate,true), (HumanBodyBones.LeftThumbIntermediate, HumanBodyBones.LeftThumbDistal,true), + (HumanBodyBones.LeftIndexProximal,HumanBodyBones.LeftIndexIntermediate,true), (HumanBodyBones.LeftIndexIntermediate, HumanBodyBones.LeftIndexDistal,true), + (HumanBodyBones.LeftMiddleProximal,HumanBodyBones.LeftMiddleIntermediate,true), (HumanBodyBones.LeftMiddleIntermediate, HumanBodyBones.LeftMiddleDistal,true), + (HumanBodyBones.LeftRingProximal,HumanBodyBones.LeftRingIntermediate,true), (HumanBodyBones.LeftRingIntermediate, HumanBodyBones.LeftRingDistal,true), + (HumanBodyBones.LeftLittleProximal,HumanBodyBones.LeftLittleIntermediate,true), (HumanBodyBones.LeftLittleIntermediate, HumanBodyBones.LeftLittleDistal,true), + (HumanBodyBones.RightThumbProximal,HumanBodyBones.RightThumbIntermediate,false), (HumanBodyBones.RightThumbIntermediate, HumanBodyBones.RightThumbDistal,false), + (HumanBodyBones.RightIndexProximal,HumanBodyBones.RightIndexIntermediate,false), (HumanBodyBones.RightIndexIntermediate, HumanBodyBones.RightIndexDistal,false), + (HumanBodyBones.RightMiddleProximal,HumanBodyBones.RightMiddleIntermediate,false), (HumanBodyBones.RightMiddleIntermediate, HumanBodyBones.RightMiddleDistal,false), + (HumanBodyBones.RightRingProximal,HumanBodyBones.RightRingIntermediate,false), (HumanBodyBones.RightRingIntermediate, HumanBodyBones.RightRingDistal,false), + (HumanBodyBones.RightLittleProximal,HumanBodyBones.RightLittleIntermediate,false), (HumanBodyBones.RightLittleIntermediate, HumanBodyBones.RightLittleDistal,false) + }; + + public static FingerSystem Instance { get; private set; } = null; + + RotationOffset m_leftHandOffset; // From avatar hand to controller wrist + RotationOffset m_rightHandOffset; + readonly List m_leftFingerOffsets = null; // From controller finger bone to avatar finger bone + readonly List m_rightFingerOffsets = null; + + public readonly float[] m_lastValues; + + bool m_ready = false; + HumanPose m_pose; + + internal FingerSystem() + { + if(Instance == null) + Instance = this; + + m_leftFingerOffsets = new List(); + m_rightFingerOffsets = new List(); + + m_pose = new HumanPose(); + m_lastValues = new float[40]; + } + internal void Cleanup() + { + if(Instance == this) + Instance = null; + + m_leftFingerOffsets.Clear(); + m_rightFingerOffsets.Clear(); + m_ready = false; + } + + internal void OnAvatarSetup() + { + if(PlayerSetup.Instance._animator.isHuman) + { + Utils.SetAvatarTPose(); + InputHandler.Instance.Rebind(PlayerSetup.Instance.transform.rotation); + + // Try to "fix" rotations of fingers + foreach(var l_tuple in ms_rotationFixChains) + { + ReorientateTowards( + PlayerSetup.Instance._animator.GetBoneTransform(l_tuple.Item1), + PlayerSetup.Instance._animator.GetBoneTransform(l_tuple.Item2), + InputHandler.Instance.GetSourceForBone(l_tuple.Item1, l_tuple.Item3), + InputHandler.Instance.GetSourceForBone(l_tuple.Item2, l_tuple.Item3), + PlaneType.OXZ + ); + ReorientateTowards( + PlayerSetup.Instance._animator.GetBoneTransform(l_tuple.Item1), + PlayerSetup.Instance._animator.GetBoneTransform(l_tuple.Item2), + InputHandler.Instance.GetSourceForBone(l_tuple.Item1, l_tuple.Item3), + InputHandler.Instance.GetSourceForBone(l_tuple.Item2, l_tuple.Item3), + PlaneType.OYX + ); + } + + // Bind hands + m_leftHandOffset.m_source = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.LeftHand); + m_leftHandOffset.m_target = InputHandler.Instance.GetSourceForBone(HumanBodyBones.LeftHand, true); + if((m_leftHandOffset.m_source != null) && (m_leftHandOffset.m_target != null)) + m_leftHandOffset.m_offset = Quaternion.Inverse(m_leftHandOffset.m_source.rotation) * m_leftHandOffset.m_target.rotation; + + m_rightHandOffset.m_source = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.RightHand); + m_rightHandOffset.m_target = InputHandler.Instance.GetSourceForBone(HumanBodyBones.RightHand, false); + if((m_rightHandOffset.m_source != null) && (m_rightHandOffset.m_target != null)) + m_rightHandOffset.m_offset = Quaternion.Inverse(m_rightHandOffset.m_source.rotation) * m_rightHandOffset.m_target.rotation; + + // Bind fingers + foreach(HumanBodyBones p_bone in ms_leftFingerBones) + { + Transform l_avatarBone = PlayerSetup.Instance._animator.GetBoneTransform(p_bone); + Transform l_controllerBone = InputHandler.Instance.GetSourceForBone(p_bone, true); + if((l_avatarBone != null) && (l_controllerBone != null)) + { + RotationOffset l_offset = new RotationOffset(); + l_offset.m_source = l_controllerBone; + l_offset.m_target = l_avatarBone; + l_offset.m_offset = Quaternion.Inverse(l_controllerBone.rotation) * l_avatarBone.rotation; + m_leftFingerOffsets.Add(l_offset); + } + } + foreach(HumanBodyBones p_bone in ms_rightFingerBones) + { + Transform l_avatarBone = PlayerSetup.Instance._animator.GetBoneTransform(p_bone); + Transform l_controllerBone = InputHandler.Instance.GetSourceForBone(p_bone, false); + if((l_avatarBone != null) && (l_controllerBone != null)) + { + RotationOffset l_offset = new RotationOffset(); + l_offset.m_source = l_controllerBone; + l_offset.m_target = l_avatarBone; + l_offset.m_offset = Quaternion.Inverse(l_controllerBone.rotation) * l_avatarBone.rotation; + m_rightFingerOffsets.Add(l_offset); + } + } + + m_ready = ((m_leftFingerOffsets.Count > 0) || (m_rightFingerOffsets.Count > 0)); + } + } + + internal void OnAvatarClear() + { + m_ready = false; + m_pose = new HumanPose(); + + m_leftHandOffset.Reset(); + m_rightHandOffset.Reset(); + + m_leftFingerOffsets.Clear(); + m_rightFingerOffsets.Clear(); + } + + internal void OnReinitializeAvatar() + { + OnAvatarClear(); + OnAvatarSetup(); + } + + internal void OnIKSystemLateUpdate(HumanPoseHandler p_handler, Transform p_hips) + { + if(m_ready && MetaPort.Instance.isUsingVr && (p_handler != null) && Settings.SkeletalInput) + { + if(CVRInputManager.Instance._leftController != ABI_RC.Systems.InputManagement.XR.eXRControllerType.None) + { + Quaternion l_turnBack = (m_leftHandOffset.m_source.rotation * m_leftHandOffset.m_offset) * Quaternion.Inverse(m_leftHandOffset.m_target.rotation); + foreach(var l_offset in m_leftFingerOffsets) + l_offset.m_target.rotation = l_turnBack * (l_offset.m_source.rotation * l_offset.m_offset); + } + + if(CVRInputManager.Instance._rightController != ABI_RC.Systems.InputManagement.XR.eXRControllerType.None) + { + Quaternion l_turnBack = (m_rightHandOffset.m_source.rotation * m_rightHandOffset.m_offset) * Quaternion.Inverse(m_rightHandOffset.m_target.rotation); + foreach(var l_offset in m_rightFingerOffsets) + l_offset.m_target.rotation = l_turnBack * (l_offset.m_source.rotation * l_offset.m_offset); + } + + p_handler.GetHumanPose(ref m_pose); + m_lastValues[0] = m_pose.muscles[(int)MuscleIndex.LeftThumb1Stretched]; + m_lastValues[1] = m_pose.muscles[(int)MuscleIndex.LeftThumb2Stretched]; + m_lastValues[2] = m_pose.muscles[(int)MuscleIndex.LeftThumb3Stretched]; + m_lastValues[3] = m_pose.muscles[(int)MuscleIndex.LeftThumbSpread]; + m_lastValues[4] = m_pose.muscles[(int)MuscleIndex.LeftIndex1Stretched]; + m_lastValues[5] = m_pose.muscles[(int)MuscleIndex.LeftIndex2Stretched]; + m_lastValues[6] = m_pose.muscles[(int)MuscleIndex.LeftIndex3Stretched]; + m_lastValues[7] = m_pose.muscles[(int)MuscleIndex.LeftIndexSpread]; + m_lastValues[8] = m_pose.muscles[(int)MuscleIndex.LeftMiddle1Stretched]; + m_lastValues[9] = m_pose.muscles[(int)MuscleIndex.LeftMiddle2Stretched]; + m_lastValues[10] = m_pose.muscles[(int)MuscleIndex.LeftMiddle3Stretched]; + m_lastValues[11] = m_pose.muscles[(int)MuscleIndex.LeftMiddleSpread]; + m_lastValues[12] = m_pose.muscles[(int)MuscleIndex.LeftRing1Stretched]; + m_lastValues[13] = m_pose.muscles[(int)MuscleIndex.LeftRing2Stretched]; + m_lastValues[14] = m_pose.muscles[(int)MuscleIndex.LeftRing3Stretched]; + m_lastValues[15] = m_pose.muscles[(int)MuscleIndex.LeftRingSpread]; + m_lastValues[16] = m_pose.muscles[(int)MuscleIndex.LeftLittle1Stretched]; + m_lastValues[17] = m_pose.muscles[(int)MuscleIndex.LeftLittle2Stretched]; + m_lastValues[18] = m_pose.muscles[(int)MuscleIndex.LeftLittle3Stretched]; + m_lastValues[19] = m_pose.muscles[(int)MuscleIndex.LeftLittleSpread]; + m_lastValues[20] = m_pose.muscles[(int)MuscleIndex.RightThumb1Stretched]; + m_lastValues[21] = m_pose.muscles[(int)MuscleIndex.RightThumb2Stretched]; + m_lastValues[22] = m_pose.muscles[(int)MuscleIndex.RightThumb3Stretched]; + m_lastValues[23] = m_pose.muscles[(int)MuscleIndex.RightThumbSpread]; + m_lastValues[24] = m_pose.muscles[(int)MuscleIndex.RightIndex1Stretched]; + m_lastValues[25] = m_pose.muscles[(int)MuscleIndex.RightIndex2Stretched]; + m_lastValues[26] = m_pose.muscles[(int)MuscleIndex.RightIndex3Stretched]; + m_lastValues[27] = m_pose.muscles[(int)MuscleIndex.RightIndexSpread]; + m_lastValues[28] = m_pose.muscles[(int)MuscleIndex.RightMiddle1Stretched]; + m_lastValues[29] = m_pose.muscles[(int)MuscleIndex.RightMiddle2Stretched]; + m_lastValues[30] = m_pose.muscles[(int)MuscleIndex.RightMiddle3Stretched]; + m_lastValues[31] = m_pose.muscles[(int)MuscleIndex.RightMiddleSpread]; + m_lastValues[32] = m_pose.muscles[(int)MuscleIndex.RightRing1Stretched]; + m_lastValues[33] = m_pose.muscles[(int)MuscleIndex.RightRing2Stretched]; + m_lastValues[34] = m_pose.muscles[(int)MuscleIndex.RightRing3Stretched]; + m_lastValues[35] = m_pose.muscles[(int)MuscleIndex.RightRingSpread]; + m_lastValues[36] = m_pose.muscles[(int)MuscleIndex.RightLittle1Stretched]; + m_lastValues[37] = m_pose.muscles[(int)MuscleIndex.RightLittle2Stretched]; + m_lastValues[38] = m_pose.muscles[(int)MuscleIndex.RightLittle3Stretched]; + m_lastValues[39] = m_pose.muscles[(int)MuscleIndex.RightLittleSpread]; + + if(Settings.MechanimFilter && (p_hips != null)) + { + // Yoinked from IKSystem.OnPostSolverUpdateGeneral + Vector3 l_pos = p_hips.position; + Quaternion l_rot = p_hips.rotation; + p_handler.SetHumanPose(ref m_pose); + p_hips.SetPositionAndRotation(l_pos, l_rot); + } + } + } + + void ReorientateTowards(Transform p_target, Transform p_targetEnd, Transform p_source, Transform p_sourceEnd, PlaneType p_plane) + { + if((p_target != null) && (p_targetEnd != null) && (p_source != null) && (p_sourceEnd != null)) + { + Quaternion l_playerInv = Quaternion.Inverse(PlayerSetup.Instance.transform.rotation); + Vector3 l_targetDir = l_playerInv * (p_targetEnd.position - p_target.position); + Vector3 l_sourceDir = l_playerInv * (p_sourceEnd.position - p_source.position); + switch(p_plane) + { + case PlaneType.OXZ: + l_targetDir.y = 0f; + l_sourceDir.y = 0f; + break; + case PlaneType.OYX: + l_targetDir.z = 0f; + l_sourceDir.z = 0f; + break; + } + l_targetDir = Vector3.Normalize(l_targetDir); + l_sourceDir = Vector3.Normalize(l_sourceDir); + + Quaternion l_targetRot = Quaternion.identity; + Quaternion l_sourceRot = Quaternion.identity; + switch(p_plane) + { + case PlaneType.OXZ: + l_targetRot = Quaternion.LookRotation(l_targetDir, Vector3.up); + l_sourceRot = Quaternion.LookRotation(l_sourceDir, Vector3.up); + break; + case PlaneType.OYX: + l_targetRot = Quaternion.LookRotation(l_targetDir, Vector3.forward); + l_sourceRot = Quaternion.LookRotation(l_sourceDir, Vector3.forward); + break; + } + + Quaternion l_diff = Quaternion.Inverse(l_targetRot) * l_sourceRot; + if(p_plane == PlaneType.OYX) + l_diff = Quaternion.Euler(0f, 0f, l_diff.eulerAngles.y); + + Quaternion l_adjusted = l_diff * (l_playerInv * p_target.rotation); + p_target.rotation = PlayerSetup.Instance.transform.rotation * l_adjusted; + } + } + } +} diff --git a/ml_bft/HandHandler.cs b/ml_bft/HandHandler.cs new file mode 100644 index 0000000..8a0ce54 --- /dev/null +++ b/ml_bft/HandHandler.cs @@ -0,0 +1,59 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace ml_bft +{ + class HandHandler + { + protected bool m_left = false; + protected List m_bones = null; + protected List m_localRotations = null; + protected Transform m_prefabRoot = null; + protected List m_renderers = null; + + protected HandHandler(bool p_left) + { + m_left = p_left; + m_bones = new List(); + m_localRotations = new List(); + m_renderers = new List(); + + Settings.ShowHandsChange += this.OnShowHandsChange; + } + + public virtual void Cleanup() + { + if(m_prefabRoot != null) + Object.Destroy(m_prefabRoot.gameObject); + m_prefabRoot = null; + + m_bones.Clear(); + m_localRotations.Clear(); + m_renderers.Clear(); + + Settings.ShowHandsChange -= this.OnShowHandsChange; + } + + public virtual void Update() + { + } + + public virtual Transform GetSourceForBone(HumanBodyBones p_bone) + { + return default; + } + + public virtual void Rebind(Quaternion p_base) + { + } + + protected void OnShowHandsChange(bool p_state) + { + foreach(var l_render in m_renderers) + { + if(l_render != null) + l_render.enabled = p_state; + } + } + } +} diff --git a/ml_bft/HandHandlerVR.cs b/ml_bft/HandHandlerVR.cs new file mode 100644 index 0000000..bff8246 --- /dev/null +++ b/ml_bft/HandHandlerVR.cs @@ -0,0 +1,249 @@ +using UnityEngine; +using Valve.VR; + +namespace ml_bft +{ + class HandHandlerVR : HandHandler + { + // 31 bones in each hand, get index at Valve.VR.SteamVR_Skeleton_JointIndexes or SteamVR_Skeleton_JointIndexEnum + const int c_fingerBonesCount = (int)SteamVR_Skeleton_JointIndexEnum.pinkyAux + 1; + + SteamVR_Action_Skeleton m_skeletonAction; + + public HandHandlerVR(Transform p_root, bool p_left) : base(p_left) + { + for(int i = 0; i < c_fingerBonesCount; i++) + { + m_bones.Add(null); + m_localRotations.Add(Quaternion.identity); + } + + // Fill finger transforms + m_prefabRoot = AssetsHandler.GetAsset(string.Format("assets/steamvr/models/[openvr] {0}.prefab", m_left ? "left" : "right")).transform; + m_prefabRoot.name = "[FingersTracking_VR]"; + m_prefabRoot.parent = p_root; + m_prefabRoot.localPosition = Vector3.zero; + m_prefabRoot.localRotation = Quaternion.identity; + + m_prefabRoot.GetComponentsInChildren(true, m_renderers); + + // Ah yes, the stupid code + char l_side = (m_left ? 'l' : 'r'); + m_bones[(int)SteamVR_Skeleton_JointIndexEnum.root] = m_prefabRoot.Find("Root"); + m_bones[(int)SteamVR_Skeleton_JointIndexEnum.wrist] = m_prefabRoot.Find(string.Format("Root/wrist_{0}", l_side)); + m_bones[(int)SteamVR_Skeleton_JointIndexEnum.thumbProximal] = m_prefabRoot.Find(string.Format("Root/wrist_{0}/finger_thumb_0_{0}", l_side)); + m_bones[(int)SteamVR_Skeleton_JointIndexEnum.thumbMiddle] = m_prefabRoot.Find(string.Format("Root/wrist_{0}/finger_thumb_0_{0}/finger_thumb_1_{0}", l_side)); + m_bones[(int)SteamVR_Skeleton_JointIndexEnum.thumbDistal] = m_prefabRoot.Find(string.Format("Root/wrist_{0}/finger_thumb_0_{0}/finger_thumb_1_{0}/finger_thumb_2_{0}", l_side)); + m_bones[(int)SteamVR_Skeleton_JointIndexEnum.thumbTip] = m_prefabRoot.Find(string.Format("Root/wrist_{0}/finger_thumb_0_{0}/finger_thumb_1_{0}/finger_thumb_2_{0}/finger_thumb_{0}_end", l_side)); + + m_bones[(int)SteamVR_Skeleton_JointIndexEnum.indexMetacarpal] = m_prefabRoot.Find(string.Format("Root/wrist_{0}/finger_index_meta_{0}", l_side)); + m_bones[(int)SteamVR_Skeleton_JointIndexEnum.indexProximal] = m_prefabRoot.Find(string.Format("Root/wrist_{0}/finger_index_meta_{0}/finger_index_0_{0}", l_side)); + m_bones[(int)SteamVR_Skeleton_JointIndexEnum.indexMiddle] = m_prefabRoot.Find(string.Format("Root/wrist_{0}/finger_index_meta_{0}/finger_index_0_{0}/finger_index_1_{0}", l_side)); + m_bones[(int)SteamVR_Skeleton_JointIndexEnum.indexDistal] = m_prefabRoot.Find(string.Format("Root/wrist_{0}/finger_index_meta_{0}/finger_index_0_{0}/finger_index_1_{0}/finger_index_2_{0}", l_side)); + m_bones[(int)SteamVR_Skeleton_JointIndexEnum.indexTip] = m_prefabRoot.Find(string.Format("Root/wrist_{0}/finger_index_meta_{0}/finger_index_0_{0}/finger_index_1_{0}/finger_index_2_{0}/finger_index_{0}_end", l_side)); + + m_bones[(int)SteamVR_Skeleton_JointIndexEnum.middleMetacarpal] = m_prefabRoot.Find(string.Format("Root/wrist_{0}/finger_middle_meta_{0}", l_side)); + m_bones[(int)SteamVR_Skeleton_JointIndexEnum.middleProximal] = m_prefabRoot.Find(string.Format("Root/wrist_{0}/finger_middle_meta_{0}/finger_middle_0_{0}", l_side)); + m_bones[(int)SteamVR_Skeleton_JointIndexEnum.middleMiddle] = m_prefabRoot.Find(string.Format("Root/wrist_{0}/finger_middle_meta_{0}/finger_middle_0_{0}/finger_middle_1_{0}", l_side)); + m_bones[(int)SteamVR_Skeleton_JointIndexEnum.middleDistal] = m_prefabRoot.Find(string.Format("Root/wrist_{0}/finger_middle_meta_{0}/finger_middle_0_{0}/finger_middle_1_{0}/finger_middle_2_{0}", l_side)); + m_bones[(int)SteamVR_Skeleton_JointIndexEnum.middleTip] = m_prefabRoot.Find(string.Format("Root/wrist_{0}/finger_middle_meta_{0}/finger_middle_0_{0}/finger_middle_1_{0}/finger_middle_2_{0}/finger_middle_{0}_end", l_side)); + + m_bones[(int)SteamVR_Skeleton_JointIndexEnum.ringMetacarpal] = m_prefabRoot.Find(string.Format("Root/wrist_{0}/finger_ring_meta_{0}", l_side)); + m_bones[(int)SteamVR_Skeleton_JointIndexEnum.ringProximal] = m_prefabRoot.Find(string.Format("Root/wrist_{0}/finger_ring_meta_{0}/finger_ring_0_{0}", l_side)); + m_bones[(int)SteamVR_Skeleton_JointIndexEnum.ringMiddle] = m_prefabRoot.Find(string.Format("Root/wrist_{0}/finger_ring_meta_{0}/finger_ring_0_{0}/finger_ring_1_{0}", l_side)); + m_bones[(int)SteamVR_Skeleton_JointIndexEnum.ringDistal] = m_prefabRoot.Find(string.Format("Root/wrist_{0}/finger_ring_meta_{0}/finger_ring_0_{0}/finger_ring_1_{0}/finger_ring_2_{0}", l_side)); + m_bones[(int)SteamVR_Skeleton_JointIndexEnum.ringTip] = m_prefabRoot.Find(string.Format("Root/wrist_{0}/finger_ring_meta_{0}/finger_ring_0_{0}/finger_ring_1_{0}/finger_ring_2_{0}/finger_ring_{0}_end", l_side)); + + m_bones[(int)SteamVR_Skeleton_JointIndexEnum.pinkyMetacarpal] = m_prefabRoot.Find(string.Format("Root/wrist_{0}/finger_pinky_meta_{0}", l_side)); + m_bones[(int)SteamVR_Skeleton_JointIndexEnum.pinkyProximal] = m_prefabRoot.Find(string.Format("Root/wrist_{0}/finger_pinky_meta_{0}/finger_pinky_0_{0}", l_side)); + m_bones[(int)SteamVR_Skeleton_JointIndexEnum.pinkyMiddle] = m_prefabRoot.Find(string.Format("Root/wrist_{0}/finger_pinky_meta_{0}/finger_pinky_0_{0}/finger_pinky_1_{0}", l_side)); + m_bones[(int)SteamVR_Skeleton_JointIndexEnum.pinkyDistal] = m_prefabRoot.Find(string.Format("Root/wrist_{0}/finger_pinky_meta_{0}/finger_pinky_0_{0}/finger_pinky_1_{0}/finger_pinky_2_{0}", l_side)); + m_bones[(int)SteamVR_Skeleton_JointIndexEnum.pinkyTip] = m_prefabRoot.Find(string.Format("Root/wrist_{0}/finger_pinky_meta_{0}/finger_pinky_0_{0}/finger_pinky_1_{0}/finger_pinky_2_{0}/finger_pinky_{0}_end", l_side)); + + m_bones[(int)SteamVR_Skeleton_JointIndexEnum.thumbAux] = m_prefabRoot.Find(string.Format("Root/finger_thumb_{0}_aux", l_side)); + m_bones[(int)SteamVR_Skeleton_JointIndexEnum.indexAux] = m_prefabRoot.Find(string.Format("Root/finger_index_{0}_aux", l_side)); + m_bones[(int)SteamVR_Skeleton_JointIndexEnum.middleAux] = m_prefabRoot.Find(string.Format("Root/finger_middle_{0}_aux", l_side)); + m_bones[(int)SteamVR_Skeleton_JointIndexEnum.ringAux] = m_prefabRoot.Find(string.Format("Root/finger_ring_{0}_aux", l_side)); + m_bones[(int)SteamVR_Skeleton_JointIndexEnum.pinkyAux] = m_prefabRoot.Find(string.Format("Root/finger_pinky_{0}_aux", l_side)); + + // Remember local rotations + for(int i = 0; i < c_fingerBonesCount; i++) + { + if(m_bones[i] != null) + m_localRotations[i] = m_bones[i].localRotation; + } + + m_skeletonAction = SteamVR_Input.GetAction(p_left ? "SkeletonLeftHand" : "SkeletonRightHand"); + + base.OnShowHandsChange(Settings.ShowHands); + OnMotionRangeChange(Settings.MotionRange); + + Settings.MotionRangeChange += this.OnMotionRangeChange; + } + + public override void Cleanup() + { + base.Cleanup(); + + m_skeletonAction = null; + + Settings.MotionRangeChange -= this.OnMotionRangeChange; + } + + public override void Update() + { + if(m_skeletonAction != null) + { + var l_rotations = m_skeletonAction.GetBoneRotations(); + var l_positions = m_skeletonAction.GetBonePositions(); + for(int i = 0; i < c_fingerBonesCount; i++) + { + if(m_bones[i] != null) + { + m_bones[i].localRotation = l_rotations[i]; + m_bones[i].localPosition = l_positions[i]; + } + } + } + } + + public override Transform GetSourceForBone(HumanBodyBones p_bone) + { + Transform l_result = null; + switch(p_bone) + { + case HumanBodyBones.LeftHand: + l_result = (m_left ? m_bones[(int)SteamVR_Skeleton_JointIndexEnum.wrist] : null); + break; + case HumanBodyBones.LeftThumbProximal: + l_result = (m_left ? m_bones[(int)SteamVR_Skeleton_JointIndexEnum.thumbProximal] : null); + break; + case HumanBodyBones.LeftThumbIntermediate: + l_result = (m_left ? m_bones[(int)SteamVR_Skeleton_JointIndexEnum.thumbMiddle] : null); + break; + case HumanBodyBones.LeftThumbDistal: + l_result = (m_left ? m_bones[(int)SteamVR_Skeleton_JointIndexEnum.thumbDistal] : null); + break; + + case HumanBodyBones.LeftIndexProximal: + l_result = (m_left ? m_bones[(int)SteamVR_Skeleton_JointIndexEnum.indexProximal] : null); + break; + case HumanBodyBones.LeftIndexIntermediate: + l_result = (m_left ? m_bones[(int)SteamVR_Skeleton_JointIndexEnum.indexMiddle] : null); + break; + case HumanBodyBones.LeftIndexDistal: + l_result = (m_left ? m_bones[(int)SteamVR_Skeleton_JointIndexEnum.indexDistal] : null); + break; + + case HumanBodyBones.LeftMiddleProximal: + l_result = (m_left ? m_bones[(int)SteamVR_Skeleton_JointIndexEnum.middleProximal] : null); + break; + case HumanBodyBones.LeftMiddleIntermediate: + l_result = (m_left ? m_bones[(int)SteamVR_Skeleton_JointIndexEnum.middleMiddle] : null); + break; + case HumanBodyBones.LeftMiddleDistal: + l_result = (m_left ? m_bones[(int)SteamVR_Skeleton_JointIndexEnum.middleDistal] : null); + break; + + case HumanBodyBones.LeftRingProximal: + l_result = (m_left ? m_bones[(int)SteamVR_Skeleton_JointIndexEnum.ringProximal] : null); + break; + case HumanBodyBones.LeftRingIntermediate: + l_result = (m_left ? m_bones[(int)SteamVR_Skeleton_JointIndexEnum.ringMiddle] : null); + break; + case HumanBodyBones.LeftRingDistal: + l_result = (m_left ? m_bones[(int)SteamVR_Skeleton_JointIndexEnum.ringDistal] : null); + break; + + case HumanBodyBones.LeftLittleProximal: + l_result = (m_left ? m_bones[(int)SteamVR_Skeleton_JointIndexEnum.pinkyProximal] : null); + break; + case HumanBodyBones.LeftLittleIntermediate: + l_result = (m_left ? m_bones[(int)SteamVR_Skeleton_JointIndexEnum.pinkyMiddle] : null); + break; + case HumanBodyBones.LeftLittleDistal: + l_result = (m_left ? m_bones[(int)SteamVR_Skeleton_JointIndexEnum.pinkyDistal] : null); + break; + + case HumanBodyBones.RightHand: + l_result = (!m_left ? m_bones[(int)SteamVR_Skeleton_JointIndexEnum.wrist] : null); + break; + case HumanBodyBones.RightThumbProximal: + l_result = (!m_left ? m_bones[(int)SteamVR_Skeleton_JointIndexEnum.thumbProximal] : null); + break; + case HumanBodyBones.RightThumbIntermediate: + l_result = (!m_left ? m_bones[(int)SteamVR_Skeleton_JointIndexEnum.thumbMiddle] : null); + break; + case HumanBodyBones.RightThumbDistal: + l_result = (!m_left ? m_bones[(int)SteamVR_Skeleton_JointIndexEnum.thumbDistal] : null); + break; + + case HumanBodyBones.RightIndexProximal: + l_result = (!m_left ? m_bones[(int)SteamVR_Skeleton_JointIndexEnum.indexProximal] : null); + break; + case HumanBodyBones.RightIndexIntermediate: + l_result = (!m_left ? m_bones[(int)SteamVR_Skeleton_JointIndexEnum.indexMiddle] : null); + break; + case HumanBodyBones.RightIndexDistal: + l_result = (!m_left ? m_bones[(int)SteamVR_Skeleton_JointIndexEnum.indexDistal] : null); + break; + + case HumanBodyBones.RightMiddleProximal: + l_result = (!m_left ? m_bones[(int)SteamVR_Skeleton_JointIndexEnum.middleProximal] : null); + break; + case HumanBodyBones.RightMiddleIntermediate: + l_result = (!m_left ? m_bones[(int)SteamVR_Skeleton_JointIndexEnum.middleMiddle] : null); + break; + case HumanBodyBones.RightMiddleDistal: + l_result = (!m_left ? m_bones[(int)SteamVR_Skeleton_JointIndexEnum.middleDistal] : null); + break; + + case HumanBodyBones.RightRingProximal: + l_result = (!m_left ? m_bones[(int)SteamVR_Skeleton_JointIndexEnum.ringProximal] : null); + break; + case HumanBodyBones.RightRingIntermediate: + l_result = (!m_left ? m_bones[(int)SteamVR_Skeleton_JointIndexEnum.ringMiddle] : null); + break; + case HumanBodyBones.RightRingDistal: + l_result = (!m_left ? m_bones[(int)SteamVR_Skeleton_JointIndexEnum.ringDistal] : null); + break; + + case HumanBodyBones.RightLittleProximal: + l_result = (!m_left ? m_bones[(int)SteamVR_Skeleton_JointIndexEnum.pinkyProximal] : null); + break; + case HumanBodyBones.RightLittleIntermediate: + l_result = (!m_left ? m_bones[(int)SteamVR_Skeleton_JointIndexEnum.pinkyMiddle] : null); + break; + case HumanBodyBones.RightLittleDistal: + l_result = (!m_left ? m_bones[(int)SteamVR_Skeleton_JointIndexEnum.pinkyDistal] : null); + break; + } + return l_result; + } + + public override void Rebind(Quaternion p_base) + { + for(int i = 0; i < c_fingerBonesCount; i++) + { + if(m_bones[i] != null) + m_bones[i].localRotation = m_localRotations[i]; + } + + if(m_bones[(int)SteamVR_Skeleton_JointIndexEnum.root] != null) + m_bones[(int)SteamVR_Skeleton_JointIndexEnum.root].rotation = p_base * (m_left ? Quaternion.Euler(0f, -90f, -90f) : Quaternion.Euler(0f, 90f, 90f)); + } + + void OnMotionRangeChange(Settings.MotionRangeType p_mode) + { + switch(p_mode) + { + case Settings.MotionRangeType.WithController: + m_skeletonAction?.SetRangeOfMotion(EVRSkeletalMotionRange.WithController); + break; + case Settings.MotionRangeType.WithoutController: + m_skeletonAction?.SetRangeOfMotion(EVRSkeletalMotionRange.WithoutController); + break; + } + } + } +} diff --git a/ml_bft/HandHandlerXR.cs b/ml_bft/HandHandlerXR.cs new file mode 100644 index 0000000..3f08692 --- /dev/null +++ b/ml_bft/HandHandlerXR.cs @@ -0,0 +1,229 @@ +using UnityEngine; +using UnityEngine.XR.OpenXR; +using UnityEngine.XR.Hands; +using UnityEngine.XR; + +namespace ml_bft +{ + class HandHandlerXR : HandHandler + { + // 26 bones, get in XRHandJointID enum + const int c_fingerBonesCount = (int)XRHandJointID.EndMarker - 1; + + public HandHandlerXR(Transform p_root, bool p_left) : base(p_left) + { + for(int i = 0; i < c_fingerBonesCount; i++) + { + m_bones.Add(null); + m_localRotations.Add(Quaternion.identity); + } + + m_prefabRoot = AssetsHandler.GetAsset(string.Format("Assets/OpenXR/Models/{0}Hand_IK.prefab", m_left ? "Left" : "Right")).transform; + m_prefabRoot.name = "[FingersTracking_XR]"; + m_prefabRoot.parent = p_root; + m_prefabRoot.localPosition = Vector3.zero; + m_prefabRoot.localRotation = Quaternion.identity; + + m_prefabRoot.GetComponentsInChildren(true, m_renderers); + + // Ah yes, the stupid code + char l_side = (m_left ? 'L' : 'R'); + m_bones[(int)XRHandJointID.Wrist - 1] = m_prefabRoot.Find(string.Format("{0}_Wrist", l_side)); + m_bones[(int)XRHandJointID.Palm - 1] = m_prefabRoot.Find(string.Format("{0}_Wrist/{0}_Palm", l_side)); + + m_bones[(int)XRHandJointID.ThumbMetacarpal - 1] = m_prefabRoot.Find(string.Format("{0}_Wrist/{0}_ThumbMetacarpal", l_side)); + m_bones[(int)XRHandJointID.ThumbProximal - 1] = m_prefabRoot.Find(string.Format("{0}_Wrist/{0}_ThumbMetacarpal/{0}_Wrist/{0}_ThumbProximal", l_side)); + m_bones[(int)XRHandJointID.ThumbDistal - 1] = m_prefabRoot.Find(string.Format("{0}_Wrist/{0}_ThumbMetacarpal/{0}_Wrist/{0}_ThumbProximal/{0}_ThumbDistal", l_side)); + m_bones[(int)XRHandJointID.ThumbTip - 1] = m_prefabRoot.Find(string.Format("{0}_Wrist/{0}_ThumbMetacarpal/{0}_Wrist/{0}_ThumbProximal/{0}_ThumbDistal/{0}_ThumbTip", l_side)); + m_bones[(int)XRHandJointID.IndexMetacarpal - 1] = m_prefabRoot.Find(string.Format("{0}_Wrist/{0}_IndexMetacarpal", l_side)); + m_bones[(int)XRHandJointID.IndexProximal - 1] = m_prefabRoot.Find(string.Format("{0}_Wrist/{0}_IndexMetacarpal/{0}_IndexProximal", l_side)); + m_bones[(int)XRHandJointID.IndexIntermediate - 1] = m_prefabRoot.Find(string.Format("{0}_Wrist/{0}_IndexMetacarpal/{0}_IndexProximal/{0}_IndexIntermediate", l_side)); + m_bones[(int)XRHandJointID.IndexDistal - 1] = m_prefabRoot.Find(string.Format("{0}_Wrist/{0}_IndexMetacarpal/{0}_IndexProximal/{0}_IndexIntermediate/{0}_IndexDistal", l_side)); + m_bones[(int)XRHandJointID.IndexTip - 1] = m_prefabRoot.Find(string.Format("{0}_Wrist/{0}_IndexMetacarpal/{0}_IndexProximal/{0}_IndexIntermediate/{0}_IndexDistal/{0}_IndexTip", l_side)); + + m_bones[(int)XRHandJointID.MiddleMetacarpal - 1] = m_prefabRoot.Find(string.Format("{0}_Wrist/{0}_MiddleMetacarpal", l_side)); + m_bones[(int)XRHandJointID.MiddleProximal - 1] = m_prefabRoot.Find(string.Format("{0}_Wrist/{0}_MiddleMetacarpal/{0}_MiddleProximal", l_side)); + m_bones[(int)XRHandJointID.MiddleIntermediate - 1] = m_prefabRoot.Find(string.Format("{0}_Wrist/{0}_MiddleMetacarpal/{0}_MiddleProximal/{0}_MiddleIntermediate", l_side)); + m_bones[(int)XRHandJointID.MiddleDistal - 1] = m_prefabRoot.Find(string.Format("{0}_Wrist/{0}_MiddleMetacarpal/{0}_MiddleProximal/{0}_MiddleIntermediate/{0}_MiddleDistal", l_side)); + m_bones[(int)XRHandJointID.MiddleTip - 1] = m_prefabRoot.Find(string.Format("{0}_Wrist/{0}_MiddleMetacarpal/{0}_MiddleProximal/{0}_MiddleIntermediate/{0}_MiddleDistal/{0}_MiddleTip", l_side)); + + m_bones[(int)XRHandJointID.RingMetacarpal - 1] = m_prefabRoot.Find(string.Format("{0}_Wrist/{0}_RingMetacarpal", l_side)); + m_bones[(int)XRHandJointID.RingProximal - 1] = m_prefabRoot.Find(string.Format("{0}_Wrist/{0}_RingMetacarpal/{0}_RingProximal", l_side)); + m_bones[(int)XRHandJointID.RingIntermediate - 1] = m_prefabRoot.Find(string.Format("{0}_Wrist/{0}_RingMetacarpal/{0}_RingProximal/{0}_RingIntermediate", l_side)); + m_bones[(int)XRHandJointID.RingDistal - 1] = m_prefabRoot.Find(string.Format("{0}_Wrist/{0}_RingMetacarpal/{0}_RingProximal/{0}_RingIntermediate/{0}_RingDistal", l_side)); + m_bones[(int)XRHandJointID.RingTip - 1] = m_prefabRoot.Find(string.Format("{0}_Wrist/{0}_RingMetacarpal/{0}_RingProximal/{0}_RingIntermediate/{0}_RingDistal/{0}_RingTip", l_side)); + + m_bones[(int)XRHandJointID.LittleMetacarpal - 1] = m_prefabRoot.Find(string.Format("{0}_Wrist/{0}_LittleMetacarpal", l_side)); + m_bones[(int)XRHandJointID.LittleProximal - 1] = m_prefabRoot.Find(string.Format("{0}_Wrist/{0}_LittleMetacarpal/{0}_LittleProximal", l_side)); + m_bones[(int)XRHandJointID.LittleIntermediate - 1] = m_prefabRoot.Find(string.Format("{0}_Wrist/{0}_LittleMetacarpal/{0}_LittleProximal/{0}_LittleIntermediate", l_side)); + m_bones[(int)XRHandJointID.LittleDistal - 1] = m_prefabRoot.Find(string.Format("{0}_Wrist/{0}_LittleMetacarpal/{0}_LittleProximal/{0}_LittleIntermediate/{0}_LittleDistal", l_side)); + m_bones[(int)XRHandJointID.LittleTip - 1] = m_prefabRoot.Find(string.Format("{0}_Wrist/{0}_LittleMetacarpal/{0}_LittleProximal/{0}_LittleIntermediate/{0}_LittleDistal/{0}_LittleTip", l_side)); + + for(int i = 0; i < c_fingerBonesCount; i++) + { + if(m_bones[i] != null) + m_localRotations[i] = m_bones[i].localRotation; + } + + base.OnShowHandsChange(Settings.ShowHands); + } + + public override Transform GetSourceForBone(HumanBodyBones p_bone) + { + Transform l_result = null; + if(m_left) + { + switch(p_bone) + { + case HumanBodyBones.LeftHand: + l_result = m_bones[(int)XRHandJointID.Wrist - 1]; + break; + case HumanBodyBones.LeftThumbProximal: + l_result = m_bones[(int)XRHandJointID.ThumbMetacarpal - 1]; + break; + case HumanBodyBones.LeftThumbIntermediate: + l_result = m_bones[(int)XRHandJointID.ThumbProximal - 1]; + break; + case HumanBodyBones.LeftThumbDistal: + l_result = m_bones[(int)XRHandJointID.ThumbDistal - 1]; + break; + + case HumanBodyBones.LeftIndexProximal: + l_result = m_bones[(int)XRHandJointID.IndexProximal - 1]; + break; + case HumanBodyBones.LeftIndexIntermediate: + l_result = m_bones[(int)XRHandJointID.IndexIntermediate - 1]; + break; + case HumanBodyBones.LeftIndexDistal: + l_result = m_bones[(int)XRHandJointID.IndexDistal - 1]; + break; + + case HumanBodyBones.LeftMiddleProximal: + l_result = m_bones[(int)XRHandJointID.MiddleProximal - 1]; + break; + case HumanBodyBones.LeftMiddleIntermediate: + l_result = m_bones[(int)XRHandJointID.MiddleIntermediate - 1]; + break; + case HumanBodyBones.LeftMiddleDistal: + l_result = m_bones[(int)XRHandJointID.MiddleDistal - 1]; + break; + + case HumanBodyBones.LeftRingProximal: + l_result = m_bones[(int)XRHandJointID.RingProximal - 1]; + break; + case HumanBodyBones.LeftRingIntermediate: + l_result = m_bones[(int)XRHandJointID.RingIntermediate - 1]; + break; + case HumanBodyBones.LeftRingDistal: + l_result = m_bones[(int)XRHandJointID.RingDistal - 1]; + break; + + case HumanBodyBones.LeftLittleProximal: + l_result = m_bones[(int)XRHandJointID.LittleProximal - 1]; + break; + case HumanBodyBones.LeftLittleIntermediate: + l_result = m_bones[(int)XRHandJointID.LittleIntermediate - 1]; + break; + case HumanBodyBones.LeftLittleDistal: + l_result = m_bones[(int)XRHandJointID.LittleDistal - 1]; + break; + } + } + else + { + switch(p_bone) + { + case HumanBodyBones.RightHand: + l_result = m_bones[(int)XRHandJointID.Wrist - 1]; + break; + case HumanBodyBones.RightThumbProximal: + l_result = m_bones[(int)XRHandJointID.ThumbMetacarpal - 1]; + break; + case HumanBodyBones.RightThumbIntermediate: + l_result = m_bones[(int)XRHandJointID.ThumbProximal - 1]; + break; + case HumanBodyBones.RightThumbDistal: + l_result = m_bones[(int)XRHandJointID.ThumbDistal - 1]; + break; + + case HumanBodyBones.RightIndexProximal: + l_result = m_bones[(int)XRHandJointID.IndexProximal - 1]; + break; + case HumanBodyBones.RightIndexIntermediate: + l_result = m_bones[(int)XRHandJointID.IndexIntermediate - 1]; + break; + case HumanBodyBones.RightIndexDistal: + l_result = m_bones[(int)XRHandJointID.IndexDistal - 1]; + break; + + case HumanBodyBones.RightMiddleProximal: + l_result = m_bones[(int)XRHandJointID.MiddleProximal - 1]; + break; + case HumanBodyBones.RightMiddleIntermediate: + l_result = m_bones[(int)XRHandJointID.MiddleIntermediate - 1]; + break; + case HumanBodyBones.RightMiddleDistal: + l_result = m_bones[(int)XRHandJointID.MiddleDistal - 1]; + break; + + case HumanBodyBones.RightRingProximal: + l_result = m_bones[(int)XRHandJointID.RingProximal - 1]; + break; + case HumanBodyBones.RightRingIntermediate: + l_result = m_bones[(int)XRHandJointID.RingIntermediate - 1]; + break; + case HumanBodyBones.RightRingDistal: + l_result = m_bones[(int)XRHandJointID.RingDistal - 1]; + break; + + case HumanBodyBones.RightLittleProximal: + l_result = m_bones[(int)XRHandJointID.LittleProximal - 1]; + break; + case HumanBodyBones.RightLittleIntermediate: + l_result = m_bones[(int)XRHandJointID.LittleIntermediate - 1]; + break; + case HumanBodyBones.RightLittleDistal: + l_result = m_bones[(int)XRHandJointID.LittleDistal - 1]; + break; + } + } + return l_result; + } + + public override void Update() + { + var l_tracking = OpenXRSettings.Instance.GetFeature(); + var l_device = InputDevices.GetDeviceAtXRNode(m_left ? XRNode.LeftHand : XRNode.RightHand); + if((l_device != null) && l_device.TryGetFeatureValue(CommonUsages.deviceRotation, out Quaternion l_deviceRot) && (l_tracking != null)) + { + Quaternion l_handInv = Quaternion.Inverse(l_deviceRot); + l_tracking.GetHandJoints(m_left ? HandTrackingFeature.Hand_Index.L : HandTrackingFeature.Hand_Index.R, out var l_positions, out var l_rotations, out _); + if(l_positions.Length >= c_fingerBonesCount) + { + // Joints rotations are in global space, locations are in ... space??? ... wth is wrong with OpenXR? + Quaternion l_prefabRot = m_prefabRoot.rotation; + for(int i = 0; i < c_fingerBonesCount; i++) + { + if(m_bones[i] != null) + { + //m_bones[i].localPosition = l_positions[i]; + m_bones[i].rotation = l_prefabRot * (l_handInv * l_rotations[i]); + } + } + } + } + } + + public override void Rebind(Quaternion p_base) + { + for(int i = 0; i < c_fingerBonesCount; i++) + { + if(m_bones[i] != null) + m_bones[i].localRotation = m_localRotations[i]; + } + + if(m_bones[(int)XRHandJointID.Wrist - 1] != null) + m_bones[(int)XRHandJointID.Wrist - 1].rotation = p_base * (m_left ? Quaternion.Euler(0f, -90f, 0f) : Quaternion.Euler(0f, 90f, 0f)); + } + } +} diff --git a/ml_bft/InputHandler.cs b/ml_bft/InputHandler.cs new file mode 100644 index 0000000..1b0a9ac --- /dev/null +++ b/ml_bft/InputHandler.cs @@ -0,0 +1,161 @@ +using ABI_RC.Core.Savior; +using ABI_RC.Systems.InputManagement; +using ABI_RC.Systems.VRModeSwitch; +using UnityEngine; + +namespace ml_bft +{ + // Not an actual module, but can be used as one + class InputHandler + { + public static InputHandler Instance { get; private set; } = null; + + bool m_active = false; + + HandHandler m_leftHandHandler = null; + HandHandler m_rightHandHandler = null; + + internal InputHandler() + { + if(Instance == null) + Instance = this; + + m_active = false; + + if(MetaPort.Instance.isUsingVr) + SetupHandlers(); + + VRModeSwitchEvents.OnInitializeXR.AddListener(this.OnSwitchToVR); + VRModeSwitchEvents.OnDeinitializeXR.AddListener(this.OnSwitchToDesktop); + + Settings.SkeletalInputChange += this.OnSkeletalInputChange; + } + internal void Cleanup() + { + if(Instance == this) + Instance = null; + + RemoveHandlers(); + } + + void SetupHandlers() + { + if(!CheckVR.Instance.forceOpenXr) + { + m_leftHandHandler = new HandHandlerVR(CVRInputManager.Instance.leftHandTransform, true); + m_rightHandHandler = new HandHandlerVR(CVRInputManager.Instance.rightHandTransform, false); + m_active = true; + } + } + void RemoveHandlers() + { + m_leftHandHandler?.Cleanup(); + m_leftHandHandler = null; + m_rightHandHandler?.Cleanup(); + m_rightHandHandler = null; + m_active = false; + } + + public void Rebind(Quaternion p_base) + { + if(m_active) + { + m_leftHandHandler?.Rebind(p_base); + m_rightHandHandler?.Rebind(p_base); + } + } + + public Transform GetSourceForBone(HumanBodyBones p_bone, bool p_left) + { + Transform l_result; + if(p_left) + l_result = m_leftHandHandler?.GetSourceForBone(p_bone); + else + l_result = m_rightHandHandler?.GetSourceForBone(p_bone); + return l_result; + } + + // Game events + internal void OnInputUpdate() + { + if(m_active && Settings.SkeletalInput) + { + m_leftHandHandler?.Update(); + m_rightHandHandler?.Update(); + + CVRInputManager.Instance.individualFingerTracking = true; + CVRInputManager.Instance.finger1StretchedLeftThumb = FingerSystem.Instance.m_lastValues[0]; + CVRInputManager.Instance.finger2StretchedLeftThumb = FingerSystem.Instance.m_lastValues[1]; + CVRInputManager.Instance.finger3StretchedLeftThumb = FingerSystem.Instance.m_lastValues[2]; + CVRInputManager.Instance.fingerSpreadLeftThumb = FingerSystem.Instance.m_lastValues[3]; + CVRInputManager.Instance.finger1StretchedLeftIndex = FingerSystem.Instance.m_lastValues[4]; + CVRInputManager.Instance.finger2StretchedLeftIndex = FingerSystem.Instance.m_lastValues[5]; + CVRInputManager.Instance.finger3StretchedLeftIndex = FingerSystem.Instance.m_lastValues[6]; + CVRInputManager.Instance.fingerSpreadLeftIndex = FingerSystem.Instance.m_lastValues[7]; + CVRInputManager.Instance.finger1StretchedLeftMiddle = FingerSystem.Instance.m_lastValues[8]; + CVRInputManager.Instance.finger2StretchedLeftMiddle = FingerSystem.Instance.m_lastValues[9]; + CVRInputManager.Instance.finger3StretchedLeftMiddle = FingerSystem.Instance.m_lastValues[10]; + CVRInputManager.Instance.fingerSpreadLeftMiddle = FingerSystem.Instance.m_lastValues[11]; + CVRInputManager.Instance.finger1StretchedLeftRing = FingerSystem.Instance.m_lastValues[12]; + CVRInputManager.Instance.finger2StretchedLeftRing = FingerSystem.Instance.m_lastValues[13]; + CVRInputManager.Instance.finger3StretchedLeftRing = FingerSystem.Instance.m_lastValues[14]; + CVRInputManager.Instance.fingerSpreadLeftRing = FingerSystem.Instance.m_lastValues[15]; + CVRInputManager.Instance.finger1StretchedLeftPinky = FingerSystem.Instance.m_lastValues[16]; + CVRInputManager.Instance.finger2StretchedLeftPinky = FingerSystem.Instance.m_lastValues[17]; + CVRInputManager.Instance.finger3StretchedLeftPinky = FingerSystem.Instance.m_lastValues[18]; + CVRInputManager.Instance.fingerSpreadLeftPinky = FingerSystem.Instance.m_lastValues[19]; + CVRInputManager.Instance.finger1StretchedRightThumb = FingerSystem.Instance.m_lastValues[20]; + CVRInputManager.Instance.finger2StretchedRightThumb = FingerSystem.Instance.m_lastValues[21]; + CVRInputManager.Instance.finger3StretchedRightThumb = FingerSystem.Instance.m_lastValues[22]; + CVRInputManager.Instance.fingerSpreadRightThumb = FingerSystem.Instance.m_lastValues[23]; + CVRInputManager.Instance.finger1StretchedRightIndex = FingerSystem.Instance.m_lastValues[24]; + CVRInputManager.Instance.finger2StretchedRightIndex = FingerSystem.Instance.m_lastValues[25]; + CVRInputManager.Instance.finger3StretchedRightIndex = FingerSystem.Instance.m_lastValues[26]; + CVRInputManager.Instance.fingerSpreadRightIndex = FingerSystem.Instance.m_lastValues[27]; + CVRInputManager.Instance.finger1StretchedRightMiddle = FingerSystem.Instance.m_lastValues[28]; + CVRInputManager.Instance.finger2StretchedRightMiddle = FingerSystem.Instance.m_lastValues[29]; + CVRInputManager.Instance.finger3StretchedRightMiddle = FingerSystem.Instance.m_lastValues[30]; + CVRInputManager.Instance.fingerSpreadRightMiddle = FingerSystem.Instance.m_lastValues[31]; + CVRInputManager.Instance.finger1StretchedRightRing = FingerSystem.Instance.m_lastValues[32]; + CVRInputManager.Instance.finger2StretchedRightRing = FingerSystem.Instance.m_lastValues[33]; + CVRInputManager.Instance.finger3StretchedRightRing = FingerSystem.Instance.m_lastValues[34]; + CVRInputManager.Instance.fingerSpreadRightRing = FingerSystem.Instance.m_lastValues[35]; + CVRInputManager.Instance.finger1StretchedRightPinky = FingerSystem.Instance.m_lastValues[36]; + CVRInputManager.Instance.finger2StretchedRightPinky = FingerSystem.Instance.m_lastValues[37]; + CVRInputManager.Instance.finger3StretchedRightPinky = FingerSystem.Instance.m_lastValues[38]; + CVRInputManager.Instance.fingerSpreadRightPinky = FingerSystem.Instance.m_lastValues[39]; + } + } + + void OnSwitchToVR() + { + try + { + SetupHandlers(); + } + catch(System.Exception e) + { + MelonLoader.MelonLogger.Error(e); + } + } + + void OnSwitchToDesktop() + { + try + { + RemoveHandlers(); + } + catch(System.Exception e) + { + MelonLoader.MelonLogger.Error(e); + } + } + + // Settings + void OnSkeletalInputChange(bool p_value) + { + if(!p_value) + CVRInputManager.Instance.individualFingerTracking = Utils.AreKnucklesInUse(); + } + } +} diff --git a/ml_bft/Main.cs b/ml_bft/Main.cs new file mode 100644 index 0000000..87b5a6c --- /dev/null +++ b/ml_bft/Main.cs @@ -0,0 +1,141 @@ +using ABI_RC.Core.Player; +using ABI_RC.Systems.IK; +using ABI_RC.Systems.InputManagement; +using System; +using System.Collections; +using System.Reflection; +using UnityEngine; + +namespace ml_bft +{ + public class BetterFingersTracking : MelonLoader.MelonMod + { + static BetterFingersTracking ms_instance = null; + + InputHandler m_inputHandler = null; + FingerSystem m_fingerSystem = null; + + public override void OnInitializeMelon() + { + if(ms_instance == null) + ms_instance = this; + + Settings.Init(); + AssetsHandler.Load(); + + // Needed patches: avatar initialization and reinitialization on vr switch, after input update, after late iksystem update + HarmonyInstance.Patch( + typeof(PlayerSetup).GetMethod(nameof(PlayerSetup.SetupAvatar)), + null, + new HarmonyLib.HarmonyMethod(typeof(BetterFingersTracking).GetMethod(nameof(OnSetupAvatar_Postfix), BindingFlags.Static | BindingFlags.NonPublic)) + ); + HarmonyInstance.Patch( + typeof(PlayerSetup).GetMethod(nameof(PlayerSetup.ClearAvatar)), + null, + new HarmonyLib.HarmonyMethod(typeof(BetterFingersTracking).GetMethod(nameof(OnAvatarClear_Postfix), BindingFlags.Static | BindingFlags.NonPublic)) + ); + HarmonyInstance.Patch( + typeof(IKSystem).GetMethod(nameof(IKSystem.ReinitializeAvatar), BindingFlags.Instance | BindingFlags.Public), + null, + new HarmonyLib.HarmonyMethod(typeof(BetterFingersTracking).GetMethod(nameof(OnReinitializeAvatar_Postfix), BindingFlags.Static | BindingFlags.NonPublic)) + ); + HarmonyInstance.Patch( + typeof(CVRInputManager).GetMethod("UpdateInput", BindingFlags.NonPublic | BindingFlags.Instance), + null, + new HarmonyLib.HarmonyMethod(typeof(BetterFingersTracking).GetMethod(nameof(OnInputUpdate_Postfix), BindingFlags.NonPublic | BindingFlags.Static)) + ); + HarmonyInstance.Patch( + typeof(IKSystem).GetMethod("LateUpdate", BindingFlags.NonPublic | BindingFlags.Instance), + null, + new HarmonyLib.HarmonyMethod(typeof(BetterFingersTracking).GetMethod(nameof(OnIKSystemLateUpdate_Postfix), BindingFlags.NonPublic | BindingFlags.Static)) + ); + + MelonLoader.MelonCoroutines.Start(WaitForInstances()); + } + IEnumerator WaitForInstances() + { + while(CVRInputManager.Instance == null) + yield return null; + + m_inputHandler = new InputHandler(); + m_fingerSystem = new FingerSystem(); + } + + public override void OnDeinitializeMelon() + { + if(ms_instance == this) + ms_instance = null; + + m_inputHandler?.Cleanup(); + m_inputHandler = null; + + m_fingerSystem?.Cleanup(); + m_fingerSystem = null; + } + + static void OnSetupAvatar_Postfix() => ms_instance?.OnSetupAvatar(); + void OnSetupAvatar() + { + try + { + m_fingerSystem?.OnAvatarSetup(); + } + catch(Exception e) + { + MelonLoader.MelonLogger.Error(e); + } + } + + static void OnAvatarClear_Postfix() => ms_instance?.OnAvatarClear(); + void OnAvatarClear() + { + try + { + m_fingerSystem?.OnAvatarClear(); + } + catch(Exception e) + { + MelonLoader.MelonLogger.Error(e); + } + } + + static void OnReinitializeAvatar_Postfix() => ms_instance?.OnAvatarReinitialize(); + void OnAvatarReinitialize() + { + try + { + m_fingerSystem?.OnReinitializeAvatar(); + } + catch(Exception e) + { + MelonLoader.MelonLogger.Error(e); + } + } + + static void OnInputUpdate_Postfix() => ms_instance?.OnInputUpdate(); + void OnInputUpdate() + { + try + { + m_inputHandler?.OnInputUpdate(); + } + catch(Exception e) + { + MelonLoader.MelonLogger.Error(e); + } + } + + static void OnIKSystemLateUpdate_Postfix(HumanPoseHandler ____humanPoseHandler, Transform ____hipTransform) => ms_instance?.OnIKSystemLateUpdate(____humanPoseHandler, ____hipTransform); + void OnIKSystemLateUpdate(HumanPoseHandler p_handler, Transform p_hips) + { + try + { + m_fingerSystem?.OnIKSystemLateUpdate(p_handler, p_hips); + } + catch(Exception e) + { + MelonLoader.MelonLogger.Error(e); + } + } + } +} diff --git a/ml_bft/Properties/AssemblyInfo.cs b/ml_bft/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..e9dd526 --- /dev/null +++ b/ml_bft/Properties/AssemblyInfo.cs @@ -0,0 +1,4 @@ +[assembly: MelonLoader.MelonInfo(typeof(ml_bft.BetterFingersTracking), "BetterFingersTracking", "1.0.1", "SDraw", "https://github.com/SDraw/ml_mods_cvr")] +[assembly: MelonLoader.MelonGame(null, "ChilloutVR")] +[assembly: MelonLoader.MelonPlatform(MelonLoader.MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)] +[assembly: MelonLoader.MelonPlatformDomain(MelonLoader.MelonPlatformDomainAttribute.CompatibleDomains.MONO)] diff --git a/ml_bft/README.md b/ml_bft/README.md new file mode 100644 index 0000000..40de03a --- /dev/null +++ b/ml_bft/README.md @@ -0,0 +1,21 @@ +# Better Fingers Tracking +Mod that overhauls behaviour of fingers tracking. + +# Installation +* Install [latest MelonLoader](https://github.com/LavaGang/MelonLoader) +* Get [latest release DLL](../../../releases/latest): + * Put `ml_bft.dll` in `Mods` folder of game + +# Usage +Available mod's settings in `Settings - Input & Key-Bindings - Better Fingers Tracking`: +* **Force SteamVR skeletal input:** forced usage of SteamVR skeletal input (works as long as controllers' driver supplies skeletal pose throught OpenVR interfaces); `false` by default +* **Motion range:** fingers tracking motion range/mode/type; `With controller` by default +* **Filter humanoid limits:** Limits fingers rotations to be valid for Unity's Mechanim; `true` by default + * Note: Enabling this option ensures that visual representation of your fingers will be same for you and remote players, but it cancels out additional finger segments rotations that can be better visually in most cases. +* **Show hands model:** shows transparent hands model (mostly as debug option); `false` by default + +# Notes +* Currently supports only SteamVR environment, OpenXR support is planned. +* Fingers tracking quality is highly dependant on avatar's hand state in Unity's T-pose, possible solutions are in search. +* For Oculus Quest controllers (all versions) be sure that skeleton bindings are properly set up in SteamVR controllers bindings. + diff --git a/ml_bft/ResourcesHandler.cs b/ml_bft/ResourcesHandler.cs new file mode 100644 index 0000000..1d444a2 --- /dev/null +++ b/ml_bft/ResourcesHandler.cs @@ -0,0 +1,26 @@ +using System; +using System.IO; +using System.Reflection; + +namespace ml_bft +{ + static class ResourcesHandler + { + public static string GetEmbeddedResource(string p_name) + { + string l_result = ""; + Assembly l_assembly = Assembly.GetExecutingAssembly(); + string l_assemblyName = l_assembly.GetName().Name; + + try + { + Stream l_libraryStream = l_assembly.GetManifestResourceStream(l_assemblyName + ".resources." + p_name); + StreamReader l_streadReader = new StreamReader(l_libraryStream); + l_result = l_streadReader.ReadToEnd(); + } + catch(Exception) { } + + return l_result; + } + } +} diff --git a/ml_bft/Settings.cs b/ml_bft/Settings.cs new file mode 100644 index 0000000..c846166 --- /dev/null +++ b/ml_bft/Settings.cs @@ -0,0 +1,127 @@ +using ABI_RC.Core.InteractionSystem; +using System; +using System.Collections.Generic; + +namespace ml_bft +{ + static class Settings + { + public enum MotionRangeType + { + WithController = 0, + WithoutController + } + enum ModSetting + { + SkeletalInput = 0, + MotionRange, + ShowHands, + MechanimFilter + } + + public static bool SkeletalInput { get; private set; } = false; + public static MotionRangeType MotionRange { get; private set; } = MotionRangeType.WithController; + public static bool ShowHands { get; private set; } = false; + public static bool MechanimFilter { get; private set; } = true; + + static MelonLoader.MelonPreferences_Category ms_category = null; + static List ms_entries = null; + + public static event Action SkeletalInputChange; + public static event Action MotionRangeChange; + public static event Action ShowHandsChange; + public static event Action MechanimFilterChange; + + internal static void Init() + { + ms_category = MelonLoader.MelonPreferences.CreateCategory("BFT", null, true); + + ms_entries = new List() + { + ms_category.CreateEntry(ModSetting.SkeletalInput.ToString(), SkeletalInput), + ms_category.CreateEntry(ModSetting.MotionRange.ToString(), (int)MotionRange), + ms_category.CreateEntry(ModSetting.ShowHands.ToString(), ShowHands), + ms_category.CreateEntry(ModSetting.MechanimFilter.ToString(), MechanimFilter) + }; + + SkeletalInput = (bool)ms_entries[(int)ModSetting.SkeletalInput].BoxedValue; + MotionRange = (MotionRangeType)(int)ms_entries[(int)ModSetting.MotionRange].BoxedValue; + ShowHands = (bool)ms_entries[(int)ModSetting.ShowHands].BoxedValue; + + MelonLoader.MelonCoroutines.Start(WaitMainMenuUi()); + } + + static System.Collections.IEnumerator WaitMainMenuUi() + { + while(ViewManager.Instance == null) + yield return null; + while(ViewManager.Instance.gameMenuView == null) + yield return null; + while(ViewManager.Instance.gameMenuView.Listener == null) + yield return null; + + ViewManager.Instance.gameMenuView.Listener.ReadyForBindings += () => + { + ViewManager.Instance.gameMenuView.View.BindCall("OnToggleUpdate_" + ms_category.Identifier, new Action(OnToggleUpdate)); + ViewManager.Instance.gameMenuView.View.BindCall("OnDropdownUpdate_" + ms_category.Identifier, new Action(OnDropdownUpdate)); + }; + ViewManager.Instance.gameMenuView.Listener.FinishLoad += (_) => + { + ViewManager.Instance.gameMenuView.View.ExecuteScript(ResourcesHandler.GetEmbeddedResource("mods_extension.js")); + ViewManager.Instance.gameMenuView.View.ExecuteScript(ResourcesHandler.GetEmbeddedResource("mod_menu.js")); + foreach(var l_entry in ms_entries) + ViewManager.Instance.gameMenuView.View.TriggerEvent("updateModSetting", ms_category.Identifier, l_entry.DisplayName, l_entry.GetValueAsString()); + }; + } + + static void OnToggleUpdate(string p_name, string p_value) + { + if(Enum.TryParse(p_name, out ModSetting l_setting)) + { + switch(l_setting) + { + case ModSetting.SkeletalInput: + { + SkeletalInput = bool.Parse(p_value); + SkeletalInputChange?.Invoke(SkeletalInput); + } + break; + + case ModSetting.ShowHands: + { + ShowHands = bool.Parse(p_value); + ShowHandsChange?.Invoke(ShowHands); + } + break; + + case ModSetting.MechanimFilter: + { + MechanimFilter = bool.Parse(p_value); + MechanimFilterChange?.Invoke(MechanimFilter); + } + break; + } + + ms_entries[(int)l_setting].BoxedValue = bool.Parse(p_value); + } + } + + static void OnDropdownUpdate(string p_name, string p_value) + { + if(Enum.TryParse(p_name, out ModSetting l_setting)) + { + switch(l_setting) + { + case ModSetting.MotionRange: + { + MotionRange = (MotionRangeType)int.Parse(p_value); + MotionRangeChange?.Invoke(MotionRange); + } + break; + } + + ms_entries[(int)l_setting].BoxedValue = int.Parse(p_value); + } + } + } +} diff --git a/ml_bft/Utils.cs b/ml_bft/Utils.cs new file mode 100644 index 0000000..340630f --- /dev/null +++ b/ml_bft/Utils.cs @@ -0,0 +1,25 @@ +using ABI_RC.Core.Player; +using ABI_RC.Core.UI; +using ABI_RC.Systems.IK; +using ABI_RC.Systems.InputManagement; +using System.Reflection; +using UnityEngine; + +namespace ml_bft +{ + static class Utils + { + static readonly FieldInfo ms_view = typeof(CohtmlControlledViewWrapper).GetField("_view", BindingFlags.NonPublic | BindingFlags.Instance); + + public static void ExecuteScript(this CohtmlControlledViewWrapper p_instance, string p_script) => ((cohtml.Net.View)ms_view.GetValue(p_instance)).ExecuteScript(p_script); + + public static bool AreKnucklesInUse() => ((CVRInputManager.Instance._leftController == ABI_RC.Systems.InputManagement.XR.eXRControllerType.Index) || (CVRInputManager.Instance._rightController == ABI_RC.Systems.InputManagement.XR.eXRControllerType.Index)); + + public static void SetAvatarTPose() + { + IKSystem.Instance.SetAvatarPose(IKSystem.AvatarPose.TPose); + PlayerSetup.Instance._avatar.transform.localPosition = Vector3.zero; + PlayerSetup.Instance._avatar.transform.localRotation = Quaternion.identity; + } + } +} diff --git a/ml_bft/ml_bft.csproj b/ml_bft/ml_bft.csproj new file mode 100644 index 0000000..0869bde --- /dev/null +++ b/ml_bft/ml_bft.csproj @@ -0,0 +1,100 @@ + + + + netstandard2.1 + x64 + BetterFingersTracking + SDraw + None + BetterFingersTracking + 1.0.1 + + + + embedded + true + + + + + + + + + + + + + + + D:\games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\0Harmony.dll + false + false + + + D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Assembly-CSharp.dll + false + false + + + D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\cohtml.Net.dll + false + false + + + D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Cohtml.Runtime.dll + false + false + + + D:\games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\MelonLoader.dll + false + false + + + D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\SteamVR.dll + false + false + + + D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Unity.XR.Hands.dll + false + false + + + D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Unity.XR.OpenVR.dll + false + false + + + D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Unity.XR.OpenXR.dll + false + false + + + D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.AnimationModule.dll + false + false + + + D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.AssetBundleModule.dll + false + false + + + D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.CoreModule.dll + false + false + + + D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.XRModule.dll + false + false + + + + + + + + diff --git a/ml_bft/resources/mod_menu.js b/ml_bft/resources/mod_menu.js new file mode 100644 index 0000000..e682f83 --- /dev/null +++ b/ml_bft/resources/mod_menu.js @@ -0,0 +1,46 @@ +{ + let l_block = document.createElement('div'); + l_block.innerHTML = ` + + Better Fingers Tracking + + + + + Force SteamVR skeletal input: + + + + + + + Motion range: + + + + + + + Filter humanoid limits: + + + + + + + Show hands model: + + + + + `; + document.getElementById('settings-input').appendChild(l_block); + + // Toggles + for (let l_toggle of l_block.querySelectorAll('.inp_toggle')) + modsExtension.addSetting('BFT', l_toggle.id, modsExtension.createToggle(l_toggle, 'OnToggleUpdate_BFT')); + + // Dropdowns + for (let l_dropdown of l_block.querySelectorAll('.inp_dropdown')) + modsExtension.addSetting('BFT', l_dropdown.id, modsExtension.createDropdown(l_dropdown, 'OnDropdownUpdate_BFT')); +} diff --git a/ml_bft/resources/ovr_fingers.asset b/ml_bft/resources/ovr_fingers.asset new file mode 100644 index 0000000..9e52c60 Binary files /dev/null and b/ml_bft/resources/ovr_fingers.asset differ diff --git a/ml_bft/resources/oxr_fingers.asset b/ml_bft/resources/oxr_fingers.asset new file mode 100644 index 0000000..f4cd5b7 Binary files /dev/null and b/ml_bft/resources/oxr_fingers.asset differ diff --git a/ml_dht/DataParser.cs b/ml_dht/DataParser.cs index abcceaf..a1844ed 100644 --- a/ml_dht/DataParser.cs +++ b/ml_dht/DataParser.cs @@ -1,29 +1,29 @@ -namespace ml_dht -{ - class DataParser - { - MemoryMapReader m_mapReader = null; - byte[] m_buffer = null; - TrackingData m_trackingData; - - public DataParser() - { - m_buffer = new byte[1024]; - m_mapReader = new MemoryMapReader(); - m_mapReader.Open("head/data"); - } - ~DataParser() - { - m_mapReader.Close(); - m_mapReader = null; - } - - public void Update() - { - if(m_mapReader.Read(ref m_buffer)) - m_trackingData = TrackingData.ToObject(m_buffer); - } - - public ref TrackingData GetLatestTrackingData() => ref m_trackingData; - } -} +namespace ml_dht +{ + class DataParser + { + MemoryMapReader m_mapReader = null; + byte[] m_buffer = null; + TrackingData m_trackingData; + + public DataParser() + { + m_buffer = new byte[1024]; + m_mapReader = new MemoryMapReader(); + m_mapReader.Open("head/data"); + } + ~DataParser() + { + m_mapReader.Close(); + m_mapReader = null; + } + + public void Update() + { + if(m_mapReader.Read(ref m_buffer)) + m_trackingData = TrackingData.ToObject(m_buffer); + } + + public ref TrackingData GetLatestTrackingData() => ref m_trackingData; + } +} diff --git a/ml_dht/HeadTracked.cs b/ml_dht/HeadTracked.cs index ab2a265..434a825 100644 --- a/ml_dht/HeadTracked.cs +++ b/ml_dht/HeadTracked.cs @@ -1,197 +1,198 @@ -using ABI.CCK.Components; -using ABI_RC.Core.Player; -using ABI_RC.Core.Player.EyeMovement; -using ABI_RC.Systems.FaceTracking; -using ABI_RC.Systems.VRModeSwitch; -using RootMotion.FinalIK; -using System; -using System.Reflection; -using UnityEngine; -using ViveSR.anipal.Lip; - -namespace ml_dht -{ - [DisallowMultipleComponent] - class HeadTracked : MonoBehaviour - { - static FieldInfo ms_emotePlaying = typeof(PlayerSetup).GetField("_emotePlaying", BindingFlags.NonPublic | BindingFlags.Instance); - - bool m_enabled = false; - bool m_headTracking = true; - float m_smoothing = 0.5f; - - CVRAvatar m_avatarDescriptor = null; - Transform m_camera = null; - LookAtIK m_lookIK = null; - Transform m_headBone = null; - - Vector3 m_headPosition; - Quaternion m_headRotation; - Vector2 m_gazeDirection; - float m_blinkProgress = 0f; - LipData_v2 m_lipData; - bool m_lipDataSent = false; - - Quaternion m_bindRotation; - Quaternion m_lastHeadRotation; - - internal HeadTracked() - { - m_lipData = new LipData_v2(); - m_lipData.frame = 0; - m_lipData.time = 0; - m_lipData.image = IntPtr.Zero; - m_lipData.prediction_data = new PredictionData_v2(); - m_lipData.prediction_data.blend_shape_weight = new float[(int)LipShape_v2.Max]; - } - - // Unity events - void Start() - { - SetEnabled(Settings.Enabled); - SetHeadTracking(Settings.HeadTracking); - SetSmoothing(Settings.Smoothing); - - Settings.EnabledChange += this.SetEnabled; - Settings.HeadTrackingChange += this.SetHeadTracking; - Settings.SmoothingChange += this.SetSmoothing; - } - - void OnDestroy() - { - Settings.EnabledChange -= this.SetEnabled; - Settings.HeadTrackingChange -= this.SetHeadTracking; - Settings.SmoothingChange -= this.SetSmoothing; - } - - void Update() - { - if(m_lipDataSent) - m_lipDataSent = false; - } - - // Tracking updates - public void UpdateTrackingData(ref TrackingData p_data) - { - m_headPosition.Set(p_data.m_headPositionX * (Settings.Mirrored ? -1f : 1f), p_data.m_headPositionY, p_data.m_headPositionZ); - m_headRotation.Set(p_data.m_headRotationX, p_data.m_headRotationY * (Settings.Mirrored ? -1f : 1f), p_data.m_headRotationZ * (Settings.Mirrored ? -1f : 1f), p_data.m_headRotationW); - m_gazeDirection.Set(Settings.Mirrored ? (1f - p_data.m_gazeX) : p_data.m_gazeX, p_data.m_gazeY); - m_blinkProgress = p_data.m_blink; - - float l_weight = Mathf.Clamp01(Mathf.InverseLerp(0.25f, 1f, Mathf.Abs(p_data.m_mouthShape))); - m_lipData.prediction_data.blend_shape_weight[(int)LipShape_v2.Jaw_Open] = p_data.m_mouthOpen; - m_lipData.prediction_data.blend_shape_weight[(int)LipShape_v2.Mouth_Pout] = ((p_data.m_mouthShape > 0f) ? l_weight : 0f); - m_lipData.prediction_data.blend_shape_weight[(int)LipShape_v2.Mouth_Smile_Left] = ((p_data.m_mouthShape < 0f) ? l_weight : 0f); - m_lipData.prediction_data.blend_shape_weight[(int)LipShape_v2.Mouth_Smile_Right] = ((p_data.m_mouthShape < 0f) ? l_weight : 0f); - } - - void OnLookIKPostUpdate() - { - if(m_enabled && m_headTracking && (m_headBone != null)) - { - m_lastHeadRotation = Quaternion.Slerp(m_lastHeadRotation, m_avatarDescriptor.transform.rotation * (m_headRotation * m_bindRotation), m_smoothing); - - if(!(bool)ms_emotePlaying.GetValue(PlayerSetup.Instance)) - m_headBone.rotation = m_lastHeadRotation; - } - } - - // Game events - internal void OnSetupAvatar() - { - m_camera = PlayerSetup.Instance.GetActiveCamera().transform; - m_avatarDescriptor = PlayerSetup.Instance._avatar.GetComponent(); - m_headBone = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.Head); - m_lookIK = PlayerSetup.Instance._avatar.GetComponent(); - - if(m_headBone != null) - m_bindRotation = (m_avatarDescriptor.transform.GetMatrix().inverse * m_headBone.GetMatrix()).rotation; - - if(m_lookIK != null) - m_lookIK.onPostSolverUpdate.AddListener(this.OnLookIKPostUpdate); - - } - internal void OnAvatarClear() - { - m_avatarDescriptor = null; - m_lookIK = null; - m_headBone = null; - m_lastHeadRotation = Quaternion.identity; - m_bindRotation = Quaternion.identity; - } - internal void OnAvatarReinitialize() - { - m_camera = PlayerSetup.Instance.GetActiveCamera().transform; - m_lookIK = PlayerSetup.Instance._avatar.GetComponent(); - if(m_lookIK != null) - m_lookIK.onPostSolverUpdate.AddListener(this.OnLookIKPostUpdate); - } - - internal void OnEyeControllerUpdate(EyeMovementController p_component) - { - if(m_enabled) - { - // Gaze - if(Settings.EyeTracking && (m_camera != null)) - { - p_component.manualViewTarget = true; - p_component.targetViewPosition = m_camera.position + m_camera.rotation * new Vector3((m_gazeDirection.x - 0.5f) * 2f, (m_gazeDirection.y - 0.5f) * 2f, 1f); - } - - // Blink - if(Settings.Blinking) - { - p_component.manualBlinking = true; - p_component.blinkProgress = m_blinkProgress; - } - } - } - - internal bool UpdateFaceTracking(CVRFaceTracking p_component) - { - bool l_result = false; - if(m_enabled && Settings.FaceTracking) - { - if(!m_lipDataSent) - { - FaceTrackingManager.Instance.SubmitNewFacialData(m_lipData); - m_lipDataSent = true; - } - p_component.LipSyncWasUpdated = true; - p_component.UpdateShapesLocal_Private(); - - l_result = true; - } - return l_result; - } - - // Settings - void SetEnabled(bool p_state) - { - if(m_enabled != p_state) - { - m_enabled = p_state; - TryRestoreHeadRotation(); - } - } - void SetHeadTracking(bool p_state) - { - if(m_headTracking != p_state) - { - m_headTracking = p_state; - TryRestoreHeadRotation(); - } - } - void SetSmoothing(float p_value) - { - m_smoothing = 1f - Mathf.Clamp(p_value, 0f, 0.99f); - } - - // Arbitrary - void TryRestoreHeadRotation() - { - if(m_enabled && m_headTracking) - m_lastHeadRotation = ((m_headBone != null) ? m_headBone.rotation : m_bindRotation); - } - } -} +using ABI.CCK.Components; +using ABI_RC.Core.Player; +using ABI_RC.Core.Player.EyeMovement; +using ABI_RC.Systems.FaceTracking; +using RootMotion.FinalIK; +using System; +using System.Reflection; +using UnityEngine; +using ViveSR.anipal.Lip; + +namespace ml_dht +{ + [DisallowMultipleComponent] + class HeadTracked : MonoBehaviour + { + static FieldInfo ms_emotePlaying = typeof(PlayerSetup).GetField("_emotePlaying", BindingFlags.NonPublic | BindingFlags.Instance); + + bool m_enabled = false; + bool m_headTracking = true; + float m_smoothing = 0.5f; + + CVRAvatar m_avatarDescriptor = null; + Transform m_camera = null; + LookAtIK m_lookIK = null; + Transform m_headBone = null; + + Vector3 m_headPosition; + Quaternion m_headRotation; + Vector2 m_gazeDirection; + float m_blinkProgress = 0f; + LipData_v2 m_lipData; + bool m_lipDataSent = false; + + Quaternion m_bindRotation; + Quaternion m_lastHeadRotation; + + internal HeadTracked() + { + m_lipData = new LipData_v2(); + m_lipData.frame = 0; + m_lipData.time = 0; + m_lipData.image = IntPtr.Zero; + m_lipData.prediction_data = new PredictionData_v2(); + m_lipData.prediction_data.blend_shape_weight = new float[(int)LipShape_v2.Max]; + } + + // Unity events + void Start() + { + SetEnabled(Settings.Enabled); + SetHeadTracking(Settings.HeadTracking); + SetSmoothing(Settings.Smoothing); + + Settings.EnabledChange += this.SetEnabled; + Settings.HeadTrackingChange += this.SetHeadTracking; + Settings.SmoothingChange += this.SetSmoothing; + } + + void OnDestroy() + { + Settings.EnabledChange -= this.SetEnabled; + Settings.HeadTrackingChange -= this.SetHeadTracking; + Settings.SmoothingChange -= this.SetSmoothing; + } + + void Update() + { + if(m_lipDataSent) + m_lipDataSent = false; + } + + // Tracking updates + public void UpdateTrackingData(ref TrackingData p_data) + { + m_headPosition.Set(p_data.m_headPositionX * (Settings.Mirrored ? -1f : 1f), p_data.m_headPositionY, p_data.m_headPositionZ); + m_headRotation.Set(p_data.m_headRotationX, p_data.m_headRotationY * (Settings.Mirrored ? -1f : 1f), p_data.m_headRotationZ * (Settings.Mirrored ? -1f : 1f), p_data.m_headRotationW); + m_gazeDirection.Set(Settings.Mirrored ? (1f - p_data.m_gazeX) : p_data.m_gazeX, p_data.m_gazeY); + m_blinkProgress = p_data.m_blink; + + float l_weight = Mathf.Clamp01(Mathf.InverseLerp(0.25f, 1f, Mathf.Abs(p_data.m_mouthShape))); + m_lipData.prediction_data.blend_shape_weight[(int)LipShape_v2.Jaw_Open] = p_data.m_mouthOpen; + m_lipData.prediction_data.blend_shape_weight[(int)LipShape_v2.Mouth_Pout] = ((p_data.m_mouthShape > 0f) ? l_weight : 0f); + m_lipData.prediction_data.blend_shape_weight[(int)LipShape_v2.Mouth_Smile_Left] = ((p_data.m_mouthShape < 0f) ? l_weight : 0f); + m_lipData.prediction_data.blend_shape_weight[(int)LipShape_v2.Mouth_Smile_Right] = ((p_data.m_mouthShape < 0f) ? l_weight : 0f); + } + + void OnLookIKPostUpdate() + { + if(m_enabled && m_headTracking && (m_headBone != null)) + { + m_lastHeadRotation = Quaternion.Slerp(m_lastHeadRotation, m_avatarDescriptor.transform.rotation * (m_headRotation * m_bindRotation), m_smoothing); + + if(!(bool)ms_emotePlaying.GetValue(PlayerSetup.Instance)) + m_headBone.rotation = m_lastHeadRotation; + } + } + + // Game events + internal void OnSetupAvatar() + { + Utils.SetAvatarTPose(); + + m_camera = PlayerSetup.Instance.GetActiveCamera().transform; + m_avatarDescriptor = PlayerSetup.Instance._avatar.GetComponent(); + m_headBone = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.Head); + m_lookIK = PlayerSetup.Instance._avatar.GetComponent(); + + if(m_headBone != null) + m_bindRotation = Quaternion.Inverse(m_avatarDescriptor.transform.rotation) * m_headBone.rotation; + + if(m_lookIK != null) + m_lookIK.onPostSolverUpdate.AddListener(this.OnLookIKPostUpdate); + + } + internal void OnAvatarClear() + { + m_avatarDescriptor = null; + m_lookIK = null; + m_headBone = null; + m_lastHeadRotation = Quaternion.identity; + m_bindRotation = Quaternion.identity; + } + internal void OnAvatarReinitialize() + { + m_camera = PlayerSetup.Instance.GetActiveCamera().transform; + m_lookIK = PlayerSetup.Instance._avatar.GetComponent(); + if(m_lookIK != null) + m_lookIK.onPostSolverUpdate.AddListener(this.OnLookIKPostUpdate); + } + + internal void OnEyeControllerUpdate(EyeMovementController p_component) + { + if(m_enabled) + { + // Gaze + if(Settings.EyeTracking && (m_camera != null)) + { + p_component.manualViewTarget = true; + p_component.targetViewPosition = m_camera.position + m_camera.rotation * new Vector3((m_gazeDirection.x - 0.5f) * 2f, (m_gazeDirection.y - 0.5f) * 2f, 1f); + } + + // Blink + if(Settings.Blinking) + { + p_component.manualBlinking = true; + p_component.blinkProgress = m_blinkProgress; + } + } + } + + internal bool UpdateFaceTracking(CVRFaceTracking p_component) + { + bool l_result = false; + if(m_enabled && Settings.FaceTracking) + { + if(!m_lipDataSent) + { + FaceTrackingManager.Instance.SubmitNewFacialData(m_lipData); + m_lipDataSent = true; + } + p_component.LipSyncWasUpdated = true; + p_component.UpdateShapesLocal_Private(); + + l_result = true; + } + return l_result; + } + + // Settings + void SetEnabled(bool p_state) + { + if(m_enabled != p_state) + { + m_enabled = p_state; + TryRestoreHeadRotation(); + } + } + void SetHeadTracking(bool p_state) + { + if(m_headTracking != p_state) + { + m_headTracking = p_state; + TryRestoreHeadRotation(); + } + } + void SetSmoothing(float p_value) + { + m_smoothing = 1f - Mathf.Clamp(p_value, 0f, 0.99f); + } + + // Arbitrary + void TryRestoreHeadRotation() + { + if(m_enabled && m_headTracking) + m_lastHeadRotation = ((m_headBone != null) ? m_headBone.rotation : m_bindRotation); + } + } +} diff --git a/ml_dht/Main.cs b/ml_dht/Main.cs index c701136..ae4f9f8 100644 --- a/ml_dht/Main.cs +++ b/ml_dht/Main.cs @@ -1,155 +1,155 @@ -using ABI.CCK.Components; -using ABI_RC.Core.Player; -using ABI_RC.Core.Player.EyeMovement; -using ABI_RC.Core.Savior; -using ABI_RC.Systems.IK; -using System.Reflection; - -namespace ml_dht -{ - public class DesktopHeadTracking : MelonLoader.MelonMod - { - static DesktopHeadTracking ms_instance = null; - - DataParser m_dataParser = null; - HeadTracked m_localTracked = null; - - public override void OnInitializeMelon() - { - if(ms_instance == null) - ms_instance = this; - - Settings.Init(); - - // Patches - HarmonyInstance.Patch( - typeof(PlayerSetup).GetMethod(nameof(PlayerSetup.ClearAvatar)), - null, - new HarmonyLib.HarmonyMethod(typeof(DesktopHeadTracking).GetMethod(nameof(OnAvatarClear_Postfix), BindingFlags.Static | BindingFlags.NonPublic)) - ); - HarmonyInstance.Patch( - typeof(PlayerSetup).GetMethod(nameof(PlayerSetup.SetupAvatar)), - null, - new HarmonyLib.HarmonyMethod(typeof(DesktopHeadTracking).GetMethod(nameof(OnSetupAvatar_Postfix), BindingFlags.Static | BindingFlags.NonPublic)) - ); - HarmonyInstance.Patch( - typeof(IKSystem).GetMethod(nameof(IKSystem.ReinitializeAvatar), BindingFlags.Instance | BindingFlags.Public), - null, - new HarmonyLib.HarmonyMethod(typeof(DesktopHeadTracking).GetMethod(nameof(OnAvatarReinitialize_Postfix), BindingFlags.Static | BindingFlags.NonPublic)) - ); - - MelonLoader.MelonCoroutines.Start(WaitForInstances()); - } - - System.Collections.IEnumerator WaitForInstances() - { - while(MetaPort.Instance == null) - yield return null; - - while(PlayerSetup.Instance == null) - yield return null; - - m_dataParser = new DataParser(); - m_localTracked = PlayerSetup.Instance.gameObject.AddComponent(); - - // If you think it's a joke to put patch here, go on, try to put it in OnInitializeMelon, you melon :> - HarmonyInstance.Patch( - typeof(EyeMovementController).GetMethod("Update", BindingFlags.Instance | BindingFlags.NonPublic), - null, - new HarmonyLib.HarmonyMethod(typeof(DesktopHeadTracking).GetMethod(nameof(OnEyeControllerUpdate_Postfix), BindingFlags.Static | BindingFlags.NonPublic)) - ); - HarmonyInstance.Patch( - typeof(CVRFaceTracking).GetMethod("UpdateLocalData", BindingFlags.Instance | BindingFlags.NonPublic), - new HarmonyLib.HarmonyMethod(typeof(DesktopHeadTracking).GetMethod(nameof(OnFaceTrackingLocalUpdate_Prefix), BindingFlags.Static | BindingFlags.NonPublic)) - ); - } - - public override void OnDeinitializeMelon() - { - if(ms_instance == this) - ms_instance = null; - - m_dataParser = null; - m_localTracked = null; - } - - public override void OnUpdate() - { - if(Settings.Enabled && (m_dataParser != null)) - { - m_dataParser.Update(); - if(m_localTracked != null) - m_localTracked.UpdateTrackingData(ref m_dataParser.GetLatestTrackingData()); - } - } - - static void OnSetupAvatar_Postfix() => ms_instance?.OnSetupAvatar(); - void OnSetupAvatar() - { - try - { - if(m_localTracked != null) - m_localTracked.OnSetupAvatar(); - } - catch(System.Exception e) - { - MelonLoader.MelonLogger.Error(e); - } - } - - static void OnAvatarClear_Postfix() => ms_instance?.OnAvatarClear(); - void OnAvatarClear() - { - try - { - if(m_localTracked != null) - m_localTracked.OnAvatarClear(); - } - catch(System.Exception e) - { - MelonLoader.MelonLogger.Error(e); - } - } - - static void OnAvatarReinitialize_Postfix() => ms_instance?.OnAvatarReinitialize(); - void OnAvatarReinitialize() - { - try - { - if(m_localTracked != null) - m_localTracked.OnAvatarReinitialize(); - } - catch(System.Exception e) - { - MelonLoader.MelonLogger.Error(e); - } - } - - static void OnEyeControllerUpdate_Postfix(ref EyeMovementController __instance) => ms_instance?.OnEyeControllerUpdate(__instance); - void OnEyeControllerUpdate(EyeMovementController p_component) - { - try - { - if(p_component.IsLocal && (m_localTracked != null)) - m_localTracked.OnEyeControllerUpdate(p_component); - } - catch(System.Exception e) - { - MelonLoader.MelonLogger.Error(e); - } - } - - static bool OnFaceTrackingLocalUpdate_Prefix(ref CVRFaceTracking __instance) - { - bool? l_result = ms_instance?.OnFaceTrackingLocalUpdate(__instance); - return l_result.GetValueOrDefault(true); - } - bool OnFaceTrackingLocalUpdate(CVRFaceTracking p_component) - { - bool l_result = true; - if(p_component.UseFacialTracking && (m_localTracked != null)) - l_result = !m_localTracked.UpdateFaceTracking(p_component); - return l_result; - } - } +using ABI.CCK.Components; +using ABI_RC.Core.Player; +using ABI_RC.Core.Player.EyeMovement; +using ABI_RC.Core.Savior; +using ABI_RC.Systems.IK; +using System.Reflection; + +namespace ml_dht +{ + public class DesktopHeadTracking : MelonLoader.MelonMod + { + static DesktopHeadTracking ms_instance = null; + + DataParser m_dataParser = null; + HeadTracked m_localTracked = null; + + public override void OnInitializeMelon() + { + if(ms_instance == null) + ms_instance = this; + + Settings.Init(); + + // Patches + HarmonyInstance.Patch( + typeof(PlayerSetup).GetMethod(nameof(PlayerSetup.ClearAvatar)), + null, + new HarmonyLib.HarmonyMethod(typeof(DesktopHeadTracking).GetMethod(nameof(OnAvatarClear_Postfix), BindingFlags.Static | BindingFlags.NonPublic)) + ); + HarmonyInstance.Patch( + typeof(PlayerSetup).GetMethod(nameof(PlayerSetup.SetupAvatar)), + null, + new HarmonyLib.HarmonyMethod(typeof(DesktopHeadTracking).GetMethod(nameof(OnSetupAvatar_Postfix), BindingFlags.Static | BindingFlags.NonPublic)) + ); + HarmonyInstance.Patch( + typeof(IKSystem).GetMethod(nameof(IKSystem.ReinitializeAvatar), BindingFlags.Instance | BindingFlags.Public), + null, + new HarmonyLib.HarmonyMethod(typeof(DesktopHeadTracking).GetMethod(nameof(OnAvatarReinitialize_Postfix), BindingFlags.Static | BindingFlags.NonPublic)) + ); + + MelonLoader.MelonCoroutines.Start(WaitForInstances()); + } + + System.Collections.IEnumerator WaitForInstances() + { + while(MetaPort.Instance == null) + yield return null; + + while(PlayerSetup.Instance == null) + yield return null; + + m_dataParser = new DataParser(); + m_localTracked = PlayerSetup.Instance.gameObject.AddComponent(); + + // If you think it's a joke to put patch here, go on, try to put it in OnInitializeMelon, you melon :> + HarmonyInstance.Patch( + typeof(EyeMovementController).GetMethod("Update", BindingFlags.Instance | BindingFlags.NonPublic), + null, + new HarmonyLib.HarmonyMethod(typeof(DesktopHeadTracking).GetMethod(nameof(OnEyeControllerUpdate_Postfix), BindingFlags.Static | BindingFlags.NonPublic)) + ); + HarmonyInstance.Patch( + typeof(CVRFaceTracking).GetMethod("UpdateLocalData", BindingFlags.Instance | BindingFlags.NonPublic), + new HarmonyLib.HarmonyMethod(typeof(DesktopHeadTracking).GetMethod(nameof(OnFaceTrackingLocalUpdate_Prefix), BindingFlags.Static | BindingFlags.NonPublic)) + ); + } + + public override void OnDeinitializeMelon() + { + if(ms_instance == this) + ms_instance = null; + + m_dataParser = null; + m_localTracked = null; + } + + public override void OnUpdate() + { + if(Settings.Enabled && (m_dataParser != null)) + { + m_dataParser.Update(); + if(m_localTracked != null) + m_localTracked.UpdateTrackingData(ref m_dataParser.GetLatestTrackingData()); + } + } + + static void OnSetupAvatar_Postfix() => ms_instance?.OnSetupAvatar(); + void OnSetupAvatar() + { + try + { + if(m_localTracked != null) + m_localTracked.OnSetupAvatar(); + } + catch(System.Exception e) + { + MelonLoader.MelonLogger.Error(e); + } + } + + static void OnAvatarClear_Postfix() => ms_instance?.OnAvatarClear(); + void OnAvatarClear() + { + try + { + if(m_localTracked != null) + m_localTracked.OnAvatarClear(); + } + catch(System.Exception e) + { + MelonLoader.MelonLogger.Error(e); + } + } + + static void OnAvatarReinitialize_Postfix() => ms_instance?.OnAvatarReinitialize(); + void OnAvatarReinitialize() + { + try + { + if(m_localTracked != null) + m_localTracked.OnAvatarReinitialize(); + } + catch(System.Exception e) + { + MelonLoader.MelonLogger.Error(e); + } + } + + static void OnEyeControllerUpdate_Postfix(ref EyeMovementController __instance) => ms_instance?.OnEyeControllerUpdate(__instance); + void OnEyeControllerUpdate(EyeMovementController p_component) + { + try + { + if(p_component.IsLocal && (m_localTracked != null)) + m_localTracked.OnEyeControllerUpdate(p_component); + } + catch(System.Exception e) + { + MelonLoader.MelonLogger.Error(e); + } + } + + static bool OnFaceTrackingLocalUpdate_Prefix(ref CVRFaceTracking __instance) + { + bool? l_result = ms_instance?.OnFaceTrackingLocalUpdate(__instance); + return l_result.GetValueOrDefault(true); + } + bool OnFaceTrackingLocalUpdate(CVRFaceTracking p_component) + { + bool l_result = true; + if(p_component.UseFacialTracking && (m_localTracked != null)) + l_result = !m_localTracked.UpdateFaceTracking(p_component); + return l_result; + } + } } \ No newline at end of file diff --git a/ml_dht/Properties/AssemblyInfo.cs b/ml_dht/Properties/AssemblyInfo.cs index 7b7e58e..e58c6a7 100644 --- a/ml_dht/Properties/AssemblyInfo.cs +++ b/ml_dht/Properties/AssemblyInfo.cs @@ -1,4 +1,4 @@ -[assembly: MelonLoader.MelonInfo(typeof(ml_dht.DesktopHeadTracking), "DesktopHeadTracking", "1.2.1-ex", "SDraw", "https://github.com/SDraw/ml_mods_cvr")] -[assembly: MelonLoader.MelonGame(null, "ChilloutVR")] -[assembly: MelonLoader.MelonPlatform(MelonLoader.MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)] +[assembly: MelonLoader.MelonInfo(typeof(ml_dht.DesktopHeadTracking), "DesktopHeadTracking", "1.2.1", "SDraw", "https://github.com/SDraw/ml_mods_cvr")] +[assembly: MelonLoader.MelonGame(null, "ChilloutVR")] +[assembly: MelonLoader.MelonPlatform(MelonLoader.MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)] [assembly: MelonLoader.MelonPlatformDomain(MelonLoader.MelonPlatformDomainAttribute.CompatibleDomains.MONO)] \ No newline at end of file diff --git a/ml_dht/Settings.cs b/ml_dht/Settings.cs index 60bd767..c0ad6e9 100644 --- a/ml_dht/Settings.cs +++ b/ml_dht/Settings.cs @@ -28,14 +28,13 @@ namespace ml_dht static MelonLoader.MelonPreferences_Category ms_category = null; static List ms_entries = null; - static public event Action EnabledChange; - static public event Action HeadTrackingChange; - static public event Action EyeTrackingChange; - static public event Action FaceTrackingChange; - static public event Action BlinkingChange; - static public event Action MirroredChange; - static public event Action SmoothingChange; - + public static event Action EnabledChange; + public static event Action HeadTrackingChange; + public static event Action EyeTrackingChange; + public static event Action FaceTrackingChange; + public static event Action BlinkingChange; + public static event Action MirroredChange; + public static event Action SmoothingChange; internal static void Init() { diff --git a/ml_dht/TrackingData.cs b/ml_dht/TrackingData.cs index 43f9f97..45a881c 100644 --- a/ml_dht/TrackingData.cs +++ b/ml_dht/TrackingData.cs @@ -17,7 +17,7 @@ struct TrackingData public float m_mouthShape; // Range - [-1;1], -1 - wide, 1 - narrow public float m_brows; // Range - [-1;1], -1 - up, 1 - down; not used yet - static public byte[] ToBytes(TrackingData p_faceData) + public static byte[] ToBytes(TrackingData p_faceData) { int l_size = Marshal.SizeOf(p_faceData); byte[] l_arr = new byte[l_size]; @@ -29,7 +29,7 @@ struct TrackingData return l_arr; } - static public TrackingData ToObject(byte[] p_buffer) + public static TrackingData ToObject(byte[] p_buffer) { TrackingData l_faceData = new TrackingData(); diff --git a/ml_dht/Utils.cs b/ml_dht/Utils.cs index e3cff3c..547ff32 100644 --- a/ml_dht/Utils.cs +++ b/ml_dht/Utils.cs @@ -1,23 +1,27 @@ -using ABI.CCK.Components; -using ABI_RC.Core.UI; -using System.Reflection; -using UnityEngine; - -namespace ml_dht -{ - static class Utils - { - static readonly object[] ms_emptyArray = new object[0]; - static readonly FieldInfo ms_view = typeof(CohtmlControlledViewWrapper).GetField("_view", BindingFlags.NonPublic | BindingFlags.Instance); - static readonly MethodInfo ms_updateShapesLocal = typeof(CVRFaceTracking).GetMethod("UpdateShapesLocal", BindingFlags.NonPublic | BindingFlags.Instance); - - static public void ExecuteScript(this CohtmlControlledViewWrapper p_instance, string p_script) => ((cohtml.Net.View)ms_view.GetValue(p_instance)).ExecuteScript(p_script); - - static public void UpdateShapesLocal_Private(this CVRFaceTracking p_instance) => ms_updateShapesLocal?.Invoke(p_instance, ms_emptyArray); - - public static Matrix4x4 GetMatrix(this Transform p_transform, bool p_pos = true, bool p_rot = true, bool p_scl = false) - { - return Matrix4x4.TRS(p_pos ? p_transform.position : Vector3.zero, p_rot ? p_transform.rotation : Quaternion.identity, p_scl ? p_transform.localScale : Vector3.one); - } - } -} +using ABI.CCK.Components; +using ABI_RC.Core.Player; +using ABI_RC.Core.UI; +using ABI_RC.Systems.IK; +using System.Reflection; +using UnityEngine; + +namespace ml_dht +{ + static class Utils + { + static readonly object[] ms_emptyArray = new object[0]; + static readonly FieldInfo ms_view = typeof(CohtmlControlledViewWrapper).GetField("_view", BindingFlags.NonPublic | BindingFlags.Instance); + static readonly MethodInfo ms_updateShapesLocal = typeof(CVRFaceTracking).GetMethod("UpdateShapesLocal", BindingFlags.NonPublic | BindingFlags.Instance); + + public static void ExecuteScript(this CohtmlControlledViewWrapper p_instance, string p_script) => ((cohtml.Net.View)ms_view.GetValue(p_instance)).ExecuteScript(p_script); + + public static void UpdateShapesLocal_Private(this CVRFaceTracking p_instance) => ms_updateShapesLocal?.Invoke(p_instance, ms_emptyArray); + + public static void SetAvatarTPose() + { + IKSystem.Instance.SetAvatarPose(IKSystem.AvatarPose.TPose); + PlayerSetup.Instance._avatar.transform.localPosition = Vector3.zero; + PlayerSetup.Instance._avatar.transform.localRotation = Quaternion.identity; + } + } +} diff --git a/ml_dht/ml_dht.csproj b/ml_dht/ml_dht.csproj index 85d58d2..cc35d26 100644 --- a/ml_dht/ml_dht.csproj +++ b/ml_dht/ml_dht.csproj @@ -1,84 +1,84 @@ - - - - netstandard2.1 - DesktopHeadTracking - SDraw - None - DesktopHeadTracking - 1.2.1 - x64 - - - - x64 - none - false - - - - - - - - - - - - - - - - - - D:\games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\0Harmony.dll - false - false - - - D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Assembly-CSharp.dll - false - false - - - D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Assembly-CSharp-firstpass.dll - false - false - - - D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\cohtml.Net.dll - false - false - - - D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Cohtml.Runtime.dll - false - false - - - D:\games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\MelonLoader.dll - false - false - - - D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.dll - false - false - - - D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.AnimationModule.dll - false - false - - - D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.CoreModule.dll - false - false - - - - - - - - + + + + netstandard2.1 + DesktopHeadTracking + SDraw + None + DesktopHeadTracking + 1.2.1 + x64 + + + + x64 + embedded + true + + + + + + + + + + + + + + + + + + D:\games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\0Harmony.dll + false + false + + + D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Assembly-CSharp.dll + false + false + + + D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Assembly-CSharp-firstpass.dll + false + false + + + D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\cohtml.Net.dll + false + false + + + D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Cohtml.Runtime.dll + false + false + + + D:\games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\MelonLoader.dll + false + false + + + D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.dll + false + false + + + D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.AnimationModule.dll + false + false + + + D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.CoreModule.dll + false + false + + + + + + + + diff --git a/ml_lme/LeapHand.cs b/ml_lme/LeapHand.cs index 23f15b1..797f87b 100644 --- a/ml_lme/LeapHand.cs +++ b/ml_lme/LeapHand.cs @@ -4,7 +4,7 @@ namespace ml_lme { class LeapHand { - public enum FingerBone + enum FingerBone { ThumbMetacarpal = 0, ThumbProximal, @@ -25,9 +25,12 @@ namespace ml_lme PinkyMetacarpal, PinkyProximal, PinkyIntermediate, - PinkyDistal + PinkyDistal, + + Count }; + readonly bool m_left = false; readonly Transform m_root = null; readonly Transform m_wrist = null; readonly GameObject m_mesh = null; @@ -36,14 +39,15 @@ namespace ml_lme public LeapHand(Transform p_root, bool p_left) { - m_fingersBones = new Transform[20]; - m_initialRotations = new Quaternion[20]; + m_left = p_left; + m_fingersBones = new Transform[(int)FingerBone.Count]; + m_initialRotations = new Quaternion[(int)FingerBone.Count]; m_root = p_root; if(m_root != null) { - m_mesh = m_root.Find(p_left ? "GenericHandL" : "GenericHandR")?.gameObject; - m_wrist = m_root.Find(p_left ? "LeftHand/Wrist" : "RightHand/Wrist"); + m_mesh = m_root.Find(m_left ? "GenericHandL" : "GenericHandR")?.gameObject; + m_wrist = m_root.Find(m_left ? "LeftHand/Wrist" : "RightHand/Wrist"); if(m_wrist != null) { m_fingersBones[0] = null; // Actual thumb-meta, look at Leap Motion docs, dummy, it's zero point @@ -91,7 +95,7 @@ namespace ml_lme { if(m_fingersBones[i] != null) { - //m_fingers[i].position = p_data.m_fingerPosition[i]; + //m_fingersBones[i].position = p_data.m_fingerPosition[i]; m_fingersBones[i].rotation = p_data.m_fingerRotation[i]; } } @@ -101,12 +105,14 @@ namespace ml_lme } } - public void Reset() + public void Rebind(Quaternion p_base) { if(m_wrist != null) { m_wrist.localPosition = Vector3.zero; m_wrist.localRotation = Quaternion.identity; + + m_wrist.rotation = p_base * Quaternion.Euler(0f, m_left ? -90f : 90f, 0f); } for(int i = 0; i < 20; i++) @@ -117,8 +123,111 @@ namespace ml_lme } public Transform GetRoot() => m_root; - public Transform GetWrist() => m_wrist; - public Transform GetFingersBone(FingerBone p_bone) => m_fingersBones[(int)p_bone]; + public Transform GetBone(HumanBodyBones p_bone) + { + Transform l_result = null; + switch(p_bone) + { + case HumanBodyBones.LeftHand: + l_result = (m_left ? m_wrist : null); + break; + case HumanBodyBones.LeftThumbProximal: + l_result = (m_left ? m_fingersBones[(int)FingerBone.ThumbProximal] : null); + break; + case HumanBodyBones.LeftThumbIntermediate: + l_result = (m_left ? m_fingersBones[(int)FingerBone.ThumbIntermediate] : null); + break; + case HumanBodyBones.LeftThumbDistal: + l_result = (m_left ? m_fingersBones[(int)FingerBone.ThumbDistal] : null); + break; + case HumanBodyBones.LeftIndexProximal: + l_result = (m_left ? m_fingersBones[(int)FingerBone.IndexProximal] : null); + break; + case HumanBodyBones.LeftIndexIntermediate: + l_result = (m_left ? m_fingersBones[(int)FingerBone.IndexIntermediate] : null); + break; + case HumanBodyBones.LeftIndexDistal: + l_result = (m_left ? m_fingersBones[(int)FingerBone.IndexDistal] : null); + break; + case HumanBodyBones.LeftMiddleProximal: + l_result = (m_left ? m_fingersBones[(int)FingerBone.MiddleProximal] : null); + break; + case HumanBodyBones.LeftMiddleIntermediate: + l_result = (m_left ? m_fingersBones[(int)FingerBone.MiddleIntermediate] : null); + break; + case HumanBodyBones.LeftMiddleDistal: + l_result = (m_left ? m_fingersBones[(int)FingerBone.MiddleDistal] : null); + break; + case HumanBodyBones.LeftRingProximal: + l_result = (m_left ? m_fingersBones[(int)FingerBone.RingProximal] : null); + break; + case HumanBodyBones.LeftRingIntermediate: + l_result = (m_left ? m_fingersBones[(int)FingerBone.RingIntermediate] : null); + break; + case HumanBodyBones.LeftRingDistal: + l_result = (m_left ? m_fingersBones[(int)FingerBone.RingDistal] : null); + break; + case HumanBodyBones.LeftLittleProximal: + l_result = (m_left ? m_fingersBones[(int)FingerBone.PinkyProximal] : null); + break; + case HumanBodyBones.LeftLittleIntermediate: + l_result = (m_left ? m_fingersBones[(int)FingerBone.PinkyIntermediate] : null); + break; + case HumanBodyBones.LeftLittleDistal: + l_result = (m_left ? m_fingersBones[(int)FingerBone.PinkyDistal] : null); + break; + + case HumanBodyBones.RightHand: + l_result = (!m_left ? m_wrist : null); + break; + case HumanBodyBones.RightThumbProximal: + l_result = (!m_left ? m_fingersBones[(int)FingerBone.ThumbProximal] : null); + break; + case HumanBodyBones.RightThumbIntermediate: + l_result = (!m_left ? m_fingersBones[(int)FingerBone.ThumbIntermediate] : null); + break; + case HumanBodyBones.RightThumbDistal: + l_result = (!m_left ? m_fingersBones[(int)FingerBone.ThumbDistal] : null); + break; + case HumanBodyBones.RightIndexProximal: + l_result = (!m_left ? m_fingersBones[(int)FingerBone.IndexProximal] : null); + break; + case HumanBodyBones.RightIndexIntermediate: + l_result = (!m_left ? m_fingersBones[(int)FingerBone.IndexIntermediate] : null); + break; + case HumanBodyBones.RightIndexDistal: + l_result = (!m_left ? m_fingersBones[(int)FingerBone.IndexDistal] : null); + break; + case HumanBodyBones.RightMiddleProximal: + l_result = (!m_left ? m_fingersBones[(int)FingerBone.MiddleProximal] : null); + break; + case HumanBodyBones.RightMiddleIntermediate: + l_result = (!m_left ? m_fingersBones[(int)FingerBone.MiddleIntermediate] : null); + break; + case HumanBodyBones.RightMiddleDistal: + l_result = (!m_left ? m_fingersBones[(int)FingerBone.MiddleDistal] : null); + break; + case HumanBodyBones.RightRingProximal: + l_result = (!m_left ? m_fingersBones[(int)FingerBone.RingProximal] : null); + break; + case HumanBodyBones.RightRingIntermediate: + l_result = (!m_left ? m_fingersBones[(int)FingerBone.RingIntermediate] : null); + break; + case HumanBodyBones.RightRingDistal: + l_result = (!m_left ? m_fingersBones[(int)FingerBone.RingDistal] : null); + break; + case HumanBodyBones.RightLittleProximal: + l_result = (!m_left ? m_fingersBones[(int)FingerBone.PinkyProximal] : null); + break; + case HumanBodyBones.RightLittleIntermediate: + l_result = (!m_left ? m_fingersBones[(int)FingerBone.PinkyIntermediate] : null); + break; + case HumanBodyBones.RightLittleDistal: + l_result = (!m_left ? m_fingersBones[(int)FingerBone.PinkyDistal] : null); + break; + } + return l_result; + } public void SetMeshActive(bool p_state) { diff --git a/ml_lme/LeapInput.cs b/ml_lme/LeapInput.cs index 8a31b3b..a588d35 100644 --- a/ml_lme/LeapInput.cs +++ b/ml_lme/LeapInput.cs @@ -38,7 +38,7 @@ namespace ml_lme m_handRayLeft.hand = true; m_handRayLeft.generalMask = -269; m_handRayLeft.isInteractionRay = true; - m_handRayLeft.triggerGazeEvents = false; + //m_handRayLeft.triggerGazeEvents = false; m_handRayLeft.holderRoot = m_handRayLeft.gameObject; m_handRayLeft.attachmentDistance = 0f; m_handRayLeft.uiMask = 32; @@ -60,7 +60,7 @@ namespace ml_lme m_handRayRight.hand = false; m_handRayRight.generalMask = -269; m_handRayRight.isInteractionRay = true; - m_handRayRight.triggerGazeEvents = false; + //m_handRayRight.triggerGazeEvents = false; m_handRayRight.holderRoot = m_handRayRight.gameObject; m_handRayRight.attachmentDistance = 0f; m_handRayRight.uiMask = 32; @@ -538,29 +538,29 @@ namespace ml_lme { if(p_left) { - base._inputManager.finger1StretchedLeftThumb = -0.5f; - base._inputManager.finger2StretchedLeftThumb = 0.7f; - base._inputManager.finger3StretchedLeftThumb = 0.7f; + base._inputManager.finger1StretchedLeftThumb = 0f; + base._inputManager.finger2StretchedLeftThumb = 0f; + base._inputManager.finger3StretchedLeftThumb = 0f; base._inputManager.fingerSpreadLeftThumb = 0f; - base._inputManager.finger1StretchedLeftIndex = 0.5f; - base._inputManager.finger2StretchedLeftIndex = 0.7f; - base._inputManager.finger3StretchedLeftIndex = 0.7f; + base._inputManager.finger1StretchedLeftIndex = 0f; + base._inputManager.finger2StretchedLeftIndex =0f; + base._inputManager.finger3StretchedLeftIndex = 0f; base._inputManager.fingerSpreadLeftIndex = 0f; - base._inputManager.finger1StretchedLeftMiddle = 0.5f; - base._inputManager.finger2StretchedLeftMiddle = 0.7f; - base._inputManager.finger3StretchedLeftMiddle = 0.7f; + base._inputManager.finger1StretchedLeftMiddle = 0f; + base._inputManager.finger2StretchedLeftMiddle = 0f; + base._inputManager.finger3StretchedLeftMiddle = 0f; base._inputManager.fingerSpreadLeftMiddle = 0f; - base._inputManager.finger1StretchedLeftRing = 0.5f; - base._inputManager.finger2StretchedLeftRing = 0.7f; - base._inputManager.finger3StretchedLeftRing = 0.7f; + base._inputManager.finger1StretchedLeftRing = 0f; + base._inputManager.finger2StretchedLeftRing = 0f; + base._inputManager.finger3StretchedLeftRing = 0f; base._inputManager.fingerSpreadLeftRing = 0f; - base._inputManager.finger1StretchedLeftPinky = 0.5f; - base._inputManager.finger2StretchedLeftPinky = 0.7f; - base._inputManager.finger3StretchedLeftPinky = 0.7f; + base._inputManager.finger1StretchedLeftPinky = 0f; + base._inputManager.finger2StretchedLeftPinky = 0f; + base._inputManager.finger3StretchedLeftPinky = 0f; base._inputManager.fingerSpreadLeftPinky = 0f; base._inputManager.fingerFullCurlNormalizedLeftThumb = 0f; @@ -571,29 +571,29 @@ namespace ml_lme } else { - base._inputManager.finger1StretchedRightThumb = -0.5f; - base._inputManager.finger2StretchedRightThumb = 0.7f; - base._inputManager.finger3StretchedRightThumb = 0.7f; + base._inputManager.finger1StretchedRightThumb = 0f; + base._inputManager.finger2StretchedRightThumb = 0f; + base._inputManager.finger3StretchedRightThumb = 0f; base._inputManager.fingerSpreadRightThumb = 0f; - base._inputManager.finger1StretchedRightIndex = 0.5f; - base._inputManager.finger2StretchedRightIndex = 0.7f; - base._inputManager.finger3StretchedRightIndex = 0.7f; + base._inputManager.finger1StretchedRightIndex = 0f; + base._inputManager.finger2StretchedRightIndex = 0f; + base._inputManager.finger3StretchedRightIndex = 0f; base._inputManager.fingerSpreadRightIndex = 0f; - base._inputManager.finger1StretchedRightMiddle = 0.5f; - base._inputManager.finger2StretchedRightMiddle = 0.7f; - base._inputManager.finger3StretchedRightMiddle = 0.7f; + base._inputManager.finger1StretchedRightMiddle = 0f; + base._inputManager.finger2StretchedRightMiddle = 0f; + base._inputManager.finger3StretchedRightMiddle = 0f; base._inputManager.fingerSpreadRightMiddle = 0f; - base._inputManager.finger1StretchedRightRing = 0.5f; - base._inputManager.finger2StretchedRightRing = 0.7f; - base._inputManager.finger3StretchedRightRing = 0.7f; + base._inputManager.finger1StretchedRightRing = 0f; + base._inputManager.finger2StretchedRightRing = 0f; + base._inputManager.finger3StretchedRightRing = 0f; base._inputManager.fingerSpreadRightRing = 0f; - base._inputManager.finger1StretchedRightPinky = 0.5f; - base._inputManager.finger2StretchedRightPinky = 0.7f; - base._inputManager.finger3StretchedRightPinky = 0.7f; + base._inputManager.finger1StretchedRightPinky = 0f; + base._inputManager.finger2StretchedRightPinky = 0f; + base._inputManager.finger3StretchedRightPinky = 0f; base._inputManager.fingerSpreadRightPinky = 0f; base._inputManager.fingerFullCurlNormalizedRightThumb = 0f; diff --git a/ml_lme/LeapTracked.cs b/ml_lme/LeapTracked.cs index 9d73b23..b410e22 100644 --- a/ml_lme/LeapTracked.cs +++ b/ml_lme/LeapTracked.cs @@ -1,7 +1,6 @@ using ABI_RC.Core.Player; using ABI_RC.Systems.IK; using RootMotion.FinalIK; -using System.Collections; using System.Collections.Generic; using UnityEngine; @@ -11,6 +10,12 @@ namespace ml_lme [DefaultExecutionOrder(999999)] class LeapTracked : MonoBehaviour { + enum PlaneType + { + OXZ, + OYX + } + struct IKInfo { public Vector4 m_armsWeights; @@ -21,50 +26,69 @@ namespace ml_lme public Transform m_rightElbowTarget; } - struct FingerBoneInfo + struct RotationOffset { - public LeapHand.FingerBone m_bone; - public Transform m_targetBone; - public Transform m_sourceBone; + public Transform m_target; + public Transform m_source; public Quaternion m_offset; + + public void Reset() + { + m_source = null; + m_target = null; + m_offset = Quaternion.identity; + } } static readonly Quaternion ms_offsetLeft = Quaternion.Euler(0f, 90f, 0f); static readonly Quaternion ms_offsetRight = Quaternion.Euler(0f, 270f, 0f); - static readonly (HumanBodyBones, LeapHand.FingerBone, bool)[] ms_fingerBonesLinks = + static readonly (HumanBodyBones, bool)[] ms_fingers = { - (HumanBodyBones.LeftThumbProximal, LeapHand.FingerBone.ThumbProximal, true), - (HumanBodyBones.LeftThumbIntermediate, LeapHand.FingerBone.ThumbIntermediate, true), - (HumanBodyBones.LeftThumbDistal, LeapHand.FingerBone.ThumbDistal, true), - (HumanBodyBones.LeftIndexProximal, LeapHand.FingerBone.IndexProximal, true), - (HumanBodyBones.LeftIndexIntermediate, LeapHand.FingerBone.IndexIntermediate, true), - (HumanBodyBones.LeftIndexDistal, LeapHand.FingerBone.IndexDistal, true), - (HumanBodyBones.LeftMiddleProximal, LeapHand.FingerBone.MiddleProximal, true), - (HumanBodyBones.LeftMiddleIntermediate, LeapHand.FingerBone.MiddleIntermediate, true), - (HumanBodyBones.LeftMiddleDistal, LeapHand.FingerBone.MiddleDistal, true), - (HumanBodyBones.LeftRingProximal, LeapHand.FingerBone.RingProximal, true), - (HumanBodyBones.LeftRingIntermediate, LeapHand.FingerBone.RingIntermediate, true), - (HumanBodyBones.LeftRingDistal, LeapHand.FingerBone.RingDistal, true), - (HumanBodyBones.LeftLittleProximal, LeapHand.FingerBone.PinkyProximal, true), - (HumanBodyBones.LeftLittleIntermediate, LeapHand.FingerBone.PinkyIntermediate, true), - (HumanBodyBones.LeftLittleDistal, LeapHand.FingerBone.PinkyDistal, true), + (HumanBodyBones.LeftThumbProximal, true), + (HumanBodyBones.LeftThumbIntermediate, true), + (HumanBodyBones.LeftThumbDistal, true), + (HumanBodyBones.LeftIndexProximal, true), + (HumanBodyBones.LeftIndexIntermediate, true), + (HumanBodyBones.LeftIndexDistal, true), + (HumanBodyBones.LeftMiddleProximal, true), + (HumanBodyBones.LeftMiddleIntermediate, true), + (HumanBodyBones.LeftMiddleDistal, true), + (HumanBodyBones.LeftRingProximal, true), + (HumanBodyBones.LeftRingIntermediate, true), + (HumanBodyBones.LeftRingDistal, true), + (HumanBodyBones.LeftLittleProximal, true), + (HumanBodyBones.LeftLittleIntermediate, true), + (HumanBodyBones.LeftLittleDistal, true), - (HumanBodyBones.RightThumbProximal, LeapHand.FingerBone.ThumbProximal, false), - (HumanBodyBones.RightThumbIntermediate, LeapHand.FingerBone.ThumbIntermediate, false), - (HumanBodyBones.RightThumbDistal, LeapHand.FingerBone.ThumbDistal, false), - (HumanBodyBones.RightIndexProximal, LeapHand.FingerBone.IndexProximal, false), - (HumanBodyBones.RightIndexIntermediate, LeapHand.FingerBone.IndexIntermediate, false), - (HumanBodyBones.RightIndexDistal, LeapHand.FingerBone.IndexDistal, false), - (HumanBodyBones.RightMiddleProximal, LeapHand.FingerBone.MiddleProximal, false), - (HumanBodyBones.RightMiddleIntermediate, LeapHand.FingerBone.MiddleIntermediate, false), - (HumanBodyBones.RightMiddleDistal, LeapHand.FingerBone.MiddleDistal, false), - (HumanBodyBones.RightRingProximal, LeapHand.FingerBone.RingProximal, false), - (HumanBodyBones.RightRingIntermediate, LeapHand.FingerBone.RingIntermediate, false), - (HumanBodyBones.RightRingDistal, LeapHand.FingerBone.RingDistal, false), - (HumanBodyBones.RightLittleProximal, LeapHand.FingerBone.PinkyProximal, false), - (HumanBodyBones.RightLittleIntermediate, LeapHand.FingerBone.PinkyIntermediate, false), - (HumanBodyBones.RightLittleDistal, LeapHand.FingerBone.PinkyDistal, false), + (HumanBodyBones.RightThumbProximal, false), + (HumanBodyBones.RightThumbIntermediate, false), + (HumanBodyBones.RightThumbDistal, false), + (HumanBodyBones.RightIndexProximal, false), + (HumanBodyBones.RightIndexIntermediate, false), + (HumanBodyBones.RightIndexDistal, false), + (HumanBodyBones.RightMiddleProximal, false), + (HumanBodyBones.RightMiddleIntermediate, false), + (HumanBodyBones.RightMiddleDistal, false), + (HumanBodyBones.RightRingProximal, false), + (HumanBodyBones.RightRingIntermediate, false), + (HumanBodyBones.RightRingDistal, false), + (HumanBodyBones.RightLittleProximal, false), + (HumanBodyBones.RightLittleIntermediate, false), + (HumanBodyBones.RightLittleDistal, false), + }; + static readonly (HumanBodyBones, HumanBodyBones, bool)[] ms_rotationFixChains = + { + (HumanBodyBones.LeftThumbProximal,HumanBodyBones.LeftThumbIntermediate,true), (HumanBodyBones.LeftThumbIntermediate, HumanBodyBones.LeftThumbDistal,true), + (HumanBodyBones.LeftIndexProximal,HumanBodyBones.LeftIndexIntermediate,true), (HumanBodyBones.LeftIndexIntermediate, HumanBodyBones.LeftIndexDistal,true), + (HumanBodyBones.LeftMiddleProximal,HumanBodyBones.LeftMiddleIntermediate,true), (HumanBodyBones.LeftMiddleIntermediate, HumanBodyBones.LeftMiddleDistal,true), + (HumanBodyBones.LeftRingProximal,HumanBodyBones.LeftRingIntermediate,true), (HumanBodyBones.LeftRingIntermediate, HumanBodyBones.LeftRingDistal,true), + (HumanBodyBones.LeftLittleProximal,HumanBodyBones.LeftLittleIntermediate,true), (HumanBodyBones.LeftLittleIntermediate, HumanBodyBones.LeftLittleDistal,true), + (HumanBodyBones.RightThumbProximal,HumanBodyBones.RightThumbIntermediate,false), (HumanBodyBones.RightThumbIntermediate, HumanBodyBones.RightThumbDistal,false), + (HumanBodyBones.RightIndexProximal,HumanBodyBones.RightIndexIntermediate,false), (HumanBodyBones.RightIndexIntermediate, HumanBodyBones.RightIndexDistal,false), + (HumanBodyBones.RightMiddleProximal,HumanBodyBones.RightMiddleIntermediate,false), (HumanBodyBones.RightMiddleIntermediate, HumanBodyBones.RightMiddleDistal,false), + (HumanBodyBones.RightRingProximal,HumanBodyBones.RightRingIntermediate,false), (HumanBodyBones.RightRingIntermediate, HumanBodyBones.RightRingDistal,false), + (HumanBodyBones.RightLittleProximal,HumanBodyBones.RightLittleIntermediate,false), (HumanBodyBones.RightLittleIntermediate, HumanBodyBones.RightLittleDistal,false) }; public static readonly float[] ms_lastLeftFingerBones = new float[20]; @@ -72,13 +96,12 @@ namespace ml_lme bool m_inVR = false; VRIK m_vrIK = null; + Transform m_hips = null; bool m_enabled = true; bool m_fingersOnly = false; bool m_trackElbows = true; - Transform m_leftHand = null; - Transform m_rightHand = null; IKInfo m_vrIKInfo; ArmIK m_leftArmIK = null; ArmIK m_rightArmIK = null; @@ -89,16 +112,15 @@ namespace ml_lme bool m_leftTargetActive = false; // VRIK only bool m_rightTargetActive = false; // VRIK only - readonly List m_leftFingerBones = null; - readonly List m_rightFingerBones = null; - - Quaternion m_leftWristOffset; - Quaternion m_rightWristOffset; + RotationOffset m_leftHandOffset; // From avatar hand to Leap wrist + RotationOffset m_rightHandOffset; + readonly List m_leftFingerOffsets = null; // From Leap finger bone to avatar finger bone + readonly List m_rightFingerOffsets = null; internal LeapTracked() { - m_leftFingerBones = new List(); - m_rightFingerBones = new List(); + m_leftFingerOffsets = new List(); + m_rightFingerOffsets = new List(); } // Unity events @@ -181,42 +203,44 @@ namespace ml_lme LeapParser.LeapData l_data = LeapManager.Instance.GetLatestData(); if(l_data.m_leftHand.m_present) { - Transform l_leapWrist = LeapTracking.Instance.GetLeftHand().GetWrist(); - Quaternion l_turnBack = (m_leftHand.rotation * m_leftWristOffset) * Quaternion.Inverse(l_leapWrist.rotation); - foreach(var l_info in m_leftFingerBones) - l_info.m_targetBone.rotation = l_turnBack * (l_info.m_sourceBone.rotation * l_info.m_offset); + Quaternion l_turnBack = (m_leftHandOffset.m_source.rotation * m_leftHandOffset.m_offset) * Quaternion.Inverse(m_leftHandOffset.m_target.rotation); + foreach(var l_info in m_leftFingerOffsets) + l_info.m_target.rotation = l_turnBack * (l_info.m_source.rotation * l_info.m_offset); } if(l_data.m_rightHand.m_present) { - Transform l_leapWrist = LeapTracking.Instance.GetRightHand().GetWrist(); - Quaternion l_turnBack = (m_rightHand.rotation * m_rightWristOffset) * Quaternion.Inverse(l_leapWrist.rotation); - foreach(var l_info in m_rightFingerBones) - l_info.m_targetBone.rotation = l_turnBack * (l_info.m_sourceBone.rotation * l_info.m_offset); + Quaternion l_turnBack = (m_rightHandOffset.m_source.rotation * m_rightHandOffset.m_offset) * Quaternion.Inverse(m_rightHandOffset.m_target.rotation); + foreach(var l_info in m_rightFingerOffsets) + l_info.m_target.rotation = l_turnBack * (l_info.m_source.rotation * l_info.m_offset); } m_poseHandler.GetHumanPose(ref m_pose); - if(l_data.m_leftHand.m_present) + if(l_data.m_leftHand.m_present || l_data.m_rightHand.m_present) { for(int i = 0; i < 5; i++) { int l_offset = i * 4; + ms_lastLeftFingerBones[l_offset] = m_pose.muscles[(int)MuscleIndex.LeftThumb1Stretched + l_offset]; ms_lastLeftFingerBones[l_offset + 1] = m_pose.muscles[(int)MuscleIndex.LeftThumb2Stretched + l_offset]; ms_lastLeftFingerBones[l_offset + 2] = m_pose.muscles[(int)MuscleIndex.LeftThumb3Stretched + l_offset]; ms_lastLeftFingerBones[l_offset + 3] = m_pose.muscles[(int)MuscleIndex.LeftThumbSpread + l_offset]; - } - } - if(l_data.m_rightHand.m_present) - { - for(int i = 0; i < 5; i++) - { - int l_offset = i * 4; + ms_lastRightFingerBones[l_offset] = m_pose.muscles[(int)MuscleIndex.RightThumb1Stretched + l_offset]; ms_lastRightFingerBones[l_offset + 1] = m_pose.muscles[(int)MuscleIndex.RightThumb2Stretched + l_offset]; ms_lastRightFingerBones[l_offset + 2] = m_pose.muscles[(int)MuscleIndex.RightThumb3Stretched + l_offset]; ms_lastRightFingerBones[l_offset + 3] = m_pose.muscles[(int)MuscleIndex.RightThumbSpread + l_offset]; } } + + if(Settings.MechanimFilter && (m_hips != null)) + { + // Yoinked from IKSystem.OnPostSolverUpdateGeneral + Vector3 l_pos = m_hips.position; + Quaternion l_rot = m_hips.rotation; + m_poseHandler.SetHumanPose(ref m_pose); + m_hips.SetPositionAndRotation(l_pos, l_rot); + } } } @@ -224,6 +248,7 @@ namespace ml_lme internal void OnAvatarClear() { m_vrIK = null; + m_hips = null; m_leftArmIK = null; m_rightArmIK = null; m_leftTargetActive = false; @@ -237,41 +262,35 @@ namespace ml_lme m_rightHandTarget.localPosition = Vector3.zero; m_rightHandTarget.localRotation = Quaternion.identity; - m_leftFingerBones.Clear(); - m_rightFingerBones.Clear(); + m_leftHandOffset.Reset(); + m_rightHandOffset.Reset(); - m_leftHand = null; - m_rightHand = null; - m_leftWristOffset = Quaternion.identity; - m_rightWristOffset = Quaternion.identity; + m_leftFingerOffsets.Clear(); + m_rightFingerOffsets.Clear(); } internal void OnAvatarSetup() { m_inVR = Utils.IsInVR(); - m_vrIK = PlayerSetup.Instance._animator.GetComponent(); if(PlayerSetup.Instance._animator.isHuman) { + Utils.SetAvatarTPose(); + m_poseHandler = new HumanPoseHandler(PlayerSetup.Instance._animator.avatar, PlayerSetup.Instance._animator.transform); m_poseHandler.GetHumanPose(ref m_pose); - if(m_inVR) - { - PlayerSetup.Instance._avatar.transform.localPosition = Vector3.zero; - PlayerSetup.Instance._avatar.transform.localRotation = Quaternion.identity; - } - else - PoseHelper.ForceTPose(PlayerSetup.Instance._animator); + m_hips = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.Hips); - m_leftHand = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.LeftHand); - m_leftHandTarget.localRotation = ms_offsetLeft * (Quaternion.Inverse(PlayerSetup.Instance._avatar.transform.rotation) * m_leftHand.rotation); + m_leftHandOffset.m_source = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.LeftHand); + m_leftHandTarget.localRotation = ms_offsetLeft * (Quaternion.Inverse(PlayerSetup.Instance._avatar.transform.rotation) * m_leftHandOffset.m_source.rotation); - m_rightHand = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.RightHand); - m_rightHandTarget.localRotation = ms_offsetRight * (Quaternion.Inverse(PlayerSetup.Instance._avatar.transform.rotation) * m_rightHand.rotation); + m_rightHandOffset.m_source = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.RightHand); + m_rightHandTarget.localRotation = ms_offsetRight * (Quaternion.Inverse(PlayerSetup.Instance._avatar.transform.rotation) * m_rightHandOffset.m_source.rotation); ParseFingersBones(); + m_vrIK = PlayerSetup.Instance._animator.GetComponent(); if(m_vrIK != null) { m_vrIK.onPreSolverUpdate.AddListener(this.OnIKPreUpdate); @@ -298,7 +317,7 @@ namespace ml_lme } else { - PoseHelper.ForceTPose(PlayerSetup.Instance._animator); + Utils.SetAvatarTPose(); SetupArmIK(); } } @@ -453,31 +472,96 @@ namespace ml_lme void ParseFingersBones() { - LeapTracking.Instance.GetLeftHand().Reset(); - LeapTracking.Instance.GetLeftHand().GetWrist().rotation = PlayerSetup.Instance.transform.rotation * ms_offsetRight; // Weird, but that's how it works - m_leftWristOffset = Quaternion.Inverse(m_leftHand.rotation) * LeapTracking.Instance.GetLeftHand().GetWrist().rotation; + LeapTracking.Instance.Rebind(PlayerSetup.Instance.transform.rotation); - LeapTracking.Instance.GetRightHand().Reset(); - LeapTracking.Instance.GetRightHand().GetWrist().rotation = PlayerSetup.Instance.transform.rotation * ms_offsetLeft; // Weird, but that's how it works - m_rightWristOffset = Quaternion.Inverse(m_rightHand.rotation) * LeapTracking.Instance.GetRightHand().GetWrist().rotation; + // Try to "fix" rotations, slightly inaccurate after 0YX plane rotation + foreach(var l_tuple in ms_rotationFixChains) + { + ReorientateTowards( + PlayerSetup.Instance._animator.GetBoneTransform(l_tuple.Item1), + PlayerSetup.Instance._animator.GetBoneTransform(l_tuple.Item2), + l_tuple.Item3 ? LeapTracking.Instance.GetLeftHand().GetBone(l_tuple.Item1) : LeapTracking.Instance.GetRightHand().GetBone(l_tuple.Item1), + l_tuple.Item3 ? LeapTracking.Instance.GetLeftHand().GetBone(l_tuple.Item2) : LeapTracking.Instance.GetRightHand().GetBone(l_tuple.Item2), + PlaneType.OXZ + ); + ReorientateTowards( + PlayerSetup.Instance._animator.GetBoneTransform(l_tuple.Item1), + PlayerSetup.Instance._animator.GetBoneTransform(l_tuple.Item2), + l_tuple.Item3 ? LeapTracking.Instance.GetLeftHand().GetBone(l_tuple.Item1) : LeapTracking.Instance.GetRightHand().GetBone(l_tuple.Item1), + l_tuple.Item3 ? LeapTracking.Instance.GetLeftHand().GetBone(l_tuple.Item2) : LeapTracking.Instance.GetRightHand().GetBone(l_tuple.Item2), + PlaneType.OYX + ); + } - foreach(var l_link in ms_fingerBonesLinks) + // Bind + m_leftHandOffset.m_target = LeapTracking.Instance.GetLeftHand().GetBone(HumanBodyBones.LeftHand); + if((m_leftHandOffset.m_source != null) && (m_leftHandOffset.m_target != null)) + m_leftHandOffset.m_offset = Quaternion.Inverse(m_leftHandOffset.m_source.rotation) * m_leftHandOffset.m_target.rotation; + + m_rightHandOffset.m_target = LeapTracking.Instance.GetRightHand().GetBone(HumanBodyBones.RightHand); + if((m_rightHandOffset.m_source != null) && (m_rightHandOffset.m_target != null)) + m_rightHandOffset.m_offset = Quaternion.Inverse(m_rightHandOffset.m_source.rotation) * m_rightHandOffset.m_target.rotation; + + foreach(var l_link in ms_fingers) { Transform l_transform = PlayerSetup.Instance._animator.GetBoneTransform(l_link.Item1); if(l_transform != null) { - FingerBoneInfo l_info = new FingerBoneInfo(); - l_info.m_bone = l_link.Item2; - l_info.m_targetBone = l_transform; - l_info.m_sourceBone = (l_link.Item3 ? LeapTracking.Instance.GetLeftHand().GetFingersBone(l_link.Item2) : LeapTracking.Instance.GetRightHand().GetFingersBone(l_link.Item2)); - l_info.m_offset = Quaternion.Inverse(l_info.m_sourceBone.rotation) * l_info.m_targetBone.rotation; + RotationOffset l_offset = new RotationOffset(); + l_offset.m_target = l_transform; + l_offset.m_source = (l_link.Item2 ? LeapTracking.Instance.GetLeftHand().GetBone(l_link.Item1) : LeapTracking.Instance.GetRightHand().GetBone(l_link.Item1)); + l_offset.m_offset = Quaternion.Inverse(l_offset.m_source.rotation) * l_offset.m_target.rotation; - if(l_link.Item3) - m_leftFingerBones.Add(l_info); + if(l_link.Item2) + m_leftFingerOffsets.Add(l_offset); else - m_rightFingerBones.Add(l_info); + m_rightFingerOffsets.Add(l_offset); } } } + + void ReorientateTowards(Transform p_target, Transform p_targetEnd, Transform p_source, Transform p_sourceEnd, PlaneType p_plane) + { + if((p_target != null) && (p_targetEnd != null) && (p_source != null) && (p_sourceEnd != null)) + { + Quaternion l_playerInv = Quaternion.Inverse(PlayerSetup.Instance.transform.rotation); + Vector3 l_targetDir = l_playerInv * (p_targetEnd.position - p_target.position); + Vector3 l_sourceDir = l_playerInv * (p_sourceEnd.position - p_source.position); + switch(p_plane) + { + case PlaneType.OXZ: + l_targetDir.y = 0f; + l_sourceDir.y = 0f; + break; + case PlaneType.OYX: + l_targetDir.z = 0f; + l_sourceDir.z = 0f; + break; + } + l_targetDir = Vector3.Normalize(l_targetDir); + l_sourceDir = Vector3.Normalize(l_sourceDir); + + Quaternion l_targetRot = Quaternion.identity; + Quaternion l_sourceRot = Quaternion.identity; + switch(p_plane) + { + case PlaneType.OXZ: + l_targetRot = Quaternion.LookRotation(l_targetDir, Vector3.up); + l_sourceRot = Quaternion.LookRotation(l_sourceDir, Vector3.up); + break; + case PlaneType.OYX: + l_targetRot = Quaternion.LookRotation(l_targetDir, Vector3.forward); + l_sourceRot = Quaternion.LookRotation(l_sourceDir, Vector3.forward); + break; + } + + Quaternion l_diff = Quaternion.Inverse(l_targetRot) * l_sourceRot; + if(p_plane == PlaneType.OYX) + l_diff = Quaternion.Euler(0f, 0f, l_diff.eulerAngles.y); + + Quaternion l_adjusted = l_diff * (l_playerInv * p_target.rotation); + p_target.rotation = PlayerSetup.Instance.transform.rotation * l_adjusted; + } + } } } diff --git a/ml_lme/LeapTracking.cs b/ml_lme/LeapTracking.cs index 535805d..8704bef 100644 --- a/ml_lme/LeapTracking.cs +++ b/ml_lme/LeapTracking.cs @@ -18,8 +18,8 @@ namespace ml_lme GameObject m_leapHands = null; LeapHand m_leapHandLeft = null; LeapHand m_leapHandRight = null; - GameObject m_leapElbowLeft = null; - GameObject m_leapElbowRight = null; + Transform m_leapElbowLeft = null; + Transform m_leapElbowRight = null; GameObject m_leapControllerModel = null; float m_scaleRelation = 1f; @@ -31,15 +31,15 @@ namespace ml_lme m_inVR = Utils.IsInVR(); - m_leapElbowLeft = new GameObject("LeapElbowLeft"); - m_leapElbowLeft.transform.parent = this.transform; - m_leapElbowLeft.transform.localPosition = Vector3.zero; - m_leapElbowLeft.transform.localRotation = Quaternion.identity; + m_leapElbowLeft = new GameObject("LeapElbowLeft").transform; + m_leapElbowLeft.parent = this.transform; + m_leapElbowLeft.localPosition = Vector3.zero; + m_leapElbowLeft.localRotation = Quaternion.identity; - m_leapElbowRight = new GameObject("LeapElbowRight"); - m_leapElbowRight.transform.parent = this.transform; - m_leapElbowRight.transform.localPosition = Vector3.zero; - m_leapElbowRight.transform.localRotation = Quaternion.identity; + m_leapElbowRight = new GameObject("LeapElbowRight").transform; + m_leapElbowRight.parent = this.transform; + m_leapElbowRight.localPosition = Vector3.zero; + m_leapElbowRight.localRotation = Quaternion.identity; m_leapControllerModel = AssetsHandler.GetAsset("assets/models/leapmotion/leap_motion_1_0.obj"); if(m_leapControllerModel != null) @@ -101,11 +101,11 @@ namespace ml_lme m_leapHandRight = null; if(m_leapElbowLeft != null) - Object.Destroy(m_leapElbowLeft); + Object.Destroy(m_leapElbowLeft.gameObject); m_leapElbowLeft = null; if(m_leapElbowRight != null) - Object.Destroy(m_leapElbowRight); + Object.Destroy(m_leapElbowRight.gameObject); m_leapElbowRight = null; if(m_leapControllerModel != null) @@ -140,7 +140,7 @@ namespace ml_lme m_leapHandLeft.GetRoot().localRotation = l_data.m_leftHand.m_rotation; OrientationAdjustment(ref l_data.m_leftHand.m_elbowPosition, ref ms_dummyRotation, Settings.TrackingMode); - m_leapElbowLeft.transform.localPosition = l_data.m_leftHand.m_elbowPosition; + m_leapElbowLeft.localPosition = l_data.m_leftHand.m_elbowPosition; m_leapHandLeft?.Update(l_data.m_leftHand); } @@ -155,7 +155,7 @@ namespace ml_lme m_leapHandRight.GetRoot().localRotation = l_data.m_rightHand.m_rotation; OrientationAdjustment(ref l_data.m_rightHand.m_elbowPosition, ref ms_dummyRotation, Settings.TrackingMode); - m_leapElbowRight.transform.localPosition = l_data.m_rightHand.m_elbowPosition; + m_leapElbowRight.localPosition = l_data.m_rightHand.m_elbowPosition; m_leapHandRight?.Update(l_data.m_rightHand); } @@ -164,8 +164,13 @@ namespace ml_lme public LeapHand GetLeftHand() => m_leapHandLeft; public LeapHand GetRightHand() => m_leapHandRight; - public Transform GetLeftElbow() => m_leapElbowLeft.transform; - public Transform GetRightElbow() => m_leapElbowRight.transform; + public Transform GetLeftElbow() => m_leapElbowLeft; + public Transform GetRightElbow() => m_leapElbowRight; + public void Rebind(Quaternion p_base) + { + m_leapHandLeft?.Rebind(p_base); + m_leapHandRight?.Rebind(p_base); + } // Settings void OnDesktopOffsetChange(Vector3 p_offset) diff --git a/ml_lme/Main.cs b/ml_lme/Main.cs index 5ee3cc0..2210141 100644 --- a/ml_lme/Main.cs +++ b/ml_lme/Main.cs @@ -1,166 +1,166 @@ -using ABI.CCK.Components; -using ABI_RC.Core.Player; -using ABI_RC.Systems.IK; -using System.Collections; -using System.Reflection; -using UnityEngine; - -namespace ml_lme -{ - - public class LeapMotionExtension : MelonLoader.MelonMod - { - static LeapMotionExtension ms_instance = null; - - LeapManager m_leapManager = null; - - public override void OnInitializeMelon() - { - if(ms_instance == null) - ms_instance = this; - - DependenciesHandler.ExtractDependencies(); - Settings.Init(); - AssetsHandler.Load(); - - // Patches - HarmonyInstance.Patch( - typeof(PlayerSetup).GetMethod(nameof(PlayerSetup.ClearAvatar)), - null, - new HarmonyLib.HarmonyMethod(typeof(LeapMotionExtension).GetMethod(nameof(OnAvatarClear_Postfix), BindingFlags.Static | BindingFlags.NonPublic)) - ); - HarmonyInstance.Patch( - typeof(PlayerSetup).GetMethod(nameof(PlayerSetup.SetupAvatar)), - null, - new HarmonyLib.HarmonyMethod(typeof(LeapMotionExtension).GetMethod(nameof(OnSetupAvatar_Postfix), BindingFlags.Static | BindingFlags.NonPublic)) - ); - HarmonyInstance.Patch( - typeof(IKSystem).GetMethod(nameof(IKSystem.ReinitializeAvatar), BindingFlags.Instance | BindingFlags.Public), - null, - new HarmonyLib.HarmonyMethod(typeof(LeapMotionExtension).GetMethod(nameof(OnAvatarReinitialize_Postfix), BindingFlags.Static | BindingFlags.NonPublic)) - ); - HarmonyInstance.Patch( - typeof(PlayerSetup).GetMethod(nameof(PlayerSetup.SetControllerRayScale)), - null, - new HarmonyLib.HarmonyMethod(typeof(LeapMotionExtension).GetMethod(nameof(OnRayScale_Postfix), BindingFlags.Static | BindingFlags.NonPublic)) - ); - HarmonyInstance.Patch( - typeof(PlayerSetup).GetMethod("SetPlaySpaceScale", BindingFlags.NonPublic | BindingFlags.Instance), - null, - new HarmonyLib.HarmonyMethod(typeof(LeapMotionExtension).GetMethod(nameof(OnPlayspaceScale_Postfix), BindingFlags.Static | BindingFlags.NonPublic)) - ); - HarmonyInstance.Patch( - - typeof(CVRPickupObject).GetMethod(nameof(CVRPickupObject.Grab), BindingFlags.Public | BindingFlags.Instance), - null, - new HarmonyLib.HarmonyMethod(typeof(LeapMotionExtension).GetMethod(nameof(OnPickupGrab_Postfix), BindingFlags.Static | BindingFlags.NonPublic)) - ); - - ModSupporter.Init(); - MelonLoader.MelonCoroutines.Start(WaitForRootLogic()); - } - - public override void OnDeinitializeMelon() - { - if(ms_instance == this) - ms_instance = null; - - if(m_leapManager != null) - Object.Destroy(m_leapManager); - m_leapManager = null; - } - - IEnumerator WaitForRootLogic() - { - while(ABI_RC.Core.RootLogic.Instance == null) - yield return null; - - m_leapManager = new GameObject("LeapMotionManager").AddComponent(); - } - - // Patches - static void OnAvatarClear_Postfix() => ms_instance?.OnAvatarClear(); - void OnAvatarClear() - { - try - { - if(m_leapManager != null) - m_leapManager.OnAvatarClear(); - } - catch(System.Exception e) - { - MelonLoader.MelonLogger.Error(e); - } - } - - static void OnSetupAvatar_Postfix() => ms_instance?.OnSetupAvatar(); - void OnSetupAvatar() - { - try - { - if(m_leapManager != null) - m_leapManager.OnAvatarSetup(); - } - catch(System.Exception e) - { - MelonLoader.MelonLogger.Error(e); - } - } - - static void OnAvatarReinitialize_Postfix() => ms_instance?.OnAvatarReinitialize(); - void OnAvatarReinitialize() - { - try - { - if(m_leapManager != null) - m_leapManager.OnAvatarReinitialize(); - } - catch(System.Exception e) - { - MelonLoader.MelonLogger.Error(e); - } - } - - static void OnRayScale_Postfix(float __0) => ms_instance?.OnRayScale(__0); - void OnRayScale(float p_scale) - { - try - { - if(m_leapManager != null) - m_leapManager.OnRayScale(p_scale); - } - catch(System.Exception e) - { - MelonLoader.MelonLogger.Error(e); - } - } - - static void OnPlayspaceScale_Postfix(float ____avatarScaleRelation) => ms_instance?.OnPlayspaceScale(____avatarScaleRelation); - void OnPlayspaceScale(float p_relation) - { - try - { - if(m_leapManager != null) - m_leapManager.OnPlayspaceScale(p_relation); - } - catch(System.Exception e) - { - MelonLoader.MelonLogger.Error(e); - } - } - - static void OnPickupGrab_Postfix(ref CVRPickupObject __instance) => ms_instance?.OnPickupGrab(__instance); - void OnPickupGrab(CVRPickupObject p_pickup) - { - try - { - if(m_leapManager != null) - m_leapManager.OnPickupGrab(p_pickup); - } - catch(System.Exception e) - { - MelonLoader.MelonLogger.Error(e); - } - } - } -} +using ABI.CCK.Components; +using ABI_RC.Core.Player; +using ABI_RC.Systems.IK; +using System.Collections; +using System.Reflection; +using UnityEngine; + +namespace ml_lme +{ + + public class LeapMotionExtension : MelonLoader.MelonMod + { + static LeapMotionExtension ms_instance = null; + + LeapManager m_leapManager = null; + + public override void OnInitializeMelon() + { + if(ms_instance == null) + ms_instance = this; + + DependenciesHandler.ExtractDependencies(); + Settings.Init(); + AssetsHandler.Load(); + + // Patches + HarmonyInstance.Patch( + typeof(PlayerSetup).GetMethod(nameof(PlayerSetup.ClearAvatar)), + null, + new HarmonyLib.HarmonyMethod(typeof(LeapMotionExtension).GetMethod(nameof(OnAvatarClear_Postfix), BindingFlags.Static | BindingFlags.NonPublic)) + ); + HarmonyInstance.Patch( + typeof(PlayerSetup).GetMethod(nameof(PlayerSetup.SetupAvatar)), + null, + new HarmonyLib.HarmonyMethod(typeof(LeapMotionExtension).GetMethod(nameof(OnSetupAvatar_Postfix), BindingFlags.Static | BindingFlags.NonPublic)) + ); + HarmonyInstance.Patch( + typeof(IKSystem).GetMethod(nameof(IKSystem.ReinitializeAvatar), BindingFlags.Instance | BindingFlags.Public), + null, + new HarmonyLib.HarmonyMethod(typeof(LeapMotionExtension).GetMethod(nameof(OnAvatarReinitialize_Postfix), BindingFlags.Static | BindingFlags.NonPublic)) + ); + HarmonyInstance.Patch( + typeof(PlayerSetup).GetMethod(nameof(PlayerSetup.SetControllerRayScale)), + null, + new HarmonyLib.HarmonyMethod(typeof(LeapMotionExtension).GetMethod(nameof(OnRayScale_Postfix), BindingFlags.Static | BindingFlags.NonPublic)) + ); + HarmonyInstance.Patch( + typeof(PlayerSetup).GetMethod("SetPlaySpaceScale", BindingFlags.NonPublic | BindingFlags.Instance), + null, + new HarmonyLib.HarmonyMethod(typeof(LeapMotionExtension).GetMethod(nameof(OnPlayspaceScale_Postfix), BindingFlags.Static | BindingFlags.NonPublic)) + ); + HarmonyInstance.Patch( + + typeof(CVRPickupObject).GetMethod(nameof(CVRPickupObject.Grab), BindingFlags.Public | BindingFlags.Instance), + null, + new HarmonyLib.HarmonyMethod(typeof(LeapMotionExtension).GetMethod(nameof(OnPickupGrab_Postfix), BindingFlags.Static | BindingFlags.NonPublic)) + ); + + ModSupporter.Init(); + MelonLoader.MelonCoroutines.Start(WaitForRootLogic()); + } + + public override void OnDeinitializeMelon() + { + if(ms_instance == this) + ms_instance = null; + + if(m_leapManager != null) + Object.Destroy(m_leapManager); + m_leapManager = null; + } + + IEnumerator WaitForRootLogic() + { + while(ABI_RC.Core.RootLogic.Instance == null) + yield return null; + + m_leapManager = new GameObject("LeapMotionManager").AddComponent(); + } + + // Patches + static void OnAvatarClear_Postfix() => ms_instance?.OnAvatarClear(); + void OnAvatarClear() + { + try + { + if(m_leapManager != null) + m_leapManager.OnAvatarClear(); + } + catch(System.Exception e) + { + MelonLoader.MelonLogger.Error(e); + } + } + + static void OnSetupAvatar_Postfix() => ms_instance?.OnSetupAvatar(); + void OnSetupAvatar() + { + try + { + if(m_leapManager != null) + m_leapManager.OnAvatarSetup(); + } + catch(System.Exception e) + { + MelonLoader.MelonLogger.Error(e); + } + } + + static void OnAvatarReinitialize_Postfix() => ms_instance?.OnAvatarReinitialize(); + void OnAvatarReinitialize() + { + try + { + if(m_leapManager != null) + m_leapManager.OnAvatarReinitialize(); + } + catch(System.Exception e) + { + MelonLoader.MelonLogger.Error(e); + } + } + + static void OnRayScale_Postfix(float __0) => ms_instance?.OnRayScale(__0); + void OnRayScale(float p_scale) + { + try + { + if(m_leapManager != null) + m_leapManager.OnRayScale(p_scale); + } + catch(System.Exception e) + { + MelonLoader.MelonLogger.Error(e); + } + } + + static void OnPlayspaceScale_Postfix(float ____avatarScaleRelation) => ms_instance?.OnPlayspaceScale(____avatarScaleRelation); + void OnPlayspaceScale(float p_relation) + { + try + { + if(m_leapManager != null) + m_leapManager.OnPlayspaceScale(p_relation); + } + catch(System.Exception e) + { + MelonLoader.MelonLogger.Error(e); + } + } + + static void OnPickupGrab_Postfix(ref CVRPickupObject __instance) => ms_instance?.OnPickupGrab(__instance); + void OnPickupGrab(CVRPickupObject p_pickup) + { + try + { + if(m_leapManager != null) + m_leapManager.OnPickupGrab(p_pickup); + } + catch(System.Exception e) + { + MelonLoader.MelonLogger.Error(e); + } + } + } +} diff --git a/ml_lme/PoseHelper.cs b/ml_lme/PoseHelper.cs deleted file mode 100644 index 4dbf3e7..0000000 --- a/ml_lme/PoseHelper.cs +++ /dev/null @@ -1,26 +0,0 @@ -using UnityEngine; -using ABI_RC.Systems.IK; - -namespace ml_lme -{ - static class PoseHelper - { - public static void ForceTPose(Animator p_animator) - { - if(p_animator.isHuman) - { - HumanPoseHandler l_handler = new HumanPoseHandler(p_animator.avatar, p_animator.transform); - HumanPose l_pose = new HumanPose(); - l_handler.GetHumanPose(ref l_pose); - - for(int i=0, j = Mathf.Min(l_pose.muscles.Length,MusclePoses.TPoseMuscles.Length); i < j; i++) - l_pose.muscles[i] = MusclePoses.TPoseMuscles[i]; - - l_pose.bodyPosition = Vector3.up; - l_pose.bodyRotation = Quaternion.identity; - l_handler.SetHumanPose(ref l_pose); - l_handler.Dispose(); - } - } - } -} diff --git a/ml_lme/Properties/AssemblyInfo.cs b/ml_lme/Properties/AssemblyInfo.cs index 2a759ab..28e79bd 100644 --- a/ml_lme/Properties/AssemblyInfo.cs +++ b/ml_lme/Properties/AssemblyInfo.cs @@ -1,4 +1,4 @@ -[assembly: MelonLoader.MelonInfo(typeof(ml_lme.LeapMotionExtension), "LeapMotionExtension", "1.4.6-ex", "SDraw", "https://github.com/SDraw/ml_mods_cvr")] +[assembly: MelonLoader.MelonInfo(typeof(ml_lme.LeapMotionExtension), "LeapMotionExtension", "1.4.7", "SDraw", "https://github.com/SDraw/ml_mods_cvr")] [assembly: MelonLoader.MelonGame(null, "ChilloutVR")] [assembly: MelonLoader.MelonOptionalDependencies("ml_pmc")] [assembly: MelonLoader.MelonPlatform(MelonLoader.MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)] diff --git a/ml_lme/README.md b/ml_lme/README.md index c4f83cd..0d2a0ec 100644 --- a/ml_lme/README.md +++ b/ml_lme/README.md @@ -25,3 +25,5 @@ Available mod's settings in `Settings - Implementation - Leap Motion Tracking`: * **Recognize gestures:** sets avatar gestures (fist, gun, rock'n'roll and etc.) based on current fingers pose; `false` by default. * **Interact gesture threadhold:** activation limit for interaction based on hand gesture; 80 by default. * **Grip gesture threadhold:** activation limit for grip based on hand gesture; 40 by default. +* **Filter humanoid limits:** Limits fingers rotations to be valid for Unity's Mechanim; `true` by default + * Note: Enabling this option ensures that visual representation of your fingers will be same for you and remote players, but it cancels out additional finger segments rotations that can be better visually in most cases. diff --git a/ml_lme/Settings.cs b/ml_lme/Settings.cs index e93faf5..ced9738 100644 --- a/ml_lme/Settings.cs +++ b/ml_lme/Settings.cs @@ -35,7 +35,8 @@ namespace ml_lme Gestures, InteractThreadhold, GripThreadhold, - VisualHands + VisualHands, + MechanimFilter }; public static bool Enabled { get; private set; } = false; @@ -52,24 +53,26 @@ namespace ml_lme public static float InteractThreadhold { get; private set; } = 0.8f; public static float GripThreadhold { get; private set; } = 0.4f; public static bool VisualHands { get; private set; } = false; + public static bool MechanimFilter { get; private set; } = true; static MelonLoader.MelonPreferences_Category ms_category = null; static List ms_entries = null; - static public event Action EnabledChange; - static public event Action DesktopOffsetChange; - static public event Action FingersOnlyChange; - static public event Action ModelVisibilityChange; - static public event Action TrackingModeChange; - static public event Action RootAngleChange; - static public event Action HeadAttachChange; - static public event Action HeadOffsetChange; - static public event Action TrackElbowsChange; - static public event Action InteractionChange; - static public event Action GesturesChange; - static public event Action InteractThreadholdChange; - static public event Action GripThreadholdChange; - static public event Action VisualHandsChange; + public static event Action EnabledChange; + public static event Action DesktopOffsetChange; + public static event Action FingersOnlyChange; + public static event Action ModelVisibilityChange; + public static event Action TrackingModeChange; + public static event Action RootAngleChange; + public static event Action HeadAttachChange; + public static event Action HeadOffsetChange; + public static event Action TrackElbowsChange; + public static event Action InteractionChange; + public static event Action GesturesChange; + public static event Action InteractThreadholdChange; + public static event Action GripThreadholdChange; + public static event Action VisualHandsChange; + public static event Action MechanimFilterChange; internal static void Init() { @@ -96,7 +99,8 @@ namespace ml_lme ms_category.CreateEntry(ModSetting.Gestures.ToString(), Gestures), ms_category.CreateEntry(ModSetting.InteractThreadhold.ToString(), (int)(InteractThreadhold * 100f)), ms_category.CreateEntry(ModSetting.GripThreadhold.ToString(), (int)(GripThreadhold * 100f)), - ms_category.CreateEntry(ModSetting.VisualHands.ToString(), VisualHands) + ms_category.CreateEntry(ModSetting.VisualHands.ToString(), VisualHands), + ms_category.CreateEntry(ModSetting.MechanimFilter.ToString(), MechanimFilter) }; Load(); @@ -156,6 +160,7 @@ namespace ml_lme InteractThreadhold = (int)ms_entries[(int)ModSetting.InteractThreadhold].BoxedValue * 0.01f; GripThreadhold = (int)ms_entries[(int)ModSetting.GripThreadhold].BoxedValue * 0.01f; VisualHands = (bool)ms_entries[(int)ModSetting.VisualHands].BoxedValue; + MechanimFilter = (bool)ms_entries[(int)ModSetting.MechanimFilter].BoxedValue; } static void OnToggleUpdate(string p_name, string p_value) @@ -219,6 +224,13 @@ namespace ml_lme VisualHandsChange?.Invoke(VisualHands); } break; + + case ModSetting.MechanimFilter: + { + MechanimFilter = bool.Parse(p_value); + MechanimFilterChange?.Invoke(MechanimFilter); + } + break; } ms_entries[(int)l_setting].BoxedValue = bool.Parse(p_value); diff --git a/ml_lme/Utils.cs b/ml_lme/Utils.cs index 1cb14f3..2638fb7 100644 --- a/ml_lme/Utils.cs +++ b/ml_lme/Utils.cs @@ -1,6 +1,8 @@ using ABI_RC.Core.InteractionSystem; +using ABI_RC.Core.Player; using ABI_RC.Core.Savior; using ABI_RC.Core.UI; +using ABI_RC.Systems.IK; using ABI_RC.Systems.InputManagement; using System.Collections.Generic; using System.Reflection; @@ -49,7 +51,14 @@ namespace ml_lme } } - static public void ExecuteScript(this CohtmlControlledViewWrapper p_instance, string p_script) => ((cohtml.Net.View)ms_view.GetValue(p_instance)).ExecuteScript(p_script); + public static void ExecuteScript(this CohtmlControlledViewWrapper p_instance, string p_script) => ((cohtml.Net.View)ms_view.GetValue(p_instance)).ExecuteScript(p_script); + + public static void SetAvatarTPose() + { + IKSystem.Instance.SetAvatarPose(IKSystem.AvatarPose.TPose); + PlayerSetup.Instance._avatar.transform.localPosition = Vector3.zero; + PlayerSetup.Instance._avatar.transform.localRotation = Quaternion.identity; + } public static void Swap(ref T lhs, ref T rhs) { diff --git a/ml_lme/ml_lme.csproj b/ml_lme/ml_lme.csproj index 2d70ed7..cb0ccc9 100644 --- a/ml_lme/ml_lme.csproj +++ b/ml_lme/ml_lme.csproj @@ -4,7 +4,7 @@ netstandard2.1 x64 LeapMotionExtension - 1.4.6 + 1.4.7 SDraw None LeapMotionExtension diff --git a/ml_lme/resources/mod_menu.js b/ml_lme/resources/mod_menu.js index dadaee1..96a11f0 100644 --- a/ml_lme/resources/mod_menu.js +++ b/ml_lme/resources/mod_menu.js @@ -145,6 +145,13 @@ + + + Filter humanoid limits: + + + + `; document.getElementById('settings-implementation').appendChild(l_block); diff --git a/ml_mods_cvr.sln b/ml_mods_cvr.sln index 573d2e1..52c57a2 100644 --- a/ml_mods_cvr.sln +++ b/ml_mods_cvr.sln @@ -24,6 +24,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ml_pin", "ml_pin\ml_pin.csp EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ml_dht", "ml_dht\ml_dht.csproj", "{31987392-989C-40C1-A48B-7F6099816EBE}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ml_bft", "ml_bft\ml_bft.csproj", "{331C995D-9648-44AD-8B02-D5F3A89FDC1F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -60,6 +62,9 @@ Global {31987392-989C-40C1-A48B-7F6099816EBE}.Debug|x64.Build.0 = Debug|x64 {31987392-989C-40C1-A48B-7F6099816EBE}.Release|x64.ActiveCfg = Release|x64 {31987392-989C-40C1-A48B-7F6099816EBE}.Release|x64.Build.0 = Release|x64 + {331C995D-9648-44AD-8B02-D5F3A89FDC1F}.Debug|x64.ActiveCfg = Debug|x64 + {331C995D-9648-44AD-8B02-D5F3A89FDC1F}.Release|x64.ActiveCfg = Release|x64 + {331C995D-9648-44AD-8B02-D5F3A89FDC1F}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/ml_pam/ArmMover.cs b/ml_pam/ArmMover.cs index 7aa119c..b14dc40 100644 --- a/ml_pam/ArmMover.cs +++ b/ml_pam/ArmMover.cs @@ -332,8 +332,7 @@ namespace ml_pam if(PlayerSetup.Instance._animator.isHuman) { - if(!m_inVR) - PoseHelper.ForceTPose(PlayerSetup.Instance._animator); + Utils.SetAvatarTPose(); Transform l_leftHand = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.LeftHand); if(l_leftHand != null) diff --git a/ml_pam/Main.cs b/ml_pam/Main.cs index b6e17a9..24da047 100644 --- a/ml_pam/Main.cs +++ b/ml_pam/Main.cs @@ -1,160 +1,160 @@ -using ABI.CCK.Components; -using ABI_RC.Core.InteractionSystem; -using ABI_RC.Core.Player; -using ABI_RC.Systems.IK; -using System; -using System.Reflection; -using UnityEngine; - -namespace ml_pam -{ - public class PickupArmMovement : MelonLoader.MelonMod - { - static PickupArmMovement ms_instance = null; - - ArmMover m_localMover = null; - - public override void OnInitializeMelon() - { - if(ms_instance == null) - ms_instance = this; - - Settings.Init(); - - HarmonyInstance.Patch( - typeof(PlayerSetup).GetMethod(nameof(PlayerSetup.ClearAvatar)), - null, - new HarmonyLib.HarmonyMethod(typeof(PickupArmMovement).GetMethod(nameof(OnAvatarClear_Postfix), BindingFlags.NonPublic | BindingFlags.Static)) - ); - HarmonyInstance.Patch( - typeof(PlayerSetup).GetMethod(nameof(PlayerSetup.SetupAvatar)), - null, - new HarmonyLib.HarmonyMethod(typeof(PickupArmMovement).GetMethod(nameof(OnSetupAvatar_Postfix), BindingFlags.Static | BindingFlags.NonPublic)) - ); - HarmonyInstance.Patch( - typeof(IKSystem).GetMethod(nameof(IKSystem.ReinitializeAvatar), BindingFlags.Instance | BindingFlags.Public), - null, - new HarmonyLib.HarmonyMethod(typeof(PickupArmMovement).GetMethod(nameof(OnAvatarReinitialize_Postfix), BindingFlags.Static | BindingFlags.NonPublic)) - ); - HarmonyInstance.Patch( - typeof(CVRPickupObject).GetMethod(nameof(CVRPickupObject.Grab)), - null, - new HarmonyLib.HarmonyMethod(typeof(PickupArmMovement).GetMethod(nameof(OnCVRPickupObjectGrab_Postfix), BindingFlags.Static | BindingFlags.NonPublic)) - ); - HarmonyInstance.Patch( - typeof(CVRPickupObject).GetMethod(nameof(CVRPickupObject.Drop)), - null, - new HarmonyLib.HarmonyMethod(typeof(PickupArmMovement).GetMethod(nameof(OnCVRPickupObjectDrop_Postfix), BindingFlags.Static | BindingFlags.NonPublic)) - ); - HarmonyInstance.Patch( - typeof(PlayerSetup).GetMethod("SetPlaySpaceScale", BindingFlags.NonPublic | BindingFlags.Instance), - null, - new HarmonyLib.HarmonyMethod(typeof(PickupArmMovement).GetMethod(nameof(OnPlayspaceScale_Postfix), BindingFlags.Static | BindingFlags.NonPublic)) - ); - - MelonLoader.MelonCoroutines.Start(WaitForLocalPlayer()); - } - - System.Collections.IEnumerator WaitForLocalPlayer() - { - while(PlayerSetup.Instance == null) - yield return null; - - m_localMover = PlayerSetup.Instance.gameObject.AddComponent(); - } - - public override void OnDeinitializeMelon() - { - if(ms_instance == this) - ms_instance = null; - - if(m_localMover != null) - UnityEngine.Object.Destroy(m_localMover); - m_localMover = null; - } - - static void OnAvatarClear_Postfix() => ms_instance?.OnAvatarClear(); - void OnAvatarClear() - { - try - { - if(m_localMover != null) - m_localMover.OnAvatarClear(); - } - catch(Exception e) - { - MelonLoader.MelonLogger.Error(e); - } - } - - static void OnSetupAvatar_Postfix() => ms_instance?.OnSetupAvatar(); - void OnSetupAvatar() - { - try - { - if(m_localMover != null) - m_localMover.OnAvatarSetup(); - } - catch(Exception e) - { - MelonLoader.MelonLogger.Error(e); - } - } - - static void OnAvatarReinitialize_Postfix() => ms_instance?.OnAvatarReinitialize(); - void OnAvatarReinitialize() - { - try - { - if(m_localMover != null) - m_localMover.OnAvatarReinitialize(); - } - catch(System.Exception e) - { - MelonLoader.MelonLogger.Error(e); - } - } - - static void OnCVRPickupObjectGrab_Postfix(ref CVRPickupObject __instance, ControllerRay __1, Vector3 __2) => ms_instance?.OnCVRPickupObjectGrab(__instance, __1, __2); - void OnCVRPickupObjectGrab(CVRPickupObject p_pickup, ControllerRay p_ray, Vector3 p_hit) - { - try - { - if(p_pickup.IsGrabbedByMe() && (m_localMover != null)) - m_localMover.OnPickupGrab(p_pickup, p_ray, p_hit); - } - catch(Exception e) - { - MelonLoader.MelonLogger.Error(e); - } - } - - static void OnCVRPickupObjectDrop_Postfix(ref CVRPickupObject __instance) => ms_instance?.OnCVRPickupObjectDrop(__instance); - void OnCVRPickupObjectDrop(CVRPickupObject p_pickup) - { - try - { - if(m_localMover != null) - m_localMover.OnPickupDrop(p_pickup); - } - catch(Exception e) - { - MelonLoader.MelonLogger.Error(e); - } - } - - static void OnPlayspaceScale_Postfix(float ____avatarScaleRelation) => ms_instance?.OnPlayspaceScale(____avatarScaleRelation); - void OnPlayspaceScale(float p_relation) - { - try - { - if(m_localMover != null) - m_localMover.OnPlayspaceScale(p_relation); - } - catch(Exception e) - { - MelonLoader.MelonLogger.Error(e); - } - } - } -} +using ABI.CCK.Components; +using ABI_RC.Core.InteractionSystem; +using ABI_RC.Core.Player; +using ABI_RC.Systems.IK; +using System; +using System.Reflection; +using UnityEngine; + +namespace ml_pam +{ + public class PickupArmMovement : MelonLoader.MelonMod + { + static PickupArmMovement ms_instance = null; + + ArmMover m_localMover = null; + + public override void OnInitializeMelon() + { + if(ms_instance == null) + ms_instance = this; + + Settings.Init(); + + HarmonyInstance.Patch( + typeof(PlayerSetup).GetMethod(nameof(PlayerSetup.ClearAvatar)), + null, + new HarmonyLib.HarmonyMethod(typeof(PickupArmMovement).GetMethod(nameof(OnAvatarClear_Postfix), BindingFlags.NonPublic | BindingFlags.Static)) + ); + HarmonyInstance.Patch( + typeof(PlayerSetup).GetMethod(nameof(PlayerSetup.SetupAvatar)), + null, + new HarmonyLib.HarmonyMethod(typeof(PickupArmMovement).GetMethod(nameof(OnSetupAvatar_Postfix), BindingFlags.Static | BindingFlags.NonPublic)) + ); + HarmonyInstance.Patch( + typeof(IKSystem).GetMethod(nameof(IKSystem.ReinitializeAvatar), BindingFlags.Instance | BindingFlags.Public), + null, + new HarmonyLib.HarmonyMethod(typeof(PickupArmMovement).GetMethod(nameof(OnAvatarReinitialize_Postfix), BindingFlags.Static | BindingFlags.NonPublic)) + ); + HarmonyInstance.Patch( + typeof(CVRPickupObject).GetMethod(nameof(CVRPickupObject.Grab)), + null, + new HarmonyLib.HarmonyMethod(typeof(PickupArmMovement).GetMethod(nameof(OnCVRPickupObjectGrab_Postfix), BindingFlags.Static | BindingFlags.NonPublic)) + ); + HarmonyInstance.Patch( + typeof(CVRPickupObject).GetMethod(nameof(CVRPickupObject.Drop)), + null, + new HarmonyLib.HarmonyMethod(typeof(PickupArmMovement).GetMethod(nameof(OnCVRPickupObjectDrop_Postfix), BindingFlags.Static | BindingFlags.NonPublic)) + ); + HarmonyInstance.Patch( + typeof(PlayerSetup).GetMethod("SetPlaySpaceScale", BindingFlags.NonPublic | BindingFlags.Instance), + null, + new HarmonyLib.HarmonyMethod(typeof(PickupArmMovement).GetMethod(nameof(OnPlayspaceScale_Postfix), BindingFlags.Static | BindingFlags.NonPublic)) + ); + + MelonLoader.MelonCoroutines.Start(WaitForLocalPlayer()); + } + + System.Collections.IEnumerator WaitForLocalPlayer() + { + while(PlayerSetup.Instance == null) + yield return null; + + m_localMover = PlayerSetup.Instance.gameObject.AddComponent(); + } + + public override void OnDeinitializeMelon() + { + if(ms_instance == this) + ms_instance = null; + + if(m_localMover != null) + UnityEngine.Object.Destroy(m_localMover); + m_localMover = null; + } + + static void OnAvatarClear_Postfix() => ms_instance?.OnAvatarClear(); + void OnAvatarClear() + { + try + { + if(m_localMover != null) + m_localMover.OnAvatarClear(); + } + catch(Exception e) + { + MelonLoader.MelonLogger.Error(e); + } + } + + static void OnSetupAvatar_Postfix() => ms_instance?.OnSetupAvatar(); + void OnSetupAvatar() + { + try + { + if(m_localMover != null) + m_localMover.OnAvatarSetup(); + } + catch(Exception e) + { + MelonLoader.MelonLogger.Error(e); + } + } + + static void OnAvatarReinitialize_Postfix() => ms_instance?.OnAvatarReinitialize(); + void OnAvatarReinitialize() + { + try + { + if(m_localMover != null) + m_localMover.OnAvatarReinitialize(); + } + catch(System.Exception e) + { + MelonLoader.MelonLogger.Error(e); + } + } + + static void OnCVRPickupObjectGrab_Postfix(ref CVRPickupObject __instance, ControllerRay __1, Vector3 __2) => ms_instance?.OnCVRPickupObjectGrab(__instance, __1, __2); + void OnCVRPickupObjectGrab(CVRPickupObject p_pickup, ControllerRay p_ray, Vector3 p_hit) + { + try + { + if(p_pickup.IsGrabbedByMe() && (m_localMover != null)) + m_localMover.OnPickupGrab(p_pickup, p_ray, p_hit); + } + catch(Exception e) + { + MelonLoader.MelonLogger.Error(e); + } + } + + static void OnCVRPickupObjectDrop_Postfix(ref CVRPickupObject __instance) => ms_instance?.OnCVRPickupObjectDrop(__instance); + void OnCVRPickupObjectDrop(CVRPickupObject p_pickup) + { + try + { + if(m_localMover != null) + m_localMover.OnPickupDrop(p_pickup); + } + catch(Exception e) + { + MelonLoader.MelonLogger.Error(e); + } + } + + static void OnPlayspaceScale_Postfix(float ____avatarScaleRelation) => ms_instance?.OnPlayspaceScale(____avatarScaleRelation); + void OnPlayspaceScale(float p_relation) + { + try + { + if(m_localMover != null) + m_localMover.OnPlayspaceScale(p_relation); + } + catch(Exception e) + { + MelonLoader.MelonLogger.Error(e); + } + } + } +} diff --git a/ml_pam/PoseHelper.cs b/ml_pam/PoseHelper.cs deleted file mode 100644 index ecf871f..0000000 --- a/ml_pam/PoseHelper.cs +++ /dev/null @@ -1,26 +0,0 @@ -using UnityEngine; -using ABI_RC.Systems.IK; - -namespace ml_pam -{ - static class PoseHelper - { - public static void ForceTPose(Animator p_animator) - { - if(p_animator.isHuman) - { - HumanPoseHandler l_handler = new HumanPoseHandler(p_animator.avatar, p_animator.transform); - HumanPose l_pose = new HumanPose(); - l_handler.GetHumanPose(ref l_pose); - - for(int i = 0, j = Mathf.Min(l_pose.muscles.Length, MusclePoses.TPoseMuscles.Length); i < j; i++) - l_pose.muscles[i] = MusclePoses.TPoseMuscles[i]; - - l_pose.bodyPosition = Vector3.up; - l_pose.bodyRotation = Quaternion.identity; - l_handler.SetHumanPose(ref l_pose); - l_handler.Dispose(); - } - } - } -} diff --git a/ml_pam/Properties/AssemblyInfo.cs b/ml_pam/Properties/AssemblyInfo.cs index 9bd9a57..1cfa86b 100644 --- a/ml_pam/Properties/AssemblyInfo.cs +++ b/ml_pam/Properties/AssemblyInfo.cs @@ -1,4 +1,4 @@ -[assembly: MelonLoader.MelonInfo(typeof(ml_pam.PickupArmMovement), "PickupArmMovement", "1.1.0-ex", "SDraw", "https://github.com/SDraw/ml_mods_cvr")] +[assembly: MelonLoader.MelonInfo(typeof(ml_pam.PickupArmMovement), "PickupArmMovement", "1.1.0", "SDraw", "https://github.com/SDraw/ml_mods_cvr")] [assembly: MelonLoader.MelonGame(null, "ChilloutVR")] [assembly: MelonLoader.MelonPriority(1)] [assembly: MelonLoader.MelonPlatform(MelonLoader.MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)] diff --git a/ml_pam/Settings.cs b/ml_pam/Settings.cs index ff841ac..c1562e4 100644 --- a/ml_pam/Settings.cs +++ b/ml_pam/Settings.cs @@ -28,10 +28,10 @@ namespace ml_pam static MelonLoader.MelonPreferences_Category ms_category = null; static List ms_entries = null; - static public event Action EnabledChange; - static public event Action GrabOffsetChange; - static public event Action LeadingHandChange; - static public event Action HandsExtensionChange; + public static event Action EnabledChange; + public static event Action GrabOffsetChange; + public static event Action LeadingHandChange; + public static event Action HandsExtensionChange; internal static void Init() { diff --git a/ml_pam/Utils.cs b/ml_pam/Utils.cs index ec4513b..39b9b3c 100644 --- a/ml_pam/Utils.cs +++ b/ml_pam/Utils.cs @@ -1,22 +1,31 @@ -using ABI_RC.Core.Savior; -using ABI_RC.Core.UI; -using System.Reflection; -using UnityEngine; - -namespace ml_pam -{ - static class Utils - { - static readonly FieldInfo ms_view = typeof(CohtmlControlledViewWrapper).GetField("_view", BindingFlags.NonPublic | BindingFlags.Instance); - - public static bool IsInVR() => ((MetaPort.Instance != null) && MetaPort.Instance.isUsingVr); - - static public void ExecuteScript(this CohtmlControlledViewWrapper p_instance, string p_script) => ((cohtml.Net.View)ms_view.GetValue(p_instance)).ExecuteScript(p_script); - - // Extensions - public static Matrix4x4 GetMatrix(this Transform p_transform, bool p_pos = true, bool p_rot = true, bool p_scl = false) - { - return Matrix4x4.TRS(p_pos ? p_transform.position : Vector3.zero, p_rot ? p_transform.rotation : Quaternion.identity, p_scl ? p_transform.localScale : Vector3.one); - } - } -} +using ABI_RC.Core.Player; +using ABI_RC.Core.Savior; +using ABI_RC.Core.UI; +using ABI_RC.Systems.IK; +using System.Reflection; +using UnityEngine; + +namespace ml_pam +{ + static class Utils + { + static readonly FieldInfo ms_view = typeof(CohtmlControlledViewWrapper).GetField("_view", BindingFlags.NonPublic | BindingFlags.Instance); + + public static bool IsInVR() => ((MetaPort.Instance != null) && MetaPort.Instance.isUsingVr); + + public static void ExecuteScript(this CohtmlControlledViewWrapper p_instance, string p_script) => ((cohtml.Net.View)ms_view.GetValue(p_instance)).ExecuteScript(p_script); + + public static void SetAvatarTPose() + { + IKSystem.Instance.SetAvatarPose(IKSystem.AvatarPose.TPose); + PlayerSetup.Instance._avatar.transform.localPosition = Vector3.zero; + PlayerSetup.Instance._avatar.transform.localRotation = Quaternion.identity; + } + + // Extensions + public static Matrix4x4 GetMatrix(this Transform p_transform, bool p_pos = true, bool p_rot = true, bool p_scl = false) + { + return Matrix4x4.TRS(p_pos ? p_transform.position : Vector3.zero, p_rot ? p_transform.rotation : Quaternion.identity, p_scl ? p_transform.localScale : Vector3.one); + } + } +} diff --git a/ml_pam/ml_pam.csproj b/ml_pam/ml_pam.csproj index 37228e9..1decd72 100644 --- a/ml_pam/ml_pam.csproj +++ b/ml_pam/ml_pam.csproj @@ -11,8 +11,8 @@ - none - false + embedded + true diff --git a/ml_pin/Main.cs b/ml_pin/Main.cs index 59f5c03..4a4cf1b 100644 --- a/ml_pin/Main.cs +++ b/ml_pin/Main.cs @@ -1,113 +1,113 @@ -using ABI_RC.Core.AudioEffects; -using ABI_RC.Core.Networking.IO.Social; -using ABI_RC.Core.Player; -using ABI_RC.Core.Savior; -using ABI_RC.Systems.GameEventSystem; -using System; -using System.Collections; - -namespace ml_pin -{ - public class PlayersInstanceNotifier : MelonLoader.MelonMod - { - SoundManager m_soundManager = null; - - public override void OnInitializeMelon() - { - Settings.Init(); - ResourcesHandler.ExtractAudioResources(); - - MelonLoader.MelonCoroutines.Start(WaitForInstances()); - } - - public override void OnDeinitializeMelon() - { - m_soundManager = null; - } - - IEnumerator WaitForInstances() - { - if(InterfaceAudio.Instance == null) - yield return null; - - m_soundManager = new SoundManager(); - m_soundManager.LoadSounds(); - - CVRGameEventSystem.Player.OnJoin.AddListener(OnPlayerJoin); - CVRGameEventSystem.Player.OnLeave.AddListener(OnPlayerLeave); - } - - void OnPlayerJoin(PlayerDescriptor p_player) - { - try - { - if(p_player != null) // This happens sometimes, no idea why - { - bool l_isFriend = Friends.FriendsWith(p_player.ownerId); - bool l_notify = false; - - switch(Settings.NotifyType) - { - case Settings.NotificationType.None: - l_notify = false; - break; - case Settings.NotificationType.Friends: - l_notify = (l_isFriend && ShouldNotifyInCurrentInstance()); - break; - case Settings.NotificationType.All: - l_notify = ShouldNotifyInCurrentInstance(); - break; - } - l_notify |= (l_isFriend && Settings.FriendsAlways); - - if(l_notify) - m_soundManager?.PlaySound(l_isFriend ? SoundManager.SoundType.FriendJoin : SoundManager.SoundType.PlayerJoin); - } - } - catch(Exception e) - { - MelonLoader.MelonLogger.Warning(e); - } - } - void OnPlayerLeave(PlayerDescriptor p_player) - { - try - { - if(p_player != null) // This happens sometimes, no idea why - { - bool l_isFriend = Friends.FriendsWith(p_player.ownerId); - bool l_notify = false; - - switch(Settings.NotifyType) - { - case Settings.NotificationType.None: - l_notify = false; - break; - case Settings.NotificationType.Friends: - l_notify = (l_isFriend && ShouldNotifyInCurrentInstance()); - break; - case Settings.NotificationType.All: - l_notify = ShouldNotifyInCurrentInstance(); - break; - } - l_notify |= (l_isFriend && Settings.FriendsAlways); - - if(l_notify) - m_soundManager?.PlaySound(l_isFriend ? SoundManager.SoundType.FriendLeave : SoundManager.SoundType.PlayerLeave); - } - } - catch(Exception e) - { - MelonLoader.MelonLogger.Warning(e); - } - } - - bool ShouldNotifyInCurrentInstance() - { - bool l_isInPublic = (MetaPort.Instance.CurrentInstancePrivacy.Contains("Public") && Settings.NotifyInPublic); - bool l_isInFriends = (MetaPort.Instance.CurrentInstancePrivacy.Contains("Friends") && Settings.NotifyInFriends); - bool l_isInPrivate = (MetaPort.Instance.CurrentInstancePrivacy.Contains("invite") && Settings.NotifyInPrivate); - return (l_isInPublic || l_isInFriends || l_isInPrivate); - } - } -} +using ABI_RC.Core.AudioEffects; +using ABI_RC.Core.Networking.IO.Social; +using ABI_RC.Core.Player; +using ABI_RC.Core.Savior; +using ABI_RC.Systems.GameEventSystem; +using System; +using System.Collections; + +namespace ml_pin +{ + public class PlayersInstanceNotifier : MelonLoader.MelonMod + { + SoundManager m_soundManager = null; + + public override void OnInitializeMelon() + { + Settings.Init(); + ResourcesHandler.ExtractAudioResources(); + + MelonLoader.MelonCoroutines.Start(WaitForInstances()); + } + + public override void OnDeinitializeMelon() + { + m_soundManager = null; + } + + IEnumerator WaitForInstances() + { + if(InterfaceAudio.Instance == null) + yield return null; + + m_soundManager = new SoundManager(); + m_soundManager.LoadSounds(); + + CVRGameEventSystem.Player.OnJoin.AddListener(OnPlayerJoin); + CVRGameEventSystem.Player.OnLeave.AddListener(OnPlayerLeave); + } + + void OnPlayerJoin(PlayerDescriptor p_player) + { + try + { + if(p_player != null) // This happens sometimes, no idea why + { + bool l_isFriend = Friends.FriendsWith(p_player.ownerId); + bool l_notify = false; + + switch(Settings.NotifyType) + { + case Settings.NotificationType.None: + l_notify = false; + break; + case Settings.NotificationType.Friends: + l_notify = (l_isFriend && ShouldNotifyInCurrentInstance()); + break; + case Settings.NotificationType.All: + l_notify = ShouldNotifyInCurrentInstance(); + break; + } + l_notify |= (l_isFriend && Settings.FriendsAlways); + + if(l_notify) + m_soundManager?.PlaySound(l_isFriend ? SoundManager.SoundType.FriendJoin : SoundManager.SoundType.PlayerJoin); + } + } + catch(Exception e) + { + MelonLoader.MelonLogger.Warning(e); + } + } + void OnPlayerLeave(PlayerDescriptor p_player) + { + try + { + if(p_player != null) // This happens sometimes, no idea why + { + bool l_isFriend = Friends.FriendsWith(p_player.ownerId); + bool l_notify = false; + + switch(Settings.NotifyType) + { + case Settings.NotificationType.None: + l_notify = false; + break; + case Settings.NotificationType.Friends: + l_notify = (l_isFriend && ShouldNotifyInCurrentInstance()); + break; + case Settings.NotificationType.All: + l_notify = ShouldNotifyInCurrentInstance(); + break; + } + l_notify |= (l_isFriend && Settings.FriendsAlways); + + if(l_notify) + m_soundManager?.PlaySound(l_isFriend ? SoundManager.SoundType.FriendLeave : SoundManager.SoundType.PlayerLeave); + } + } + catch(Exception e) + { + MelonLoader.MelonLogger.Warning(e); + } + } + + bool ShouldNotifyInCurrentInstance() + { + bool l_isInPublic = (MetaPort.Instance.CurrentInstancePrivacy.Contains("Public") && Settings.NotifyInPublic); + bool l_isInFriends = (MetaPort.Instance.CurrentInstancePrivacy.Contains("Friends") && Settings.NotifyInFriends); + bool l_isInPrivate = (MetaPort.Instance.CurrentInstancePrivacy.Contains("invite") && Settings.NotifyInPrivate); + return (l_isInPublic || l_isInFriends || l_isInPrivate); + } + } +} diff --git a/ml_pin/Properties/AssemblyInfo.cs b/ml_pin/Properties/AssemblyInfo.cs index 67b4925..8698d98 100644 --- a/ml_pin/Properties/AssemblyInfo.cs +++ b/ml_pin/Properties/AssemblyInfo.cs @@ -1,4 +1,4 @@ -[assembly: MelonLoader.MelonInfo(typeof(ml_pin.PlayersInstanceNotifier), "PlayersInstanceNotifier", "1.0.2-ex", "SDraw", "https://github.com/SDraw/ml_mods_cvr")] -[assembly: MelonLoader.MelonGame(null, "ChilloutVR")] -[assembly: MelonLoader.MelonPlatform(MelonLoader.MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)] -[assembly: MelonLoader.MelonPlatformDomain(MelonLoader.MelonPlatformDomainAttribute.CompatibleDomains.MONO)] +[assembly: MelonLoader.MelonInfo(typeof(ml_pin.PlayersInstanceNotifier), "PlayersInstanceNotifier", "1.0.2", "SDraw", "https://github.com/SDraw/ml_mods_cvr")] +[assembly: MelonLoader.MelonGame(null, "ChilloutVR")] +[assembly: MelonLoader.MelonPlatform(MelonLoader.MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)] +[assembly: MelonLoader.MelonPlatformDomain(MelonLoader.MelonPlatformDomainAttribute.CompatibleDomains.MONO)] diff --git a/ml_pin/Settings.cs b/ml_pin/Settings.cs index d8a45f9..a25cc10 100644 --- a/ml_pin/Settings.cs +++ b/ml_pin/Settings.cs @@ -33,12 +33,12 @@ namespace ml_pin static MelonLoader.MelonPreferences_Category ms_category = null; static List ms_entries = null; - static public event Action NotifyTypeChange; - static public event Action VolumeChange; - static public event Action NotifyInPublicChange; - static public event Action NotifyInFriendsChange; - static public event Action NotifyInPrivateChange; - static public event Action FriendsAlwaysChange; + public static event Action NotifyTypeChange; + public static event Action VolumeChange; + public static event Action NotifyInPublicChange; + public static event Action NotifyInFriendsChange; + public static event Action NotifyInPrivateChange; + public static event Action FriendsAlwaysChange; internal static void Init() { diff --git a/ml_pin/Utils.cs b/ml_pin/Utils.cs index 105f01a..f2e993b 100644 --- a/ml_pin/Utils.cs +++ b/ml_pin/Utils.cs @@ -7,6 +7,6 @@ namespace ml_pin { static readonly FieldInfo ms_view = typeof(CohtmlControlledViewWrapper).GetField("_view", BindingFlags.NonPublic | BindingFlags.Instance); - static public void ExecuteScript(this CohtmlControlledViewWrapper p_instance, string p_script) => ((cohtml.Net.View)ms_view.GetValue(p_instance)).ExecuteScript(p_script); + public static void ExecuteScript(this CohtmlControlledViewWrapper p_instance, string p_script) => ((cohtml.Net.View)ms_view.GetValue(p_instance)).ExecuteScript(p_script); } } diff --git a/ml_pin/ml_pin.csproj b/ml_pin/ml_pin.csproj index 9228213..bbb4d30 100644 --- a/ml_pin/ml_pin.csproj +++ b/ml_pin/ml_pin.csproj @@ -1,86 +1,91 @@ - - - - netstandard2.1 - x64 - PlayersInstanceNotifier - SDraw - None - PlayersInstanceNotifier - 1.0.2 - - - - - - - - - - - - - - - - - - - - - D:\Games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\0Harmony.dll - false - false - - - D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Assembly-CSharp.dll - false - false - - - D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\cohtml.Net.dll - false - false - - - D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Cohtml.Runtime.dll - false - false - - - D:\Games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\MelonLoader.dll - false - false - - - D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.dll - false - false - - - D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.AudioModule.dll - false - false - - - D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.CoreModule.dll - false - false - - - D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.UnityWebRequestAudioModule.dll - false - false - - - D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.UnityWebRequestModule.dll - false - false - - - - - - - - + + + + netstandard2.1 + x64 + PlayersInstanceNotifier + SDraw + None + PlayersInstanceNotifier + 1.0.2 + + + + embedded + true + + + + + + + + + + + + + + + + + + + + + D:\Games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\0Harmony.dll + false + false + + + D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Assembly-CSharp.dll + false + false + + + D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\cohtml.Net.dll + false + false + + + D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Cohtml.Runtime.dll + false + false + + + D:\Games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\MelonLoader.dll + false + false + + + D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.dll + false + false + + + D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.AudioModule.dll + false + false + + + D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.CoreModule.dll + false + false + + + D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.UnityWebRequestAudioModule.dll + false + false + + + D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.UnityWebRequestModule.dll + false + false + + + + + + + + diff --git a/ml_pmc/PoseCopycat.cs b/ml_pmc/PoseCopycat.cs index 3ccfeaa..59edb7c 100644 --- a/ml_pmc/PoseCopycat.cs +++ b/ml_pmc/PoseCopycat.cs @@ -3,7 +3,6 @@ using ABI_RC.Systems.IK; using ABI_RC.Systems.IK.SubSystems; using ABI_RC.Systems.InputManagement; using ABI_RC.Systems.Movement; -using ABI_RC.Systems.VRModeSwitch; using RootMotion.FinalIK; using UnityEngine; @@ -14,8 +13,8 @@ namespace ml_pmc { static readonly Vector4 ms_pointVector = new Vector4(0f, 0f, 0f, 1f); - static public PoseCopycat Instance { get; private set; } = null; - static internal System.Action OnActivityChange; + public static PoseCopycat Instance { get; private set; } = null; + internal static System.Action OnActivityChange; Animator m_animator = null; VRIK m_vrIk = null; @@ -372,29 +371,29 @@ namespace ml_pmc if(!CVRInputManager.Instance.individualFingerTracking) { // Left hand - CVRInputManager.Instance.finger1StretchedLeftThumb = -0.5f; - CVRInputManager.Instance.finger2StretchedLeftThumb = 0.7f; - CVRInputManager.Instance.finger3StretchedLeftThumb = 0.7f; + CVRInputManager.Instance.finger1StretchedLeftThumb = -0f; + CVRInputManager.Instance.finger2StretchedLeftThumb = 0f; + CVRInputManager.Instance.finger3StretchedLeftThumb = 0f; CVRInputManager.Instance.fingerSpreadLeftThumb = 0f; - CVRInputManager.Instance.finger1StretchedLeftIndex = 0.5f; - CVRInputManager.Instance.finger2StretchedLeftIndex = 0.7f; - CVRInputManager.Instance.finger3StretchedLeftIndex = 0.7f; + CVRInputManager.Instance.finger1StretchedLeftIndex = 0f; + CVRInputManager.Instance.finger2StretchedLeftIndex = 0f; + CVRInputManager.Instance.finger3StretchedLeftIndex = 0f; CVRInputManager.Instance.fingerSpreadLeftIndex = 0f; - CVRInputManager.Instance.finger1StretchedLeftMiddle = 0.5f; - CVRInputManager.Instance.finger2StretchedLeftMiddle = 0.7f; - CVRInputManager.Instance.finger3StretchedLeftMiddle = 0.7f; + CVRInputManager.Instance.finger1StretchedLeftMiddle = 0; + CVRInputManager.Instance.finger2StretchedLeftMiddle = 0f; + CVRInputManager.Instance.finger3StretchedLeftMiddle = 0f; CVRInputManager.Instance.fingerSpreadLeftMiddle = 0f; - CVRInputManager.Instance.finger1StretchedLeftRing = 0.5f; - CVRInputManager.Instance.finger2StretchedLeftRing = 0.7f; - CVRInputManager.Instance.finger3StretchedLeftRing = 0.7f; + CVRInputManager.Instance.finger1StretchedLeftRing = 0f; + CVRInputManager.Instance.finger2StretchedLeftRing = 0f; + CVRInputManager.Instance.finger3StretchedLeftRing = 0f; CVRInputManager.Instance.fingerSpreadLeftRing = 0f; - CVRInputManager.Instance.finger1StretchedLeftPinky = 0.5f; - CVRInputManager.Instance.finger2StretchedLeftPinky = 0.7f; - CVRInputManager.Instance.finger3StretchedLeftPinky = 0.7f; + CVRInputManager.Instance.finger1StretchedLeftPinky = 0f; + CVRInputManager.Instance.finger2StretchedLeftPinky = 0f; + CVRInputManager.Instance.finger3StretchedLeftPinky = 0f; CVRInputManager.Instance.fingerSpreadLeftPinky = 0f; CVRInputManager.Instance.fingerFullCurlNormalizedLeftThumb = 0f; @@ -404,29 +403,29 @@ namespace ml_pmc CVRInputManager.Instance.fingerFullCurlNormalizedLeftPinky = 0f; // Right hand - CVRInputManager.Instance.finger1StretchedRightThumb = -0.5f; - CVRInputManager.Instance.finger2StretchedRightThumb = 0.7f; - CVRInputManager.Instance.finger3StretchedRightThumb = 0.7f; + CVRInputManager.Instance.finger1StretchedRightThumb = 0f; + CVRInputManager.Instance.finger2StretchedRightThumb = 0f; + CVRInputManager.Instance.finger3StretchedRightThumb = 0f; CVRInputManager.Instance.fingerSpreadRightThumb = 0f; - CVRInputManager.Instance.finger1StretchedRightIndex = 0.5f; - CVRInputManager.Instance.finger2StretchedRightIndex = 0.7f; - CVRInputManager.Instance.finger3StretchedRightIndex = 0.7f; + CVRInputManager.Instance.finger1StretchedRightIndex = 0f; + CVRInputManager.Instance.finger2StretchedRightIndex = 0f; + CVRInputManager.Instance.finger3StretchedRightIndex = 0f; CVRInputManager.Instance.fingerSpreadRightIndex = 0f; - CVRInputManager.Instance.finger1StretchedRightMiddle = 0.5f; - CVRInputManager.Instance.finger2StretchedRightMiddle = 0.7f; - CVRInputManager.Instance.finger3StretchedRightMiddle = 0.7f; + CVRInputManager.Instance.finger1StretchedRightMiddle = 0f; + CVRInputManager.Instance.finger2StretchedRightMiddle = 0f; + CVRInputManager.Instance.finger3StretchedRightMiddle = 0f; CVRInputManager.Instance.fingerSpreadRightMiddle = 0f; - CVRInputManager.Instance.finger1StretchedRightRing = 0.5f; - CVRInputManager.Instance.finger2StretchedRightRing = 0.7f; - CVRInputManager.Instance.finger3StretchedRightRing = 0.7f; + CVRInputManager.Instance.finger1StretchedRightRing = 0f; + CVRInputManager.Instance.finger2StretchedRightRing = 0f; + CVRInputManager.Instance.finger3StretchedRightRing = 0f; CVRInputManager.Instance.fingerSpreadRightRing = 0f; - CVRInputManager.Instance.finger1StretchedRightPinky = 0.5f; - CVRInputManager.Instance.finger2StretchedRightPinky = 0.7f; - CVRInputManager.Instance.finger3StretchedRightPinky = 0.7f; + CVRInputManager.Instance.finger1StretchedRightPinky = 0f; + CVRInputManager.Instance.finger2StretchedRightPinky = 0f; + CVRInputManager.Instance.finger3StretchedRightPinky = 0f; CVRInputManager.Instance.fingerSpreadRightPinky = 0f; CVRInputManager.Instance.fingerFullCurlNormalizedRightThumb = 0f; diff --git a/ml_pmc/Properties/AssemblyInfo.cs b/ml_pmc/Properties/AssemblyInfo.cs index 518b369..58290fc 100644 --- a/ml_pmc/Properties/AssemblyInfo.cs +++ b/ml_pmc/Properties/AssemblyInfo.cs @@ -1,4 +1,4 @@ -[assembly: MelonLoader.MelonInfo(typeof(ml_pmc.PlayerMovementCopycat), "PlayerMovementCopycat", "1.0.5-ex", "SDraw", "https://github.com/SDraw/ml_mods_cvr")] +[assembly: MelonLoader.MelonInfo(typeof(ml_pmc.PlayerMovementCopycat), "PlayerMovementCopycat", "1.0.5", "SDraw", "https://github.com/SDraw/ml_mods_cvr")] [assembly: MelonLoader.MelonGame(null, "ChilloutVR")] [assembly: MelonLoader.MelonPriority(3)] [assembly: MelonLoader.MelonAdditionalDependencies("BTKUILib")] diff --git a/ml_pmc/ml_pmc.csproj b/ml_pmc/ml_pmc.csproj index 75e32d0..75ac2cb 100644 --- a/ml_pmc/ml_pmc.csproj +++ b/ml_pmc/ml_pmc.csproj @@ -10,6 +10,11 @@ 1.0.5 + + embedded + true + + diff --git a/ml_prm/ModUi.cs b/ml_prm/ModUi.cs index 98a36bb..56964a8 100644 --- a/ml_prm/ModUi.cs +++ b/ml_prm/ModUi.cs @@ -29,7 +29,7 @@ namespace ml_prm FallLimit } - static public event Action SwitchChange; + public static event Action SwitchChange; static List ms_uiElements = null; diff --git a/ml_prm/Properties/AssemblyInfo.cs b/ml_prm/Properties/AssemblyInfo.cs index 3b540bd..0a8d1f9 100644 --- a/ml_prm/Properties/AssemblyInfo.cs +++ b/ml_prm/Properties/AssemblyInfo.cs @@ -1,4 +1,4 @@ -[assembly: MelonLoader.MelonInfo(typeof(ml_prm.PlayerRagdollMod), "PlayerRagdollMod", "1.1.3-ex", "SDraw", "https://github.com/SDraw/ml_mods_cvr")] +[assembly: MelonLoader.MelonInfo(typeof(ml_prm.PlayerRagdollMod), "PlayerRagdollMod", "1.1.3", "SDraw", "https://github.com/SDraw/ml_mods_cvr")] [assembly: MelonLoader.MelonGame(null, "ChilloutVR")] [assembly: MelonLoader.MelonPriority(2)] [assembly: MelonLoader.MelonOptionalDependencies("BTKUILib")] diff --git a/ml_prm/RagdollController.cs b/ml_prm/RagdollController.cs index c1b3e9b..dc38bc4 100644 --- a/ml_prm/RagdollController.cs +++ b/ml_prm/RagdollController.cs @@ -288,6 +288,8 @@ namespace ml_prm if(PlayerSetup.Instance._animator.isHuman) { + Utils.SetAvatarTPose(); + BipedRagdollReferences l_avatarReferences = BipedRagdollReferences.FromAvatar(PlayerSetup.Instance._animator); m_puppet = new GameObject("Root").transform; diff --git a/ml_prm/Settings.cs b/ml_prm/Settings.cs index 112dfae..b241af5 100644 --- a/ml_prm/Settings.cs +++ b/ml_prm/Settings.cs @@ -47,24 +47,24 @@ namespace ml_prm public static bool FallDamage { get; private set; } = true; public static float FallLimit { get; private set; } = 5f; - static public event Action HotkeyChange; - static public event Action HotkeyKeyChange; - static public event Action VelocityMultiplierChange; - static public event Action MovementDragChange; - static public event Action AngularDragChange; - static public event Action GravityChange; - static public event Action PointersReactionChange; - static public event Action IgnoreLocalChange; - static public event Action CombatReactionChange; - static public event Action AutoRecoverChange; - static public event Action RecoverDelayChange; - static public event Action SlipperinessChange; - static public event Action BouncinessChange; - static public event Action ViewVelocityChange; - static public event Action JumpRecoverChange; - static public event Action BuoyancyChange; - static public event Action FallDamageChange; - static public event Action FallLimitChange; + public static event Action HotkeyChange; + public static event Action HotkeyKeyChange; + public static event Action VelocityMultiplierChange; + public static event Action MovementDragChange; + public static event Action AngularDragChange; + public static event Action GravityChange; + public static event Action PointersReactionChange; + public static event Action IgnoreLocalChange; + public static event Action CombatReactionChange; + public static event Action AutoRecoverChange; + public static event Action RecoverDelayChange; + public static event Action SlipperinessChange; + public static event Action BouncinessChange; + public static event Action ViewVelocityChange; + public static event Action JumpRecoverChange; + public static event Action BuoyancyChange; + public static event Action FallDamageChange; + public static event Action FallLimitChange; static MelonLoader.MelonPreferences_Category ms_category = null; static List ms_entries = null; diff --git a/ml_prm/Utils.cs b/ml_prm/Utils.cs index 41b3ba0..52aacf9 100644 --- a/ml_prm/Utils.cs +++ b/ml_prm/Utils.cs @@ -1,5 +1,7 @@ using ABI.CCK.Components; +using ABI_RC.Core.Player; using ABI_RC.Core.Savior; +using ABI_RC.Systems.IK; using ABI_RC.Systems.Movement; using System.Collections.Generic; using System.Reflection; @@ -46,5 +48,12 @@ namespace ml_prm (ms_influencerTouchingVolumes.GetValue(p_instance) as List)?.Clear(); (ms_influencerSubmergedColliders.GetValue(p_instance) as Dictionary)?.Clear(); } + + public static void SetAvatarTPose() + { + IKSystem.Instance.SetAvatarPose(IKSystem.AvatarPose.TPose); + PlayerSetup.Instance._avatar.transform.localPosition = Vector3.zero; + PlayerSetup.Instance._avatar.transform.localRotation = Quaternion.identity; + } } } diff --git a/ml_prm/ml_prm.csproj b/ml_prm/ml_prm.csproj index e84b7ac..9b23b10 100644 --- a/ml_prm/ml_prm.csproj +++ b/ml_prm/ml_prm.csproj @@ -10,6 +10,11 @@ PlayerRagdollMod + + embedded + true + + diff --git a/ml_vei/Properties/AssemblyInfo.cs b/ml_vei/Properties/AssemblyInfo.cs index 1fb0b47..129e479 100644 --- a/ml_vei/Properties/AssemblyInfo.cs +++ b/ml_vei/Properties/AssemblyInfo.cs @@ -1,4 +1,4 @@ -[assembly: MelonLoader.MelonInfo(typeof(ml_vei.ViveExtendedInput), "ViveExtendedInput", "1.0.1-ex", "SDraw", "https://github.com/SDraw/ml_mods_cvr")] -[assembly: MelonLoader.MelonGame(null, "ChilloutVR")] -[assembly: MelonLoader.MelonPlatform(MelonLoader.MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)] -[assembly: MelonLoader.MelonPlatformDomain(MelonLoader.MelonPlatformDomainAttribute.CompatibleDomains.MONO)] +[assembly: MelonLoader.MelonInfo(typeof(ml_vei.ViveExtendedInput), "ViveExtendedInput", "1.0.1", "SDraw", "https://github.com/SDraw/ml_mods_cvr")] +[assembly: MelonLoader.MelonGame(null, "ChilloutVR")] +[assembly: MelonLoader.MelonPlatform(MelonLoader.MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)] +[assembly: MelonLoader.MelonPlatformDomain(MelonLoader.MelonPlatformDomainAttribute.CompatibleDomains.MONO)] diff --git a/ml_vei/Settings.cs b/ml_vei/Settings.cs index 7a412c0..9f0f435 100644 --- a/ml_vei/Settings.cs +++ b/ml_vei/Settings.cs @@ -26,9 +26,9 @@ namespace ml_vei static MelonLoader.MelonPreferences_Category ms_category = null; static List ms_entries = null; - static public event Action GesturesChange; - static public event Action GripTriggerChange; - static public event Action AxisPriorityChange; + public static event Action GesturesChange; + public static event Action GripTriggerChange; + public static event Action AxisPriorityChange; internal static void Init() { diff --git a/ml_vei/Utils.cs b/ml_vei/Utils.cs index ad1851f..5da1fd9 100644 --- a/ml_vei/Utils.cs +++ b/ml_vei/Utils.cs @@ -7,6 +7,6 @@ namespace ml_vei { static readonly FieldInfo ms_view = typeof(CohtmlControlledViewWrapper).GetField("_view", BindingFlags.NonPublic | BindingFlags.Instance); - static public void ExecuteScript(this CohtmlControlledViewWrapper p_instance, string p_script) => ((cohtml.Net.View)ms_view.GetValue(p_instance)).ExecuteScript(p_script); + public static void ExecuteScript(this CohtmlControlledViewWrapper p_instance, string p_script) => ((cohtml.Net.View)ms_view.GetValue(p_instance)).ExecuteScript(p_script); } } diff --git a/ml_vei/ml_vei.csproj b/ml_vei/ml_vei.csproj index ffd4ad5..f1eb3b8 100644 --- a/ml_vei/ml_vei.csproj +++ b/ml_vei/ml_vei.csproj @@ -1,78 +1,83 @@ - - - - netstandard2.1 - x64 - ViveExtendedInput - 1.0.1 - SDraw - None - ViveExtendedInput - - - - - - - - - - D:\games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\0Harmony.dll - false - false - - - D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Assembly-CSharp.dll - false - false - - - D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\cohtml.Net.dll - false - false - - - D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Cohtml.Runtime.dll - false - false - - - D:\games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\MelonLoader.dll - false - false - - - D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Unity.Postprocessing.Runtime.dll - false - false - - - D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.dll - false - false - - - D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.AnimationModule.dll - false - false - - - D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.CoreModule.dll - false - false - - - - - - - - - - - - - - - - + + + + netstandard2.1 + x64 + ViveExtendedInput + 1.0.1 + SDraw + None + ViveExtendedInput + + + + embedded + true + + + + + + + + + + D:\games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\0Harmony.dll + false + false + + + D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Assembly-CSharp.dll + false + false + + + D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\cohtml.Net.dll + false + false + + + D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Cohtml.Runtime.dll + false + false + + + D:\games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\MelonLoader.dll + false + false + + + D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Unity.Postprocessing.Runtime.dll + false + false + + + D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.dll + false + false + + + D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.AnimationModule.dll + false + false + + + D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.CoreModule.dll + false + false + + + + + + + + + + + + + + + +