dacha 0.18.0-alpha.9 → 0.18.0

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 (49) hide show
  1. package/build/contrib/components/index.d.ts +1 -0
  2. package/build/contrib/components/index.js +1 -0
  3. package/build/contrib/components/interpolation/index.d.ts +85 -0
  4. package/build/contrib/components/interpolation/index.js +101 -0
  5. package/build/contrib/components/rigid-body/index.d.ts +8 -0
  6. package/build/contrib/components/rigid-body/index.js +8 -0
  7. package/build/contrib/systems/index.d.ts +2 -0
  8. package/build/contrib/systems/index.js +1 -0
  9. package/build/contrib/systems/interpolator/api.d.ts +40 -0
  10. package/build/contrib/systems/interpolator/api.js +70 -0
  11. package/build/contrib/systems/interpolator/index.d.ts +3 -0
  12. package/build/contrib/systems/interpolator/index.js +2 -0
  13. package/build/contrib/systems/interpolator/system.d.ts +31 -0
  14. package/build/contrib/systems/interpolator/system.js +96 -0
  15. package/build/contrib/systems/interpolator/tests/helpers.d.ts +13 -0
  16. package/build/contrib/systems/interpolator/tests/helpers.js +45 -0
  17. package/build/contrib/systems/interpolator/utils.d.ts +5 -0
  18. package/build/contrib/systems/interpolator/utils.js +41 -0
  19. package/build/contrib/systems/physics-system/api.d.ts +45 -1
  20. package/build/contrib/systems/physics-system/api.js +49 -0
  21. package/build/contrib/systems/physics-system/consts.d.ts +3 -1
  22. package/build/contrib/systems/physics-system/consts.js +4 -1
  23. package/build/contrib/systems/physics-system/physics-system.js +7 -1
  24. package/build/contrib/systems/physics-system/subsystems/collision-detection/dynamic-aabb-tree/index.d.ts +1 -1
  25. package/build/contrib/systems/physics-system/subsystems/collision-detection/dynamic-aabb-tree/index.js +21 -15
  26. package/build/contrib/systems/physics-system/subsystems/collision-detection/index.d.ts +11 -11
  27. package/build/contrib/systems/physics-system/subsystems/collision-detection/index.js +130 -124
  28. package/build/contrib/systems/physics-system/subsystems/collision-detection/query-utils.d.ts +5 -0
  29. package/build/contrib/systems/physics-system/subsystems/collision-detection/query-utils.js +97 -81
  30. package/build/contrib/systems/physics-system/subsystems/collision-detection/types.d.ts +0 -15
  31. package/build/contrib/systems/physics-system/subsystems/collision-detection/utils.d.ts +3 -0
  32. package/build/contrib/systems/physics-system/subsystems/collision-detection/utils.js +9 -0
  33. package/build/contrib/systems/physics-system/subsystems/physics/mass-properties.js +1 -1
  34. package/build/contrib/systems/physics-system/types.d.ts +12 -2
  35. package/build/contrib/systems/renderer/actor-render-tree.js +15 -6
  36. package/build/engine/data-lib/index.d.ts +1 -1
  37. package/build/engine/data-lib/index.js +1 -1
  38. package/build/engine/data-lib/pool.d.ts +9 -0
  39. package/build/engine/data-lib/pool.js +19 -0
  40. package/build/engine/game-loop.js +3 -3
  41. package/build/engine/math-lib/math/ops.d.ts +16 -0
  42. package/build/engine/math-lib/math/ops.js +25 -0
  43. package/package.json +1 -1
  44. package/build/contrib/systems/physics-system/subsystems/collision-detection/dispersion-calculator/index.d.ts +0 -11
  45. package/build/contrib/systems/physics-system/subsystems/collision-detection/dispersion-calculator/index.js +0 -41
  46. package/build/engine/data-lib/sort/index.d.ts +0 -1
  47. package/build/engine/data-lib/sort/index.js +0 -1
  48. package/build/engine/data-lib/sort/insertion-sort.d.ts +0 -1
  49. package/build/engine/data-lib/sort/insertion-sort.js +0 -14
@@ -1,47 +1,42 @@
1
1
  import { ActorQuery } from '../../../../../engine/actor';
2
- import { Transform, Collider, RigidBody } from '../../../../components';
2
+ import { Transform, Collider } from '../../../../components';
3
3
  import { AddActor, RemoveActor } from '../../../../../engine/events';
4
- import { insertionSort } from '../../../../../engine/data-lib';
4
+ import { Pool } from '../../../../../engine/data-lib';
5
5
  import { DynamicAABBTree } from './dynamic-aabb-tree';
6
6
  import { geometryBuilders } from './geometry-builders';
7
7
  import { aabbBuilders } from './aabb-builders';
8
8
  import { intersectionCheckers } from './intersection-checkers';
9
- import { DispersionCalculator } from './dispersion-calculator';
10
9
  import { checkTransform, checkCollider, getOrientationData, } from './reorientation-checkers';
11
- import { buildQueryProxy, buildActorQueryProxy, raycast, raycastAll, overlap, getOverlapQueryType, shapeCast, shapeCastAll, getShapeCastQueryType, getActorCastQueryType, } from './query-utils';
10
+ import { buildQueryProxy, buildActorQueryProxy, raycast, raycastAll, raycastEach, overlap, overlapEach, getOverlapQueryType, shapeCast, shapeCastAll, shapeCastEach, getShapeCastQueryType, getActorCastQueryType, makeCastHit, makeOverlapHit, } from './query-utils';
11
+ import { isStatic, isDisabled } from './utils';
12
12
  export class CollisionDetectionSubsystem {
13
13
  actorQuery;
14
- axis;
15
14
  queryTree;
16
15
  proxiesByActorId;
17
16
  proxyPairs;
18
17
  contacts;
19
18
  actorIdsToDelete;
20
19
  collisionMatrix;
21
- queryCandidates;
20
+ candidateBufferPool;
21
+ castHitPool;
22
+ overlapHitPool;
22
23
  constructor(options) {
23
24
  const settings = options.globalOptions.physics;
24
25
  this.actorQuery = new ActorQuery({
25
26
  scene: options.scene,
26
27
  filter: [Collider, Transform],
27
28
  });
28
- this.axis = {
29
- x: {
30
- sortedList: [],
31
- dispersionCalculator: new DispersionCalculator('x'),
32
- },
33
- y: {
34
- sortedList: [],
35
- dispersionCalculator: new DispersionCalculator('y'),
36
- },
37
- };
38
29
  this.queryTree = new DynamicAABBTree();
39
30
  this.proxiesByActorId = new Map();
40
31
  this.proxyPairs = [];
41
32
  this.contacts = [];
42
33
  this.actorIdsToDelete = new Set();
43
34
  this.collisionMatrix = settings?.collisionMatrix ?? {};
44
- this.queryCandidates = [];
35
+ this.candidateBufferPool = new Pool(() => [], (buffer) => {
36
+ buffer.length = 0;
37
+ });
38
+ this.castHitPool = new Pool(makeCastHit);
39
+ this.overlapHitPool = new Pool(makeOverlapHit);
45
40
  this.actorQuery.getActors().forEach((actor) => this.addProxy(actor));
46
41
  this.actorQuery.addEventListener(AddActor, this.handleActorAdd);
47
42
  this.actorQuery.addEventListener(RemoveActor, this.handleActorRemove);
@@ -54,18 +49,15 @@ export class CollisionDetectionSubsystem {
54
49
  }
55
50
  raycast(params) {
56
51
  const queryProxy = buildQueryProxy('ray', params);
57
- this.collectQueryCandidates(queryProxy);
58
- return raycast(queryProxy, this.queryCandidates);
52
+ return this.withCandidates(queryProxy, (candidates) => raycast(queryProxy, candidates));
59
53
  }
60
54
  raycastAll(params) {
61
55
  const queryProxy = buildQueryProxy('ray', params);
62
- this.collectQueryCandidates(queryProxy);
63
- return raycastAll(queryProxy, this.queryCandidates);
56
+ return this.withCandidates(queryProxy, (candidates) => raycastAll(queryProxy, candidates));
64
57
  }
65
58
  overlapShape(params) {
66
59
  const queryProxy = buildQueryProxy(params.shape.type, params);
67
- this.collectQueryCandidates(queryProxy);
68
- return overlap(params.shape.type, queryProxy, this.queryCandidates);
60
+ return this.withCandidates(queryProxy, (candidates) => overlap(params.shape.type, queryProxy, candidates));
69
61
  }
70
62
  overlapActor(params) {
71
63
  const queryType = getOverlapQueryType(params);
@@ -73,20 +65,17 @@ export class CollisionDetectionSubsystem {
73
65
  return [];
74
66
  }
75
67
  const queryProxy = buildActorQueryProxy(queryType, params);
76
- this.collectQueryCandidates(queryProxy);
77
- return overlap(queryType, queryProxy, this.queryCandidates);
68
+ return this.withCandidates(queryProxy, (candidates) => overlap(queryType, queryProxy, candidates));
78
69
  }
79
70
  shapeCast(params) {
80
71
  const queryType = getShapeCastQueryType(params);
81
72
  const queryProxy = buildQueryProxy(queryType, params);
82
- this.collectQueryCandidates(queryProxy);
83
- return shapeCast(queryType, queryProxy, this.queryCandidates);
73
+ return this.withCandidates(queryProxy, (candidates) => shapeCast(queryType, queryProxy, candidates));
84
74
  }
85
75
  shapeCastAll(params) {
86
76
  const queryType = getShapeCastQueryType(params);
87
77
  const queryProxy = buildQueryProxy(queryType, params);
88
- this.collectQueryCandidates(queryProxy);
89
- return shapeCastAll(queryType, queryProxy, this.queryCandidates);
78
+ return this.withCandidates(queryProxy, (candidates) => shapeCastAll(queryType, queryProxy, candidates));
90
79
  }
91
80
  castActor(params) {
92
81
  const queryType = getActorCastQueryType(params);
@@ -94,8 +83,7 @@ export class CollisionDetectionSubsystem {
94
83
  return null;
95
84
  }
96
85
  const queryProxy = buildActorQueryProxy(queryType, params);
97
- this.collectQueryCandidates(queryProxy);
98
- return shapeCast(queryType, queryProxy, this.queryCandidates);
86
+ return this.withCandidates(queryProxy, (candidates) => shapeCast(queryType, queryProxy, candidates));
99
87
  }
100
88
  castActorAll(params) {
101
89
  const queryType = getActorCastQueryType(params);
@@ -103,8 +91,76 @@ export class CollisionDetectionSubsystem {
103
91
  return [];
104
92
  }
105
93
  const queryProxy = buildActorQueryProxy(queryType, params);
106
- this.collectQueryCandidates(queryProxy);
107
- return shapeCastAll(queryType, queryProxy, this.queryCandidates);
94
+ return this.withCandidates(queryProxy, (candidates) => shapeCastAll(queryType, queryProxy, candidates));
95
+ }
96
+ raycastEach(params, callback) {
97
+ const queryProxy = buildQueryProxy('ray', params);
98
+ this.withCandidates(queryProxy, (candidates) => {
99
+ const hit = this.castHitPool.acquire();
100
+ try {
101
+ raycastEach(queryProxy, candidates, hit, callback);
102
+ }
103
+ finally {
104
+ this.castHitPool.release(hit);
105
+ }
106
+ });
107
+ }
108
+ shapeCastEach(params, callback) {
109
+ const queryType = getShapeCastQueryType(params);
110
+ const queryProxy = buildQueryProxy(queryType, params);
111
+ this.withCandidates(queryProxy, (candidates) => {
112
+ const hit = this.castHitPool.acquire();
113
+ try {
114
+ shapeCastEach(queryType, queryProxy, candidates, hit, callback);
115
+ }
116
+ finally {
117
+ this.castHitPool.release(hit);
118
+ }
119
+ });
120
+ }
121
+ overlapEach(params, callback) {
122
+ const queryProxy = buildQueryProxy(params.shape.type, params);
123
+ this.withCandidates(queryProxy, (candidates) => {
124
+ const hit = this.overlapHitPool.acquire();
125
+ try {
126
+ overlapEach(params.shape.type, queryProxy, candidates, hit, callback);
127
+ }
128
+ finally {
129
+ this.overlapHitPool.release(hit);
130
+ }
131
+ });
132
+ }
133
+ castActorEach(params, callback) {
134
+ const queryType = getActorCastQueryType(params);
135
+ if (!queryType) {
136
+ return;
137
+ }
138
+ const queryProxy = buildActorQueryProxy(queryType, params);
139
+ this.withCandidates(queryProxy, (candidates) => {
140
+ const hit = this.castHitPool.acquire();
141
+ try {
142
+ shapeCastEach(queryType, queryProxy, candidates, hit, callback);
143
+ }
144
+ finally {
145
+ this.castHitPool.release(hit);
146
+ }
147
+ });
148
+ }
149
+ overlapActorEach(params, callback) {
150
+ const queryType = getOverlapQueryType(params);
151
+ if (!queryType) {
152
+ return;
153
+ }
154
+ const queryProxy = buildActorQueryProxy(queryType, params);
155
+ this.withCandidates(queryProxy, (candidates) => {
156
+ const hit = this.overlapHitPool.acquire();
157
+ try {
158
+ overlapEach(queryType, queryProxy, candidates, hit, callback);
159
+ }
160
+ finally {
161
+ this.overlapHitPool.release(hit);
162
+ }
163
+ });
108
164
  }
109
165
  handleActorAdd = (event) => {
110
166
  this.addProxy(event.actor);
@@ -138,10 +194,6 @@ export class CollisionDetectionSubsystem {
138
194
  layer: collider.layer,
139
195
  };
140
196
  proxy.treeEntryId = this.queryTree.insert(aabb, proxy);
141
- this.axis.x.dispersionCalculator.addToSample(aabb);
142
- this.addToSortedList(proxy, 'x');
143
- this.axis.y.dispersionCalculator.addToSample(aabb);
144
- this.addToSortedList(proxy, 'y');
145
197
  this.proxiesByActorId.set(actor.id, proxy);
146
198
  }
147
199
  updateProxy(actor) {
@@ -150,72 +202,34 @@ export class CollisionDetectionSubsystem {
150
202
  const geometry = geometryBuilders[collider.shape.type](collider, transform);
151
203
  const aabb = aabbBuilders[collider.shape.type](geometry);
152
204
  const proxy = this.proxiesByActorId.get(actor.id);
153
- const prevAABB = proxy.aabb;
154
205
  proxy.aabb = aabb;
155
206
  proxy.geometry = geometry;
156
207
  proxy.orientationData = getOrientationData(actor);
157
208
  proxy.layer = collider.layer;
158
209
  this.queryTree.update(proxy.treeEntryId, aabb);
159
- this.axis.x.dispersionCalculator.removeFromSample(prevAABB);
160
- this.axis.x.dispersionCalculator.addToSample(aabb);
161
- this.updateSortedList(proxy, 'x');
162
- this.axis.y.dispersionCalculator.removeFromSample(prevAABB);
163
- this.axis.y.dispersionCalculator.addToSample(aabb);
164
- this.updateSortedList(proxy, 'y');
165
- }
166
- addToSortedList(proxy, axis) {
167
- const min = { value: proxy.aabb.min[axis], proxy };
168
- const max = { value: proxy.aabb.max[axis], proxy };
169
- this.axis[axis].sortedList.push(min, max);
170
- proxy.edges ??= {};
171
- proxy.edges[axis] = [min, max];
172
- }
173
- updateSortedList(proxy, axis) {
174
- const [min, max] = proxy.edges[axis];
175
- min.value = proxy.aabb.min[axis];
176
- min.proxy = proxy;
177
- max.value = proxy.aabb.max[axis];
178
- max.proxy = proxy;
179
- }
180
- clearSortedList(axis) {
181
- this.axis[axis].sortedList = this.axis[axis].sortedList.filter((item) => !this.actorIdsToDelete.has(item.proxy.actor.id));
182
- }
183
- getAxes() {
184
- const xDispersion = this.axis.x.dispersionCalculator.getDispersion();
185
- const yDispersion = this.axis.y.dispersionCalculator.getDispersion();
186
- return xDispersion >= yDispersion ? ['x', 'y'] : ['y', 'x'];
187
- }
188
- areStaticBodies(proxy1, proxy2) {
189
- const { actor: actor1 } = proxy1;
190
- const { actor: actor2 } = proxy2;
191
- const rigidBody1 = actor1.getComponent(RigidBody);
192
- const rigidBody2 = actor2.getComponent(RigidBody);
193
- return rigidBody1?.type === 'static' && rigidBody2?.type === 'static';
194
- }
195
- testAABB(proxy1, proxy2, axis) {
196
- const aabb1 = proxy1.aabb;
197
- const aabb2 = proxy2.aabb;
198
- return (aabb1.max[axis] >= aabb2.min[axis] && aabb1.min[axis] <= aabb2.max[axis]);
199
210
  }
200
211
  testCollisionLayers(proxy1, proxy2) {
201
212
  return (this.collisionMatrix[proxy1.layer]?.[proxy2.layer] ??
202
213
  this.collisionMatrix[proxy2.layer]?.[proxy1.layer] ??
203
214
  true);
204
215
  }
205
- testState(proxy1, proxy2) {
206
- const actor1 = 'actor' in proxy1 ? proxy1.actor : undefined;
207
- const actor2 = 'actor' in proxy2 ? proxy2.actor : undefined;
208
- const collider1 = actor1?.getComponent(Collider);
209
- const collider2 = actor2?.getComponent(Collider);
210
- return !collider1?.disabled && !collider2?.disabled;
216
+ withCandidates(queryProxy, run) {
217
+ const candidates = this.candidateBufferPool.acquire();
218
+ try {
219
+ this.collectQueryCandidates(queryProxy, candidates);
220
+ return run(candidates);
221
+ }
222
+ finally {
223
+ this.candidateBufferPool.release(candidates);
224
+ }
211
225
  }
212
- collectQueryCandidates(queryProxy) {
226
+ collectQueryCandidates(queryProxy, out) {
213
227
  let candidateIndex = 0;
214
228
  this.queryTree.query(queryProxy.aabb, (proxy) => {
215
229
  if (queryProxy.excludedActors?.has(proxy.actor)) {
216
230
  return;
217
231
  }
218
- if (!this.testState(proxy, queryProxy)) {
232
+ if (isDisabled(proxy)) {
219
233
  return;
220
234
  }
221
235
  if (queryProxy.layer !== undefined &&
@@ -225,44 +239,40 @@ export class CollisionDetectionSubsystem {
225
239
  if (queryProxy.actorFilter && !queryProxy.actorFilter(proxy.actor)) {
226
240
  return;
227
241
  }
228
- this.queryCandidates[candidateIndex] = proxy;
242
+ out[candidateIndex] = proxy;
229
243
  candidateIndex += 1;
230
244
  });
231
- this.queryCandidates.length = candidateIndex;
245
+ out.length = candidateIndex;
232
246
  }
233
- sweepAndPrune() {
234
- const [mainAxis, secondAxis] = this.getAxes();
235
- const { sortedList } = this.axis[mainAxis];
236
- insertionSort(sortedList, (arg1, arg2) => arg1.value - arg2.value);
237
- const activeProxies = new Set();
238
- let proxyPairIndex = 0;
239
- for (const item of sortedList) {
240
- const { proxy } = item;
241
- if (!activeProxies.has(proxy)) {
242
- activeProxies.forEach((activeProxy) => {
243
- if (!this.testState(proxy, activeProxy)) {
244
- return;
245
- }
246
- if (!this.testAABB(proxy, activeProxy, secondAxis)) {
247
- return;
248
- }
249
- if (this.areStaticBodies(proxy, activeProxy)) {
250
- return;
251
- }
252
- if (!this.testCollisionLayers(proxy, activeProxy)) {
253
- return;
254
- }
255
- this.proxyPairs[proxyPairIndex] = [proxy, activeProxy];
256
- proxyPairIndex += 1;
257
- });
258
- activeProxies.add(proxy);
247
+ collectPairs() {
248
+ let pairIndex = 0;
249
+ this.proxiesByActorId.forEach((proxy) => {
250
+ if (isStatic(proxy)) {
251
+ return;
259
252
  }
260
- else {
261
- activeProxies.delete(proxy);
253
+ if (isDisabled(proxy)) {
254
+ return;
262
255
  }
263
- }
264
- if (this.proxyPairs.length > proxyPairIndex) {
265
- this.proxyPairs.length = proxyPairIndex;
256
+ this.queryTree.query(proxy.aabb, (other) => {
257
+ if (other === proxy) {
258
+ return;
259
+ }
260
+ // Deduplicate dynamic-dynamic pairs
261
+ if (!isStatic(other) && proxy.treeEntryId >= other.treeEntryId) {
262
+ return;
263
+ }
264
+ if (isDisabled(other)) {
265
+ return;
266
+ }
267
+ if (!this.testCollisionLayers(proxy, other)) {
268
+ return;
269
+ }
270
+ this.proxyPairs[pairIndex] = [proxy, other];
271
+ pairIndex += 1;
272
+ });
273
+ });
274
+ if (this.proxyPairs.length > pairIndex) {
275
+ this.proxyPairs.length = pairIndex;
266
276
  }
267
277
  }
268
278
  checkOnIntersection(proxyPair) {
@@ -293,13 +303,9 @@ export class CollisionDetectionSubsystem {
293
303
  if (this.actorIdsToDelete.size === 0) {
294
304
  return;
295
305
  }
296
- this.clearSortedList('x');
297
- this.clearSortedList('y');
298
306
  this.actorIdsToDelete.forEach((id) => {
299
307
  const proxy = this.proxiesByActorId.get(id);
300
308
  this.queryTree.remove(proxy.treeEntryId);
301
- this.axis.x.dispersionCalculator.removeFromSample(proxy.aabb);
302
- this.axis.y.dispersionCalculator.removeFromSample(proxy.aabb);
303
309
  this.proxiesByActorId.delete(id);
304
310
  });
305
311
  this.actorIdsToDelete.clear();
@@ -312,7 +318,7 @@ export class CollisionDetectionSubsystem {
312
318
  }
313
319
  this.updateProxy(actor);
314
320
  });
315
- this.sweepAndPrune();
321
+ this.collectPairs();
316
322
  let contactIndex = 0;
317
323
  this.proxyPairs.forEach((proxyPair) => {
318
324
  const intersection = this.checkOnIntersection(proxyPair);
@@ -10,9 +10,14 @@ export declare function buildActorQueryProxy(type: OverlapQueryType, params: Ove
10
10
  export declare const getOverlapQueryType: (params: OverlapActorParams) => OverlapQueryType | null;
11
11
  export declare const getShapeCastQueryType: (params: ShapeCastParams) => ShapeCastQueryType;
12
12
  export declare const getActorCastQueryType: (params: CastActorParams) => ShapeCastQueryType | null;
13
+ export declare const makeCastHit: () => CastHit;
14
+ export declare const makeOverlapHit: () => OverlapHit;
13
15
  export declare const overlap: (type: OverlapQueryType, queryProxy: QueryProxy, proxies: Iterable<ActorProxy>) => OverlapHit[];
14
16
  export declare const raycast: (queryProxy: QueryProxy, proxies: Iterable<ActorProxy>) => CastHit | null;
15
17
  export declare const raycastAll: (queryProxy: QueryProxy, proxies: Iterable<ActorProxy>) => CastHit[];
16
18
  export declare const shapeCast: (type: ShapeCastQueryType, queryProxy: QueryProxy, proxies: Iterable<ActorProxy>) => CastHit | null;
17
19
  export declare const shapeCastAll: (type: ShapeCastQueryType, queryProxy: QueryProxy, proxies: Iterable<ActorProxy>) => CastHit[];
20
+ export declare const raycastEach: (queryProxy: QueryProxy, proxies: Iterable<ActorProxy>, hit: CastHit, callback: (hit: CastHit) => void) => void;
21
+ export declare const shapeCastEach: (type: ShapeCastQueryType, queryProxy: QueryProxy, proxies: Iterable<ActorProxy>, hit: CastHit, callback: (hit: CastHit) => void) => void;
22
+ export declare const overlapEach: (type: OverlapQueryType, queryProxy: QueryProxy, proxies: Iterable<ActorProxy>, hit: OverlapHit, callback: (hit: OverlapHit) => void) => void;
18
23
  export {};
@@ -1,3 +1,4 @@
1
+ import { Vector2 } from '../../../../../engine/math-lib';
1
2
  import { Collider, Transform } from '../../../../components';
2
3
  import { geometryBuilders } from './geometry-builders';
3
4
  import { aabbBuilders } from './aabb-builders';
@@ -10,7 +11,7 @@ export function buildQueryProxy(type, params) {
10
11
  aabb: aabbBuilders[type](geometry),
11
12
  geometry,
12
13
  layer: params.layer,
13
- excludedActors: params.excludeActors
14
+ excludedActors: params.excludeActors?.length
14
15
  ? new Set(params.excludeActors)
15
16
  : undefined,
16
17
  actorFilter: params.actorFilter,
@@ -62,130 +63,145 @@ export const getActorCastQueryType = (params) => {
62
63
  return null;
63
64
  }
64
65
  };
66
+ export const makeCastHit = () => ({
67
+ actor: undefined,
68
+ point: { x: 0, y: 0 },
69
+ normal: new Vector2(0, 0),
70
+ distance: 0,
71
+ });
72
+ const cloneCastHit = (hit) => ({
73
+ actor: hit.actor,
74
+ point: hit.point,
75
+ normal: hit.normal,
76
+ distance: hit.distance,
77
+ });
78
+ export const makeOverlapHit = () => ({
79
+ actor: undefined,
80
+ normal: new Vector2(0, 0),
81
+ penetration: 0,
82
+ contactPoints: [],
83
+ });
84
+ const cloneOverlapHit = (hit) => ({
85
+ actor: hit.actor,
86
+ normal: hit.normal,
87
+ penetration: hit.penetration,
88
+ contactPoints: hit.contactPoints,
89
+ });
65
90
  export const overlap = (type, queryProxy, proxies) => {
91
+ const scratch = makeOverlapHit();
66
92
  const hits = [];
67
- for (const proxy of proxies) {
68
- const collider = proxy.actor.getComponent(Collider);
69
- const checker = intersectionCheckers[type]?.[collider.shape.type];
70
- if (!checker) {
71
- continue;
72
- }
73
- const intersection = checker(queryProxy.geometry, proxy.geometry);
74
- if (!intersection) {
75
- continue;
76
- }
77
- const hit = {
78
- actor: proxy.actor,
79
- normal: intersection.normal.clone().multiplyNumber(-1),
80
- penetration: intersection.penetration,
81
- contactPoints: intersection.contactPoints,
82
- };
83
- if (queryProxy.hitFilter && !queryProxy.hitFilter(hit)) {
84
- continue;
85
- }
86
- hits.push(hit);
87
- }
93
+ overlapEach(type, queryProxy, proxies, scratch, (hit) => {
94
+ hits.push(cloneOverlapHit(hit));
95
+ });
88
96
  return hits;
89
97
  };
90
98
  export const raycast = (queryProxy, proxies) => {
99
+ const scratch = makeCastHit();
91
100
  let nearestDistance = Infinity;
92
101
  let nearestHit = null;
93
- for (const proxy of proxies) {
94
- const collider = proxy.actor.getComponent(Collider);
95
- const hit = raycastCheckers.ray[collider.shape.type](queryProxy.geometry, proxy.geometry);
96
- if (!hit) {
97
- continue;
98
- }
99
- const castHit = {
100
- actor: proxy.actor,
101
- point: hit.point,
102
- normal: hit.normal,
103
- distance: hit.distance,
104
- };
105
- if (queryProxy.hitFilter && !queryProxy.hitFilter(castHit)) {
106
- continue;
107
- }
102
+ raycastEach(queryProxy, proxies, scratch, (hit) => {
108
103
  if (hit.distance < nearestDistance) {
109
- nearestHit = castHit;
110
104
  nearestDistance = hit.distance;
105
+ nearestHit = nearestHit ?? makeCastHit();
106
+ nearestHit.actor = hit.actor;
107
+ nearestHit.point = hit.point;
108
+ nearestHit.normal = hit.normal;
109
+ nearestHit.distance = hit.distance;
111
110
  }
112
- }
111
+ });
113
112
  return nearestHit;
114
113
  };
115
114
  export const raycastAll = (queryProxy, proxies) => {
115
+ const scratch = makeCastHit();
116
116
  const hits = [];
117
+ raycastEach(queryProxy, proxies, scratch, (hit) => {
118
+ hits.push(cloneCastHit(hit));
119
+ });
120
+ hits.sort((arg1, arg2) => arg1.distance - arg2.distance);
121
+ return hits;
122
+ };
123
+ export const shapeCast = (type, queryProxy, proxies) => {
124
+ const scratch = makeCastHit();
125
+ let nearestDistance = Infinity;
126
+ let nearestHit = null;
127
+ shapeCastEach(type, queryProxy, proxies, scratch, (hit) => {
128
+ if (hit.distance < nearestDistance) {
129
+ nearestDistance = hit.distance;
130
+ nearestHit = nearestHit ?? makeCastHit();
131
+ nearestHit.actor = hit.actor;
132
+ nearestHit.point = hit.point;
133
+ nearestHit.normal = hit.normal;
134
+ nearestHit.distance = hit.distance;
135
+ }
136
+ });
137
+ return nearestHit;
138
+ };
139
+ export const shapeCastAll = (type, queryProxy, proxies) => {
140
+ const scratch = makeCastHit();
141
+ const hits = [];
142
+ shapeCastEach(type, queryProxy, proxies, scratch, (hit) => {
143
+ hits.push(cloneCastHit(hit));
144
+ });
145
+ hits.sort((arg1, arg2) => arg1.distance - arg2.distance);
146
+ return hits;
147
+ };
148
+ export const raycastEach = (queryProxy, proxies, hit, callback) => {
117
149
  for (const proxy of proxies) {
118
150
  const collider = proxy.actor.getComponent(Collider);
119
- const hit = raycastCheckers.ray[collider.shape.type](queryProxy.geometry, proxy.geometry);
120
- if (!hit) {
151
+ const result = raycastCheckers.ray[collider.shape.type](queryProxy.geometry, proxy.geometry);
152
+ if (!result) {
121
153
  continue;
122
154
  }
123
- const castHit = {
124
- actor: proxy.actor,
125
- point: hit.point,
126
- normal: hit.normal,
127
- distance: hit.distance,
128
- };
129
- if (queryProxy.hitFilter && !queryProxy.hitFilter(castHit)) {
155
+ hit.actor = proxy.actor;
156
+ hit.point = result.point;
157
+ hit.normal = result.normal;
158
+ hit.distance = result.distance;
159
+ if (queryProxy.hitFilter && !queryProxy.hitFilter(hit)) {
130
160
  continue;
131
161
  }
132
- hits.push(castHit);
162
+ callback(hit);
133
163
  }
134
- hits.sort((arg1, arg2) => arg1.distance - arg2.distance);
135
- return hits;
136
164
  };
137
- export const shapeCast = (type, queryProxy, proxies) => {
138
- let nearestDistance = Infinity;
139
- let nearestHit = null;
165
+ export const shapeCastEach = (type, queryProxy, proxies, hit, callback) => {
140
166
  for (const proxy of proxies) {
141
167
  const collider = proxy.actor.getComponent(Collider);
142
168
  const checker = shapeCastCheckers[type]?.[collider.shape.type];
143
169
  if (!checker) {
144
170
  continue;
145
171
  }
146
- const castHit = checker(queryProxy.geometry, proxy.geometry);
147
- if (!castHit) {
172
+ const result = checker(queryProxy.geometry, proxy.geometry);
173
+ if (!result) {
148
174
  continue;
149
175
  }
150
- const hit = {
151
- actor: proxy.actor,
152
- point: castHit.point,
153
- normal: castHit.normal,
154
- distance: castHit.distance,
155
- };
176
+ hit.actor = proxy.actor;
177
+ hit.point = result.point;
178
+ hit.normal = result.normal;
179
+ hit.distance = result.distance;
156
180
  if (queryProxy.hitFilter && !queryProxy.hitFilter(hit)) {
157
181
  continue;
158
182
  }
159
- if (castHit.distance < nearestDistance) {
160
- nearestHit = hit;
161
- nearestDistance = castHit.distance;
162
- }
183
+ callback(hit);
163
184
  }
164
- return nearestHit;
165
185
  };
166
- export const shapeCastAll = (type, queryProxy, proxies) => {
167
- const hits = [];
186
+ export const overlapEach = (type, queryProxy, proxies, hit, callback) => {
168
187
  for (const proxy of proxies) {
169
188
  const collider = proxy.actor.getComponent(Collider);
170
- const checker = shapeCastCheckers[type]?.[collider.shape.type];
189
+ const checker = intersectionCheckers[type]?.[collider.shape.type];
171
190
  if (!checker) {
172
191
  continue;
173
192
  }
174
- const castHit = checker(queryProxy.geometry, proxy.geometry);
175
- if (!castHit) {
193
+ const intersection = checker(queryProxy.geometry, proxy.geometry);
194
+ if (!intersection) {
176
195
  continue;
177
196
  }
178
- const hit = {
179
- actor: proxy.actor,
180
- point: castHit.point,
181
- normal: castHit.normal,
182
- distance: castHit.distance,
183
- };
197
+ intersection.normal.multiplyNumber(-1);
198
+ hit.actor = proxy.actor;
199
+ hit.normal = intersection.normal;
200
+ hit.penetration = intersection.penetration;
201
+ hit.contactPoints = intersection.contactPoints;
184
202
  if (queryProxy.hitFilter && !queryProxy.hitFilter(hit)) {
185
203
  continue;
186
204
  }
187
- hits.push(hit);
205
+ callback(hit);
188
206
  }
189
- hits.sort((arg1, arg2) => arg1.distance - arg2.distance);
190
- return hits;
191
207
  };
@@ -2,7 +2,6 @@ import type { Vector2, Point } from '../../../../../engine/math-lib';
2
2
  import type { Actor } from '../../../../../engine/actor';
3
3
  import type { CastHit, OverlapHit } from '../../types';
4
4
  import type { DynamicAABBTreeEntryId } from './dynamic-aabb-tree';
5
- import type { DispersionCalculator } from './dispersion-calculator';
6
5
  export type { Point };
7
6
  export interface AABB {
8
7
  min: Point;
@@ -83,7 +82,6 @@ export interface ActorProxy {
83
82
  aabb: AABB;
84
83
  geometry: Geometry;
85
84
  orientationData: OrientationData;
86
- edges: Record<Axis, [SortedItem, SortedItem]>;
87
85
  treeEntryId: DynamicAABBTreeEntryId;
88
86
  layer: string;
89
87
  }
@@ -96,19 +94,6 @@ export interface QueryProxy {
96
94
  hitFilter?: (hit: OverlapHit | CastHit) => boolean;
97
95
  }
98
96
  export type Proxy = ActorProxy | QueryProxy;
99
- export interface SortedItem {
100
- proxy: ActorProxy;
101
- value: number;
102
- }
103
- export interface AxisEntry {
104
- sortedList: SortedItem[];
105
- dispersionCalculator: DispersionCalculator;
106
- }
107
- export type Axis = 'x' | 'y';
108
- export interface Axes {
109
- x: AxisEntry;
110
- y: AxisEntry;
111
- }
112
97
  export type ProxyPair = [ActorProxy, ActorProxy];
113
98
  export interface Contact {
114
99
  actor1: Actor;