Skip to content

Commit b946fca

Browse files
committed
WIP - WebXR Hands API
1 parent 0953c6b commit b946fca

File tree

15 files changed

+472
-155
lines changed

15 files changed

+472
-155
lines changed

Assets/WebGLTemplates/WebXR/webxr.js

Lines changed: 61 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
this.gamepads = [];
1111
this.controllerA = new XRControllerData();
1212
this.controllerB = new XRControllerData();
13+
this.handLeft = new XRHandData();
14+
this.handRight = new XRHandData();
1315
this.xrData = null;
1416
}
1517

@@ -36,6 +38,23 @@
3638
this.buttonB = 0;
3739
}
3840

41+
function XRHandData() {
42+
// TODO: set enabled 0 if hand was enable and then disable
43+
this.enabled = 0;
44+
this.hand = 0;
45+
this.joints = [];
46+
for (let i = 0; i < 25; i++) {
47+
this.joints.push(new XRJointData());
48+
}
49+
}
50+
51+
function XRJointData() {
52+
this.enabled = 0;
53+
this.position = [0, 0, 0];
54+
this.rotation = [0, 0, 0, 1];
55+
this.radius = 0;
56+
}
57+
3958
function XRManager() {
4059
this.arSession = null;
4160
this.vrSession = null;
@@ -111,7 +130,7 @@
111130
window.requestAnimationFrame( tempRender );
112131
navigator.xr.requestSession('immersive-ar', {
113132
requiredFeatures: ['local-floor'], // TODO: Get this value from Unity
114-
optionalFeatures: ['dom-overlay'],
133+
optionalFeatures: ['dom-overlay', 'hand-tracking'],
115134
domOverlay: {root: this.canvas.parentElement}
116135
}).then(async (session) => {
117136
this.waitingHandheldARHack = false;
@@ -128,7 +147,8 @@
128147
XRManager.prototype.onRequestVRSession = function () {
129148
if (!this.isVRSupported) return;
130149
navigator.xr.requestSession('immersive-vr', {
131-
requiredFeatures: ['local-floor'] // TODO: Get this value from Unity
150+
requiredFeatures: ['local-floor'], // TODO: Get this value from Unity
151+
optionalFeatures: ['hand-tracking']
132152
}).then(async (session) => {
133153
session.isImmersive = true;
134154
session.isInSession = true;
@@ -342,7 +362,35 @@
342362
for (var i = 0; i < inputSources.length; i++) {
343363
let inputSource = inputSources[i];
344364
// Show the input source if it has a grip space
345-
if (inputSource.gripSpace) {
365+
if (inputSource.hand) {
366+
var hand = 1;
367+
var xrHand = xrData.handLeft;
368+
if (inputSource.handedness == 'right') {
369+
hand = 2;
370+
xrHand = xrData.handRight;
371+
}
372+
xrHand.enabled = 1;
373+
xrHand.hand = hand;
374+
for (let j = 0; j < 25; j++) {
375+
let joint = null;
376+
if (inputSource.hand[j] !== null) {
377+
joint = frame.getJointPose(inputSource.hand[j], refSpace);
378+
}
379+
if (joint !== null) {
380+
xrHand.joints[j].enabled = 1;
381+
xrHand.joints[j].position[0] = joint.transform.position.x;
382+
xrHand.joints[j].position[1] = joint.transform.position.y;
383+
xrHand.joints[j].position[2] = -joint.transform.position.z;
384+
xrHand.joints[j].rotation[0] = -joint.transform.orientation.x;
385+
xrHand.joints[j].rotation[1] = -joint.transform.orientation.y;
386+
xrHand.joints[j].rotation[2] = joint.transform.orientation.z;
387+
xrHand.joints[j].rotation[3] = joint.transform.orientation.w;
388+
if (joint.radius !== null) {
389+
xrHand.joints[j].radius = joint.radius;
390+
}
391+
}
392+
}
393+
} else if (inputSource.gripSpace) {
346394
let inputPose = frame.getPose(inputSource.gripSpace, refSpace);
347395
if (inputPose) {
348396
var position = inputPose.transform.position;
@@ -544,6 +592,16 @@
544592
controllerA: xrData.controllerA,
545593
controllerB: xrData.controllerB
546594
}}));
595+
596+
document.dispatchEvent(new CustomEvent('XRControllersData', { detail: {
597+
controllerA: xrData.controllerA,
598+
controllerB: xrData.controllerB
599+
}}));
600+
601+
document.dispatchEvent(new CustomEvent('XRHandsData', { detail: {
602+
handLeft: xrData.handLeft,
603+
handRight: xrData.handRight
604+
}}));
547605

548606
if (!this.didNotifyUnity)
549607
{

Assets/WebXR/Plugins/WebGL/webxr.c

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,17 @@ webxr_void_int on_start_ar_ref;
99
webxr_void_int on_start_vr_ref;
1010
webxr_void on_end_xr_ref;
1111
webxr_void_string on_xr_capabilities_ref;
12-
webxr_void_string on_webxr_data_ref;
1312

1413
void set_webxr_events(
1514
webxr_void_int _on_start_ar,
1615
webxr_void_int _on_start_vr,
1716
webxr_void _on_end_xr,
18-
webxr_void_string _on_xr_capabilities,
19-
webxr_void_string _on_webxr_data
17+
webxr_void_string _on_xr_capabilities
2018
) {
2119
on_start_ar_ref = _on_start_ar;
2220
on_start_vr_ref = _on_start_vr;
2321
on_end_xr_ref = _on_end_xr;
2422
on_xr_capabilities_ref = _on_xr_capabilities;
25-
on_webxr_data_ref = _on_webxr_data;
2623
}
2724

2825
void EMSCRIPTEN_KEEPALIVE on_start_ar(int views_count)
@@ -45,9 +42,3 @@ void EMSCRIPTEN_KEEPALIVE on_xr_capabilities(const char *display_capabilities)
4542
{
4643
on_xr_capabilities_ref(display_capabilities);
4744
}
48-
49-
// TODO: find a better way to transfer array of controllers and buttons
50-
void EMSCRIPTEN_KEEPALIVE on_webxr_data(const char *webxr_data)
51-
{
52-
on_webxr_data_ref(webxr_data);
53-
}

Assets/WebXR/Plugins/WebGL/webxr.jslib

Lines changed: 55 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,18 @@
11
/* functions called from unity */
22
mergeInto(LibraryManager.library, {
3-
InitSharedArray: function(byteOffset, length) {
4-
SharedArray = new Float32Array(buffer, byteOffset, length);
3+
InitXRSharedArray: function(byteOffset, length) {
4+
XRSharedArray = new Float32Array(buffer, byteOffset, length);
55
document.dispatchEvent(new CustomEvent('UnityLoaded', {detail: 'Ready'}));
66
},
77

8+
InitControllersArray: function(byteOffset, length) {
9+
ControllersArray = new Float32Array(buffer, byteOffset, length);
10+
},
11+
12+
InitHandsArray: function(byteOffset, length) {
13+
HandsArray = new Float32Array(buffer, byteOffset, length);
14+
},
15+
816
ListenWebXRData: function() {
917
// Listen for headset updates from webxr.jspre and load data into shared Array which we pick up in Unity.
1018
document.addEventListener('XRData', function(evt) {
@@ -14,28 +22,52 @@ mergeInto(LibraryManager.library, {
1422
var dataLength = data[key].length;
1523
if (dataLength) {
1624
for (var x = 0; x < dataLength; x++) {
17-
SharedArray[index++] = data[key][x];
25+
XRSharedArray[index++] = data[key][x];
1826
}
19-
} else {
20-
SharedArray[index++] = data[key].enabled;
21-
SharedArray[index++] = data[key].hand;
22-
SharedArray[index++] = data[key].positionX;
23-
SharedArray[index++] = data[key].positionY;
24-
SharedArray[index++] = data[key].positionZ;
25-
SharedArray[index++] = data[key].rotationX;
26-
SharedArray[index++] = data[key].rotationY;
27-
SharedArray[index++] = data[key].rotationZ;
28-
SharedArray[index++] = data[key].rotationW;
29-
SharedArray[index++] = data[key].trigger;
30-
SharedArray[index++] = data[key].squeeze;
31-
SharedArray[index++] = data[key].thumbstick;
32-
SharedArray[index++] = data[key].thumbstickX;
33-
SharedArray[index++] = data[key].thumbstickY;
34-
SharedArray[index++] = data[key].touchpad;
35-
SharedArray[index++] = data[key].touchpadX;
36-
SharedArray[index++] = data[key].touchpadY;
37-
SharedArray[index++] = data[key].buttonA;
38-
SharedArray[index++] = data[key].buttonB;
27+
}
28+
});
29+
});
30+
document.addEventListener('XRControllersData', function(evt) {
31+
var data = evt.detail;
32+
var index = 0;
33+
Object.keys(data).forEach(function (key, i) {
34+
ControllersArray[index++] = data[key].enabled;
35+
ControllersArray[index++] = data[key].hand;
36+
ControllersArray[index++] = data[key].positionX;
37+
ControllersArray[index++] = data[key].positionY;
38+
ControllersArray[index++] = data[key].positionZ;
39+
ControllersArray[index++] = data[key].rotationX;
40+
ControllersArray[index++] = data[key].rotationY;
41+
ControllersArray[index++] = data[key].rotationZ;
42+
ControllersArray[index++] = data[key].rotationW;
43+
ControllersArray[index++] = data[key].trigger;
44+
ControllersArray[index++] = data[key].squeeze;
45+
ControllersArray[index++] = data[key].thumbstick;
46+
ControllersArray[index++] = data[key].thumbstickX;
47+
ControllersArray[index++] = data[key].thumbstickY;
48+
ControllersArray[index++] = data[key].touchpad;
49+
ControllersArray[index++] = data[key].touchpadX;
50+
ControllersArray[index++] = data[key].touchpadY;
51+
ControllersArray[index++] = data[key].buttonA;
52+
ControllersArray[index++] = data[key].buttonB;
53+
});
54+
});
55+
document.addEventListener('XRHandsData', function(evt) {
56+
var data = evt.detail;
57+
var index = 0;
58+
Object.keys(data).forEach(function (key, i) {
59+
HandsArray[index++] = data[key].enabled;
60+
HandsArray[index++] = data[key].hand;
61+
for (var j = 0; j < 25; j++) {
62+
HandsArray[index++] = data[key].joints[j].enabled;
63+
HandsArray[index++] = data[key].joints[j].position[0];
64+
HandsArray[index++] = data[key].joints[j].position[1];
65+
HandsArray[index++] = data[key].joints[j].position[2];
66+
HandsArray[index++] = data[key].joints[j].rotation[0];
67+
HandsArray[index++] = data[key].joints[j].rotation[1];
68+
HandsArray[index++] = data[key].joints[j].rotation[2];
69+
HandsArray[index++] = data[key].joints[j].rotation[3];
70+
HandsArray[index++] = data[key].joints[j].radius;
3971
}
4072
});
4173
});

Assets/WebXR/Plugins/WebGL/webxr.jspre

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,3 @@ Module['WebXR'].OnXRCapabilities = function (display_capabilities) {
4141
this.OnXRCapabilitiesInternal = this.OnXRCapabilitiesInternal || Module.cwrap("on_xr_capabilities", null, ["string"]);
4242
this.OnXRCapabilitiesInternal(display_capabilities);
4343
}
44-
45-
Module['WebXR'].OnWebXRData = function (webxr_data) {
46-
this.OnWebXRDataInternal = this.OnWebXRDataInternal || Module.cwrap("on_webxr_data", null, ["string"]);
47-
this.OnWebXRDataInternal(webxr_data);
48-
}
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
%YAML 1.1
2+
%TAG !u! tag:unity3d.com,2011:
3+
--- !u!1 &8362309011548659104
4+
GameObject:
5+
m_ObjectHideFlags: 0
6+
m_CorrespondingSourceObject: {fileID: 0}
7+
m_PrefabInstance: {fileID: 0}
8+
m_PrefabAsset: {fileID: 0}
9+
serializedVersion: 6
10+
m_Component:
11+
- component: {fileID: 8513787482403080262}
12+
- component: {fileID: 7408311752882702317}
13+
- component: {fileID: 99441339568455309}
14+
- component: {fileID: 198303582149867540}
15+
- component: {fileID: -766105182191756829}
16+
m_Layer: 0
17+
m_Name: Cube
18+
m_TagString: Untagged
19+
m_Icon: {fileID: 0}
20+
m_NavMeshLayer: 0
21+
m_StaticEditorFlags: 0
22+
m_IsActive: 1
23+
--- !u!4 &8513787482403080262
24+
Transform:
25+
m_ObjectHideFlags: 0
26+
m_CorrespondingSourceObject: {fileID: 0}
27+
m_PrefabInstance: {fileID: 0}
28+
m_PrefabAsset: {fileID: 0}
29+
m_GameObject: {fileID: 8362309011548659104}
30+
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
31+
m_LocalPosition: {x: 0, y: 0, z: 0}
32+
m_LocalScale: {x: 1, y: 1, z: 1}
33+
m_Children: []
34+
m_Father: {fileID: 0}
35+
m_RootOrder: 0
36+
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
37+
--- !u!33 &7408311752882702317
38+
MeshFilter:
39+
m_ObjectHideFlags: 0
40+
m_CorrespondingSourceObject: {fileID: 0}
41+
m_PrefabInstance: {fileID: 0}
42+
m_PrefabAsset: {fileID: 0}
43+
m_GameObject: {fileID: 8362309011548659104}
44+
m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
45+
--- !u!23 &99441339568455309
46+
MeshRenderer:
47+
m_ObjectHideFlags: 0
48+
m_CorrespondingSourceObject: {fileID: 0}
49+
m_PrefabInstance: {fileID: 0}
50+
m_PrefabAsset: {fileID: 0}
51+
m_GameObject: {fileID: 8362309011548659104}
52+
m_Enabled: 1
53+
m_CastShadows: 1
54+
m_ReceiveShadows: 1
55+
m_DynamicOccludee: 1
56+
m_MotionVectors: 1
57+
m_LightProbeUsage: 1
58+
m_ReflectionProbeUsage: 1
59+
m_RayTracingMode: 2
60+
m_RenderingLayerMask: 1
61+
m_RendererPriority: 0
62+
m_Materials:
63+
- {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0}
64+
m_StaticBatchInfo:
65+
firstSubMesh: 0
66+
subMeshCount: 0
67+
m_StaticBatchRoot: {fileID: 0}
68+
m_ProbeAnchor: {fileID: 0}
69+
m_LightProbeVolumeOverride: {fileID: 0}
70+
m_ScaleInLightmap: 1
71+
m_ReceiveGI: 1
72+
m_PreserveUVs: 0
73+
m_IgnoreNormalsForChartDetection: 0
74+
m_ImportantGI: 0
75+
m_StitchLightmapSeams: 1
76+
m_SelectedEditorRenderState: 3
77+
m_MinimumChartSize: 4
78+
m_AutoUVMaxDistance: 0.5
79+
m_AutoUVMaxAngle: 89
80+
m_LightmapParameters: {fileID: 0}
81+
m_SortingLayerID: 0
82+
m_SortingLayer: 0
83+
m_SortingOrder: 0
84+
--- !u!65 &198303582149867540
85+
BoxCollider:
86+
m_ObjectHideFlags: 0
87+
m_CorrespondingSourceObject: {fileID: 0}
88+
m_PrefabInstance: {fileID: 0}
89+
m_PrefabAsset: {fileID: 0}
90+
m_GameObject: {fileID: 8362309011548659104}
91+
m_Material: {fileID: 0}
92+
m_IsTrigger: 0
93+
m_Enabled: 1
94+
serializedVersion: 2
95+
m_Size: {x: 1, y: 1, z: 1}
96+
m_Center: {x: 0, y: 0, z: 0}
97+
--- !u!54 &-766105182191756829
98+
Rigidbody:
99+
m_ObjectHideFlags: 0
100+
m_CorrespondingSourceObject: {fileID: 0}
101+
m_PrefabInstance: {fileID: 0}
102+
m_PrefabAsset: {fileID: 0}
103+
m_GameObject: {fileID: 8362309011548659104}
104+
serializedVersion: 2
105+
m_Mass: 1
106+
m_Drag: 0
107+
m_AngularDrag: 0.05
108+
m_UseGravity: 1
109+
m_IsKinematic: 0
110+
m_Interpolate: 0
111+
m_Constraints: 0
112+
m_CollisionDetection: 0

Assets/WebXR/Samples/Desert/Prefabs/Cube.prefab.meta

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

0 commit comments

Comments
 (0)