diff --git a/.travis.yml b/.travis.yml index 73a40d0761a7b442cd2435b7ac37a59f5c13a875..890fcfd3ac3ca6f4d4ac9507586110c817dc3edf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,4 +5,6 @@ before_install: - curl -o- -L https://yarnpkg.com/install.sh | bash -s -- --version 1.5.1 - export PATH="$HOME/.yarn/bin:$PATH" install: yarn -script: yarn lint +script: +- yarn lint +- ./scripts/check-yarn-lock.sh diff --git a/package.json b/package.json index 173823aca710f4d58bcfedb3787de67b2d025918..ad584f83bcf0827ec6c4f2914bf9e38abc20234b 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,8 @@ "aframe-extras": "^4.0.0", "aframe-input-mapping-component": "https://github.com/johnshaughnessy/aframe-input-mapping-component#feature/map-to-array", "aframe-physics-extras": "https://github.com/infinitelee/aframe-physics-extras#fix/physics-collider-crash", - "aframe-physics-system": "https://github.com/donmccurdy/aframe-physics-system", + "aframe-physics-system": "https://github.com/infinitelee/aframe-physics-system#feature/shape-component", + "aframe-rounded": "^1.0.3", "aframe-teleport-controls": "^0.3.1", "aframe-xr": "github:brianpeiris/aframe-xr#3162aed", "classnames": "^2.2.5", @@ -35,7 +36,7 @@ "mobile-detect": "^1.4.1", "moving-average": "^1.0.0", "naf-janus-adapter": "https://github.com/mozilla/naf-janus-adapter#feature/disconnect", - "networked-aframe": "github:mozillareality/networked-aframe#feature/sound-source-set", + "networked-aframe": "github:mozillareality/networked-aframe#mr-social-client/master", "nipplejs": "^0.6.7", "query-string": "^5.0.1", "raven-js": "^3.20.1", @@ -74,6 +75,7 @@ "rimraf": "^2.6.2", "sass-loader": "^6.0.7", "selfsigned": "^1.10.2", + "shelljs": "^0.8.1", "style-loader": "^0.20.2", "webpack": "^4.0.1", "webpack-cli": "^2.0.9", diff --git a/scripts/check-yarn-lock.sh b/scripts/check-yarn-lock.sh new file mode 100755 index 0000000000000000000000000000000000000000..e27b56ab47bc9746933266a02f0c30b7a7b9c6e1 --- /dev/null +++ b/scripts/check-yarn-lock.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +if [ `git diff yarn.lock | wc -l` -ne 0 ]; then + echo "" + tput setaf 1 + echo "!! UNCOMMITED YARN.LOCK CHANGES !!" + tput sgr0 + echo "" + exit 1 +fi diff --git a/src/assets/hud/avatar.png b/src/assets/hud/avatar.png new file mode 100644 index 0000000000000000000000000000000000000000..d604f8f18d51b481bcc0013f30e2c610100b231c Binary files /dev/null and b/src/assets/hud/avatar.png differ diff --git a/src/assets/interactables/duck/DuckyMesh.glb b/src/assets/interactables/duck/DuckyMesh.glb index 5bb10e0cd79c623f72f87d438f6eddbe1d0dc250..f50dc37942d519c36e16a5e41bb8a44a18c4c7f1 100644 Binary files a/src/assets/interactables/duck/DuckyMesh.glb and b/src/assets/interactables/duck/DuckyMesh.glb differ diff --git a/src/assets/interactables/duck/gltf/DuckyMesh.fbm/Ducky.jpg b/src/assets/interactables/duck/gltf/DuckyMesh.fbm/Ducky.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6c27845f40d1e4b91ddb5534c413f408580ed51e Binary files /dev/null and b/src/assets/interactables/duck/gltf/DuckyMesh.fbm/Ducky.jpg differ diff --git a/src/assets/interactables/duck/gltf/DuckyMesh.gltf b/src/assets/interactables/duck/gltf/DuckyMesh.gltf new file mode 100644 index 0000000000000000000000000000000000000000..83cc03646480c7cec3849fda0a8fd3580cec3abc --- /dev/null +++ b/src/assets/interactables/duck/gltf/DuckyMesh.gltf @@ -0,0 +1,248 @@ +{ + "asset": { + "generator": "FBX2glTF", + "version": "2.0" + }, + "scene": 0, + "buffers": [ + { + "byteLength": 18600, + "uri": "buffer.bin" + } + ], + "bufferViews": [ + { + "buffer": 0, + "byteLength": 3336, + "byteOffset": 0, + "target": 34963 + }, + { + "buffer": 0, + "byteLength": 5724, + "byteOffset": 3336, + "target": 34962 + }, + { + "buffer": 0, + "byteLength": 5724, + "byteOffset": 9060, + "target": 34962 + }, + { + "buffer": 0, + "byteLength": 3816, + "byteOffset": 14784, + "target": 34962 + } + ], + "scenes": [ + { + "name": "Root Scene", + "nodes": [ + 0 + ], + "extras": { + "components": { + "shape": [ + { + "shape": "box", + "halfExtents": { + "x": 0.06, + "y": 0.04, + "z": 0.08 + }, + "offset": { + "x": 0, + "y": 0.052671334114334445, + "z": 0.01001389278835843 + }, + "orientation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + } + }, + { + "shape": "sphere", + "radius": 0.05, + "offset": { + "x": 0, + "y": 0.1287570519589527, + "z": 0.033095376412929145 + }, + "orientation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + } + }, + { + "shape": "cylinder", + "radiusTop": 0.02, + "radiusBottom": 0.02, + "height": 0.030000000000000013, + "numSegments": 8, + "offset": { + "x": 0, + "y": 0.12657048237702667, + "z": 0.09010837508332667 + }, + "orientation": { + "x": 0.7071067811865476, + "y": 0, + "z": 0, + "w": 0.7071067811865475 + } + } + ] + } + } + } + ], + "accessors": [ + { + "componentType": 5123, + "type": "SCALAR", + "count": 1668, + "bufferView": 0, + "byteOffset": 0 + }, + { + "componentType": 5126, + "type": "VEC3", + "count": 477, + "bufferView": 1, + "byteOffset": 0, + "min": [ + -0.0587991699576378, + 0.0129461474716663, + -0.0740185976028442 + ], + "max": [ + 0.0618169121444225, + 0.173104390501976, + 0.104356855154037 + ] + }, + { + "componentType": 5126, + "type": "VEC3", + "count": 477, + "bufferView": 2, + "byteOffset": 0 + }, + { + "componentType": 5126, + "type": "VEC2", + "count": 477, + "bufferView": 3, + "byteOffset": 0 + } + ], + "images": [ + { + "name": "DuckyMesh.fbm/Ducky.jpg", + "uri": "DuckyMesh.fbm/Ducky.jpg" + } + ], + "samplers": [ + {} + ], + "textures": [ + { + "name": "file4", + "sampler": 0, + "source": 0 + } + ], + "materials": [ + { + "name": "lambert2", + "alphaMode": "OPAQUE", + "extras": { + "fromFBX": { + "shadingModel": "Lambert", + "isTruePBR": false + } + }, + "pbrMetallicRoughness": { + "baseColorTexture": { + "index": 0, + "texCoord": 0 + }, + "baseColorFactor": [ + 0.800000011920929, + 0.800000011920929, + 0.800000011920929, + 1 + ], + "metallicFactor": 0.200000002980232, + "roughnessFactor": 0.800000011920929 + } + } + ], + "meshes": [ + { + "name": "Ducky", + "primitives": [ + { + "material": 0, + "mode": 4, + "attributes": { + "NORMAL": 2, + "POSITION": 1, + "TEXCOORD_0": 3 + }, + "indices": 0 + } + ] + } + ], + "nodes": [ + { + "name": "RootNode", + "translation": [ + 0, + 0, + 0 + ], + "rotation": [ + 0, + 0, + 0, + 1 + ], + "scale": [ + 1, + 1, + 1 + ], + "children": [ + 1 + ] + }, + { + "name": "Ducky", + "translation": [ + 0, + 0, + 0 + ], + "rotation": [ + 0, + 0, + 0, + 1 + ], + "scale": [ + 1, + 1, + 1 + ], + "mesh": 0 + } + ] +} \ No newline at end of file diff --git a/src/assets/interactables/duck/gltf/DuckyMesh.json b/src/assets/interactables/duck/gltf/DuckyMesh.json new file mode 100644 index 0000000000000000000000000000000000000000..97b41944a2ca7e12e5e12cf4892d2534ef73e5b4 --- /dev/null +++ b/src/assets/interactables/duck/gltf/DuckyMesh.json @@ -0,0 +1,60 @@ +{ + "scenes": { + "Root Scene": { + "shape": [ + { + "shape": "box", + "halfExtents": { + "x": 0.06, + "y": 0.04, + "z": 0.08 + }, + "offset": { + "x": 0, + "y": 0.052671334114334445, + "z": 0.01001389278835843 + }, + "orientation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + } + }, + { + "shape": "sphere", + "radius": 0.05, + "offset": { + "x": 0, + "y": 0.1287570519589527, + "z": 0.033095376412929145 + }, + "orientation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + } + }, + { + "shape": "cylinder", + "radiusTop": 0.02, + "radiusBottom": 0.02, + "height": 0.030000000000000013, + "numSegments": 8, + "offset": { + "x": 0, + "y": 0.12657048237702667, + "z": 0.09010837508332667 + }, + "orientation": { + "x": 0.7071067811865476, + "y": 0, + "z": 0, + "w": 0.7071067811865475 + } + } + ] + } + } +} diff --git a/src/assets/interactables/duck/gltf/buffer.bin b/src/assets/interactables/duck/gltf/buffer.bin new file mode 100644 index 0000000000000000000000000000000000000000..d7ed323e11aef0c4b2045dd8f2ddd34154244b84 Binary files /dev/null and b/src/assets/interactables/duck/gltf/buffer.bin differ diff --git a/src/assets/stylesheets/2d-hud.css b/src/assets/stylesheets/2d-hud.css index a8324a5bb8b054c4da44f82aadfe96647c5cb45b..a50436114181d18997248dce77a3cc1d9500363f 100644 --- a/src/assets/stylesheets/2d-hud.css +++ b/src/assets/stylesheets/2d-hud.css @@ -3,12 +3,45 @@ top: 10px; display: flex; justify-content: center; - height: 60px; + align-items: center; + height: 80px; width: 100%; } -:local(.bg) { - position: absolute; +:local(.panel) { + display: flex; + justify-content: space-around; + align-items: center; + padding: 10px; + background-color: rgba(0, 0, 0, 0.35); +} + +:local(.panel.left) { + border-bottom-left-radius: 30px; + border-top-left-radius: 30px; + padding-left: 20px; + padding-right: 50px; + margin-right: -40px; +} + +:local(.panel.right) { + border-bottom-right-radius: 30px; + border-top-right-radius: 30px; + padding-right: 20px; + padding-left: 50px; + margin-left: -40px; +} + +:local(.modeButton) { + width: 80px; + height: 80px; + display: flex; + align-items: center; + justify-content: center; + z-index: 10; +} + +:local(.panel) { display: flex; justify-content: space-around; align-items: center; @@ -16,6 +49,7 @@ background-color: rgba(0, 0, 0, 0.35); } + :local(.nametag) { display: flex; justify-content: center; @@ -26,21 +60,20 @@ } :local(.avatar) { - display: flex; - width: 48px; - height: 48px; + width: 80px; + height: 80px; background-size: 100%; - background-image: url(../hud/avatar.jpg); + background-image: url(../hud/avatar.png); } :local(.mic) { display: flex; - width: 48px; - height: 48px; + width: 32px; + height: 32px; -webkit-mask: url(../hud/unmuted.png); - -webkit-mask-size: 48px; + -webkit-mask-size: 32px; mask: url(../hud/unmuted.png); - mask-size: 48px; + mask-size: 32px; background-color: white; cursor: pointer; } @@ -59,9 +92,9 @@ :local(.mic.muted) { -webkit-mask: url(../hud/muted.png); - -webkit-mask-size: 48px; + -webkit-mask-size: 32px; mask: url(../hud/muted.png); - mask-size: 48px; + mask-size: 32px; } :local(.mic.muted:active) { diff --git a/src/behaviours/oculus-touch-joystick-dpad4.js b/src/behaviours/joystick-dpad4.js similarity index 86% rename from src/behaviours/oculus-touch-joystick-dpad4.js rename to src/behaviours/joystick-dpad4.js index bf397ba2fbda2357c7fdf1cfc0310c8aa1ba548c..d0cc1e2a87d1882346deb724d5ba14570d46b02c 100644 --- a/src/behaviours/oculus-touch-joystick-dpad4.js +++ b/src/behaviours/joystick-dpad4.js @@ -1,7 +1,7 @@ import { angleTo4Direction } from "../utils/dpad"; // @TODO specify 4 or 8 direction -function oculus_touch_joystick_dpad4(el, outputPrefix) { +function joystick_dpad4(el, outputPrefix) { this.angleToDirection = angleTo4Direction; this.outputPrefix = outputPrefix; this.centerRadius = 0.6; @@ -11,7 +11,7 @@ function oculus_touch_joystick_dpad4(el, outputPrefix) { el.addEventListener("axismove", this.emitDPad4); } -oculus_touch_joystick_dpad4.prototype = { +joystick_dpad4.prototype = { emitDPad4: function(event) { const x = event.detail.axis[0]; const y = event.detail.axis[1]; @@ -25,4 +25,4 @@ oculus_touch_joystick_dpad4.prototype = { } }; -export { oculus_touch_joystick_dpad4 }; +export default joystick_dpad4; diff --git a/src/behaviours/vive-trackpad-dpad4.js b/src/behaviours/trackpad-dpad4.js similarity index 93% rename from src/behaviours/vive-trackpad-dpad4.js rename to src/behaviours/trackpad-dpad4.js index 99bdf8873d4cbcce3541691e6073af71c153db2b..b23ca1dbdb1f61ec6d99e9d2fab17ab4c96b0248 100644 --- a/src/behaviours/vive-trackpad-dpad4.js +++ b/src/behaviours/trackpad-dpad4.js @@ -1,6 +1,6 @@ import { angleTo4Direction } from "../utils/dpad"; -function vive_trackpad_dpad4(el, outputPrefix) { +function trackpad_dpad4(el, outputPrefix) { this.outputPrefix = outputPrefix; this.lastDirection = ""; this.previous = ""; @@ -15,7 +15,7 @@ function vive_trackpad_dpad4(el, outputPrefix) { el.addEventListener("trackpadup", this.unpress); } -vive_trackpad_dpad4.prototype = { +trackpad_dpad4.prototype = { press: function() { this.pressed = true; }, @@ -51,4 +51,4 @@ vive_trackpad_dpad4.prototype = { } }; -export { vive_trackpad_dpad4 }; +export default trackpad_dpad4; diff --git a/src/components/hud-controller.js b/src/components/hud-controller.js new file mode 100644 index 0000000000000000000000000000000000000000..4ee274f34ae9daa6f24c8e414eee824b845c49db --- /dev/null +++ b/src/components/hud-controller.js @@ -0,0 +1,77 @@ +import { AppModes } from "../systems/app-mode.js"; + +const TWOPI = Math.PI * 2; +function deltaAngle(a, b) { + const p = Math.abs(b - a) % TWOPI; + return p > Math.PI ? TWOPI - p : p; +} + +/** + * Positions the HUD and toggles app mode based on where the user is looking + */ +AFRAME.registerComponent("hud-controller", { + schema: { + head: { type: "selector" }, + offset: { default: 0.7 }, // distance from hud above head, + lookCutoff: { default: 20 }, // angle at which the hud should be "on", + animRange: { default: 30 }, // degrees over which to animate the hud into view + yawCutoff: { default: 50 } // yaw degrees at wich the hud should reoirent even if the user is looking up + }, + init() { + this.isYLocked = false; + this.lockedHeadPositionY = 0; + }, + + pause() { + // TODO: this assumes full control over current app mode reguardless of what else might be manipulating it, this is obviously wrong + const AppModeSystem = this.el.sceneEl.systems["app-mode"]; + AppModeSystem.setMode(AppModes.DEFAULT); + }, + + tick() { + const hud = this.el.object3D; + const head = this.data.head.object3D; + const sceneEl = this.el.sceneEl; + + const { offset, lookCutoff, animRange, yawCutoff } = this.data; + + const pitch = head.rotation.x * THREE.Math.RAD2DEG; + const yawDif = deltaAngle(head.rotation.y, hud.rotation.y) * THREE.Math.RAD2DEG; + + // Reorient the hud only if the user is looking away from the hud, for right now this arbitrarily means the hud is 1/3 way animated away + // TODO: come up with better huristics for this that maybe account for the user turning away from the hud "too far", also animate the position so that it doesnt just snap. + if (yawDif >= yawCutoff || pitch < lookCutoff - animRange / 3) { + const lookDir = new THREE.Vector3(0, 0, -1); + lookDir.applyQuaternion(head.quaternion); + lookDir.add(head.position); + hud.position.x = lookDir.x; + hud.position.z = lookDir.z; + hud.setRotationFromEuler(new THREE.Euler(0, head.rotation.y, 0)); + } + + // animate the hud into place over animRange degrees as the user aproaches the lookCutoff angle + const t = 1 - THREE.Math.clamp(lookCutoff - pitch, 0, animRange) / animRange; + + // Lock the hud in place relative to a known head position so it doesn't bob up and down + // with the user's head + if (!this.isYLocked && t === 1) { + this.lockedHeadPositionY = head.position.y; + } + const EPSILON = 0.001; + this.isYLocked = t > 1 - EPSILON; + + hud.position.y = (this.isYLocked ? this.lockedHeadPositionY : head.position.y) + offset + (1 - t) * offset; + hud.rotation.x = (1 - t) * THREE.Math.DEG2RAD * 90; + + // update the app mode when the HUD locks on or off + // TODO: this assumes full control over current app mode reguardless of what else might be manipulating it, this is obviously wrong + const AppModeSystem = sceneEl.systems["app-mode"]; + if (pitch > lookCutoff && AppModeSystem.mode !== AppModes.HUD) { + AppModeSystem.setMode(AppModes.HUD); + sceneEl.renderer.sortObjects = true; + } else if (pitch < lookCutoff && AppModeSystem.mode === AppModes.HUD) { + AppModeSystem.setMode(AppModes.DEFAULT); + sceneEl.renderer.sortObjects = false; + } + } +}); diff --git a/src/components/in-world-hud.js b/src/components/in-world-hud.js index aa7e8aae756207dc05a640f7d02f6c0f728b41ca..9e307e498256da344e5e2e3533baf82b09f93b6f 100644 --- a/src/components/in-world-hud.js +++ b/src/components/in-world-hud.js @@ -4,29 +4,11 @@ AFRAME.registerComponent("in-world-hud", { raycaster: { type: "selector" } }, init() { - this.bg = this.el.querySelector(".bg"); this.mic = this.el.querySelector(".mic"); - this.nametag = this.el.querySelector(".username"); - this.avatar = this.el.querySelector(".avatar"); - this.nametag.object3DMap.text.material.depthTest = false; - this.nametag.object3DMap.text.renderOrder = 1; - this.mic.object3DMap.mesh.renderOrder = 1; - this.avatar.object3DMap.mesh.renderOrder = 1; - - const line = this.data.raycaster.object3DMap.line; - line.renderOrder = 2; - line.material.depthTest = false; - // Set opacity to 0.99 to force the line to render in the - // transparent pass, so that it renders on top of the HUD - this.data.raycaster.setAttribute("line", "opacity", 0.99); const muted = this.el.sceneEl.is("muted"); this.mic.setAttribute("src", muted ? "#muted" : "#unmuted"); - const scene = this.el.sceneEl; - this.onUsernameChanged = this.onUsernameChanged.bind(this); - scene.addEventListener("username-changed", this.onUsernameChanged); - this.showCorrectMuteState = () => { const muted = this.el.sceneEl.is("muted"); this.mic.setAttribute("src", muted ? "#muted" : "#unmuted"); @@ -95,9 +77,6 @@ AFRAME.registerComponent("in-world-hud", { }, pause() { - this.nametag.removeEventListener("raycaster-intersected", this.onNametagHovered); - this.nametag.removeEventListener("raycaster-intersected-cleared", this.onNametagUnhovered); - this.el.sceneEl.removeEventListener("stateadded", this.onStateChange); this.el.sceneEl.removeEventListener("stateremoved", this.onStateChange); @@ -120,12 +99,5 @@ AFRAME.registerComponent("in-world-hud", { volume: this.volume, levels: this.levels }); - }, - - onUsernameChanged(evt) { - const { username } = evt.detail; - const width = evt.detail.username.length == 0 ? 1 : 40 / username.length; - this.nametag.setAttribute("text", "width", Math.min(width, 6)); - this.nametag.setAttribute("text", "value", username); } }); diff --git a/src/components/nav-mesh-helper.js b/src/components/nav-mesh-helper.js new file mode 100644 index 0000000000000000000000000000000000000000..5c1be4ce96946dddb6ba98590dc7da27d403ab10 --- /dev/null +++ b/src/components/nav-mesh-helper.js @@ -0,0 +1,16 @@ +AFRAME.registerComponent("nav-mesh-helper", { + schema: { + teleportControls: { type: "selectorAll", default: "[teleport-controls]" } + }, + + init: function() { + const teleportControls = this.data.teleportControls; + this.el.addEventListener("bundleloaded", () => { + if (!teleportControls) return; + + for (let i = 0; i < teleportControls.length; i++) { + teleportControls[i].components["teleport-controls"].queryCollisionEntities(); + } + }); + } +}); diff --git a/src/components/super-cursor.js b/src/components/super-cursor.js index b8daee493abcbfb543db553538c5b968748da381..ee70e960663144709f17108d70a2378a6bc9f935 100644 --- a/src/components/super-cursor.js +++ b/src/components/super-cursor.js @@ -92,7 +92,7 @@ AFRAME.registerComponent("super-cursor", { this.data.cursor.object3D.position.copy(this.point); } - this.isInteractable = intersection && intersection.object.el.className === "interactable"; + this.isInteractable = intersection && this._isInteractable(intersection.object.el); if ((this.isGrabbing || this.isInteractable) && !this.wasIntersecting) { this.wasIntersecting = true; @@ -103,6 +103,13 @@ AFRAME.registerComponent("super-cursor", { } }, + _isInteractable: function(el) { + return ( + el.className === "interactable" || + (el.parentNode && el.parentNode != el.sceneEl && this._isInteractable(el.parentNode)) + ); + }, + _handleMouseDown: function() { if (this.isInteractable) { const lookControls = this.data.camera.components["look-controls"]; @@ -122,7 +129,19 @@ AFRAME.registerComponent("super-cursor", { }, _handleWheel: function(e) { - if (this.isGrabbing) this.currentDistanceMod += e.deltaY / 10; + if (this.isGrabbing) { + switch (e.deltaMode) { + case e.DOM_DELTA_PIXEL: + this.currentDistanceMod += e.deltaY / 500; + break; + case e.DOM_DELTA_LINE: + this.currentDistanceMod += e.deltaY / 10; + break; + case e.DOM_DELTA_PAGE: + this.currentDistanceMod += e.deltaY / 2; + break; + } + } }, _handleEnterVR: function() { diff --git a/src/gltf-component-mappings.js b/src/gltf-component-mappings.js index 06e81969a0f72d0d3a90ae7b247f28bf68fa2aaa..2166e7a62a186c9caa1ce3da8f39b5cafa4fff31 100644 --- a/src/gltf-component-mappings.js +++ b/src/gltf-component-mappings.js @@ -2,5 +2,6 @@ import "./components/gltf-model-plus"; AFRAME.GLTFModelPlus.registerComponent("scale-audio-feedback", "scale-audio-feedback"); AFRAME.GLTFModelPlus.registerComponent("loop-animation", "loop-animation"); +AFRAME.GLTFModelPlus.registerComponent("shape", "shape"); AFRAME.GLTFModelPlus.registerComponent("visible", "visible"); AFRAME.GLTFModelPlus.registerComponent("nav-mesh", "nav-mesh"); diff --git a/src/hub.html b/src/hub.html index b1f063732df95a6da2da232a209ccdffc465d4bd..9892cdcf01e840af3d78a69d2417e1c20b5fabe1 100644 --- a/src/hub.html +++ b/src/hub.html @@ -29,7 +29,7 @@ <a-assets> <img id="unmuted" src="./assets/hud/unmuted.png" > <img id="muted" src="./assets/hud/muted.png" > - <img id="avatar" src="./assets/hud/avatar.jpg" > + <img id="avatar" src="./assets/hud/avatar.png" > <a-asset-item id="botdefault" response-type="arraybuffer" src="https://asset-bundles-prod.reticulum.io/bots/BotDefault_Avatar-9f71f8ff22.gltf"></a-asset-item> <a-asset-item id="botbobo" response-type="arraybuffer" src="https://asset-bundles-prod.reticulum.io/bots/BotBobo_Avatar-f9740a010b.gltf"></a-asset-item> @@ -105,11 +105,11 @@ <template id="interactable-template"> <a-entity - gltf-model-plus="src: #interactable-duck" + gltf-model-plus="src: #interactable-duck; inflate: true;" scale="2 2 2" class="interactable" super-networked-interactable="counter: #counter; mass: 5;" - body="type: dynamic; mass: 5; shape: box;" + body="type: dynamic; shape: none; mass: 5;" grabbable stretchable="useWorldPosition: true;" ></a-entity> @@ -173,11 +173,11 @@ vr-mode-toggle-visibility vr-mode-toggle-playing__hud-controller > - <a-entity in-world-hud="haptic:#player-right-controller;raycaster:#player-right-controller;" rotation="-39 0 0"> - <a-box geometry="height:0.13;width:0.6;depth:0.001" material="depthTest:false; color:#000000;opacity:0.35" class="hud bg"></a-box> - <a-image src="#unmuted" scale="-0.1 0.1 0.1" position="-0.2 0 0.001" class="hud mic" material="alphaTest:0.1;depthTest:false;"></a-image> - <a-text scale="0.3 0.3 0.3" position="0 0 0.001" class="hud username" text="width:6;alphaTest:0.1;align:center;"></a-text> - <a-image src="#avatar" scale="0.1 0.1 0.1" position="0.2 0 0.001" class="hud avatar" material="depthTest:false;"></a-image> + <a-entity in-world-hud="haptic:#player-right-controller;raycaster:#player-right-controller;" rotation="30 0 0"> + <a-rounded height="0.13" width="0.6" color="#000000" position="-0.3 -0.065 0" radius="0.065" opacity="0.35" class="hud bg"></a-rounded> + <a-image src="#unmuted" scale="0.1 0.1 0.1" position="-0.2 0 0.001" class="hud mic" material="alphaTest:0.1;"></a-image> + <a-image src="#avatar" scale="0.2 0.2 0.2" position="0 0 0.001" class="hud avatar"></a-image> + <a-image src="#unmuted" scale="0.1 0.1 0.1" position="0.2 0 0.001" class="hud mic" material="alphaTest:0.1;"></a-image> </a-entity> </a-entity> @@ -203,8 +203,6 @@ haptic-feedback ></a-entity> - - <a-entity id="player-right-controller" class="right-controller" @@ -280,7 +278,7 @@ ></a-entity> <!-- Environment --> - <a-entity id="environment-root" position="0 0 0"></a-entity> + <a-entity id="environment-root" position="0 0 0" nav-mesh-helper></a-entity> <a-entity id="skybox" diff --git a/src/hub.js b/src/hub.js index 1a1c08b453a285a9d95a1c1b8d5ad40b17980ed0..9f2c8ffd8901d091b1108ef3ba6838f4d5c7e6d7 100644 --- a/src/hub.js +++ b/src/hub.js @@ -11,10 +11,11 @@ import "naf-janus-adapter"; import "aframe-teleport-controls"; import "aframe-input-mapping-component"; import "aframe-billboard-component"; +import "aframe-rounded"; import "webrtc-adapter"; -import { vive_trackpad_dpad4 } from "./behaviours/vive-trackpad-dpad4"; -import { oculus_touch_joystick_dpad4 } from "./behaviours/oculus-touch-joystick-dpad4"; +import trackpad_dpad4 from "./behaviours/trackpad-dpad4"; +import { joystick_dpad4 } from "./behaviours/joystick-dpad4"; import { PressedMove } from "./activators/pressedmove"; import { ReverseY } from "./activators/reversey"; import "./activators/shortpress"; @@ -44,6 +45,7 @@ import "./components/loop-animation"; import "./components/hand-poses"; import "./components/gltf-model-plus"; import "./components/gltf-bundle"; +import "./components/hud-controller"; import ReactDOM from "react-dom"; import React from "react"; @@ -77,6 +79,8 @@ import "./components/super-spawner"; import "./components/super-cursor"; import "./components/event-repeater"; +import "./components/nav-mesh-helper"; + import registerNetworkSchemas from "./network-schemas"; import { inGameActions, config as inputConfig } from "./input-mappings"; import registerTelemetry from "./telemetry"; @@ -94,8 +98,8 @@ function qsTruthy(param) { registerTelemetry(); -AFRAME.registerInputBehaviour("vive_trackpad_dpad4", vive_trackpad_dpad4); -AFRAME.registerInputBehaviour("oculus_touch_joystick_dpad4", oculus_touch_joystick_dpad4); +AFRAME.registerInputBehaviour("trackpad_dpad4", trackpad_dpad4); +AFRAME.registerInputBehaviour("joystick_dpad4", joystick_dpad4); AFRAME.registerInputActivator("pressedmove", PressedMove); AFRAME.registerInputActivator("reverseY", ReverseY); AFRAME.registerInputMappings(inputConfig, true); diff --git a/src/input-mappings.js b/src/input-mappings.js index 035110dd677efa748c280ae0d6b8f14fde1ae6f4..9296fd71ffa9f31c32308cff00cd7d4bd3096dcf 100644 --- a/src/input-mappings.js +++ b/src/input-mappings.js @@ -21,29 +21,37 @@ const config = { behaviours: { default: { "oculus-touch-controls": { - joystick: "oculus_touch_joystick_dpad4" + joystick: "joystick_dpad4" }, "vive-controls": { - trackpad: "vive_trackpad_dpad4" + trackpad: "trackpad_dpad4" + }, + "daydream-controls": { + trackpad: "trackpad_dpad4" + }, + "gearvr-controls": { + trackpad: "trackpad_dpad4" } } }, mappings: { default: { "vive-controls": { - menudown: ["action_mute", "thumb_down"], - menuup: "thumb_up", "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" }, + trackpad_dpad4_pressed_north_down: { right: "action_teleport_down" }, + trackpad_dpad4_pressed_south_down: { right: "action_teleport_down" }, trackpadup: { right: "action_teleport_up" }, + menudown: "thumb_down", + menuup: "thumb_up", gripdown: ["action_grab", "middle_ring_pinky_down", "index_down"], gripup: ["action_release", "middle_ring_pinky_up", "index_up"], trackpadtouchstart: "thumb_down", trackpadtouchend: "thumb_up", - triggerdown: "index_down", - triggerup: "index_up" + triggerdown: ["action_grab", "index_down"], + triggerup: ["action_release", "index_up"] }, "oculus-touch-controls": { joystick_dpad4_west: { @@ -52,7 +60,6 @@ const config = { joystick_dpad4_east: { right: "snap_rotate_right" }, - xbuttondown: "action_mute", gripdown: ["action_grab", "middle_ring_pinky_down"], gripup: ["action_release", "middle_ring_pinky_up"], abuttontouchstart: "thumb_down", @@ -67,20 +74,26 @@ const config = { surfacetouchend: "thumb_up", thumbsticktouchstart: "thumb_down", thumbsticktouchend: "thumb_up", - triggerdown: ["action_teleport_down", "index_down"], - triggerup: ["action_teleport_up", "index_up"], + triggerdown: "index_down", + triggerup: "index_up", "axismove.reverseY": { left: "move" }, - right_dpad_east: "snap_rotate_right", - right_dpad_west: "snap_rotate_left", abuttondown: "action_teleport_down", abuttonup: "action_teleport_up" }, "daydream-controls": { - trackpaddown: "action_teleport_down", + trackpad_dpad4_pressed_west_down: "snap_rotate_left", + trackpad_dpad4_pressed_east_down: "snap_rotate_right", + trackpad_dpad4_pressed_center_down: "action_teleport_down", + trackpad_dpad4_pressed_north_down: "action_teleport_down", + trackpad_dpad4_pressed_south_down: "action_teleport_down", trackpadup: "action_teleport_up" }, "gearvr-controls": { - trackpaddown: "action_teleport_down", + trackpad_dpad4_pressed_west_down: "snap_rotate_left", + trackpad_dpad4_pressed_east_down: "snap_rotate_right", + trackpad_dpad4_pressed_center_down: "action_teleport_down", + trackpad_dpad4_pressed_north_down: "action_teleport_down", + trackpad_dpad4_pressed_south_down: "action_teleport_down", trackpadup: "action_teleport_up" }, keyboard: { @@ -100,14 +113,14 @@ const config = { 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" + arrowup_down: "w_down", + arrowup_up: "w_up", + arrowleft_down: "a_down", + arrowleft_up: "a_up", + arrowdown_down: "s_down", + arrowdown_up: "s_up", + arrowright_down: "d_down", + arrowright_up: "d_up" } }, hud: { @@ -116,8 +129,8 @@ const config = { triggerup: { right: "action_ui_select_up" } }, "oculus-touch-controls": { - triggerdown: { right: "action_ui_select_down" }, - triggerup: { right: "action_ui_select_up" }, + abuttondown: "action_ui_select_down", + abuttonup: "action_ui_select_up", gripdown: "middle_ring_pinky_down", gripup: "middle_ring_pinky_up", abuttontouchstart: "thumb_down", diff --git a/src/react-components/2d-hud.js b/src/react-components/2d-hud.js index 8d9223a072c97c670210e67e48434cfc58543d49..9679bb9c090af49966c6f5cbf03072201c860f00 100644 --- a/src/react-components/2d-hud.js +++ b/src/react-components/2d-hud.js @@ -4,18 +4,21 @@ import cx from "classnames"; import styles from "../assets/stylesheets/2d-hud.css"; -const TwoDHUD = ({ name, muted, onToggleMute }) => ( +const TwoDHUD = ({ muted, onToggleMute }) => ( <div className={styles.container}> - <div className={cx("ui-interactive", styles.bg)}> + <div className={cx("ui-interactive", styles.panel, styles.left)}> <div className={cx(styles.mic, { [styles.muted]: muted })} onClick={onToggleMute} /> - <div className={styles.nametag}>{name}</div> + </div> + <div className={cx("ui-interactive", styles.modeButton)}> <div className={styles.avatar} /> </div> + <div className={cx("ui-interactive", styles.panel, styles.right)}> + <div className={cx(styles.mic, { [styles.muted]: muted })} onClick={onToggleMute} /> + </div> </div> ); TwoDHUD.propTypes = { - name: PropTypes.string, muted: PropTypes.bool, onToggleMute: PropTypes.func }; diff --git a/src/react-components/ui-root.js b/src/react-components/ui-root.js index 3970a2305c4439900b4936f8e465d2d0039a8de0..282f4b4572156fb4e757616a1e8c02e19df34727 100644 --- a/src/react-components/ui-root.js +++ b/src/react-components/ui-root.js @@ -737,11 +737,7 @@ class UIRoot extends Component { )} </div> {this.state.entryStep === ENTRY_STEPS.finished ? ( - <TwoDHUD - name={this.props.store.state.profile.display_name} - muted={this.state.muted} - onToggleMute={this.toggleMute} - /> + <TwoDHUD muted={this.state.muted} onToggleMute={this.toggleMute} /> ) : null} </div> </IntlProvider> diff --git a/src/systems/app-mode.js b/src/systems/app-mode.js index 1ef1c2cd0d7b79c75d0738717558901370320e5b..49e8d361bd90de4612fe4548fc0ecb4cc027d53d 100644 --- a/src/systems/app-mode.js +++ b/src/systems/app-mode.js @@ -1,6 +1,6 @@ /* global AFRAME, console, setTimeout, clearTimeout */ -const AppModes = Object.freeze({ DEFAULT: "default", HUD: "hud" }); +export const AppModes = Object.freeze({ DEFAULT: "default", HUD: "hud" }); /** * Simple system for keeping track of a modal app state @@ -89,82 +89,6 @@ AFRAME.registerComponent("app-mode-input-mappings", { } }); -const TWOPI = Math.PI * 2; -function deltaAngle(a, b) { - const p = Math.abs(b - a) % TWOPI; - return p > Math.PI ? TWOPI - p : p; -} - -/** - * Positions the HUD and toggles app mode based on where the user is looking - */ -AFRAME.registerComponent("hud-controller", { - schema: { - head: { type: "selector" }, - offset: { default: 1 }, // distance from hud below head, - lookCutoff: { default: -25 }, // angle at which the hud should be "on", - animRange: { default: 30 }, // degrees over which to animate the hud into view - yawCutoff: { default: 100 } // yaw degrees at wich the hud should reoirent even if the user is looking down - }, - init() { - this.isYLocked = false; - this.lockedHeadPositionY = 0; - }, - - pause() { - // TODO: this assumes full control over current app mode reguardless of what else might be manipulating it, this is obviously wrong - const AppModeSystem = this.el.sceneEl.systems["app-mode"]; - AppModeSystem.setMode(AppModes.DEFAULT); - }, - - tick() { - const hud = this.el.object3D; - const head = this.data.head.object3D; - const sceneEl = this.el.sceneEl; - - const { offset, lookCutoff, animRange, yawCutoff } = this.data; - - const pitch = head.rotation.x * THREE.Math.RAD2DEG; - const yawDif = deltaAngle(head.rotation.y, hud.rotation.y) * THREE.Math.RAD2DEG; - - // Reorient the hud only if the user is looking "up", for right now this arbitrarily means the hud is 1/3 way animated away - // TODO: come up with better huristics for this that maybe account for the user turning away from the hud "too far", also animate the position so that it doesnt just snap. - if (yawDif >= yawCutoff || pitch > lookCutoff + animRange / 3) { - const lookDir = new THREE.Vector3(0, 0, -1); - lookDir.applyQuaternion(head.quaternion); - lookDir.add(head.position); - hud.position.x = lookDir.x; - hud.position.z = lookDir.z; - hud.setRotationFromEuler(new THREE.Euler(0, head.rotation.y, 0)); - } - - //animate the hud into place over animRange degrees as the user aproaches the lookCutoff angle - const t = 1 - THREE.Math.clamp(pitch - lookCutoff, 0, animRange) / animRange; - - // Lock the hud in place relative to a known head position so it doesn't bob up and down - // with the user's head - if (!this.isYLocked && t === 1) { - this.lockedHeadPositionY = head.position.y; - } - const EPSILON = 0.001; - this.isYLocked = t > 1 - EPSILON; - - hud.position.y = (this.isYLocked ? this.lockedHeadPositionY : head.position.y) - offset - offset * (1 - t); - hud.rotation.x = (1 - t) * THREE.Math.DEG2RAD * 90; - - // update the app mode when the HUD locks on or off - // TODO: this assumes full control over current app mode reguardless of what else might be manipulating it, this is obviously wrong - const AppModeSystem = sceneEl.systems["app-mode"]; - if (pitch < lookCutoff && AppModeSystem.mode !== AppModes.HUD) { - AppModeSystem.setMode(AppModes.HUD); - sceneEl.renderer.sortObjects = true; - } else if (pitch > lookCutoff && AppModeSystem.mode === AppModes.HUD) { - AppModeSystem.setMode(AppModes.DEFAULT); - sceneEl.renderer.sortObjects = false; - } - } -}); - /** * Toggle visibility of an entity based on if the user is in vr mode or not */ diff --git a/yarn.lock b/yarn.lock index 352623872827845ef84e0f8c46f3535945602f45..9e4e6fbae46ef483424f8b4ada1dadce72744951 100644 --- a/yarn.lock +++ b/yarn.lock @@ -170,19 +170,13 @@ aframe-extras@^4.0.0: version "0.1.2" resolved "https://github.com/johnshaughnessy/aframe-input-mapping-component#4c7e493ad6c4a25eef27d32551c94d8b78541191" -aframe-lerp-component@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/aframe-lerp-component/-/aframe-lerp-component-1.1.0.tgz#dc8c12c53698770c30f25eef8873e34a4e79c765" - dependencies: - almost-equal "^1.1.0" - "aframe-physics-extras@https://github.com/infinitelee/aframe-physics-extras#fix/physics-collider-crash": version "0.1.2" resolved "https://github.com/infinitelee/aframe-physics-extras#49b2d5d3c0caac905783aee51d9e89dbdf7199b8" -"aframe-physics-system@https://github.com/donmccurdy/aframe-physics-system": - version "3.0.1" - resolved "https://github.com/donmccurdy/aframe-physics-system#08a98a4c3d77c4c38a1fa27067aa0d894447902e" +"aframe-physics-system@https://github.com/infinitelee/aframe-physics-system#feature/shape-component": + version "3.0.2" + resolved "https://github.com/infinitelee/aframe-physics-system#be3e43f1af5b995952e1b3d27dce216ab2e79f05" dependencies: browserify "^14.3.0" budo "^10.0.3" @@ -190,6 +184,10 @@ aframe-lerp-component@^1.1.0: three-to-cannon "^1.2.0" webworkify "^1.4.0" +aframe-rounded@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/aframe-rounded/-/aframe-rounded-1.0.3.tgz#72c7d9c0ff02e94ee138bf217c284e187efa3cfb" + aframe-teleport-controls@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/aframe-teleport-controls/-/aframe-teleport-controls-0.3.1.tgz#7d7ef206f483ea92425a6333b0f8fb26c9596d1c" @@ -230,10 +228,6 @@ ajv@^6.0.1, ajv@^6.1.0: fast-json-stable-stringify "^2.0.0" json-schema-traverse "^0.3.0" -almost-equal@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/almost-equal/-/almost-equal-1.1.0.tgz#f851c631138757994276aa2efbe8dfa3066cccdd" - alphanum-sort@^1.0.1, alphanum-sort@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3" @@ -5351,11 +5345,10 @@ neo-async@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.5.0.tgz#76b1c823130cca26acfbaccc8fbaf0a2fa33b18f" -"networked-aframe@github:mozillareality/networked-aframe#feature/sound-source-set": - version "0.6.0" - resolved "https://codeload.github.com/mozillareality/networked-aframe/tar.gz/978b7c25aff26a0c1db47beb35d8a6556f717d36" +"networked-aframe@github:mozillareality/networked-aframe#mr-social-client/master": + version "0.6.1" + resolved "https://codeload.github.com/mozillareality/networked-aframe/tar.gz/69be0e7e5f66070526c8240cb795b9e88da971a9" dependencies: - aframe-lerp-component "^1.1.0" easyrtc "1.1.0" express "^4.10.7" serve-static "^1.8.0" @@ -7202,6 +7195,14 @@ shelljs@^0.7.0: interpret "^1.0.0" rechoir "^0.6.2" +shelljs@^0.8.1: + version "0.8.1" + resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.1.tgz#729e038c413a2254c4078b95ed46e0397154a9f1" + dependencies: + glob "^7.0.0" + interpret "^1.0.0" + rechoir "^0.6.2" + sigmund@~1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/sigmund/-/sigmund-1.0.1.tgz#3ff21f198cad2175f9f3b781853fd94d0d19b590"