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.
- package/build/contrib/systems/physics-system/subsystems/collision-detection/dynamic-aabb-tree/index.d.ts +28 -0
- package/build/contrib/systems/physics-system/subsystems/collision-detection/dynamic-aabb-tree/index.js +299 -0
- package/build/contrib/systems/physics-system/subsystems/collision-detection/index.d.ts +2 -1
- package/build/contrib/systems/physics-system/subsystems/collision-detection/index.js +18 -34
- package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/box-box/check-box-cast-and-box.d.ts +6 -0
- package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/box-box/check-box-cast-and-box.js +6 -0
- package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/box-capsule/check-box-cast-and-capsule.d.ts +6 -0
- package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/box-capsule/check-box-cast-and-capsule.js +8 -1
- package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/box-capsule/utils.d.ts +3 -0
- package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/box-capsule/utils.js +23 -0
- package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/box-circle/check-box-cast-and-circle.d.ts +6 -0
- package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/box-circle/check-box-cast-and-circle.js +6 -0
- package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/box-segment/check-box-cast-and-segment.d.ts +5 -0
- package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/box-segment/check-box-cast-and-segment.js +7 -5
- package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/box-utils.d.ts +2 -2
- package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/box-utils.js +24 -120
- package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/capsule-box/check-capsule-cast-and-box.d.ts +5 -0
- package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/capsule-box/check-capsule-cast-and-box.js +5 -0
- package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/capsule-capsule/check-capsule-cast-and-capsule.d.ts +5 -0
- package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/capsule-capsule/check-capsule-cast-and-capsule.js +5 -0
- package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/capsule-circle/check-capsule-cast-and-circle.d.ts +5 -0
- package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/capsule-circle/check-capsule-cast-and-circle.js +5 -0
- package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/capsule-segment/check-capsule-cast-and-segment.d.ts +5 -0
- package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/capsule-segment/check-capsule-cast-and-segment.js +5 -0
- package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/circle-box/check-circle-cast-and-box.d.ts +5 -0
- package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/circle-box/check-circle-cast-and-box.js +5 -0
- package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/circle-capsule/check-circle-cast-and-capsule.d.ts +5 -0
- package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/circle-capsule/check-circle-cast-and-capsule.js +5 -0
- package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/circle-circle/check-circle-cast-and-circle.d.ts +5 -0
- package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/circle-circle/check-circle-cast-and-circle.js +5 -0
- package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/circle-segment/check-circle-cast-and-segment.d.ts +5 -0
- package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/circle-segment/check-circle-cast-and-segment.js +5 -0
- package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/common/axis-aligned-box-workspace.d.ts +24 -0
- package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/common/axis-aligned-box-workspace.js +60 -0
- package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/common/constants.d.ts +1 -0
- package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/common/constants.js +6 -0
- package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/common/convex-hull-workspace.d.ts +92 -0
- package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/common/convex-hull-workspace.js +205 -0
- package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/common/geometry.d.ts +6 -0
- package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/common/geometry.js +10 -0
- package/build/contrib/systems/physics-system/subsystems/collision-detection/types.d.ts +2 -0
- 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
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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 {
|
|
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,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 {
|
|
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 =
|
|
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,
|
|
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;
|