ArmIK usage

Elbows tracking
Tracking without VR controllers
This commit is contained in:
SDraw 2022-10-18 00:22:20 +03:00
parent 3b33dc0cfb
commit 7fcfb04e0f
No known key found for this signature in database
GPG key ID: BB95B4DAB2BB8BB5
11 changed files with 238 additions and 121 deletions

View file

@ -8,5 +8,5 @@ Merged set of MelonLoader mods for ChilloutVR.
| Desktop Head Tracking | ml_dht | 1.0.6 | Yes | Working | | Desktop Head Tracking | ml_dht | 1.0.6 | Yes | Working |
| Desktop Reticle Switch | ml_drs | 1.0.0 | Yes | Working | | Desktop Reticle Switch | ml_drs | 1.0.0 | Yes | Working |
| Four Point Tracking | ml_fpt | 1.0.8 | Yes | Working | | Four Point Tracking | ml_fpt | 1.0.8 | Yes | Working |
| Leap Motion Extension | ml_lme | 1.2.2 | On review | Working | | Leap Motion Extension | ml_lme | 1.2.3 | On review | Working |
| Server Connection Info | ml_sci | 1.0.2 | Yes | Working | | Server Connection Info | ml_sci | 1.0.2 | Yes | Working |

View file

@ -22,6 +22,7 @@ namespace ml_lme
public bool[] m_handsPresenses = null; public bool[] m_handsPresenses = null;
public Vector3[] m_handsPositons = null; public Vector3[] m_handsPositons = null;
public Quaternion[] m_handsRotations = null; public Quaternion[] m_handsRotations = null;
public Vector3[] m_elbowPositions = null;
public float[] m_leftFingersBends = null; public float[] m_leftFingersBends = null;
public float[] m_leftFingersSpreads = null; public float[] m_leftFingersSpreads = null;
public float[] m_rightFingersBends = null; public float[] m_rightFingersBends = null;
@ -32,6 +33,7 @@ namespace ml_lme
m_handsPresenses = new bool[ms_handsCount]; m_handsPresenses = new bool[ms_handsCount];
m_handsPositons = new Vector3[ms_handsCount]; m_handsPositons = new Vector3[ms_handsCount];
m_handsRotations = new Quaternion[ms_handsCount]; m_handsRotations = new Quaternion[ms_handsCount];
m_elbowPositions = new Vector3[ms_handsCount];
m_leftFingersBends = new float[ms_fingersCount]; m_leftFingersBends = new float[ms_fingersCount];
m_leftFingersSpreads = new float[ms_fingersCount]; m_leftFingersSpreads = new float[ms_fingersCount];
m_rightFingersBends = new float[ms_fingersCount]; m_rightFingersBends = new float[ms_fingersCount];
@ -61,6 +63,7 @@ namespace ml_lme
p_data.m_handsPresenses[l_sideID] = true; p_data.m_handsPresenses[l_sideID] = true;
FillHandPosition(l_hand, ref p_data.m_handsPositons[l_sideID]); FillHandPosition(l_hand, ref p_data.m_handsPositons[l_sideID]);
FillHandRotation(l_hand, ref p_data.m_handsRotations[l_sideID]); FillHandRotation(l_hand, ref p_data.m_handsRotations[l_sideID]);
FillElbowPosition(l_hand, ref p_data.m_elbowPositions[l_sideID]);
switch(l_sideID) switch(l_sideID)
{ {
case 0: case 0:
@ -96,6 +99,13 @@ namespace ml_lme
p_rot.w = p_hand.Rotation.w; p_rot.w = p_hand.Rotation.w;
} }
static void FillElbowPosition(Leap.Hand p_hand, ref Vector3 p_pos)
{
p_pos.x = p_hand.Arm.ElbowPosition.x;
p_pos.y = p_hand.Arm.ElbowPosition.y;
p_pos.z = p_hand.Arm.ElbowPosition.z;
}
static void FillFingerBends(Leap.Hand p_hand, ref float[] p_bends) static void FillFingerBends(Leap.Hand p_hand, ref float[] p_bends)
{ {
foreach(Leap.Finger l_finger in p_hand.Fingers) foreach(Leap.Finger l_finger in p_hand.Fingers)

View file

@ -1,66 +0,0 @@
using UnityEngine;
namespace ml_lme
{
[DisallowMultipleComponent]
class LeapIK : MonoBehaviour
{
bool m_enabled = true;
bool m_fingersOnly = false;
Animator m_animator = null;
Transform m_leftHand = null;
Transform m_rightHand = null;
float m_leftHandWeight = 0f;
float m_rightHandWeight = 0f;
bool m_leftHandVisible = false;
bool m_rightHandVisible = false;
void Start()
{
m_animator = this.GetComponent<Animator>();
}
void OnAnimatorIK()
{
if(m_enabled && !m_fingersOnly && (m_animator != null))
{
if(m_leftHand != null)
{
m_leftHandWeight = Mathf.Lerp(m_leftHandWeight, m_leftHandVisible ? 1f : 0f, 0.25f);
m_animator.SetIKPositionWeight(AvatarIKGoal.LeftHand, m_leftHandWeight);
m_animator.SetIKRotationWeight(AvatarIKGoal.LeftHand, m_leftHandWeight);
m_animator.SetIKPosition(AvatarIKGoal.LeftHand, m_leftHand.position);
m_animator.SetIKRotation(AvatarIKGoal.LeftHand, m_leftHand.rotation);
}
if(m_rightHand != null)
{
m_rightHandWeight = Mathf.Lerp(m_rightHandWeight, m_rightHandVisible ? 1f : 0f, 0.25f);
m_animator.SetIKPositionWeight(AvatarIKGoal.RightHand, m_rightHandWeight);
m_animator.SetIKRotationWeight(AvatarIKGoal.RightHand, m_rightHandWeight);
m_animator.SetIKPosition(AvatarIKGoal.RightHand, m_rightHand.position);
m_animator.SetIKRotation(AvatarIKGoal.RightHand, m_rightHand.rotation);
}
}
}
public void SetEnabled(bool p_state) => m_enabled = p_state;
public void SetFingersOnly(bool p_state) => m_fingersOnly = p_state;
public void SetHands(Transform p_left, Transform p_right)
{
m_leftHand = p_left;
m_rightHand = p_right;
}
public void SetHandsVisibility(bool p_left, bool p_right)
{
m_leftHandVisible = p_left;
m_rightHandVisible = p_right;
}
}
}

View file

@ -2,6 +2,7 @@
using ABI_RC.Core.Savior; using ABI_RC.Core.Savior;
using ABI_RC.Systems.IK; using ABI_RC.Systems.IK;
using RootMotion.FinalIK; using RootMotion.FinalIK;
using System.Reflection;
using UnityEngine; using UnityEngine;
namespace ml_lme namespace ml_lme
@ -9,20 +10,28 @@ namespace ml_lme
[DisallowMultipleComponent] [DisallowMultipleComponent]
class LeapTracked : MonoBehaviour class LeapTracked : MonoBehaviour
{ {
static readonly float[] ms_tposeMuscles = typeof(ABI_RC.Systems.IK.SubSystems.BodySystem).GetField("TPoseMuscles", BindingFlags.Static | BindingFlags.NonPublic).GetValue(null) as float[];
static readonly Quaternion ms_offsetLeft = Quaternion.Euler(0f, 0f, 270f); static readonly Quaternion ms_offsetLeft = Quaternion.Euler(0f, 0f, 270f);
static readonly Quaternion ms_offsetRight = Quaternion.Euler(0f, 0f, 90f); static readonly Quaternion ms_offsetRight = Quaternion.Euler(0f, 0f, 90f);
static readonly Quaternion ms_offsetLeftDesktop = Quaternion.Euler(0f, 90f, 0f);
static readonly Quaternion ms_offsetRightDesktop = Quaternion.Euler(0f, 270f, 0f);
IndexIK m_indexIK = null; IndexIK m_indexIK = null;
VRIK m_vrIK = null; VRIK m_vrIK = null;
Vector2 m_armsWeights = Vector2.zero;
bool m_enabled = true; bool m_enabled = true;
bool m_fingersOnly = false; bool m_fingersOnly = false;
bool m_trackElbows = true;
LeapIK m_leapIK = null; ArmIK m_leftIK = null;
ArmIK m_rightIK = null;
Transform m_leftHand = null; Transform m_leftHand = null;
Transform m_rightHand = null; Transform m_rightHand = null;
Transform m_leftHandTarget = null; Transform m_leftHandTarget = null;
Transform m_rightHandTarget = null; Transform m_rightHandTarget = null;
Transform m_leftElbow = null;
Transform m_rightElbow = null;
bool m_leftTargetActive = false; bool m_leftTargetActive = false;
bool m_rightTargetActive = false; bool m_rightTargetActive = false;
@ -56,8 +65,11 @@ namespace ml_lme
CVRInputManager.Instance.individualFingerTracking = (m_enabled || Utils.AreKnucklesInUse()); CVRInputManager.Instance.individualFingerTracking = (m_enabled || Utils.AreKnucklesInUse());
} }
if(m_leapIK != null) if((m_leftIK != null) && (m_rightIK != null))
m_leapIK.SetEnabled(m_enabled); {
m_leftIK.enabled = (m_enabled && !m_fingersOnly);
m_rightIK.enabled = (m_enabled && !m_fingersOnly);
}
if(!m_enabled || m_fingersOnly) if(!m_enabled || m_fingersOnly)
RestoreIK(); RestoreIK();
@ -67,25 +79,55 @@ namespace ml_lme
{ {
m_fingersOnly = p_state; m_fingersOnly = p_state;
if(m_leapIK != null) if((m_leftIK != null) && (m_rightIK != null))
m_leapIK.SetFingersOnly(m_fingersOnly); {
m_leftIK.enabled = (m_enabled && !m_fingersOnly);
m_rightIK.enabled = (m_enabled && !m_fingersOnly);
}
if(!m_enabled || m_fingersOnly) if(!m_enabled || m_fingersOnly)
RestoreIK(); RestoreIK();
} }
public void SetHands(Transform p_left, Transform p_right) public void SetTrackElbows(bool p_state)
{
m_trackElbows = p_state;
if((m_leftIK != null) && (m_rightIK != null))
{
m_leftIK.solver.arm.bendGoalWeight = (m_trackElbows ? 1f : 0f);
m_rightIK.solver.arm.bendGoalWeight = (m_trackElbows ? 1f : 0f);
}
if(m_vrIK != null)
{
if(m_leftTargetActive)
m_vrIK.solver.leftArm.bendGoalWeight = (m_trackElbows ? 1f : 0f);
if(m_rightTargetActive)
m_vrIK.solver.rightArm.bendGoalWeight = (m_trackElbows ? 1f : 0f);
}
}
public void SetTransforms(Transform p_left, Transform p_right, Transform p_leftElbow, Transform p_rightElbow)
{ {
m_leftHand = p_left; m_leftHand = p_left;
m_rightHand = p_right; m_rightHand = p_right;
m_leftElbow = p_leftElbow;
m_rightElbow = p_rightElbow;
} }
public void UpdateTracking(GestureMatcher.GesturesData p_gesturesData) public void UpdateTracking(GestureMatcher.GesturesData p_gesturesData)
{ {
if(m_enabled && (m_indexIK != null)) if(m_enabled && (m_indexIK != null))
{ {
if(m_leapIK != null) if((m_leftIK != null) && (m_rightIK != null))
m_leapIK.SetHandsVisibility(p_gesturesData.m_handsPresenses[0], p_gesturesData.m_handsPresenses[1]); {
m_leftIK.solver.IKPositionWeight = Mathf.Lerp(m_leftIK.solver.IKPositionWeight, (p_gesturesData.m_handsPresenses[0] && !m_fingersOnly) ? 1f : 0f, 0.25f);
m_leftIK.solver.IKRotationWeight = Mathf.Lerp(m_leftIK.solver.IKRotationWeight, (p_gesturesData.m_handsPresenses[0] && !m_fingersOnly) ? 1f : 0f, 0.25f);
m_rightIK.solver.IKPositionWeight = Mathf.Lerp(m_rightIK.solver.IKPositionWeight, (p_gesturesData.m_handsPresenses[1] && !m_fingersOnly) ? 1f : 0f, 0.25f);
m_rightIK.solver.IKRotationWeight = Mathf.Lerp(m_rightIK.solver.IKPositionWeight, (p_gesturesData.m_handsPresenses[1] && !m_fingersOnly) ? 1f : 0f, 0.25f);
}
if(p_gesturesData.m_handsPresenses[0]) if(p_gesturesData.m_handsPresenses[0])
{ {
@ -128,22 +170,30 @@ namespace ml_lme
if(p_gesturesData.m_handsPresenses[0] && !m_leftTargetActive) if(p_gesturesData.m_handsPresenses[0] && !m_leftTargetActive)
{ {
m_vrIK.solver.leftArm.target = m_leftHandTarget; m_vrIK.solver.leftArm.target = m_leftHandTarget;
m_vrIK.solver.leftArm.bendGoal = m_leftElbow;
m_vrIK.solver.leftArm.bendGoalWeight = (m_trackElbows ? 1f : 0f);
m_leftTargetActive = true; m_leftTargetActive = true;
} }
if(!p_gesturesData.m_handsPresenses[0] && m_leftTargetActive) if(!p_gesturesData.m_handsPresenses[0] && m_leftTargetActive)
{ {
m_vrIK.solver.leftArm.target = IKSystem.Instance.leftHandAnchor; m_vrIK.solver.leftArm.target = IKSystem.Instance.leftHandAnchor;
m_vrIK.solver.leftArm.bendGoal = null;
m_vrIK.solver.leftArm.bendGoalWeight = 0f;
m_leftTargetActive = false; m_leftTargetActive = false;
} }
if(p_gesturesData.m_handsPresenses[1] && !m_rightTargetActive) if(p_gesturesData.m_handsPresenses[1] && !m_rightTargetActive)
{ {
m_vrIK.solver.rightArm.target = m_rightHandTarget; m_vrIK.solver.rightArm.target = m_rightHandTarget;
m_vrIK.solver.rightArm.bendGoal = m_rightElbow;
m_vrIK.solver.rightArm.bendGoalWeight = (m_trackElbows ? 1f : 0f);
m_rightTargetActive = true; m_rightTargetActive = true;
} }
if(!p_gesturesData.m_handsPresenses[1] && m_rightTargetActive) if(!p_gesturesData.m_handsPresenses[1] && m_rightTargetActive)
{ {
m_vrIK.solver.rightArm.target = IKSystem.Instance.rightHandAnchor; m_vrIK.solver.rightArm.target = IKSystem.Instance.rightHandAnchor;
m_vrIK.solver.rightArm.bendGoal = null;
m_vrIK.solver.rightArm.bendGoalWeight = 0f;
m_rightTargetActive = false; m_rightTargetActive = false;
} }
} }
@ -152,8 +202,9 @@ namespace ml_lme
public void OnAvatarClear() public void OnAvatarClear()
{ {
m_leapIK = null;
m_vrIK = null; m_vrIK = null;
m_leftIK = null;
m_rightIK = null;
m_leftTargetActive = false; m_leftTargetActive = false;
m_rightTargetActive = false; m_rightTargetActive = false;
@ -161,6 +212,8 @@ namespace ml_lme
m_leftHandTarget.localRotation = Quaternion.identity; m_leftHandTarget.localRotation = Quaternion.identity;
m_rightHandTarget.localPosition = Vector3.zero; m_rightHandTarget.localPosition = Vector3.zero;
m_rightHandTarget.localRotation = Quaternion.identity; m_rightHandTarget.localRotation = Quaternion.identity;
m_armsWeights.Set(0f, 0f);
} }
public void OnCalibrateAvatar() public void OnCalibrateAvatar()
@ -176,24 +229,107 @@ namespace ml_lme
if(PlayerSetup.Instance._animator.isHuman) if(PlayerSetup.Instance._animator.isHuman)
{ {
HumanPoseHandler l_poseHandler = null;
HumanPose l_initPose = new HumanPose();
// Force desktop non-VRIK avatar into T-Pose
if(m_vrIK == null)
{
l_poseHandler = new HumanPoseHandler(PlayerSetup.Instance._animator.avatar, PlayerSetup.Instance._avatar.transform);
l_poseHandler.GetHumanPose(ref l_initPose);
HumanPose l_tPose = new HumanPose();
l_tPose.bodyPosition = l_initPose.bodyPosition;
l_tPose.bodyRotation = l_initPose.bodyRotation;
l_tPose.muscles = new float[l_initPose.muscles.Length];
for(int i = 0; i < l_tPose.muscles.Length; i++)
l_tPose.muscles[i] = ms_tposeMuscles[i];
l_poseHandler.SetHumanPose(ref l_tPose);
}
Transform l_hand = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.LeftHand); Transform l_hand = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.LeftHand);
if(l_hand != null) if(l_hand != null)
m_leftHandTarget.localRotation = ms_offsetLeft * (PlayerSetup.Instance._avatar.transform.GetMatrix().inverse * l_hand.GetMatrix()).rotation; m_leftHandTarget.localRotation = ((m_vrIK != null) ? ms_offsetLeft : ms_offsetLeftDesktop) * (PlayerSetup.Instance._avatar.transform.GetMatrix().inverse * l_hand.GetMatrix()).rotation;
l_hand = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.RightHand); l_hand = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.RightHand);
if(l_hand != null) if(l_hand != null)
m_rightHandTarget.localRotation = ms_offsetRight * (PlayerSetup.Instance._avatar.transform.GetMatrix().inverse * l_hand.GetMatrix()).rotation; m_rightHandTarget.localRotation = ((m_vrIK != null) ? ms_offsetRight : ms_offsetRightDesktop) * (PlayerSetup.Instance._avatar.transform.GetMatrix().inverse * l_hand.GetMatrix()).rotation;
if(m_vrIK == null) if(m_vrIK == null)
{ {
m_leapIK = PlayerSetup.Instance._animator.gameObject.AddComponent<LeapIK>(); Transform l_chest = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.UpperChest);
m_leapIK.SetHands(m_leftHand, m_rightHand); if(l_chest == null)
m_leapIK.SetEnabled(m_enabled); l_chest = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.Chest);
m_leapIK.SetFingersOnly(m_fingersOnly); if(l_chest == null)
l_chest = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.Spine);
m_leftIK = PlayerSetup.Instance._avatar.AddComponent<ArmIK>();
m_leftIK.solver.isLeft = true;
m_leftIK.solver.SetChain(
l_chest,
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.LeftShoulder),
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.LeftUpperArm),
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.LeftLowerArm),
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.LeftHand),
PlayerSetup.Instance._animator.transform
);
m_leftIK.solver.arm.target = m_leftHandTarget;
m_leftIK.solver.arm.bendGoal = m_leftElbow;
m_leftIK.solver.arm.bendGoalWeight = (m_trackElbows ? 1f : 0f);
m_leftIK.enabled = (m_enabled && !m_fingersOnly);
m_rightIK = PlayerSetup.Instance._avatar.AddComponent<ArmIK>();
m_rightIK.solver.isLeft = false;
m_rightIK.solver.SetChain(
l_chest,
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.RightShoulder),
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.RightUpperArm),
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.RightLowerArm),
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.RightHand),
PlayerSetup.Instance._animator.transform
);
m_rightIK.solver.arm.target = m_rightHandTarget;
m_rightIK.solver.arm.bendGoal = m_rightElbow;
m_rightIK.solver.arm.bendGoalWeight = (m_trackElbows ? 1f : 0f);
m_rightIK.enabled = (m_enabled && !m_fingersOnly);
l_poseHandler.SetHumanPose(ref l_initPose);
} }
else
{
m_vrIK.onPreSolverUpdate.AddListener(this.OnIKPreUpdate);
m_vrIK.onPostSolverUpdate.AddListener(this.OnIKPostUpdate);
}
l_poseHandler?.Dispose();
} }
} }
void OnIKPreUpdate()
{
m_armsWeights.Set(m_vrIK.solver.leftArm.positionWeight, m_vrIK.solver.rightArm.positionWeight);
if(m_leftTargetActive && Mathf.Approximately(m_armsWeights.x, 0f))
{
m_vrIK.solver.leftArm.positionWeight = 1f;
m_vrIK.solver.leftArm.rotationWeight = 1f;
}
if(m_rightTargetActive && Mathf.Approximately(m_armsWeights.y, 0f))
{
m_vrIK.solver.rightArm.positionWeight = 1f;
m_vrIK.solver.rightArm.rotationWeight = 1f;
}
}
void OnIKPostUpdate()
{
m_vrIK.solver.leftArm.positionWeight = m_armsWeights.x;
m_vrIK.solver.leftArm.rotationWeight = m_armsWeights.x;
m_vrIK.solver.rightArm.positionWeight = m_armsWeights.y;
m_vrIK.solver.rightArm.rotationWeight = m_armsWeights.y;
}
void RestoreIK() void RestoreIK()
{ {
if(m_vrIK != null) if(m_vrIK != null)
@ -201,11 +337,15 @@ namespace ml_lme
if(m_leftTargetActive) if(m_leftTargetActive)
{ {
m_vrIK.solver.leftArm.target = IKSystem.Instance.leftHandAnchor; m_vrIK.solver.leftArm.target = IKSystem.Instance.leftHandAnchor;
m_vrIK.solver.leftArm.bendGoal = null;
m_vrIK.solver.leftArm.bendGoalWeight = 0f;
m_leftTargetActive = false; m_leftTargetActive = false;
} }
if(m_rightTargetActive) if(m_rightTargetActive)
{ {
m_vrIK.solver.rightArm.target = IKSystem.Instance.rightHandAnchor; m_vrIK.solver.rightArm.target = IKSystem.Instance.rightHandAnchor;
m_vrIK.solver.rightArm.bendGoal = null;
m_vrIK.solver.rightArm.bendGoalWeight = 0f;
m_rightTargetActive = false; m_rightTargetActive = false;
} }
} }

View file

@ -1,7 +1,7 @@
using ABI_RC.Core.Player; using ABI_RC.Core.Player;
using ABI_RC.Core.UI; using ABI_RC.Core.UI;
using UnityEngine;
using System.Reflection; using System.Reflection;
using UnityEngine;
namespace ml_lme namespace ml_lme
{ {
@ -18,6 +18,7 @@ namespace ml_lme
GameObject m_leapTrackingRoot = null; GameObject m_leapTrackingRoot = null;
GameObject[] m_leapHands = null; GameObject[] m_leapHands = null;
GameObject[] m_leapElbows = null;
GameObject m_leapControllerModel = null; GameObject m_leapControllerModel = null;
LeapTracked m_leapTracked = null; LeapTracked m_leapTracked = null;
@ -29,14 +30,15 @@ namespace ml_lme
DependenciesHandler.ExtractDependencies(); DependenciesHandler.ExtractDependencies();
Settings.Init(); Settings.Init();
Settings.EnabledChange += this.OnSettingsEnableChange; Settings.EnabledChange += this.OnEnableChange;
Settings.DesktopOffsetChange += this.OnSettingsDesktopOffsetChange; Settings.DesktopOffsetChange += this.OnDesktopOffsetChange;
Settings.FingersOnlyChange += this.OnSettingsFingersOptionChange; Settings.FingersOnlyChange += this.OnFingersOptionChange;
Settings.ModelVisibilityChange += this.OnSettingsModelVisibilityChange; Settings.ModelVisibilityChange += this.OnModelVisibilityChange;
Settings.TrackingModeChange += this.OnSettingsTrackingModeChange; Settings.TrackingModeChange += this.OnTrackingModeChange;
Settings.RootAngleChange += this.OnSettingsRootAngleChange; Settings.RootAngleChange += this.OnRootAngleChange;
Settings.HeadAttachChange += this.OnSettingsHeadAttachChange; Settings.HeadAttachChange += this.OnHeadAttachChange;
Settings.HeadOffsetChange += this.OnSettingsHeadOffsetChange; Settings.HeadOffsetChange += this.OnHeadOffsetChange;
Settings.TrackElbowsChange += this.OnTrackElbowsChange;
m_leapController = new Leap.Controller(); m_leapController = new Leap.Controller();
m_leapController.Device += this.OnLeapDeviceInitialized; m_leapController.Device += this.OnLeapDeviceInitialized;
@ -47,6 +49,7 @@ namespace ml_lme
m_gesturesData = new GestureMatcher.GesturesData(); m_gesturesData = new GestureMatcher.GesturesData();
m_leapHands = new GameObject[GestureMatcher.GesturesData.ms_handsCount]; m_leapHands = new GameObject[GestureMatcher.GesturesData.ms_handsCount];
m_leapElbows = new GameObject[GestureMatcher.GesturesData.ms_handsCount];
// Patches // Patches
HarmonyInstance.Patch( HarmonyInstance.Patch(
@ -87,6 +90,11 @@ namespace ml_lme
m_leapHands[i].transform.parent = m_leapTrackingRoot.transform; m_leapHands[i].transform.parent = m_leapTrackingRoot.transform;
m_leapHands[i].transform.localPosition = Vector3.zero; m_leapHands[i].transform.localPosition = Vector3.zero;
m_leapHands[i].transform.localRotation = Quaternion.identity; m_leapHands[i].transform.localRotation = Quaternion.identity;
m_leapElbows[i] = new GameObject("LeapElbow" + i);
m_leapElbows[i].transform.parent = m_leapTrackingRoot.transform;
m_leapElbows[i].transform.localPosition = Vector3.zero;
m_leapElbows[i].transform.localRotation = Quaternion.identity;
} }
m_leapControllerModel = AssetsHandler.GetAsset("assets/models/leapmotion/leap_motion_1_0.obj"); m_leapControllerModel = AssetsHandler.GetAsset("assets/models/leapmotion/leap_motion_1_0.obj");
@ -100,13 +108,14 @@ namespace ml_lme
// Player setup // Player setup
m_leapTracked = PlayerSetup.Instance.gameObject.AddComponent<LeapTracked>(); m_leapTracked = PlayerSetup.Instance.gameObject.AddComponent<LeapTracked>();
m_leapTracked.SetHands(m_leapHands[0].transform, m_leapHands[1].transform); m_leapTracked.SetTransforms(m_leapHands[0].transform, m_leapHands[1].transform, m_leapElbows[0].transform, m_leapElbows[1].transform);
m_leapTracked.SetTrackElbows(Settings.TrackElbows);
OnSettingsEnableChange(Settings.Enabled); OnEnableChange(Settings.Enabled);
OnSettingsFingersOptionChange(Settings.FingersOnly); OnFingersOptionChange(Settings.FingersOnly);
OnSettingsModelVisibilityChange(Settings.ModelVisibility); OnModelVisibilityChange(Settings.ModelVisibility);
OnSettingsTrackingModeChange(Settings.TrackingMode); OnTrackingModeChange(Settings.TrackingMode);
OnSettingsHeadAttachChange(Settings.HeadAttach); // Includes offsets and parenting OnHeadAttachChange(Settings.HeadAttach); // Includes offsets and parenting
} }
public override void OnUpdate() public override void OnUpdate()
@ -132,6 +141,10 @@ namespace ml_lme
ReorientateLeapToUnity(ref l_pos, ref l_rot, Settings.TrackingMode); ReorientateLeapToUnity(ref l_pos, ref l_rot, Settings.TrackingMode);
m_leapHands[i].transform.localPosition = l_pos; m_leapHands[i].transform.localPosition = l_pos;
m_leapHands[i].transform.localRotation = l_rot; m_leapHands[i].transform.localRotation = l_rot;
l_pos = m_gesturesData.m_elbowPositions[i];
ReorientateLeapToUnity(ref l_pos, ref l_rot, Settings.TrackingMode);
m_leapElbows[i].transform.localPosition = l_pos;
} }
} }
} }
@ -143,7 +156,7 @@ namespace ml_lme
} }
// Settings changes // Settings changes
void OnSettingsEnableChange(bool p_state) void OnEnableChange(bool p_state)
{ {
if(p_state) if(p_state)
{ {
@ -157,7 +170,7 @@ namespace ml_lme
m_leapTracked.SetEnabled(p_state); m_leapTracked.SetEnabled(p_state);
} }
void OnSettingsDesktopOffsetChange(Vector3 p_offset) void OnDesktopOffsetChange(Vector3 p_offset)
{ {
if((m_leapTrackingRoot != null) && !Settings.HeadAttach) if((m_leapTrackingRoot != null) && !Settings.HeadAttach)
{ {
@ -168,19 +181,19 @@ namespace ml_lme
} }
} }
void OnSettingsFingersOptionChange(bool p_state) void OnFingersOptionChange(bool p_state)
{ {
if(m_leapTracked != null) if(m_leapTracked != null)
m_leapTracked.SetFingersOnly(p_state); m_leapTracked.SetFingersOnly(p_state);
} }
void OnSettingsModelVisibilityChange(bool p_state) void OnModelVisibilityChange(bool p_state)
{ {
if(m_leapControllerModel != null) if(m_leapControllerModel != null)
m_leapControllerModel.SetActive(p_state); m_leapControllerModel.SetActive(p_state);
} }
void OnSettingsTrackingModeChange(Settings.LeapTrackingMode p_mode) void OnTrackingModeChange(Settings.LeapTrackingMode p_mode)
{ {
if(Settings.Enabled) if(Settings.Enabled)
UpdateDeviceTrackingMode(); UpdateDeviceTrackingMode();
@ -202,13 +215,13 @@ namespace ml_lme
} }
} }
void OnSettingsRootAngleChange(float p_angle) void OnRootAngleChange(float p_angle)
{ {
if(m_leapTrackingRoot != null) if(m_leapTrackingRoot != null)
m_leapTrackingRoot.transform.localRotation = Quaternion.Euler(p_angle, 0f, 0f); m_leapTrackingRoot.transform.localRotation = Quaternion.Euler(p_angle, 0f, 0f);
} }
void OnSettingsHeadAttachChange(bool p_state) void OnHeadAttachChange(bool p_state)
{ {
if(m_leapTrackingRoot != null) if(m_leapTrackingRoot != null)
{ {
@ -247,7 +260,7 @@ namespace ml_lme
} }
} }
void OnSettingsHeadOffsetChange(Vector3 p_offset) void OnHeadOffsetChange(Vector3 p_offset)
{ {
if((m_leapTrackingRoot != null) && Settings.HeadAttach) if((m_leapTrackingRoot != null) && Settings.HeadAttach)
{ {
@ -257,6 +270,12 @@ namespace ml_lme
m_leapTrackingRoot.transform.localPosition = p_offset; m_leapTrackingRoot.transform.localPosition = p_offset;
} }
} }
void OnTrackElbowsChange(bool p_state)
{
if(m_leapTracked != null)
m_leapTracked.SetTrackElbows(p_state);
}
// Internal utility // Internal utility
void UpdateDeviceTrackingMode() void UpdateDeviceTrackingMode()
@ -328,8 +347,7 @@ namespace ml_lme
MelonLoader.MelonLogger.Error(e); MelonLoader.MelonLogger.Error(e);
} }
} }
// Sneaky forced IndexIK calibration
static void OnCalibrateAvatar_Postfix() => ms_instance?.OnCalibrateAvatar(); static void OnCalibrateAvatar_Postfix() => ms_instance?.OnCalibrateAvatar();
void OnCalibrateAvatar() void OnCalibrateAvatar()
{ {
@ -338,7 +356,7 @@ namespace ml_lme
if(m_leapTracked != null) if(m_leapTracked != null)
m_leapTracked.OnCalibrateAvatar(); m_leapTracked.OnCalibrateAvatar();
OnSettingsHeadAttachChange(Settings.HeadAttach); OnHeadAttachChange(Settings.HeadAttach);
} }
catch(System.Exception e) catch(System.Exception e)
{ {

View file

@ -1,10 +1,10 @@
using System.Reflection; using System.Reflection;
[assembly: AssemblyTitle("LeapMotionExtension")] [assembly: AssemblyTitle("LeapMotionExtension")]
[assembly: AssemblyVersion("1.2.2")] [assembly: AssemblyVersion("1.2.3")]
[assembly: AssemblyFileVersion("1.2.2")] [assembly: AssemblyFileVersion("1.2.3")]
[assembly: MelonLoader.MelonInfo(typeof(ml_lme.LeapMotionExtension), "LeapMotionExtension", "1.2.2", "SDraw", "https://github.com/SDraw/ml_mods_cvr")] [assembly: MelonLoader.MelonInfo(typeof(ml_lme.LeapMotionExtension), "LeapMotionExtension", "1.2.3", "SDraw", "https://github.com/SDraw/ml_mods_cvr")]
[assembly: MelonLoader.MelonGame(null, "ChilloutVR")] [assembly: MelonLoader.MelonGame(null, "ChilloutVR")]
[assembly: MelonLoader.MelonPlatform(MelonLoader.MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)] [assembly: MelonLoader.MelonPlatform(MelonLoader.MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)]
[assembly: MelonLoader.MelonPlatformDomain(MelonLoader.MelonPlatformDomainAttribute.CompatibleDomains.MONO)] [assembly: MelonLoader.MelonPlatformDomain(MelonLoader.MelonPlatformDomainAttribute.CompatibleDomains.MONO)]

View file

@ -18,5 +18,6 @@ Available mod's settings in `Settings - Implementation - Leap Motion Tracking`:
* **Attach to head:** attach hands transformation to head instead of body, disabled by default. * **Attach to head:** attach hands transformation to head instead of body, disabled by default.
* **Head offset X/Y/Z:** offset position for head attachment (`Attach to head` is **`true`**), (0, -30, 15) by default. * **Head offset X/Y/Z:** offset position for head attachment (`Attach to head` is **`true`**), (0, -30, 15) by default.
* **Offset angle:** rotation around X axis, useful for neck mounts, 0 by default. * **Offset angle:** rotation around X axis, useful for neck mounts, 0 by default.
* **Track elbows:** elbows tracking, works best in `Screentop` and `HMD` tracking modes, `true` by default.
* **Fingers tracking only:** apply only fingers tracking, disabled by default. * **Fingers tracking only:** apply only fingers tracking, disabled by default.
* **Model visibility:** show Leap Motion controller model, useful for tracking visualizing, disabled by default. * **Model visibility:** show Leap Motion controller model, useful for tracking visualizing, disabled by default.

View file

@ -28,7 +28,8 @@ namespace ml_lme
Head, Head,
HeadX, HeadX,
HeadY, HeadY,
HeadZ HeadZ,
TrackElbows
}; };
static bool ms_enabled = false; static bool ms_enabled = false;
@ -39,6 +40,7 @@ namespace ml_lme
static float ms_rootAngle = 0f; static float ms_rootAngle = 0f;
static bool ms_headAttach = false; static bool ms_headAttach = false;
static Vector3 ms_headOffset = new Vector3(0f, -0.3f, 0.15f); static Vector3 ms_headOffset = new Vector3(0f, -0.3f, 0.15f);
static bool ms_trackElbows = true;
static MelonLoader.MelonPreferences_Category ms_category = null; static MelonLoader.MelonPreferences_Category ms_category = null;
static List<MelonLoader.MelonPreferences_Entry> ms_entries = null; static List<MelonLoader.MelonPreferences_Entry> ms_entries = null;
@ -51,6 +53,7 @@ namespace ml_lme
static public event Action<float> RootAngleChange; static public event Action<float> RootAngleChange;
static public event Action<bool> HeadAttachChange; static public event Action<bool> HeadAttachChange;
static public event Action<Vector3> HeadOffsetChange; static public event Action<Vector3> HeadOffsetChange;
static public event Action<bool> TrackElbowsChange;
public static void Init() public static void Init()
{ {
@ -69,6 +72,7 @@ namespace ml_lme
ms_entries.Add(ms_category.CreateEntry(ModSetting.HeadX.ToString(), 0)); ms_entries.Add(ms_category.CreateEntry(ModSetting.HeadX.ToString(), 0));
ms_entries.Add(ms_category.CreateEntry(ModSetting.HeadY.ToString(), -30)); ms_entries.Add(ms_category.CreateEntry(ModSetting.HeadY.ToString(), -30));
ms_entries.Add(ms_category.CreateEntry(ModSetting.HeadZ.ToString(), 15)); ms_entries.Add(ms_category.CreateEntry(ModSetting.HeadZ.ToString(), 15));
ms_entries.Add(ms_category.CreateEntry(ModSetting.TrackElbows.ToString(), true));
Load(); Load();
@ -116,6 +120,7 @@ namespace ml_lme
(int)ms_entries[(int)ModSetting.HeadY].BoxedValue, (int)ms_entries[(int)ModSetting.HeadY].BoxedValue,
(int)ms_entries[(int)ModSetting.HeadZ].BoxedValue (int)ms_entries[(int)ModSetting.HeadZ].BoxedValue
) * 0.01f; ) * 0.01f;
ms_trackElbows = (bool)ms_entries[(int)ModSetting.TrackElbows].BoxedValue;
} }
static void OnToggleUpdate(string p_name, string p_value) static void OnToggleUpdate(string p_name, string p_value)
@ -151,6 +156,12 @@ namespace ml_lme
HeadAttachChange?.Invoke(ms_headAttach); HeadAttachChange?.Invoke(ms_headAttach);
} }
break; break;
case ModSetting.TrackElbows:
{
ms_trackElbows = bool.Parse(p_value);
TrackElbowsChange?.Invoke(ms_trackElbows);
} break;
} }
ms_entries[(int)l_setting].BoxedValue = bool.Parse(p_value); ms_entries[(int)l_setting].BoxedValue = bool.Parse(p_value);
@ -235,40 +246,37 @@ namespace ml_lme
{ {
get => ms_enabled; get => ms_enabled;
} }
public static Vector3 DesktopOffset public static Vector3 DesktopOffset
{ {
get => ms_desktopOffset; get => ms_desktopOffset;
} }
public static bool FingersOnly public static bool FingersOnly
{ {
get => ms_fingersOnly; get => ms_fingersOnly;
} }
public static bool ModelVisibility public static bool ModelVisibility
{ {
get => ms_modelVisibility; get => ms_modelVisibility;
} }
public static LeapTrackingMode TrackingMode public static LeapTrackingMode TrackingMode
{ {
get => ms_trackingMode; get => ms_trackingMode;
} }
public static float RootAngle public static float RootAngle
{ {
get => ms_rootAngle; get => ms_rootAngle;
} }
public static bool HeadAttach public static bool HeadAttach
{ {
get => ms_headAttach; get => ms_headAttach;
} }
public static Vector3 HeadOffset public static Vector3 HeadOffset
{ {
get => ms_headOffset; get => ms_headOffset;
} }
public static bool TrackElbows
{
get => ms_trackElbows;
}
} }
} }

View file

@ -10,7 +10,7 @@ namespace ml_lme
public static Matrix4x4 GetMatrix(this Transform p_transform, bool p_pos = true, bool p_rot = true, bool p_scl = false) public static Matrix4x4 GetMatrix(this Transform p_transform, bool p_pos = true, bool p_rot = true, bool p_scl = false)
{ {
return Matrix4x4.TRS(p_pos ? p_transform.position : Vector3.zero, p_rot ? p_transform.rotation : Quaternion.identity, p_scl ? p_transform.localScale : Vector3.one); return Matrix4x4.TRS(p_pos ? p_transform.position : Vector3.zero, p_rot ? p_transform.rotation : Quaternion.identity, p_scl ? p_transform.lossyScale : Vector3.one);
} }
public static void Swap<T>(ref T lhs, ref T rhs) public static void Swap<T>(ref T lhs, ref T rhs)

View file

@ -84,7 +84,6 @@
<Compile Include="AssetsHandler.cs" /> <Compile Include="AssetsHandler.cs" />
<Compile Include="DependenciesHandler.cs" /> <Compile Include="DependenciesHandler.cs" />
<Compile Include="GestureMatcher.cs" /> <Compile Include="GestureMatcher.cs" />
<Compile Include="LeapIK.cs" />
<Compile Include="LeapTracked.cs" /> <Compile Include="LeapTracked.cs" />
<Compile Include="Main.cs" /> <Compile Include="Main.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />

View file

@ -350,6 +350,13 @@ function inp_dropdown_mod_lme(_obj, _callbackName) {
</div> </div>
</div> </div>
<div class ="row-wrapper">
<div class ="option-caption">Track elbows: </div>
<div class ="option-input">
<div id="TrackElbows" class ="inp_toggle no-scroll" data-current="true"></div>
</div>
</div>
<div class ="row-wrapper"> <div class ="row-wrapper">
<div class ="option-caption">Fingers tracking only: </div> <div class ="option-caption">Fingers tracking only: </div>
<div class ="option-input"> <div class ="option-input">