OculusHandTools plugin for Unreal Engine on Meta Quest. It showcases how to encode hand poses with weighted bone matching, build gesture state machines, and implement cone-based raycasting with distance-based velocity scaling for force grab interactions.OculusHandTools plugin with 3 modules: OculusHandPoseRecognition, OculusInteractable, and OculusUtils. For detailed build and configuration steps, see the project README.| Feature | What it demonstrates | Key concepts |
|---|---|---|
Pose encoding | Storing hand poses as bone rotation data | Pitch/yaw/roll per bone, compact string encoding |
Weighted bone matching | Emphasizing specific bones for pose recognition | *3 multipliers on key bones, omitting irrelevant bones |
Gesture state machine | Detecting sequential pose transitions | Point/200 states, Flick gesture |
Cone-based raycasting | Far-field object selection with acquisition/stickiness | AInteractableSelector, 10° acquisition, 20° stickiness |
Force grab | Pulling distant objects with velocity scaling | Distance-based velocity (1x at 20cm, 4x at 40cm) |
Custom collision | Dedicated channel for interactables | ECC_GameTraceChannel1, “Interactable” channel |
AInteractableSelector uses a cone-based raycast with 10° acquisition angle for initial selection and 20° stickiness angle to prevent accidental deselection. Force grab pulls objects toward the hand with exponential velocity scaling: 1x speed at 20cm distance, ramping to 4x speed at 40cm distance.// Gun pose example: weight index finger bones by 3x, omit middle finger // This makes the recognizer more sensitive to index finger position // while ignoring middle finger state entirely BoneWeight[IndexFingerBone] = 3.0f; BoneWeight[MiddleFingerBone] = 0.0f; // Omitted from matching
// Gesture states: Point -> 200 = Flick // Each state transition requires the target pose confidence // to exceed threshold within the timing window
AInteractableSelector uses a cone-shaped raycast for far-field selection. The acquisition cone (10°) is narrower than the stickiness cone (20°), creating hysteresis that prevents flickering between targets:// Acquisition: object must be within 10° of ray center to select // Stickiness: selected object stays active within 20° of ray center // This prevents rapid selection switching during hand movement
// Velocity scaling: exponential curve from 1x at 20cm to 4x at 40cm // Closer objects move slowly for precision // Farther objects accelerate to reduce wait time float VelocityScale = FMath::InterpExpoOut(1.0f, 4.0f, DistanceFactor);
// Project Settings > Collision > New Trace Channel: "Interactable" // Maps to ECC_GameTraceChannel1 // Used by AInteractableSelector for cone-based raycasting