diff --git a/public/webXR.html b/public/webXR.html index 3a8c33548ff959afe00590070da83bfa35742673..f0a49afe3c895492ce4a9a3965e7828b01608000 100644 --- a/public/webXR.html +++ b/public/webXR.html @@ -1,289 +1,28 @@ <!doctype html> -<!-- -Copyright 2021 The Immersive Web Community Group -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ---> <html> - <head> - <meta charset='utf-8'> - <meta name='viewport' content='width=device-width, initial-scale=1, user-scalable=no'> - <meta name='mobile-web-app-capable' content='yes'> - <meta name='apple-mobile-web-app-capable' content='yes'> - <link rel='icon' type='image/png' sizes='32x32' href='favicon-32x32.png'> - <link rel='icon' type='image/png' sizes='96x96' href='favicon-96x96.png'> - <link rel='stylesheet' href='css/common.css'> - - <title>Anchors</title> - </head> - <body> - <header> - <details open> - <summary>Anchors</summary> - <p> - This sample demonstrates the use of anchors to place virtual objects in stable, real-world locations. - <a class="back" href="./">Back</a> - </p> - </details> - </header> - <script type="module"> - import {WebXRButton} from './js/util/webxr-button.js'; - import {Scene} from './js/render/scenes/scene.js'; - import {Renderer, createWebGLContext} from './js/render/core/renderer.js'; - import {SkyboxNode} from './js/render/nodes/skybox.js'; - import {InlineViewerHelper} from './js/util/inline-viewer-helper.js'; - import {Gltf2Node} from './js/render/nodes/gltf2.js'; - import {QueryArgs} from './js/util/query-args.js'; - - // If requested, use the polyfill to provide support for mobile devices - // and devices which only support WebVR. - import WebXRPolyfill from './js/third-party/webxr-polyfill/build/webxr-polyfill.module.js'; - if (QueryArgs.getBool('usePolyfill', true)) { - let polyfill = new WebXRPolyfill(); - } - - // XR globals. - let xrButton = null; - let xrImmersiveRefSpace = null; - let inlineViewerHelper = null; - - let isARAvailable = false; - let isVRAvailable = false; - let xrSessionString = 'immersive-vr'; - - // WebGL scene globals. - let gl = null; - let renderer = null; - let scene = new Scene(); - let solarSystem = new Gltf2Node({url: 'media/gltf/space/space.gltf'}); - // The solar system is big (citation needed). Scale it down so that users - // can move around the planets more easily. - solarSystem.scale = [0.1, 0.1, 0.1]; - scene.addNode(solarSystem); - // Still adding a skybox, but only for the benefit of the inline view. - let skybox = new SkyboxNode({url: 'media/textures/milky-way-4k.png'}); - scene.addNode(skybox); - - const MAX_ANCHORED_OBJECTS = 30; - let anchoredObjects = []; - - // Set with all anchors tracked in a previous frame. - let all_previous_anchors = new Set(); - - function initXR() { - xrButton = new WebXRButton({ - onRequestSession: onRequestSession, - onEndSession: onEndSession, - textEnterXRTitle: isARAvailable ? "START AR" : "START VR", - textXRNotFoundTitle: isARAvailable ? "AR NOT FOUND" : "VR NOT FOUND", - textExitXRTitle: isARAvailable ? "EXIT AR" : "EXIT VR", - }); - document.querySelector('header').appendChild(xrButton.domElement); - - if (navigator.xr) { - // Checks to ensure that 'immersive-ar' or 'immersive-vr' mode is available, - // and only enables the button if so. - navigator.xr.isSessionSupported('immersive-ar').then((supported) => { - isARAvailable = supported; - xrButton.enabled = supported; - if (!supported) { - navigator.xr.isSessionSupported('immersive-vr').then((supported) => { - isVRAvailable = supported; - xrButton.enabled = supported; - }); - } else { - xrSessionString = 'immersive-ar'; - } - }); - - navigator.xr.requestSession('inline').then(onSessionStarted); - } - } - - function onRequestSession() { - // Requests an 'immersive-ar' or 'immersive-vr' session, depending on which is supported, - // and requests the 'anchors' module as a required feature. - return navigator.xr.requestSession(xrSessionString, {requiredFeatures: ['anchors']}) - .then((session) => { - xrButton.setSession(session); - session.isImmersive = true; - onSessionStarted(session); - }); - } - - function initGL() { - if (gl) - return; - - gl = createWebGLContext({ - xrCompatible: true - }); - document.body.appendChild(gl.canvas); - - function onResize() { - gl.canvas.width = gl.canvas.clientWidth * window.devicePixelRatio; - gl.canvas.height = gl.canvas.clientHeight * window.devicePixelRatio; - } - window.addEventListener('resize', onResize); - onResize(); - - renderer = new Renderer(gl); - - scene.setRenderer(renderer); - } - - function onSessionStarted(session) { - session.addEventListener('end', onSessionEnded); - session.addEventListener('select', onSelect); - - if (session.isImmersive && isARAvailable) { - // When in 'immersive-ar' mode don't draw an opaque background because - // we want the real world to show through. - skybox.visible = false; - } - - initGL(); - - // This and all future samples that visualize controllers will use this - // convenience method to listen for changes to the active XRInputSources - // and load the right meshes based on the profiles array. - scene.inputRenderer.useProfileControllerMeshes(session); - - session.updateRenderState({ baseLayer: new XRWebGLLayer(session, gl) }); - - let refSpaceType = session.isImmersive ? 'local' : 'viewer'; - session.requestReferenceSpace(refSpaceType).then((refSpace) => { - if (session.isImmersive) { - xrImmersiveRefSpace = refSpace; - } else { - inlineViewerHelper = new InlineViewerHelper(gl.canvas, refSpace); - } - session.requestAnimationFrame(onXRFrame); - }); - } - - function onEndSession(session) { - session.end(); - } - - function onSessionEnded(event) { - if (event.session.isImmersive) { - xrButton.setSession(null); - // Turn the background back on when we go back to the inlive view. - skybox.visible = true; - } - } - - function addAnchoredObjectToScene(anchor) { - console.debug("Anchor created"); - - anchor.context = {}; - - let flower = new Gltf2Node({url: 'media/gltf/sunflower/sunflower.gltf'}); - scene.addNode(flower); - anchor.context.sceneObject = flower; - flower.anchor = anchor; - anchoredObjects.push(flower); - - // For performance reasons if we add too many objects start - // removing the oldest ones to keep the scene complexity - // from growing too much. - if (anchoredObjects.length > MAX_ANCHORED_OBJECTS) { - let objectToRemove = anchoredObjects.shift(); - scene.removeNode(objectToRemove); - objectToRemove.anchor.delete(); - } - } - - function onSelect(event) { - let frame = event.frame; - let session = frame.session; - let anchorPose = new XRRigidTransform(); - let inputSource = event.inputSource; - - // If the user is on a screen based device, place the anchor 1 meter in front of them. - // Otherwise place the anchor at the location of the input device - if (inputSource.targetRayMode == 'screen') { - anchorPose = new XRRigidTransform( - {x: 0, y: 0, z: -1}, - {x: 0, y: 0, z: 0, w: 1}); - } - - if (session.isImmersive) { - // Create a free-floating anchor. - frame.createAnchor(anchorPose, inputSource.targetRaySpace).then((anchor) => { - addAnchoredObjectToScene(anchor); - }, (error) => { - console.error("Could not create anchor: " + error); - }); - } - } - - // Called every time a XRSession requests that a new frame be drawn. - function onXRFrame(t, frame) { - let session = frame.session; - let xrRefSpace = session.isImmersive ? - xrImmersiveRefSpace : - inlineViewerHelper.referenceSpace; - let pose = frame.getViewerPose(xrRefSpace); - - // Update the position of all the anchored objects based on the currently reported positions of their anchors - const tracked_anchors = frame.trackedAnchors; - if(tracked_anchors){ - all_previous_anchors.forEach(anchor => { - if(!tracked_anchors.has(anchor)){ - scene.removeNode(anchor.sceneObject); - } - }); - - tracked_anchors.forEach(anchor => { - const anchorPose = frame.getPose(anchor.anchorSpace, xrRefSpace); - if (anchorPose) { - anchor.context.sceneObject.matrix = anchorPose.transform.matrix; - anchor.context.sceneObject.visible = true; - } else { - anchor.context.sceneObject.visible = false; - } - }); - - all_previous_anchors = tracked_anchors; - } else { - all_previous_anchors.forEach(anchor => { - scene.removeNode(anchor.sceneObject); - }); - - all_previous_anchors = new Set(); - } - - // In this sample and most samples after it we'll use a helper function - // to automatically add the right meshes for the session's input sources - // each frame. This also does simple hit detection to position the - // cursors correctly on the surface of selectable nodes. - scene.updateInputSources(frame, xrRefSpace); - - scene.startFrame(); - - session.requestAnimationFrame(onXRFrame); - - scene.drawXRFrame(frame, pose); - - scene.endFrame(); - } - - // Start the XR application. - initXR(); - </script> - </body> +<head> + <meta charset="UTF-8"> + <meta name="viewport" + content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> + <title>Hello WebXR!</title> + + <!-- three.js --> + <script src="https://unpkg.com/three@0.126.0/build/three.js"></script> +</head> +<body> + +<!-- Starting an immersive WebXR session requires user interaction. + We start this one with a simple button. --> +<button onclick="activateXR()">Start Hello WebXR</button> +<script> +async function activateXR() { + // Add a canvas element and initialize a WebGL context that is compatible with WebXR. + const canvas = document.createElement("canvas"); + document.body.appendChild(canvas); + const gl = canvas.getContext("webgl", {xrCompatible: true}); + + // To be continued in upcoming steps. +} +</script> +</body> </html>