NAK_CVR_Mods/DesktopVRIK/VRIKUtils.cs
NotAKidoS 1eeaf4be54 No longer rely on AMT, IKSystem, or PlayerSetup
Partially rewrote everything so it is cleaner, does not need Traverse/Reflection, and no longer piggybacks on the game handling VRIK on Desktop for whatever reason. This version also handles VRIK Locomotion weight standalone instead of requiring AvatarMotionTweaker, but if AMT is found it will rely on it anyways. This can be overwridden in the melonprefs, but its still gonna be double the maths for no reason.
2023-03-28 22:48:44 -05:00

188 lines
No EOL
6.9 KiB
C#

using RootMotion.FinalIK;
using UnityEngine;
namespace NAK.Melons.DesktopVRIK;
public static class VRIKUtils
{
public static void ConfigureVRIKReferences(VRIK vrik, bool useVRIKToes, bool findUnmappedToes, out bool foundUnmappedToes)
{
foundUnmappedToes = false;
//might not work over netik
FixChestAndSpineReferences(vrik);
if (!useVRIKToes)
{
vrik.references.leftToes = null;
vrik.references.rightToes = null;
}
else if (findUnmappedToes)
{
//doesnt work with netik, but its toes...
FindAndSetUnmappedToes(vrik, out foundUnmappedToes);
}
//bullshit fix to not cause death
FixFingerBonesError(vrik);
}
private static void FixChestAndSpineReferences(VRIK vrik)
{
Transform leftShoulderBone = vrik.references.leftShoulder;
Transform rightShoulderBone = vrik.references.rightShoulder;
Transform assumedChest = leftShoulderBone?.parent;
if (assumedChest != null && rightShoulderBone.parent == assumedChest &&
vrik.references.chest != assumedChest)
{
vrik.references.chest = assumedChest;
vrik.references.spine = assumedChest.parent;
}
}
private static void FindAndSetUnmappedToes(VRIK vrik, out bool foundUnmappedToes)
{
foundUnmappedToes = false;
Transform leftToes = vrik.references.leftToes;
Transform rightToes = vrik.references.rightToes;
if (leftToes == null && rightToes == null)
{
leftToes = FindUnmappedToe(vrik.references.leftFoot);
rightToes = FindUnmappedToe(vrik.references.rightFoot);
if (leftToes != null && rightToes != null)
{
vrik.references.leftToes = leftToes;
vrik.references.rightToes = rightToes;
foundUnmappedToes = true;
}
}
}
private static Transform FindUnmappedToe(Transform foot)
{
foreach (Transform bone in foot)
{
if (bone.name.ToLowerInvariant().Contains("toe") ||
bone.name.ToLowerInvariant().EndsWith("_end"))
{
return bone;
}
}
return null;
}
private static void FixFingerBonesError(VRIK vrik)
{
FixFingerBones(vrik, vrik.references.leftHand, vrik.solver.leftArm);
FixFingerBones(vrik, vrik.references.rightHand, vrik.solver.rightArm);
}
private static void FixFingerBones(VRIK vrik, Transform hand, IKSolverVR.Arm armSolver)
{
if (hand.childCount == 0)
{
armSolver.wristToPalmAxis = Vector3.up;
armSolver.palmToThumbAxis = hand == vrik.references.leftHand ? -Vector3.forward : Vector3.forward;
}
}
public static void CalculateKneeBendNormals(VRIK vrik, out Vector3 leftKneeNormal, out Vector3 rightKneeNormal)
{
// Helper function to get position or default to Vector3.zero
Vector3 GetPositionOrDefault(Transform transform) => transform?.position ?? Vector3.zero;
// Get assumed left knee normal
Vector3[] leftVectors = {
GetPositionOrDefault(vrik.references.leftThigh),
GetPositionOrDefault(vrik.references.leftCalf),
GetPositionOrDefault(vrik.references.leftFoot)
};
leftKneeNormal = Quaternion.Inverse(vrik.references.root.rotation) * GetNormalFromArray(leftVectors);
// Get assumed right knee normal
Vector3[] rightVectors = {
GetPositionOrDefault(vrik.references.rightThigh),
GetPositionOrDefault(vrik.references.rightCalf),
GetPositionOrDefault(vrik.references.rightFoot)
};
rightKneeNormal = Quaternion.Inverse(vrik.references.root.rotation) * GetNormalFromArray(rightVectors);
}
public static void ApplyKneeBendNormals(VRIK vrik, Vector3 leftKneeNormal, Vector3 rightKneeNormal)
{
// 0 uses bendNormalRelToPelvis, 1 is bendNormalRelToTarget
// modifying pelvis normal weight is easier math
vrik.solver.leftLeg.bendToTargetWeight = 0f;
vrik.solver.rightLeg.bendToTargetWeight = 0f;
var pelvis_localRotationInverse = Quaternion.Inverse(vrik.references.pelvis.localRotation);
vrik.solver.leftLeg.bendNormalRelToPelvis = pelvis_localRotationInverse * leftKneeNormal;
vrik.solver.rightLeg.bendNormalRelToPelvis = pelvis_localRotationInverse * rightKneeNormal;
}
private static Vector3 GetNormalFromArray(Vector3[] positions)
{
Vector3 centroid = Vector3.zero;
for (int i = 0; i < positions.Length; i++)
{
centroid += positions[i];
}
centroid /= positions.Length;
Vector3 normal = Vector3.zero;
for (int i = 0; i < positions.Length - 2; i++)
{
Vector3 side1 = positions[i] - centroid;
Vector3 side2 = positions[i + 1] - centroid;
normal += Vector3.Cross(side1, side2);
}
return normal.normalized;
}
public static void CalculateInitialIKScaling(VRIK vrik, out float initialFootDistance, out float initialStepThreshold, out float initialStepHeight)
{
// Get distance between feet and thighs
float scaleModifier = Mathf.Max(1f, vrik.references.pelvis.lossyScale.x);
float footDistance = Vector3.Distance(vrik.references.leftFoot.position, vrik.references.rightFoot.position);
initialFootDistance = footDistance * 0.5f;
initialStepThreshold = footDistance * scaleModifier;
initialStepHeight = Vector3.Distance(vrik.references.leftFoot.position, vrik.references.leftCalf.position) * 0.2f;
}
public static void SetupHeadIKTarget(VRIK vrik)
{
// Lazy HeadIKTarget calibration
if (vrik.solver.spine.headTarget == null)
{
vrik.solver.spine.headTarget = new GameObject("Head IK Target").transform;
}
vrik.solver.spine.headTarget.parent = vrik.references.head;
vrik.solver.spine.headTarget.localPosition = Vector3.zero;
vrik.solver.spine.headTarget.localRotation = Quaternion.identity;
}
public static void ApplyScaleToVRIK(VRIK vrik, float footDistance, float stepThreshold, float stepHeight, float modifier)
{
vrik.solver.locomotion.footDistance = footDistance * modifier;
vrik.solver.locomotion.stepThreshold = stepThreshold * modifier;
ScaleStepHeight(vrik.solver.locomotion.stepHeight, stepHeight * modifier);
}
private static void ScaleStepHeight(AnimationCurve stepHeightCurve, float mag)
{
Keyframe[] keyframes = stepHeightCurve.keys;
keyframes[1].value = mag;
stepHeightCurve.keys = keyframes;
}
public static void InitiateVRIKSolver(VRIK vrik)
{
vrik.solver.SetToReferences(vrik.references);
vrik.solver.Initiate(vrik.transform);
}
}