import { Body } from '../objects/Body' import { Vec3 } from '../math/Vec3' import { Quaternion } from '../math/Quaternion' import type { AABB } from '../collision/AABB' import type { World } from '../world/World' /** * Base class for broadphase implementations * @author schteppe */ export class Broadphase { /** * The world to search for collisions in. */ world: World | null /** * If set to true, the broadphase uses bounding boxes for intersection tests, else it uses bounding spheres. */ useBoundingBoxes: boolean /** * Set to true if the objects in the world moved. */ dirty: boolean constructor() { this.world = null this.useBoundingBoxes = false this.dirty = true } /** * Get the collision pairs from the world * @param world The world to search in * @param p1 Empty array to be filled with body objects * @param p2 Empty array to be filled with body objects */ collisionPairs(world: World, p1: Body[], p2: Body[]): void { throw new Error('collisionPairs not implemented for this BroadPhase class!') } /** * Check if a body pair needs to be intersection tested at all. */ needBroadphaseCollision(bodyA: Body, bodyB: Body): boolean { // Check collision filter masks if ( (bodyA.collisionFilterGroup & bodyB.collisionFilterMask) === 0 || (bodyB.collisionFilterGroup & bodyA.collisionFilterMask) === 0 ) { return false } // Check types if ( ((bodyA.type & Body.STATIC) !== 0 || bodyA.sleepState === Body.SLEEPING) && ((bodyB.type & Body.STATIC) !== 0 || bodyB.sleepState === Body.SLEEPING) ) { // Both bodies are static or sleeping. Skip. return false } return true } /** * Check if the bounding volumes of two bodies intersect. */ intersectionTest(bodyA: Body, bodyB: Body, pairs1: Body[], pairs2: Body[]): void { if (this.useBoundingBoxes) { this.doBoundingBoxBroadphase(bodyA, bodyB, pairs1, pairs2) } else { this.doBoundingSphereBroadphase(bodyA, bodyB, pairs1, pairs2) } } /** * Check if the bounding spheres of two bodies are intersecting. * @param pairs1 bodyA is appended to this array if intersection * @param pairs2 bodyB is appended to this array if intersection */ doBoundingSphereBroadphase(bodyA: Body, bodyB: Body, pairs1: Body[], pairs2: Body[]): void { const r = Broadphase_collisionPairs_r bodyB.position.vsub(bodyA.position, r) const boundingRadiusSum2 = (bodyA.boundingRadius + bodyB.boundingRadius) ** 2 const norm2 = r.lengthSquared() if (norm2 < boundingRadiusSum2) { pairs1.push(bodyA) pairs2.push(bodyB) } } /** * Check if the bounding boxes of two bodies are intersecting. */ doBoundingBoxBroadphase(bodyA: Body, bodyB: Body, pairs1: Body[], pairs2: Body[]): void { if (bodyA.aabbNeedsUpdate) { bodyA.updateAABB() } if (bodyB.aabbNeedsUpdate) { bodyB.updateAABB() } // Check AABB / AABB if (bodyA.aabb.overlaps(bodyB.aabb)) { pairs1.push(bodyA) pairs2.push(bodyB) } } /** * Removes duplicate pairs from the pair arrays. */ makePairsUnique(pairs1: Body[], pairs2: Body[]): void { const t = Broadphase_makePairsUnique_temp const p1 = Broadphase_makePairsUnique_p1 const p2 = Broadphase_makePairsUnique_p2 const N = pairs1.length for (let i = 0; i !== N; i++) { p1[i] = pairs1[i] p2[i] = pairs2[i] } pairs1.length = 0 pairs2.length = 0 for (let i = 0; i !== N; i++) { const id1 = p1[i].id const id2 = p2[i].id const key = id1 < id2 ? `${id1},${id2}` : `${id2},${id1}` t[key] = i t.keys.push(key) } for (let i = 0; i !== t.keys.length; i++) { const key = t.keys.pop() const pairIndex = t[key] pairs1.push(p1[pairIndex]) pairs2.push(p2[pairIndex]) delete t[key] } } /** * To be implemented by subcasses */ setWorld(world: World): void {} /** * Check if the bounding spheres of two bodies overlap. */ static boundingSphereCheck(bodyA: Body, bodyB: Body): boolean { const dist = new Vec3() // bsc_dist; bodyA.position.vsub(bodyB.position, dist) const sa = bodyA.shapes[0] const sb = bodyB.shapes[0] return Math.pow(sa.boundingSphereRadius + sb.boundingSphereRadius, 2) > dist.lengthSquared() } /** * Returns all the bodies within the AABB. */ aabbQuery(world: World, aabb: AABB, result: Body[]): Body[] { console.warn('.aabbQuery is not implemented in this Broadphase subclass.') return [] } } // Temp objects const Broadphase_collisionPairs_r = new Vec3() const Broadphase_collisionPairs_normal = new Vec3() const Broadphase_collisionPairs_quat = new Quaternion() const Broadphase_collisionPairs_relpos = new Vec3() const Broadphase_makePairsUnique_temp: Record = { keys: [] } const Broadphase_makePairsUnique_p1: Body[] = [] const Broadphase_makePairsUnique_p2: Body[] = [] const bsc_dist = new Vec3()