From e793f546bd3b95ed1b6d549beb31c340df34c0ba Mon Sep 17 00:00:00 2001
From: netpro2k <netpro2k@gmail.com>
Date: Mon, 2 Oct 2017 16:09:38 -0700
Subject: [PATCH] Add ratchet turning, this removes the need for rig-selctor
 for now.

---
 public/index.html               | 70 +++++++++++-----------------
 src/components/axis-dpad.js     | 81 +++++++++++++++++++++++++++++++++
 src/components/snap-rotation.js | 62 +++++++++++++++++++++++++
 src/index.js                    |  7 ++-
 4 files changed, 175 insertions(+), 45 deletions(-)
 create mode 100644 src/components/axis-dpad.js
 create mode 100644 src/components/snap-rotation.js

diff --git a/public/index.html b/public/index.html
index 55de75fee..6f23a9b0e 100644
--- a/public/index.html
+++ b/public/index.html
@@ -24,53 +24,37 @@
       <script id="hand-template" type="text/html">
         <a-box class="hand" scale="0.2 0.1 0.3"></a-box>
       </script>
-
-      <script id="vive-rig" type="text/html">
-        <a-entity id="player-rig" networked wasd-controls>
-          <a-entity id="head" camera="userHeight: 1.6" look-controls networked="template:#head-template;showLocalTemplate:false;"></a-entity>
-
-          <a-entity id="left-hand" vive-controls="hand: left" teleport-controls="cameraRig: #player-rig" networked="template:#hand-template;showLocalTemplate:false;"></a-entity>
-          <a-entity id="right-hand" vive-controls="hand: right" teleport-controls="cameraRig: #player-rig" networked="template:#hand-template;showLocalTemplate:false;"></a-entity>
-        </a-entity>
-      </script>
-
-      <script id="oculus-rig" type="text/html">
-        <a-entity id="player-rig" networked wasd-controls>
-          <a-entity id="head" camera="userHeight: 1.6" look-controls networked="template:#head-template;showLocalTemplate:false;"></a-entity>
-
-          <a-entity oculus-touch-controls="left" teleport-controls="cameraRig:#player-rig;button:trigger;" networked="template:#hand-template;showLocalTemplate:false;"></a-entity>
-          <a-entity oculus-touch-controls="right" teleport-controls="cameraRig:#player-rig;button:trigger;" networked="template:#hand-template;showLocalTemplate:false;"></a-entity>
-        </a-entity>
-      </script>
-
-      <script id="daydream-rig" type="text/html">
-        <a-entity id="player-rig" networked wasd-controls>
-          <a-entity id="head" camera="userHeight: 1.6" look-controls networked="template:#head-template;showLocalTemplate:false;"></a-entity>
-
-          <a-entity daydream-controls networked="template:#hand-template;showLocalTemplate:true;"></a-entity>
-        </a-entity>
-      </script>
-
-      <script id="gearvr-rig" type="text/html">
-        <a-entity id="player-rig" networked wasd-controls>
-          <a-entity id="head" camera="userHeight: 1.6" look-controls networked="template:#head-template;showLocalTemplate:false;"></a-entity>
-
-          <a-entity gearvr-controls networked="template:#hand-template;showLocalTemplate:true;"></a-entity>
-        </a-entity>
-      </script>
-
-      <script id="dolly-rig" type="text/html">
-        <a-entity id="player-rig" networked wasd-controls>
-          <a-entity id="head" camera="userHeight: 1.6" look-controls networked="template:#head-template;showLocalTemplate:false;"></a-entity>
-        </a-entity>
-      </script>
     </a-assets>
 
-    <a-entity rig-selector="vive:#vive-rig;oculus:oculus-rig;daydream:#daydream-rig;desktop:#dolly-rig;mobile:dolly-rig;"></a-entity>
+    <a-entity id="player-rig" networked wasd-controls snap-rotation="leftEventSrc: #right-hand; rightEventSrc: #right-hand">
+        <a-entity
+            id="head"
+            camera="userHeight: 1.6"
+            look-controls
+            networked="template:#head-template;showLocalTemplate:false;"></a-entity>
+
+        <a-entity
+            id="left-hand"
+            vive-controls="hand: left"
+            oculus-touch-controls="left"
+            axis-dpad="centerZone: 1"
+            teleport-controls="cameraRig: #player-rig; button: dpadcenter"
+            networked="template:#hand-template;showLocalTemplate:false;"></a-entity>
+
+        <a-entity
+            id="right-hand"
+            vive-controls="hand: right"
+            oculus-touch-controls="right"
+            daydream-controls
+            gearvr-controls
+            axis-dpad
+            teleport-controls="cameraRig: #player-rig; button: dpadcenter"
+            networked="template:#hand-template;showLocalTemplate:false;"></a-entity>
+    </a-entity>
 
     <a-entity id="ground" position="0 0 0"
-        geometry="primitive: plane; width: 10000; height: 10000;" rotation="-90 0 0"
-        material="shader: flat; src: #grid; repeat: 10000 10000;"></a-entity>
+        geometry="primitive: plane; width: 100; height: 100;" rotation="-90 0 0"
+        material="shader: flat; src: #grid; repeat: 100 100;"></a-entity>
     <a-sky src="#sky" rotation="0 -90 0"></a-sky>
   </a-scene>
 </body>
diff --git a/src/components/axis-dpad.js b/src/components/axis-dpad.js
new file mode 100644
index 000000000..90fb29176
--- /dev/null
+++ b/src/components/axis-dpad.js
@@ -0,0 +1,81 @@
+/**
+ * @fileOverview
+ * Treats a pair of axes and a button as a dpad
+ * This is useful for Vive trackpad and Oculus Touch thumbstick
+ *
+ * @name axis-dpad.js
+ * @TODO allow use of thumbstick without press
+ * @TODO make axes configurable
+ */
+
+const angleToDirection = function(angle) {
+  angle = (angle * THREE.Math.RAD2DEG + 180 + 45) % 360;
+  if (angle > 0 && angle < 90) {
+    return "down";
+  } else if (angle >= 90 && angle < 180) {
+    return "left";
+  } else if (angle >= 180 && angle < 270) {
+    return "up";
+  } else {
+    return "right";
+  }
+};
+
+AFRAME.registerComponent("axis-dpad", {
+  schema: {
+    centerZone: { default: 0.5 },
+    moveEvents: { default: ["axismove"] },
+    downEvents: { default: ["trackpaddown", "thumbstickdown"] },
+    upEvents: { default: ["trackpadup", "thumbstickup"] }
+  },
+
+  init: function() {
+    this.onAxisMove = this.onAxisMove.bind(this);
+    this.onButtonPressed = this.onButtonPressed.bind(this);
+    this.lastPos = [0, 0];
+  },
+
+  play: function() {
+    const { moveEvents, downEvents, upEvents } = this.data;
+    moveEvents.forEach(moveEvent => {
+      this.el.addEventListener(moveEvent, this.onAxisMove);
+    });
+    downEvents.concat(upEvents).forEach(eventName => {
+      this.el.addEventListener(eventName, this.onButtonPressed);
+    });
+  },
+
+  pause: function() {
+    const { moveEvents, downEvents, upEvents } = this.data;
+    moveEvents.forEach(moveEvent => {
+      this.el.removeEventListener(moveEvent, this.onAxisMove);
+    });
+    downEvents.concat(upEvents).forEach(eventName => {
+      this.el.removeEventListener(eventName, this.onButtonPressed);
+    });
+  },
+
+  onAxisMove: function(e) {
+    this.lastPos = e.detail.axis;
+  },
+
+  onButtonPressed: function(e) {
+    const [x, y] = this.lastPos;
+    const { upEvents, centerZone } = this.data;
+    const state = upEvents.includes(e.type) ? "up" : "down";
+    const direction =
+      state === "up" && this.lastDirection // Always trigger the up event for the last down event
+        ? this.lastDirection
+        : x * x + y * y < centerZone * centerZone // If within center zone angle does not matter
+          ? "center"
+          : angleToDirection(Math.atan2(x, y));
+
+    this.el.emit(`dpad${direction}${state}`);
+
+    if (state === "down") {
+      this.lastDirection = direction;
+    } else {
+      delete this.lastDirection;
+    }
+  }
+});
diff --git a/src/components/snap-rotation.js b/src/components/snap-rotation.js
new file mode 100644
index 000000000..5836a5596
--- /dev/null
+++ b/src/components/snap-rotation.js
@@ -0,0 +1,62 @@
+/**
+ * @fileOverview
+ * Rotate an entity in fixed increments based on events or keyboard input
+ * @name snap-rotation.js
+ * @TODO pull keyboard input out into a component that just emits events
+ * @TODO allow specifying multiple events and sources
+ */
+
+AFRAME.registerComponent("snap-rotation", {
+  schema: {
+    rotationAxis: { type: "vec3", default: { x: 0, y: 1, z: 0 } },
+    rotationDegres: { default: 45 },
+
+    leftKey: { default: "q" },
+    leftEvent: { default: "dpadleftdown" },
+    leftEventSrc: { type: "selector" },
+
+    rightKey: { default: "e" },
+    rightEvent: { default: "dpadrightdown" },
+    rightEventSrc: { type: "selector" }
+  },
+
+  init: function() {
+    this.onButtonPressed = this.onButtonPressed.bind(this);
+  },
+
+  play: function() {
+    const { leftEventSrc, leftEvent, rightEventSrc, rightEvent } = this.data;
+    window.addEventListener("keypress", this.onButtonPressed);
+    rightEventSrc &&
+      rightEventSrc.addEventListener(rightEvent, this.onButtonPressed);
+    leftEventSrc &&
+      leftEventSrc.addEventListener(leftEvent, this.onButtonPressed);
+  },
+
+  pause: function() {
+    const { leftEventSrc, leftEvent, rightEventSrc, rightEvent } = this.data;
+    window.removeEventListener("keypress", this.onButtonPRessed);
+    rightEventSrc &&
+      rightEventSrc.removeEventListener(rightEvent, this.onButtonPressed);
+    leftEventSrc &&
+      leftEventSrc.removeEventListener(leftEvent, this.onButtonPressed);
+  },
+
+  onButtonPressed: function(e) {
+    const {
+      rotationAxis,
+      rotationDegres,
+      leftKey,
+      leftEvent,
+      rightKey,
+      rightEvent
+    } = this.data;
+    const obj = this.el.object3D;
+
+    if (e.type === leftEvent || (leftKey && e.key === leftKey)) {
+      obj.rotateOnAxis(rotationAxis, rotationDegres * THREE.Math.DEG2RAD);
+    } else if (e.type === rightEvent || (rightKey && e.key === rightKey)) {
+      obj.rotateOnAxis(rotationAxis, -rotationDegres * THREE.Math.DEG2RAD);
+    }
+  }
+});
diff --git a/src/index.js b/src/index.js
index e379fd736..85a49209a 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,3 +1,6 @@
 require("networked-aframe");
-require("./components/rig-selector");
-require('aframe-teleport-controls');
+require("aframe-teleport-controls");
+
+// require("./components/rig-selector");
+require("./components/axis-dpad");
+require("./components/snap-rotation");
-- 
GitLab