|
@@ -0,0 +1,472 @@
|
|
|
+/******/ (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;
|
|
|
+
|
|
|
+
|
|
|
+/***/ }
|
|
|
+/******/ ]);
|