From 6b674712175a407105b69e766c831a42d07de6db Mon Sep 17 00:00:00 2001 From: SDraw Date: Thu, 15 Sep 2022 00:14:12 +0300 Subject: [PATCH] Pose detection and animator transitions for regular avatars --- README.md | 2 +- ml_amt/Main.cs | 14 +++++ ml_amt/MotionTweaker.cs | 90 ++++++++++++++++++++++++------- ml_amt/Properties/AssemblyInfo.cs | 6 +-- ml_amt/README.md | 20 ++++--- ml_amt/Settings.cs | 40 ++++++++++++-- ml_amt/resources/menu.js | 18 ++++++- 7 files changed, 152 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index 099603a..9cdb578 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.2 | Yes | Working | -| Avatar Motion Tweaker | ml_amt | 1.0.7 | On review | Working | +| Avatar Motion Tweaker | ml_amt | 1.0.8 | On review | Working | | Desktop Reticle Switch | ml_drs | 1.0.0 | Yes | Working | | Four Point Tracking | ml_fpt | 1.0.4 | Yes | Working | | Leap Motion Extension | ml_lme | 1.1.8 | Yes | Working | diff --git a/ml_amt/Main.cs b/ml_amt/Main.cs index b700cf4..011a084 100644 --- a/ml_amt/Main.cs +++ b/ml_amt/Main.cs @@ -15,6 +15,8 @@ namespace ml_amt Settings.Init(); Settings.IKOverrideChange += this.OnIKOverrideChange; Settings.CrouchLimitChange += this.OnCrouchLimitChange; + Settings.DetectPoseChange += this.OnDetectPoseChange; + Settings.ProneLimitChange += this.OnProneLimitChange; HarmonyInstance.Patch( typeof(PlayerSetup).GetMethod(nameof(PlayerSetup.ClearAvatar)), @@ -38,6 +40,8 @@ namespace ml_amt m_localTweaker = PlayerSetup.Instance.gameObject.AddComponent(); m_localTweaker.SetIKOverride(Settings.IKOverride); m_localTweaker.SetCrouchLimit(Settings.CrouchLimit); + m_localTweaker.SetDetectPose(Settings.DetectPose); + m_localTweaker.SetProneLimit(Settings.ProneLimit); } @@ -51,6 +55,16 @@ namespace ml_amt if(m_localTweaker != null) m_localTweaker.SetCrouchLimit(p_value); } + void OnDetectPoseChange(bool p_state) + { + if(m_localTweaker != null) + m_localTweaker.SetDetectPose(p_state); + } + void OnProneLimitChange(float p_value) + { + if(m_localTweaker != null) + m_localTweaker.SetProneLimit(p_value); + } static void OnAvatarClear_Postfix() => ms_instance?.OnAvatarClear(); void OnAvatarClear() diff --git a/ml_amt/MotionTweaker.cs b/ml_amt/MotionTweaker.cs index 3489224..8f1dc33 100644 --- a/ml_amt/MotionTweaker.cs +++ b/ml_amt/MotionTweaker.cs @@ -40,6 +40,7 @@ namespace ml_amt float m_locomotionWeight = 1f; // Original weight bool m_avatarReady = false; + bool m_compatibleAvatar = false; bool m_ikOverride = true; float m_currentUpright = 1f; @@ -47,7 +48,10 @@ namespace ml_amt float m_crouchLimit = 0.65f; bool m_customCrouchLimit = false; - float m_proneLimit = 0.3f; // Unused + + bool m_detectPose = true; + float m_proneLimit = 0.3f; + bool m_customProneLimit = false; bool m_customLocomotionOffset = false; Vector3 m_locomotionOffset = Vector3.zero; @@ -70,10 +74,39 @@ namespace ml_amt m_currentUpright = Mathf.Clamp((((l_currentHeight > 0f) && (l_avatarViewHeight > 0f)) ? (l_currentHeight / l_avatarViewHeight) : 0f), 0f, 1f); PoseState l_poseState = (m_currentUpright <= m_proneLimit) ? PoseState.Proning : ((m_currentUpright <= m_crouchLimit) ? PoseState.Crouching : PoseState.Standing); - if(m_ikOverride && (m_vrIk != null) && m_vrIk.enabled) + if((m_vrIk != null) && m_vrIk.enabled) { - if((m_poseState != l_poseState) && (l_poseState == PoseState.Standing)) + if(m_ikOverride && (m_poseState != l_poseState) && (l_poseState == PoseState.Standing)) ms_rootVelocity.SetValue(m_vrIk.solver, Vector3.zero); + + if(m_detectPose && !m_compatibleAvatar && !PlayerSetup.Instance.fullBodyActive) + { + switch(l_poseState) + { + case PoseState.Standing: + { + PlayerSetup.Instance._movementSystem.ChangeCrouch(false); + PlayerSetup.Instance._movementSystem.ChangeProne(false); + PlayerSetup.Instance.animatorManager.SetAnimatorParameterBool("Crouching", false); // Forced to stop transitioning to standing locomotion + PlayerSetup.Instance.animatorManager.SetAnimatorParameterBool("Prone", false); // Forced to stop transitioning to standing locomotion + } + break; + + case PoseState.Crouching: + PlayerSetup.Instance._movementSystem.ChangeCrouch(true); + PlayerSetup.Instance.animatorManager.SetAnimatorParameterBool("Crouching", true); + PlayerSetup.Instance.animatorManager.SetAnimatorParameterBool("Prone", false); + break; + + case PoseState.Proning: + { + PlayerSetup.Instance._movementSystem.ChangeProne(true); + PlayerSetup.Instance.animatorManager.SetAnimatorParameterBool("Crouching", false); + PlayerSetup.Instance.animatorManager.SetAnimatorParameterBool("Prone", true); + } + break; + } + } } m_poseState = l_poseState; @@ -106,10 +139,12 @@ namespace ml_amt public void OnAvatarClear() { m_avatarReady = false; + m_compatibleAvatar = false; m_vrIk = null; m_poseState = PoseState.Standing; m_parameters.Clear(); m_customCrouchLimit = false; + m_customProneLimit = false; m_customLocomotionOffset = false; m_locomotionOffset = Vector3.zero; } @@ -143,10 +178,16 @@ namespace ml_amt } } + m_compatibleAvatar = m_parameters.Exists(p => p.m_name.Contains("Upright")); + Transform l_customTransform = PlayerSetup.Instance._avatar.transform.Find("CrouchLimit"); m_customCrouchLimit = (l_customTransform != null); m_crouchLimit = m_customCrouchLimit ? Mathf.Clamp(l_customTransform.localPosition.y, 0f, 1f) : Settings.CrouchLimit; + l_customTransform = PlayerSetup.Instance._avatar.transform.Find("ProneLimit"); + m_customProneLimit = (l_customTransform != null); + m_proneLimit = m_customProneLimit ? Mathf.Clamp(l_customTransform.localPosition.y, 0f, 1f) : Settings.ProneLimit; + l_customTransform = PlayerSetup.Instance._avatar.transform.Find("LocomotionOffset"); m_customLocomotionOffset = (l_customTransform != null); m_locomotionOffset = m_customLocomotionOffset ? l_customTransform.localPosition : Vector3.zero; @@ -164,23 +205,6 @@ namespace ml_amt m_avatarReady = true; } - - public void SetIKOverride(bool p_state) - { - m_ikOverride = p_state; - } - - public void SetCrouchLimit(float p_value) - { - if(!m_customCrouchLimit) - m_crouchLimit = Mathf.Clamp(p_value, 0f, 1f); - } - - public void SetProneLimit(float p_value) - { - m_proneLimit = Mathf.Clamp(p_value, 0f, 1f); - } - void OnIKPreUpdate() { if(m_ikOverride) @@ -196,5 +220,31 @@ namespace ml_amt if(m_ikOverride) m_vrIk.solver.locomotion.weight = m_locomotionWeight; } + + public void SetIKOverride(bool p_state) + { + m_ikOverride = p_state; + } + public void SetCrouchLimit(float p_value) + { + if(!m_customCrouchLimit) + m_crouchLimit = Mathf.Clamp(p_value, 0f, 1f); + } + public void SetDetectPose(bool p_state) + { + m_detectPose = p_state; + + if(!m_detectPose && m_avatarReady && !m_compatibleAvatar && PlayerSetup.Instance._inVr) + { + PlayerSetup.Instance._movementSystem.ChangeCrouch(false); + PlayerSetup.Instance.animatorManager.SetAnimatorParameterBool("Crouching", false); + PlayerSetup.Instance._movementSystem.ChangeProne(false); + PlayerSetup.Instance.animatorManager.SetAnimatorParameterBool("Prone", false); + } + } + public void SetProneLimit(float p_value) + { + m_proneLimit = Mathf.Clamp(p_value, 0f, 1f); + } } } diff --git a/ml_amt/Properties/AssemblyInfo.cs b/ml_amt/Properties/AssemblyInfo.cs index f986b17..13ce87b 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.0.7")] -[assembly: AssemblyFileVersion("1.0.7")] +[assembly: AssemblyVersion("1.0.8")] +[assembly: AssemblyFileVersion("1.0.8")] -[assembly: MelonLoader.MelonInfo(typeof(ml_amt.AvatarMotionTweaker), "AvatarMotionTweaker", "1.0.7", "SDraw", "https://github.com/SDraw/ml_mods_cvr")] +[assembly: MelonLoader.MelonInfo(typeof(ml_amt.AvatarMotionTweaker), "AvatarMotionTweaker", "1.0.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 e9e2b45..b1d6524 100644 --- a/ml_amt/README.md +++ b/ml_amt/README.md @@ -1,5 +1,5 @@ # Avatar Motion Tweaker -This mod adds `Upright` parameter for usage in AAS animator and allows disabling legs autostep upon reaching specific `Upright` value. +This mod adds features for AAS animator and avatar locomotion behaviour. ![](.github/img_01.png) @@ -10,17 +10,24 @@ This mod adds `Upright` parameter for usage in AAS animator and allows disabling # Usage Available mod's settings in `Settings - Implementation - Avatar Motion Tweaker`: -* **Legs locomotion upright limit:** defines upright limit of legs autostep. If HMD tracking goes below set limit, legs autostep is disabled. Default value - 65. - * Limit can be overrided by avatar. For this avatar has to have child gameobject with name `CrouchLimit` and its Y-axis location will be used as limit, should be in range [0.0, 1.0]. +* **IK locomotion override:** disables legs locomotion/autostep upon HMD reaching height of `CrouchLimit`; default value - `true`. +* **Crouch limit:** defines first limit; default value - `65`. + * Note: Can be overrided by avatar. For this avatar has to have child gameobject with name `CrouchLimit`, its Y-axis location will be used as limit, should be in range [0.0, 1.0]. +* **Detect pose (regular avatars):** forces regular avatars' animations to transit to crouching/proning animation states; default value - `true`. + * Note: Avatar is considered as regular if its animator doesn't have `Upright` parameter. +* **Prone limit (regular avatars):** defines second limit; default value - `30`. + * 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]. + * Note: Has no effect for mod compatible avatars. Available additional parameters for AAS animator: * **`Upright`:** defines linear coefficient between current viewpoint height and avatar's viewpoint height. Range - [0.0,1.0] (0.0 - floor, 1.0 - full standing). - * Note: can be set as local-only (not synced) if starts with `#` character. + * Note: Can be set as local-only (not synced) if starts with `#` character. + * Note: Defining this parameter in AAS animator will consider avatar as compatible with mod. Additional avatars tweaks: * If avatar has child object with name `LocomotionOffset` its local position will be used for offsetting VRIK locomotion center. - -## Example of usage in AAS animator for mixed desktop and VR + +## Advanced usage in AAS animator for mixed desktop and VR * To differentiate between desktop and VR players use `CVR Parameter Stream` component on avatar's root gameobject. As example, `InVR` and `InFBT` are boolean typed animator parameters: ![](.github/img_02.png) * Add additional transitions between standing, crouching and proning blend trees: @@ -38,5 +45,4 @@ Additional avatars tweaks: ![](.github/img_08.png) # Notes -* Sometimes after restoring legs autostep avatar's torso shakes, currently investigating solution. * Usage of `Upright` parameter for transition between poses (standing/crouching/proning) in desktop mode is useless, because in this case your animations are updating value of `Upright` parameter, not the other way around. diff --git a/ml_amt/Settings.cs b/ml_amt/Settings.cs index c3435d5..1790502 100644 --- a/ml_amt/Settings.cs +++ b/ml_amt/Settings.cs @@ -10,17 +10,23 @@ namespace ml_amt enum ModSetting { IKOverride = 0, - CrouchLimit + CrouchLimit, + DetectPose, + ProneLimit }; static bool ms_ikOverride = true; static float ms_crouchLimit = 0.65f; + static bool ms_detectPose = true; + static float ms_proneLimit = 0.3f; static MelonLoader.MelonPreferences_Category ms_category = null; static List ms_entries = null; static public event Action IKOverrideChange; static public event Action CrouchLimitChange; + static public event Action DetectPoseChange; + static public event Action ProneLimitChange; public static void Init() { @@ -29,6 +35,8 @@ namespace ml_amt ms_entries = new List(); ms_entries.Add(ms_category.CreateEntry(ModSetting.IKOverride.ToString(), true)); ms_entries.Add(ms_category.CreateEntry(ModSetting.CrouchLimit.ToString(), 65)); + ms_entries.Add(ms_category.CreateEntry(ModSetting.DetectPose.ToString(), true)); + ms_entries.Add(ms_category.CreateEntry(ModSetting.ProneLimit.ToString(), 30)); Load(); @@ -61,6 +69,8 @@ namespace ml_amt { ms_ikOverride = (bool)ms_entries[(int)ModSetting.IKOverride].BoxedValue; ms_crouchLimit = ((int)ms_entries[(int)ModSetting.CrouchLimit].BoxedValue) * 0.01f; + ms_detectPose = (bool)ms_entries[(int)ModSetting.DetectPose].BoxedValue; + ms_proneLimit = ((int)ms_entries[(int)ModSetting.ProneLimit].BoxedValue) * 0.01f; } static void OnSliderUpdate(string p_name, string p_value) @@ -73,8 +83,13 @@ namespace ml_amt { ms_crouchLimit = int.Parse(p_value) * 0.01f; CrouchLimitChange?.Invoke(ms_crouchLimit); - } - break; + } break; + + case ModSetting.ProneLimit: + { + ms_proneLimit = int.Parse(p_value) * 0.01f; + ProneLimitChange?.Invoke(ms_proneLimit); + } break; } ms_entries[(int)l_setting].BoxedValue = int.Parse(p_value); @@ -91,8 +106,13 @@ namespace ml_amt { ms_ikOverride = bool.Parse(p_value); IKOverrideChange?.Invoke(ms_ikOverride); - } - break; + } break; + + case ModSetting.DetectPose: + { + ms_detectPose = bool.Parse(p_value); + DetectPoseChange?.Invoke(ms_detectPose); + } break; } ms_entries[(int)l_setting].BoxedValue = bool.Parse(p_value); @@ -108,5 +128,15 @@ namespace ml_amt { get => ms_ikOverride; } + + public static bool DetectPose + { + get => ms_detectPose; + } + + public static float ProneLimit + { + get => ms_proneLimit; + } } } diff --git a/ml_amt/resources/menu.js b/ml_amt/resources/menu.js index a01f83b..a541b22 100644 --- a/ml_amt/resources/menu.js +++ b/ml_amt/resources/menu.js @@ -181,18 +181,32 @@ function inp_toggle_mod_amt(_obj, _callbackName) {
-
IK override:
+
IK locomotion override:
-
Legs locomotion upright limit:
+
Crouch limit:
+ +
+
Detect pose (regular avatars):
+
+
+
+
+ +
+
Prone limit (regular avatars):
+
+
+
+
`; document.getElementById('settings-implementation').appendChild(l_block);