dacha 0.18.0-alpha.8 → 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 (110) 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 +108 -14
  6. package/build/contrib/components/rigid-body/index.js +191 -22
  7. package/build/contrib/systems/animator/index.d.ts +3 -2
  8. package/build/contrib/systems/animator/index.js +6 -3
  9. package/build/contrib/systems/behavior-system/system.d.ts +4 -2
  10. package/build/contrib/systems/behavior-system/system.js +11 -3
  11. package/build/contrib/systems/behavior-system/types.d.ts +7 -10
  12. package/build/contrib/systems/character-controller/index.d.ts +4 -2
  13. package/build/contrib/systems/character-controller/index.js +22 -17
  14. package/build/contrib/systems/character-controller/one-way-validator.d.ts +15 -0
  15. package/build/contrib/systems/character-controller/one-way-validator.js +73 -0
  16. package/build/contrib/systems/game-stats-meter/index.d.ts +4 -3
  17. package/build/contrib/systems/game-stats-meter/index.js +11 -10
  18. package/build/contrib/systems/index.d.ts +2 -0
  19. package/build/contrib/systems/index.js +1 -0
  20. package/build/contrib/systems/interpolator/api.d.ts +40 -0
  21. package/build/contrib/systems/interpolator/api.js +70 -0
  22. package/build/contrib/systems/interpolator/index.d.ts +3 -0
  23. package/build/contrib/systems/interpolator/index.js +2 -0
  24. package/build/contrib/systems/interpolator/system.d.ts +31 -0
  25. package/build/contrib/systems/interpolator/system.js +96 -0
  26. package/build/contrib/systems/interpolator/tests/helpers.d.ts +13 -0
  27. package/build/contrib/systems/interpolator/tests/helpers.js +45 -0
  28. package/build/contrib/systems/interpolator/utils.d.ts +5 -0
  29. package/build/contrib/systems/interpolator/utils.js +41 -0
  30. package/build/contrib/systems/physics-system/api.d.ts +45 -1
  31. package/build/contrib/systems/physics-system/api.js +49 -0
  32. package/build/contrib/systems/physics-system/consts.d.ts +13 -0
  33. package/build/contrib/systems/physics-system/consts.js +17 -0
  34. package/build/contrib/systems/physics-system/physics-system.d.ts +2 -2
  35. package/build/contrib/systems/physics-system/physics-system.js +24 -4
  36. package/build/contrib/systems/physics-system/subsystems/collision-detection/dynamic-aabb-tree/index.d.ts +1 -1
  37. package/build/contrib/systems/physics-system/subsystems/collision-detection/dynamic-aabb-tree/index.js +21 -15
  38. package/build/contrib/systems/physics-system/subsystems/collision-detection/index.d.ts +11 -11
  39. package/build/contrib/systems/physics-system/subsystems/collision-detection/index.js +133 -125
  40. package/build/contrib/systems/physics-system/subsystems/collision-detection/intersection-checkers/box-box/check-boxes-intersection.js +5 -3
  41. package/build/contrib/systems/physics-system/subsystems/collision-detection/intersection-checkers/box-box/utils.d.ts +1 -0
  42. package/build/contrib/systems/physics-system/subsystems/collision-detection/intersection-checkers/box-box/utils.js +2 -1
  43. package/build/contrib/systems/physics-system/subsystems/collision-detection/intersection-checkers/box-capsule/check-box-and-capsule-intersection.js +4 -1
  44. package/build/contrib/systems/physics-system/subsystems/collision-detection/intersection-checkers/box-segment/check-box-and-segment-intersection.js +4 -2
  45. package/build/contrib/systems/physics-system/subsystems/collision-detection/intersection-checkers/common/points.d.ts +2 -0
  46. package/build/contrib/systems/physics-system/subsystems/collision-detection/intersection-checkers/common/points.js +16 -0
  47. package/build/contrib/systems/physics-system/subsystems/collision-detection/intersection-checkers/index.js +2 -0
  48. package/build/contrib/systems/physics-system/subsystems/collision-detection/intersection-checkers/tests/helpers.js +3 -14
  49. package/build/contrib/systems/physics-system/subsystems/collision-detection/query-utils.d.ts +5 -0
  50. package/build/contrib/systems/physics-system/subsystems/collision-detection/query-utils.js +97 -81
  51. package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/tests/helpers.js +2 -7
  52. package/build/contrib/systems/physics-system/subsystems/collision-detection/tests/assertions.d.ts +1 -0
  53. package/build/contrib/systems/physics-system/subsystems/collision-detection/tests/assertions.js +7 -0
  54. package/build/contrib/systems/physics-system/subsystems/collision-detection/types.d.ts +0 -15
  55. package/build/contrib/systems/physics-system/subsystems/collision-detection/utils.d.ts +3 -0
  56. package/build/contrib/systems/physics-system/subsystems/collision-detection/utils.js +9 -0
  57. package/build/contrib/systems/physics-system/subsystems/constraint-solver/contact-state-manager.d.ts +63 -0
  58. package/build/contrib/systems/physics-system/subsystems/constraint-solver/contact-state-manager.js +170 -0
  59. package/build/contrib/systems/physics-system/subsystems/constraint-solver/contact-utils.d.ts +5 -0
  60. package/build/contrib/systems/physics-system/subsystems/constraint-solver/contact-utils.js +41 -0
  61. package/build/contrib/systems/physics-system/subsystems/constraint-solver/impulse-utils.d.ts +16 -0
  62. package/build/contrib/systems/physics-system/subsystems/constraint-solver/impulse-utils.js +77 -0
  63. package/build/contrib/systems/physics-system/subsystems/constraint-solver/index.d.ts +53 -4
  64. package/build/contrib/systems/physics-system/subsystems/constraint-solver/index.js +334 -116
  65. package/build/contrib/systems/physics-system/subsystems/constraint-solver/one-way-validator.d.ts +11 -0
  66. package/build/contrib/{utils → systems/physics-system/subsystems/constraint-solver}/one-way-validator.js +22 -25
  67. package/build/contrib/systems/physics-system/subsystems/constraint-solver/sleep-support-tracker.d.ts +13 -0
  68. package/build/contrib/systems/physics-system/subsystems/constraint-solver/sleep-support-tracker.js +42 -0
  69. package/build/contrib/systems/physics-system/subsystems/constraint-solver/tests/helpers.d.ts +4 -0
  70. package/build/contrib/systems/physics-system/subsystems/constraint-solver/tests/helpers.js +20 -0
  71. package/build/contrib/systems/physics-system/subsystems/physics/index.d.ts +15 -4
  72. package/build/contrib/systems/physics-system/subsystems/physics/index.js +131 -53
  73. package/build/contrib/systems/physics-system/subsystems/physics/mass-properties.d.ts +2 -0
  74. package/build/contrib/systems/physics-system/subsystems/physics/mass-properties.js +55 -0
  75. package/build/contrib/systems/physics-system/tests/helpers.d.ts +5 -2
  76. package/build/contrib/systems/physics-system/tests/helpers.js +11 -2
  77. package/build/contrib/systems/physics-system/types.d.ts +18 -2
  78. package/build/contrib/systems/renderer/actor-render-tree.js +15 -6
  79. package/build/contrib/systems/renderer/filters/index.d.ts +1 -1
  80. package/build/contrib/systems/renderer/filters/index.js +1 -1
  81. package/build/contrib/systems/renderer/material/index.d.ts +2 -1
  82. package/build/contrib/systems/renderer/material/index.js +1 -1
  83. package/build/contrib/systems/renderer/renderer.d.ts +2 -2
  84. package/build/contrib/systems/renderer/renderer.js +3 -4
  85. package/build/contrib/systems/renderer/types.d.ts +0 -3
  86. package/build/engine/data-lib/index.d.ts +1 -1
  87. package/build/engine/data-lib/index.js +1 -1
  88. package/build/engine/data-lib/pool.d.ts +9 -0
  89. package/build/engine/data-lib/pool.js +19 -0
  90. package/build/engine/engine.js +4 -1
  91. package/build/engine/game-loop.d.ts +3 -1
  92. package/build/engine/game-loop.js +11 -8
  93. package/build/engine/math-lib/math/ops.d.ts +16 -0
  94. package/build/engine/math-lib/math/ops.js +25 -0
  95. package/build/engine/scene/scene-manager.d.ts +4 -1
  96. package/build/engine/scene/scene-manager.js +5 -1
  97. package/build/engine/system/index.d.ts +1 -1
  98. package/build/engine/system/system.d.ts +10 -11
  99. package/build/engine/system/system.js +5 -2
  100. package/build/engine/time.d.ts +54 -0
  101. package/build/engine/time.js +77 -0
  102. package/build/index.d.ts +2 -1
  103. package/package.json +1 -1
  104. package/build/contrib/systems/physics-system/subsystems/collision-detection/dispersion-calculator/index.d.ts +0 -11
  105. package/build/contrib/systems/physics-system/subsystems/collision-detection/dispersion-calculator/index.js +0 -41
  106. package/build/contrib/utils/one-way-validator.d.ts +0 -12
  107. package/build/engine/data-lib/sort/index.d.ts +0 -1
  108. package/build/engine/data-lib/sort/index.js +0 -1
  109. package/build/engine/data-lib/sort/insertion-sort.d.ts +0 -1
  110. 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,70 +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
- return this.collisionMatrix[proxy1.layer]?.[proxy2.layer] ?? true;
212
+ return (this.collisionMatrix[proxy1.layer]?.[proxy2.layer] ??
213
+ this.collisionMatrix[proxy2.layer]?.[proxy1.layer] ??
214
+ true);
202
215
  }
203
- testState(proxy1, proxy2) {
204
- const actor1 = 'actor' in proxy1 ? proxy1.actor : undefined;
205
- const actor2 = 'actor' in proxy2 ? proxy2.actor : undefined;
206
- const collider1 = actor1?.getComponent(Collider);
207
- const collider2 = actor2?.getComponent(Collider);
208
- 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
+ }
209
225
  }
210
- collectQueryCandidates(queryProxy) {
226
+ collectQueryCandidates(queryProxy, out) {
211
227
  let candidateIndex = 0;
212
228
  this.queryTree.query(queryProxy.aabb, (proxy) => {
213
229
  if (queryProxy.excludedActors?.has(proxy.actor)) {
214
230
  return;
215
231
  }
216
- if (!this.testState(proxy, queryProxy)) {
232
+ if (isDisabled(proxy)) {
217
233
  return;
218
234
  }
219
235
  if (queryProxy.layer !== undefined &&
@@ -223,44 +239,40 @@ export class CollisionDetectionSubsystem {
223
239
  if (queryProxy.actorFilter && !queryProxy.actorFilter(proxy.actor)) {
224
240
  return;
225
241
  }
226
- this.queryCandidates[candidateIndex] = proxy;
242
+ out[candidateIndex] = proxy;
227
243
  candidateIndex += 1;
228
244
  });
229
- this.queryCandidates.length = candidateIndex;
245
+ out.length = candidateIndex;
230
246
  }
231
- sweepAndPrune() {
232
- const [mainAxis, secondAxis] = this.getAxes();
233
- const { sortedList } = this.axis[mainAxis];
234
- insertionSort(sortedList, (arg1, arg2) => arg1.value - arg2.value);
235
- const activeProxies = new Set();
236
- let proxyPairIndex = 0;
237
- for (const item of sortedList) {
238
- const { proxy } = item;
239
- if (!activeProxies.has(proxy)) {
240
- activeProxies.forEach((activeProxy) => {
241
- if (!this.testState(proxy, activeProxy)) {
242
- return;
243
- }
244
- if (!this.testAABB(proxy, activeProxy, secondAxis)) {
245
- return;
246
- }
247
- if (this.areStaticBodies(proxy, activeProxy)) {
248
- return;
249
- }
250
- if (!this.testCollisionLayers(proxy, activeProxy)) {
251
- return;
252
- }
253
- this.proxyPairs[proxyPairIndex] = [proxy, activeProxy];
254
- proxyPairIndex += 1;
255
- });
256
- activeProxies.add(proxy);
247
+ collectPairs() {
248
+ let pairIndex = 0;
249
+ this.proxiesByActorId.forEach((proxy) => {
250
+ if (isStatic(proxy)) {
251
+ return;
257
252
  }
258
- else {
259
- activeProxies.delete(proxy);
253
+ if (isDisabled(proxy)) {
254
+ return;
260
255
  }
261
- }
262
- if (this.proxyPairs.length > proxyPairIndex) {
263
- 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;
264
276
  }
265
277
  }
266
278
  checkOnIntersection(proxyPair) {
@@ -291,13 +303,9 @@ export class CollisionDetectionSubsystem {
291
303
  if (this.actorIdsToDelete.size === 0) {
292
304
  return;
293
305
  }
294
- this.clearSortedList('x');
295
- this.clearSortedList('y');
296
306
  this.actorIdsToDelete.forEach((id) => {
297
307
  const proxy = this.proxiesByActorId.get(id);
298
308
  this.queryTree.remove(proxy.treeEntryId);
299
- this.axis.x.dispersionCalculator.removeFromSample(proxy.aabb);
300
- this.axis.y.dispersionCalculator.removeFromSample(proxy.aabb);
301
309
  this.proxiesByActorId.delete(id);
302
310
  });
303
311
  this.actorIdsToDelete.clear();
@@ -310,7 +318,7 @@ export class CollisionDetectionSubsystem {
310
318
  }
311
319
  this.updateProxy(actor);
312
320
  });
313
- this.sweepAndPrune();
321
+ this.collectPairs();
314
322
  let contactIndex = 0;
315
323
  this.proxyPairs.forEach((proxyPair) => {
316
324
  const intersection = this.checkOnIntersection(proxyPair);
@@ -1,5 +1,6 @@
1
1
  import { orientNormal } from '../common/normals';
2
- import { findMinBoxesOverlap, buildContactPoints } from './utils';
2
+ import { sortPoints } from '../common/points';
3
+ import { findMinBoxesOverlap, buildContactPoints, OVERLAP_TIE_EPSILON, } from './utils';
3
4
  /**
4
5
  * Checks box colliders for intersection.
5
6
  *
@@ -17,7 +18,7 @@ export const checkBoxesIntersection = (geometry1, geometry2) => {
17
18
  if (overlap2 === false) {
18
19
  return false;
19
20
  }
20
- const isArg1Reference = overlap1.overlap <= overlap2.overlap;
21
+ const isArg1Reference = overlap1.overlap <= overlap2.overlap + OVERLAP_TIE_EPSILON;
21
22
  const referenceGeometry = isArg1Reference ? geometry1 : geometry2;
22
23
  const incidentGeometry = isArg1Reference ? geometry2 : geometry1;
23
24
  const referenceOverlap = isArg1Reference ? overlap1 : overlap2;
@@ -26,9 +27,10 @@ export const checkBoxesIntersection = (geometry1, geometry2) => {
26
27
  if (!isArg1Reference) {
27
28
  referenceNormal.multiplyNumber(-1);
28
29
  }
30
+ const contactPoints = buildContactPoints(referenceGeometry, referenceNormal, incidentGeometry);
29
31
  return {
30
32
  normal,
31
33
  penetration: referenceOverlap.overlap,
32
- contactPoints: buildContactPoints(referenceGeometry, referenceNormal, incidentGeometry),
34
+ contactPoints: sortPoints(contactPoints, normal),
33
35
  };
34
36
  };
@@ -1,6 +1,7 @@
1
1
  import { Vector2 } from '../../../../../../../engine/math-lib';
2
2
  import type { BoxGeometry, Point } from '../../types';
3
3
  export declare const CONTACT_EPSILON = 0.0001;
4
+ export declare const OVERLAP_TIE_EPSILON = 0.0001;
4
5
  export declare const MAX_CONTACT_POINTS = 2;
5
6
  export interface AxisOverlap {
6
7
  axis: Vector2;
@@ -1,6 +1,7 @@
1
1
  import { Vector2, VectorOps } from '../../../../../../../engine/math-lib';
2
2
  import { getProjectionOverlap, projectPolygon } from '../common/projections';
3
3
  export const CONTACT_EPSILON = 1e-4;
4
+ export const OVERLAP_TIE_EPSILON = 1e-4;
4
5
  export const MAX_CONTACT_POINTS = 2;
5
6
  /**
6
7
  * Runs the SAT pass for one box's face normals against another box.
@@ -22,7 +23,7 @@ export const findMinBoxesOverlap = (geometry1, geometry2) => {
22
23
  if (overlap === false) {
23
24
  return false;
24
25
  }
25
- if (overlap < minOverlap) {
26
+ if (overlap < minOverlap - OVERLAP_TIE_EPSILON) {
26
27
  minOverlap = overlap;
27
28
  bestAxis = axis;
28
29
  }
@@ -1,4 +1,5 @@
1
1
  import { orientNormal } from '../common/normals';
2
+ import { sortPoints } from '../common/points';
2
3
  import { buildBoxCapsuleIntersection } from './utils';
3
4
  /**
4
5
  * Checks a box against a capsule.
@@ -23,8 +24,10 @@ export const checkBoxAndCapsuleIntersection = (box, capsule) => {
23
24
  if (!intersection) {
24
25
  return false;
25
26
  }
27
+ const normal = orientNormal(intersection.normal, box.center, capsule.center);
26
28
  return {
27
29
  ...intersection,
28
- normal: orientNormal(intersection.normal, box.center, capsule.center),
30
+ normal,
31
+ contactPoints: sortPoints(intersection.contactPoints, normal),
29
32
  };
30
33
  };
@@ -1,5 +1,6 @@
1
1
  import { buildBoxSegmentContactPoints, findMinBoxSegmentOverlap, } from './utils';
2
2
  import { orientNormal } from '../common/normals';
3
+ import { sortPoints } from '../common/points';
3
4
  /**
4
5
  * Checks a box against a segment.
5
6
  *
@@ -17,9 +18,10 @@ export const checkBoxAndSegmentIntersection = (box, segment) => {
17
18
  if (contactPoints.length === 0) {
18
19
  return false;
19
20
  }
21
+ const normal = orientNormal(overlap.axis.clone(), box.center, segment.center);
20
22
  return {
21
- normal: orientNormal(overlap.axis.clone(), box.center, segment.center),
23
+ normal,
22
24
  penetration: overlap.overlap,
23
- contactPoints,
25
+ contactPoints: sortPoints(contactPoints, normal),
24
26
  };
25
27
  };
@@ -1,4 +1,6 @@
1
+ import type { Vector2 } from '../../../../../../../engine/math-lib';
1
2
  import type { Point } from '../../types';
2
3
  export declare const arePointsEqual: (point1: Point, point2: Point) => boolean;
3
4
  export declare const lerpPoint: (point1: Point, point2: Point, value: number) => Point;
4
5
  export declare const getDistanceSquared: (point1: Point, point2: Point) => number;
6
+ export declare const sortPoints: (points: Point[], normal: Vector2) => Point[];
@@ -5,3 +5,19 @@ export const lerpPoint = (point1, point2, value) => ({
5
5
  y: point1.y + (point2.y - point1.y) * value,
6
6
  });
7
7
  export const getDistanceSquared = (point1, point2) => (point1.x - point2.x) ** 2 + (point1.y - point2.y) ** 2;
8
+ export const sortPoints = (points, normal) => {
9
+ if (points.length !== 2) {
10
+ return points;
11
+ }
12
+ const tangentX = -normal.y;
13
+ const tangentY = normal.x;
14
+ const projection1 = points[0].x * tangentX + points[0].y * tangentY;
15
+ const projection2 = points[1].x * tangentX + points[1].y * tangentY;
16
+ if (projection1 <= projection2) {
17
+ return points;
18
+ }
19
+ const point = points[0];
20
+ points[0] = points[1];
21
+ points[1] = point;
22
+ return points;
23
+ };
@@ -1,3 +1,4 @@
1
+ import { sortPoints } from './common/points';
1
2
  import { checkBoxAndCapsuleIntersection } from './box-capsule/check-box-and-capsule-intersection';
2
3
  import { checkBoxAndCircleIntersection } from './box-circle/check-box-and-circle-intersection';
3
4
  import { checkBoxAndSegmentIntersection } from './box-segment/check-box-and-segment-intersection';
@@ -18,6 +19,7 @@ const swapArgs = (checker) => {
18
19
  return false;
19
20
  }
20
21
  intersection.normal.multiplyNumber(-1);
22
+ sortPoints(intersection.contactPoints, intersection.normal);
21
23
  return intersection;
22
24
  };
23
25
  };
@@ -5,6 +5,7 @@ import { buildCircleGeometry } from '../../geometry-builders/build-circle-geomet
5
5
  import { buildPointGeometry } from '../../geometry-builders/build-point-geometry';
6
6
  import { buildRayGeometry } from '../../geometry-builders/build-ray-geometry';
7
7
  import { buildSegmentGeometry } from '../../geometry-builders/build-segment-geometry';
8
+ import { expectHit } from '../../tests/assertions';
8
9
  export const createBoxGeometry = (centerX, centerY, sizeX, sizeY) => {
9
10
  return createRotatedBoxGeometry(centerX, centerY, sizeX, sizeY, 0);
10
11
  };
@@ -71,20 +72,8 @@ export const expectToBeClose = (point, x, y, digits = 6) => {
71
72
  expect(point.x).toBeCloseTo(x, digits);
72
73
  expect(point.y).toBeCloseTo(y, digits);
73
74
  };
74
- export const expectIntersection = (intersection) => {
75
- expect(intersection).not.toBe(false);
76
- if (intersection === false) {
77
- throw new Error('Expected intersection, received false');
78
- }
79
- return intersection;
80
- };
81
- export const expectCastHit = (hit) => {
82
- expect(hit).not.toBe(false);
83
- if (hit === false) {
84
- throw new Error('Expected cast hit, received false');
85
- }
86
- return hit;
87
- };
75
+ export const expectIntersection = (intersection) => expectHit(intersection, 'intersection');
76
+ export const expectCastHit = (hit) => expectHit(hit, 'cast hit');
88
77
  export const sortPoints = (points) => [...points].sort((point1, point2) => {
89
78
  if (point1.x !== point2.x) {
90
79
  return point1.x - point2.x;
@@ -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 {};