/******/ (function(modules) { // webpackBootstrap /******/ // The module cache /******/ var installedModules = {}; /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ // Check if module is in cache /******/ if(installedModules[moduleId]) /******/ return installedModules[moduleId].exports; /******/ // Create a new module (and put it into the cache) /******/ var module = installedModules[moduleId] = { /******/ exports: {}, /******/ id: moduleId, /******/ loaded: false /******/ }; /******/ // Execute the module function /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); /******/ // Flag the module as loaded /******/ module.loaded = true; /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ // expose the modules object (__webpack_modules__) /******/ __webpack_require__.m = modules; /******/ // expose the module cache /******/ __webpack_require__.c = installedModules; /******/ // __webpack_public_path__ /******/ __webpack_require__.p = ""; /******/ // Load entry module and return exports /******/ return __webpack_require__(0); /******/ }) /************************************************************************/ /******/ ([ /* 0 */ /***/ function(module, exports, __webpack_require__) { // Browser distrubution of the A-Frame component. (function (AFRAME) { if (!AFRAME) { console.error('Component attempted to register before AFRAME was available.'); return; } (AFRAME.aframeCore || AFRAME).registerComponent('gamepad-controls', __webpack_require__(1)); }(window.AFRAME)); /***/ }, /* 1 */ /***/ function(module, exports, __webpack_require__) { /** * Gamepad controls for A-Frame. * * For more information about the Gamepad API, see: * https://developer.mozilla.org/en-US/docs/Web/API/Gamepad_API/Using_the_Gamepad_API */ var GamepadButton = __webpack_require__(2), GamepadButtonEvent = __webpack_require__(3); var MAX_DELTA = 200, // ms PI_2 = Math.PI / 2; var JOYSTICK_EPS = 0.2; module.exports = { /******************************************************************* * Statics */ GamepadButton: GamepadButton, /******************************************************************* * Schema */ schema: { // Controller 0-3 controller: { default: 0, oneOf: [0, 1, 2, 3] }, // Enable/disable features enabled: { default: true }, movementEnabled: { default: true }, lookEnabled: { default: true }, flyEnabled: { default: false }, invertAxisY: { default: false }, // Constants easing: { default: 20 }, acceleration: { default: 65 }, sensitivity: { default: 0.04 }, // Control axes pitchAxis: { default: 'x', oneOf: [ 'x', 'y', 'z' ] }, yawAxis: { default: 'y', oneOf: [ 'x', 'y', 'z' ] }, rollAxis: { default: 'z', oneOf: [ 'x', 'y', 'z' ] }, // Debugging debug: { default: false } }, /******************************************************************* * Core */ /** * Called once when component is attached. Generally for initial setup. */ init: function () { // Movement this.velocity = new THREE.Vector3(0, 0, 0); this.direction = new THREE.Vector3(0, 0, 0); // Rotation this.pitch = new THREE.Object3D(); this.yaw = new THREE.Object3D(); this.yaw.position.y = 10; this.yaw.add(this.pitch); // Button state this.buttons = {}; if (!this.getGamepad()) { console.warn( 'Gamepad #%d not found. Connect controller and press any button to continue.', this.data.controller ); } }, /** * Called on each iteration of main render loop. */ tick: function (t, dt) { this.updateRotation(dt); this.updatePosition(dt); this.updateButtonState(); }, /******************************************************************* * Movement */ updatePosition: function (dt) { var data = this.data; var acceleration = data.acceleration; var easing = data.easing; var velocity = this.velocity; var rollAxis = data.rollAxis; var pitchAxis = data.pitchAxis; var el = this.el; var gamepad = this.getGamepad(); // If data has changed or FPS is too low // we reset the velocity if (dt > MAX_DELTA) { velocity[rollAxis] = 0; velocity[pitchAxis] = 0; return; } velocity[rollAxis] -= velocity[rollAxis] * easing * dt / 1000; velocity[pitchAxis] -= velocity[pitchAxis] * easing * dt / 1000; var position = el.getAttribute('position'); if (data.enabled && data.movementEnabled && gamepad) { var dpad = this.getDpad(), inputX = dpad.x || this.getJoystick(0).x, inputY = dpad.y || this.getJoystick(0).y; if (Math.abs(inputX) > JOYSTICK_EPS) { velocity[pitchAxis] += inputX * acceleration * dt / 1000; } if (Math.abs(inputY) > JOYSTICK_EPS) { velocity[rollAxis] += inputY * acceleration * dt / 1000; } } var movementVector = this.getMovementVector(dt); el.object3D.translateX(movementVector.x); el.object3D.translateY(movementVector.y); el.object3D.translateZ(movementVector.z); el.setAttribute('position', { x: position.x + movementVector.x, y: position.y + movementVector.y, z: position.z + movementVector.z }); }, getMovementVector: function (dt) { if (this._getMovementVector) { return this._getMovementVector(dt); } var euler = new THREE.Euler(0, 0, 0, 'YXZ'), rotation = new THREE.Vector3(); this._getMovementVector = function (dt) { rotation.copy(this.el.getAttribute('rotation')); this.direction.copy(this.velocity); this.direction.multiplyScalar(dt / 1000); if (!rotation) { return this.direction; } if (!this.data.flyEnabled) { rotation.x = 0; } euler.set( THREE.Math.degToRad(rotation.x), THREE.Math.degToRad(rotation.y), 0 ); this.direction.applyEuler(euler); return this.direction; }; return this._getMovementVector(dt); }, /******************************************************************* * Rotation */ updateRotation: function () { if (this._updateRotation) { return this._updateRotation(); } var initialRotation = new THREE.Vector3(), prevInitialRotation = new THREE.Vector3(), prevFinalRotation = new THREE.Vector3(); var tCurrent, tLastLocalActivity = 0, tLastExternalActivity = 0; var ROTATION_EPS = 0.0001, DEBOUNCE = 500; this._updateRotation = function () { if (!this.data.lookEnabled || !this.getGamepad()) { return; } tCurrent = Date.now(); initialRotation.copy(this.el.getAttribute('rotation') || initialRotation); // If initial rotation for this frame is different from last frame, and // doesn't match last gamepad state, assume an external component is // active on this element. if (initialRotation.distanceToSquared(prevInitialRotation) > ROTATION_EPS && initialRotation.distanceToSquared(prevFinalRotation) > ROTATION_EPS) { prevInitialRotation.copy(initialRotation); tLastExternalActivity = tCurrent; return; } prevInitialRotation.copy(initialRotation); // If external controls have been active in last 500ms, wait. if (tCurrent - tLastExternalActivity < DEBOUNCE) { return; } var lookVector = this.getJoystick(1); if (Math.abs(lookVector.x) <= JOYSTICK_EPS) lookVector.x = 0; if (Math.abs(lookVector.y) <= JOYSTICK_EPS) lookVector.y = 0; if (this.data.invertAxisY) lookVector.y = -lookVector.y; // If external controls have been active more recently than gamepad, // and gamepad hasn't moved, don't overwrite the existing rotation. if (tLastExternalActivity > tLastLocalActivity && !lookVector.lengthSq()) { return; } lookVector.multiplyScalar(this.data.sensitivity); this.yaw.rotation.y -= lookVector.x; this.pitch.rotation.x -= lookVector.y; this.pitch.rotation.x = Math.max(-PI_2, Math.min(PI_2, this.pitch.rotation.x)); this.el.setAttribute('rotation', { x: THREE.Math.radToDeg(this.pitch.rotation.x), y: THREE.Math.radToDeg(this.yaw.rotation.y), z: 0 }); prevFinalRotation.copy(this.el.getAttribute('rotation')); tLastLocalActivity = tCurrent; }; return this._updateRotation(); }, /******************************************************************* * Button events */ updateButtonState: function () { var gamepad = this.getGamepad(); if (this.data.enabled && gamepad) { // Fire DOM events for button state changes. for (var i = 0; i < gamepad.buttons.length; i++) { if (gamepad.buttons[i].pressed && !this.buttons[i]) { this.emit(new GamepadButtonEvent('gamepadbuttondown', i, gamepad.buttons[i])); } else if (!gamepad.buttons[i].pressed && this.buttons[i]) { this.emit(new GamepadButtonEvent('gamepadbuttonup', i, gamepad.buttons[i])); } this.buttons[i] = gamepad.buttons[i].pressed; } } else if (Object.keys(this.buttons)) { // Reset state if controls are disabled or controller is lost. this.buttons = {}; } }, emit: function (event) { // Emit original event. this.el.emit(event.type, event); // Emit convenience event, identifying button index. this.el.emit( event.type + ':' + event.index, new GamepadButtonEvent(event.type, event.index, event) ); }, /******************************************************************* * Gamepad state */ /** * Returns the Gamepad instance attached to the component. If connected, * a proxy-controls component may provide access to Gamepad input from a * remote device. * * @return {Gamepad} */ getGamepad: function () { var localGamepad = navigator.getGamepads && navigator.getGamepads()[this.data.controller], proxyControls = this.el.sceneEl.components['proxy-controls'], proxyGamepad = proxyControls && proxyControls.isConnected() && proxyControls.getGamepad(this.data.controller); return proxyGamepad || localGamepad; }, /** * Returns the state of the given button. * @param {number} index The button (0-N) for which to find state. * @return {GamepadButton} */ getButton: function (index) { return this.getGamepad().buttons[index]; }, /** * Returns state of the given axis. Axes are labelled 0-N, where 0-1 will * represent X/Y on the first joystick, and 2-3 X/Y on the second. * @param {number} index The axis (0-N) for which to find state. * @return {number} On the interval [-1,1]. */ getAxis: function (index) { return this.getGamepad().axes[index]; }, /** * Returns the state of the given joystick (0 or 1) as a THREE.Vector2. * @param {number} id The joystick (0, 1) for which to find state. * @return {THREE.Vector2} */ getJoystick: function (index) { var gamepad = this.getGamepad(); switch (index) { case 0: return new THREE.Vector2(gamepad.axes[0], gamepad.axes[1]); case 1: return new THREE.Vector2(gamepad.axes[2], gamepad.axes[3]); default: throw new Error('Unexpected joystick index "%d".', index); } }, /** * Returns the state of the dpad as a THREE.Vector2. * @return {THREE.Vector2} */ getDpad: function () { var gamepad = this.getGamepad(); if (!gamepad.buttons[GamepadButton.DPAD_RIGHT]) { return new THREE.Vector2(); } return new THREE.Vector2( (gamepad.buttons[GamepadButton.DPAD_RIGHT].pressed ? 1 : 0) + (gamepad.buttons[GamepadButton.DPAD_LEFT].pressed ? -1 : 0), (gamepad.buttons[GamepadButton.DPAD_UP].pressed ? -1 : 0) + (gamepad.buttons[GamepadButton.DPAD_DOWN].pressed ? 1 : 0) ); }, /** * Returns true if the gamepad is currently connected to the system. * @return {boolean} */ isConnected: function () { var gamepad = this.getGamepad(); return !!(gamepad && gamepad.connected); }, /** * Returns a string containing some information about the controller. Result * may vary across browsers, for a given controller. * @return {string} */ getID: function () { return this.getGamepad().id; } }; /***/ }, /* 2 */ /***/ function(module, exports) { module.exports = Object.assign(function GamepadButton () {}, { FACE_1: 0, FACE_2: 1, FACE_3: 2, FACE_4: 3, L_SHOULDER_1: 4, R_SHOULDER_1: 5, L_SHOULDER_2: 6, R_SHOULDER_2: 7, SELECT: 8, START: 9, DPAD_UP: 12, DPAD_DOWN: 13, DPAD_LEFT: 14, DPAD_RIGHT: 15, VENDOR: 16, }); /***/ }, /* 3 */ /***/ function(module, exports) { function GamepadButtonEvent (type, index, details) { this.type = type; this.index = index; this.pressed = details.pressed; this.value = details.value; } module.exports = GamepadButtonEvent; /***/ } /******/ ]);