mirror of
https://github.com/storytold/spark.git
synced 2026-10-09 00:09:53 +00:00
165 lines
4.8 KiB
TypeScript
165 lines
4.8 KiB
TypeScript
import type * as THREE from "three";
|
|
|
|
export class VRButton {
|
|
static createButton(
|
|
renderer: THREE.WebGLRenderer,
|
|
sessionInit: XRSessionInit = {},
|
|
): HTMLElement | null {
|
|
const navigatorXr = navigator.xr;
|
|
if (!navigatorXr) {
|
|
// Only allow creation if WebXR is supported
|
|
return null;
|
|
}
|
|
const xr = navigatorXr;
|
|
|
|
const button = document.createElement("button");
|
|
renderer.xr.enabled = true;
|
|
renderer.xr.setReferenceSpaceType("local");
|
|
|
|
function showEnterVR(/*device*/) {
|
|
let currentSession: XRSession | null = null;
|
|
|
|
async function onSessionStarted(session: XRSession) {
|
|
console.log("onSessionStarted");
|
|
|
|
session.addEventListener("end", onSessionEnded);
|
|
|
|
await renderer.xr.setSession(session);
|
|
button.textContent = "EXIT VR";
|
|
|
|
currentSession = session;
|
|
}
|
|
|
|
function onSessionEnded(/*event*/) {
|
|
console.log("onSessionEnded");
|
|
currentSession?.removeEventListener("end", onSessionEnded);
|
|
|
|
button.textContent = "ENTER VR";
|
|
|
|
currentSession = null;
|
|
}
|
|
|
|
button.style.display = "";
|
|
button.style.cursor = "pointer";
|
|
button.style.left = "calc(50% - 100px)";
|
|
button.style.width = "200px";
|
|
button.style.height = "100px";
|
|
button.textContent = "ENTER VR";
|
|
|
|
// WebXR's requestReferenceSpace only works if the corresponding feature
|
|
// was requested at session creation time. For simplicity, just ask for
|
|
// the interesting ones as optional features, but be aware that the
|
|
// requestReferenceSpace call will fail if it turns out to be unavailable.
|
|
// ('local' is always available for immersive sessions and doesn't need to
|
|
// be requested separately.)
|
|
|
|
const sessionOptions: XRSessionInit = {
|
|
...sessionInit,
|
|
optionalFeatures: [
|
|
// "local-floor",
|
|
// "bounded-floor",
|
|
// "layers",
|
|
...(sessionInit.optionalFeatures || []),
|
|
],
|
|
};
|
|
|
|
button.onmouseenter = () => {
|
|
button.style.opacity = "1.0";
|
|
};
|
|
button.onmouseleave = () => {
|
|
button.style.opacity = "0.5";
|
|
};
|
|
button.onclick = () => {
|
|
if (currentSession === null) {
|
|
console.log("requesting session");
|
|
xr.requestSession("immersive-vr", sessionOptions).then(
|
|
onSessionStarted,
|
|
);
|
|
// xr.requestSession( "immersive-ar", sessionOptions ).then( onSessionStarted );
|
|
} else {
|
|
console.log("ending session");
|
|
currentSession.end();
|
|
}
|
|
};
|
|
}
|
|
|
|
function disableButton() {
|
|
button.style.display = "none";
|
|
button.style.cursor = "auto";
|
|
button.style.left = "calc(50% - 75px)";
|
|
button.style.width = "150px";
|
|
|
|
button.onmouseenter = null;
|
|
button.onmouseleave = null;
|
|
button.onclick = null;
|
|
}
|
|
|
|
function showWebXRNotFound() {
|
|
disableButton();
|
|
button.textContent = "VR NOT SUPPORTED";
|
|
}
|
|
|
|
function showVRNotAllowed(exception: any) {
|
|
disableButton();
|
|
console.warn(
|
|
"Exception when trying to call xr.isSessionSupported",
|
|
exception,
|
|
);
|
|
button.textContent = "VR NOT ALLOWED";
|
|
}
|
|
|
|
function stylizeElement(element: HTMLElement) {
|
|
element.style.position = "absolute";
|
|
element.style.bottom = "20px";
|
|
element.style.padding = "12px 6px";
|
|
element.style.border = "1px solid #fff";
|
|
element.style.borderRadius = "4px";
|
|
element.style.background = "rgba(0,0,0,0.1)";
|
|
element.style.color = "#fff";
|
|
element.style.font = "normal 13px sans-serif";
|
|
element.style.textAlign = "center";
|
|
element.style.opacity = "0.5";
|
|
element.style.outline = "none";
|
|
element.style.zIndex = "999";
|
|
}
|
|
|
|
button.id = "VRButton";
|
|
button.style.display = "none";
|
|
stylizeElement(button);
|
|
|
|
xr.isSessionSupported("immersive-vr")
|
|
.then((supported) => {
|
|
// xr.isSessionSupported( "immersive-ar" ).then( function ( supported ) {
|
|
supported ? showEnterVR() : showWebXRNotFound();
|
|
|
|
if (supported && VRButton.xrSessionIsGranted) {
|
|
button.click();
|
|
}
|
|
})
|
|
.catch(showVRNotAllowed);
|
|
|
|
return button;
|
|
}
|
|
|
|
static registerSessionGrantedListener() {
|
|
const navigatorXr = navigator.xr;
|
|
if (!navigatorXr) {
|
|
// Only allow creation if WebXR is supported
|
|
return null;
|
|
}
|
|
const xr = navigatorXr;
|
|
|
|
// WebXRViewer (based on Firefox) has a bug where addEventListener
|
|
// throws a silent exception and aborts execution entirely.
|
|
if (/WebXRViewer\//i.test(navigator.userAgent)) return;
|
|
|
|
xr.addEventListener("sessiongranted", () => {
|
|
VRButton.xrSessionIsGranted = true;
|
|
});
|
|
}
|
|
|
|
static xrSessionIsGranted = false;
|
|
}
|
|
|
|
VRButton.registerSessionGrantedListener();
|