From 68163cb69466a75a8eddb5a701eb5bfa2abb8758 Mon Sep 17 00:00:00 2001 From: Brian Peiris <brianpeiris@gmail.com> Date: Mon, 17 Sep 2018 18:32:58 -0700 Subject: [PATCH] Add heightfield component --- src/components/heightfield.js | 68 ++++++++++++++++++++++++++++++++++ src/gltf-component-mappings.js | 1 + src/hub.js | 1 + 3 files changed, 70 insertions(+) create mode 100644 src/components/heightfield.js diff --git a/src/components/heightfield.js b/src/components/heightfield.js new file mode 100644 index 000000000..cdb6b0014 --- /dev/null +++ b/src/components/heightfield.js @@ -0,0 +1,68 @@ +/* global CANNON */ +AFRAME.registerComponent("heightfield", { + init() { + this.el.addEventListener("componentinitialized", e => { + if (e.detail.name === "static-body") { + this.initShape(this.el.components["static-body"]); + } + }); + this.el.setAttribute("static-body", { shape: "none", mass: 0 }); + }, + initShape(body) { + console.log("BPDEBUG body", body); + const mesh = this.el.object3D.getObjectByProperty("type", "Mesh"); + mesh.geometry.computeBoundingBox(); + const size = new THREE.Vector3(); + mesh.geometry.boundingBox.getSize(size); + + const minDistance = 0.25; + const resolution = (size.x + size.z) / 2 / minDistance; + const distance = Math.max(minDistance, (size.x + size.z) / 2 / resolution); + + const data = []; + const down = new THREE.Vector3(0, -1, 0); + const position = new THREE.Vector3(); + const raycaster = new THREE.Raycaster(); + const intersections = []; + const meshPos = new THREE.Vector3(); + mesh.getWorldPosition(meshPos); + const offsetX = -size.x / 2 + meshPos.x; + const offsetZ = -size.z / 2 + meshPos.z; + let min = Infinity; + for (let z = 0; z < resolution; z++) { + data[z] = []; + for (let x = 0; x < resolution; x++) { + position.set(offsetX + x * distance, size.y / 2, offsetZ + z * distance); + raycaster.set(position, down); + intersections.length = 0; + raycaster.intersectObject(mesh, false, intersections); + let val; + if (intersections.length) { + val = -intersections[0].distance + size.y / 2; + } else { + val = -size.y / 2; + } + data[z][x] = val; + if (val < min) { + min = data[z][x]; + } + } + } + // Cannon doesn't like heightfields with negative heights. + for (let z = 0; z < resolution; z++) { + for (let x = 0; x < resolution; x++) { + data[z][x] -= min; + } + } + + const orientation = new CANNON.Quaternion(); + orientation.setFromAxisAngle(new CANNON.Vec3(1, 0, 0), -Math.PI / 2); + const rotation = new CANNON.Quaternion(); + rotation.setFromAxisAngle(new CANNON.Vec3(0, 1, 0), -Math.PI / 2); + rotation.mult(orientation, orientation); + const offset = new CANNON.Vec3(-size.x / 2, min, -size.z / 2); + + const shape = new CANNON.Heightfield(data, { elementSize: distance }); + body.addShape(shape, offset, orientation); + } +}); diff --git a/src/gltf-component-mappings.js b/src/gltf-component-mappings.js index 5dde948e6..3576a1049 100644 --- a/src/gltf-component-mappings.js +++ b/src/gltf-component-mappings.js @@ -28,6 +28,7 @@ AFRAME.GLTFModelPlus.registerComponent("scale-audio-feedback", "scale-audio-feed AFRAME.GLTFModelPlus.registerComponent("animation-mixer", "animation-mixer"); AFRAME.GLTFModelPlus.registerComponent("loop-animation", "loop-animation"); AFRAME.GLTFModelPlus.registerComponent("shape", "shape"); +AFRAME.GLTFModelPlus.registerComponent("heightfield", "heightfield"); AFRAME.GLTFModelPlus.registerComponent( "box-collider", "shape", diff --git a/src/hub.js b/src/hub.js index 6bc7382c8..40c4f178e 100644 --- a/src/hub.js +++ b/src/hub.js @@ -133,6 +133,7 @@ import "./components/cardboard-controls"; import "./components/cursor-controller"; +import "./components/heightfield"; import "./components/nav-mesh-helper"; import "./systems/tunnel-effect"; -- GitLab