diff --git a/src/behaviours/joystick-dpad4.js b/src/behaviours/joystick-dpad4.js index d0cc1e2a87d1882346deb724d5ba14570d46b02c..0eae7e1d194dfb6f207da53a7499caaf8e483c02 100644 --- a/src/behaviours/joystick-dpad4.js +++ b/src/behaviours/joystick-dpad4.js @@ -8,10 +8,16 @@ function joystick_dpad4(el, outputPrefix) { this.previous = "none"; this.hapticIntensity = "low"; this.emitDPad4 = this.emitDPad4.bind(this); - el.addEventListener("axismove", this.emitDPad4); + this.el = el; } joystick_dpad4.prototype = { + addEventListeners: function() { + this.el.addEventListener("axismove", this.emitDPad4); + }, + removeEventListeners: function() { + this.el.removeEventListener("axismove", this.emitDPad4); + }, emitDPad4: function(event) { const x = event.detail.axis[0]; const y = event.detail.axis[1]; diff --git a/src/behaviours/trackpad-dpad4.js b/src/behaviours/trackpad-dpad4.js index b23ca1dbdb1f61ec6d99e9d2fab17ab4c96b0248..f15908d34d5854d60ae158af7026007745293dfb 100644 --- a/src/behaviours/trackpad-dpad4.js +++ b/src/behaviours/trackpad-dpad4.js @@ -10,12 +10,20 @@ function trackpad_dpad4(el, outputPrefix) { this.unpress = this.unpress.bind(this); this.hapticIntensity = "low"; this.centerRadius = 0.6; - el.addEventListener("axismove", this.emitDPad4); - el.addEventListener("trackpaddown", this.press); - el.addEventListener("trackpadup", this.unpress); + this.el = el; } trackpad_dpad4.prototype = { + addEventListeners: function() { + this.el.addEventListener("axismove", this.emitDPad4); + this.el.addEventListener("trackpaddown", this.press); + this.el.addEventListener("trackpadup", this.unpress); + }, + removeEventListeners: function() { + this.el.removeEventListener("axismove", this.emitDPad4); + this.el.removeEventListener("trackpaddown", this.press); + this.el.removeEventListener("trackpadup", this.unpress); + }, press: function() { this.pressed = true; }, diff --git a/src/components/cursor-controller.js b/src/components/cursor-controller.js new file mode 100644 index 0000000000000000000000000000000000000000..572b17d91f606912b612fb3e1b8e440f33be46a0 --- /dev/null +++ b/src/components/cursor-controller.js @@ -0,0 +1,445 @@ +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; + +AFRAME.registerComponent("cursor-controller", { + dependencies: ["raycaster", "line"], + schema: { + cursor: { type: "selector" }, + camera: { type: "selector" }, + playerRig: { type: "selector" }, + gazeTeleportControls: { type: "selector" }, + physicalHandSelector: { type: "string" }, + handedness: { default: "right", oneOf: ["right", "left"] }, + maxDistance: { default: 3 }, + minDistance: { default: 0.5 }, + cursorColorHovered: { default: "#2F80ED" }, + cursorColorUnhovered: { default: "#FFFFFF" }, + primaryDown: { default: "action_primary_down" }, + primaryUp: { default: "action_primary_up" }, + grabEvent: { default: "action_grab" }, + releaseEvent: { default: "action_release" } + }, + + init: function() { + this.inVR = false; + this.isMobile = AFRAME.utils.device.isMobile(); + this.hasPointingDevice = false; + this.currentTargetType = TARGET_TYPE_NONE; + this.grabStarting = false; + this.currentDistance = this.data.maxDistance; + this.currentDistanceMod = 0; + this.mousePos = new THREE.Vector2(); + this.controller = null; + this.controllerQueue = []; + this.wasCursorHovered = false; + this.wasPhysicalHandGrabbing = false; + this.origin = new THREE.Vector3(); + this.direction = new THREE.Vector3(); + this.controllerQuaternion = new THREE.Quaternion(); + + this.data.cursor.setAttribute("material", { color: this.data.cursorColorUnhovered }); + + this._handleMouseDown = this._handleMouseDown.bind(this); + this._handleMouseMove = this._handleMouseMove.bind(this); + this._handleMouseUp = this._handleMouseUp.bind(this); + this._handleWheel = this._handleWheel.bind(this); + this._handleEnterVR = this._handleEnterVR.bind(this); + this._handleExitVR = this._handleExitVR.bind(this); + this._handlePrimaryDown = this._handlePrimaryDown.bind(this); + this._handlePrimaryUp = this._handlePrimaryUp.bind(this); + this._handleModelLoaded = this._handleModelLoaded.bind(this); + this._handleCursorLoaded = this._handleCursorLoaded.bind(this); + 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); + }, + + remove: function() { + this.data.cursor.removeEventListener("loaded", this._cursorLoadedListener); + }, + + update: function(oldData) { + if (oldData.physicalHand !== this.data.physicalHandSelector) { + this._handleModelLoaded(); + } + + if (oldData.handedness !== this.data.handedness) { + //TODO + } + }, + + play: function() { + if (!this.inVR && this.isMobile && !this.hasPointingDevice) { + document.addEventListener("touchstart", this._handleTouchStart); + document.addEventListener("touchmove", this._handleTouchMove); + document.addEventListener("touchend", this._handleTouchEnd); + } else { + 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); + + this.data.playerRig.addEventListener(this.data.primaryDown, this._handlePrimaryDown); + this.data.playerRig.addEventListener(this.data.primaryUp, this._handlePrimaryUp); + this.data.playerRig.addEventListener(this.data.grabEvent, this._handlePrimaryDown); + this.data.playerRig.addEventListener(this.data.releaseEvent, this._handlePrimaryUp); + + this.data.playerRig.addEventListener("model-loaded", this._handleModelLoaded); + + this.el.sceneEl.addEventListener("controllerconnected", this._handleControllerConnected); + this.el.sceneEl.addEventListener("controllerdisconnected", this._handleControllerDisconnected); + }, + + pause: function() { + document.removeEventListener("touchstart", this._handleTouchStart); + document.removeEventListener("touchmove", this._handleTouchMove); + document.removeEventListener("touchend", this._handleTouchEnd); + 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); + + this.data.playerRig.removeEventListener(this.data.primaryDown, this._handlePrimaryDown); + this.data.playerRig.removeEventListener(this.data.primaryUp, this._handlePrimaryUp); + this.data.playerRig.removeEventListener(this.data.grabEvent, this._handlePrimaryDown); + this.data.playerRig.removeEventListener(this.data.releaseEvent, this._handlePrimaryUp); + + this.data.playerRig.removeEventListener("model-loaded", this._handleModelLoaded); + + this.el.sceneEl.removeEventListener("controllerconnected", this._handleControllerConnected); + this.el.sceneEl.removeEventListener("controllerdisconnected", this._handleControllerDisconnected); + }, + + tick: function() { + //handle physical hand + if (this.physicalHand) { + const state = this.physicalHand.components["super-hands"].state; + const isPhysicalHandGrabbing = state.has("grab-start") || state.has("hover-start"); + if (this.wasPhysicalHandGrabbing != isPhysicalHandGrabbing) { + this._setCursorVisibility(!isPhysicalHandGrabbing); + this.currentTargetType = TARGET_TYPE_NONE; + } + this.wasPhysicalHandGrabbing = isPhysicalHandGrabbing; + if (isPhysicalHandGrabbing) return; + } + + //set raycaster origin/direction + const camera = this.data.camera.components.camera.camera; + if (!this.inVR) { + //mouse cursor mode + const raycaster = this.el.components.raycaster.raycaster; + raycaster.setFromCamera(this.mousePos, camera); + this.origin.copy(raycaster.ray.origin); + this.direction.copy(raycaster.ray.direction); + } else if ((this.inVR || this.isMobile) && !this.hasPointingDevice) { + //gaze cursor mode + camera.getWorldPosition(this.origin); + camera.getWorldDirection(this.direction); + } else if (this.controller != null) { + //3d cursor mode + this.controller.object3D.getWorldPosition(this.origin); + this.controller.object3D.getWorldQuaternion(this.controllerQuaternion); + this.direction + .set(0, 0, -1) + .applyQuaternion(this.controllerQuaternion) + .normalize(); + } + + this.el.setAttribute("raycaster", { origin: this.origin, direction: this.direction }); + + let intersection = null; + + //update cursor position + if (!this._isGrabbing()) { + const intersections = this.el.components.raycaster.intersections; + if (intersections.length > 0 && intersections[0].distance <= this.data.maxDistance) { + intersection = intersections[0]; + this.data.cursor.object3D.position.copy(intersection.point); + this.currentDistance = intersections[0].distance; + } else { + this.currentDistance = this.data.maxDistance; + } + this.currentDistanceMod = 0; + } + + if (this._isGrabbing() || !intersection) { + const max = Math.max(this.data.minDistance, this.currentDistance - this.currentDistanceMod); + const distance = Math.min(max, this.data.maxDistance); + this.currentDistanceMod = this.currentDistance - distance; + this.direction.multiplyScalar(distance); + this.data.cursor.object3D.position.addVectors(this.origin, this.direction); + } + + //update currentTargetType + if (this._isGrabbing() && !intersection) { + this.currentTargetType = TARGET_TYPE_INTERACTABLE; + } else if (intersection) { + if (intersection.object.el.matches(".interactable, .interactable *")) { + this.currentTargetType = TARGET_TYPE_INTERACTABLE; + } else if (intersection.object.el.matches(".ui, .ui *")) { + this.currentTargetType = TARGET_TYPE_UI; + } + } else { + this.currentTargetType = TARGET_TYPE_NONE; + } + + //update cursor material + const isTarget = this._isTargetOfType(TARGET_TYPE_INTERACTABLE_OR_UI); + if ((this._isGrabbing() || isTarget) && !this.wasCursorHovered) { + this.wasCursorHovered = true; + this.data.cursor.setAttribute("material", { color: this.data.cursorColorHovered }); + } else if (!this._isGrabbing() && !isTarget && this.wasCursorHovered) { + this.wasCursorHovered = false; + this.data.cursor.setAttribute("material", { color: this.data.cursorColorUnhovered }); + } + + //update line + if (this.hasPointingDevice) { + this.el.setAttribute("line", { start: this.origin.clone(), end: this.data.cursor.object3D.position.clone() }); + } + }, + + _isGrabbing() { + return this.data.cursor.components["super-hands"].state.has("grab-start"); + }, + + _isTargetOfType: function(mask) { + return (this.currentTargetType & mask) === this.currentTargetType; + }, + + _setCursorVisibility(visible) { + this.data.cursor.setAttribute("visible", visible); + this.el.setAttribute("line", { visible: visible && this.hasPointingDevice }); + }, + + _setLookControlsEnabled(enabled) { + const lookControls = this.data.camera.components["look-controls"]; + if (lookControls) { + if (enabled) { + lookControls.play(); + } else { + lookControls.pause(); + } + } + }, + + _startTeleport: function() { + if (this.controller != null) { + this.controller.emit("cursor-teleport_down", {}); + } else if (this.inVR) { + this.data.gazeTeleportControls.emit("cursor-teleport_down", {}); + } + this._setCursorVisibility(false); + }, + + _endTeleport: function() { + if (this.controller != null) { + this.controller.emit("cursor-teleport_up", {}); + } else if (this.inVR) { + this.data.gazeTeleportControls.emit("cursor-teleport_up", {}); + } + this._setCursorVisibility(true); + }, + + _handleTouchStart: function(e) { + 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; + } + } + + 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; + const camera = this.data.camera.components.camera.camera; + 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(); + }, + + _handleTouchMove: function(e) { + 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; + } + }, + + _handleTouchEnd: function(e) { + for (let i = 0; i < e.changedTouches.length; i++) { + const touch = e.changedTouches[i]; + const thisTouchDidNotDriveMousePos = + Math.abs(touch.clientX - this.lastTouch.clientX) > 0.1 && + Math.abs(touch.clientY - this.lastTouch.clientY) > 0.1; + if (thisTouchDidNotDriveMousePos) { + return; + } + } + this._setLookControlsEnabled(true); + this.data.cursor.emit("cursor-release", {}); + }, + + _handleMouseDown: function() { + if (this._isTargetOfType(TARGET_TYPE_INTERACTABLE_OR_UI)) { + this._setLookControlsEnabled(false); + this.data.cursor.emit("cursor-grab", {}); + } else if (this.inVR || this.isMobile) { + this._startTeleport(); + } + }, + + _handleMouseMove: function(e) { + this.mousePos.set(e.clientX / window.innerWidth * 2 - 1, -(e.clientY / window.innerHeight) * 2 + 1); + }, + + _handleMouseUp: function() { + this._setLookControlsEnabled(true); + this.data.cursor.emit("cursor-release", {}); + this._endTeleport(); + }, + + _handleWheel: function(e) { + 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() { + if (AFRAME.utils.device.checkHeadsetConnected()) { + this.inVR = true; + this._updateController(); + } + }, + + _handleExitVR: function() { + this.inVR = false; + this._updateController(); + }, + + _handlePrimaryDown: function(e) { + if (e.target === this.controller) { + const isInteractable = this._isTargetOfType(TARGET_TYPE_INTERACTABLE) && !this.grabStarting; + if (isInteractable || this._isTargetOfType(TARGET_TYPE_UI)) { + this.grabStarting = true; + this.data.cursor.emit("cursor-grab", e.detail); + } else if (e.type !== this.data.grabEvent) { + this._startTeleport(); + } + } + }, + + _handlePrimaryUp: function(e) { + if (e.target === this.controller) { + 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(); + } + } + }, + + _handleModelLoaded: function() { + this.physicalHand = this.data.playerRig.querySelector(this.data.physicalHandSelector); + }, + + _handleCursorLoaded: function() { + this.data.cursor.object3DMap.mesh.renderOrder = 1; + }, + + _handleControllerConnected: function(e) { + const data = { + controller: e.target, + handedness: e.detail.component.data.hand + }; + + if (data.handedness === this.data.handedness) { + this.controllerQueue.unshift(data); + } else { + this.controllerQueue.push(data); + } + + this._updateController(); + }, + + _handleControllerDisconnected: function(e) { + for (let i = 0; i < this.controllerQueue.length; i++) { + if (e.target === this.controllerQueue[i].controller) { + this.controllerQueue.splice(i, 1); + this._updateController(); + return; + } + } + }, + + _updateController: function() { + this.hasPointingDevice = this.controllerQueue.length > 0 && this.inVR; + + this._setCursorVisibility(this.hasPointingDevice); + + if (this.hasPointingDevice) { + const controllerData = this.controllerQueue[0]; + const hand = controllerData.handedness; + this.el.setAttribute("cursor-controller", { physicalHand: `#${hand}-super-hand` }); + this.controller = controllerData.controller; + } else { + this.controller = null; + } + } +}); diff --git a/src/components/event-repeater.js b/src/components/event-repeater.js index 64e28720698d8aaa26a8f0ce1525301cebd1ee2f..e5e5ffd192b2b170a4efeac2e0c30e9ee43dd92a 100644 --- a/src/components/event-repeater.js +++ b/src/components/event-repeater.js @@ -24,6 +24,6 @@ AFRAME.registerComponent("event-repeater", { }, _handleEvent: function(event, e) { - this.el.emit(event, e.details); + this.el.emit(event, e.detail); } }); diff --git a/src/components/super-cursor.js b/src/components/super-cursor.js deleted file mode 100644 index ee70e960663144709f17108d70a2378a6bc9f935..0000000000000000000000000000000000000000 --- a/src/components/super-cursor.js +++ /dev/null @@ -1,168 +0,0 @@ -AFRAME.registerComponent("super-cursor", { - dependencies: ["raycaster"], - schema: { - cursor: { type: "selector" }, - camera: { type: "selector" }, - maxDistance: { default: 3 }, - minDistance: { default: 0.5 }, - cursorColorHovered: { default: "#FF0000" }, - cursorColorUnhovered: { efault: "#FFFFFF" } - }, - - init: function() { - this.isGrabbing = false; - this.isInteractable = false; - this.wasIntersecting = false; - this.currentDistance = this.data.maxDistance; - this.currentDistanceMod = 0; - this.enabled = true; - this.origin = new THREE.Vector3(); - this.direction = new THREE.Vector3(); - this.point = new THREE.Vector3(); - this.mousePos = new THREE.Vector2(); - - this.data.cursor.setAttribute("material", { color: this.data.cursorColorUnhovered }); - - this.mouseDownListener = this._handleMouseDown.bind(this); - this.mouseMoveListener = this._handleMouseMove.bind(this); - this.mouseUpListener = this._handleMouseUp.bind(this); - this.wheelListener = this._handleWheel.bind(this); - this.enterVRListener = this._handleEnterVR.bind(this); - this.exitVRListener = this._handleExitVR.bind(this); - }, - - play: function() { - document.addEventListener("mousedown", this.mouseDownListener); - document.addEventListener("mousemove", this.mouseMoveListener); - document.addEventListener("mouseup", this.mouseUpListener); - document.addEventListener("wheel", this.wheelListener); - window.addEventListener("enter-vr", this.enterVRListener); - window.addEventListener("exit-vr", this.exitVRListener); - - this._enable(); - }, - - pause: function() { - document.removeEventListener("mousedown", this.mouseDownListener); - document.removeEventListener("mousemove", this.mouseMoveListener); - document.removeEventListener("mouseup", this.mouseUpListener); - document.removeEventListener("wheel", this.wheelListener); - window.removeEventListener("enter-vr", this.enterVRListener); - window.removeEventListener("exit-vr", this.exitVRListener); - - this._disable(); - }, - - tick: function() { - if (!this.enabled) { - return; - } - - this.isGrabbing = this.data.cursor.components["super-hands"].state.has("grab-start"); - - const camera = this.data.camera.components.camera.camera; - const raycaster = this.el.components.raycaster.raycaster; - 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 }); - - let intersection = null; - - if (!this.isGrabbing) { - const intersections = this.el.components.raycaster.intersections; - if (intersections.length > 0 && intersections[0].distance <= this.data.maxDistance) { - 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; - } - } - - if (this.isGrabbing || !intersection) { - const distance = Math.min( - Math.max(this.data.minDistance, this.currentDistance - this.currentDistanceMod), - this.data.maxDistance - ); - this.currentDistanceMod = this.currentDistance - distance; - this.direction.multiplyScalar(distance); - this.point.addVectors(this.origin, this.direction); - this.data.cursor.object3D.position.copy(this.point); - } - - this.isInteractable = intersection && this._isInteractable(intersection.object.el); - - if ((this.isGrabbing || this.isInteractable) && !this.wasIntersecting) { - this.wasIntersecting = true; - this.data.cursor.setAttribute("material", { color: this.data.cursorColorHovered }); - } else if (!this.isGrabbing && !this.isInteractable && this.wasIntersecting) { - this.wasIntersecting = false; - this.data.cursor.setAttribute("material", { color: this.data.cursorColorUnhovered }); - } - }, - - _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"]; - if (lookControls) lookControls.pause(); - } - this.data.cursor.emit("action_grab", {}); - }, - - _handleMouseMove: function(e) { - this.mousePos.set(e.clientX / window.innerWidth * 2 - 1, -(e.clientY / window.innerHeight) * 2 + 1); - }, - - _handleMouseUp: function() { - const lookControls = this.data.camera.components["look-controls"]; - if (lookControls) lookControls.play(); - this.data.cursor.emit("action_release", {}); - }, - - _handleWheel: function(e) { - 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() { - if (AFRAME.utils.device.checkHeadsetConnected() || AFRAME.utils.device.isMobile()) { - this._disable(); - } - }, - - _handleExitVR: function() { - this._enable(); - }, - - _enable: function() { - this.enabled = true; - this.data.cursor.setAttribute("visible", true); - this.el.setAttribute("raycaster", { enabled: true }); - }, - - _disable: function() { - this.enabled = false; - this.data.cursor.setAttribute("visible", false); - this.el.setAttribute("raycaster", { enabled: false }); - } -}); diff --git a/src/components/super-spawner.js b/src/components/super-spawner.js index bb6762a200eeacf53c29179f95554c7dc8fdab57..9d36c6583f01909e0db08f93450c78cd78c3c3aa 100644 --- a/src/components/super-spawner.js +++ b/src/components/super-spawner.js @@ -2,7 +2,8 @@ AFRAME.registerComponent("super-spawner", { schema: { template: { default: "" }, useCustomSpawnPosition: { default: false }, - spawnPosition: { type: "vec3" } + spawnPosition: { type: "vec3" }, + events: { default: ["cursor-grab", "action_grab"] } }, init: function() { @@ -36,7 +37,6 @@ AFRAME.registerComponent("super-spawner", { const componentinInitializedListener = this._handleComponentInitialzed.bind(this, entity); const bodyLoadedListener = this._handleBodyLoaded.bind(this, entity); - this.entities.set(entity, { hand: hand, componentInitialized: false, @@ -68,8 +68,9 @@ AFRAME.registerComponent("super-spawner", { _emitEvents: function(entity) { const data = this.entities.get(entity); if (data.componentInitialized && data.bodyLoaded) { - data.hand.emit("action_grab", { targetEntity: entity }); - entity.emit("grab-start", { hand: data.hand }); + for (let i = 0; i < this.data.events.length; i++) { + data.hand.emit(this.data.events[i], { targetEntity: entity }); + } entity.removeEventListener("componentinitialized", data.componentinInitializedListener); entity.removeEventListener("body-loaded", data.bodyLoadedListener); diff --git a/src/hub.html b/src/hub.html index 2ca2c34ce04b8b9f5cb03aef765140094687f7a8..6b759eb1ab65fb7bef6b43c2b3c4f261a8a50576 100644 --- a/src/hub.html +++ b/src/hub.html @@ -24,8 +24,6 @@ physics mute-mic="eventSrc: a-scene; toggleEvents: action_mute" personal-space-bubble="debug: false;" - - app-mode-input-mappings="modes: default, hud; actionSets: default, hud;" > <a-assets> @@ -118,12 +116,12 @@ </template> <a-mixin id="super-hands" - super-hands="colliderEvent: collisions; colliderEventProperty: els; + super-hands=" + colliderEvent: collisions; colliderEventProperty: els; colliderEndEvent: collisions; colliderEndEventProperty: clearedEls; grabStartButtons: action_grab; grabEndButtons: action_release; stretchStartButtons: action_grab; stretchEndButtons: action_release; - dragDropStartButtons: action_grab; dragDropEndButtons: action_release; - " + dragDropStartButtons: action_grab; dragDropEndButtons: action_release;" collision-filter="collisionForces: false" physics-collider ></a-mixin> @@ -142,19 +140,33 @@ ></a-entity> <a-entity - id="super-cursor" - super-cursor="cursor: #3d-cursor; camera: #player-camera;" - raycaster="objects: .collidable, .interactable; far: 10;" - > - <a-sphere - id="3d-cursor" - radius="0.02" - static-body="shape: sphere;" - mixin="super-hands" - segments-height="9" - segments-width="9" - ></a-sphere> - </a-entity> + id="cursor-controller" + cursor-controller=" + cursor: #cursor; + camera: #player-camera; + playerRig: #player-rig; + physicalHandSelector: #right-super-hand; + gazeTeleportControls: #gaze-teleport;" + raycaster="objects: .collidable, .interactable, .ui; far: 3;" + line="visible: false; color: white; opacity: 0.2;" + ></a-entity> + + <a-sphere + id="cursor" + material="depthTest: false; opacity:0.9;" + radius="0.02" + static-body="shape: sphere;" + collision-filter="collisionForces: false" + super-hands=" + colliderEvent: raycaster-intersection; colliderEventProperty: els; + colliderEndEvent: raycaster-intersection-cleared; colliderEndEventProperty: clearedEls; + grabStartButtons: cursor-grab; grabEndButtons: cursor-release; + stretchStartButtons: cursor-grab; stretchEndButtons: cursor-release; + dragDropStartButtons: cursor-grab; dragDropEndButtons: cursor-release;" + segments-height="9" + segments-width="9" + event-repeater="events: raycaster-intersection, raycaster-intersection-cleared; eventSource: #cursor-controller" + ></a-sphere> <!-- Player Rig --> <a-entity @@ -164,13 +176,11 @@ wasd-to-analog2d character-controller="pivot: #player-camera" ik-root - app-mode-toggle-playing__character-controller="mode: hud; invert: true;" - app-mode-toggle-playing__wasd-to-analog2d="mode: hud; invert: true;" player-info > - <a-entity id="player-hud" + class="ui" hud-controller="head: #player-camera;" vr-mode-toggle-visibility vr-mode-toggle-playing__hud-controller @@ -189,7 +199,21 @@ camera position="0 1.6 0" personal-space-bubble="radius: 0.4" - ></a-entity> + > + <a-entity + id="gaze-teleport" + position = "0.15 0 0" + teleport-controls=" + cameraRig: #player-rig; + teleportOrigin: #player-camera; + button: cursor-teleport_; + collisionEntities: [nav-mesh]; + drawIncrementally: true; + incrementalDrawMs: 600; + hitOpacity: 0.3; + missOpacity: 0.2;" + ></a-entity> + </a-entity> <a-entity id="player-left-controller" @@ -197,15 +221,14 @@ hand-controls2="left" tracked-controls teleport-controls=" + cameraRig: #player-rig; + teleportOrigin: #player-camera; + button: cursor-teleport_; + collisionEntities: [nav-mesh]; drawIncrementally: true; incrementalDrawMs: 600; hitOpacity: 0.3; - missOpacity: 0.2; - cameraRig: #player-rig; - teleportOrigin: #player-camera; - button: action_teleport_; - collisionEntities: [nav-mesh];" - app-mode-toggle-playing__teleport-controls="mode: hud; invert: true;" + missOpacity: 0.2;" haptic-feedback ></a-entity> @@ -215,22 +238,15 @@ hand-controls2="right" tracked-controls teleport-controls=" + cameraRig: #player-rig; + teleportOrigin: #player-camera; + button: cursor-teleport_; + collisionEntities: [nav-mesh]; drawIncrementally: true; incrementalDrawMs: 600; hitOpacity: 0.3; - missOpacity: 0.2; - cameraRig: #player-rig; - teleportOrigin: #player-camera; - button: action_teleport_; - collisionEntities: [nav-mesh];" + missOpacity: 0.2;" haptic-feedback - raycaster="objects:.hud; showLine: true; far: 2;" - cursor="fuse: false; downEvents: action_ui_select_down; upEvents: action_ui_select_up;" - - app-mode-toggle-playing__teleport-controls="mode: hud; invert: true;" - app-mode-toggle-playing__raycaster="mode: hud;" - app-mode-toggle-playing__cursor="mode: hud;" - app-mode-toggle-attribute__line="mode: hud; property: visible;" ></a-entity> <a-entity gltf-model-plus="inflate: true;" class="model"> @@ -258,7 +274,10 @@ <template data-selector=".LeftHand"> <a-entity bone-visibility> <a-entity - event-repeater="events: action_grab, action_release; eventSource: #player-left-controller" + id="left-super-hand" + event-repeater=" + events: action_grab, action_release, action_primary_down, action_primary_up; + eventSource: #player-left-controller" static-body="shape: sphere; sphereRadius: 0.02" mixin="super-hands" position="0 0.05 0" @@ -269,7 +288,10 @@ <template data-selector=".RightHand"> <a-entity bone-visibility> <a-entity - event-repeater="events: action_grab, action_release; eventSource: #player-right-controller" + id="right-super-hand" + event-repeater=" + events: action_grab, action_release, action_primary_down, action_primary_up; + eventSource: #player-right-controller" static-body="shape: sphere; sphereRadius: 0.02" mixin="super-hands" position="0 -0.05 0" @@ -292,7 +314,6 @@ id="environment-root" nav-mesh-helper static-body="shape: none;" - class="collidable" ></a-entity> <a-entity diff --git a/src/hub.js b/src/hub.js index 5a5dfc3d34e435437ae0793cd22eb65a1252667a..a412bb4d909087dfd627a994df1ec214f4444dd7 100644 --- a/src/hub.js +++ b/src/hub.js @@ -82,9 +82,10 @@ import "super-hands"; import "./components/super-networked-interactable"; import "./components/networked-counter"; import "./components/super-spawner"; -import "./components/super-cursor"; import "./components/event-repeater"; +import "./components/cursor-controller"; + import "./components/nav-mesh-helper"; import registerNetworkSchemas from "./network-schemas"; diff --git a/src/input-mappings.js b/src/input-mappings.js index 8196d0119579623d9f7703bdae9e17bd724150d2..30345c9358003890ce1c0d1ed409bf4321ff0afd 100644 --- a/src/input-mappings.js +++ b/src/input-mappings.js @@ -40,18 +40,18 @@ const config = { "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" }, + trackpad_dpad4_pressed_center_down: { right: "action_primary_down" }, + trackpad_dpad4_pressed_north_down: { right: "action_primary_down" }, + trackpad_dpad4_pressed_south_down: { right: "action_primary_down" }, + trackpadup: { right: "action_primary_up" }, menudown: "thumb_down", menuup: "thumb_up", gripdown: ["action_grab", "middle_ring_pinky_down"], gripup: ["action_release", "middle_ring_pinky_up"], trackpadtouchstart: "thumb_down", trackpadtouchend: "thumb_up", - triggerdown: ["action_grab", "index_down"], - triggerup: ["action_release", "index_up"] + triggerdown: ["action_primary_down", "action_grab", "index_down"], + triggerup: ["action_primary_up", "action_release", "index_up"] }, "oculus-touch-controls": { joystick_dpad4_west: { @@ -74,27 +74,29 @@ const config = { surfacetouchend: "thumb_up", thumbsticktouchstart: "thumb_down", thumbsticktouchend: "thumb_up", - triggerdown: "index_down", - triggerup: "index_up", + triggerdown: ["action_primary_down", "action_grab", "index_down"], + triggerup: ["action_primary_up", "action_release", "index_up"], "axismove.reverseY": { left: "move" }, - abuttondown: "action_teleport_down", - abuttonup: "action_teleport_up" + abuttondown: "action_primary_down", + abuttonup: "action_primary_up" }, "daydream-controls": { 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" + trackpad_dpad4_pressed_center_down: ["action_primary_down"], + trackpad_dpad4_pressed_north_down: ["action_primary_down"], + trackpad_dpad4_pressed_south_down: ["action_primary_down"], + trackpadup: ["action_primary_up"] }, "gearvr-controls": { 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" + trackpad_dpad4_pressed_center_down: ["action_primary_down"], + trackpad_dpad4_pressed_north_down: ["action_primary_down"], + trackpad_dpad4_pressed_south_down: ["action_primary_down"], + trackpadup: ["action_primary_up"], + triggerdown: ["action_primary_down"], + triggerup: ["action_primary_up"] }, keyboard: { m_press: "action_mute", diff --git a/yarn.lock b/yarn.lock index 01d377b1e4441b058dc29d7eb865da622de5781a..96e3c1b583d7d84ba0eef1f880d865217529938b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -168,7 +168,7 @@ aframe-extras@^4.0.0: "aframe-input-mapping-component@https://github.com/johnshaughnessy/aframe-input-mapping-component#feature/map-to-array": version "0.1.2" - resolved "https://github.com/johnshaughnessy/aframe-input-mapping-component#4c7e493ad6c4a25eef27d32551c94d8b78541191" + resolved "https://github.com/johnshaughnessy/aframe-input-mapping-component#33d7ad4c82a5e2b74defca39c7fa5ef15aab493e" "aframe-physics-extras@https://github.com/infinitelee/aframe-physics-extras#fix/physics-collider-crash": version "0.1.2" @@ -7745,7 +7745,7 @@ subarg@^1.0.0: "super-hands@https://github.com/infinitelee/aframe-super-hands-component#mr-social-client/master": version "2.1.0" - resolved "https://github.com/infinitelee/aframe-super-hands-component#0df9e947d652d9b8918e9bfd4db39169ed610ce2" + resolved "https://github.com/infinitelee/aframe-super-hands-component#e4d91f4b9bc91a57f35aa9d43f3e797ebfc5477d" supports-color@1.3.1: version "1.3.1"