Skip to content

Commit de2fbc6

Browse files
authored
WebXR: Added XRButton (#25781)
* Added XRButton.js * Examples: Updated relevant examples using XRButton. * WebXRManager: Added setSessionMode(). * WebGLRenderer: Force clear when sessionMode is 'immersive-ar'. * Clean up. * WebXRManager: Force alpha: true in layerInit. * WebGLRenderer: Set state.buffers.color before calling clear() for 'immersive-ar'. * Fix typo * WebGLBackground: Handle WebXR sessions environmentBlendMode. * WebXRManager: Removed setSessionMode(). * XRButton: Revert extra removal.
1 parent a5aa3b7 commit de2fbc6

18 files changed

+244
-436
lines changed

examples/files.json

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -357,14 +357,9 @@
357357
],
358358
"webxr": [
359359
"webxr_ar_cones",
360-
"webxr_ar_dragging",
361360
"webxr_ar_hittest",
362361
"webxr_ar_lighting",
363-
"webxr_ar_paint",
364362
"webxr_ar_plane_detection",
365-
"webxr_vr_ballshooter",
366-
"webxr_vr_cubes",
367-
"webxr_vr_dragging",
368363
"webxr_vr_handinput",
369364
"webxr_vr_handinput_cubes",
370365
"webxr_vr_handinput_profiles",
@@ -375,12 +370,15 @@
375370
"webxr_vr_layers",
376371
"webxr_vr_panorama",
377372
"webxr_vr_panorama_depth",
378-
"webxr_vr_paint",
379373
"webxr_vr_rollercoaster",
380374
"webxr_vr_sandbox",
381-
"webxr_vr_sculpt",
382375
"webxr_vr_teleport",
383-
"webxr_vr_video"
376+
"webxr_vr_video",
377+
"webxr_xr_ballshooter",
378+
"webxr_xr_cubes",
379+
"webxr_xr_dragging",
380+
"webxr_xr_paint",
381+
"webxr_xr_sculpt"
384382
],
385383
"games": [
386384
"games_fps"

examples/jsm/webxr/XRButton.js

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
class XRButton {
2+
3+
static createButton( renderer ) {
4+
5+
const button = document.createElement( 'button' );
6+
7+
function showStartXR( mode ) {
8+
9+
let currentSession = null;
10+
11+
async function onSessionStarted( session ) {
12+
13+
session.addEventListener( 'end', onSessionEnded );
14+
15+
await renderer.xr.setSession( session );
16+
17+
button.textContent = 'STOP XR';
18+
19+
currentSession = session;
20+
21+
}
22+
23+
function onSessionEnded( /*event*/ ) {
24+
25+
currentSession.removeEventListener( 'end', onSessionEnded );
26+
27+
button.textContent = 'START XR';
28+
29+
currentSession = null;
30+
31+
}
32+
33+
//
34+
35+
button.style.display = '';
36+
37+
button.style.cursor = 'pointer';
38+
button.style.left = 'calc(50% - 50px)';
39+
button.style.width = '100px';
40+
41+
button.textContent = 'START XR';
42+
43+
button.onmouseenter = function () {
44+
45+
button.style.opacity = '1.0';
46+
47+
};
48+
49+
button.onmouseleave = function () {
50+
51+
button.style.opacity = '0.5';
52+
53+
};
54+
55+
button.onclick = function () {
56+
57+
if ( currentSession === null ) {
58+
59+
const sessionInit = {
60+
optionalFeatures: [
61+
'local-floor',
62+
'bounded-floor',
63+
'hand-tracking',
64+
'layers'
65+
]
66+
};
67+
68+
navigator.xr.requestSession( mode, sessionInit )
69+
.then( onSessionStarted );
70+
71+
} else {
72+
73+
currentSession.end();
74+
75+
}
76+
77+
};
78+
79+
}
80+
81+
function disableButton() {
82+
83+
button.style.display = '';
84+
85+
button.style.cursor = 'auto';
86+
button.style.left = 'calc(50% - 75px)';
87+
button.style.width = '150px';
88+
89+
button.onmouseenter = null;
90+
button.onmouseleave = null;
91+
92+
button.onclick = null;
93+
94+
}
95+
96+
function showXRNotSupported() {
97+
98+
disableButton();
99+
100+
button.textContent = 'XR NOT SUPPORTED';
101+
102+
}
103+
104+
function showXRNotAllowed( exception ) {
105+
106+
disableButton();
107+
108+
console.warn( 'Exception when trying to call xr.isSessionSupported', exception );
109+
110+
button.textContent = 'XR NOT ALLOWED';
111+
112+
}
113+
114+
function stylizeElement( element ) {
115+
116+
element.style.position = 'absolute';
117+
element.style.bottom = '20px';
118+
element.style.padding = '12px 6px';
119+
element.style.border = '1px solid #fff';
120+
element.style.borderRadius = '4px';
121+
element.style.background = 'rgba(0,0,0,0.1)';
122+
element.style.color = '#fff';
123+
element.style.font = 'normal 13px sans-serif';
124+
element.style.textAlign = 'center';
125+
element.style.opacity = '0.5';
126+
element.style.outline = 'none';
127+
element.style.zIndex = '999';
128+
129+
}
130+
131+
if ( 'xr' in navigator ) {
132+
133+
button.id = 'XRButton';
134+
button.style.display = 'none';
135+
136+
stylizeElement( button );
137+
138+
navigator.xr.isSessionSupported( 'immersive-ar' )
139+
.then( function ( supported ) {
140+
141+
if ( supported ) {
142+
143+
showStartXR( 'immersive-ar' );
144+
145+
} else {
146+
147+
navigator.xr.isSessionSupported( 'immersive-vr' )
148+
.then( function ( supported ) {
149+
150+
if ( supported ) {
151+
152+
showStartXR( 'immersive-vr' );
153+
154+
} else {
155+
156+
showXRNotSupported();
157+
158+
}
159+
160+
} ).catch( showXRNotAllowed );
161+
162+
}
163+
164+
} ).catch( showXRNotAllowed );
165+
166+
return button;
167+
168+
} else {
169+
170+
const message = document.createElement( 'a' );
171+
172+
if ( window.isSecureContext === false ) {
173+
174+
message.href = document.location.href.replace( /^http:/, 'https:' );
175+
message.innerHTML = 'WEBXR NEEDS HTTPS'; // TODO Improve message
176+
177+
} else {
178+
179+
message.href = 'https://immersiveweb.dev/';
180+
message.innerHTML = 'WEBXR NOT AVAILABLE';
181+
182+
}
183+
184+
message.style.left = 'calc(50% - 90px)';
185+
message.style.width = '180px';
186+
message.style.textDecoration = 'none';
187+
188+
stylizeElement( message );
189+
190+
return message;
191+
192+
}
193+
194+
}
195+
196+
}
197+
198+
export { XRButton };
-31.1 KB
Binary file not shown.
-3.33 KB
Binary file not shown.
File renamed without changes.
File renamed without changes.

0 commit comments

Comments
 (0)