From a3b4f461d11c9023895abb7669a748c9b61d2db9 Mon Sep 17 00:00:00 2001 From: SDraw Date: Fri, 4 Nov 2022 02:13:40 +0300 Subject: [PATCH] Hips following option `Moving` parameter --- README.md | 2 +- ml_amt/Main.cs | 1 + ml_amt/MotionTweaker.cs | 53 ++++++++++++++++++++++++++++++- ml_amt/Properties/AssemblyInfo.cs | 6 ++-- ml_amt/README.md | 3 ++ ml_amt/Settings.cs | 18 ++++++++++- ml_amt/resources/menu.js | 7 ++++ 7 files changed, 84 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index d9de291..f19a7eb 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Merged set of MelonLoader mods for ChilloutVR. | Full name | Short name | Latest version | Available in [CVRMA](https://github.com/knah/CVRMelonAssistant) | Current Status | Notes | |-----------|------------|----------------|-----------------------------------------------------------------|----------------|-------| | Avatar Change Info | ml_aci | 1.0.3 | Yes | Working | -| Avatar Motion Tweaker | ml_amt | 1.1.7 | Yes | Working | +| Avatar Motion Tweaker | ml_amt | 1.1.8 | Yes, pending update | Working | | Desktop Head Tracking | ml_dht | 1.0.7 | Yes | Working | | Desktop Reticle Switch | ml_drs | 1.0.0 | Yes | Working | | Four Point Tracking | ml_fpt | 1.0.9 | Yes | Working | diff --git a/ml_amt/Main.cs b/ml_amt/Main.cs index 82a8926..ea622e4 100644 --- a/ml_amt/Main.cs +++ b/ml_amt/Main.cs @@ -45,6 +45,7 @@ System.Collections.IEnumerator WaitForLocalPlayer() m_localTweaker.SetIKOverrideFly(Settings.IKOverrideFly); m_localTweaker.SetIKOverrideJump(Settings.IKOverrideJump); m_localTweaker.SetDetectEmotes(Settings.DetectEmotes); + m_localTweaker.SetFollowHips(Settings.FollowHips); } public override void OnDeinitializeMelon() diff --git a/ml_amt/MotionTweaker.cs b/ml_amt/MotionTweaker.cs index f86cbac..56090a0 100644 --- a/ml_amt/MotionTweaker.cs +++ b/ml_amt/MotionTweaker.cs @@ -18,7 +18,8 @@ class MotionTweaker : MonoBehaviour enum ParameterType { Upright, - GroundedRaw + GroundedRaw, + Moving } enum ParameterSyncType @@ -49,6 +50,7 @@ enum PoseState float m_ikWeight = 1f; // Original weight float m_locomotionWeight = 1f; // Original weight float m_avatarScale = 1f; // Instantiated scale + Transform m_avatarHips = null; bool m_avatarReady = false; bool m_compatibleAvatar = false; @@ -56,6 +58,7 @@ enum PoseState PoseState m_poseState = PoseState.Standing; bool m_grounded = false; bool m_groundedRaw = false; + bool m_moving = false; bool m_ikOverrideCrouch = true; float m_crouchLimit = 0.65f; @@ -76,6 +79,9 @@ enum PoseState bool m_detectEmotes = true; bool m_emoteActive = false; + bool m_followHips = true; + Vector3 m_hipsToPlayer = Vector3.zero; + readonly List m_parameters = null; public MotionTweaker() @@ -94,6 +100,7 @@ void Start() Settings.IKOverrideFlyChange += this.SetIKOverrideFly; Settings.IKOverrideJumpChange += this.SetIKOverrideJump; Settings.DetectEmotesChange += this.SetDetectEmotes; + Settings.FollowHipsChange += this.SetFollowHips; } void OnDestroy() @@ -107,6 +114,7 @@ void OnDestroy() Settings.IKOverrideFlyChange -= this.SetIKOverrideFly; Settings.IKOverrideJumpChange -= this.SetIKOverrideJump; Settings.DetectEmotesChange -= this.SetDetectEmotes; + Settings.FollowHipsChange -= this.SetFollowHips; } void Update() @@ -115,6 +123,7 @@ void Update() { m_grounded = (bool)ms_grounded.GetValue(MovementSystem.Instance); m_groundedRaw = (bool)ms_groundedRaw.GetValue(MovementSystem.Instance); + m_moving = !Mathf.Approximately(MovementSystem.Instance.movementVector.magnitude, 0f); // Update upright Matrix4x4 l_hmdMatrix = PlayerSetup.Instance.transform.GetMatrix().inverse * (PlayerSetup.Instance._inVr ? PlayerSetup.Instance.vrHeadTracker.transform.GetMatrix() : PlayerSetup.Instance.desktopCameraRig.transform.GetMatrix()); @@ -124,6 +133,12 @@ void Update() m_upright = Mathf.Clamp(((l_avatarViewHeight > 0f) ? (l_currentHeight / l_avatarViewHeight) : 0f), 0f, 1f); PoseState l_poseState = (m_upright <= m_proneLimit) ? PoseState.Proning : ((m_upright <= m_crouchLimit) ? PoseState.Crouching : PoseState.Standing); + if(m_followHips && (m_avatarHips != null)) + { + Vector4 l_hipsToPlayer = (PlayerSetup.Instance.transform.GetMatrix().inverse * m_avatarHips.GetMatrix()) * ms_pointVector; + m_hipsToPlayer.Set(l_hipsToPlayer.x, 0f, l_hipsToPlayer.z); + } + if(PlayerSetup.Instance._inVr && (m_vrIk != null) && m_vrIk.enabled) { if(m_poseState != l_poseState) @@ -196,6 +211,20 @@ void Update() } } break; + + case ParameterType.Moving: + { + switch(l_param.m_sync) + { + case ParameterSyncType.Local: + PlayerSetup.Instance._animator.SetBool(l_param.m_hash, m_moving); + break; + case ParameterSyncType.Synced: + PlayerSetup.Instance.animatorManager.SetAnimatorParameterBool(l_param.m_name, m_moving); + break; + } + } + break; } } } @@ -217,6 +246,9 @@ public void OnAvatarClear() m_locomotionOffset = Vector3.zero; m_avatarScale = 1f; m_emoteActive = false; + m_moving = false; + m_hipsToPlayer = Vector3.zero; + m_avatarHips = null; m_parameters.Clear(); } @@ -224,6 +256,7 @@ public void OnSetupAvatar() { m_vrIk = PlayerSetup.Instance._avatar.GetComponent(); m_locomotionLayer = PlayerSetup.Instance._animator.GetLayerIndex("Locomotion/Emotes"); + m_avatarHips = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.Hips); // Parse animator parameters AnimatorControllerParameter[] l_params = PlayerSetup.Instance._animator.parameters; @@ -280,6 +313,8 @@ public void OnSetupAvatar() void OnIKPreUpdate() { + bool l_overrided = false; + m_ikWeight = m_vrIk.solver.IKPositionWeight; m_locomotionWeight = m_vrIk.solver.locomotion.weight; @@ -290,14 +325,26 @@ void OnIKPreUpdate() if(PlayerSetup.Instance._inVr) { if((m_ikOverrideCrouch && (m_poseState != PoseState.Standing)) || (m_ikOverrideProne && (m_poseState == PoseState.Proning))) + { m_vrIk.solver.locomotion.weight = 0f; + l_overrided = true; + } if(m_ikOverrideFly && MovementSystem.Instance.flying) + { m_vrIk.solver.locomotion.weight = 0f; + l_overrided = true; + } } // But not this if(m_ikOverrideJump && !m_grounded && !MovementSystem.Instance.flying) + { m_vrIk.solver.locomotion.weight = 0f; + l_overrided = true; + } + + if(l_overrided && m_followHips && !m_moving) + PlayerSetup.Instance._avatar.transform.localPosition = m_hipsToPlayer; } void OnIKPostUpdate() @@ -356,5 +403,9 @@ public void SetDetectEmotes(bool p_state) { m_detectEmotes = p_state; } + public void SetFollowHips(bool p_state) + { + m_followHips = p_state; + } } } diff --git a/ml_amt/Properties/AssemblyInfo.cs b/ml_amt/Properties/AssemblyInfo.cs index d126a9e..a157c8d 100644 --- a/ml_amt/Properties/AssemblyInfo.cs +++ b/ml_amt/Properties/AssemblyInfo.cs @@ -1,10 +1,10 @@ using System.Reflection; [assembly: AssemblyTitle("AvatarMotionTweaker")] -[assembly: AssemblyVersion("1.1.7")] -[assembly: AssemblyFileVersion("1.1.7")] +[assembly: AssemblyVersion("1.1.8")] +[assembly: AssemblyFileVersion("1.1.8")] -[assembly: MelonLoader.MelonInfo(typeof(ml_amt.AvatarMotionTweaker), "AvatarMotionTweaker", "1.1.7", "SDraw", "https://github.com/SDraw/ml_mods_cvr")] +[assembly: MelonLoader.MelonInfo(typeof(ml_amt.AvatarMotionTweaker), "AvatarMotionTweaker", "1.1.8", "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_amt/README.md b/ml_amt/README.md index 63ad380..184b5c5 100644 --- a/ml_amt/README.md +++ b/ml_amt/README.md @@ -18,6 +18,7 @@ Available mod's settings in `Settings - Implementation - Avatar Motion Tweaker`: * Note: Can be overrided by avatar. For this avatar has to have child gameobject with name `ProneLimit`, its Y-axis location will be used as limit, should be in range [0.0, 1.0]. * **IK override while flying:** disables legs locomotion/autostep in fly mode; default value - `true`. * **IK override while jumping:** disables legs locomotion/autostep in jump; default value - `true`. +* **Follow hips on IK override:** adjust avatar position to overcome animation snapping on IK override; default value - `true`. * **Pose transitions:** allows regular avatars animator to transit in crouch/prone states; default value - `true`. * Note: Avatar is considered as regular if its AAS animator doesn't have `Upright` parameter. * **Adjusted pose movement speed:** scales movement speed upon crouching/proning; default value - `true`. @@ -31,6 +32,8 @@ Available additional parameters for AAS animator: * Note: Can't be used for transitions between poses in desktop mode. In desktop mode its value is driven by avatar animations. Use `CVR Parameter Stream` for detecting desktop/VR modes and change AAS animator transitions accordingly. * **`GroundedRaw`:** defines instant grounding state of player instead of delayed default parameter `Grounded`. * Note: Can be set as local-only (not synced) if starts with `#` character. +* **`Moving`:** defines movement state of player + * Note: Can be set as local-only (not synced) if starts with `#` character. Additional avatars tweaks: * If avatar has child object with name `LocomotionOffset` its local position will be used for offsetting VRIK locomotion mass center. diff --git a/ml_amt/Settings.cs b/ml_amt/Settings.cs index 210e57e..2048398 100644 --- a/ml_amt/Settings.cs +++ b/ml_amt/Settings.cs @@ -17,7 +17,8 @@ enum ModSetting AdjustedMovement, IKOverrideFly, IKOverrideJump, - DetectEmotes + DetectEmotes, + FollowHips }; static bool ms_ikOverrideCrouch = true; @@ -29,6 +30,7 @@ enum ModSetting static bool ms_ikOverrideFly = true; static bool ms_ikOverrideJump = true; static bool ms_detectEmotes = true; + static bool ms_followHips = true; static MelonLoader.MelonPreferences_Category ms_category = null; static List ms_entries = null; @@ -42,6 +44,7 @@ enum ModSetting static public event Action IKOverrideFlyChange; static public event Action IKOverrideJumpChange; static public event Action DetectEmotesChange; + static public event Action FollowHipsChange; public static void Init() { @@ -57,6 +60,7 @@ public static void Init() ms_entries.Add(ms_category.CreateEntry(ModSetting.IKOverrideFly.ToString(), true)); ms_entries.Add(ms_category.CreateEntry(ModSetting.IKOverrideJump.ToString(), true)); ms_entries.Add(ms_category.CreateEntry(ModSetting.DetectEmotes.ToString(), true)); + ms_entries.Add(ms_category.CreateEntry(ModSetting.FollowHips.ToString(), true)); Load(); @@ -96,6 +100,7 @@ static void Load() ms_ikOverrideFly = (bool)ms_entries[(int)ModSetting.IKOverrideFly].BoxedValue; ms_ikOverrideJump = (bool)ms_entries[(int)ModSetting.IKOverrideJump].BoxedValue; ms_detectEmotes = (bool)ms_entries[(int)ModSetting.DetectEmotes].BoxedValue; + ms_followHips = (bool)ms_entries[(int)ModSetting.FollowHips].BoxedValue; } static void OnSliderUpdate(string p_name, string p_value) @@ -177,6 +182,13 @@ static void OnToggleUpdate(string p_name, string p_value) DetectEmotesChange?.Invoke(ms_detectEmotes); } break; + + case ModSetting.FollowHips: + { + ms_followHips = bool.Parse(p_value); + FollowHipsChange?.Invoke(ms_followHips); + } + break; } ms_entries[(int)l_setting].BoxedValue = bool.Parse(p_value); @@ -219,5 +231,9 @@ public static bool DetectEmotes { get => ms_detectEmotes; } + public static bool FollowHips + { + get => ms_followHips; + } } } diff --git a/ml_amt/resources/menu.js b/ml_amt/resources/menu.js index c54e3c8..78c7499 100644 --- a/ml_amt/resources/menu.js +++ b/ml_amt/resources/menu.js @@ -221,6 +221,13 @@ function inp_toggle_mod_amt(_obj, _callbackName) {
+ +
+
Follow hips on IK override:
+
+
+
+
Pose transitions: