Skip to content

Commit 713c426

Browse files
authored
Merge pull request #20938 from cabanier/updated-hands
Updated WebXR Hand implementation to the new API
2 parents a276648 + 1d3f443 commit 713c426

File tree

5 files changed

+158
-105
lines changed

5 files changed

+158
-105
lines changed

examples/jsm/webxr/XRHandOculusMeshModel.js

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,49 @@ class XRHandOculusMeshModel {
5555
'b_%_pinky3', // XRHand.LITTLE_PHALANX_DISTAL,
5656
'b_%_pinkynull', // XRHand.LITTLE_PHALANX_TIP
5757
];
58+
59+
const joints = [
60+
'wrist',
61+
'thumb-metacarpal',
62+
'thumb-phalanx-proximal',
63+
'thumb-phalanx-distal',
64+
'thumb-tip',
65+
'index-finger-metacarpal',
66+
'index-finger-phalanx-proximal',
67+
'index-finger-phalanx-intermediate',
68+
'index-finger-phalanx-distal',
69+
'index-finger-tip',
70+
'middle-finger-metacarpal',
71+
'middle-finger-phalanx-proximal',
72+
'middle-finger-phalanx-intermediate',
73+
'middle-finger-phalanx-distal',
74+
'middle-finger-tip',
75+
'ring-finger-metacarpal',
76+
'ring-finger-phalanx-proximal',
77+
'ring-finger-phalanx-intermediate',
78+
'ring-finger-phalanx-distal',
79+
'ring-finger-tip',
80+
'pinky-finger-metacarpal',
81+
'pinky-finger-phalanx-proximal',
82+
'pinky-finger-phalanx-intermediate',
83+
'pinky-finger-phalanx-distal',
84+
'pinky-finger-tip',
85+
];
86+
87+
let i = 0;
88+
5889
bonesMapping.forEach( boneName => {
5990

6091
if ( boneName ) {
6192

6293
const bone = object.getObjectByName( boneName.replace( /%/g, handedness === 'right' ? 'r' : 'l' ) );
94+
95+
if ( bone !== undefined) {
96+
97+
bone.jointName = joints [ i ];
98+
99+
}
100+
63101
this.bones.push( bone );
64102

65103
} else {
@@ -68,6 +106,8 @@ class XRHandOculusMeshModel {
68106

69107
}
70108

109+
i ++;
110+
71111
} );
72112

73113
} );
@@ -81,9 +121,10 @@ class XRHandOculusMeshModel {
81121
for ( let i = 0; i < this.bones.length; i ++ ) {
82122

83123
const bone = this.bones[ i ];
84-
const XRJoint = XRJoints[ i ];
85124

86-
if ( XRJoint ) {
125+
if ( bone ) {
126+
127+
const XRJoint = XRJoints[ bone.jointName ];
87128

88129
if ( XRJoint.visible ) {
89130

examples/jsm/webxr/XRHandPrimitiveModel.js

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,18 +35,40 @@ class XRHandPrimitiveModel {
3535
const jointMaterial = new MeshStandardMaterial( { color: 0xffffff, roughness: 1, metalness: 0 } );
3636
const tipMaterial = new MeshStandardMaterial( { color: 0x999999, roughness: 1, metalness: 0 } );
3737

38-
const tipIndexes = [
39-
window.XRHand.THUMB_PHALANX_TIP,
40-
window.XRHand.INDEX_PHALANX_TIP,
41-
window.XRHand.MIDDLE_PHALANX_TIP,
42-
window.XRHand.RING_PHALANX_TIP,
43-
window.XRHand.LITTLE_PHALANX_TIP
38+
const joints = [
39+
'wrist',
40+
'thumb-metacarpal',
41+
'thumb-phalanx-proximal',
42+
'thumb-phalanx-distal',
43+
'thumb-tip',
44+
'index-finger-metacarpal',
45+
'index-finger-phalanx-proximal',
46+
'index-finger-phalanx-intermediate',
47+
'index-finger-phalanx-distal',
48+
'index-finger-tip',
49+
'middle-finger-metacarpal',
50+
'middle-finger-phalanx-proximal',
51+
'middle-finger-phalanx-intermediate',
52+
'middle-finger-phalanx-distal',
53+
'middle-finger-tip',
54+
'ring-finger-metacarpal',
55+
'ring-finger-phalanx-proximal',
56+
'ring-finger-phalanx-intermediate',
57+
'ring-finger-phalanx-distal',
58+
'ring-finger-tip',
59+
'pinky-finger-metacarpal',
60+
'pinky-finger-phalanx-proximal',
61+
'pinky-finger-phalanx-intermediate',
62+
'pinky-finger-phalanx-distal',
63+
'pinky-finger-tip'
4464
];
45-
for ( let i = 0; i <= window.XRHand.LITTLE_PHALANX_TIP; i ++ ) {
4665

47-
var cube = new Mesh( geometry, tipIndexes.indexOf( i ) !== - 1 ? tipMaterial : jointMaterial );
66+
for ( const jointName of joints ) {
67+
68+
var cube = new Mesh( geometry, jointName.indexOf( 'tip' ) !== - 1 ? tipMaterial : jointMaterial );
4869
cube.castShadow = true;
4970
cube.receiveShadow = true;
71+
cube.jointName = jointName;
5072
this.handMesh.add( cube );
5173

5274
}
@@ -66,7 +88,7 @@ class XRHandPrimitiveModel {
6688
for ( let i = 0; i < objects.length; i ++ ) {
6789

6890
const jointMesh = objects[ i ];
69-
const XRJoint = XRJoints[ i ];
91+
const XRJoint = XRJoints[ jointMesh.jointName ];
7092

7193
if ( XRJoint.visible ) {
7294

examples/webxr_vr_handinput_cubes.html

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@
163163

164164
if ( grabbing ) {
165165

166-
const indexTip = controller.joints[ XRHand.INDEX_PHALANX_TIP ];
166+
const indexTip = controller.joints[ 'index-finger-tip' ];
167167
const sphere = collideObject( indexTip );
168168

169169
if ( sphere ) {
@@ -175,7 +175,7 @@
175175
scaling.active = true;
176176
scaling.object = sphere;
177177
scaling.initialScale = sphere.scale.x;
178-
scaling.initialDistance = indexTip.position.distanceTo( hand2.joints[ XRHand.INDEX_PHALANX_TIP ].position );
178+
scaling.initialDistance = indexTip.position.distanceTo( hand2.joints[ 'index-finger-tip' ].position );
179179
return;
180180

181181
}
@@ -193,7 +193,7 @@
193193
const spawn = new THREE.Mesh( geometry, material );
194194
spawn.geometry.computeBoundingSphere();
195195

196-
const indexTip = controller.joints[ XRHand.INDEX_PHALANX_TIP ];
196+
const indexTip = controller.joints[ 'index-finger-tip' ];
197197
spawn.position.copy( indexTip.position );
198198
spawn.quaternion.copy( indexTip.quaternion );
199199

@@ -225,7 +225,7 @@
225225
function onPinchStartRight( event ) {
226226

227227
const controller = event.target;
228-
const indexTip = controller.joints[ XRHand.INDEX_PHALANX_TIP ];
228+
const indexTip = controller.joints[ 'index-finger-tip' ];
229229
const object = collideObject( indexTip );
230230
if ( object ) {
231231

@@ -269,8 +269,8 @@
269269

270270
if ( scaling.active ) {
271271

272-
const indexTip1Pos = hand1.joints[ XRHand.INDEX_PHALANX_TIP ].position;
273-
const indexTip2Pos = hand2.joints[ XRHand.INDEX_PHALANX_TIP ].position;
272+
const indexTip1Pos = hand1.joints[ 'index-finger-tip' ].position;
273+
const indexTip2Pos = hand2.joints[ 'index-finger-tip' ].position;
274274
const distance = indexTip1Pos.distanceTo( indexTip2Pos );
275275
const newScale = scaling.initialScale + distance / scaling.initialDistance - 1;
276276
scaling.object.scale.setScalar( newScale );

src/renderers/webxr/WebXR.d.ts

Lines changed: 34 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -264,47 +264,44 @@ export interface XRPlane {
264264
lastChangedTime: number;
265265
}
266266

267-
export interface XRJointSpace extends XRSpace {}
267+
export declare enum XRHandJoint {
268+
'wrist',
269+
'thumb-metacarpal',
270+
'thumb-phalanx-proximal',
271+
'thumb-phalanx-distal',
272+
'thumb-tip',
273+
'index-finger-metacarpal',
274+
'index-finger-phalanx-proximal',
275+
'index-finger-phalanx-intermediate',
276+
'index-finger-phalanx-distal',
277+
'index-finger-tip',
278+
'middle-finger-metacarpal',
279+
'middle-finger-phalanx-proximal',
280+
'middle-finger-phalanx-intermediate',
281+
'middle-finger-phalanx-distal',
282+
'middle-finger-tip',
283+
'ring-finger-metacarpal',
284+
'ring-finger-phalanx-proximal',
285+
'ring-finger-phalanx-intermediate',
286+
'ring-finger-phalanx-distal',
287+
'ring-finger-tip',
288+
'pinky-finger-metacarpal',
289+
'pinky-finger-phalanx-proximal',
290+
'pinky-finger-phalanx-intermediate',
291+
'pinky-finger-phalanx-distal',
292+
'pinky-finger-tip'
293+
}
294+
295+
export interface XRJointSpace extends XRSpace {
296+
readonly jointName: XRHandJoint;
297+
}
268298

269299
export interface XRJointPose extends XRPose {
270-
radius: number | undefined;
300+
readonly radius: number | undefined;
271301
}
272302

273-
export declare class XRHand extends Array<XRJointSpace> {
274-
275-
readonly length: number;
276-
277-
static readonly WRIST = 0;
278-
279-
static readonly THUMB_METACARPAL = 1;
280-
static readonly THUMB_PHALANX_PROXIMAL = 2;
281-
static readonly THUMB_PHALANX_DISTAL = 3;
282-
static readonly THUMB_PHALANX_TIP = 4;
283-
284-
static readonly INDEX_METACARPAL = 5;
285-
static readonly INDEX_PHALANX_PROXIMAL = 6;
286-
static readonly INDEX_PHALANX_INTERMEDIATE = 7;
287-
static readonly INDEX_PHALANX_DISTAL = 8;
288-
static readonly INDEX_PHALANX_TIP = 9;
289-
290-
static readonly MIDDLE_METACARPAL = 10;
291-
static readonly MIDDLE_PHALANX_PROXIMAL = 11;
292-
static readonly MIDDLE_PHALANX_INTERMEDIATE = 12;
293-
static readonly MIDDLE_PHALANX_DISTAL = 13;
294-
static readonly MIDDLE_PHALANX_TIP = 14;
295-
296-
static readonly RING_METACARPAL = 15;
297-
static readonly RING_PHALANX_PROXIMAL = 16;
298-
static readonly RING_PHALANX_INTERMEDIATE = 17;
299-
static readonly RING_PHALANX_DISTAL = 18;
300-
static readonly RING_PHALANX_TIP = 19;
301-
302-
static readonly LITTLE_METACARPAL = 20;
303-
static readonly LITTLE_PHALANX_PROXIMAL = 21;
304-
static readonly LITTLE_PHALANX_INTERMEDIATE = 22;
305-
static readonly LITTLE_PHALANX_DISTAL = 23;
306-
static readonly LITTLE_PHALANX_TIP = 24;
307-
303+
export interface XRHand extends Map<XRHandJoint, XRJointSpace> {
304+
readonly size: number;
308305
}
309306

310307
declare type Constructor<T = object> = {

src/renderers/webxr/WebXRController.js

Lines changed: 44 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -20,25 +20,9 @@ Object.assign( WebXRController.prototype, {
2020
this._hand.matrixAutoUpdate = false;
2121
this._hand.visible = false;
2222

23-
this._hand.joints = [];
23+
this._hand.joints = {};
2424
this._hand.inputState = { pinching: false };
2525

26-
if ( window.XRHand ) {
27-
28-
for ( let i = 0; i <= window.XRHand.LITTLE_PHALANX_TIP; i ++ ) {
29-
30-
// The transform of this joint will be updated with the joint pose on each frame
31-
const joint = new Group();
32-
joint.matrixAutoUpdate = false;
33-
joint.visible = false;
34-
this._hand.joints.push( joint );
35-
// ??
36-
this._hand.add( joint );
37-
38-
}
39-
40-
}
41-
4226
}
4327

4428
return this._hand;
@@ -139,55 +123,64 @@ Object.assign( WebXRController.prototype, {
139123

140124
handPose = true;
141125

142-
for ( let i = 0; i <= window.XRHand.LITTLE_PHALANX_TIP; i ++ ) {
126+
for ( const inputjoint of inputSource.hand.values() ) {
143127

144-
if ( inputSource.hand[ i ] ) {
128+
// Update the joints groups with the XRJoint poses
129+
const jointPose = frame.getJointPose( inputjoint, referenceSpace );
145130

146-
// Update the joints groups with the XRJoint poses
147-
const jointPose = frame.getJointPose( inputSource.hand[ i ], referenceSpace );
148-
const joint = hand.joints[ i ];
131+
if ( hand.joints[ inputjoint.jointName ] === undefined ) {
149132

150-
if ( jointPose !== null ) {
133+
// The transform of this joint will be updated with the joint pose on each frame
134+
const joint = new Group();
135+
joint.matrixAutoUpdate = false;
136+
joint.visible = false;
137+
hand.joints[ inputjoint.jointName ] = joint;
138+
// ??
139+
hand.add( joint );
151140

152-
joint.matrix.fromArray( jointPose.transform.matrix );
153-
joint.matrix.decompose( joint.position, joint.rotation, joint.scale );
154-
joint.jointRadius = jointPose.radius;
141+
}
142+
143+
const joint = hand.joints[ inputjoint.jointName ];
144+
145+
if ( jointPose !== null ) {
155146

156-
}
147+
joint.matrix.fromArray( jointPose.transform.matrix );
148+
joint.matrix.decompose( joint.position, joint.rotation, joint.scale );
149+
joint.jointRadius = jointPose.radius;
157150

158-
joint.visible = jointPose !== null;
151+
}
159152

160-
// Custom events
153+
joint.visible = jointPose !== null;
161154

162-
// Check pinch
163-
const indexTip = hand.joints[ window.XRHand.INDEX_PHALANX_TIP ];
164-
const thumbTip = hand.joints[ window.XRHand.THUMB_PHALANX_TIP ];
165-
const distance = indexTip.position.distanceTo( thumbTip.position );
155+
}
166156

167-
const distanceToPinch = 0.02;
168-
const threshold = 0.005;
157+
// Custom events
169158

170-
if ( hand.inputState.pinching && distance > distanceToPinch + threshold ) {
159+
// Check pinchz
160+
const indexTip = hand.joints[ 'index-finger-tip' ];
161+
const thumbTip = hand.joints[ 'thumb-tip' ];
162+
const distance = indexTip.position.distanceTo( thumbTip.position );
171163

172-
hand.inputState.pinching = false;
173-
this.dispatchEvent( {
174-
type: 'pinchend',
175-
handedness: inputSource.handedness,
176-
target: this
177-
} );
164+
const distanceToPinch = 0.02;
165+
const threshold = 0.005;
178166

179-
} else if ( ! hand.inputState.pinching && distance <= distanceToPinch - threshold ) {
167+
if ( hand.inputState.pinching && distance > distanceToPinch + threshold ) {
180168

181-
hand.inputState.pinching = true;
182-
this.dispatchEvent( {
183-
type: 'pinchstart',
184-
handedness: inputSource.handedness,
185-
target: this
186-
} );
169+
hand.inputState.pinching = false;
170+
this.dispatchEvent( {
171+
type: 'pinchend',
172+
handedness: inputSource.handedness,
173+
target: this
174+
} );
187175

188-
}
176+
} else if ( ! hand.inputState.pinching && distance <= distanceToPinch - threshold ) {
189177

190-
}
178+
hand.inputState.pinching = true;
179+
this.dispatchEvent( {
180+
type: 'pinchstart',
181+
handedness: inputSource.handedness,
182+
target: this
183+
} );
191184

192185
}
193186

0 commit comments

Comments
 (0)