diff --git a/IKFixes/ChatBoxExtensions.csproj b/IKFixes/ChatBoxExtensions.csproj new file mode 100644 index 0000000..d83ed14 --- /dev/null +++ b/IKFixes/ChatBoxExtensions.csproj @@ -0,0 +1,21 @@ + + + + + + $(NoWarn);MSB3270 + + + + + + $(MsBuildThisFileDirectory)\..\.ManagedLibs\ml_prm.dll + False + + + $(MsBuildThisFileDirectory)\..\.ManagedLibs\ChatBox.dll + False + + + + diff --git a/IKFixes/HarmonyPatches.cs b/IKFixes/HarmonyPatches.cs index 0e89332..6dc8bb7 100644 --- a/IKFixes/HarmonyPatches.cs +++ b/IKFixes/HarmonyPatches.cs @@ -2,11 +2,11 @@ using ABI_RC.Core.Player; using ABI_RC.Systems.IK; using ABI_RC.Systems.IK.SubSystems; +using ABI_RC.Systems.InputManagement; using ABI_RC.Systems.MovementSystem; using HarmonyLib; using RootMotion.FinalIK; using UnityEngine; -using UnityEngine.Events; namespace NAK.IKFixes.HarmonyPatches; @@ -22,7 +22,7 @@ internal static class BodySystemPatches { Transform parent = null; float offsetDistance = 0f; - + switch (trackingPoint.assignedRole) { case TrackingPoint.TrackingRole.LeftKnee: @@ -42,27 +42,27 @@ internal static class BodySystemPatches offsetDistance = -0.15f; break; } - - if (parent != null) - { - // Set the offset transform's parent and reset its local position and rotation - trackingPoint.offsetTransform.parent = parent; - trackingPoint.offsetTransform.localPosition = Vector3.zero; - trackingPoint.offsetTransform.localRotation = Quaternion.identity; - trackingPoint.offsetTransform.parent = trackingPoint.referenceTransform; - - // Apply additional offset based on the assigned role - Vector3 additionalOffset = IKSystem.vrik.references.root.forward * offsetDistance; - trackingPoint.offsetTransform.position += additionalOffset; - - // Game originally sets them to about half a meter out, which fucks with slime tracker users and - // makes the bendGoals less responsive/less accurate. - - //Funny thing is that IKTweaks specifically made this an option, which should be added to both CVR & Standable for the same reason. - /// Elbow / knee / chest bend goal offset - controls how far bend goal targets will be away from the actual joint. - /// Lower values should produce better precision with bent joint, higher values - better stability with straight joint. - /// Sensible range of values is between 0 and 1. - } + + if (parent == null) + continue; + + // Set the offset transform's parent and reset its local position and rotation + trackingPoint.offsetTransform.parent = parent; + trackingPoint.offsetTransform.localPosition = Vector3.zero; + trackingPoint.offsetTransform.localRotation = Quaternion.identity; + trackingPoint.offsetTransform.parent = trackingPoint.referenceTransform; + + // Apply additional offset based on the assigned role + Vector3 additionalOffset = IKSystem.vrik.references.root.forward * offsetDistance; + trackingPoint.offsetTransform.position += additionalOffset; + + // Game originally sets them to about half a meter out, which fucks with slime tracker users and + // makes the bendGoals less responsive/less accurate. + + //Funny thing is that IKTweaks specifically made this an option, which should be added to both CVR & Standable for the same reason. + /// Elbow / knee / chest bend goal offset - controls how far bend goal targets will be away from the actual joint. + /// Lower values should produce better precision with bent joint, higher values - better stability with straight joint. + /// Sensible range of values is between 0 and 1. } } @@ -73,7 +73,6 @@ internal static class BodySystemPatches arm.shoulderRotationWeight = weight; arm.shoulderTwistWeight = weight; // assumed fix of bend goal weight if arms disabled with elbows (havent tested) - // why is there no "usingElbowTracker" flag like knees? where is the consistancy??? arm.bendGoalWeight = arm.bendGoal != null ? weight : 0f; } @@ -130,25 +129,33 @@ internal static class BodySystemPatches float maxRootAngle = 25f; float rootHeadingOffset = 0f; - if (BodySystem.isCalibratedAsFullBody || IKFixes.EntryUseFakeRootAngle.Value) + if (BodySystem.isCalibratedAsFullBody + || IKFixes.EntryUseFakeRootAngle.Value + || CVRInputManager.Instance.movementVector.sqrMagnitude > 0f) maxRootAngle = 0f; + // fixes body being wrong direction while playing emotes (root rotation) if (PlayerSetup.Instance._emotePlaying) maxRootAngle = 180f; - + + // fixes feet always pointing toward head direction if (IKFixes.EntryUseFakeRootAngle.Value && !BodySystem.isCalibratedAsFullBody) { float weightedAngleLimit = IKFixes.EntryFakeRootAngleLimit.Value * solver.locomotion.weight; - float pivotAngle = MovementSystem.Instance.rotationPivot.eulerAngles.y; - float deltaAngleRoot = Mathf.DeltaAngle(pivotAngle, _ikSimulatedRootAngle); - float absDeltaAngleRoot = Mathf.Abs(deltaAngleRoot); - - deltaAngleRoot = Mathf.Clamp(deltaAngleRoot, -weightedAngleLimit, weightedAngleLimit); - _ikSimulatedRootAngle = Mathf.MoveTowardsAngle(_ikSimulatedRootAngle, pivotAngle, absDeltaAngleRoot - weightedAngleLimit); + float playerDirection = MovementSystem.Instance.rotationPivot.eulerAngles.y; + + float deltaAngleRoot = Mathf.DeltaAngle(playerDirection, _ikSimulatedRootAngle); + float angleOverLimit = Mathf.Abs(deltaAngleRoot) - weightedAngleLimit; + if (angleOverLimit > 0) + { + deltaAngleRoot = Mathf.Sign(deltaAngleRoot) * weightedAngleLimit; + _ikSimulatedRootAngle = Mathf.MoveTowardsAngle(_ikSimulatedRootAngle, playerDirection, angleOverLimit); + } + rootHeadingOffset = deltaAngleRoot; } - + solver.spine.maxRootAngle = maxRootAngle; solver.spine.rootHeadingOffset = rootHeadingOffset; @@ -207,7 +214,7 @@ internal static class BodySystemPatches if (BodySystem.isCalibratedAsFullBody && BodySystem.TrackingPositionWeight > 0f) { - bool isRunning = MovementSystem.Instance.movementVector.magnitude > 0f; + bool isRunning = MovementSystem.Instance.movementVector.sqrMagnitude > 0f; bool isGrounded = MovementSystem.Instance._isGrounded; bool isFlying = MovementSystem.Instance.flying; bool playRunningAnimation = BodySystem.PlayRunningAnimationInFullBody; @@ -257,128 +264,6 @@ internal static class IKSystemPatches __instance.applyOriginalHipPosition = true; __instance.applyOriginalHipRotation = true; } - - [HarmonyPostfix] - [HarmonyPatch(typeof(IKSystem), nameof(IKSystem.InitializeHalfBodyIK))] - private static void Postfix_IKSystem_InitializeHalfBodyIK(ref IKSystem __instance) - { - if (!IKFixes.EntryUseIKPose.Value) return; - - __instance._poseHandler.GetHumanPose(ref __instance.humanPose); - - for (int i = 0; i < IKPoseMuscles.Length; i++) - { - __instance.ApplyMuscleValue((MuscleIndex)i, IKPoseMuscles[i], ref __instance.humanPose.muscles); - } - __instance.humanPose.bodyPosition = Vector3.up; - __instance.humanPose.bodyRotation = Quaternion.identity; - __instance._poseHandler.SetHumanPose(ref __instance.humanPose); - - // recentering avatar so it doesnt need to step from random place on switch - IKSystem.vrik.transform.localPosition = Vector3.zero; - IKSystem.vrik.transform.localRotation = Quaternion.identity; - // janky fix, initializing early with correct pose - IKSystem.vrik.solver.Initiate(IKSystem.vrik.transform); - } - - private static readonly float[] IKPoseMuscles = new float[] - { - 0.00133321f, - 8.195831E-06f, - 8.537738E-07f, - -0.002669832f, - -7.651234E-06f, - -0.001659694f, - 0f, - 0f, - 0f, - 0.04213953f, - 0.0003007996f, - -0.008032114f, - -0.03059979f, - -0.0003182998f, - 0.009640567f, - 0f, - 0f, - 0f, - 0f, - 0f, - 0f, - 0.5768794f, - 0.01061097f, - -0.1127839f, - 0.9705755f, - 0.07972051f, - -0.0268422f, - 0.007237188f, - 0f, - 0.5768792f, - 0.01056608f, - -0.1127519f, - 0.9705756f, - 0.07971933f, - -0.02682396f, - 0.007229362f, - 0f, - -5.651802E-06f, - -3.034899E-07f, - 0.4100508f, - 0.3610304f, - -0.0838329f, - 0.9262537f, - 0.1353517f, - -0.03578902f, - 0.06005657f, - -4.95989E-06f, - -1.43007E-06f, - 0.4096187f, - 0.363263f, - -0.08205152f, - 0.9250782f, - 0.1345718f, - -0.03572125f, - 0.06055461f, - -1.079177f, - 0.2095419f, - 0.6140652f, - 0.6365265f, - 0.6683931f, - -0.4764312f, - 0.8099416f, - 0.8099371f, - 0.6658203f, - -0.7327053f, - 0.8113618f, - 0.8114051f, - 0.6643661f, - -0.40341f, - 0.8111364f, - 0.8111367f, - 0.6170399f, - -0.2524227f, - 0.8138723f, - 0.8110135f, - -1.079171f, - 0.2095456f, - 0.6140658f, - 0.6365255f, - 0.6683878f, - -0.4764301f, - 0.8099402f, - 0.8099376f, - 0.6658241f, - -0.7327023f, - 0.8113653f, - 0.8113793f, - 0.664364f, - -0.4034042f, - 0.811136f, - 0.8111364f, - 0.6170469f, - -0.2524345f, - 0.8138595f, - 0.8110138f - }; } internal static class PlayerSetupPatches @@ -392,34 +277,30 @@ internal static class PlayerSetupPatches [HarmonyPatch(typeof(PlayerSetup), nameof(PlayerSetup.ResetIk))] private static bool Prefix_PlayerSetup_ResetIk() { - if (IKSystem.vrik == null) return false; + if (IKSystem.vrik == null) + return false; CVRMovementParent currentParent = MovementSystem.Instance._currentParent; - if (currentParent != null && currentParent._referencePoint != null) - { - // Get current position, VR pivots around VR camera - Vector3 currentPosition = MovementSystem.Instance.rotationPivot.transform.position; - currentPosition.y = IKSystem.vrik.transform.position.y; // set pivot to floor - Quaternion currentRotation = Quaternion.Euler(0f, currentParent.transform.rotation.eulerAngles.y, 0f); + if (currentParent == null || currentParent._referencePoint == null) + return true; + + // Get current position, VR pivots around VR camera + Vector3 currentPosition = MovementSystem.Instance.rotationPivot.transform.position; + currentPosition.y = IKSystem.vrik.transform.position.y; // set pivot to floor + Quaternion currentRotation = Quaternion.Euler(0f, currentParent.transform.rotation.eulerAngles.y, 0f); - // Convert to delta position (how much changed since last frame) - Vector3 deltaPosition = currentPosition - lastMovementPosition; - Quaternion deltaRotation = Quaternion.Inverse(lastMovementRotation) * currentRotation; + // Convert to delta position (how much changed since last frame) + Vector3 deltaPosition = currentPosition - lastMovementPosition; + Quaternion deltaRotation = Quaternion.Inverse(lastMovementRotation) * currentRotation; - // Prevent targeting other parent position - if (lastMovementParent == currentParent || lastMovementParent == null) - { - // Add platform motion to IK solver - IKSystem.vrik.solver.AddPlatformMotion(deltaPosition, deltaRotation, currentPosition); - } - - // Store for next frame - lastMovementParent = currentParent; - lastMovementPosition = currentPosition; - lastMovementRotation = currentRotation; - return false; - } - - return true; + // Prevent targeting previous movement parent + if (lastMovementParent == currentParent || lastMovementParent == null) + IKSystem.vrik.solver.AddPlatformMotion(deltaPosition, deltaRotation, currentPosition); + + lastMovementParent = currentParent; + lastMovementPosition = currentPosition; + lastMovementRotation = currentRotation; + + return false; } } \ No newline at end of file diff --git a/IKFixes/IKFixes.csproj b/IKFixes/IKFixes.csproj index 66a50a8..ddc9306 100644 --- a/IKFixes/IKFixes.csproj +++ b/IKFixes/IKFixes.csproj @@ -1,2 +1,11 @@ - + + + + + $(MsBuildThisFileDirectory)\..\.ManagedLibs\UIExpansionKit.dll + False + + + + diff --git a/IKFixes/Integrations/UIExKitAddon.cs b/IKFixes/Integrations/UIExKitAddon.cs new file mode 100644 index 0000000..bf8c4a7 --- /dev/null +++ b/IKFixes/Integrations/UIExKitAddon.cs @@ -0,0 +1,21 @@ +using System.Runtime.CompilerServices; +using MelonLoader; +using UIExpansionKit.API; + +namespace NAK.IKFixes.Integrations; + +public static class UIExKitAddon +{ + [MethodImpl(MethodImplOptions.NoInlining)] + public static void Initialize() + { + var settings = ExpansionKitApi.GetSettingsCategory(IKFixes.SettingsCategory); + settings.AddSimpleButton("Reset Settings (Only visually updates bool values, UIExpansionKit bug!)", ResetSettings); + } + + private static void ResetSettings() + { + foreach (MelonPreferences_Entry setting in IKFixes.Category.Entries) + setting.ResetToDefault(); + } +} \ No newline at end of file diff --git a/IKFixes/Main.cs b/IKFixes/Main.cs index a4d7234..ac2a1df 100644 --- a/IKFixes/Main.cs +++ b/IKFixes/Main.cs @@ -4,41 +4,47 @@ namespace NAK.IKFixes; public class IKFixes : MelonMod { + internal const string SettingsCategory = nameof(IKFixes); + public static readonly MelonPreferences_Category Category = - MelonPreferences.CreateCategory(nameof(IKFixes)); + MelonPreferences.CreateCategory(SettingsCategory); public static readonly MelonPreferences_Entry EntryUseFakeRootAngle = Category.CreateEntry("Use Fake Root Angle", true, description: "Emulates maxRootAngle. This fixes feet pointing in direction of head when looking around."); public static readonly MelonPreferences_Entry EntryFakeRootAngleLimit = - Category.CreateEntry("Fake Root Angle Limit", 25f, description: "Specifies the maximum angle the lower body can have relative to the head when rotating."); + Category.CreateEntry("Fake Root Angle Limit (25f)", 25f, description: "Specifies the maximum angle the lower body can have relative to the head when rotating."); public static readonly MelonPreferences_Entry EntryNeckStiffness = - Category.CreateEntry("Neck Stiffness", 0.2f, description: "Neck stiffness."); + Category.CreateEntry("Neck Stiffness (0.2f)", 0.2f, description: "Neck stiffness."); public static readonly MelonPreferences_Entry EntryBodyRotStiffness = - Category.CreateEntry("Body Rot Stiffness", 0.1f, description: "Body rotation stiffness."); + Category.CreateEntry("Body Rot Stiffness (0.1f)", 0.1f, description: "Body rotation stiffness."); public static readonly MelonPreferences_Entry EntryRotateChestByHands = - Category.CreateEntry("Rot Chest By Hands", 1f, description: "Rotate chest by hands."); + Category.CreateEntry("Rot Chest By Hands (1f)", 1f, description: "Rotate chest by hands."); public static readonly MelonPreferences_Entry EntryBendToTargetWeight = - Category.CreateEntry("Leg Bend To Target", 1f, description: "Leg bend to target weight"); + Category.CreateEntry("Leg Bend To Target (1f)", 1f, description: "Leg bend to target weight"); public static readonly MelonPreferences_Entry EntryAssignRemainingTrackers = - Category.CreateEntry("Assign Remaining Trackers", true, description: "Should the game calibrate any additional trackers as secondary trackers for already-tracked points?"); - - public static readonly MelonPreferences_Entry EntryUseIKPose = - Category.CreateEntry("Use IK Pose", true, description: "Should an IKPose be used after VRIK initialization? This can fix some issues with feet targeting."); - - public static readonly MelonPreferences_Entry EntryNetIKPass = - Category.CreateEntry("Network IK Pass", true, description: "Should NetIK pass be applied? This fixes a bunch of small rotation errors after VRIK is run."); - + Category.CreateEntry("Assign Remaining Trackers (true)", true, description: "Should the game calibrate any additional trackers as secondary trackers for already-tracked points?"); + public override void OnInitializeMelon() { ApplyPatches(typeof(HarmonyPatches.BodySystemPatches)); ApplyPatches(typeof(HarmonyPatches.PlayerSetupPatches)); ApplyPatches(typeof(HarmonyPatches.IKSystemPatches)); + InitializeIntegration("UI Expansion Kit", Integrations.UIExKitAddon.Initialize); + } + + private void InitializeIntegration(string modName, Action integrationAction) + { + if (RegisteredMelons.All(it => it.Info.Name != modName)) + return; + + LoggerInstance.Msg($"Initializing {modName} integration."); + integrationAction.Invoke(); } private void ApplyPatches(Type type) diff --git a/IKFixes/Properties/AssemblyInfo.cs b/IKFixes/Properties/AssemblyInfo.cs index 251d81f..ca5b645 100644 --- a/IKFixes/Properties/AssemblyInfo.cs +++ b/IKFixes/Properties/AssemblyInfo.cs @@ -25,6 +25,6 @@ using System.Reflection; namespace NAK.IKFixes.Properties; internal static class AssemblyInfoParams { - public const string Version = "1.0.6"; + public const string Version = "1.0.7"; public const string Author = "NotAKidoS"; } \ No newline at end of file diff --git a/IKFixes/format.json b/IKFixes/format.json index 5945c31..9db2229 100644 --- a/IKFixes/format.json +++ b/IKFixes/format.json @@ -1,23 +1,24 @@ { "_id": 142, "name": "IKFixes", - "modversion": "1.0.6", - "gameversion": "2022r170p1", + "modversion": "1.0.7", + "gameversion": "2023r171", "loaderversion": "0.6.1", "modtype": "Mod", "author": "NotAKidoS", - "description": "A few small fixes and configuration options to IK. Major ones are listed below:\n\n**FBT** - Fixes knee tracking, running animation direction, and animation bleeding into IK while calibrating/running. (no more chest tracker wiggle)\n\n**Halfbody** - Fixes footsteps while on a MovementParent, uses root angle offset to prevent feet from only pointing in head direction, and uses an IKPose to fix feet pointing upward.", + "description": "A few small fixes and configuration options to IK. Major ones are listed below:\n\n**FBT** - Fixes root rotation animation bleeding, chest tracking, locomotion animations, and bend goal offsets.\n\n**Halfbody** - Fixes footsteps while on a Movement Parent & feet always pointing in head direction.", "searchtags": [ "knee", "ik", "tracking", - "fix" + "fix", + "chest" ], "requirements": [ "None" ], "downloadlink": "https://github.com/NotAKidOnSteam/NAK_CVR_Mods/releases/download/r8/IKFixes.dll", "sourcelink": "https://github.com/NotAKidOnSteam/NAK_CVR_Mods/tree/main/IKFixes/", - "changelog": "- Added fix for animations affecting root to bleed into IK.\n- Use an IKPose after HalfbodyIK Initialization.\n- Fixed patch to leg solver that prevented lower leg rotation while using knee trackers.\n- Adjusted elbow target offsets for a balance between accuracy and stability. Makes StandableFBE-tracked elbows feel much better to use.\n- Added option to disable AssignRemainingTrackers feature.\n- Exposed a couple VRIK solver settings for more fine-tuning.", + "changelog": "- Updates for 2023r171\n- Removed unneeded settings (IKPose & NetIKPass).\n- Fixed halfbody fake root angle option.\n- Added Reset Settings UIExpansionKit button.", "embedcolor": "f46e49" } \ No newline at end of file