[AlternateIKSystem] Add BTKUI support. Fixed teleporting not resetting IK.

This commit is contained in:
NotAKidoS 2023-07-11 22:53:09 -05:00
parent eaccf42d1b
commit 74803a0e09
7 changed files with 165 additions and 32 deletions

View file

@ -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)
{

View file

@ -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;

View file

@ -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);
}
}
}

View file

@ -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

View 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
}

View file

@ -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)

View file

@ -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.");
}