cannon-es-control
Version:
A lightweight 3D physics engine written in JavaScript with control system tools
181 lines (158 loc) • 5.25 kB
text/typescript
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<string, any> = { keys: [] }
const Broadphase_makePairsUnique_p1: Body[] = []
const Broadphase_makePairsUnique_p2: Body[] = []
const bsc_dist = new Vec3()