mirror of
https://github.com/NotAKidoS/NAK_CVR_Mods.git
synced 2025-09-02 14:29:25 +00:00
[AlternateIKSystem] Add BTKUI support. Fixed teleporting not resetting IK.
This commit is contained in:
parent
eaccf42d1b
commit
74803a0e09
7 changed files with 165 additions and 32 deletions
|
@ -86,12 +86,13 @@ internal class PlayerSetupPatches
|
|||
{
|
||||
try
|
||||
{
|
||||
CVRMovementParent currentParent = __instance._movementSystem._currentParent;
|
||||
if (currentParent?._referencePoint == null)
|
||||
if (IKManager.Instance == null)
|
||||
return;
|
||||
|
||||
if (IKManager.Instance != null)
|
||||
__runOriginal = !IKManager.Instance.OnPlayerHandleMovementParent(currentParent);
|
||||
CVRMovementParent currentParent = __instance._movementSystem._currentParent;
|
||||
__runOriginal = currentParent?._referencePoint != null
|
||||
? IKManager.Instance.OnPlayerHandleMovementParent(currentParent)
|
||||
: IKManager.Instance.OnPlayerTeleported();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
|
|
@ -13,7 +13,7 @@ internal static class IKCalibrator
|
|||
vrik = animator.gameObject.AddComponent<VRIK>();
|
||||
vrik.AutoDetectReferences();
|
||||
|
||||
if (!ModSettings.EntryUseVRIKToes.Value)
|
||||
if (!ModSettings.EntryUseToesForVRIK.Value)
|
||||
{
|
||||
vrik.references.leftToes = null;
|
||||
vrik.references.rightToes = null;
|
||||
|
|
|
@ -70,27 +70,16 @@ internal class IKHandlerDesktop : IKHandler
|
|||
|
||||
#region VRIK Solver Events
|
||||
|
||||
//TODO: properly expose these settings
|
||||
|
||||
private bool EntryPlantFeet = true;
|
||||
|
||||
private float EntryBodyLeanWeight = 1f;
|
||||
private bool EntryProneThrusting = true;
|
||||
|
||||
private float EntryBodyHeadingLimit = 30f;
|
||||
private float EntryPelvisHeadingWeight = 0.25f;
|
||||
private float EntryChestHeadingWeight = 0.75f;
|
||||
|
||||
private float _ikSimulatedRootAngle = 0f;
|
||||
|
||||
private void OnPreSolverUpdate()
|
||||
{
|
||||
_solver.plantFeet = EntryPlantFeet;
|
||||
_solver.plantFeet = ModSettings.EntryPlantFeet.Value;
|
||||
|
||||
// Emulate old VRChat hip movement
|
||||
if (EntryBodyLeanWeight > 0)
|
||||
if (ModSettings.EntryBodyLeanWeight.Value > 0)
|
||||
{
|
||||
float weightedAngle = EntryProneThrusting ? 1f : EntryBodyLeanWeight * _solver.locomotion.weight;
|
||||
float weightedAngle = ModSettings.EntryProneThrusting.Value ? 1f : ModSettings.EntryBodyLeanWeight.Value * _solver.locomotion.weight;
|
||||
float angle = IKManager.Instance._desktopCamera.localEulerAngles.x;
|
||||
angle = angle > 180 ? angle - 360 : angle;
|
||||
Quaternion rotation = Quaternion.AngleAxis(angle * weightedAngle, _vrik.transform.right);
|
||||
|
@ -98,9 +87,9 @@ internal class IKHandlerDesktop : IKHandler
|
|||
}
|
||||
|
||||
// Make root heading follow within a set limit
|
||||
if (EntryBodyHeadingLimit > 0)
|
||||
if (ModSettings.EntryBodyHeadingLimit.Value > 0)
|
||||
{
|
||||
float weightedAngleLimit = EntryBodyHeadingLimit * _solver.locomotion.weight;
|
||||
float weightedAngleLimit = ModSettings.EntryBodyHeadingLimit.Value * _solver.locomotion.weight;
|
||||
float deltaAngleRoot = Mathf.DeltaAngle(IKManager.Instance.transform.eulerAngles.y, _ikSimulatedRootAngle);
|
||||
float absDeltaAngleRoot = Mathf.Abs(deltaAngleRoot);
|
||||
|
||||
|
@ -109,18 +98,18 @@ internal class IKHandlerDesktop : IKHandler
|
|||
deltaAngleRoot = Mathf.Sign(deltaAngleRoot) * weightedAngleLimit;
|
||||
_ikSimulatedRootAngle = Mathf.MoveTowardsAngle(_ikSimulatedRootAngle, IKManager.Instance.transform.eulerAngles.y, absDeltaAngleRoot - weightedAngleLimit);
|
||||
}
|
||||
|
||||
|
||||
_solver.spine.rootHeadingOffset = deltaAngleRoot;
|
||||
|
||||
if (EntryPelvisHeadingWeight > 0)
|
||||
if (ModSettings.EntryPelvisHeadingWeight.Value > 0)
|
||||
{
|
||||
_solver.spine.pelvisRotationOffset *= Quaternion.Euler(0f, deltaAngleRoot * EntryPelvisHeadingWeight, 0f);
|
||||
_solver.spine.chestRotationOffset *= Quaternion.Euler(0f, -deltaAngleRoot * EntryPelvisHeadingWeight, 0f);
|
||||
_solver.spine.pelvisRotationOffset *= Quaternion.Euler(0f, deltaAngleRoot * ModSettings.EntryPelvisHeadingWeight.Value, 0f);
|
||||
_solver.spine.chestRotationOffset *= Quaternion.Euler(0f, -deltaAngleRoot * ModSettings.EntryPelvisHeadingWeight.Value, 0f);
|
||||
}
|
||||
|
||||
if (EntryChestHeadingWeight > 0)
|
||||
if (ModSettings.EntryChestHeadingWeight.Value > 0)
|
||||
{
|
||||
_solver.spine.chestRotationOffset *= Quaternion.Euler(0f, deltaAngleRoot * EntryChestHeadingWeight, 0f);
|
||||
_solver.spine.chestRotationOffset *= Quaternion.Euler(0f, deltaAngleRoot * ModSettings.EntryChestHeadingWeight.Value, 0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ public class IKManager : MonoBehaviour
|
|||
private static LookAtIK _lookAtIk;
|
||||
public static LookAtIK lookAtIk => _lookAtIk;
|
||||
|
||||
private bool _isAvatarInitialized = false;
|
||||
private bool _isAvatarInitialized;
|
||||
|
||||
// IK Handling
|
||||
private IKHandler _ikHandler;
|
||||
|
@ -160,6 +160,15 @@ public class IKManager : MonoBehaviour
|
|||
return true;
|
||||
}
|
||||
|
||||
public bool OnPlayerTeleported()
|
||||
{
|
||||
if (!_isAvatarInitialized)
|
||||
return false;
|
||||
|
||||
_vrik?.solver.Reset();
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
|
80
AlternateIKSystem/Integrations/BTKUIAddon.cs
Normal file
80
AlternateIKSystem/Integrations/BTKUIAddon.cs
Normal file
|
@ -0,0 +1,80 @@
|
|||
using BTKUILib;
|
||||
using BTKUILib.UIObjects;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace NAK.AlternateIKSystem.Integrations;
|
||||
|
||||
public static class BTKUIAddon
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static void Initialize()
|
||||
{
|
||||
// Add mod to the Misc Menu
|
||||
Page miscPage = QuickMenuAPI.MiscTabPage;
|
||||
Category miscCategory = miscPage.AddCategory(ModSettings.SettingsCategory);
|
||||
|
||||
AddMelonToggle(ref miscCategory, ModSettings.EntryEnabled);
|
||||
|
||||
SetupGeneralIKConfigurationPage(ref miscCategory);
|
||||
SetupDesktopIKConfigurationPage(ref miscCategory);
|
||||
//SetupHalfBodyIKConfigurationPage(ref miscCategory);
|
||||
}
|
||||
|
||||
private static void SetupGeneralIKConfigurationPage(ref Category parentCategory)
|
||||
{
|
||||
Page generalIKPage = parentCategory.AddPage("General IK Settings", "", "Configure the settings for general IK.", ModSettings.SettingsCategory);
|
||||
generalIKPage.MenuTitle = "General IK Settings";
|
||||
Category generalIKCategory = generalIKPage.AddCategory(generalIKPage.MenuTitle);
|
||||
|
||||
// General Settings
|
||||
AddMelonToggle(ref generalIKCategory, ModSettings.EntryPlantFeet);
|
||||
|
||||
// Calibration Settings
|
||||
AddMelonToggle(ref generalIKCategory, ModSettings.EntryUseToesForVRIK);
|
||||
|
||||
// Fine-tuning Settings
|
||||
AddMelonToggle(ref generalIKCategory, ModSettings.EntryResetFootstepsOnIdle);
|
||||
|
||||
// Lerp Speed
|
||||
AddMelonSlider(ref generalIKPage, ModSettings.EntryIKLerpSpeed, 0, 20f, 0);
|
||||
}
|
||||
|
||||
private static void SetupDesktopIKConfigurationPage(ref Category parentCategory)
|
||||
{
|
||||
Page desktopIKPage = parentCategory.AddPage("Desktop IK Settings", "", "Configure the settings for desktop IK.", ModSettings.SettingsCategory);
|
||||
desktopIKPage.MenuTitle = "Desktop IK Settings";
|
||||
Category desktopIKCategory = desktopIKPage.AddCategory(desktopIKPage.MenuTitle);
|
||||
|
||||
// Funny Settings
|
||||
AddMelonToggle(ref desktopIKCategory, ModSettings.EntryProneThrusting);
|
||||
|
||||
// Body Leaning Weight
|
||||
AddMelonSlider(ref desktopIKPage, ModSettings.EntryBodyLeanWeight, 0, 1f, 1);
|
||||
|
||||
// Max Root Heading Limit & Weights
|
||||
AddMelonSlider(ref desktopIKPage, ModSettings.EntryBodyHeadingLimit, 0, 90f, 0);
|
||||
AddMelonSlider(ref desktopIKPage, ModSettings.EntryPelvisHeadingWeight, 0, 1f, 1);
|
||||
AddMelonSlider(ref desktopIKPage, ModSettings.EntryChestHeadingWeight, 0, 1f, 1);
|
||||
}
|
||||
|
||||
private static void SetupHalfBodyIKConfigurationPage(ref Category parentCategory)
|
||||
{
|
||||
Page halfBodyIKPage = parentCategory.AddPage("HalfBody IK Settings", "", "Configure the settings for halfbody IK.", ModSettings.SettingsCategory);
|
||||
halfBodyIKPage.MenuTitle = "HalfBody IK Settings";
|
||||
Category halfBodyIKCategory = halfBodyIKPage.AddCategory(halfBodyIKPage.MenuTitle);
|
||||
}
|
||||
|
||||
#region Melon Pref Helpers
|
||||
|
||||
private static void AddMelonToggle(ref Category category, MelonLoader.MelonPreferences_Entry<bool> entry)
|
||||
{
|
||||
category.AddToggle(entry.DisplayName, entry.Description, entry.Value).OnValueUpdated += b => entry.Value = b;
|
||||
}
|
||||
|
||||
private static void AddMelonSlider(ref Page page, MelonLoader.MelonPreferences_Entry<float> entry, float min, float max, int decimalPlaces = 2)
|
||||
{
|
||||
page.AddSlider(entry.DisplayName, entry.Description, entry.Value, min, max, decimalPlaces).OnValueUpdated += f => entry.Value = f;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
|
@ -20,6 +20,17 @@ public class AlternateIKSystem : MelonMod
|
|||
|
||||
ApplyPatches(typeof(HarmonyPatches.PlayerSetupPatches));
|
||||
ApplyPatches(typeof(HarmonyPatches.IKSystemPatches));
|
||||
|
||||
InitializeIntegration("BTKUILib", Integrations.BTKUIAddon.Initialize);
|
||||
}
|
||||
|
||||
private static void InitializeIntegration(string modName, Action integrationAction)
|
||||
{
|
||||
if (RegisteredMelons.All(it => it.Info.Name != modName))
|
||||
return;
|
||||
|
||||
Logger.Msg($"Initializing {modName} integration.");
|
||||
integrationAction.Invoke();
|
||||
}
|
||||
|
||||
private void ApplyPatches(Type type)
|
||||
|
|
|
@ -9,9 +9,52 @@ public static class ModSettings
|
|||
public static readonly MelonPreferences_Category Category =
|
||||
MelonPreferences.CreateCategory(SettingsCategory);
|
||||
|
||||
public static readonly MelonPreferences_Entry<bool> EntryEnabled =
|
||||
Category.CreateEntry("Enabled", true, description: "Toggle AlternateIKSystem entirely. Requires avatar reload.");
|
||||
// Shared Settings
|
||||
|
||||
public static readonly MelonPreferences_Entry<bool> EntryUseVRIKToes =
|
||||
Category.CreateEntry("Use VRIK Toes", false, description: "Determines if VRIK uses humanoid toes for IK solving, which can cause feet to idle behind the avatar.");
|
||||
public static readonly MelonPreferences_Entry<bool> EntryEnabled =
|
||||
Category.CreateEntry("Enabled", true,
|
||||
description: "Toggle AlternateIKSystem entirely. Requires avatar reload.");
|
||||
|
||||
public static readonly MelonPreferences_Entry<bool> EntryUseToesForVRIK =
|
||||
Category.CreateEntry("Use VRIK Toes", false,
|
||||
description:
|
||||
"Determines if VRIK uses humanoid toes for IK solving, which can cause feet to idle behind the avatar.");
|
||||
|
||||
public static readonly MelonPreferences_Entry<bool> EntryPlantFeet =
|
||||
Category.CreateEntry("Enforce Plant Feet", true,
|
||||
description: "Forces VRIK Plant Feet enabled to prevent hovering when stopping movement.");
|
||||
|
||||
public static readonly MelonPreferences_Entry<bool> EntryResetFootstepsOnIdle =
|
||||
Category.CreateEntry("Reset Footsteps on Idle", false,
|
||||
description:
|
||||
"Determines if the Locomotion Footsteps will be reset to their calibration position when entering idle.");
|
||||
|
||||
public static readonly MelonPreferences_Entry<float> EntryIKLerpSpeed =
|
||||
Category.CreateEntry("IK Lerp Speed", 10f,
|
||||
description: "Determines fast the IK & Locomotion weights blend after entering idle. Set to 0 to disable.");
|
||||
|
||||
// Desktop Settings
|
||||
|
||||
public static readonly MelonPreferences_Entry<float> EntryBodyLeanWeight =
|
||||
Category.CreateEntry("Body Lean Weight", 0.5f,
|
||||
description: "Adds rotational influence to the body solver when looking up/down. Set to 0 to disable.");
|
||||
|
||||
public static readonly MelonPreferences_Entry<float> EntryBodyHeadingLimit =
|
||||
Category.CreateEntry("Body Heading Limit", 20f,
|
||||
description:
|
||||
"Specifies the maximum angle the lower body can have relative to the head when rotating. Set to 0 to disable.");
|
||||
|
||||
public static readonly MelonPreferences_Entry<float> EntryPelvisHeadingWeight =
|
||||
Category.CreateEntry("Pelvis Heading Weight", 0.25f,
|
||||
description:
|
||||
"Determines how much the pelvis will face the Body Heading Limit. Set to 0 to align with head.");
|
||||
|
||||
public static readonly MelonPreferences_Entry<float> EntryChestHeadingWeight =
|
||||
Category.CreateEntry("Chest Heading Weight", 0.75f,
|
||||
description:
|
||||
"Determines how much the chest will face the Body Heading Limit. Set to 0 to align with head.");
|
||||
|
||||
public static readonly MelonPreferences_Entry<bool> EntryProneThrusting =
|
||||
Category.CreateEntry("Prone Thrusting", false,
|
||||
description: "Allows Body Lean Weight to take effect while crouched or prone.");
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue