Newer
Older
reroad-test / 2020-ryusei / aframe-master / src / systems / camera.js
@ryusei ryusei on 22 Oct 2020 8 KB パノラマ表示
var constants = require('../constants/');
var registerSystem = require('../core/system').registerSystem;

var DEFAULT_CAMERA_ATTR = 'data-aframe-default-camera';

/**
 * Camera system. Manages which camera is active among multiple cameras in scene.
 *
 * @member {object} activeCameraEl - Active camera entity.
 */
module.exports.System = registerSystem('camera', {
  init: function () {
    this.activeCameraEl = null;

    this.render = this.render.bind(this);
    this.unwrapRender = this.unwrapRender.bind(this);
    this.wrapRender = this.wrapRender.bind(this);

    this.initialCameraFound = false;
    this.numUserCameras = 0;
    this.numUserCamerasChecked = 0;
    this.setupInitialCamera();
  },

  /**
   * Setup initial camera, either searching for camera or
   * creating a default camera if user has not added one during the initial scene traversal.
   * We want sceneEl.camera to be ready, set, and initialized before the rest of the scene
   * loads.
   *
   * Default camera offset height is at average eye level (~1.6m).
   */
  setupInitialCamera: function () {
    var cameraEls;
    var i;
    var sceneEl = this.sceneEl;
    var self = this;

    // Camera already defined or the one defined it is an spectator one.
    if (sceneEl.camera && !sceneEl.camera.el.getAttribute('camera').spectator) {
      sceneEl.emit('cameraready', {cameraEl: sceneEl.camera.el});
      return;
    }

    // Search for initial user-defined camera.
    cameraEls = sceneEl.querySelectorAll('a-camera, [camera]');

    // No user cameras, create default one.
    if (!cameraEls.length) {
      this.createDefaultCamera();
      return;
    }

    this.numUserCameras = cameraEls.length;
    for (i = 0; i < cameraEls.length; i++) {
      cameraEls[i].addEventListener('object3dset', function (evt) {
        if (evt.detail.type !== 'camera') { return; }
        self.checkUserCamera(this);
      });

      // Load camera and wait for camera to initialize.
      if (cameraEls[i].isNode) {
        cameraEls[i].load();
      } else {
        cameraEls[i].addEventListener('nodeready', function () {
          this.load();
        });
      }
    }
  },

  /**
   * Check if a user-defined camera entity is appropriate to be initial camera.
   * (active + non-spectator).
   *
   * Keep track of the number of cameras we checked and whether we found one.
   */
  checkUserCamera: function (cameraEl) {
    var cameraData;
    var sceneEl = this.el.sceneEl;
    this.numUserCamerasChecked++;

    // Already found one.
    if (this.initialCameraFound) { return; }

    // Check if camera is appropriate for being the initial camera.
    cameraData = cameraEl.getAttribute('camera');
    if (!cameraData.active || cameraData.spectator) {
      // No user cameras eligible, create default camera.
      if (this.numUserCamerasChecked === this.numUserCameras) {
        this.createDefaultCamera();
      }
      return;
    }

    this.initialCameraFound = true;
    sceneEl.camera = cameraEl.getObject3D('camera');
    sceneEl.emit('cameraready', {cameraEl: cameraEl});
  },

  createDefaultCamera: function () {
    var defaultCameraEl;
    var sceneEl = this.sceneEl;

    // Set up default camera.
    defaultCameraEl = document.createElement('a-entity');
    defaultCameraEl.setAttribute('camera', {active: true});
    defaultCameraEl.setAttribute('position', {
      x: 0,
      y: constants.DEFAULT_CAMERA_HEIGHT,
      z: 0
    });
    defaultCameraEl.setAttribute('wasd-controls', '');
    defaultCameraEl.setAttribute('look-controls', '');
    defaultCameraEl.setAttribute(constants.AFRAME_INJECTED, '');

    defaultCameraEl.addEventListener('object3dset', function (evt) {
      if (evt.detail.type !== 'camera') { return; }
      sceneEl.camera = evt.detail.object;
      sceneEl.emit('cameraready', {cameraEl: defaultCameraEl});
    });

    sceneEl.appendChild(defaultCameraEl);
  },

  /**
   * Set a different active camera.
   * When we choose a (sort of) random scene camera as the replacement, set its `active` to
   * true. The camera component will call `setActiveCamera` and handle passing the torch to
   * the new camera.
   */
  disableActiveCamera: function () {
    var cameraEls;
    var newActiveCameraEl;
    cameraEls = this.sceneEl.querySelectorAll('[camera]');
    newActiveCameraEl = cameraEls[cameraEls.length - 1];
    newActiveCameraEl.setAttribute('camera', 'active', true);
  },

  /**
   * Set active camera to be used by renderer.
   * Removes the default camera (if present).
   * Disables all other cameras in the scene.
   *
   * @param {Element} newCameraEl - Entity with camera component.
   */
  setActiveCamera: function (newCameraEl) {
    var cameraEl;
    var cameraEls;
    var i;
    var newCamera;
    var previousCamera = this.activeCameraEl;
    var sceneEl = this.sceneEl;

    // Same camera.
    newCamera = newCameraEl.getObject3D('camera');
    if (!newCamera || newCameraEl === this.activeCameraEl) { return; }

    // Grab the default camera.
    var defaultCameraWrapper = sceneEl.querySelector('[' + DEFAULT_CAMERA_ATTR + ']');
    var defaultCameraEl = defaultCameraWrapper &&
                          defaultCameraWrapper.querySelector('[camera]');

    // Remove default camera if new camera is not the default camera.
    if (newCameraEl !== defaultCameraEl) { removeDefaultCamera(sceneEl); }

    // Make new camera active.
    this.activeCameraEl = newCameraEl;
    this.activeCameraEl.play();
    sceneEl.camera = newCamera;

    // Disable current camera
    if (previousCamera) {
      previousCamera.setAttribute('camera', 'active', false);
    }

    // Disable other cameras in the scene
    cameraEls = sceneEl.querySelectorAll('[camera]');
    for (i = 0; i < cameraEls.length; i++) {
      cameraEl = cameraEls[i];
      if (!cameraEl.isEntity || newCameraEl === cameraEl) { continue; }
      cameraEl.setAttribute('camera', 'active', false);
      cameraEl.pause();
    }
    sceneEl.emit('camera-set-active', {cameraEl: newCameraEl});
  },

  /**
   * Set spectator camera to render the scene on a 2D display.
   *
   * @param {Element} newCameraEl - Entity with camera component.
   */
  setSpectatorCamera: function (newCameraEl) {
    var newCamera;
    var previousCamera = this.spectatorCameraEl;
    var sceneEl = this.sceneEl;
    var spectatorCameraEl;

    // Same camera.
    newCamera = newCameraEl.getObject3D('camera');
    if (!newCamera || newCameraEl === this.spectatorCameraEl) { return; }

    // Disable current camera
    if (previousCamera) {
      previousCamera.setAttribute('camera', 'spectator', false);
    }

    spectatorCameraEl = this.spectatorCameraEl = newCameraEl;

    sceneEl.addEventListener('enter-vr', this.wrapRender);
    sceneEl.addEventListener('exit-vr', this.unwrapRender);

    spectatorCameraEl.setAttribute('camera', 'active', false);
    spectatorCameraEl.play();

    sceneEl.emit('camera-set-spectator', {cameraEl: newCameraEl});
  },

  /**
   * Disables current spectator camera.
   */
  disableSpectatorCamera: function () {
    this.spectatorCameraEl = undefined;
  },

  /**
   * Wrap the render method of the renderer to render
   * the spectator camera after vrDisplay.submitFrame.
   */
  wrapRender: function () {
    if (!this.spectatorCameraEl || this.originalRender) { return; }
    this.originalRender = this.sceneEl.renderer.render;
    this.sceneEl.renderer.render = this.render;
  },

  unwrapRender: function () {
    if (!this.originalRender) { return; }
    this.sceneEl.renderer.render = this.originalRender;
    this.originalRender = undefined;
  },

  render: function (scene, camera) {
    var isVREnabled;
    var sceneEl = this.sceneEl;
    var spectatorCamera;

    isVREnabled = sceneEl.renderer.xr.enabled;
    this.originalRender.call(sceneEl.renderer, scene, camera);
    if (!this.spectatorCameraEl || sceneEl.isMobile || !isVREnabled) { return; }
    spectatorCamera = this.spectatorCameraEl.components.camera.camera;
    sceneEl.renderer.xr.enabled = false;
    this.originalRender.call(sceneEl.renderer, scene, spectatorCamera);
    sceneEl.renderer.xr.enabled = isVREnabled;
  }
});

/**
 * Remove injected default camera from scene, if present.
 *
 * @param {Element} sceneEl
 */
function removeDefaultCamera (sceneEl) {
  var defaultCamera;
  var camera = sceneEl.camera;
  if (!camera) { return; }

  // Remove default camera if present.
  defaultCamera = sceneEl.querySelector('[' + DEFAULT_CAMERA_ATTR + ']');
  if (!defaultCamera) { return; }
  sceneEl.removeChild(defaultCamera);
}