Collider.ts 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372
  1. /** @format */
  2. import {Agent} from './Agent'
  3. import {cBody} from './Body'
  4. import {Dirty, Trigger, cObject} from './Object'
  5. import {ShapeSupport} from './Shape'
  6. export class cCollider {
  7. private id: number = 0
  8. private pools: Array<cBody> = []
  9. private static _inst: cCollider = null
  10. static get inst() {
  11. if (this._inst == null) {
  12. this._inst = new cCollider()
  13. }
  14. return this._inst
  15. }
  16. private axis: number = -1
  17. private frameID: number = 0
  18. private bodys: Array<cBody> = []
  19. private isDirty: boolean = false
  20. private pairs: Map<number, any> = new Map()
  21. // 创建 body
  22. create(obj: cObject) {
  23. let body = this.pools.pop()
  24. if (!body) {
  25. body = new cBody(obj)
  26. body.id = this.id++
  27. return body
  28. }
  29. body.object = obj
  30. return body
  31. }
  32. //插入 body, force 强制更新数据
  33. insert(body: cBody, force: boolean = false) {
  34. if (!body) return
  35. if (!body.inCollider) {
  36. //不在列表,重新插入
  37. this.bodys.push(body)
  38. body.inCollider = true
  39. }
  40. //复位状态
  41. body.isRemove = false
  42. body.isRetrieve = false
  43. //强制刷新body数据
  44. if (force && body.object) {
  45. body.object.isDirty = Dirty.RTS
  46. }
  47. }
  48. //删除 body: 提前标记删除 ,retrieve: 是否回收body
  49. remove(body: cBody, retrieve: boolean = false) {
  50. if (!body) return
  51. //标记移除body,
  52. body.isRemove = true //下一帧update中执行移除
  53. body.isRetrieve = retrieve //是否回收复用body?
  54. }
  55. reset() {
  56. //重置回收bodys
  57. this.axis = -1
  58. this.frameID = 0
  59. this.isDirty = true
  60. //回收bodys
  61. let bodys = this.bodys
  62. for (let i = bodys.length - 1; i >= 0; i--) {
  63. let body = bodys[i]
  64. this.pools.push(body)
  65. body.clear()
  66. }
  67. bodys.length = 0
  68. }
  69. clear() {
  70. //退出释放bodys
  71. this.id = 0
  72. this.axis = -1
  73. this.frameID = 0
  74. this.isDirty = true
  75. this.pools.length = 0
  76. //清空bodys
  77. let bodys = this.bodys
  78. for (let i = bodys.length - 1; i >= 0; i--) {
  79. bodys[i].clear()
  80. }
  81. bodys.length = 0
  82. }
  83. update(dt: number) {
  84. this.reBuild()
  85. this.triggers()
  86. Agent.inst.process(this.bodys)
  87. }
  88. //相交碰撞测试
  89. private triggers(): void {
  90. //resultCB: (a: Body, b: Body) => void
  91. ++this.frameID
  92. let axis = this.axis,
  93. n = (axis >> 2) & 0x3,
  94. m = (axis >> 4) & 0x3
  95. let bodys = this.bodys
  96. let agentMgr = Agent.inst
  97. let i = 0,
  98. j = 0,
  99. N = bodys.length
  100. for (i = 0; i < N; i++) {
  101. let bi = bodys[i]
  102. if (bi.isRemove) continue
  103. let A = bi.aabb,
  104. an = A[n],
  105. am = A[m],
  106. mask = bi.mask,
  107. group = bi.group,
  108. upper = bi.upper,
  109. objA = bi.object
  110. for (j = i + 1; j < N; j++) {
  111. let bj = bodys[j]
  112. if (bj.isRemove) continue
  113. if (upper < bj.lower) {
  114. break
  115. }
  116. let B = bj.aabb,
  117. objB = bj.object
  118. let a2b = mask & bj.group,
  119. b2a = bj.mask & group
  120. if (!(an > B[n + 3] || B[n] > A[n + 3] || am > B[m + 3] || B[m] > A[m + 3])) {
  121. if (bi.isAgent && bj.isAgent) {
  122. let priority = bi.priority - bj.priority
  123. if (priority <= 0) agentMgr.check(bi, bj)
  124. if (priority >= 0) agentMgr.check(bj, bi)
  125. }
  126. if (a2b || b2a) {
  127. let at = objA.shape.type
  128. let bt = objB.shape.type
  129. if (at > bt) {
  130. if (!ShapeSupport[bt | at](bj, bi)) continue
  131. } else {
  132. if (!ShapeSupport[at | bt](bi, bj)) continue
  133. }
  134. this.onTrigger(bi, bj, (a2b ? 1 : 0) | (b2a ? 2 : 0))
  135. }
  136. }
  137. }
  138. }
  139. this.endTrigger()
  140. }
  141. private onTrigger(bi: cBody, bj: cBody, state: number) {
  142. let trigger = 0,
  143. id = 0,
  144. aid = bi.id,
  145. bid = bj.id
  146. if (aid < bid) {
  147. id = aid
  148. aid = bid
  149. bid = id
  150. }
  151. id = ((aid * (aid + 1)) >> 1) + bid - 1
  152. let pairs = this.pairs
  153. let data = pairs.get(id)
  154. if (data !== undefined) {
  155. trigger = Trigger.stay
  156. data.frameID = this.frameID
  157. data.state = state
  158. } else {
  159. trigger = Trigger.enter
  160. pairs.set(id, {id: id, a: bi, b: bj, frameID: this.frameID, state: state})
  161. }
  162. let objA = bi.object
  163. if (state & 1 && objA && objA.trigger && !bi.isRemove) {
  164. objA.onTrigger(bj, trigger)
  165. }
  166. let objB = bj.object
  167. if (state & 2 && objB && objB.trigger && !bj.isRemove) {
  168. objB.onTrigger(bi, trigger)
  169. }
  170. }
  171. private endTrigger() {
  172. let deletes = []
  173. let pairs = this.pairs
  174. let length = pairs.size
  175. let frameID = this.frameID
  176. let entries = pairs.values()
  177. for (let i = 0; i < length; i++) {
  178. let value = entries.next().value
  179. let bi = value.a
  180. let bj = value.b
  181. if (value.frameID != frameID || bi.isRemove || bi.isRemove) {
  182. let objA = bi.object
  183. if (objA && objA.trigger && !bi.isRemove) objA.onTrigger(bj, Trigger.exit)
  184. let objB = bj.object
  185. if (objB && objB.trigger && !bj.isRemove) objB.onTrigger(bi, Trigger.exit)
  186. deletes.push(value.id)
  187. }
  188. }
  189. length = deletes.length - 1
  190. while (length >= 0) {
  191. pairs.delete(deletes[length--])
  192. }
  193. deletes.length = 0
  194. }
  195. private reBuild(): void {
  196. let change = false
  197. let axis = this.preBuildAxis()
  198. if ((axis & 0x3) != (this.axis & 0x3) || this.axis < 0) {
  199. this.axis = axis
  200. change = true
  201. }
  202. if (change || this.isDirty) {
  203. this.isDirty = false
  204. let bodys = this.bodys
  205. axis = this.axis & 0x3
  206. for (let i = 0, N = bodys.length; i !== N; i++) {
  207. let bi = bodys[i]
  208. let aabb = bi.aabb
  209. bi.lower = aabb[axis]
  210. bi.upper = aabb[axis + 3]
  211. bi.object.curInBody.length = 0
  212. bi.object.curOutBody.length = 0
  213. bi.object.removeNotInBody()
  214. }
  215. if (!change) this.sort(bodys)
  216. else bodys.sort((a: cBody, b: cBody) => a.lower - b.lower)
  217. }
  218. }
  219. private sort(a: Array<cBody>): void {
  220. let i = 0,
  221. j = 0,
  222. l = 0
  223. for (i = 1, l = a.length; i < l; i++) {
  224. let v = a[i]
  225. let lower = v.lower
  226. for (j = i - 1; j >= 0; j--) {
  227. let w = a[j]
  228. if (w.lower <= lower) {
  229. break
  230. }
  231. a[j + 1] = w
  232. }
  233. if (j + 1 != i) {
  234. a[j + 1] = v
  235. }
  236. }
  237. }
  238. private preBuildAxis(): number {
  239. let axis = 0,
  240. sumX = 0,
  241. sumX2 = 0,
  242. sumY = 0,
  243. sumY2 = 0,
  244. sumZ = 0,
  245. sumZ2 = 0,
  246. x = 0.0,
  247. y = 0.0,
  248. z = 0.0
  249. let bodys = this.bodys
  250. let N = bodys.length
  251. let length = 0
  252. let isDirty = false
  253. for (let i = 0; i < N; i++) {
  254. let body = bodys[i]
  255. //删除body
  256. if (body.isRemove) {
  257. //是否回收body
  258. if (body.isRetrieve) {
  259. this.pools.push(body)
  260. body.clear()
  261. }
  262. //已从collider移除
  263. body.inCollider = false
  264. continue
  265. }
  266. if (++length <= i) {
  267. bodys[length - 1] = body
  268. }
  269. if (body.updateBound()) isDirty = true
  270. let s = body.aabb,
  271. sx = s[3] - s[0],
  272. sy = s[4] - s[1],
  273. sz = s[5] - s[2]
  274. ;(x += sx * sx), (y += sy * sy)
  275. z += sz * sz
  276. let cX = (s[3] + s[0]) * 0.5
  277. ;(sumX += cX), (sumX2 += cX * cX)
  278. let cY = (s[4] + s[1]) * 0.5
  279. ;(sumY += cY), (sumY2 += cY * cY)
  280. let cZ = (s[5] + s[2]) * 0.5
  281. ;(sumZ += cZ), (sumZ2 += cZ * cZ)
  282. }
  283. this.bodys.length = length
  284. this.isDirty = isDirty
  285. let invN = 1.0 / length
  286. x = x > 0 ? length / x : 0
  287. y = y > 0 ? length / y : 0
  288. z = z > 0 ? length / z : 0
  289. let X = (sumX2 - sumX * sumX * invN) * x
  290. let Y = (sumY2 - sumY * sumY * invN) * y
  291. let Z = (sumZ2 - sumZ * sumZ * invN) * z
  292. if (X == 0) X = x
  293. if (Y == 0) Y = y
  294. if (Z == 0) Z = z
  295. if (X > Y) {
  296. if (X > Z) {
  297. axis = 0
  298. axis |= Y > Z ? (1 << 2) | (2 << 4) : (1 << 4) | (2 << 2) //yz:zy;
  299. } else {
  300. axis = 2
  301. axis |= X > Y ? (0 << 2) | (1 << 4) : (0 << 4) | (1 << 2) //xy:yx;
  302. }
  303. } else if (Y > Z) {
  304. axis = 1
  305. axis |= X > Z ? (0 << 2) | (2 << 4) : (0 << 4) | (2 << 2) //xz:zx;
  306. } else {
  307. axis = 2
  308. axis |= X > Y ? (0 << 2) | (1 << 4) : (0 << 4) | (1 << 2) //xy:yx;
  309. }
  310. return axis
  311. }
  312. }