dacha 0.18.0-alpha.6 → 0.18.0-alpha.7

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 (42) hide show
  1. package/build/contrib/systems/physics-system/subsystems/collision-detection/dynamic-aabb-tree/index.d.ts +28 -0
  2. package/build/contrib/systems/physics-system/subsystems/collision-detection/dynamic-aabb-tree/index.js +299 -0
  3. package/build/contrib/systems/physics-system/subsystems/collision-detection/index.d.ts +2 -1
  4. package/build/contrib/systems/physics-system/subsystems/collision-detection/index.js +18 -34
  5. package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/box-box/check-box-cast-and-box.d.ts +6 -0
  6. package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/box-box/check-box-cast-and-box.js +6 -0
  7. package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/box-capsule/check-box-cast-and-capsule.d.ts +6 -0
  8. package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/box-capsule/check-box-cast-and-capsule.js +8 -1
  9. package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/box-capsule/utils.d.ts +3 -0
  10. package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/box-capsule/utils.js +23 -0
  11. package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/box-circle/check-box-cast-and-circle.d.ts +6 -0
  12. package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/box-circle/check-box-cast-and-circle.js +6 -0
  13. package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/box-segment/check-box-cast-and-segment.d.ts +5 -0
  14. package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/box-segment/check-box-cast-and-segment.js +7 -5
  15. package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/box-utils.d.ts +2 -2
  16. package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/box-utils.js +24 -120
  17. package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/capsule-box/check-capsule-cast-and-box.d.ts +5 -0
  18. package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/capsule-box/check-capsule-cast-and-box.js +5 -0
  19. package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/capsule-capsule/check-capsule-cast-and-capsule.d.ts +5 -0
  20. package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/capsule-capsule/check-capsule-cast-and-capsule.js +5 -0
  21. package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/capsule-circle/check-capsule-cast-and-circle.d.ts +5 -0
  22. package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/capsule-circle/check-capsule-cast-and-circle.js +5 -0
  23. package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/capsule-segment/check-capsule-cast-and-segment.d.ts +5 -0
  24. package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/capsule-segment/check-capsule-cast-and-segment.js +5 -0
  25. package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/circle-box/check-circle-cast-and-box.d.ts +5 -0
  26. package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/circle-box/check-circle-cast-and-box.js +5 -0
  27. package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/circle-capsule/check-circle-cast-and-capsule.d.ts +5 -0
  28. package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/circle-capsule/check-circle-cast-and-capsule.js +5 -0
  29. package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/circle-circle/check-circle-cast-and-circle.d.ts +5 -0
  30. package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/circle-circle/check-circle-cast-and-circle.js +5 -0
  31. package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/circle-segment/check-circle-cast-and-segment.d.ts +5 -0
  32. package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/circle-segment/check-circle-cast-and-segment.js +5 -0
  33. package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/common/axis-aligned-box-workspace.d.ts +24 -0
  34. package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/common/axis-aligned-box-workspace.js +60 -0
  35. package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/common/constants.d.ts +1 -0
  36. package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/common/constants.js +6 -0
  37. package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/common/convex-hull-workspace.d.ts +92 -0
  38. package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/common/convex-hull-workspace.js +205 -0
  39. package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/common/geometry.d.ts +6 -0
  40. package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/common/geometry.js +10 -0
  41. package/build/contrib/systems/physics-system/subsystems/collision-detection/types.d.ts +2 -0
  42. package/package.json +1 -1
@@ -0,0 +1,28 @@
1
+ import type { AABB } from '../types';
2
+ export type DynamicAABBTreeEntryId = number;
3
+ export declare class DynamicAABBTree<T> {
4
+ private root;
5
+ private nodesById;
6
+ private freeInternalNodes;
7
+ private queryStack;
8
+ private nextId;
9
+ get size(): number;
10
+ insert(aabb: AABB, value: T): DynamicAABBTreeEntryId;
11
+ update(id: DynamicAABBTreeEntryId, aabb: AABB): void;
12
+ remove(id: DynamicAABBTreeEntryId): void;
13
+ query(aabb: AABB, visitor: (value: T) => unknown): void;
14
+ queryAll(aabb: AABB, output?: T[]): T[];
15
+ clear(): void;
16
+ private createNode;
17
+ private getNode;
18
+ private insertLeaf;
19
+ private removeLeaf;
20
+ private createInternalNode;
21
+ private releaseInternalNode;
22
+ private findBestSibling;
23
+ private getDescentCost;
24
+ private fixAncestors;
25
+ private balance;
26
+ private rotateLeft;
27
+ private rotateRight;
28
+ }
@@ -0,0 +1,299 @@
1
+ const isLeaf = (node) => node.child1 === null;
2
+ const createAABB = () => ({
3
+ min: { x: 0, y: 0 },
4
+ max: { x: 0, y: 0 },
5
+ });
6
+ const setCombinedAABB = (target, aabb1, aabb2) => {
7
+ target.min.x = Math.min(aabb1.min.x, aabb2.min.x);
8
+ target.min.y = Math.min(aabb1.min.y, aabb2.min.y);
9
+ target.max.x = Math.max(aabb1.max.x, aabb2.max.x);
10
+ target.max.y = Math.max(aabb1.max.y, aabb2.max.y);
11
+ };
12
+ const getPerimeter = (aabb) => 2 *
13
+ (Math.max(0, aabb.max.x - aabb.min.x) + Math.max(0, aabb.max.y - aabb.min.y));
14
+ const getCombinedPerimeter = (aabb1, aabb2) => {
15
+ const minX = Math.min(aabb1.min.x, aabb2.min.x);
16
+ const minY = Math.min(aabb1.min.y, aabb2.min.y);
17
+ const maxX = Math.max(aabb1.max.x, aabb2.max.x);
18
+ const maxY = Math.max(aabb1.max.y, aabb2.max.y);
19
+ return 2 * (Math.max(0, maxX - minX) + Math.max(0, maxY - minY));
20
+ };
21
+ const overlaps = (aabb1, aabb2) => aabb1.max.x >= aabb2.min.x &&
22
+ aabb1.min.x <= aabb2.max.x &&
23
+ aabb1.max.y >= aabb2.min.y &&
24
+ aabb1.min.y <= aabb2.max.y;
25
+ export class DynamicAABBTree {
26
+ root = null;
27
+ nodesById = new Map();
28
+ freeInternalNodes = [];
29
+ queryStack = [];
30
+ nextId = 1;
31
+ get size() {
32
+ return this.nodesById.size;
33
+ }
34
+ insert(aabb, value) {
35
+ const node = this.createNode(aabb, value);
36
+ this.nodesById.set(node.id, node);
37
+ this.insertLeaf(node);
38
+ return node.id;
39
+ }
40
+ update(id, aabb) {
41
+ const node = this.getNode(id);
42
+ this.removeLeaf(node);
43
+ node.aabb = aabb;
44
+ this.insertLeaf(node);
45
+ }
46
+ remove(id) {
47
+ const node = this.getNode(id);
48
+ this.removeLeaf(node);
49
+ this.nodesById.delete(id);
50
+ }
51
+ query(aabb, visitor) {
52
+ if (!this.root) {
53
+ return;
54
+ }
55
+ const stack = this.queryStack;
56
+ stack.length = 0;
57
+ stack.push(this.root);
58
+ while (stack.length > 0) {
59
+ const node = stack.pop();
60
+ if (!overlaps(node.aabb, aabb)) {
61
+ continue;
62
+ }
63
+ if (isLeaf(node)) {
64
+ if (visitor(node.value) === false) {
65
+ stack.length = 0;
66
+ return;
67
+ }
68
+ continue;
69
+ }
70
+ stack.push(node.child1, node.child2);
71
+ }
72
+ }
73
+ queryAll(aabb, output = []) {
74
+ this.query(aabb, (value) => {
75
+ output.push(value);
76
+ });
77
+ return output;
78
+ }
79
+ clear() {
80
+ this.root = null;
81
+ this.nodesById.clear();
82
+ this.freeInternalNodes.length = 0;
83
+ this.queryStack.length = 0;
84
+ }
85
+ createNode(aabb, value) {
86
+ return {
87
+ id: this.nextId++,
88
+ aabb,
89
+ value,
90
+ parent: null,
91
+ child1: null,
92
+ child2: null,
93
+ height: 0,
94
+ };
95
+ }
96
+ getNode(id) {
97
+ const node = this.nodesById.get(id);
98
+ if (!node) {
99
+ throw new Error(`DynamicAABBTree entry ${id} does not exist.`);
100
+ }
101
+ return node;
102
+ }
103
+ insertLeaf(leaf) {
104
+ if (!this.root) {
105
+ this.root = leaf;
106
+ leaf.parent = null;
107
+ return;
108
+ }
109
+ const sibling = this.findBestSibling(leaf.aabb);
110
+ const oldParent = sibling.parent;
111
+ const newParent = this.createInternalNode(leaf, sibling, oldParent);
112
+ sibling.parent = newParent;
113
+ leaf.parent = newParent;
114
+ if (oldParent) {
115
+ if (oldParent.child1 === sibling) {
116
+ oldParent.child1 = newParent;
117
+ }
118
+ else {
119
+ oldParent.child2 = newParent;
120
+ }
121
+ }
122
+ else {
123
+ this.root = newParent;
124
+ }
125
+ this.fixAncestors(leaf.parent);
126
+ }
127
+ removeLeaf(leaf) {
128
+ if (leaf === this.root) {
129
+ this.root = null;
130
+ leaf.parent = null;
131
+ return;
132
+ }
133
+ const parent = leaf.parent;
134
+ const grandParent = parent.parent;
135
+ const sibling = parent.child1 === leaf ? parent.child2 : parent.child1;
136
+ if (grandParent) {
137
+ if (grandParent.child1 === parent) {
138
+ grandParent.child1 = sibling;
139
+ }
140
+ else {
141
+ grandParent.child2 = sibling;
142
+ }
143
+ sibling.parent = grandParent;
144
+ this.fixAncestors(grandParent);
145
+ }
146
+ else {
147
+ this.root = sibling;
148
+ sibling.parent = null;
149
+ }
150
+ leaf.parent = null;
151
+ this.releaseInternalNode(parent);
152
+ }
153
+ createInternalNode(leaf, sibling, parent) {
154
+ const node = this.freeInternalNodes.pop() ?? {
155
+ id: 0,
156
+ aabb: createAABB(),
157
+ value: null,
158
+ parent: null,
159
+ child1: null,
160
+ child2: null,
161
+ height: 0,
162
+ };
163
+ setCombinedAABB(node.aabb, leaf.aabb, sibling.aabb);
164
+ node.value = null;
165
+ node.parent = parent;
166
+ node.child1 = sibling;
167
+ node.child2 = leaf;
168
+ node.height = sibling.height + 1;
169
+ return node;
170
+ }
171
+ releaseInternalNode(node) {
172
+ node.value = null;
173
+ node.parent = null;
174
+ node.child1 = null;
175
+ node.child2 = null;
176
+ node.height = 0;
177
+ this.freeInternalNodes.push(node);
178
+ }
179
+ findBestSibling(aabb) {
180
+ let node = this.root;
181
+ while (!isLeaf(node)) {
182
+ const child1 = node.child1;
183
+ const child2 = node.child2;
184
+ const perimeter = getPerimeter(node.aabb);
185
+ const combinedPerimeter = getCombinedPerimeter(node.aabb, aabb);
186
+ const inheritedCost = 2 * (combinedPerimeter - perimeter);
187
+ const cost = 2 * combinedPerimeter;
188
+ const cost1 = this.getDescentCost(child1, aabb, inheritedCost);
189
+ const cost2 = this.getDescentCost(child2, aabb, inheritedCost);
190
+ if (cost < cost1 && cost < cost2) {
191
+ break;
192
+ }
193
+ node = cost1 < cost2 ? child1 : child2;
194
+ }
195
+ return node;
196
+ }
197
+ getDescentCost(child, aabb, inheritedCost) {
198
+ const combinedPerimeter = getCombinedPerimeter(child.aabb, aabb);
199
+ if (isLeaf(child)) {
200
+ return combinedPerimeter + inheritedCost;
201
+ }
202
+ return combinedPerimeter - getPerimeter(child.aabb) + inheritedCost;
203
+ }
204
+ fixAncestors(node) {
205
+ while (node) {
206
+ node = this.balance(node);
207
+ const child1 = node.child1;
208
+ const child2 = node.child2;
209
+ node.height = 1 + Math.max(child1.height, child2.height);
210
+ setCombinedAABB(node.aabb, child1.aabb, child2.aabb);
211
+ node = node.parent;
212
+ }
213
+ }
214
+ balance(node) {
215
+ if (isLeaf(node) || node.height < 2) {
216
+ return node;
217
+ }
218
+ const child1 = node.child1;
219
+ const child2 = node.child2;
220
+ const balance = child2.height - child1.height;
221
+ if (balance > 1) {
222
+ return this.rotateLeft(node);
223
+ }
224
+ if (balance < -1) {
225
+ return this.rotateRight(node);
226
+ }
227
+ return node;
228
+ }
229
+ rotateLeft(node) {
230
+ const child1 = node.child1;
231
+ const child2 = node.child2;
232
+ const grandChild1 = child2.child1;
233
+ const grandChild2 = child2.child2;
234
+ child2.child1 = node;
235
+ child2.parent = node.parent;
236
+ node.parent = child2;
237
+ if (child2.parent) {
238
+ if (child2.parent.child1 === node) {
239
+ child2.parent.child1 = child2;
240
+ }
241
+ else {
242
+ child2.parent.child2 = child2;
243
+ }
244
+ }
245
+ else {
246
+ this.root = child2;
247
+ }
248
+ if (grandChild1.height > grandChild2.height) {
249
+ child2.child2 = grandChild1;
250
+ node.child2 = grandChild2;
251
+ grandChild2.parent = node;
252
+ }
253
+ else {
254
+ child2.child2 = grandChild2;
255
+ node.child2 = grandChild1;
256
+ grandChild1.parent = node;
257
+ }
258
+ setCombinedAABB(node.aabb, child1.aabb, node.child2.aabb);
259
+ setCombinedAABB(child2.aabb, node.aabb, child2.child2.aabb);
260
+ node.height = 1 + Math.max(child1.height, node.child2.height);
261
+ child2.height = 1 + Math.max(node.height, child2.child2.height);
262
+ return child2;
263
+ }
264
+ rotateRight(node) {
265
+ const child1 = node.child1;
266
+ const child2 = node.child2;
267
+ const grandChild1 = child1.child1;
268
+ const grandChild2 = child1.child2;
269
+ child1.child1 = node;
270
+ child1.parent = node.parent;
271
+ node.parent = child1;
272
+ if (child1.parent) {
273
+ if (child1.parent.child1 === node) {
274
+ child1.parent.child1 = child1;
275
+ }
276
+ else {
277
+ child1.parent.child2 = child1;
278
+ }
279
+ }
280
+ else {
281
+ this.root = child1;
282
+ }
283
+ if (grandChild1.height > grandChild2.height) {
284
+ child1.child2 = grandChild1;
285
+ node.child1 = grandChild2;
286
+ grandChild2.parent = node;
287
+ }
288
+ else {
289
+ child1.child2 = grandChild2;
290
+ node.child1 = grandChild1;
291
+ grandChild1.parent = node;
292
+ }
293
+ setCombinedAABB(node.aabb, node.child1.aabb, child2.aabb);
294
+ setCombinedAABB(child1.aabb, node.aabb, child1.child2.aabb);
295
+ node.height = 1 + Math.max(node.child1.height, child2.height);
296
+ child1.height = 1 + Math.max(node.height, child1.child2.height);
297
+ return child1;
298
+ }
299
+ }
@@ -4,6 +4,7 @@ import type { Contact } from './types';
4
4
  export declare class CollisionDetectionSubsystem {
5
5
  private actorQuery;
6
6
  private axis;
7
+ private queryTree;
7
8
  private proxiesByActorId;
8
9
  private proxyPairs;
9
10
  private contacts;
@@ -33,7 +34,7 @@ export declare class CollisionDetectionSubsystem {
33
34
  private testAABB;
34
35
  private testCollisionLayers;
35
36
  private testState;
36
- private sweepAndPruneQuery;
37
+ private collectQueryCandidates;
37
38
  private sweepAndPrune;
38
39
  private checkOnIntersection;
39
40
  private storeContact;
@@ -2,6 +2,7 @@ import { ActorQuery } from '../../../../../engine/actor';
2
2
  import { Transform, Collider, RigidBody } from '../../../../components';
3
3
  import { AddActor, RemoveActor } from '../../../../../engine/events';
4
4
  import { insertionSort } from '../../../../../engine/data-lib';
5
+ import { DynamicAABBTree } from './dynamic-aabb-tree';
5
6
  import { geometryBuilders } from './geometry-builders';
6
7
  import { aabbBuilders } from './aabb-builders';
7
8
  import { intersectionCheckers } from './intersection-checkers';
@@ -11,6 +12,7 @@ import { buildQueryProxy, buildActorQueryProxy, raycast, raycastAll, overlap, ge
11
12
  export class CollisionDetectionSubsystem {
12
13
  actorQuery;
13
14
  axis;
15
+ queryTree;
14
16
  proxiesByActorId;
15
17
  proxyPairs;
16
18
  contacts;
@@ -33,6 +35,7 @@ export class CollisionDetectionSubsystem {
33
35
  dispersionCalculator: new DispersionCalculator('y'),
34
36
  },
35
37
  };
38
+ this.queryTree = new DynamicAABBTree();
36
39
  this.proxiesByActorId = new Map();
37
40
  this.proxyPairs = [];
38
41
  this.contacts = [];
@@ -47,20 +50,21 @@ export class CollisionDetectionSubsystem {
47
50
  this.actorQuery.removeEventListener(AddActor, this.handleActorAdd);
48
51
  this.actorQuery.removeEventListener(RemoveActor, this.handleActorRemove);
49
52
  this.actorQuery.destroy();
53
+ this.queryTree.clear();
50
54
  }
51
55
  raycast(params) {
52
56
  const queryProxy = buildQueryProxy('ray', params);
53
- this.sweepAndPruneQuery(queryProxy);
57
+ this.collectQueryCandidates(queryProxy);
54
58
  return raycast(queryProxy, this.queryCandidates);
55
59
  }
56
60
  raycastAll(params) {
57
61
  const queryProxy = buildQueryProxy('ray', params);
58
- this.sweepAndPruneQuery(queryProxy);
62
+ this.collectQueryCandidates(queryProxy);
59
63
  return raycastAll(queryProxy, this.queryCandidates);
60
64
  }
61
65
  overlapShape(params) {
62
66
  const queryProxy = buildQueryProxy(params.shape.type, params);
63
- this.sweepAndPruneQuery(queryProxy);
67
+ this.collectQueryCandidates(queryProxy);
64
68
  return overlap(params.shape.type, queryProxy, this.queryCandidates);
65
69
  }
66
70
  overlapActor(params) {
@@ -69,19 +73,19 @@ export class CollisionDetectionSubsystem {
69
73
  return [];
70
74
  }
71
75
  const queryProxy = buildActorQueryProxy(queryType, params);
72
- this.sweepAndPruneQuery(queryProxy);
76
+ this.collectQueryCandidates(queryProxy);
73
77
  return overlap(queryType, queryProxy, this.queryCandidates);
74
78
  }
75
79
  shapeCast(params) {
76
80
  const queryType = getShapeCastQueryType(params);
77
81
  const queryProxy = buildQueryProxy(queryType, params);
78
- this.sweepAndPruneQuery(queryProxy);
82
+ this.collectQueryCandidates(queryProxy);
79
83
  return shapeCast(queryType, queryProxy, this.queryCandidates);
80
84
  }
81
85
  shapeCastAll(params) {
82
86
  const queryType = getShapeCastQueryType(params);
83
87
  const queryProxy = buildQueryProxy(queryType, params);
84
- this.sweepAndPruneQuery(queryProxy);
88
+ this.collectQueryCandidates(queryProxy);
85
89
  return shapeCastAll(queryType, queryProxy, this.queryCandidates);
86
90
  }
87
91
  castActor(params) {
@@ -90,7 +94,7 @@ export class CollisionDetectionSubsystem {
90
94
  return null;
91
95
  }
92
96
  const queryProxy = buildActorQueryProxy(queryType, params);
93
- this.sweepAndPruneQuery(queryProxy);
97
+ this.collectQueryCandidates(queryProxy);
94
98
  return shapeCast(queryType, queryProxy, this.queryCandidates);
95
99
  }
96
100
  castActorAll(params) {
@@ -99,7 +103,7 @@ export class CollisionDetectionSubsystem {
99
103
  return [];
100
104
  }
101
105
  const queryProxy = buildActorQueryProxy(queryType, params);
102
- this.sweepAndPruneQuery(queryProxy);
106
+ this.collectQueryCandidates(queryProxy);
103
107
  return shapeCastAll(queryType, queryProxy, this.queryCandidates);
104
108
  }
105
109
  handleActorAdd = (event) => {
@@ -130,8 +134,10 @@ export class CollisionDetectionSubsystem {
130
134
  aabb,
131
135
  geometry,
132
136
  orientationData: getOrientationData(actor),
137
+ treeEntryId: 0,
133
138
  layer: collider.layer,
134
139
  };
140
+ proxy.treeEntryId = this.queryTree.insert(aabb, proxy);
135
141
  this.axis.x.dispersionCalculator.addToSample(aabb);
136
142
  this.addToSortedList(proxy, 'x');
137
143
  this.axis.y.dispersionCalculator.addToSample(aabb);
@@ -149,6 +155,7 @@ export class CollisionDetectionSubsystem {
149
155
  proxy.geometry = geometry;
150
156
  proxy.orientationData = getOrientationData(actor);
151
157
  proxy.layer = collider.layer;
158
+ this.queryTree.update(proxy.treeEntryId, aabb);
152
159
  this.axis.x.dispersionCalculator.removeFromSample(prevAABB);
153
160
  this.axis.x.dispersionCalculator.addToSample(aabb);
154
161
  this.updateSortedList(proxy, 'x');
@@ -200,39 +207,15 @@ export class CollisionDetectionSubsystem {
200
207
  const collider2 = actor2?.getComponent(Collider);
201
208
  return !collider1?.disabled && !collider2?.disabled;
202
209
  }
203
- sweepAndPruneQuery(queryProxy) {
204
- const [mainAxis, secondAxis] = this.getAxes();
205
- const sortedList = this.axis[mainAxis].sortedList;
206
- const candidates = new Set();
207
- for (const item of sortedList) {
208
- const { proxy, value } = item;
209
- if (value < queryProxy.aabb.min[mainAxis]) {
210
- if (candidates.has(proxy)) {
211
- candidates.delete(proxy);
212
- }
213
- else {
214
- candidates.add(proxy);
215
- }
216
- continue;
217
- }
218
- if (value > queryProxy.aabb.max[mainAxis]) {
219
- break;
220
- }
221
- if (!candidates.has(proxy)) {
222
- candidates.add(proxy);
223
- }
224
- }
210
+ collectQueryCandidates(queryProxy) {
225
211
  let candidateIndex = 0;
226
- candidates.forEach((proxy) => {
212
+ this.queryTree.query(queryProxy.aabb, (proxy) => {
227
213
  if (queryProxy.excludedActors?.has(proxy.actor)) {
228
214
  return;
229
215
  }
230
216
  if (!this.testState(proxy, queryProxy)) {
231
217
  return;
232
218
  }
233
- if (!this.testAABB(proxy, queryProxy, secondAxis)) {
234
- return;
235
- }
236
219
  if (queryProxy.layer !== undefined &&
237
220
  !this.testCollisionLayers(proxy, queryProxy)) {
238
221
  return;
@@ -312,6 +295,7 @@ export class CollisionDetectionSubsystem {
312
295
  this.clearSortedList('y');
313
296
  this.actorIdsToDelete.forEach((id) => {
314
297
  const proxy = this.proxiesByActorId.get(id);
298
+ this.queryTree.remove(proxy.treeEntryId);
315
299
  this.axis.x.dispersionCalculator.removeFromSample(proxy.aabb);
316
300
  this.axis.y.dispersionCalculator.removeFromSample(proxy.aabb);
317
301
  this.proxiesByActorId.delete(id);
@@ -1,3 +1,9 @@
1
1
  import type { BoxCastGeometry, BoxGeometry } from '../../types';
2
2
  import type { ShapeCastCheckerFn } from '../types';
3
+ /**
4
+ * Casts a moving box by expanding every target box corner by the moving box
5
+ * half-extents, building the convex hull, and raycasting the moving box center
6
+ * against that expanded target. The final contact point is shifted back from
7
+ * the center ray to the moving box support point.
8
+ */
3
9
  export declare const checkBoxCastAndBox: ShapeCastCheckerFn<BoxCastGeometry, BoxGeometry>;
@@ -1,6 +1,12 @@
1
1
  import { intersectionCheckers } from '../../intersection-checkers';
2
2
  import { buildInitialOverlapHit } from '../utils';
3
3
  import { checkBoxCastAndConvexPoints, correctContactPoint } from '../box-utils';
4
+ /**
5
+ * Casts a moving box by expanding every target box corner by the moving box
6
+ * half-extents, building the convex hull, and raycasting the moving box center
7
+ * against that expanded target. The final contact point is shifted back from
8
+ * the center ray to the moving box support point.
9
+ */
4
10
  export const checkBoxCastAndBox = (boxCast, box) => {
5
11
  const overlapIntersection = intersectionCheckers.box.box(boxCast, box);
6
12
  if (overlapIntersection) {
@@ -1,3 +1,9 @@
1
1
  import type { BoxCastGeometry, CapsuleGeometry } from '../../types';
2
2
  import type { ShapeCastCheckerFn } from '../types';
3
+ /**
4
+ * Casts a moving box against a capsule by delegating to the box-capsule helper:
5
+ * the capsule segment is expanded by the box half-extents, the capsule caps are
6
+ * checked as circles, and the nearest hit is converted back to the moving box
7
+ * support point.
8
+ */
3
9
  export declare const checkBoxCastAndCapsule: ShapeCastCheckerFn<BoxCastGeometry, CapsuleGeometry>;
@@ -1,6 +1,13 @@
1
1
  import { intersectionCheckers } from '../../intersection-checkers';
2
2
  import { buildInitialOverlapHit } from '../utils';
3
- import { checkBoxCastAndCapsuleGeometry, correctContactPoint, } from '../box-utils';
3
+ import { correctContactPoint } from '../box-utils';
4
+ import { checkBoxCastAndCapsuleGeometry } from './utils';
5
+ /**
6
+ * Casts a moving box against a capsule by delegating to the box-capsule helper:
7
+ * the capsule segment is expanded by the box half-extents, the capsule caps are
8
+ * checked as circles, and the nearest hit is converted back to the moving box
9
+ * support point.
10
+ */
4
11
  export const checkBoxCastAndCapsule = (boxCast, capsule) => {
5
12
  const overlapIntersection = intersectionCheckers.box.capsule(boxCast, capsule);
6
13
  if (overlapIntersection) {
@@ -0,0 +1,3 @@
1
+ import type { BoxCastGeometry, CapsuleGeometry } from '../../types';
2
+ import type { ShapeCastCheckerHit } from '../types';
3
+ export declare const checkBoxCastAndCapsuleGeometry: (box: BoxCastGeometry, capsule: CapsuleGeometry) => ShapeCastCheckerHit | false;
@@ -0,0 +1,23 @@
1
+ import { chooseNearestIntersection } from '../../intersection-checkers/common/cast';
2
+ import { checkRayAndCapsuleIntersection } from '../../raycast-checkers/ray-capsule/check-ray-and-capsule-intersection';
3
+ import { raycastCheckers } from '../../raycast-checkers';
4
+ import { checkBoxCastAndCircleGeometry } from '../box-utils';
5
+ import { ConvexHullWorkspace } from '../common/convex-hull-workspace';
6
+ const convexHullWorkspace = new ConvexHullWorkspace(8);
7
+ const buildBoxExpandedCapsuleGeometry = (halfExtents, capsule) => {
8
+ convexHullWorkspace.start();
9
+ convexHullWorkspace.addExpandedPoint(capsule.point1, halfExtents);
10
+ convexHullWorkspace.addExpandedPoint(capsule.point2, halfExtents);
11
+ return convexHullWorkspace.buildGeometry();
12
+ };
13
+ export const checkBoxCastAndCapsuleGeometry = (box, capsule) => {
14
+ const { halfExtents } = box;
15
+ const expandedCapsule = buildBoxExpandedCapsuleGeometry(halfExtents, capsule);
16
+ let nearest = raycastCheckers.ray.box(box, expandedCapsule);
17
+ nearest = chooseNearestIntersection(nearest, checkBoxCastAndCircleGeometry(box, capsule.point1, capsule.radius));
18
+ nearest = chooseNearestIntersection(nearest, checkBoxCastAndCircleGeometry(box, capsule.point2, capsule.radius));
19
+ for (const edge of expandedCapsule.edges) {
20
+ nearest = chooseNearestIntersection(nearest, checkRayAndCapsuleIntersection(box, edge, capsule.radius));
21
+ }
22
+ return nearest;
23
+ };
@@ -1,3 +1,9 @@
1
1
  import type { BoxCastGeometry, CircleGeometry } from '../../types';
2
2
  import type { ShapeCastCheckerFn } from '../types';
3
+ /**
4
+ * Casts a moving box against a circle by treating the target as the union of
5
+ * two expanded axis-aligned rectangles and four corner circles. The nearest
6
+ * sub-hit is then shifted back from the center ray to the moving box support
7
+ * point.
8
+ */
3
9
  export declare const checkBoxCastAndCircle: ShapeCastCheckerFn<BoxCastGeometry, CircleGeometry>;
@@ -1,6 +1,12 @@
1
1
  import { intersectionCheckers } from '../../intersection-checkers';
2
2
  import { buildInitialOverlapHit } from '../utils';
3
3
  import { checkBoxCastAndCircleGeometry, correctContactPoint, } from '../box-utils';
4
+ /**
5
+ * Casts a moving box against a circle by treating the target as the union of
6
+ * two expanded axis-aligned rectangles and four corner circles. The nearest
7
+ * sub-hit is then shifted back from the center ray to the moving box support
8
+ * point.
9
+ */
4
10
  export const checkBoxCastAndCircle = (boxCast, circle) => {
5
11
  const overlapIntersection = intersectionCheckers.box.circle(boxCast, circle);
6
12
  if (overlapIntersection) {
@@ -1,3 +1,8 @@
1
1
  import type { BoxCastGeometry, SegmentGeometry } from '../../types';
2
2
  import type { ShapeCastCheckerFn } from '../types';
3
+ /**
4
+ * Casts a moving box against a segment by expanding both segment endpoints by
5
+ * the moving box half-extents, building the convex hull, and raycasting the box
6
+ * center against that expanded segment shape.
7
+ */
3
8
  export declare const checkBoxCastAndSegment: ShapeCastCheckerFn<BoxCastGeometry, SegmentGeometry>;
@@ -1,15 +1,17 @@
1
1
  import { intersectionCheckers } from '../../intersection-checkers';
2
2
  import { buildInitialOverlapHit } from '../utils';
3
- import { checkBoxCastAndConvexPoints, correctContactPoint } from '../box-utils';
3
+ import { checkBoxCastAndConvexPointPair, correctContactPoint, } from '../box-utils';
4
+ /**
5
+ * Casts a moving box against a segment by expanding both segment endpoints by
6
+ * the moving box half-extents, building the convex hull, and raycasting the box
7
+ * center against that expanded segment shape.
8
+ */
4
9
  export const checkBoxCastAndSegment = (boxCast, segment) => {
5
10
  const overlapIntersection = intersectionCheckers.box.segment(boxCast, segment);
6
11
  if (overlapIntersection) {
7
12
  return buildInitialOverlapHit(overlapIntersection);
8
13
  }
9
- const hit = checkBoxCastAndConvexPoints(boxCast, [
10
- segment.point1,
11
- segment.point2,
12
- ]);
14
+ const hit = checkBoxCastAndConvexPointPair(boxCast, segment.point1, segment.point2);
13
15
  correctContactPoint(boxCast, hit);
14
16
  return hit;
15
17
  };
@@ -1,7 +1,7 @@
1
1
  import type { RaycastCheckerHit } from '../raycast-checkers/types';
2
- import type { BoxCastGeometry, CapsuleGeometry, Point } from '../types';
2
+ import type { BoxCastGeometry, Point } from '../types';
3
3
  import type { ShapeCastCheckerHit } from './types';
4
4
  export declare const correctContactPoint: (box: BoxCastGeometry, hit: RaycastCheckerHit | false) => void;
5
5
  export declare const checkBoxCastAndConvexPoints: (box: BoxCastGeometry, points: Point[]) => ShapeCastCheckerHit | false;
6
+ export declare const checkBoxCastAndConvexPointPair: (box: BoxCastGeometry, point1: Point, point2: Point) => ShapeCastCheckerHit | false;
6
7
  export declare const checkBoxCastAndCircleGeometry: (box: BoxCastGeometry, center: Point, radius: number) => ShapeCastCheckerHit | false;
7
- export declare const checkBoxCastAndCapsuleGeometry: (box: BoxCastGeometry, capsule: CapsuleGeometry) => ShapeCastCheckerHit | false;