diff --git a/README.md b/README.md
index b0180ed..5c860ea 100644
--- a/README.md
+++ b/README.md
@@ -3,16 +3,16 @@ Merged set of MelonLoader mods for ChilloutVR.
**Table for game build 2023r173:**
| Full name | Short name | Latest version | Available in [CVRMA](https://github.com/knah/CVRMelonAssistant) |
|:---------:|:----------:|:--------------:| :----------------------------------------------------------------|
-| [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 |
-| [Player Movement Copycat](/ml_pmc/README.md)| ml_pmc | 1.0.5 [:arrow_down:](../../releases/latest/download/ml_pmc.dll)| ✔ Yes |
-| [Player Ragdoll Mod](/ml_prm/README.md) | ml_prm | 1.1.3 [:arrow_down:](../../releases/latest/download/ml_prm.dll)| ✔ Yes |
-| [Players Instance Notifier](/ml_pin/README.md) | ml_pin | 1.0.2 [:arrow_down:](../../releases/latest/download/ml_ml_pin.dll)| ✔ Yes |
-| [Vive Extended Input](/ml_vei/README.md) | ml_vei | 1.0.1 [:arrow_down:](../../releases/latest/download/ml_vei.dll)| ✔ Yes |
+| [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)| Yes
Update 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.7 [:arrow_down:](../../releases/latest/download/ml_lme.dll)| Yes
Update review |
+| [Pickup Arm Movement](/ml_pam/README.md)| ml_pam | 1.1.0 [:arrow_down:](../../releases/latest/download/ml_pam.dll)| Yes |
+| [Player Movement Copycat](/ml_pmc/README.md)| ml_pmc | 1.0.5 [:arrow_down:](../../releases/latest/download/ml_pmc.dll)| Yes |
+| [Player Ragdoll Mod](/ml_prm/README.md) | ml_prm | 1.1.3 [:arrow_down:](../../releases/latest/download/ml_prm.dll)| Yes |
+| [Players Instance Notifier](/ml_pin/README.md) | ml_pin | 1.0.2 [:arrow_down:](../../releases/latest/download/ml_ml_pin.dll)| Yes |
+| [Vive Extended Input](/ml_vei/README.md) | ml_vei | 1.0.1 [:arrow_down:](../../releases/latest/download/ml_vei.dll)| Yes |
**Archived mods:**
| Full name | Short name | Notes |
diff --git a/ml_bft/FingerSystem.cs b/ml_bft/FingerSystem.cs
index 32a0a47..9bc903c 100644
--- a/ml_bft/FingerSystem.cs
+++ b/ml_bft/FingerSystem.cs
@@ -9,14 +9,27 @@ namespace ml_bft
{
class FingerSystem
{
+ enum PlaneType
+ {
+ OXZ,
+ OYX
+ }
+
struct RotationOffset
{
public Transform m_target;
public Transform m_source;
public Quaternion m_offset;
+
+ public void Reset()
+ {
+ m_source = null;
+ m_target = null;
+ m_offset = Quaternion.identity;
+ }
}
- static readonly List ms_leftFingerBones = new List()
+ static readonly HumanBodyBones[] ms_leftFingerBones =
{
HumanBodyBones.LeftThumbProximal, HumanBodyBones.LeftThumbIntermediate, HumanBodyBones.LeftThumbDistal,
HumanBodyBones.LeftIndexProximal, HumanBodyBones.LeftIndexIntermediate, HumanBodyBones.LeftIndexDistal,
@@ -24,7 +37,7 @@ namespace ml_bft
HumanBodyBones.LeftRingProximal, HumanBodyBones.LeftRingIntermediate, HumanBodyBones.LeftRingDistal,
HumanBodyBones.LeftLittleProximal, HumanBodyBones.LeftLittleIntermediate, HumanBodyBones.LeftLittleDistal
};
- static readonly List ms_rightFingerBones = new List()
+ static readonly HumanBodyBones[] ms_rightFingerBones =
{
HumanBodyBones.RightThumbProximal, HumanBodyBones.RightThumbIntermediate, HumanBodyBones.RightThumbDistal,
HumanBodyBones.RightIndexProximal, HumanBodyBones.RightIndexIntermediate, HumanBodyBones.RightIndexDistal,
@@ -32,6 +45,19 @@ namespace ml_bft
HumanBodyBones.RightRingProximal, HumanBodyBones.RightRingIntermediate, HumanBodyBones.RightRingDistal,
HumanBodyBones.RightLittleProximal, HumanBodyBones.RightLittleIntermediate, HumanBodyBones.RightLittleDistal
};
+ static readonly (HumanBodyBones, HumanBodyBones, bool)[] ms_rotationFixChains =
+ {
+ (HumanBodyBones.LeftThumbProximal,HumanBodyBones.LeftThumbIntermediate,true), (HumanBodyBones.LeftThumbIntermediate, HumanBodyBones.LeftThumbDistal,true),
+ (HumanBodyBones.LeftIndexProximal,HumanBodyBones.LeftIndexIntermediate,true), (HumanBodyBones.LeftIndexIntermediate, HumanBodyBones.LeftIndexDistal,true),
+ (HumanBodyBones.LeftMiddleProximal,HumanBodyBones.LeftMiddleIntermediate,true), (HumanBodyBones.LeftMiddleIntermediate, HumanBodyBones.LeftMiddleDistal,true),
+ (HumanBodyBones.LeftRingProximal,HumanBodyBones.LeftRingIntermediate,true), (HumanBodyBones.LeftRingIntermediate, HumanBodyBones.LeftRingDistal,true),
+ (HumanBodyBones.LeftLittleProximal,HumanBodyBones.LeftLittleIntermediate,true), (HumanBodyBones.LeftLittleIntermediate, HumanBodyBones.LeftLittleDistal,true),
+ (HumanBodyBones.RightThumbProximal,HumanBodyBones.RightThumbIntermediate,false), (HumanBodyBones.RightThumbIntermediate, HumanBodyBones.RightThumbDistal,false),
+ (HumanBodyBones.RightIndexProximal,HumanBodyBones.RightIndexIntermediate,false), (HumanBodyBones.RightIndexIntermediate, HumanBodyBones.RightIndexDistal,false),
+ (HumanBodyBones.RightMiddleProximal,HumanBodyBones.RightMiddleIntermediate,false), (HumanBodyBones.RightMiddleIntermediate, HumanBodyBones.RightMiddleDistal,false),
+ (HumanBodyBones.RightRingProximal,HumanBodyBones.RightRingIntermediate,false), (HumanBodyBones.RightRingIntermediate, HumanBodyBones.RightRingDistal,false),
+ (HumanBodyBones.RightLittleProximal,HumanBodyBones.RightLittleIntermediate,false), (HumanBodyBones.RightLittleIntermediate, HumanBodyBones.RightLittleDistal,false)
+ };
public static FingerSystem Instance { get; private set; } = null;
@@ -71,22 +97,43 @@ namespace ml_bft
if(PlayerSetup.Instance._animator.isHuman)
{
Utils.SetAvatarTPose();
- InputHandler.Instance?.Rebind(PlayerSetup.Instance.transform.rotation);
+ InputHandler.Instance.Rebind(PlayerSetup.Instance.transform.rotation);
+ // Try to "fix" rotations of fingers
+ foreach(var l_tuple in ms_rotationFixChains)
+ {
+ ReorientateTowards(
+ PlayerSetup.Instance._animator.GetBoneTransform(l_tuple.Item1),
+ PlayerSetup.Instance._animator.GetBoneTransform(l_tuple.Item2),
+ InputHandler.Instance.GetSourceForBone(l_tuple.Item1, l_tuple.Item3),
+ InputHandler.Instance.GetSourceForBone(l_tuple.Item2, l_tuple.Item3),
+ PlaneType.OXZ
+ );
+ ReorientateTowards(
+ PlayerSetup.Instance._animator.GetBoneTransform(l_tuple.Item1),
+ PlayerSetup.Instance._animator.GetBoneTransform(l_tuple.Item2),
+ InputHandler.Instance.GetSourceForBone(l_tuple.Item1, l_tuple.Item3),
+ InputHandler.Instance.GetSourceForBone(l_tuple.Item2, l_tuple.Item3),
+ PlaneType.OYX
+ );
+ }
+
+ // Bind hands
m_leftHandOffset.m_source = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.LeftHand);
- m_leftHandOffset.m_target = InputHandler.Instance?.GetSourceForBone(HumanBodyBones.LeftHand, true);
+ 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);
+ 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;
+ // Bind fingers
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);
+ Transform l_controllerBone = InputHandler.Instance.GetSourceForBone(p_bone, true);
if((l_avatarBone != null) && (l_controllerBone != null))
{
RotationOffset l_offset = new RotationOffset();
@@ -96,11 +143,10 @@ namespace ml_bft
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);
+ Transform l_controllerBone = InputHandler.Instance.GetSourceForBone(p_bone, false);
if((l_avatarBone != null) && (l_controllerBone != null))
{
RotationOffset l_offset = new RotationOffset();
@@ -119,6 +165,10 @@ namespace ml_bft
{
m_ready = false;
m_pose = new HumanPose();
+
+ m_leftHandOffset.Reset();
+ m_rightHandOffset.Reset();
+
m_leftFingerOffsets.Clear();
m_rightFingerOffsets.Clear();
}
@@ -199,5 +249,49 @@ namespace ml_bft
}
}
}
+
+ void ReorientateTowards(Transform p_target, Transform p_targetEnd, Transform p_source, Transform p_sourceEnd, PlaneType p_plane)
+ {
+ if((p_target != null) && (p_targetEnd != null) && (p_source != null) && (p_sourceEnd != null))
+ {
+ Quaternion l_playerInv = Quaternion.Inverse(PlayerSetup.Instance.transform.rotation);
+ Vector3 l_targetDir = l_playerInv * (p_targetEnd.position - p_target.position);
+ Vector3 l_sourceDir = l_playerInv * (p_sourceEnd.position - p_source.position);
+ switch(p_plane)
+ {
+ case PlaneType.OXZ:
+ l_targetDir.y = 0f;
+ l_sourceDir.y = 0f;
+ break;
+ case PlaneType.OYX:
+ l_targetDir.z = 0f;
+ l_sourceDir.z = 0f;
+ break;
+ }
+ l_targetDir = Vector3.Normalize(l_targetDir);
+ l_sourceDir = Vector3.Normalize(l_sourceDir);
+
+ Quaternion l_targetRot = Quaternion.identity;
+ Quaternion l_sourceRot = Quaternion.identity;
+ switch(p_plane)
+ {
+ case PlaneType.OXZ:
+ l_targetRot = Quaternion.LookRotation(l_targetDir, Vector3.up);
+ l_sourceRot = Quaternion.LookRotation(l_sourceDir, Vector3.up);
+ break;
+ case PlaneType.OYX:
+ l_targetRot = Quaternion.LookRotation(l_targetDir, Vector3.forward);
+ l_sourceRot = Quaternion.LookRotation(l_sourceDir, Vector3.forward);
+ break;
+ }
+
+ Quaternion l_diff = Quaternion.Inverse(l_targetRot) * l_sourceRot;
+ if(p_plane == PlaneType.OYX)
+ l_diff = Quaternion.Euler(0f, 0f, l_diff.eulerAngles.y);
+
+ Quaternion l_adjusted = l_diff * (l_playerInv * p_target.rotation);
+ p_target.rotation = PlayerSetup.Instance.transform.rotation * l_adjusted;
+ }
+ }
}
}
diff --git a/ml_bft/HandHandler.cs b/ml_bft/HandHandler.cs
index 703279d..8a0ce54 100644
--- a/ml_bft/HandHandler.cs
+++ b/ml_bft/HandHandler.cs
@@ -30,7 +30,7 @@ namespace ml_bft
m_bones.Clear();
m_localRotations.Clear();
m_renderers.Clear();
-
+
Settings.ShowHandsChange -= this.OnShowHandsChange;
}
diff --git a/ml_bft/HandHandlerVR.cs b/ml_bft/HandHandlerVR.cs
index 341fcba..bff8246 100644
--- a/ml_bft/HandHandlerVR.cs
+++ b/ml_bft/HandHandlerVR.cs
@@ -110,121 +110,113 @@ namespace ml_bft
public override Transform GetSourceForBone(HumanBodyBones p_bone)
{
Transform l_result = null;
- if(m_left)
+ switch(p_bone)
{
- 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.LeftHand:
+ l_result = (m_left ? m_bones[(int)SteamVR_Skeleton_JointIndexEnum.wrist] : null);
+ break;
+ case HumanBodyBones.LeftThumbProximal:
+ l_result = (m_left ? m_bones[(int)SteamVR_Skeleton_JointIndexEnum.thumbProximal] : null);
+ break;
+ case HumanBodyBones.LeftThumbIntermediate:
+ l_result = (m_left ? m_bones[(int)SteamVR_Skeleton_JointIndexEnum.thumbMiddle] : null);
+ break;
+ case HumanBodyBones.LeftThumbDistal:
+ l_result = (m_left ? m_bones[(int)SteamVR_Skeleton_JointIndexEnum.thumbDistal] : null);
+ 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.LeftIndexProximal:
+ l_result = (m_left ? m_bones[(int)SteamVR_Skeleton_JointIndexEnum.indexProximal] : null);
+ break;
+ case HumanBodyBones.LeftIndexIntermediate:
+ l_result = (m_left ? m_bones[(int)SteamVR_Skeleton_JointIndexEnum.indexMiddle] : null);
+ break;
+ case HumanBodyBones.LeftIndexDistal:
+ l_result = (m_left ? m_bones[(int)SteamVR_Skeleton_JointIndexEnum.indexDistal] : null);
+ 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.LeftMiddleProximal:
+ l_result = (m_left ? m_bones[(int)SteamVR_Skeleton_JointIndexEnum.middleProximal] : null);
+ break;
+ case HumanBodyBones.LeftMiddleIntermediate:
+ l_result = (m_left ? m_bones[(int)SteamVR_Skeleton_JointIndexEnum.middleMiddle] : null);
+ break;
+ case HumanBodyBones.LeftMiddleDistal:
+ l_result = (m_left ? m_bones[(int)SteamVR_Skeleton_JointIndexEnum.middleDistal] : null);
+ 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.LeftRingProximal:
+ l_result = (m_left ? m_bones[(int)SteamVR_Skeleton_JointIndexEnum.ringProximal] : null);
+ break;
+ case HumanBodyBones.LeftRingIntermediate:
+ l_result = (m_left ? m_bones[(int)SteamVR_Skeleton_JointIndexEnum.ringMiddle] : null);
+ break;
+ case HumanBodyBones.LeftRingDistal:
+ l_result = (m_left ? m_bones[(int)SteamVR_Skeleton_JointIndexEnum.ringDistal] : null);
+ 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.LeftLittleProximal:
+ l_result = (m_left ? m_bones[(int)SteamVR_Skeleton_JointIndexEnum.pinkyProximal] : null);
+ break;
+ case HumanBodyBones.LeftLittleIntermediate:
+ l_result = (m_left ? m_bones[(int)SteamVR_Skeleton_JointIndexEnum.pinkyMiddle] : null);
+ break;
+ case HumanBodyBones.LeftLittleDistal:
+ l_result = (m_left ? m_bones[(int)SteamVR_Skeleton_JointIndexEnum.pinkyDistal] : null);
+ 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.RightHand:
+ l_result = (!m_left ? m_bones[(int)SteamVR_Skeleton_JointIndexEnum.wrist] : null);
+ break;
+ case HumanBodyBones.RightThumbProximal:
+ l_result = (!m_left ? m_bones[(int)SteamVR_Skeleton_JointIndexEnum.thumbProximal] : null);
+ break;
+ case HumanBodyBones.RightThumbIntermediate:
+ l_result = (!m_left ? m_bones[(int)SteamVR_Skeleton_JointIndexEnum.thumbMiddle] : null);
+ break;
+ case HumanBodyBones.RightThumbDistal:
+ l_result = (!m_left ? m_bones[(int)SteamVR_Skeleton_JointIndexEnum.thumbDistal] : null);
+ 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.RightIndexProximal:
+ l_result = (!m_left ? m_bones[(int)SteamVR_Skeleton_JointIndexEnum.indexProximal] : null);
+ break;
+ case HumanBodyBones.RightIndexIntermediate:
+ l_result = (!m_left ? m_bones[(int)SteamVR_Skeleton_JointIndexEnum.indexMiddle] : null);
+ break;
+ case HumanBodyBones.RightIndexDistal:
+ l_result = (!m_left ? m_bones[(int)SteamVR_Skeleton_JointIndexEnum.indexDistal] : null);
+ 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.RightMiddleProximal:
+ l_result = (!m_left ? m_bones[(int)SteamVR_Skeleton_JointIndexEnum.middleProximal] : null);
+ break;
+ case HumanBodyBones.RightMiddleIntermediate:
+ l_result = (!m_left ? m_bones[(int)SteamVR_Skeleton_JointIndexEnum.middleMiddle] : null);
+ break;
+ case HumanBodyBones.RightMiddleDistal:
+ l_result = (!m_left ? m_bones[(int)SteamVR_Skeleton_JointIndexEnum.middleDistal] : null);
+ 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;
- }
+ case HumanBodyBones.RightRingProximal:
+ l_result = (!m_left ? m_bones[(int)SteamVR_Skeleton_JointIndexEnum.ringProximal] : null);
+ break;
+ case HumanBodyBones.RightRingIntermediate:
+ l_result = (!m_left ? m_bones[(int)SteamVR_Skeleton_JointIndexEnum.ringMiddle] : null);
+ break;
+ case HumanBodyBones.RightRingDistal:
+ l_result = (!m_left ? m_bones[(int)SteamVR_Skeleton_JointIndexEnum.ringDistal] : null);
+ break;
+
+ case HumanBodyBones.RightLittleProximal:
+ l_result = (!m_left ? m_bones[(int)SteamVR_Skeleton_JointIndexEnum.pinkyProximal] : null);
+ break;
+ case HumanBodyBones.RightLittleIntermediate:
+ l_result = (!m_left ? m_bones[(int)SteamVR_Skeleton_JointIndexEnum.pinkyMiddle] : null);
+ break;
+ case HumanBodyBones.RightLittleDistal:
+ l_result = (!m_left ? m_bones[(int)SteamVR_Skeleton_JointIndexEnum.pinkyDistal] : null);
+ break;
}
return l_result;
}
diff --git a/ml_bft/HandHandlerXR.cs b/ml_bft/HandHandlerXR.cs
index 1ac278c..3f08692 100644
--- a/ml_bft/HandHandlerXR.cs
+++ b/ml_bft/HandHandlerXR.cs
@@ -200,13 +200,13 @@ namespace ml_bft
l_tracking.GetHandJoints(m_left ? HandTrackingFeature.Hand_Index.L : HandTrackingFeature.Hand_Index.R, out var l_positions, out var l_rotations, out _);
if(l_positions.Length >= c_fingerBonesCount)
{
- // Joints rotations are in global space, locations are in local space ... wth is wrong with OpenXR?
+ // Joints rotations are in global space, locations are in ... space??? ... wth is wrong with OpenXR?
Quaternion l_prefabRot = m_prefabRoot.rotation;
for(int i = 0; i < c_fingerBonesCount; i++)
{
if(m_bones[i] != null)
{
- m_bones[i].localPosition = l_positions[i];
+ //m_bones[i].localPosition = l_positions[i];
m_bones[i].rotation = l_prefabRot * (l_handInv * l_rotations[i]);
}
}
diff --git a/ml_bft/Properties/AssemblyInfo.cs b/ml_bft/Properties/AssemblyInfo.cs
index 5d86f38..e9dd526 100644
--- a/ml_bft/Properties/AssemblyInfo.cs
+++ b/ml_bft/Properties/AssemblyInfo.cs
@@ -1,4 +1,4 @@
-[assembly: MelonLoader.MelonInfo(typeof(ml_bft.BetterFingersTracking), "BetterFingersTracking", "1.0.0", "SDraw", "https://github.com/SDraw/ml_mods_cvr")]
+[assembly: MelonLoader.MelonInfo(typeof(ml_bft.BetterFingersTracking), "BetterFingersTracking", "1.0.1", "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
index 56e39d3..40de03a 100644
--- a/ml_bft/README.md
+++ b/ml_bft/README.md
@@ -10,7 +10,7 @@ Mod that overhauls behaviour of fingers tracking.
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
* **Motion range:** fingers tracking motion range/mode/type; `With controller` by default
-* **Filter humanoid limits:** Limits fingers rotations to be valid for Unity's Mechanim; `false` by default
+* **Filter humanoid limits:** Limits fingers rotations to be valid for Unity's Mechanim; `true` by default
* Note: Enabling this option ensures that visual representation of your fingers will be same for you and remote players, but it cancels out additional finger segments rotations that can be better visually in most cases.
* **Show hands model:** shows transparent hands model (mostly as debug option); `false` by default
diff --git a/ml_bft/Settings.cs b/ml_bft/Settings.cs
index 45c1c3d..c846166 100644
--- a/ml_bft/Settings.cs
+++ b/ml_bft/Settings.cs
@@ -22,7 +22,7 @@ namespace ml_bft
public static bool SkeletalInput { get; private set; } = false;
public static MotionRangeType MotionRange { get; private set; } = MotionRangeType.WithController;
public static bool ShowHands { get; private set; } = false;
- public static bool MechanimFilter { get; private set; } = false;
+ public static bool MechanimFilter { get; private set; } = true;
static MelonLoader.MelonPreferences_Category ms_category = null;
static List ms_entries = null;
diff --git a/ml_bft/ml_bft.csproj b/ml_bft/ml_bft.csproj
index b399ead..0869bde 100644
--- a/ml_bft/ml_bft.csproj
+++ b/ml_bft/ml_bft.csproj
@@ -7,6 +7,7 @@
SDraw
None
BetterFingersTracking
+ 1.0.1
diff --git a/ml_bft/resources/mod_menu.js b/ml_bft/resources/mod_menu.js
index a8eb618..e682f83 100644
--- a/ml_bft/resources/mod_menu.js
+++ b/ml_bft/resources/mod_menu.js
@@ -23,7 +23,7 @@
diff --git a/ml_lme/LeapHand.cs b/ml_lme/LeapHand.cs
index 2fd20c2..797f87b 100644
--- a/ml_lme/LeapHand.cs
+++ b/ml_lme/LeapHand.cs
@@ -4,7 +4,7 @@ namespace ml_lme
{
class LeapHand
{
- public enum FingerBone
+ enum FingerBone
{
ThumbMetacarpal = 0,
ThumbProximal,
@@ -25,9 +25,12 @@ namespace ml_lme
PinkyMetacarpal,
PinkyProximal,
PinkyIntermediate,
- PinkyDistal
+ PinkyDistal,
+
+ Count
};
+ readonly bool m_left = false;
readonly Transform m_root = null;
readonly Transform m_wrist = null;
readonly GameObject m_mesh = null;
@@ -36,14 +39,15 @@ namespace ml_lme
public LeapHand(Transform p_root, bool p_left)
{
- m_fingersBones = new Transform[20];
- m_initialRotations = new Quaternion[20];
+ m_left = p_left;
+ m_fingersBones = new Transform[(int)FingerBone.Count];
+ m_initialRotations = new Quaternion[(int)FingerBone.Count];
m_root = p_root;
if(m_root != null)
{
- m_mesh = m_root.Find(p_left ? "GenericHandL" : "GenericHandR")?.gameObject;
- m_wrist = m_root.Find(p_left ? "LeftHand/Wrist" : "RightHand/Wrist");
+ m_mesh = m_root.Find(m_left ? "GenericHandL" : "GenericHandR")?.gameObject;
+ m_wrist = m_root.Find(m_left ? "LeftHand/Wrist" : "RightHand/Wrist");
if(m_wrist != null)
{
m_fingersBones[0] = null; // Actual thumb-meta, look at Leap Motion docs, dummy, it's zero point
@@ -101,12 +105,14 @@ namespace ml_lme
}
}
- public void Reset()
+ public void Rebind(Quaternion p_base)
{
if(m_wrist != null)
{
m_wrist.localPosition = Vector3.zero;
m_wrist.localRotation = Quaternion.identity;
+
+ m_wrist.rotation = p_base * Quaternion.Euler(0f, m_left ? -90f : 90f, 0f);
}
for(int i = 0; i < 20; i++)
@@ -117,8 +123,111 @@ namespace ml_lme
}
public Transform GetRoot() => m_root;
- public Transform GetWrist() => m_wrist;
- public Transform GetFingersBone(FingerBone p_bone) => m_fingersBones[(int)p_bone];
+ public Transform GetBone(HumanBodyBones p_bone)
+ {
+ Transform l_result = null;
+ switch(p_bone)
+ {
+ case HumanBodyBones.LeftHand:
+ l_result = (m_left ? m_wrist : null);
+ break;
+ case HumanBodyBones.LeftThumbProximal:
+ l_result = (m_left ? m_fingersBones[(int)FingerBone.ThumbProximal] : null);
+ break;
+ case HumanBodyBones.LeftThumbIntermediate:
+ l_result = (m_left ? m_fingersBones[(int)FingerBone.ThumbIntermediate] : null);
+ break;
+ case HumanBodyBones.LeftThumbDistal:
+ l_result = (m_left ? m_fingersBones[(int)FingerBone.ThumbDistal] : null);
+ break;
+ case HumanBodyBones.LeftIndexProximal:
+ l_result = (m_left ? m_fingersBones[(int)FingerBone.IndexProximal] : null);
+ break;
+ case HumanBodyBones.LeftIndexIntermediate:
+ l_result = (m_left ? m_fingersBones[(int)FingerBone.IndexIntermediate] : null);
+ break;
+ case HumanBodyBones.LeftIndexDistal:
+ l_result = (m_left ? m_fingersBones[(int)FingerBone.IndexDistal] : null);
+ break;
+ case HumanBodyBones.LeftMiddleProximal:
+ l_result = (m_left ? m_fingersBones[(int)FingerBone.MiddleProximal] : null);
+ break;
+ case HumanBodyBones.LeftMiddleIntermediate:
+ l_result = (m_left ? m_fingersBones[(int)FingerBone.MiddleIntermediate] : null);
+ break;
+ case HumanBodyBones.LeftMiddleDistal:
+ l_result = (m_left ? m_fingersBones[(int)FingerBone.MiddleDistal] : null);
+ break;
+ case HumanBodyBones.LeftRingProximal:
+ l_result = (m_left ? m_fingersBones[(int)FingerBone.RingProximal] : null);
+ break;
+ case HumanBodyBones.LeftRingIntermediate:
+ l_result = (m_left ? m_fingersBones[(int)FingerBone.RingIntermediate] : null);
+ break;
+ case HumanBodyBones.LeftRingDistal:
+ l_result = (m_left ? m_fingersBones[(int)FingerBone.RingDistal] : null);
+ break;
+ case HumanBodyBones.LeftLittleProximal:
+ l_result = (m_left ? m_fingersBones[(int)FingerBone.PinkyProximal] : null);
+ break;
+ case HumanBodyBones.LeftLittleIntermediate:
+ l_result = (m_left ? m_fingersBones[(int)FingerBone.PinkyIntermediate] : null);
+ break;
+ case HumanBodyBones.LeftLittleDistal:
+ l_result = (m_left ? m_fingersBones[(int)FingerBone.PinkyDistal] : null);
+ break;
+
+ case HumanBodyBones.RightHand:
+ l_result = (!m_left ? m_wrist : null);
+ break;
+ case HumanBodyBones.RightThumbProximal:
+ l_result = (!m_left ? m_fingersBones[(int)FingerBone.ThumbProximal] : null);
+ break;
+ case HumanBodyBones.RightThumbIntermediate:
+ l_result = (!m_left ? m_fingersBones[(int)FingerBone.ThumbIntermediate] : null);
+ break;
+ case HumanBodyBones.RightThumbDistal:
+ l_result = (!m_left ? m_fingersBones[(int)FingerBone.ThumbDistal] : null);
+ break;
+ case HumanBodyBones.RightIndexProximal:
+ l_result = (!m_left ? m_fingersBones[(int)FingerBone.IndexProximal] : null);
+ break;
+ case HumanBodyBones.RightIndexIntermediate:
+ l_result = (!m_left ? m_fingersBones[(int)FingerBone.IndexIntermediate] : null);
+ break;
+ case HumanBodyBones.RightIndexDistal:
+ l_result = (!m_left ? m_fingersBones[(int)FingerBone.IndexDistal] : null);
+ break;
+ case HumanBodyBones.RightMiddleProximal:
+ l_result = (!m_left ? m_fingersBones[(int)FingerBone.MiddleProximal] : null);
+ break;
+ case HumanBodyBones.RightMiddleIntermediate:
+ l_result = (!m_left ? m_fingersBones[(int)FingerBone.MiddleIntermediate] : null);
+ break;
+ case HumanBodyBones.RightMiddleDistal:
+ l_result = (!m_left ? m_fingersBones[(int)FingerBone.MiddleDistal] : null);
+ break;
+ case HumanBodyBones.RightRingProximal:
+ l_result = (!m_left ? m_fingersBones[(int)FingerBone.RingProximal] : null);
+ break;
+ case HumanBodyBones.RightRingIntermediate:
+ l_result = (!m_left ? m_fingersBones[(int)FingerBone.RingIntermediate] : null);
+ break;
+ case HumanBodyBones.RightRingDistal:
+ l_result = (!m_left ? m_fingersBones[(int)FingerBone.RingDistal] : null);
+ break;
+ case HumanBodyBones.RightLittleProximal:
+ l_result = (!m_left ? m_fingersBones[(int)FingerBone.PinkyProximal] : null);
+ break;
+ case HumanBodyBones.RightLittleIntermediate:
+ l_result = (!m_left ? m_fingersBones[(int)FingerBone.PinkyIntermediate] : null);
+ break;
+ case HumanBodyBones.RightLittleDistal:
+ l_result = (!m_left ? m_fingersBones[(int)FingerBone.PinkyDistal] : null);
+ break;
+ }
+ return l_result;
+ }
public void SetMeshActive(bool p_state)
{
diff --git a/ml_lme/LeapInput.cs b/ml_lme/LeapInput.cs
index 90d30d7..a588d35 100644
--- a/ml_lme/LeapInput.cs
+++ b/ml_lme/LeapInput.cs
@@ -38,7 +38,7 @@ namespace ml_lme
m_handRayLeft.hand = true;
m_handRayLeft.generalMask = -269;
m_handRayLeft.isInteractionRay = true;
- m_handRayLeft.triggerGazeEvents = false;
+ //m_handRayLeft.triggerGazeEvents = false;
m_handRayLeft.holderRoot = m_handRayLeft.gameObject;
m_handRayLeft.attachmentDistance = 0f;
m_handRayLeft.uiMask = 32;
@@ -60,7 +60,7 @@ namespace ml_lme
m_handRayRight.hand = false;
m_handRayRight.generalMask = -269;
m_handRayRight.isInteractionRay = true;
- m_handRayRight.triggerGazeEvents = false;
+ //m_handRayRight.triggerGazeEvents = false;
m_handRayRight.holderRoot = m_handRayRight.gameObject;
m_handRayRight.attachmentDistance = 0f;
m_handRayRight.uiMask = 32;
diff --git a/ml_lme/LeapTracked.cs b/ml_lme/LeapTracked.cs
index 905a5b9..b410e22 100644
--- a/ml_lme/LeapTracked.cs
+++ b/ml_lme/LeapTracked.cs
@@ -10,6 +10,12 @@ namespace ml_lme
[DefaultExecutionOrder(999999)]
class LeapTracked : MonoBehaviour
{
+ enum PlaneType
+ {
+ OXZ,
+ OYX
+ }
+
struct IKInfo
{
public Vector4 m_armsWeights;
@@ -20,50 +26,69 @@ namespace ml_lme
public Transform m_rightElbowTarget;
}
- struct FingerBoneInfo
+ struct RotationOffset
{
- public LeapHand.FingerBone m_bone;
- public Transform m_targetBone;
- public Transform m_sourceBone;
+ public Transform m_target;
+ public Transform m_source;
public Quaternion m_offset;
+
+ public void Reset()
+ {
+ m_source = null;
+ m_target = null;
+ m_offset = Quaternion.identity;
+ }
}
static readonly Quaternion ms_offsetLeft = Quaternion.Euler(0f, 90f, 0f);
static readonly Quaternion ms_offsetRight = Quaternion.Euler(0f, 270f, 0f);
- static readonly (HumanBodyBones, LeapHand.FingerBone, bool)[] ms_fingerBonesLinks =
+ static readonly (HumanBodyBones, bool)[] ms_fingers =
{
- (HumanBodyBones.LeftThumbProximal, LeapHand.FingerBone.ThumbProximal, true),
- (HumanBodyBones.LeftThumbIntermediate, LeapHand.FingerBone.ThumbIntermediate, true),
- (HumanBodyBones.LeftThumbDistal, LeapHand.FingerBone.ThumbDistal, true),
- (HumanBodyBones.LeftIndexProximal, LeapHand.FingerBone.IndexProximal, true),
- (HumanBodyBones.LeftIndexIntermediate, LeapHand.FingerBone.IndexIntermediate, true),
- (HumanBodyBones.LeftIndexDistal, LeapHand.FingerBone.IndexDistal, true),
- (HumanBodyBones.LeftMiddleProximal, LeapHand.FingerBone.MiddleProximal, true),
- (HumanBodyBones.LeftMiddleIntermediate, LeapHand.FingerBone.MiddleIntermediate, true),
- (HumanBodyBones.LeftMiddleDistal, LeapHand.FingerBone.MiddleDistal, true),
- (HumanBodyBones.LeftRingProximal, LeapHand.FingerBone.RingProximal, true),
- (HumanBodyBones.LeftRingIntermediate, LeapHand.FingerBone.RingIntermediate, true),
- (HumanBodyBones.LeftRingDistal, LeapHand.FingerBone.RingDistal, true),
- (HumanBodyBones.LeftLittleProximal, LeapHand.FingerBone.PinkyProximal, true),
- (HumanBodyBones.LeftLittleIntermediate, LeapHand.FingerBone.PinkyIntermediate, true),
- (HumanBodyBones.LeftLittleDistal, LeapHand.FingerBone.PinkyDistal, true),
+ (HumanBodyBones.LeftThumbProximal, true),
+ (HumanBodyBones.LeftThumbIntermediate, true),
+ (HumanBodyBones.LeftThumbDistal, true),
+ (HumanBodyBones.LeftIndexProximal, true),
+ (HumanBodyBones.LeftIndexIntermediate, true),
+ (HumanBodyBones.LeftIndexDistal, true),
+ (HumanBodyBones.LeftMiddleProximal, true),
+ (HumanBodyBones.LeftMiddleIntermediate, true),
+ (HumanBodyBones.LeftMiddleDistal, true),
+ (HumanBodyBones.LeftRingProximal, true),
+ (HumanBodyBones.LeftRingIntermediate, true),
+ (HumanBodyBones.LeftRingDistal, true),
+ (HumanBodyBones.LeftLittleProximal, true),
+ (HumanBodyBones.LeftLittleIntermediate, true),
+ (HumanBodyBones.LeftLittleDistal, true),
- (HumanBodyBones.RightThumbProximal, LeapHand.FingerBone.ThumbProximal, false),
- (HumanBodyBones.RightThumbIntermediate, LeapHand.FingerBone.ThumbIntermediate, false),
- (HumanBodyBones.RightThumbDistal, LeapHand.FingerBone.ThumbDistal, false),
- (HumanBodyBones.RightIndexProximal, LeapHand.FingerBone.IndexProximal, false),
- (HumanBodyBones.RightIndexIntermediate, LeapHand.FingerBone.IndexIntermediate, false),
- (HumanBodyBones.RightIndexDistal, LeapHand.FingerBone.IndexDistal, false),
- (HumanBodyBones.RightMiddleProximal, LeapHand.FingerBone.MiddleProximal, false),
- (HumanBodyBones.RightMiddleIntermediate, LeapHand.FingerBone.MiddleIntermediate, false),
- (HumanBodyBones.RightMiddleDistal, LeapHand.FingerBone.MiddleDistal, false),
- (HumanBodyBones.RightRingProximal, LeapHand.FingerBone.RingProximal, false),
- (HumanBodyBones.RightRingIntermediate, LeapHand.FingerBone.RingIntermediate, false),
- (HumanBodyBones.RightRingDistal, LeapHand.FingerBone.RingDistal, false),
- (HumanBodyBones.RightLittleProximal, LeapHand.FingerBone.PinkyProximal, false),
- (HumanBodyBones.RightLittleIntermediate, LeapHand.FingerBone.PinkyIntermediate, false),
- (HumanBodyBones.RightLittleDistal, LeapHand.FingerBone.PinkyDistal, false),
+ (HumanBodyBones.RightThumbProximal, false),
+ (HumanBodyBones.RightThumbIntermediate, false),
+ (HumanBodyBones.RightThumbDistal, false),
+ (HumanBodyBones.RightIndexProximal, false),
+ (HumanBodyBones.RightIndexIntermediate, false),
+ (HumanBodyBones.RightIndexDistal, false),
+ (HumanBodyBones.RightMiddleProximal, false),
+ (HumanBodyBones.RightMiddleIntermediate, false),
+ (HumanBodyBones.RightMiddleDistal, false),
+ (HumanBodyBones.RightRingProximal, false),
+ (HumanBodyBones.RightRingIntermediate, false),
+ (HumanBodyBones.RightRingDistal, false),
+ (HumanBodyBones.RightLittleProximal, false),
+ (HumanBodyBones.RightLittleIntermediate, false),
+ (HumanBodyBones.RightLittleDistal, false),
+ };
+ static readonly (HumanBodyBones, HumanBodyBones, bool)[] ms_rotationFixChains =
+ {
+ (HumanBodyBones.LeftThumbProximal,HumanBodyBones.LeftThumbIntermediate,true), (HumanBodyBones.LeftThumbIntermediate, HumanBodyBones.LeftThumbDistal,true),
+ (HumanBodyBones.LeftIndexProximal,HumanBodyBones.LeftIndexIntermediate,true), (HumanBodyBones.LeftIndexIntermediate, HumanBodyBones.LeftIndexDistal,true),
+ (HumanBodyBones.LeftMiddleProximal,HumanBodyBones.LeftMiddleIntermediate,true), (HumanBodyBones.LeftMiddleIntermediate, HumanBodyBones.LeftMiddleDistal,true),
+ (HumanBodyBones.LeftRingProximal,HumanBodyBones.LeftRingIntermediate,true), (HumanBodyBones.LeftRingIntermediate, HumanBodyBones.LeftRingDistal,true),
+ (HumanBodyBones.LeftLittleProximal,HumanBodyBones.LeftLittleIntermediate,true), (HumanBodyBones.LeftLittleIntermediate, HumanBodyBones.LeftLittleDistal,true),
+ (HumanBodyBones.RightThumbProximal,HumanBodyBones.RightThumbIntermediate,false), (HumanBodyBones.RightThumbIntermediate, HumanBodyBones.RightThumbDistal,false),
+ (HumanBodyBones.RightIndexProximal,HumanBodyBones.RightIndexIntermediate,false), (HumanBodyBones.RightIndexIntermediate, HumanBodyBones.RightIndexDistal,false),
+ (HumanBodyBones.RightMiddleProximal,HumanBodyBones.RightMiddleIntermediate,false), (HumanBodyBones.RightMiddleIntermediate, HumanBodyBones.RightMiddleDistal,false),
+ (HumanBodyBones.RightRingProximal,HumanBodyBones.RightRingIntermediate,false), (HumanBodyBones.RightRingIntermediate, HumanBodyBones.RightRingDistal,false),
+ (HumanBodyBones.RightLittleProximal,HumanBodyBones.RightLittleIntermediate,false), (HumanBodyBones.RightLittleIntermediate, HumanBodyBones.RightLittleDistal,false)
};
public static readonly float[] ms_lastLeftFingerBones = new float[20];
@@ -71,13 +96,12 @@ namespace ml_lme
bool m_inVR = false;
VRIK m_vrIK = null;
+ Transform m_hips = null;
bool m_enabled = true;
bool m_fingersOnly = false;
bool m_trackElbows = true;
- Transform m_leftHand = null;
- Transform m_rightHand = null;
IKInfo m_vrIKInfo;
ArmIK m_leftArmIK = null;
ArmIK m_rightArmIK = null;
@@ -88,16 +112,15 @@ namespace ml_lme
bool m_leftTargetActive = false; // VRIK only
bool m_rightTargetActive = false; // VRIK only
- readonly List m_leftFingerBones = null;
- readonly List m_rightFingerBones = null;
-
- Quaternion m_leftWristOffset;
- Quaternion m_rightWristOffset;
+ RotationOffset m_leftHandOffset; // From avatar hand to Leap wrist
+ RotationOffset m_rightHandOffset;
+ readonly List m_leftFingerOffsets = null; // From Leap finger bone to avatar finger bone
+ readonly List m_rightFingerOffsets = null;
internal LeapTracked()
{
- m_leftFingerBones = new List();
- m_rightFingerBones = new List();
+ m_leftFingerOffsets = new List();
+ m_rightFingerOffsets = new List();
}
// Unity events
@@ -180,17 +203,15 @@ namespace ml_lme
LeapParser.LeapData l_data = LeapManager.Instance.GetLatestData();
if(l_data.m_leftHand.m_present)
{
- Transform l_leapWrist = LeapTracking.Instance.GetLeftHand().GetWrist();
- Quaternion l_turnBack = (m_leftHand.rotation * m_leftWristOffset) * Quaternion.Inverse(l_leapWrist.rotation);
- foreach(var l_info in m_leftFingerBones)
- l_info.m_targetBone.rotation = l_turnBack * (l_info.m_sourceBone.rotation * l_info.m_offset);
+ Quaternion l_turnBack = (m_leftHandOffset.m_source.rotation * m_leftHandOffset.m_offset) * Quaternion.Inverse(m_leftHandOffset.m_target.rotation);
+ foreach(var l_info in m_leftFingerOffsets)
+ l_info.m_target.rotation = l_turnBack * (l_info.m_source.rotation * l_info.m_offset);
}
if(l_data.m_rightHand.m_present)
{
- Transform l_leapWrist = LeapTracking.Instance.GetRightHand().GetWrist();
- Quaternion l_turnBack = (m_rightHand.rotation * m_rightWristOffset) * Quaternion.Inverse(l_leapWrist.rotation);
- foreach(var l_info in m_rightFingerBones)
- l_info.m_targetBone.rotation = l_turnBack * (l_info.m_sourceBone.rotation * l_info.m_offset);
+ Quaternion l_turnBack = (m_rightHandOffset.m_source.rotation * m_rightHandOffset.m_offset) * Quaternion.Inverse(m_rightHandOffset.m_target.rotation);
+ foreach(var l_info in m_rightFingerOffsets)
+ l_info.m_target.rotation = l_turnBack * (l_info.m_source.rotation * l_info.m_offset);
}
m_poseHandler.GetHumanPose(ref m_pose);
@@ -211,6 +232,15 @@ namespace ml_lme
ms_lastRightFingerBones[l_offset + 3] = m_pose.muscles[(int)MuscleIndex.RightThumbSpread + l_offset];
}
}
+
+ if(Settings.MechanimFilter && (m_hips != null))
+ {
+ // Yoinked from IKSystem.OnPostSolverUpdateGeneral
+ Vector3 l_pos = m_hips.position;
+ Quaternion l_rot = m_hips.rotation;
+ m_poseHandler.SetHumanPose(ref m_pose);
+ m_hips.SetPositionAndRotation(l_pos, l_rot);
+ }
}
}
@@ -218,6 +248,7 @@ namespace ml_lme
internal void OnAvatarClear()
{
m_vrIK = null;
+ m_hips = null;
m_leftArmIK = null;
m_rightArmIK = null;
m_leftTargetActive = false;
@@ -231,13 +262,11 @@ namespace ml_lme
m_rightHandTarget.localPosition = Vector3.zero;
m_rightHandTarget.localRotation = Quaternion.identity;
- m_leftFingerBones.Clear();
- m_rightFingerBones.Clear();
+ m_leftHandOffset.Reset();
+ m_rightHandOffset.Reset();
- m_leftHand = null;
- m_rightHand = null;
- m_leftWristOffset = Quaternion.identity;
- m_rightWristOffset = Quaternion.identity;
+ m_leftFingerOffsets.Clear();
+ m_rightFingerOffsets.Clear();
}
internal void OnAvatarSetup()
@@ -251,11 +280,13 @@ namespace ml_lme
m_poseHandler = new HumanPoseHandler(PlayerSetup.Instance._animator.avatar, PlayerSetup.Instance._animator.transform);
m_poseHandler.GetHumanPose(ref m_pose);
- m_leftHand = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.LeftHand);
- m_leftHandTarget.localRotation = ms_offsetLeft * (Quaternion.Inverse(PlayerSetup.Instance._avatar.transform.rotation) * m_leftHand.rotation);
+ m_hips = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.Hips);
- m_rightHand = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.RightHand);
- m_rightHandTarget.localRotation = ms_offsetRight * (Quaternion.Inverse(PlayerSetup.Instance._avatar.transform.rotation) * m_rightHand.rotation);
+ m_leftHandOffset.m_source = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.LeftHand);
+ m_leftHandTarget.localRotation = ms_offsetLeft * (Quaternion.Inverse(PlayerSetup.Instance._avatar.transform.rotation) * m_leftHandOffset.m_source.rotation);
+
+ m_rightHandOffset.m_source = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.RightHand);
+ m_rightHandTarget.localRotation = ms_offsetRight * (Quaternion.Inverse(PlayerSetup.Instance._avatar.transform.rotation) * m_rightHandOffset.m_source.rotation);
ParseFingersBones();
@@ -441,31 +472,96 @@ namespace ml_lme
void ParseFingersBones()
{
- LeapTracking.Instance.GetLeftHand().Reset();
- LeapTracking.Instance.GetLeftHand().GetWrist().rotation = PlayerSetup.Instance.transform.rotation * ms_offsetRight; // Weird, but that's how it works
- m_leftWristOffset = Quaternion.Inverse(m_leftHand.rotation) * LeapTracking.Instance.GetLeftHand().GetWrist().rotation;
+ LeapTracking.Instance.Rebind(PlayerSetup.Instance.transform.rotation);
- LeapTracking.Instance.GetRightHand().Reset();
- LeapTracking.Instance.GetRightHand().GetWrist().rotation = PlayerSetup.Instance.transform.rotation * ms_offsetLeft; // Weird, but that's how it works
- m_rightWristOffset = Quaternion.Inverse(m_rightHand.rotation) * LeapTracking.Instance.GetRightHand().GetWrist().rotation;
+ // Try to "fix" rotations, slightly inaccurate after 0YX plane rotation
+ foreach(var l_tuple in ms_rotationFixChains)
+ {
+ ReorientateTowards(
+ PlayerSetup.Instance._animator.GetBoneTransform(l_tuple.Item1),
+ PlayerSetup.Instance._animator.GetBoneTransform(l_tuple.Item2),
+ l_tuple.Item3 ? LeapTracking.Instance.GetLeftHand().GetBone(l_tuple.Item1) : LeapTracking.Instance.GetRightHand().GetBone(l_tuple.Item1),
+ l_tuple.Item3 ? LeapTracking.Instance.GetLeftHand().GetBone(l_tuple.Item2) : LeapTracking.Instance.GetRightHand().GetBone(l_tuple.Item2),
+ PlaneType.OXZ
+ );
+ ReorientateTowards(
+ PlayerSetup.Instance._animator.GetBoneTransform(l_tuple.Item1),
+ PlayerSetup.Instance._animator.GetBoneTransform(l_tuple.Item2),
+ l_tuple.Item3 ? LeapTracking.Instance.GetLeftHand().GetBone(l_tuple.Item1) : LeapTracking.Instance.GetRightHand().GetBone(l_tuple.Item1),
+ l_tuple.Item3 ? LeapTracking.Instance.GetLeftHand().GetBone(l_tuple.Item2) : LeapTracking.Instance.GetRightHand().GetBone(l_tuple.Item2),
+ PlaneType.OYX
+ );
+ }
- foreach(var l_link in ms_fingerBonesLinks)
+ // Bind
+ m_leftHandOffset.m_target = LeapTracking.Instance.GetLeftHand().GetBone(HumanBodyBones.LeftHand);
+ 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_target = LeapTracking.Instance.GetRightHand().GetBone(HumanBodyBones.RightHand);
+ 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(var l_link in ms_fingers)
{
Transform l_transform = PlayerSetup.Instance._animator.GetBoneTransform(l_link.Item1);
if(l_transform != null)
{
- FingerBoneInfo l_info = new FingerBoneInfo();
- l_info.m_bone = l_link.Item2;
- l_info.m_targetBone = l_transform;
- l_info.m_sourceBone = (l_link.Item3 ? LeapTracking.Instance.GetLeftHand().GetFingersBone(l_link.Item2) : LeapTracking.Instance.GetRightHand().GetFingersBone(l_link.Item2));
- l_info.m_offset = Quaternion.Inverse(l_info.m_sourceBone.rotation) * l_info.m_targetBone.rotation;
+ RotationOffset l_offset = new RotationOffset();
+ l_offset.m_target = l_transform;
+ l_offset.m_source = (l_link.Item2 ? LeapTracking.Instance.GetLeftHand().GetBone(l_link.Item1) : LeapTracking.Instance.GetRightHand().GetBone(l_link.Item1));
+ l_offset.m_offset = Quaternion.Inverse(l_offset.m_source.rotation) * l_offset.m_target.rotation;
- if(l_link.Item3)
- m_leftFingerBones.Add(l_info);
+ if(l_link.Item2)
+ m_leftFingerOffsets.Add(l_offset);
else
- m_rightFingerBones.Add(l_info);
+ m_rightFingerOffsets.Add(l_offset);
}
}
}
+
+ void ReorientateTowards(Transform p_target, Transform p_targetEnd, Transform p_source, Transform p_sourceEnd, PlaneType p_plane)
+ {
+ if((p_target != null) && (p_targetEnd != null) && (p_source != null) && (p_sourceEnd != null))
+ {
+ Quaternion l_playerInv = Quaternion.Inverse(PlayerSetup.Instance.transform.rotation);
+ Vector3 l_targetDir = l_playerInv * (p_targetEnd.position - p_target.position);
+ Vector3 l_sourceDir = l_playerInv * (p_sourceEnd.position - p_source.position);
+ switch(p_plane)
+ {
+ case PlaneType.OXZ:
+ l_targetDir.y = 0f;
+ l_sourceDir.y = 0f;
+ break;
+ case PlaneType.OYX:
+ l_targetDir.z = 0f;
+ l_sourceDir.z = 0f;
+ break;
+ }
+ l_targetDir = Vector3.Normalize(l_targetDir);
+ l_sourceDir = Vector3.Normalize(l_sourceDir);
+
+ Quaternion l_targetRot = Quaternion.identity;
+ Quaternion l_sourceRot = Quaternion.identity;
+ switch(p_plane)
+ {
+ case PlaneType.OXZ:
+ l_targetRot = Quaternion.LookRotation(l_targetDir, Vector3.up);
+ l_sourceRot = Quaternion.LookRotation(l_sourceDir, Vector3.up);
+ break;
+ case PlaneType.OYX:
+ l_targetRot = Quaternion.LookRotation(l_targetDir, Vector3.forward);
+ l_sourceRot = Quaternion.LookRotation(l_sourceDir, Vector3.forward);
+ break;
+ }
+
+ Quaternion l_diff = Quaternion.Inverse(l_targetRot) * l_sourceRot;
+ if(p_plane == PlaneType.OYX)
+ l_diff = Quaternion.Euler(0f, 0f, l_diff.eulerAngles.y);
+
+ Quaternion l_adjusted = l_diff * (l_playerInv * p_target.rotation);
+ p_target.rotation = PlayerSetup.Instance.transform.rotation * l_adjusted;
+ }
+ }
}
}
diff --git a/ml_lme/LeapTracking.cs b/ml_lme/LeapTracking.cs
index 535805d..8704bef 100644
--- a/ml_lme/LeapTracking.cs
+++ b/ml_lme/LeapTracking.cs
@@ -18,8 +18,8 @@ namespace ml_lme
GameObject m_leapHands = null;
LeapHand m_leapHandLeft = null;
LeapHand m_leapHandRight = null;
- GameObject m_leapElbowLeft = null;
- GameObject m_leapElbowRight = null;
+ Transform m_leapElbowLeft = null;
+ Transform m_leapElbowRight = null;
GameObject m_leapControllerModel = null;
float m_scaleRelation = 1f;
@@ -31,15 +31,15 @@ namespace ml_lme
m_inVR = Utils.IsInVR();
- m_leapElbowLeft = new GameObject("LeapElbowLeft");
- m_leapElbowLeft.transform.parent = this.transform;
- m_leapElbowLeft.transform.localPosition = Vector3.zero;
- m_leapElbowLeft.transform.localRotation = Quaternion.identity;
+ m_leapElbowLeft = new GameObject("LeapElbowLeft").transform;
+ m_leapElbowLeft.parent = this.transform;
+ m_leapElbowLeft.localPosition = Vector3.zero;
+ m_leapElbowLeft.localRotation = Quaternion.identity;
- m_leapElbowRight = new GameObject("LeapElbowRight");
- m_leapElbowRight.transform.parent = this.transform;
- m_leapElbowRight.transform.localPosition = Vector3.zero;
- m_leapElbowRight.transform.localRotation = Quaternion.identity;
+ m_leapElbowRight = new GameObject("LeapElbowRight").transform;
+ m_leapElbowRight.parent = this.transform;
+ m_leapElbowRight.localPosition = Vector3.zero;
+ m_leapElbowRight.localRotation = Quaternion.identity;
m_leapControllerModel = AssetsHandler.GetAsset("assets/models/leapmotion/leap_motion_1_0.obj");
if(m_leapControllerModel != null)
@@ -101,11 +101,11 @@ namespace ml_lme
m_leapHandRight = null;
if(m_leapElbowLeft != null)
- Object.Destroy(m_leapElbowLeft);
+ Object.Destroy(m_leapElbowLeft.gameObject);
m_leapElbowLeft = null;
if(m_leapElbowRight != null)
- Object.Destroy(m_leapElbowRight);
+ Object.Destroy(m_leapElbowRight.gameObject);
m_leapElbowRight = null;
if(m_leapControllerModel != null)
@@ -140,7 +140,7 @@ namespace ml_lme
m_leapHandLeft.GetRoot().localRotation = l_data.m_leftHand.m_rotation;
OrientationAdjustment(ref l_data.m_leftHand.m_elbowPosition, ref ms_dummyRotation, Settings.TrackingMode);
- m_leapElbowLeft.transform.localPosition = l_data.m_leftHand.m_elbowPosition;
+ m_leapElbowLeft.localPosition = l_data.m_leftHand.m_elbowPosition;
m_leapHandLeft?.Update(l_data.m_leftHand);
}
@@ -155,7 +155,7 @@ namespace ml_lme
m_leapHandRight.GetRoot().localRotation = l_data.m_rightHand.m_rotation;
OrientationAdjustment(ref l_data.m_rightHand.m_elbowPosition, ref ms_dummyRotation, Settings.TrackingMode);
- m_leapElbowRight.transform.localPosition = l_data.m_rightHand.m_elbowPosition;
+ m_leapElbowRight.localPosition = l_data.m_rightHand.m_elbowPosition;
m_leapHandRight?.Update(l_data.m_rightHand);
}
@@ -164,8 +164,13 @@ namespace ml_lme
public LeapHand GetLeftHand() => m_leapHandLeft;
public LeapHand GetRightHand() => m_leapHandRight;
- public Transform GetLeftElbow() => m_leapElbowLeft.transform;
- public Transform GetRightElbow() => m_leapElbowRight.transform;
+ public Transform GetLeftElbow() => m_leapElbowLeft;
+ public Transform GetRightElbow() => m_leapElbowRight;
+ public void Rebind(Quaternion p_base)
+ {
+ m_leapHandLeft?.Rebind(p_base);
+ m_leapHandRight?.Rebind(p_base);
+ }
// Settings
void OnDesktopOffsetChange(Vector3 p_offset)
diff --git a/ml_lme/Properties/AssemblyInfo.cs b/ml_lme/Properties/AssemblyInfo.cs
index 066024d..28e79bd 100644
--- a/ml_lme/Properties/AssemblyInfo.cs
+++ b/ml_lme/Properties/AssemblyInfo.cs
@@ -1,4 +1,4 @@
-[assembly: MelonLoader.MelonInfo(typeof(ml_lme.LeapMotionExtension), "LeapMotionExtension", "1.4.6", "SDraw", "https://github.com/SDraw/ml_mods_cvr")]
+[assembly: MelonLoader.MelonInfo(typeof(ml_lme.LeapMotionExtension), "LeapMotionExtension", "1.4.7", "SDraw", "https://github.com/SDraw/ml_mods_cvr")]
[assembly: MelonLoader.MelonGame(null, "ChilloutVR")]
[assembly: MelonLoader.MelonOptionalDependencies("ml_pmc")]
[assembly: MelonLoader.MelonPlatform(MelonLoader.MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)]
diff --git a/ml_lme/README.md b/ml_lme/README.md
index c4f83cd..0d2a0ec 100644
--- a/ml_lme/README.md
+++ b/ml_lme/README.md
@@ -25,3 +25,5 @@ Available mod's settings in `Settings - Implementation - Leap Motion Tracking`:
* **Recognize gestures:** sets avatar gestures (fist, gun, rock'n'roll and etc.) based on current fingers pose; `false` by default.
* **Interact gesture threadhold:** activation limit for interaction based on hand gesture; 80 by default.
* **Grip gesture threadhold:** activation limit for grip based on hand gesture; 40 by default.
+* **Filter humanoid limits:** Limits fingers rotations to be valid for Unity's Mechanim; `true` by default
+ * Note: Enabling this option ensures that visual representation of your fingers will be same for you and remote players, but it cancels out additional finger segments rotations that can be better visually in most cases.
diff --git a/ml_lme/Settings.cs b/ml_lme/Settings.cs
index 0599ce4..ced9738 100644
--- a/ml_lme/Settings.cs
+++ b/ml_lme/Settings.cs
@@ -35,7 +35,8 @@ namespace ml_lme
Gestures,
InteractThreadhold,
GripThreadhold,
- VisualHands
+ VisualHands,
+ MechanimFilter
};
public static bool Enabled { get; private set; } = false;
@@ -52,6 +53,7 @@ namespace ml_lme
public static float InteractThreadhold { get; private set; } = 0.8f;
public static float GripThreadhold { get; private set; } = 0.4f;
public static bool VisualHands { get; private set; } = false;
+ public static bool MechanimFilter { get; private set; } = true;
static MelonLoader.MelonPreferences_Category ms_category = null;
static List ms_entries = null;
@@ -70,6 +72,7 @@ namespace ml_lme
public static event Action InteractThreadholdChange;
public static event Action GripThreadholdChange;
public static event Action VisualHandsChange;
+ public static event Action MechanimFilterChange;
internal static void Init()
{
@@ -96,7 +99,8 @@ namespace ml_lme
ms_category.CreateEntry(ModSetting.Gestures.ToString(), Gestures),
ms_category.CreateEntry(ModSetting.InteractThreadhold.ToString(), (int)(InteractThreadhold * 100f)),
ms_category.CreateEntry(ModSetting.GripThreadhold.ToString(), (int)(GripThreadhold * 100f)),
- ms_category.CreateEntry(ModSetting.VisualHands.ToString(), VisualHands)
+ ms_category.CreateEntry(ModSetting.VisualHands.ToString(), VisualHands),
+ ms_category.CreateEntry(ModSetting.MechanimFilter.ToString(), MechanimFilter)
};
Load();
@@ -156,6 +160,7 @@ namespace ml_lme
InteractThreadhold = (int)ms_entries[(int)ModSetting.InteractThreadhold].BoxedValue * 0.01f;
GripThreadhold = (int)ms_entries[(int)ModSetting.GripThreadhold].BoxedValue * 0.01f;
VisualHands = (bool)ms_entries[(int)ModSetting.VisualHands].BoxedValue;
+ MechanimFilter = (bool)ms_entries[(int)ModSetting.MechanimFilter].BoxedValue;
}
static void OnToggleUpdate(string p_name, string p_value)
@@ -219,6 +224,13 @@ namespace ml_lme
VisualHandsChange?.Invoke(VisualHands);
}
break;
+
+ case ModSetting.MechanimFilter:
+ {
+ MechanimFilter = bool.Parse(p_value);
+ MechanimFilterChange?.Invoke(MechanimFilter);
+ }
+ break;
}
ms_entries[(int)l_setting].BoxedValue = bool.Parse(p_value);
diff --git a/ml_lme/ml_lme.csproj b/ml_lme/ml_lme.csproj
index 2d70ed7..cb0ccc9 100644
--- a/ml_lme/ml_lme.csproj
+++ b/ml_lme/ml_lme.csproj
@@ -4,7 +4,7 @@
netstandard2.1
x64
LeapMotionExtension
- 1.4.6
+ 1.4.7
SDraw
None
LeapMotionExtension
diff --git a/ml_lme/resources/mod_menu.js b/ml_lme/resources/mod_menu.js
index dadaee1..96a11f0 100644
--- a/ml_lme/resources/mod_menu.js
+++ b/ml_lme/resources/mod_menu.js
@@ -145,6 +145,13 @@
+
+
+
Filter humanoid limits:
+
+
`;
document.getElementById('settings-implementation').appendChild(l_block);