From 9e562b1242203c2679de4a2d8cb74a10a656a546 Mon Sep 17 00:00:00 2001
From: joni <johnfshaughnessy@gmail.com>
Date: Thu, 7 Dec 2017 14:51:12 -0800
Subject: [PATCH] Use latest input mapping.

---
 package.json                                  |   2 +-
 src/activators/pressedmove.js                 |  35 +++++
 src/behaviours/keyboard-dpad4.js              |   0
 src/behaviours/oculus-touch-joystick-dpad4.js |  31 +++++
 src/behaviours/vive-trackpad-dpad4.js         |  57 ++++++++
 src/components/character-controller.js        |  25 +---
 src/components/dpad-as-axes.js                |  89 ++++---------
 src/components/haptic-feedback.js             |  14 +-
 .../oculus-touch-controls-extended.js         |  62 ---------
 src/components/vive-controls-extended.js      | 105 ---------------
 src/components/wasd-to-analog2d.js            | 100 ++++++++++++++
 src/input-mappings.js                         | 124 ++++++++++++------
 src/room.js                                   |  30 +++--
 templates/room.hbs                            |  29 +---
 yarn.lock                                     |   6 +-
 15 files changed, 380 insertions(+), 329 deletions(-)
 create mode 100644 src/activators/pressedmove.js
 create mode 100644 src/behaviours/keyboard-dpad4.js
 create mode 100644 src/behaviours/oculus-touch-joystick-dpad4.js
 create mode 100644 src/behaviours/vive-trackpad-dpad4.js
 create mode 100644 src/components/wasd-to-analog2d.js

diff --git a/package.json b/package.json
index 8e9686b49..67a39e290 100644
--- a/package.json
+++ b/package.json
@@ -12,7 +12,7 @@
   "dependencies": {
     "aframe": "https://github.com/robertlong/aframe#fix-increasing-offset",
     "aframe-extras": "^3.12.4",
-    "aframe-input-mapping-component": "https://github.com/fernandojsg/aframe-input-mapping-component#6ebc38f",
+    "aframe-input-mapping-component": "https://github.com/fernandojsg/aframe-input-mapping-component",
     "aframe-teleport-controls": "https://github.com/netpro2k/aframe-teleport-controls#feature/teleport-origin",
     "extract-text-webpack-plugin": "^3.0.2",
     "material-design-lite": "^1.3.0",
diff --git a/src/activators/pressedmove.js b/src/activators/pressedmove.js
new file mode 100644
index 000000000..c03538318
--- /dev/null
+++ b/src/activators/pressedmove.js
@@ -0,0 +1,35 @@
+function PressedMove(el, button, onActivate) {
+  this.down = button + "down";
+  this.up = button + "up";
+  this.pressed = false;
+  this.onActivate = onActivate;
+  this.el = el;
+  this.onButtonDown = this.onButtonDown.bind(this);
+  this.onButtonUp = this.onButtonUp.bind(this);
+  this.onAxisMove = this.onAxisMove.bind(this);
+  el.addEventListener(this.down, this.onButtonDown);
+  el.addEventListener(this.up, this.onButtonUp);
+  el.addEventListener("axismove", this.onAxisMove);
+}
+
+PressedMove.prototype = {
+  onAxisMove: function(event) {
+    if (this.pressed) {
+      this.onActivate(event);
+    }
+  },
+  onButtonDown: function(event) {
+    this.pressed = true;
+  },
+  onButtonUp: function(event) {
+    this.pressed = false;
+  },
+
+  removeListeners: function() {
+    this.el.addEventListener(this.down, this.onButtonDown);
+    this.el.addEventListener(this.up, this.onButtonUp);
+    this.el.addEventListener("axismove", this.onAxisMove);
+  }
+};
+
+export { PressedMove };
diff --git a/src/behaviours/keyboard-dpad4.js b/src/behaviours/keyboard-dpad4.js
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/behaviours/oculus-touch-joystick-dpad4.js b/src/behaviours/oculus-touch-joystick-dpad4.js
new file mode 100644
index 000000000..ed85bca95
--- /dev/null
+++ b/src/behaviours/oculus-touch-joystick-dpad4.js
@@ -0,0 +1,31 @@
+import { angleTo4Direction, angleTo8Direction } from "../utils";
+
+// @TODO specify 4 or 8 direction
+function oculus_touch_joystick_dpad4(el, outputPrefix) {
+  this.angleToDirection = angleTo4Direction;
+  this.outputPrefix = outputPrefix;
+  this.centerRadius = 0.6;
+  this.previous = "none";
+  this.hapticIntensity = "low";
+  this.emitDPad4 = this.emitDPad4.bind(this);
+  el.addEventListener("axismove", this.emitDPad4);
+}
+
+oculus_touch_joystick_dpad4.prototype = {
+  emitDPad4: function(event) {
+    const x = event.detail.axis[0];
+    const y = event.detail.axis[1];
+    const inCenter =
+      Math.abs(x) < this.centerRadius && Math.abs(y) < this.centerRadius;
+    const current = inCenter
+      ? "center"
+      : this.angleToDirection(Math.atan2(x, -y));
+    if (current !== this.previous) {
+      this.previous = current;
+      event.target.emit(`${this.outputPrefix}_dpad4_${current}`);
+      event.target.emit("haptic_pulse", { intensity: this.hapticIntensity });
+    }
+  }
+};
+
+export { oculus_touch_joystick_dpad4 };
diff --git a/src/behaviours/vive-trackpad-dpad4.js b/src/behaviours/vive-trackpad-dpad4.js
new file mode 100644
index 000000000..4ef7364ab
--- /dev/null
+++ b/src/behaviours/vive-trackpad-dpad4.js
@@ -0,0 +1,57 @@
+import { angleTo4Direction, angleTo8Direction } from "../utils";
+
+function vive_trackpad_dpad4(el, outputPrefix) {
+  this.outputPrefix = outputPrefix;
+  this.lastDirection = "";
+  this.previous = "";
+  this.pressed = false;
+  this.emitDPad4 = this.emitDPad4.bind(this);
+  this.press = this.press.bind(this);
+  this.unpress = this.unpress.bind(this);
+  this.hapticIntensity = "low";
+  this.centerRadius = 0.6;
+  el.addEventListener("axismove", this.emitDPad4);
+  el.addEventListener("trackpaddown", this.press);
+  el.addEventListener("trackpadup", this.unpress);
+}
+
+vive_trackpad_dpad4.prototype = {
+  press: function(_) {
+    this.pressed = true;
+  },
+  unpress: function(_) {
+    this.pressed = false;
+  },
+  emitDPad4: function(event) {
+    const x = event.detail.axis[0];
+    const y = event.detail.axis[1];
+    const inCenter =
+      Math.abs(x) < this.centerRadius && Math.abs(y) < this.centerRadius;
+    const direction = inCenter
+      ? "center"
+      : angleTo4Direction(Math.atan2(x, -y));
+    const pressed = this.pressed ? "pressed_" : "";
+    const current = `${pressed + direction}`; // e.g. "pressed_north"
+
+    // Real axismove events are not perfectly [0,0]...
+    // This is a touchend event.
+    if (x === 0 && y === 0) {
+      event.target.emit(`${this.outputPrefix}_dpad4_${this.previous}_up`);
+      this.previous = ""; // Clear this because the user has lifted their finger.
+      return;
+    }
+
+    if (current === this.previous) {
+      return;
+    }
+
+    if (this.previous !== "") {
+      event.target.emit(`${this.outputPrefix}_dpad4_${this.previous}_up`);
+    }
+
+    event.target.emit(`${this.outputPrefix}_dpad4_${current}_down`);
+    this.previous = current;
+  }
+};
+
+export { vive_trackpad_dpad4 };
diff --git a/src/components/character-controller.js b/src/components/character-controller.js
index c941d4913..bd0a6617a 100644
--- a/src/components/character-controller.js
+++ b/src/components/character-controller.js
@@ -14,11 +14,9 @@ AFRAME.registerComponent("character-controller", {
   init: function() {
     this.velocity = new THREE.Vector3(0, 0, 0);
     this.accelerationInput = new THREE.Vector3(0, 0, 0);
-    this.boost = 1.0;
     this.pendingSnapRotationMatrix = new THREE.Matrix4();
     this.angularVelocity = 0; // Scalar value because we only allow rotation around Y
     this.setAccelerationInput = this.setAccelerationInput.bind(this);
-    this.onBoost = this.onBoost.bind(this);
     this.snapRotateLeft = this.snapRotateLeft.bind(this);
     this.snapRotateRight = this.snapRotateRight.bind(this);
     this.setAngularVelocity = this.setAngularVelocity.bind(this);
@@ -37,23 +35,16 @@ AFRAME.registerComponent("character-controller", {
     const eventSrc = this.el.sceneEl;
     eventSrc.addEventListener("move", this.setAccelerationInput);
     eventSrc.addEventListener("rotateY", this.setAngularVelocity);
-    eventSrc.addEventListener("action_snap_rotate_left", this.snapRotateLeft);
-    eventSrc.addEventListener("action_snap_rotate_right", this.snapRotateRight);
-    eventSrc.addEventListener("boost", this.onBoost);
+    eventSrc.addEventListener("snap_rotate_left", this.snapRotateLeft);
+    eventSrc.addEventListener("snap_rotate_right", this.snapRotateRight);
   },
 
   pause: function() {
     const eventSrc = this.el.sceneEl;
     eventSrc.removeEventListener("move", this.setAccelerationInput);
     eventSrc.removeEventListener("rotateY", this.setAngularVelocity);
-    eventSrc.removeEventListener(
-      "action_snap_rotate_left",
-      this.snapRotateLeft
-    );
-    eventSrc.removeEventListener(
-      "action_snap_rotate_right",
-      this.snapRotateRight
-    );
+    eventSrc.removeEventListener("snap_rotate_left", this.snapRotateLeft);
+    eventSrc.removeEventListener("snap_rotate_right", this.snapRotateRight);
   },
 
   setAccelerationInput: function(event) {
@@ -73,10 +64,6 @@ AFRAME.registerComponent("character-controller", {
     this.pendingSnapRotationMatrix.copy(this.rightRotationMatrix);
   },
 
-  onBoost: function(event) {
-    this.boost = event.detail;
-  },
-
   tick: (function() {
     const move = new THREE.Matrix4();
     const trans = new THREE.Matrix4();
@@ -181,8 +168,8 @@ AFRAME.registerComponent("character-controller", {
       velocity.z = 0;
     }
 
-    const dvx = data.groundAcc * dt * this.accelerationInput.x * this.boost;
-    const dvz = data.groundAcc * dt * -this.accelerationInput.z * this.boost;
+    const dvx = data.groundAcc * dt * this.accelerationInput.x;
+    const dvz = data.groundAcc * dt * -this.accelerationInput.z;
     velocity.x += dvx;
     velocity.z += dvz;
 
diff --git a/src/components/dpad-as-axes.js b/src/components/dpad-as-axes.js
index c0a232a55..1529552da 100644
--- a/src/components/dpad-as-axes.js
+++ b/src/components/dpad-as-axes.js
@@ -1,74 +1,41 @@
-AFRAME.registerComponent("dpad-as-axes", {
+/*
+function DPadAsAnalog2D (el, buttonName){
+  this.output = [0,0];
+  el.addEventListener("dpad")
+};
+
+ */
+
+AFRAME.registerComponent("wasd-to-analog2d", {
   schema: {
-    dpadActionPrefix: { default: "dpad" },
-    analog2dOutputAction: { default: "keyboard_dpad_axes" },
-    emitter: { default: "#left-hand" }
+    analog2dOutputAction: { default: "keyboard_dpad_axes" }
   },
 
   init: function() {
-    this.handlers = [];
-    this.directionsAndAxes = [
-      {
-        direction: "north",
-        axes: [0, 1]
-      },
-      {
-        direction: "northeast",
-        axes: [1, 1]
-      },
-      {
-        direction: "east",
-        axes: [1, 0]
-      },
-      {
-        direction: "southeast",
-        axes: [1, -1]
-      },
-      {
-        direction: "south",
-        axes: [0, -1]
-      },
-      {
-        direction: "southwest",
-        axes: [-1, -1]
-      },
-      {
-        direction: "west",
-        axes: [-1, 0]
-      },
-      {
-        direction: "northwest",
-        axes: [-1, 1]
-      }
-    ];
+    this.directionsAndAxes = {
+      north: [0, 1],
+      northeast: [1, 1],
+      east: [1, 0],
+      southeast: [1, -1],
+      south: [0, -1],
+      southwest: [-1, -1],
+      west: [-1, 0],
+      northwest: [-1, 1]
+    };
+    this.onWasd = this.onWasd.bind(this);
   },
 
   play: function() {
-    const inputAction = this.data.dpadActionPrefix;
-    for (var tuple of this.directionsAndAxes) {
-      this.handlers[tuple.direction] = this.emitAnalog2d(tuple.axes).bind(this);
-      this.el.addEventListener(
-        `${inputAction}_${tuple.direction}`,
-        this.handlers[tuple.direction]
-      );
-    }
+    this.addEventListener("wasd", onWasd);
   },
 
   pause: function() {
-    const inputAction = this.data.dpadActionPrefix;
-    for (var tuple of this.directionsAndAxes) {
-      this.el.removeEventListener(
-        `${inputAction}_${tuple.direction}`,
-        this.handlers[tuple.direction]
-      );
-    }
+    this.removeEventListener("wasd", onWasd);
   },
 
-  emitAnalog2d: function(axes) {
-    const outputAction = this.data.analog2dOutputAction;
-    const emitter = document.querySelector(this.data.emitter);
-    return function(event) {
-      emitter.emit(outputAction, { axis: [axes[0], axes[1]] });
-    };
-  }
+  onWasd: function(event) {
+    console.log(event);
+  },
+
+  emitAnalog2d: function(axes) {}
 });
diff --git a/src/components/haptic-feedback.js b/src/components/haptic-feedback.js
index 0ac0512d2..1be3f7921 100644
--- a/src/components/haptic-feedback.js
+++ b/src/components/haptic-feedback.js
@@ -11,7 +11,12 @@ AFRAME.registerComponent("haptic-feedback", {
 
   tryGetActuator() {
     var trackedControls = this.el.components["tracked-controls"];
-    if (trackedControls && trackedControls.controller) {
+    if (
+      trackedControls &&
+      trackedControls.controller &&
+      trackedControls.controller.hapticActuators &&
+      trackedControls.controller.hapticActuators.length > 0
+    ) {
       this.actuator = trackedControls.controller.hapticActuators[0];
     } else {
       setTimeout(this.tryGetActuator, 1000);
@@ -19,10 +24,10 @@ AFRAME.registerComponent("haptic-feedback", {
   },
 
   play: function() {
-    this.el.addEventListener(`${this.data.hand}_haptic_pulse`, this.pulse);
+    this.el.addEventListener(this.data.hapticEventName, this.pulse);
   },
   pause: function() {
-    this.el.removeEventListener(`${this.data.hand}_haptic_pulse`, this.pulse);
+    this.el.removeEventListener(this.data.hapticEventName, this.pulse);
   },
 
   pulse: function(event) {
@@ -31,14 +36,17 @@ AFRAME.registerComponent("haptic-feedback", {
       case "low": {
         strength = 0.07;
         duration = 12;
+        break;
       }
       case "medium": {
         strength = 0.2;
         duration = 12;
+        break;
       }
       case "high": {
         strength = 1;
         duration = 12;
+        break;
       }
       case "none": {
         return;
diff --git a/src/components/oculus-touch-controls-extended.js b/src/components/oculus-touch-controls-extended.js
index 519e33c34..e69de29bb 100644
--- a/src/components/oculus-touch-controls-extended.js
+++ b/src/components/oculus-touch-controls-extended.js
@@ -1,62 +0,0 @@
-import { angleTo4Direction, angleTo8Direction } from "../utils";
-
-AFRAME.registerComponent("oculus-touch-controls-extended", {
-  schema: {
-    hand: { default: "left" },
-    dpad_enabled: { default: false },
-    dpad_deadzone: { default: 0.85 },
-    dpad_livezone: { default: 0.35 },
-    dpad_directions: { default: 4 }, // one of [4, 8]
-    dpad_turbo: { default: false },
-    dpad_haptic_intensity: { default: "none" } // one of ["none", "low", "medium", "high"]
-  },
-
-  init: function() {
-    this.filterAxisMove = this.filterAxisMove.bind(this);
-    this.dpadCanFire = true;
-  },
-
-  play: function(old) {
-    this.el.addEventListener("axismove", this.filterAxisMove);
-  },
-
-  pause: function(old) {
-    this.el.removeEventListener("axismove", this.filterAxisMove);
-  },
-
-  filterAxisMove: function(event) {
-    const x = event.detail.axis[0];
-    const y = event.detail.axis[1];
-    const deadzone = this.data.dpad_deadzone;
-    const turbo = this.data.dpad_turbo;
-    const livezone = this.data.dpad_livezone;
-    const directions = this.data.dpad_directions;
-    const haptic_intensity = this.data.dpad_haptic_intensity;
-    const hand = this.data.hand;
-
-    event.target.emit(`${hand}_axismove`, {
-      axis: [event.detail.axis[0], -event.detail.axis[1]]
-    });
-
-    if (!this.data.dpad_enabled) {
-      return;
-    }
-    if (!turbo && Math.abs(x) < livezone && Math.abs(y) < livezone) {
-      this.dpadCanFire = true;
-    }
-    if (!this.dpadCanFire) return;
-
-    const deadzoneFilteredX = Math.abs(x) < deadzone ? 0 : x;
-    const deadzoneFilteredY = Math.abs(y) < deadzone ? 0 : y;
-    if (deadzoneFilteredX == 0 && deadzoneFilteredY == 0) return;
-    const angle = Math.atan2(deadzoneFilteredX, deadzoneFilteredY);
-    const direction =
-      directions === 4 ? angleTo4Direction(angle) : angleTo8Direction(angle);
-
-    event.target.emit(`${hand}_dpad_${direction}`);
-    event.target.emit(`${hand}_haptic_pulse`, { intensity: haptic_intensity });
-    if (!turbo) {
-      this.dpadCanFire = false;
-    }
-  }
-});
diff --git a/src/components/vive-controls-extended.js b/src/components/vive-controls-extended.js
index 7dfde12a0..e69de29bb 100644
--- a/src/components/vive-controls-extended.js
+++ b/src/components/vive-controls-extended.js
@@ -1,105 +0,0 @@
-import { angleTo4Direction, angleTo8Direction } from "../utils";
-
-AFRAME.registerComponent("vive-controls-extended", {
-  schema: {
-    hand: { type: "string" },
-    dpad_enabled: { default: false },
-    dpad_livezone: { default: 0.3 },
-    dpad_deadzone: { default: 0.7 },
-    dpad_directions: { default: 4 },
-    dpad_turbo: { default: false },
-    dpad_pressed_turbo: { default: false },
-    center_zone: { default: 0.3 },
-    dpad_haptic_intensity: { default: "none" },
-    dpad_pressed_haptic_intensity: { default: "none" }
-  },
-
-  init: function() {
-    this.dpadCanFire = true;
-    this.dpadPressedCanFire = true;
-    this.trackpadPressed = false;
-    this.onAxisMove = this.onAxisMove.bind(this);
-    this.lastSeenAxes = [0, 0];
-    this.onButtonChanged = this.onButtonChanged.bind(this);
-  },
-
-  play: function() {
-    this.el.addEventListener("axismove", this.onAxisMove);
-    this.el.addEventListener("trackpadchanged", this.onButtonChanged);
-  },
-
-  pause: function() {
-    this.el.removeEventListener("axismove", this.onAxisMove);
-    this.el.removeEventListener("trackpadchanged", this.onButtonChanged);
-  },
-
-  onAxisMove: function(event) {
-    const x = event.detail.axis[0];
-    const y = event.detail.axis[1];
-    this.lastSeenAxes = [x, y];
-    const hand = this.data.hand;
-    const pressed = this.trackpadPressed ? "_pressed" : "";
-    const axisMoveEvent = `${hand}_trackpad${pressed}_axismove`;
-    this.el.emit(axisMoveEvent, {
-      axis: [x, y]
-    });
-
-    const deadzone = this.data.dpad_deadzone;
-    const turbo = this.data.dpad_turbo;
-    const pressedTurbo = this.data.dpad_pressed_turbo;
-    const livezone = this.data.dpad_livezone;
-    const directions = this.data.dpad_directions;
-    const hapticIntensity = this.data.dpad_haptic_intensity;
-    const pressedHapticIntensity = this.data.dpad_pressed_haptic_intensity;
-
-    if (!turbo && Math.abs(x) < livezone && Math.abs(y) < livezone) {
-      this.dpadCanFire = true;
-    }
-
-    event.target.emit(`${hand}_haptic_pulse`, {
-      intensity: this.trackpadPressed ? pressedHapticIntensity : hapticIntensity
-    });
-
-    const deadzoneFilteredX = Math.abs(x) < deadzone ? 0 : x;
-    const deadzoneFilteredY = Math.abs(y) < deadzone ? 0 : y;
-    if (deadzoneFilteredX == 0 && deadzoneFilteredY == 0) return;
-    const angle = Math.atan2(deadzoneFilteredX, deadzoneFilteredY);
-    const direction =
-      directions === 4 ? angleTo4Direction(angle) : angleTo8Direction(angle);
-
-    if (!this.trackpadPressed && !this.dpadCanFire) {
-      return;
-    }
-    if (this.trackpadPressed && !this.dpadPressedCanFire) {
-      return;
-    }
-    const dpadEvent = `${hand}_trackpad_dpad${pressed}_${direction}`;
-    event.target.emit(dpadEvent);
-
-    if (!this.trackpadPressed && !turbo) {
-      this.dpadCanFire = false;
-    } else if (this.trackpadPressed && !pressedTurbo) {
-      this.dpadPressedCanFire = false;
-    }
-  },
-
-  onButtonChanged: function(event) {
-    const x = this.lastSeenAxes[0];
-    const y = this.lastSeenAxes[1];
-    const hand = this.data.hand;
-    const centerZone = this.data.center_zone;
-    const down = !this.trackpadPressed && event.detail.pressed;
-    const up = this.trackpadPressed && !event.detail.pressed;
-    const center =
-      Math.abs(x) < centerZone && Math.abs(y) < centerZone ? "_center" : "";
-    const eventName = `${hand}_trackpad${center}${up ? "_up" : ""}${down
-      ? "_down"
-      : ""}`;
-    this.el.emit(eventName);
-    if (up) {
-      this.dpadPressedCanFire = true;
-    }
-
-    this.trackpadPressed = event.detail.pressed;
-  }
-});
diff --git a/src/components/wasd-to-analog2d.js b/src/components/wasd-to-analog2d.js
new file mode 100644
index 000000000..1b83f8f63
--- /dev/null
+++ b/src/components/wasd-to-analog2d.js
@@ -0,0 +1,100 @@
+AFRAME.registerComponent("wasd-to-analog2d", {
+  schema: {
+    analog2dOutputAction: { default: "wasd_analog2d" }
+  },
+
+  init: function() {
+    this.output = [0, 0];
+    this.vectors = {
+      w: [0, 1],
+      a: [-1, 0],
+      s: [0, -1],
+      d: [1, 0]
+    };
+    this.onWasd = this.onWasd.bind(this);
+    this.keys = {};
+    this.move = this.move.bind(this);
+  },
+
+  play: function() {
+    const eventNames = [
+      "w_down",
+      "w_up",
+      "a_down",
+      "a_up",
+      "s_down",
+      "s_up",
+      "d_down",
+      "d_up"
+    ];
+    for (var name of eventNames) {
+      this.el.sceneEl.addEventListener(name, this.onWasd);
+    }
+    // I listen to events that this component generates instead of emitting "move"
+    // directly because ideally this would live as an input mapping, but the events
+    // generated by this component won't actually get mapped.
+    this.el.sceneEl.addEventListener(this.data.analog2dOutputAction, this.move);
+  },
+
+  move: function(event) {
+    this.el.emit("move", { axis: event.detail.axis });
+  },
+
+  pause: function() {
+    this.el.sceneEl.removeEventListener("wasd", this.onWasd);
+    this.el.sceneEl.removeEventListener(
+      this.data.analog2dOutputAction,
+      this.move
+    );
+  },
+
+  onWasd: function(event) {
+    const keyEvent = event.type;
+    const down = keyEvent.indexOf("down") !== -1;
+    const key = keyEvent[0].toLowerCase();
+    this.keys[key] = down;
+  },
+
+  tick: function(t, dt) {
+    this.target = [0, 0];
+
+    for (var key in this.keys) {
+      if (this.keys[key] && this.vectors[key]) {
+        this.target = [
+          this.target[0] + this.vectors[key][0],
+          this.target[1] + this.vectors[key][1]
+        ];
+      }
+    }
+
+    const targetMagnitude = Math.sqrt(
+      this.target[0] * this.target[0] + this.target[1] * this.target[1]
+    );
+    if (targetMagnitude !== 0) {
+      this.target[0] = this.target[0] / targetMagnitude;
+      this.target[1] = this.target[1] / targetMagnitude;
+    }
+
+    const epsilon = 0.01;
+    if (
+      Math.abs(this.output[0]) < epsilon &&
+      Math.abs(this.output[1]) < epsilon &&
+      this.target[0] === 0 &&
+      this.target[1] === 0
+    ) {
+      return; // Staying at [0,0] doesn't require new events.
+    }
+
+    const easeInSpeed = 0.25;
+    this.output = [
+      this.output[0] + easeInSpeed * (this.target[0] - this.output[0]),
+      this.output[1] + easeInSpeed * (this.target[1] - this.output[1])
+    ];
+
+    if (this.output !== [0, 0]) {
+      this.el.emit(this.data.analog2dOutputAction, {
+        axis: this.output
+      });
+    }
+  }
+});
diff --git a/src/input-mappings.js b/src/input-mappings.js
index 3c8610c60..27414e45b 100644
--- a/src/input-mappings.js
+++ b/src/input-mappings.js
@@ -1,43 +1,89 @@
-export default function registerInputMappings() {
-  AFRAME.registerInputMappings({
-    mappings: {
-      default: {
-        common: {
-          keyboard_dpad_axes: "move" // This won't get received by the character controller if it is in the "keyboard" section, but this dpad is powered by wasd.
-        },
-        "vive-controls": {
-          menudown: "action_mute",
-          left_trackpad_pressed_axismove: "move",
-          right_trackpad_dpad_pressed_west: "action_snap_rotate_left",
-          right_trackpad_dpad_pressed_east: "action_snap_rotate_right",
-          right_trackpad_center_down: "action_teleport_down", // @TODO once once #30 lands in aframe-teleport controls this just maps to "action_teleport_aim"
-          right_trackpad_center_up: "action_teleport_up", // @TODO once once #30 lands in aframe-teleport controls this just maps to "action_teleport_teleport"
-          right_trackpad_up: "action_teleport_up"
-        },
-        "oculus-touch-controls": {
-          xbuttondown: "action_mute",
-          gripdown: "middle_ring_pinky_down",
-          gripup: "middle_ring_pinky_up",
-          thumbsticktouchstart: "thumb_down",
-          thumbsticktouchend: "thumb_up",
-          triggerdown: "index_down",
-          triggerup: "index_up",
-          left_axismove: "move",
-          right_dpad_east: "action_snap_rotate_right",
-          right_dpad_west: "action_snap_rotate_left",
-          abuttondown: "action_teleport_down",
-          abuttonup: "action_teleport_up"
+const inGameActions = {
+  // Define action sets here.
+  // An action set separates "driving" controls from "menu" controls.
+  // Only one action set is active at a time.
+  default: {
+    move: { label: "Move" },
+    snap_rotate_left: { label: "Snap Rotate Left" },
+    snap_rotate_right: { label: "Snap Rotate Right" },
+    action_mute: { label: "Mute" },
+    action_teleport_down: { label: "Teleport Aim" },
+    action_teleport_up: { label: "Teleport" },
+    action_share_screen: { label: "Share Screen" }
+  }
+};
+
+const config = {
+  behaviours: {
+    default: {
+      "oculus-touch-controls": {
+        joystick: "oculus_touch_joystick_dpad4"
+      },
+      "vive-controls": {
+        trackpad: "vive_trackpad_dpad4"
+      }
+    }
+  },
+  mappings: {
+    default: {
+      "vive-controls": {
+        menudown: "action_mute",
+        "trackpad.pressedmove": { left: "move" },
+        trackpad_dpad4_pressed_west_down: { right: "snap_rotate_left" },
+        trackpad_dpad4_pressed_east_down: { right: "snap_rotate_right" },
+        trackpad_dpad4_pressed_center_down: { right: "action_teleport_down" },
+        trackpadup: { right: "action_teleport_up" }
+      },
+      "oculus-touch-controls": {
+        joystick_dpad4_west: {
+          right: "snap_rotate_left"
         },
-        daydream: {
-          menudown: "action_mute"
+        joystick_dpad4_east: {
+          right: "snap_rotate_right"
         },
-        keyboard: {
-          m_press: "action_mute",
-          q_press: "action_snap_rotate_left",
-          e_press: "action_snap_rotate_right",
-          v_press: "action_share_screen"
-        }
+        xbuttondown: "action_mute",
+        gripdown: "middle_ring_pinky_down",
+        gripup: "middle_ring_pinky_up",
+        thumbsticktouchstart: "thumb_down",
+        thumbsticktouchend: "thumb_up",
+        triggerdown: "index_down",
+        triggerup: "index_up",
+        left_axismove: "move",
+        right_dpad_east: "snap_rotate_right",
+        right_dpad_west: "snap_rotate_left",
+        abuttondown: "action_teleport_down",
+        abuttonup: "action_teleport_up"
+      },
+      daydream: {
+        menudown: "action_mute"
+      },
+      keyboard: {
+        m_press: "action_mute",
+        q_press: "snap_rotate_left",
+        e_press: "snap_rotate_right",
+        v_press: "action_share_screen",
+
+        // We can't create a keyboard behaviour with AFIM yet,
+        // so these will get captured by wasd-to-analog2d
+        w_down: "w_down",
+        w_up: "w_up",
+        a_down: "a_down",
+        a_up: "a_up",
+        s_down: "s_down",
+        s_up: "s_up",
+        d_down: "d_down",
+        d_up: "d_up",
+        W_down: "w_down",
+        W_up: "w_up",
+        A_down: "a_down",
+        A_up: "a_up",
+        S_down: "s_down",
+        S_up: "s_up",
+        D_down: "d_down",
+        D_up: "d_up"
       }
     }
-  });
-}
+  }
+};
+
+export { inGameActions, config };
diff --git a/src/room.js b/src/room.js
index b660adffe..a1caf6dda 100644
--- a/src/room.js
+++ b/src/room.js
@@ -10,7 +10,12 @@ import "aframe-input-mapping-component";
 import animationMixer from "aframe-extras/src/loaders/animation-mixer";
 AFRAME.registerComponent("animation-mixer", animationMixer);
 
-import "./components/axis-dpad";
+import { vive_trackpad_dpad4 } from "./behaviours/vive-trackpad-dpad4";
+import { oculus_touch_joystick_dpad4 } from "./behaviours/oculus-touch-joystick-dpad4";
+import { PressedMove } from "./activators/pressedmove";
+import "./behaviours/keyboard-dpad4";
+import "./components/wasd-to-analog2d"; //Might be a behaviour or activator in the future
+
 import "./components/mute-mic";
 import "./components/audio-feedback";
 import "./components/nametag-transform";
@@ -19,11 +24,6 @@ import "./components/virtual-gamepad-controls";
 import "./components/body-controller";
 import "./components/hand-controls2";
 import "./components/character-controller";
-import "./components/split-axis-events";
-import "./components/dpad-as-axes";
-import "./components/keyboard-dpad";
-import "./components/oculus-touch-controls-extended";
-import "./components/vive-controls-extended";
 import "./components/haptic-feedback";
 import "./components/networked-video-player";
 import "./components/offset-relative-to";
@@ -31,12 +31,20 @@ import "./components/cached-gltf-model";
 import "./components/spawn-controller";
 import "./systems/personal-space-bubble";
 
-import registerNetworkScheams from "./network-schemas";
-import registerInputMappings from "./input-mappings";
 import { promptForName, getCookie, parseJwt } from "./utils";
-
-registerNetworkScheams();
-registerInputMappings();
+import registerNetworkSchemas from "./network-schemas";
+import { inGameActions, config } from "./input-mappings";
+
+AFRAME.registerInputBehaviour("vive_trackpad_dpad4", vive_trackpad_dpad4);
+AFRAME.registerInputBehaviour(
+  "oculus_touch_joystick_dpad4",
+  oculus_touch_joystick_dpad4
+);
+AFRAME.registerInputActivator("pressedmove", PressedMove);
+AFRAME.registerInputActions(inGameActions, "default");
+AFRAME.registerInputMappings(config);
+
+registerNetworkSchemas();
 
 function shareScreen() {
   const track = NAF.connection.adapter.localMediaStream.getVideoTracks()[0];
diff --git a/templates/room.hbs b/templates/room.hbs
index 5234c5de1..23938f7f8 100644
--- a/templates/room.hbs
+++ b/templates/room.hbs
@@ -14,7 +14,7 @@
     <script src="{{asset "manifest.js" }}"></script>
     <script src="{{asset "room-vendor.js" }}"></script>
     <script src="{{asset "room.js" }}"></script>
-    
+    <meta charset="UTF-8">
     <style>
         .a-enter-vr {
             top: 90px;
@@ -121,6 +121,7 @@
             id="player-rig"
             networked
             spawn-controller="radius: 4;"
+            wasd-to-analog2d
             character-controller="pivot: #head"
         >
             <a-sphere scale="0.1 0.1 0.1"></a-sphere>
@@ -147,20 +148,7 @@
                 id="left-hand"
                 hand-controls2="left"
                 tracked-controls
-                haptic-feedback="hapticEventName: left_haptic_pulse;"
-                vive-controls-extended="
-                    hand: left;
-                    dpad_turbo: true;
-                    dpad_pressed_haptic_intensity: low"
-                oculus-touch-controls-extended="
-                    dpad_enabled: true;
-                    dpad_directions: 8;
-                    dpad_turbo: true;
-                    dpad_haptic_intensity: none"
-                dpad-as-axes="
-                    dpadActionPrefix:left_dpad;
-                    analog2dOutputAction:left_dpad_axes;
-                    emitter: #left-hand"
+                haptic-feedback
                 teleport-controls="cameraRig: #player-rig; teleportOrigin: #head; button: action_teleport_"
                 networked="template: #left-hand-template;"
             >
@@ -177,16 +165,7 @@
             <a-entity
                 id="right-hand"
                 hand-controls2="right"
-                haptic-feedback="hapticEventName: right_haptic_pulse;"
-                vive-controls-extended="
-                    hand:right;
-                    dpad_enabled: true;
-                    dpad_livezone: 1.0;
-                    dpad_deadzone: 0.6;
-                    dpad_pressed_haptic_intensity: low;"
-                oculus-touch-controls-extended="
-                    hand:right;
-                    dpad_enabled: true;"
+                haptic-feedback
                 teleport-controls="cameraRig: #player-rig;
                                     teleportOrigin: #head;
                                     hitEntity: #telepor-indicator;
diff --git a/yarn.lock b/yarn.lock
index b7dcd3fb1..60f4a772c 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -55,9 +55,9 @@ aframe-extras@^3.12.4:
     aframe-physics-system "^1.4.3"
     three-pathfinding "^0.2.2"
 
-"aframe-input-mapping-component@https://github.com/fernandojsg/aframe-input-mapping-component#6ebc38f":
-  version "0.1.1"
-  resolved "https://github.com/fernandojsg/aframe-input-mapping-component#6ebc38f0e871e8ab66673aef5cd11f6ce052076c"
+"aframe-input-mapping-component@https://github.com/fernandojsg/aframe-input-mapping-component":
+  version "0.1.2"
+  resolved "https://github.com/fernandojsg/aframe-input-mapping-component#d3921f818131dcbc73eb68c8cdb3d0255a022c2d"
 
 aframe-lerp-component@^1.1.0:
   version "1.1.0"
-- 
GitLab