diff --git a/README.md b/README.md index a15f12f..b0180ed 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ Merged set of MelonLoader mods for ChilloutVR. |:---------:|:----------:|:--------------:| :----------------------------------------------------------------| | [Avatar Motion Tweaker](/ml_amt/README.md) | ml_amt | 1.3.7 [:arrow_down:](../../releases/latest/download/ml_amt.dll)| ✔ Yes | | [Avatar Synced Look](/ml_asl/README.md) | ml_asl | 1.0.1 [:arrow_down:](../../releases/latest/download/ml_asl.dll)| ✔ Yes | +| [Better Fingers Tracking](/ml_bft/README.md) | ml_bft | 1.0.0 [:arrow_down:](../../releases/latest/download/ml_bft.dll)| On review | | [Desktop Head Tracking](/ml_dht/README.md) | ml_dht | 1.2.1 [:arrow_down:](../../releases/latest/download/ml_dht.dll) | ✔ Yes | | [Leap Motion Extension](/ml_lme/README.md)| ml_lme | 1.4.6 [:arrow_down:](../../releases/latest/download/ml_lme.dll)| ✔ Yes | | [Pickup Arm Movement](/ml_pam/README.md)| ml_pam | 1.1.0 [:arrow_down:](../../releases/latest/download/ml_pam.dll)| ✔ Yes | diff --git a/ml_amt/ml_amt.csproj b/ml_amt/ml_amt.csproj index 2d26d8b..195a72c 100644 --- a/ml_amt/ml_amt.csproj +++ b/ml_amt/ml_amt.csproj @@ -14,8 +14,8 @@ x64 4 - none - false + embedded + true false TRACE diff --git a/ml_asl/ml_asl.csproj b/ml_asl/ml_asl.csproj index 26aad9a..ceeab33 100644 --- a/ml_asl/ml_asl.csproj +++ b/ml_asl/ml_asl.csproj @@ -10,6 +10,11 @@ 1.0.1 + + embedded + true + + diff --git a/ml_bft/AssetsHandler.cs b/ml_bft/AssetsHandler.cs new file mode 100644 index 0000000..87e74d7 --- /dev/null +++ b/ml_bft/AssetsHandler.cs @@ -0,0 +1,89 @@ +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using UnityEngine; + +namespace ml_bft +{ + static class AssetsHandler + { + static readonly List ms_assets = new List() + { + "ovr_fingers.asset", + "oxr_fingers.asset" + }; + + static readonly Dictionary ms_loadedAssets = new Dictionary(); + static readonly Dictionary ms_loadedObjects = new Dictionary(); + + public static void Load() + { + Assembly l_assembly = Assembly.GetExecutingAssembly(); + string l_assemblyName = l_assembly.GetName().Name; + + foreach(string l_assetName in ms_assets) + { + try + { + Stream l_assetStream = l_assembly.GetManifestResourceStream(l_assemblyName + ".resources." + l_assetName); + if(l_assetStream != null) + { + MemoryStream l_memorySteam = new MemoryStream((int)l_assetStream.Length); + l_assetStream.CopyTo(l_memorySteam); + AssetBundle l_assetBundle = AssetBundle.LoadFromMemory(l_memorySteam.ToArray(), 0); + if(l_assetBundle != null) + { + l_assetBundle.hideFlags |= HideFlags.DontUnloadUnusedAsset; + ms_loadedAssets.Add(l_assetName, l_assetBundle); + } + else + MelonLoader.MelonLogger.Warning("Unable to load bundled '" + l_assetName + "' asset"); + } + else + MelonLoader.MelonLogger.Warning("Unable to get bundled '" + l_assetName + "' asset stream"); + } + catch(System.Exception e) + { + MelonLoader.MelonLogger.Warning("Unable to load bundled '" + l_assetName + "' asset, reason: " + e.Message); + } + } + } + + public static GameObject GetAsset(string p_name) + { + GameObject l_result = null; + if(ms_loadedObjects.ContainsKey(p_name)) + { + l_result = Object.Instantiate(ms_loadedObjects[p_name]); + l_result.SetActive(true); + l_result.hideFlags |= HideFlags.DontUnloadUnusedAsset; + } + else + { + foreach(var l_pair in ms_loadedAssets) + { + if(l_pair.Value.Contains(p_name)) + { + GameObject l_bundledObject = (GameObject)l_pair.Value.LoadAsset(p_name, typeof(GameObject)); + if(l_bundledObject != null) + { + ms_loadedObjects.Add(p_name, l_bundledObject); + l_result = Object.Instantiate(l_bundledObject); + l_result.SetActive(true); + l_result.hideFlags |= HideFlags.DontUnloadUnusedAsset; + } + break; + } + } + } + return l_result; + } + + public static void Unload() + { + foreach(var l_pair in ms_loadedAssets) + Object.Destroy(l_pair.Value); + ms_loadedAssets.Clear(); + } + } +} diff --git a/ml_bft/FingerSystem.cs b/ml_bft/FingerSystem.cs new file mode 100644 index 0000000..4b9d5b0 --- /dev/null +++ b/ml_bft/FingerSystem.cs @@ -0,0 +1,199 @@ +using ABI_RC.Core.Player; +using ABI_RC.Core.Savior; +using ABI_RC.Systems.IK; +using ABI_RC.Systems.InputManagement; +using System.Collections.Generic; +using UnityEngine; + +namespace ml_bft +{ + class FingerSystem + { + struct RotationOffset + { + public Transform m_target; + public Transform m_source; + public Quaternion m_offset; + } + + static readonly List ms_leftFingerBones = new List() + { + HumanBodyBones.LeftThumbProximal, HumanBodyBones.LeftThumbIntermediate, HumanBodyBones.LeftThumbDistal, + HumanBodyBones.LeftIndexProximal, HumanBodyBones.LeftIndexIntermediate, HumanBodyBones.LeftIndexDistal, + HumanBodyBones.LeftMiddleProximal, HumanBodyBones.LeftMiddleIntermediate, HumanBodyBones.LeftMiddleDistal, + HumanBodyBones.LeftRingProximal, HumanBodyBones.LeftRingIntermediate, HumanBodyBones.LeftRingDistal, + HumanBodyBones.LeftLittleProximal, HumanBodyBones.LeftLittleIntermediate, HumanBodyBones.LeftLittleDistal + }; + static readonly List ms_rightFingerBones = new List() + { + HumanBodyBones.RightThumbProximal, HumanBodyBones.RightThumbIntermediate, HumanBodyBones.RightThumbDistal, + HumanBodyBones.RightIndexProximal, HumanBodyBones.RightIndexIntermediate, HumanBodyBones.RightIndexDistal, + HumanBodyBones.RightMiddleProximal, HumanBodyBones.RightMiddleIntermediate, HumanBodyBones.RightMiddleDistal, + HumanBodyBones.RightRingProximal, HumanBodyBones.RightRingIntermediate, HumanBodyBones.RightRingDistal, + HumanBodyBones.RightLittleProximal, HumanBodyBones.RightLittleIntermediate, HumanBodyBones.RightLittleDistal + }; + + public static FingerSystem Instance { get; private set; } = null; + + RotationOffset m_leftHandOffset; // From avatar hand to controller wrist + RotationOffset m_rightHandOffset; + readonly List m_leftFingerOffsets = null; // From controller finger bone to avatar finger bone + readonly List m_rightFingerOffsets = null; + + public readonly float[] m_lastValues; + + bool m_ready = false; + HumanPose m_pose; + + internal FingerSystem() + { + if(Instance == null) + Instance = this; + + m_leftFingerOffsets = new List(); + m_rightFingerOffsets = new List(); + + m_pose = new HumanPose(); + m_lastValues = new float[40]; + } + internal void Cleanup() + { + if(Instance == this) + Instance = null; + + m_leftFingerOffsets.Clear(); + m_rightFingerOffsets.Clear(); + m_ready = false; + } + + internal void OnAvatarSetup() + { + if(PlayerSetup.Instance._animator.isHuman) + { + IKSystem.Instance.SetAvatarPose(IKSystem.AvatarPose.TPose); + PlayerSetup.Instance._avatar.transform.localPosition = Vector3.zero; + PlayerSetup.Instance._avatar.transform.localRotation = Quaternion.identity; + InputHandler.Instance?.Rebind(PlayerSetup.Instance.transform.rotation); + + m_leftHandOffset.m_source = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.LeftHand); + m_leftHandOffset.m_target = InputHandler.Instance?.GetSourceForBone(HumanBodyBones.LeftHand, true); + if((m_leftHandOffset.m_source != null) && (m_leftHandOffset.m_target != null)) + m_leftHandOffset.m_offset = Quaternion.Inverse(m_leftHandOffset.m_source.rotation) * m_leftHandOffset.m_target.rotation; + + m_rightHandOffset.m_source = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.RightHand); + m_rightHandOffset.m_target = InputHandler.Instance?.GetSourceForBone(HumanBodyBones.RightHand, false); + if((m_rightHandOffset.m_source != null) && (m_rightHandOffset.m_target != null)) + m_rightHandOffset.m_offset = Quaternion.Inverse(m_rightHandOffset.m_source.rotation) * m_rightHandOffset.m_target.rotation; + + foreach(HumanBodyBones p_bone in ms_leftFingerBones) + { + Transform l_avatarBone = PlayerSetup.Instance._animator.GetBoneTransform(p_bone); + Transform l_controllerBone = InputHandler.Instance?.GetSourceForBone(p_bone, true); + if((l_avatarBone != null) && (l_controllerBone != null)) + { + RotationOffset l_offset = new RotationOffset(); + l_offset.m_source = l_controllerBone; + l_offset.m_target = l_avatarBone; + l_offset.m_offset = Quaternion.Inverse(l_controllerBone.rotation) * l_avatarBone.rotation; + m_leftFingerOffsets.Add(l_offset); + } + } + + foreach(HumanBodyBones p_bone in ms_rightFingerBones) + { + Transform l_avatarBone = PlayerSetup.Instance._animator.GetBoneTransform(p_bone); + Transform l_controllerBone = InputHandler.Instance?.GetSourceForBone(p_bone, false); + if((l_avatarBone != null) && (l_controllerBone != null)) + { + RotationOffset l_offset = new RotationOffset(); + l_offset.m_source = l_controllerBone; + l_offset.m_target = l_avatarBone; + l_offset.m_offset = Quaternion.Inverse(l_controllerBone.rotation) * l_avatarBone.rotation; + m_rightFingerOffsets.Add(l_offset); + } + } + + m_ready = ((m_leftFingerOffsets.Count > 0) || (m_rightFingerOffsets.Count > 0)); + } + } + + internal void OnAvatarClear() + { + m_ready = false; + m_pose = new HumanPose(); + m_leftFingerOffsets.Clear(); + m_rightFingerOffsets.Clear(); + } + + internal void OnReinitializeAvatar() + { + OnAvatarClear(); + OnAvatarSetup(); + } + + internal void OnIKSystemLateUpdate(HumanPoseHandler p_handler) + { + if(m_ready && MetaPort.Instance.isUsingVr && (p_handler != null) && Settings.SkeletalInput) + { + // Virtually allign controllers wrist bone to avatar hands with offset and apply global rotation to avatar finger bones with individial offset + // This is done to apply rotation changes from controller bones to avatar finger bones as in local space + if(CVRInputManager.Instance._leftController != ABI_RC.Systems.InputManagement.XR.eXRControllerType.None) + { + Quaternion l_turnBack = (m_leftHandOffset.m_source.rotation * m_leftHandOffset.m_offset) * Quaternion.Inverse(m_leftHandOffset.m_target.rotation); + foreach(var l_offset in m_leftFingerOffsets) + l_offset.m_target.rotation = l_turnBack * (l_offset.m_source.rotation * l_offset.m_offset); + } + + if(CVRInputManager.Instance._rightController != ABI_RC.Systems.InputManagement.XR.eXRControllerType.None) + { + Quaternion l_turnBack = (m_rightHandOffset.m_source.rotation * m_rightHandOffset.m_offset) * Quaternion.Inverse(m_rightHandOffset.m_target.rotation); + foreach(var l_offset in m_rightFingerOffsets) + l_offset.m_target.rotation = l_turnBack * (l_offset.m_source.rotation * l_offset.m_offset); + } + + // No matter if hands are tracked, fill muscles values + p_handler.GetHumanPose(ref m_pose); + m_lastValues[0] = m_pose.muscles[(int)MuscleIndex.LeftThumb1Stretched]; + m_lastValues[1] = m_pose.muscles[(int)MuscleIndex.LeftThumb2Stretched]; + m_lastValues[2] = m_pose.muscles[(int)MuscleIndex.LeftThumb3Stretched]; + m_lastValues[3] = m_pose.muscles[(int)MuscleIndex.LeftThumbSpread]; + m_lastValues[4] = m_pose.muscles[(int)MuscleIndex.LeftIndex1Stretched]; + m_lastValues[5] = m_pose.muscles[(int)MuscleIndex.LeftIndex2Stretched]; + m_lastValues[6] = m_pose.muscles[(int)MuscleIndex.LeftIndex3Stretched]; + m_lastValues[7] = m_pose.muscles[(int)MuscleIndex.LeftIndexSpread]; + m_lastValues[8] = m_pose.muscles[(int)MuscleIndex.LeftMiddle1Stretched]; + m_lastValues[9] = m_pose.muscles[(int)MuscleIndex.LeftMiddle2Stretched]; + m_lastValues[10] = m_pose.muscles[(int)MuscleIndex.LeftMiddle3Stretched]; + m_lastValues[11] = m_pose.muscles[(int)MuscleIndex.LeftMiddleSpread]; + m_lastValues[12] = m_pose.muscles[(int)MuscleIndex.LeftRing1Stretched]; + m_lastValues[13] = m_pose.muscles[(int)MuscleIndex.LeftRing2Stretched]; + m_lastValues[14] = m_pose.muscles[(int)MuscleIndex.LeftRing3Stretched]; + m_lastValues[15] = m_pose.muscles[(int)MuscleIndex.LeftRingSpread]; + m_lastValues[16] = m_pose.muscles[(int)MuscleIndex.LeftLittle1Stretched]; + m_lastValues[17] = m_pose.muscles[(int)MuscleIndex.LeftLittle2Stretched]; + m_lastValues[18] = m_pose.muscles[(int)MuscleIndex.LeftLittle3Stretched]; + m_lastValues[19] = m_pose.muscles[(int)MuscleIndex.LeftLittleSpread]; + m_lastValues[20] = m_pose.muscles[(int)MuscleIndex.RightThumb1Stretched]; + m_lastValues[21] = m_pose.muscles[(int)MuscleIndex.RightThumb2Stretched]; + m_lastValues[22] = m_pose.muscles[(int)MuscleIndex.RightThumb3Stretched]; + m_lastValues[23] = m_pose.muscles[(int)MuscleIndex.RightThumbSpread]; + m_lastValues[24] = m_pose.muscles[(int)MuscleIndex.RightIndex1Stretched]; + m_lastValues[25] = m_pose.muscles[(int)MuscleIndex.RightIndex2Stretched]; + m_lastValues[26] = m_pose.muscles[(int)MuscleIndex.RightIndex3Stretched]; + m_lastValues[27] = m_pose.muscles[(int)MuscleIndex.RightIndexSpread]; + m_lastValues[28] = m_pose.muscles[(int)MuscleIndex.RightMiddle1Stretched]; + m_lastValues[29] = m_pose.muscles[(int)MuscleIndex.RightMiddle2Stretched]; + m_lastValues[30] = m_pose.muscles[(int)MuscleIndex.RightMiddle3Stretched]; + m_lastValues[31] = m_pose.muscles[(int)MuscleIndex.RightMiddleSpread]; + m_lastValues[32] = m_pose.muscles[(int)MuscleIndex.RightRing1Stretched]; + m_lastValues[33] = m_pose.muscles[(int)MuscleIndex.RightRing2Stretched]; + m_lastValues[34] = m_pose.muscles[(int)MuscleIndex.RightRing3Stretched]; + m_lastValues[35] = m_pose.muscles[(int)MuscleIndex.RightRingSpread]; + m_lastValues[36] = m_pose.muscles[(int)MuscleIndex.RightLittle1Stretched]; + m_lastValues[37] = m_pose.muscles[(int)MuscleIndex.RightLittle2Stretched]; + m_lastValues[38] = m_pose.muscles[(int)MuscleIndex.RightLittle3Stretched]; + m_lastValues[39] = m_pose.muscles[(int)MuscleIndex.RightLittleSpread]; + } + } + } +} diff --git a/ml_bft/HandHandler.cs b/ml_bft/HandHandler.cs new file mode 100644 index 0000000..703279d --- /dev/null +++ b/ml_bft/HandHandler.cs @@ -0,0 +1,59 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace ml_bft +{ + class HandHandler + { + protected bool m_left = false; + protected List m_bones = null; + protected List m_localRotations = null; + protected Transform m_prefabRoot = null; + protected List m_renderers = null; + + protected HandHandler(bool p_left) + { + m_left = p_left; + m_bones = new List(); + m_localRotations = new List(); + m_renderers = new List(); + + Settings.ShowHandsChange += this.OnShowHandsChange; + } + + public virtual void Cleanup() + { + if(m_prefabRoot != null) + Object.Destroy(m_prefabRoot.gameObject); + m_prefabRoot = null; + + m_bones.Clear(); + m_localRotations.Clear(); + m_renderers.Clear(); + + Settings.ShowHandsChange -= this.OnShowHandsChange; + } + + public virtual void Update() + { + } + + public virtual Transform GetSourceForBone(HumanBodyBones p_bone) + { + return default; + } + + public virtual void Rebind(Quaternion p_base) + { + } + + protected void OnShowHandsChange(bool p_state) + { + foreach(var l_render in m_renderers) + { + if(l_render != null) + l_render.enabled = p_state; + } + } + } +} diff --git a/ml_bft/HandHandlerOVR.cs b/ml_bft/HandHandlerOVR.cs new file mode 100644 index 0000000..d0fa6f0 --- /dev/null +++ b/ml_bft/HandHandlerOVR.cs @@ -0,0 +1,238 @@ +using UnityEngine; +using Valve.VR; + +namespace ml_bft +{ + class HandHandlerOVR : HandHandler + { + // 31 bones in each hand, get index at Valve.VR.SteamVR_Skeleton_JointIndexes or SteamVR_Skeleton_JointIndexEnum + const int c_fingerBonesCount = (int)SteamVR_Skeleton_JointIndexEnum.pinkyAux + 1; + + SteamVR_Action_Skeleton m_skeletonAction; + + public HandHandlerOVR(Transform p_root, bool p_left) : base(p_left) + { + for(int i = 0; i < c_fingerBonesCount; i++) + { + m_bones.Add(null); + m_localRotations.Add(Quaternion.identity); + } + + // Fill finger transforms + m_prefabRoot = AssetsHandler.GetAsset(string.Format("assets/steamvr/models/[openvr] {0}.prefab", m_left ? "left" : "right")).transform; + m_prefabRoot.name = "[FingersTracking_OVR]"; + m_prefabRoot.parent = p_root; + m_prefabRoot.localPosition = Vector3.zero; + m_prefabRoot.localRotation = Quaternion.identity; + + m_prefabRoot.GetComponentsInChildren(true, m_renderers); + + // Ah yes, the stupid code + m_bones[(int)SteamVR_Skeleton_JointIndexEnum.root] = m_prefabRoot.Find("Root"); + m_bones[(int)SteamVR_Skeleton_JointIndexEnum.wrist] = m_prefabRoot.Find(string.Format("Root/wrist_{0}", m_left ? 'l' : 'r')); + m_bones[(int)SteamVR_Skeleton_JointIndexEnum.thumbProximal] = m_prefabRoot.Find(string.Format("Root/wrist_{0}/finger_thumb_0_{0}", m_left ? 'l' : 'r')); + m_bones[(int)SteamVR_Skeleton_JointIndexEnum.thumbMiddle] = m_prefabRoot.Find(string.Format("Root/wrist_{0}/finger_thumb_0_{0}/finger_thumb_1_{0}", m_left ? 'l' : 'r')); + m_bones[(int)SteamVR_Skeleton_JointIndexEnum.thumbDistal] = m_prefabRoot.Find(string.Format("Root/wrist_{0}/finger_thumb_0_{0}/finger_thumb_1_{0}/finger_thumb_2_{0}", m_left ? 'l' : 'r')); + m_bones[(int)SteamVR_Skeleton_JointIndexEnum.thumbTip] = m_prefabRoot.Find(string.Format("Root/wrist_{0}/finger_thumb_0_{0}/finger_thumb_1_{0}/finger_thumb_2_{0}/finger_thumb_{0}_end", m_left ? 'l' : 'r')); + + m_bones[(int)SteamVR_Skeleton_JointIndexEnum.indexMetacarpal] = m_prefabRoot.Find(string.Format("Root/wrist_{0}/finger_index_meta_{0}", m_left ? 'l' : 'r')); + m_bones[(int)SteamVR_Skeleton_JointIndexEnum.indexProximal] = m_prefabRoot.Find(string.Format("Root/wrist_{0}/finger_index_meta_{0}/finger_index_0_{0}", m_left ? 'l' : 'r')); + m_bones[(int)SteamVR_Skeleton_JointIndexEnum.indexMiddle] = m_prefabRoot.Find(string.Format("Root/wrist_{0}/finger_index_meta_{0}/finger_index_0_{0}/finger_index_1_{0}", m_left ? 'l' : 'r')); + m_bones[(int)SteamVR_Skeleton_JointIndexEnum.indexDistal] = m_prefabRoot.Find(string.Format("Root/wrist_{0}/finger_index_meta_{0}/finger_index_0_{0}/finger_index_1_{0}/finger_index_2_{0}", m_left ? 'l' : 'r')); + m_bones[(int)SteamVR_Skeleton_JointIndexEnum.indexTip] = m_prefabRoot.Find(string.Format("Root/wrist_{0}/finger_index_meta_{0}/finger_index_0_{0}/finger_index_1_{0}/finger_index_2_{0}/finger_index_{0}_end", m_left ? 'l' : 'r')); + + m_bones[(int)SteamVR_Skeleton_JointIndexEnum.middleMetacarpal] = m_prefabRoot.Find(string.Format("Root/wrist_{0}/finger_middle_meta_{0}", m_left ? 'l' : 'r')); + m_bones[(int)SteamVR_Skeleton_JointIndexEnum.middleProximal] = m_prefabRoot.Find(string.Format("Root/wrist_{0}/finger_middle_meta_{0}/finger_middle_0_{0}", m_left ? 'l' : 'r')); + m_bones[(int)SteamVR_Skeleton_JointIndexEnum.middleMiddle] = m_prefabRoot.Find(string.Format("Root/wrist_{0}/finger_middle_meta_{0}/finger_middle_0_{0}/finger_middle_1_{0}", m_left ? 'l' : 'r')); + m_bones[(int)SteamVR_Skeleton_JointIndexEnum.middleDistal] = m_prefabRoot.Find(string.Format("Root/wrist_{0}/finger_middle_meta_{0}/finger_middle_0_{0}/finger_middle_1_{0}/finger_middle_2_{0}", m_left ? 'l' : 'r')); + m_bones[(int)SteamVR_Skeleton_JointIndexEnum.middleTip] = m_prefabRoot.Find(string.Format("Root/wrist_{0}/finger_middle_meta_{0}/finger_middle_0_{0}/finger_middle_1_{0}/finger_middle_2_{0}/finger_middle_{0}_end", m_left ? 'l' : 'r')); + + m_bones[(int)SteamVR_Skeleton_JointIndexEnum.ringMetacarpal] = m_prefabRoot.Find(string.Format("Root/wrist_{0}/finger_ring_meta_{0}", m_left ? 'l' : 'r')); + m_bones[(int)SteamVR_Skeleton_JointIndexEnum.ringProximal] = m_prefabRoot.Find(string.Format("Root/wrist_{0}/finger_ring_meta_{0}/finger_ring_0_{0}", m_left ? 'l' : 'r')); + m_bones[(int)SteamVR_Skeleton_JointIndexEnum.ringMiddle] = m_prefabRoot.Find(string.Format("Root/wrist_{0}/finger_ring_meta_{0}/finger_ring_0_{0}/finger_ring_1_{0}", m_left ? 'l' : 'r')); + m_bones[(int)SteamVR_Skeleton_JointIndexEnum.ringDistal] = m_prefabRoot.Find(string.Format("Root/wrist_{0}/finger_ring_meta_{0}/finger_ring_0_{0}/finger_ring_1_{0}/finger_ring_2_{0}", m_left ? 'l' : 'r')); + m_bones[(int)SteamVR_Skeleton_JointIndexEnum.ringTip] = m_prefabRoot.Find(string.Format("Root/wrist_{0}/finger_ring_meta_{0}/finger_ring_0_{0}/finger_ring_1_{0}/finger_ring_2_{0}/finger_ring_{0}_end", m_left ? 'l' : 'r')); + + m_bones[(int)SteamVR_Skeleton_JointIndexEnum.pinkyMetacarpal] = m_prefabRoot.Find(string.Format("Root/wrist_{0}/finger_pinky_meta_{0}", m_left ? 'l' : 'r')); + m_bones[(int)SteamVR_Skeleton_JointIndexEnum.pinkyProximal] = m_prefabRoot.Find(string.Format("Root/wrist_{0}/finger_pinky_meta_{0}/finger_pinky_0_{0}", m_left ? 'l' : 'r')); + m_bones[(int)SteamVR_Skeleton_JointIndexEnum.pinkyMiddle] = m_prefabRoot.Find(string.Format("Root/wrist_{0}/finger_pinky_meta_{0}/finger_pinky_0_{0}/finger_pinky_1_{0}", m_left ? 'l' : 'r')); + m_bones[(int)SteamVR_Skeleton_JointIndexEnum.pinkyDistal] = m_prefabRoot.Find(string.Format("Root/wrist_{0}/finger_pinky_meta_{0}/finger_pinky_0_{0}/finger_pinky_1_{0}/finger_pinky_2_{0}", m_left ? 'l' : 'r')); + m_bones[(int)SteamVR_Skeleton_JointIndexEnum.pinkyTip] = m_prefabRoot.Find(string.Format("Root/wrist_{0}/finger_pinky_meta_{0}/finger_pinky_0_{0}/finger_pinky_1_{0}/finger_pinky_2_{0}/finger_pinky_{0}_end", m_left ? 'l' : 'r')); + + m_bones[(int)SteamVR_Skeleton_JointIndexEnum.thumbAux] = m_prefabRoot.Find(string.Format("Root/finger_thumb_{0}_aux", m_left ? 'l' : 'r')); + m_bones[(int)SteamVR_Skeleton_JointIndexEnum.indexAux] = m_prefabRoot.Find(string.Format("Root/finger_index_{0}_aux", m_left ? 'l' : 'r')); + m_bones[(int)SteamVR_Skeleton_JointIndexEnum.middleAux] = m_prefabRoot.Find(string.Format("Root/finger_middle_{0}_aux", m_left ? 'l' : 'r')); + m_bones[(int)SteamVR_Skeleton_JointIndexEnum.ringAux] = m_prefabRoot.Find(string.Format("Root/finger_ring_{0}_aux", m_left ? 'l' : 'r')); + m_bones[(int)SteamVR_Skeleton_JointIndexEnum.pinkyAux] = m_prefabRoot.Find(string.Format("Root/finger_pinky_{0}_aux", m_left ? 'l' : 'r')); + + // Remember local rotations + for(int i = 0; i < c_fingerBonesCount; i++) + { + if(m_bones[i] != null) + m_localRotations[i] = m_bones[i].localRotation; + } + + m_skeletonAction = SteamVR_Input.GetAction(p_left ? "SkeletonLeftHand" : "SkeletonRightHand"); + + base.OnShowHandsChange(Settings.ShowHands); + } + + public override void Cleanup() + { + base.Cleanup(); + + m_skeletonAction = null; + } + + public override void Update() + { + if(m_skeletonAction != null) + { + var l_rotations = m_skeletonAction.GetBoneRotations(); + var l_positions = m_skeletonAction.GetBonePositions(); + for(int i = 0; i < c_fingerBonesCount; i++) + { + if(m_bones[i] != null) + { + m_bones[i].localRotation = l_rotations[i]; + m_bones[i].localPosition = l_positions[i]; + } + } + } + } + + public override Transform GetSourceForBone(HumanBodyBones p_bone) + { + Transform l_result = null; + if(m_left) + { + switch(p_bone) + { + case HumanBodyBones.LeftHand: + l_result = m_bones[(int)SteamVR_Skeleton_JointIndexEnum.wrist]; + break; + case HumanBodyBones.LeftThumbProximal: + l_result = m_bones[(int)SteamVR_Skeleton_JointIndexEnum.thumbProximal]; + break; + case HumanBodyBones.LeftThumbIntermediate: + l_result = m_bones[(int)SteamVR_Skeleton_JointIndexEnum.thumbMiddle]; + break; + case HumanBodyBones.LeftThumbDistal: + l_result = m_bones[(int)SteamVR_Skeleton_JointIndexEnum.thumbDistal]; + break; + + case HumanBodyBones.LeftIndexProximal: + l_result = m_bones[(int)SteamVR_Skeleton_JointIndexEnum.indexProximal]; + break; + case HumanBodyBones.LeftIndexIntermediate: + l_result = m_bones[(int)SteamVR_Skeleton_JointIndexEnum.indexMiddle]; + break; + case HumanBodyBones.LeftIndexDistal: + l_result = m_bones[(int)SteamVR_Skeleton_JointIndexEnum.indexDistal]; + break; + + case HumanBodyBones.LeftMiddleProximal: + l_result = m_bones[(int)SteamVR_Skeleton_JointIndexEnum.middleProximal]; + break; + case HumanBodyBones.LeftMiddleIntermediate: + l_result = m_bones[(int)SteamVR_Skeleton_JointIndexEnum.middleMiddle]; + break; + case HumanBodyBones.LeftMiddleDistal: + l_result = m_bones[(int)SteamVR_Skeleton_JointIndexEnum.middleDistal]; + break; + + case HumanBodyBones.LeftRingProximal: + l_result = m_bones[(int)SteamVR_Skeleton_JointIndexEnum.ringProximal]; + break; + case HumanBodyBones.LeftRingIntermediate: + l_result = m_bones[(int)SteamVR_Skeleton_JointIndexEnum.ringMiddle]; + break; + case HumanBodyBones.LeftRingDistal: + l_result = m_bones[(int)SteamVR_Skeleton_JointIndexEnum.ringDistal]; + break; + + case HumanBodyBones.LeftLittleProximal: + l_result = m_bones[(int)SteamVR_Skeleton_JointIndexEnum.pinkyProximal]; + break; + case HumanBodyBones.LeftLittleIntermediate: + l_result = m_bones[(int)SteamVR_Skeleton_JointIndexEnum.pinkyMiddle]; + break; + case HumanBodyBones.LeftLittleDistal: + l_result = m_bones[(int)SteamVR_Skeleton_JointIndexEnum.pinkyDistal]; + break; + } + } + else + { + switch(p_bone) + { + case HumanBodyBones.RightHand: + l_result = m_bones[(int)SteamVR_Skeleton_JointIndexEnum.wrist]; + break; + case HumanBodyBones.RightThumbProximal: + l_result = m_bones[(int)SteamVR_Skeleton_JointIndexEnum.thumbProximal]; + break; + case HumanBodyBones.RightThumbIntermediate: + l_result = m_bones[(int)SteamVR_Skeleton_JointIndexEnum.thumbMiddle]; + break; + case HumanBodyBones.RightThumbDistal: + l_result = m_bones[(int)SteamVR_Skeleton_JointIndexEnum.thumbDistal]; + break; + + case HumanBodyBones.RightIndexProximal: + l_result = m_bones[(int)SteamVR_Skeleton_JointIndexEnum.indexProximal]; + break; + case HumanBodyBones.RightIndexIntermediate: + l_result = m_bones[(int)SteamVR_Skeleton_JointIndexEnum.indexMiddle]; + break; + case HumanBodyBones.RightIndexDistal: + l_result = m_bones[(int)SteamVR_Skeleton_JointIndexEnum.indexDistal]; + break; + + case HumanBodyBones.RightMiddleProximal: + l_result = m_bones[(int)SteamVR_Skeleton_JointIndexEnum.middleProximal]; + break; + case HumanBodyBones.RightMiddleIntermediate: + l_result = m_bones[(int)SteamVR_Skeleton_JointIndexEnum.middleMiddle]; + break; + case HumanBodyBones.RightMiddleDistal: + l_result = m_bones[(int)SteamVR_Skeleton_JointIndexEnum.middleDistal]; + break; + + case HumanBodyBones.RightRingProximal: + l_result = m_bones[(int)SteamVR_Skeleton_JointIndexEnum.ringProximal]; + break; + case HumanBodyBones.RightRingIntermediate: + l_result = m_bones[(int)SteamVR_Skeleton_JointIndexEnum.ringMiddle]; + break; + case HumanBodyBones.RightRingDistal: + l_result = m_bones[(int)SteamVR_Skeleton_JointIndexEnum.ringDistal]; + break; + + case HumanBodyBones.RightLittleProximal: + l_result = m_bones[(int)SteamVR_Skeleton_JointIndexEnum.pinkyProximal]; + break; + case HumanBodyBones.RightLittleIntermediate: + l_result = m_bones[(int)SteamVR_Skeleton_JointIndexEnum.pinkyMiddle]; + break; + case HumanBodyBones.RightLittleDistal: + l_result = m_bones[(int)SteamVR_Skeleton_JointIndexEnum.pinkyDistal]; + break; + } + } + return l_result; + } + + public override void Rebind(Quaternion p_base) + { + for(int i = 0; i < c_fingerBonesCount; i++) + { + if(m_bones[i] != null) + m_bones[i].localRotation = m_localRotations[i]; + } + + if(m_bones[(int)SteamVR_Skeleton_JointIndexEnum.root] != null) + m_bones[(int)SteamVR_Skeleton_JointIndexEnum.root].rotation = p_base * (m_left ? Quaternion.Euler(0f, -90f, 0f) : Quaternion.Euler(0f, 90f, 0f)); + } + } +} diff --git a/ml_bft/HandHandlerXR.cs b/ml_bft/HandHandlerXR.cs new file mode 100644 index 0000000..00f72e6 --- /dev/null +++ b/ml_bft/HandHandlerXR.cs @@ -0,0 +1,225 @@ +using UnityEngine; +using UnityEngine.XR.OpenXR; +using UnityEngine.XR.Hands; + +namespace ml_bft +{ + class HandHandlerXR : HandHandler + { + // 26 bones, get in XRHandJointID enum + const int c_fingerBonesCount = (int)XRHandJointID.EndMarker - 1; + + public HandHandlerXR(Transform p_root, bool p_left) : base(p_left) + { + for(int i = 0; i < c_fingerBonesCount; i++) + { + m_bones.Add(null); + m_localRotations.Add(Quaternion.identity); + } + + m_prefabRoot = AssetsHandler.GetAsset(string.Format("Assets/OpenXR/Models/{0}Hand_IK.prefab", m_left ? "Left" : "Right")).transform; + m_prefabRoot.name = "[FingersTracking_XR]"; + m_prefabRoot.parent = p_root; + m_prefabRoot.localPosition = Vector3.zero; + m_prefabRoot.localRotation = Quaternion.identity; + + m_prefabRoot.GetComponentsInChildren(true, m_renderers); + + // Ah yes, the stupid code + m_bones[(int)XRHandJointID.Wrist - 1] = m_prefabRoot.Find(string.Format("{0}_Wrist", m_left ? 'L' : 'R')); + m_bones[(int)XRHandJointID.Palm - 1] = m_prefabRoot.Find(string.Format("{0}_Wrist/{0}_Palm", m_left ? 'L' : 'R')); + + m_bones[(int)XRHandJointID.ThumbMetacarpal - 1] = m_prefabRoot.Find(string.Format("{0}_Wrist/{0}_ThumbMetacarpal", m_left ? 'L' : 'R')); + m_bones[(int)XRHandJointID.ThumbProximal - 1] = m_prefabRoot.Find(string.Format("{0}_Wrist/{0}_ThumbMetacarpal/{0}_Wrist/{0}_ThumbProximal", m_left ? 'L' : 'R')); + m_bones[(int)XRHandJointID.ThumbDistal - 1] = m_prefabRoot.Find(string.Format("{0}_Wrist/{0}_ThumbMetacarpal/{0}_Wrist/{0}_ThumbProximal/{0}_ThumbDistal", m_left ? 'L' : 'R')); + m_bones[(int)XRHandJointID.ThumbTip - 1] = m_prefabRoot.Find(string.Format("{0}_Wrist/{0}_ThumbMetacarpal/{0}_Wrist/{0}_ThumbProximal/{0}_ThumbDistal/{0}_ThumbTip", m_left ? 'L' : 'R')); + m_bones[(int)XRHandJointID.IndexMetacarpal - 1] = m_prefabRoot.Find(string.Format("{0}_Wrist/{0}_IndexMetacarpal", m_left ? 'L' : 'R')); + m_bones[(int)XRHandJointID.IndexProximal - 1] = m_prefabRoot.Find(string.Format("{0}_Wrist/{0}_IndexMetacarpal/{0}_IndexProximal", m_left ? 'L' : 'R')); + m_bones[(int)XRHandJointID.IndexIntermediate - 1] = m_prefabRoot.Find(string.Format("{0}_Wrist/{0}_IndexMetacarpal/{0}_IndexProximal/{0}_IndexIntermediate", m_left ? 'L' : 'R')); + m_bones[(int)XRHandJointID.IndexDistal - 1] = m_prefabRoot.Find(string.Format("{0}_Wrist/{0}_IndexMetacarpal/{0}_IndexProximal/{0}_IndexIntermediate/{0}_IndexDistal", m_left ? 'L' : 'R')); + m_bones[(int)XRHandJointID.IndexTip - 1] = m_prefabRoot.Find(string.Format("{0}_Wrist/{0}_IndexMetacarpal/{0}_IndexProximal/{0}_IndexIntermediate/{0}_IndexDistal/{0}_IndexTip", m_left ? 'L' : 'R')); + + m_bones[(int)XRHandJointID.MiddleMetacarpal - 1] = m_prefabRoot.Find(string.Format("{0}_Wrist/{0}_MiddleMetacarpal", m_left ? 'L' : 'R')); + m_bones[(int)XRHandJointID.MiddleProximal - 1] = m_prefabRoot.Find(string.Format("{0}_Wrist/{0}_MiddleMetacarpal/{0}_MiddleProximal", m_left ? 'L' : 'R')); + m_bones[(int)XRHandJointID.MiddleIntermediate - 1] = m_prefabRoot.Find(string.Format("{0}_Wrist/{0}_MiddleMetacarpal/{0}_MiddleProximal/{0}_MiddleIntermediate", m_left ? 'L' : 'R')); + m_bones[(int)XRHandJointID.MiddleDistal - 1] = m_prefabRoot.Find(string.Format("{0}_Wrist/{0}_MiddleMetacarpal/{0}_MiddleProximal/{0}_MiddleIntermediate/{0}_MiddleDistal", m_left ? 'L' : 'R')); + m_bones[(int)XRHandJointID.MiddleTip - 1] = m_prefabRoot.Find(string.Format("{0}_Wrist/{0}_MiddleMetacarpal/{0}_MiddleProximal/{0}_MiddleIntermediate/{0}_MiddleDistal/{0}_MiddleTip", m_left ? 'L' : 'R')); + + m_bones[(int)XRHandJointID.RingMetacarpal - 1] = m_prefabRoot.Find(string.Format("{0}_Wrist/{0}_RingMetacarpal", m_left ? 'L' : 'R')); + m_bones[(int)XRHandJointID.RingProximal - 1] = m_prefabRoot.Find(string.Format("{0}_Wrist/{0}_RingMetacarpal/{0}_RingProximal", m_left ? 'L' : 'R')); + m_bones[(int)XRHandJointID.RingIntermediate - 1] = m_prefabRoot.Find(string.Format("{0}_Wrist/{0}_RingMetacarpal/{0}_RingProximal/{0}_RingIntermediate", m_left ? 'L' : 'R')); + m_bones[(int)XRHandJointID.RingDistal - 1] = m_prefabRoot.Find(string.Format("{0}_Wrist/{0}_RingMetacarpal/{0}_RingProximal/{0}_RingIntermediate/{0}_RingDistal", m_left ? 'L' : 'R')); + m_bones[(int)XRHandJointID.RingTip - 1] = m_prefabRoot.Find(string.Format("{0}_Wrist/{0}_RingMetacarpal/{0}_RingProximal/{0}_RingIntermediate/{0}_RingDistal/{0}_RingTip", m_left ? 'L' : 'R')); + + m_bones[(int)XRHandJointID.LittleMetacarpal - 1] = m_prefabRoot.Find(string.Format("{0}_Wrist/{0}_LittleMetacarpal", m_left ? 'L' : 'R')); + m_bones[(int)XRHandJointID.LittleProximal - 1] = m_prefabRoot.Find(string.Format("{0}_Wrist/{0}_LittleMetacarpal/{0}_LittleProximal", m_left ? 'L' : 'R')); + m_bones[(int)XRHandJointID.LittleIntermediate - 1] = m_prefabRoot.Find(string.Format("{0}_Wrist/{0}_LittleMetacarpal/{0}_LittleProximal/{0}_LittleIntermediate", m_left ? 'L' : 'R')); + m_bones[(int)XRHandJointID.LittleDistal - 1] = m_prefabRoot.Find(string.Format("{0}_Wrist/{0}_LittleMetacarpal/{0}_LittleProximal/{0}_LittleIntermediate/{0}_LittleDistal", m_left ? 'L' : 'R')); + m_bones[(int)XRHandJointID.LittleTip - 1] = m_prefabRoot.Find(string.Format("{0}_Wrist/{0}_LittleMetacarpal/{0}_LittleProximal/{0}_LittleIntermediate/{0}_LittleDistal/{0}_LittleTip", m_left ? 'L' : 'R')); + + for(int i = 0; i < c_fingerBonesCount; i++) + { + if(m_bones[i] != null) + m_localRotations[i] = m_bones[i].localRotation; + } + + base.OnShowHandsChange(Settings.ShowHands); + } + + public override Transform GetSourceForBone(HumanBodyBones p_bone) + { + Transform l_result = null; + if(m_left) + { + switch(p_bone) + { + case HumanBodyBones.LeftHand: + l_result = m_bones[(int)XRHandJointID.Wrist - 1]; + break; + case HumanBodyBones.LeftThumbProximal: + l_result = m_bones[(int)XRHandJointID.ThumbMetacarpal - 1]; + break; + case HumanBodyBones.LeftThumbIntermediate: + l_result = m_bones[(int)XRHandJointID.ThumbProximal - 1]; + break; + case HumanBodyBones.LeftThumbDistal: + l_result = m_bones[(int)XRHandJointID.ThumbDistal - 1]; + break; + + case HumanBodyBones.LeftIndexProximal: + l_result = m_bones[(int)XRHandJointID.IndexProximal - 1]; + break; + case HumanBodyBones.LeftIndexIntermediate: + l_result = m_bones[(int)XRHandJointID.IndexIntermediate - 1]; + break; + case HumanBodyBones.LeftIndexDistal: + l_result = m_bones[(int)XRHandJointID.IndexDistal - 1]; + break; + + case HumanBodyBones.LeftMiddleProximal: + l_result = m_bones[(int)XRHandJointID.MiddleProximal - 1]; + break; + case HumanBodyBones.LeftMiddleIntermediate: + l_result = m_bones[(int)XRHandJointID.MiddleIntermediate - 1]; + break; + case HumanBodyBones.LeftMiddleDistal: + l_result = m_bones[(int)XRHandJointID.MiddleDistal - 1]; + break; + + case HumanBodyBones.LeftRingProximal: + l_result = m_bones[(int)XRHandJointID.RingProximal - 1]; + break; + case HumanBodyBones.LeftRingIntermediate: + l_result = m_bones[(int)XRHandJointID.RingIntermediate - 1]; + break; + case HumanBodyBones.LeftRingDistal: + l_result = m_bones[(int)XRHandJointID.RingDistal - 1]; + break; + + case HumanBodyBones.LeftLittleProximal: + l_result = m_bones[(int)XRHandJointID.LittleProximal - 1]; + break; + case HumanBodyBones.LeftLittleIntermediate: + l_result = m_bones[(int)XRHandJointID.LittleIntermediate - 1]; + break; + case HumanBodyBones.LeftLittleDistal: + l_result = m_bones[(int)XRHandJointID.LittleDistal - 1]; + break; + } + } + else + { + switch(p_bone) + { + case HumanBodyBones.RightHand: + l_result = m_bones[(int)XRHandJointID.Wrist - 1]; + break; + case HumanBodyBones.RightThumbProximal: + l_result = m_bones[(int)XRHandJointID.ThumbMetacarpal - 1]; + break; + case HumanBodyBones.RightThumbIntermediate: + l_result = m_bones[(int)XRHandJointID.ThumbProximal - 1]; + break; + case HumanBodyBones.RightThumbDistal: + l_result = m_bones[(int)XRHandJointID.ThumbDistal - 1]; + break; + + case HumanBodyBones.RightIndexProximal: + l_result = m_bones[(int)XRHandJointID.IndexProximal - 1]; + break; + case HumanBodyBones.RightIndexIntermediate: + l_result = m_bones[(int)XRHandJointID.IndexIntermediate - 1]; + break; + case HumanBodyBones.RightIndexDistal: + l_result = m_bones[(int)XRHandJointID.IndexDistal - 1]; + break; + + case HumanBodyBones.RightMiddleProximal: + l_result = m_bones[(int)XRHandJointID.MiddleProximal - 1]; + break; + case HumanBodyBones.RightMiddleIntermediate: + l_result = m_bones[(int)XRHandJointID.MiddleIntermediate - 1]; + break; + case HumanBodyBones.RightMiddleDistal: + l_result = m_bones[(int)XRHandJointID.MiddleDistal - 1]; + break; + + case HumanBodyBones.RightRingProximal: + l_result = m_bones[(int)XRHandJointID.RingProximal - 1]; + break; + case HumanBodyBones.RightRingIntermediate: + l_result = m_bones[(int)XRHandJointID.RingIntermediate - 1]; + break; + case HumanBodyBones.RightRingDistal: + l_result = m_bones[(int)XRHandJointID.RingDistal - 1]; + break; + + case HumanBodyBones.RightLittleProximal: + l_result = m_bones[(int)XRHandJointID.LittleProximal - 1]; + break; + case HumanBodyBones.RightLittleIntermediate: + l_result = m_bones[(int)XRHandJointID.LittleIntermediate - 1]; + break; + case HumanBodyBones.RightLittleDistal: + l_result = m_bones[(int)XRHandJointID.LittleDistal - 1]; + break; + } + } + return l_result; + } + + public override void Update() + { + var l_tracking = OpenXRSettings.Instance.GetFeature(); + if(l_tracking != null) + { + // Bones from API are in playspace, not local in comparison with OpenVR + l_tracking.GetHandJoints(m_left ? HandTrackingFeature.Hand_Index.L : HandTrackingFeature.Hand_Index.R, out var l_positions, out var l_rotations, out var l_radius); + if(l_positions.Length >= c_fingerBonesCount) + { + // This stuff rotates debug hands in a weird way, but it doesn't matter because of funny math in new FingerSystem + Quaternion l_rot = m_prefabRoot.rotation; + m_prefabRoot.rotation = Quaternion.identity; + for(int i = 0; i < c_fingerBonesCount; i++) + { + if(m_bones[i] != null) + m_bones[i].rotation = l_rotations[i]; + } + m_prefabRoot.rotation = l_rot; + } + } + } + + public override void Rebind(Quaternion p_base) + { + for(int i = 0; i < c_fingerBonesCount; i++) + { + if(m_bones[i] != null) + m_bones[i].localRotation = m_localRotations[i]; + } + + if(m_bones[(int)XRHandJointID.Wrist - 1] != null) + m_bones[(int)XRHandJointID.Wrist - 1].rotation = p_base * (m_left ? Quaternion.Euler(0f, -90f, 0f) : Quaternion.Euler(0f, 90f, 0f)); + } + } +} diff --git a/ml_bft/InputHandler.cs b/ml_bft/InputHandler.cs new file mode 100644 index 0000000..52d3f5a --- /dev/null +++ b/ml_bft/InputHandler.cs @@ -0,0 +1,161 @@ +using ABI_RC.Core.Savior; +using ABI_RC.Systems.InputManagement; +using ABI_RC.Systems.VRModeSwitch; +using UnityEngine; + +namespace ml_bft +{ + // Not an actual module, but can be used as one + class InputHandler + { + public static InputHandler Instance { get; private set; } = null; + + bool m_active = false; + + HandHandler m_leftHandHandler = null; + HandHandler m_rightHandHandler = null; + + internal InputHandler() + { + if(Instance == null) + Instance = this; + + m_active = false; + + if(MetaPort.Instance.isUsingVr) + SetupHandlers(); + + VRModeSwitchEvents.OnInitializeXR.AddListener(this.OnSwitchToVR); + VRModeSwitchEvents.OnDeinitializeXR.AddListener(this.OnSwitchToDesktop); + + Settings.SkeletalInputChange += this.OnSkeletalInputChange; + } + internal void Cleanup() + { + if(Instance == this) + Instance = null; + + RemoveHandlers(); + } + + void SetupHandlers() + { + if(!CheckVR.Instance.forceOpenXr) + { + m_leftHandHandler = new HandHandlerOVR(CVRInputManager.Instance.leftHandTransform, true); + m_rightHandHandler = new HandHandlerOVR(CVRInputManager.Instance.rightHandTransform, false); + m_active = true; + } + } + void RemoveHandlers() + { + m_leftHandHandler?.Cleanup(); + m_leftHandHandler = null; + m_rightHandHandler?.Cleanup(); + m_rightHandHandler = null; + m_active = false; + } + + public void Rebind(Quaternion p_base) + { + if(m_active) + { + m_leftHandHandler?.Rebind(p_base); + m_rightHandHandler?.Rebind(p_base); + } + } + + public Transform GetSourceForBone(HumanBodyBones p_bone, bool p_left) + { + Transform l_result; + if(p_left) + l_result = m_leftHandHandler?.GetSourceForBone(p_bone); + else + l_result = m_rightHandHandler?.GetSourceForBone(p_bone); + return l_result; + } + + // Game events + internal void OnInputUpdate() + { + if(m_active && Settings.SkeletalInput) + { + m_leftHandHandler?.Update(); + m_rightHandHandler?.Update(); + + CVRInputManager.Instance.individualFingerTracking = true; + CVRInputManager.Instance.finger1StretchedLeftThumb = FingerSystem.Instance.m_lastValues[0]; + CVRInputManager.Instance.finger2StretchedLeftThumb = FingerSystem.Instance.m_lastValues[1]; + CVRInputManager.Instance.finger3StretchedLeftThumb = FingerSystem.Instance.m_lastValues[2]; + CVRInputManager.Instance.fingerSpreadLeftThumb = FingerSystem.Instance.m_lastValues[3]; + CVRInputManager.Instance.finger1StretchedLeftIndex = FingerSystem.Instance.m_lastValues[4]; + CVRInputManager.Instance.finger2StretchedLeftIndex = FingerSystem.Instance.m_lastValues[5]; + CVRInputManager.Instance.finger3StretchedLeftIndex = FingerSystem.Instance.m_lastValues[6]; + CVRInputManager.Instance.fingerSpreadLeftIndex = FingerSystem.Instance.m_lastValues[7]; + CVRInputManager.Instance.finger1StretchedLeftMiddle = FingerSystem.Instance.m_lastValues[8]; + CVRInputManager.Instance.finger2StretchedLeftMiddle = FingerSystem.Instance.m_lastValues[9]; + CVRInputManager.Instance.finger3StretchedLeftMiddle = FingerSystem.Instance.m_lastValues[10]; + CVRInputManager.Instance.fingerSpreadLeftMiddle = FingerSystem.Instance.m_lastValues[11]; + CVRInputManager.Instance.finger1StretchedLeftRing = FingerSystem.Instance.m_lastValues[12]; + CVRInputManager.Instance.finger2StretchedLeftRing = FingerSystem.Instance.m_lastValues[13]; + CVRInputManager.Instance.finger3StretchedLeftRing = FingerSystem.Instance.m_lastValues[14]; + CVRInputManager.Instance.fingerSpreadLeftRing = FingerSystem.Instance.m_lastValues[15]; + CVRInputManager.Instance.finger1StretchedLeftPinky = FingerSystem.Instance.m_lastValues[16]; + CVRInputManager.Instance.finger2StretchedLeftPinky = FingerSystem.Instance.m_lastValues[17]; + CVRInputManager.Instance.finger3StretchedLeftPinky = FingerSystem.Instance.m_lastValues[18]; + CVRInputManager.Instance.fingerSpreadLeftPinky = FingerSystem.Instance.m_lastValues[19]; + CVRInputManager.Instance.finger1StretchedRightThumb = FingerSystem.Instance.m_lastValues[20]; + CVRInputManager.Instance.finger2StretchedRightThumb = FingerSystem.Instance.m_lastValues[21]; + CVRInputManager.Instance.finger3StretchedRightThumb = FingerSystem.Instance.m_lastValues[22]; + CVRInputManager.Instance.fingerSpreadRightThumb = FingerSystem.Instance.m_lastValues[23]; + CVRInputManager.Instance.finger1StretchedRightIndex = FingerSystem.Instance.m_lastValues[24]; + CVRInputManager.Instance.finger2StretchedRightIndex = FingerSystem.Instance.m_lastValues[25]; + CVRInputManager.Instance.finger3StretchedRightIndex = FingerSystem.Instance.m_lastValues[26]; + CVRInputManager.Instance.fingerSpreadRightIndex = FingerSystem.Instance.m_lastValues[27]; + CVRInputManager.Instance.finger1StretchedRightMiddle = FingerSystem.Instance.m_lastValues[28]; + CVRInputManager.Instance.finger2StretchedRightMiddle = FingerSystem.Instance.m_lastValues[29]; + CVRInputManager.Instance.finger3StretchedRightMiddle = FingerSystem.Instance.m_lastValues[30]; + CVRInputManager.Instance.fingerSpreadRightMiddle = FingerSystem.Instance.m_lastValues[31]; + CVRInputManager.Instance.finger1StretchedRightRing = FingerSystem.Instance.m_lastValues[32]; + CVRInputManager.Instance.finger2StretchedRightRing = FingerSystem.Instance.m_lastValues[33]; + CVRInputManager.Instance.finger3StretchedRightRing = FingerSystem.Instance.m_lastValues[34]; + CVRInputManager.Instance.fingerSpreadRightRing = FingerSystem.Instance.m_lastValues[35]; + CVRInputManager.Instance.finger1StretchedRightPinky = FingerSystem.Instance.m_lastValues[36]; + CVRInputManager.Instance.finger2StretchedRightPinky = FingerSystem.Instance.m_lastValues[37]; + CVRInputManager.Instance.finger3StretchedRightPinky = FingerSystem.Instance.m_lastValues[38]; + CVRInputManager.Instance.fingerSpreadRightPinky = FingerSystem.Instance.m_lastValues[39]; + } + } + + void OnSwitchToVR() + { + try + { + SetupHandlers(); + } + catch(System.Exception e) + { + MelonLoader.MelonLogger.Error(e); + } + } + + void OnSwitchToDesktop() + { + try + { + RemoveHandlers(); + } + catch(System.Exception e) + { + MelonLoader.MelonLogger.Error(e); + } + } + + // Settings + void OnSkeletalInputChange(bool p_value) + { + if(!p_value) + CVRInputManager.Instance.individualFingerTracking = Utils.AreKnucklesInUse(); + } + } +} diff --git a/ml_bft/Main.cs b/ml_bft/Main.cs new file mode 100644 index 0000000..0c632e6 --- /dev/null +++ b/ml_bft/Main.cs @@ -0,0 +1,141 @@ +using ABI_RC.Core.Player; +using ABI_RC.Systems.IK; +using ABI_RC.Systems.InputManagement; +using System; +using System.Collections; +using System.Reflection; +using UnityEngine; + +namespace ml_bft +{ + public class BetterFingersTracking : MelonLoader.MelonMod + { + static BetterFingersTracking ms_instance = null; + + InputHandler m_inputHandler = null; + FingerSystem m_fingerSystem = null; + + public override void OnInitializeMelon() + { + if(ms_instance == null) + ms_instance = this; + + Settings.Init(); + AssetsHandler.Load(); + + // Needed patches: avatar initialization and reinitialization on vr switch, after input update, after late iksystem update + HarmonyInstance.Patch( + typeof(PlayerSetup).GetMethod(nameof(PlayerSetup.SetupAvatar)), + null, + new HarmonyLib.HarmonyMethod(typeof(BetterFingersTracking).GetMethod(nameof(OnSetupAvatar_Postfix), BindingFlags.Static | BindingFlags.NonPublic)) + ); + HarmonyInstance.Patch( + typeof(PlayerSetup).GetMethod(nameof(PlayerSetup.ClearAvatar)), + null, + new HarmonyLib.HarmonyMethod(typeof(BetterFingersTracking).GetMethod(nameof(OnAvatarClear_Postfix), BindingFlags.Static | BindingFlags.NonPublic)) + ); + HarmonyInstance.Patch( + typeof(IKSystem).GetMethod(nameof(IKSystem.ReinitializeAvatar), BindingFlags.Instance | BindingFlags.Public), + null, + new HarmonyLib.HarmonyMethod(typeof(BetterFingersTracking).GetMethod(nameof(OnReinitializeAvatar_Postfix), BindingFlags.Static | BindingFlags.NonPublic)) + ); + HarmonyInstance.Patch( + typeof(CVRInputManager).GetMethod("UpdateInput", BindingFlags.NonPublic | BindingFlags.Instance), + null, + new HarmonyLib.HarmonyMethod(typeof(BetterFingersTracking).GetMethod(nameof(OnInputUpdate_Postfix), BindingFlags.NonPublic | BindingFlags.Static)) + ); + HarmonyInstance.Patch( + typeof(IKSystem).GetMethod("LateUpdate", BindingFlags.NonPublic | BindingFlags.Instance), + null, + new HarmonyLib.HarmonyMethod(typeof(BetterFingersTracking).GetMethod(nameof(OnIKSystemLateUpdate_Postfix), BindingFlags.NonPublic | BindingFlags.Static)) + ); + + MelonLoader.MelonCoroutines.Start(WaitForInstances()); + } + IEnumerator WaitForInstances() + { + while(CVRInputManager.Instance == null) + yield return null; + + m_inputHandler = new InputHandler(); + m_fingerSystem = new FingerSystem(); + } + + public override void OnDeinitializeMelon() + { + if(ms_instance == this) + ms_instance = null; + + m_inputHandler?.Cleanup(); + m_inputHandler = null; + + m_fingerSystem?.Cleanup(); + m_fingerSystem = null; + } + + static void OnSetupAvatar_Postfix() => ms_instance?.OnSetupAvatar(); + void OnSetupAvatar() + { + try + { + m_fingerSystem?.OnAvatarSetup(); + } + catch(Exception e) + { + MelonLoader.MelonLogger.Error(e); + } + } + + static void OnAvatarClear_Postfix() => ms_instance?.OnAvatarClear(); + void OnAvatarClear() + { + try + { + m_fingerSystem?.OnAvatarClear(); + } + catch(Exception e) + { + MelonLoader.MelonLogger.Error(e); + } + } + + static void OnReinitializeAvatar_Postfix() => ms_instance?.OnAvatarReinitialize(); + void OnAvatarReinitialize() + { + try + { + m_fingerSystem?.OnReinitializeAvatar(); + } + catch(Exception e) + { + MelonLoader.MelonLogger.Error(e); + } + } + + static void OnInputUpdate_Postfix() => ms_instance?.OnInputUpdate(); + void OnInputUpdate() + { + try + { + m_inputHandler?.OnInputUpdate(); + } + catch(Exception e) + { + MelonLoader.MelonLogger.Error(e); + } + } + + static void OnIKSystemLateUpdate_Postfix(HumanPoseHandler ____humanPoseHandler) => ms_instance?.OnIKSystemLateUpdate(____humanPoseHandler); + void OnIKSystemLateUpdate(HumanPoseHandler p_handler) + { + try + { + m_fingerSystem?.OnIKSystemLateUpdate(p_handler); + } + catch(Exception e) + { + MelonLoader.MelonLogger.Error(e); + } + } + } +} diff --git a/ml_bft/Properties/AssemblyInfo.cs b/ml_bft/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..5d86f38 --- /dev/null +++ b/ml_bft/Properties/AssemblyInfo.cs @@ -0,0 +1,4 @@ +[assembly: MelonLoader.MelonInfo(typeof(ml_bft.BetterFingersTracking), "BetterFingersTracking", "1.0.0", "SDraw", "https://github.com/SDraw/ml_mods_cvr")] +[assembly: MelonLoader.MelonGame(null, "ChilloutVR")] +[assembly: MelonLoader.MelonPlatform(MelonLoader.MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)] +[assembly: MelonLoader.MelonPlatformDomain(MelonLoader.MelonPlatformDomainAttribute.CompatibleDomains.MONO)] diff --git a/ml_bft/README.md b/ml_bft/README.md new file mode 100644 index 0000000..4530eff --- /dev/null +++ b/ml_bft/README.md @@ -0,0 +1,16 @@ +# Better Fingers Tracking +Mod that overhauls behaviour of fingers tracking. + +# Installation +* Install [latest MelonLoader](https://github.com/LavaGang/MelonLoader) +* Get [latest release DLL](../../../releases/latest): + * Put `ml_bft.dll` in `Mods` folder of game + +# Usage +Available mod's settings in `Settings - Input & Key-Bindings - Better Fingers Tracking`: +* **Force SteamVR skeletal input:** forced usage of SteamVR skeletal input (works as long as controllers' driver supplies skeletal pose throught OpenVR interfaces); `false` by default +* **Show hands model:** shows transparent hands model (mostly as debug option); `false` by default + +# Notes +* Currently supports only SteamVR environment, OpenXR support is planned. +* Fingers tracking quaity is highly dependant on avatar's hand state in Unity's T-pose, possible solutions are in search. diff --git a/ml_bft/ResourcesHandler.cs b/ml_bft/ResourcesHandler.cs new file mode 100644 index 0000000..1d444a2 --- /dev/null +++ b/ml_bft/ResourcesHandler.cs @@ -0,0 +1,26 @@ +using System; +using System.IO; +using System.Reflection; + +namespace ml_bft +{ + static class ResourcesHandler + { + public static string GetEmbeddedResource(string p_name) + { + string l_result = ""; + Assembly l_assembly = Assembly.GetExecutingAssembly(); + string l_assemblyName = l_assembly.GetName().Name; + + try + { + Stream l_libraryStream = l_assembly.GetManifestResourceStream(l_assemblyName + ".resources." + p_name); + StreamReader l_streadReader = new StreamReader(l_libraryStream); + l_result = l_streadReader.ReadToEnd(); + } + catch(Exception) { } + + return l_result; + } + } +} diff --git a/ml_bft/Settings.cs b/ml_bft/Settings.cs new file mode 100644 index 0000000..f1b7658 --- /dev/null +++ b/ml_bft/Settings.cs @@ -0,0 +1,87 @@ +using ABI_RC.Core.InteractionSystem; +using System; +using System.Collections.Generic; + +namespace ml_bft +{ + static class Settings + { + enum ModSetting + { + SkeletalInput = 0, + ShowHands + } + + public static bool SkeletalInput { get; private set; } = false; + public static bool ShowHands { get; private set; } = false; + + static MelonLoader.MelonPreferences_Category ms_category = null; + static List ms_entries = null; + + static public event Action SkeletalInputChange; + static public event Action ShowHandsChange; + + internal static void Init() + { + ms_category = MelonLoader.MelonPreferences.CreateCategory("BFT", null, true); + + ms_entries = new List() + { + ms_category.CreateEntry(ModSetting.SkeletalInput.ToString(), SkeletalInput), + ms_category.CreateEntry(ModSetting.ShowHands.ToString(), ShowHands) + }; + + SkeletalInput = (bool)ms_entries[(int)ModSetting.SkeletalInput].BoxedValue; + ShowHands = (bool)ms_entries[(int)ModSetting.ShowHands].BoxedValue; + + MelonLoader.MelonCoroutines.Start(WaitMainMenuUi()); + } + + static System.Collections.IEnumerator WaitMainMenuUi() + { + while(ViewManager.Instance == null) + yield return null; + while(ViewManager.Instance.gameMenuView == null) + yield return null; + while(ViewManager.Instance.gameMenuView.Listener == null) + yield return null; + + ViewManager.Instance.gameMenuView.Listener.ReadyForBindings += () => + { + ViewManager.Instance.gameMenuView.View.BindCall("OnToggleUpdate_" + ms_category.Identifier, new Action(OnToggleUpdate)); + }; + ViewManager.Instance.gameMenuView.Listener.FinishLoad += (_) => + { + ViewManager.Instance.gameMenuView.View.ExecuteScript(ResourcesHandler.GetEmbeddedResource("mods_extension.js")); + ViewManager.Instance.gameMenuView.View.ExecuteScript(ResourcesHandler.GetEmbeddedResource("mod_menu.js")); + foreach(var l_entry in ms_entries) + ViewManager.Instance.gameMenuView.View.TriggerEvent("updateModSetting", ms_category.Identifier, l_entry.DisplayName, l_entry.GetValueAsString()); + }; + } + + static void OnToggleUpdate(string p_name, string p_value) + { + if(Enum.TryParse(p_name, out ModSetting l_setting)) + { + switch(l_setting) + { + case ModSetting.SkeletalInput: + { + SkeletalInput = bool.Parse(p_value); + SkeletalInputChange?.Invoke(SkeletalInput); + } + break; + + case ModSetting.ShowHands: + { + ShowHands = bool.Parse(p_value); + ShowHandsChange?.Invoke(ShowHands); + } + break; + } + + ms_entries[(int)l_setting].BoxedValue = bool.Parse(p_value); + } + } + } +} diff --git a/ml_bft/Utils.cs b/ml_bft/Utils.cs new file mode 100644 index 0000000..6026364 --- /dev/null +++ b/ml_bft/Utils.cs @@ -0,0 +1,15 @@ +using ABI_RC.Core.UI; +using ABI_RC.Systems.InputManagement; +using System.Reflection; + +namespace ml_bft +{ + static class Utils + { + static readonly FieldInfo ms_view = typeof(CohtmlControlledViewWrapper).GetField("_view", BindingFlags.NonPublic | BindingFlags.Instance); + + static public void ExecuteScript(this CohtmlControlledViewWrapper p_instance, string p_script) => ((cohtml.Net.View)ms_view.GetValue(p_instance)).ExecuteScript(p_script); + + public static bool AreKnucklesInUse() => ((CVRInputManager.Instance._leftController == ABI_RC.Systems.InputManagement.XR.eXRControllerType.Index) || (CVRInputManager.Instance._rightController == ABI_RC.Systems.InputManagement.XR.eXRControllerType.Index)); + } +} diff --git a/ml_bft/ml_bft.csproj b/ml_bft/ml_bft.csproj new file mode 100644 index 0000000..eb82897 --- /dev/null +++ b/ml_bft/ml_bft.csproj @@ -0,0 +1,102 @@ + + + + netstandard2.1 + x64 + BetterFingersTracking + SDraw + None + BetterFingersTracking + + + + embedded + true + + + + + + + + + + + + + + + + + + + + + + + D:\games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\0Harmony.dll + false + false + + + D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Assembly-CSharp.dll + false + false + + + D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\cohtml.Net.dll + false + false + + + D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Cohtml.Runtime.dll + false + false + + + D:\games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\MelonLoader.dll + false + false + + + D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\SteamVR.dll + false + false + + + D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Unity.XR.Hands.dll + false + false + + + D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Unity.XR.OpenXR.dll + false + false + + + D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.AnimationModule.dll + false + false + + + D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.AssetBundleModule.dll + false + false + + + D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.CoreModule.dll + false + false + + + D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.XRModule.dll + false + false + + + + + + + + diff --git a/ml_bft/resources/mod_menu.js b/ml_bft/resources/mod_menu.js new file mode 100644 index 0000000..20c391f --- /dev/null +++ b/ml_bft/resources/mod_menu.js @@ -0,0 +1,28 @@ +{ + let l_block = document.createElement('div'); + l_block.innerHTML = ` +
+
Better Finger Tracking
+
+
+ +
+
Force SteamVR skeletal input:
+
+
+
+
+ +
+
Show hands model:
+
+
+
+
+ `; + document.getElementById('settings-input').appendChild(l_block); + + // Toggles + for (let l_toggle of l_block.querySelectorAll('.inp_toggle')) + modsExtension.addSetting('BFT', l_toggle.id, modsExtension.createToggle(l_toggle, 'OnToggleUpdate_BFT')); +} diff --git a/ml_bft/resources/ovr_fingers.asset b/ml_bft/resources/ovr_fingers.asset new file mode 100644 index 0000000..73cec84 Binary files /dev/null and b/ml_bft/resources/ovr_fingers.asset differ diff --git a/ml_bft/resources/oxr_fingers.asset b/ml_bft/resources/oxr_fingers.asset new file mode 100644 index 0000000..f4cd5b7 Binary files /dev/null and b/ml_bft/resources/oxr_fingers.asset differ diff --git a/ml_dht/ml_dht.csproj b/ml_dht/ml_dht.csproj index bef7127..cc35d26 100644 --- a/ml_dht/ml_dht.csproj +++ b/ml_dht/ml_dht.csproj @@ -12,8 +12,8 @@ x64 - none - false + embedded + true diff --git a/ml_lme/LeapInput.cs b/ml_lme/LeapInput.cs index 8a31b3b..90d30d7 100644 --- a/ml_lme/LeapInput.cs +++ b/ml_lme/LeapInput.cs @@ -538,29 +538,29 @@ namespace ml_lme { if(p_left) { - base._inputManager.finger1StretchedLeftThumb = -0.5f; - base._inputManager.finger2StretchedLeftThumb = 0.7f; - base._inputManager.finger3StretchedLeftThumb = 0.7f; + base._inputManager.finger1StretchedLeftThumb = 0f; + base._inputManager.finger2StretchedLeftThumb = 0f; + base._inputManager.finger3StretchedLeftThumb = 0f; base._inputManager.fingerSpreadLeftThumb = 0f; - base._inputManager.finger1StretchedLeftIndex = 0.5f; - base._inputManager.finger2StretchedLeftIndex = 0.7f; - base._inputManager.finger3StretchedLeftIndex = 0.7f; + base._inputManager.finger1StretchedLeftIndex = 0f; + base._inputManager.finger2StretchedLeftIndex =0f; + base._inputManager.finger3StretchedLeftIndex = 0f; base._inputManager.fingerSpreadLeftIndex = 0f; - base._inputManager.finger1StretchedLeftMiddle = 0.5f; - base._inputManager.finger2StretchedLeftMiddle = 0.7f; - base._inputManager.finger3StretchedLeftMiddle = 0.7f; + base._inputManager.finger1StretchedLeftMiddle = 0f; + base._inputManager.finger2StretchedLeftMiddle = 0f; + base._inputManager.finger3StretchedLeftMiddle = 0f; base._inputManager.fingerSpreadLeftMiddle = 0f; - base._inputManager.finger1StretchedLeftRing = 0.5f; - base._inputManager.finger2StretchedLeftRing = 0.7f; - base._inputManager.finger3StretchedLeftRing = 0.7f; + base._inputManager.finger1StretchedLeftRing = 0f; + base._inputManager.finger2StretchedLeftRing = 0f; + base._inputManager.finger3StretchedLeftRing = 0f; base._inputManager.fingerSpreadLeftRing = 0f; - base._inputManager.finger1StretchedLeftPinky = 0.5f; - base._inputManager.finger2StretchedLeftPinky = 0.7f; - base._inputManager.finger3StretchedLeftPinky = 0.7f; + base._inputManager.finger1StretchedLeftPinky = 0f; + base._inputManager.finger2StretchedLeftPinky = 0f; + base._inputManager.finger3StretchedLeftPinky = 0f; base._inputManager.fingerSpreadLeftPinky = 0f; base._inputManager.fingerFullCurlNormalizedLeftThumb = 0f; @@ -571,29 +571,29 @@ namespace ml_lme } else { - base._inputManager.finger1StretchedRightThumb = -0.5f; - base._inputManager.finger2StretchedRightThumb = 0.7f; - base._inputManager.finger3StretchedRightThumb = 0.7f; + base._inputManager.finger1StretchedRightThumb = 0f; + base._inputManager.finger2StretchedRightThumb = 0f; + base._inputManager.finger3StretchedRightThumb = 0f; base._inputManager.fingerSpreadRightThumb = 0f; - base._inputManager.finger1StretchedRightIndex = 0.5f; - base._inputManager.finger2StretchedRightIndex = 0.7f; - base._inputManager.finger3StretchedRightIndex = 0.7f; + base._inputManager.finger1StretchedRightIndex = 0f; + base._inputManager.finger2StretchedRightIndex = 0f; + base._inputManager.finger3StretchedRightIndex = 0f; base._inputManager.fingerSpreadRightIndex = 0f; - base._inputManager.finger1StretchedRightMiddle = 0.5f; - base._inputManager.finger2StretchedRightMiddle = 0.7f; - base._inputManager.finger3StretchedRightMiddle = 0.7f; + base._inputManager.finger1StretchedRightMiddle = 0f; + base._inputManager.finger2StretchedRightMiddle = 0f; + base._inputManager.finger3StretchedRightMiddle = 0f; base._inputManager.fingerSpreadRightMiddle = 0f; - base._inputManager.finger1StretchedRightRing = 0.5f; - base._inputManager.finger2StretchedRightRing = 0.7f; - base._inputManager.finger3StretchedRightRing = 0.7f; + base._inputManager.finger1StretchedRightRing = 0f; + base._inputManager.finger2StretchedRightRing = 0f; + base._inputManager.finger3StretchedRightRing = 0f; base._inputManager.fingerSpreadRightRing = 0f; - base._inputManager.finger1StretchedRightPinky = 0.5f; - base._inputManager.finger2StretchedRightPinky = 0.7f; - base._inputManager.finger3StretchedRightPinky = 0.7f; + base._inputManager.finger1StretchedRightPinky = 0f; + base._inputManager.finger2StretchedRightPinky = 0f; + base._inputManager.finger3StretchedRightPinky = 0f; base._inputManager.fingerSpreadRightPinky = 0f; base._inputManager.fingerFullCurlNormalizedRightThumb = 0f; diff --git a/ml_mods_cvr.sln b/ml_mods_cvr.sln index 573d2e1..52c57a2 100644 --- a/ml_mods_cvr.sln +++ b/ml_mods_cvr.sln @@ -24,6 +24,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ml_pin", "ml_pin\ml_pin.csp EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ml_dht", "ml_dht\ml_dht.csproj", "{31987392-989C-40C1-A48B-7F6099816EBE}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ml_bft", "ml_bft\ml_bft.csproj", "{331C995D-9648-44AD-8B02-D5F3A89FDC1F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -60,6 +62,9 @@ Global {31987392-989C-40C1-A48B-7F6099816EBE}.Debug|x64.Build.0 = Debug|x64 {31987392-989C-40C1-A48B-7F6099816EBE}.Release|x64.ActiveCfg = Release|x64 {31987392-989C-40C1-A48B-7F6099816EBE}.Release|x64.Build.0 = Release|x64 + {331C995D-9648-44AD-8B02-D5F3A89FDC1F}.Debug|x64.ActiveCfg = Debug|x64 + {331C995D-9648-44AD-8B02-D5F3A89FDC1F}.Release|x64.ActiveCfg = Release|x64 + {331C995D-9648-44AD-8B02-D5F3A89FDC1F}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/ml_pam/ml_pam.csproj b/ml_pam/ml_pam.csproj index 37228e9..1decd72 100644 --- a/ml_pam/ml_pam.csproj +++ b/ml_pam/ml_pam.csproj @@ -11,8 +11,8 @@ - none - false + embedded + true diff --git a/ml_pin/ml_pin.csproj b/ml_pin/ml_pin.csproj index baa0a25..bbb4d30 100644 --- a/ml_pin/ml_pin.csproj +++ b/ml_pin/ml_pin.csproj @@ -10,6 +10,11 @@ 1.0.2 + + embedded + true + + diff --git a/ml_pmc/ml_pmc.csproj b/ml_pmc/ml_pmc.csproj index 75e32d0..75ac2cb 100644 --- a/ml_pmc/ml_pmc.csproj +++ b/ml_pmc/ml_pmc.csproj @@ -10,6 +10,11 @@ 1.0.5 + + embedded + true + + diff --git a/ml_prm/ml_prm.csproj b/ml_prm/ml_prm.csproj index e84b7ac..9b23b10 100644 --- a/ml_prm/ml_prm.csproj +++ b/ml_prm/ml_prm.csproj @@ -10,6 +10,11 @@ PlayerRagdollMod + + embedded + true + + diff --git a/ml_vei/ml_vei.csproj b/ml_vei/ml_vei.csproj index cb12959..f1eb3b8 100644 --- a/ml_vei/ml_vei.csproj +++ b/ml_vei/ml_vei.csproj @@ -10,6 +10,11 @@ ViveExtendedInput + + embedded + true + +