fix movement parent ik

This commit is contained in:
NotAKidoS 2023-03-08 21:28:53 -06:00
parent 51ff6e8333
commit 81a84c125c
3 changed files with 105 additions and 43 deletions

View file

@ -1,10 +1,13 @@
using ABI_RC.Core.Player; using ABI.CCK.Components;
using ABI_RC.Core.Player;
using ABI_RC.Systems.IK.SubSystems; using ABI_RC.Systems.IK.SubSystems;
using ABI_RC.Systems.MovementSystem; using ABI_RC.Systems.MovementSystem;
using HarmonyLib;
using RootMotion.FinalIK; using RootMotion.FinalIK;
using System.Reflection; using System.Reflection;
using UnityEngine; using UnityEngine;
namespace NAK.Melons.DesktopVRIK; namespace NAK.Melons.DesktopVRIK;
public class DesktopVRIK : MonoBehaviour public class DesktopVRIK : MonoBehaviour
@ -16,127 +19,179 @@ public class DesktopVRIK : MonoBehaviour
public bool public bool
Setting_Enabled = true, Setting_Enabled = true,
Setting_PlantFeet = true; Setting_PlantFeet = true;
public float public float
Setting_BodyLeanWeight, Setting_BodyLeanWeight,
Setting_BodyHeadingLimit, Setting_BodyHeadingLimit,
Setting_PelvisHeadingWeight, Setting_PelvisHeadingWeight,
Setting_ChestHeadingWeight; Setting_ChestHeadingWeight;
// Internal Stuff // DesktopVRIK References
bool ps_emoteIsPlaying; bool _isEmotePlaying;
float ik_SimulatedRootAngle; float _simulatedRootAngle;
Transform desktopCameraTransform; Transform _avatarTransform;
static readonly FieldInfo ms_isGrounded = typeof(MovementSystem).GetField("_isGrounded", BindingFlags.NonPublic | BindingFlags.Instance); Transform _cameraTransform;
// IK Stuff
VRIK _vrik;
LookAtIK _lookAtIK;
IKSolverVR _ikSolver;
// Movement System Stuff
MovementSystem movementSystem;
Traverse _isGroundedTraverse;
// Movement Parent Stuff
private Vector3 _previousPosition;
private Quaternion _previousRotation;
private Traverse _currentParentTraverse;
static readonly FieldInfo _referencePointField = typeof(CVRMovementParent).GetField("_referencePoint", BindingFlags.NonPublic | BindingFlags.Instance);
void Start() void Start()
{ {
desktopCameraTransform = PlayerSetup.Instance.desktopCamera.transform;
Calibrator = new DesktopVRIKCalibrator();
Instance = this; Instance = this;
Calibrator = new DesktopVRIKCalibrator();
DesktopVRIKMod.UpdateAllSettings(); DesktopVRIKMod.UpdateAllSettings();
movementSystem = GetComponent<MovementSystem>();
_cameraTransform = PlayerSetup.Instance.desktopCamera.transform;
_isGroundedTraverse = Traverse.Create(movementSystem).Field("_isGrounded");
_currentParentTraverse = Traverse.Create(movementSystem).Field("_currentParent");
} }
public void OnSetupAvatarDesktop() public void OnSetupAvatarDesktop()
{ {
if (!Setting_Enabled) return; if (!Setting_Enabled) return;
Calibrator.CalibrateDesktopVRIK(); Calibrator.CalibrateDesktopVRIK();
ResetDesktopVRIK();
_vrik = Calibrator.vrik;
_lookAtIK = Calibrator.lookAtIK;
_ikSolver = Calibrator.vrik.solver;
_avatarTransform = Calibrator.avatarTransform;
_simulatedRootAngle = transform.eulerAngles.y;
} }
public bool OnSetupIKScaling(float scaleDifference) public bool OnSetupIKScaling(float scaleDifference)
{ {
if (Calibrator.vrik == null) return false; if (_vrik == null) return false;
VRIKUtils.ApplyScaleToVRIK VRIKUtils.ApplyScaleToVRIK
( (
Calibrator.vrik, _vrik,
Calibrator.initialFootDistance, Calibrator.initialFootDistance,
Calibrator.initialStepThreshold, Calibrator.initialStepThreshold,
Calibrator.initialStepHeight, Calibrator.initialStepHeight,
scaleDifference scaleDifference
); );
_ikSolver?.Reset();
ResetDesktopVRIK(); ResetDesktopVRIK();
return true; return true;
} }
public void OnPlayerSetupUpdate(bool isEmotePlaying) public void OnPlayerSetupUpdate(bool isEmotePlaying)
{ {
bool changed = isEmotePlaying != ps_emoteIsPlaying; bool changed = isEmotePlaying != _isEmotePlaying;
if (!changed) return; if (!changed) return;
ps_emoteIsPlaying = isEmotePlaying; _isEmotePlaying = isEmotePlaying;
Calibrator.avatarTransform.localPosition = Vector3.zero; _avatarTransform.localPosition = Vector3.zero;
Calibrator.avatarTransform.localRotation = Quaternion.identity; _avatarTransform.localRotation = Quaternion.identity;
if (Calibrator.lookAtIK != null) if (_lookAtIK != null)
Calibrator.lookAtIK.enabled = !isEmotePlaying; _lookAtIK.enabled = !isEmotePlaying;
BodySystem.TrackingEnabled = !isEmotePlaying; BodySystem.TrackingEnabled = !isEmotePlaying;
Calibrator.vrik.solver?.Reset(); _vrik.solver?.Reset();
ResetDesktopVRIK(); ResetDesktopVRIK();
} }
public bool OnPlayerSetupResetIk()
{
if (_vrik == null) return false;
CVRMovementParent currentParent = _currentParentTraverse.GetValue<CVRMovementParent>();
if (currentParent == null) return false;
Transform referencePoint = (Transform)_referencePointField.GetValue(currentParent);
if (referencePoint == null) return false;
var currentPosition = referencePoint.position;
var currentRotation = currentParent.transform.rotation;
// Keep only the Y-axis rotation
currentRotation = Quaternion.Euler(0f, currentRotation.eulerAngles.y, 0f);
var deltaPosition = currentPosition - _previousPosition;
var deltaRotation = Quaternion.Inverse(_previousRotation) * currentRotation;
var platformPivot = transform.position;
_ikSolver.AddPlatformMotion(deltaPosition, deltaRotation, platformPivot);
_previousPosition = currentPosition;
_previousRotation = currentRotation;
ResetDesktopVRIK();
return true;
}
public void ResetDesktopVRIK() public void ResetDesktopVRIK()
{ {
ik_SimulatedRootAngle = transform.eulerAngles.y; _simulatedRootAngle = transform.eulerAngles.y;
} }
public void OnPreSolverUpdate() public void OnPreSolverUpdate()
{ {
if (ps_emoteIsPlaying) return; if (_isEmotePlaying) return;
var movementSystem = MovementSystem.Instance; bool isGrounded = _isGroundedTraverse.GetValue<bool>();
var vrikSolver = Calibrator.vrik.solver;
var avatarTransform = Calibrator.avatarTransform;
bool isGrounded = (bool)ms_isGrounded.GetValue(movementSystem);
// Calculate weight // Calculate weight
float weight = vrikSolver.IKPositionWeight; float weight = _ikSolver.IKPositionWeight;
weight *= 1f - movementSystem.movementVector.magnitude; weight *= 1f - movementSystem.movementVector.magnitude;
weight *= isGrounded ? 1f : 0f; weight *= isGrounded ? 1f : 0f;
// Reset avatar offset // Reset avatar offset
avatarTransform.localPosition = Vector3.zero; _avatarTransform.localPosition = Vector3.zero;
avatarTransform.localRotation = Quaternion.identity; _avatarTransform.localRotation = Quaternion.identity;
// Set plant feet // Set plant feet
vrikSolver.plantFeet = Setting_PlantFeet; _ikSolver.plantFeet = Setting_PlantFeet;
// Emulate old VRChat hip movement // Emulate old VRChat hip movement
if (Setting_BodyLeanWeight > 0) if (Setting_BodyLeanWeight > 0)
{ {
float weightedAngle = Setting_BodyLeanWeight * weight; float weightedAngle = Setting_BodyLeanWeight * weight;
float angle = desktopCameraTransform.localEulerAngles.x; float angle = _cameraTransform.localEulerAngles.x;
angle = angle > 180 ? angle - 360 : angle; angle = angle > 180 ? angle - 360 : angle;
Quaternion rotation = Quaternion.AngleAxis(angle * weightedAngle, avatarTransform.right); Quaternion rotation = Quaternion.AngleAxis(angle * weightedAngle, _avatarTransform.right);
vrikSolver.AddRotationOffset(IKSolverVR.RotationOffset.Head, rotation); _ikSolver.spine.headRotationOffset *= rotation;
} }
// Make root heading follow within a set limit // Make root heading follow within a set limit
if (Setting_BodyHeadingLimit > 0) if (Setting_BodyHeadingLimit > 0)
{ {
float weightedAngleLimit = Setting_BodyHeadingLimit * weight; float weightedAngleLimit = Setting_BodyHeadingLimit * weight;
float currentAngle = Mathf.DeltaAngle(transform.eulerAngles.y, ik_SimulatedRootAngle); float deltaAngleRoot = Mathf.DeltaAngle(transform.eulerAngles.y, _simulatedRootAngle);
float angleMaxDelta = Mathf.Abs(currentAngle); float absDeltaAngleRoot = Mathf.Abs(deltaAngleRoot);
if (angleMaxDelta > weightedAngleLimit) if (absDeltaAngleRoot > weightedAngleLimit)
{ {
currentAngle = Mathf.Sign(currentAngle) * weightedAngleLimit; deltaAngleRoot = Mathf.Sign(deltaAngleRoot) * weightedAngleLimit;
ik_SimulatedRootAngle = Mathf.MoveTowardsAngle(ik_SimulatedRootAngle, transform.eulerAngles.y, angleMaxDelta - weightedAngleLimit); _simulatedRootAngle = Mathf.MoveTowardsAngle(_simulatedRootAngle, transform.eulerAngles.y, absDeltaAngleRoot - weightedAngleLimit);
} }
vrikSolver.spine.rootHeadingOffset = currentAngle; _ikSolver.spine.rootHeadingOffset = deltaAngleRoot;
if (Setting_PelvisHeadingWeight > 0) if (Setting_PelvisHeadingWeight > 0)
{ {
vrikSolver.AddRotationOffset(IKSolverVR.RotationOffset.Pelvis, new Vector3(0f, currentAngle * Setting_PelvisHeadingWeight, 0f)); _ikSolver.spine.pelvisRotationOffset *= Quaternion.Euler(0f, deltaAngleRoot * Setting_PelvisHeadingWeight, 0f);
vrikSolver.AddRotationOffset(IKSolverVR.RotationOffset.Chest, new Vector3(0f, -currentAngle * Setting_PelvisHeadingWeight, 0f)); _ikSolver.spine.chestRotationOffset *= Quaternion.Euler(0f, -deltaAngleRoot * Setting_PelvisHeadingWeight, 0f);
} }
if (Setting_ChestHeadingWeight > 0) if (Setting_ChestHeadingWeight > 0)
{ {
vrikSolver.AddRotationOffset(IKSolverVR.RotationOffset.Chest, new Vector3(0f, currentAngle * Setting_ChestHeadingWeight, 0f)); _ikSolver.spine.chestRotationOffset *= Quaternion.Euler(0f, deltaAngleRoot * Setting_ChestHeadingWeight, 0f);
} }
} }
} }

View file

@ -46,6 +46,13 @@ class PlayerSetupPatches
{ {
return !(bool)DesktopVRIK.Instance?.OnSetupIKScaling(1f + ___scaleDifference.y); return !(bool)DesktopVRIK.Instance?.OnSetupIKScaling(1f + ___scaleDifference.y);
} }
[HarmonyPrefix]
[HarmonyPatch(typeof(PlayerSetup), "ResetIk")]
static bool Prefix_PlayerSetup_ResetIk()
{
return !(bool)DesktopVRIK.Instance?.OnPlayerSetupResetIk();
}
} }
class IKSystemPatches class IKSystemPatches

View file

@ -1,6 +1,6 @@
using RootMotion.FinalIK; using RootMotion.FinalIK;
using UnityEngine;
using System.Reflection; using System.Reflection;
using UnityEngine;
namespace NAK.Melons.DesktopVRIK; namespace NAK.Melons.DesktopVRIK;