Files
2026-03-04 10:03:45 +08:00

386 lines
16 KiB
C#

using System;
using UnityEngine;
namespace RootMotion.FinalIK
{
public static class VRIKCalibrator
{
[Serializable]
public class Settings
{
[Tooltip("Multiplies character scale")]
public float scaleMlp = 1f;
[Tooltip("Local axis of the HMD facing forward.")]
public Vector3 headTrackerForward = Vector3.forward;
[Tooltip("Local axis of the HMD facing up.")]
public Vector3 headTrackerUp = Vector3.up;
[Tooltip("Local axis of the body tracker towards the player's forward direction.")]
public Vector3 bodyTrackerForward = Vector3.forward;
[Tooltip("Local axis of the body tracker towards the up direction.")]
public Vector3 bodyTrackerUp = Vector3.up;
[Tooltip("Local axis of the hand trackers pointing from the wrist towards the palm.")]
public Vector3 handTrackerForward = Vector3.forward;
[Tooltip("Local axis of the hand trackers pointing in the direction of the surface normal of the back of the hand.")]
public Vector3 handTrackerUp = Vector3.up;
[Tooltip("Local axis of the foot trackers towards the player's forward direction.")]
public Vector3 footTrackerForward = Vector3.forward;
[Tooltip("Local axis of the foot tracker towards the up direction.")]
public Vector3 footTrackerUp = Vector3.up;
[Space(10f)]
[Tooltip("Offset of the head bone from the HMD in (headTrackerForward, headTrackerUp) space relative to the head tracker.")]
public Vector3 headOffset;
[Tooltip("Offset of the hand bones from the hand trackers in (handTrackerForward, handTrackerUp) space relative to the hand trackers.")]
public Vector3 handOffset;
[Tooltip("Forward offset of the foot bones from the foot trackers.")]
public float footForwardOffset;
[Tooltip("Inward offset of the foot bones from the foot trackers.")]
public float footInwardOffset;
[Tooltip("Used for adjusting foot heading relative to the foot trackers.")]
[Range(-180f, 180f)]
public float footHeadingOffset;
[Range(0f, 1f)]
public float pelvisPositionWeight = 1f;
[Range(0f, 1f)]
public float pelvisRotationWeight = 1f;
}
[Serializable]
public class CalibrationData
{
[Serializable]
public class Target
{
public bool used;
public Vector3 localPosition;
public Quaternion localRotation;
public Target(Transform t)
{
used = t != null;
if (used)
{
localPosition = t.localPosition;
localRotation = t.localRotation;
}
}
public void SetTo(Transform t)
{
if (used)
{
t.localPosition = localPosition;
t.localRotation = localRotation;
}
}
}
public float scale;
public Target head;
public Target leftHand;
public Target rightHand;
public Target pelvis;
public Target leftFoot;
public Target rightFoot;
public Target leftLegGoal;
public Target rightLegGoal;
public Vector3 pelvisTargetRight;
public float pelvisPositionWeight;
public float pelvisRotationWeight;
}
public static void RecalibrateScale(VRIK ik, Settings settings)
{
float num = (ik.solver.spine.headTarget.position.y - ik.references.root.position.y) / (ik.references.head.position.y - ik.references.root.position.y);
ik.references.root.localScale *= num * settings.scaleMlp;
}
public static CalibrationData Calibrate(VRIK ik, Settings settings, Transform headTracker, Transform bodyTracker = null, Transform leftHandTracker = null, Transform rightHandTracker = null, Transform leftFootTracker = null, Transform rightFootTracker = null)
{
if (!ik.solver.initiated)
{
Debug.LogError("Can not calibrate before VRIK has initiated.");
return null;
}
if (headTracker == null)
{
Debug.LogError("Can not calibrate VRIK without the head tracker.");
return null;
}
CalibrationData calibrationData = new CalibrationData();
ik.solver.FixTransforms();
Vector3 position = headTracker.position + headTracker.rotation * Quaternion.LookRotation(settings.headTrackerForward, settings.headTrackerUp) * settings.headOffset;
ik.references.root.position = new Vector3(position.x, ik.references.root.position.y, position.z);
Vector3 forward = headTracker.rotation * settings.headTrackerForward;
forward.y = 0f;
ik.references.root.rotation = Quaternion.LookRotation(forward);
Transform transform = ((ik.solver.spine.headTarget == null) ? new GameObject("Head Target").transform : ik.solver.spine.headTarget);
transform.position = position;
transform.rotation = ik.references.head.rotation;
transform.parent = headTracker;
ik.solver.spine.headTarget = transform;
float num = (transform.position.y - ik.references.root.position.y) / (ik.references.head.position.y - ik.references.root.position.y);
ik.references.root.localScale *= num * settings.scaleMlp;
if (bodyTracker != null)
{
Transform transform2 = ((ik.solver.spine.pelvisTarget == null) ? new GameObject("Pelvis Target").transform : ik.solver.spine.pelvisTarget);
transform2.position = ik.references.pelvis.position;
transform2.rotation = ik.references.pelvis.rotation;
transform2.parent = bodyTracker;
ik.solver.spine.pelvisTarget = transform2;
ik.solver.spine.pelvisPositionWeight = settings.pelvisPositionWeight;
ik.solver.spine.pelvisRotationWeight = settings.pelvisRotationWeight;
ik.solver.plantFeet = false;
ik.solver.spine.maxRootAngle = 180f;
}
else if (leftFootTracker != null && rightFootTracker != null)
{
ik.solver.spine.maxRootAngle = 0f;
}
if (leftHandTracker != null)
{
Transform transform3 = ((ik.solver.leftArm.target == null) ? new GameObject("Left Hand Target").transform : ik.solver.leftArm.target);
transform3.position = leftHandTracker.position + leftHandTracker.rotation * Quaternion.LookRotation(settings.handTrackerForward, settings.handTrackerUp) * settings.handOffset;
transform3.rotation = QuaTools.MatchRotation(upAxis: Vector3.Cross(ik.solver.leftArm.wristToPalmAxis, ik.solver.leftArm.palmToThumbAxis), targetRotation: leftHandTracker.rotation * Quaternion.LookRotation(settings.handTrackerForward, settings.handTrackerUp), targetforwardAxis: settings.handTrackerForward, targetUpAxis: settings.handTrackerUp, forwardAxis: ik.solver.leftArm.wristToPalmAxis);
transform3.parent = leftHandTracker;
ik.solver.leftArm.target = transform3;
ik.solver.leftArm.positionWeight = 1f;
ik.solver.leftArm.rotationWeight = 1f;
}
else
{
ik.solver.leftArm.positionWeight = 0f;
ik.solver.leftArm.rotationWeight = 0f;
}
if (rightHandTracker != null)
{
Transform transform4 = ((ik.solver.rightArm.target == null) ? new GameObject("Right Hand Target").transform : ik.solver.rightArm.target);
transform4.position = rightHandTracker.position + rightHandTracker.rotation * Quaternion.LookRotation(settings.handTrackerForward, settings.handTrackerUp) * settings.handOffset;
transform4.rotation = QuaTools.MatchRotation(upAxis: -Vector3.Cross(ik.solver.rightArm.wristToPalmAxis, ik.solver.rightArm.palmToThumbAxis), targetRotation: rightHandTracker.rotation * Quaternion.LookRotation(settings.handTrackerForward, settings.handTrackerUp), targetforwardAxis: settings.handTrackerForward, targetUpAxis: settings.handTrackerUp, forwardAxis: ik.solver.rightArm.wristToPalmAxis);
transform4.parent = rightHandTracker;
ik.solver.rightArm.target = transform4;
ik.solver.rightArm.positionWeight = 1f;
ik.solver.rightArm.rotationWeight = 1f;
}
else
{
ik.solver.rightArm.positionWeight = 0f;
ik.solver.rightArm.rotationWeight = 0f;
}
if (leftFootTracker != null)
{
CalibrateLeg(settings, leftFootTracker, ik.solver.leftLeg, (ik.references.leftToes != null) ? ik.references.leftToes : ik.references.leftFoot, ik.references.root.forward, isLeft: true);
}
if (rightFootTracker != null)
{
CalibrateLeg(settings, rightFootTracker, ik.solver.rightLeg, (ik.references.rightToes != null) ? ik.references.rightToes : ik.references.rightFoot, ik.references.root.forward, isLeft: false);
}
bool num2 = bodyTracker != null || (leftFootTracker != null && rightFootTracker != null);
VRIKRootController vRIKRootController = ik.references.root.GetComponent<VRIKRootController>();
if (num2)
{
if (vRIKRootController == null)
{
vRIKRootController = ik.references.root.gameObject.AddComponent<VRIKRootController>();
}
vRIKRootController.Calibrate();
}
else if (vRIKRootController != null)
{
UnityEngine.Object.Destroy(vRIKRootController);
}
ik.solver.spine.minHeadHeight = 0f;
ik.solver.locomotion.weight = ((bodyTracker == null && leftFootTracker == null && rightFootTracker == null) ? 1f : 0f);
calibrationData.scale = ik.references.root.localScale.y;
calibrationData.head = new CalibrationData.Target(ik.solver.spine.headTarget);
calibrationData.pelvis = new CalibrationData.Target(ik.solver.spine.pelvisTarget);
calibrationData.leftHand = new CalibrationData.Target(ik.solver.leftArm.target);
calibrationData.rightHand = new CalibrationData.Target(ik.solver.rightArm.target);
calibrationData.leftFoot = new CalibrationData.Target(ik.solver.leftLeg.target);
calibrationData.rightFoot = new CalibrationData.Target(ik.solver.rightLeg.target);
calibrationData.leftLegGoal = new CalibrationData.Target(ik.solver.leftLeg.bendGoal);
calibrationData.rightLegGoal = new CalibrationData.Target(ik.solver.rightLeg.bendGoal);
calibrationData.pelvisTargetRight = ((vRIKRootController != null) ? vRIKRootController.pelvisTargetRight : Vector3.zero);
calibrationData.pelvisPositionWeight = ik.solver.spine.pelvisPositionWeight;
calibrationData.pelvisRotationWeight = ik.solver.spine.pelvisRotationWeight;
return calibrationData;
}
private static void CalibrateLeg(Settings settings, Transform tracker, IKSolverVR.Leg leg, Transform lastBone, Vector3 rootForward, bool isLeft)
{
string text = (isLeft ? "Left" : "Right");
Transform transform = ((leg.target == null) ? new GameObject(text + " Foot Target").transform : leg.target);
Quaternion quaternion = tracker.rotation * Quaternion.LookRotation(settings.footTrackerForward, settings.footTrackerUp);
Vector3 vector = quaternion * Vector3.forward;
vector.y = 0f;
quaternion = Quaternion.LookRotation(vector);
float x = (isLeft ? settings.footInwardOffset : (0f - settings.footInwardOffset));
transform.position = tracker.position + quaternion * new Vector3(x, 0f, settings.footForwardOffset);
transform.position = new Vector3(transform.position.x, lastBone.position.y, transform.position.z);
transform.rotation = lastBone.rotation;
Vector3 vector2 = AxisTools.GetAxisVectorToDirection(lastBone, rootForward);
if (Vector3.Dot(lastBone.rotation * vector2, rootForward) < 0f)
{
vector2 = -vector2;
}
Vector3 vector3 = Quaternion.Inverse(Quaternion.LookRotation(transform.rotation * vector2)) * vector;
float num = Mathf.Atan2(vector3.x, vector3.z) * 57.29578f;
float num2 = (isLeft ? settings.footHeadingOffset : (0f - settings.footHeadingOffset));
transform.rotation = Quaternion.AngleAxis(num + num2, Vector3.up) * transform.rotation;
transform.parent = tracker;
leg.target = transform;
leg.positionWeight = 1f;
leg.rotationWeight = 1f;
Transform transform2 = ((leg.bendGoal == null) ? new GameObject(text + " Leg Bend Goal").transform : leg.bendGoal);
transform2.position = lastBone.position + quaternion * Vector3.forward + quaternion * Vector3.up;
transform2.parent = tracker;
leg.bendGoal = transform2;
leg.bendGoalWeight = 1f;
}
public static void Calibrate(VRIK ik, CalibrationData data, Transform headTracker, Transform bodyTracker = null, Transform leftHandTracker = null, Transform rightHandTracker = null, Transform leftFootTracker = null, Transform rightFootTracker = null)
{
if (!ik.solver.initiated)
{
Debug.LogError("Can not calibrate before VRIK has initiated.");
return;
}
if (headTracker == null)
{
Debug.LogError("Can not calibrate VRIK without the head tracker.");
return;
}
ik.solver.FixTransforms();
Transform transform = ((ik.solver.spine.headTarget == null) ? new GameObject("Head Target").transform : ik.solver.spine.headTarget);
transform.parent = headTracker;
data.head.SetTo(transform);
ik.solver.spine.headTarget = transform;
ik.references.root.localScale = data.scale * Vector3.one;
if (bodyTracker != null)
{
Transform transform2 = ((ik.solver.spine.pelvisTarget == null) ? new GameObject("Pelvis Target").transform : ik.solver.spine.pelvisTarget);
transform2.parent = bodyTracker;
data.pelvis.SetTo(transform2);
ik.solver.spine.pelvisTarget = transform2;
ik.solver.spine.pelvisPositionWeight = data.pelvisPositionWeight;
ik.solver.spine.pelvisRotationWeight = data.pelvisRotationWeight;
ik.solver.plantFeet = false;
ik.solver.spine.maxRootAngle = 180f;
}
else if (leftFootTracker != null && rightFootTracker != null)
{
ik.solver.spine.maxRootAngle = 0f;
}
if (leftHandTracker != null)
{
Transform transform3 = ((ik.solver.leftArm.target == null) ? new GameObject("Left Hand Target").transform : ik.solver.leftArm.target);
transform3.parent = leftHandTracker;
data.leftHand.SetTo(transform3);
ik.solver.leftArm.target = transform3;
ik.solver.leftArm.positionWeight = 1f;
ik.solver.leftArm.rotationWeight = 1f;
}
else
{
ik.solver.leftArm.positionWeight = 0f;
ik.solver.leftArm.rotationWeight = 0f;
}
if (rightHandTracker != null)
{
Transform transform4 = ((ik.solver.rightArm.target == null) ? new GameObject("Right Hand Target").transform : ik.solver.rightArm.target);
transform4.parent = rightHandTracker;
data.rightHand.SetTo(transform4);
ik.solver.rightArm.target = transform4;
ik.solver.rightArm.positionWeight = 1f;
ik.solver.rightArm.rotationWeight = 1f;
}
else
{
ik.solver.rightArm.positionWeight = 0f;
ik.solver.rightArm.rotationWeight = 0f;
}
if (leftFootTracker != null)
{
CalibrateLeg(data, leftFootTracker, ik.solver.leftLeg, (ik.references.leftToes != null) ? ik.references.leftToes : ik.references.leftFoot, ik.references.root.forward, isLeft: true);
}
if (rightFootTracker != null)
{
CalibrateLeg(data, rightFootTracker, ik.solver.rightLeg, (ik.references.rightToes != null) ? ik.references.rightToes : ik.references.rightFoot, ik.references.root.forward, isLeft: false);
}
bool num = bodyTracker != null || (leftFootTracker != null && rightFootTracker != null);
VRIKRootController vRIKRootController = ik.references.root.GetComponent<VRIKRootController>();
if (num)
{
if (vRIKRootController == null)
{
vRIKRootController = ik.references.root.gameObject.AddComponent<VRIKRootController>();
}
vRIKRootController.Calibrate(data);
}
else if (vRIKRootController != null)
{
UnityEngine.Object.Destroy(vRIKRootController);
}
ik.solver.spine.minHeadHeight = 0f;
ik.solver.locomotion.weight = ((bodyTracker == null && leftFootTracker == null && rightFootTracker == null) ? 1f : 0f);
}
private static void CalibrateLeg(CalibrationData data, Transform tracker, IKSolverVR.Leg leg, Transform lastBone, Vector3 rootForward, bool isLeft)
{
string text = (isLeft ? "Left" : "Right");
Transform transform = ((leg.target == null) ? new GameObject(text + " Foot Target").transform : leg.target);
transform.parent = tracker;
if (isLeft)
{
data.leftFoot.SetTo(transform);
}
else
{
data.rightFoot.SetTo(transform);
}
leg.target = transform;
leg.positionWeight = 1f;
leg.rotationWeight = 1f;
Transform transform2 = ((leg.bendGoal == null) ? new GameObject(text + " Leg Bend Goal").transform : leg.bendGoal);
transform2.parent = tracker;
if (isLeft)
{
data.leftLegGoal.SetTo(transform2);
}
else
{
data.rightLegGoal.SetTo(transform2);
}
leg.bendGoal = transform2;
leg.bendGoalWeight = 1f;
}
}
}