diff --git a/src/assets/stylesheets/room.scss b/src/assets/stylesheets/room.scss index 8f3cfb7b5ebf1553653649575d03c5e0a215956c..d042519b31409aa888dde3bbe83e69758fb1cb54 100644 --- a/src/assets/stylesheets/room.scss +++ b/src/assets/stylesheets/room.scss @@ -17,3 +17,10 @@ bottom: 20px; } +.a-canvas.a-grab-cursor:hover { + cursor: none; +} + +.a-canvas.a-grab-cursor:active { + cursor: none; +} diff --git a/src/components/super-cursor.js b/src/components/super-cursor.js index d1b28b0f1566d43fe68e40d1fc0d9e0ecc13be65..e498a1acc2ad9b99c9131134bd17560bee1af531 100644 --- a/src/components/super-cursor.js +++ b/src/components/super-cursor.js @@ -2,42 +2,55 @@ AFRAME.registerComponent("super-cursor", { dependencies: ["raycaster"], schema: { cursor: { type: "selector" }, - maxDistance: { type: "number", default: 3 }, - minDistance: { type: "number", default: 0.5 } + 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.isGrabbing = false; 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 }); - play: function() { 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); + 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() { @@ -46,14 +59,21 @@ AFRAME.registerComponent("super-cursor", { } this.isGrabbing = this.data.cursor.components["super-hands"].state.has("grab-start"); - let isIntersecting = false; + + 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) { - isIntersecting = true; - this.point = intersections[0].point; - this.data.cursor.object3D.position.copy(this.point); + intersection = intersections[0]; + this.data.cursor.object3D.position.copy(intersection.point); this.currentDistance = intersections[0].distance; this.currentDistanceMod = 0; } else { @@ -61,50 +81,69 @@ AFRAME.registerComponent("super-cursor", { } } - if (this.isGrabbing || !isIntersecting) { - const head = this.el.object3D; - head.getWorldPosition(this.origin); - head.getWorldDirection(this.direction); + 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.direction.multiplyScalar(distance); this.point.addVectors(this.origin, this.direction); this.data.cursor.object3D.position.copy(this.point); } - if ((this.isGrabbing || isIntersecting) && !this.wasIntersecting) { + this.isInteractable = intersection && intersection.object.el.className === "interactable"; + + if ((this.isGrabbing || this.isInteractable) && !this.wasIntersecting) { this.wasIntersecting = true; - this.data.cursor.setAttribute("material", {color: "#00FF00"}); - } else if (!this.isGrabbing && !isIntersecting && this.wasIntersecting) { + 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: "#00EFFF"}); + this.data.cursor.setAttribute("material", { color: this.data.cursorColorUnhovered }); } }, _handleMouseDown: function(e) { + if (this.isInteractable) { + const lookControls = this.data.camera.components["look-controls"]; + 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(e) { + const lookControls = this.data.camera.components["look-controls"]; + lookControls.play(); this.data.cursor.emit("action_release", {}); }, _handleWheel: function(e) { if (this.isGrabbing) this.currentDistanceMod += e.deltaY / 10; - }, + }, _handleEnterVR: function(e) { if (AFRAME.utils.device.checkHeadsetConnected() || AFRAME.utils.device.isMobile()) { - this.enabled = false; - this.data.cursor.setAttribute("visible", false); + this._disable(); } }, _handleExitVR: function(e) { + 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 42d7c9b48f3067e87f0242aebc15b7ff07332eca..72d24eff785b9eebed42c60e2e4feb21e659468a 100644 --- a/src/components/super-spawner.js +++ b/src/components/super-spawner.js @@ -21,7 +21,7 @@ AFRAME.registerComponent("super-spawner", { remove: function() { for (let entity of this.entities.keys()) { const data = this.entities.get(entity); - entity.removeEventListener("componentinitialized", data.componentinItializedListener); + entity.removeEventListener("componentinitialized", data.componentinInitializedListener); entity.removeEventListener("bodyloaded", data.bodyLoadedListener); } @@ -34,18 +34,18 @@ AFRAME.registerComponent("super-spawner", { entity.setAttribute("networked", "template:" + this.data.template); - const componentinItializedListener = this._handleComponentInitialzed.bind(this, entity); + const componentinInitializedListener = this._handleComponentInitialzed.bind(this, entity); const bodyLoadedListener = this._handleBodyLoaded.bind(this, entity); this.entities.set(entity, { hand: hand, componentInitialized: false, bodyLoaded: false, - componentinItializedListener: componentinItializedListener, + componentinInitializedListener: componentinInitializedListener, bodyLoadedListener: bodyLoadedListener }); - entity.addEventListener("componentinitialized", componentinItializedListener); + entity.addEventListener("componentinitialized", componentinInitializedListener); entity.addEventListener("body-loaded", bodyLoadedListener); const pos = this.data.useCustomSpawnPosition ? this.data.spawnPosition : this.el.getAttribute("position"); @@ -71,7 +71,7 @@ AFRAME.registerComponent("super-spawner", { data.hand.emit("action_grab", { targetEntity: entity }); entity.emit("grab-start", { hand: data.hand }); - entity.removeEventListener("componentinitialized", data.componentinItializedListener); + entity.removeEventListener("componentinitialized", data.componentinInitializedListener); entity.removeEventListener("body-loaded", data.bodyLoadedListener); this.entities.delete(entity); diff --git a/src/room.html b/src/room.html index 0bc4d32f480927adc71b3d6fee8f06ed1891a60b..71e58c59ec504ac1bf619db21bc755e910325894 100644 --- a/src/room.html +++ b/src/room.html @@ -96,13 +96,12 @@ <a-entity gltf-model="#interactable-duck" scale="2 2 2" - class="collidable" + class="interactable" super-networked-interactable="counter: #counter; mass: 5;" body="type: dynamic; mass: 5; shape: box;" grabbable stretchable="useWorldPosition: true;" - > - </a-entity> + ></a-entity> </template> <a-mixin id="super-hands" @@ -123,21 +122,24 @@ <a-entity gltf-model="#interactable-duck" scale="2 2 2" - class="collidable" - class="collidable" + class="interactable" super-spawner="template: #interactable-template;" position="2.5 1.2 0" body="mass: 0; type: static; shape: box;" ></a-entity> - <a-sphere - id="3d-cursor" - material="color: #00EFFF" - radius=0.02 - static-body="shape: sphere;" - mixin="super-hands" - ></a-sphere> - + <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" + ></a-sphere> + </a-entity> <!-- Player Rig --> <a-entity @@ -155,14 +157,8 @@ camera position="0 1.6 0" personal-space-bubble - > - <a-entity - id="super-cursor" - super-cursor="cursor: #3d-cursor" - position="0 0 0" - raycaster="objects: .collidable; direction: 0 0 -1;" - ></a-entity> - </a-entity> + look-controls + ></a-entity> <a-entity id="player-left-controller" @@ -303,8 +299,8 @@ height="35" width="35" static-body - class="collidable"> - </a-plane> + class="collidable" + ></a-plane> </a-scene> <div id="ui-root" class="ui"></div> diff --git a/src/room.js b/src/room.js index b8e18315139946b4afb258c256f753466ffe79b8..66314fdf8cf77a81540a963a00f061d35d3cde5e 100644 --- a/src/room.js +++ b/src/room.js @@ -152,7 +152,6 @@ async function enterScene(mediaStream, enterInVR) { } AFRAME.registerInputActions(inGameActions, "default"); - document.querySelector("#player-camera").setAttribute("look-controls", "pointerLockEnabled: true;"); scene.setAttribute("networked-scene", { adapter: "janus",