Skip to content

Commit 7b37e75

Browse files
committed
#32 Add hand tracking by using frame.fillPoses for devices that support it
1 parent 069fade commit 7b37e75

6 files changed

Lines changed: 155 additions & 41 deletions

File tree

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
%YAML 1.1
2+
%TAG !u! tag:unity3d.com,2011:
3+
--- !u!114 &11400000
4+
MonoBehaviour:
5+
m_ObjectHideFlags: 0
6+
m_CorrespondingSourceObject: {fileID: 0}
7+
m_PrefabInstance: {fileID: 0}
8+
m_PrefabAsset: {fileID: 0}
9+
m_GameObject: {fileID: 0}
10+
m_Enabled: 1
11+
m_EditorHideFlags: 0
12+
m_Script: {fileID: 11500000, guid: 8275efdbe76bdff49a97a8e82fba118d, type: 3}
13+
m_Name: SimpleWebXRHandTrackingProfile
14+
m_EditorClassIdentifier:
15+
isCustomProfile: 1
16+
jointPrefab: {fileID: 1955475817299902, guid: 6a3f88d2571cd234a86d95ee5856b9ec,
17+
type: 3}
18+
palmPrefab: {fileID: 6797406804172968804, guid: 750bdc3344567a447960aae3eda2b462,
19+
type: 3}
20+
fingertipPrefab: {fileID: 7094064642998883381, guid: b37dde41a983d664c8a09a91313733e7,
21+
type: 3}
22+
handMeshPrefab: {fileID: 1887883006053652, guid: a86f479797fea8f4189f924b3b6ad979,
23+
type: 3}
24+
handMeshVisualizationModes: -1
25+
handJointVisualizationModes: 0

SimpleWebXR-Demo/Assets/SimpleWebXR/Scripts/MRTK/Profiles/SimpleWebXRHandTrackingProfile.asset.meta

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

SimpleWebXR-Demo/Assets/SimpleWebXR/Scripts/MRTK/Profiles/SimpleWebXRInputSystemProfile.asset

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,5 +74,5 @@ MonoBehaviour:
7474
type: 2}
7575
controllerVisualizationProfile: {fileID: 11400000, guid: 345c06fdf3732db46b96299bd3cba653,
7676
type: 2}
77-
handTrackingProfile: {fileID: 11400000, guid: 7f1e3cd673742f94ca860ac7ae733024,
77+
handTrackingProfile: {fileID: 11400000, guid: 1ee5870617471ac4e82a5d1fe41dac1b,
7878
type: 2}

SimpleWebXR-Demo/Assets/SimpleWebXR/Scripts/MRTK/SimpleWebXRHand.cs

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ public override bool TryGetJoint(TrackedHandJoint joint, out MixedRealityPose po
2828

2929
#endregion IMixedRealityHand Implementation
3030

31+
private ArticulatedHandDefinition handDefinition;
32+
private ArticulatedHandDefinition HandDefinition => handDefinition ?? (handDefinition = Definition as ArticulatedHandDefinition);
33+
34+
3135

3236
public override MixedRealityInteractionMapping[] DefaultInteractions => new[]
3337
{
@@ -50,24 +54,35 @@ public override void SetupDefaultInteractions()
5054

5155
public override bool IsInPointingPose => true;
5256

57+
private MixedRealityPose GetJointMixedRealityPose(WebXRJoint joint)
58+
{
59+
60+
var position = MixedRealityPlayspace.TransformPoint(joint.Position);
61+
var rotation = MixedRealityPlayspace.Rotation * joint.Rotation;
62+
63+
return new MixedRealityPose(position, rotation);
64+
}
65+
5366
public void UpdateController(WebXRInputSource controller)
5467
{
5568
if (!Enabled) return;
5669

5770
IsPositionAvailable = IsRotationAvailable = controller.Hand.Available;
5871

5972

60-
for (int i = 0; i < WebXRHand.JOINT_COUNT; i++)
73+
jointPoses[TrackedHandJoint.Wrist] = GetJointMixedRealityPose(controller.Hand.Joints[WebXRHand.WRIST]);
74+
75+
76+
for (int i = WebXRHand.THUMB_METACARPAL; i < WebXRHand.JOINT_COUNT; i++)
6177
{
6278
var joint = controller.Hand.Joints[i];
6379

64-
var position = MixedRealityPlayspace.TransformPoint(joint.Position);
65-
var rotation = MixedRealityPlayspace.Rotation * joint.Rotation;
66-
67-
jointPoses[(TrackedHandJoint)(i + 1)] = new MixedRealityPose(position, rotation);
80+
jointPoses[(TrackedHandJoint)(i + 2)] = GetJointMixedRealityPose(joint);
6881
}
6982

70-
var indexPose = jointPoses[(TrackedHandJoint)WebXRHand.INDEX_PHALANX_TIP + 1];
83+
jointPoses[TrackedHandJoint.Palm] = new MixedRealityPose((jointPoses[TrackedHandJoint.MiddleMetacarpal].Position + jointPoses[TrackedHandJoint.MiddleMetacarpal].Position)/2, jointPoses[TrackedHandJoint.MiddleMetacarpal].Rotation) ;
84+
85+
var indexPose = jointPoses[TrackedHandJoint.IndexTip];
7186

7287
bool isSelecting;
7388
MixedRealityPose spatialPointerPose;
@@ -152,6 +167,9 @@ public void UpdateController(WebXRInputSource controller)
152167
CoreServices.InputSystem?.RaisePoseInputChanged(InputSource, ControllerHandedness, Interactions[i].MixedRealityInputAction, Interactions[i].PoseData);
153168
}
154169
break;
170+
case DeviceInputType.ThumbStick:
171+
HandDefinition?.UpdateCurrentTeleportPose(Interactions[i]);
172+
break;
155173
}
156174
}
157175
}

SimpleWebXR-Demo/ProjectSettings/EditorSettings.asset

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,33 @@
33
--- !u!159 &1
44
EditorSettings:
55
m_ObjectHideFlags: 0
6-
serializedVersion: 7
6+
serializedVersion: 9
77
m_ExternalVersionControlSupport: Visible Meta Files
88
m_SerializationMode: 2
99
m_LineEndingsForNewScripts: 2
1010
m_DefaultBehaviorMode: 0
11+
m_PrefabRegularEnvironment: {fileID: 0}
12+
m_PrefabUIEnvironment: {fileID: 0}
1113
m_SpritePackerMode: 0
1214
m_SpritePackerPaddingPower: 1
1315
m_EtcTextureCompressorBehavior: 1
1416
m_EtcTextureFastCompressor: 1
1517
m_EtcTextureNormalCompressor: 2
1618
m_EtcTextureBestCompressor: 4
17-
m_ProjectGenerationIncludedExtensions: txt;xml;fnt;cd
19+
m_ProjectGenerationIncludedExtensions: txt;xml;fnt;cd;asmref
1820
m_ProjectGenerationRootNamespace:
19-
m_UserGeneratedProjectSuffix:
2021
m_CollabEditorSettings:
2122
inProgressEnabled: 1
23+
m_EnableTextureStreamingInEditMode: 1
24+
m_EnableTextureStreamingInPlayMode: 1
25+
m_AsyncShaderCompilation: 1
26+
m_EnterPlayModeOptionsEnabled: 0
27+
m_EnterPlayModeOptions: 3
28+
m_ShowLightmapResolutionOverlay: 1
29+
m_UseLegacyProbeSampleCount: 1
30+
m_AssetPipelineMode: 0
31+
m_CacheServerMode: 0
32+
m_CacheServerEndpoint:
33+
m_CacheServerNamespacePrefix: default
34+
m_CacheServerEnableDownload: 1
35+
m_CacheServerEnableUpload: 1

com.rufus31415.simplewebxr/Runtime/Plugins/WebGL/SimpleWebXR.jslib

Lines changed: 80 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -85,20 +85,20 @@ mergeInto(LibraryManager.library, {
8585
_isArSupported = false;
8686

8787
if (!navigator.xr) {
88-
document.dispatchEvent(new CustomEvent("SimpleWebXRInitialized", {success: false}));
88+
document.dispatchEvent(new CustomEvent("SimpleWebXRInitialized", { success: false }));
8989
return
9090
};
9191

9292
// Check if WebXR immersive VR is supported (check immersive-vr before immersive-ar to make it work on Oculus Quest Browser)
9393
navigator.xr.isSessionSupported('immersive-vr').then(function (supported) {
9494
_isVrSupported = supported;
95-
document.dispatchEvent(new CustomEvent("SimpleWebXRSessionSupported", {xrSessionMode:'immersive-vr', supported: supported}));
95+
document.dispatchEvent(new CustomEvent("SimpleWebXRSessionSupported", { xrSessionMode: 'immersive-vr', supported: supported }));
9696
});
9797

9898
// Check if WebXR immersive AR is supported
9999
navigator.xr.isSessionSupported('immersive-ar').then(function (supported) {
100100
_isArSupported = supported;
101-
document.dispatchEvent(new CustomEvent("SimpleWebXRSessionSupported", {xrSessionMode:'immersive-ar', supported: supported}));
101+
document.dispatchEvent(new CustomEvent("SimpleWebXRSessionSupported", { xrSessionMode: 'immersive-ar', supported: supported }));
102102
});
103103

104104
// Initialize pointers to shared arrays that contains data (projection matrix, position, orientation, input sources)
@@ -126,7 +126,7 @@ mergeInto(LibraryManager.library, {
126126
// Share position
127127
var position = view.transform.position;
128128

129-
if(_firstFrame) _yOffset = position.y;
129+
if (_firstFrame) _yOffset = position.y;
130130

131131
_dataArray[floatStartId + 16] = position.x;
132132
_dataArray[floatStartId + 17] = position.y - _yOffset;
@@ -220,10 +220,10 @@ mergeInto(LibraryManager.library, {
220220
_byteArray[byteStartId + 3] = 0
221221
}
222222

223-
if(_dataArray[103+id]>0 && inputSource.gamepad.hapticActuators && inputSource.gamepad.hapticActuators.length>0){
223+
if (_dataArray[103 + id] > 0 && inputSource.gamepad.hapticActuators && inputSource.gamepad.hapticActuators.length > 0) {
224224
// Trigger of haptic vibration pulse(intensity [0..1], duration in ms)
225-
inputSource.gamepad.hapticActuators[0].pulse(_dataArray[101+id], _dataArray[103+id]);
226-
_dataArray[103+id] = 0; // reset flag once it's done
225+
inputSource.gamepad.hapticActuators[0].pulse(_dataArray[101 + id], _dataArray[103 + id]);
226+
_dataArray[103 + id] = 0; // reset flag once it's done
227227
}
228228
}
229229
else {
@@ -253,31 +253,80 @@ mergeInto(LibraryManager.library, {
253253

254254
// Hand detection
255255
// https://immersive-web.github.io/webxr-hand-input/#skeleton-joints-section
256-
if (inputSource.hand && inputSource.hand.length == 25) {
257-
_byteArray[46 + id] = 1; // hand supported
258-
var delta = (_useLocalSpaceForInput ? 0 : _dataArray[100])
259-
for (var j = 0; j < 25; j++) {
260-
if (inputSource.hand[j] !== null) {
261-
var joint = frame.getJointPose(inputSource.hand[j], _useLocalSpaceForInput ? _arSession.localSpace : _arSession.localFloorSpace);
262-
if (joint !== null) {
263-
var i = id * 200 + j * 8;
264-
_handArray[i] = joint.transform.position.x;
265-
_handArray[i + 1] = joint.transform.position.y - delta - _yOffset;
266-
_handArray[i + 2] = joint.transform.position.z;
267-
_handArray[i + 3] = joint.transform.orientation.x;
268-
_handArray[i + 4] = joint.transform.orientation.y;
269-
_handArray[i + 5] = joint.transform.orientation.z;
270-
_handArray[i + 6] = joint.transform.orientation.w;
271-
if (joint.radius !== null) {
272-
_handArray[i + 7] = joint.radius;
256+
if (inputSource.hand) {
257+
258+
// For browsers that support fillPoses
259+
if (typeof frame.fillPoses === "function") {
260+
_byteArray[46 + id] = 1; // hand supported
261+
262+
var delta = (_useLocalSpaceForInput ? 0 : _dataArray[100])
263+
264+
var refSpace = _useLocalSpaceForInput ? _arSession.localSpace : _arSession.localFloorSpace;
265+
266+
var radii = new Float32Array(25);
267+
var poses = new Float32Array(16 * 25);
268+
269+
if (inputSource.hand.values) {
270+
frame.fillPoses(inputSource.hand.values(), refSpace, poses);
271+
frame.fillJointRadii(inputSource.hand.values(), radii);
272+
} else {
273+
frame.fillPoses(inputSource.hand, refSpace, poses);
274+
frame.fillJointRadii(inputSource.hand, radii);
275+
}
276+
277+
for (var j = 0; j < 25; j++) {
278+
var jointIndex = j * 16;
279+
280+
var i = id * 200 + j * 8;
281+
_handArray[i] = poses[jointIndex + 12];
282+
_handArray[i + 1] = poses[jointIndex + 13] - delta - _yOffset;
283+
_handArray[i + 2] = poses[jointIndex + 14];
284+
285+
286+
_handArray[i + 7] = radii[j];
287+
288+
var quaternion = new Float32Array(4);
289+
290+
quaternion[3] = Math.sqrt(Math.max(0, 1 + poses[jointIndex + 0] + poses[jointIndex + 5] + poses[jointIndex + 10])) / 2;
291+
quaternion[0] = Math.sqrt(Math.max(0, 1 + poses[jointIndex + 0] - poses[jointIndex + 5] - poses[jointIndex + 10])) / 2;
292+
quaternion[1] = Math.sqrt(Math.max(0, 1 - poses[jointIndex + 0] + poses[jointIndex + 5] - poses[jointIndex + 10])) / 2;
293+
quaternion[2] = Math.sqrt(Math.max(0, 1 - poses[jointIndex + 0] - poses[jointIndex + 5] + poses[jointIndex + 10])) / 2;
294+
quaternion[0] *= Math.sign(quaternion[0] * (poses[jointIndex + 6] - poses[jointIndex + 9]));
295+
quaternion[1] *= Math.sign(quaternion[1] * (poses[jointIndex + 8] - poses[jointIndex + 2]));
296+
quaternion[2] *= Math.sign(quaternion[2] * (poses[jointIndex + 1] - poses[jointIndex + 4]));
297+
298+
_handArray[i + 3] = quaternion[0];
299+
_handArray[i + 4] = quaternion[1];
300+
_handArray[i + 5] = quaternion[2];
301+
_handArray[i + 6] = quaternion[3];
302+
}
303+
}
304+
else {
305+
_byteArray[46 + id] = 1; // hand supported
306+
307+
for (var j = 0; j < 25; j++) {
308+
if (inputSource.hand[j] !== null) {
309+
var joint = frame.getJointPose(inputSource.hand[j], refSpace);
310+
if (joint !== null) {
311+
var i = id * 200 + j * 8;
312+
_handArray[i] = joint.transform.position.x;
313+
_handArray[i + 1] = joint.transform.position.y - delta - _yOffset;
314+
_handArray[i + 2] = joint.transform.position.z;
315+
_handArray[i + 3] = joint.transform.orientation.x;
316+
_handArray[i + 4] = joint.transform.orientation.y;
317+
_handArray[i + 5] = joint.transform.orientation.z;
318+
_handArray[i + 6] = joint.transform.orientation.w;
319+
if (joint.radius !== null) {
320+
_handArray[i + 7] = joint.radius;
321+
}
322+
else {
323+
_handArray[i + 7] = NaN;
324+
}
273325
}
274326
else {
275-
_handArray[i + 7] = NaN;
327+
_byteArray[46 + id] = 0; // hand not fully supported
276328
}
277329
}
278-
else {
279-
_byteArray[46 + id] = 0; // hand not fully supported
280-
}
281330
}
282331
}
283332
}
@@ -434,7 +483,7 @@ mergeInto(LibraryManager.library, {
434483
}
435484
}(GLctx.bindFramebuffer);
436485

437-
document.dispatchEvent(new CustomEvent("SimpleWebXRInitialized", {success: true}));
486+
document.dispatchEvent(new CustomEvent("SimpleWebXRInitialized", { success: true }));
438487
},
439488

440489
/****************************************************************************/
@@ -468,7 +517,7 @@ mergeInto(LibraryManager.library, {
468517
GLctx.ARSessionStarted = _isArSupported;
469518
session.isInSession = true; // add field in session to indicate that a session in running
470519

471-
document.dispatchEvent(new CustomEvent("SimpleWebXRSessionStarted", {session: session, GLctx: GLctx}));
520+
document.dispatchEvent(new CustomEvent("SimpleWebXRSessionStarted", { session: session, GLctx: GLctx }));
472521

473522
var glLayer = new XRWebGLLayer(session, GLctx);
474523

@@ -531,7 +580,7 @@ mergeInto(LibraryManager.library, {
531580
_orientationInfo[0] = 0;
532581

533582
_onDeviceOrientation = function (event) {
534-
if(_orientationInfo[0] == 0) document.dispatchEvent(new CustomEvent("SimpleWebXRDeviceOrientationStarted"));
583+
if (_orientationInfo[0] == 0) document.dispatchEvent(new CustomEvent("SimpleWebXRDeviceOrientationStarted"));
535584

536585
_orientationInfo[0] = 1;
537586
_orientationArray[0] = event.alpha;

0 commit comments

Comments
 (0)