mirror of
https://github.com/NotAKidoS/NAK_CVR_Mods.git
synced 2025-09-02 14:29:25 +00:00
clean up
This commit is contained in:
parent
20222e563e
commit
3807482093
8 changed files with 347 additions and 396 deletions
|
@ -1,5 +1,4 @@
|
||||||
using ABI_RC.Core.Player;
|
using ABI_RC.Core.Player;
|
||||||
using ABI_RC.Systems.IK;
|
|
||||||
using ABI_RC.Systems.IK.SubSystems;
|
using ABI_RC.Systems.IK.SubSystems;
|
||||||
using ABI_RC.Systems.MovementSystem;
|
using ABI_RC.Systems.MovementSystem;
|
||||||
using RootMotion.FinalIK;
|
using RootMotion.FinalIK;
|
||||||
|
@ -28,6 +27,8 @@ public class DesktopVRIK : MonoBehaviour
|
||||||
float ik_SimulatedRootAngle;
|
float ik_SimulatedRootAngle;
|
||||||
Transform desktopCameraTransform;
|
Transform desktopCameraTransform;
|
||||||
static readonly FieldInfo ms_isGrounded = typeof(MovementSystem).GetField("_isGrounded", BindingFlags.NonPublic | BindingFlags.Instance);
|
static readonly FieldInfo ms_isGrounded = typeof(MovementSystem).GetField("_isGrounded", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||||
|
bool forceSteps;
|
||||||
|
bool forceStepsNow;
|
||||||
|
|
||||||
void Start()
|
void Start()
|
||||||
{
|
{
|
||||||
|
@ -44,46 +45,43 @@ public class DesktopVRIK : MonoBehaviour
|
||||||
ResetDesktopVRIK();
|
ResetDesktopVRIK();
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool OnSetupIKScaling(float avatarHeight, float scaleDifference)
|
public bool OnSetupIKScaling(float scaleDifference)
|
||||||
{
|
{
|
||||||
if (Calibrator.vrik != null)
|
if (Calibrator.vrik == null) return false;
|
||||||
{
|
|
||||||
Calibrator.vrik.solver.locomotion.footDistance = Calibrator.initialFootDistance * scaleDifference;
|
|
||||||
Calibrator.vrik.solver.locomotion.stepThreshold = Calibrator.initialStepThreshold * scaleDifference;
|
|
||||||
DesktopVRIK.ScaleStepHeight(Calibrator.vrik.solver.locomotion.stepHeight, Calibrator.initialStepHeight * scaleDifference);
|
|
||||||
//Calibrator.vrik.solver.Reset();
|
|
||||||
ResetDesktopVRIK();
|
|
||||||
|
|
||||||
return true;
|
VRIKUtils.ApplyScaleToVRIK
|
||||||
}
|
(
|
||||||
return false;
|
Calibrator.vrik,
|
||||||
}
|
Calibrator.initialFootDistance,
|
||||||
|
Calibrator.initialStepThreshold,
|
||||||
|
Calibrator.initialStepHeight,
|
||||||
|
scaleDifference
|
||||||
|
);
|
||||||
|
|
||||||
public static void ScaleStepHeight(AnimationCurve stepHeightCurve, float mag)
|
ResetDesktopVRIK();
|
||||||
{
|
return true;
|
||||||
Keyframe[] keyframes = stepHeightCurve.keys;
|
|
||||||
keyframes[1].value = mag;
|
|
||||||
stepHeightCurve.keys = keyframes;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnPlayerSetupUpdate(bool isEmotePlaying)
|
public void OnPlayerSetupUpdate(bool isEmotePlaying)
|
||||||
{
|
{
|
||||||
bool changed = isEmotePlaying != ps_emoteIsPlaying;
|
bool changed = isEmotePlaying != ps_emoteIsPlaying;
|
||||||
if (changed)
|
if (!changed) return;
|
||||||
{
|
|
||||||
ps_emoteIsPlaying = isEmotePlaying;
|
ps_emoteIsPlaying = isEmotePlaying;
|
||||||
Calibrator.avatarTransform.localPosition = Vector3.zero;
|
|
||||||
Calibrator.avatarTransform.localRotation = Quaternion.identity;
|
Calibrator.avatarTransform.localPosition = Vector3.zero;
|
||||||
if (Calibrator.lookAtIK != null)
|
Calibrator.avatarTransform.localRotation = Quaternion.identity;
|
||||||
{
|
|
||||||
Calibrator.lookAtIK.enabled = !isEmotePlaying;
|
if (Calibrator.lookAtIK != null)
|
||||||
}
|
Calibrator.lookAtIK.enabled = !isEmotePlaying;
|
||||||
BodySystem.TrackingEnabled = !isEmotePlaying;
|
|
||||||
Calibrator.vrik.solver?.Reset();
|
BodySystem.TrackingEnabled = !isEmotePlaying;
|
||||||
ResetDesktopVRIK();
|
|
||||||
}
|
Calibrator.vrik.solver?.Reset();
|
||||||
|
ResetDesktopVRIK();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void ResetDesktopVRIK()
|
public void ResetDesktopVRIK()
|
||||||
{
|
{
|
||||||
ik_SimulatedRootAngle = transform.eulerAngles.y;
|
ik_SimulatedRootAngle = transform.eulerAngles.y;
|
||||||
|
@ -91,33 +89,34 @@ public class DesktopVRIK : MonoBehaviour
|
||||||
|
|
||||||
public void OnPreSolverUpdate()
|
public void OnPreSolverUpdate()
|
||||||
{
|
{
|
||||||
if (ps_emoteIsPlaying)
|
if (ps_emoteIsPlaying) return;
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isGrounded = (bool)ms_isGrounded.GetValue(MovementSystem.Instance);
|
var movementSystem = MovementSystem.Instance;
|
||||||
|
var vrikSolver = Calibrator.vrik.solver;
|
||||||
|
var avatarTransform = Calibrator.avatarTransform;
|
||||||
|
|
||||||
// Calculate everything that affects weight
|
bool isGrounded = (bool)ms_isGrounded.GetValue(movementSystem);
|
||||||
float weight = Calibrator.vrik.solver.IKPositionWeight;
|
|
||||||
weight *= (1 - MovementSystem.Instance.movementVector.magnitude);
|
// Calculate weight
|
||||||
|
float weight = vrikSolver.IKPositionWeight;
|
||||||
|
weight *= 1f - movementSystem.movementVector.magnitude;
|
||||||
weight *= isGrounded ? 1f : 0f;
|
weight *= isGrounded ? 1f : 0f;
|
||||||
|
|
||||||
// Reset avatar offset (VRIK will literally make you walk away from root otherwise)
|
// Reset avatar offset
|
||||||
Calibrator.avatarTransform.localPosition = Vector3.zero;
|
avatarTransform.localPosition = Vector3.zero;
|
||||||
Calibrator.avatarTransform.localRotation = Quaternion.identity;
|
avatarTransform.localRotation = Quaternion.identity;
|
||||||
|
|
||||||
// Plant feet is nice for Desktop
|
// Set plant feet
|
||||||
Calibrator.vrik.solver.plantFeet = Setting_PlantFeet;
|
vrikSolver.plantFeet = Setting_PlantFeet;
|
||||||
|
|
||||||
// Old VRChat hip movement emulation
|
// 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 = desktopCameraTransform.localEulerAngles.x;
|
||||||
angle = (angle > 180) ? angle - 360 : angle;
|
angle = angle > 180 ? angle - 360 : angle;
|
||||||
Quaternion rotation = Quaternion.AngleAxis(angle * weightedAngle, Calibrator.avatarTransform.right);
|
Quaternion rotation = Quaternion.AngleAxis(angle * weightedAngle, avatarTransform.right);
|
||||||
Calibrator.vrik.solver.AddRotationOffset(IKSolverVR.RotationOffset.Head, rotation);
|
vrikSolver.AddRotationOffset(IKSolverVR.RotationOffset.Head, rotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make root heading follow within a set limit
|
// Make root heading follow within a set limit
|
||||||
|
@ -131,15 +130,15 @@ public class DesktopVRIK : MonoBehaviour
|
||||||
currentAngle = Mathf.Sign(currentAngle) * weightedAngleLimit;
|
currentAngle = Mathf.Sign(currentAngle) * weightedAngleLimit;
|
||||||
ik_SimulatedRootAngle = Mathf.MoveTowardsAngle(ik_SimulatedRootAngle, transform.eulerAngles.y, angleMaxDelta - weightedAngleLimit);
|
ik_SimulatedRootAngle = Mathf.MoveTowardsAngle(ik_SimulatedRootAngle, transform.eulerAngles.y, angleMaxDelta - weightedAngleLimit);
|
||||||
}
|
}
|
||||||
Calibrator.vrik.solver.spine.rootHeadingOffset = currentAngle;
|
vrikSolver.spine.rootHeadingOffset = currentAngle;
|
||||||
if (Setting_PelvisHeadingWeight > 0)
|
if (Setting_PelvisHeadingWeight > 0)
|
||||||
{
|
{
|
||||||
Calibrator.vrik.solver.AddRotationOffset(IKSolverVR.RotationOffset.Pelvis, new Vector3(0f, currentAngle * Setting_PelvisHeadingWeight, 0f));
|
vrikSolver.AddRotationOffset(IKSolverVR.RotationOffset.Pelvis, new Vector3(0f, currentAngle * Setting_PelvisHeadingWeight, 0f));
|
||||||
Calibrator.vrik.solver.AddRotationOffset(IKSolverVR.RotationOffset.Chest, new Vector3(0f, -currentAngle * Setting_PelvisHeadingWeight, 0f));
|
vrikSolver.AddRotationOffset(IKSolverVR.RotationOffset.Chest, new Vector3(0f, -currentAngle * Setting_PelvisHeadingWeight, 0f));
|
||||||
}
|
}
|
||||||
if (Setting_ChestHeadingWeight > 0)
|
if (Setting_ChestHeadingWeight > 0)
|
||||||
{
|
{
|
||||||
Calibrator.vrik.solver.AddRotationOffset(IKSolverVR.RotationOffset.Chest, new Vector3(0f, currentAngle * Setting_ChestHeadingWeight, 0f));
|
vrikSolver.AddRotationOffset(IKSolverVR.RotationOffset.Chest, new Vector3(0f, currentAngle * Setting_ChestHeadingWeight, 0f));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,8 +16,6 @@ public class DesktopVRIKCalibrator
|
||||||
// Settings
|
// Settings
|
||||||
public bool Setting_UseVRIKToes = true;
|
public bool Setting_UseVRIKToes = true;
|
||||||
public bool Setting_FindUnmappedToes = true;
|
public bool Setting_FindUnmappedToes = true;
|
||||||
public bool Setting_ExperimentalKneeBend = true;
|
|
||||||
public bool Setting_DebugCalibrationPose = false;
|
|
||||||
|
|
||||||
// Avatar Component References
|
// Avatar Component References
|
||||||
public CVRAvatar avatar;
|
public CVRAvatar avatar;
|
||||||
|
@ -27,26 +25,26 @@ public class DesktopVRIKCalibrator
|
||||||
public LookAtIK lookAtIK;
|
public LookAtIK lookAtIK;
|
||||||
|
|
||||||
// Calibrated Values
|
// Calibrated Values
|
||||||
public float
|
public float
|
||||||
initialFootDistance,
|
initialFootDistance,
|
||||||
initialStepThreshold,
|
initialStepThreshold,
|
||||||
initialStepHeight;
|
initialStepHeight;
|
||||||
|
|
||||||
// Calibration Internals
|
// Calibration Internals
|
||||||
bool DebugCalibrationPose;
|
|
||||||
bool fixTransformsRequired;
|
bool fixTransformsRequired;
|
||||||
Vector3 leftKneeNormal, rightKneeNormal;
|
Vector3 leftKneeNormal, rightKneeNormal;
|
||||||
HumanPose initialHumanPose;
|
HumanPose initialHumanPose;
|
||||||
HumanPoseHandler humanPoseHandler;
|
HumanPoseHandler humanPoseHandler;
|
||||||
|
|
||||||
// Traverse
|
// Traverse
|
||||||
IKSystem ikSystem;
|
IKSystem ikSystem;
|
||||||
PlayerSetup playerSetup;
|
PlayerSetup playerSetup;
|
||||||
Traverse
|
Traverse
|
||||||
_vrikTraverse,
|
_vrikTraverse,
|
||||||
_lookIKTraverse,
|
_lookIKTraverse,
|
||||||
_avatarTraverse,
|
_avatarTraverse,
|
||||||
_animatorManagerTraverse,
|
_animatorManagerTraverse,
|
||||||
_poseHandlerTraverse,
|
_poseHandlerTraverse,
|
||||||
_avatarRootHeightTraverse;
|
_avatarRootHeightTraverse;
|
||||||
|
|
||||||
public DesktopVRIKCalibrator()
|
public DesktopVRIKCalibrator()
|
||||||
|
@ -68,15 +66,10 @@ public class DesktopVRIKCalibrator
|
||||||
|
|
||||||
public void CalibrateDesktopVRIK()
|
public void CalibrateDesktopVRIK()
|
||||||
{
|
{
|
||||||
PreInitialize();
|
// Scan avatar for issues/references
|
||||||
|
ScanAvatarForCalibration();
|
||||||
// Don't do anything else if just debugging calibration pose
|
// Prepare CVR IKSystem for external VRIK
|
||||||
DebugCalibrationPose = !DebugCalibrationPose;
|
PrepareIKSystem();
|
||||||
if (Setting_DebugCalibrationPose && DebugCalibrationPose)
|
|
||||||
{
|
|
||||||
ForceCalibrationPose();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add VRIK and configure
|
// Add VRIK and configure
|
||||||
PrepareAvatarVRIK();
|
PrepareAvatarVRIK();
|
||||||
|
@ -86,51 +79,42 @@ public class DesktopVRIKCalibrator
|
||||||
PostInitialize();
|
PostInitialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ForceCalibrationPose(bool toggle = true)
|
|
||||||
{
|
|
||||||
animator.enabled = !toggle;
|
|
||||||
SetHumanPose(0f);
|
|
||||||
//SetAvatarIKPose(toggle);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void PreInitialize()
|
|
||||||
{
|
|
||||||
// Scan avatar for issues/references
|
|
||||||
ScanAvatarForCalibration();
|
|
||||||
// Prepare CVR IKSystem for external VRIK
|
|
||||||
PrepareIKSystem();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Initialize()
|
private void Initialize()
|
||||||
{
|
{
|
||||||
// Calculate bend normals with motorcycle pose
|
// Calculate bend normals with motorcycle pose
|
||||||
SetHumanPose(0f);
|
SetHumanPose(0f);
|
||||||
CalculateKneeBendNormals();
|
VRIKUtils.CalculateKneeBendNormals(vrik, out leftKneeNormal, out rightKneeNormal);
|
||||||
|
|
||||||
|
// Calculate initial IK scaling values with IKPose
|
||||||
SetAvatarIKPose(true);
|
SetAvatarIKPose(true);
|
||||||
|
VRIKUtils.CalculateInitialIKScaling(vrik, out initialFootDistance, out initialStepThreshold, out initialStepHeight);
|
||||||
|
|
||||||
// Setup HeadIK target & calculate initial footstep values
|
// Setup HeadIK target & calculate initial footstep values
|
||||||
SetupDesktopHeadIKTarget();
|
SetupDesktopHeadIKTarget();
|
||||||
CalculateInitialIKScaling();
|
|
||||||
|
|
||||||
// Initiate VRIK manually
|
// Initiate VRIK manually
|
||||||
ForceInitiateVRIKSolver();
|
VRIKUtils.InitiateVRIKSolver(vrik);
|
||||||
|
|
||||||
|
// Return avatar to original pose
|
||||||
SetAvatarIKPose(false);
|
SetAvatarIKPose(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void PostInitialize()
|
private void PostInitialize()
|
||||||
{
|
{
|
||||||
ApplyKneeBendNormals();
|
VRIKUtils.ApplyScaleToVRIK
|
||||||
ApplyInitialIKScaling();
|
(
|
||||||
|
vrik,
|
||||||
|
initialFootDistance,
|
||||||
|
initialStepThreshold,
|
||||||
|
initialStepHeight,
|
||||||
|
1f
|
||||||
|
);
|
||||||
|
VRIKUtils.ApplyKneeBendNormals(vrik, leftKneeNormal, rightKneeNormal);
|
||||||
vrik.onPreSolverUpdate.AddListener(new UnityAction(DesktopVRIK.Instance.OnPreSolverUpdate));
|
vrik.onPreSolverUpdate.AddListener(new UnityAction(DesktopVRIK.Instance.OnPreSolverUpdate));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ScanAvatarForCalibration()
|
private void ScanAvatarForCalibration()
|
||||||
{
|
{
|
||||||
// Reset some stuff to default
|
|
||||||
fixTransformsRequired = false;
|
|
||||||
|
|
||||||
// Find required avatar components
|
// Find required avatar components
|
||||||
avatar = playerSetup._avatar.GetComponent<CVRAvatar>();
|
avatar = playerSetup._avatar.GetComponent<CVRAvatar>();
|
||||||
animator = avatar.GetComponent<Animator>();
|
animator = avatar.GetComponent<Animator>();
|
||||||
|
@ -138,22 +122,15 @@ public class DesktopVRIKCalibrator
|
||||||
lookAtIK = _lookIKTraverse.GetValue<LookAtIK>();
|
lookAtIK = _lookIKTraverse.GetValue<LookAtIK>();
|
||||||
|
|
||||||
// Apply some fixes for weird setups
|
// Apply some fixes for weird setups
|
||||||
if (!animator.enabled)
|
fixTransformsRequired = !animator.enabled;
|
||||||
{
|
|
||||||
fixTransformsRequired = true;
|
|
||||||
DesktopVRIKMod.Logger.Error("Avatar has Animator disabled by default!");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Center avatar local offsets
|
// Center avatar local position
|
||||||
avatarTransform.localPosition = Vector3.zero;
|
avatarTransform.localPosition = Vector3.zero;
|
||||||
//avatarTransform.localRotation = Quaternion.identity;
|
|
||||||
|
|
||||||
// Store original human pose
|
// Create a new human pose handler and dispose the old one
|
||||||
if (humanPoseHandler != null)
|
humanPoseHandler?.Dispose();
|
||||||
{
|
|
||||||
humanPoseHandler.Dispose();
|
|
||||||
}
|
|
||||||
humanPoseHandler = new HumanPoseHandler(animator.avatar, avatarTransform);
|
humanPoseHandler = new HumanPoseHandler(animator.avatar, avatarTransform);
|
||||||
|
// Store original human pose
|
||||||
humanPoseHandler.GetHumanPose(ref initialHumanPose);
|
humanPoseHandler.GetHumanPose(ref initialHumanPose);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,21 +145,13 @@ public class DesktopVRIKCalibrator
|
||||||
|
|
||||||
// Set the animator for the IK system
|
// Set the animator for the IK system
|
||||||
ikSystem.animator = animator;
|
ikSystem.animator = animator;
|
||||||
if (ikSystem.animator != null)
|
animatorManager.SetAnimator(ikSystem.animator, ikSystem.animator.runtimeAnimatorController);
|
||||||
{
|
|
||||||
animatorManager.SetAnimator(ikSystem.animator, ikSystem.animator.runtimeAnimatorController);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the avatar height float
|
// Set the avatar height float
|
||||||
float avatarHeight = ikSystem.vrPlaySpace.transform.InverseTransformPoint(avatarTransform.position).y;
|
_avatarRootHeightTraverse.SetValue(ikSystem.vrPlaySpace.transform.InverseTransformPoint(avatarTransform.position).y);
|
||||||
_avatarRootHeightTraverse.SetValue(avatarHeight);
|
|
||||||
|
|
||||||
// Create a new human pose handler and dispose the old one
|
// Create a new human pose handler and dispose the old one
|
||||||
if (ikHumanPoseHandler != null)
|
ikHumanPoseHandler?.Dispose();
|
||||||
{
|
|
||||||
ikHumanPoseHandler.Dispose();
|
|
||||||
_poseHandlerTraverse.SetValue(null);
|
|
||||||
}
|
|
||||||
ikHumanPoseHandler = new HumanPoseHandler(ikSystem.animator.avatar, avatarTransform);
|
ikHumanPoseHandler = new HumanPoseHandler(ikSystem.animator.avatar, avatarTransform);
|
||||||
_poseHandlerTraverse.SetValue(ikHumanPoseHandler);
|
_poseHandlerTraverse.SetValue(ikHumanPoseHandler);
|
||||||
|
|
||||||
|
@ -206,156 +175,48 @@ public class DesktopVRIKCalibrator
|
||||||
|
|
||||||
private void PrepareAvatarVRIK()
|
private void PrepareAvatarVRIK()
|
||||||
{
|
{
|
||||||
//add and configure VRIK
|
// Add and configure VRIK
|
||||||
vrik = avatar.gameObject.AddComponentIfMissing<VRIK>();
|
vrik = avatar.gameObject.AddComponentIfMissing<VRIK>();
|
||||||
vrik.AutoDetectReferences();
|
vrik.AutoDetectReferences();
|
||||||
ConfigureVRIKReferences();
|
|
||||||
_vrikTraverse.SetValue(vrik);
|
|
||||||
|
|
||||||
//in testing, not really needed
|
VRIKUtils.ConfigureVRIKReferences(vrik, Setting_UseVRIKToes, Setting_FindUnmappedToes, out bool foundUnmappedToes);
|
||||||
//only required if Setting_FindUnmappedToes
|
|
||||||
//and non-human mapped toes are found
|
|
||||||
vrik.fixTransforms = fixTransformsRequired;
|
|
||||||
|
|
||||||
//default solver settings
|
// Fix animator issue or non-human mapped toes
|
||||||
|
vrik.fixTransforms = fixTransformsRequired || foundUnmappedToes;
|
||||||
|
|
||||||
|
// Default solver settings
|
||||||
vrik.solver.locomotion.weight = 0f;
|
vrik.solver.locomotion.weight = 0f;
|
||||||
vrik.solver.locomotion.angleThreshold = 30f;
|
vrik.solver.locomotion.angleThreshold = 30f;
|
||||||
vrik.solver.locomotion.maxLegStretch = 0.75f;
|
vrik.solver.locomotion.maxLegStretch = 1f;
|
||||||
vrik.solver.spine.minHeadHeight = 0f;
|
vrik.solver.spine.minHeadHeight = 0f;
|
||||||
vrik.solver.IKPositionWeight = 1f;
|
vrik.solver.IKPositionWeight = 1f;
|
||||||
//disable to not bleed into anims
|
|
||||||
vrik.solver.spine.chestClampWeight = 0f;
|
vrik.solver.spine.chestClampWeight = 0f;
|
||||||
vrik.solver.spine.maintainPelvisPosition = 0f;
|
vrik.solver.spine.maintainPelvisPosition = 0f;
|
||||||
//for body leaning
|
|
||||||
vrik.solver.spine.neckStiffness = 0.0001f; //cannot be 0
|
// Body leaning settings
|
||||||
|
vrik.solver.spine.neckStiffness = 0.0001f;
|
||||||
vrik.solver.spine.bodyPosStiffness = 1f;
|
vrik.solver.spine.bodyPosStiffness = 1f;
|
||||||
vrik.solver.spine.bodyRotStiffness = 0.2f;
|
vrik.solver.spine.bodyRotStiffness = 0.2f;
|
||||||
//disable so avatar doesnt try and walk away
|
|
||||||
|
// Disable locomotion
|
||||||
vrik.solver.locomotion.velocityFactor = 0f;
|
vrik.solver.locomotion.velocityFactor = 0f;
|
||||||
vrik.solver.locomotion.maxVelocity = 0f;
|
vrik.solver.locomotion.maxVelocity = 0f;
|
||||||
//fixes nameplate spazzing on remote & magicacloth
|
|
||||||
vrik.solver.locomotion.rootSpeed = 1000f;
|
vrik.solver.locomotion.rootSpeed = 1000f;
|
||||||
//disable so PAM & BID dont make body shake
|
|
||||||
|
// Disable chest rotation by hands
|
||||||
vrik.solver.spine.rotateChestByHands = 0f;
|
vrik.solver.spine.rotateChestByHands = 0f;
|
||||||
//enable to prioritize LookAtIK
|
|
||||||
|
// Prioritize LookAtIK
|
||||||
vrik.solver.spine.headClampWeight = 0.2f;
|
vrik.solver.spine.headClampWeight = 0.2f;
|
||||||
//disable to not go on tippytoes
|
|
||||||
|
// Disable going on tippytoes
|
||||||
vrik.solver.spine.positionWeight = 0f;
|
vrik.solver.spine.positionWeight = 0f;
|
||||||
vrik.solver.spine.rotationWeight = 1f;
|
vrik.solver.spine.rotationWeight = 1f;
|
||||||
|
|
||||||
//vrik.solver.spine.maintainPelvisPosition = 1f;
|
// Tell IKSystem about new VRIK
|
||||||
//vrik.solver.locomotion.weight = 0f;
|
_vrikTraverse.SetValue(vrik);
|
||||||
//vrik.solver.spine.positionWeight = 0f;
|
|
||||||
//vrik.solver.spine.pelvisPositionWeight = 0f;
|
|
||||||
//vrik.solver.leftArm.positionWeight = 0f;
|
|
||||||
//vrik.solver.leftArm.rotationWeight = 0f;
|
|
||||||
//vrik.solver.rightArm.positionWeight = 0f;
|
|
||||||
//vrik.solver.rightArm.rotationWeight = 0f;
|
|
||||||
//vrik.solver.leftLeg.positionWeight = 0f;
|
|
||||||
//vrik.solver.leftLeg.rotationWeight = 0f;
|
|
||||||
//vrik.solver.rightLeg.positionWeight = 0f;
|
|
||||||
//vrik.solver.rightLeg.rotationWeight = 0f;
|
|
||||||
//vrik.solver.IKPositionWeight = 0f;
|
|
||||||
|
|
||||||
//THESE ARE CONFIGURABLE IN GAME IK SETTINGS
|
|
||||||
//vrik.solver.leftLeg.target = null;
|
|
||||||
//vrik.solver.leftLeg.bendGoal = null;
|
|
||||||
//vrik.solver.leftLeg.positionWeight = 0f;
|
|
||||||
//vrik.solver.leftLeg.bendGoalWeight = 0f;
|
|
||||||
//vrik.solver.rightLeg.target = null;
|
|
||||||
//vrik.solver.rightLeg.bendGoal = null;
|
|
||||||
//vrik.solver.rightLeg.positionWeight = 0f;
|
|
||||||
//vrik.solver.rightLeg.bendGoalWeight = 0f;
|
|
||||||
//vrik.solver.spine.pelvisTarget = null;
|
|
||||||
//vrik.solver.spine.chestGoal = null;
|
|
||||||
//vrik.solver.spine.positionWeight = 0f;
|
|
||||||
//vrik.solver.spine.rotationWeight = 0f;
|
|
||||||
//vrik.solver.spine.pelvisPositionWeight = 0f;
|
|
||||||
//vrik.solver.spine.pelvisRotationWeight = 0f;
|
|
||||||
//vrik.solver.spine.chestGoalWeight = 0f;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CalculateKneeBendNormals()
|
|
||||||
{
|
|
||||||
// Get assumed left knee normal
|
|
||||||
Vector3[] leftVectors = new Vector3[]
|
|
||||||
{
|
|
||||||
vrik.references.leftThigh?.position ?? Vector3.zero,
|
|
||||||
vrik.references.leftCalf?.position ?? Vector3.zero,
|
|
||||||
vrik.references.leftFoot?.position ?? Vector3.zero,
|
|
||||||
};
|
|
||||||
leftKneeNormal = Quaternion.Inverse(vrik.references.root.rotation) * GetNormalFromArray(leftVectors);
|
|
||||||
|
|
||||||
// Get assumed right knee normal
|
|
||||||
Vector3[] rightVectors = new Vector3[]
|
|
||||||
{
|
|
||||||
vrik.references.rightThigh?.position ?? Vector3.zero,
|
|
||||||
vrik.references.rightCalf?.position ?? Vector3.zero,
|
|
||||||
vrik.references.rightFoot?.position ?? Vector3.zero,
|
|
||||||
};
|
|
||||||
rightKneeNormal = Quaternion.Inverse(vrik.references.root.rotation) * GetNormalFromArray(rightVectors);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ApplyKneeBendNormals()
|
|
||||||
{
|
|
||||||
if (!Setting_ExperimentalKneeBend)
|
|
||||||
{
|
|
||||||
//enable so knees on fucked models work better
|
|
||||||
vrik.solver.leftLeg.useAnimatedBendNormal = true;
|
|
||||||
vrik.solver.rightLeg.useAnimatedBendNormal = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
vrik.solver.leftLeg.bendToTargetWeight = 0f;
|
|
||||||
vrik.solver.rightLeg.bendToTargetWeight = 0f;
|
|
||||||
|
|
||||||
Traverse leftLeg_bendNormalRelToPelvisTraverse = Traverse.Create(vrik.solver.leftLeg).Field("bendNormalRelToPelvis");
|
|
||||||
Traverse rightLeg_bendNormalRelToPelvisTraverse = Traverse.Create(vrik.solver.rightLeg).Field("bendNormalRelToPelvis");
|
|
||||||
|
|
||||||
// Calculate knee normal without root rotation but with pelvis rotation
|
|
||||||
Quaternion pelvisLocalRotationInverse = Quaternion.Inverse(vrik.references.pelvis.localRotation);
|
|
||||||
Vector3 leftLegBendNormalRelToPelvis = pelvisLocalRotationInverse * leftKneeNormal;
|
|
||||||
Vector3 rightLegBendNormalRelToPelvis = pelvisLocalRotationInverse * rightKneeNormal;
|
|
||||||
//Quaternion rootRotation = vrik.references.root.rotation;
|
|
||||||
//Quaternion pelvisRotationRelativeToRoot = Quaternion.Inverse(rootRotation) * vrik.references.pelvis.rotation;
|
|
||||||
//Quaternion pelvisRotationInverse = Quaternion.Inverse(pelvisRotationRelativeToRoot);
|
|
||||||
leftLeg_bendNormalRelToPelvisTraverse.SetValue(leftLegBendNormalRelToPelvis);
|
|
||||||
rightLeg_bendNormalRelToPelvisTraverse.SetValue(rightLegBendNormalRelToPelvis);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Vector3 GetNormalFromArray(Vector3[] positions)
|
|
||||||
{
|
|
||||||
Vector3 vector = Vector3.zero;
|
|
||||||
Vector3 vector2 = Vector3.zero;
|
|
||||||
for (int i = 0; i < positions.Length; i++)
|
|
||||||
{
|
|
||||||
vector2 += positions[i];
|
|
||||||
}
|
|
||||||
vector2 /= (float)positions.Length;
|
|
||||||
for (int j = 0; j < positions.Length - 1; j++)
|
|
||||||
{
|
|
||||||
vector += Vector3.Cross(positions[j] - vector2, positions[j + 1] - vector2).normalized;
|
|
||||||
}
|
|
||||||
return Vector3.Normalize(vector);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CalculateInitialIKScaling()
|
|
||||||
{
|
|
||||||
// Get distance between feets and thighs
|
|
||||||
float footDistance = Vector3.Distance(vrik.references.leftFoot.position, vrik.references.rightFoot.position);
|
|
||||||
initialFootDistance = footDistance * 0.5f;
|
|
||||||
initialStepThreshold = footDistance * 0.8f;
|
|
||||||
initialStepHeight = Vector3.Distance(vrik.references.leftFoot.position, vrik.references.leftCalf.position) * 0.2f;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ApplyInitialIKScaling()
|
|
||||||
{
|
|
||||||
// Set initial values
|
|
||||||
vrik.solver.locomotion.footDistance = initialFootDistance;
|
|
||||||
vrik.solver.locomotion.stepThreshold = initialStepThreshold;
|
|
||||||
DesktopVRIK.ScaleStepHeight(vrik.solver.locomotion.stepHeight, initialStepHeight);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SetupDesktopHeadIKTarget()
|
private void SetupDesktopHeadIKTarget()
|
||||||
{
|
{
|
||||||
// Lazy HeadIKTarget calibration
|
// Lazy HeadIKTarget calibration
|
||||||
|
@ -405,95 +266,6 @@ public class DesktopVRIKCalibrator
|
||||||
humanPoseHandler.SetHumanPose(ref ikSystem.humanPose);
|
humanPoseHandler.SetHumanPose(ref ikSystem.humanPose);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ForceInitiateVRIKSolver()
|
|
||||||
{
|
|
||||||
//force immediate calibration before animator decides to fuck us
|
|
||||||
vrik.solver.SetToReferences(vrik.references);
|
|
||||||
vrik.solver.Initiate(vrik.transform);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ConfigureVRIKReferences()
|
|
||||||
{
|
|
||||||
//might not work over netik
|
|
||||||
FixChestAndSpineReferences();
|
|
||||||
|
|
||||||
if (!Setting_UseVRIKToes)
|
|
||||||
{
|
|
||||||
vrik.references.leftToes = null;
|
|
||||||
vrik.references.rightToes = null;
|
|
||||||
}
|
|
||||||
else if (Setting_FindUnmappedToes)
|
|
||||||
{
|
|
||||||
//doesnt work with netik, but its toes...
|
|
||||||
FindAndSetUnmappedToes();
|
|
||||||
}
|
|
||||||
|
|
||||||
//bullshit fix to not cause death
|
|
||||||
FixFingerBonesError();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void FixChestAndSpineReferences()
|
|
||||||
{
|
|
||||||
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 void FindAndSetUnmappedToes()
|
|
||||||
{
|
|
||||||
Transform leftToes = vrik.references.leftToes;
|
|
||||||
Transform rightToes = vrik.references.rightToes;
|
|
||||||
|
|
||||||
if (leftToes == null && rightToes == null)
|
|
||||||
{
|
|
||||||
leftToes = FindUnmappedToe(vrik.references.leftFoot);
|
|
||||||
rightToes = FindUnmappedToe(vrik.references.rightFoot);
|
|
||||||
|
|
||||||
if (leftToes != null && rightToes != null)
|
|
||||||
{
|
|
||||||
vrik.references.leftToes = leftToes;
|
|
||||||
vrik.references.rightToes = rightToes;
|
|
||||||
fixTransformsRequired = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Transform FindUnmappedToe(Transform foot)
|
|
||||||
{
|
|
||||||
foreach (Transform bone in foot)
|
|
||||||
{
|
|
||||||
if (bone.name.ToLowerInvariant().Contains("toe") ||
|
|
||||||
bone.name.ToLowerInvariant().EndsWith("_end"))
|
|
||||||
{
|
|
||||||
return bone;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void FixFingerBonesError()
|
|
||||||
{
|
|
||||||
FixFingerBones(vrik.references.leftHand, vrik.solver.leftArm);
|
|
||||||
FixFingerBones(vrik.references.rightHand, vrik.solver.rightArm);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void FixFingerBones(Transform hand, IKSolverVR.Arm armSolver)
|
|
||||||
{
|
|
||||||
if (hand.childCount == 0)
|
|
||||||
{
|
|
||||||
armSolver.wristToPalmAxis = Vector3.up;
|
|
||||||
armSolver.palmToThumbAxis = hand == vrik.references.leftHand ? -Vector3.forward : Vector3.forward;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static readonly float[] IKPoseMuscles = new float[]
|
private static readonly float[] IKPoseMuscles = new float[]
|
||||||
{
|
{
|
||||||
0.00133321f,
|
0.00133321f,
|
||||||
|
|
|
@ -44,7 +44,7 @@ class PlayerSetupPatches
|
||||||
[HarmonyPatch(typeof(PlayerSetup), "SetupIKScaling")]
|
[HarmonyPatch(typeof(PlayerSetup), "SetupIKScaling")]
|
||||||
private static bool Prefix_PlayerSetup_SetupIKScaling(float height, ref Vector3 ___scaleDifference)
|
private static bool Prefix_PlayerSetup_SetupIKScaling(float height, ref Vector3 ___scaleDifference)
|
||||||
{
|
{
|
||||||
return !(bool)DesktopVRIK.Instance?.OnSetupIKScaling(height, 1f + ___scaleDifference.y);
|
return !(bool)DesktopVRIK.Instance?.OnSetupIKScaling(1f + ___scaleDifference.y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,31 +14,30 @@ public static class BTKUIAddon
|
||||||
Page miscPage = QuickMenuAPI.MiscTabPage;
|
Page miscPage = QuickMenuAPI.MiscTabPage;
|
||||||
Category miscCategory = miscPage.AddCategory(DesktopVRIKMod.SettingsCategory);
|
Category miscCategory = miscPage.AddCategory(DesktopVRIKMod.SettingsCategory);
|
||||||
|
|
||||||
AddMelonToggle(ref miscCategory, DesktopVRIKMod.m_entryEnabled);
|
AddMelonToggle(ref miscCategory, DesktopVRIKMod.EntryEnabled);
|
||||||
|
|
||||||
//Add my own page to not clog up Misc Menu
|
//Add my own page to not clog up Misc Menu
|
||||||
|
|
||||||
Page desktopVRIKPage = miscCategory.AddPage("DesktopVRIK Settings", "", "Configure the settings for DesktopVRIK.", "DesktopVRIK");
|
Page desktopVRIKPage = miscCategory.AddPage("DesktopVRIK Settings", "", "Configure the settings for DesktopVRIK.", "DesktopVRIK");
|
||||||
desktopVRIKPage.MenuTitle = "DesktopVRIK Settings";
|
desktopVRIKPage.MenuTitle = "DesktopVRIK Settings";
|
||||||
desktopVRIKPage.MenuSubtitle = "Simplified settings for VRIK on Desktop.";
|
|
||||||
|
|
||||||
Category desktopVRIKCategory = desktopVRIKPage.AddCategory("DesktopVRIK");
|
Category desktopVRIKCategory = desktopVRIKPage.AddCategory(DesktopVRIKMod.SettingsCategory);
|
||||||
|
|
||||||
// General Settings
|
// General Settings
|
||||||
AddMelonToggle(ref desktopVRIKCategory, DesktopVRIKMod.m_entryEnabled);
|
AddMelonToggle(ref desktopVRIKCategory, DesktopVRIKMod.EntryEnabled);
|
||||||
AddMelonToggle(ref desktopVRIKCategory, DesktopVRIKMod.m_entryPlantFeet);
|
AddMelonToggle(ref desktopVRIKCategory, DesktopVRIKMod.EntryPlantFeet);
|
||||||
|
|
||||||
// Calibration Settings
|
// Calibration Settings
|
||||||
AddMelonToggle(ref desktopVRIKCategory, DesktopVRIKMod.m_entryUseVRIKToes);
|
AddMelonToggle(ref desktopVRIKCategory, DesktopVRIKMod.EntryUseVRIKToes);
|
||||||
AddMelonToggle(ref desktopVRIKCategory, DesktopVRIKMod.m_entryFindUnmappedToes);
|
AddMelonToggle(ref desktopVRIKCategory, DesktopVRIKMod.EntryFindUnmappedToes);
|
||||||
|
|
||||||
// Body Leaning Weight
|
// Body Leaning Weight
|
||||||
AddMelonSlider(ref desktopVRIKPage, DesktopVRIKMod.m_entryBodyLeanWeight, 0, 1f, 1);
|
AddMelonSlider(ref desktopVRIKPage, DesktopVRIKMod.EntryBodyLeanWeight, 0, 1f, 1);
|
||||||
|
|
||||||
// Max Root Heading Limit & Weights
|
// Max Root Heading Limit & Weights
|
||||||
AddMelonSlider(ref desktopVRIKPage, DesktopVRIKMod.m_entryBodyHeadingLimit, 0, 90f, 0);
|
AddMelonSlider(ref desktopVRIKPage, DesktopVRIKMod.EntryBodyHeadingLimit, 0, 90f, 0);
|
||||||
AddMelonSlider(ref desktopVRIKPage, DesktopVRIKMod.m_entryPelvisHeadingWeight, 0, 1f, 1);
|
AddMelonSlider(ref desktopVRIKPage, DesktopVRIKMod.EntryPelvisHeadingWeight, 0, 1f, 1);
|
||||||
AddMelonSlider(ref desktopVRIKPage, DesktopVRIKMod.m_entryChestHeadingWeight, 0, 1f, 1);
|
AddMelonSlider(ref desktopVRIKPage, DesktopVRIKMod.EntryChestHeadingWeight, 0, 1f, 1);
|
||||||
}
|
}
|
||||||
private static void AddMelonToggle(ref Category category, MelonLoader.MelonPreferences_Entry<bool> entry)
|
private static void AddMelonToggle(ref Category category, MelonLoader.MelonPreferences_Entry<bool> entry)
|
||||||
{
|
{
|
||||||
|
|
|
@ -6,41 +6,38 @@ namespace NAK.Melons.DesktopVRIK;
|
||||||
public class DesktopVRIKMod : MelonMod
|
public class DesktopVRIKMod : MelonMod
|
||||||
{
|
{
|
||||||
internal static MelonLogger.Instance Logger;
|
internal static MelonLogger.Instance Logger;
|
||||||
internal const string SettingsCategory = "DesktopVRIK";
|
public const string SettingsCategory = "DesktopVRIK";
|
||||||
internal static MelonPreferences_Category m_categoryDesktopVRIK;
|
public static readonly MelonPreferences_Category CategoryDesktopVRIK = MelonPreferences.CreateCategory(SettingsCategory);
|
||||||
internal static MelonPreferences_Entry<bool>
|
|
||||||
m_entryEnabled,
|
public static readonly MelonPreferences_Entry<bool> EntryEnabled =
|
||||||
m_entryEnforceViewPosition,
|
CategoryDesktopVRIK.CreateEntry("Enabled", true, description: "Toggle DesktopVRIK entirely. Requires avatar reload.");
|
||||||
m_entryResetIKOnLand,
|
|
||||||
m_entryPlantFeet,
|
public static readonly MelonPreferences_Entry<bool> EntryPlantFeet =
|
||||||
m_entryUseVRIKToes,
|
CategoryDesktopVRIK.CreateEntry("Enforce Plant Feet", true, description: "Forces VRIK Plant Feet enabled to prevent hovering when stopping movement.");
|
||||||
m_entryFindUnmappedToes,
|
|
||||||
m_entryExperimentalKneeBend;
|
public static readonly MelonPreferences_Entry<bool> EntryUseVRIKToes =
|
||||||
internal static MelonPreferences_Entry<float>
|
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.");
|
||||||
m_entryBodyLeanWeight,
|
|
||||||
m_entryBodyHeadingLimit,
|
public static readonly MelonPreferences_Entry<bool> EntryFindUnmappedToes =
|
||||||
m_entryPelvisHeadingWeight,
|
CategoryDesktopVRIK.CreateEntry("Find Unmapped Toes", false, description: "Determines if DesktopVRIK should look for unmapped toe bones if the humanoid rig does not have any.");
|
||||||
m_entryChestHeadingWeight;
|
|
||||||
|
public static readonly MelonPreferences_Entry<float> 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.");
|
||||||
|
|
||||||
|
public static readonly MelonPreferences_Entry<float> 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.");
|
||||||
|
|
||||||
|
public static readonly MelonPreferences_Entry<float> 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.");
|
||||||
|
|
||||||
|
public static readonly MelonPreferences_Entry<float> 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.");
|
||||||
|
|
||||||
public override void OnInitializeMelon()
|
public override void OnInitializeMelon()
|
||||||
{
|
{
|
||||||
Logger = LoggerInstance;
|
Logger = LoggerInstance;
|
||||||
m_categoryDesktopVRIK = MelonPreferences.CreateCategory(SettingsCategory);
|
|
||||||
m_entryEnabled = m_categoryDesktopVRIK.CreateEntry<bool>("Enabled", true, description: "Toggle DesktopVRIK entirely. Requires avatar reload.");
|
|
||||||
//m_entryEnforceViewPosition = m_categoryDesktopVRIK.CreateEntry<bool>("Enforce View Position", false, description: "Corrects view position to use VRIK offsets.");
|
|
||||||
m_entryPlantFeet = m_categoryDesktopVRIK.CreateEntry<bool>("Enforce Plant Feet", true, description: "Forces VRIK Plant Feet enabled. This prevents the little hover when you stop moving.");
|
|
||||||
m_entryUseVRIKToes = m_categoryDesktopVRIK.CreateEntry<bool>("Use VRIK Toes", false, description: "Should VRIK use your humanoid toes for IK solving? This can cause your feet to idle behind you.");
|
|
||||||
m_entryFindUnmappedToes = m_categoryDesktopVRIK.CreateEntry<bool>("Find Unmapped Toes", false, description: "Should DesktopVRIK look for unmapped toe bones if humanoid rig does not have any?");
|
|
||||||
m_entryExperimentalKneeBend = m_categoryDesktopVRIK.CreateEntry<bool>("Experimental Knee Bend", true, description: "Experimental method to calculate knee bend normal. This may break avatars.");
|
|
||||||
|
|
||||||
m_entryBodyLeanWeight = m_categoryDesktopVRIK.CreateEntry<float>("Body Lean Weight", 0.5f, description: "Emulates old VRChat-like body leaning when looking up/down. Set to 0 to disable.");
|
CategoryDesktopVRIK.Entries.ForEach(e => e.OnEntryValueChangedUntyped.Subscribe(OnUpdateSettings));
|
||||||
m_entryBodyHeadingLimit = m_categoryDesktopVRIK.CreateEntry<float>("Body Heading Limit", 20f, description: "Emulates VRChat-like body and head offset when rotating left/right. Set to 0 to disable.");
|
|
||||||
m_entryPelvisHeadingWeight = m_categoryDesktopVRIK.CreateEntry<float>("Pelvis Heading Weight", 0.25f, description: "How much the pelvis will face the heading limit. Set to 0 to align with head.");
|
|
||||||
m_entryChestHeadingWeight = m_categoryDesktopVRIK.CreateEntry<float>("Chest Heading Weight", 0.75f, description: "How much the chest will face the heading limit. Set to 0 to align with head.");
|
|
||||||
|
|
||||||
foreach (var setting in m_categoryDesktopVRIK.Entries)
|
|
||||||
{
|
|
||||||
setting.OnEntryValueChangedUntyped.Subscribe(OnUpdateSettings);
|
|
||||||
}
|
|
||||||
|
|
||||||
ApplyPatches(typeof(HarmonyPatches.PlayerSetupPatches));
|
ApplyPatches(typeof(HarmonyPatches.PlayerSetupPatches));
|
||||||
ApplyPatches(typeof(HarmonyPatches.IKSystemPatches));
|
ApplyPatches(typeof(HarmonyPatches.IKSystemPatches));
|
||||||
|
@ -52,18 +49,17 @@ public class DesktopVRIKMod : MelonMod
|
||||||
{
|
{
|
||||||
if (!DesktopVRIK.Instance) return;
|
if (!DesktopVRIK.Instance) return;
|
||||||
// DesktopVRIK Settings
|
// DesktopVRIK Settings
|
||||||
DesktopVRIK.Instance.Setting_Enabled = m_entryEnabled.Value;
|
DesktopVRIK.Instance.Setting_Enabled = EntryEnabled.Value;
|
||||||
DesktopVRIK.Instance.Setting_PlantFeet = m_entryPlantFeet.Value;
|
DesktopVRIK.Instance.Setting_PlantFeet = EntryPlantFeet.Value;
|
||||||
|
|
||||||
DesktopVRIK.Instance.Setting_BodyLeanWeight = Mathf.Clamp01(m_entryBodyLeanWeight.Value);
|
DesktopVRIK.Instance.Setting_BodyLeanWeight = Mathf.Clamp01(EntryBodyLeanWeight.Value);
|
||||||
DesktopVRIK.Instance.Setting_BodyHeadingLimit = Mathf.Clamp(m_entryBodyHeadingLimit.Value, 0f, 90f);
|
DesktopVRIK.Instance.Setting_BodyHeadingLimit = Mathf.Clamp(EntryBodyHeadingLimit.Value, 0f, 90f);
|
||||||
DesktopVRIK.Instance.Setting_PelvisHeadingWeight = (1f - Mathf.Clamp01(m_entryPelvisHeadingWeight.Value));
|
DesktopVRIK.Instance.Setting_PelvisHeadingWeight = (1f - Mathf.Clamp01(EntryPelvisHeadingWeight.Value));
|
||||||
DesktopVRIK.Instance.Setting_ChestHeadingWeight = (1f - Mathf.Clamp01(m_entryChestHeadingWeight.Value));
|
DesktopVRIK.Instance.Setting_ChestHeadingWeight = (1f - Mathf.Clamp01(EntryChestHeadingWeight.Value));
|
||||||
|
|
||||||
// Calibration Settings
|
// Calibration Settings
|
||||||
DesktopVRIK.Instance.Calibrator.Setting_UseVRIKToes = m_entryUseVRIKToes.Value;
|
DesktopVRIK.Instance.Calibrator.Setting_UseVRIKToes = EntryUseVRIKToes.Value;
|
||||||
DesktopVRIK.Instance.Calibrator.Setting_FindUnmappedToes = m_entryFindUnmappedToes.Value;
|
DesktopVRIK.Instance.Calibrator.Setting_FindUnmappedToes = EntryFindUnmappedToes.Value;
|
||||||
DesktopVRIK.Instance.Calibrator.Setting_ExperimentalKneeBend = m_entryExperimentalKneeBend.Value;
|
|
||||||
}
|
}
|
||||||
private void OnUpdateSettings(object arg1, object arg2) => UpdateAllSettings();
|
private void OnUpdateSettings(object arg1, object arg2) => UpdateAllSettings();
|
||||||
|
|
||||||
|
@ -72,7 +68,7 @@ public class DesktopVRIKMod : MelonMod
|
||||||
//BTKUILib Misc Tab
|
//BTKUILib Misc Tab
|
||||||
if (MelonMod.RegisteredMelons.Any(it => it.Info.Name == "BTKUILib"))
|
if (MelonMod.RegisteredMelons.Any(it => it.Info.Name == "BTKUILib"))
|
||||||
{
|
{
|
||||||
MelonLogger.Msg("Initializing BTKUILib support.");
|
Logger.Msg("Initializing BTKUILib support.");
|
||||||
BTKUIAddon.Init();
|
BTKUIAddon.Init();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,6 @@ using System.Reflection;
|
||||||
namespace NAK.Melons.DesktopVRIK.Properties;
|
namespace NAK.Melons.DesktopVRIK.Properties;
|
||||||
internal static class AssemblyInfoParams
|
internal static class AssemblyInfoParams
|
||||||
{
|
{
|
||||||
public const string Version = "4.0.0";
|
public const string Version = "4.0.1";
|
||||||
public const string Author = "NotAKidoS";
|
public const string Author = "NotAKidoS";
|
||||||
}
|
}
|
183
DesktopVRIK/VRIKUtils.cs
Normal file
183
DesktopVRIK/VRIKUtils.cs
Normal file
|
@ -0,0 +1,183 @@
|
||||||
|
using HarmonyLib;
|
||||||
|
using RootMotion.FinalIK;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace NAK.Melons.DesktopVRIK;
|
||||||
|
|
||||||
|
public static class VRIKUtils
|
||||||
|
{
|
||||||
|
public static void ConfigureVRIKReferences(VRIK vrik, bool useVRIKToes, bool findUnmappedToes, out bool foundUnmappedToes)
|
||||||
|
{
|
||||||
|
foundUnmappedToes = false;
|
||||||
|
|
||||||
|
//might not work over netik
|
||||||
|
FixChestAndSpineReferences(vrik);
|
||||||
|
|
||||||
|
if (!useVRIKToes)
|
||||||
|
{
|
||||||
|
vrik.references.leftToes = null;
|
||||||
|
vrik.references.rightToes = null;
|
||||||
|
}
|
||||||
|
else if (findUnmappedToes)
|
||||||
|
{
|
||||||
|
//doesnt work with netik, but its toes...
|
||||||
|
FindAndSetUnmappedToes(vrik, out foundUnmappedToes);
|
||||||
|
}
|
||||||
|
|
||||||
|
//bullshit fix to not cause death
|
||||||
|
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 FindAndSetUnmappedToes(VRIK vrik, out bool foundUnmappedToes)
|
||||||
|
{
|
||||||
|
foundUnmappedToes = false;
|
||||||
|
|
||||||
|
Transform leftToes = vrik.references.leftToes;
|
||||||
|
Transform rightToes = vrik.references.rightToes;
|
||||||
|
|
||||||
|
if (leftToes == null && rightToes == null)
|
||||||
|
{
|
||||||
|
leftToes = FindUnmappedToe(vrik.references.leftFoot);
|
||||||
|
rightToes = FindUnmappedToe(vrik.references.rightFoot);
|
||||||
|
|
||||||
|
if (leftToes != null && rightToes != null)
|
||||||
|
{
|
||||||
|
vrik.references.leftToes = leftToes;
|
||||||
|
vrik.references.rightToes = rightToes;
|
||||||
|
foundUnmappedToes = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Transform FindUnmappedToe(Transform foot)
|
||||||
|
{
|
||||||
|
foreach (Transform bone in foot)
|
||||||
|
{
|
||||||
|
if (bone.name.ToLowerInvariant().Contains("toe") ||
|
||||||
|
bone.name.ToLowerInvariant().EndsWith("_end"))
|
||||||
|
{
|
||||||
|
return bone;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void FixFingerBonesError(VRIK vrik)
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
// 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)
|
||||||
|
};
|
||||||
|
leftKneeNormal = 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)
|
||||||
|
};
|
||||||
|
rightKneeNormal = Quaternion.Inverse(vrik.references.root.rotation) * GetNormalFromArray(rightVectors);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ApplyKneeBendNormals(VRIK vrik, Vector3 leftKneeNormal, Vector3 rightKneeNormal)
|
||||||
|
{
|
||||||
|
// 0 uses bendNormalRelToPelvis, 1 is bendNormalRelToTarget
|
||||||
|
// modifying pelvis normal weight is better math
|
||||||
|
vrik.solver.leftLeg.bendToTargetWeight = 0f;
|
||||||
|
vrik.solver.rightLeg.bendToTargetWeight = 0f;
|
||||||
|
|
||||||
|
var leftLeg_bendNormalRelToPelvisTraverse = Traverse.Create(vrik.solver.leftLeg).Field("bendNormalRelToPelvis");
|
||||||
|
var rightLeg_bendNormalRelToPelvisTraverse = Traverse.Create(vrik.solver.rightLeg).Field("bendNormalRelToPelvis");
|
||||||
|
|
||||||
|
var pelvis_localRotationInverse = Quaternion.Inverse(vrik.references.pelvis.localRotation);
|
||||||
|
var leftLeg_bendNormalRelToPelvis = pelvis_localRotationInverse * leftKneeNormal;
|
||||||
|
var rightLeg_bendNormalRelToPelvis = pelvis_localRotationInverse * rightKneeNormal;
|
||||||
|
|
||||||
|
leftLeg_bendNormalRelToPelvisTraverse.SetValue(leftLeg_bendNormalRelToPelvis);
|
||||||
|
rightLeg_bendNormalRelToPelvisTraverse.SetValue(rightLeg_bendNormalRelToPelvis);
|
||||||
|
}
|
||||||
|
|
||||||
|
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, out float initialFootDistance, out float initialStepThreshold, out float initialStepHeight)
|
||||||
|
{
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ApplyScaleToVRIK(VRIK vrik, float footDistance, float stepThreshold, float stepHeight, float modifier)
|
||||||
|
{
|
||||||
|
vrik.solver.locomotion.footDistance = footDistance * modifier;
|
||||||
|
vrik.solver.locomotion.stepThreshold = stepThreshold * modifier;
|
||||||
|
ScaleStepHeight(vrik.solver.locomotion.stepHeight, stepHeight * modifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ScaleStepHeight(AnimationCurve stepHeightCurve, float mag)
|
||||||
|
{
|
||||||
|
Keyframe[] 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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,23 +1,25 @@
|
||||||
{
|
{
|
||||||
"_id": 117,
|
"_id": 117,
|
||||||
"name": "DesktopVRIK",
|
"name": "DesktopVRIK",
|
||||||
"modversion": "2.0.5",
|
"modversion": "4.0.1",
|
||||||
"gameversion": "2022r170",
|
"gameversion": "2022r170",
|
||||||
"loaderversion": "0.5.7",
|
"loaderversion": "0.5.7",
|
||||||
"modtype": "Mod",
|
"modtype": "Mod",
|
||||||
"author": "NotAKidoS",
|
"author": "NotAKidoS",
|
||||||
"description": "Adds VRIK to Desktop avatars. No longer will you be a liveless sliding statue~!\nAdds the small feet stepping when looking around on Desktop.\n\nAdditional option to emulate VRChat-like hip movement if you like being fish.",
|
"description": "Adds VRIK to Desktop avatars. No longer will you be a liveless sliding statue~!\nAdds the small feet stepping when looking around on Desktop.\n\nIt is highly recommended to use AvatarMotionTweaker alongside this mod.",
|
||||||
"searchtags": [
|
"searchtags": [
|
||||||
"desktop",
|
"desktop",
|
||||||
"vrik",
|
"vrik",
|
||||||
"ik",
|
"ik",
|
||||||
"feet"
|
"feet",
|
||||||
|
"fish"
|
||||||
],
|
],
|
||||||
"requirements": [
|
"requirements": [
|
||||||
"BTKUILib"
|
"BTKUILib",
|
||||||
|
"AvatarMotionTweaker"
|
||||||
],
|
],
|
||||||
"downloadlink": "https://github.com/NotAKidOnSteam/DesktopVRIK/releases/download/v2.0.5/DesktopVRIK.dll",
|
"downloadlink": "https://github.com/NotAKidOnSteam/DesktopVRIK/releases/download/v4.0.1/DesktopVRIK.dll",
|
||||||
"sourcelink": "https://github.com/NotAKidOnSteam/DesktopVRIK/",
|
"sourcelink": "https://github.com/NotAKidOnSteam/DesktopVRIK/",
|
||||||
"changelog": "- Tweaks to VRIK settings so BetterInteractDesktop & PickupArmMovement better behave alongside VRIK.\n- Tweaks to BTKUI integration.",
|
"changelog": "- Implemented fixes for terrible armatures that had broken knee bending, initial hip rotation, and initial step calculations.\n- Added pelvis & chest weight sliders for heading angle limit option.\n- Implemented bandaid fixes for disabled animator and avatars without fingers.\n- Added options to use or remove toes from VRIK and find unmapped non-humanoid toe bones.\n- Contact me if you find an avatar that is still broken.",
|
||||||
"embedcolor": "9b59b6"
|
"embedcolor": "9b59b6"
|
||||||
}
|
}
|
Loading…
Add table
Add a link
Reference in a new issue