chromium/chrome/test/data/xr/e2e_test_files/resources/webxr_boilerplate.js

// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Add additional setup steps to the object from webxr_e2e.js if it exists.
if (typeof initializationSteps !== 'undefined') {
  initializationSteps['magicWindowStarted'] = false;
} else {
  // Create here if it doesn't exist so we can access it later without checking
  // if it's defined.
  var initializationSteps = {};
}

var webglCanvas = document.getElementById('webgl-canvas');
var glAttribs = {
  alpha: false,
  xrCompatible: true,
};
var gl = null;
var onMagicWindowXRFrameCallback = null;
var onImmersiveXRFrameCallback = null;
var onARFrameCallback = null;
var onSessionStartedCallback = null;
var onPoseCallback = null;
var shouldSubmitFrame = true;
var hasPresentedFrame = false;
var arSessionRequestWouldTriggerPermissionPrompt = null;
var shouldSetBaseLayer = true;

var sessionTypes = Object.freeze({
  IMMERSIVE: 1,
  AR: 2,
  MAGIC_WINDOW: 3
});
var sessionTypeToRequest = sessionTypes.IMMERSIVE;

var referenceSpaceMap = {
  [sessionTypes.IMMERSIVE]: 'local',
  [sessionTypes.MAGIC_WINDOW]: 'viewer',
  [sessionTypes.AR]: 'viewer'
}

class SessionInfo {
  constructor() {
    this.session = null;
    this.frameOfRef = null;
    this.error = null;
  }

  get currentSession() {
    return this.session;
  }

  get currentRefSpace() {
    return this.frameOfRef;
  }

  set currentSession(session) {
    this.session = session;
  }

  set currentRefSpace(frameOfRef) {
    this.frameOfRef = frameOfRef;
  }

  clearSession() {
    this.session = null;
    this.frameOfRef = null;
    this.error = null;
  }
}

var sessionInfos = {}
sessionInfos[sessionTypes.IMMERSIVE] = new SessionInfo();
sessionInfos[sessionTypes.AR] = new SessionInfo();
sessionInfos[sessionTypes.MAGIC_WINDOW] = new SessionInfo();

var immersiveSessionInit = {};
// AR sessions will use the `immersiveSessionInit` and `immersiveArSessionInit`
// to request a session. If they both contain the same keys, the one present in
// `immersiveArSessionInit` will be chosen.
var immersiveArSessionInit = { requiredFeatures: ['hit-test', 'anchors'] };
var nonImmersiveSessionInit = {};

function getSessionType(session) {
  if (session.mode == 'immersive-vr') {
    return sessionTypes.IMMERSIVE;
  } else if (session.mode == 'immersive-ar') {
    return sessionTypes.AR;
  } else {
    return sessionTypes.MAGIC_WINDOW;
  }
}

function sessionTypeWouldTriggerConsent(sessionType) {
  if (sessionType === sessionTypes.MAGIC_WINDOW) {
    return false;
  }
  if (typeof navigator.xr.startedSessionTypes === 'undefined') {
    return true;
  }
  return !(sessionType in navigator.xr.startedSessionTypes);
}

function onRequestSession() {
  console.log('onRequestSession');
  switch (sessionTypeToRequest) {
    case sessionTypes.IMMERSIVE:
      console.info('Requesting immersive VR session');
      navigator.xr.requestSession('immersive-vr', immersiveSessionInit)
      .then( (session) => {
        session.mode = 'immersive-vr';
        console.info('Immersive VR session request succeeded');
        sessionInfos[sessionTypes.IMMERSIVE].currentSession = session;
        onSessionStarted(session);
      }, (error) => {
        sessionInfos[sessionTypes.IMMERSIVE].error = error;
        console.info('Immersive VR session request rejected with: ' + error);
      });
      break;
    case sessionTypes.AR:
      console.info('Requesting Immersive AR session');
      navigator.xr.requestSession('immersive-ar',
        Object.assign({}, immersiveSessionInit, immersiveArSessionInit))
      .then((session) => {
        session.mode = 'immersive-ar';
        console.info('Immersive AR session request succeeded');
        sessionInfos[sessionTypes.AR].currentSession = session;
        onSessionStarted(session);
      }, (error) => {
        sessionInfos[sessionTypes.AR].error = error;
        console.info('Immersive AR session request rejected with: ' + error);
     });
      break;
    case sessionTypes.MAGIC_WINDOW:
      console.info('Requesting Magic Window session');
      requestMagicWindowSession()
      .then(() => {
          console.info('Inline session request succeeded');
      }, error => {
        console.info('Inline session request rejected with: ' + error);
      });
      break;
    default:
      throw 'Given unsupported WebXR session type enum ' + sessionTypeToRequest;
  }
}

function onSessionStarted(session) {
  console.info('onSessionStarted');
  // Record that we've started this session type so that we know not to expect
  // the consent dialog for it in the future.
  let sessionType = getSessionType(session);
  if (typeof navigator.xr.startedSessionTypes === 'undefined') {
    navigator.xr.startedSessionTypes = {};
  }
  navigator.xr.startedSessionTypes[sessionType] = undefined;

  session.addEventListener('end', onSessionEnded);
  // Initialize the WebGL context for use with XR if it hasn't been already
  if (!gl) {
    gl = webglCanvas.getContext('webgl', glAttribs);
    if (!gl) {
      throw 'Failed to get WebGL context';
    }
    gl.clearColor(0.0, 1.0, 0.0, 1.0);
    gl.enable(gl.DEPTH_TEST);
    gl.enable(gl.CULL_FACE);
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
  }

  if (onSessionStartedCallback) {
    onSessionStartedCallback(session);
  }

  if (shouldSetBaseLayer) {
    session.updateRenderState({ baseLayer: new XRWebGLLayer(session, gl) });
  }

  session.requestReferenceSpace(referenceSpaceMap[sessionType])
      .then( (refSpace) => {
        sessionInfos[sessionType].currentRefSpace = refSpace;
        session.requestAnimationFrame(onXRFrame);
      });
}

function onSessionEnded(event) {
  console.info('onSessionEnded');
  sessionInfos[getSessionType(event.session)].clearSession();
}

function onXRFrame(t, frame) {
  let session = frame.session;
  session.requestAnimationFrame(onXRFrame);

  let refSpace = sessionInfos[getSessionType(session)].currentRefSpace;
  let pose = frame.getViewerPose(refSpace);
  if (onPoseCallback) {
    onPoseCallback(pose);
  }

  // Exiting the rAF callback without dirtying the GL context is interpreted
  // as not submitting a frame
  if (!shouldSubmitFrame) {
    return;
  }

  gl.bindFramebuffer(gl.FRAMEBUFFER, session.renderState.baseLayer.framebuffer);

  // If in an immersive session, set canvas to blue.
  // If in an AR session, just draw the camera.
  // Otherwise, red.
  switch (getSessionType(session)) {
    case sessionTypes.IMMERSIVE:
      gl.clearColor(0.0, 0.0, 1.0, 1.0);
      if (onImmersiveXRFrameCallback) {
        onImmersiveXRFrameCallback(session, frame, gl);
      }
      gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
      break;
    case sessionTypes.AR:
      if (onARFrameCallback) {
        onARFrameCallback(session, frame, t);
      }
      break;
    default:
      if (onMagicWindowXRFrameCallback) {
        onMagicWindowXRFrameCallback(session, frame);
      }
      gl.clearColor(1.0, 0.0, 0.0, 1.0);
      gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
  }

  hasPresentedFrame = true;
}

function requestMagicWindowSession() {
  // Set up an inline session (magic window) drawing into the full screen canvas
  // on the page
  return navigator.xr.requestSession('inline', nonImmersiveSessionInit)
  .then(session => {
    session.mode = 'inline';
    sessionInfos[sessionTypes.MAGIC_WINDOW].currentSession = session;
    onSessionStarted(session);
    return session;
  })
  .then(session => {
    initializationSteps['magicWindowStarted'] = true;
    return session;
  });
}

if (navigator.xr) {

  // WebXR for VR tests want an inline session to be automatically
  // created on page load to reduce the amount of boilerplate code necessary.
  // However, doing so during AR tests currently fails due to AR sessions
  // always requiring a user gesture. So, allow a page to set a variable
  // before loading this JavaScript file if they wish to skip the automatic
  // inline session creation.
  if (typeof shouldAutoCreateNonImmersiveSession === 'undefined'
      || shouldAutoCreateNonImmersiveSession === true) {
    requestMagicWindowSession();
  } else {
    initializationSteps['magicWindowStarted'] = true;
  }
} else {
  initializationSteps['magicWindowStarted'] = true;
}

var canvasClicked = false;
webglCanvas.onclick = function(ev) {
  console.log('canvas onclick');
  canvasClicked = true;
  onRequestSession();
}