diff --git a/ml_lme/AssetsHandler.cs b/ml_lme/AssetsHandler.cs index bbbb1e7..a56affb 100644 --- a/ml_lme/AssetsHandler.cs +++ b/ml_lme/AssetsHandler.cs @@ -9,7 +9,8 @@ namespace ml_lme { static readonly List ms_assets = new List() { - "leapmotion_controller.asset" + "leapmotion_controller.asset", + "leapmotion_hands.asset" }; static Dictionary ms_loadedAssets = new Dictionary(); diff --git a/ml_lme/GestureMatcher.cs b/ml_lme/GestureMatcher.cs index 9b93c24..cc8f39a 100644 --- a/ml_lme/GestureMatcher.cs +++ b/ml_lme/GestureMatcher.cs @@ -22,11 +22,15 @@ namespace ml_lme public readonly float[] m_spreads = null; public readonly float[] m_bends = null; public float m_grabStrength = 0f; + public Vector3[] m_fingerPosition; + public Quaternion[] m_fingerRotation; public HandData() { m_spreads = new float[5]; m_bends = new float[5]; + m_fingerPosition = new Vector3[20]; + m_fingerRotation = new Quaternion[20]; } public void Reset() @@ -37,6 +41,11 @@ namespace ml_lme m_bends[i] = 0f; m_spreads[i] = 0f; } + for(int i = 0; i < 20; i++) + { + m_fingerPosition[i].Set(0f, 0f, 0f); + m_fingerRotation[i].Set(0f, 0f, 0f, 1f); + } m_grabStrength = 0f; } } @@ -89,6 +98,9 @@ namespace ml_lme float l_angle = 0f; foreach(Leap.Bone l_bone in l_finger.bones) { + p_data.m_fingerPosition[(int)l_finger.Type * 4 + (int)l_bone.Type].Set(l_bone.PrevJoint.x, l_bone.PrevJoint.y, l_bone.PrevJoint.z); + p_data.m_fingerRotation[(int)l_finger.Type * 4 + (int)l_bone.Type].Set(l_bone.Rotation.x, l_bone.Rotation.y, l_bone.Rotation.z, l_bone.Rotation.w); + if(l_bone.Type == Leap.Bone.BoneType.TYPE_METACARPAL) { l_prevSegment = new Quaternion(l_bone.Rotation.x, l_bone.Rotation.y, l_bone.Rotation.z, l_bone.Rotation.w); diff --git a/ml_lme/LeapTracking.cs b/ml_lme/LeapTracking.cs index 251b9cd..80272fb 100644 --- a/ml_lme/LeapTracking.cs +++ b/ml_lme/LeapTracking.cs @@ -17,6 +17,9 @@ namespace ml_lme GameObject m_leapElbowLeft = null; GameObject m_leapElbowRight = null; GameObject m_leapControllerModel = null; + GameObject m_visualHands = null; + VisualHand m_visualHandLeft = null; + VisualHand m_visualHandRight = null; float m_scaleRelation = 1f; @@ -58,8 +61,21 @@ namespace ml_lme m_leapControllerModel.transform.localRotation = Quaternion.identity; } + m_visualHands = AssetsHandler.GetAsset("assets/models/hands/leaphands.prefab"); + if(m_visualHands != null) + { + m_visualHands.name = "VisualHands"; + m_visualHands.transform.parent = this.transform; + m_visualHands.transform.localPosition = Vector3.zero; + m_visualHands.transform.localRotation = Quaternion.identity; + + m_visualHandLeft = new VisualHand(m_visualHands.transform.Find("HandL"), true); + m_visualHandRight = new VisualHand(m_visualHands.transform.Find("HandR"), false); + } + Settings.DesktopOffsetChange += this.OnDesktopOffsetChange; Settings.ModelVisibilityChange += this.OnModelVisibilityChange; + Settings.VisualHandsChange += this.OnVisualHandsChange; Settings.TrackingModeChange += this.OnTrackingModeChange; Settings.RootAngleChange += this.OnRootAngleChange; Settings.HeadAttachChange += this.OnHeadAttachChange; @@ -68,6 +84,7 @@ namespace ml_lme MelonLoader.MelonCoroutines.Start(WaitForLocalPlayer()); OnModelVisibilityChange(Settings.ModelVisibility); + OnVisualHandsChange(Settings.VisualHands); OnTrackingModeChange(Settings.TrackingMode); OnRootAngleChange(Settings.RootAngle); } @@ -102,21 +119,33 @@ namespace ml_lme 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); + for(int i = 0; i < 20; i++) + Utils.LeapToUnity(ref l_data.m_leftHand.m_fingerPosition[i], ref l_data.m_leftHand.m_fingerRotation[i], 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(Settings.VisualHands) + m_visualHandLeft?.Update(l_data.m_leftHand); } 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); + for(int i = 0; i < 20; i++) + Utils.LeapToUnity(ref l_data.m_rightHand.m_fingerPosition[i], ref l_data.m_rightHand.m_fingerRotation[i], 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; + + if(Settings.VisualHands) + m_visualHandRight?.Update(l_data.m_rightHand); } } } @@ -138,6 +167,11 @@ namespace ml_lme m_leapControllerModel.SetActive(p_state); } + void OnVisualHandsChange(bool p_state) + { + m_visualHands.SetActive(p_state); + } + void OnTrackingModeChange(Settings.LeapTrackingMode p_mode) { switch(p_mode) diff --git a/ml_lme/Properties/AssemblyInfo.cs b/ml_lme/Properties/AssemblyInfo.cs index 48dc288..525b41d 100644 --- a/ml_lme/Properties/AssemblyInfo.cs +++ b/ml_lme/Properties/AssemblyInfo.cs @@ -1,10 +1,10 @@ using System.Reflection; [assembly: AssemblyTitle("LeapMotionExtension")] -[assembly: AssemblyVersion("1.3.7")] -[assembly: AssemblyFileVersion("1.3.7")] +[assembly: AssemblyVersion("1.3.8")] +[assembly: AssemblyFileVersion("1.3.8")] -[assembly: MelonLoader.MelonInfo(typeof(ml_lme.LeapMotionExtension), "LeapMotionExtension", "1.3.7", "SDraw", "https://github.com/SDraw/ml_mods_cvr")] +[assembly: MelonLoader.MelonInfo(typeof(ml_lme.LeapMotionExtension), "LeapMotionExtension", "1.3.8", "SDraw", "https://github.com/SDraw/ml_mods_cvr")] [assembly: MelonLoader.MelonGame(null, "ChilloutVR")] [assembly: MelonLoader.MelonOptionalDependencies("ml_pmc")] [assembly: MelonLoader.MelonPlatform(MelonLoader.MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)] diff --git a/ml_lme/README.md b/ml_lme/README.md index 663a96e..f523f1e 100644 --- a/ml_lme/README.md +++ b/ml_lme/README.md @@ -21,6 +21,7 @@ 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:** applies only fingers tracking, disabled by default. * **Model visibility:** shows Leap Motion controller model, useful for tracking visualizing, disabled by default. +* **Visualize hands:** shows overlayed hands model, disabled by default. * **Interaction input:** enables in-game interactions (props, menu and etc.); `true` by default. * **Interact gesture threadhold:** activation limit for interaction based on hand gesture; 80 by default. * **Grip gesture threadhold:** activation limit for grip based on hand gesture; 40 by default. diff --git a/ml_lme/Settings.cs b/ml_lme/Settings.cs index 56ff521..eea6c63 100644 --- a/ml_lme/Settings.cs +++ b/ml_lme/Settings.cs @@ -34,7 +34,8 @@ namespace ml_lme TrackElbows, Input, InteractThreadhold, - GripThreadhold + GripThreadhold, + VisualHands }; public static bool Enabled { get; private set; } = false; @@ -49,6 +50,7 @@ namespace ml_lme public static bool Input { get; private set; } = true; public static float InteractThreadhold { get; private set; } = 0.8f; public static float GripThreadhold { get; private set; } = 0.4f; + public static bool VisualHands { get; private set; } = false; static MelonLoader.MelonPreferences_Category ms_category = null; static List ms_entries = null; @@ -65,6 +67,7 @@ namespace ml_lme static public event Action InputChange; static public event Action InteractThreadholdChange; static public event Action GripThreadholdChange; + static public event Action VisualHandsChange; internal static void Init() { @@ -90,6 +93,7 @@ namespace ml_lme ms_category.CreateEntry(ModSetting.Input.ToString(), Input), ms_category.CreateEntry(ModSetting.InteractThreadhold.ToString(), (int)(InteractThreadhold * 100f)), ms_category.CreateEntry(ModSetting.GripThreadhold.ToString(), (int)(GripThreadhold * 100f)), + ms_category.CreateEntry(ModSetting.VisualHands.ToString(), VisualHands) }; Load(); @@ -146,6 +150,7 @@ namespace ml_lme Input = (bool)ms_entries[(int)ModSetting.Input].BoxedValue; InteractThreadhold = (int)ms_entries[(int)ModSetting.InteractThreadhold].BoxedValue * 0.01f; GripThreadhold = (int)ms_entries[(int)ModSetting.GripThreadhold].BoxedValue * 0.01f; + VisualHands = (bool)ms_entries[(int)ModSetting.VisualHands].BoxedValue; } static void OnToggleUpdate(string p_name, string p_value) @@ -195,6 +200,13 @@ namespace ml_lme InputChange?.Invoke(Input); } break; + + case ModSetting.VisualHands: + { + VisualHands = bool.Parse(p_value); + VisualHandsChange?.Invoke(VisualHands); + } + break; } ms_entries[(int)l_setting].BoxedValue = bool.Parse(p_value); diff --git a/ml_lme/VisualHand.cs b/ml_lme/VisualHand.cs new file mode 100644 index 0000000..ee7157e --- /dev/null +++ b/ml_lme/VisualHand.cs @@ -0,0 +1,71 @@ +using UnityEngine; + +namespace ml_lme +{ + class VisualHand + { + Transform m_root = null; + Transform m_wrist = null; + Transform[] m_fingers = null; + + public VisualHand(Transform p_root, bool p_left) + { + m_root = p_root; + + if(m_root != null) + { + m_wrist = m_root.Find(p_left ? "LeftHand/Wrist" : "RightHand/Wrist"); + if(m_wrist != null) + { + m_fingers = new Transform[20]; + + m_fingers[0] = null; // Actual thumb-meta, look at Leap Motion docs, dummy + m_fingers[1] = m_wrist.Find("thumb_meta"); + m_fingers[2] = m_wrist.Find("thumb_meta/thumb_a"); + m_fingers[3] = m_wrist.Find("thumb_meta/thumb_a/thumb_b"); + + m_fingers[4] = m_wrist.Find("index_meta"); + m_fingers[5] = m_wrist.Find("index_meta/index_a"); + m_fingers[6] = m_wrist.Find("index_meta/index_a/index_b"); + m_fingers[7] = m_wrist.Find("index_meta/index_a/index_b/index_c"); + + m_fingers[8] = m_wrist.Find("middle_meta"); + m_fingers[9] = m_wrist.Find("middle_meta/middle_a"); + m_fingers[10] = m_wrist.Find("middle_meta/middle_a/middle_b"); + m_fingers[11] = m_wrist.Find("middle_meta/middle_a/middle_b/middle_c"); + + m_fingers[12] = m_wrist.Find("ring_meta"); + m_fingers[13] = m_wrist.Find("ring_meta/ring_a"); + m_fingers[14] = m_wrist.Find("ring_meta/ring_a/ring_b"); + m_fingers[15] = m_wrist.Find("ring_meta/ring_a/ring_b/ring_c"); + + m_fingers[16] = m_wrist.Find("pinky_meta"); + m_fingers[17] = m_wrist.Find("pinky_meta/pinky_a"); + m_fingers[18] = m_wrist.Find("pinky_meta/pinky_a/pinky_b"); + m_fingers[19] = m_wrist.Find("pinky_meta/pinky_a/pinky_b/pinky_c"); + } + } + } + + public void Update(GestureMatcher.HandData p_data) + { + if(m_wrist != null) + { + m_wrist.position = p_data.m_position; + m_wrist.rotation = p_data.m_rotation; + + for(int i = 0; i < 20; i++) + { + if(m_fingers[i] != null) + { + //m_fingers[i].position = p_data.m_fingerPosition[i]; + m_fingers[i].rotation = p_data.m_fingerRotation[i]; + } + } + + m_wrist.localPosition = p_data.m_position; + m_wrist.localRotation = p_data.m_rotation; + } + } + } +} diff --git a/ml_lme/ml_lme.csproj b/ml_lme/ml_lme.csproj index 62a7c85..18a5ecf 100644 --- a/ml_lme/ml_lme.csproj +++ b/ml_lme/ml_lme.csproj @@ -83,6 +83,14 @@ C:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.CoreModule.dll False + + False + D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.InputLegacyModule.dll + + + False + D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.PhysicsModule.dll + @@ -130,6 +138,7 @@ + @@ -142,6 +151,9 @@ + + + copy /y "$(TargetPath)" "D:\Games\Steam\steamapps\common\ChilloutVR\Mods\" diff --git a/ml_lme/resources/leapmotion_hands.asset b/ml_lme/resources/leapmotion_hands.asset new file mode 100644 index 0000000..24c8aea Binary files /dev/null and b/ml_lme/resources/leapmotion_hands.asset differ diff --git a/ml_lme/resources/menu.js b/ml_lme/resources/menu.js index 3a54314..6ad2321 100644 --- a/ml_lme/resources/menu.js +++ b/ml_lme/resources/menu.js @@ -385,6 +385,13 @@ function inp_dropdown_mod_lme(_obj, _callbackName) { +
+
Visualize hands:
+
+
+
+
+
Interaction input: