Newer
Older
reroad-test / 2020-ryusei / aframe-master / src / components / tracked-controls-webxr.js
@ryusei ryusei on 22 Oct 2020 8 KB パノラマ表示
var controllerUtils = require('../utils/tracked-controls');
var registerComponent = require('../core/component').registerComponent;

var EVENTS = {
  AXISMOVE: 'axismove',
  BUTTONCHANGED: 'buttonchanged',
  BUTTONDOWN: 'buttondown',
  BUTTONUP: 'buttonup',
  TOUCHSTART: 'touchstart',
  TOUCHEND: 'touchend'
};

module.exports.Component = registerComponent('tracked-controls-webxr', {
  schema: {
    id: {type: 'string', default: ''},
    hand: {type: 'string', default: ''},
    handTrackingEnabled: {default: false},
    index: {type: 'int', default: -1},
    iterateControllerProfiles: {default: false}
  },

  init: function () {
    this.addSessionEventListeners = this.addSessionEventListeners.bind(this);
    this.updateController = this.updateController.bind(this);
    this.emitButtonUpEvent = this.emitButtonUpEvent.bind(this);
    this.emitButtonDownEvent = this.emitButtonDownEvent.bind(this);

    this.selectEventDetails = {id: 'trigger', state: {pressed: false}};
    this.buttonEventDetails = {};
    this.buttonStates = this.el.components['tracked-controls'].buttonStates = {};
    this.axis = this.el.components['tracked-controls'].axis = [0, 0, 0];
    this.changedAxes = [];
    this.axisMoveEventDetail = {axis: this.axis, changed: this.changedAxes};
  },

  update: function () {
    this.updateController();
  },

  play: function () {
    var sceneEl = this.el.sceneEl;
    this.updateController();
    this.addSessionEventListeners();
    sceneEl.addEventListener('enter-vr', this.addSessionEventListeners);
    sceneEl.addEventListener('controllersupdated', this.updateController);
  },

  pause: function () {
    var sceneEl = this.el.sceneEl;
    this.removeSessionEventListeners();
    sceneEl.removeEventListener('enter-vr', this.addSessionEventListeners);
    sceneEl.removeEventListener('controllersupdated', this.updateController);
  },

  addSessionEventListeners: function () {
    var sceneEl = this.el.sceneEl;
    if (!sceneEl.xrSession) { return; }
    sceneEl.xrSession.addEventListener('selectstart', this.emitButtonDownEvent);
    sceneEl.xrSession.addEventListener('selectend', this.emitButtonUpEvent);
  },

  removeSessionEventListeners: function () {
    var sceneEl = this.el.sceneEl;
    if (!sceneEl.xrSession) { return; }
    sceneEl.xrSession.removeEventListener('selectstart', this.emitButtonDownEvent);
    sceneEl.xrSession.removeEventListener('selectend', this.emitButtonUpEvent);
  },

  isControllerPresent: function (evt) {
    if (!this.controller || this.controller.gamepad) { return false; }
    if (evt.inputSource.handedness !== 'none' &&
        evt.inputSource.handedness !== this.data.hand) {
      return false;
    }
    return true;
  },

  emitButtonDownEvent: function (evt) {
    if (!this.isControllerPresent(evt)) { return; }

    this.selectEventDetails.state.pressed = true;
    this.el.emit('buttondown', this.selectEventDetails);
    this.el.emit('buttonchanged', this.selectEventDetails);
    this.el.emit('triggerdown');
  },

  emitButtonUpEvent: function (evt) {
    if (!this.isControllerPresent(evt)) { return; }

    this.selectEventDetails.state.pressed = false;
    this.el.emit('buttonup', this.selectEventDetails);
    this.el.emit('buttonchanged', this.selectEventDetails);
    this.el.emit('triggerup');
  },

  /**
   * Handle update controller match criteria (such as `id`, `idPrefix`, `hand`, `controller`)
   */
  updateController: function () {
    this.controller = controllerUtils.findMatchingControllerWebXR(
      this.system.controllers,
      this.data.id,
      this.data.hand,
      this.data.index,
      this.data.iterateControllerProfiles,
      this.data.handTrackingEnabled
    );
    // Legacy handle to the controller for old components.
    this.el.components['tracked-controls'].controller = this.controller;
    if (this.data.autoHide) { this.el.object3D.visible = !!this.controller; }
  },

  tick: function () {
    var sceneEl = this.el.sceneEl;
    var controller = this.controller;
    var frame = sceneEl.frame;
    if (!controller || !sceneEl.frame || !this.system.referenceSpace) { return; }
    if (!controller.hand) {
      this.pose = frame.getPose(controller.targetRaySpace, this.system.referenceSpace);
      this.updatePose();
      this.updateButtons();
    }
  },

  updatePose: function () {
    var object3D = this.el.object3D;
    var pose = this.pose;
    if (!pose) { return; }
    object3D.matrix.elements = pose.transform.matrix;
    object3D.matrix.decompose(object3D.position, object3D.rotation, object3D.scale);
  },

  /**
   * Handle button changes including axes, presses, touches, values.
   */
  updateButtons: function () {
    var buttonState;
    var id;
    var controller = this.controller;
    var gamepad;
    if (!controller || !controller.gamepad) { return; }

    gamepad = controller.gamepad;
    // Check every button.
    for (id = 0; id < gamepad.buttons.length; ++id) {
      // Initialize button state.
      if (!this.buttonStates[id]) {
        this.buttonStates[id] = {pressed: false, touched: false, value: 0};
      }
      if (!this.buttonEventDetails[id]) {
        this.buttonEventDetails[id] = {id: id, state: this.buttonStates[id]};
      }

      buttonState = gamepad.buttons[id];
      this.handleButton(id, buttonState);
    }
    // Check axes.
    this.handleAxes();
  },

  /**
   * Handle presses and touches for a single button.
   *
   * @param {number} id - Index of button in Gamepad button array.
   * @param {number} buttonState - Value of button state from 0 to 1.
   * @returns {boolean} Whether button has changed in any way.
   */
  handleButton: function (id, buttonState) {
    var changed;
    changed = this.handlePress(id, buttonState) |
              this.handleTouch(id, buttonState) |
              this.handleValue(id, buttonState);
    if (!changed) { return false; }
    this.el.emit(EVENTS.BUTTONCHANGED, this.buttonEventDetails[id], false);
    return true;
  },

  /**
   * An axis is an array of values from -1 (up, left) to 1 (down, right).
   * Compare each component of the axis to the previous value to determine change.
   *
   * @returns {boolean} Whether axes changed.
   */
  handleAxes: function () {
    var changed = false;
    var controllerAxes = this.controller.gamepad.axes;
    var i;
    var previousAxis = this.axis;
    var changedAxes = this.changedAxes;

    // Check if axis changed.
    this.changedAxes.splice(0, this.changedAxes.length);
    for (i = 0; i < controllerAxes.length; ++i) {
      changedAxes.push(previousAxis[i] !== controllerAxes[i]);
      if (changedAxes[i]) { changed = true; }
    }
    if (!changed) { return false; }

    this.axis.splice(0, this.axis.length);
    for (i = 0; i < controllerAxes.length; i++) {
      this.axis.push(controllerAxes[i]);
    }
    this.el.emit(EVENTS.AXISMOVE, this.axisMoveEventDetail, false);
    return true;
  },

  /**
   * Determine whether a button press has occured and emit events as appropriate.
   *
   * @param {string} id - ID of the button to check.
   * @param {object} buttonState - State of the button to check.
   * @returns {boolean} Whether button press state changed.
   */
  handlePress: function (id, buttonState) {
    var evtName;
    var previousButtonState = this.buttonStates[id];

    // Not changed.
    if (buttonState.pressed === previousButtonState.pressed) { return false; }

    evtName = buttonState.pressed ? EVENTS.BUTTONDOWN : EVENTS.BUTTONUP;
    this.el.emit(evtName, this.buttonEventDetails[id], false);
    previousButtonState.pressed = buttonState.pressed;
    return true;
  },

  /**
   * Determine whether a button touch has occured and emit events as appropriate.
   *
   * @param {string} id - ID of the button to check.
   * @param {object} buttonState - State of the button to check.
   * @returns {boolean} Whether button touch state changed.
   */
  handleTouch: function (id, buttonState) {
    var evtName;
    var previousButtonState = this.buttonStates[id];

    // Not changed.
    if (buttonState.touched === previousButtonState.touched) { return false; }

    evtName = buttonState.touched ? EVENTS.TOUCHSTART : EVENTS.TOUCHEND;
    this.el.emit(evtName, this.buttonEventDetails[id], false);
    previousButtonState.touched = buttonState.touched;
    return true;
  },

  /**
   * Determine whether a button value has changed.
   *
   * @param {string} id - Id of the button to check.
   * @param {object} buttonState - State of the button to check.
   * @returns {boolean} Whether button value changed.
   */
  handleValue: function (id, buttonState) {
    var previousButtonState = this.buttonStates[id];

    // Not changed.
    if (buttonState.value === previousButtonState.value) { return false; }

    previousButtonState.value = buttonState.value;
    return true;
  }
});