diff --git a/src/components/auto-box-collider.js b/src/components/auto-box-collider.js index 9930dc0f2957000603c512d48439869db663c601..e6a78c6b086b85086eb8af3fb55294b707cf1a1f 100644 --- a/src/components/auto-box-collider.js +++ b/src/components/auto-box-collider.js @@ -1,41 +1,43 @@ AFRAME.registerComponent("auto-box-collider", { schema: { - setInitialScale: { default: false } + resize: { default: false }, + resizeLength: { default: 0.5 } }, + init() { this.onLoaded = this.onLoaded.bind(this); this.el.addEventListener("model-loaded", this.onLoaded); - window.autobox = this; + window.auto = this; }, + onLoaded() { this.el.removeEventListener("model-loaded", this.onLoaded); - const initialRotation = this.el.object3D.rotation.clone(); - const initialScale = this.el.object3D.scale.clone(); - const { min, max } = new THREE.Box3().setFromObject(this.el.object3DMap.mesh); + const rotation = this.el.getAttribute("rotation"); this.el.setAttribute("rotation", { x: 0, y: 0, z: 0 }); - let halfExtents = new THREE.Vector3().addVectors(min.negate(), max).multiplyScalar(0.5); + const { min, max } = new THREE.Box3().setFromObject(this.el.object3DMap.mesh); + let halfExtents = new THREE.Vector3() + .addVectors(min.clone().negate(), max) + .multiplyScalar(0.5 / this.el.object3D.scale.x); this.el.setAttribute("shape", { shape: "box", - halfExtents: halfExtents.multiplyScalar(1 / initialScale.x), + halfExtents: halfExtents, offset: new THREE.Vector3(0, halfExtents.y, 0) }); - if (this.data.setInitialScale) { - this.setInitialScale(); + if (this.data.resize) { + this.resize(min, max); } - this.el.setAttribute("rotation", initialRotation); + this.el.setAttribute("rotation", rotation); + this.el.removeAttribute("auto-box-collider"); }, - // Adjust the scale such that the object fits within a box whose longest component axis is lengthOfLongestComponent meters. - // In other words, a tall model will be scaled down to be only half a meter tall, a wide model will be scaled down to be only - // half a meter wide, a long model will be scaled down to be only half a meter long. - setInitialScale() { - const worldBox = new THREE.Box3().setFromObject(this.el.object3DMap.mesh); - const dX = Math.abs(worldBox.max.x - worldBox.min.x); - const dY = Math.abs(worldBox.max.y - worldBox.min.y); - const dZ = Math.abs(worldBox.max.z - worldBox.min.z); - const max = Math.max(dX, dY, dZ); + + // Adjust the scale such that the object fits within a box of a specified size. + resize(min, max) { + const dX = Math.abs(max.x - min.x); + const dY = Math.abs(max.y - min.y); + const dZ = Math.abs(max.z - min.z); + const lengthOfLongestComponent = Math.max(dX, dY, dZ); + const correctiveFactor = this.data.resizeLength / lengthOfLongestComponent; const scale = this.el.object3D.scale; - const lengthOfLongestComponent = 0.5; - const correctiveFactor = lengthOfLongestComponent / max; this.el.setAttribute("scale", { x: scale.x * correctiveFactor, y: scale.y * correctiveFactor, diff --git a/src/components/auto-scale-cannon-physics-body.js b/src/components/auto-scale-cannon-physics-body.js index 578ad1123de86806af071f6bb48afd7e97cc27a6..f5ae24b5bb906f623eb1ba52111583884d64ff87 100644 --- a/src/components/auto-scale-cannon-physics-body.js +++ b/src/components/auto-scale-cannon-physics-body.js @@ -1,5 +1,5 @@ -function almostEquals(u, v, eps) { - return Math.abs(u.x - v.x) < eps && Math.abs(u.y - v.y) < eps && Math.abs(u.z - v.z) < eps; +function almostEquals(u, v, epsilon) { + return Math.abs(u.x - v.x) < epsilon && Math.abs(u.y - v.y) < epsilon && Math.abs(u.z - v.z) < epsilon; } function debounce(fn, delay) { @@ -13,25 +13,27 @@ function debounce(fn, delay) { }; } -AFRAME.registerComponent("auto-scale-cannon-physics-body", { +const autoScaleCannonPhysicsBody = { dependencies: ["body"], - init: function() { + init() { this.body = this.el.components["body"]; this.prevScale = this.el.object3D.scale.clone(); - this.updateCannonScale = debounce(this.updateCannonScale.bind(this), 200); + this.updateCannonScale = debounce(this.updateCannonScale.bind(this), 100); }, - tick: function() { + tick() { const scale = this.el.object3D.scale; + // Note: This only checks if the LOCAL scale of the object3D changes. if (!almostEquals(scale, this.prevScale, 0.001)) { this.updateCannonScale(); this.prevScale.copy(scale); } }, - updateCannonScale: function() { + updateCannonScale() { this.body.updateCannonScale(); - console.log("updating?"); } -}); +}; + +AFRAME.registerComponent("auto-scale-cannon-physics-body", autoScaleCannonPhysicsBody); diff --git a/src/components/position-at-bounding-radius.js b/src/components/position-at-box-border.js similarity index 60% rename from src/components/position-at-bounding-radius.js rename to src/components/position-at-box-border.js index 96d3aa9486d731316a42fa1527553645e217ecd8..8485b496a757d2fb486cff8a2015ead31629332f 100644 --- a/src/components/position-at-bounding-radius.js +++ b/src/components/position-at-box-border.js @@ -1,55 +1,43 @@ -const PI_HALF = Math.PI / 2; +const PI = Math.PI; +const PI_HALF = PI / 2; +const THREE_PI_HALF = 3 * PI / 2; const right = new THREE.Vector3(1, 0, 0); const forward = new THREE.Vector3(0, 0, 1); const left = new THREE.Vector3(-1, 0, 0); const back = new THREE.Vector3(0, 0, -1); +const dirs = [left, right, forward, back]; +const rotations = [THREE_PI_HALF, PI_HALF, 0, PI]; function almostEquals(u, v, eps) { return Math.abs(u.x - v.x) < eps && Math.abs(u.y - v.y) < eps && Math.abs(u.z - v.z) < eps; } AFRAME.registerComponent("position-at-box-border", { - dependencies: ["shape"], schema: { target: { type: "string" } }, init() { - window.p = this; - this.body = this.el.components["shape"]; this.position = new THREE.Vector3(); this.prevScale = new THREE.Vector3(-1, -1, -1); - this.rotation = this.el.object3D.rotation.clone(); - this.el.addEventListener("model-loaded", () => { - this.modelLoaded = true; - this.target = this.el.querySelector(this.data.target).object3D; - }); - this.worldPos = new THREE.Vector3(); this.cam = document.querySelector("[camera]").object3D; this.camWorldPos = new THREE.Vector3(); }, tick() { - if (!this.modelLoaded) return; + if (!this.shape) { + this.shape = this.el.components["shape"]; + if (!this.shape) return; + } if (!this.target) { - console.log("uh oh"); this.target = this.el.querySelector(this.data.target).object3D; + if (!this.target) return; } - - const scale = this.el.object3D.scale; - if (!almostEquals(scale, this.prevScale, 0.001)) { - this.target.scale.set(1 / (2 * scale.x), 1 / (2 * scale.y), 1 / (2 * scale.z)); - this.prevScale.copy(scale); - } - - this.el.object3D.getWorldPosition(this.worldPos); + const halfExtents = this.shape.data.halfExtents; + const halfExtentDirs = [halfExtents.x, halfExtents.x, halfExtents.z, halfExtents.z]; this.cam.getWorldPosition(this.camWorldPos); - const halfExtents = this.body.data.halfExtents; - const dirs = [left, right, forward, back]; - const rotations = [3 * PI_HALF, PI_HALF, 0, 2 * PI_HALF]; - const halfExtentDirs = [halfExtents.x, halfExtents.x, halfExtents.z, halfExtents.z]; let minDistance = Infinity; - let minDir = left; + let minDir = dirs[0]; let minHalfExtentDir = halfExtentDirs[0]; let minRotation = rotations[0]; for (let i = 0; i < dirs.length; i++) { @@ -64,14 +52,13 @@ AFRAME.registerComponent("position-at-box-border", { minRotation = rotations[i]; } } + this.target.position.copy( minDir .clone() .multiplyScalar(minHalfExtentDir) - .add(this.body.data.offset) + .add(this.shape.data.offset) ); - // this.lookTarget = minDir.clone().multiplyScalar(30000); - // this.el.object3D.localToWorld(this.lookTarget); this.target.rotation.set(0, minRotation, 0); } }); diff --git a/src/components/reposition-relative-to-target.js b/src/components/reposition-relative-to-target.js new file mode 100644 index 0000000000000000000000000000000000000000..760c48cac4d591827049d6c11a0d4bcd410b9ed0 --- /dev/null +++ b/src/components/reposition-relative-to-target.js @@ -0,0 +1,28 @@ +AFRAME.registerComponent("reposition-relative-to-target", { + schema: { + on: { type: "string" }, + target: { type: "selector" }, + offset: { type: "vec3" } + }, + + init() { + this.reposition = this.reposition.bind(this); + if (this.data.on) { + this.el.addEventListener(this.data.on, this.reposition); + } else { + this.reposition(); + } + }, + + reposition() { + const obj = this.el.object3D; + const target = this.data.target.object3D; + const position = new THREE.Vector3().copy(this.data.offset); + target.localToWorld(position); + obj.position.copy(position); + target.getWorldQuaternion(obj.quaternion); + + this.el.removeEventListener(this.data.on, this.reposition); + this.el.removeAttribute("reposition-relative-to-target"); + } +}); diff --git a/src/components/spawn-in-front-of-object.js b/src/components/spawn-in-front-of-object.js deleted file mode 100644 index 0a5d0c0d6a63aab2e845acdae548fde881d317cb..0000000000000000000000000000000000000000 --- a/src/components/spawn-in-front-of-object.js +++ /dev/null @@ -1,14 +0,0 @@ -AFRAME.registerComponent("spawn-in-front-of-object", { - schema: {}, - init() { - this.onLoaded = this.onLoaded.bind(this); - this.el.addEventListener("model-loaded", this.onLoaded); - }, - onLoaded() { - const billboardTarget = document.querySelector("#player-camera").object3D; - const worldPos = new THREE.Vector3().copy({ x: 0, y: 0, z: -1.5 }); - billboardTarget.localToWorld(worldPos); - this.el.object3D.position.copy(worldPos); - billboardTarget.getWorldQuaternion(this.el.object3D.quaternion); - } -}); diff --git a/src/hub.html b/src/hub.html index 2c473fd18bc8a054e18eb14e2e504966218c5de8..221240c6b8863ece4f137e26d7d6d38a6d991ea7 100644 --- a/src/hub.html +++ b/src/hub.html @@ -182,11 +182,11 @@ stretchable="useWorldPosition: true; usePhysics: never" hoverable sticky-object="autoLockOnRelease: true; autoLockOnLoad: true;" - auto-scale-cannon-physics-body auto-box-collider position-at-box-border="target:.deleteButton" + auto-scale-cannon-physics-body > - <a-entity class="deleteButton" visible-while-frozen> + <a-entity class="deleteButton" visible-while-frozen scale="0.08 0.08 0.08"> <a-entity mixin="rounded-text-button" remove-object-button position="0 0 0"> </a-entity> <a-entity text=" value:Delete; width:2.5; align:center;" text-raycast-hack position="0 0 0.01"></a-entity> </a-entity> @@ -361,7 +361,6 @@ collisionEntities: [nav-mesh]; drawIncrementally: true; incrementalDrawMs: 600; - curveShootingSpeed: 5; hitOpacity: 0.3; missOpacity: 0.2;" haptic-feedback diff --git a/src/hub.js b/src/hub.js index c0d0687b0606a69ad975ff3d34cf165c95b61b33..9dfac3c7d01615318b31ea1a075f7d814c2f9123 100644 --- a/src/hub.js +++ b/src/hub.js @@ -65,14 +65,14 @@ import "./components/scene-shadow"; import "./components/avatar-replay"; import "./components/image-plus"; import "./components/auto-box-collider"; -import "./components/spawn-in-front-of-object"; +import "./components/reposition-relative-to-target"; import "./components/pinch-to-move"; import "./components/look-on-mobile"; import "./components/pitch-yaw-rotator"; import "./components/input-configurator"; import "./components/sticky-object"; import "./components/auto-scale-cannon-physics-body"; -import "./components/position-at-bounding-radius"; +import "./components/position-at-box-border"; import ReactDOM from "react-dom"; import React from "react"; @@ -302,15 +302,19 @@ const onReady = async () => { NAF.connection.entities.completeSync(ev.detail.clientId); }); + const offset = { x: 0, y: 0, z: -1.5 }; const addMedia = mediaUrl => { const scene = AFRAME.scenes[0]; if (mediaUrl.endsWith(".gltf") || mediaUrl.endsWith(".glb")) { const model = document.createElement("a-entity"); model.id = "interactable-model-" + Date.now(); - model.setAttribute("position", { x: 0, y: 2, z: 1 }); - model.setAttribute("spawn-in-front-of-object", ""); + model.setAttribute("reposition-relative-to-target", { + on: "model-loaded", + target: "#player-camera", + offset: offset + }); model.setAttribute("gltf-model-plus", "src", mediaUrl); - model.setAttribute("auto-box-collider", "setInitialScale", true); + model.setAttribute("auto-box-collider", { resize: true }); model.setAttribute("networked", { template: "#interactable-model" }); scene.appendChild(model); } else { diff --git a/yarn.lock b/yarn.lock index 22aa09c6bab961a20f5d21463c5f60fbafb269cf..7e9cfcb55b2e8b5622d3ec4af2cf62a56cc678d0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -184,11 +184,11 @@ aframe-physics-extras@^0.1.3: "aframe-physics-system@https://github.com/mozillareality/aframe-physics-system#monitor-scale-on-body": version "3.1.2" - resolved "https://github.com/mozillareality/aframe-physics-system#4c83e7bdd666b054e31e229576404bbc069595b6" + resolved "https://github.com/mozillareality/aframe-physics-system#7cd2bf83d125194cc76aa48bce700884de113215" dependencies: browserify "^14.3.0" budo "^10.0.3" - cannon "https://github.com/mozillareality/cannon.js#resize-cylinder" + cannon "github:donmccurdy/cannon.js#v0.6.2-dev1" three-to-cannon "^1.3.0" webworkify "^1.4.0" @@ -1748,9 +1748,9 @@ caniuse-lite@^1.0.30000792: version "1.0.30000810" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000810.tgz#47585fffce0e9f3593a6feea4673b945424351d9" -"cannon@https://github.com/mozillareality/cannon.js#resize-cylinder": +"cannon@github:donmccurdy/cannon.js#v0.6.2-dev1": version "0.6.2" - resolved "https://github.com/mozillareality/cannon.js#b401ccbec3b9996e31d6ad060028065a432fcff7" + resolved "https://codeload.github.com/donmccurdy/cannon.js/tar.gz/022e8ba53fa83abf0ad8a0e4fd08623123838a17" caseless@~0.11.0: version "0.11.0"