From c41d2df6e3f928a31d249d7c843bcfd7ce2c939d Mon Sep 17 00:00:00 2001 From: joni <johnfshaughnessy@gmail.com> Date: Mon, 21 May 2018 17:31:53 -0700 Subject: [PATCH] Add MouseEventHandler with optional use of the Pointer Lock API --- src/components/cursor-controller.js | 43 ++++---- src/components/look-on-mobile.js | 1 - src/components/virtual-gamepad-controls.js | 10 +- src/hub.js | 8 +- src/utils/mouse-events-handler.js | 115 +++++++++++++++++++++ 5 files changed, 148 insertions(+), 29 deletions(-) create mode 100644 src/utils/mouse-events-handler.js diff --git a/src/components/cursor-controller.js b/src/components/cursor-controller.js index 71f50fde5..da43d4f7d 100644 --- a/src/components/cursor-controller.js +++ b/src/components/cursor-controller.js @@ -49,10 +49,13 @@ AFRAME.registerComponent("cursor-controller", { 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); - this._handleWheel = this._handleWheel.bind(this); + + window.APP.mouseEventsHandler.registerCursor(this); + this.handleMouseDown = this.handleMouseDown.bind(this); + this.handleMouseMove = this.handleMouseMove.bind(this); + this.handleMouseUp = this.handleMouseUp.bind(this); + this.handleMouseWheel = this.handleMouseWheel.bind(this); + this._handleEnterVR = this._handleEnterVR.bind(this); this._handleExitVR = this._handleExitVR.bind(this); this._handlePrimaryDown = this._handlePrimaryDown.bind(this); @@ -80,11 +83,6 @@ AFRAME.registerComponent("cursor-controller", { }, play: function() { - document.addEventListener("mousedown", this._handleMouseDown); - document.addEventListener("mousemove", this._handleMouseMove); - document.addEventListener("mouseup", this._handleMouseUp); - document.addEventListener("wheel", this._handleWheel); - window.addEventListener("enter-vr", this._handleEnterVR); window.addEventListener("exit-vr", this._handleExitVR); @@ -101,11 +99,6 @@ AFRAME.registerComponent("cursor-controller", { }, pause: function() { - document.removeEventListener("mousedown", this._handleMouseDown); - document.removeEventListener("mousemove", this._handleMouseMove); - document.removeEventListener("mouseup", this._handleMouseUp); - document.removeEventListener("wheel", this._handleWheel); - window.removeEventListener("enter-vr", this._handleEnterVR); window.removeEventListener("exit-vr", this._handleExitVR); @@ -226,11 +219,12 @@ AFRAME.registerComponent("cursor-controller", { _setLookControlsEnabled(enabled) { const lookControls = this.data.camera.components["look-controls"]; - if (!lookControls) return; - if (enabled) { - lookControls.play(); - } else { - lookControls.pause(); + if (lookControls) { + if (enabled) { + lookControls.play(); + } else { + lookControls.pause(); + } } }, @@ -289,24 +283,27 @@ AFRAME.registerComponent("cursor-controller", { this._setCursorVisibility(false); }, - _handleMouseDown: function() { + handleMouseDown: function() { if (this.isMobile && !this.inVR && !this.hasPointingDevice) return; if (this._isTargetOfType(TARGET_TYPE_INTERACTABLE_OR_UI)) { this._setLookControlsEnabled(false); this.data.cursor.emit("cursor-grab", {}); + return true; } else if (this.inVR || this.isMobile) { this._startTeleport(); + return; } + return false; }, - _handleMouseMove: function(e) { + handleMouseMove: function(e) { if (this.isMobile && !this.inVR && !this.hasPointingDevice) return; this.mousePos.set(e.clientX / window.innerWidth * 2 - 1, -(e.clientY / window.innerHeight) * 2 + 1); }, - _handleMouseUp: function() { + handleMouseUp: function() { if (this.isMobile && !this.inVR && !this.hasPointingDevice) return; this._setLookControlsEnabled(true); @@ -314,7 +311,7 @@ AFRAME.registerComponent("cursor-controller", { this._endTeleport(); }, - _handleWheel: function(e) { + handleMouseWheel: function(e) { if (this._isGrabbing()) { switch (e.deltaMode) { case e.DOM_DELTA_PIXEL: diff --git a/src/components/look-on-mobile.js b/src/components/look-on-mobile.js index 53670eb8f..2287910ee 100644 --- a/src/components/look-on-mobile.js +++ b/src/components/look-on-mobile.js @@ -39,7 +39,6 @@ AFRAME.registerComponent("look-on-mobile", { this.hmdEuler = new THREE.Euler(); this.prevX = this.hmdEuler.x; this.prevY = this.hmdEuler.y; - this.ticks = 0; this.pendingLookX = 0; this.onRotateX = this.onRotateX.bind(this); this.dXBuffer = []; diff --git a/src/components/virtual-gamepad-controls.js b/src/components/virtual-gamepad-controls.js index 44843532d..e90f7ac36 100644 --- a/src/components/virtual-gamepad-controls.js +++ b/src/components/virtual-gamepad-controls.js @@ -89,8 +89,9 @@ AFRAME.registerComponent("virtual-gamepad-controls", { onMoveJoystickChanged(event, joystick) { const angle = joystick.angle.radian; const force = joystick.force < 1 ? joystick.force : 1; - const x = Math.cos(angle) * force * 0.85; - const z = Math.sin(angle) * force * 0.85; + const moveStrength = 0.85; + const x = Math.cos(angle) * force * moveStrength; + const z = Math.sin(angle) * force * moveStrength; this.moving = true; this.moveEvent.axis[0] = x; this.moveEvent.axis[1] = z; @@ -107,9 +108,10 @@ AFRAME.registerComponent("virtual-gamepad-controls", { // Set pitch and yaw angles on right stick move const angle = joystick.angle.radian; const force = joystick.force < 1 ? joystick.force : 1; + const turnStrength = 0.5; this.rotating = true; - this.rotateYEvent.value = Math.cos(angle) * force * 0.5; - this.rotateXEvent.value = Math.sin(angle) * force * 0.5; + this.rotateYEvent.value = Math.cos(angle) * force * turnStrength; + this.rotateXEvent.value = Math.sin(angle) * force * turnStrength; }, onLookJoystickEnd() { diff --git a/src/hub.js b/src/hub.js index d38367bde..bbd203f7d 100644 --- a/src/hub.js +++ b/src/hub.js @@ -123,7 +123,9 @@ import { generateDefaultProfile, generateRandomName } from "./utils/identity.js" import { getAvailableVREntryTypes, VR_DEVICE_AVAILABILITY } from "./utils/vr-caps-detect.js"; import ConcurrentLoadDetector from "./utils/concurrent-load-detector.js"; import TouchEventsHandler from "./utils/touch-events-handler.js"; +import MouseEventsHandler from "./utils/mouse-events-handler.js"; window.APP.touchEventsHandler = new TouchEventsHandler(); +window.APP.mouseEventsHandler = new MouseEventsHandler(); function qsTruthy(param) { const val = qs[param]; @@ -242,10 +244,14 @@ const onReady = async () => { const camera = document.querySelector("#player-camera"); const registerLookControls = e => { if (e.detail.name !== "look-controls") return; - window.APP.touchEventsHandler.registerLookControls(camera.components["look-controls"]); camera.removeEventListener("componentinitialized", registerLookControls); + + window.APP.touchEventsHandler.registerLookControls(camera.components["look-controls"]); scene.components["look-on-mobile"].registerLookControls(camera.components["look-controls"]); scene.setAttribute("look-on-mobile", "enabled", true); + + window.APP.mouseEventsHandler.registerLookControls(camera.components["look-controls"]); + window.APP.mouseEventsHandler.setInverseMouseLook(qsTruthy("invertMouseLook")); }; camera.addEventListener("componentinitialized", registerLookControls); camera.setAttribute("look-controls", { diff --git a/src/utils/mouse-events-handler.js b/src/utils/mouse-events-handler.js new file mode 100644 index 000000000..bd55ccb9b --- /dev/null +++ b/src/utils/mouse-events-handler.js @@ -0,0 +1,115 @@ +const HORIZONTAL_LOOK_SPEED = 0.0035; +const VERTICAL_LOOK_SPEED = 0.0021; +const PI_4 = Math.PI / 4; + +export default class MouseEventsHandler { + constructor() { + this.cursor = null; + this.lookControls = null; + this.isMouseDown = false; + this.isMouseDownHandledByCursor = false; + this.isPointerLocked = false; + this.dXBuffer = []; + this.dYBuffer = []; + + this.registerCursor = this.registerCursor.bind(this); + this.registerLookControls = this.registerLookControls.bind(this); + this.isReady = this.isReady.bind(this); + this.addEventListeners = this.addEventListeners.bind(this); + this.onMouseDown = this.onMouseDown.bind(this); + this.onMouseMove = this.onMouseMove.bind(this); + this.onMouseUp = this.onMouseUp.bind(this); + this.onMouseWheel = this.onMouseWheel.bind(this); + this.look = this.look.bind(this); + + document.addEventListener("contextmenu", function(e) { + e.preventDefault(); + }); + } + + setInverseMouseLook(invert) { + this.invertMouseLook = invert; + } + + registerCursor(cursor) { + this.cursor = cursor; + if (this.isReady()) { + this.addEventListeners(); + } + } + + registerLookControls(lookControls) { + this.lookControls = lookControls; + if (this.isReady()) { + this.addEventListeners(); + } + } + + isReady() { + return this.cursor && this.lookControls; + } + + addEventListeners() { + document.addEventListener("mousedown", this.onMouseDown); + document.addEventListener("mousemove", this.onMouseMove); + document.addEventListener("mouseup", this.onMouseUp); + document.addEventListener("wheel", this.onMouseWheel); + } + + onMouseDown(e) { + const isLeftButton = e.button === 0; + if (isLeftButton) { + this.isMouseDownHandledByCursor = this.cursor.handleMouseDown(); + this.isMouseDown = true; + } else { + if (this.isPointerLocked) { + document.exitPointerLock(); + this.isPointerLocked = false; + } else { + document.body.requestPointerLock(); + this.isPointerLocked = true; + } + } + } + + onMouseWheel(e) { + this.cursor.handleMouseWheel(e); + } + + onMouseMove(e) { + const shouldLook = (this.isMouseDown && !this.isMouseDownHandledByCursor) || this.isPointerLocked; + if (shouldLook) { + this.look(e); + } + + this.cursor.handleMouseMove(e); + } + + look(e) { + const movementX = e.movementX; + const movementY = e.movementY; + + const sign = this.invertMouseLook ? 1 : -1; + this.lookControls.yawObject.rotation.y += sign * movementX * HORIZONTAL_LOOK_SPEED; + this.lookControls.pitchObject.rotation.x += sign * movementY * VERTICAL_LOOK_SPEED; + this.lookControls.pitchObject.rotation.x = Math.max( + -PI_4, + Math.min(PI_4, this.lookControls.pitchObject.rotation.x) + ); + this.lookControls.updateOrientation(); + } + + onMouseUp(e) { + const isLeftButton = e.button === 0; + if (isLeftButton) { + if (this.isMouseDownHandledByCursor) { + this.cursor.handleMouseUp(); + } + this.isMouseDownHandledByCursor = false; + this.isMouseDown = false; + this.dXBuffer = []; + this.dYBuffer = []; + } else { + } + } +} -- GitLab