import { Shape } from '../shapes/Shape' import { Vec3 } from '../math/Vec3' import { ConvexPolyhedron } from '../shapes/ConvexPolyhedron' import type { Quaternion } from '../math/Quaternion' /** * A 3d box shape. * @example * const size = 1 * const halfExtents = new CANNON.Vec3(size, size, size) * const boxShape = new CANNON.Box(halfExtents) * const boxBody = new CANNON.Body({ mass: 1, shape: boxShape }) * world.addBody(boxBody) */ export class Box extends Shape { /** * The half extents of the box. */ halfExtents: Vec3 /** * Used by the contact generator to make contacts with other convex polyhedra for example. */ convexPolyhedronRepresentation: ConvexPolyhedron constructor(halfExtents: Vec3) { super({ type: Shape.types.BOX }) this.halfExtents = halfExtents this.convexPolyhedronRepresentation = (null as unknown) as ConvexPolyhedron this.updateConvexPolyhedronRepresentation() this.updateBoundingSphereRadius() } /** * Updates the local convex polyhedron representation used for some collisions. */ updateConvexPolyhedronRepresentation(): void { const sx = this.halfExtents.x const sy = this.halfExtents.y const sz = this.halfExtents.z const V = Vec3 const vertices = [ new V(-sx, -sy, -sz), new V(sx, -sy, -sz), new V(sx, sy, -sz), new V(-sx, sy, -sz), new V(-sx, -sy, sz), new V(sx, -sy, sz), new V(sx, sy, sz), new V(-sx, sy, sz), ] const faces = [ [3, 2, 1, 0], // -z [4, 5, 6, 7], // +z [5, 4, 0, 1], // -y [2, 3, 7, 6], // +y [0, 4, 7, 3], // -x [1, 2, 6, 5], // +x ] const axes = [new V(0, 0, 1), new V(0, 1, 0), new V(1, 0, 0)] const h = new ConvexPolyhedron({ vertices, faces, axes }) this.convexPolyhedronRepresentation = h h.material = this.material } /** * Calculate the inertia of the box. */ calculateLocalInertia(mass: number, target = new Vec3()): Vec3 { Box.calculateInertia(this.halfExtents, mass, target) return target } static calculateInertia(halfExtents: Vec3, mass: number, target: Vec3): void { const e = halfExtents target.x = (1.0 / 12.0) * mass * (2 * e.y * 2 * e.y + 2 * e.z * 2 * e.z) target.y = (1.0 / 12.0) * mass * (2 * e.x * 2 * e.x + 2 * e.z * 2 * e.z) target.z = (1.0 / 12.0) * mass * (2 * e.y * 2 * e.y + 2 * e.x * 2 * e.x) } /** * Get the box 6 side normals * @param sixTargetVectors An array of 6 vectors, to store the resulting side normals in. * @param quat Orientation to apply to the normal vectors. If not provided, the vectors will be in respect to the local frame. */ getSideNormals(sixTargetVectors: Vec3[], quat: Quaternion): Vec3[] { const sides = sixTargetVectors const ex = this.halfExtents sides[0].set(ex.x, 0, 0) sides[1].set(0, ex.y, 0) sides[2].set(0, 0, ex.z) sides[3].set(-ex.x, 0, 0) sides[4].set(0, -ex.y, 0) sides[5].set(0, 0, -ex.z) if (quat !== undefined) { for (let i = 0; i !== sides.length; i++) { quat.vmult(sides[i], sides[i]) } } return sides } /** * Returns the volume of the box. */ volume(): number { return 8.0 * this.halfExtents.x * this.halfExtents.y * this.halfExtents.z } /** * updateBoundingSphereRadius */ updateBoundingSphereRadius(): void { this.boundingSphereRadius = this.halfExtents.length() } /** * forEachWorldCorner */ forEachWorldCorner(pos: Vec3, quat: Quaternion, callback: (x: number, y: number, z: number) => void): void { const e = this.halfExtents const corners = [ [e.x, e.y, e.z], [-e.x, e.y, e.z], [-e.x, -e.y, e.z], [-e.x, -e.y, -e.z], [e.x, -e.y, -e.z], [e.x, e.y, -e.z], [-e.x, e.y, -e.z], [e.x, -e.y, e.z], ] for (let i = 0; i < corners.length; i++) { worldCornerTempPos.set(corners[i][0], corners[i][1], corners[i][2]) quat.vmult(worldCornerTempPos, worldCornerTempPos) pos.vadd(worldCornerTempPos, worldCornerTempPos) callback(worldCornerTempPos.x, worldCornerTempPos.y, worldCornerTempPos.z) } } /** * calculateWorldAABB */ calculateWorldAABB(pos: Vec3, quat: Quaternion, min: Vec3, max: Vec3): void { const e = this.halfExtents worldCornersTemp[0].set(e.x, e.y, e.z) worldCornersTemp[1].set(-e.x, e.y, e.z) worldCornersTemp[2].set(-e.x, -e.y, e.z) worldCornersTemp[3].set(-e.x, -e.y, -e.z) worldCornersTemp[4].set(e.x, -e.y, -e.z) worldCornersTemp[5].set(e.x, e.y, -e.z) worldCornersTemp[6].set(-e.x, e.y, -e.z) worldCornersTemp[7].set(e.x, -e.y, e.z) const wc = worldCornersTemp[0] quat.vmult(wc, wc) pos.vadd(wc, wc) max.copy(wc) min.copy(wc) for (let i = 1; i < 8; i++) { const wc = worldCornersTemp[i] quat.vmult(wc, wc) pos.vadd(wc, wc) const x = wc.x const y = wc.y const z = wc.z if (x > max.x) { max.x = x } if (y > max.y) { max.y = y } if (z > max.z) { max.z = z } if (x < min.x) { min.x = x } if (y < min.y) { min.y = y } if (z < min.z) { min.z = z } } // Get each axis max // min.set(Infinity,Infinity,Infinity); // max.set(-Infinity,-Infinity,-Infinity); // this.forEachWorldCorner(pos,quat,function(x,y,z){ // if(x > max.x){ // max.x = x; // } // if(y > max.y){ // max.y = y; // } // if(z > max.z){ // max.z = z; // } // if(x < min.x){ // min.x = x; // } // if(y < min.y){ // min.y = y; // } // if(z < min.z){ // min.z = z; // } // }); } } const worldCornerTempPos = new Vec3() const worldCornersTemp = [ new Vec3(), new Vec3(), new Vec3(), new Vec3(), new Vec3(), new Vec3(), new Vec3(), new Vec3(), ]