[AlternateIKSystem] Slight cleanup

This commit is contained in:
NotAKidoS 2023-07-13 23:28:04 -05:00
parent 684b330a4c
commit cd34aebeb0
10 changed files with 380 additions and 203 deletions

View file

@ -33,7 +33,7 @@ public class BodyControl
public static bool TrackingLeftLeg = true;
public static bool TrackingRightLeg = true;
public static bool TrackingLocomotion = true;
public static float TrackingPositionWeight = 1f;
public static float TrackingIKPositionWeight = 1f;
// TODO: decide if this is considered "Tracking Controls"
public static float TrackingMaxRootAngle = 0f;
@ -52,6 +52,8 @@ public class BodyControl
#endregion
#region Public Methods
public void Update()
{
TrackingAll = ShouldTrackAll();
@ -59,6 +61,8 @@ public class BodyControl
AvatarUpright = GetPlayerUpright();
}
#endregion
#region Private Methods
private static bool ShouldTrackAll()
@ -87,20 +91,26 @@ public class BodyControl
#region Solver Weight Helpers
public static void SetHeadWeight(IKSolverVR.Spine spine, LookAtIK lookAtIk, float weight)
// I am unsure on this...?
public static void SetLookAtWeight(LookAtIK lookAtIk, float weight)
{
if (lookAtIk != null)
lookAtIk.solver.IKPositionWeight = weight;
}
public static void SetHeadWeight(IKSolverVR.Spine spine, float weight)
{
spine.positionWeight = weight;
spine.rotationWeight = weight;
if (lookAtIk != null)
lookAtIk.solver.IKPositionWeight = weight;
}
public static void SetArmWeight(IKSolverVR.Arm arm, float weight)
{
arm.positionWeight = weight;
arm.rotationWeight = weight;
arm.shoulderRotationWeight = weight;
arm.shoulderTwistWeight = weight;
//arm.shoulderRotationWeight = weight;
//arm.shoulderTwistWeight = weight;
arm.bendGoalWeight = arm.bendGoal != null ? weight : 0f;
}
@ -117,5 +127,21 @@ public class BodyControl
spine.pelvisRotationWeight = weight;
}
public static void SetLocomotionWeight(IKSolverVR.Locomotion locomotion, float weight)
{
locomotion.weight = weight;
}
public static void SetIKPositionWeight(IKSolverVR solver, float weight)
{
solver.IKPositionWeight = weight;
}
public static void SetIKPositionWeight(LookAtIK lookAtIk, float weight)
{
if (lookAtIk != null)
lookAtIk.solver.IKPositionWeight = weight;
}
#endregion
}

View file

@ -1,14 +1,17 @@
using RootMotion.FinalIK;
using UnityEngine;
using Object = UnityEngine.Object;
namespace NAK.AlternateIKSystem.IK;
internal static class IKCalibrator
{
#region VRIK Setup
public static VRIK SetupVrIk(Animator animator)
{
if (animator.gameObject.TryGetComponent(out VRIK vrik))
UnityEngine.Object.DestroyImmediate(vrik);
Object.DestroyImmediate(vrik);
vrik = animator.gameObject.AddComponent<VRIK>();
vrik.AutoDetectReferences();
@ -66,6 +69,10 @@ internal static class IKCalibrator
// hack to prevent death
vrik.fixTransforms = !animator.enabled;
// Avatar Motion Tweaker uses this hack!
vrik.solver.leftLeg.useAnimatedBendNormal = false;
vrik.solver.rightLeg.useAnimatedBendNormal = false;
// purposefully initiating early
vrik.solver.Initiate(vrik.transform);
vrik.solver.Reset();
@ -106,6 +113,10 @@ internal static class IKCalibrator
twistRelaxer.parentChildCrossfade = 0.8f;
}
#endregion
#region VRIK Configuration
public static void ConfigureDesktopVrIk(VRIK vrik)
{
// From DesktopVRIK
@ -170,6 +181,8 @@ internal static class IKCalibrator
vrik.solver.plantFeet = false;
}
#endregion
// TODO: figure out proper Desktop & VR organization
public static void SetupHeadIKTargetDesktop(VRIK vrik)
{

View file

@ -5,32 +5,150 @@ using UnityEngine;
namespace NAK.AlternateIKSystem.IK.IKHandlers;
internal class IKHandler
internal abstract class IKHandler
{
#region Variables
internal VRIK _vrik;
internal IKSolverVR _solver;
internal bool shouldTrackAll = true;
internal bool shouldTrackHead = true;
internal bool shouldTrackLeftArm = true;
internal bool shouldTrackRightArm = true;
internal bool shouldTrackLeftLeg = true;
internal bool shouldTrackRightLeg = true;
internal bool shouldTrackPelvis = true;
internal bool shouldTrackLocomotion = true;
// VRIK Calibration Info
internal VRIKLocomotionData _locomotionData;
// Last Movement Parent Info
internal Vector3 _movementPosition;
internal Quaternion _movementRotation;
internal CVRMovementParent _movementParent;
#region Virtual Methods
// Solver Info
internal float _scaleDifference = 1f;
internal float _locomotionWeight = 1f;
internal float _ikSimulatedRootAngle = 0f;
internal bool _wasTrackingLocomotion = false;
public virtual void OnInitializeIk()
#endregion
#region Virtual Game Methods
public virtual void OnInitializeIk() { }
public virtual void OnPlayerScaled(float scaleDifference) { }
public virtual void OnPlayerHandleMovementParent(CVRMovementParent currentParent) { }
#endregion
#region Virtual IK Weights
public virtual void UpdateWeights()
{
if (!shouldTrackAll)
return;
if (shouldTrackHead)
Update_HeadWeight();
if (shouldTrackLeftArm)
Update_LeftArmWeight();
if (shouldTrackRightArm)
Update_RightArmWeight();
if (shouldTrackLeftLeg)
Update_LeftLegWeight();
if (shouldTrackRightLeg)
Update_RightLegWeight();
if (shouldTrackPelvis)
Update_PelvisWeight();
if (shouldTrackLocomotion)
{
Update_LocomotionWeight();
ResetSolverIfNeeded();
}
Update_IKPositionWeight();
}
public virtual void OnUpdate()
protected virtual void Update_HeadWeight()
{
float targetWeight = GetTargetWeight(BodyControl.TrackingHead, true);
BodyControl.SetHeadWeight(_solver.spine, targetWeight);
BodyControl.SetLookAtWeight(IKManager.lookAtIk, targetWeight);
}
public virtual void OnPlayerScaled(float scaleDifference, VRIKCalibrationData calibrationData)
protected virtual void Update_LeftArmWeight()
{
float leftArmWeight = GetTargetWeight(BodyControl.TrackingLeftArm, _solver.leftArm.target != null);
BodyControl.SetArmWeight(_solver.leftArm, leftArmWeight);
}
public virtual void OnPlayerHandleMovementParent(CVRMovementParent currentParent)
protected virtual void Update_RightArmWeight()
{
float rightArmWeight = GetTargetWeight(BodyControl.TrackingRightArm, _solver.rightArm.target != null);
BodyControl.SetArmWeight(_solver.rightArm, rightArmWeight);
}
protected virtual void Update_LeftLegWeight()
{
float leftLegWeight = GetTargetWeight(BodyControl.TrackingLeftLeg, _solver.leftLeg.target != null);
BodyControl.SetLegWeight(_solver.leftLeg, leftLegWeight);
}
protected virtual void Update_RightLegWeight()
{
float rightLegWeight = GetTargetWeight(BodyControl.TrackingRightLeg, _solver.rightLeg.target != null);
BodyControl.SetLegWeight(_solver.rightLeg, rightLegWeight);
}
protected virtual void Update_PelvisWeight()
{
float pelvisWeight = GetTargetWeight(BodyControl.TrackingPelvis, _solver.spine.pelvisTarget != null);
BodyControl.SetPelvisWeight(_solver.spine, pelvisWeight);
}
protected virtual void Update_LocomotionWeight()
{
_locomotionWeight = Mathf.Lerp(_locomotionWeight, BodyControl.TrackingLocomotion ? 1f : 0f,
Time.deltaTime * ModSettings.EntryIKLerpSpeed.Value * 2f);
BodyControl.SetLocomotionWeight(_solver.locomotion, _locomotionWeight);
}
protected virtual void Update_IKPositionWeight()
{
float ikPositionWeight = BodyControl.TrackingAll ? BodyControl.TrackingIKPositionWeight : 0f;
BodyControl.SetIKPositionWeight(_solver, ikPositionWeight);
BodyControl.SetIKPositionWeight(IKManager.lookAtIk, ikPositionWeight);
}
protected virtual float GetTargetWeight(bool isTracking, bool hasTarget)
{
return isTracking && hasTarget ? 1f : 0f;
}
#endregion
#region Private Methods
private void ResetSolverIfNeeded()
{
if (_wasTrackingLocomotion == BodyControl.TrackingLocomotion)
return;
_wasTrackingLocomotion = BodyControl.TrackingLocomotion;
VRIKUtils.ResetToInitialFootsteps(_vrik, _locomotionData, _scaleDifference);
_solver.Reset();
}
#endregion

View file

@ -2,7 +2,6 @@
using NAK.AlternateIKSystem.VRIKHelpers;
using RootMotion.FinalIK;
using UnityEngine;
using UnityEngine.Events;
namespace NAK.AlternateIKSystem.IK.IKHandlers;
@ -14,44 +13,44 @@ internal class IKHandlerDesktop : IKHandler
_solver = vrik.solver;
}
#region Overrides
#region Game Overrides
public override void OnInitializeIk()
{
_vrik.onPreSolverUpdate.AddListener(new UnityAction(OnPreSolverUpdate));
// Default tracking for Desktop
shouldTrackHead = true;
shouldTrackLeftArm = false;
shouldTrackRightArm = false;
shouldTrackLeftLeg = false;
shouldTrackRightLeg = false;
shouldTrackPelvis = false;
shouldTrackLocomotion = true;
_vrik.onPreSolverUpdate.AddListener(OnPreSolverUpdateDesktop);
}
public override void OnUpdate()
{
// Reset avatar local position
_vrik.transform.localPosition = Vector3.zero;
_vrik.transform.localRotation = Quaternion.identity;
UpdateWeights();
}
public override void OnPlayerScaled(float scaleDifference, VRIKCalibrationData calibrationData)
public override void OnPlayerScaled(float scaleDifference)
{
VRIKUtils.ApplyScaleToVRIK
(
_vrik,
calibrationData,
scaleDifference
_locomotionData,
_scaleDifference = scaleDifference
);
}
public override void OnPlayerHandleMovementParent(CVRMovementParent currentParent)
{
// Get current position
var currentPosition = currentParent._referencePoint.position;
var currentRotation = Quaternion.Euler(0f, currentParent.transform.rotation.eulerAngles.y, 0f);
Vector3 currentPosition = currentParent._referencePoint.position;
Quaternion currentRotation = Quaternion.Euler(0f, currentParent.transform.rotation.eulerAngles.y, 0f);
// Convert to delta position (how much changed since last frame)
var deltaPosition = currentPosition - _movementPosition;
var deltaRotation = Quaternion.Inverse(_movementRotation) * currentRotation;
Vector3 deltaPosition = currentPosition - _movementPosition;
Quaternion deltaRotation = Quaternion.Inverse(_movementRotation) * currentRotation;
// Desktop pivots from playerlocal transform
var platformPivot = IKManager.Instance.transform.position;
Vector3 platformPivot = IKManager.Instance.transform.position;
// Prevent targeting other parent position
if (_movementParent == currentParent)
@ -68,11 +67,76 @@ internal class IKHandlerDesktop : IKHandler
#endregion
#region Weight Overrides
public override void UpdateWeights()
{
// Reset avatar local position
_vrik.transform.localPosition = Vector3.zero;
_vrik.transform.localRotation = Quaternion.identity;
base.UpdateWeights();
// Desktop should never have head position weight
_solver.spine.positionWeight = 0f;
}
protected override void Update_HeadWeight()
{
float targetWeight = GetTargetWeight(BodyControl.TrackingHead, true);
BodyControl.SetHeadWeight(_solver.spine, targetWeight);
BodyControl.SetLookAtWeight(IKManager.lookAtIk, targetWeight);
}
protected override void Update_LeftArmWeight()
{
float leftArmWeight = GetTargetWeight(BodyControl.TrackingLeftArm, _solver.leftArm.target != null);
BodyControl.SetArmWeight(_solver.leftArm, leftArmWeight);
}
protected override void Update_RightArmWeight()
{
float rightArmWeight = GetTargetWeight(BodyControl.TrackingRightArm, _solver.rightArm.target != null);
BodyControl.SetArmWeight(_solver.rightArm, rightArmWeight);
}
protected override void Update_LeftLegWeight()
{
float leftLegWeight = GetTargetWeight(BodyControl.TrackingLeftLeg, _solver.leftLeg.target != null);
BodyControl.SetLegWeight(_solver.leftLeg, leftLegWeight);
}
protected override void Update_RightLegWeight()
{
float rightLegWeight = GetTargetWeight(BodyControl.TrackingRightLeg, _solver.rightLeg.target != null);
BodyControl.SetLegWeight(_solver.rightLeg, rightLegWeight);
}
protected override void Update_PelvisWeight()
{
float pelvisWeight = GetTargetWeight(BodyControl.TrackingPelvis, _solver.spine.pelvisTarget != null);
BodyControl.SetPelvisWeight(_solver.spine, pelvisWeight);
}
protected override void Update_LocomotionWeight()
{
_locomotionWeight = Mathf.Lerp(_locomotionWeight, BodyControl.TrackingLocomotion ? 1f : 0f,
Time.deltaTime * ModSettings.EntryIKLerpSpeed.Value * 2f);
BodyControl.SetLocomotionWeight(_solver.locomotion, _locomotionWeight);
}
protected override void Update_IKPositionWeight()
{
float ikPositionWeight = BodyControl.TrackingAll ? BodyControl.TrackingIKPositionWeight : 0f;
BodyControl.SetIKPositionWeight(_solver, ikPositionWeight);
BodyControl.SetIKPositionWeight(IKManager.lookAtIk, ikPositionWeight);
}
#endregion
#region VRIK Solver Events
private float _ikSimulatedRootAngle = 0f;
private void OnPreSolverUpdate()
private void OnPreSolverUpdateDesktop()
{
_solver.plantFeet = ModSettings.EntryPlantFeet.Value;
@ -98,7 +162,7 @@ internal class IKHandlerDesktop : IKHandler
deltaAngleRoot = Mathf.Sign(deltaAngleRoot) * weightedAngleLimit;
_ikSimulatedRootAngle = Mathf.MoveTowardsAngle(_ikSimulatedRootAngle, IKManager.Instance.transform.eulerAngles.y, absDeltaAngleRoot - weightedAngleLimit);
}
_solver.spine.rootHeadingOffset = deltaAngleRoot;
if (ModSettings.EntryPelvisHeadingWeight.Value > 0)
@ -115,59 +179,4 @@ internal class IKHandlerDesktop : IKHandler
}
#endregion
#region Private Methods
private float _locomotionWeight = 1f;
private void UpdateWeights()
{
// Lerp locomotion weight, lerp to BodyControl.TrackingUpright???
float targetWeight =
(BodyControl.TrackingAll && BodyControl.TrackingLocomotion && BodyControl.AvatarUpright > 0.8f)
? 1f
: 0.0f;
_locomotionWeight = Mathf.Lerp(_locomotionWeight, targetWeight, Time.deltaTime * 20f);
if (BodyControl.TrackingAll)
{
_vrik.enabled = true;
_solver.IKPositionWeight = BodyControl.TrackingPositionWeight;
_solver.locomotion.weight = _locomotionWeight;
_solver.spine.maxRootAngle = BodyControl.TrackingMaxRootAngle;
BodyControl.SetHeadWeight(_solver.spine, IKManager.lookAtIk, BodyControl.TrackingHead ? 1f : 0f);
BodyControl.SetArmWeight(_solver.leftArm, BodyControl.TrackingLeftArm && _solver.leftArm.target != null ? 1f : 0f);
BodyControl.SetArmWeight(_solver.rightArm, BodyControl.TrackingRightArm && _solver.rightArm.target != null ? 1f : 0f);
BodyControl.SetLegWeight(_solver.leftLeg, BodyControl.TrackingLeftLeg && _solver.leftLeg.target != null ? 1f : 0f);
BodyControl.SetLegWeight(_solver.rightLeg, BodyControl.TrackingRightLeg && _solver.rightLeg.target != null ? 1f : 0f);
BodyControl.SetPelvisWeight(_solver.spine, BodyControl.TrackingPelvis && _solver.spine.pelvisTarget != null ? 1f : 0f);
}
else
{
_vrik.enabled = false;
_solver.IKPositionWeight = 0f;
_solver.locomotion.weight = 0f;
_solver.spine.maxRootAngle = 0f;
BodyControl.SetHeadWeight(_solver.spine, IKManager.lookAtIk, 0f);
BodyControl.SetArmWeight(_solver.leftArm, 0f);
BodyControl.SetArmWeight(_solver.rightArm, 0f);
BodyControl.SetLegWeight(_solver.leftLeg, 0f);
BodyControl.SetLegWeight(_solver.rightLeg, 0f);
BodyControl.SetPelvisWeight(_solver.spine, 0f);
}
// Desktop should never have head position weight
_solver.spine.positionWeight = 0f;
// Avatar Motion Tweaker uses this hack, so we must reset it
_solver.leftLeg.useAnimatedBendNormal = false;
_solver.rightLeg.useAnimatedBendNormal = false;
}
#endregion
}

View file

@ -0,0 +1,67 @@
using ABI.CCK.Components;
using NAK.AlternateIKSystem.VRIKHelpers;
using RootMotion.FinalIK;
using UnityEngine;
namespace NAK.AlternateIKSystem.IK.IKHandlers;
internal class IKHandlerHalfBody : IKHandler
{
public IKHandlerHalfBody(VRIK vrik)
{
_vrik = vrik;
_solver = vrik.solver;
}
#region Game Overrides
public override void OnInitializeIk()
{
// Default tracking for HalfBody
shouldTrackHead = true;
shouldTrackLeftArm = true;
shouldTrackRightArm = true;
shouldTrackLeftLeg = false;
shouldTrackRightLeg = false;
shouldTrackPelvis = false;
shouldTrackLocomotion = true;
}
public override void OnPlayerScaled(float scaleDifference)
{
VRIKUtils.ApplyScaleToVRIK
(
_vrik,
_locomotionData,
_scaleDifference = scaleDifference
);
}
public override void OnPlayerHandleMovementParent(CVRMovementParent currentParent)
{
// Get current position
Vector3 currentPosition = currentParent._referencePoint.position;
Quaternion currentRotation = Quaternion.Euler(0f, currentParent.transform.rotation.eulerAngles.y, 0f);
// Convert to delta position (how much changed since last frame)
Vector3 deltaPosition = currentPosition - _movementPosition;
Quaternion deltaRotation = Quaternion.Inverse(_movementRotation) * currentRotation;
// Desktop pivots from playerlocal transform
Vector3 platformPivot = IKManager.Instance.transform.position;
// Prevent targeting other parent position
if (_movementParent == currentParent)
{
_solver.AddPlatformMotion(deltaPosition, deltaRotation, platformPivot);
_ikSimulatedRootAngle = Mathf.Repeat(_ikSimulatedRootAngle + deltaRotation.eulerAngles.y, 360f);
}
// Store for next frame
_movementParent = currentParent;
_movementPosition = currentPosition;
_movementRotation = currentRotation;
}
#endregion
}

View file

@ -1,7 +1,6 @@
using ABI.CCK.Components;
using ABI_RC.Core.Player;
using ABI_RC.Core.Savior;
using ABI_RC.Systems.MovementSystem;
using NAK.AlternateIKSystem.IK.IKHandlers;
using NAK.AlternateIKSystem.VRIKHelpers;
using RootMotion.FinalIK;
@ -14,6 +13,8 @@ public class IKManager : MonoBehaviour
{
public static IKManager Instance;
#region Variables
public BodyControl BodyControl = new BodyControl();
public static VRIK vrik => _vrik;
@ -44,8 +45,9 @@ public class IKManager : MonoBehaviour
private HumanPose _humanPose;
private HumanPose _humanPoseInitial;
// VRIK Calibration Info
private VRIKCalibrationData _calibrationData;
#endregion
#region Unity Methods
private void Start()
{
@ -67,9 +69,11 @@ public class IKManager : MonoBehaviour
if (!_isAvatarInitialized)
return;
_ikHandler?.OnUpdate();
_ikHandler?.UpdateWeights();
}
#endregion
#region Avatar Events
public void OnAvatarInitialized(GameObject inAvatar)
@ -137,7 +141,7 @@ public class IKManager : MonoBehaviour
if (!_isAvatarInitialized)
return false;
_ikHandler?.OnPlayerScaled(scaleDifference, _calibrationData);
_ikHandler?.OnPlayerScaled(scaleDifference);
return true;
}
@ -164,7 +168,7 @@ public class IKManager : MonoBehaviour
{
if (!_isAvatarInitialized)
return false;
_vrik?.solver.Reset();
return true;
}
@ -175,23 +179,31 @@ public class IKManager : MonoBehaviour
private void InitializeDesktopIk()
{
PreSetupIkGeneral();
SetupIkGeneral();
IKCalibrator.ConfigureDesktopVrIk(_vrik);
_ikHandler = new IKHandlerDesktop(_vrik);
IKCalibrator.SetupHeadIKTargetDesktop(_vrik);
InitializeIkGeneral();
_ikHandler = new IKHandlerDesktop(_vrik);
_ikHandler.OnInitializeIk();
}
private void InitializeHalfBodyIk()
{
PreSetupIkGeneral();
SetupIkGeneral();
IKCalibrator.ConfigureHalfBodyVrIk(_vrik);
_ikHandler = new IKHandlerHalfBody(_vrik);
InitializeIkGeneral();
_ikHandler.OnInitializeIk();
}
private void PreSetupIkGeneral()
private void SetupIkGeneral()
{
SetAvatarPose(AvatarPose.Default);
_vrik = IKCalibrator.SetupVrIk(_animator);
@ -201,12 +213,12 @@ public class IKManager : MonoBehaviour
{
SetAvatarPose(AvatarPose.IKPose);
VRIKUtils.CalculateInitialIKScaling(_vrik, ref _calibrationData);
VRIKUtils.CalculateInitialFootsteps(_vrik, ref _calibrationData);
VRIKUtils.CalculateInitialIKScaling(_vrik, ref _ikHandler._locomotionData);
VRIKUtils.CalculateInitialFootsteps(_vrik, ref _ikHandler._locomotionData);
VRIKUtils.ApplyScaleToVRIK(_vrik, _calibrationData, 1f);
_vrik.solver.Initiate(_vrik.transform); // initiate a second time
VRIKUtils.InitiateVRIKSolver(_vrik); // initiate again to store ikpose
VRIKUtils.ApplyScaleToVRIK(_vrik, _ikHandler._locomotionData, 1f);
_vrik.onPreSolverUpdate.AddListener(new UnityAction(OnPreSolverUpdateGeneral));
_vrik.onPostSolverUpdate.AddListener(new UnityAction(OnPostSolverUpdateGeneral));
@ -263,6 +275,7 @@ public class IKManager : MonoBehaviour
_hipTransform.rotation = hipRot;
}
// "NetIk Pass", or "Additional Humanoid Pass" hack
private void OnPostSolverUpdateGeneral()
{
Vector3 hipPos = _hipTransform.position;

View file

@ -2,28 +2,22 @@
namespace NAK.AlternateIKSystem.VRIKHelpers;
public struct VRIKCalibrationData
public struct VRIKLocomotionData
{
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 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;

View file

@ -5,72 +5,18 @@ namespace NAK.AlternateIKSystem.VRIKHelpers;
public static class VRIKUtils
{
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)
public static void CalculateInitialIKScaling(VRIK vrik, ref VRIKLocomotionData locomotionData)
{
// 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);
locomotionData.InitialFootDistance = footDistance * 0.5f;
locomotionData.InitialStepThreshold = footDistance * scaleModifier;
locomotionData.InitialStepHeight = Vector3.Distance(vrik.references.leftFoot.position, vrik.references.leftCalf.position) * 0.2f;
}
public static void CalculateInitialFootsteps(VRIK vrik, ref VRIKCalibrationData calibrationData)
public static void CalculateInitialFootsteps(VRIK vrik, ref VRIKLocomotionData locomotionData)
{
Transform root = vrik.references.root;
Transform leftFoot = vrik.references.leftFoot;
@ -80,46 +26,37 @@ public static class VRIKUtils
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;
locomotionData.InitialFootPosLeft = root.InverseTransformPoint(leftFoot.position);
locomotionData.InitialFootPosRight = root.InverseTransformPoint(rightFoot.position);
locomotionData.InitialFootRotLeft = Quaternion.Inverse(rootWorldRot) * leftFoot.rotation;
locomotionData.InitialFootRotRight = Quaternion.Inverse(rootWorldRot) * rightFoot.rotation;
}
public static void ResetToInitialFootsteps(VRIK vrik, VRIKCalibrationData calibrationData, float scaleModifier)
public static void ResetToInitialFootsteps(VRIK vrik, VRIKLocomotionData locomotionData, float scaleModifier)
{
var locomotionSolver = vrik.solver.locomotion;
Transform root = vrik.references.root;
Quaternion rootWorldRot = vrik.references.root.rotation;
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);
// hack, use parent transform instead as setting feet position moves root (root.parent), but does not work for VR
var footsteps = vrik.solver.locomotion.footsteps;
footsteps[0].Reset(rootWorldRot, root.TransformPoint(locomotionData.InitialFootPosLeft * scaleModifier),
rootWorldRot * locomotionData.InitialFootRotLeft);
footsteps[1].Reset(rootWorldRot, root.TransformPoint(locomotionData.InitialFootPosRight * scaleModifier),
rootWorldRot * locomotionData.InitialFootRotRight);
}
public static void ApplyScaleToVRIK(VRIK vrik, VRIKCalibrationData calibrationData, float scaleModifier)
public static void ApplyScaleToVRIK(VRIK vrik, VRIKLocomotionData locomotionData, float scaleModifier)
{
var locomotionSolver = vrik.solver.locomotion;
locomotionSolver.footDistance = calibrationData.InitialFootDistance * scaleModifier;
locomotionSolver.stepThreshold = calibrationData.InitialStepThreshold * scaleModifier;
ScaleStepHeight(locomotionSolver.stepHeight, calibrationData.InitialStepHeight * scaleModifier);
IKSolverVR.Locomotion locomotionSolver = vrik.solver.locomotion;
locomotionSolver.footDistance = locomotionData.InitialFootDistance * scaleModifier;
locomotionSolver.stepThreshold = locomotionData.InitialStepThreshold * scaleModifier;
ScaleStepHeight(locomotionSolver.stepHeight, locomotionData.InitialStepHeight * scaleModifier);
}
static void ScaleStepHeight(AnimationCurve stepHeightCurve, float mag)
private static void ScaleStepHeight(AnimationCurve stepHeightCurve, float mag)
{
Keyframe[] keyframes = stepHeightCurve.keys;
var 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);
}
}

View file

@ -25,7 +25,7 @@ public static class BTKUIAddon
Page generalIKPage = parentCategory.AddPage("General IK Settings", "", "Configure the settings for general IK.", ModSettings.SettingsCategory);
generalIKPage.MenuTitle = "General IK Settings";
Category generalIKCategory = generalIKPage.AddCategory(generalIKPage.MenuTitle);
// General Settings
AddMelonToggle(ref generalIKCategory, ModSettings.EntryPlantFeet);

View file

@ -26,7 +26,7 @@ public class AlternateIKSystem : MelonMod
private static void InitializeIntegration(string modName, Action integrationAction)
{
if (RegisteredMelons.All(it => it.Info.Name != modName))
if (RegisteredMelons.All(it => it.Info.Name != modName))
return;
Logger.Msg($"Initializing {modName} integration.");