Bladeren bron

add virtual game controls

Nikolay Suslov 6 jaren geleden
bovenliggende
commit
2e4c76d27b

+ 3 - 0
public/index.html

@@ -57,6 +57,9 @@ Copyright (c) 2014-2018 Nikolai Suslov and the Krestianstvo.org project contribu
     <!-- <link rel="stylesheet" href="https://unpkg.com/material-components-web@latest/dist/material-components-web.min.css">
   <script src="https://unpkg.com/material-components-web@latest/dist/material-components-web.min.js"></script> -->
 
+  <script type="text/javascript" src="/vwf/model/aframe/addon/virtualgc/nipplejs.js"></script>
+  <link rel="stylesheet" type="text/css" href="/vwf/model/aframe/addon/virtualgc/virtual-gamepad-controls.css" />
+
     <script type="text/javascript" src="/lib/lively.vm_standalone.js"></script>
     <script type="text/javascript" src="/lib/async/async.min.js"></script>
     <script type="text/javascript" src="/lib/require.js"></script>

+ 1 - 1
public/vwf.js

@@ -328,7 +328,7 @@ Copyright (c) 2014-2018 Nikolai Suslov and the Krestianstvo.org project contribu
                         "vwf/model/aframe/addon/THREE.MeshLine"
                     ]
                     }
-                    
+
                 }
             };
 

+ 162 - 1
public/vwf/model/aframe/addon/aframe-components.js

@@ -628,4 +628,165 @@ AFRAME.registerComponent('viewoffset', {
     tick: function (t) {
 
     }
-})
+})
+
+AFRAME.registerComponent("virtual-gamepad-controls", {
+    schema: {},
+  
+    init() {
+      this.onEnterVr = this.onEnterVr.bind(this);
+      this.onExitVr = this.onExitVr.bind(this);
+      this.onFirstInteraction = this.onFirstInteraction.bind(this);
+      this.onMoveJoystickChanged = this.onMoveJoystickChanged.bind(this);
+      this.onMoveJoystickEnd = this.onMoveJoystickEnd.bind(this);
+      // this.onLookJoystickChanged = this.onLookJoystickChanged.bind(this);
+      // this.onLookJoystickEnd = this.onLookJoystickEnd.bind(this);
+  
+      this.mockJoystickContainer = document.createElement("div");
+      this.mockJoystickContainer.classList.add('mockJoystickContainer');
+      const leftMock = document.createElement("div");
+      leftMock.classList.add('mockJoystick');
+      const leftMockSmall = document.createElement("div");
+      leftMockSmall.classList.add('mockJoystick', 'inner');
+      leftMock.appendChild(leftMockSmall);
+      this.mockJoystickContainer.appendChild(leftMock);
+      // const rightMock = document.createElement("div");
+      // rightMock.classList.add('mockJoystick');
+      // const rightMockSmall = document.createElement("div");
+      // rightMockSmall.classList.add('mockJoystick', 'inner');
+      // rightMock.appendChild(rightMockSmall);
+      // this.mockJoystickContainer.appendChild(rightMock);
+      document.body.appendChild(this.mockJoystickContainer);
+  
+      // Setup gamepad elements
+      const leftTouchZone = document.createElement("div");
+      leftTouchZone.classList.add('touchZone', 'left');
+      document.body.appendChild(leftTouchZone);
+  
+      this.leftTouchZone = leftTouchZone;
+  
+      this.leftStick = nipplejs.create({
+        zone: this.leftTouchZone,
+        color: "white",
+        fadeTime: 0
+      });
+  
+      this.leftStick.on("start", this.onFirstInteraction);
+      this.leftStick.on("move", this.onMoveJoystickChanged);
+      this.leftStick.on("end", this.onMoveJoystickEnd);
+  
+      // const rightTouchZone = document.createElement("div");
+      // rightTouchZone.classList.add('touchZone', 'right');
+      // document.body.appendChild(rightTouchZone);
+  
+      // this.rightTouchZone = rightTouchZone;
+  
+      // this.rightStick = nipplejs.create({
+      //   zone: this.rightTouchZone,
+      //   color: "white",
+      //   fadeTime: 0
+      // });
+  
+      // this.rightStick.on("start", this.onFirstInteraction);
+      // this.rightStick.on("move", this.onLookJoystickChanged);
+      // this.rightStick.on("end", this.onLookJoystickEnd);
+  
+      this.inVr = false;
+      this.moving = false;
+      this.rotating = false;
+  
+      this.moveEvent = {
+        axis: [0, 0]
+      };
+  
+      // this.rotateYEvent = {
+      //   value: 0
+      // };
+      // this.rotateXEvent = {
+      //   value: 0
+      // };
+  
+      this.el.sceneEl.addEventListener("enter-vr", this.onEnterVr);
+      this.el.sceneEl.addEventListener("exit-vr", this.onExitVr);
+    },
+  
+    onFirstInteraction() {
+      this.leftStick.off("start", this.onFirstInteraction);
+      //this.rightStick.off("start", this.onFirstInteraction);
+      document.body.removeChild(this.mockJoystickContainer);
+    },
+  
+    onMoveJoystickChanged(event, joystick) {
+      const angle = joystick.angle.radian;
+      const force = joystick.force < 1 ? joystick.force : 1;
+      const moveStrength = 1.85;
+      const x = Math.cos(angle) * force * moveStrength;
+      const z = Math.sin(angle) * force * moveStrength;
+      this.moving = true;
+      this.moveEvent.axis[0] = x;
+      this.moveEvent.axis[1] = z;
+    },
+  
+    onMoveJoystickEnd() {
+      this.moving = false;
+      this.moveEvent.axis[0] = 0;
+      this.moveEvent.axis[1] = 0;
+      this.el.emit("move", this.moveEvent);
+    },
+  
+    onLookJoystickChanged(event, joystick) {
+      // Set pitch and yaw angles on right stick move
+      const angle = joystick.angle.radian;
+      const force = joystick.force < 1 ? joystick.force : 1;
+      const turnStrength = 0.5;
+      this.rotating = true;
+      this.rotateYEvent.value = Math.cos(angle) * force * turnStrength;
+      this.rotateXEvent.value = Math.sin(angle) * force * turnStrength;
+    },
+  
+    onLookJoystickEnd() {
+      this.rotating = false;
+      this.rotateYEvent.value = 0;
+      this.rotateXEvent.value = 0;
+      this.el.emit("rotateY", this.rotateYEvent);
+      this.el.emit("rotateX", this.rotateXEvent);
+    },
+  
+    tick() {
+      if (!this.inVr) {
+        if (this.moving) {
+          this.el.emit("move", this.moveEvent);
+        }
+  
+        // if (this.rotating) {
+        //   this.el.emit("rotateY", this.rotateYEvent);
+        //   this.el.emit("rotateX", this.rotateXEvent);
+        // }
+      }
+    },
+  
+    onEnterVr() {
+      // Hide the joystick controls
+      this.inVr = true;
+      this.leftTouchZone.style.display = "none";
+      // this.rightTouchZone.style.display = "none";
+    },
+  
+    onExitVr() {
+      // Show the joystick controls
+      this.inVr = false;
+      this.leftTouchZone.style.display = "block";
+      // this.rightTouchZone.style.display = "block";
+    },
+  
+    remove() {
+      this.el.sceneEl.removeEventListener("entervr", this.onEnterVr);
+      this.el.sceneEl.removeEventListener("exitvr", this.onExitVr);
+      if (document.getElementsByClassName('mockJoystickContainer').length > 0){
+        document.body.removeChild(this.mockJoystickContainer);
+      }
+      document.body.removeChild(this.leftTouchZone);
+      // document.body.removeChild(this.rightTouchZone);
+    }
+  });
+  

+ 0 - 1724
public/vwf/model/aframe/addon/aframe-extras.controls.js

@@ -1,1724 +0,0 @@
-(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
-'use strict';
-
-require('./src/controls');
-
-},{"./src/controls":7}],2:[function(require,module,exports){
-"use strict";
-
-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(require,module,exports){
-"use strict";
-
-function GamepadButtonEvent(type, index, details) {
-  this.type = type;
-  this.index = index;
-  this.pressed = details.pressed;
-  this.value = details.value;
-}
-
-module.exports = GamepadButtonEvent;
-
-},{}],4:[function(require,module,exports){
-'use strict';
-
-/**
- * Polyfill for the additional KeyboardEvent properties defined in the D3E and
- * D4E draft specifications, by @inexorabletash.
- *
- * See: https://github.com/inexorabletash/polyfill
- */
-
-(function (global) {
-  var nativeKeyboardEvent = 'KeyboardEvent' in global;
-  if (!nativeKeyboardEvent) global.KeyboardEvent = function KeyboardEvent() {
-    throw TypeError('Illegal constructor');
-  };
-
-  if (!('DOM_KEY_LOCATION_STANDARD' in global.KeyboardEvent)) global.KeyboardEvent.DOM_KEY_LOCATION_STANDARD = 0x00; // Default or unknown location
-  if (!('DOM_KEY_LOCATION_LEFT' in global.KeyboardEvent)) global.KeyboardEvent.DOM_KEY_LOCATION_LEFT = 0x01; // e.g. Left Alt key
-  if (!('DOM_KEY_LOCATION_RIGHT' in global.KeyboardEvent)) global.KeyboardEvent.DOM_KEY_LOCATION_RIGHT = 0x02; // e.g. Right Alt key
-  if (!('DOM_KEY_LOCATION_NUMPAD' in global.KeyboardEvent)) global.KeyboardEvent.DOM_KEY_LOCATION_NUMPAD = 0x03; // e.g. Numpad 0 or +
-
-  var STANDARD = window.KeyboardEvent.DOM_KEY_LOCATION_STANDARD,
-      LEFT = window.KeyboardEvent.DOM_KEY_LOCATION_LEFT,
-      RIGHT = window.KeyboardEvent.DOM_KEY_LOCATION_RIGHT,
-      NUMPAD = window.KeyboardEvent.DOM_KEY_LOCATION_NUMPAD;
-
-  //--------------------------------------------------------------------
-  //
-  // Utilities
-  //
-  //--------------------------------------------------------------------
-
-  function contains(s, ss) {
-    return String(s).indexOf(ss) !== -1;
-  }
-
-  var os = function () {
-    if (contains(navigator.platform, 'Win')) {
-      return 'win';
-    }
-    if (contains(navigator.platform, 'Mac')) {
-      return 'mac';
-    }
-    if (contains(navigator.platform, 'CrOS')) {
-      return 'cros';
-    }
-    if (contains(navigator.platform, 'Linux')) {
-      return 'linux';
-    }
-    if (contains(navigator.userAgent, 'iPad') || contains(navigator.platform, 'iPod') || contains(navigator.platform, 'iPhone')) {
-      return 'ios';
-    }
-    return '';
-  }();
-
-  var browser = function () {
-    if (contains(navigator.userAgent, 'Chrome/')) {
-      return 'chrome';
-    }
-    if (contains(navigator.vendor, 'Apple')) {
-      return 'safari';
-    }
-    if (contains(navigator.userAgent, 'MSIE')) {
-      return 'ie';
-    }
-    if (contains(navigator.userAgent, 'Gecko/')) {
-      return 'moz';
-    }
-    if (contains(navigator.userAgent, 'Opera/')) {
-      return 'opera';
-    }
-    return '';
-  }();
-
-  var browser_os = browser + '-' + os;
-
-  function mergeIf(baseTable, select, table) {
-    if (browser_os === select || browser === select || os === select) {
-      Object.keys(table).forEach(function (keyCode) {
-        baseTable[keyCode] = table[keyCode];
-      });
-    }
-  }
-
-  function remap(o, key) {
-    var r = {};
-    Object.keys(o).forEach(function (k) {
-      var item = o[k];
-      if (key in item) {
-        r[item[key]] = item;
-      }
-    });
-    return r;
-  }
-
-  function invert(o) {
-    var r = {};
-    Object.keys(o).forEach(function (k) {
-      r[o[k]] = k;
-    });
-    return r;
-  }
-
-  //--------------------------------------------------------------------
-  //
-  // Generic Mappings
-  //
-  //--------------------------------------------------------------------
-
-  // "keyInfo" is a dictionary:
-  //   code: string - name from DOM Level 3 KeyboardEvent code Values
-  //     https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3Events-code.html
-  //   location (optional): number - one of the DOM_KEY_LOCATION values
-  //   keyCap (optional): string - keyboard label in en-US locale
-  // USB code Usage ID from page 0x07 unless otherwise noted (Informative)
-
-  // Map of keyCode to keyInfo
-  var keyCodeToInfoTable = {
-    // 0x01 - VK_LBUTTON
-    // 0x02 - VK_RBUTTON
-    0x03: { code: 'Cancel' }, // [USB: 0x9b] char \x0018 ??? (Not in D3E)
-    // 0x04 - VK_MBUTTON
-    // 0x05 - VK_XBUTTON1
-    // 0x06 - VK_XBUTTON2
-    0x06: { code: 'Help' }, // [USB: 0x75] ???
-    // 0x07 - undefined
-    0x08: { code: 'Backspace' }, // [USB: 0x2a] Labelled Delete on Macintosh keyboards.
-    0x09: { code: 'Tab' }, // [USB: 0x2b]
-    // 0x0A-0x0B - reserved
-    0X0C: { code: 'Clear' }, // [USB: 0x9c] NumPad Center (Not in D3E)
-    0X0D: { code: 'Enter' }, // [USB: 0x28]
-    // 0x0E-0x0F - undefined
-
-    0x10: { code: 'Shift' },
-    0x11: { code: 'Control' },
-    0x12: { code: 'Alt' },
-    0x13: { code: 'Pause' }, // [USB: 0x48]
-    0x14: { code: 'CapsLock' }, // [USB: 0x39]
-    0x15: { code: 'KanaMode' }, // [USB: 0x88] - "HangulMode" for Korean layout
-    0x16: { code: 'HangulMode' }, // [USB: 0x90] 0x15 as well in MSDN VK table ???
-    0x17: { code: 'JunjaMode' }, // (Not in D3E)
-    0x18: { code: 'FinalMode' }, // (Not in D3E)
-    0x19: { code: 'KanjiMode' }, // [USB: 0x91] - "HanjaMode" for Korean layout
-    // 0x1A - undefined
-    0x1B: { code: 'Escape' }, // [USB: 0x29]
-    0x1C: { code: 'Convert' }, // [USB: 0x8a]
-    0x1D: { code: 'NonConvert' }, // [USB: 0x8b]
-    0x1E: { code: 'Accept' }, // (Not in D3E)
-    0x1F: { code: 'ModeChange' }, // (Not in D3E)
-
-    0x20: { code: 'Space' }, // [USB: 0x2c]
-    0x21: { code: 'PageUp' }, // [USB: 0x4b]
-    0x22: { code: 'PageDown' }, // [USB: 0x4e]
-    0x23: { code: 'End' }, // [USB: 0x4d]
-    0x24: { code: 'Home' }, // [USB: 0x4a]
-    0x25: { code: 'ArrowLeft' }, // [USB: 0x50]
-    0x26: { code: 'ArrowUp' }, // [USB: 0x52]
-    0x27: { code: 'ArrowRight' }, // [USB: 0x4f]
-    0x28: { code: 'ArrowDown' }, // [USB: 0x51]
-    0x29: { code: 'Select' }, // (Not in D3E)
-    0x2A: { code: 'Print' }, // (Not in D3E)
-    0x2B: { code: 'Execute' }, // [USB: 0x74] (Not in D3E)
-    0x2C: { code: 'PrintScreen' }, // [USB: 0x46]
-    0x2D: { code: 'Insert' }, // [USB: 0x49]
-    0x2E: { code: 'Delete' }, // [USB: 0x4c]
-    0x2F: { code: 'Help' }, // [USB: 0x75] ???
-
-    0x30: { code: 'Digit0', keyCap: '0' }, // [USB: 0x27] 0)
-    0x31: { code: 'Digit1', keyCap: '1' }, // [USB: 0x1e] 1!
-    0x32: { code: 'Digit2', keyCap: '2' }, // [USB: 0x1f] 2@
-    0x33: { code: 'Digit3', keyCap: '3' }, // [USB: 0x20] 3#
-    0x34: { code: 'Digit4', keyCap: '4' }, // [USB: 0x21] 4$
-    0x35: { code: 'Digit5', keyCap: '5' }, // [USB: 0x22] 5%
-    0x36: { code: 'Digit6', keyCap: '6' }, // [USB: 0x23] 6^
-    0x37: { code: 'Digit7', keyCap: '7' }, // [USB: 0x24] 7&
-    0x38: { code: 'Digit8', keyCap: '8' }, // [USB: 0x25] 8*
-    0x39: { code: 'Digit9', keyCap: '9' }, // [USB: 0x26] 9(
-    // 0x3A-0x40 - undefined
-
-    0x41: { code: 'KeyA', keyCap: 'a' }, // [USB: 0x04]
-    0x42: { code: 'KeyB', keyCap: 'b' }, // [USB: 0x05]
-    0x43: { code: 'KeyC', keyCap: 'c' }, // [USB: 0x06]
-    0x44: { code: 'KeyD', keyCap: 'd' }, // [USB: 0x07]
-    0x45: { code: 'KeyE', keyCap: 'e' }, // [USB: 0x08]
-    0x46: { code: 'KeyF', keyCap: 'f' }, // [USB: 0x09]
-    0x47: { code: 'KeyG', keyCap: 'g' }, // [USB: 0x0a]
-    0x48: { code: 'KeyH', keyCap: 'h' }, // [USB: 0x0b]
-    0x49: { code: 'KeyI', keyCap: 'i' }, // [USB: 0x0c]
-    0x4A: { code: 'KeyJ', keyCap: 'j' }, // [USB: 0x0d]
-    0x4B: { code: 'KeyK', keyCap: 'k' }, // [USB: 0x0e]
-    0x4C: { code: 'KeyL', keyCap: 'l' }, // [USB: 0x0f]
-    0x4D: { code: 'KeyM', keyCap: 'm' }, // [USB: 0x10]
-    0x4E: { code: 'KeyN', keyCap: 'n' }, // [USB: 0x11]
-    0x4F: { code: 'KeyO', keyCap: 'o' }, // [USB: 0x12]
-
-    0x50: { code: 'KeyP', keyCap: 'p' }, // [USB: 0x13]
-    0x51: { code: 'KeyQ', keyCap: 'q' }, // [USB: 0x14]
-    0x52: { code: 'KeyR', keyCap: 'r' }, // [USB: 0x15]
-    0x53: { code: 'KeyS', keyCap: 's' }, // [USB: 0x16]
-    0x54: { code: 'KeyT', keyCap: 't' }, // [USB: 0x17]
-    0x55: { code: 'KeyU', keyCap: 'u' }, // [USB: 0x18]
-    0x56: { code: 'KeyV', keyCap: 'v' }, // [USB: 0x19]
-    0x57: { code: 'KeyW', keyCap: 'w' }, // [USB: 0x1a]
-    0x58: { code: 'KeyX', keyCap: 'x' }, // [USB: 0x1b]
-    0x59: { code: 'KeyY', keyCap: 'y' }, // [USB: 0x1c]
-    0x5A: { code: 'KeyZ', keyCap: 'z' }, // [USB: 0x1d]
-    0x5B: { code: 'OSLeft', location: LEFT }, // [USB: 0xe3]
-    0x5C: { code: 'OSRight', location: RIGHT }, // [USB: 0xe7]
-    0x5D: { code: 'ContextMenu' }, // [USB: 0x65] Context Menu
-    // 0x5E - reserved
-    0x5F: { code: 'Standby' }, // [USB: 0x82] Sleep
-
-    0x60: { code: 'Numpad0', keyCap: '0', location: NUMPAD }, // [USB: 0x62]
-    0x61: { code: 'Numpad1', keyCap: '1', location: NUMPAD }, // [USB: 0x59]
-    0x62: { code: 'Numpad2', keyCap: '2', location: NUMPAD }, // [USB: 0x5a]
-    0x63: { code: 'Numpad3', keyCap: '3', location: NUMPAD }, // [USB: 0x5b]
-    0x64: { code: 'Numpad4', keyCap: '4', location: NUMPAD }, // [USB: 0x5c]
-    0x65: { code: 'Numpad5', keyCap: '5', location: NUMPAD }, // [USB: 0x5d]
-    0x66: { code: 'Numpad6', keyCap: '6', location: NUMPAD }, // [USB: 0x5e]
-    0x67: { code: 'Numpad7', keyCap: '7', location: NUMPAD }, // [USB: 0x5f]
-    0x68: { code: 'Numpad8', keyCap: '8', location: NUMPAD }, // [USB: 0x60]
-    0x69: { code: 'Numpad9', keyCap: '9', location: NUMPAD }, // [USB: 0x61]
-    0x6A: { code: 'NumpadMultiply', keyCap: '*', location: NUMPAD }, // [USB: 0x55]
-    0x6B: { code: 'NumpadAdd', keyCap: '+', location: NUMPAD }, // [USB: 0x57]
-    0x6C: { code: 'NumpadComma', keyCap: ',', location: NUMPAD }, // [USB: 0x85]
-    0x6D: { code: 'NumpadSubtract', keyCap: '-', location: NUMPAD }, // [USB: 0x56]
-    0x6E: { code: 'NumpadDecimal', keyCap: '.', location: NUMPAD }, // [USB: 0x63]
-    0x6F: { code: 'NumpadDivide', keyCap: '/', location: NUMPAD }, // [USB: 0x54]
-
-    0x70: { code: 'F1' }, // [USB: 0x3a]
-    0x71: { code: 'F2' }, // [USB: 0x3b]
-    0x72: { code: 'F3' }, // [USB: 0x3c]
-    0x73: { code: 'F4' }, // [USB: 0x3d]
-    0x74: { code: 'F5' }, // [USB: 0x3e]
-    0x75: { code: 'F6' }, // [USB: 0x3f]
-    0x76: { code: 'F7' }, // [USB: 0x40]
-    0x77: { code: 'F8' }, // [USB: 0x41]
-    0x78: { code: 'F9' }, // [USB: 0x42]
-    0x79: { code: 'F10' }, // [USB: 0x43]
-    0x7A: { code: 'F11' }, // [USB: 0x44]
-    0x7B: { code: 'F12' }, // [USB: 0x45]
-    0x7C: { code: 'F13' }, // [USB: 0x68]
-    0x7D: { code: 'F14' }, // [USB: 0x69]
-    0x7E: { code: 'F15' }, // [USB: 0x6a]
-    0x7F: { code: 'F16' }, // [USB: 0x6b]
-
-    0x80: { code: 'F17' }, // [USB: 0x6c]
-    0x81: { code: 'F18' }, // [USB: 0x6d]
-    0x82: { code: 'F19' }, // [USB: 0x6e]
-    0x83: { code: 'F20' }, // [USB: 0x6f]
-    0x84: { code: 'F21' }, // [USB: 0x70]
-    0x85: { code: 'F22' }, // [USB: 0x71]
-    0x86: { code: 'F23' }, // [USB: 0x72]
-    0x87: { code: 'F24' }, // [USB: 0x73]
-    // 0x88-0x8F - unassigned
-
-    0x90: { code: 'NumLock', location: NUMPAD }, // [USB: 0x53]
-    0x91: { code: 'ScrollLock' }, // [USB: 0x47]
-    // 0x92-0x96 - OEM specific
-    // 0x97-0x9F - unassigned
-
-    // NOTE: 0xA0-0xA5 usually mapped to 0x10-0x12 in browsers
-    0xA0: { code: 'ShiftLeft', location: LEFT }, // [USB: 0xe1]
-    0xA1: { code: 'ShiftRight', location: RIGHT }, // [USB: 0xe5]
-    0xA2: { code: 'ControlLeft', location: LEFT }, // [USB: 0xe0]
-    0xA3: { code: 'ControlRight', location: RIGHT }, // [USB: 0xe4]
-    0xA4: { code: 'AltLeft', location: LEFT }, // [USB: 0xe2]
-    0xA5: { code: 'AltRight', location: RIGHT }, // [USB: 0xe6]
-
-    0xA6: { code: 'BrowserBack' }, // [USB: 0x0c/0x0224]
-    0xA7: { code: 'BrowserForward' }, // [USB: 0x0c/0x0225]
-    0xA8: { code: 'BrowserRefresh' }, // [USB: 0x0c/0x0227]
-    0xA9: { code: 'BrowserStop' }, // [USB: 0x0c/0x0226]
-    0xAA: { code: 'BrowserSearch' }, // [USB: 0x0c/0x0221]
-    0xAB: { code: 'BrowserFavorites' }, // [USB: 0x0c/0x0228]
-    0xAC: { code: 'BrowserHome' }, // [USB: 0x0c/0x0222]
-    0xAD: { code: 'VolumeMute' }, // [USB: 0x7f]
-    0xAE: { code: 'VolumeDown' }, // [USB: 0x81]
-    0xAF: { code: 'VolumeUp' }, // [USB: 0x80]
-
-    0xB0: { code: 'MediaTrackNext' }, // [USB: 0x0c/0x00b5]
-    0xB1: { code: 'MediaTrackPrevious' }, // [USB: 0x0c/0x00b6]
-    0xB2: { code: 'MediaStop' }, // [USB: 0x0c/0x00b7]
-    0xB3: { code: 'MediaPlayPause' }, // [USB: 0x0c/0x00cd]
-    0xB4: { code: 'LaunchMail' }, // [USB: 0x0c/0x018a]
-    0xB5: { code: 'MediaSelect' },
-    0xB6: { code: 'LaunchApp1' },
-    0xB7: { code: 'LaunchApp2' },
-    // 0xB8-0xB9 - reserved
-    0xBA: { code: 'Semicolon', keyCap: ';' }, // [USB: 0x33] ;: (US Standard 101)
-    0xBB: { code: 'Equal', keyCap: '=' }, // [USB: 0x2e] =+
-    0xBC: { code: 'Comma', keyCap: ',' }, // [USB: 0x36] ,<
-    0xBD: { code: 'Minus', keyCap: '-' }, // [USB: 0x2d] -_
-    0xBE: { code: 'Period', keyCap: '.' }, // [USB: 0x37] .>
-    0xBF: { code: 'Slash', keyCap: '/' }, // [USB: 0x38] /? (US Standard 101)
-
-    0xC0: { code: 'Backquote', keyCap: '`' }, // [USB: 0x35] `~ (US Standard 101)
-    // 0xC1-0xCF - reserved
-
-    // 0xD0-0xD7 - reserved
-    // 0xD8-0xDA - unassigned
-    0xDB: { code: 'BracketLeft', keyCap: '[' }, // [USB: 0x2f] [{ (US Standard 101)
-    0xDC: { code: 'Backslash', keyCap: '\\' }, // [USB: 0x31] \| (US Standard 101)
-    0xDD: { code: 'BracketRight', keyCap: ']' }, // [USB: 0x30] ]} (US Standard 101)
-    0xDE: { code: 'Quote', keyCap: '\'' }, // [USB: 0x34] '" (US Standard 101)
-    // 0xDF - miscellaneous/varies
-
-    // 0xE0 - reserved
-    // 0xE1 - OEM specific
-    0xE2: { code: 'IntlBackslash', keyCap: '\\' }, // [USB: 0x64] \| (UK Standard 102)
-    // 0xE3-0xE4 - OEM specific
-    0xE5: { code: 'Process' }, // (Not in D3E)
-    // 0xE6 - OEM specific
-    // 0xE7 - VK_PACKET
-    // 0xE8 - unassigned
-    // 0xE9-0xEF - OEM specific
-
-    // 0xF0-0xF5 - OEM specific
-    0xF6: { code: 'Attn' }, // [USB: 0x9a] (Not in D3E)
-    0xF7: { code: 'CrSel' }, // [USB: 0xa3] (Not in D3E)
-    0xF8: { code: 'ExSel' }, // [USB: 0xa4] (Not in D3E)
-    0xF9: { code: 'EraseEof' }, // (Not in D3E)
-    0xFA: { code: 'Play' }, // (Not in D3E)
-    0xFB: { code: 'ZoomToggle' }, // (Not in D3E)
-    // 0xFC - VK_NONAME - reserved
-    // 0xFD - VK_PA1
-    0xFE: { code: 'Clear' // [USB: 0x9c] (Not in D3E)
-    } };
-
-  // No legacy keyCode, but listed in D3E:
-
-  // code: usb
-  // 'IntlHash': 0x070032,
-  // 'IntlRo': 0x070087,
-  // 'IntlYen': 0x070089,
-  // 'NumpadBackspace': 0x0700bb,
-  // 'NumpadClear': 0x0700d8,
-  // 'NumpadClearEntry': 0x0700d9,
-  // 'NumpadMemoryAdd': 0x0700d3,
-  // 'NumpadMemoryClear': 0x0700d2,
-  // 'NumpadMemoryRecall': 0x0700d1,
-  // 'NumpadMemoryStore': 0x0700d0,
-  // 'NumpadMemorySubtract': 0x0700d4,
-  // 'NumpadParenLeft': 0x0700b6,
-  // 'NumpadParenRight': 0x0700b7,
-
-  //--------------------------------------------------------------------
-  //
-  // Browser/OS Specific Mappings
-  //
-  //--------------------------------------------------------------------
-
-  mergeIf(keyCodeToInfoTable, 'moz', {
-    0x3B: { code: 'Semicolon', keyCap: ';' }, // [USB: 0x33] ;: (US Standard 101)
-    0x3D: { code: 'Equal', keyCap: '=' }, // [USB: 0x2e] =+
-    0x6B: { code: 'Equal', keyCap: '=' }, // [USB: 0x2e] =+
-    0x6D: { code: 'Minus', keyCap: '-' }, // [USB: 0x2d] -_
-    0xBB: { code: 'NumpadAdd', keyCap: '+', location: NUMPAD }, // [USB: 0x57]
-    0xBD: { code: 'NumpadSubtract', keyCap: '-', location: NUMPAD // [USB: 0x56]
-    } });
-
-  mergeIf(keyCodeToInfoTable, 'moz-mac', {
-    0x0C: { code: 'NumLock', location: NUMPAD }, // [USB: 0x53]
-    0xAD: { code: 'Minus', keyCap: '-' // [USB: 0x2d] -_
-    } });
-
-  mergeIf(keyCodeToInfoTable, 'moz-win', {
-    0xAD: { code: 'Minus', keyCap: '-' // [USB: 0x2d] -_
-    } });
-
-  mergeIf(keyCodeToInfoTable, 'chrome-mac', {
-    0x5D: { code: 'OSRight', location: RIGHT // [USB: 0xe7]
-    } });
-
-  // Windows via Bootcamp (!)
-  if (0) {
-    mergeIf(keyCodeToInfoTable, 'chrome-win', {
-      0xC0: { code: 'Quote', keyCap: '\'' }, // [USB: 0x34] '" (US Standard 101)
-      0xDE: { code: 'Backslash', keyCap: '\\' }, // [USB: 0x31] \| (US Standard 101)
-      0xDF: { code: 'Backquote', keyCap: '`' // [USB: 0x35] `~ (US Standard 101)
-      } });
-
-    mergeIf(keyCodeToInfoTable, 'ie', {
-      0xC0: { code: 'Quote', keyCap: '\'' }, // [USB: 0x34] '" (US Standard 101)
-      0xDE: { code: 'Backslash', keyCap: '\\' }, // [USB: 0x31] \| (US Standard 101)
-      0xDF: { code: 'Backquote', keyCap: '`' // [USB: 0x35] `~ (US Standard 101)
-      } });
-  }
-
-  mergeIf(keyCodeToInfoTable, 'safari', {
-    0x03: { code: 'Enter' }, // [USB: 0x28] old Safari
-    0x19: { code: 'Tab' // [USB: 0x2b] old Safari for Shift+Tab
-    } });
-
-  mergeIf(keyCodeToInfoTable, 'ios', {
-    0x0A: { code: 'Enter', location: STANDARD // [USB: 0x28]
-    } });
-
-  mergeIf(keyCodeToInfoTable, 'safari-mac', {
-    0x5B: { code: 'OSLeft', location: LEFT }, // [USB: 0xe3]
-    0x5D: { code: 'OSRight', location: RIGHT }, // [USB: 0xe7]
-    0xE5: { code: 'KeyQ', keyCap: 'Q' // [USB: 0x14] On alternate presses, Ctrl+Q sends this
-    } });
-
-  //--------------------------------------------------------------------
-  //
-  // Identifier Mappings
-  //
-  //--------------------------------------------------------------------
-
-  // Cases where newer-ish browsers send keyIdentifier which can be
-  // used to disambiguate keys.
-
-  // keyIdentifierTable[keyIdentifier] -> keyInfo
-
-  var keyIdentifierTable = {};
-  if ('cros' === os) {
-    keyIdentifierTable['U+00A0'] = { code: 'ShiftLeft', location: LEFT };
-    keyIdentifierTable['U+00A1'] = { code: 'ShiftRight', location: RIGHT };
-    keyIdentifierTable['U+00A2'] = { code: 'ControlLeft', location: LEFT };
-    keyIdentifierTable['U+00A3'] = { code: 'ControlRight', location: RIGHT };
-    keyIdentifierTable['U+00A4'] = { code: 'AltLeft', location: LEFT };
-    keyIdentifierTable['U+00A5'] = { code: 'AltRight', location: RIGHT };
-  }
-  if ('chrome-mac' === browser_os) {
-    keyIdentifierTable['U+0010'] = { code: 'ContextMenu' };
-  }
-  if ('safari-mac' === browser_os) {
-    keyIdentifierTable['U+0010'] = { code: 'ContextMenu' };
-  }
-  if ('ios' === os) {
-    // These only generate keyup events
-    keyIdentifierTable['U+0010'] = { code: 'Function' };
-
-    keyIdentifierTable['U+001C'] = { code: 'ArrowLeft' };
-    keyIdentifierTable['U+001D'] = { code: 'ArrowRight' };
-    keyIdentifierTable['U+001E'] = { code: 'ArrowUp' };
-    keyIdentifierTable['U+001F'] = { code: 'ArrowDown' };
-
-    keyIdentifierTable['U+0001'] = { code: 'Home' }; // [USB: 0x4a] Fn + ArrowLeft
-    keyIdentifierTable['U+0004'] = { code: 'End' }; // [USB: 0x4d] Fn + ArrowRight
-    keyIdentifierTable['U+000B'] = { code: 'PageUp' }; // [USB: 0x4b] Fn + ArrowUp
-    keyIdentifierTable['U+000C'] = { code: 'PageDown' }; // [USB: 0x4e] Fn + ArrowDown
-  }
-
-  //--------------------------------------------------------------------
-  //
-  // Location Mappings
-  //
-  //--------------------------------------------------------------------
-
-  // Cases where newer-ish browsers send location/keyLocation which
-  // can be used to disambiguate keys.
-
-  // locationTable[location][keyCode] -> keyInfo
-  var locationTable = [];
-  locationTable[LEFT] = {
-    0x10: { code: 'ShiftLeft', location: LEFT }, // [USB: 0xe1]
-    0x11: { code: 'ControlLeft', location: LEFT }, // [USB: 0xe0]
-    0x12: { code: 'AltLeft', location: LEFT // [USB: 0xe2]
-    } };
-  locationTable[RIGHT] = {
-    0x10: { code: 'ShiftRight', location: RIGHT }, // [USB: 0xe5]
-    0x11: { code: 'ControlRight', location: RIGHT }, // [USB: 0xe4]
-    0x12: { code: 'AltRight', location: RIGHT // [USB: 0xe6]
-    } };
-  locationTable[NUMPAD] = {
-    0x0D: { code: 'NumpadEnter', location: NUMPAD // [USB: 0x58]
-    } };
-
-  mergeIf(locationTable[NUMPAD], 'moz', {
-    0x6D: { code: 'NumpadSubtract', location: NUMPAD }, // [USB: 0x56]
-    0x6B: { code: 'NumpadAdd', location: NUMPAD // [USB: 0x57]
-    } });
-  mergeIf(locationTable[LEFT], 'moz-mac', {
-    0xE0: { code: 'OSLeft', location: LEFT // [USB: 0xe3]
-    } });
-  mergeIf(locationTable[RIGHT], 'moz-mac', {
-    0xE0: { code: 'OSRight', location: RIGHT // [USB: 0xe7]
-    } });
-  mergeIf(locationTable[RIGHT], 'moz-win', {
-    0x5B: { code: 'OSRight', location: RIGHT // [USB: 0xe7]
-    } });
-
-  mergeIf(locationTable[RIGHT], 'mac', {
-    0x5D: { code: 'OSRight', location: RIGHT // [USB: 0xe7]
-    } });
-
-  mergeIf(locationTable[NUMPAD], 'chrome-mac', {
-    0x0C: { code: 'NumLock', location: NUMPAD // [USB: 0x53]
-    } });
-
-  mergeIf(locationTable[NUMPAD], 'safari-mac', {
-    0x0C: { code: 'NumLock', location: NUMPAD }, // [USB: 0x53]
-    0xBB: { code: 'NumpadAdd', location: NUMPAD }, // [USB: 0x57]
-    0xBD: { code: 'NumpadSubtract', location: NUMPAD }, // [USB: 0x56]
-    0xBE: { code: 'NumpadDecimal', location: NUMPAD }, // [USB: 0x63]
-    0xBF: { code: 'NumpadDivide', location: NUMPAD // [USB: 0x54]
-    } });
-
-  //--------------------------------------------------------------------
-  //
-  // Key Values
-  //
-  //--------------------------------------------------------------------
-
-  // Mapping from `code` values to `key` values. Values defined at:
-  // https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3Events-key.html
-  // Entries are only provided when `key` differs from `code`. If
-  // printable, `shiftKey` has the shifted printable character. This
-  // assumes US Standard 101 layout
-
-  var codeToKeyTable = {
-    // Modifier Keys
-    ShiftLeft: { key: 'Shift' },
-    ShiftRight: { key: 'Shift' },
-    ControlLeft: { key: 'Control' },
-    ControlRight: { key: 'Control' },
-    AltLeft: { key: 'Alt' },
-    AltRight: { key: 'Alt' },
-    OSLeft: { key: 'OS' },
-    OSRight: { key: 'OS' },
-
-    // Whitespace Keys
-    NumpadEnter: { key: 'Enter' },
-    Space: { key: ' ' },
-
-    // Printable Keys
-    Digit0: { key: '0', shiftKey: ')' },
-    Digit1: { key: '1', shiftKey: '!' },
-    Digit2: { key: '2', shiftKey: '@' },
-    Digit3: { key: '3', shiftKey: '#' },
-    Digit4: { key: '4', shiftKey: '$' },
-    Digit5: { key: '5', shiftKey: '%' },
-    Digit6: { key: '6', shiftKey: '^' },
-    Digit7: { key: '7', shiftKey: '&' },
-    Digit8: { key: '8', shiftKey: '*' },
-    Digit9: { key: '9', shiftKey: '(' },
-    KeyA: { key: 'a', shiftKey: 'A' },
-    KeyB: { key: 'b', shiftKey: 'B' },
-    KeyC: { key: 'c', shiftKey: 'C' },
-    KeyD: { key: 'd', shiftKey: 'D' },
-    KeyE: { key: 'e', shiftKey: 'E' },
-    KeyF: { key: 'f', shiftKey: 'F' },
-    KeyG: { key: 'g', shiftKey: 'G' },
-    KeyH: { key: 'h', shiftKey: 'H' },
-    KeyI: { key: 'i', shiftKey: 'I' },
-    KeyJ: { key: 'j', shiftKey: 'J' },
-    KeyK: { key: 'k', shiftKey: 'K' },
-    KeyL: { key: 'l', shiftKey: 'L' },
-    KeyM: { key: 'm', shiftKey: 'M' },
-    KeyN: { key: 'n', shiftKey: 'N' },
-    KeyO: { key: 'o', shiftKey: 'O' },
-    KeyP: { key: 'p', shiftKey: 'P' },
-    KeyQ: { key: 'q', shiftKey: 'Q' },
-    KeyR: { key: 'r', shiftKey: 'R' },
-    KeyS: { key: 's', shiftKey: 'S' },
-    KeyT: { key: 't', shiftKey: 'T' },
-    KeyU: { key: 'u', shiftKey: 'U' },
-    KeyV: { key: 'v', shiftKey: 'V' },
-    KeyW: { key: 'w', shiftKey: 'W' },
-    KeyX: { key: 'x', shiftKey: 'X' },
-    KeyY: { key: 'y', shiftKey: 'Y' },
-    KeyZ: { key: 'z', shiftKey: 'Z' },
-    Numpad0: { key: '0' },
-    Numpad1: { key: '1' },
-    Numpad2: { key: '2' },
-    Numpad3: { key: '3' },
-    Numpad4: { key: '4' },
-    Numpad5: { key: '5' },
-    Numpad6: { key: '6' },
-    Numpad7: { key: '7' },
-    Numpad8: { key: '8' },
-    Numpad9: { key: '9' },
-    NumpadMultiply: { key: '*' },
-    NumpadAdd: { key: '+' },
-    NumpadComma: { key: ',' },
-    NumpadSubtract: { key: '-' },
-    NumpadDecimal: { key: '.' },
-    NumpadDivide: { key: '/' },
-    Semicolon: { key: ';', shiftKey: ':' },
-    Equal: { key: '=', shiftKey: '+' },
-    Comma: { key: ',', shiftKey: '<' },
-    Minus: { key: '-', shiftKey: '_' },
-    Period: { key: '.', shiftKey: '>' },
-    Slash: { key: '/', shiftKey: '?' },
-    Backquote: { key: '`', shiftKey: '~' },
-    BracketLeft: { key: '[', shiftKey: '{' },
-    Backslash: { key: '\\', shiftKey: '|' },
-    BracketRight: { key: ']', shiftKey: '}' },
-    Quote: { key: '\'', shiftKey: '"' },
-    IntlBackslash: { key: '\\', shiftKey: '|' }
-  };
-
-  mergeIf(codeToKeyTable, 'mac', {
-    OSLeft: { key: 'Meta' },
-    OSRight: { key: 'Meta' }
-  });
-
-  // Corrections for 'key' names in older browsers (e.g. FF36-)
-  // https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent.key#Key_values
-  var keyFixTable = {
-    Esc: 'Escape',
-    Nonconvert: 'NonConvert',
-    Left: 'ArrowLeft',
-    Up: 'ArrowUp',
-    Right: 'ArrowRight',
-    Down: 'ArrowDown',
-    Del: 'Delete',
-    Menu: 'ContextMenu',
-    MediaNextTrack: 'MediaTrackNext',
-    MediaPreviousTrack: 'MediaTrackPrevious',
-    SelectMedia: 'MediaSelect',
-    HalfWidth: 'Hankaku',
-    FullWidth: 'Zenkaku',
-    RomanCharacters: 'Romaji',
-    Crsel: 'CrSel',
-    Exsel: 'ExSel',
-    Zoom: 'ZoomToggle'
-  };
-
-  //--------------------------------------------------------------------
-  //
-  // Exported Functions
-  //
-  //--------------------------------------------------------------------
-
-
-  var codeTable = remap(keyCodeToInfoTable, 'code');
-
-  try {
-    var nativeLocation = nativeKeyboardEvent && 'location' in new KeyboardEvent('');
-  } catch (_) {}
-
-  function keyInfoForEvent(event) {
-    var keyCode = 'keyCode' in event ? event.keyCode : 'which' in event ? event.which : 0;
-
-    var keyInfo = function () {
-      if (nativeLocation || 'keyLocation' in event) {
-        var location = nativeLocation ? event.location : event.keyLocation;
-        if (location && keyCode in locationTable[location]) {
-          return locationTable[location][keyCode];
-        }
-      }
-      if ('keyIdentifier' in event && event.keyIdentifier in keyIdentifierTable) {
-        return keyIdentifierTable[event.keyIdentifier];
-      }
-      if (keyCode in keyCodeToInfoTable) {
-        return keyCodeToInfoTable[keyCode];
-      }
-      return null;
-    }();
-
-    // TODO: Track these down and move to general tables
-    if (0) {
-      // TODO: Map these for newerish browsers?
-      // TODO: iOS only?
-      // TODO: Override with more common keyIdentifier name?
-      switch (event.keyIdentifier) {
-        case 'U+0010':
-          keyInfo = { code: 'Function' };break;
-        case 'U+001C':
-          keyInfo = { code: 'ArrowLeft' };break;
-        case 'U+001D':
-          keyInfo = { code: 'ArrowRight' };break;
-        case 'U+001E':
-          keyInfo = { code: 'ArrowUp' };break;
-        case 'U+001F':
-          keyInfo = { code: 'ArrowDown' };break;
-      }
-    }
-
-    if (!keyInfo) return null;
-
-    var key = function () {
-      var entry = codeToKeyTable[keyInfo.code];
-      if (!entry) return keyInfo.code;
-      return event.shiftKey && 'shiftKey' in entry ? entry.shiftKey : entry.key;
-    }();
-
-    return {
-      code: keyInfo.code,
-      key: key,
-      location: keyInfo.location,
-      keyCap: keyInfo.keyCap
-    };
-  }
-
-  function queryKeyCap(code, locale) {
-    code = String(code);
-    if (!codeTable.hasOwnProperty(code)) return 'Undefined';
-    if (locale && String(locale).toLowerCase() !== 'en-us') throw Error('Unsupported locale');
-    var keyInfo = codeTable[code];
-    return keyInfo.keyCap || keyInfo.code || 'Undefined';
-  }
-
-  if ('KeyboardEvent' in global && 'defineProperty' in Object) {
-    (function () {
-      function define(o, p, v) {
-        if (p in o) return;
-        Object.defineProperty(o, p, v);
-      }
-
-      define(KeyboardEvent.prototype, 'code', { get: function get() {
-          var keyInfo = keyInfoForEvent(this);
-          return keyInfo ? keyInfo.code : '';
-        } });
-
-      // Fix for nonstandard `key` values (FF36-)
-      if ('key' in KeyboardEvent.prototype) {
-        var desc = Object.getOwnPropertyDescriptor(KeyboardEvent.prototype, 'key');
-        Object.defineProperty(KeyboardEvent.prototype, 'key', { get: function get() {
-            var key = desc.get.call(this);
-            return keyFixTable.hasOwnProperty(key) ? keyFixTable[key] : key;
-          } });
-      }
-
-      define(KeyboardEvent.prototype, 'key', { get: function get() {
-          var keyInfo = keyInfoForEvent(this);
-          return keyInfo && 'key' in keyInfo ? keyInfo.key : 'Unidentified';
-        } });
-
-      define(KeyboardEvent.prototype, 'location', { get: function get() {
-          var keyInfo = keyInfoForEvent(this);
-          return keyInfo && 'location' in keyInfo ? keyInfo.location : STANDARD;
-        } });
-
-      define(KeyboardEvent.prototype, 'locale', { get: function get() {
-          return '';
-        } });
-    })();
-  }
-
-  if (!('queryKeyCap' in global.KeyboardEvent)) global.KeyboardEvent.queryKeyCap = queryKeyCap;
-
-  // Helper for IE8-
-  global.identifyKey = function (event) {
-    if ('code' in event) return;
-
-    var keyInfo = keyInfoForEvent(event);
-    event.code = keyInfo ? keyInfo.code : '';
-    event.key = keyInfo && 'key' in keyInfo ? keyInfo.key : 'Unidentified';
-    event.location = 'location' in event ? event.location : 'keyLocation' in event ? event.keyLocation : keyInfo && 'location' in keyInfo ? keyInfo.location : STANDARD;
-    event.locale = '';
-  };
-})(window);
-
-},{}],5:[function(require,module,exports){
-'use strict';
-
-var EPS = 0.1;
-
-module.exports = AFRAME.registerComponent('checkpoint-controls', {
-  schema: {
-    enabled: { default: true },
-    mode: { default: 'teleport', oneOf: ['teleport', 'animate'] },
-    animateSpeed: { default: 3.0 }
-  },
-
-  init: function init() {
-    this.active = true;
-    this.checkpoint = null;
-
-    this.isNavMeshConstrained = false;
-
-    this.offset = new THREE.Vector3();
-    this.position = new THREE.Vector3();
-    this.targetPosition = new THREE.Vector3();
-  },
-
-  play: function play() {
-    this.active = true;
-  },
-  pause: function pause() {
-    this.active = false;
-  },
-
-  setCheckpoint: function setCheckpoint(checkpoint) {
-    var el = this.el;
-
-    if (!this.active) return;
-    if (this.checkpoint === checkpoint) return;
-
-    if (this.checkpoint) {
-      el.emit('navigation-end', { checkpoint: this.checkpoint });
-    }
-
-    this.checkpoint = checkpoint;
-    this.sync();
-
-    // Ignore new checkpoint if we're already there.
-    if (this.position.distanceTo(this.targetPosition) < EPS) {
-      this.checkpoint = null;
-      return;
-    }
-
-    el.emit('navigation-start', { checkpoint: checkpoint });
-
-    if (this.data.mode === 'teleport') {
-      this.el.setAttribute('position', this.targetPosition);
-      this.checkpoint = null;
-      el.emit('navigation-end', { checkpoint: checkpoint });
-      el.components['movement-controls'].updateNavLocation();
-    }
-  },
-
-  isVelocityActive: function isVelocityActive() {
-    return !!(this.active && this.checkpoint);
-  },
-
-  getVelocity: function getVelocity() {
-    if (!this.active) return;
-
-    var data = this.data;
-    var offset = this.offset;
-    var position = this.position;
-    var targetPosition = this.targetPosition;
-    var checkpoint = this.checkpoint;
-
-    this.sync();
-    if (position.distanceTo(targetPosition) < EPS) {
-      this.checkpoint = null;
-      this.el.emit('navigation-end', { checkpoint: checkpoint });
-      return offset.set(0, 0, 0);
-    }
-    offset.setLength(data.animateSpeed);
-    return offset;
-  },
-
-  sync: function sync() {
-    var offset = this.offset;
-    var position = this.position;
-    var targetPosition = this.targetPosition;
-
-    position.copy(this.el.getAttribute('position'));
-    targetPosition.copy(this.checkpoint.object3D.getWorldPosition());
-    targetPosition.add(this.checkpoint.components.checkpoint.getOffset());
-    offset.copy(targetPosition).sub(position);
-  }
-});
-
-},{}],6:[function(require,module,exports){
-'use strict';
-
-/**
- * Gamepad controls for A-Frame.
- *
- * Stripped-down version of: https://github.com/donmccurdy/aframe-gamepad-controls
- *
- * 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 = require('../../lib/GamepadButton'),
-    GamepadButtonEvent = require('../../lib/GamepadButtonEvent');
-
-var JOYSTICK_EPS = 0.2;
-
-module.exports = AFRAME.registerComponent('gamepad-controls', {
-
-  /*******************************************************************
-   * Statics
-   */
-
-  GamepadButton: GamepadButton,
-
-  /*******************************************************************
-   * Schema
-   */
-
-  schema: {
-    // Controller 0-3
-    controller: { default: 0, oneOf: [0, 1, 2, 3] },
-
-    // Enable/disable features
-    enabled: { default: true },
-
-    // Debugging
-    debug: { default: false },
-
-    // Heading element for rotation
-    camera: { default: '[camera]', type: 'selector' },
-
-    // Rotation sensitivity
-    rotationSensitivity: { default: 2.0 }
-  },
-
-  /*******************************************************************
-   * Core
-   */
-
-  /**
-   * Called once when component is attached. Generally for initial setup.
-   */
-  init: function init() {
-    var scene = this.el.sceneEl;
-    this.prevTime = window.performance.now();
-
-    // Button state
-    this.buttons = {};
-
-    // Rotation
-    var rotation = this.el.object3D.rotation;
-    this.pitch = new THREE.Object3D();
-    this.pitch.rotation.x = THREE.Math.degToRad(rotation.x);
-    this.yaw = new THREE.Object3D();
-    this.yaw.position.y = 10;
-    this.yaw.rotation.y = THREE.Math.degToRad(rotation.y);
-    this.yaw.add(this.pitch);
-
-    scene.addBehavior(this);
-  },
-
-  /**
-   * Called when component is attached and when component data changes.
-   * Generally modifies the entity based on the data.
-   */
-  update: function update() {
-    this.tick();
-  },
-
-  /**
-   * Called on each iteration of main render loop.
-   */
-  tick: function tick(t, dt) {
-    this.updateButtonState();
-    this.updateRotation(dt);
-  },
-
-  /**
-   * Called when a component is removed (e.g., via removeAttribute).
-   * Generally undoes all modifications to the entity.
-   */
-  remove: function remove() {},
-
-  /*******************************************************************
-   * Movement
-   */
-
-  isVelocityActive: function isVelocityActive() {
-    if (!this.data.enabled || !this.isConnected()) return false;
-
-    var dpad = this.getDpad(),
-        joystick0 = this.getJoystick(0),
-        inputX = dpad.x || joystick0.x,
-        inputY = dpad.y || joystick0.y;
-
-    return Math.abs(inputX) > JOYSTICK_EPS || Math.abs(inputY) > JOYSTICK_EPS;
-  },
-
-  getVelocityDelta: function getVelocityDelta() {
-    var dpad = this.getDpad(),
-        joystick0 = this.getJoystick(0),
-        inputX = dpad.x || joystick0.x,
-        inputY = dpad.y || joystick0.y,
-        dVelocity = new THREE.Vector3();
-
-    if (Math.abs(inputX) > JOYSTICK_EPS) {
-      dVelocity.x += inputX;
-    }
-    if (Math.abs(inputY) > JOYSTICK_EPS) {
-      dVelocity.z += inputY;
-    }
-
-    return dVelocity;
-  },
-
-  /*******************************************************************
-   * Rotation
-   */
-
-  isRotationActive: function isRotationActive() {
-    if (!this.data.enabled || !this.isConnected()) return false;
-
-    var joystick1 = this.getJoystick(1);
-
-    return Math.abs(joystick1.x) > JOYSTICK_EPS || Math.abs(joystick1.y) > JOYSTICK_EPS;
-  },
-
-  updateRotation: function updateRotation(dt) {
-    if (!this.isRotationActive()) return;
-
-    var data = this.data;
-    var yaw = this.yaw;
-    var pitch = this.pitch;
-    var lookControls = data.camera.components['look-controls'];
-    var hasLookControls = lookControls && lookControls.pitchObject && lookControls.yawObject;
-
-    // Sync with look-controls pitch/yaw if available.
-    if (hasLookControls) {
-      pitch.rotation.copy(lookControls.pitchObject.rotation);
-      yaw.rotation.copy(lookControls.yawObject.rotation);
-    }
-
-    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;
-
-    lookVector.multiplyScalar(data.rotationSensitivity * dt / 1000);
-    yaw.rotation.y -= lookVector.x;
-    pitch.rotation.x -= lookVector.y;
-    pitch.rotation.x = Math.max(-Math.PI / 2, Math.min(Math.PI / 2, pitch.rotation.x));
-    data.camera.object3D.rotation.set(pitch.rotation.x, yaw.rotation.y, 0);
-
-    // Sync with look-controls pitch/yaw if available.
-    if (hasLookControls) {
-      lookControls.pitchObject.rotation.copy(pitch.rotation);
-      lookControls.yawObject.rotation.copy(yaw.rotation);
-    }
-  },
-
-  /*******************************************************************
-   * Button events
-   */
-
-  updateButtonState: function updateButtonState() {
-    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 emit(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 getGamepad() {
-    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 getButton(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 getAxis(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 getJoystick(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 getDpad() {
-    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 isConnected() {
-    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 getID() {
-    return this.getGamepad().id;
-  }
-});
-
-},{"../../lib/GamepadButton":2,"../../lib/GamepadButtonEvent":3}],7:[function(require,module,exports){
-'use strict';
-
-require('./checkpoint-controls');
-require('./gamepad-controls');
-require('./keyboard-controls');
-require('./touch-controls');
-require('./movement-controls');
-require('./trackpad-controls');
-
-},{"./checkpoint-controls":5,"./gamepad-controls":6,"./keyboard-controls":8,"./movement-controls":9,"./touch-controls":10,"./trackpad-controls":11}],8:[function(require,module,exports){
-'use strict';
-
-require('../../lib/keyboard.polyfill');
-
-var MAX_DELTA = 0.2,
-    PROXY_FLAG = '__keyboard-controls-proxy';
-
-var KeyboardEvent = window.KeyboardEvent;
-
-/**
- * Keyboard Controls component.
- *
- * Stripped-down version of: https://github.com/donmccurdy/aframe-keyboard-controls
- *
- * Bind keyboard events to components, or control your entities with the WASD keys.
- *
- * Why use KeyboardEvent.code? "This is set to a string representing the key that was pressed to
- * generate the KeyboardEvent, without taking the current keyboard layout (e.g., QWERTY vs.
- * Dvorak), locale (e.g., English vs. French), or any modifier keys into account. This is useful
- * when you care about which physical key was pressed, rather thanwhich character it corresponds
- * to. For example, if you’re a writing a game, you might want a certain set of keys to move the
- * player in different directions, and that mapping should ideally be independent of keyboard
- * layout. See: https://developers.google.com/web/updates/2016/04/keyboardevent-keys-codes
- *
- * @namespace wasd-controls
- * keys the entity moves and if you release it will stop. Easing simulates friction.
- * to the entity when pressing the keys.
- * @param {bool} [enabled=true] - To completely enable or disable the controls
- */
-module.exports = AFRAME.registerComponent('keyboard-controls', {
-  schema: {
-    enabled: { default: true },
-    debug: { default: false }
-  },
-
-  init: function init() {
-    this.dVelocity = new THREE.Vector3();
-    this.localKeys = {};
-    this.listeners = {
-      keydown: this.onKeyDown.bind(this),
-      keyup: this.onKeyUp.bind(this),
-      blur: this.onBlur.bind(this)
-    };
-    this.attachEventListeners();
-  },
-
-  /*******************************************************************
-  * Movement
-  */
-
-  isVelocityActive: function isVelocityActive() {
-    return this.data.enabled && !!Object.keys(this.getKeys()).length;
-  },
-
-  getVelocityDelta: function getVelocityDelta() {
-    var data = this.data,
-        keys = this.getKeys();
-
-    this.dVelocity.set(0, 0, 0);
-    if (data.enabled) {
-      if (keys.KeyW || keys.ArrowUp) {
-        this.dVelocity.z -= 1;
-      }
-      if (keys.KeyA || keys.ArrowLeft) {
-        this.dVelocity.x -= 1;
-      }
-      if (keys.KeyS || keys.ArrowDown) {
-        this.dVelocity.z += 1;
-      }
-      if (keys.KeyD || keys.ArrowRight) {
-        this.dVelocity.x += 1;
-      }
-    }
-
-    return this.dVelocity.clone();
-  },
-
-  /*******************************************************************
-  * Events
-  */
-
-  play: function play() {
-    this.attachEventListeners();
-  },
-
-  pause: function pause() {
-    this.removeEventListeners();
-  },
-
-  remove: function remove() {
-    this.pause();
-  },
-
-  attachEventListeners: function attachEventListeners() {
-    window.addEventListener('keydown', this.listeners.keydown, false);
-    window.addEventListener('keyup', this.listeners.keyup, false);
-    window.addEventListener('blur', this.listeners.blur, false);
-  },
-
-  removeEventListeners: function removeEventListeners() {
-    window.removeEventListener('keydown', this.listeners.keydown);
-    window.removeEventListener('keyup', this.listeners.keyup);
-    window.removeEventListener('blur', this.listeners.blur);
-  },
-
-  onKeyDown: function onKeyDown(event) {
-    if (AFRAME.utils.shouldCaptureKeyEvent(event)) {
-      this.localKeys[event.code] = true;
-      this.emit(event);
-    }
-  },
-
-  onKeyUp: function onKeyUp(event) {
-    if (AFRAME.utils.shouldCaptureKeyEvent(event)) {
-      delete this.localKeys[event.code];
-      this.emit(event);
-    }
-  },
-
-  onBlur: function onBlur() {
-    for (var code in this.localKeys) {
-      if (this.localKeys.hasOwnProperty(code)) {
-        delete this.localKeys[code];
-      }
-    }
-  },
-
-  emit: function emit(event) {
-    // TODO - keydown only initially?
-    // TODO - where the f is the spacebar
-
-    // Emit original event.
-    if (PROXY_FLAG in event) {
-      // TODO - Method never triggered.
-      this.el.emit(event.type, event);
-    }
-
-    // Emit convenience event, identifying key.
-    this.el.emit(event.type + ':' + event.code, new KeyboardEvent(event.type, event));
-    if (this.data.debug) console.log(event.type + ':' + event.code);
-  },
-
-  /*******************************************************************
-  * Accessors
-  */
-
-  isPressed: function isPressed(code) {
-    return code in this.getKeys();
-  },
-
-  getKeys: function getKeys() {
-    if (this.isProxied()) {
-      return this.el.sceneEl.components['proxy-controls'].getKeyboard();
-    }
-    return this.localKeys;
-  },
-
-  isProxied: function isProxied() {
-    var proxyControls = this.el.sceneEl.components['proxy-controls'];
-    return proxyControls && proxyControls.isConnected();
-  }
-
-});
-
-},{"../../lib/keyboard.polyfill":4}],9:[function(require,module,exports){
-'use strict';
-
-/**
- * Movement Controls
- *
- * @author Don McCurdy <dm@donmccurdy.com>
- */
-
-var COMPONENT_SUFFIX = '-controls',
-    MAX_DELTA = 0.2,
-
-// ms
-EPS = 10e-6;
-
-module.exports = AFRAME.registerComponent('movement-controls', {
-
-  /*******************************************************************
-   * Schema
-   */
-
-  dependencies: ['rotation'],
-
-  schema: {
-    enabled: { default: true },
-    controls: { default: ['gamepad', 'trackpad', 'keyboard', 'touch'] },
-    speed: { default: 0.3, min: 0 },
-    fly: { default: false },
-    constrainToNavMesh: { default: false },
-    camera: { default: '[camera]', type: 'selector' }
-  },
-
-  /*******************************************************************
-   * Lifecycle
-   */
-
-  init: function init() {
-    var el = this.el;
-
-    this.velocityCtrl = null;
-
-    this.velocity = new THREE.Vector3();
-    this.heading = new THREE.Quaternion();
-
-    // Navigation
-    this.navGroup = null;
-    this.navNode = null;
-
-    if (el.sceneEl.hasLoaded) {
-      this.injectControls();
-    } else {
-      el.sceneEl.addEventListener('loaded', this.injectControls.bind(this));
-    }
-  },
-
-  update: function update(prevData) {
-    var el = this.el;
-    var data = this.data;
-    if (el.sceneEl.hasLoaded) {
-      this.injectControls();
-    }
-    if (data.constrainToNavMesh !== prevData.constrainToNavMesh) {
-      var nav = el.sceneEl.systems.nav;
-      data.constrainToNavMesh ? nav.addAgent(this) : nav.removeAgent(this);
-    }
-  },
-
-  injectControls: function injectControls() {
-    var data = this.data;
-    var name;
-
-    for (var i = 0; i < data.controls.length; i++) {
-      name = data.controls[i] + COMPONENT_SUFFIX;
-      if (!this.el.components[name]) {
-        this.el.setAttribute(name, '');
-      }
-    }
-  },
-
-  updateNavLocation: function updateNavLocation() {
-    this.navGroup = null;
-    this.navNode = null;
-  },
-
-  /*******************************************************************
-   * Tick
-   */
-
-  tick: function () {
-    var start = new THREE.Vector3();
-    var end = new THREE.Vector3();
-    var clampedEnd = new THREE.Vector3();
-
-    return function (t, dt) {
-      if (!dt) return;
-
-      var el = this.el;
-      var data = this.data;
-
-      if (!data.enabled) return;
-
-      this.updateVelocityCtrl();
-      var velocityCtrl = this.velocityCtrl;
-      var velocity = this.velocity;
-
-      if (!velocityCtrl) return;
-
-      // Update velocity. If FPS is too low, reset.
-      if (dt / 1000 > MAX_DELTA) {
-        velocity.set(0, 0, 0);
-      } else {
-        this.updateVelocity(dt);
-      }
-
-      if (data.constrainToNavMesh && velocityCtrl.isNavMeshConstrained !== false) {
-
-        if (velocity.lengthSq() < EPS) return;
-
-        start.copy(el.object3D.position);
-        end.copy(velocity).multiplyScalar(dt / 1000).add(start);
-
-        var nav = el.sceneEl.systems.nav;
-        this.navGroup = this.navGroup === null ? nav.getGroup(start) : this.navGroup;
-        this.navNode = this.navNode || nav.getNode(start, this.navGroup);
-        this.navNode = nav.clampStep(start, end, this.navGroup, this.navNode, clampedEnd);
-        el.object3D.position.copy(clampedEnd);
-      } else if (el.hasAttribute('velocity')) {
-        el.setAttribute('velocity', velocity);
-      } else {
-        el.object3D.position.x += velocity.x * dt / 1000;
-        el.object3D.position.y += velocity.y * dt / 1000;
-        el.object3D.position.z += velocity.z * dt / 1000;
-      }
-    };
-  }(),
-
-  /*******************************************************************
-   * Movement
-   */
-
-  updateVelocityCtrl: function updateVelocityCtrl() {
-    var data = this.data;
-    if (data.enabled) {
-      for (var i = 0, l = data.controls.length; i < l; i++) {
-        var control = this.el.components[data.controls[i] + COMPONENT_SUFFIX];
-        if (control && control.isVelocityActive()) {
-          this.velocityCtrl = control;
-          return;
-        }
-      }
-      this.velocityCtrl = null;
-    }
-  },
-
-  updateVelocity: function () {
-    var vector2 = new THREE.Vector2();
-    // var matrix = new THREE.Matrix4();
-    // var matrix2 = new THREE.Matrix4();
-    // var position = new THREE.Vector3();
-    // var quaternion = new THREE.Quaternion();
-    // var scale = new THREE.Vector3();
-
-    return function (dt) {
-      var dVelocity = void 0;
-      var el = this.el;
-      var control = this.velocityCtrl;
-      var velocity = this.velocity;
-      var data = this.data;
-
-      if (control) {
-        if (control.getVelocityDelta) {
-          dVelocity = control.getVelocityDelta(dt);
-        } else if (control.getVelocity) {
-          velocity.copy(control.getVelocity());
-          return;
-        } else if (control.getPositionDelta) {
-          velocity.copy(control.getPositionDelta(dt).multiplyScalar(1000 / dt));
-          return;
-        } else {
-          throw new Error('Incompatible movement controls: ', control);
-        }
-      }
-
-      if (el.hasAttribute('velocity') && !data.constrainToNavMesh) {
-        velocity.copy(this.el.getAttribute('velocity'));
-      }
-
-      if (dVelocity && data.enabled) {
-        // TODO: Handle rotated rig.
-        var cameraEl = data.camera;
-        // matrix.copy(cameraEl.object3D.matrixWorld);
-        // matrix2.getInverse(el.object3D.matrixWorld);
-        // matrix.multiply(matrix2);
-        // matrix.decompose(position, quaternion, scale);
-        // dVelocity.applyQuaternion(quaternion);
-
-        // Rotate to heading
-        dVelocity.applyQuaternion(cameraEl.object3D.quaternion);
-
-        var factor = dVelocity.length();
-        if (data.fly) {
-          velocity.copy(dVelocity);
-          velocity.multiplyScalar(this.data.speed * dt);
-        } else {
-          vector2.set(dVelocity.x, dVelocity.z);
-          vector2.setLength(factor * this.data.speed * dt);
-          velocity.x = vector2.x;
-          velocity.z = vector2.y;
-        }
-      }
-    };
-  }()
-});
-
-},{}],10:[function(require,module,exports){
-'use strict';
-
-/**
- * Touch-to-move-forward controls for mobile.
- */
-
-module.exports = AFRAME.registerComponent('touch-controls', {
-  schema: {
-    enabled: { default: true }
-  },
-
-  init: function init() {
-    this.dVelocity = new THREE.Vector3();
-    this.bindMethods();
-  },
-
-  play: function play() {
-    this.addEventListeners();
-  },
-
-  pause: function pause() {
-    this.removeEventListeners();
-    this.dVelocity.set(0, 0, 0);
-  },
-
-  remove: function remove() {
-    this.pause();
-  },
-
-  addEventListeners: function addEventListeners() {
-    var sceneEl = this.el.sceneEl;
-    var canvasEl = sceneEl.canvas;
-
-    if (!canvasEl) {
-      sceneEl.addEventListener('render-target-loaded', this.addEventListeners.bind(this));
-      return;
-    }
-
-    canvasEl.addEventListener('touchstart', this.onTouchStart);
-    canvasEl.addEventListener('touchend', this.onTouchEnd);
-  },
-
-  removeEventListeners: function removeEventListeners() {
-    var canvasEl = this.el.sceneEl && this.el.sceneEl.canvas;
-    if (!canvasEl) {
-      return;
-    }
-
-    canvasEl.removeEventListener('touchstart', this.onTouchStart);
-    canvasEl.removeEventListener('touchend', this.onTouchEnd);
-  },
-
-  isVelocityActive: function isVelocityActive() {
-    return this.data.enabled && this.isMoving;
-  },
-
-  getVelocityDelta: function getVelocityDelta() {
-    this.dVelocity.z = this.isMoving ? -1 : 0;
-    return this.dVelocity.clone();
-  },
-
-  bindMethods: function bindMethods() {
-    this.onTouchStart = this.onTouchStart.bind(this);
-    this.onTouchEnd = this.onTouchEnd.bind(this);
-  },
-
-  onTouchStart: function onTouchStart(e) {
-    this.isMoving = true;
-    e.preventDefault();
-  },
-
-  onTouchEnd: function onTouchEnd(e) {
-    this.isMoving = false;
-    e.preventDefault();
-  }
-});
-
-},{}],11:[function(require,module,exports){
-'use strict';
-
-/**
- * 3dof (Gear VR, Daydream) controls for mobile.
- */
-
-module.exports = AFRAME.registerComponent('trackpad-controls', {
-  schema: {
-    enabled: { default: true }
-  },
-
-  init: function init() {
-    this.dVelocity = new THREE.Vector3();
-    this.zVel = 0;
-    this.bindMethods();
-  },
-
-  play: function play() {
-    this.addEventListeners();
-  },
-
-  pause: function pause() {
-    this.removeEventListeners();
-    this.dVelocity.set(0, 0, 0);
-  },
-
-  remove: function remove() {
-    this.pause();
-  },
-
-  addEventListeners: function addEventListeners() {
-    var sceneEl = this.el.sceneEl;
-
-    sceneEl.addEventListener('axismove', this.onAxisMove);
-    sceneEl.addEventListener('trackpadtouchstart', this.onTouchStart);
-    sceneEl.addEventListener('trackpadtouchend', this.onTouchEnd);
-  },
-
-  removeEventListeners: function removeEventListeners() {
-    var sceneEl = this.el.sceneEl;
-
-    sceneEl.removeEventListener('axismove', this.onAxisMove);
-    sceneEl.removeEventListener('trackpadtouchstart', this.onTouchStart);
-    sceneEl.removeEventListener('trackpadtouchend', this.onTouchEnd);
-  },
-
-  isVelocityActive: function isVelocityActive() {
-    return this.data.enabled && this.isMoving;
-  },
-
-  getVelocityDelta: function getVelocityDelta() {
-    this.dVelocity.z = this.isMoving ? -this.zVel : 1;
-    return this.dVelocity.clone();
-  },
-
-  bindMethods: function bindMethods() {
-    this.onTouchStart = this.onTouchStart.bind(this);
-    this.onTouchEnd = this.onTouchEnd.bind(this);
-    this.onAxisMove = this.onAxisMove.bind(this);
-  },
-
-  onTouchStart: function onTouchStart(e) {
-    this.isMoving = true;
-    e.preventDefault();
-  },
-
-  onTouchEnd: function onTouchEnd(e) {
-    this.isMoving = false;
-    e.preventDefault();
-  },
-
-  onAxisMove: function onAxisMove(e) {
-    var axis_data = e.detail.axis;
-
-    if (axis_data[1] < 0) {
-      this.zVel = 1;
-    }
-
-    if (axis_data[1] > 0) {
-      this.zVel = -1;
-    }
-  }
-
-});
-
-},{}]},{},[1]);

File diff suppressed because it is too large
+ 0 - 0
public/vwf/model/aframe/addon/aframe-extras.controls.min.js


+ 0 - 472
public/vwf/model/aframe/addon/aframe-gamepad-controls.js

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

File diff suppressed because it is too large
+ 0 - 0
public/vwf/model/aframe/addon/aframe-gamepad-controls.min.js


+ 0 - 322
public/vwf/model/aframe/addon/aframe-interpolation 2.js

@@ -1,322 +0,0 @@
-/*
-The MIT License (MIT)
-Copyright (c) 2014-2018 Nikolai Suslov and the Krestianstvo.org project contributors. (https://github.com/NikolaySuslov/livecodingspace/blob/master/LICENSE.md)
-
-The MIT License (MIT) Copyright (c) 2015 Kevin Ngo
-https://github.com/scenevr/aframe-interpolate-component.git
-*/
-
-// Interpolate component for A-Frame VR. For using in LiveCoding.space and A-Frame 0.8.x
-
-/* globals AFRAME, performance, THREE */
-
-if (typeof AFRAME === 'undefined') {
-  throw new Error('Component attempted to register before AFRAME was available.');
-}
-
-
-class Interpolator {
-  constructor(comp) {
-    this.component = comp;
-    this.time = this.getMillis();
-  }
-
-  active() {
-    return this.previous && this.next && (this.getTime() < 1);
-  }
-
-  getMillis() {
-    return new Date().getTime();
-  }
-
-  getTime() {
-    return (this.getMillis() - this.time) / this.component.timestep;
-  }
-
-  vecCmp(a, b, delta) {
-
-    let distance = a.distanceTo(b);
-    if (distance > delta) {
-      return false;
-    }
-    return true;
-  }
-
-}
-
-class RotationInterpolator extends Interpolator {
-
-  constructor(comp) {
-    super(comp);
-
-    this.lastRotation = this.component.el.getAttribute('rotation');
-    this.previous = (new THREE.Quaternion()).setFromEuler(new THREE.Euler(
-      this.radians(this.lastRotation.x),
-      this.radians(this.lastRotation.y),
-      this.radians(this.lastRotation.z), 'YXZ'
-    ));
-    this.next = (new THREE.Quaternion()).setFromEuler(new THREE.Euler(
-      this.radians(this.lastRotation.x),
-      this.radians(this.lastRotation.y),
-      this.radians(this.lastRotation.z), 'YXZ'
-    ));
-
-
-  }
-
-  radians(degrees) {
-    // return degrees * Math.PI / 180.0;
-    return THREE.Math.degToRad(degrees)
-  }
-
-  makeInterpolation() {
-    let q = new THREE.Quaternion();
-    let e = new THREE.Euler();
-
-    THREE.Quaternion.slerp(this.previous, this.next, q, this.getTime());
-    return e.setFromQuaternion(q);
-  }
-
-  testForLerp() {
-
-    if (this.component.deltaRot == 0) {
-      return true
-    }
-
-    let prev = (new THREE.Euler()).setFromQuaternion(this.previous).toVector3();
-    let next = (new THREE.Euler()).setFromQuaternion(this.next).toVector3();
-
-    if (prev && next && this.vecCmp(prev, next, this.component.deltaRot)) {
-      return true
-    }
-    return false
-  }
-
-
-  inTick(currentRotation) {
-
-    if (this.getTime() < 0.5) {
-      // fixme - ignore multiple calls
-      return;
-    }
-
-
-    if (!this.previous) {
-      // this.previous = new THREE.Quaternion();
-      // this.next = new THREE.Quaternion();
-      this.previous = (new THREE.Quaternion()).setFromEuler(new THREE.Euler(
-        this.radians(this.lastRotation.x),
-        this.radians(this.lastRotation.y),
-        this.radians(this.lastRotation.z), 'YXZ'
-      ));
-
-      this.next = (new THREE.Quaternion()).setFromEuler(new THREE.Euler(
-        this.radians(currentRotation.x),
-        this.radians(currentRotation.y),
-        this.radians(currentRotation.z), 'YXZ'
-      ));
-    }
-
-    this.time = this.getMillis();
-    this.previous.copy(this.next);
-    this.next.setFromEuler(new THREE.Euler(
-      this.radians(currentRotation.x),
-      this.radians(currentRotation.y),
-      this.radians(currentRotation.z), 'YXZ'
-    ));
-
-    this.lastRotation = currentRotation;
-
-  }
-
-}
-
-
-class PositionInterpolator extends Interpolator {
-
-  constructor(comp) {
-    super(comp);
-    //this.lastPosition = new THREE.Vector3().copy(this.component.el.object3D.position);
-   this.lastPosition = new THREE.Vector3().copy(this.component.el.getAttribute('position'));
-    //this.lastPosition = this.component.el.getAttribute('position');
-    this.previous = (new THREE.Vector3()).fromArray(Object.values(this.lastPosition));
-    this.next = (new THREE.Vector3()).fromArray(Object.values(this.lastPosition));
-
-  }
-
-
-  testForLerp() {
-
-    if (this.component.deltaPos == 0) {
-      return true
-    }
-
-    if (this.previous && this.next && this.vecCmp(this.previous, this.next, this.component.deltaPos)) {
-      return true
-    }
-    return false
-  }
-
-  makeInterpolation() {
-    return this.previous.lerp(this.next, this.getTime());
-  }
-
-  inTick(currentPosition) {
-    //console.log(this.getTime());
-    if (this.getTime()< 0.5) {
-      // fixme - ignore multiple calls
-      return;
-    }
-
-    if (!this.previous) {
-      this.previous = (new THREE.Vector3()).fromArray(Object.values(this.lastPosition));
-      this.next = (new THREE.Vector3()).fromArray(Object.values(currentPosition));
-    }
-
-    
-    this.previous.copy(this.next);
-    this.next.copy(currentPosition);
-
-    //this.lastPosition = currentPosition;
-    this.lastPosition.copy(currentPosition);
-    this.time = this.getMillis();
-
-  }
-
-}
-
-/**
- * Interpolate component for A-Frame.
- */
-AFRAME.registerComponent('interpolation', {
-  schema: {
-    duration: { default: 50 },
-    enabled: { default: true },
-    deltaPos: { default: 0 },
-    deltaRot: { default: 0 }
-  },
-
-
-
-  /**
-   * Called once when component is attached. Generally for initial setup.
-   */
-  init: function () {
-
-    // Set up the tick throttling.
-    //this.tick = AFRAME.utils.throttleTick(this.throttledTick, 0, this);
-
-    //var el = this.el;
-    //this.lastPosition = el.getAttribute('position');
-
-
-    //this.timestep = 50;
-    //this.time = this.getMillis();
-    //  this.previous = (new THREE.Vector3()).fromArray(Object.values(this.lastPosition));
-    //  this.next = (new THREE.Vector3()).fromArray(Object.values(this.lastPosition));
-
-  },
-
-  // throttledTick: function (time, deltaTime) {
-
-  // },
-
-  /**
-   * Called when component is attached and when component data changes.
-   * Generally modifies the entity based on the data.
-   */
-  update: function (oldData) {
-
-    if (!this.interpolation) {
-      this.timestep = parseInt(this.data.duration, 10);
-      this.deltaPos = parseFloat(this.data.deltaPos);
-      this.deltaRot = THREE.Math.degToRad(parseFloat(this.data.deltaRot));
-
-      this.enabled = JSON.parse(this.data.enabled);
-
-      if (this.enabled) {
-        this.posInterpolator = new PositionInterpolator(this);
-        this.rotInterpolator = new RotationInterpolator(this);
-      }
-    }
-
-    // if (!this.interpolation) {
-    //   var timestep = parseInt(this.data.duration, 10);
-
-    //   this.positionInterpolator = new PositionInterpolator(timestep, this);
-    //   this.rotationInterpolator = new RotationInterpolator(timestep, this);
-    // }
-  },
-
-  /**
-   * Called when a component is removed (e.g., via removeAttribute).
-   * Generally undoes all modifications to the entity.
-   */
-  remove: function () { },
-
-  /**
-   * Called on each scene tick.
-   */
-  tick: function () {
-
-    
-   //let currentPosition = this.el.getAttribute('position');
-    //let currentPosition = new THREE.Vector3().copy(this.el.getAttribute('position'));
-    //let currentPosition = new THREE.Vector3().copy(this.el.object3D.position);
-    //console.log(dt);
-    //let currentRotation = this.el.getAttribute('rotation');
-
-    if (this.enabled) {
-
-
-      var el = this.el;
-      var rotationTmp = this.rotationTmp = this.rotationTmp || {x: 0, y: 0, z: 0};
-      var rotation = el.getAttribute('rotation');
-      rotationTmp.x = rotation.x + 0.1;
-      rotationTmp.y = rotation.y + 0.1;
-      rotationTmp.z = rotation.z + 0.1;
-      el.setAttribute('rotation', rotationTmp);
-
-
-      // if (this.posInterpolator.lastPosition.equals(currentPosition) === false) {
-      //   //if (this.posInterpolator.lastPosition != currentPosition) {
-      //   this.posInterpolator.inTick(currentPosition)
-      // }
-      // if (this.posInterpolator.active() && this.posInterpolator.testForLerp()) {
-      //   let newPos = this.posInterpolator.makeInterpolation();
-      //  this.el.setAttribute('position',this.posInterpolator.makeInterpolation())
-        
-      //   //this.el.object3D.position.copy(this.posInterpolator.makeInterpolation());
-      // }
-
-
-      // if (this.rotInterpolator.lastRotation != currentRotation) {
-      //   this.rotInterpolator.inTick(currentRotation)
-      // }
-      // if (this.rotInterpolator.active() && this.rotInterpolator.testForLerp()) {
-      //   this.el.object3D.rotation.copy(this.rotInterpolator.makeInterpolation());
-      // }
-
-    }
-
-    // if (this.positionInterpolator && this.positionInterpolator.active()) {
-    //   this.el.object3D.position.copy(this.positionInterpolator.get());
-    // }
-
-    // if (this.rotationInterpolator && this.rotationInterpolator.active()) {
-    //   this.el.object3D.rotation.copy(this.rotationInterpolator.get());
-    // }
-  },
-
-  /**
-   * Called when entity pauses.
-   * Use to stop or remove any dynamic or background behavior such as events.
-   */
-  pause: function () { },
-
-  /**
-   * Called when entity resumes.
-   * Use to continue or add any dynamic or background behavior such as events.
-   */
-  play: function () { },
-});

+ 0 - 246
public/vwf/model/aframe/addon/aframe-interpolation_from_vle.js

@@ -1,246 +0,0 @@
-
-/*
-The MIT License (MIT)
-Copyright (c) 2014-2018 Nikolai Suslov and the Krestianstvo.org project contributors. (https://github.com/NikolaySuslov/livecodingspace/blob/master/LICENSE.md)
-*/
-
-if (typeof AFRAME === 'undefined') {
-  throw new Error('Component attempted to register before AFRAME was available.');
-}
-
-AFRAME.registerComponent('interpolation', {
-  schema: {
-    enabled: { default: true },
-    deltaPos: { default: 0.001 },
-    deltaRot: { default: 0.1 }
-  },
-
-  init: function () {
-
-    this.driver = vwf.views["vwf/view/aframeComponent"];
-    this.tickTime = 0;
-
-  },
-
-  update: function (oldData) {
-
-    if (!this.interpolation) {
-      this.deltaPos = parseFloat(this.data.deltaPos);
-      this.deltaRot = THREE.Math.degToRad(parseFloat(this.data.deltaRot));
-
-      this.enabled = JSON.parse(this.data.enabled);
-      if (this.enabled) {
-      }
-    }
-  },
-
-  /**
-   * Called when a component is removed (e.g., via removeAttribute).
-   * Generally undoes all modifications to the entity.
-   */
-  remove: function () { },
-
-  /**
-   * Called on each scene tick.
-   */
-  tick: function (t, dt) {
-
-    if (!this.node) {
-      let interNode = Object.entries(this.driver.state.nodes).find(el => 
-        el[1].parentID == this.el.id && el[1].extendsID == "http://vwf.example.com/aframe/interpolation-component.vwf"
-      );
-      this.node = this.driver.nodes[interNode[0]];
-      this.nodeState = interNode[1];
-    }
-
-    if (this.enabled && this.node && this.node.interpolate) {
-
-
-      this.setInterpolatedTransformsNew(dt);
-      //this.applyViewTransformOverridesNew(timepassed);
-
-
-      //this.setInterpolatedTransforms(dt);
-      //this.restoreTransforms();
-
-    }
-
-  },
-
-  setInterpolatedTransformsNew: function(deltaTime) {
-
-
-    //deltaTime = Math.min(deltaTime,this.realTickDif)
-    this.tickTime += deltaTime || 0;
-
-
-    var hit = 0;
-    while (this.tickTime > 50) {
-        hit++;
-        this.tickTime -= 50;
-    }
-    var step = (this.tickTime) / (50);
-    if (hit === 1) {
-
-        this.resetInterpolationNew();
-
-    }
-
-    var lerpStep = Math.min(1, .2 * (deltaTime / 16.6)); //the slower the frames ,the more we have to move per frame. Should feel the same at 60 0r 20
-        this.interpolateOneNode(lerpStep,step);
-
-},
-
-  resetInterpolationNew: function() {
-
-     //don't do interpolation for static objects
-     //if (this.nodes[i].isStatic) continue;
-     if (this.node.lastTransformStep + 1 < vwf.time()) {
-          this.node.lastTickTransform = null;
-          this.node.lastFrameInterp = null;
-          this.node.thisTickTransform = null;
-     } else if (this.nodeState) {
-       if(!this.node.thisTickTransform)
-        this.node.thisTickTransform = new THREE.Vector3();
-
-         this.node.lastTickTransform = (new THREE.Vector3()).copy(this.node.thisTickTransform); //matset(this.nodes[i].lastTickTransform, this.nodes[i].thisTickTransform);
-         this.node.thisTickTransform = (new THREE.Vector3()).copy(this.nodeState.aframeObj.el.object3D.position)//this.nodeState.gettingProperty('position')); //matset(this.nodes[i].thisTickTransform, this.state.nodes[i].gettingProperty('transform'));
-     }
-
-  },
-
-  interpolateOneNode:function(lerpStep,step)
-  {
-
-       //don't do interpolation for static objects
-          //if (this.nodes[i].isStatic) return;
-
-        if(this.node.lastTickTransform && this.node.thisTickTransform) {
-
-
-          var interp = null;
-          var last = (new THREE.Vector3()).copy(this.node.lastTickTransform);
-          var now = (new THREE.Vector3()).copy(this.node.thisTickTransform);
-          if (last && now) {
-
-              interp = (new THREE.Vector3()).copy(last); //matset(interp, last);
-              interp = last.lerp(now, step);
-
-              //interp = this.matrixLerp(last, now, step, interp);
-
-              this.node.currentTickTransform = (new THREE.Vector3()).copy(this.nodeState.aframeObj.el.object3D.position)//this.nodeState.gettingProperty('position'))//matset(this.nodes[i].currentTickTransform, this.state.nodes[i].gettingProperty('transform'));
-
-                  if (this.node.lastFrameInterp)
-                     // interp = this.matrixLerp(this.node.lastFrameInterp, now, lerpStep, interp);
-                     interp = this.node.lastFrameInterp.lerp(now, lerpStep);
-
-                  //this.state.node.setTransformInternal(interp, false);
-                  this.el.object3D.position.set(interp.x, interp.y, interp.z);
-                  this.node.lastFrameInterp = (new THREE.Vector3()).copy(interp) //matset(this.nodes[i].lastFrameInterp || [], interp);
-          }
-
-        }
-
-  },
-
-
-  vecCmp: function (a, b, delta) {
-
-    let distance = a.distanceTo(b);
-    if (distance > delta) {
-      return false;
-    }
-
-    return true;
-  },
-
-  restoreTransforms: function () {
-
-    let r = new THREE.Euler();
-    let rot = r.copy(this.node.interpolate.rotation.selfTick);
-
-    if (rot && this.node.needTransformRestore) {
-      this.el.object3D.rotation.set(rot.x, rot.y, rot.z)
-      this.node.needTransformRestore = false;
-    }
-
-  },
-
-  setInterpolatedTransforms: function (deltaTime) {
-
-    var step = (this.node.tickTime) / (this.driver.realTickDif);
-    step = Math.min(step, 1);
-    deltaTime = Math.min(deltaTime, this.driver.realTickDif)
-    this.node.tickTime += deltaTime || 0;
-
-    this.interpolatePosition(step);
-    this.interpolateRotation(step);
-
-  },
-  radians: function (degrees) {
-    // return degrees * Math.PI / 180.0;
-    return THREE.Math.degToRad(degrees)
-  },
-
-  interpolateRotation: function (step) {
-
-    let last = this.node.interpolate.rotation.lastTick;
-    let now = this.node.interpolate.rotation.selfTick;
-
-    if (last && now) {
-
-      let comp = this.vecCmp(last.toVector3(), now.toVector3(), this.deltaRot);
-
-      if (!comp) {
-
-        // console.log('Last:', last, ' Now: ', now);
-
-        let lastV = (new THREE.Quaternion()).setFromEuler(new THREE.Euler(
-          (last.x),
-          (last.y),
-          (last.z), 'YXZ'
-        ));
-
-        let nowV = (new THREE.Quaternion()).setFromEuler(new THREE.Euler(
-          (now.x),
-          (now.y),
-          (now.z), 'YXZ'
-        ));
-
-        let q = new THREE.Quaternion();
-        let e = new THREE.Euler();
-
-        THREE.Quaternion.slerp(lastV, nowV, q, step || 0);
-        let interp = e.setFromQuaternion(q, 'YXZ');
-
-        this.el.object3D.rotation.set(interp.x, interp.y, interp.z);
-        this.node.needTransformRestore = true;
-      }
-    }
-  },
-
-  interpolatePosition: function (step) {
-
-    var last = this.node.interpolate.position.lastTick; //this.node.lastTickTransform;
-    var now = this.node.interpolate.position.selfTick; //Transform;
-
-    if (last && now) {
-
-      let comp = this.vecCmp(last, now, this.deltaPos);
-
-      if (!comp) {
-
-        var lastV = (new THREE.Vector3()).copy(last);
-        var nowV = (new THREE.Vector3()).copy(now);
-
-        var interp = lastV.lerp(nowV, step || 0);
-        //this.el.setAttribute('position',interp);
-
-        this.el.object3D.position.set(interp.x, interp.y, interp.z);
-        this.node.needTransformRestore = true;
-      }
-    }
-  },
-  pause: function () { },
-  play: function () { }
-});

File diff suppressed because it is too large
+ 0 - 0
public/vwf/model/aframe/addon/virtualgc/nipplejs.js


+ 44 - 0
public/vwf/model/aframe/addon/virtualgc/virtual-gamepad-controls.css

@@ -0,0 +1,44 @@
+.touchZone {
+  position: absolute;
+  height: 20vh;
+  bottom: 0;
+}
+
+.touchZone.left {
+  left: 0;
+  right: 55%;
+}
+
+.touchZone.right {
+  left: 55%;
+  right: 0;
+}
+
+.mockJoystickContainer {
+  position: absolute;
+  height: 0;
+  left: 0;
+  right: 0;
+  bottom: 10vh;
+  display: flex;
+  align-items: center;
+  justify-content: space-around;
+}
+
+.mockJoystick {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  width: 100px;
+  height: 100px;
+  background-color: rgba(255,255,255,0.5);
+  border-top-left-radius: 50%;
+  border-top-right-radius: 50%;
+  border-bottom-right-radius: 50%;
+  border-bottom-left-radius: 50%;
+}
+
+.mockJoystick.inner {
+  width: 50px;
+  height: 50px;
+}

+ 72 - 1
public/vwf/view/aframe.js

@@ -25,6 +25,20 @@ define(["module", "vwf/view"], function (module, view) {
             // this.lastRealTick = performance.now();
 
             this.state.appInitialized = false;
+            this.state.showMobileJoystick = function(){
+                let controlEl = document.querySelector('#avatarControl');
+                if(controlEl){
+                controlEl.setAttribute("virtual-gamepad-controls", {});
+                controlEl.addEventListener("move", setJoystickMoveInput);
+                }
+            }
+            this.state.hideMobileJoystick = function(){
+                let controlEl = document.querySelector('#avatarControl');
+                if(controlEl){
+                controlEl.removeAttribute("virtual-gamepad-controls");
+                controlEl.removeEventListener("move", setJoystickMoveInput);
+                }
+            }
 
             if (options === undefined) { options = {}; }
 
@@ -754,6 +768,56 @@ define(["module", "vwf/view"], function (module, view) {
     }
 
 
+     function getMovementVector(el, vel) {
+        var directionVector = new THREE.Vector3(0, 0, 0);
+        var rotationEuler = new THREE.Euler(0, 0, 0, 'YXZ');
+    
+        
+          var rotation = el.getAttribute('rotation');
+          var velocity = vel;
+    
+          directionVector.copy(velocity);
+          directionVector.multiplyScalar(0.05);
+    
+          // Absolute.
+          if (!rotation) { return directionVector; }
+    
+          //xRotation = this.data.fly ? rotation.x : 0;
+    
+          // Transform direction relative to heading.
+          rotationEuler.set(THREE.Math.degToRad(0), THREE.Math.degToRad(rotation.y), 0);
+          directionVector.applyEuler(rotationEuler);
+          return directionVector;
+      }
+
+    function setJoystickMoveInput (event) {
+        const axes = event.detail.axis;
+        //console.log(axes);
+        let el = document.querySelector('#avatarControl');
+        let position = new THREE.Vector3();
+        el.object3D.getWorldPosition(position);
+        let vel = new THREE.Vector3(axes[0], 0, -axes[1]);
+        el.object3D.position.add(getMovementVector(el,vel));
+    }
+
+    function setJoystickRotateY (event) {
+        const val = event.detail.value;
+        //console.log(val);
+        let el = document.querySelector('#avatarControl');
+        let rotation = el.object3D.rotation;
+        el.object3D.rotation.y += (-val)+rotation.y;
+        //el.object3D.rotation.set(rotation.x,-val+rotation.y, rotation.z)
+    }
+
+    function setJoystickRotateX (event) {
+        const val = event.detail.value;
+        //console.log(val);
+        let el = document.querySelector('#avatarControl');
+        let rotation = el.object3D.rotation;
+        el.object3D.rotation.x += val+rotation.x;
+        //el.object3D.rotation.set(val+rotation.x, rotation.y, rotation.z)
+    }
+
     function createAvatarControl(aScene, cb) {
 
         let avatarName = 'avatar-' + self.kernel.moniker();
@@ -776,7 +840,14 @@ define(["module", "vwf/view"], function (module, view) {
         controlEl.setAttribute('look-controls', { pointerLockEnabled: false });
         //controlEl.setAttribute('gamepad-controls', {'controller': 0});
 
-
+        if (AFRAME.utils.device.isMobile()) {
+            //self.state.showMobileJoystick()
+            controlEl.setAttribute("virtual-gamepad-controls", {});
+            controlEl.addEventListener("move", setJoystickMoveInput);
+        }
+        //controlEl.addEventListener("rotateY", setJoystickRotateY);
+        //controlEl.addEventListener("rotateX", setJoystickRotateX);
+        
 
         //controlEl.setAttribute('gearvr-controls',{});
 

+ 38 - 43
public/vwf/view/editor-new.js

@@ -488,53 +488,48 @@ define([
                         $type: "div",
                         class: "mdc-layout-grid__inner",
                         $components: [
-
                             {
                                 $type: "div",
                                 class: "mdc-layout-grid__cell mdc-layout-grid__cell--span-12",
                                 $components: [
-
-                                    self.widgets.buttonStroked(
-                                        {
-                                            "label": "Go forward",
-                                            "onclick": function (e) {
-
-                                                function getMovementVector(el) {
-                                                    var directionVector = new THREE.Vector3(0, 0, 0);
-                                                    var rotationEuler = new THREE.Euler(0, 0, 0, 'YXZ');
-
-                                                    var rotation = el.getAttribute('rotation');
-                                                    var velocity = new THREE.Vector3(0, 0, -0.5);
-                                                    var xRotation;
-
-                                                    directionVector.copy(velocity);
-                                                    directionVector.multiplyScalar(1.0);
-
-                                                    // Absolute.
-                                                    if (!rotation) { return directionVector; }
-
-                                                    xRotation = 0;
-
-                                                    // Transform direction relative to heading.
-                                                    rotationEuler.set(THREE.Math.degToRad(xRotation), THREE.Math.degToRad(rotation.y), 0);
-                                                    directionVector.applyEuler(rotationEuler);
-                                                    return directionVector;
-
-                                                }
-
-                                                let el = document.querySelector('#avatarControl');
-
-                                                let currentPosition = el.getAttribute('position');
-                                                let movementVector = getMovementVector(el);
-                                                let position = {};
-
-                                                position.x = currentPosition.x + movementVector.x;
-                                                position.y = currentPosition.y + movementVector.y;
-                                                position.z = currentPosition.z + movementVector.z;
-                                                el.setAttribute('position', position);
-
+                                    self.widgets.icontoggle({
+                                        'id': "mobile_joystick",
+                                        'label': 'visibility',
+                                        'on': JSON.stringify({ "content": "gamepad", "label": "Show" }),
+                                        'off': JSON.stringify({ "content": "gamepad", "label": "Hide" }),
+                                        'state': false,
+                                        'init': function () {
+                                            this._driver = vwf.views["vwf/view/aframe"];
+                                            this._comp = new mdc.iconToggle.MDCIconToggle(this);
+                                            mdc.iconToggle.MDCIconToggle.attachTo(this);
+                                            if (document.getElementsByClassName('touchZone').length > 0){
+                                                this._comp.on = true;
+                                                this.style.color = "#3e7e40";
+                                            } else {
+                                                this._comp.on = false;
+                                                this.style.color = "#333333";
                                             }
-                                        }),
+                                            
+
+                                                this.addEventListener('MDCIconToggle:change', (e) => {
+                                                    let chkAttr = e.detail.isOn;
+                                                    let driver = e.target._driver;
+                                                    if (chkAttr) {
+                                                    driver.state.showMobileJoystick();
+                                                    this.style.color = "#3e7e40";
+                                                   // this.classList.add('mdc-icon-toggle--enabled');
+                                                        console.log("on");
+    
+                                                    } else {
+                                                        driver.state.hideMobileJoystick();
+                                                        this.style.color = "#333333";
+                                                        //this.classList.add('mdc-icon-toggle--disabled');
+                                                        console.log("off")
+                                                    }
+
+                                                });
+                                        }
+                                    }),
                                     self.widgets.divider
 
                                 ]
@@ -1868,7 +1863,7 @@ define([
                                         }
                                     }),
                                     self.widgets.floatActionButton({
-                                        label: "priority_high",
+                                        label: "person",
                                         styleClass: "mdc-fab--mini",
                                         onclickfunc: function () {
                                             var nodeID = document.querySelector('#currentNode')._currentNode;

Some files were not shown because too many files changed in this diff