[DesktopVRIK] Clean up VRIK calibration and configuration. Add NetIKPass.

This commit is contained in:
NotAKidoS 2023-05-19 02:53:41 -05:00
parent 9c627d3cce
commit d21018001e
11 changed files with 681 additions and 593 deletions

View file

@ -0,0 +1,28 @@
using RootMotion.FinalIK;
namespace NAK.DesktopVRIK.VRIKHelper;
// I don't think this was needed at all, but it looks fancy.
//https://github.com/knah/VRCMods/blob/master/IKTweaks/CachedSolver.cs
public struct CachedSolver
{
public readonly IKSolverVR Solver;
public readonly IKSolverVR.Spine Spine;
public readonly IKSolverVR.Leg LeftLeg;
public readonly IKSolverVR.Leg RightLeg;
public readonly IKSolverVR.Arm LeftArm;
public readonly IKSolverVR.Arm RightArm;
public readonly IKSolverVR.Locomotion Locomotion;
public CachedSolver(IKSolverVR solver)
{
Solver = solver;
Spine = solver.spine;
LeftArm = solver.leftArm;
LeftLeg = solver.leftLeg;
RightArm = solver.rightArm;
RightLeg = solver.rightLeg;
Locomotion = solver.locomotion;
}
}

View file

@ -0,0 +1,33 @@
using UnityEngine;
namespace NAK.DesktopVRIK.VRIKHelper;
public struct VRIKCalibrationData
{
public Vector3 KneeNormalLeft;
public Vector3 KneeNormalRight;
public Vector3 InitialFootPosLeft;
public Vector3 InitialFootPosRight;
public Quaternion InitialFootRotLeft;
public Quaternion InitialFootRotRight;
public float InitialHeadHeight;
public float InitialFootDistance;
public float InitialStepThreshold;
public float InitialStepHeight;
public bool FixTransformsRequired;
public void Clear()
{
KneeNormalLeft = Vector3.zero;
KneeNormalRight = Vector3.zero;
InitialFootPosLeft = Vector3.zero;
InitialFootPosRight = Vector3.zero;
InitialFootRotLeft = Quaternion.identity;
InitialFootRotRight = Quaternion.identity;
InitialHeadHeight = 0f;
InitialFootDistance = 0f;
InitialStepThreshold = 0f;
InitialStepHeight = 0f;
FixTransformsRequired = false;
}
}

View file

@ -0,0 +1,87 @@
namespace NAK.DesktopVRIK.VRIKHelper;
public class VRIKConfiguration
{
// Solver settings
public float LocomotionWeight { get; set; }
public float LocomotionAngleThreshold { get; set; }
public float LocomotionMaxLegStretch { get; set; }
public float SpineMinHeadHeight { get; set; }
public float SolverIKPositionWeight { get; set; }
public float SpineChestClampWeight { get; set; }
public float SpineMaintainPelvisPosition { get; set; }
// Body leaning settings
public float SpineBodyPosStiffness { get; set; }
public float SpineBodyRotStiffness { get; set; }
public float SpineNeckStiffness { get; set; }
// Locomotion settings
public float LocomotionVelocityFactor { get; set; }
public float LocomotionMaxVelocity { get; set; }
public float LocomotionRootSpeed { get; set; }
// Chest rotation
public float SpineRotateChestByHands { get; set; }
public float SpineHeadClampWeight { get; set; }
public float SpinePositionWeight { get; set; }
public float SpineRotationWeight { get; set; }
public float SpineMaxRootAngle { get; set; }
// BodySystem
public float SpinePelvisPositionWeight { get; set; }
public float LeftArmPositionWeight { get; set; }
public float LeftArmRotationWeight { get; set; }
public float RightArmPositionWeight { get; set; }
public float RightArmRotationWeight { get; set; }
public float LeftLegPositionWeight { get; set; }
public float LeftLegRotationWeight { get; set; }
public float RightLegPositionWeight { get; set; }
public float RightLegRotationWeight { get; set; }
}
public static class VRIKConfigurator
{
public static void ApplyVRIKConfiguration(CachedSolver cachedSolver, VRIKConfiguration config)
{
cachedSolver.Solver.IKPositionWeight = config.SolverIKPositionWeight;
cachedSolver.Locomotion.weight = config.LocomotionWeight;
cachedSolver.Locomotion.angleThreshold = config.LocomotionAngleThreshold;
cachedSolver.Locomotion.maxLegStretch = config.LocomotionMaxLegStretch;
cachedSolver.Spine.chestClampWeight = config.SpineChestClampWeight;
cachedSolver.Spine.maintainPelvisPosition = config.SpineMaintainPelvisPosition;
cachedSolver.Spine.minHeadHeight = config.SpineMinHeadHeight;
cachedSolver.Spine.bodyPosStiffness = config.SpineBodyPosStiffness;
cachedSolver.Spine.bodyRotStiffness = config.SpineBodyRotStiffness;
cachedSolver.Spine.neckStiffness = config.SpineNeckStiffness;
cachedSolver.Locomotion.velocityFactor = config.LocomotionVelocityFactor;
cachedSolver.Locomotion.maxVelocity = config.LocomotionMaxVelocity;
cachedSolver.Locomotion.rootSpeed = config.LocomotionRootSpeed;
cachedSolver.Spine.rotateChestByHands = config.SpineRotateChestByHands;
cachedSolver.Spine.headClampWeight = config.SpineHeadClampWeight;
cachedSolver.Spine.positionWeight = config.SpinePositionWeight;
cachedSolver.Spine.rotationWeight = config.SpineRotationWeight;
cachedSolver.Spine.maxRootAngle = config.SpineMaxRootAngle;
cachedSolver.Spine.maintainPelvisPosition = config.SpineMaintainPelvisPosition;
cachedSolver.Spine.pelvisPositionWeight = config.SpinePelvisPositionWeight;
cachedSolver.LeftArm.positionWeight = config.LeftArmPositionWeight;
cachedSolver.LeftArm.rotationWeight = config.LeftArmRotationWeight;
cachedSolver.RightArm.positionWeight = config.RightArmPositionWeight;
cachedSolver.RightArm.rotationWeight = config.RightArmRotationWeight;
cachedSolver.LeftLeg.positionWeight = config.LeftLegPositionWeight;
cachedSolver.LeftLeg.rotationWeight = config.LeftLegRotationWeight;
cachedSolver.RightLeg.positionWeight = config.RightLegPositionWeight;
cachedSolver.RightLeg.rotationWeight = config.RightLegRotationWeight;
}
}

View file

@ -0,0 +1,53 @@
namespace NAK.DesktopVRIK.VRIKHelper;
public static class VRIKConfigurations
{
public static VRIKConfiguration DesktopVRIKConfiguration()
{
return new VRIKConfiguration
{
// Solver settings
LocomotionWeight = 0f,
LocomotionAngleThreshold = 30f,
LocomotionMaxLegStretch = 1f,
SpineMinHeadHeight = 0f,
SolverIKPositionWeight = 1f,
SpineChestClampWeight = 0f,
SpineMaintainPelvisPosition = 1f,
// Body leaning settings
SpineBodyPosStiffness = 1f,
SpineBodyRotStiffness = 0.2f,
SpineNeckStiffness = 0.0001f, //hack
// Locomotion settings
LocomotionVelocityFactor = 0f,
LocomotionMaxVelocity = 0f,
LocomotionRootSpeed = 1000f,
// Chest rotation
SpineRotateChestByHands = 0f, //pam, bid, leap motion change
// LookAtIK priority
SpineHeadClampWeight = 0.2f,
// Tippytoes
SpinePositionWeight = 0f,
SpineRotationWeight = 1f,
// Emotes
SpineMaxRootAngle = 180f,
// BodySystem
SpinePelvisPositionWeight = 0f,
LeftArmPositionWeight = 0f,
LeftArmRotationWeight = 0f,
RightArmPositionWeight = 0f,
RightArmRotationWeight = 0f,
LeftLegPositionWeight = 0f,
LeftLegRotationWeight = 0f,
RightLegPositionWeight = 0f,
RightLegRotationWeight = 0f,
};
}
}

View file

@ -0,0 +1,164 @@
using RootMotion.FinalIK;
using UnityEngine;
namespace NAK.DesktopVRIK.VRIKHelper;
public static class VRIKUtils
{
public static void ConfigureVRIKReferences(VRIK vrik, bool useVRIKToes)
{
if (!useVRIKToes)
{
vrik.references.leftToes = null;
vrik.references.rightToes = null;
}
//bullshit fix to not cause death
FixFingerBonesError(vrik);
}
private static void FixFingerBonesError(VRIK vrik)
{
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;
}
}
FixFingerBones(vrik, vrik.references.leftHand, vrik.solver.leftArm);
FixFingerBones(vrik, vrik.references.rightHand, vrik.solver.rightArm);
}
public static void CalculateKneeBendNormals(VRIK vrik, ref VRIKCalibrationData calibrationData)
{
// 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)
};
calibrationData.KneeNormalLeft = 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)
};
calibrationData.KneeNormalRight = Quaternion.Inverse(vrik.references.root.rotation) * GetNormalFromArray(rightVectors);
}
public static void ApplyKneeBendNormals(VRIK vrik, VRIKCalibrationData calibrationData)
{
// 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 * calibrationData.KneeNormalLeft;
vrik.solver.rightLeg.bendNormalRelToPelvis = pelvis_localRotationInverse * calibrationData.KneeNormalRight;
}
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, ref VRIKCalibrationData calibrationData)
{
// 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);
calibrationData.InitialFootDistance = footDistance * 0.5f;
calibrationData.InitialStepThreshold = footDistance * scaleModifier;
calibrationData.InitialStepHeight = Vector3.Distance(vrik.references.leftFoot.position, vrik.references.leftCalf.position) * 0.2f;
calibrationData.InitialHeadHeight = Mathf.Abs(vrik.references.head.position.y - vrik.references.rightFoot.position.y);
}
public static void CalculateInitialFootsteps(VRIK vrik, ref VRIKCalibrationData calibrationData)
{
Transform root = vrik.references.root;
Transform leftFoot = vrik.references.leftFoot;
Transform rightFoot = vrik.references.rightFoot;
// Calculate the world rotation of the root bone at the current frame
Quaternion rootWorldRot = root.rotation;
// Calculate the world rotation of the left and right feet relative to the root bone
calibrationData.InitialFootPosLeft = root.parent.InverseTransformPoint(leftFoot.position);
calibrationData.InitialFootPosRight = root.parent.InverseTransformPoint(rightFoot.position);
calibrationData.InitialFootRotLeft = Quaternion.Inverse(rootWorldRot) * leftFoot.rotation;
calibrationData.InitialFootRotRight = Quaternion.Inverse(rootWorldRot) * rightFoot.rotation;
}
public static void ResetToInitialFootsteps(VRIK vrik, VRIKCalibrationData calibrationData, float scaleModifier)
{
var locomotionSolver = vrik.solver.locomotion;
var footsteps = locomotionSolver.footsteps;
var footstepLeft = footsteps[0];
var footstepRight = footsteps[1];
var root = vrik.references.root;
var rootWorldRot = vrik.references.root.rotation;
// hack, use parent transform instead as setting feet position moves root
footstepLeft.Reset(rootWorldRot, root.parent.TransformPoint(calibrationData.InitialFootPosLeft * scaleModifier), rootWorldRot * calibrationData.InitialFootRotLeft);
footstepRight.Reset(rootWorldRot, root.parent.TransformPoint(calibrationData.InitialFootPosRight * scaleModifier), rootWorldRot * calibrationData.InitialFootRotRight);
}
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, VRIKCalibrationData calibrationData, float scaleModifier)
{
var locomotionSolver = vrik.solver.locomotion;
locomotionSolver.footDistance = calibrationData.InitialFootDistance * scaleModifier;
locomotionSolver.stepThreshold = calibrationData.InitialStepThreshold * scaleModifier;
ScaleStepHeight(locomotionSolver.stepHeight, calibrationData.InitialStepHeight * scaleModifier);
}
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);
}
}