Input implementation

Internal keyword usage (wow!)
This commit is contained in:
SDraw 2023-01-28 16:33:54 +03:00
parent 9afa795626
commit 8737f61bdc
No known key found for this signature in database
GPG key ID: BB95B4DAB2BB8BB5
22 changed files with 1037 additions and 623 deletions

View file

@ -9,6 +9,6 @@ Merged set of MelonLoader mods for ChilloutVR.
| Desktop Reticle Switch | ml_drs | 1.0.0 | Yes | Working |
| Extended Game Notifications | ml_egn | 1.0.0 | Yes | Working
| Four Point Tracking | ml_fpt | 1.0.9 | Retired | Deprecated | In-game feature since 2022r170 update
| Leap Motion Extension | ml_lme | 1.2.9 | Yes | Working |
| Leap Motion Extension | ml_lme | 1.3.0 | Yes, update review | Working |
| Pickup Arm Movement | ml_pam | 1.0.0 | Yes | Working |
| Server Connection Info | ml_sci | 1.0.2 | Retired | Retired | Superseded by `Extended Game Notifications`

View file

@ -4,6 +4,7 @@ using ABI_RC.Systems.IK.SubSystems;
using ABI_RC.Systems.MovementSystem;
using System.Reflection;
using UnityEngine;
using System.Collections;
namespace ml_amt
{
@ -71,7 +72,7 @@ namespace ml_amt
MelonLoader.MelonCoroutines.Start(WaitForLocalPlayer());
}
System.Collections.IEnumerator WaitForLocalPlayer()
IEnumerator WaitForLocalPlayer()
{
while(PlayerSetup.Instance == null)
yield return null;

View file

@ -66,7 +66,7 @@ namespace ml_amt
readonly List<AvatarParameter> m_parameters = null;
public MotionTweaker()
internal MotionTweaker()
{
m_parameters = new List<AvatarParameter>();
}
@ -159,7 +159,7 @@ namespace ml_amt
}
}
public void OnAvatarClear()
internal void OnAvatarClear()
{
m_vrIk = null;
m_locomotionLayer = -1;
@ -181,7 +181,7 @@ namespace ml_amt
m_parameters.Clear();
}
public void OnSetupAvatar()
internal void OnSetupAvatar()
{
m_isInVR = Utils.IsInVR();
m_vrIk = PlayerSetup.Instance._avatar.GetComponent<VRIK>();
@ -239,7 +239,7 @@ namespace ml_amt
m_avatarReady = true;
}
public void OnCalibrate()
internal void OnCalibrate()
{
if(m_avatarReady && BodySystem.isCalibratedAsFullBody && BodySystem.enableHipTracking && !BodySystem.enableRightFootTracking && !BodySystem.enableLeftFootTracking && !BodySystem.enableLeftKneeTracking && !BodySystem.enableRightKneeTracking)
{

View file

@ -49,22 +49,24 @@ namespace ml_amt
static public event Action<bool> FollowHipsChange;
static public event Action<bool> CollisionScaleChange;
public static void Init()
internal static void Init()
{
ms_category = MelonLoader.MelonPreferences.CreateCategory("AMT");
ms_entries = new List<MelonLoader.MelonPreferences_Entry>();
ms_entries.Add(ms_category.CreateEntry(ModSetting.IKOverrideCrouch.ToString(), true));
ms_entries.Add(ms_category.CreateEntry(ModSetting.CrouchLimit.ToString(), 65));
ms_entries.Add(ms_category.CreateEntry(ModSetting.IKOverrideProne.ToString(), true));
ms_entries.Add(ms_category.CreateEntry(ModSetting.ProneLimit.ToString(), 30));
ms_entries.Add(ms_category.CreateEntry(ModSetting.PoseTransitions.ToString(), true));
ms_entries.Add(ms_category.CreateEntry(ModSetting.AdjustedMovement.ToString(), true));
ms_entries.Add(ms_category.CreateEntry(ModSetting.IKOverrideFly.ToString(), true));
ms_entries.Add(ms_category.CreateEntry(ModSetting.IKOverrideJump.ToString(), true));
ms_entries.Add(ms_category.CreateEntry(ModSetting.DetectEmotes.ToString(), true));
ms_entries.Add(ms_category.CreateEntry(ModSetting.FollowHips.ToString(), true));
ms_entries.Add(ms_category.CreateEntry(ModSetting.CollisionScale.ToString(), true));
ms_entries = new List<MelonLoader.MelonPreferences_Entry>()
{
ms_category.CreateEntry(ModSetting.IKOverrideCrouch.ToString(), true),
ms_category.CreateEntry(ModSetting.CrouchLimit.ToString(), 65),
ms_category.CreateEntry(ModSetting.IKOverrideProne.ToString(), true),
ms_category.CreateEntry(ModSetting.ProneLimit.ToString(), 30),
ms_category.CreateEntry(ModSetting.PoseTransitions.ToString(), true),
ms_category.CreateEntry(ModSetting.AdjustedMovement.ToString(), true),
ms_category.CreateEntry(ModSetting.IKOverrideFly.ToString(), true),
ms_category.CreateEntry(ModSetting.IKOverrideJump.ToString(), true),
ms_category.CreateEntry(ModSetting.DetectEmotes.ToString(), true),
ms_category.CreateEntry(ModSetting.FollowHips.ToString(), true),
ms_category.CreateEntry(ModSetting.CollisionScale.ToString(), true)
};
Load();

View file

@ -77,7 +77,7 @@ namespace ml_dht
}
}
public void OnEyeControllerUpdate(CVREyeController p_component)
internal void OnEyeControllerUpdate(CVREyeController p_component)
{
if(m_enabled)
{
@ -99,7 +99,7 @@ namespace ml_dht
}
}
public void OnFaceTrackingUpdate(CVRFaceTracking p_component)
internal void OnFaceTrackingUpdate(CVRFaceTracking p_component)
{
if(m_enabled && m_faceOverride)
{
@ -117,7 +117,7 @@ namespace ml_dht
}
}
public void OnSetupAvatar()
internal void OnSetupAvatar()
{
m_avatarDescriptor = PlayerSetup.Instance._avatar.GetComponent<CVRAvatar>();
m_headBone = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.Head);
@ -130,7 +130,7 @@ namespace ml_dht
m_lookIK.solver.OnPostUpdate += this.OnLookIKPostUpdate;
}
public void OnAvatarClear()
internal void OnAvatarClear()
{
m_avatarDescriptor = null;
m_lookIK = null;

View file

@ -37,18 +37,20 @@ namespace ml_dht
static public event Action<float> SmoothingChange;
static public event Action<bool> FaceOverrideChange;
public static void Init()
internal static void Init()
{
ms_category = MelonLoader.MelonPreferences.CreateCategory("DHT");
ms_entries = new List<MelonLoader.MelonPreferences_Entry>();
ms_entries.Add(ms_category.CreateEntry(ModSetting.Enabled.ToString(), false));
ms_entries.Add(ms_category.CreateEntry(ModSetting.HeadTracking.ToString(), false));
ms_entries.Add(ms_category.CreateEntry(ModSetting.EyeTracking.ToString(), true));
ms_entries.Add(ms_category.CreateEntry(ModSetting.Blinking.ToString(), true));
ms_entries.Add(ms_category.CreateEntry(ModSetting.Mirrored.ToString(), false));
ms_entries.Add(ms_category.CreateEntry(ModSetting.Smoothing.ToString(), 50));
ms_entries.Add(ms_category.CreateEntry(ModSetting.FaceOverride.ToString(), true));
ms_entries = new List<MelonLoader.MelonPreferences_Entry>()
{
ms_category.CreateEntry(ModSetting.Enabled.ToString(), false),
ms_category.CreateEntry(ModSetting.HeadTracking.ToString(), false),
ms_category.CreateEntry(ModSetting.EyeTracking.ToString(), true),
ms_category.CreateEntry(ModSetting.Blinking.ToString(), true),
ms_category.CreateEntry(ModSetting.Mirrored.ToString(), false),
ms_category.CreateEntry(ModSetting.Smoothing.ToString(), 50),
ms_category.CreateEntry(ModSetting.FaceOverride.ToString(), true)
};
Load();
@ -130,13 +132,15 @@ namespace ml_dht
{
ms_eyeTracking = bool.Parse(p_value);
EyeTrackingChange?.Invoke(ms_eyeTracking);
} break;
}
break;
case ModSetting.Blinking:
{
ms_blinking = bool.Parse(p_value);
BlinkingChange?.Invoke(ms_blinking);
} break;
}
break;
case ModSetting.Mirrored:
{
@ -149,7 +153,8 @@ namespace ml_dht
{
ms_faceOverride = bool.Parse(p_value);
FaceOverrideChange?.Invoke(ms_faceOverride);
} break;
}
break;
}
ms_entries[(int)l_setting].BoxedValue = bool.Parse(p_value);

View file

@ -88,6 +88,6 @@
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PostBuildEvent>copy /y "$(TargetPath)" "C:\Games\Steam\common\ChilloutVR\Mods\"</PostBuildEvent>
<PostBuildEvent>copy /y "$(TargetPath)" "D:\Games\Steam\steamapps\common\ChilloutVR\Mods\"</PostBuildEvent>
</PropertyGroup>
</Project>

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ReferencePath>C:\Games\Steam\common\ChilloutVR\MelonLoader\;C:\Games\Steam\common\ChilloutVR\ChilloutVR_Data\Managed\</ReferencePath>
<ReferencePath>D:\Games\Steam\steamapps\common\ChilloutVR\MelonLoader\;D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\</ReferencePath>
</PropertyGroup>
</Project>

View file

@ -59,7 +59,7 @@ namespace ml_egn
else
{
if(Utils.IsMenuOpened())
ViewManager.Instance.TriggerAlert("Prop Error", "Not connected to live instance", -1, true);
Utils.ShowMenuAlert("Prop Error", "Not connected to live instance");
else
Utils.ShowHUDNotification("(Local) Client", "Unable to spawn prop", "Not connected to live instance");
}
@ -70,7 +70,7 @@ namespace ml_egn
}
}
static void OnGameNetworkConnectionClosed(object __0, DisconnectedEventArgs __1)
static void OnGameNetworkConnectionClosed(DisconnectedEventArgs __1)
{
try
{

View file

@ -14,100 +14,75 @@ namespace ml_lme
new Vector2(-10f, 25f)
};
public class GesturesData
public class HandData
{
readonly public static int ms_handsCount = 2;
readonly public static int ms_fingersCount = 5;
public bool m_present = false;
public Vector3 m_position = Vector3.zero;
public Quaternion m_rotation = Quaternion.identity;
public Vector3 m_elbowPosition = Vector3.zero;
public readonly float[] m_spreads = null;
public readonly float[] m_bends = null;
public float m_grabStrength = 0f;
public bool[] m_handsPresenses = null;
public Vector3[] m_handsPositons = null;
public Quaternion[] m_handsRotations = null;
public Vector3[] m_elbowsPositions = null;
public float[] m_leftFingersBends = null;
public float[] m_leftFingersSpreads = null;
public float[] m_rightFingersBends = null;
public float[] m_rightFingersSpreads = null;
public GesturesData()
public HandData()
{
m_handsPresenses = new bool[ms_handsCount];
m_handsPositons = new Vector3[ms_handsCount];
m_handsRotations = new Quaternion[ms_handsCount];
m_elbowsPositions = new Vector3[ms_handsCount];
m_leftFingersBends = new float[ms_fingersCount];
m_leftFingersSpreads = new float[ms_fingersCount];
m_rightFingersBends = new float[ms_fingersCount];
m_rightFingersSpreads = new float[ms_fingersCount];
m_spreads = new float[5];
m_bends = new float[5];
}
public void Reset()
{
m_present = false;
for(int i = 0; i < 5; i++)
{
m_bends[i] = 0f;
m_spreads[i] = 0f;
}
m_grabStrength = 0f;
}
}
public static void GetGestures(Leap.Frame p_frame, ref GesturesData p_data)
public class LeapData
{
// Fill as default
for(int i = 0; i < GesturesData.ms_handsCount; i++)
p_data.m_handsPresenses[i] = false;
for(int i = 0; i < GesturesData.ms_fingersCount; i++)
public readonly HandData m_leftHand = null;
public readonly HandData m_rightHand = null;
public LeapData()
{
p_data.m_leftFingersBends[i] = 0f;
p_data.m_leftFingersSpreads[i] = 0f;
p_data.m_rightFingersBends[i] = 0f;
p_data.m_leftFingersSpreads[i] = 0f;
m_leftHand = new HandData();
m_rightHand = new HandData();
}
public void Reset()
{
m_leftHand.Reset();
m_rightHand.Reset();
}
}
public static void GetFrameData(Leap.Frame p_frame, LeapData p_data)
{
p_data.Reset();
// Fill hands data
foreach(Leap.Hand l_hand in p_frame.Hands)
{
int l_sideID = (l_hand.IsLeft ? 0 : 1);
if(!p_data.m_handsPresenses[l_sideID])
{
p_data.m_handsPresenses[l_sideID] = true;
FillHandPosition(l_hand, ref p_data.m_handsPositons[l_sideID]);
FillHandRotation(l_hand, ref p_data.m_handsRotations[l_sideID]);
FillElbowPosition(l_hand, ref p_data.m_elbowsPositions[l_sideID]);
switch(l_sideID)
{
case 0:
{
FillFingerBends(l_hand, ref p_data.m_leftFingersBends);
FilFingerSpreads(l_hand, ref p_data.m_leftFingersSpreads);
}
break;
case 1:
{
FillFingerBends(l_hand, ref p_data.m_rightFingersBends);
FilFingerSpreads(l_hand, ref p_data.m_rightFingersSpreads);
}
break;
}
}
if(l_hand.IsLeft && !p_data.m_leftHand.m_present)
FillHandData(l_hand, p_data.m_leftHand);
if(l_hand.IsRight && !p_data.m_rightHand.m_present)
FillHandData(l_hand, p_data.m_rightHand);
}
}
static void FillHandPosition(Leap.Hand p_hand, ref Vector3 p_pos)
static void FillHandData(Leap.Hand p_hand, HandData p_data)
{
// Unity's IK and FinalIK move hand bones to target, therefore - wrist
p_pos.x = p_hand.WristPosition.x;
p_pos.y = p_hand.WristPosition.y;
p_pos.z = p_hand.WristPosition.z;
}
p_data.m_present = true;
p_data.m_position.Set(p_hand.WristPosition.x, p_hand.WristPosition.y, p_hand.WristPosition.z);
p_data.m_rotation.Set(p_hand.Rotation.x, p_hand.Rotation.y, p_hand.Rotation.z, p_hand.Rotation.w);
p_data.m_elbowPosition.Set(p_hand.Arm.ElbowPosition.x, p_hand.Arm.ElbowPosition.y, p_hand.Arm.ElbowPosition.z);
static void FillHandRotation(Leap.Hand p_hand, ref Quaternion p_rot)
{
p_rot.x = p_hand.Rotation.x;
p_rot.y = p_hand.Rotation.y;
p_rot.z = p_hand.Rotation.z;
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)
{
// Bends
foreach(Leap.Finger l_finger in p_hand.Fingers)
{
Quaternion l_prevSegment = Quaternion.identity;
@ -132,12 +107,10 @@ namespace ml_lme
l_angle += l_curAngle;
}
p_bends[(int)l_finger.Type] = Mathf.InverseLerp(0f, (l_finger.Type == Leap.Finger.FingerType.TYPE_THUMB) ? 90f : 180f, l_angle);
}
p_data.m_bends[(int)l_finger.Type] = Mathf.InverseLerp(0f, (l_finger.Type == Leap.Finger.FingerType.TYPE_THUMB) ? 90f : 180f, l_angle);
}
static void FilFingerSpreads(Leap.Hand p_hand, ref float[] p_spreads)
{
// Spreads
foreach(Leap.Finger l_finger in p_hand.Fingers)
{
float l_angle = 0f;
@ -162,13 +135,15 @@ namespace ml_lme
if(l_finger.Type != Leap.Finger.FingerType.TYPE_THUMB)
{
if(l_angle < 0f)
p_spreads[(int)l_finger.Type] = 0.5f * Mathf.InverseLerp(ms_fingerLimits[(int)l_finger.Type].x, 0f, l_angle);
p_data.m_spreads[(int)l_finger.Type] = 0.5f * Mathf.InverseLerp(ms_fingerLimits[(int)l_finger.Type].x, 0f, l_angle);
else
p_spreads[(int)l_finger.Type] = 0.5f + 0.5f * Mathf.InverseLerp(0f, ms_fingerLimits[(int)l_finger.Type].y, l_angle);
p_data.m_spreads[(int)l_finger.Type] = 0.5f + 0.5f * Mathf.InverseLerp(0f, ms_fingerLimits[(int)l_finger.Type].y, l_angle);
}
else
p_spreads[(int)l_finger.Type] = Mathf.InverseLerp(ms_fingerLimits[(int)l_finger.Type].x, ms_fingerLimits[(int)l_finger.Type].y, l_angle);
p_data.m_spreads[(int)l_finger.Type] = Mathf.InverseLerp(ms_fingerLimits[(int)l_finger.Type].x, ms_fingerLimits[(int)l_finger.Type].y, l_angle);
}
p_data.m_grabStrength = (p_data.m_bends[1] + p_data.m_bends[2] + p_data.m_bends[3] + p_data.m_bends[4]) * 0.25f;
}
}
}

247
ml_lme/LeapInput.cs Normal file
View file

@ -0,0 +1,247 @@
using ABI_RC.Core.InteractionSystem;
using ABI_RC.Core.Player;
using ABI_RC.Core.Savior;
using ABI_RC.Systems.IK;
using System.Collections;
using System.Reflection;
using UnityEngine;
namespace ml_lme
{
[DisallowMultipleComponent]
class LeapInput : CVRInputModule
{
static readonly FieldInfo ms_indexGestureToggle = typeof(InputModuleSteamVR).GetField("_steamVrIndexGestureToggleValue", BindingFlags.Instance | BindingFlags.NonPublic);
CVRInputManager m_inputManager = null;
InputModuleSteamVR m_steamVrModule = null;
bool m_inVR = false;
ControllerRay m_handRayLeft = null;
ControllerRay m_handRayRight = null;
LineRenderer m_lineLeft = null;
LineRenderer m_lineRight = null;
bool m_interactLeft = false;
bool m_interactRight = false;
bool m_gripLeft = false;
bool m_gripRight = false;
public new void Start()
{
base.Start();
m_inputManager = CVRInputManager.Instance; // _inputManager is stripped out, cool beans
m_steamVrModule = this.GetComponent<InputModuleSteamVR>();
m_inVR = Utils.IsInVR();
m_handRayLeft = LeapTracking.GetInstance().GetLeftHand().gameObject.AddComponent<ControllerRay>();
m_handRayLeft.hand = true;
m_handRayLeft.generalMask = -1485;
m_handRayLeft.isInteractionRay = true;
m_handRayLeft.triggerGazeEvents = false;
m_handRayLeft.holderRoot = m_handRayLeft.gameObject;
m_handRayRight = LeapTracking.GetInstance().GetRightHand().gameObject.AddComponent<ControllerRay>();
m_handRayRight.hand = false;
m_handRayRight.generalMask = -1485;
m_handRayRight.isInteractionRay = true;
m_handRayRight.triggerGazeEvents = false;
m_handRayRight.holderRoot = m_handRayRight.gameObject;
m_lineLeft = m_handRayLeft.gameObject.AddComponent<LineRenderer>();
m_lineLeft.endWidth = 1f;
m_lineLeft.startWidth = 1f;
m_lineLeft.textureMode = LineTextureMode.Tile;
m_lineLeft.useWorldSpace = false;
m_lineLeft.widthMultiplier = 1f;
m_lineLeft.allowOcclusionWhenDynamic = false;
m_lineLeft.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;
m_lineLeft.enabled = false;
m_lineLeft.receiveShadows = false;
m_handRayLeft.lineRenderer = m_lineLeft;
m_lineRight = m_handRayRight.gameObject.AddComponent<LineRenderer>();
m_lineRight.endWidth = 1f;
m_lineRight.startWidth = 1f;
m_lineRight.textureMode = LineTextureMode.Tile;
m_lineRight.useWorldSpace = false;
m_lineRight.widthMultiplier = 1f;
m_lineRight.allowOcclusionWhenDynamic = false;
m_lineRight.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;
m_lineRight.enabled = false;
m_lineRight.receiveShadows = false;
m_handRayRight.lineRenderer = m_lineRight;
Settings.EnabledChange += this.OnEnableChange;
Settings.InputChange += this.OnInputChange;
OnEnableChange(Settings.Enabled);
OnInputChange(Settings.Input);
MelonLoader.MelonCoroutines.Start(WaitForMaterial());
}
IEnumerator WaitForMaterial()
{
while(PlayerSetup.Instance == null)
yield return null;
while(PlayerSetup.Instance.leftRay == null)
yield return null;
while(PlayerSetup.Instance.leftRay.lineRenderer == null)
yield return null;
m_lineLeft.material = PlayerSetup.Instance.leftRay.lineRenderer.material;
m_lineRight.material = PlayerSetup.Instance.leftRay.lineRenderer.material;
}
void OnDestroy()
{
Settings.EnabledChange -= this.OnEnableChange;
Settings.InputChange -= this.OnInputChange;
}
void Update()
{
GestureMatcher.LeapData l_data = LeapManager.GetInstance().GetLatestData();
if(Settings.Enabled)
{
if(l_data.m_leftHand.m_present)
SetFingersInput(l_data.m_leftHand, true);
if(l_data.m_rightHand.m_present)
SetFingersInput(l_data.m_rightHand, false);
}
m_handRayLeft.enabled = (l_data.m_leftHand.m_present && (!m_inVR || (VRTrackerManager.Instance.leftHand == null) || !VRTrackerManager.Instance.leftHand.active || !Settings.FingersOnly));
m_handRayRight.enabled = (l_data.m_rightHand.m_present && (!m_inVR || (VRTrackerManager.Instance.rightHand == null) || !VRTrackerManager.Instance.rightHand.active || !Settings.FingersOnly));
}
public override void UpdateInput()
{
if(Settings.Enabled && Settings.Input)
{
GestureMatcher.LeapData l_data = LeapManager.GetInstance().GetLatestData();
if(l_data.m_leftHand.m_present && (!m_inVR || (VRTrackerManager.Instance.leftHand == null) || !VRTrackerManager.Instance.leftHand.active || !Settings.FingersOnly))
{
float l_strength = l_data.m_leftHand.m_grabStrength;
m_inputManager.interactLeftValue = l_strength;
if(m_interactLeft != (l_strength > Settings.HoldThreadhold))
{
m_interactLeft = (l_strength > Settings.HoldThreadhold);
m_inputManager.interactLeftUp |= m_interactLeft;
m_inputManager.interactLeftDown |= m_interactLeft;
}
m_inputManager.gripLeftValue = 1f - l_strength; // Inversed
if(m_gripLeft != (l_strength < Settings.ReleaseThreadhold))
{
m_gripLeft = (l_strength < Settings.ReleaseThreadhold);
m_inputManager.gripLeftUp |= m_gripLeft;
m_inputManager.gripLeftDown |= m_gripLeft;
}
}
if(l_data.m_rightHand.m_present && (!m_inVR || (VRTrackerManager.Instance.rightHand == null) || !VRTrackerManager.Instance.rightHand.active || !Settings.FingersOnly))
{
float l_strength = l_data.m_rightHand.m_grabStrength;
m_inputManager.interactRightValue = l_strength;
if(m_interactRight != (l_strength > Settings.HoldThreadhold))
{
m_interactRight = (l_strength > Settings.HoldThreadhold);
m_inputManager.interactRightUp |= m_interactRight;
m_inputManager.interactRightDown |= m_interactRight;
}
m_inputManager.gripRightValue = 1f - l_strength;
if(m_gripRight != (l_strength < Settings.HoldThreadhold))
{
m_gripRight = (l_strength < Settings.HoldThreadhold);
m_inputManager.gripRightUp |= m_gripRight;
m_inputManager.gripRightDown |= m_gripRight;
}
}
}
}
// Settings changes
void OnEnableChange(bool p_state)
{
OnInputChange(p_state && Settings.Input);
UpdateFingerTracking();
}
void OnInputChange(bool p_state)
{
(m_handRayLeft as MonoBehaviour).enabled = (p_state && Settings.Enabled);
(m_handRayRight as MonoBehaviour).enabled = (p_state && Settings.Enabled);
m_lineLeft.enabled = (p_state && Settings.Enabled);
m_lineRight.enabled = (p_state && Settings.Enabled);
if(!p_state)
{
m_handRayLeft.DropObject(true);
m_handRayLeft.ClearGrabbedObject();
m_handRayRight.DropObject(true);
m_handRayRight.ClearGrabbedObject();
m_interactLeft = false;
m_interactRight = false;
m_gripLeft = false;
m_gripRight = false;
}
}
// Game events
internal void OnAvatarSetup()
{
m_inVR = Utils.IsInVR();
UpdateFingerTracking();
}
internal void OnRayScale(float p_scale)
{
m_handRayLeft.SetRayScale(p_scale);
m_handRayRight.SetRayScale(p_scale);
}
// Arbitrary
void UpdateFingerTracking()
{
m_inputManager.individualFingerTracking = (Settings.Enabled || (m_inVR && Utils.AreKnucklesInUse() && !(bool)ms_indexGestureToggle.GetValue(m_steamVrModule)));
IKSystem.Instance.FingerSystem.controlActive = m_inputManager.individualFingerTracking;
}
void SetFingersInput(GestureMatcher.HandData p_hand, bool p_left)
{
m_inputManager.individualFingerTracking = true;
IKSystem.Instance.FingerSystem.controlActive = true;
if(p_left)
{
m_inputManager.fingerCurlLeftThumb = p_hand.m_bends[0];
m_inputManager.fingerCurlLeftIndex = p_hand.m_bends[1];
m_inputManager.fingerCurlLeftMiddle = p_hand.m_bends[2];
m_inputManager.fingerCurlLeftRing = p_hand.m_bends[3];
m_inputManager.fingerCurlLeftPinky = p_hand.m_bends[4];
IKSystem.Instance.FingerSystem.leftThumbCurl = p_hand.m_bends[0];
IKSystem.Instance.FingerSystem.leftIndexCurl = p_hand.m_bends[1];
IKSystem.Instance.FingerSystem.leftMiddleCurl = p_hand.m_bends[2];
IKSystem.Instance.FingerSystem.leftRingCurl = p_hand.m_bends[3];
IKSystem.Instance.FingerSystem.leftPinkyCurl = p_hand.m_bends[4];
}
else
{
m_inputManager.fingerCurlRightThumb = p_hand.m_bends[0];
m_inputManager.fingerCurlRightIndex = p_hand.m_bends[1];
m_inputManager.fingerCurlRightMiddle = p_hand.m_bends[2];
m_inputManager.fingerCurlRightRing = p_hand.m_bends[3];
m_inputManager.fingerCurlRightPinky = p_hand.m_bends[4];
IKSystem.Instance.FingerSystem.rightThumbCurl = p_hand.m_bends[0];
IKSystem.Instance.FingerSystem.rightIndexCurl = p_hand.m_bends[1];
IKSystem.Instance.FingerSystem.rightMiddleCurl = p_hand.m_bends[2];
IKSystem.Instance.FingerSystem.rightRingCurl = p_hand.m_bends[3];
IKSystem.Instance.FingerSystem.rightPinkyCurl = p_hand.m_bends[4];
}
}
}
}

204
ml_lme/LeapManager.cs Normal file
View file

@ -0,0 +1,204 @@
using ABI_RC.Core.Player;
using ABI_RC.Core.Savior;
using System.Collections;
using UnityEngine;
namespace ml_lme
{
[DisallowMultipleComponent]
class LeapManager : MonoBehaviour
{
static LeapManager ms_instance = null;
readonly Leap.Controller m_leapController = null;
readonly GestureMatcher.LeapData m_leapData = null;
LeapTracking m_leapTracking = null;
LeapTracked m_leapTracked = null;
LeapInput m_leapInput = null;
public static LeapManager GetInstance() => ms_instance;
internal LeapManager()
{
m_leapController = new Leap.Controller();
m_leapData = new GestureMatcher.LeapData();
}
~LeapManager()
{
m_leapController.StopConnection();
m_leapController.Dispose();
}
void Start()
{
if(ms_instance == null)
ms_instance = this;
DontDestroyOnLoad(this);
m_leapController.Device += this.OnLeapDeviceInitialized;
m_leapController.DeviceFailure += this.OnLeapDeviceFailure;
m_leapController.DeviceLost += this.OnLeapDeviceLost;
m_leapController.Connect += this.OnLeapServiceConnect;
m_leapController.Disconnect += this.OnLeapServiceDisconnect;
Settings.EnabledChange += this.OnEnableChange;
Settings.TrackingModeChange += this.OnTrackingModeChange;
m_leapTracking = new GameObject("[LeapTrackingRoot]").AddComponent<LeapTracking>();
m_leapTracking.transform.parent = this.transform;
MelonLoader.MelonCoroutines.Start(WaitForInputManager());
MelonLoader.MelonCoroutines.Start(WaitForLocalPlayer());
OnEnableChange(Settings.Enabled);
OnTrackingModeChange(Settings.TrackingMode);
}
void OnDestroy()
{
if(ms_instance == this)
ms_instance = null;
m_leapController.Device -= this.OnLeapDeviceInitialized;
m_leapController.DeviceFailure -= this.OnLeapDeviceFailure;
m_leapController.DeviceLost -= this.OnLeapDeviceLost;
m_leapController.Connect -= this.OnLeapServiceConnect;
m_leapController.Disconnect -= this.OnLeapServiceDisconnect;
Settings.EnabledChange -= this.OnEnableChange;
Settings.TrackingModeChange -= this.OnTrackingModeChange;
}
IEnumerator WaitForInputManager()
{
while(CVRInputManager.Instance == null)
yield return null;
m_leapInput = CVRInputManager.Instance.gameObject.AddComponent<LeapInput>();
}
IEnumerator WaitForLocalPlayer()
{
while(PlayerSetup.Instance == null)
yield return null;
m_leapTracked = PlayerSetup.Instance.gameObject.AddComponent<LeapTracked>();
}
void Update()
{
if(Settings.Enabled)
{
m_leapData.Reset();
if(m_leapController.IsConnected)
{
Leap.Frame l_frame = m_leapController.Frame();
GestureMatcher.GetFrameData(l_frame, m_leapData);
}
}
}
public GestureMatcher.LeapData GetLatestData() => m_leapData;
// Device events
void OnLeapDeviceInitialized(object p_sender, Leap.DeviceEventArgs p_args)
{
if(Settings.Enabled)
{
m_leapController.SubscribeToDeviceEvents(p_args.Device);
UpdateDeviceTrackingMode();
}
Utils.ShowHUDNotification("Leap Motion Extension", "Device initialized");
}
void OnLeapDeviceFailure(object p_sender, Leap.DeviceFailureEventArgs p_args)
{
Utils.ShowHUDNotification("Leap Motion Extension", "Device failure", "Code " + p_args.ErrorCode + ": " + p_args.ErrorMessage);
}
void OnLeapDeviceLost(object p_sender, Leap.DeviceEventArgs p_args)
{
m_leapController.UnsubscribeFromDeviceEvents(p_args.Device);
Utils.ShowHUDNotification("Leap Motion Extension", "Device lost");
}
void OnLeapServiceConnect(object p_sender, Leap.ConnectionEventArgs p_args)
{
Utils.ShowHUDNotification("Leap Motion Extension", "Service connected");
}
void OnLeapServiceDisconnect(object p_sender, Leap.ConnectionLostEventArgs p_args)
{
Utils.ShowHUDNotification("Leap Motion Extension", "Service disconnected");
}
// Settings
void OnEnableChange(bool p_state)
{
if(p_state)
{
m_leapController.StartConnection();
UpdateDeviceTrackingMode();
}
else
m_leapController.StopConnection();
}
void OnTrackingModeChange(Settings.LeapTrackingMode p_mode)
{
if(Settings.Enabled)
UpdateDeviceTrackingMode();
}
// Game events
internal void OnAvatarClear()
{
if(m_leapTracked != null)
m_leapTracked.OnAvatarClear();
}
internal void OnAvatarSetup()
{
if(m_leapTracking != null)
m_leapTracking.OnAvatarSetup();
if(m_leapInput != null)
m_leapInput.OnAvatarSetup();
if(m_leapTracked != null)
m_leapTracked.OnAvatarSetup();
}
internal void OnCalibrate()
{
if(m_leapTracked != null)
m_leapTracked.OnCalibrate();
}
internal void OnRayScale(float p_scale)
{
if(m_leapInput != null)
m_leapInput.OnRayScale(p_scale);
}
// Arbitrary
void UpdateDeviceTrackingMode()
{
m_leapController.ClearPolicy(Leap.Controller.PolicyFlag.POLICY_OPTIMIZE_SCREENTOP, null);
m_leapController.ClearPolicy(Leap.Controller.PolicyFlag.POLICY_OPTIMIZE_HMD, null);
switch(Settings.TrackingMode)
{
case Settings.LeapTrackingMode.Screentop:
m_leapController.SetPolicy(Leap.Controller.PolicyFlag.POLICY_OPTIMIZE_SCREENTOP, null);
break;
case Settings.LeapTrackingMode.HMD:
m_leapController.SetPolicy(Leap.Controller.PolicyFlag.POLICY_OPTIMIZE_HMD, null);
break;
}
}
}
}

View file

@ -1,5 +1,4 @@
using ABI_RC.Core.Player;
using ABI_RC.Core.Savior;
using ABI_RC.Systems.IK;
using RootMotion.FinalIK;
using System.Reflection;
@ -11,20 +10,17 @@ namespace ml_lme
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 FieldInfo ms_indexGestureToggle = typeof(InputModuleSteamVR).GetField("_steamVrIndexGestureToggleValue", BindingFlags.Instance | BindingFlags.NonPublic);
static readonly Quaternion ms_offsetLeft = Quaternion.Euler(0f, 0f, 270f);
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);
InputModuleSteamVR m_steamVrModule = null;
VRIK m_vrIK = null;
Vector2 m_armsWeights = Vector2.zero;
bool m_isInVR = false;
bool m_inVR = false;
Transform m_origElbowLeft = null;
Transform m_origElbowRight = null;
Transform m_hips = null;
Vector3 m_hipsLocal = Vector3.zero;
bool m_enabled = true;
bool m_fingersOnly = false;
@ -34,38 +30,32 @@ namespace ml_lme
ArmIK m_rightIK = null;
HumanPoseHandler m_poseHandler = null;
HumanPose m_pose;
Transform m_leftHand = null;
Transform m_rightHand = null;
Transform m_leftHandTarget = null;
Transform m_rightHandTarget = null;
Transform m_leftElbow = null;
Transform m_rightElbow = null;
bool m_leftTargetActive = false;
bool m_rightTargetActive = false;
void Start()
{
m_steamVrModule = CVRInputManager.Instance.GetComponent<InputModuleSteamVR>();
m_isInVR = Utils.IsInVR();
m_inVR = Utils.IsInVR();
if(m_leftHand != null)
{
m_leftHandTarget = new GameObject("RotationTarget").transform;
m_leftHandTarget.parent = m_leftHand;
m_leftHandTarget.parent = LeapTracking.GetInstance().GetLeftHand();
m_leftHandTarget.localPosition = Vector3.zero;
m_leftHandTarget.localRotation = Quaternion.identity;
}
if(m_rightHand != null)
{
m_rightHandTarget = new GameObject("RotationTarget").transform;
m_rightHandTarget.parent = m_rightHand;
m_rightHandTarget.parent = LeapTracking.GetInstance().GetRightHand();
m_rightHandTarget.localPosition = Vector3.zero;
m_rightHandTarget.localRotation = Quaternion.identity;
}
Settings.EnabledChange += this.SetEnabled;
Settings.FingersOnlyChange += this.SetFingersOnly;
Settings.TrackElbowsChange += this.SetTrackElbows;
SetEnabled(Settings.Enabled);
SetFingersOnly(Settings.FingersOnly);
SetTrackElbows(Settings.TrackElbows);
}
void OnDestroy()
@ -75,17 +65,16 @@ namespace ml_lme
Settings.TrackElbowsChange -= this.SetTrackElbows;
}
public void SetEnabled(bool p_state)
void SetEnabled(bool p_state)
{
m_enabled = p_state;
RefreshFingersTracking();
RefreshArmIK();
if(!m_enabled || m_fingersOnly)
RestoreVRIK();
}
public void SetFingersOnly(bool p_state)
void SetFingersOnly(bool p_state)
{
m_fingersOnly = p_state;
@ -94,7 +83,7 @@ namespace ml_lme
RestoreVRIK();
}
public void SetTrackElbows(bool p_state)
void SetTrackElbows(bool p_state)
{
m_trackElbows = p_state;
@ -107,190 +96,148 @@ namespace ml_lme
RestoreVRIK();
}
public void SetTransforms(Transform p_left, Transform p_right, Transform p_leftElbow, Transform p_rightElbow)
{
m_leftHand = p_left;
m_rightHand = p_right;
m_leftElbow = p_leftElbow;
m_rightElbow = p_rightElbow;
}
public void UpdateTracking(GestureMatcher.GesturesData p_data)
void Update()
{
if(m_enabled)
{
GestureMatcher.LeapData l_data = LeapManager.GetInstance().GetLatestData();
if((m_leftIK != null) && (m_rightIK != null))
{
m_leftIK.solver.IKPositionWeight = Mathf.Lerp(m_leftIK.solver.IKPositionWeight, (p_data.m_handsPresenses[0] && !m_fingersOnly) ? 1f : 0f, 0.25f);
m_leftIK.solver.IKRotationWeight = Mathf.Lerp(m_leftIK.solver.IKRotationWeight, (p_data.m_handsPresenses[0] && !m_fingersOnly) ? 1f : 0f, 0.25f);
m_leftIK.solver.IKPositionWeight = Mathf.Lerp(m_leftIK.solver.IKPositionWeight, (l_data.m_leftHand.m_present && !m_fingersOnly) ? 1f : 0f, 0.25f);
m_leftIK.solver.IKRotationWeight = Mathf.Lerp(m_leftIK.solver.IKRotationWeight, (l_data.m_leftHand.m_present && !m_fingersOnly) ? 1f : 0f, 0.25f);
if(m_trackElbows)
m_leftIK.solver.arm.bendGoalWeight = Mathf.Lerp(m_leftIK.solver.arm.bendGoalWeight, (p_data.m_handsPresenses[0] && !m_fingersOnly) ? 1f : 0f, 0.25f);
m_leftIK.solver.arm.bendGoalWeight = Mathf.Lerp(m_leftIK.solver.arm.bendGoalWeight, (l_data.m_leftHand.m_present && !m_fingersOnly) ? 1f : 0f, 0.25f);
m_rightIK.solver.IKPositionWeight = Mathf.Lerp(m_rightIK.solver.IKPositionWeight, (p_data.m_handsPresenses[1] && !m_fingersOnly) ? 1f : 0f, 0.25f);
m_rightIK.solver.IKRotationWeight = Mathf.Lerp(m_rightIK.solver.IKRotationWeight, (p_data.m_handsPresenses[1] && !m_fingersOnly) ? 1f : 0f, 0.25f);
m_rightIK.solver.IKPositionWeight = Mathf.Lerp(m_rightIK.solver.IKPositionWeight, (l_data.m_rightHand.m_present && !m_fingersOnly) ? 1f : 0f, 0.25f);
m_rightIK.solver.IKRotationWeight = Mathf.Lerp(m_rightIK.solver.IKRotationWeight, (l_data.m_rightHand.m_present && !m_fingersOnly) ? 1f : 0f, 0.25f);
if(m_trackElbows)
m_rightIK.solver.arm.bendGoalWeight = Mathf.Lerp(m_rightIK.solver.arm.bendGoalWeight, (p_data.m_handsPresenses[1] && !m_fingersOnly) ? 1f : 0f, 0.25f);
m_rightIK.solver.arm.bendGoalWeight = Mathf.Lerp(m_rightIK.solver.arm.bendGoalWeight, (l_data.m_rightHand.m_present && !m_fingersOnly) ? 1f : 0f, 0.25f);
}
if((m_vrIK != null) && !m_fingersOnly)
{
if(p_data.m_handsPresenses[0] && !m_leftTargetActive)
if(l_data.m_leftHand.m_present && !m_leftTargetActive)
{
m_vrIK.solver.leftArm.target = m_leftHandTarget;
m_vrIK.solver.leftArm.bendGoal = m_leftElbow;
m_vrIK.solver.leftArm.bendGoal = LeapTracking.GetInstance().GetLeftElbow();
m_vrIK.solver.leftArm.bendGoalWeight = (m_trackElbows ? 1f : 0f);
m_leftTargetActive = true;
}
if(!p_data.m_handsPresenses[0] && m_leftTargetActive)
if(!l_data.m_leftHand.m_present && m_leftTargetActive)
{
m_vrIK.solver.leftArm.target = IKSystem.Instance.leftHandAnchor;
m_vrIK.solver.leftArm.target = (m_inVR ? IKSystem.Instance.leftHandAnchor : null);
m_vrIK.solver.leftArm.bendGoal = m_origElbowLeft;
m_vrIK.solver.leftArm.bendGoalWeight = ((m_origElbowLeft != null) ? 1f : 0f);
m_leftTargetActive = false;
}
if(p_data.m_handsPresenses[1] && !m_rightTargetActive)
if(l_data.m_rightHand.m_present && !m_rightTargetActive)
{
m_vrIK.solver.rightArm.target = m_rightHandTarget;
m_vrIK.solver.rightArm.bendGoal = m_rightElbow;
m_vrIK.solver.rightArm.bendGoal = LeapTracking.GetInstance().GetRightElbow();
m_vrIK.solver.rightArm.bendGoalWeight = (m_trackElbows ? 1f : 0f);
m_rightTargetActive = true;
}
if(!p_data.m_handsPresenses[1] && m_rightTargetActive)
if(!l_data.m_rightHand.m_present && m_rightTargetActive)
{
m_vrIK.solver.rightArm.target = IKSystem.Instance.rightHandAnchor;
m_vrIK.solver.rightArm.target = (m_inVR ? IKSystem.Instance.rightHandAnchor : null);
m_vrIK.solver.rightArm.bendGoal = m_origElbowRight;
m_vrIK.solver.rightArm.bendGoalWeight = ((m_origElbowRight != null) ? 1f : 0f);
m_rightTargetActive = false;
}
}
if(p_data.m_handsPresenses[0])
{
CVRInputManager.Instance.individualFingerTracking = true;
CVRInputManager.Instance.fingerCurlLeftThumb = p_data.m_leftFingersBends[0];
CVRInputManager.Instance.fingerCurlLeftIndex = p_data.m_leftFingersBends[1];
CVRInputManager.Instance.fingerCurlLeftMiddle = p_data.m_leftFingersBends[2];
CVRInputManager.Instance.fingerCurlLeftRing = p_data.m_leftFingersBends[3];
CVRInputManager.Instance.fingerCurlLeftPinky = p_data.m_leftFingersBends[4];
IKSystem.Instance.FingerSystem.controlActive = true;
IKSystem.Instance.FingerSystem.leftThumbCurl = p_data.m_leftFingersBends[0];
IKSystem.Instance.FingerSystem.leftIndexCurl = p_data.m_leftFingersBends[1];
IKSystem.Instance.FingerSystem.leftMiddleCurl = p_data.m_leftFingersBends[2];
IKSystem.Instance.FingerSystem.leftRingCurl = p_data.m_leftFingersBends[3];
IKSystem.Instance.FingerSystem.leftPinkyCurl = p_data.m_leftFingersBends[4];
}
if(p_data.m_handsPresenses[1])
{
CVRInputManager.Instance.individualFingerTracking = true;
CVRInputManager.Instance.fingerCurlRightThumb = p_data.m_rightFingersBends[0];
CVRInputManager.Instance.fingerCurlRightIndex = p_data.m_rightFingersBends[1];
CVRInputManager.Instance.fingerCurlRightMiddle = p_data.m_rightFingersBends[2];
CVRInputManager.Instance.fingerCurlRightRing = p_data.m_rightFingersBends[3];
CVRInputManager.Instance.fingerCurlRightPinky = p_data.m_rightFingersBends[4];
IKSystem.Instance.FingerSystem.controlActive = true;
IKSystem.Instance.FingerSystem.rightThumbCurl = p_data.m_rightFingersBends[0];
IKSystem.Instance.FingerSystem.rightIndexCurl = p_data.m_rightFingersBends[1];
IKSystem.Instance.FingerSystem.rightMiddleCurl = p_data.m_rightFingersBends[2];
IKSystem.Instance.FingerSystem.rightRingCurl = p_data.m_rightFingersBends[3];
IKSystem.Instance.FingerSystem.rightPinkyCurl = p_data.m_rightFingersBends[4];
}
}
}
public void UpdateTrackingLate(GestureMatcher.GesturesData p_data)
void LateUpdate()
{
if(m_enabled && !m_isInVR && (m_poseHandler != null))
if(m_enabled && !m_inVR && (m_poseHandler != null))
{
if(m_hips != null)
m_hipsLocal = m_hips.localPosition;
GestureMatcher.LeapData l_data = LeapManager.GetInstance().GetLatestData();
Vector3 l_hipsLocalPos = m_hips.localPosition;
m_poseHandler.GetHumanPose(ref m_pose);
UpdateFingers(p_data);
UpdateFingers(l_data);
m_poseHandler.SetHumanPose(ref m_pose);
if(m_hips != null)
m_hips.localPosition = m_hipsLocal;
m_hips.localPosition = l_hipsLocalPos;
}
}
void UpdateFingers(GestureMatcher.GesturesData p_data)
void UpdateFingers(GestureMatcher.LeapData p_data)
{
if(p_data.m_handsPresenses[0])
if(p_data.m_leftHand.m_present)
{
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftThumb1Stretched, Mathf.Lerp(0.85f, -0.85f, p_data.m_leftFingersBends[0]));
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftThumb2Stretched, Mathf.Lerp(0.85f, -0.85f, p_data.m_leftFingersBends[0]));
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftThumb3Stretched, Mathf.Lerp(0.85f, -0.85f, p_data.m_leftFingersBends[0]));
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftThumbSpread, Mathf.Lerp(-1.5f, 1.0f, p_data.m_leftFingersSpreads[0])); // Ok
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftThumb1Stretched, Mathf.Lerp(0.85f, -0.85f, p_data.m_leftHand.m_bends[0]));
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftThumb2Stretched, Mathf.Lerp(0.85f, -0.85f, p_data.m_leftHand.m_bends[0]));
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftThumb3Stretched, Mathf.Lerp(0.85f, -0.85f, p_data.m_leftHand.m_bends[0]));
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftThumbSpread, Mathf.Lerp(-1.5f, 1.0f, p_data.m_leftHand.m_spreads[0])); // Ok
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftIndex1Stretched, Mathf.Lerp(0.7f, -1f, p_data.m_leftFingersBends[1]));
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftIndex2Stretched, Mathf.Lerp(0.7f, -1f, p_data.m_leftFingersBends[1]));
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftIndex3Stretched, Mathf.Lerp(0.7f, -1f, p_data.m_leftFingersBends[1]));
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftIndexSpread, Mathf.Lerp(1f, -1f, p_data.m_leftFingersSpreads[1])); // Ok
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftIndex1Stretched, Mathf.Lerp(0.7f, -1f, p_data.m_leftHand.m_bends[1]));
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftIndex2Stretched, Mathf.Lerp(0.7f, -1f, p_data.m_leftHand.m_bends[1]));
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftIndex3Stretched, Mathf.Lerp(0.7f, -1f, p_data.m_leftHand.m_bends[1]));
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftIndexSpread, Mathf.Lerp(1f, -1f, p_data.m_leftHand.m_spreads[1])); // Ok
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftMiddle1Stretched, Mathf.Lerp(0.7f, -1f, p_data.m_leftFingersBends[2]));
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftMiddle2Stretched, Mathf.Lerp(0.7f, -1f, p_data.m_leftFingersBends[2]));
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftMiddle3Stretched, Mathf.Lerp(0.7f, -1f, p_data.m_leftFingersBends[2]));
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftMiddleSpread, Mathf.Lerp(2f, -2f, p_data.m_leftFingersSpreads[2]));
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftMiddle1Stretched, Mathf.Lerp(0.7f, -1f, p_data.m_leftHand.m_bends[2]));
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftMiddle2Stretched, Mathf.Lerp(0.7f, -1f, p_data.m_leftHand.m_bends[2]));
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftMiddle3Stretched, Mathf.Lerp(0.7f, -1f, p_data.m_leftHand.m_bends[2]));
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftMiddleSpread, Mathf.Lerp(2f, -2f, p_data.m_leftHand.m_spreads[2]));
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftRing1Stretched, Mathf.Lerp(0.7f, -1f, p_data.m_leftFingersBends[3]));
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftRing2Stretched, Mathf.Lerp(0.7f, -1f, p_data.m_leftFingersBends[3]));
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftRing3Stretched, Mathf.Lerp(0.7f, -1f, p_data.m_leftFingersBends[3]));
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftRingSpread, Mathf.Lerp(-2f, 2f, p_data.m_leftFingersSpreads[3]));
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftRing1Stretched, Mathf.Lerp(0.7f, -1f, p_data.m_leftHand.m_bends[3]));
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftRing2Stretched, Mathf.Lerp(0.7f, -1f, p_data.m_leftHand.m_bends[3]));
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftRing3Stretched, Mathf.Lerp(0.7f, -1f, p_data.m_leftHand.m_bends[3]));
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftRingSpread, Mathf.Lerp(-2f, 2f, p_data.m_leftHand.m_spreads[3]));
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftLittle1Stretched, Mathf.Lerp(0.7f, -1f, p_data.m_leftFingersBends[4]));
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftLittle2Stretched, Mathf.Lerp(0.7f, -1f, p_data.m_leftFingersBends[4]));
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftLittle3Stretched, Mathf.Lerp(0.7f, -1f, p_data.m_leftFingersBends[4]));
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftLittleSpread, Mathf.Lerp(-0.5f, 1f, p_data.m_leftFingersSpreads[4]));
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftLittle1Stretched, Mathf.Lerp(0.7f, -1f, p_data.m_leftHand.m_bends[4]));
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftLittle2Stretched, Mathf.Lerp(0.7f, -1f, p_data.m_leftHand.m_bends[4]));
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftLittle3Stretched, Mathf.Lerp(0.7f, -1f, p_data.m_leftHand.m_bends[4]));
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftLittleSpread, Mathf.Lerp(-0.5f, 1f, p_data.m_leftHand.m_spreads[4]));
}
if(p_data.m_handsPresenses[1])
if(p_data.m_rightHand.m_present)
{
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightThumb1Stretched, Mathf.Lerp(0.85f, -0.85f, p_data.m_rightFingersBends[0]));
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightThumb2Stretched, Mathf.Lerp(0.85f, -0.85f, p_data.m_rightFingersBends[0]));
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightThumb3Stretched, Mathf.Lerp(0.85f, -0.85f, p_data.m_rightFingersBends[0]));
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightThumbSpread, Mathf.Lerp(-1.5f, 1.0f, p_data.m_rightFingersSpreads[0])); // Ok
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightThumb1Stretched, Mathf.Lerp(0.85f, -0.85f, p_data.m_rightHand.m_bends[0]));
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightThumb2Stretched, Mathf.Lerp(0.85f, -0.85f, p_data.m_rightHand.m_bends[0]));
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightThumb3Stretched, Mathf.Lerp(0.85f, -0.85f, p_data.m_rightHand.m_bends[0]));
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightThumbSpread, Mathf.Lerp(-1.5f, 1.0f, p_data.m_rightHand.m_spreads[0])); // Ok
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightIndex1Stretched, Mathf.Lerp(0.7f, -1f, p_data.m_rightFingersBends[1]));
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightIndex2Stretched, Mathf.Lerp(0.7f, -1f, p_data.m_rightFingersBends[1]));
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightIndex3Stretched, Mathf.Lerp(0.7f, -1f, p_data.m_rightFingersBends[1]));
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightIndexSpread, Mathf.Lerp(1f, -1f, p_data.m_rightFingersSpreads[1])); // Ok
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightIndex1Stretched, Mathf.Lerp(0.7f, -1f, p_data.m_rightHand.m_bends[1]));
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightIndex2Stretched, Mathf.Lerp(0.7f, -1f, p_data.m_rightHand.m_bends[1]));
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightIndex3Stretched, Mathf.Lerp(0.7f, -1f, p_data.m_rightHand.m_bends[1]));
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightIndexSpread, Mathf.Lerp(1f, -1f, p_data.m_rightHand.m_spreads[1])); // Ok
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightMiddle1Stretched, Mathf.Lerp(0.7f, -1f, p_data.m_rightFingersBends[2]));
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightMiddle2Stretched, Mathf.Lerp(0.7f, -1f, p_data.m_rightFingersBends[2]));
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightMiddle3Stretched, Mathf.Lerp(0.7f, -1f, p_data.m_rightFingersBends[2]));
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightMiddleSpread, Mathf.Lerp(2f, -2f, p_data.m_rightFingersSpreads[2]));
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightMiddle1Stretched, Mathf.Lerp(0.7f, -1f, p_data.m_rightHand.m_bends[2]));
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightMiddle2Stretched, Mathf.Lerp(0.7f, -1f, p_data.m_rightHand.m_bends[2]));
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightMiddle3Stretched, Mathf.Lerp(0.7f, -1f, p_data.m_rightHand.m_bends[2]));
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightMiddleSpread, Mathf.Lerp(2f, -2f, p_data.m_rightHand.m_spreads[2]));
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightRing1Stretched, Mathf.Lerp(0.7f, -1f, p_data.m_rightFingersBends[3]));
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightRing2Stretched, Mathf.Lerp(0.7f, -1f, p_data.m_rightFingersBends[3]));
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightRing3Stretched, Mathf.Lerp(0.7f, -1f, p_data.m_rightFingersBends[3]));
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightRingSpread, Mathf.Lerp(-2f, 2f, p_data.m_rightFingersSpreads[3]));
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightRing1Stretched, Mathf.Lerp(0.7f, -1f, p_data.m_rightHand.m_bends[3]));
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightRing2Stretched, Mathf.Lerp(0.7f, -1f, p_data.m_rightHand.m_bends[3]));
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightRing3Stretched, Mathf.Lerp(0.7f, -1f, p_data.m_rightHand.m_bends[3]));
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightRingSpread, Mathf.Lerp(-2f, 2f, p_data.m_rightHand.m_spreads[3]));
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightLittle1Stretched, Mathf.Lerp(0.7f, -1f, p_data.m_rightFingersBends[4]));
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightLittle2Stretched, Mathf.Lerp(0.7f, -1f, p_data.m_rightFingersBends[4]));
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightLittle3Stretched, Mathf.Lerp(0.7f, -1f, p_data.m_rightFingersBends[4]));
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightLittleSpread, Mathf.Lerp(-0.5f, 1f, p_data.m_rightFingersSpreads[4]));
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightLittle1Stretched, Mathf.Lerp(0.7f, -1f, p_data.m_rightHand.m_bends[4]));
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightLittle2Stretched, Mathf.Lerp(0.7f, -1f, p_data.m_rightHand.m_bends[4]));
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightLittle3Stretched, Mathf.Lerp(0.7f, -1f, p_data.m_rightHand.m_bends[4]));
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightLittleSpread, Mathf.Lerp(-0.5f, 1f, p_data.m_rightHand.m_spreads[4]));
}
}
public void OnAvatarClear()
internal void OnAvatarClear()
{
m_vrIK = null;
m_origElbowLeft = null;
m_origElbowRight = null;
m_hips = null;
m_hipsLocal = Vector3.zero;
m_armsWeights = Vector2.zero;
m_leftIK = null;
m_rightIK = null;
m_leftTargetActive = false;
m_rightTargetActive = false;
if(!m_isInVR)
if(!m_inVR)
m_poseHandler?.Dispose();
m_poseHandler = null;
@ -300,27 +247,27 @@ namespace ml_lme
m_rightHandTarget.localRotation = Quaternion.identity;
}
public void OnSetupAvatar()
internal void OnAvatarSetup()
{
m_isInVR = Utils.IsInVR();
m_inVR = Utils.IsInVR();
m_vrIK = PlayerSetup.Instance._animator.GetComponent<VRIK>();
RefreshFingersTracking();
if(PlayerSetup.Instance._animator.isHuman)
{
m_hips = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.Hips);
if(!m_isInVR)
if(!m_inVR)
{
// Force desktop avatar into T-Pose
m_poseHandler = new HumanPoseHandler(PlayerSetup.Instance._animator.avatar, PlayerSetup.Instance._avatar.transform);
m_poseHandler.GetHumanPose(ref m_pose);
HumanPose l_tPose = new HumanPose();
l_tPose.bodyPosition = m_pose.bodyPosition;
l_tPose.bodyRotation = m_pose.bodyRotation;
l_tPose.muscles = new float[m_pose.muscles.Length];
HumanPose l_tPose = new HumanPose
{
bodyPosition = m_pose.bodyPosition,
bodyRotation = m_pose.bodyRotation,
muscles = new float[m_pose.muscles.Length]
};
for(int i = 0; i < l_tPose.muscles.Length; i++)
l_tPose.muscles[i] = ms_tposeMuscles[i];
m_poseHandler.SetHumanPose(ref l_tPose);
@ -353,7 +300,7 @@ namespace ml_lme
PlayerSetup.Instance._animator.transform
);
m_leftIK.solver.arm.target = m_leftHandTarget;
m_leftIK.solver.arm.bendGoal = m_leftElbow;
m_leftIK.solver.arm.bendGoal = LeapTracking.GetInstance().GetLeftElbow();
m_leftIK.solver.arm.bendGoalWeight = (m_trackElbows ? 1f : 0f);
m_leftIK.enabled = (m_enabled && !m_fingersOnly);
@ -368,7 +315,7 @@ namespace ml_lme
PlayerSetup.Instance._animator.transform
);
m_rightIK.solver.arm.target = m_rightHandTarget;
m_rightIK.solver.arm.bendGoal = m_rightElbow;
m_rightIK.solver.arm.bendGoal = LeapTracking.GetInstance().GetRightElbow();
m_rightIK.solver.arm.bendGoalWeight = (m_trackElbows ? 1f : 0f);
m_rightIK.enabled = (m_enabled && !m_fingersOnly);
@ -382,7 +329,7 @@ namespace ml_lme
}
}
public void OnCalibrate()
internal void OnCalibrate()
{
if(m_vrIK != null)
{
@ -421,14 +368,14 @@ namespace ml_lme
{
if(m_leftTargetActive)
{
m_vrIK.solver.leftArm.target = IKSystem.Instance.leftHandAnchor;
m_vrIK.solver.leftArm.target = (m_inVR ? IKSystem.Instance.leftHandAnchor : null);
m_vrIK.solver.leftArm.bendGoal = m_origElbowLeft;
m_vrIK.solver.leftArm.bendGoalWeight = ((m_origElbowLeft != null) ? 1f : 0f);
m_leftTargetActive = false;
}
if(m_rightTargetActive)
{
m_vrIK.solver.rightArm.target = IKSystem.Instance.rightHandAnchor;
m_vrIK.solver.rightArm.target = (m_inVR ? IKSystem.Instance.rightHandAnchor : null);
m_vrIK.solver.rightArm.bendGoal = m_origElbowRight;
m_vrIK.solver.rightArm.bendGoalWeight = ((m_origElbowRight != null) ? 1f : 0f);
m_rightTargetActive = false;
@ -445,12 +392,6 @@ namespace ml_lme
}
}
void RefreshFingersTracking()
{
CVRInputManager.Instance.individualFingerTracking = (m_enabled || (m_isInVR && Utils.AreKnucklesInUse() && !(bool)ms_indexGestureToggle.GetValue(m_steamVrModule)));
IKSystem.Instance.FingerSystem.controlActive = CVRInputManager.Instance.individualFingerTracking;
}
static void UpdatePoseMuscle(ref HumanPose p_pose, int p_index, float p_value)
{
if(p_pose.muscles.Length > p_index)

221
ml_lme/LeapTracking.cs Normal file
View file

@ -0,0 +1,221 @@
using ABI_RC.Core.Player;
using System.Collections;
using UnityEngine;
namespace ml_lme
{
[DisallowMultipleComponent]
class LeapTracking : MonoBehaviour
{
static LeapTracking ms_instance = null;
static Quaternion ms_identityRotation = Quaternion.identity;
bool m_inVR = false;
GameObject m_leapHandLeft = null;
GameObject m_leapHandRight = null;
GameObject m_leapElbowLeft = null;
GameObject m_leapElbowRight = null;
GameObject m_leapControllerModel = null;
public static LeapTracking GetInstance() => ms_instance;
void Start()
{
if(ms_instance == null)
ms_instance = this;
m_inVR = Utils.IsInVR();
m_leapHandLeft = new GameObject("LeapHandLeft");
m_leapHandLeft.transform.parent = this.transform;
m_leapHandLeft.transform.localPosition = Vector3.zero;
m_leapHandLeft.transform.localRotation = Quaternion.identity;
m_leapHandRight = new GameObject("LeapHandRight");
m_leapHandRight.transform.parent = this.transform;
m_leapHandRight.transform.localPosition = Vector3.zero;
m_leapHandRight.transform.localRotation = Quaternion.identity;
m_leapElbowLeft = new GameObject("LeapElbowLeft");
m_leapElbowLeft.transform.parent = this.transform;
m_leapElbowLeft.transform.localPosition = Vector3.zero;
m_leapElbowLeft.transform.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_leapControllerModel = AssetsHandler.GetAsset("assets/models/leapmotion/leap_motion_1_0.obj");
if(m_leapControllerModel != null)
{
m_leapControllerModel.name = "LeapModel";
m_leapControllerModel.transform.parent = this.transform;
m_leapControllerModel.transform.localPosition = Vector3.zero;
m_leapControllerModel.transform.localRotation = Quaternion.identity;
}
Settings.DesktopOffsetChange += this.OnDesktopOffsetChange;
Settings.ModelVisibilityChange += this.OnModelVisibilityChange;
Settings.TrackingModeChange += this.OnTrackingModeChange;
Settings.RootAngleChange += this.OnRootAngleChange;
Settings.HeadAttachChange += this.OnHeadAttachChange;
Settings.HeadOffsetChange += this.OnHeadOffsetChange;
MelonLoader.MelonCoroutines.Start(WaitForLocalPlayer());
OnModelVisibilityChange(Settings.ModelVisibility);
OnTrackingModeChange(Settings.TrackingMode);
OnRootAngleChange(Settings.RootAngle);
}
IEnumerator WaitForLocalPlayer()
{
while(PlayerSetup.Instance == null)
yield return null;
OnDesktopOffsetChange(Settings.DesktopOffset);
OnHeadAttachChange(Settings.HeadAttach);
OnHeadOffsetChange(Settings.HeadOffset);
}
void OnDestroy()
{
if(ms_instance == this)
ms_instance = null;
Settings.DesktopOffsetChange -= this.OnDesktopOffsetChange;
Settings.ModelVisibilityChange -= this.OnModelVisibilityChange;
Settings.TrackingModeChange -= this.OnTrackingModeChange;
Settings.RootAngleChange -= this.OnRootAngleChange;
Settings.HeadAttachChange -= this.OnHeadAttachChange;
Settings.HeadOffsetChange -= this.OnHeadOffsetChange;
}
void Update()
{
if(Settings.Enabled)
{
GestureMatcher.LeapData l_data = LeapManager.GetInstance().GetLatestData();
if(l_data.m_leftHand.m_present)
{
Utils.LeapToUnity(ref l_data.m_leftHand.m_position, ref l_data.m_leftHand.m_rotation, Settings.TrackingMode);
m_leapHandLeft.transform.localPosition = l_data.m_leftHand.m_position;
m_leapHandLeft.transform.localRotation = l_data.m_leftHand.m_rotation;
Utils.LeapToUnity(ref l_data.m_leftHand.m_elbowPosition, ref ms_identityRotation, Settings.TrackingMode);
m_leapElbowLeft.transform.localPosition = l_data.m_leftHand.m_elbowPosition;
}
if(l_data.m_rightHand.m_present)
{
Utils.LeapToUnity(ref l_data.m_rightHand.m_position, ref l_data.m_rightHand.m_rotation, Settings.TrackingMode);
m_leapHandRight.transform.localPosition = l_data.m_rightHand.m_position;
m_leapHandRight.transform.localRotation = l_data.m_rightHand.m_rotation;
Utils.LeapToUnity(ref l_data.m_rightHand.m_elbowPosition, ref ms_identityRotation, Settings.TrackingMode);
m_leapElbowRight.transform.localPosition = l_data.m_rightHand.m_elbowPosition;
}
}
}
public Transform GetLeftHand() => m_leapHandLeft.transform;
public Transform GetRightHand() => m_leapHandRight.transform;
public Transform GetLeftElbow() => m_leapElbowLeft.transform;
public Transform GetRightElbow() => m_leapElbowRight.transform;
// Settings
void OnDesktopOffsetChange(Vector3 p_offset)
{
if(!Settings.HeadAttach)
{
if(!m_inVR)
this.transform.localPosition = p_offset * PlayerSetup.Instance.vrCameraRig.transform.localScale.x;
else
this.transform.localPosition = p_offset;
}
}
void OnModelVisibilityChange(bool p_state)
{
m_leapControllerModel.SetActive(p_state);
}
void OnTrackingModeChange(Settings.LeapTrackingMode p_mode)
{
switch(p_mode)
{
case Settings.LeapTrackingMode.Screentop:
m_leapControllerModel.transform.localRotation = Quaternion.Euler(0f, 0f, 180f);
break;
case Settings.LeapTrackingMode.Desktop:
m_leapControllerModel.transform.localRotation = Quaternion.identity;
break;
case Settings.LeapTrackingMode.HMD:
m_leapControllerModel.transform.localRotation = Quaternion.Euler(270f, 180f, 0f);
break;
}
}
void OnRootAngleChange(Vector3 p_angle)
{
this.transform.localRotation = Quaternion.Euler(p_angle);
}
void OnHeadAttachChange(bool p_state)
{
if(p_state)
{
if(!m_inVR)
{
this.transform.parent = PlayerSetup.Instance.desktopCamera.transform;
this.transform.localPosition = Settings.HeadOffset * PlayerSetup.Instance.vrCameraRig.transform.localScale.x;
this.transform.localScale = PlayerSetup.Instance.vrCameraRig.transform.localScale;
}
else
{
this.transform.parent = PlayerSetup.Instance.vrCamera.transform;
this.transform.localPosition = Settings.HeadOffset;
this.transform.localScale = Vector3.one;
}
}
else
{
if(!m_inVR)
{
this.transform.parent = PlayerSetup.Instance.desktopCameraRig.transform;
this.transform.localPosition = Settings.DesktopOffset * PlayerSetup.Instance.vrCameraRig.transform.localScale.x;
this.transform.localScale = PlayerSetup.Instance.vrCameraRig.transform.localScale;
}
else
{
this.transform.parent = PlayerSetup.Instance.vrCameraRig.transform;
this.transform.localPosition = Settings.DesktopOffset;
this.transform.localScale = Vector3.one;
}
}
this.transform.localRotation = Quaternion.Euler(Settings.RootAngle);
}
void OnHeadOffsetChange(Vector3 p_offset)
{
if(Settings.HeadAttach)
{
if(!m_inVR)
this.transform.localPosition = p_offset * PlayerSetup.Instance.vrCameraRig.transform.localScale.x;
else
this.transform.localPosition = p_offset;
}
}
// Game events
internal void OnAvatarSetup()
{
m_inVR = Utils.IsInVR();
OnHeadAttachChange(Settings.HeadAttach);
}
}
}

View file

@ -1,6 +1,6 @@
using ABI_RC.Core.Player;
using ABI_RC.Core.UI;
using ABI_RC.Systems.IK.SubSystems;
using System.Collections;
using System.Reflection;
using UnityEngine;
@ -11,16 +11,7 @@ namespace ml_lme
{
static LeapMotionExtension ms_instance = null;
Leap.Controller m_leapController = null;
GestureMatcher.GesturesData m_gesturesData = null;
GameObject m_leapTrackingRoot = null;
GameObject[] m_leapHands = null;
GameObject[] m_leapElbows = null;
GameObject m_leapControllerModel = null;
LeapTracked m_leapTracked = null;
bool m_isInVR = false;
LeapManager m_leapManager = null;
public override void OnInitializeMelon()
{
@ -28,26 +19,8 @@ namespace ml_lme
ms_instance = this;
DependenciesHandler.ExtractDependencies();
Settings.Init();
Settings.EnabledChange += this.OnEnableChange;
Settings.DesktopOffsetChange += this.OnDesktopOffsetChange;
Settings.ModelVisibilityChange += this.OnModelVisibilityChange;
Settings.TrackingModeChange += this.OnTrackingModeChange;
Settings.RootAngleChange += this.OnRootAngleChange;
Settings.HeadAttachChange += this.OnHeadAttachChange;
Settings.HeadOffsetChange += this.OnHeadOffsetChange;
m_leapController = new Leap.Controller();
m_leapController.Device += this.OnLeapDeviceInitialized;
m_leapController.DeviceFailure += this.OnLeapDeviceFailure;
m_leapController.DeviceLost += this.OnLeapDeviceLost;
m_leapController.Connect += this.OnLeapServiceConnect;
m_leapController.Disconnect += this.OnLeapServiceDisconnect;
m_gesturesData = new GestureMatcher.GesturesData();
m_leapHands = new GameObject[GestureMatcher.GesturesData.ms_handsCount];
m_leapElbows = new GameObject[GestureMatcher.GesturesData.ms_handsCount];
AssetsHandler.Load();
// Patches
HarmonyInstance.Patch(
@ -65,280 +38,27 @@ namespace ml_lme
null,
new HarmonyLib.HarmonyMethod(typeof(LeapMotionExtension).GetMethod(nameof(OnCalibrate_Postfix), BindingFlags.Static | BindingFlags.NonPublic))
);
HarmonyInstance.Patch(
typeof(PlayerSetup).GetMethod(nameof(PlayerSetup.SetControllerRayScale)),
null,
new HarmonyLib.HarmonyMethod(typeof(LeapMotionExtension).GetMethod(nameof(OnRayScale_Postfix), BindingFlags.Static | BindingFlags.NonPublic))
);
MelonLoader.MelonCoroutines.Start(CreateTrackingObjects());
}
System.Collections.IEnumerator CreateTrackingObjects()
{
AssetsHandler.Load();
while(PlayerSetup.Instance == null)
yield return null;
while(PlayerSetup.Instance.desktopCameraRig == null)
yield return null;
while(PlayerSetup.Instance.desktopCamera == null)
yield return null;
while(PlayerSetup.Instance.vrCameraRig == null)
yield return null;
while(PlayerSetup.Instance.vrCamera == null)
yield return null;
m_isInVR = Utils.IsInVR();
m_leapTrackingRoot = new GameObject("[LeapRoot]");
for(int i = 0; i < GestureMatcher.GesturesData.ms_handsCount; i++)
{
m_leapHands[i] = new GameObject("LeapHand" + i);
m_leapHands[i].transform.parent = m_leapTrackingRoot.transform;
m_leapHands[i].transform.localPosition = Vector3.zero;
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");
if(m_leapControllerModel != null)
{
m_leapControllerModel.name = "LeapModel";
m_leapControllerModel.transform.parent = m_leapTrackingRoot.transform;
m_leapControllerModel.transform.localPosition = Vector3.zero;
m_leapControllerModel.transform.localRotation = Quaternion.identity;
}
// Player setup
m_leapTracked = PlayerSetup.Instance.gameObject.AddComponent<LeapTracked>();
m_leapTracked.SetTransforms(m_leapHands[0].transform, m_leapHands[1].transform, m_leapElbows[0].transform, m_leapElbows[1].transform);
m_leapTracked.SetEnabled(Settings.Enabled);
m_leapTracked.SetTrackElbows(Settings.TrackElbows);
m_leapTracked.SetFingersOnly(Settings.FingersOnly);
OnEnableChange(Settings.Enabled);
OnModelVisibilityChange(Settings.ModelVisibility);
OnTrackingModeChange(Settings.TrackingMode);
OnHeadAttachChange(Settings.HeadAttach); // Includes offsets and parenting
MelonLoader.MelonCoroutines.Start(WaitForRootLogic());
}
public override void OnDeinitializeMelon()
{
if(ms_instance == this)
ms_instance = null;
m_leapController?.StopConnection();
m_leapController?.Dispose();
m_leapController = null;
m_gesturesData = null;
}
public override void OnUpdate()
IEnumerator WaitForRootLogic()
{
if(Settings.Enabled)
{
for(int i = 0; i < GestureMatcher.GesturesData.ms_handsCount; i++)
m_gesturesData.m_handsPresenses[i] = false;
while(ABI_RC.Core.RootLogic.Instance == null)
yield return null;
if((m_leapController != null) && m_leapController.IsConnected)
{
Leap.Frame l_frame = m_leapController.Frame();
if(l_frame != null)
{
GestureMatcher.GetGestures(l_frame, ref m_gesturesData);
for(int i = 0; i < GestureMatcher.GesturesData.ms_handsCount; i++)
{
if((m_leapHands[i] != null) && m_gesturesData.m_handsPresenses[i])
{
Vector3 l_pos = m_gesturesData.m_handsPositons[i];
Quaternion l_rot = m_gesturesData.m_handsRotations[i];
Utils.LeapToUnity(ref l_pos, ref l_rot, Settings.TrackingMode);
m_leapHands[i].transform.localPosition = l_pos;
m_leapHands[i].transform.localRotation = l_rot;
l_pos = m_gesturesData.m_elbowsPositions[i];
Utils.LeapToUnity(ref l_pos, ref l_rot, Settings.TrackingMode);
m_leapElbows[i].transform.localPosition = l_pos;
}
}
}
}
if(m_leapTracked != null)
m_leapTracked.UpdateTracking(m_gesturesData);
}
}
public override void OnLateUpdate()
{
if(Settings.Enabled && !m_isInVR && (m_leapTracked != null))
m_leapTracked.UpdateTrackingLate(m_gesturesData);
}
// Settings changes
void OnEnableChange(bool p_state)
{
if(p_state)
{
m_leapController?.StartConnection();
UpdateDeviceTrackingMode();
}
else
m_leapController?.StopConnection();
}
void OnDesktopOffsetChange(Vector3 p_offset)
{
if((m_leapTrackingRoot != null) && !Settings.HeadAttach)
{
if(!m_isInVR)
m_leapTrackingRoot.transform.localPosition = p_offset * PlayerSetup.Instance.vrCameraRig.transform.localScale.x;
else
m_leapTrackingRoot.transform.localPosition = p_offset;
}
}
void OnModelVisibilityChange(bool p_state)
{
if(m_leapControllerModel != null)
m_leapControllerModel.SetActive(p_state);
}
void OnTrackingModeChange(Settings.LeapTrackingMode p_mode)
{
if(Settings.Enabled)
UpdateDeviceTrackingMode();
if(m_leapControllerModel != null)
{
switch(p_mode)
{
case Settings.LeapTrackingMode.Screentop:
m_leapControllerModel.transform.localRotation = Quaternion.Euler(0f, 0f, 180f);
break;
case Settings.LeapTrackingMode.Desktop:
m_leapControllerModel.transform.localRotation = Quaternion.identity;
break;
case Settings.LeapTrackingMode.HMD:
m_leapControllerModel.transform.localRotation = Quaternion.Euler(270f, 180f, 0f);
break;
}
}
}
void OnRootAngleChange(Vector3 p_angle)
{
if(m_leapTrackingRoot != null)
m_leapTrackingRoot.transform.localRotation = Quaternion.Euler(p_angle);
}
void OnHeadAttachChange(bool p_state)
{
if(m_leapTrackingRoot != null)
{
if(p_state)
{
if(!m_isInVR)
{
m_leapTrackingRoot.transform.parent = PlayerSetup.Instance.desktopCamera.transform;
m_leapTrackingRoot.transform.localPosition = Settings.HeadOffset * PlayerSetup.Instance.vrCameraRig.transform.localScale.x;
m_leapTrackingRoot.transform.localScale = PlayerSetup.Instance.vrCameraRig.transform.localScale;
}
else
{
m_leapTrackingRoot.transform.parent = PlayerSetup.Instance.vrCamera.transform;
m_leapTrackingRoot.transform.localPosition = Settings.HeadOffset;
m_leapTrackingRoot.transform.localScale = Vector3.one;
}
}
else
{
if(!m_isInVR)
{
m_leapTrackingRoot.transform.parent = PlayerSetup.Instance.desktopCameraRig.transform;
m_leapTrackingRoot.transform.localPosition = Settings.DesktopOffset * PlayerSetup.Instance.vrCameraRig.transform.localScale.x;
m_leapTrackingRoot.transform.localScale = PlayerSetup.Instance.vrCameraRig.transform.localScale;
}
else
{
m_leapTrackingRoot.transform.parent = PlayerSetup.Instance.vrCameraRig.transform;
m_leapTrackingRoot.transform.localPosition = Settings.DesktopOffset;
m_leapTrackingRoot.transform.localScale = Vector3.one;
}
}
m_leapTrackingRoot.transform.localRotation = Quaternion.Euler(Settings.RootAngle);
}
}
void OnHeadOffsetChange(Vector3 p_offset)
{
if((m_leapTrackingRoot != null) && Settings.HeadAttach)
{
if(!m_isInVR)
m_leapTrackingRoot.transform.localPosition = p_offset * PlayerSetup.Instance.vrCameraRig.transform.localScale.x;
else
m_leapTrackingRoot.transform.localPosition = p_offset;
}
}
// Internal utility
void UpdateDeviceTrackingMode()
{
m_leapController?.ClearPolicy(Leap.Controller.PolicyFlag.POLICY_OPTIMIZE_SCREENTOP, null);
m_leapController?.ClearPolicy(Leap.Controller.PolicyFlag.POLICY_OPTIMIZE_HMD, null);
switch(Settings.TrackingMode)
{
case Settings.LeapTrackingMode.Screentop:
m_leapController?.SetPolicy(Leap.Controller.PolicyFlag.POLICY_OPTIMIZE_SCREENTOP, null);
break;
case Settings.LeapTrackingMode.HMD:
m_leapController?.SetPolicy(Leap.Controller.PolicyFlag.POLICY_OPTIMIZE_HMD, null);
break;
}
}
// Leap events
void OnLeapDeviceInitialized(object p_sender, Leap.DeviceEventArgs p_args)
{
if(Settings.Enabled)
{
m_leapController?.SubscribeToDeviceEvents(p_args.Device);
UpdateDeviceTrackingMode();
}
if(CohtmlHud.Instance != null)
CohtmlHud.Instance.ViewDropText("Leap Motion Extension", "Device initialized");
}
void OnLeapDeviceFailure(object p_sender, Leap.DeviceFailureEventArgs p_args)
{
if(CohtmlHud.Instance != null)
CohtmlHud.Instance.ViewDropText("Leap Motion Extension", "Device failure, code " + p_args.ErrorCode + ": " + p_args.ErrorMessage);
}
void OnLeapDeviceLost(object p_sender, Leap.DeviceEventArgs p_args)
{
m_leapController?.UnsubscribeFromDeviceEvents(p_args.Device);
if(CohtmlHud.Instance != null)
CohtmlHud.Instance.ViewDropText("Leap Motion Extension", "Device lost");
}
void OnLeapServiceConnect(object p_sender, Leap.ConnectionEventArgs p_args)
{
if(CohtmlHud.Instance != null)
CohtmlHud.Instance.ViewDropText("Leap Motion Extension", "Service connected");
}
void OnLeapServiceDisconnect(object p_sender, Leap.ConnectionLostEventArgs p_args)
{
if(CohtmlHud.Instance != null)
CohtmlHud.Instance.ViewDropText("Leap Motion Extension", "Service disconnected");
m_leapManager = new GameObject("LeapMotionManager").AddComponent<LeapManager>();
}
// Patches
@ -347,8 +67,8 @@ namespace ml_lme
{
try
{
if(m_leapTracked != null)
m_leapTracked.OnAvatarClear();
if(m_leapManager != null)
m_leapManager.OnAvatarClear();
}
catch(System.Exception e)
{
@ -361,12 +81,8 @@ namespace ml_lme
{
try
{
m_isInVR = Utils.IsInVR();
if(m_leapTracked != null)
m_leapTracked.OnSetupAvatar();
OnHeadAttachChange(Settings.HeadAttach);
if(m_leapManager != null)
m_leapManager.OnAvatarSetup();
}
catch(System.Exception e)
{
@ -379,8 +95,22 @@ namespace ml_lme
{
try
{
if(m_leapTracked != null)
m_leapTracked.OnCalibrate();
if(m_leapManager != null)
m_leapManager.OnCalibrate();
}
catch(System.Exception e)
{
MelonLoader.MelonLogger.Error(e);
}
}
static void OnRayScale_Postfix(float __0) => ms_instance?.OnRayScale(__0);
void OnRayScale(float p_scale)
{
try
{
if(m_leapManager != null)
m_leapManager.OnRayScale(p_scale);
}
catch(System.Exception e)
{

View file

@ -1,10 +1,10 @@
using System.Reflection;
[assembly: AssemblyTitle("LeapMotionExtension")]
[assembly: AssemblyVersion("1.2.9")]
[assembly: AssemblyFileVersion("1.2.9")]
[assembly: AssemblyVersion("1.3.0")]
[assembly: AssemblyFileVersion("1.3.0")]
[assembly: MelonLoader.MelonInfo(typeof(ml_lme.LeapMotionExtension), "LeapMotionExtension", "1.2.9", "SDraw", "https://github.com/SDraw/ml_mods_cvr")]
[assembly: MelonLoader.MelonInfo(typeof(ml_lme.LeapMotionExtension), "LeapMotionExtension", "1.3.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)]

View file

@ -1,5 +1,5 @@
# Leap Motion Extension
This mod allows you to use your Leap Motion controller for hands and fingers visual tracking.
This mod allows you to use your Leap Motion controller for hands and fingers tracking.
[![](.github/img_01.png)](https://youtu.be/nak1C8uibgc)
@ -21,3 +21,6 @@ Available mod's settings in `Settings - Implementation - Leap Motion Tracking`:
* **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.
* **Model visibility:** show Leap Motion controller model, useful for tracking visualizing, disabled by default.
* **Interaction input:** enables in-game interactions (props, menu and etc.); `true` by default.
** Note: Suggested to use with disabled `Settings - Input & Key-Bindings - Use grip to grab`.
* **Hold/Release gesture threadhold:** limits for interaction/grip activation based on hand gesture; 50 by default.

View file

@ -31,7 +31,10 @@ namespace ml_lme
HeadX,
HeadY,
HeadZ,
TrackElbows
TrackElbows,
Input,
HoldThreadhold,
ReleaseThreadhold
};
static bool ms_enabled = false;
@ -43,6 +46,9 @@ namespace ml_lme
static bool ms_headAttach = false;
static Vector3 ms_headOffset = new Vector3(0f, -0.3f, 0.15f);
static bool ms_trackElbows = true;
static bool ms_input = true;
static float ms_holdThreadhold = 0.5f;
static float ms_releaseThreadhold = 0.5f;
static MelonLoader.MelonPreferences_Category ms_category = null;
static List<MelonLoader.MelonPreferences_Entry> ms_entries = null;
@ -56,27 +62,35 @@ namespace ml_lme
static public event Action<bool> HeadAttachChange;
static public event Action<Vector3> HeadOffsetChange;
static public event Action<bool> TrackElbowsChange;
static public event Action<bool> InputChange;
static public event Action<float> HoldThreadholdChange;
static public event Action<float> ReleaseThreadholdChange;
public static void Init()
internal static void Init()
{
ms_category = MelonLoader.MelonPreferences.CreateCategory("LME");
ms_entries = new List<MelonLoader.MelonPreferences_Entry>();
ms_entries.Add(ms_category.CreateEntry(ModSetting.Enabled.ToString(), ms_enabled));
ms_entries.Add(ms_category.CreateEntry(ModSetting.DesktopX.ToString(), 0));
ms_entries.Add(ms_category.CreateEntry(ModSetting.DesktopY.ToString(), -45));
ms_entries.Add(ms_category.CreateEntry(ModSetting.DesktopZ.ToString(), 30));
ms_entries.Add(ms_category.CreateEntry(ModSetting.FingersOnly.ToString(), ms_modelVisibility));
ms_entries.Add(ms_category.CreateEntry(ModSetting.Model.ToString(), ms_modelVisibility));
ms_entries.Add(ms_category.CreateEntry(ModSetting.Mode.ToString(), (int)ms_trackingMode));
ms_entries.Add(ms_category.CreateEntry(ModSetting.AngleX.ToString(), 0));
ms_entries.Add(ms_category.CreateEntry(ModSetting.AngleY.ToString(), 0));
ms_entries.Add(ms_category.CreateEntry(ModSetting.AngleZ.ToString(), 0));
ms_entries.Add(ms_category.CreateEntry(ModSetting.Head.ToString(), ms_headAttach));
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.HeadZ.ToString(), 15));
ms_entries.Add(ms_category.CreateEntry(ModSetting.TrackElbows.ToString(), true));
ms_entries = new List<MelonLoader.MelonPreferences_Entry>()
{
ms_category.CreateEntry(ModSetting.Enabled.ToString(), ms_enabled),
ms_category.CreateEntry(ModSetting.DesktopX.ToString(), 0),
ms_category.CreateEntry(ModSetting.DesktopY.ToString(), -45),
ms_category.CreateEntry(ModSetting.DesktopZ.ToString(), 30),
ms_category.CreateEntry(ModSetting.FingersOnly.ToString(), ms_modelVisibility),
ms_category.CreateEntry(ModSetting.Model.ToString(), ms_modelVisibility),
ms_category.CreateEntry(ModSetting.Mode.ToString(), (int)ms_trackingMode),
ms_category.CreateEntry(ModSetting.AngleX.ToString(), 0),
ms_category.CreateEntry(ModSetting.AngleY.ToString(), 0),
ms_category.CreateEntry(ModSetting.AngleZ.ToString(), 0),
ms_category.CreateEntry(ModSetting.Head.ToString(), ms_headAttach),
ms_category.CreateEntry(ModSetting.HeadX.ToString(), 0),
ms_category.CreateEntry(ModSetting.HeadY.ToString(), -30),
ms_category.CreateEntry(ModSetting.HeadZ.ToString(), 15),
ms_category.CreateEntry(ModSetting.TrackElbows.ToString(), true),
ms_category.CreateEntry(ModSetting.Input.ToString(), true),
ms_category.CreateEntry(ModSetting.HoldThreadhold.ToString(), 50),
ms_category.CreateEntry(ModSetting.ReleaseThreadhold.ToString(), 50),
};
Load();
@ -129,6 +143,9 @@ namespace ml_lme
(int)ms_entries[(int)ModSetting.HeadZ].BoxedValue
) * 0.01f;
ms_trackElbows = (bool)ms_entries[(int)ModSetting.TrackElbows].BoxedValue;
ms_input = (bool)ms_entries[(int)ModSetting.Input].BoxedValue;
ms_holdThreadhold = (int)ms_entries[(int)ModSetting.HoldThreadhold].BoxedValue * 0.01f;
ms_releaseThreadhold = (int)ms_entries[(int)ModSetting.ReleaseThreadhold].BoxedValue * 0.01f;
}
static void OnToggleUpdate(string p_name, string p_value)
@ -171,6 +188,13 @@ namespace ml_lme
TrackElbowsChange?.Invoke(ms_trackElbows);
}
break;
case ModSetting.Input:
{
ms_input = bool.Parse(p_value);
InputChange?.Invoke(ms_input);
}
break;
}
ms_entries[(int)l_setting].BoxedValue = bool.Parse(p_value);
@ -241,6 +265,18 @@ namespace ml_lme
HeadOffsetChange?.Invoke(ms_headOffset);
}
break;
case ModSetting.HoldThreadhold:
{
ms_holdThreadhold = int.Parse(p_value) * 0.01f;
HoldThreadholdChange?.Invoke(ms_holdThreadhold);
}
break;
case ModSetting.ReleaseThreadhold:
{
ms_releaseThreadhold = int.Parse(p_value) * 0.01f;
ReleaseThreadholdChange?.Invoke(ms_releaseThreadhold);
}
break;
}
ms_entries[(int)l_setting].BoxedValue = int.Parse(p_value);
@ -301,5 +337,17 @@ namespace ml_lme
{
get => ms_trackElbows;
}
public static bool Input
{
get => ms_input;
}
public static float HoldThreadhold
{
get => ms_holdThreadhold;
}
public static float ReleaseThreadhold
{
get => ms_releaseThreadhold;
}
}
}

View file

@ -1,4 +1,5 @@
using ABI_RC.Core.Player;
using ABI_RC.Core.UI;
using System.Linq;
using UnityEngine;
@ -18,6 +19,17 @@ namespace ml_lme
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 ShowHUDNotification(string p_title, string p_message, string p_small = "", bool p_immediate = false)
{
if(CohtmlHud.Instance != null)
{
if(p_immediate)
CohtmlHud.Instance.ViewDropTextImmediate(p_title, p_message, p_small);
else
CohtmlHud.Instance.ViewDropText(p_title, p_message, p_small);
}
}
public static void LeapToUnity(ref Vector3 p_pos, ref Quaternion p_rot, Settings.LeapTrackingMode p_mode)
{
p_pos *= 0.001f;

View file

@ -84,7 +84,10 @@
<Compile Include="AssetsHandler.cs" />
<Compile Include="DependenciesHandler.cs" />
<Compile Include="GestureMatcher.cs" />
<Compile Include="LeapInput.cs" />
<Compile Include="LeapManager.cs" />
<Compile Include="LeapTracked.cs" />
<Compile Include="LeapTracking.cs" />
<Compile Include="Main.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Scripts.cs" />

View file

@ -384,6 +384,27 @@ function inp_dropdown_mod_lme(_obj, _callbackName) {
<div id="Model" class ="inp_toggle no-scroll" data-current="false"></div>
</div>
</div>
<div class ="row-wrapper">
<div class ="option-caption">Interaction input: </div>
<div class ="option-input">
<div id="Input" class ="inp_toggle no-scroll" data-current="false"></div>
</div>
</div>
<div class ="row-wrapper">
<div class ="option-caption">Hold gesture threadhold: </div>
<div class ="option-input">
<div id="HoldThreadhold" class ="inp_slider no-scroll" data-min="0" data-max="100" data-current="50"></div>
</div>
</div>
<div class ="row-wrapper">
<div class ="option-caption">Release gesture threadhold: </div>
<div class ="option-input">
<div id="ReleaseThreadhold" class ="inp_slider no-scroll" data-min="0" data-max="100" data-current="50"></div>
</div>
</div>
`;
document.getElementById('settings-implementation').appendChild(l_block);

View file

@ -4,6 +4,7 @@ using UnityEngine;
namespace ml_pam
{
[DisallowMultipleComponent]
class ArmMover : MonoBehaviour
{
static readonly Vector4 ms_pointVector = new Vector4(0f, 0f, 0f, 1f);