diff --git a/public/index.html b/public/index.html index 55de75fee265c1f5cd022d42f134d4d12b12986b..6f23a9b0e66ddb637564801e81d092255a482545 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 0000000000000000000000000000000000000000..90fb29176013384f42d3c2da02631a1f0a88c634 --- /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 0000000000000000000000000000000000000000..5836a55965601d1aa05f700196c6ad95e75b2e28 --- /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 e379fd736749cf5cf54cff18465d301eeaba1106..85a49209aa9b82aa5ba6cbf2b2366d9e119367ba 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");