Merge remote-tracking branch 'IKFixes/main'

Merge IKFixes
This commit is contained in:
NotAKidoS 2023-04-16 06:53:36 -05:00
commit 0646498ae3
7 changed files with 453 additions and 0 deletions

243
IKFixes/HarmonyPatches.cs Normal file
View file

@ -0,0 +1,243 @@
using ABI.CCK.Components;
using ABI_RC.Core.Player;
using ABI_RC.Systems.IK;
using ABI_RC.Systems.IK.SubSystems;
using ABI_RC.Systems.MovementSystem;
using HarmonyLib;
using RootMotion.FinalIK;
using UnityEngine;
namespace NAK.Melons.IKFixes.HarmonyPatches;
internal static class BodySystemPatches
{
static float _ikSimulatedRootAngle = 0f;
[HarmonyPostfix]
[HarmonyPatch(typeof(BodySystem), "SetupOffsets")]
private static void Postfix_BodySystem_SetupOffsets(List<TrackingPoint> trackingPoints)
{
//redo offsets for knees as native is too far from pivot
foreach (TrackingPoint trackingPoint in trackingPoints)
{
Transform parent = null;
if (trackingPoint.assignedRole == TrackingPoint.TrackingRole.LeftKnee)
{
parent = IKSystem.vrik.references.leftCalf;
}
else if (trackingPoint.assignedRole == TrackingPoint.TrackingRole.RightKnee)
{
parent = IKSystem.vrik.references.rightCalf;
}
if (parent != null)
{
trackingPoint.offsetTransform.parent = parent;
trackingPoint.offsetTransform.localPosition = Vector3.zero;
trackingPoint.offsetTransform.localRotation = Quaternion.identity;
trackingPoint.offsetTransform.parent = trackingPoint.referenceTransform;
// small amount forward, as pivot is different for users who place
// tracker on upper/lower leg. 0.5f was too much for users using upper leg.
Vector3 b = IKSystem.vrik.references.root.forward * 0.1f;
trackingPoint.offsetTransform.position += b;
}
}
}
[HarmonyPrefix]
[HarmonyPatch(typeof(BodySystem), "Update")]
private static bool Prefix_BodySystem_Update(ref BodySystem __instance)
{
if (IKSystem.vrik != null)
{
IKSolverVR solver = IKSystem.vrik.solver;
if (BodySystem.TrackingEnabled)
{
IKSystem.vrik.enabled = true;
solver.IKPositionWeight = BodySystem.TrackingPositionWeight;
solver.locomotion.weight = BodySystem.TrackingLocomotionEnabled ? 1f : 0f;
// fixes arm weights not being set if leftArm & rightArm targets are null
// game handles TrackingLegs in PlayerSetup, but not for knee goals
SetArmWeight(solver.leftArm, BodySystem.TrackingLeftArmEnabled && solver.leftArm.target != null ? 1f : 0f);
SetArmWeight(solver.rightArm, BodySystem.TrackingRightArmEnabled && solver.rightArm.target != null ? 1f : 0f);
SetLegWeight(solver.leftLeg, BodySystem.TrackingLeftLegEnabled && solver.leftLeg.target != null ? 1f : 0f);
SetLegWeight(solver.rightLeg, BodySystem.TrackingRightLegEnabled && solver.leftLeg.target != null ? 1f : 0f);
SetPelvisWeight(solver.spine, solver.spine.pelvisTarget != null ? 1f : 0f);
// makes running animation look better
if (BodySystem.isCalibratedAsFullBody)
{
bool isRunning = BodySystem.PlayRunningAnimationInFullBody && MovementSystem.Instance.movementVector.magnitude > 0f;
if (isRunning) SetPelvisWeight(solver.spine, 0f);
}
}
else
{
IKSystem.vrik.enabled = false;
solver.IKPositionWeight = 0f;
solver.locomotion.weight = 0f;
SetArmWeight(solver.leftArm, 0f);
SetArmWeight(solver.rightArm, 0f);
SetLegWeight(solver.leftLeg, 0f);
SetLegWeight(solver.rightLeg, 0f);
SetPelvisWeight(solver.spine, 0f);
}
if (IKFixesMod.EntryUseFakeRootAngle.Value && !BodySystem.isCalibratedAsFullBody)
{
// Emulate maxRootAngle because CVR doesn't have the player controller set up ideally for VRIK.
// This is a small small fix, but makes it so the feet dont point in the direction of the head
// when turning. It also means turning with joystick & turning IRL make feet behave the same and follow behind.
float weightedAngleLimit = IKFixesMod.EntryFakeRootAngleLimit.Value * solver.locomotion.weight;
float pivotAngle = MovementSystem.Instance.rotationPivot.eulerAngles.y;
float deltaAngleRoot = Mathf.DeltaAngle(pivotAngle, _ikSimulatedRootAngle);
float absDeltaAngleRoot = Mathf.Abs(deltaAngleRoot);
if (absDeltaAngleRoot > weightedAngleLimit)
{
deltaAngleRoot = Mathf.Sign(deltaAngleRoot) * weightedAngleLimit;
_ikSimulatedRootAngle = Mathf.MoveTowardsAngle(_ikSimulatedRootAngle, pivotAngle, absDeltaAngleRoot - weightedAngleLimit);
}
solver.spine.maxRootAngle = 0f;
solver.spine.rootHeadingOffset = deltaAngleRoot;
}
else
{
// Allow avatar to rotate seperatly from Player (Desktop&VR)
// FBT needs avatar root to follow head
// VR default is 25 degrees, but maybe during emotes needs 180 degrees..?
solver.spine.maxRootAngle = BodySystem.isCalibratedAsFullBody ? 0f : 25f;
}
}
int num = 0;
int count = IKSystem.Instance.AllTrackingPoints.FindAll((TrackingPoint m) => m.isActive && m.isValid && m.suggestedRole > TrackingPoint.TrackingRole.Invalid).Count;
// fixes having all tracking points disabled forcing calibration
if (count == 0)
{
__instance._fbtAvailable = false;
return false;
}
// solid body count block
if (BodySystem.enableLeftFootTracking) num++;
if (BodySystem.enableRightFootTracking) num++;
if (BodySystem.enableHipTracking) num++;
if (BodySystem.enableLeftKneeTracking) num++;
if (BodySystem.enableRightKneeTracking) num++;
if (BodySystem.enableChestTracking) num++;
if (BodySystem.enableLeftElbowTracking) num++;
if (BodySystem.enableRightElbowTracking) num++;
__instance._fbtAvailable = (count >= num);
void SetArmWeight(IKSolverVR.Arm arm, float weight)
{
arm.positionWeight = weight;
arm.rotationWeight = weight;
arm.shoulderRotationWeight = weight;
arm.shoulderTwistWeight = weight;
// assumed fix of bend goal weight if arms disabled with elbows (havent tested)
arm.bendGoalWeight = arm.bendGoal != null ? weight : 0f;
}
void SetLegWeight(IKSolverVR.Leg leg, float weight)
{
leg.positionWeight = weight;
leg.rotationWeight = weight;
// fixes knees bending to tracker if feet disabled (running anim)
leg.bendGoalWeight = leg.bendGoal != null ? weight : 0f;
}
void SetPelvisWeight(IKSolverVR.Spine spine, float weight)
{
// looks better when hips are disabled while running
spine.pelvisPositionWeight = weight;
spine.pelvisRotationWeight = weight;
}
return false;
}
}
internal static class VRIKPatches
{
/**
Leg solver uses virtual bone calf and foot, plus world tracked knee position for normal maths.
This breaks as you playspace up, because calf and foot position aren't offset yet in solve order.
**/
[HarmonyPrefix]
[HarmonyPatch(typeof(IKSolverVR.Leg), "ApplyOffsets")]
private static bool Prefix_IKSolverVR_Leg_ApplyOffsets(ref IKSolverVR.Leg __instance)
{
//This is the second part of the above fix, preventing the solver from calculating a bad bendNormal
//when it doesn't need to. The knee tracker should dictate the bendNormal completely.
if (__instance.usingKneeTracker)
{
__instance.ApplyPositionOffset(__instance.footPositionOffset, 1f);
__instance.ApplyRotationOffset(__instance.footRotationOffset, 1f);
Quaternion quaternion = Quaternion.FromToRotation(__instance.footPosition - __instance.position, __instance.footPosition + __instance.heelPositionOffset - __instance.position);
__instance.footPosition = __instance.position + quaternion * (__instance.footPosition - __instance.position);
__instance.footRotation = quaternion * __instance.footRotation;
return false;
}
// run full method like normal otherwise
float num = __instance.bendGoalWeight;
__instance.bendGoalWeight = 0f;
__instance.ApplyOffsetsOld();
__instance.bendGoalWeight = num;
return false;
}
[HarmonyPrefix]
[HarmonyPatch(typeof(IKSolverVR.Leg), "Solve")]
private static void Prefix_IKSolverVR_Leg_Solve(ref IKSolverVR.Leg __instance)
{
//Turns out VRIK applies bend goal maths before root is offset in solving process.
//We will apply ourselves before then to fix it.
if (__instance.usingKneeTracker)
__instance.ApplyBendGoal();
}
}
internal static class PlayerSetupPatches
{
// Last Movement Parent Info
static Vector3 lastMovementPosition;
static Quaternion lastMovementRotation;
[HarmonyPrefix]
[HarmonyPatch(typeof(PlayerSetup), "ResetIk")]
private static bool Prefix_PlayerSetup_ResetIk()
{
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);
// Convert to delta position (how much changed since last frame)
Vector3 deltaPosition = currentPosition - lastMovementPosition;
Quaternion deltaRotation = Quaternion.Inverse(lastMovementRotation) * currentRotation;
// Add platform motion to IK solver
IKSystem.vrik.solver.AddPlatformMotion(deltaPosition, deltaRotation, currentPosition);
// Store for next frame
lastMovementPosition = currentPosition;
lastMovementRotation = currentRotation;
return false;
}
return true;
}
}

72
IKFixes/IKFixes.csproj Normal file
View file

@ -0,0 +1,72 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<LangVersion>latest</LangVersion>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<Reference Include="0Harmony">
<HintPath>C:\Users\krist\Documents\GitHub\_ChilloutVR Modding\_ManagedLibs\0Harmony.dll</HintPath>
</Reference>
<Reference Include="Assembly-CSharp">
<HintPath>C:\Users\krist\Documents\GitHub\_ChilloutVR Modding\_ManagedLibs\Assembly-CSharp.dll</HintPath>
</Reference>
<Reference Include="Assembly-CSharp-firstpass">
<HintPath>C:\Users\krist\Documents\GitHub\_ChilloutVR Modding\_ManagedLibs\Assembly-CSharp-firstpass.dll</HintPath>
</Reference>
<Reference Include="Aura2_Core">
<HintPath>C:\Users\krist\Documents\GitHub\_ChilloutVR Modding\_ManagedLibs\Aura2_Core.dll</HintPath>
</Reference>
<Reference Include="BTKUILib">
<HintPath>..\..\..\..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\ChilloutVR\Mods\BTKUILib.dll</HintPath>
</Reference>
<Reference Include="cohtml.Net">
<HintPath>C:\Users\krist\Documents\GitHub\_ChilloutVR Modding\_ManagedLibs\cohtml.Net.dll</HintPath>
</Reference>
<Reference Include="Cohtml.Runtime">
<HintPath>C:\Users\krist\Documents\GitHub\_ChilloutVR Modding\_ManagedLibs\Cohtml.Runtime.dll</HintPath>
</Reference>
<Reference Include="MelonLoader">
<HintPath>C:\Users\krist\Documents\GitHub\_ChilloutVR Modding\_ManagedLibs\MelonLoader.dll</HintPath>
</Reference>
<Reference Include="SteamVR">
<HintPath>C:\Users\krist\Documents\GitHub\_ChilloutVR Modding\_ManagedLibs\SteamVR.dll</HintPath>
</Reference>
<Reference Include="Unity.Postprocessing.Runtime">
<HintPath>C:\Users\krist\Documents\GitHub\_ChilloutVR Modding\_ManagedLibs\Unity.Postprocessing.Runtime.dll</HintPath>
</Reference>
<Reference Include="Unity.TextMeshPro">
<HintPath>C:\Users\krist\Documents\GitHub\_ChilloutVR Modding\_ManagedLibs\Unity.TextMeshPro.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.AnimationModule">
<HintPath>C:\Users\krist\Documents\GitHub\_ChilloutVR Modding\_ManagedLibs\UnityEngine.AnimationModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.CoreModule">
<HintPath>C:\Users\krist\Documents\GitHub\_ChilloutVR Modding\_ManagedLibs\UnityEngine.CoreModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.InputLegacyModule">
<HintPath>C:\Users\krist\Documents\GitHub\_ChilloutVR Modding\_ManagedLibs\UnityEngine.InputLegacyModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.PhysicsModule">
<HintPath>C:\Users\krist\Documents\GitHub\_ChilloutVR Modding\_ManagedLibs\UnityEngine.PhysicsModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.VRModule">
<HintPath>C:\Users\krist\Documents\GitHub\_ChilloutVR Modding\_ManagedLibs\UnityEngine.VRModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.XRModule">
<HintPath>C:\Users\krist\Documents\GitHub\_ChilloutVR Modding\_ManagedLibs\UnityEngine.XRModule.dll</HintPath>
</Reference>
</ItemGroup>
<Target Name="Deploy" AfterTargets="Build">
<Copy SourceFiles="$(TargetPath)" DestinationFolder="C:\Program Files (x86)\Steam\steamapps\common\ChilloutVR\Mods\" />
<Message Text="Copied $(TargetPath) to C:\Program Files (x86)\Steam\steamapps\common\ChilloutVR\Mods\" Importance="high" />
</Target>
</Project>

25
IKFixes/IKFixes.sln Normal file
View file

@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.2.32630.192
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IKFixes", "IKFixes.csproj", "{AD8D4FA6-8A34-4773-AA24-61F65976182F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{AD8D4FA6-8A34-4773-AA24-61F65976182F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AD8D4FA6-8A34-4773-AA24-61F65976182F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AD8D4FA6-8A34-4773-AA24-61F65976182F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AD8D4FA6-8A34-4773-AA24-61F65976182F}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {1D85DEE4-4BBC-43DD-8AA4-53478A0915F7}
EndGlobalSection
EndGlobal

35
IKFixes/Main.cs Normal file
View file

@ -0,0 +1,35 @@
using MelonLoader;
namespace NAK.Melons.IKFixes;
public class IKFixesMod : MelonMod
{
public const string SettingsCategory = "IKFixes";
public static readonly MelonPreferences_Category CategoryIKFixes = MelonPreferences.CreateCategory(SettingsCategory);
public static readonly MelonPreferences_Entry<bool> EntryUseFakeRootAngle =
CategoryIKFixes.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<float> EntryFakeRootAngleLimit =
CategoryIKFixes.CreateEntry("Fake Root Angle Limit", 25f, description: "Specifies the maximum angle the lower body can have relative to the head when rotating.");
public override void OnInitializeMelon()
{
ApplyPatches(typeof(HarmonyPatches.VRIKPatches));
ApplyPatches(typeof(HarmonyPatches.BodySystemPatches));
ApplyPatches(typeof(HarmonyPatches.PlayerSetupPatches));
}
void ApplyPatches(Type type)
{
try
{
HarmonyInstance.PatchAll(type);
}
catch (Exception e)
{
LoggerInstance.Msg($"Failed while patching {type.Name}!");
LoggerInstance.Error(e);
}
}
}

View file

@ -0,0 +1,30 @@
using MelonLoader;
using NAK.Melons.IKFixes.Properties;
using System.Reflection;
[assembly: AssemblyVersion(AssemblyInfoParams.Version)]
[assembly: AssemblyFileVersion(AssemblyInfoParams.Version)]
[assembly: AssemblyInformationalVersion(AssemblyInfoParams.Version)]
[assembly: AssemblyTitle(nameof(NAK.Melons.IKFixes))]
[assembly: AssemblyCompany(AssemblyInfoParams.Author)]
[assembly: AssemblyProduct(nameof(NAK.Melons.IKFixes))]
[assembly: MelonInfo(
typeof(NAK.Melons.IKFixes.IKFixesMod),
nameof(NAK.Melons.IKFixes),
AssemblyInfoParams.Version,
AssemblyInfoParams.Author,
downloadLink: "https://github.com/NotAKidOnSteam/IKFixes"
)]
[assembly: MelonGame("Alpha Blend Interactive", "ChilloutVR")]
[assembly: MelonPlatform(MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)]
[assembly: MelonPlatformDomain(MelonPlatformDomainAttribute.CompatibleDomains.MONO)]
[assembly: HarmonyDontPatchAll]
namespace NAK.Melons.IKFixes.Properties;
internal static class AssemblyInfoParams
{
public const string Version = "1.0.2";
public const string Author = "NotAKidoS";
}

23
IKFixes/format.json Normal file
View file

@ -0,0 +1,23 @@
{
"_id": 142,
"name": "IKFixes",
"modversion": "1.0.2",
"gameversion": "2022r170",
"loaderversion": "0.5.7",
"modtype": "Mod",
"author": "NotAKidoS",
"description": "A few small fixes to IK. Major ones are listed below:\n\n**FBT** - Fixes knee tracking & running animations.\n\n**Halfbody** - Fixes footsteps while on a MovementParent & uses root angle offset to prevent feet from only pointing in head direction.",
"searchtags": [
"knee",
"ik",
"tracking",
"fix"
],
"requirements": [
"None"
],
"downloadlink": "https://github.com/NotAKidOnSteam/IKFixes/releases/download/v1.0.2/IKFixes.dll",
"sourcelink": "https://github.com/NotAKidOnSteam/IKFixes/",
"changelog": "- Initial Release\n- Added FakeRootAngle fix for Halfbody. Feet will no longer only point in the direction of head while looking around/turning. This can be disabled in mod settings.",
"embedcolor": "f46e49"
}

View file

@ -134,6 +134,31 @@ Does nothing on Knuckles controllers as the bind is used for finger tracking.
![VirtualDesktop Android-20220907-172923](https://user-images.githubusercontent.com/37721153/188999382-7663a863-49be-4b9b-8839-8b6e8c32783b.jpg)
# IKFixes
A few small fixes to IK.
**FBT Fixes** -
* Knee tracking.
* Running animations..
* Emotes playing in wrong direction.
* Forced to calibrate if all IK Tracking Settings are disabled.
**Halfbody Fixes** -
* Locomotion footsteps while on Movement Parents.
* Root Angle Offset while looking around. Fixes feet only pointing in direction of head.
## Relevant Feedback Posts:
https://feedback.abinteractive.net/p/ik-knee-tracking
https://feedback.abinteractive.net/p/2022r170-ex3-knee-ik-weirdness-when-using-knee-trackers
https://feedback.abinteractive.net/p/disabling-all-tracked-points-makes-game-assume-fbt
https://feedback.abinteractive.net/p/about-ik-behaviour
https://feedback.abinteractive.net/p/vrik-addplatformmotion-for-movement-parents
https://feedback.abinteractive.net/p/halfbodyik-feet-will-only-point-in-direction-of-head
---