From d21018001ef1f49e97928580628670e7edb33f35 Mon Sep 17 00:00:00 2001 From: NotAKidoS <37721153+NotAKidOnSteam@users.noreply.github.com> Date: Fri, 19 May 2023 02:53:41 -0500 Subject: [PATCH] [DesktopVRIK] Clean up VRIK calibration and configuration. Add NetIKPass. --- DesktopVRIK/DesktopVRIK.csproj | 2 +- DesktopVRIK/DesktopVRIKCalibrator.cs | 321 ++++++++++ DesktopVRIK/DesktopVRIKSystem.cs | 590 +++--------------- DesktopVRIK/Integrations/BTKUIAddon.cs | 2 +- DesktopVRIK/Main.cs | 68 +- DesktopVRIK/Properties/AssemblyInfo.cs | 2 +- DesktopVRIK/VRIKHelpers/CachedSolver.cs | 28 + .../VRIKHelpers/VRIKCalibrationData.cs | 33 + DesktopVRIK/VRIKHelpers/VRIKConfiguration.cs | 87 +++ DesktopVRIK/VRIKHelpers/VRIKConfigurations.cs | 53 ++ DesktopVRIK/{ => VRIKHelpers}/VRIKUtils.cs | 88 ++- 11 files changed, 681 insertions(+), 593 deletions(-) create mode 100644 DesktopVRIK/DesktopVRIKCalibrator.cs create mode 100644 DesktopVRIK/VRIKHelpers/CachedSolver.cs create mode 100644 DesktopVRIK/VRIKHelpers/VRIKCalibrationData.cs create mode 100644 DesktopVRIK/VRIKHelpers/VRIKConfiguration.cs create mode 100644 DesktopVRIK/VRIKHelpers/VRIKConfigurations.cs rename DesktopVRIK/{ => VRIKHelpers}/VRIKUtils.cs (59%) diff --git a/DesktopVRIK/DesktopVRIK.csproj b/DesktopVRIK/DesktopVRIK.csproj index 66a50a8..e94f9dc 100644 --- a/DesktopVRIK/DesktopVRIK.csproj +++ b/DesktopVRIK/DesktopVRIK.csproj @@ -1,2 +1,2 @@ - + diff --git a/DesktopVRIK/DesktopVRIKCalibrator.cs b/DesktopVRIK/DesktopVRIKCalibrator.cs new file mode 100644 index 0000000..66da1ab --- /dev/null +++ b/DesktopVRIK/DesktopVRIKCalibrator.cs @@ -0,0 +1,321 @@ +using ABI_RC.Core.Base; +using ABI_RC.Systems.IK; +using ABI_RC.Systems.IK.SubSystems; +using NAK.DesktopVRIK.VRIKHelper; +using RootMotion.FinalIK; +using UnityEngine; +using UnityEngine.Events; + +namespace NAK.DesktopVRIK; + +internal class DesktopVRIKCalibrator +{ + public static Dictionary BoneExists; + public static readonly float[] IKPoseMuscles = new float[] + { + 0.00133321f, + 8.195831E-06f, + 8.537738E-07f, + -0.002669832f, + -7.651234E-06f, + -0.001659694f, + 0f, + 0f, + 0f, + 0.04213953f, + 0.0003007996f, + -0.008032114f, + -0.03059979f, + -0.0003182998f, + 0.009640567f, + 0f, + 0f, + 0f, + 0f, + 0f, + 0f, + 0.5768794f, + 0.01061097f, + -0.1127839f, + 0.9705755f, + 0.07972051f, + -0.0268422f, + 0.007237188f, + 0f, + 0.5768792f, + 0.01056608f, + -0.1127519f, + 0.9705756f, + 0.07971933f, + -0.02682396f, + 0.007229362f, + 0f, + -5.651802E-06f, + -3.034899E-07f, + 0.4100508f, + 0.3610304f, + -0.0838329f, + 0.9262537f, + 0.1353517f, + -0.03578902f, + 0.06005657f, + -4.95989E-06f, + -1.43007E-06f, + 0.4096187f, + 0.363263f, + -0.08205152f, + 0.9250782f, + 0.1345718f, + -0.03572125f, + 0.06055461f, + -1.079177f, + 0.2095419f, + 0.6140652f, + 0.6365265f, + 0.6683931f, + -0.4764312f, + 0.8099416f, + 0.8099371f, + 0.6658203f, + -0.7327053f, + 0.8113618f, + 0.8114051f, + 0.6643661f, + -0.40341f, + 0.8111364f, + 0.8111367f, + 0.6170399f, + -0.2524227f, + 0.8138723f, + 0.8110135f, + -1.079171f, + 0.2095456f, + 0.6140658f, + 0.6365255f, + 0.6683878f, + -0.4764301f, + 0.8099402f, + 0.8099376f, + 0.6658241f, + -0.7327023f, + 0.8113653f, + 0.8113793f, + 0.664364f, + -0.4034042f, + 0.811136f, + 0.8111364f, + 0.6170469f, + -0.2524345f, + 0.8138595f, + 0.8110138f + }; + enum AvatarPose + { + Default = 0, + Initial = 1, + IKPose = 2, + TPose = 3 + } + + readonly DesktopVRIKSystem ikSystem; + + // Avatar Components + Animator _animator; + + // Calibration Objects + public HumanPoseHandler _humanPoseHandler; + public HumanPose _humanPose; + public HumanPose _humanPoseInitial; + + // Animator Info + int _animLocomotionLayer = -1; + int _animIKPoseLayer = -1; + + internal DesktopVRIKCalibrator() + { + ikSystem = DesktopVRIKSystem.Instance; + BoneExists = new Dictionary(); + } + + public void ApplyNetIKPass() + { + Transform hipTransform = _animator.GetBoneTransform(HumanBodyBones.Hips); + Vector3 hipPosition = hipTransform.position; + Quaternion hipRotation = hipTransform.rotation; + + _humanPoseHandler.GetHumanPose(ref _humanPose); + _humanPoseHandler.SetHumanPose(ref _humanPose); + + hipTransform.position = hipPosition; + hipTransform.rotation = hipRotation; + } + + public void CalibrateDesktopVRIK(Animator animator) + { + ScanAvatar(animator); + SetupVRIK(); + CalibrateVRIK(); + ConfigureVRIK(); + } + + void ScanAvatar(Animator animator) + { + // Find required avatar components + _animator = animator; + ikSystem.avatarTransform = animator.gameObject.transform; + ikSystem.avatarLookAtIK = animator.gameObject.GetComponent(); + + // Get animator layer inticies + _animIKPoseLayer = _animator.GetLayerIndex("IKPose"); + _animLocomotionLayer = _animator.GetLayerIndex("Locomotion/Emotes"); + + // Dispose and create new _humanPoseHandler + _humanPoseHandler?.Dispose(); + _humanPoseHandler = new HumanPoseHandler(_animator.avatar, ikSystem.avatarTransform); + + // Get initial human poses + _humanPoseHandler.GetHumanPose(ref _humanPose); + _humanPoseHandler.GetHumanPose(ref _humanPoseInitial); + + ikSystem.calibrationData.Clear(); + + // Dumb fix for rare upload issue + ikSystem.calibrationData.FixTransformsRequired = !_animator.enabled; + + // Find available HumanoidBodyBones + BoneExists.Clear(); + foreach (HumanBodyBones bone in Enum.GetValues(typeof(HumanBodyBones))) + { + if (bone != HumanBodyBones.LastBone) + { + BoneExists.Add(bone, _animator.GetBoneTransform(bone) != null); + } + } + } + + void SetupVRIK() + { + // Add and configure VRIK + ikSystem.avatarVRIK = ikSystem.avatarTransform.AddComponentIfMissing(); + ikSystem.avatarVRIK.AutoDetectReferences(); + ikSystem.cachedSolver = new CachedSolver(ikSystem.avatarVRIK.solver); + + // Why do I love to overcomplicate things? + VRIKUtils.ConfigureVRIKReferences(ikSystem.avatarVRIK, DesktopVRIK.EntryUseVRIKToes.Value); + VRIKConfigurator.ApplyVRIKConfiguration(ikSystem.cachedSolver, VRIKConfigurations.DesktopVRIKConfiguration()); + + // Fix potential animator issue + ikSystem.avatarVRIK.fixTransforms = ikSystem.calibrationData.FixTransformsRequired; + } + + void CalibrateVRIK() + { + SetAvatarPose(AvatarPose.Default); + + // Calculate bend normals with motorcycle pose + VRIKUtils.CalculateKneeBendNormals(ikSystem.avatarVRIK, ref ikSystem.calibrationData); + + SetAvatarPose(AvatarPose.IKPose); + + // Calculate initial IK scaling values with IKPose + VRIKUtils.CalculateInitialIKScaling(ikSystem.avatarVRIK, ref ikSystem.calibrationData); + + // Calculate initial Footstep positions + VRIKUtils.CalculateInitialFootsteps(ikSystem.avatarVRIK, ref ikSystem.calibrationData); + + // Setup HeadIKTarget + VRIKUtils.SetupHeadIKTarget(ikSystem.avatarVRIK); + + // Initiate VRIK manually + VRIKUtils.InitiateVRIKSolver(ikSystem.avatarVRIK); + + SetAvatarPose(AvatarPose.Initial); + } + + void ConfigureVRIK() + { + ikSystem.OnSetupIKScaling(1f); + + VRIKUtils.ApplyKneeBendNormals(ikSystem.avatarVRIK, ikSystem.calibrationData); + + ikSystem.avatarVRIK.onPreSolverUpdate.AddListener(new UnityAction(ikSystem.OnPreSolverUpdate)); + ikSystem.avatarVRIK.onPostSolverUpdate.AddListener(new UnityAction(ikSystem.OnPostSolverUpdate)); + } + + void SetAvatarPose(AvatarPose pose) + { + switch (pose) + { + case AvatarPose.Default: + SetMusclesToValue(0f); + break; + case AvatarPose.Initial: + if (HasCustomIKPose()) + { + SetCustomLayersWeights(0f, 1f); + return; + } + _humanPoseHandler.SetHumanPose(ref _humanPoseInitial); + break; + case AvatarPose.IKPose: + if (HasCustomIKPose()) + { + SetCustomLayersWeights(1f, 0f); + return; + } + SetMusclesToPose(IKPoseMuscles); + break; + case AvatarPose.TPose: + SetMusclesToPose(BodySystem.TPoseMuscles); + break; + default: + break; + } + } + + bool HasCustomIKPose() + { + return _animLocomotionLayer != -1 && _animIKPoseLayer != -1; + } + + void SetCustomLayersWeights(float customIKPoseLayerWeight, float locomotionLayerWeight) + { + _animator.SetLayerWeight(_animIKPoseLayer, customIKPoseLayerWeight); + _animator.SetLayerWeight(_animLocomotionLayer, locomotionLayerWeight); + _animator.Update(0f); + } + + void SetMusclesToValue(float value) + { + _humanPoseHandler.GetHumanPose(ref _humanPose); + + for (int i = 0; i < _humanPose.muscles.Length; i++) + { + ApplyMuscleValue((MuscleIndex)i, value, ref _humanPose.muscles); + } + + _humanPose.bodyRotation = Quaternion.identity; + _humanPoseHandler.SetHumanPose(ref _humanPose); + } + + void SetMusclesToPose(float[] muscles) + { + _humanPoseHandler.GetHumanPose(ref _humanPose); + + for (int i = 0; i < _humanPose.muscles.Length; i++) + { + ApplyMuscleValue((MuscleIndex)i, muscles[i], ref _humanPose.muscles); + } + + _humanPose.bodyRotation = Quaternion.identity; + _humanPoseHandler.SetHumanPose(ref _humanPose); + } + + void ApplyMuscleValue(MuscleIndex index, float value, ref float[] muscles) + { + if (BoneExists.ContainsKey(IKSystem.MusclesToHumanBodyBones[(int)index]) && BoneExists[IKSystem.MusclesToHumanBodyBones[(int)index]]) + { + muscles[(int)index] = value; + } + } +} diff --git a/DesktopVRIK/DesktopVRIKSystem.cs b/DesktopVRIK/DesktopVRIKSystem.cs index 5d74e70..9883ffd 100644 --- a/DesktopVRIK/DesktopVRIKSystem.cs +++ b/DesktopVRIK/DesktopVRIKSystem.cs @@ -1,178 +1,31 @@ using ABI.CCK.Components; -using ABI_RC.Core.Base; using ABI_RC.Core.Player; -using ABI_RC.Systems.IK; using ABI_RC.Systems.IK.SubSystems; using ABI_RC.Systems.MovementSystem; +using NAK.DesktopVRIK.VRIKHelper; using RootMotion.FinalIK; using UnityEngine; -using UnityEngine.Events; namespace NAK.DesktopVRIK; internal class DesktopVRIKSystem : MonoBehaviour { public static DesktopVRIKSystem Instance; - public static Dictionary BoneExists; - public static readonly float[] IKPoseMuscles = new float[] - { - 0.00133321f, - 8.195831E-06f, - 8.537738E-07f, - -0.002669832f, - -7.651234E-06f, - -0.001659694f, - 0f, - 0f, - 0f, - 0.04213953f, - 0.0003007996f, - -0.008032114f, - -0.03059979f, - -0.0003182998f, - 0.009640567f, - 0f, - 0f, - 0f, - 0f, - 0f, - 0f, - 0.5768794f, - 0.01061097f, - -0.1127839f, - 0.9705755f, - 0.07972051f, - -0.0268422f, - 0.007237188f, - 0f, - 0.5768792f, - 0.01056608f, - -0.1127519f, - 0.9705756f, - 0.07971933f, - -0.02682396f, - 0.007229362f, - 0f, - -5.651802E-06f, - -3.034899E-07f, - 0.4100508f, - 0.3610304f, - -0.0838329f, - 0.9262537f, - 0.1353517f, - -0.03578902f, - 0.06005657f, - -4.95989E-06f, - -1.43007E-06f, - 0.4096187f, - 0.363263f, - -0.08205152f, - 0.9250782f, - 0.1345718f, - -0.03572125f, - 0.06055461f, - -1.079177f, - 0.2095419f, - 0.6140652f, - 0.6365265f, - 0.6683931f, - -0.4764312f, - 0.8099416f, - 0.8099371f, - 0.6658203f, - -0.7327053f, - 0.8113618f, - 0.8114051f, - 0.6643661f, - -0.40341f, - 0.8111364f, - 0.8111367f, - 0.6170399f, - -0.2524227f, - 0.8138723f, - 0.8110135f, - -1.079171f, - 0.2095456f, - 0.6140658f, - 0.6365255f, - 0.6683878f, - -0.4764301f, - 0.8099402f, - 0.8099376f, - 0.6658241f, - -0.7327023f, - 0.8113653f, - 0.8113793f, - 0.664364f, - -0.4034042f, - 0.811136f, - 0.8111364f, - 0.6170469f, - -0.2524345f, - 0.8138595f, - 0.8110138f - }; + public static DesktopVRIKCalibrator Calibrator; - enum AvatarPose - { - Default = 0, - Initial = 1, - IKPose = 2, - TPose = 3 - } - - // DesktopVRIK Settings - public bool Setting_Enabled = true; - public bool Setting_PlantFeet; - public bool Setting_ResetFootsteps; - public bool Setting_ProneThrusting; - public float Setting_BodyLeanWeight; - public float Setting_BodyHeadingLimit; - public float Setting_PelvisHeadingWeight; - public float Setting_ChestHeadingWeight; - public float Setting_IKLerpSpeed; - - // Calibration Settings - public bool Setting_UseVRIKToes; - public bool Setting_FindUnmappedToes; - - // Integration Settings - public bool Setting_IntegrationAMT; + // VRIK Calibration Info + public VRIKCalibrationData calibrationData; // Avatar Components - public CVRAvatar avatarDescriptor = null; - public Animator avatarAnimator = null; - public Transform avatarTransform = null; - public LookAtIK avatarLookAtIK = null; public VRIK avatarVRIK = null; - public IKSolverVR avatarIKSolver = null; + public LookAtIK avatarLookAtIK = null; + public Transform avatarTransform = null; + public CachedSolver cachedSolver; // ChilloutVR Player Components PlayerSetup playerSetup; MovementSystem movementSystem; - // Calibration Objects - HumanPose _humanPose; - HumanPose _humanPoseInitial; - HumanPoseHandler _humanPoseHandler; - - // Animator Info - int _animLocomotionLayer = -1; - int _animIKPoseLayer = -1; - - // VRIK Calibration Info - Vector3 _vrikKneeNormalLeft; - Vector3 _vrikKneeNormalRight; - Vector3 _vrikInitialFootPosLeft; - Vector3 _vrikInitialFootPosRight; - Quaternion _vrikInitialFootRotLeft; - Quaternion _vrikInitialFootRotRight; - float _vrikInitialHeadHeight; - float _vrikInitialFootDistance; - float _vrikInitialStepThreshold; - float _vrikInitialStepHeight; - bool _vrikFixTransformsRequired; - // Player Info Transform _cameraTransform; bool _ikEmotePlaying; @@ -186,21 +39,16 @@ internal class DesktopVRIKSystem : MonoBehaviour Quaternion _movementRotation; CVRMovementParent _movementParent; - DesktopVRIKSystem() - { - BoneExists = new Dictionary(); - } - void Start() { Instance = this; + Calibrator = new DesktopVRIKCalibrator(); + playerSetup = GetComponent(); movementSystem = GetComponent(); _cameraTransform = playerSetup.desktopCamera.transform; - - DesktopVRIK.UpdateAllSettings(); } void Update() @@ -220,7 +68,7 @@ internal class DesktopVRIKSystem : MonoBehaviour if (shouldTrackLocomotion != BodySystem.TrackingLocomotionEnabled) { BodySystem.TrackingLocomotionEnabled = shouldTrackLocomotion; - avatarIKSolver.Reset(); + IKResetSolver(); ResetDesktopVRIK(); if (shouldTrackLocomotion) IKResetFootsteps(); } @@ -242,29 +90,26 @@ internal class DesktopVRIKSystem : MonoBehaviour bool IsStanding() { // Let AMT handle it if available - if (Setting_IntegrationAMT) return true; + if (DesktopVRIK.EntryIntegrationAMT.Value) return true; // Get Upright value - Vector3 delta = avatarIKSolver.spine.headPosition - avatarTransform.position; + Vector3 delta = cachedSolver.Spine.headPosition - avatarTransform.position; Vector3 deltaRotated = Quaternion.Euler(0, avatarTransform.rotation.eulerAngles.y, 0) * delta; - float upright = Mathf.InverseLerp(0f, _vrikInitialHeadHeight * _scaleDifference, deltaRotated.y); + float upright = Mathf.InverseLerp(0f, calibrationData.InitialHeadHeight * _scaleDifference, deltaRotated.y); return upright > 0.85f; } - void UpdateLocomotionWeight() { float targetWeight = BodySystem.TrackingEnabled && BodySystem.TrackingLocomotionEnabled ? 1.0f : 0.0f; - if (Setting_IKLerpSpeed > 0) + if (DesktopVRIK.EntryIKLerpSpeed.Value > 0) { - _ikWeightLerp = Mathf.Lerp(_ikWeightLerp, targetWeight, Time.deltaTime * Setting_IKLerpSpeed); - _locomotionWeight = Mathf.Lerp(_locomotionWeight, targetWeight, Time.deltaTime * Setting_IKLerpSpeed * 2f); - } - else - { - _ikWeightLerp = targetWeight; - _locomotionWeight = targetWeight; + _ikWeightLerp = Mathf.Lerp(_ikWeightLerp, targetWeight, Time.deltaTime * DesktopVRIK.EntryIKLerpSpeed.Value); + _locomotionWeight = Mathf.Lerp(_locomotionWeight, targetWeight, Time.deltaTime * DesktopVRIK.EntryIKLerpSpeed.Value * 2f); + return; } + _ikWeightLerp = targetWeight; + _locomotionWeight = targetWeight; } void ApplyBodySystemWeights() @@ -281,32 +126,33 @@ internal class DesktopVRIKSystem : MonoBehaviour leg.positionWeight = isTracked ? 1f : 0f; leg.rotationWeight = isTracked ? 1f : 0f; } + if (BodySystem.TrackingEnabled) { avatarVRIK.enabled = true; - avatarIKSolver.IKPositionWeight = BodySystem.TrackingPositionWeight; - avatarIKSolver.locomotion.weight = _locomotionWeight; + cachedSolver.Solver.IKPositionWeight = BodySystem.TrackingPositionWeight; + cachedSolver.Locomotion.weight = _locomotionWeight; bool useAnimatedBendNormal = _locomotionWeight <= 0.5f; - avatarIKSolver.leftLeg.useAnimatedBendNormal = useAnimatedBendNormal; - avatarIKSolver.rightLeg.useAnimatedBendNormal = useAnimatedBendNormal; - SetArmWeight(avatarIKSolver.leftArm, BodySystem.TrackingLeftArmEnabled && avatarIKSolver.leftArm.target != null); - SetArmWeight(avatarIKSolver.rightArm, BodySystem.TrackingRightArmEnabled && avatarIKSolver.rightArm.target != null); - SetLegWeight(avatarIKSolver.leftLeg, BodySystem.TrackingLeftLegEnabled && avatarIKSolver.leftLeg.target != null); - SetLegWeight(avatarIKSolver.rightLeg, BodySystem.TrackingRightLegEnabled && avatarIKSolver.rightLeg.target != null); + cachedSolver.LeftLeg.useAnimatedBendNormal = useAnimatedBendNormal; + cachedSolver.RightLeg.useAnimatedBendNormal = useAnimatedBendNormal; + SetArmWeight(cachedSolver.LeftArm, BodySystem.TrackingLeftArmEnabled && cachedSolver.LeftArm.target != null); + SetArmWeight(cachedSolver.RightArm, BodySystem.TrackingRightArmEnabled && cachedSolver.RightArm.target != null); + SetLegWeight(cachedSolver.LeftLeg, BodySystem.TrackingLeftLegEnabled && cachedSolver.LeftLeg.target != null); + SetLegWeight(cachedSolver.RightLeg, BodySystem.TrackingRightLegEnabled && cachedSolver.RightLeg.target != null); } else { avatarVRIK.enabled = false; - avatarIKSolver.IKPositionWeight = 0f; - avatarIKSolver.locomotion.weight = 0f; + cachedSolver.Solver.IKPositionWeight = 0f; + cachedSolver.Locomotion.weight = 0f; - avatarIKSolver.leftLeg.useAnimatedBendNormal = false; - avatarIKSolver.rightLeg.useAnimatedBendNormal = false; - SetArmWeight(avatarIKSolver.leftArm, false); - SetArmWeight(avatarIKSolver.rightArm, false); - SetLegWeight(avatarIKSolver.leftLeg, false); - SetLegWeight(avatarIKSolver.rightLeg, false); + cachedSolver.LeftLeg.useAnimatedBendNormal = false; + cachedSolver.RightLeg.useAnimatedBendNormal = false; + SetArmWeight(cachedSolver.LeftArm, false); + SetArmWeight(cachedSolver.RightArm, false); + SetLegWeight(cachedSolver.LeftLeg, false); + SetLegWeight(cachedSolver.RightLeg, false); } } @@ -319,42 +165,26 @@ internal class DesktopVRIKSystem : MonoBehaviour public void OnSetupAvatarDesktop(Animator animator) { - if (!Setting_Enabled) return; + if (!DesktopVRIK.EntryEnabled.Value) return; // only run for humanoid avatars if (animator != null && animator.avatar != null && animator.avatar.isHuman) { - CalibrateDesktopVRIK(); + Calibrator.CalibrateDesktopVRIK(animator); ResetDesktopVRIK(); } } - public bool OnSetupIKScaling(float scaleDifference) + public void OnSetupIKScaling(float scaleDifference) { - if (avatarVRIK == null) return false; - _scaleDifference = scaleDifference; VRIKUtils.ApplyScaleToVRIK ( avatarVRIK, - _vrikInitialFootDistance, - _vrikInitialStepThreshold, - _vrikInitialStepHeight, + calibrationData, _scaleDifference ); - - //VRIKUtils.SetFootsteps - //( - // avatarVRIK, - // _vrikInitialFootPosLeft * _scaleDifference, - // _vrikInitialFootPosRight * _scaleDifference, - // _vrikInitialFootRotLeft, - // _vrikInitialFootRotRight - //); - - ResetDesktopVRIK(); - return true; } public void OnPlayerSetupUpdate(bool isEmotePlaying) @@ -367,13 +197,13 @@ internal class DesktopVRIKSystem : MonoBehaviour // Disable tracking completely while emoting BodySystem.TrackingEnabled = !isEmotePlaying; - avatarIKSolver.Reset(); + IKResetSolver(); ResetDesktopVRIK(); } public void OnPlayerSetupSetSitting() { - avatarIKSolver.Reset(); + IKResetSolver(); ResetDesktopVRIK(); } @@ -398,7 +228,7 @@ internal class DesktopVRIKSystem : MonoBehaviour if (_movementParent == currentParent) { // Add platform motion to IK solver - avatarIKSolver.AddPlatformMotion(deltaPosition, deltaRotation, platformPivot); + cachedSolver.Solver.AddPlatformMotion(deltaPosition, deltaRotation, platformPivot); ResetDesktopVRIK(); } @@ -406,80 +236,88 @@ internal class DesktopVRIKSystem : MonoBehaviour _movementParent = currentParent; _movementPosition = currentPosition; _movementRotation = currentRotation; - return; } - - // if not for movementparent, reset ik solver - avatarIKSolver.Reset(); + + // if not for movementparent, reset ik + IKResetSolver(); + IKResetFootsteps(); ResetDesktopVRIK(); - //IKResetFootsteps(); } public void OnPreSolverUpdate() { // Set plant feet - avatarIKSolver.plantFeet = Setting_PlantFeet; + cachedSolver.Solver.plantFeet = DesktopVRIK.EntryPlantFeet.Value; // Apply custom VRIK solving effects IKBodyLeaningOffset(_ikWeightLerp); IKBodyHeadingOffset(_ikWeightLerp); + + void IKBodyLeaningOffset(float weight) + { + // Emulate old VRChat hip movement + if (DesktopVRIK.EntryBodyLeanWeight.Value <= 0) return; + + if (DesktopVRIK.EntryProneThrusting.Value) weight = 1f; + float weightedAngle = DesktopVRIK.EntryBodyLeanWeight.Value * weight; + float angle = _cameraTransform.localEulerAngles.x; + angle = angle > 180 ? angle - 360 : angle; + Quaternion rotation = Quaternion.AngleAxis(angle * weightedAngle, avatarTransform.right); + cachedSolver.Spine.headRotationOffset *= rotation; + } + + void IKBodyHeadingOffset(float weight) + { + // Make root heading follow within a set limit + if (DesktopVRIK.EntryBodyHeadingLimit.Value <= 0) return; + + float weightedAngleLimit = DesktopVRIK.EntryBodyHeadingLimit.Value * weight; + float deltaAngleRoot = Mathf.DeltaAngle(transform.eulerAngles.y, _ikSimulatedRootAngle); + float absDeltaAngleRoot = Mathf.Abs(deltaAngleRoot); + + if (absDeltaAngleRoot > weightedAngleLimit) + { + deltaAngleRoot = Mathf.Sign(deltaAngleRoot) * weightedAngleLimit; + _ikSimulatedRootAngle = Mathf.MoveTowardsAngle(_ikSimulatedRootAngle, transform.eulerAngles.y, absDeltaAngleRoot - weightedAngleLimit); + } + + cachedSolver.Spine.rootHeadingOffset = deltaAngleRoot; + + if (DesktopVRIK.EntryPelvisHeadingWeight.Value > 0) + { + cachedSolver.Spine.pelvisRotationOffset *= Quaternion.Euler(0f, deltaAngleRoot * DesktopVRIK.EntryPelvisHeadingWeight.Value, 0f); + cachedSolver.Spine.chestRotationOffset *= Quaternion.Euler(0f, -deltaAngleRoot * DesktopVRIK.EntryPelvisHeadingWeight.Value, 0f); + } + + if (DesktopVRIK.EntryChestHeadingWeight.Value > 0) + { + cachedSolver.Spine.chestRotationOffset *= Quaternion.Euler(0f, deltaAngleRoot * DesktopVRIK.EntryChestHeadingWeight.Value, 0f); + } + } } - void IKBodyLeaningOffset(float weight) + public void OnPostSolverUpdate() { - // Emulate old VRChat hip movement - if (Setting_BodyLeanWeight <= 0) return; - - if (Setting_ProneThrusting) weight = 1f; - float weightedAngle = Setting_BodyLeanWeight * weight; - float angle = _cameraTransform.localEulerAngles.x; - angle = angle > 180 ? angle - 360 : angle; - Quaternion rotation = Quaternion.AngleAxis(angle * weightedAngle, avatarTransform.right); - avatarIKSolver.spine.headRotationOffset *= rotation; + if (!DesktopVRIK.EntryNetIKPass.Value) return; + Calibrator.ApplyNetIKPass(); } - void IKBodyHeadingOffset(float weight) + void IKResetSolver() { - // Make root heading follow within a set limit - if (Setting_BodyHeadingLimit <= 0) return; - - float weightedAngleLimit = Setting_BodyHeadingLimit * weight; - float deltaAngleRoot = Mathf.DeltaAngle(transform.eulerAngles.y, _ikSimulatedRootAngle); - float absDeltaAngleRoot = Mathf.Abs(deltaAngleRoot); - - if (absDeltaAngleRoot > weightedAngleLimit) - { - deltaAngleRoot = Mathf.Sign(deltaAngleRoot) * weightedAngleLimit; - _ikSimulatedRootAngle = Mathf.MoveTowardsAngle(_ikSimulatedRootAngle, transform.eulerAngles.y, absDeltaAngleRoot - weightedAngleLimit); - } - - avatarIKSolver.spine.rootHeadingOffset = deltaAngleRoot; - - if (Setting_PelvisHeadingWeight > 0) - { - avatarIKSolver.spine.pelvisRotationOffset *= Quaternion.Euler(0f, deltaAngleRoot * Setting_PelvisHeadingWeight, 0f); - avatarIKSolver.spine.chestRotationOffset *= Quaternion.Euler(0f, -deltaAngleRoot * Setting_PelvisHeadingWeight, 0f); - } - - if (Setting_ChestHeadingWeight > 0) - { - avatarIKSolver.spine.chestRotationOffset *= Quaternion.Euler(0f, deltaAngleRoot * Setting_ChestHeadingWeight, 0f); - } + cachedSolver.Solver.Reset(); } void IKResetFootsteps() { // Reset footsteps immediatly to initial - if (!Setting_ResetFootsteps) return; + if (!DesktopVRIK.EntryResetFootstepsOnIdle.Value) return; - VRIKUtils.SetFootsteps + VRIKUtils.ResetToInitialFootsteps ( avatarVRIK, - _vrikInitialFootPosLeft, - _vrikInitialFootPosRight, - _vrikInitialFootRotLeft, - _vrikInitialFootRotRight + calibrationData, + _scaleDifference ); } @@ -487,234 +325,4 @@ internal class DesktopVRIKSystem : MonoBehaviour { _ikSimulatedRootAngle = transform.eulerAngles.y; } - - void CalibrateDesktopVRIK() - { - ScanAvatar(); - SetupVRIK(); - CalibrateVRIK(); - ConfigureVRIK(); - } - - void ScanAvatar() - { - // Find required avatar components - avatarDescriptor = playerSetup._avatarDescriptor; - avatarAnimator = playerSetup._animator; - avatarTransform = playerSetup._avatar.transform; - avatarLookAtIK = playerSetup.lookIK; - - // Get animator layer inticies - _animIKPoseLayer = avatarAnimator.GetLayerIndex("IKPose"); - _animLocomotionLayer = avatarAnimator.GetLayerIndex("Locomotion/Emotes"); - - // Dispose and create new _humanPoseHandler - _humanPoseHandler?.Dispose(); - _humanPoseHandler = new HumanPoseHandler(avatarAnimator.avatar, avatarTransform); - - // Get initial human poses - _humanPoseHandler.GetHumanPose(ref _humanPose); - _humanPoseHandler.GetHumanPose(ref _humanPoseInitial); - - // Dumb fix for rare upload issue - _vrikFixTransformsRequired = !avatarAnimator.enabled; - - // Find available HumanoidBodyBones - BoneExists.Clear(); - foreach (HumanBodyBones bone in Enum.GetValues(typeof(HumanBodyBones))) - { - if (bone != HumanBodyBones.LastBone) - { - BoneExists.Add(bone, avatarAnimator.GetBoneTransform(bone) != null); - } - } - } - - void SetupVRIK() - { - // Add and configure VRIK - avatarVRIK = avatarTransform.AddComponentIfMissing(); - avatarVRIK.AutoDetectReferences(); - avatarIKSolver = avatarVRIK.solver; - - VRIKUtils.ConfigureVRIKReferences(avatarVRIK, Setting_UseVRIKToes); - - // Fix animator issue - avatarVRIK.fixTransforms = _vrikFixTransformsRequired; - - // Default solver settings - avatarIKSolver.locomotion.weight = 0f; - avatarIKSolver.locomotion.angleThreshold = 30f; - avatarIKSolver.locomotion.maxLegStretch = 1f; - avatarIKSolver.spine.minHeadHeight = 0f; - avatarIKSolver.IKPositionWeight = 1f; - avatarIKSolver.spine.chestClampWeight = 0f; - avatarIKSolver.spine.maintainPelvisPosition = 0f; - - // Body leaning settings - avatarIKSolver.spine.bodyPosStiffness = 1f; - avatarIKSolver.spine.bodyRotStiffness = 0.2f; - // this is a hack, allows chest to rotate slightly - // independent from hip rotation. Funny Spine.Solve()->Bend() - avatarIKSolver.spine.neckStiffness = 0.0001f; - - // Disable locomotion - // Setting velocity to 0 aleviated nameplate jitter issue on remote - avatarIKSolver.locomotion.velocityFactor = 0f; - avatarIKSolver.locomotion.maxVelocity = 0f; - avatarIKSolver.locomotion.rootSpeed = 1000f; - - // Disable chest rotation by hands - // this fixed Effector, Player Arm Movement, BetterInteractDesktop, ect - // from making entire body shake, as well as while running - avatarIKSolver.spine.rotateChestByHands = 0f; - - // Prioritize LookAtIK - avatarIKSolver.spine.headClampWeight = 0.2f; - - // Disable going on tippytoes - avatarIKSolver.spine.positionWeight = 0f; - avatarIKSolver.spine.rotationWeight = 1f; - - // Set so emotes play properly - avatarIKSolver.spine.maxRootAngle = 180f; - // this is different in VR, as CVR player controller is not set up optimally for VRIK. - // Desktop avatar rotates 1:1 with _PlayerLocal. VR has a disconnect because you can turn IRL. - - // We disable these ourselves now, as we no longer use BodySystem - avatarIKSolver.spine.maintainPelvisPosition = 1f; - avatarIKSolver.spine.positionWeight = 0f; - avatarIKSolver.spine.pelvisPositionWeight = 0f; - avatarIKSolver.leftArm.positionWeight = 0f; - avatarIKSolver.leftArm.rotationWeight = 0f; - avatarIKSolver.rightArm.positionWeight = 0f; - avatarIKSolver.rightArm.rotationWeight = 0f; - avatarIKSolver.leftLeg.positionWeight = 0f; - avatarIKSolver.leftLeg.rotationWeight = 0f; - avatarIKSolver.rightLeg.positionWeight = 0f; - avatarIKSolver.rightLeg.rotationWeight = 0f; - - // This is now our master Locomotion weight - avatarIKSolver.locomotion.weight = 1f; - avatarIKSolver.IKPositionWeight = 1f; - } - - void CalibrateVRIK() - { - SetAvatarPose(AvatarPose.Default); - - // Calculate bend normals with motorcycle pose - VRIKUtils.CalculateKneeBendNormals(avatarVRIK, out _vrikKneeNormalLeft, out _vrikKneeNormalRight); - - SetAvatarPose(AvatarPose.IKPose); - - _vrikInitialHeadHeight = Mathf.Abs(avatarVRIK.references.head.position.y - avatarVRIK.references.rightFoot.position.y); - - // Calculate initial IK scaling values with IKPose - VRIKUtils.CalculateInitialIKScaling(avatarVRIK, out _vrikInitialFootDistance, out _vrikInitialStepThreshold, out _vrikInitialStepHeight); - - // Calculate initial Footstep positions - VRIKUtils.CalculateInitialFootsteps(avatarVRIK, out _vrikInitialFootPosLeft, out _vrikInitialFootPosRight, out _vrikInitialFootRotLeft, out _vrikInitialFootRotRight); - - // Setup HeadIKTarget - VRIKUtils.SetupHeadIKTarget(avatarVRIK); - - // Initiate VRIK manually - VRIKUtils.InitiateVRIKSolver(avatarVRIK); - - SetAvatarPose(AvatarPose.Initial); - } - - void ConfigureVRIK() - { - // Reset scale diffrence - _scaleDifference = 1f; - VRIKUtils.ApplyScaleToVRIK - ( - avatarVRIK, - _vrikInitialFootDistance, - _vrikInitialStepThreshold, - _vrikInitialStepHeight, - 1f - ); - VRIKUtils.ApplyKneeBendNormals(avatarVRIK, _vrikKneeNormalLeft, _vrikKneeNormalRight); - avatarVRIK.onPreSolverUpdate.AddListener(new UnityAction(DesktopVRIKSystem.Instance.OnPreSolverUpdate)); - } - - void SetAvatarPose(AvatarPose pose) - { - switch (pose) - { - case AvatarPose.Default: - SetMusclesToValue(0f); - break; - case AvatarPose.Initial: - if (HasCustomIKPose()) - { - SetCustomLayersWeights(0f, 1f); - return; - } - _humanPoseHandler.SetHumanPose(ref _humanPoseInitial); - break; - case AvatarPose.IKPose: - if (HasCustomIKPose()) - { - SetCustomLayersWeights(1f, 0f); - return; - } - SetMusclesToPose(IKPoseMuscles); - break; - case AvatarPose.TPose: - SetMusclesToPose(BodySystem.TPoseMuscles); - break; - default: - break; - } - } - - bool HasCustomIKPose() - { - return _animLocomotionLayer != -1 && _animIKPoseLayer != -1; - } - - void SetCustomLayersWeights(float customIKPoseLayerWeight, float locomotionLayerWeight) - { - avatarAnimator.SetLayerWeight(_animIKPoseLayer, customIKPoseLayerWeight); - avatarAnimator.SetLayerWeight(_animLocomotionLayer, locomotionLayerWeight); - avatarAnimator.Update(0f); - } - - void SetMusclesToValue(float value) - { - _humanPoseHandler.GetHumanPose(ref _humanPose); - - for (int i = 0; i < _humanPose.muscles.Length; i++) - { - ApplyMuscleValue((MuscleIndex)i, value, ref _humanPose.muscles); - } - - _humanPose.bodyRotation = Quaternion.identity; - _humanPoseHandler.SetHumanPose(ref _humanPose); - } - - void SetMusclesToPose(float[] muscles) - { - _humanPoseHandler.GetHumanPose(ref _humanPose); - - for (int i = 0; i < _humanPose.muscles.Length; i++) - { - ApplyMuscleValue((MuscleIndex)i, muscles[i], ref _humanPose.muscles); - } - - _humanPose.bodyRotation = Quaternion.identity; - _humanPoseHandler.SetHumanPose(ref _humanPose); - } - - void ApplyMuscleValue(MuscleIndex index, float value, ref float[] muscles) - { - if (BoneExists.ContainsKey(IKSystem.MusclesToHumanBodyBones[(int)index]) && BoneExists[IKSystem.MusclesToHumanBodyBones[(int)index]]) - { - muscles[(int)index] = value; - } - } } \ No newline at end of file diff --git a/DesktopVRIK/Integrations/BTKUIAddon.cs b/DesktopVRIK/Integrations/BTKUIAddon.cs index 45cfa3e..d2e0b45 100644 --- a/DesktopVRIK/Integrations/BTKUIAddon.cs +++ b/DesktopVRIK/Integrations/BTKUIAddon.cs @@ -2,7 +2,7 @@ using BTKUILib.UIObjects; using System.Runtime.CompilerServices; -namespace NAK.DesktopVRIK; +namespace NAK.DesktopVRIK.Integrations; public static class BTKUIAddon { diff --git a/DesktopVRIK/Main.cs b/DesktopVRIK/Main.cs index bb72dae..309ab33 100644 --- a/DesktopVRIK/Main.cs +++ b/DesktopVRIK/Main.cs @@ -1,5 +1,4 @@ using MelonLoader; -using UnityEngine; namespace NAK.DesktopVRIK; @@ -8,41 +7,44 @@ public class DesktopVRIK : MelonMod internal static MelonLogger.Instance Logger; internal const string SettingsCategory = nameof(DesktopVRIK); - public static readonly MelonPreferences_Category CategoryDesktopVRIK = + public static readonly MelonPreferences_Category Category = MelonPreferences.CreateCategory(SettingsCategory); public static readonly MelonPreferences_Entry EntryEnabled = - CategoryDesktopVRIK.CreateEntry("Enabled", true, description: "Toggle DesktopVRIK entirely. Requires avatar reload."); + Category.CreateEntry("Enabled", true, description: "Toggle DesktopVRIK entirely. Requires avatar reload."); public static readonly MelonPreferences_Entry EntryPlantFeet = - CategoryDesktopVRIK.CreateEntry("Enforce Plant Feet", true, description: "Forces VRIK Plant Feet enabled to prevent hovering when stopping movement."); + Category.CreateEntry("Enforce Plant Feet", true, description: "Forces VRIK Plant Feet enabled to prevent hovering when stopping movement."); public static readonly MelonPreferences_Entry EntryResetFootstepsOnIdle = - CategoryDesktopVRIK.CreateEntry("Reset Footsteps on Idle", true, description: "Determins if the Locomotion Footsteps will be reset to their calibration position when entering idle."); + Category.CreateEntry("Reset Footsteps on Idle", false, description: "Determins if the Locomotion Footsteps will be reset to their calibration position when entering idle."); public static readonly MelonPreferences_Entry EntryUseVRIKToes = - CategoryDesktopVRIK.CreateEntry("Use VRIK Toes", false, description: "Determines if VRIK uses humanoid toes for IK solving, which can cause feet to idle behind the avatar."); + Category.CreateEntry("Use VRIK Toes", false, description: "Determines if VRIK uses humanoid toes for IK solving, which can cause feet to idle behind the avatar."); public static readonly MelonPreferences_Entry EntryBodyLeanWeight = - CategoryDesktopVRIK.CreateEntry("Body Lean Weight", 0.5f, description: "Adds rotational influence to the body solver when looking up/down. Set to 0 to disable."); + Category.CreateEntry("Body Lean Weight", 0.5f, description: "Adds rotational influence to the body solver when looking up/down. Set to 0 to disable."); public static readonly MelonPreferences_Entry EntryBodyHeadingLimit = - CategoryDesktopVRIK.CreateEntry("Body Heading Limit", 20f, description: "Specifies the maximum angle the lower body can have relative to the head when rotating. Set to 0 to disable."); + Category.CreateEntry("Body Heading Limit", 20f, description: "Specifies the maximum angle the lower body can have relative to the head when rotating. Set to 0 to disable."); public static readonly MelonPreferences_Entry EntryPelvisHeadingWeight = - CategoryDesktopVRIK.CreateEntry("Pelvis Heading Weight", 0.25f, description: "Determines how much the pelvis will face the Body Heading Limit. Set to 0 to align with head."); + Category.CreateEntry("Pelvis Heading Weight", 0.25f, description: "Determines how much the pelvis will face the Body Heading Limit. Set to 0 to align with head."); public static readonly MelonPreferences_Entry EntryChestHeadingWeight = - CategoryDesktopVRIK.CreateEntry("Chest Heading Weight", 0.75f, description: "Determines how much the chest will face the Body Heading Limit. Set to 0 to align with head."); + Category.CreateEntry("Chest Heading Weight", 0.75f, description: "Determines how much the chest will face the Body Heading Limit. Set to 0 to align with head."); public static readonly MelonPreferences_Entry EntryIKLerpSpeed = - CategoryDesktopVRIK.CreateEntry("IK Lerp Speed", 10f, description: "Determines fast the IK & Locomotion weights blend after entering idle. Set to 0 to disable."); + Category.CreateEntry("IK Lerp Speed", 10f, description: "Determines fast the IK & Locomotion weights blend after entering idle. Set to 0 to disable."); public static readonly MelonPreferences_Entry EntryProneThrusting = - CategoryDesktopVRIK.CreateEntry("Prone Thrusting", false, description: "Allows Body Lean Weight to take effect while crouched or prone."); + Category.CreateEntry("Prone Thrusting", false, description: "Allows Body Lean Weight to take effect while crouched or prone."); + + public static readonly MelonPreferences_Entry EntryNetIKPass = + Category.CreateEntry("Network IK Pass", true, description: "Should NetIK pass be applied? This fixes a bunch of small rotation errors after VRIK is run."); public static readonly MelonPreferences_Entry EntryIntegrationAMT = - CategoryDesktopVRIK.CreateEntry("AMT Integration", true, description: "Relies on AvatarMotionTweaker to handle VRIK Locomotion weights if available."); + Category.CreateEntry("AMT Integration", true, description: "Relies on AvatarMotionTweaker to handle VRIK Locomotion weights if available."); public static bool integration_AMT = false; @@ -50,57 +52,27 @@ public class DesktopVRIK : MelonMod { Logger = LoggerInstance; - CategoryDesktopVRIK.Entries.ForEach(e => e.OnEntryValueChangedUntyped.Subscribe(OnUpdateSettings)); - - ApplyPatches(typeof(HarmonyPatches.PlayerSetupPatches)); - //BTKUILib Misc Tab if (MelonMod.RegisteredMelons.Any(it => it.Info.Name == "BTKUILib")) { Logger.Msg("Initializing BTKUILib support."); - BTKUIAddon.Init(); + Integrations.BTKUIAddon.Init(); } + //AvatarMotionTweaker Handling if (MelonMod.RegisteredMelons.Any(it => it.Info.Name == "AvatarMotionTweaker")) { - Logger.Msg("AvatarMotionTweaker was found. Relying on it to handle VRIK locomotion."); integration_AMT = true; + Logger.Msg("AvatarMotionTweaker was found. Relying on it to handle VRIK locomotion."); } else { Logger.Msg("AvatarMotionTweaker was not found. Using built-in VRIK locomotion handling."); } + + ApplyPatches(typeof(HarmonyPatches.PlayerSetupPatches)); } - internal static void UpdateAllSettings() - { - if (!DesktopVRIKSystem.Instance) return; - // DesktopVRIK Settings - DesktopVRIKSystem.Instance.Setting_Enabled = EntryEnabled.Value; - DesktopVRIKSystem.Instance.Setting_PlantFeet = EntryPlantFeet.Value; - DesktopVRIKSystem.Instance.Setting_ResetFootsteps = EntryResetFootstepsOnIdle.Value; - - DesktopVRIKSystem.Instance.Setting_BodyLeanWeight = Mathf.Clamp01(EntryBodyLeanWeight.Value); - DesktopVRIKSystem.Instance.Setting_BodyHeadingLimit = Mathf.Clamp(EntryBodyHeadingLimit.Value, 0f, 90f); - DesktopVRIKSystem.Instance.Setting_PelvisHeadingWeight = (1f - Mathf.Clamp01(EntryPelvisHeadingWeight.Value)); - DesktopVRIKSystem.Instance.Setting_ChestHeadingWeight = (1f - Mathf.Clamp01(EntryChestHeadingWeight.Value)); - DesktopVRIKSystem.Instance.Setting_ChestHeadingWeight = (1f - Mathf.Clamp01(EntryChestHeadingWeight.Value)); - DesktopVRIKSystem.Instance.Setting_IKLerpSpeed = Mathf.Clamp(EntryIKLerpSpeed.Value, 0f, 20f); - - // Calibration Settings - DesktopVRIKSystem.Instance.Setting_UseVRIKToes = EntryUseVRIKToes.Value; - - // Fine-tuning Settings - DesktopVRIKSystem.Instance.Setting_ResetFootsteps = EntryResetFootstepsOnIdle.Value; - - // Integration Settings - DesktopVRIKSystem.Instance.Setting_IntegrationAMT = EntryIntegrationAMT.Value && integration_AMT; - - // Funny Settings - DesktopVRIKSystem.Instance.Setting_ProneThrusting = EntryProneThrusting.Value; - } - void OnUpdateSettings(object arg1, object arg2) => UpdateAllSettings(); - void ApplyPatches(Type type) { try diff --git a/DesktopVRIK/Properties/AssemblyInfo.cs b/DesktopVRIK/Properties/AssemblyInfo.cs index 61ac95a..c35a721 100644 --- a/DesktopVRIK/Properties/AssemblyInfo.cs +++ b/DesktopVRIK/Properties/AssemblyInfo.cs @@ -26,6 +26,6 @@ using System.Reflection; namespace NAK.DesktopVRIK.Properties; internal static class AssemblyInfoParams { - public const string Version = "4.1.8"; + public const string Version = "4.2.0"; public const string Author = "NotAKidoS"; } \ No newline at end of file diff --git a/DesktopVRIK/VRIKHelpers/CachedSolver.cs b/DesktopVRIK/VRIKHelpers/CachedSolver.cs new file mode 100644 index 0000000..2b651f1 --- /dev/null +++ b/DesktopVRIK/VRIKHelpers/CachedSolver.cs @@ -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; + } +} \ No newline at end of file diff --git a/DesktopVRIK/VRIKHelpers/VRIKCalibrationData.cs b/DesktopVRIK/VRIKHelpers/VRIKCalibrationData.cs new file mode 100644 index 0000000..f06ae46 --- /dev/null +++ b/DesktopVRIK/VRIKHelpers/VRIKCalibrationData.cs @@ -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; + } +} \ No newline at end of file diff --git a/DesktopVRIK/VRIKHelpers/VRIKConfiguration.cs b/DesktopVRIK/VRIKHelpers/VRIKConfiguration.cs new file mode 100644 index 0000000..85d783c --- /dev/null +++ b/DesktopVRIK/VRIKHelpers/VRIKConfiguration.cs @@ -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; + } +} \ No newline at end of file diff --git a/DesktopVRIK/VRIKHelpers/VRIKConfigurations.cs b/DesktopVRIK/VRIKHelpers/VRIKConfigurations.cs new file mode 100644 index 0000000..b412460 --- /dev/null +++ b/DesktopVRIK/VRIKHelpers/VRIKConfigurations.cs @@ -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, + }; + } +} \ No newline at end of file diff --git a/DesktopVRIK/VRIKUtils.cs b/DesktopVRIK/VRIKHelpers/VRIKUtils.cs similarity index 59% rename from DesktopVRIK/VRIKUtils.cs rename to DesktopVRIK/VRIKHelpers/VRIKUtils.cs index 53ef94b..ec3288d 100644 --- a/DesktopVRIK/VRIKUtils.cs +++ b/DesktopVRIK/VRIKHelpers/VRIKUtils.cs @@ -1,15 +1,12 @@ using RootMotion.FinalIK; using UnityEngine; -namespace NAK.DesktopVRIK; +namespace NAK.DesktopVRIK.VRIKHelper; public static class VRIKUtils { public static void ConfigureVRIKReferences(VRIK vrik, bool useVRIKToes) { - //might not work over netik - //FixChestAndSpineReferences(vrik); - if (!useVRIKToes) { vrik.references.leftToes = null; @@ -20,36 +17,22 @@ public static class VRIKUtils FixFingerBonesError(vrik); } - private static void FixChestAndSpineReferences(VRIK vrik) - { - Transform leftShoulderBone = vrik.references.leftShoulder; - Transform rightShoulderBone = vrik.references.rightShoulder; - Transform assumedChest = leftShoulderBone?.parent; - - if (assumedChest != null && rightShoulderBone.parent == assumedChest && - vrik.references.chest != assumedChest) - { - vrik.references.chest = assumedChest; - vrik.references.spine = assumedChest.parent; - } - } - 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); } - private static 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; - } - } - - public static void CalculateKneeBendNormals(VRIK vrik, out Vector3 leftKneeNormal, out Vector3 rightKneeNormal) + 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; @@ -60,7 +43,7 @@ public static class VRIKUtils GetPositionOrDefault(vrik.references.leftCalf), GetPositionOrDefault(vrik.references.leftFoot) }; - leftKneeNormal = Quaternion.Inverse(vrik.references.root.rotation) * GetNormalFromArray(leftVectors); + calibrationData.KneeNormalLeft = Quaternion.Inverse(vrik.references.root.rotation) * GetNormalFromArray(leftVectors); // Get assumed right knee normal Vector3[] rightVectors = { @@ -68,10 +51,10 @@ public static class VRIKUtils GetPositionOrDefault(vrik.references.rightCalf), GetPositionOrDefault(vrik.references.rightFoot) }; - rightKneeNormal = Quaternion.Inverse(vrik.references.root.rotation) * GetNormalFromArray(rightVectors); + calibrationData.KneeNormalRight = Quaternion.Inverse(vrik.references.root.rotation) * GetNormalFromArray(rightVectors); } - public static void ApplyKneeBendNormals(VRIK vrik, Vector3 leftKneeNormal, Vector3 rightKneeNormal) + public static void ApplyKneeBendNormals(VRIK vrik, VRIKCalibrationData calibrationData) { // 0 uses bendNormalRelToPelvis, 1 is bendNormalRelToTarget // modifying pelvis normal weight is easier math @@ -79,8 +62,8 @@ public static class VRIKUtils vrik.solver.rightLeg.bendToTargetWeight = 0f; var pelvis_localRotationInverse = Quaternion.Inverse(vrik.references.pelvis.localRotation); - vrik.solver.leftLeg.bendNormalRelToPelvis = pelvis_localRotationInverse * leftKneeNormal; - vrik.solver.rightLeg.bendNormalRelToPelvis = pelvis_localRotationInverse * rightKneeNormal; + vrik.solver.leftLeg.bendNormalRelToPelvis = pelvis_localRotationInverse * calibrationData.KneeNormalLeft; + vrik.solver.rightLeg.bendNormalRelToPelvis = pelvis_localRotationInverse * calibrationData.KneeNormalRight; } private static Vector3 GetNormalFromArray(Vector3[] positions) @@ -102,17 +85,19 @@ public static class VRIKUtils return normal.normalized; } - public static void CalculateInitialIKScaling(VRIK vrik, out float initialFootDistance, out float initialStepThreshold, out float initialStepHeight) + 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); - initialFootDistance = footDistance * 0.5f; - initialStepThreshold = footDistance * scaleModifier; - initialStepHeight = Vector3.Distance(vrik.references.leftFoot.position, vrik.references.leftCalf.position) * 0.2f; + + 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, out Vector3 initialFootPosLeft, out Vector3 initialFootPosRight, out Quaternion initialFootRotLeft, out Quaternion initialFootRotRight) + public static void CalculateInitialFootsteps(VRIK vrik, ref VRIKCalibrationData calibrationData) { Transform root = vrik.references.root; Transform leftFoot = vrik.references.leftFoot; @@ -122,25 +107,26 @@ public static class VRIKUtils Quaternion rootWorldRot = root.rotation; // Calculate the world rotation of the left and right feet relative to the root bone - initialFootPosLeft = root.InverseTransformPoint(leftFoot.position); - initialFootPosRight = root.InverseTransformPoint(rightFoot.position); - initialFootRotLeft = Quaternion.Inverse(rootWorldRot) * leftFoot.rotation; - initialFootRotRight = Quaternion.Inverse(rootWorldRot) * rightFoot.rotation; + 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 SetFootsteps(VRIK vrik, Vector3 footPosLeft, Vector3 footPosRight, Quaternion footRotLeft, Quaternion footRotRight) + 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(footPosLeft), rootWorldRot * footRotLeft); - footstepRight.Reset(rootWorldRot, root.parent.TransformPoint(footPosRight), rootWorldRot * footRotRight); + 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) @@ -155,15 +141,15 @@ public static class VRIKUtils vrik.solver.spine.headTarget.localRotation = Quaternion.identity; } - public static void ApplyScaleToVRIK(VRIK vrik, float footDistance, float stepThreshold, float stepHeight, float modifier) + public static void ApplyScaleToVRIK(VRIK vrik, VRIKCalibrationData calibrationData, float scaleModifier) { var locomotionSolver = vrik.solver.locomotion; - locomotionSolver.footDistance = footDistance * modifier; - locomotionSolver.stepThreshold = stepThreshold * modifier; - ScaleStepHeight(locomotionSolver.stepHeight, stepHeight * modifier); + locomotionSolver.footDistance = calibrationData.InitialFootDistance * scaleModifier; + locomotionSolver.stepThreshold = calibrationData.InitialStepThreshold * scaleModifier; + ScaleStepHeight(locomotionSolver.stepHeight, calibrationData.InitialStepHeight * scaleModifier); } - private static void ScaleStepHeight(AnimationCurve stepHeightCurve, float mag) + static void ScaleStepHeight(AnimationCurve stepHeightCurve, float mag) { Keyframe[] keyframes = stepHeightCurve.keys; keyframes[1].value = mag;