diff --git a/src/components/cursor-controller.js b/src/components/cursor-controller.js index 7f3b1bbc42fad4f891b1eef4aaaa7047f8156b60..40f90d82e5e2cb8c90bf407cb6c9155e5830db60 100644 --- a/src/components/cursor-controller.js +++ b/src/components/cursor-controller.js @@ -2,6 +2,7 @@ const TARGET_TYPE_NONE = 1; const TARGET_TYPE_INTERACTABLE = 2; const TARGET_TYPE_UI = 4; const TARGET_TYPE_INTERACTABLE_OR_UI = TARGET_TYPE_INTERACTABLE | TARGET_TYPE_UI; +const virtualJoystickCutoff = 0.8; AFRAME.registerComponent("cursor-controller", { dependencies: ["raycaster", "line"], @@ -38,9 +39,13 @@ AFRAME.registerComponent("cursor-controller", { this.origin = new THREE.Vector3(); this.direction = new THREE.Vector3(); this.controllerQuaternion = new THREE.Quaternion(); + this.activeTouch = null; this.data.cursor.setAttribute("material", { color: this.data.cursorColorUnhovered }); + this._handleTouchStart = this._handleTouchStart.bind(this); + this._handleTouchMove = this._handleTouchMove.bind(this); + this._handleTouchEnd = this._handleTouchEnd.bind(this); this._handleMouseDown = this._handleMouseDown.bind(this); this._handleMouseMove = this._handleMouseMove.bind(this); this._handleMouseUp = this._handleMouseUp.bind(this); @@ -54,17 +59,11 @@ AFRAME.registerComponent("cursor-controller", { this._handleControllerConnected = this._handleControllerConnected.bind(this); this._handleControllerDisconnected = this._handleControllerDisconnected.bind(this); - this._handleTouchStart = this._handleTouchStart.bind(this); - this._updateRaycasterIntersections = this._updateRaycasterIntersections.bind(this); - this._handleTouchMove = this._handleTouchMove.bind(this); - this._handleTouchEnd = this._handleTouchEnd.bind(this); - - this.el.sceneEl.renderer.sortObjects = true; - this.data.cursor.addEventListener("loaded", this.cursorLoadedListener); + this.data.cursor.addEventListener("loaded", this._handleCursorLoaded); }, remove: function() { - this.data.cursor.removeEventListener("loaded", this._cursorLoadedListener); + this.data.cursor.removeEventListener("loaded", this._handleCursorLoaded); }, update: function(oldData) { @@ -81,6 +80,7 @@ AFRAME.registerComponent("cursor-controller", { document.addEventListener("touchstart", this._handleTouchStart); document.addEventListener("touchmove", this._handleTouchMove); document.addEventListener("touchend", this._handleTouchEnd); + document.addEventListener("touchcancel", this._handleTouchEnd); document.addEventListener("mousedown", this._handleMouseDown); document.addEventListener("mousemove", this._handleMouseMove); document.addEventListener("mouseup", this._handleMouseUp); @@ -106,6 +106,7 @@ AFRAME.registerComponent("cursor-controller", { document.removeEventListener("touchstart", this._handleTouchStart); document.removeEventListener("touchmove", this._handleTouchMove); document.removeEventListener("touchend", this._handleTouchEnd); + document.removeEventListener("touchcancel", this._handleTouchEnd); document.removeEventListener("mousedown", this._handleMouseDown); document.removeEventListener("mousemove", this._handleMouseMove); document.removeEventListener("mouseup", this._handleMouseUp); @@ -259,50 +260,38 @@ AFRAME.registerComponent("cursor-controller", { }, _handleTouchStart: function(e) { - if (!this.isMobile || this.hasPointingDevice) return; + if (!this.isMobile || this.hasPointingDevice || this.activeTouch) return; - const touch = e.touches[0]; - if (touch.clientY / window.innerHeight >= 0.8) return true; - this.mousePos.set(touch.clientX / window.innerWidth * 2 - 1, -(touch.clientY / window.innerHeight) * 2 + 1); - this._updateRaycasterIntersections(); - - // update cursor position - if (!this.isGrabbing) { - const intersections = this.el.components.raycaster.intersections; - if (intersections.length > 0 && intersections[0].distance <= this.data.maxDistance) { - const intersection = intersections[0]; - this.data.cursor.object3D.position.copy(intersection.point); - this.currentDistance = intersections[0].distance; - this.currentDistanceMod = 0; - } else { - this.currentDistance = this.data.maxDistance; + for (let i = e.touches.length - 1; i >= 0; i--) { + const touch = e.touches[i]; + if (touch.clientY / window.innerHeight < virtualJoystickCutoff) { + this.activeTouch = touch; + break; } } + if (!this.activeTouch) return; - this._setLookControlsEnabled(false); - - // Set timeout because if I don't, the duck moves is picked up at the - // the wrong offset from the cursor: If the cursor started below and - // to the left, the duck lifts above and to the right by the same amount. - // I don't understand exactly why this is, since I am setting the - // cursor object's position manually in this function, but something else - // must happen before cursor-grab ends up doing the right thing. - // TODO : Figure this out. - window.setTimeout(() => { - this.data.cursor.emit("cursor-grab", {}); - }, 40); - - this.lastTouch = touch; - }, - - _updateRaycasterIntersections: function() { - const raycaster = this.el.components.raycaster.raycaster; + // Update the ray and cursor positions + const raycasterComp = this.el.components.raycaster; + const raycaster = raycasterComp.raycaster; const camera = this.data.camera.components.camera.camera; + const cursor = this.data.cursor; + this.mousePos.set( + this.activeTouch.clientX / window.innerWidth * 2 - 1, + -(this.activeTouch.clientY / window.innerHeight) * 2 + 1 + ); raycaster.setFromCamera(this.mousePos, camera); - this.origin = raycaster.ray.origin; - this.direction = raycaster.ray.direction; - this.el.setAttribute("raycaster", { origin: this.origin, direction: this.direction }); - this.el.components.raycaster.checkIntersections(); + this.el.setAttribute("raycaster", { origin: raycaster.ray.origin, direction: raycaster.ray.direction }); + raycasterComp.checkIntersections(); + const intersections = raycasterComp.intersections; + if (intersections.length === 0 || intersections[0].distance >= this.data.maxDistance) { + this.activeTouch = null; + return; + } + cursor.object3D.position.copy(intersections[0].point); + // Cursor position must be synced to physics before constraint is created + cursor.components["static-body"].syncToPhysics(); + cursor.emit("cursor-grab", {}); }, _handleTouchMove: function(e) { @@ -310,28 +299,28 @@ AFRAME.registerComponent("cursor-controller", { for (let i = 0; i < e.touches.length; i++) { const touch = e.touches[i]; - if (touch.clientY / window.innerHeight >= 0.8) return true; - this.mousePos.set(touch.clientX / window.innerWidth * 2 - 1, -(touch.clientY / window.innerHeight) * 2 + 1); - this.lastTouch = touch; + if ( + (!this.activeTouch && touch.clientY / window.innerHeight < virtualJoystickCutoff) || + (this.activeTouch && touch.identifier === this.activeTouch.identifier) + ) { + this.mousePos.set(touch.clientX / window.innerWidth * 2 - 1, -(touch.clientY / window.innerHeight) * 2 + 1); + return; + } } }, _handleTouchEnd: function(e) { - if (!this.isMobile || this.hasPointingDevice) return; - - for (let i = 0; i < e.changedTouches.length; i++) { - const touch = e.changedTouches[i]; - if (this.lastTouch) { - const thisTouchDidNotDriveMousePos = - Math.abs(touch.clientX - this.lastTouch.clientX) > 0.1 && - Math.abs(touch.clientY - this.lastTouch.clientY) > 0.1; - if (thisTouchDidNotDriveMousePos) { - return; - } - } + if ( + !this.isMobile || + this.hasPointingDevice || + !this.activeTouch || + Array.prototype.some.call(e.touches, touch => touch.identifier === this.activeTouch.identifier) + ) { + return; } - this._setLookControlsEnabled(true); + this.data.cursor.emit("cursor-release", {}); + this.activeTouch = null; }, _handleMouseDown: function() { @@ -400,7 +389,6 @@ AFRAME.registerComponent("cursor-controller", { _handlePrimaryUp: function(e) { if (e.target === this.controller || e.target === this.data.playerRig) { if (this._isGrabbing() || this._isTargetOfType(TARGET_TYPE_UI)) { - this.grabStarting = false; this.data.cursor.emit("cursor-release", e.detail); } else if (e.type !== this.data.releaseEvent) { this._endTeleport(); @@ -413,7 +401,7 @@ AFRAME.registerComponent("cursor-controller", { }, _handleCursorLoaded: function() { - this.data.cursor.object3DMap.mesh.renderOrder = 1; + this.data.cursor.object3DMap.mesh.renderOrder = window.APP.RENDER_ORDER.CURSOR; }, _handleControllerConnected: function(e) { diff --git a/src/components/hud-controller.js b/src/components/hud-controller.js index 67f9c4616285d38efe31e84e63475879463d84fb..2fac822d84d187d141217fa00327a63555a04332 100644 --- a/src/components/hud-controller.js +++ b/src/components/hud-controller.js @@ -81,10 +81,8 @@ AFRAME.registerComponent("hud-controller", { 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 df42d283b956bfa2b90da54977a716ee1f072849..2077b7ef28236d7257c0aa9b8e536ddd9891a499 100644 --- a/src/components/in-world-hud.js +++ b/src/components/in-world-hud.js @@ -7,6 +7,12 @@ AFRAME.registerComponent("in-world-hud", { this.mic = this.el.querySelector(".mic"); this.freeze = this.el.querySelector(".freeze"); this.bubble = this.el.querySelector(".bubble"); + this.background = this.el.querySelector(".bg"); + const renderOrder = window.APP.RENDER_ORDER; + this.mic.object3DMap.mesh.renderOrder = renderOrder.HUD_ICONS; + this.freeze.object3DMap.mesh.renderOrder = renderOrder.HUD_ICONS; + this.bubble.object3DMap.mesh.renderOrder = renderOrder.HUD_ICONS; + this.background.object3DMap.mesh.renderORder = renderOrder.HUD_BACKGROUND; this.updateButtonStates = () => { this.mic.setAttribute("icon-button", "active", this.el.sceneEl.is("muted")); diff --git a/src/hub.js b/src/hub.js index 80741c2e11939461503bb57cd99a1cee97a0ee75..b8906fd0f3f5a9a660c251b9fc5c8e08af125772 100644 --- a/src/hub.js +++ b/src/hub.js @@ -79,6 +79,11 @@ import { DEFAULT_ENVIRONMENT_URL } from "./assets/environments/environments"; import { App } from "./App"; window.APP = new App(); +window.APP.RENDER_ORDER = { + HUD_BACKGROUND: 1, + HUD_ICONS: 2, + CURSOR: 3 +}; const store = window.APP.store; const qs = queryString.parse(location.search); @@ -172,7 +177,7 @@ const onReady = async () => { const scene = document.querySelector("a-scene"); const hubChannel = new HubChannel(store); - document.querySelector("a-scene canvas").classList.add("blurred"); + document.querySelector("canvas").classList.add("blurred"); window.APP.scene = scene; registerNetworkSchemas(); @@ -215,8 +220,9 @@ const onReady = async () => { const enterScene = async (mediaStream, enterInVR, hubId) => { const scene = document.querySelector("a-scene"); + scene.renderer.sortObjects = true; const playerRig = document.querySelector("#player-rig"); - document.querySelector("a-scene canvas").classList.remove("blurred"); + document.querySelector("canvas").classList.remove("blurred"); scene.render(); if (enterInVR) {