UNPKG

playcanvas

Version:

Open-source WebGL/WebGPU 3D engine for the web

151 lines (150 loc) 5.16 kB
import { EventHandler } from "../../core/event-handler.js"; import { platform } from "../../core/platform.js"; import { Vec3 } from "../../core/math/vec3.js"; import { XRHAND_LEFT } from "./constants.js"; import { XrFinger } from "./xr-finger.js"; import { XrJoint } from "./xr-joint.js"; let fingerJointIds = []; const vecA = new Vec3(); const vecB = new Vec3(); const vecC = new Vec3(); if (platform.browser && window.XRHand) { fingerJointIds = [ ["thumb-metacarpal", "thumb-phalanx-proximal", "thumb-phalanx-distal", "thumb-tip"], ["index-finger-metacarpal", "index-finger-phalanx-proximal", "index-finger-phalanx-intermediate", "index-finger-phalanx-distal", "index-finger-tip"], ["middle-finger-metacarpal", "middle-finger-phalanx-proximal", "middle-finger-phalanx-intermediate", "middle-finger-phalanx-distal", "middle-finger-tip"], ["ring-finger-metacarpal", "ring-finger-phalanx-proximal", "ring-finger-phalanx-intermediate", "ring-finger-phalanx-distal", "ring-finger-tip"], ["pinky-finger-metacarpal", "pinky-finger-phalanx-proximal", "pinky-finger-phalanx-intermediate", "pinky-finger-phalanx-distal", "pinky-finger-tip"] ]; } class XrHand extends EventHandler { static EVENT_TRACKING = "tracking"; static EVENT_TRACKINGLOST = "trackinglost"; _manager; _inputSource; _tracking = false; _fingers = []; _joints = []; _jointsById = {}; _tips = []; _wrist = null; constructor(inputSource) { super(); const xrHand = inputSource._xrInputSource.hand; this._manager = inputSource._manager; this._inputSource = inputSource; if (xrHand.get("wrist")) { const joint = new XrJoint(0, "wrist", this, null); this._wrist = joint; this._joints.push(joint); this._jointsById.wrist = joint; } for (let f = 0; f < fingerJointIds.length; f++) { const finger = new XrFinger(f, this); for (let j = 0; j < fingerJointIds[f].length; j++) { const jointId = fingerJointIds[f][j]; if (!xrHand.get(jointId)) continue; const joint = new XrJoint(j, jointId, this, finger); this._joints.push(joint); this._jointsById[jointId] = joint; if (joint.tip) { this._tips.push(joint); finger._tip = joint; } finger._joints.push(joint); } } } update(frame) { const xrInputSource = this._inputSource._xrInputSource; for (let j = 0; j < this._joints.length; j++) { const joint = this._joints[j]; const jointSpace = xrInputSource.hand.get(joint._id); if (jointSpace) { let pose; if (frame.session.visibilityState !== "hidden") { pose = frame.getJointPose(jointSpace, this._manager._referenceSpace); } if (pose) { joint.update(pose); if (joint.wrist && !this._tracking) { this._tracking = true; this.fire("tracking"); } } else if (joint.wrist) { if (this._tracking) { this._tracking = false; this.fire("trackinglost"); } break; } } } const j1 = this._jointsById["thumb-metacarpal"]; const j4 = this._jointsById["thumb-tip"]; const j6 = this._jointsById["index-finger-phalanx-proximal"]; const j9 = this._jointsById["index-finger-tip"]; const j16 = this._jointsById["ring-finger-phalanx-proximal"]; const j21 = this._jointsById["pinky-finger-phalanx-proximal"]; if (j1 && j4 && j6 && j9 && j16 && j21) { this._inputSource._dirtyRay = true; this._inputSource._rayLocal.origin.lerp(j4._localPosition, j9._localPosition, 0.5); let jointL = j1; let jointR = j21; if (this._inputSource.handedness === XRHAND_LEFT) { const t = jointL; jointL = jointR; jointR = t; } vecA.sub2(jointL._localPosition, this._wrist._localPosition); vecB.sub2(jointR._localPosition, this._wrist._localPosition); vecC.cross(vecA, vecB).normalize(); vecA.lerp(j6._localPosition, j16._localPosition, 0.5); vecA.sub(this._wrist._localPosition).normalize(); this._inputSource._rayLocal.direction.lerp(vecC, vecA, 0.5).normalize(); } const squeezing = this._fingerIsClosed(1) && this._fingerIsClosed(2) && this._fingerIsClosed(3) && this._fingerIsClosed(4); if (squeezing) { if (!this._inputSource._squeezing) { this._inputSource._squeezing = true; this._inputSource.fire("squeezestart"); this._manager.input.fire("squeezestart", this._inputSource); } } else { if (this._inputSource._squeezing) { this._inputSource._squeezing = false; this._inputSource.fire("squeeze"); this._manager.input.fire("squeeze", this._inputSource); this._inputSource.fire("squeezeend"); this._manager.input.fire("squeezeend", this._inputSource); } } } _fingerIsClosed(index) { const finger = this._fingers[index]; vecA.sub2(finger.joints[0]._localPosition, finger.joints[1]._localPosition).normalize(); vecB.sub2(finger.joints[2]._localPosition, finger.joints[3]._localPosition).normalize(); return vecA.dot(vecB) < -0.8; } getJointById(id) { return this._jointsById[id] || null; } get fingers() { return this._fingers; } get joints() { return this._joints; } get tips() { return this._tips; } get wrist() { return this._wrist; } get tracking() { return this._tracking; } } export { XrHand };