Skip to content

Commit

Permalink
hand tracking feature complete!
Browse files Browse the repository at this point in the history
  • Loading branch information
jakzo committed Aug 3, 2024
1 parent fee6a8d commit 17de48e
Show file tree
Hide file tree
Showing 4 changed files with 156 additions and 15 deletions.
3 changes: 1 addition & 2 deletions projects/Bonelab/HandTracking/src/HandTracker.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System;
using System.Linq;
using System.Collections.Generic;
using MelonLoader;
using UnityEngine;
using SLZ.Marrow.Input;
Expand Down Expand Up @@ -83,7 +82,7 @@ public struct Options {
private int _logIndex = 0;
private static TMPro.TextMeshPro _wristLog;
private string LogString(params object[] messageParts
) => string.Join(" ", messageParts.Select(part => part.ToString()));
) => string.Join(" ", messageParts.Select(part => part?.ToString()));
internal void Log(params object[] messageParts) {
var prefix = Opts.isLeft ? "[L] " : "[R] ";
Mod.Instance.LoggerInstance.Msg(prefix + LogString(messageParts));
Expand Down
22 changes: 14 additions & 8 deletions projects/Bonelab/HandTracking/src/HeadLocomotion.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using MelonLoader;
using UnityEngine;
using Sst.Utilities;
using SLZ.Marrow.Utilities;
Expand All @@ -19,24 +20,26 @@ public class HeadLocomotion : Locomotion {
private const float RUN_SCORE_DECAY = 2f;
private const float RUN_REMAINDER = 1f - WALK_SPEED;

// Only run forwards instead of any direction (easier to control)
private const bool LOCK_FORWARDS = true;

// TODO: Running start still seems a bit too sensitive
private float _startY = 0f;
private bool _isHeadDirUp = false;
private bool _isRunning = false;
private float _runningScore = 0f;
private MelonPreferences_Entry<bool> _prefForwardsOnly;

public HeadLocomotion(MelonPreferences_Entry<bool> prefForwardsOnly) {
_prefForwardsOnly = prefForwardsOnly;
}

public override void Init(HandTracker tracker) {}

public override void Update() {
var hmd = MarrowGame.xr.HMD;

// TODO: Account for controller-rotation movement setting
var direction = Rotate(
new Vector2(hmd.Position.x, hmd.Position.z), hmd.Rotation.eulerAngles.y
);
var hmdRotY = hmd.Rotation.eulerAngles.y;
var direction =
Rotate(new Vector2(hmd.Position.x, hmd.Position.z), hmdRotY);
var directionAmount =
Mathf.Clamp01((direction.magnitude - DEADZONE) / MAX_DIST);

Expand Down Expand Up @@ -67,8 +70,11 @@ public override void Update() {
_isRunning ? RUN_REMAINDER * Mathf.Clamp01(_runningScore) : 0f;

var stickAmount = directionAmount * (WALK_SPEED + runningSpeed);
Axis = LOCK_FORWARDS ? new Vector2(0f, stickAmount)
: direction.normalized *stickAmount;

Axis = direction.normalized * stickAmount;
if (_prefForwardsOnly.Value) {
Axis = new Vector2(0f, Mathf.Clamp01(Axis.Value.y));
}

// Mod.Instance.TrackerLeft.LogToWrist(
// "dir=" + direction.ToString() + ", rs=" +
Expand Down
125 changes: 125 additions & 0 deletions projects/Bonelab/HandTracking/src/Inventory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
using System;
using System.Collections.Generic;
using System.Linq;
using SLZ.Interaction;
using SLZ.Marrow.Interaction;
using SLZ.Marrow.Utilities;
using Sst.Utilities;
using UnityEngine;

namespace Sst.HandTracking;

public class Inventory {
private const float SIZE_FACTOR = 1.5f;

private HandState _lastStateLeft = new();
private HandState _lastStateRight = new();

public void Reset() {
_lastStateLeft.Reset();
_lastStateRight.Reset();
}

public void OnHandUpdate(HandTracker tracker) {
_OnHandUpdate(tracker);
if (!tracker.Opts.isLeft) {
// Mod.Instance.TrackerLeft.LogToWrist(
// "lf", _lastStateRight.IsLeftZone, "grip",
// _lastStateRight.IsGripping, "held",
// _lastStateRight.HeldReceiver?.name, "lost",
// _lastStateRight.HasLostTracking
// );
}
}
private void _OnHandUpdate(HandTracker tracker) {
var state = tracker.Opts.isLeft ? _lastStateLeft : _lastStateRight;
if (!tracker.IsTracking) {
state.Reset();
return;
}

if (!tracker.HandState.HasState ||
tracker.HandState.HandConfidence != OVRPlugin.TrackingConfidence.High) {
state.HasLostTracking = true;
return;
}

bool? isLeftZone = IsInGraceZone(tracker.ProxyController.Position, true)
? true
: IsInGraceZone(tracker.ProxyController.Position, false) ? false
: null;
var hand = tracker.GetPhysicalHand();
if (hand == null)
return;
var heldReceiver = hand.AttachedReceiver;

if (state.HasLostTracking) {
state.HasLostTracking = false;

if (state.IsLeftZone.HasValue) {
var startedGripping = state.IsGripping && !tracker.IsGripping;
var wasHolding =
state.HeldReceiver != null && heldReceiver == state.HeldReceiver;
if (startedGripping && wasHolding && hand.HasAttachedObject()) {
var slot = GetBackInventorySlotReceiver(state.IsLeftZone.Value);
if (slot._weaponHost == null) {
Dbg.Log("Inserting to inventory via grace zone");
slot.InsertInSlot(hand.AttachedReceiver.Host.Cast<InteractableHost>(
));
}
} else {
var startedUngripping = !state.IsGripping && tracker.IsGripping;
if (startedUngripping && !hand.HasAttachedObject()) {
var slot = GetBackInventorySlotReceiver(state.IsLeftZone.Value);
if (slot._weaponHost != null) {
Dbg.Log("Removing from inventory via grace zone");
var item = slot._weaponHost;
slot.DropWeapon();
// TODO: Is this the right logic to get the grip? (seems to work)
var grip =
item.TryCast<InteractableHost>()?.GetForcePullGrip()?._grip ??
item.GetGrip();
grip.OnGrabConfirm(hand, true);
}
}
}
}
}

state.IsLeftZone = isLeftZone;
state.IsGripping = tracker.IsGripping;
state.HeldReceiver = heldReceiver;
}

private bool IsInGraceZone(Vector3 pos, bool left) {
var d = left ? -1f : 1f;
var hmd = MarrowGame.xr.HMD.Position;
return pos.x * d > hmd.x * d + 0.1f && pos.x * d < hmd.x * d + 0.6f &&
pos.y > hmd.y - 0.1f && pos.y < hmd.y + 0.3f && pos.z > hmd.z - 0.3f &&
pos.z < hmd.z + 0.4f;
}

private InventorySlotReceiver GetBackInventorySlotReceiver(bool left
) => LevelHooks.RigManager.physicsRig.m_chest
.FindChild(left ? "BackLf" : "BackRt")
.GetComponent<SlotContainer>()
.inventorySlotReceiver;

public class HandState {
public bool? IsLeftZone;
public bool IsGripping;
public HandReciever HeldReceiver;
public bool HasLostTracking = false;
public Vector3 Position;
public float LastUpdate;

public void Reset() {
IsLeftZone = null;
IsGripping = false;
HeldReceiver = null;
HasLostTracking = false;
Position = Vector3.zero;
LastUpdate = 0f;
}
}
}
21 changes: 16 additions & 5 deletions projects/Bonelab/HandTracking/src/Mod.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@ public class Mod : MelonMod {
public static Mod Instance;

private MelonPreferences_Entry<LocomotionType> _prefLoco;
private MelonPreferences_Entry<bool> _prefForwardsOnly;
private HandTracker[] _trackers = { null, null };
private Locomotion _locoState;
private Jumping _jumping;
private Inventory _inventory;
private HashSet<LaserCursor> _visibleLaserCursors = new();

public HandTracker TrackerLeft {
Expand All @@ -44,6 +46,10 @@ public override void OnInitializeMelon() {
"locomotion_type", LocomotionType.HEAD, "Locomotion type",
"How running is performed (either \"HEAD\" or \"HANDS\")"
);
_prefForwardsOnly = category.CreateEntry(
"forwards_only", true, "Locomotion forwards only",
"Locks movement to forwards only (no strafing)"
);
_prefLoco.OnEntryValueChanged.Subscribe(
(newValue, prevValue) => SetupLocomotion(newValue)
);
Expand All @@ -60,7 +66,7 @@ private void SetupLocomotion(LocomotionType type) {

case LocomotionType.HEAD:
default:
_locoState = new HeadLocomotion();
_locoState = new HeadLocomotion(_prefForwardsOnly);
break;
}

Expand Down Expand Up @@ -118,6 +124,10 @@ public override void OnUpdate() {
_jumping.Update();
}

if (_inventory == null) {
_inventory = new Inventory();
}

var nonDominantTracker =
Utils.IsLocoControllerLeft() ? TrackerRight : TrackerLeft;
nonDominantTracker.ProxyController.Joystick2DAxis = new Vector2(
Expand Down Expand Up @@ -164,11 +174,12 @@ public enum LocomotionType { HEAD, HANDS }
internal static class ControllerActionMap_Refresh {
[HarmonyPrefix]
private static bool Prefix(ControllerActionMap __instance) {
var tracker = Mod.Instance.GetTrackerFromProxyController(__instance);
var tracker = Instance.GetTrackerFromProxyController(__instance);
if (tracker == null || !tracker.IsTracking)
return true;

tracker.UpdateProxyController(Instance._locoState.Axis);
Instance._inventory?.OnHandUpdate(tracker);
return false;
}
}
Expand All @@ -194,7 +205,7 @@ internal static class LaserCursor_ShowCursor {
private static void Postfix(LaserCursor __instance) {
// TODO: Point laser pointer in direction of hand
if (!__instance.cursorHidden)
Mod.Instance._visibleLaserCursors.Add(__instance);
Instance._visibleLaserCursors.Add(__instance);
}
}

Expand All @@ -203,7 +214,7 @@ internal static class LaserCursor_HideCursor {
[HarmonyPostfix]
private static void Postfix(LaserCursor __instance) {
if (__instance.cursorHidden)
Mod.Instance._visibleLaserCursors.Remove(__instance);
Instance._visibleLaserCursors.Remove(__instance);
}
}

Expand All @@ -212,7 +223,7 @@ internal static class LaserCursor_Update {
[HarmonyPrefix]
private static void Prefix(LaserCursor __instance, ref bool __state) {
var pinchedTracker =
Mod.Instance._trackers.FirstOrDefault(t => t?.PinchUp ?? false);
Instance._trackers.FirstOrDefault(t => t?.PinchUp ?? false);
if (pinchedTracker == null)
return;

Expand Down

0 comments on commit 17de48e

Please sign in to comment.