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,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,6 +2,7 @@ import { Vector2 } from '../../../../../../../engine/math-lib';
2
2
  import { buildBoxCastGeometry } from '../../geometry-builders/build-box-cast-geometry';
3
3
  import { buildCapsuleCastGeometry } from '../../geometry-builders/build-capsule-cast-geometry';
4
4
  import { buildCircleCastGeometry } from '../../geometry-builders/build-circle-cast-geometry';
5
+ import { expectHit } from '../../tests/assertions';
5
6
  export const createCircleCastGeometry = (centerX, centerY, radius, directionX, directionY, maxDistance) => buildCircleCastGeometry({
6
7
  shape: {
7
8
  type: 'circle',
@@ -30,10 +31,4 @@ export const createBoxCastGeometry = (centerX, centerY, sizeX, sizeY, directionX
30
31
  direction: new Vector2(directionX, directionY),
31
32
  maxDistance,
32
33
  });
33
- export const expectShapeCastHit = (hit) => {
34
- expect(hit).not.toBe(false);
35
- if (hit === false) {
36
- throw new Error('Expected shape-cast hit, received false');
37
- }
38
- return hit;
39
- };
34
+ export const expectShapeCastHit = (hit) => expectHit(hit, 'shape-cast hit');
@@ -0,0 +1 @@
1
+ export declare const expectHit: <T>(hit: false | T, label: string) => T;
@@ -0,0 +1,7 @@
1
+ export const expectHit = (hit, label) => {
2
+ expect(hit).not.toBe(false);
3
+ if (hit === false) {
4
+ throw new Error(`Expected ${label}, received false`);
5
+ }
6
+ return hit;
7
+ };
@@ -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;
@@ -1,5 +1,8 @@
1
+ import type { ActorProxy } from './types';
1
2
  export declare const isZero: (value: number) => boolean;
2
3
  export declare const isDefinitelyPositive: (value: number) => boolean;
3
4
  export declare const isDefinitelyNegative: (value: number) => boolean;
4
5
  export declare const isGreaterThan: (left: number, right: number) => boolean;
5
6
  export declare const isLessThan: (left: number, right: number) => boolean;
7
+ export declare const isStatic: (proxy: ActorProxy) => boolean;
8
+ export declare const isDisabled: (proxy: ActorProxy) => boolean;
@@ -1,6 +1,15 @@
1
+ import { RigidBody, Collider } from '../../../../components';
1
2
  import { INTERSECTION_EPSILON } from './constants';
2
3
  export const isZero = (value) => Math.abs(value) <= INTERSECTION_EPSILON;
3
4
  export const isDefinitelyPositive = (value) => value > INTERSECTION_EPSILON;
4
5
  export const isDefinitelyNegative = (value) => value < -INTERSECTION_EPSILON;
5
6
  export const isGreaterThan = (left, right) => isDefinitelyPositive(left - right);
6
7
  export const isLessThan = (left, right) => isDefinitelyNegative(left - right);
8
+ export const isStatic = (proxy) => {
9
+ const rigidBody = proxy.actor.getComponent(RigidBody);
10
+ return rigidBody?.type === 'static';
11
+ };
12
+ export const isDisabled = (proxy) => {
13
+ const collider = proxy.actor.getComponent(Collider);
14
+ return collider?.disabled === true;
15
+ };
@@ -0,0 +1,63 @@
1
+ import type { Actor } from '../../../../../engine/actor';
2
+ import { RigidBody } from '../../../../components/rigid-body';
3
+ import type { Contact } from '../collision-detection/types';
4
+ export interface ContactPoint {
5
+ positionX: number;
6
+ positionY: number;
7
+ normalImpulse: number;
8
+ tangentImpulse: number;
9
+ biasImpulse: number;
10
+ anchorAX: number;
11
+ anchorAY: number;
12
+ anchorBX: number;
13
+ anchorBY: number;
14
+ normalMass: number;
15
+ tangentMass: number;
16
+ velocityBias: number;
17
+ }
18
+ export interface ContactState {
19
+ contact: Contact;
20
+ actor1: Actor;
21
+ actor2: Actor;
22
+ bodyA: RigidBody;
23
+ bodyB: RigidBody;
24
+ invMassA: number;
25
+ invInertiaA: number;
26
+ invMassB: number;
27
+ invInertiaB: number;
28
+ normalX: number;
29
+ normalY: number;
30
+ tangentX: number;
31
+ tangentY: number;
32
+ friction: number;
33
+ restitution: number;
34
+ k01: number;
35
+ determinant: number;
36
+ blockSolvable: boolean;
37
+ active: boolean;
38
+ warmStartAllowed: boolean;
39
+ skipBias: boolean;
40
+ point0: ContactPoint;
41
+ point1: ContactPoint;
42
+ pointCount: number;
43
+ version: number;
44
+ }
45
+ export declare class ContactStateManager {
46
+ private stateMap;
47
+ private states;
48
+ private version;
49
+ constructor();
50
+ updateVersion(): void;
51
+ acquire(contact: Contact): ContactState;
52
+ pruneStaleStates(): void;
53
+ forEach(callback: (state: ContactState) => void): void;
54
+ clearWarmStartImpulses(state: ContactState): void;
55
+ private clearFrameImpulses;
56
+ private get;
57
+ private set;
58
+ private delete;
59
+ private createState;
60
+ private canPreserveImpulses;
61
+ private isPointClose;
62
+ private updateContactIdentity;
63
+ }
@@ -0,0 +1,170 @@
1
+ import { RigidBody } from '../../../../components/rigid-body';
2
+ const CONTACT_NORMAL_MIN_DOT = 0.98;
3
+ const CONTACT_POINT_MAX_DISTANCE = 0.2;
4
+ const CONTACT_POINT_MAX_DISTANCE_SQUARED = CONTACT_POINT_MAX_DISTANCE * CONTACT_POINT_MAX_DISTANCE;
5
+ const createContactPoint = () => ({
6
+ positionX: 0,
7
+ positionY: 0,
8
+ normalImpulse: 0,
9
+ tangentImpulse: 0,
10
+ biasImpulse: 0,
11
+ anchorAX: 0,
12
+ anchorAY: 0,
13
+ anchorBX: 0,
14
+ anchorBY: 0,
15
+ normalMass: 0,
16
+ tangentMass: 0,
17
+ velocityBias: 0,
18
+ });
19
+ export class ContactStateManager {
20
+ stateMap;
21
+ states;
22
+ version;
23
+ constructor() {
24
+ this.stateMap = new Map();
25
+ this.states = [];
26
+ this.version = 0;
27
+ }
28
+ updateVersion() {
29
+ this.version += 1;
30
+ }
31
+ acquire(contact) {
32
+ let state = this.get(contact.actor1, contact.actor2);
33
+ if (!state) {
34
+ state = this.createState(contact);
35
+ this.set(contact.actor1, contact.actor2, state);
36
+ this.set(contact.actor2, contact.actor1, state);
37
+ this.states.push(state);
38
+ }
39
+ else {
40
+ state.contact = contact;
41
+ state.actor1 = contact.actor1;
42
+ state.actor2 = contact.actor2;
43
+ }
44
+ if (!this.canPreserveImpulses(state, contact)) {
45
+ this.clearWarmStartImpulses(state);
46
+ }
47
+ this.clearFrameImpulses(state);
48
+ this.updateContactIdentity(state, contact);
49
+ state.version = this.version;
50
+ return state;
51
+ }
52
+ pruneStaleStates() {
53
+ let stateIndex = 0;
54
+ for (const state of this.states) {
55
+ if (state.version === this.version ||
56
+ (state.bodyA.sleeping && state.bodyB.sleeping)) {
57
+ this.states[stateIndex] = state;
58
+ stateIndex += 1;
59
+ continue;
60
+ }
61
+ this.delete(state.actor1, state.actor2);
62
+ this.delete(state.actor2, state.actor1);
63
+ }
64
+ this.states.length = stateIndex;
65
+ }
66
+ forEach(callback) {
67
+ this.states.forEach(callback);
68
+ }
69
+ clearWarmStartImpulses(state) {
70
+ state.point0.normalImpulse = 0;
71
+ state.point0.tangentImpulse = 0;
72
+ state.point1.normalImpulse = 0;
73
+ state.point1.tangentImpulse = 0;
74
+ }
75
+ clearFrameImpulses(state) {
76
+ state.point0.biasImpulse = 0;
77
+ state.point1.biasImpulse = 0;
78
+ }
79
+ get(actor1, actor2) {
80
+ return this.stateMap.get(actor1)?.get(actor2);
81
+ }
82
+ set(actor1, actor2, state) {
83
+ let actorStates = this.stateMap.get(actor1);
84
+ if (!actorStates) {
85
+ actorStates = new Map();
86
+ this.stateMap.set(actor1, actorStates);
87
+ }
88
+ actorStates.set(actor2, state);
89
+ }
90
+ delete(actor1, actor2) {
91
+ const actorStates = this.stateMap.get(actor1);
92
+ if (!actorStates) {
93
+ return;
94
+ }
95
+ actorStates.delete(actor2);
96
+ if (actorStates.size === 0) {
97
+ this.stateMap.delete(actor1);
98
+ }
99
+ }
100
+ createState(contact) {
101
+ const rigidBody1 = contact.actor1.getComponent(RigidBody);
102
+ const rigidBody2 = contact.actor2.getComponent(RigidBody);
103
+ return {
104
+ contact,
105
+ actor1: contact.actor1,
106
+ actor2: contact.actor2,
107
+ bodyA: rigidBody1,
108
+ bodyB: rigidBody2,
109
+ invMassA: 0,
110
+ invInertiaA: 0,
111
+ invMassB: 0,
112
+ invInertiaB: 0,
113
+ normalX: contact.normal.x,
114
+ normalY: contact.normal.y,
115
+ tangentX: -contact.normal.y,
116
+ tangentY: contact.normal.x,
117
+ friction: 0,
118
+ restitution: 0,
119
+ k01: 0,
120
+ determinant: 0,
121
+ blockSolvable: false,
122
+ active: false,
123
+ warmStartAllowed: false,
124
+ skipBias: false,
125
+ point0: createContactPoint(),
126
+ point1: createContactPoint(),
127
+ pointCount: 0,
128
+ version: this.version,
129
+ };
130
+ }
131
+ canPreserveImpulses(state, contact) {
132
+ const contactPoints = contact.contactPoints;
133
+ if (state.pointCount !== contactPoints.length) {
134
+ return false;
135
+ }
136
+ if (state.normalX * contact.normal.x + state.normalY * contact.normal.y <
137
+ CONTACT_NORMAL_MIN_DOT) {
138
+ return false;
139
+ }
140
+ if (contactPoints[0] &&
141
+ !this.isPointClose(state.point0.positionX, state.point0.positionY, contactPoints[0].x, contactPoints[0].y)) {
142
+ return false;
143
+ }
144
+ if (contactPoints[1] &&
145
+ !this.isPointClose(state.point1.positionX, state.point1.positionY, contactPoints[1].x, contactPoints[1].y)) {
146
+ return false;
147
+ }
148
+ return true;
149
+ }
150
+ isPointClose(point1X, point1Y, point2X, point2Y) {
151
+ const distanceX = point1X - point2X;
152
+ const distanceY = point1Y - point2Y;
153
+ return (distanceX * distanceX + distanceY * distanceY <=
154
+ CONTACT_POINT_MAX_DISTANCE_SQUARED);
155
+ }
156
+ updateContactIdentity(state, contact) {
157
+ const contactPoints = contact.contactPoints;
158
+ state.normalX = contact.normal.x;
159
+ state.normalY = contact.normal.y;
160
+ state.pointCount = contactPoints.length;
161
+ if (contactPoints[0]) {
162
+ state.point0.positionX = contactPoints[0].x;
163
+ state.point0.positionY = contactPoints[0].y;
164
+ }
165
+ if (contactPoints[1]) {
166
+ state.point1.positionX = contactPoints[1].x;
167
+ state.point1.positionY = contactPoints[1].y;
168
+ }
169
+ }
170
+ }
@@ -0,0 +1,5 @@
1
+ import type { Actor } from '../../../../../engine/actor';
2
+ import { type Vector2 } from '../../../../../engine/math-lib';
3
+ import type { Contact } from '../collision-detection/types';
4
+ export declare const shouldWakeSleepingContact: (contact: Contact, contactSpeedThreshold: number) => boolean;
5
+ export declare const isSleepSupportContact: (contact: Contact, actor: Actor, gravity: Vector2) => boolean;
@@ -0,0 +1,41 @@
1
+ import { VectorOps } from '../../../../../engine/math-lib';
2
+ import { RigidBody } from '../../../../components/rigid-body';
3
+ import { Transform } from '../../../../components/transform';
4
+ import { SUPPORT_MIN_GRAVITY_DOT } from '../../consts';
5
+ const getMinNormalVelocity = (contact) => {
6
+ const rigidBody1 = contact.actor1.getComponent(RigidBody);
7
+ const rigidBody2 = contact.actor2.getComponent(RigidBody);
8
+ const transform1 = contact.actor1.getComponent(Transform);
9
+ const transform2 = contact.actor2.getComponent(Transform);
10
+ const { normal } = contact;
11
+ let minVelocity = null;
12
+ for (const point of contact.contactPoints) {
13
+ const anchor1X = point.x - transform1.world.position.x;
14
+ const anchor1Y = point.y - transform1.world.position.y;
15
+ const anchor2X = point.x - transform2.world.position.x;
16
+ const anchor2Y = point.y - transform2.world.position.y;
17
+ const velocity1X = rigidBody1.linearVelocity.x - rigidBody1.angularVelocity * anchor1Y;
18
+ const velocity1Y = rigidBody1.linearVelocity.y + rigidBody1.angularVelocity * anchor1X;
19
+ const velocity2X = rigidBody2.linearVelocity.x - rigidBody2.angularVelocity * anchor2Y;
20
+ const velocity2Y = rigidBody2.linearVelocity.y + rigidBody2.angularVelocity * anchor2X;
21
+ const velocity = (velocity2X - velocity1X) * normal.x +
22
+ (velocity2Y - velocity1Y) * normal.y;
23
+ if (minVelocity === null || velocity < minVelocity) {
24
+ minVelocity = velocity;
25
+ }
26
+ }
27
+ return minVelocity;
28
+ };
29
+ export const shouldWakeSleepingContact = (contact, contactSpeedThreshold) => {
30
+ const minNormalVelocity = getMinNormalVelocity(contact);
31
+ return (minNormalVelocity !== null && -minNormalVelocity > contactSpeedThreshold);
32
+ };
33
+ export const isSleepSupportContact = (contact, actor, gravity) => {
34
+ const gravityMagnitude = gravity.magnitude;
35
+ if (gravityMagnitude === 0) {
36
+ return false;
37
+ }
38
+ const normalGravityDot = VectorOps.dotProduct(contact.normal, gravity);
39
+ const supportDot = contact.actor1 === actor ? normalGravityDot : -normalGravityDot;
40
+ return supportDot / gravityMagnitude > SUPPORT_MIN_GRAVITY_DOT;
41
+ };
@@ -0,0 +1,16 @@
1
+ import type { RigidBody } from '../../../../components/rigid-body';
2
+ import type { ContactPoint, ContactState } from './contact-state-manager';
3
+ export declare const getContactRestitution: (rigidBody1: RigidBody, rigidBody2: RigidBody) => number;
4
+ export declare const getContactFriction: (rigidBody1: RigidBody, rigidBody2: RigidBody) => number;
5
+ /**
6
+ * Returns the effective mass at a contact point along a unit direction.
7
+ * It represents how strongly the two bodies resist an impulse applied at
8
+ * that point in that direction.
9
+ */
10
+ export declare const getEffectiveMass: (invMassA: number, invInertiaA: number, anchorAX: number, anchorAY: number, invMassB: number, invInertiaB: number, anchorBX: number, anchorBY: number, directionX: number, directionY: number) => number;
11
+ export declare const getNormalVelocity: (state: ContactState, point: ContactPoint) => number;
12
+ export declare const getTangentVelocity: (state: ContactState, point: ContactPoint) => number;
13
+ export declare const getPrevNormalVelocity: (state: ContactState, point: ContactPoint) => number;
14
+ export declare const getBiasNormalVelocity: (state: ContactState, point: ContactPoint) => number;
15
+ export declare const applyImpulse: (state: ContactState, point: ContactPoint, impulseX: number, impulseY: number) => void;
16
+ export declare const applyBiasImpulse: (state: ContactState, point: ContactPoint, impulseX: number, impulseY: number) => void;