An error occurred while loading the file. Please try again.
app.js 6.53 KiB
/*
 * Copyright 2017 Google Inc. All Rights Reserved.
 * Licensed under the Apache License, Version 2.0 (the 'License');
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *     http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an 'AS IS' BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
/**
 * Query for WebXR support. If there's no support for the `immersive-ar` mode,
 * show an error.
(async function() {
  const isArSessionSupported = navigator.xr && navigator.xr.isSessionSupported && await navigator.xr.isSessionSupported("immersive-ar");
  if (isArSessionSupported) {
    document.getElementById("enter-ar").addEventListener("click", window.app.activateXR)
  } else {
    onNoXRDevice();
})();
/**
 * Container class to manage connecting to the WebXR Device API
 * and handle rendering on every frame.
class App {
  /**
   * Run when the Start AR button is pressed.
  activateXR = async () => {
    try {
      // Initialize a WebXR session using "immersive-ar".
      this.xrSession = await navigator.xr.requestSession("immersive-ar", {
        requiredFeatures: ['hit-test', 'dom-overlay'],
        domOverlay: { root: document.body }
      });
      // Create the canvas that will contain our camera's background and our virtual scene.
      this.createXRCanvas();
      // With everything set up, start the app.
      await this.onSessionStarted();
    } catch(e) {
      console.log(e);
      onNoXRDevice();
  /**
   * Add a canvas element and initialize a WebGL context that is compatible with WebXR.
  createXRCanvas() {
    this.canvas = document.createElement("canvas");
    document.body.appendChild(this.canvas);
    this.gl = this.canvas.getContext("webgl", {xrCompatible: true});
    this.xrSession.updateRenderState({
      baseLayer: new XRWebGLLayer(this.xrSession, this.gl)
    });
  /**
   * Called when the XRSession has begun. Here we set up our three.js
* renderer, scene, and camera and attach our XRWebGLLayer to the * XRSession and kick off the render loop. */ onSessionStarted = async () => { // Add the `ar` class to our body, which will hide our 2D components document.body.classList.add('ar'); // To help with working with 3D on the web, we'll use three.js. this.setupThreeJs(); // Setup an XRReferenceSpace using the "local" coordinate system. this.localReferenceSpace = await this.xrSession.requestReferenceSpace('local'); // Create another XRReferenceSpace that has the viewer as the origin. this.viewerSpace = await this.xrSession.requestReferenceSpace('viewer'); // Perform hit testing using the viewer as origin. this.hitTestSource = await this.xrSession.requestHitTestSource({ space: this.viewerSpace }); // Start a rendering loop using this.onXRFrame. this.xrSession.requestAnimationFrame(this.onXRFrame); this.xrSession.addEventListener("select", this.onSelect); } /** Place a sunflower when the screen is tapped. */ onSelect = () => { if (window.sunflower) { const clone = window.sunflower.clone(); clone.position.copy(this.reticle.position); this.scene.add(clone) const shadowMesh = this.scene.children.find(c => c.name === 'shadowMesh'); shadowMesh.position.y = clone.position.y; } } /** * Called on the XRSession's requestAnimationFrame. * Called with the time and XRPresentationFrame. */ onXRFrame = (time, frame) => { // Queue up the next draw request. this.xrSession.requestAnimationFrame(this.onXRFrame); // Bind the graphics framebuffer to the baseLayer's framebuffer. const framebuffer = this.xrSession.renderState.baseLayer.framebuffer this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, framebuffer) this.renderer.setFramebuffer(framebuffer); // Retrieve the pose of the device. // XRFrame.getViewerPose can return null while the session attempts to establish tracking. const pose = frame.getViewerPose(this.localReferenceSpace); if (pose) { // In mobile AR, we only have one view. const view = pose.views[0]; const viewport = this.xrSession.renderState.baseLayer.getViewport(view); this.renderer.setSize(viewport.width, viewport.height) // Use the view's transform matrix and projection matrix to configure the THREE.camera. this.camera.matrix.fromArray(view.transform.matrix) this.camera.projectionMatrix.fromArray(view.projectionMatrix); this.camera.updateMatrixWorld(true); // Conduct hit test. const hitTestResults = frame.getHitTestResults(this.hitTestSource); // If we have results, consider the environment stabilized. if (!this.stabilized && hitTestResults.length > 0) { this.stabilized = true;
document.body.classList.add('stabilized'); } if (hitTestResults.length > 0) { const hitPose = hitTestResults[0].getPose(this.localReferenceSpace); // Update the reticle position this.reticle.visible = true; this.reticle.position.set(hitPose.transform.position.x, hitPose.transform.position.y, hitPose.transform.position.z) this.reticle.updateMatrixWorld(true); } // Render the scene with THREE.WebGLRenderer. this.renderer.render(this.scene, this.camera) } } /** * Initialize three.js specific rendering code, including a WebGLRenderer, * a demo scene, and a camera for viewing the 3D content. */ setupThreeJs() { // To help with working with 3D on the web, we'll use three.js. // Set up the WebGLRenderer, which handles rendering to our session's base layer. this.renderer = new THREE.WebGLRenderer({ alpha: true, preserveDrawingBuffer: true, canvas: this.canvas, context: this.gl }); this.renderer.autoClear = false; this.renderer.shadowMap.enabled = true; this.renderer.shadowMap.type = THREE.PCFSoftShadowMap; // Initialize our demo scene. this.scene = DemoUtils.createLitScene(); this.reticle = new Reticle(); this.scene.add(this.reticle); // We'll update the camera matrices directly from API, so // disable matrix auto updates so three.js doesn't attempt // to handle the matrices independently. this.camera = new THREE.PerspectiveCamera(); this.camera.matrixAutoUpdate = false; } }; window.app = new App();