@perplexdotgg/bounce 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (134) hide show
  1. package/build/bounce.d.ts +39501 -0
  2. package/build/bounce.js +17166 -0
  3. package/package.json +1 -1
  4. package/src/builders/ConvexHullBuilder.ts +0 -437
  5. package/src/builders/ConvexHullBuilder2d.ts +0 -344
  6. package/src/builders/ConvexHullBuilder3d.ts +0 -1689
  7. package/src/builders/HeightMapBuilder.ts +0 -414
  8. package/src/builders/TriangleMeshBuilder.ts +0 -92
  9. package/src/collision/CastShapesModule.ts +0 -184
  10. package/src/collision/CollideShapesModule.ts +0 -152
  11. package/src/collision/HeightMapCaster.ts +0 -38
  12. package/src/collision/HeightMapCollider.ts +0 -33
  13. package/src/collision/TriangleCaster.ts +0 -249
  14. package/src/collision/TriangleCollider.ts +0 -308
  15. package/src/collision/TriangleCollider2.ts +0 -379
  16. package/src/collision/activeEdge.ts +0 -146
  17. package/src/collision/cast/cast.ts +0 -139
  18. package/src/collision/cast/castCompoundVsCompound.ts +0 -59
  19. package/src/collision/cast/castCompoundVsConvex.ts +0 -116
  20. package/src/collision/cast/castConvexVsCompound.ts +0 -123
  21. package/src/collision/cast/castConvexVsConvex.ts +0 -213
  22. package/src/collision/cast/castConvexVsHeightMap.ts +0 -73
  23. package/src/collision/cast/castConvexVsTriangleMesh.ts +0 -56
  24. package/src/collision/cast/castRayVsCompound.ts +0 -44
  25. package/src/collision/cast/castRayVsConvex.ts +0 -45
  26. package/src/collision/cast/castRayVsHeightMap.ts +0 -58
  27. package/src/collision/cast/castRayVsTriangleMesh.ts +0 -58
  28. package/src/collision/closestPoints/closestPoints.ts +0 -23
  29. package/src/collision/closestPoints/computeBarycentricCoordinates2d.ts +0 -32
  30. package/src/collision/closestPoints/computeBarycentricCoordinates3d.ts +0 -81
  31. package/src/collision/closestPoints/computeClosestPointOnLine.ts +0 -30
  32. package/src/collision/closestPoints/computeClosestPointOnTetrahedron.ts +0 -96
  33. package/src/collision/closestPoints/computeClosestPointOnTriangle.ts +0 -195
  34. package/src/collision/closestPoints/isOriginOutsideOfPlane.ts +0 -25
  35. package/src/collision/closestPoints/isOriginOutsideOfTrianglePlanes.ts +0 -72
  36. package/src/collision/collide/collide.ts +0 -146
  37. package/src/collision/collide/collideCompoundVsCompound.ts +0 -60
  38. package/src/collision/collide/collideCompoundVsConvex.ts +0 -59
  39. package/src/collision/collide/collideCompoundVsHeightMap.ts +0 -73
  40. package/src/collision/collide/collideCompoundVsTriangleMesh.ts +0 -56
  41. package/src/collision/collide/collideConvexVsCompound.ts +0 -57
  42. package/src/collision/collide/collideConvexVsConvex.ts +0 -225
  43. package/src/collision/collide/collideConvexVsConvexImp.ts +0 -236
  44. package/src/collision/collide/collideConvexVsHeightMap.ts +0 -53
  45. package/src/collision/collide/collideConvexVsTriangleMesh.ts +0 -58
  46. package/src/collision/collide/collideHeightMapVsCompound.ts +0 -69
  47. package/src/collision/collide/collideHeightMapVsConvex.ts +0 -53
  48. package/src/collision/collide/collideSphereVsSphere.ts +0 -81
  49. package/src/collision/collide/collideTriangleMeshVsCompound.ts +0 -58
  50. package/src/collision/collide/collideTriangleMeshVsConvex.ts +0 -58
  51. package/src/collision/epa/EpaConvexHullBuilder.ts +0 -397
  52. package/src/collision/epa/StaticArray.ts +0 -154
  53. package/src/collision/epa/TriangleFactory.ts +0 -32
  54. package/src/collision/epa/arrays.ts +0 -99
  55. package/src/collision/epa/binaryHeap.ts +0 -82
  56. package/src/collision/epa/structs.ts +0 -227
  57. package/src/collision/gjk/GjkModule.ts +0 -864
  58. package/src/collision/gjk/PenetrationDepthModule.ts +0 -493
  59. package/src/collision/gjk/SupportPoints.ts +0 -50
  60. package/src/collision/imp/MinkowskiDifference.ts +0 -36
  61. package/src/collision/imp/computeExploredDistanceLowerUpperBound.ts +0 -40
  62. package/src/collision/imp/finalizeImpResult.ts +0 -69
  63. package/src/collision/imp/findContactImp.ts +0 -196
  64. package/src/collision/imp/imp.ts +0 -28
  65. package/src/collision/imp/incrementalMinimumDistanceExploreDirection.ts +0 -207
  66. package/src/collision/mpr/findPortal.ts +0 -152
  67. package/src/collision/mpr/mpr.ts +0 -29
  68. package/src/collision/mpr/updatePortal.ts +0 -52
  69. package/src/constraints/BaseConstraint.ts +0 -50
  70. package/src/constraints/ConstraintOptions.ts +0 -22
  71. package/src/constraints/ConstraintSolver.ts +0 -119
  72. package/src/constraints/DistanceConstraint.ts +0 -229
  73. package/src/constraints/FixedConstraint.ts +0 -203
  74. package/src/constraints/HingeConstraint.ts +0 -460
  75. package/src/constraints/PointConstraint.ts +0 -108
  76. package/src/constraints/components/AngleComponent.ts +0 -226
  77. package/src/constraints/components/AxisComponent.ts +0 -263
  78. package/src/constraints/components/HingeComponent.ts +0 -215
  79. package/src/constraints/components/Motor.ts +0 -36
  80. package/src/constraints/components/PointConstraintComponent.ts +0 -179
  81. package/src/constraints/components/RotationEulerComponent.ts +0 -139
  82. package/src/constraints/components/Spring.ts +0 -30
  83. package/src/constraints/components/SpringComponent.ts +0 -71
  84. package/src/constraints/types.ts +0 -6
  85. package/src/helpers.ts +0 -147
  86. package/src/index.ts +0 -50
  87. package/src/math/BasicTransform.ts +0 -19
  88. package/src/math/NumberValue.ts +0 -13
  89. package/src/math/isometry.ts +0 -64
  90. package/src/math/mat3.ts +0 -529
  91. package/src/math/mat4.ts +0 -588
  92. package/src/math/quat.ts +0 -193
  93. package/src/math/scalar.ts +0 -81
  94. package/src/math/tensor.ts +0 -17
  95. package/src/math/vec3.ts +0 -589
  96. package/src/math/vec4.ts +0 -10
  97. package/src/physics/Body.ts +0 -581
  98. package/src/physics/CollisionFilter.ts +0 -52
  99. package/src/physics/SleepModule.ts +0 -163
  100. package/src/physics/broadphase/BodyPairsModule.ts +0 -363
  101. package/src/physics/broadphase/BvhModule.ts +0 -237
  102. package/src/physics/broadphase/BvhTree.ts +0 -803
  103. package/src/physics/broadphase/ConstraintPairsModule.ts +0 -385
  104. package/src/physics/broadphase/TriangleMeshBvhTree.ts +0 -379
  105. package/src/physics/manifold/ContactManifold.ts +0 -227
  106. package/src/physics/manifold/ContactManifoldModule.ts +0 -623
  107. package/src/physics/manifold/Face.ts +0 -119
  108. package/src/physics/manifold/ManifoldCache.ts +0 -116
  109. package/src/physics/manifold/clipping/clipPolyVsEdge.ts +0 -131
  110. package/src/physics/manifold/clipping/clipPolyVsPlane.ts +0 -73
  111. package/src/physics/manifold/clipping/clipPolyVsPoly.ts +0 -72
  112. package/src/physics/narrowphase/CollideBodiesModule.ts +0 -755
  113. package/src/physics/solver/ContactConstraintModule.ts +0 -659
  114. package/src/physics/solver/ManifoldConstraint.ts +0 -420
  115. package/src/physics/solver/estimateCollisionResponse.ts +0 -146
  116. package/src/shape/Aabb.ts +0 -400
  117. package/src/shape/Box.ts +0 -231
  118. package/src/shape/Capsule.ts +0 -332
  119. package/src/shape/CompoundShape.ts +0 -288
  120. package/src/shape/Convex.ts +0 -130
  121. package/src/shape/ConvexHull.ts +0 -423
  122. package/src/shape/Cylinder.ts +0 -313
  123. package/src/shape/HeightMap.ts +0 -511
  124. package/src/shape/Line.ts +0 -14
  125. package/src/shape/Plane.ts +0 -116
  126. package/src/shape/Ray.ts +0 -81
  127. package/src/shape/Segment.ts +0 -25
  128. package/src/shape/Shape.ts +0 -77
  129. package/src/shape/Sphere.ts +0 -181
  130. package/src/shape/TransformedShape.ts +0 -51
  131. package/src/shape/Triangle.ts +0 -122
  132. package/src/shape/TriangleMesh.ts +0 -186
  133. package/src/types.ts +0 -1
  134. package/src/world.ts +0 -1335
@@ -1,803 +0,0 @@
1
- import {
2
- createClass,
3
- LazyReferenceListType,
4
- LazyReferenceType,
5
- MonomorphInstance,
6
- MonomorphType,
7
- NumberType,
8
- PropertyDefinitionMap,
9
- PropertyDefinitionReference,
10
- PropertyDefinitionReferenceList,
11
- WithPool,
12
- } from "monomorph";
13
- import { Aabb } from "../../shape/Aabb";
14
- import { assert } from "../../helpers";
15
- import { Vec3 } from "../../math/vec3";
16
- import { Body } from "../Body";
17
- import { Ray } from "../../shape/Ray";
18
- import { shouldPairCollide } from "../CollisionFilter";
19
-
20
- const expandedObjectBounds = /*@__PURE__*/ Aabb.create();
21
- const leftAabb = /*@__PURE__*/ Aabb.create();
22
- const rightAabb = /*@__PURE__*/ Aabb.create();
23
- const centroidBounds = /*@__PURE__*/ Aabb.create();
24
- const extent = /*@__PURE__*/ Vec3.create();
25
- const ray = /*@__PURE__*/ Ray.create();
26
- const halfExtents = /*@__PURE__*/ Vec3.create();
27
- const nodeBounds = /*@__PURE__*/ Aabb.create();
28
- const bodyBounds = /*@__PURE__*/ Aabb.create();
29
-
30
- const bvhNodeProps = {
31
- parent: LazyReferenceType((() => BvhNode) as () => never) as PropertyDefinitionReference<BvhNode | null, true>,
32
- left: LazyReferenceType((() => BvhNode) as () => never) as PropertyDefinitionReference<BvhNode | null, true>,
33
- right: LazyReferenceType((() => BvhNode) as () => never) as PropertyDefinitionReference<BvhNode | null, true>,
34
- computedBounds: MonomorphType(Aabb, undefined, true),
35
- height: NumberType(0),
36
- objects: LazyReferenceListType((() => Body) as () => never) as PropertyDefinitionReferenceList<Body, Body extends MonomorphInstance<infer P, infer I> ? P : never, Body extends MonomorphInstance<infer P, infer I> ? I : never>,
37
- } as const satisfies PropertyDefinitionMap;
38
-
39
- export class BvhNode extends createClass<BvhNode, typeof bvhNodeProps>(bvhNodeProps) {
40
- // objects: Body[] = [];
41
-
42
- isLeaf() {
43
- return this.left === null && this.right === null;
44
- }
45
-
46
- isRoot() {
47
- return this.parent === null;
48
- }
49
- }
50
-
51
- function sortByX(a: Body, b: Body) {
52
- return a.computedBounds.centroid.x - b.computedBounds.centroid.x;
53
- }
54
-
55
- function sortByY(a: Body, b: Body) {
56
- return a.computedBounds.centroid.y - b.computedBounds.centroid.y;
57
- }
58
-
59
- function sortByZ(a: Body, b: Body) {
60
- return a.computedBounds.centroid.z - b.computedBounds.centroid.z;
61
- }
62
-
63
- const sortByAxisFunctions = {
64
- 0: sortByX,
65
- 1: sortByY,
66
- 2: sortByZ,
67
- } as const;
68
-
69
- export type BvhTreeOptions = {
70
- maxDepth: number;
71
- maxObjectsPerLeaf: number;
72
- expansionMargin: number;
73
- };
74
-
75
- const sortedObjects: WithPool<Body>[] = [];
76
-
77
- export class BvhTree {
78
- options: BvhTreeOptions;
79
- nodes: typeof BvhNode.ReferenceList;
80
- bodyPool: typeof Body.Pool;
81
- root: BvhNode | null = null;
82
-
83
- optimizeQueue: BvhNode[];
84
- optimizeQueueHead: number;
85
-
86
- intersectStack: BvhNode[];
87
-
88
- walkStack: BvhNode[];
89
-
90
- dirtyObjects: Set<WithPool<Body>>;
91
-
92
- constructor(options: BvhTreeOptions, bvhNodePool: typeof BvhNode.Pool, bodyPool: typeof Body.Pool) {
93
- this.options = options;
94
- this.bodyPool = bodyPool;
95
- this.nodes = new BvhNode.ReferenceList(bvhNodePool);
96
- this.root = null;
97
-
98
- this.optimizeQueue = [];
99
- this.optimizeQueueHead = 0;
100
-
101
- this.intersectStack = [];
102
-
103
- this.walkStack = [];
104
-
105
- this.dirtyObjects = new Set<WithPool<Body>>();
106
- }
107
-
108
- markObjectAsDirty(object: WithPool<Body>) {
109
- this.dirtyObjects.add(object);
110
- }
111
-
112
- updateDirtyObjects() {
113
- for (const object of this.dirtyObjects) {
114
- if (this.update(object, false)) {
115
- }
116
- }
117
- this.dirtyObjects.clear();
118
- }
119
-
120
- rebalance(node: BvhNode): BvhNode {
121
- if (node.isLeaf() || node.height < 2) {
122
- return node;
123
- }
124
-
125
- const left = node.left!;
126
- const right = node.right!;
127
- const balance = right.height - left.height;
128
-
129
- // Rotate right up
130
- if (balance > 1) {
131
- const rightLeft = right.left!;
132
- const rightRight = right.right!;
133
-
134
- // Swap node and right
135
- right.left = node;
136
- right.parent = node.parent;
137
- node.parent = right;
138
-
139
- if (right.parent) {
140
- if (right.parent.left === node) {
141
- right.parent.left = right;
142
- } else {
143
- right.parent.right = right;
144
- }
145
- } else {
146
- this.root = right;
147
- }
148
-
149
- if (rightLeft.height > rightRight.height) {
150
- right.right = rightLeft;
151
- node.right = rightRight;
152
- rightRight.parent = node;
153
-
154
- node.computedBounds.unionAabbs(left.computedBounds, rightRight.computedBounds);
155
- right.computedBounds.unionAabbs(node.computedBounds, rightLeft.computedBounds);
156
-
157
- node.height = Math.max(left.height, rightRight.height) + 1;
158
- right.height = Math.max(node.height, rightLeft.height) + 1;
159
- } else {
160
- right.right = rightRight;
161
- node.right = rightLeft;
162
- rightLeft.parent = node;
163
-
164
- node.computedBounds.unionAabbs(left.computedBounds, rightLeft.computedBounds);
165
- right.computedBounds.unionAabbs(node.computedBounds, rightRight.computedBounds);
166
-
167
- node.height = Math.max(left.height, rightLeft.height) + 1;
168
- right.height = Math.max(node.height, rightRight.height) + 1;
169
- }
170
-
171
- return right;
172
- }
173
-
174
- // Rotate left up
175
- if (balance < -1) {
176
- const leftLeft = left.left!;
177
- const leftRight = left.right!;
178
-
179
- // Swap node and left
180
- left.left = node;
181
- left.parent = node.parent;
182
- node.parent = left;
183
-
184
- if (left.parent) {
185
- if (left.parent.left === node) {
186
- left.parent.left = left;
187
- } else {
188
- left.parent.right = left;
189
- }
190
- } else {
191
- this.root = left;
192
- }
193
-
194
- if (leftLeft.height > leftRight.height) {
195
- left.right = leftLeft;
196
- node.left = leftRight;
197
- leftRight.parent = node;
198
-
199
- node.computedBounds.unionAabbs(right.computedBounds, leftRight.computedBounds);
200
- left.computedBounds.unionAabbs(node.computedBounds, leftLeft.computedBounds);
201
-
202
- node.height = Math.max(right.height, leftRight.height) + 1;
203
- left.height = Math.max(node.height, leftLeft.height) + 1;
204
- } else {
205
- left.right = leftRight;
206
- node.left = leftLeft;
207
- leftLeft.parent = node;
208
-
209
- node.computedBounds.unionAabbs(right.computedBounds, leftLeft.computedBounds);
210
- left.computedBounds.unionAabbs(node.computedBounds, leftRight.computedBounds);
211
-
212
- node.height = Math.max(right.height, leftLeft.height) + 1;
213
- left.height = Math.max(node.height, leftRight.height) + 1;
214
- }
215
-
216
- return left;
217
- }
218
-
219
- return node;
220
- }
221
-
222
- refit(node: BvhNode | null) {
223
- let didRefit = false;
224
- while (node) {
225
- didRefit = true;
226
- if (node.isLeaf()) {
227
- node.computedBounds.min.fromArray([+Infinity, +Infinity, +Infinity]);
228
- node.computedBounds.max.fromArray([-Infinity, -Infinity, -Infinity]);
229
-
230
- for (const object of node.objects) {
231
- node.computedBounds.unionAabb(object.computedBounds);
232
- }
233
-
234
- node.height = 0;
235
- } else {
236
- const left = node.left!;
237
- const right = node.right!;
238
-
239
- node.computedBounds.unionAabbs(left.computedBounds, right.computedBounds);
240
- node.height = Math.max(left.height, right.height) + 1;
241
-
242
- if (node.height > 2) {
243
- // this.tryRotation(node);
244
- this.rebalance(node);
245
- }
246
- }
247
-
248
- node = node.parent;
249
- }
250
-
251
- return didRefit;
252
- }
253
-
254
- optimizeOncePerFrame(maxNodesToCheck: number) {
255
- if (!this.root) {
256
- return;
257
- }
258
-
259
- const queue = this.optimizeQueue;
260
- let head = this.optimizeQueueHead;
261
- let tail = 0;
262
-
263
- // init the queue if empty
264
- if (head === 0 && queue.length === 0) {
265
- queue.push(this.root);
266
- tail = 1;
267
- } else {
268
- tail = queue.length;
269
- }
270
-
271
- let count = 0;
272
-
273
- while (head < tail && count < maxNodesToCheck) {
274
- const node = queue[head++];
275
- if (!node.isLeaf()) {
276
- // this.tryRotation(node);
277
- this.rebalance(node);
278
- count++;
279
-
280
- if (node.left) {
281
- queue.push(node.left);
282
- }
283
-
284
- if (node.right) {
285
- queue.push(node.right);
286
- }
287
- }
288
- }
289
-
290
- // reset if done or save head for next call
291
- if (head >= queue.length) {
292
- queue.length = 0;
293
- this.optimizeQueueHead = 0;
294
- } else {
295
- this.optimizeQueueHead = head;
296
- }
297
- }
298
-
299
- splitObjects(outLeft: BvhNode, outRight: BvhNode, nodeToSplit: BvhNode) {
300
- const objects = nodeToSplit.objects;
301
- const leftObjects = outLeft.objects;
302
- const rightObjects = outRight.objects;
303
-
304
- if (objects.length < 2) {
305
- throw new Error("Cannot split fewer than 2 objects.");
306
- }
307
-
308
- // expand centroid bounds
309
- centroidBounds.min.fromArray([+Infinity, +Infinity, +Infinity]);
310
- centroidBounds.max.fromArray([-Infinity, -Infinity, -Infinity]);
311
-
312
- sortedObjects.length = 0;
313
- for (const obj of objects) {
314
- centroidBounds.expandToPoint(obj.computedBounds.centroid);
315
- sortedObjects.push(obj);
316
- }
317
-
318
- centroidBounds.computeExtents(extent);
319
- const axis = centroidBounds.computeLargestAxis();
320
-
321
- sortedObjects.sort(sortByAxisFunctions[axis]);
322
- let mid = Math.floor(sortedObjects.length / 2);
323
- if (mid <= 0 || mid >= sortedObjects.length) {
324
- mid = 1;
325
- }
326
-
327
- for (let i = 0; i < mid; i++) {
328
- leftObjects.push(sortedObjects[i]);
329
- sortedObjects[i].node = outLeft;
330
- }
331
-
332
- for (let i = mid; i < sortedObjects.length; i++) {
333
- rightObjects.push(sortedObjects[i]);
334
- sortedObjects[i].node = outRight;
335
- }
336
- }
337
-
338
- createBvhNode() {
339
- return this.nodes.create({
340
- objects: {
341
- pool: this.bodyPool,
342
- },
343
- });
344
- };
345
-
346
- destroyBvhNode(node: WithPool<BvhNode>) {
347
- // this.nodes.remove(node);
348
- node.destroy();
349
- }
350
-
351
- insert(object: WithPool<Body>, shouldUpdateDirtyObjects: boolean = true) {
352
- expandedObjectBounds.expandAabb(object.computedBounds, this.options.expansionMargin);
353
-
354
- // case: tree is empty
355
- if (!this.root) {
356
- const root = this.createBvhNode();
357
- root.computedBounds.copy(expandedObjectBounds);
358
- root.objects.push(object);
359
- root.height = 0;
360
- this.root = root;
361
- object.node = root;
362
- return true;
363
- }
364
-
365
- // find the best-fit leaf node
366
- let best = this.root;
367
- while (!best.isLeaf()) {
368
- const left = best.left!;
369
- const right = best.right!;
370
-
371
- leftAabb.unionAabbs(best.computedBounds, left.computedBounds);
372
- rightAabb.unionAabbs(best.computedBounds, right.computedBounds);
373
-
374
- const leftCost = leftAabb.computeSurfaceArea();
375
- const rightCost = rightAabb.computeSurfaceArea();
376
-
377
- best = leftCost < rightCost ? left : right;
378
- }
379
-
380
- // #v-ifdef DEV
381
- // dev assert best.isLeaf();
382
- assert(best.isLeaf(), "Expected best node to be a leaf node.");
383
- // #v-endif
384
-
385
- // case: best leaf node is not full
386
- if (best.objects.length < this.options.maxObjectsPerLeaf || best.height >= this.options.maxDepth) {
387
- best.objects.push(object);
388
- best.computedBounds.unionAabb(expandedObjectBounds);
389
- object.node = best;
390
- return this.refit(best);
391
- }
392
-
393
- // case: best leaf node is full, need to split
394
- const left = this.createBvhNode();
395
- const right = this.createBvhNode();
396
-
397
- // partition the objects in the two new leaves
398
- // we push the object to best temporarily so it can be included in the split
399
- best.objects.push(object);
400
- this.splitObjects(left, right, best);
401
- // we clear best's objects now
402
- best.objects.length = 0;
403
-
404
- // update left leaf
405
- left.computedBounds.copy(left.objects.getAtIndex(0)!.computedBounds);
406
- for (let i = 1; i < left.objects.length; i++) {
407
- left.computedBounds.unionAabb(left.objects.getAtIndex(i)!.computedBounds);
408
- }
409
- left.parent = best;
410
- left.height = 0;
411
-
412
- // update right leaf
413
- right.computedBounds.copy(right.objects.getAtIndex(0)!.computedBounds);
414
- for (let i = 1; i < right.objects.length; i++) {
415
- right.computedBounds.unionAabb(right.objects.getAtIndex(i)!.computedBounds);
416
- }
417
- right.parent = best;
418
- right.height = 0;
419
-
420
- // update best node
421
- best.left = left;
422
- best.right = right;
423
- best.computedBounds.unionAabbs(left.computedBounds, right.computedBounds);
424
- best.height = 1;
425
-
426
- return this.refit(best);
427
- }
428
-
429
- remove(object: WithPool<Body>, shouldRemoveFromDirtyObjects: boolean = false) {
430
- const node = object.node as WithPool<BvhNode> | null;
431
-
432
- if (shouldRemoveFromDirtyObjects) {
433
- this.dirtyObjects.delete(object);
434
- }
435
-
436
- if (!node) {
437
- return;
438
- }
439
-
440
- const wasFoundAndRemoved = node.objects.remove(object) > -1; // fastRemove(node.objects, object);
441
- if (!wasFoundAndRemoved) {
442
- return;
443
- }
444
-
445
- object.node = null;
446
-
447
- // If there are still objects in the node, just refit and exit
448
- if (node.objects.length > 0) {
449
- return this.refit(node);
450
- }
451
-
452
- // If the node is the root, and it's now empty
453
- if (node.isRoot()) {
454
- this.destroyBvhNode(node);
455
- this.root = null;
456
- return false;
457
- }
458
-
459
- const parent = node.parent!;
460
- const sibling = parent.left === node ? parent.right! : parent.left!;
461
-
462
- sibling.parent = parent.parent;
463
- if (parent.isRoot()) {
464
- this.root = sibling;
465
- } else {
466
- const grandparent = parent.parent!;
467
- if (grandparent.left === parent) {
468
- grandparent.left = sibling;
469
- } else {
470
- grandparent.right = sibling;
471
- }
472
- }
473
-
474
- this.destroyBvhNode(parent as WithPool<BvhNode>);
475
- this.destroyBvhNode(node);
476
-
477
- return this.refit(sibling);
478
- }
479
-
480
- update(object: WithPool<Body>, shouldUpdateDirtyObjects: boolean = true) {
481
- const node = object.node;
482
- if (!node) {
483
- this.insert(object);
484
- return false;
485
- }
486
-
487
- // avoid refit if there will be no change to node bounds
488
- if (node.computedBounds.enclosesAabb(object.computedBounds)) {
489
- return false;
490
- }
491
-
492
- this.remove(object);
493
- return this.insert(object);
494
- }
495
-
496
- intersectBody(onHit: (Body: Body) => boolean, body: Body, shouldUpdateDirtyObjects: boolean = true) {
497
- // console.log("intersect body", body);
498
- if (!this.root) {
499
- return;
500
- }
501
-
502
- if (shouldUpdateDirtyObjects) {
503
- this.updateDirtyObjects();
504
- }
505
-
506
- const stack = this.intersectStack;
507
- stack.length = 0;
508
- stack.push(this.root);
509
-
510
- while (stack.length > 0) {
511
- const node = stack.pop();
512
-
513
- if (!node) {
514
- continue;
515
- }
516
-
517
- // skip this node if it doesn't intersect the AABB
518
- if (!node.computedBounds.intersectsAabb(body.computedBounds)) {
519
- continue;
520
- }
521
-
522
- // if the node is not a leaf, push its children onto the stack and continue
523
- if (!node.isLeaf()) {
524
- stack.push(node.left!);
525
- stack.push(node.right!);
526
- continue;
527
- }
528
-
529
- // if the node is a leaf, check its objects
530
- for (const otherBody of node.objects) {
531
- if (body === otherBody) {
532
- // console.log("skipping self");
533
- continue;
534
- }
535
-
536
- if (
537
- shouldPairCollide(
538
- body.belongsToGroups,
539
- body.collidesWithGroups,
540
- otherBody.belongsToGroups,
541
- otherBody.collidesWithGroups
542
- ) === false
543
- ) {
544
- continue;
545
- }
546
-
547
- if (!body.computedBounds.intersectsAabb(otherBody.computedBounds)) {
548
- continue;
549
- }
550
-
551
- if (onHit(otherBody)) {
552
- return;
553
- }
554
- }
555
- }
556
- }
557
-
558
- walk(onNode: (node: BvhNode) => boolean) {
559
- if (!this.root) {
560
- return;
561
- }
562
-
563
- const stack = this.walkStack;
564
- stack.length = 0;
565
- stack.push(this.root);
566
-
567
- while (stack.length > 0) {
568
- const node = stack.pop();
569
-
570
- if (!node) {
571
- continue;
572
- }
573
-
574
- if (onNode(node)) {
575
- return;
576
- }
577
-
578
- if (!node.isLeaf()) {
579
- stack.push(node.left!);
580
- stack.push(node.right!);
581
- }
582
- }
583
- }
584
-
585
- isEmpty() {
586
- return this.root === null;
587
- }
588
-
589
- visitBodiesWithCollision(
590
- bodyVisitor: BodyVisitor,
591
- bounds: Aabb,
592
- belongsToGroups: number,
593
- collidesWithGroups: number,
594
- shouldUpdateDirtyObjects: boolean = true
595
- ): void {
596
- // exit if the tree is empty
597
- if (!this.root) {
598
- return;
599
- }
600
-
601
- if (shouldUpdateDirtyObjects) {
602
- this.updateDirtyObjects();
603
- }
604
-
605
- const stack = this.intersectStack;
606
- stack.length = 0;
607
- stack.push(this.root);
608
-
609
- while (stack.length > 0) {
610
- const node = stack.pop();
611
-
612
- if (!node) {
613
- continue;
614
- }
615
-
616
- // skip this node if it doesn't intersect the AABB
617
- if (node.computedBounds.intersectsAabb(bounds) === false) {
618
- continue;
619
- }
620
-
621
- // if the node is not a leaf, push its children onto the stack and continue
622
- if (!node.isLeaf()) {
623
- stack.push(node.left!);
624
- stack.push(node.right!);
625
- continue;
626
- }
627
-
628
- // if the node is a leaf, check its objects
629
- for (const body of node.objects) {
630
- if (
631
- shouldPairCollide(body.belongsToGroups, body.collidesWithGroups, belongsToGroups, collidesWithGroups) ===
632
- false
633
- ) {
634
- continue;
635
- }
636
-
637
- if (!body.computedBounds.intersectsAabb(bounds)) {
638
- continue;
639
- }
640
-
641
- // visit the body
642
- bodyVisitor.visit(body);
643
-
644
- // break if visitor requests exit
645
- if (bodyVisitor.shouldExit) {
646
- return;
647
- }
648
- }
649
- }
650
- }
651
-
652
- raycastBodies(
653
- bodyVisitor: BodyVisitor,
654
- ray: Ray,
655
- belongsToGroups: number,
656
- collidesWithGroups: number,
657
- shouldUpdateDirtyObjects: boolean = true
658
- ) {
659
- // exit if the tree is empty
660
- if (!this.root) {
661
- return;
662
- }
663
-
664
- if (shouldUpdateDirtyObjects) {
665
- this.updateDirtyObjects();
666
- }
667
-
668
- const stack = this.intersectStack;
669
- stack.length = 0;
670
- stack.push(this.root);
671
-
672
- while (stack.length > 0) {
673
- const node = stack.pop();
674
-
675
- if (!node) {
676
- continue;
677
- }
678
-
679
- // skip branch if the node's aabb does not intersect with the ray
680
- nodeBounds.copy(node.computedBounds);
681
- if (ray.intersectsAabb(nodeBounds) === false) {
682
- continue;
683
- }
684
-
685
- // if the node is not a leaf, push its children onto the stack and continue
686
- if (!node.isLeaf()) {
687
- stack.push(node.left!);
688
- stack.push(node.right!);
689
- continue;
690
- }
691
-
692
- // if the node is a leaf, check its objects
693
- for (const body of node.objects) {
694
- if (
695
- shouldPairCollide(body.belongsToGroups, body.collidesWithGroups, belongsToGroups, collidesWithGroups) ===
696
- false
697
- ) {
698
- continue;
699
- }
700
-
701
- // skip if bounds do not collide
702
- bodyBounds.copy(body.computedBounds);
703
- if (ray.intersectsAabb(bodyBounds) === false) {
704
- continue;
705
- }
706
-
707
- // visit the body
708
- bodyVisitor.visit(body);
709
-
710
- // break if visitor requests exit
711
- if (bodyVisitor.shouldExit) {
712
- return;
713
- }
714
- }
715
- }
716
- }
717
-
718
- visitBodiesWithCast(
719
- bodyVisitor: BodyVisitor,
720
- bounds: Aabb,
721
- displacement: Vec3,
722
- belongsToGroups: number,
723
- collidesWithGroups: number,
724
- shouldUpdateDirtyObjects: boolean = true
725
- ): void {
726
- // exit if the tree is empty
727
- if (!this.root) {
728
- return;
729
- }
730
-
731
- if (shouldUpdateDirtyObjects) {
732
- this.updateDirtyObjects();
733
- }
734
-
735
- // aabb cast is done by the following method
736
- // 1. shrink the shape aabb by its own extents down to a point
737
- // 2. expand the block aabb by the extents of the shape aabb
738
- // 3. cast the point by the displacement against the expanded block aabb (raycast vs aabb test)
739
-
740
- bounds.computeCentroid(ray.origin);
741
- ray.direction.normalizeVector(displacement);
742
- ray.length = displacement.length();
743
-
744
- bounds.computeHalfExtents(halfExtents);
745
-
746
- const stack = this.intersectStack;
747
- stack.length = 0;
748
- stack.push(this.root);
749
-
750
- while (stack.length > 0) {
751
- const node = stack.pop();
752
-
753
- if (!node) {
754
- continue;
755
- }
756
-
757
- // skip branch if the node's expanded aabb does not intersect with the shape's ray
758
- nodeBounds.copy(node.computedBounds);
759
- nodeBounds.expandByVector(halfExtents);
760
- if (ray.intersectsAabb(nodeBounds) === false) {
761
- continue;
762
- }
763
-
764
- // if the node is not a leaf, push its children onto the stack and continue
765
- if (!node.isLeaf()) {
766
- stack.push(node.left!);
767
- stack.push(node.right!);
768
- continue;
769
- }
770
-
771
- // if the node is a leaf, check its objects
772
- for (const body of node.objects) {
773
- if (
774
- shouldPairCollide(body.belongsToGroups, body.collidesWithGroups, belongsToGroups, collidesWithGroups) ===
775
- false
776
- ) {
777
- continue;
778
- }
779
-
780
- // expand the object aabb by the shape's aabb
781
- // skip if bounds do not collide
782
- bodyBounds.copy(body.computedBounds);
783
- bodyBounds.expandByVector(halfExtents);
784
- if (ray.intersectsAabb(bodyBounds) === false) {
785
- continue;
786
- }
787
-
788
- // visit the body
789
- bodyVisitor.visit(body);
790
-
791
- // break if visitor requests exit
792
- if (bodyVisitor.shouldExit) {
793
- return;
794
- }
795
- }
796
- }
797
- }
798
- }
799
-
800
- export interface BodyVisitor {
801
- shouldExit: boolean;
802
- visit(body: Body): void;
803
- }