This commit is contained in:
NotAKidoS 2023-01-16 01:57:30 -06:00
parent 41068ce3fb
commit b4fb9e62e6
5 changed files with 220 additions and 76 deletions

View file

@ -9,22 +9,38 @@ public static class BTKUI_Integration
[MethodImpl(MethodImplOptions.NoInlining)]
public static void Init()
{
Page miscPage = QuickMenuAPI.MiscTabPage;
Category CategoryUI = miscPage.AddCategory("DesktopVRIK");
//Add myself to the Misc Menu
var setting_Enabled = CategoryUI.AddToggle(DesktopVRIKMod.m_entryEnabled.DisplayName, DesktopVRIKMod.m_entryEnabled.Description, DesktopVRIKMod.m_entryEnabled.Value);
Page miscPage = QuickMenuAPI.MiscTabPage;
Category miscCategory = miscPage.AddCategory("DesktopVRIK");
var setting_Enabled = miscCategory.AddToggle(DesktopVRIKMod.m_entryEnabled.DisplayName, DesktopVRIKMod.m_entryEnabled.Description, DesktopVRIKMod.m_entryEnabled.Value);
setting_Enabled.OnValueUpdated += b => DesktopVRIKMod.m_entryEnabled.Value = b;
var setting_EnforceViewPosition = CategoryUI.AddToggle(DesktopVRIKMod.m_entryEnforceViewPosition.DisplayName, DesktopVRIKMod.m_entryEnforceViewPosition.Description, DesktopVRIKMod.m_entryEnforceViewPosition.Value);
//Add my own page to not clog up Misc Menu
Page desktopVRIKPage = miscCategory.AddPage("DesktopVRIK Settings", "", "Configure the settings for DesktopVRIK.", "DesktopVRIK");
desktopVRIKPage.MenuTitle = "DesktopVRIK Settings";
desktopVRIKPage.MenuSubtitle = "Simplified settings for VRIK on Desktop.";
Category desktopVRIKCategory = desktopVRIKPage.AddCategory("DesktopVRIK");
var setting_Enabled_2 = desktopVRIKCategory.AddToggle(DesktopVRIKMod.m_entryEnabled.DisplayName, DesktopVRIKMod.m_entryEnabled.Description, DesktopVRIKMod.m_entryEnabled.Value);
setting_Enabled_2.OnValueUpdated += b => DesktopVRIKMod.m_entryEnabled.Value = b;
var setting_EnforceViewPosition = desktopVRIKCategory.AddToggle(DesktopVRIKMod.m_entryEnforceViewPosition.DisplayName, DesktopVRIKMod.m_entryEnforceViewPosition.Description, DesktopVRIKMod.m_entryEnforceViewPosition.Value);
setting_EnforceViewPosition.OnValueUpdated += b => DesktopVRIKMod.m_entryEnforceViewPosition.Value = b;
var setting_DisableEmoteVRIK = CategoryUI.AddToggle(DesktopVRIKMod.m_entryEmoteVRIK.DisplayName, DesktopVRIKMod.m_entryEmoteVRIK.Description, DesktopVRIKMod.m_entryEmoteVRIK.Value);
var setting_DisableEmoteVRIK = desktopVRIKCategory.AddToggle(DesktopVRIKMod.m_entryEmoteVRIK.DisplayName, DesktopVRIKMod.m_entryEmoteVRIK.Description, DesktopVRIKMod.m_entryEmoteVRIK.Value);
setting_DisableEmoteVRIK.OnValueUpdated += b => DesktopVRIKMod.m_entryEmoteVRIK.Value = b;
var setting_DisableEmoteLookAtIK = CategoryUI.AddToggle(DesktopVRIKMod.m_entryEmoteLookAtIK.DisplayName, DesktopVRIKMod.m_entryEmoteLookAtIK.Description, DesktopVRIKMod.m_entryEmoteLookAtIK.Value);
var setting_DisableEmoteLookAtIK = desktopVRIKCategory.AddToggle(DesktopVRIKMod.m_entryEmoteLookAtIK.DisplayName, DesktopVRIKMod.m_entryEmoteLookAtIK.Description, DesktopVRIKMod.m_entryEmoteLookAtIK.Value);
setting_DisableEmoteLookAtIK.OnValueUpdated += b => DesktopVRIKMod.m_entryEmoteLookAtIK.Value = b;
var setting_EmulateHipMovementWeight = miscPage.AddSlider(DesktopVRIKMod.m_entryEmulateVRChatHipMovementWeight.DisplayName, DesktopVRIKMod.m_entryEmulateVRChatHipMovementWeight.Description, DesktopVRIKMod.m_entryEmulateVRChatHipMovementWeight.Value, 0f, 1f);
setting_EmulateHipMovementWeight.OnValueUpdated += f => DesktopVRIKMod.m_entryEmulateVRChatHipMovementWeight.Value = f;
var setting_BodyLeanWeight = desktopVRIKPage.AddSlider(DesktopVRIKMod.m_entryBodyLeanWeight.DisplayName, DesktopVRIKMod.m_entryBodyLeanWeight.Description, DesktopVRIKMod.m_entryBodyLeanWeight.Value, 0f, 1f, 1);
setting_BodyLeanWeight.OnValueUpdated += f => DesktopVRIKMod.m_entryBodyLeanWeight.Value = f;
var setting_BodyAngleLimit = desktopVRIKPage.AddSlider(DesktopVRIKMod.m_entryBodyAngleLimit.DisplayName, DesktopVRIKMod.m_entryBodyAngleLimit.Description, DesktopVRIKMod.m_entryBodyAngleLimit.Value, 0f, 90f, 0);
setting_BodyAngleLimit.OnValueUpdated += f => DesktopVRIKMod.m_entryBodyAngleLimit.Value = f;
}
}

View file

@ -13,27 +13,23 @@ public class DesktopVRIK : MonoBehaviour
{
public static DesktopVRIK Instance;
public static bool Setting_Enabled,
public static bool
Setting_Enabled,
Setting_EnforceViewPosition,
Setting_EmoteVRIK,
Setting_EmoteLookAtIK;
public static float Setting_EmulateVRChatHipMovementWeight;
public static float Setting_HipThrustMultiplier = 0.1f;
Setting_EmoteLookAtIK,
Setting_Internal_PlantFeet = true;
public static float
Setting_BodyLeanWeight = 0.3f,
Setting_BodyAngleLimit = 45f;
public Transform viewpoint;
public Vector3 initialCamPos;
Transform headIKTarget;
Transform avatarHeadBone;
public Vector3 eyeOffset;
void Start()
{
Instance = this;
// create the shared Head IK Target
headIKTarget = new GameObject("[DesktopVRIK] Head IK Target").transform;
headIKTarget.parent = PlayerSetup.Instance.transform;
headIKTarget.localPosition = new Vector3(0f, 1.8f, 0f);
headIKTarget.localRotation = Quaternion.identity;
}
public void ChangeViewpointHandling(bool enabled)
@ -45,51 +41,30 @@ public class DesktopVRIK : MonoBehaviour
PlayerSetup.Instance.desktopCamera.transform.localPosition = Vector3.zero;
return;
}
PlayerSetup.Instance.desktopCamera.transform.localPosition = initialCamPos;
PlayerSetup.Instance.desktopCamera.transform.localPosition = eyeOffset;
}
public void AlternativeOnPreSolverUpdate()
{
//this order matters, rotation offset will be choppy if avatar is not cenetered first
if (headIKTarget != null && avatarHeadBone != null)
{
headIKTarget.position = new Vector3(headIKTarget.position.x, avatarHeadBone.position.y, headIKTarget.position.z);
}
//set IK offsets (this is a really fucking weird way to do this)
DesktopVRIK_Helper.Instance?.OnUpdateVRIK();
//Reset avatar offset (VRIK will literally make you walk away from root otherwise)
IKSystem.vrik.transform.localPosition = Vector3.zero;
IKSystem.vrik.transform.localRotation = Quaternion.identity;
float movementVector = (1 - MovementSystem.Instance.movementVector.magnitude);
IKSystem.vrik.solver.spine.positionWeight = Setting_HipThrustMultiplier * movementVector;
//VRChat hip movement emulation
if (Setting_EmulateVRChatHipMovementWeight != 0)
{
float angle = PlayerSetup.Instance.desktopCamera.transform.localEulerAngles.x;
if (angle > 180) angle -= 360;
float leanAmount = angle * movementVector * Setting_EmulateVRChatHipMovementWeight;
Quaternion rotation = Quaternion.AngleAxis(leanAmount, IKSystem.Instance.avatar.transform.right);
IKSystem.vrik.solver.AddRotationOffset(IKSolverVR.RotationOffset.Head, rotation);
}
IKSystem.vrik.solver.plantFeet = true;
IKSystem.vrik.solver.plantFeet = Setting_Internal_PlantFeet;
}
public Animator animator;
//public Quaternion originalRotation;
public RuntimeAnimatorController runtimeAnimatorController;
public VRIK AlternativeCalibration(CVRAvatar avatar)
{
animator = avatar.GetComponent<Animator>();
avatarHeadBone = animator.GetBoneTransform(HumanBodyBones.Head);
Transform avatarHeadBone = animator.GetBoneTransform(HumanBodyBones.Head);
//Stuff to make bad armatures work (Fuck you Default Robot Kyle)
avatar.transform.localPosition = Vector3.zero;
//originalRotation = avatar.transform.rotation;
//avatar.transform.rotation = Quaternion.identity;
//ikpose layer (specified by avatar author)
int ikposeLayerIndex = animator.GetLayerIndex("IKPose");
@ -114,12 +89,7 @@ public class DesktopVRIK : MonoBehaviour
vrik.solver.locomotion.angleThreshold = 30f;
vrik.solver.locomotion.maxLegStretch = 0.75f;
//nuke weights
vrik.solver.spine.headClampWeight = 1f;
vrik.solver.spine.minHeadHeight = 0f;
//calm ur ass
vrik.solver.spine.positionWeight = 0.1f;
vrik.solver.spine.pelvisPositionWeight = 0f;
vrik.solver.leftArm.positionWeight = 0f;
vrik.solver.leftArm.rotationWeight = 0f;
@ -131,6 +101,19 @@ public class DesktopVRIK : MonoBehaviour
vrik.solver.rightLeg.rotationWeight = 0f;
vrik.solver.IKPositionWeight = 0f;
//calm ur ass
vrik.solver.spine.positionWeight = 0f;
//related to body & head rotation offset/limit
vrik.solver.spine.headClampWeight = 1f;
vrik.solver.spine.bodyRotStiffness = 0.8f;
//makes chest between feet and head direction
vrik.solver.spine.chestClampWeight = 0.5f;
//needed to make head 1:1 with camera still
vrik.solver.spine.neckStiffness = 1f;
//ChilloutVR specific
BodySystem.TrackingLeftArmEnabled = false;
BodySystem.TrackingRightArmEnabled = false;
@ -141,17 +124,19 @@ public class DesktopVRIK : MonoBehaviour
IKSystem.Instance.headAnchorPositionOffset = Vector3.zero;
//Custom funky AF head ik shit
foreach (Transform transform in headIKTarget)
foreach (Transform transform in DesktopVRIK_Helper.Instance.ik_HeadFollower)
{
if (transform.name == "Head IK Target")
{
Destroy(transform.gameObject);
}
}
headIKTarget.position = avatarHeadBone.position;
headIKTarget.rotation = Quaternion.identity;
VRIKCalibrator.CalibrateHead(vrik, headIKTarget.transform, IKSystem.Instance.headAnchorPositionOffset, IKSystem.Instance.headAnchorRotationOffset);
headIKTarget.localRotation = Quaternion.identity;
DesktopVRIK_Helper.Instance.avatar_HeadBone = avatarHeadBone;
DesktopVRIK_Helper.Instance.ik_HeadFollower.position = avatarHeadBone.position;
DesktopVRIK_Helper.Instance.ik_HeadFollower.rotation = Quaternion.identity;
VRIKCalibrator.CalibrateHead(vrik, DesktopVRIK_Helper.Instance.ik_HeadFollower.transform, IKSystem.Instance.headAnchorPositionOffset, IKSystem.Instance.headAnchorRotationOffset);
DesktopVRIK_Helper.Instance.ik_HeadFollower.localRotation = Quaternion.identity;
//force immediate calibration before animator decides to fuck us
vrik.solver.SetToReferences(vrik.references);
@ -167,17 +152,23 @@ public class DesktopVRIK : MonoBehaviour
}
//Find eyeoffset
initialCamPos = PlayerSetup.Instance.desktopCamera.transform.localPosition;
eyeOffset = PlayerSetup.Instance.desktopCamera.transform.localPosition;
viewpoint = avatarHeadBone.Find("LocalHeadPoint");
ChangeViewpointHandling(Setting_EnforceViewPosition);
if (vrik != null)
//reset ikpose layer
if (ikposeLayerIndex != -1)
{
vrik.onPreSolverUpdate.AddListener(new UnityAction(this.AlternativeOnPreSolverUpdate));
animator.SetLayerWeight(ikposeLayerIndex, 0f);
if (locoLayerIndex != -1)
{
animator.SetLayerWeight(locoLayerIndex, 1f);
}
}
//avatar.transform.rotation = originalRotation;
IKSystem.Instance.ResetIK();
vrik?.onPreSolverUpdate.AddListener(new UnityAction(this.AlternativeOnPreSolverUpdate));
DesktopVRIK_Helper.Instance?.OnResetIK();
return vrik;
}

View file

@ -0,0 +1,108 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using ABI_RC.Core.Player;
using ABI_RC.Systems.IK;
using ABI_RC.Systems.IK.SubSystems;
using ABI_RC.Systems.MovementSystem;
using RootMotion.FinalIK;
namespace NAK.Melons.DesktopVRIK;
internal class DesktopVRIK_Helper : MonoBehaviour
{
public static DesktopVRIK_Helper Instance;
//Avatar
public Transform avatar_HeadBone;
//DesktopVRIK
public Transform ik_HeadFollower;
public Quaternion ik_HeadRotation;
public static void CreateInstance()
{
Transform helper = new GameObject("[DesktopVRIK] Virtual Rig").transform;
helper.parent = PlayerSetup.Instance.transform;
helper.localPosition = Vector3.zero;
helper.localRotation = Quaternion.identity;
helper.gameObject.AddComponent<DesktopVRIK_Helper>();
}
void Start()
{
Instance = this;
Transform headFollower = new GameObject("HeadBone_Follower").transform;
headFollower.parent = transform;
headFollower.localPosition = new Vector3(0f, 1.8f, 0f);
headFollower.localRotation = Quaternion.identity;
ik_HeadFollower = headFollower;
}
public void OnUpdateVRIK()
{
if (avatar_HeadBone != null)
{
float globalWeight = (1 - MovementSystem.Instance.movementVector.magnitude);
globalWeight *= IKSystem.vrik.solver.locomotion.weight;
//the most important thing ever
IKSystem.vrik.solver.spine.rotationWeight = globalWeight;
HeadIK_FollowPosition();
HeadIK_RotateWithWeight(globalWeight);
HeadIK_FollowWithinAngle(globalWeight);
ik_HeadFollower.rotation = ik_HeadRotation;
}
}
public void OnResetIK()
{
ik_HeadRotation = Quaternion.Euler(transform.eulerAngles.x, transform.eulerAngles.y, transform.eulerAngles.z);
}
public void HeadIK_FollowPosition()
{
ik_HeadFollower.position = new Vector3(transform.position.x, avatar_HeadBone.position.y, transform.position.z);
}
public void HeadIK_FollowWithinAngle(float weight)
{
if (DesktopVRIK.Setting_BodyAngleLimit != 0)
{
float weightedAngle = DesktopVRIK.Setting_BodyAngleLimit * weight;
float currentAngle = Mathf.DeltaAngle(transform.eulerAngles.y, ik_HeadRotation.eulerAngles.y);
if (Mathf.Abs(currentAngle) > weightedAngle)
{
float fixedCurrentAngle = currentAngle > 0 ? currentAngle : -currentAngle;
float clampedAngle = Mathf.MoveTowardsAngle(ik_HeadRotation.eulerAngles.y, transform.eulerAngles.y, fixedCurrentAngle - weightedAngle);
ik_HeadRotation = Quaternion.Euler(ik_HeadRotation.eulerAngles.x, clampedAngle, 0);
}
}
else
{
ik_HeadRotation = Quaternion.Euler(ik_HeadRotation.eulerAngles.x, transform.eulerAngles.y, transform.eulerAngles.z);
}
}
public void HeadIK_RotateWithWeight(float weight)
{
//VRChat hip movement emulation
if (DesktopVRIK.Setting_BodyLeanWeight != 0)
{
float angle = PlayerSetup.Instance.desktopCamera.transform.localEulerAngles.x;
if (angle > 180) angle -= 360;
float leanAmount = angle * weight * DesktopVRIK.Setting_BodyLeanWeight;
ik_HeadRotation = Quaternion.Euler(leanAmount * 0.33f, ik_HeadRotation.eulerAngles.y, 0);
}
else
{
ik_HeadRotation = Quaternion.Euler(transform.eulerAngles.x, ik_HeadRotation.eulerAngles.y, transform.eulerAngles.z);
}
}
}

View file

@ -72,26 +72,47 @@ class PlayerSetupPatches
{
BodySystem.TrackingEnabled = !____emotePlaying;
IKSystem.vrik.solver?.Reset();
DesktopVRIK_Helper.Instance?.OnResetIK();
}
}
}
}
[HarmonyPostfix]
//should probably patch movement system instead
[HarmonyPrefix]
[HarmonyPatch(typeof(PlayerSetup), "HandleDesktopCameraPosition")]
private static void Postfix_PlayerSetup_HandleDesktopCameraPosition(bool ignore, ref PlayerSetup __instance, ref MovementSystem ____movementSystem, ref int ___headBobbingLevel)
private static void Prefix_PlayerSetup_HandleDesktopCameraPosition
(
bool ignore,
ref PlayerSetup __instance,
ref MovementSystem
____movementSystem,
ref int ___headBobbingLevel
)
{
if (DesktopVRIK.Setting_Enabled && DesktopVRIK.Setting_EnforceViewPosition)
if (___headBobbingLevel != 2)
{
if (!____movementSystem.disableCameraControl || ignore)
{
if (___headBobbingLevel == 2 && DesktopVRIK.Instance.viewpoint != null)
{
__instance.desktopCamera.transform.localPosition = Vector3.zero;
__instance.desktopCameraRig.transform.position = DesktopVRIK.Instance.viewpoint.position;
}
}
return;
}
if (!DesktopVRIK.Setting_Enabled || !DesktopVRIK.Setting_EnforceViewPosition)
{
return;
}
if (____movementSystem.disableCameraControl && !ignore)
{
return;
}
if (DesktopVRIK.Instance.viewpoint == null)
{
return;
}
__instance.desktopCamera.transform.position = DesktopVRIK.Instance.viewpoint.position;
return;
}
}

View file

@ -13,16 +13,20 @@ public class DesktopVRIKMod : MelonMod
m_entryEmoteVRIK,
m_entryEmoteLookAtIK,
m_entryAllowRootSlipping;
internal static MelonPreferences_Entry<float> m_entryEmulateVRChatHipMovementWeight;
internal static MelonPreferences_Entry<float>
m_entryBodyLeanWeight,
m_entryBodyAngleLimit;
public override void OnInitializeMelon()
{
m_categoryDesktopVRIK = MelonPreferences.CreateCategory(SettingsCategory);
m_entryEnabled = m_categoryDesktopVRIK.CreateEntry<bool>("Enabled", true, description: "Toggle DesktopVRIK entirely. Requires avatar reload.");
m_entryEmulateVRChatHipMovementWeight = m_categoryDesktopVRIK.CreateEntry<float>("Body Movement Weight", 0.5f, description: "Emulates VRChat-like body movement when looking up/down. Set to 0 to disable.");
m_entryEnforceViewPosition = m_categoryDesktopVRIK.CreateEntry<bool>("Enforce View Position", false, description: "Corrects view position to use VRIK offsets.");
m_entryEmoteVRIK = m_categoryDesktopVRIK.CreateEntry<bool>("Disable Emote VRIK", true, description: "Disable VRIK while emoting. Only disable if you are ok with looking dumb.");
m_entryEmoteLookAtIK = m_categoryDesktopVRIK.CreateEntry<bool>("Disable Emote LookAtIK", true, description: "Disable LookAtIK while emoting. This setting doesn't really matter, as LookAtIK isn't networked while doing an emote.");
m_entryBodyLeanWeight = m_categoryDesktopVRIK.CreateEntry<float>("Body Lean Weight", 0.3f, description: "Emulates old VRChat-like body leaning when looking up/down. Set to 0 to disable.");
m_entryBodyAngleLimit = m_categoryDesktopVRIK.CreateEntry<float>("Body Angle Limit", 45f, description: "Emulates VRChat-like body and head offset when rotating left/right. Set to 0 to disable.");
foreach (var setting in m_categoryDesktopVRIK.Entries)
{
setting.OnEntryValueChangedUntyped.Subscribe(OnUpdateSettings);
@ -46,7 +50,10 @@ public class DesktopVRIKMod : MelonMod
{
while (PlayerSetup.Instance == null)
yield return null;
DesktopVRIK_Helper.CreateInstance();
PlayerSetup.Instance.gameObject.AddComponent<DesktopVRIK>();
while (DesktopVRIK.Instance == null)
yield return null;
UpdateAllSettings();
@ -56,7 +63,8 @@ public class DesktopVRIKMod : MelonMod
{
if (!DesktopVRIK.Instance) return;
DesktopVRIK.Setting_Enabled = m_entryEnabled.Value;
DesktopVRIK.Setting_EmulateVRChatHipMovementWeight = Mathf.Clamp01(m_entryEmulateVRChatHipMovementWeight.Value);
DesktopVRIK.Setting_BodyLeanWeight = Mathf.Clamp01(m_entryBodyLeanWeight.Value);
DesktopVRIK.Setting_BodyAngleLimit = Mathf.Clamp(m_entryBodyAngleLimit.Value, 0f, 90f);
DesktopVRIK.Setting_EmoteVRIK = m_entryEmoteVRIK.Value;
DesktopVRIK.Setting_EmoteLookAtIK = m_entryEmoteLookAtIK.Value;
DesktopVRIK.Instance.ChangeViewpointHandling(m_entryEnforceViewPosition.Value);