123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372 |
- /** @format */
- import {Agent} from './Agent'
- import {cBody} from './Body'
- import {Dirty, Trigger, cObject} from './Object'
- import {ShapeSupport} from './Shape'
- export class cCollider {
- private id: number = 0
- private pools: Array<cBody> = []
- private static _inst: cCollider = null
- static get inst() {
- if (this._inst == null) {
- this._inst = new cCollider()
- }
- return this._inst
- }
- private axis: number = -1
- private frameID: number = 0
- private bodys: Array<cBody> = []
- private isDirty: boolean = false
- private pairs: Map<number, any> = new Map()
- // 创建 body
- create(obj: cObject) {
- let body = this.pools.pop()
- if (!body) {
- body = new cBody(obj)
- body.id = this.id++
- return body
- }
- body.object = obj
- return body
- }
- //插入 body, force 强制更新数据
- insert(body: cBody, force: boolean = false) {
- if (!body) return
- if (!body.inCollider) {
- //不在列表,重新插入
- this.bodys.push(body)
- body.inCollider = true
- }
- //复位状态
- body.isRemove = false
- body.isRetrieve = false
- //强制刷新body数据
- if (force && body.object) {
- body.object.isDirty = Dirty.RTS
- }
- }
- //删除 body: 提前标记删除 ,retrieve: 是否回收body
- remove(body: cBody, retrieve: boolean = false) {
- if (!body) return
- //标记移除body,
- body.isRemove = true //下一帧update中执行移除
- body.isRetrieve = retrieve //是否回收复用body?
- }
- reset() {
- //重置回收bodys
- this.axis = -1
- this.frameID = 0
- this.isDirty = true
- //回收bodys
- let bodys = this.bodys
- for (let i = bodys.length - 1; i >= 0; i--) {
- let body = bodys[i]
- this.pools.push(body)
- body.clear()
- }
- bodys.length = 0
- }
- clear() {
- //退出释放bodys
- this.id = 0
- this.axis = -1
- this.frameID = 0
- this.isDirty = true
- this.pools.length = 0
- //清空bodys
- let bodys = this.bodys
- for (let i = bodys.length - 1; i >= 0; i--) {
- bodys[i].clear()
- }
- bodys.length = 0
- }
- update(dt: number) {
- this.reBuild()
- this.triggers()
- Agent.inst.process(this.bodys)
- }
- //相交碰撞测试
- private triggers(): void {
- //resultCB: (a: Body, b: Body) => void
- ++this.frameID
- let axis = this.axis,
- n = (axis >> 2) & 0x3,
- m = (axis >> 4) & 0x3
- let bodys = this.bodys
- let agentMgr = Agent.inst
- let i = 0,
- j = 0,
- N = bodys.length
- for (i = 0; i < N; i++) {
- let bi = bodys[i]
- if (bi.isRemove) continue
- let A = bi.aabb,
- an = A[n],
- am = A[m],
- mask = bi.mask,
- group = bi.group,
- upper = bi.upper,
- objA = bi.object
- for (j = i + 1; j < N; j++) {
- let bj = bodys[j]
- if (bj.isRemove) continue
- if (upper < bj.lower) {
- break
- }
- let B = bj.aabb,
- objB = bj.object
- let a2b = mask & bj.group,
- b2a = bj.mask & group
- if (!(an > B[n + 3] || B[n] > A[n + 3] || am > B[m + 3] || B[m] > A[m + 3])) {
- if (bi.isAgent && bj.isAgent) {
- let priority = bi.priority - bj.priority
- if (priority <= 0) agentMgr.check(bi, bj)
- if (priority >= 0) agentMgr.check(bj, bi)
- }
- if (a2b || b2a) {
- let at = objA.shape.type
- let bt = objB.shape.type
- if (at > bt) {
- if (!ShapeSupport[bt | at](bj, bi)) continue
- } else {
- if (!ShapeSupport[at | bt](bi, bj)) continue
- }
- this.onTrigger(bi, bj, (a2b ? 1 : 0) | (b2a ? 2 : 0))
- }
- }
- }
- }
- this.endTrigger()
- }
- private onTrigger(bi: cBody, bj: cBody, state: number) {
- let trigger = 0,
- id = 0,
- aid = bi.id,
- bid = bj.id
- if (aid < bid) {
- id = aid
- aid = bid
- bid = id
- }
- id = ((aid * (aid + 1)) >> 1) + bid - 1
- let pairs = this.pairs
- let data = pairs.get(id)
- if (data !== undefined) {
- trigger = Trigger.stay
- data.frameID = this.frameID
- data.state = state
- } else {
- trigger = Trigger.enter
- pairs.set(id, {id: id, a: bi, b: bj, frameID: this.frameID, state: state})
- }
- let objA = bi.object
- if (state & 1 && objA && objA.trigger && !bi.isRemove) {
- objA.onTrigger(bj, trigger)
- }
- let objB = bj.object
- if (state & 2 && objB && objB.trigger && !bj.isRemove) {
- objB.onTrigger(bi, trigger)
- }
- }
- private endTrigger() {
- let deletes = []
- let pairs = this.pairs
- let length = pairs.size
- let frameID = this.frameID
- let entries = pairs.values()
- for (let i = 0; i < length; i++) {
- let value = entries.next().value
- let bi = value.a
- let bj = value.b
- if (value.frameID != frameID || bi.isRemove || bi.isRemove) {
- let objA = bi.object
- if (objA && objA.trigger && !bi.isRemove) objA.onTrigger(bj, Trigger.exit)
- let objB = bj.object
- if (objB && objB.trigger && !bj.isRemove) objB.onTrigger(bi, Trigger.exit)
- deletes.push(value.id)
- }
- }
- length = deletes.length - 1
- while (length >= 0) {
- pairs.delete(deletes[length--])
- }
- deletes.length = 0
- }
- private reBuild(): void {
- let change = false
- let axis = this.preBuildAxis()
- if ((axis & 0x3) != (this.axis & 0x3) || this.axis < 0) {
- this.axis = axis
- change = true
- }
- if (change || this.isDirty) {
- this.isDirty = false
- let bodys = this.bodys
- axis = this.axis & 0x3
- for (let i = 0, N = bodys.length; i !== N; i++) {
- let bi = bodys[i]
- let aabb = bi.aabb
- bi.lower = aabb[axis]
- bi.upper = aabb[axis + 3]
- bi.object.curInBody.length = 0
- bi.object.curOutBody.length = 0
- bi.object.removeNotInBody()
- }
- if (!change) this.sort(bodys)
- else bodys.sort((a: cBody, b: cBody) => a.lower - b.lower)
- }
- }
- private sort(a: Array<cBody>): void {
- let i = 0,
- j = 0,
- l = 0
- for (i = 1, l = a.length; i < l; i++) {
- let v = a[i]
- let lower = v.lower
- for (j = i - 1; j >= 0; j--) {
- let w = a[j]
- if (w.lower <= lower) {
- break
- }
- a[j + 1] = w
- }
- if (j + 1 != i) {
- a[j + 1] = v
- }
- }
- }
- private preBuildAxis(): number {
- let axis = 0,
- sumX = 0,
- sumX2 = 0,
- sumY = 0,
- sumY2 = 0,
- sumZ = 0,
- sumZ2 = 0,
- x = 0.0,
- y = 0.0,
- z = 0.0
- let bodys = this.bodys
- let N = bodys.length
- let length = 0
- let isDirty = false
- for (let i = 0; i < N; i++) {
- let body = bodys[i]
- //删除body
- if (body.isRemove) {
- //是否回收body
- if (body.isRetrieve) {
- this.pools.push(body)
- body.clear()
- }
- //已从collider移除
- body.inCollider = false
- continue
- }
- if (++length <= i) {
- bodys[length - 1] = body
- }
- if (body.updateBound()) isDirty = true
- let s = body.aabb,
- sx = s[3] - s[0],
- sy = s[4] - s[1],
- sz = s[5] - s[2]
- ;(x += sx * sx), (y += sy * sy)
- z += sz * sz
- let cX = (s[3] + s[0]) * 0.5
- ;(sumX += cX), (sumX2 += cX * cX)
- let cY = (s[4] + s[1]) * 0.5
- ;(sumY += cY), (sumY2 += cY * cY)
- let cZ = (s[5] + s[2]) * 0.5
- ;(sumZ += cZ), (sumZ2 += cZ * cZ)
- }
- this.bodys.length = length
- this.isDirty = isDirty
- let invN = 1.0 / length
- x = x > 0 ? length / x : 0
- y = y > 0 ? length / y : 0
- z = z > 0 ? length / z : 0
- let X = (sumX2 - sumX * sumX * invN) * x
- let Y = (sumY2 - sumY * sumY * invN) * y
- let Z = (sumZ2 - sumZ * sumZ * invN) * z
- if (X == 0) X = x
- if (Y == 0) Y = y
- if (Z == 0) Z = z
- if (X > Y) {
- if (X > Z) {
- axis = 0
- axis |= Y > Z ? (1 << 2) | (2 << 4) : (1 << 4) | (2 << 2) //yz:zy;
- } else {
- axis = 2
- axis |= X > Y ? (0 << 2) | (1 << 4) : (0 << 4) | (1 << 2) //xy:yx;
- }
- } else if (Y > Z) {
- axis = 1
- axis |= X > Z ? (0 << 2) | (2 << 4) : (0 << 4) | (2 << 2) //xz:zx;
- } else {
- axis = 2
- axis |= X > Y ? (0 << 2) | (1 << 4) : (0 << 4) | (1 << 2) //xy:yx;
- }
- return axis
- }
- }
|