[AlternateIKSystem] Add halfbody support.

This commit is contained in:
NotAKidoS 2023-07-14 01:55:30 -05:00
parent cd34aebeb0
commit 69b84775ec
5 changed files with 143 additions and 131 deletions

View file

@ -1,12 +1,13 @@
using RootMotion.FinalIK; using RootMotion.FinalIK;
using UnityEngine; using UnityEngine;
using Valve.VR;
using Object = UnityEngine.Object; using Object = UnityEngine.Object;
namespace NAK.AlternateIKSystem.IK; namespace NAK.AlternateIKSystem.IK;
internal static class IKCalibrator internal static class IKCalibrator
{ {
#region VRIK Setup #region VRIK Solver Setup
public static VRIK SetupVrIk(Animator animator) public static VRIK SetupVrIk(Animator animator)
{ {
@ -183,14 +184,39 @@ internal static class IKCalibrator
#endregion #endregion
// TODO: figure out proper Desktop & VR organization #region VRIK Calibration
public static void SetupHeadIKTargetDesktop(VRIK vrik)
public static void SetupHeadIKTarget(VRIK vrik, Transform parent = null)
{ {
// VR Camera may have Head IK Target from previous avatar
Transform existingTarget = parent?.Find("Head IK Target");
if (existingTarget != null)
Object.DestroyImmediate(existingTarget.gameObject);
parent ??= vrik.references.head;
// Lazy HeadIKTarget calibration // Lazy HeadIKTarget calibration
if (vrik.solver.spine.headTarget == null) vrik.solver.spine.headTarget = new GameObject("Head IK Target").transform;
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; vrik.solver.spine.headTarget.localRotation = Quaternion.identity;
vrik.solver.spine.headTarget.parent = parent;
vrik.solver.spine.headTarget.localPosition = Vector3.zero;
vrik.solver.spine.headTarget.localScale = Vector3.one;
} }
public static void SetupHandIKTarget(VRIK vrik, Transform handOffset, Transform handAnchor, bool isLeft)
{
handAnchor.SetParent(isLeft ? vrik.references.leftHand : vrik.references.rightHand);
handAnchor.localPosition = Vector3.zero;
handAnchor.localRotation = Quaternion.identity;
handAnchor.SetParent(handOffset, true);
handAnchor.localPosition = Vector3.zero;
if (isLeft)
vrik.solver.leftArm.target = handAnchor;
else
vrik.solver.rightArm.target = handAnchor;
}
#endregion
} }

View file

@ -41,9 +41,34 @@ internal abstract class IKHandler
public virtual void OnInitializeIk() { } public virtual void OnInitializeIk() { }
public virtual void OnPlayerScaled(float scaleDifference) { } public virtual void OnPlayerScaled(float scaleDifference)
{
VRIKUtils.ApplyScaleToVRIK
(
_vrik,
_locomotionData,
_scaleDifference = scaleDifference
);
}
public virtual void OnPlayerHandleMovementParent(CVRMovementParent currentParent) { } public virtual void OnPlayerHandleMovementParent(CVRMovementParent currentParent, Vector3 platformPivot)
{
Vector3 currentPosition = currentParent._referencePoint.position;
Quaternion currentRotation = Quaternion.Euler(0f, currentParent.transform.rotation.eulerAngles.y, 0f);
Vector3 deltaPosition = currentPosition - _movementPosition;
Quaternion deltaRotation = Quaternion.Inverse(_movementRotation) * currentRotation;
if (_movementParent == currentParent)
{
_solver.AddPlatformMotion(deltaPosition, deltaRotation, platformPivot);
_ikSimulatedRootAngle = Mathf.Repeat(_ikSimulatedRootAngle + deltaRotation.eulerAngles.y, 360f);
}
_movementParent = currentParent;
_movementPosition = currentPosition;
_movementRotation = currentRotation;
}
#endregion #endregion

View file

@ -29,42 +29,6 @@ internal class IKHandlerDesktop : IKHandler
_vrik.onPreSolverUpdate.AddListener(OnPreSolverUpdateDesktop); _vrik.onPreSolverUpdate.AddListener(OnPreSolverUpdateDesktop);
} }
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 #endregion
#region Weight Overrides #region Weight Overrides
@ -81,57 +45,6 @@ internal class IKHandlerDesktop : IKHandler
_solver.spine.positionWeight = 0f; _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 #endregion
#region VRIK Solver Events #region VRIK Solver Events

View file

@ -25,42 +25,33 @@ internal class IKHandlerHalfBody : IKHandler
shouldTrackRightLeg = false; shouldTrackRightLeg = false;
shouldTrackPelvis = false; shouldTrackPelvis = false;
shouldTrackLocomotion = true; shouldTrackLocomotion = true;
_vrik.onPreSolverUpdate.AddListener(OnPreSolverUpdateHalfBody);
} }
#endregion
public override void OnPlayerScaled(float scaleDifference) #region VRIK Solver Events
private void OnPreSolverUpdateHalfBody()
{ {
VRIKUtils.ApplyScaleToVRIK _solver.plantFeet = ModSettings.EntryPlantFeet.Value;
(
_vrik,
_locomotionData,
_scaleDifference = scaleDifference
);
}
public override void OnPlayerHandleMovementParent(CVRMovementParent currentParent) // Make root heading follow within a set limit
{ if (ModSettings.EntryBodyHeadingLimit.Value > 0)
// 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); float weightedAngleLimit = ModSettings.EntryBodyHeadingLimit.Value * _solver.locomotion.weight;
_ikSimulatedRootAngle = Mathf.Repeat(_ikSimulatedRootAngle + deltaRotation.eulerAngles.y, 360f); float currentRotation = IKManager.Instance.GetPlayerRotation().y;
} float deltaAngleRoot = Mathf.DeltaAngle(currentRotation, _ikSimulatedRootAngle);
// Store for next frame if (Mathf.Abs(deltaAngleRoot) > weightedAngleLimit)
_movementParent = currentParent; {
_movementPosition = currentPosition; deltaAngleRoot = Mathf.Sign(deltaAngleRoot) * weightedAngleLimit;
_movementRotation = currentRotation; _ikSimulatedRootAngle = Mathf.MoveTowardsAngle(_ikSimulatedRootAngle, currentRotation, Mathf.Abs(deltaAngleRoot) - weightedAngleLimit);
}
_solver.spine.rootHeadingOffset = deltaAngleRoot;
}
} }
#endregion #endregion

View file

@ -6,6 +6,7 @@ using NAK.AlternateIKSystem.VRIKHelpers;
using RootMotion.FinalIK; using RootMotion.FinalIK;
using UnityEngine; using UnityEngine;
using UnityEngine.Events; using UnityEngine.Events;
using Object = System.Object;
namespace NAK.AlternateIKSystem.IK; namespace NAK.AlternateIKSystem.IK;
@ -32,6 +33,18 @@ public class IKManager : MonoBehaviour
internal Transform _desktopCamera; internal Transform _desktopCamera;
internal Transform _vrCamera; internal Transform _vrCamera;
// Controller Info
private Transform _leftController;
private Transform _rightController;
private Transform _leftHandTarget;
private Transform _leftHandRotations;
private Transform _rightHandTarget;
private Transform _rightHandRotations;
// Hand Anchor Offsets
private Vector3 _handAnchorPositionOffset = new Vector3(-0.038f, 0.0389f, -0.138f);
private Vector3 _handAnchorRotationOffset = Vector3.zero;
// Avatar Info // Avatar Info
private Animator _animator; private Animator _animator;
private Transform _hipTransform; private Transform _hipTransform;
@ -39,6 +52,8 @@ public class IKManager : MonoBehaviour
// Animator Info // Animator Info
private int _animLocomotionLayer = -1; private int _animLocomotionLayer = -1;
private int _animIKPoseLayer = -1; private int _animIKPoseLayer = -1;
private const string _locomotionLayerName = "Locomotion/Emotes";
private const string _ikposeLayerName = "IKPose";
// Pose Info // Pose Info
private HumanPoseHandler _humanPoseHandler; private HumanPoseHandler _humanPoseHandler;
@ -60,8 +75,16 @@ public class IKManager : MonoBehaviour
_desktopCamera = PlayerSetup.Instance.desktopCamera.transform; _desktopCamera = PlayerSetup.Instance.desktopCamera.transform;
_vrCamera = PlayerSetup.Instance.vrCamera.transform; _vrCamera = PlayerSetup.Instance.vrCamera.transform;
} _leftController = PlayerSetup.Instance.vrLeftHandTracker.transform;
_rightController = PlayerSetup.Instance.vrRightHandTracker.transform;
// ouchie
_leftHandTarget = _leftController.Find("LeftHandTarget");
_leftHandRotations = _leftHandTarget.Find("LeftHandRotations");
_rightHandTarget = _rightController.Find("RightHandTarget");
_rightHandRotations = _rightHandTarget.Find("RightHandRotations");
}
private void Update() private void Update()
{ {
BodyControl.Update(); BodyControl.Update();
@ -91,8 +114,8 @@ public class IKManager : MonoBehaviour
_animator.cullingMode = AnimatorCullingMode.AlwaysAnimate; _animator.cullingMode = AnimatorCullingMode.AlwaysAnimate;
_animIKPoseLayer = _animator.GetLayerIndex("IKPose"); _animIKPoseLayer = _animator.GetLayerIndex(_ikposeLayerName);
_animLocomotionLayer = _animator.GetLayerIndex("Locomotion/Emotes"); _animLocomotionLayer = _animator.GetLayerIndex(_locomotionLayerName);
_hipTransform = _animator.GetBoneTransform(HumanBodyBones.Hips); _hipTransform = _animator.GetBoneTransform(HumanBodyBones.Hips);
@ -160,7 +183,7 @@ public class IKManager : MonoBehaviour
if (!_isAvatarInitialized) if (!_isAvatarInitialized)
return false; return false;
_ikHandler?.OnPlayerHandleMovementParent(movementParent); _ikHandler?.OnPlayerHandleMovementParent(movementParent, GetPlayerPosition());
return true; return true;
} }
@ -184,7 +207,7 @@ public class IKManager : MonoBehaviour
IKCalibrator.ConfigureDesktopVrIk(_vrik); IKCalibrator.ConfigureDesktopVrIk(_vrik);
_ikHandler = new IKHandlerDesktop(_vrik); _ikHandler = new IKHandlerDesktop(_vrik);
IKCalibrator.SetupHeadIKTargetDesktop(_vrik); IKCalibrator.SetupHeadIKTarget(_vrik);
InitializeIkGeneral(); InitializeIkGeneral();
@ -197,7 +220,17 @@ public class IKManager : MonoBehaviour
IKCalibrator.ConfigureHalfBodyVrIk(_vrik); IKCalibrator.ConfigureHalfBodyVrIk(_vrik);
_ikHandler = new IKHandlerHalfBody(_vrik); _ikHandler = new IKHandlerHalfBody(_vrik);
IKCalibrator.SetupHeadIKTarget(_vrik, _vrCamera);
IKCalibrator.SetupHandIKTarget(_vrik, _leftHandTarget, _leftHandRotations, true);
IKCalibrator.SetupHandIKTarget(_vrik, _rightHandTarget, _rightHandRotations, false);
// Configure controller offsets
_leftHandTarget.localPosition = _handAnchorPositionOffset;
_leftHandTarget.localEulerAngles = _handAnchorRotationOffset;
_rightHandTarget.localPosition = Vector3.Scale(_handAnchorPositionOffset, new Vector3(-1, 1, 1));
_rightHandTarget.localEulerAngles = Vector3.Scale(_handAnchorRotationOffset, new Vector3(1, -1, -1));
InitializeIkGeneral(); InitializeIkGeneral();
_ikHandler.OnInitializeIk(); _ikHandler.OnInitializeIk();
@ -226,6 +259,30 @@ public class IKManager : MonoBehaviour
#endregion #endregion
#region Public Methods
public Vector3 GetPlayerPosition()
{
if (!MetaPort.Instance.isUsingVr)
return transform.position;
Vector3 vrPosition = _vrCamera.transform.position;
vrPosition.y = transform.position.y;
return vrPosition;
}
public Quaternion GetPlayerRotation()
{
if (!MetaPort.Instance.isUsingVr)
return transform.rotation;
Vector3 vrForward = _vrCamera.transform.forward;
vrForward.y = 0f;
return Quaternion.LookRotation(vrForward, Vector3.up);
}
#endregion
#region VRIK Solver Events General #region VRIK Solver Events General
private void OnPreSolverUpdateGeneral() private void OnPreSolverUpdateGeneral()