@perplexdotgg/bounce 1.0.0 → 1.0.2

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 (138) hide show
  1. package/build/bounce.d.ts +39501 -0
  2. package/build/bounce.js +17166 -0
  3. package/package.json +1 -1
  4. package/src/builders/ConvexHullBuilder.ts +0 -437
  5. package/src/builders/ConvexHullBuilder2d.ts +0 -344
  6. package/src/builders/ConvexHullBuilder3d.ts +0 -1689
  7. package/src/builders/HeightMapBuilder.ts +0 -414
  8. package/src/builders/TriangleMeshBuilder.ts +0 -92
  9. package/src/collision/CastShapesModule.ts +0 -184
  10. package/src/collision/CollideShapesModule.ts +0 -152
  11. package/src/collision/HeightMapCaster.ts +0 -38
  12. package/src/collision/HeightMapCollider.ts +0 -33
  13. package/src/collision/TriangleCaster.ts +0 -249
  14. package/src/collision/TriangleCollider.ts +0 -308
  15. package/src/collision/TriangleCollider2.ts +0 -379
  16. package/src/collision/activeEdge.ts +0 -146
  17. package/src/collision/cast/cast.ts +0 -139
  18. package/src/collision/cast/castCompoundVsCompound.ts +0 -59
  19. package/src/collision/cast/castCompoundVsConvex.ts +0 -116
  20. package/src/collision/cast/castConvexVsCompound.ts +0 -123
  21. package/src/collision/cast/castConvexVsConvex.ts +0 -213
  22. package/src/collision/cast/castConvexVsHeightMap.ts +0 -73
  23. package/src/collision/cast/castConvexVsTriangleMesh.ts +0 -56
  24. package/src/collision/cast/castRayVsCompound.ts +0 -44
  25. package/src/collision/cast/castRayVsConvex.ts +0 -45
  26. package/src/collision/cast/castRayVsHeightMap.ts +0 -58
  27. package/src/collision/cast/castRayVsTriangleMesh.ts +0 -58
  28. package/src/collision/closestPoints/closestPoints.ts +0 -23
  29. package/src/collision/closestPoints/computeBarycentricCoordinates2d.ts +0 -32
  30. package/src/collision/closestPoints/computeBarycentricCoordinates3d.ts +0 -81
  31. package/src/collision/closestPoints/computeClosestPointOnLine.ts +0 -30
  32. package/src/collision/closestPoints/computeClosestPointOnTetrahedron.ts +0 -96
  33. package/src/collision/closestPoints/computeClosestPointOnTriangle.ts +0 -195
  34. package/src/collision/closestPoints/isOriginOutsideOfPlane.ts +0 -25
  35. package/src/collision/closestPoints/isOriginOutsideOfTrianglePlanes.ts +0 -72
  36. package/src/collision/collide/collide.ts +0 -146
  37. package/src/collision/collide/collideCompoundVsCompound.ts +0 -60
  38. package/src/collision/collide/collideCompoundVsConvex.ts +0 -59
  39. package/src/collision/collide/collideCompoundVsHeightMap.ts +0 -73
  40. package/src/collision/collide/collideCompoundVsTriangleMesh.ts +0 -56
  41. package/src/collision/collide/collideConvexVsCompound.ts +0 -57
  42. package/src/collision/collide/collideConvexVsConvex.ts +0 -225
  43. package/src/collision/collide/collideConvexVsConvexImp.ts +0 -236
  44. package/src/collision/collide/collideConvexVsHeightMap.ts +0 -53
  45. package/src/collision/collide/collideConvexVsTriangleMesh.ts +0 -58
  46. package/src/collision/collide/collideHeightMapVsCompound.ts +0 -69
  47. package/src/collision/collide/collideHeightMapVsConvex.ts +0 -53
  48. package/src/collision/collide/collideSphereVsSphere.ts +0 -81
  49. package/src/collision/collide/collideTriangleMeshVsCompound.ts +0 -58
  50. package/src/collision/collide/collideTriangleMeshVsConvex.ts +0 -58
  51. package/src/collision/epa/EpaConvexHullBuilder.ts +0 -397
  52. package/src/collision/epa/StaticArray.ts +0 -154
  53. package/src/collision/epa/TriangleFactory.ts +0 -32
  54. package/src/collision/epa/arrays.ts +0 -99
  55. package/src/collision/epa/binaryHeap.ts +0 -82
  56. package/src/collision/epa/structs.ts +0 -227
  57. package/src/collision/gjk/GjkModule.ts +0 -864
  58. package/src/collision/gjk/PenetrationDepthModule.ts +0 -493
  59. package/src/collision/gjk/SupportPoints.ts +0 -50
  60. package/src/collision/imp/MinkowskiDifference.ts +0 -36
  61. package/src/collision/imp/computeExploredDistanceLowerUpperBound.ts +0 -40
  62. package/src/collision/imp/finalizeImpResult.ts +0 -69
  63. package/src/collision/imp/findContactImp.ts +0 -196
  64. package/src/collision/imp/imp.ts +0 -28
  65. package/src/collision/imp/incrementalMinimumDistanceExploreDirection.ts +0 -207
  66. package/src/collision/mpr/findPortal.ts +0 -152
  67. package/src/collision/mpr/mpr.ts +0 -29
  68. package/src/collision/mpr/updatePortal.ts +0 -52
  69. package/src/constraints/BaseConstraint.ts +0 -50
  70. package/src/constraints/ConstraintOptions.ts +0 -22
  71. package/src/constraints/ConstraintSolver.ts +0 -119
  72. package/src/constraints/DistanceConstraint.ts +0 -229
  73. package/src/constraints/FixedConstraint.ts +0 -203
  74. package/src/constraints/HingeConstraint.ts +0 -460
  75. package/src/constraints/PointConstraint.ts +0 -108
  76. package/src/constraints/components/AngleComponent.ts +0 -226
  77. package/src/constraints/components/AxisComponent.ts +0 -263
  78. package/src/constraints/components/HingeComponent.ts +0 -215
  79. package/src/constraints/components/Motor.ts +0 -36
  80. package/src/constraints/components/PointConstraintComponent.ts +0 -179
  81. package/src/constraints/components/RotationEulerComponent.ts +0 -139
  82. package/src/constraints/components/Spring.ts +0 -30
  83. package/src/constraints/components/SpringComponent.ts +0 -71
  84. package/src/constraints/types.ts +0 -6
  85. package/src/helpers.ts +0 -147
  86. package/src/index.ts +0 -50
  87. package/src/math/BasicTransform.ts +0 -19
  88. package/src/math/NumberValue.ts +0 -13
  89. package/src/math/isometry.ts +0 -64
  90. package/src/math/mat3.ts +0 -529
  91. package/src/math/mat4.ts +0 -588
  92. package/src/math/quat.ts +0 -193
  93. package/src/math/scalar.ts +0 -81
  94. package/src/math/tensor.ts +0 -17
  95. package/src/math/vec3.ts +0 -589
  96. package/src/math/vec4.ts +0 -10
  97. package/src/physics/Body.ts +0 -581
  98. package/src/physics/CollisionFilter.ts +0 -52
  99. package/src/physics/SleepModule.ts +0 -163
  100. package/src/physics/broadphase/BodyPairsModule.ts +0 -363
  101. package/src/physics/broadphase/BvhModule.ts +0 -237
  102. package/src/physics/broadphase/BvhTree.ts +0 -803
  103. package/src/physics/broadphase/ConstraintPairsModule.ts +0 -385
  104. package/src/physics/broadphase/TriangleMeshBvhTree.ts +0 -379
  105. package/src/physics/manifold/ContactManifold.ts +0 -227
  106. package/src/physics/manifold/ContactManifoldModule.ts +0 -623
  107. package/src/physics/manifold/Face.ts +0 -119
  108. package/src/physics/manifold/ManifoldCache.ts +0 -116
  109. package/src/physics/manifold/clipping/clipPolyVsEdge.ts +0 -131
  110. package/src/physics/manifold/clipping/clipPolyVsPlane.ts +0 -73
  111. package/src/physics/manifold/clipping/clipPolyVsPoly.ts +0 -72
  112. package/src/physics/narrowphase/CollideBodiesModule.ts +0 -755
  113. package/src/physics/solver/ContactConstraintModule.ts +0 -659
  114. package/src/physics/solver/ManifoldConstraint.ts +0 -420
  115. package/src/physics/solver/estimateCollisionResponse.ts +0 -146
  116. package/src/shape/Aabb.ts +0 -400
  117. package/src/shape/Box.ts +0 -231
  118. package/src/shape/Capsule.ts +0 -332
  119. package/src/shape/CompoundShape.ts +0 -288
  120. package/src/shape/Convex.ts +0 -130
  121. package/src/shape/ConvexHull.ts +0 -423
  122. package/src/shape/Cylinder.ts +0 -313
  123. package/src/shape/HeightMap.ts +0 -511
  124. package/src/shape/Line.ts +0 -14
  125. package/src/shape/Plane.ts +0 -116
  126. package/src/shape/Ray.ts +0 -81
  127. package/src/shape/Segment.ts +0 -25
  128. package/src/shape/Shape.ts +0 -77
  129. package/src/shape/Sphere.ts +0 -181
  130. package/src/shape/TransformedShape.ts +0 -51
  131. package/src/shape/Triangle.ts +0 -122
  132. package/src/shape/TriangleMesh.ts +0 -186
  133. package/src/types.ts +0 -1
  134. package/src/world.ts +0 -1335
  135. package/tests/BodyPairsModule.test.ts +0 -71
  136. package/tests/BvhTree.test.ts +0 -406
  137. package/tests/test.md +0 -642
  138. package/tests/vec3.test.ts +0 -12
@@ -1,332 +0,0 @@
1
- import {
2
- createClass,
3
- LazyReferenceType,
4
- MonomorphType,
5
- NumberType,
6
- PropertyDefinitionMap,
7
- PropertyDefinitionReference,
8
- } from "monomorph";
9
- import { Vec3 } from "../math/vec3";
10
- import { Face } from "../physics/manifold/Face";
11
- import { Mat4 } from "../math/mat4";
12
- import { Quat } from "../math/quat";
13
- import { ConvexShapeInterface, ShapeType } from "./Shape";
14
- import type { World } from "../world";
15
- import { Isometry } from "../math/isometry";
16
- import { Mat3 } from "../math/mat3";
17
- import { CollisionResult } from "../collision/collide/collide";
18
- import { Aabb } from "./Aabb";
19
- import { SupportMode, SupportShapeWithConvexRadius } from "./Convex";
20
- import { Triangle } from "../collision/epa/structs";
21
-
22
- /// Used by (Tapered)CapsuleShape to determine when supporting face is an edge rather than a point (unit: meter)
23
- export const capsuleProjectionSlop = 0.02;
24
-
25
- const scaledHalfHeightOfCylinder = /*@__PURE__*/ Vec3.create();
26
- const tempVector = /*@__PURE__*/ Vec3.create();
27
- const yAxis = /*@__PURE__*/ Vec3.create();
28
- const extent = /*@__PURE__*/ Vec3.create();
29
- const radiusVector = /*@__PURE__*/ Vec3.create();
30
- const halfHeightOfCylinder = /*@__PURE__*/ Vec3.create();
31
- const isometry = /*@__PURE__*/ Isometry.create();
32
- const horizontalDirection = /*@__PURE__*/ Vec3.create();
33
- const support = /*@__PURE__*/ Vec3.create();
34
- const supportTop = /*@__PURE__*/ Vec3.create();
35
- const negatedScaledHalfHeightOfCylinder = /*@__PURE__*/ Vec3.create();
36
- const supportBottom = /*@__PURE__*/ Vec3.create();
37
-
38
- const capsuleNoConvexProps = {
39
- halfHeightOfCylinder: MonomorphType(Vec3),
40
- convexRadius: NumberType(0),
41
- } as const satisfies PropertyDefinitionMap;
42
-
43
- export class CapsuleNoConvex extends createClass<CapsuleNoConvex, typeof capsuleNoConvexProps>(capsuleNoConvexProps) {
44
- computeSupport(out: Vec3, direction: Vec3): void {
45
- if (direction.y > 0) {
46
- out.copy(this.halfHeightOfCylinder);
47
- } else {
48
- out.negateVector(this.halfHeightOfCylinder);
49
- }
50
- }
51
-
52
- getConvexRadius(): number {
53
- return this.convexRadius;
54
- }
55
-
56
- computeSupportingFace(out: Face, subShapeID: number, direction: Vec3, scale: number, centerOfMassTransform: Mat4) {
57
- throw new Error("Method not implemented.");
58
- }
59
- }
60
-
61
- const capsuleWithConvexProps = {
62
- halfHeightOfCylinder: MonomorphType(Vec3),
63
- radius: NumberType(0),
64
- } as const satisfies PropertyDefinitionMap;
65
-
66
- export class CapsuleWithConvex extends createClass<CapsuleWithConvex, typeof capsuleWithConvexProps>(
67
- capsuleWithConvexProps
68
- ) {
69
- computeSupport(out: Vec3, direction: Vec3): void {
70
- const len = direction.length();
71
-
72
- if (len > 0) {
73
- // vec3.scaleAndAdd(out, direction, direction, this.radius / len);
74
- out.scaleVector(direction, this.radius / len);
75
- } else {
76
- out.zero();
77
- }
78
-
79
- if (direction.y > 0) {
80
- out.addVector(this.halfHeightOfCylinder);
81
- } else {
82
- out.subtractVector(this.halfHeightOfCylinder);
83
- }
84
- }
85
-
86
- getConvexRadius(): number {
87
- return 0;
88
- }
89
-
90
- computeSupportingFace(
91
- out: Face,
92
- subShapeID: number,
93
- direction: Vec3,
94
- scale: number,
95
- centerOfMassTransform: Mat4
96
- ): void {
97
- throw new Error("Method not implemented.");
98
- }
99
- }
100
-
101
- const capsuleProps = {
102
- computedCenterOfMass: MonomorphType(Vec3, undefined, true),
103
- computedVolume: NumberType(0.0, true),
104
- computedAabb: MonomorphType(Aabb, undefined, true),
105
- radius: NumberType(0),
106
- height: NumberType(0),
107
- copyForDiff: LazyReferenceType((() => Capsule) as () => never) as PropertyDefinitionReference<Capsule | null, true>,
108
- capsuleNoConvex: MonomorphType(CapsuleNoConvex, undefined, true),
109
- capsuleWithConvex: MonomorphType(CapsuleWithConvex, undefined, true),
110
- } as const satisfies PropertyDefinitionMap;
111
-
112
- const afterConstructorCode = `
113
- this.world = null;
114
- `;
115
-
116
- export class Capsule
117
- extends createClass<Capsule, typeof capsuleProps>(capsuleProps, { afterConstructorCode })
118
- implements ConvexShapeInterface
119
- {
120
- type: ShapeType.capsule = ShapeType.capsule;
121
-
122
- declare world: World | null;
123
-
124
- intersectsAabb(aabb: Aabb): boolean {
125
- throw new Error("Method not implemented.");
126
- }
127
-
128
- computeSupport(out: Vec3, direction: Vec3): void {
129
- throw new Error("Method not implemented.");
130
- }
131
-
132
- getHalfHeightOfCylinder(): number {
133
- return this.height / 2;
134
- }
135
-
136
- computeSupportShape(mode: SupportMode, scale: number): SupportShapeWithConvexRadius {
137
- // get scaled capsule
138
- const absScale = Math.abs(scale);
139
-
140
- scaledHalfHeightOfCylinder.set({ x: 0, y: scale * this.getHalfHeightOfCylinder(), z: 0 });
141
- const scaledRadius = scale * this.radius;
142
-
143
- switch (mode) {
144
- case SupportMode.IncludeConvexRadius: {
145
- this.capsuleWithConvex.halfHeightOfCylinder.copy(scaledHalfHeightOfCylinder);
146
- this.capsuleWithConvex.radius = scaledRadius;
147
- return this.capsuleWithConvex;
148
- }
149
-
150
- case SupportMode.ExcludeConvexRadius: {
151
- this.capsuleNoConvex.halfHeightOfCylinder.copy(scaledHalfHeightOfCylinder);
152
- this.capsuleNoConvex.convexRadius = scaledRadius;
153
- return this.capsuleNoConvex;
154
- }
155
-
156
- default: {
157
- throw new Error(`Invalid support mode: ${mode}`);
158
- }
159
- }
160
- }
161
-
162
- computeSurfaceNormal(out: Vec3, inLocalSurfacePosition: Vec3, subShapeId?: number): void {
163
- yAxis.set({ x: 0, y: 1, z: 0 });
164
-
165
- if (inLocalSurfacePosition.y > this.getHalfHeightOfCylinder()) {
166
- tempVector.set({ x: 0, y: this.getHalfHeightOfCylinder(), z: 0 });
167
- out.subtractVectors(inLocalSurfacePosition, tempVector);
168
- out.normalize();
169
- return;
170
- } else if (inLocalSurfacePosition.y < -this.getHalfHeightOfCylinder()) {
171
- tempVector.set({ x: 0, y: -this.getHalfHeightOfCylinder(), z: 0 });
172
- out.subtractVectors(inLocalSurfacePosition, tempVector);
173
- out.normalize();
174
- return;
175
- } else {
176
- out.set({ x: inLocalSurfacePosition.x, y: 0, z: inLocalSurfacePosition.z });
177
- out.normalizedOr(out, yAxis);
178
- return;
179
- }
180
- }
181
-
182
- computeInertiaTensor(out: Mat3, mass: number): void {
183
- // assumptions:
184
- // 1. capsule's center-of-mass is at origin
185
- // 2. capsule is not transformed
186
- // 3. capsule has uniform density = 1
187
- // 4. capsule is aligned with the y-axis
188
- // derivation: chapter 14 of Game Engine Gems Volume I: http://www.gameenginegems.net/geg1.php
189
- const { radius, height } = this;
190
-
191
- const heightSquared = height * height;
192
- const radiusSquared = radius * radius;
193
-
194
- // common terms
195
- const commonMultiplier = mass / (4 * radius + 3 * height);
196
- const commonMassTerm1 = 2 * radius * commonMultiplier;
197
- const commonMassTerm2 = 3 * height * commonMultiplier;
198
- const commonRadiusTerm = (4 / 5) * radiusSquared;
199
-
200
- // axis-aligned terms
201
- const xxTerm1 = (3 / 4) * radius * height + (1 / 2) * heightSquared;
202
- const xxTerm2 = (1 / 4) * radiusSquared + (1 / 12) * heightSquared;
203
- const yyTerm = (1 / 2) * radiusSquared;
204
-
205
- // axis-aligned inertias
206
- const xxInertia = commonMassTerm1 * (commonRadiusTerm + xxTerm1) + commonMassTerm2 * xxTerm2;
207
- const yyInertia = commonMassTerm1 * commonRadiusTerm + commonMassTerm2 * yyTerm;
208
- const zzInertia = xxInertia;
209
-
210
- // set inertia tensor
211
- out.fromArray([xxInertia, 0, 0, 0, yyInertia, 0, 0, 0, zzInertia]);
212
- }
213
-
214
- computeInverseInertiaTensor(out: Mat3, mass: number): void {
215
- this.computeInertiaTensor(out, mass);
216
- out.invert();
217
- }
218
-
219
- computeWorldBounds(out: Aabb, translation: Vec3, rotation: Quat) {
220
- // TODO: apply rotation if shape's center of mass is not at the origin?
221
- isometry.fromRotationAndTranslation(rotation, translation);
222
- out.transformAabb(this.computedAabb, isometry);
223
- }
224
-
225
- collideTriangle(result: CollisionResult, triangle: Triangle, isometry: Isometry) {
226
- throw new Error("Method not implemented.");
227
- }
228
-
229
- computeSupportingFace(out: Face, subShapeID: number, direction: Vec3, scale: number, centerOfMassTransform: Mat4) {
230
- out.clear();
231
-
232
- // Get direction in horizontal plane
233
-
234
- horizontalDirection.copy(direction);
235
- horizontalDirection.y = 0;
236
-
237
- // check zero vector, in this case we're hitting from top/bottom so there's no supporting face
238
- const horizontalDirectionLength = horizontalDirection.length();
239
- if (horizontalDirectionLength === 0) {
240
- return;
241
- }
242
-
243
- // get scaled capsule
244
- scale = Math.abs(scale);
245
-
246
- scaledHalfHeightOfCylinder.set({ x: 0, y: scale * this.getHalfHeightOfCylinder(), z: 0 });
247
- const scaledRadius = scale * this.radius;
248
-
249
- // Get support point for top and bottom sphere in the opposite of 'direction' (including convex radius)
250
-
251
- support.scaleVector(horizontalDirection, scaledRadius / horizontalDirectionLength);
252
- supportTop.subtractVectors(scaledHalfHeightOfCylinder, support);
253
- negatedScaledHalfHeightOfCylinder.negateVector(scaledHalfHeightOfCylinder);
254
- supportBottom.subtractVectors(negatedScaledHalfHeightOfCylinder, support);
255
-
256
- // get projection on inDirection
257
- // Note that inDirection is not normalized, so we need to divide by inDirection.Length() to get the actual projection
258
- // We've multiplied both sides of the if below with inDirection.Length()
259
- const projTop = supportTop.dot(direction);
260
- const projBottom = supportBottom.dot(direction);
261
-
262
- // If projection is roughly equal then return line, otherwise we return nothing as there's only 1 point
263
- if (Math.abs(projTop - projBottom) < capsuleProjectionSlop * direction.length()) {
264
- supportTop.transformVectorFromMat4(supportTop, centerOfMassTransform);
265
- out.pushVertex(supportTop);
266
-
267
- supportBottom.transformVectorFromMat4(supportBottom, centerOfMassTransform);
268
- out.pushVertex(supportBottom);
269
- // out.log("hitting side capsule face");
270
- } else {
271
- // out.log("hitting non-side capsule face");
272
- }
273
-
274
- // console.log("capsule supporting face with vertex count", out.vertexCount);
275
- }
276
-
277
- get isConvex(): boolean {
278
- return true;
279
- }
280
-
281
- hasChanged() {
282
- return (
283
- this.copyForDiff !== null && (this.radius !== this.copyForDiff.radius || this.height !== this.copyForDiff.height)
284
- );
285
- }
286
-
287
- commitChanges() {
288
- if (this.hasChanged()) {
289
- updateShape(this);
290
- // this.world?.updateBodyProperties();
291
- }
292
- }
293
- }
294
-
295
- const oldCreate = Capsule.create;
296
- Capsule.create = function () {
297
- const shape = oldCreate.apply(this, arguments as any);
298
- updateShape(shape);
299
- return shape;
300
- };
301
-
302
- function updateCopyForDiff(shape: Capsule) {
303
- if (shape.copyForDiff) {
304
- shape.copyForDiff.copy(shape);
305
- shape.copyForDiff.copyForDiff = null;
306
- }
307
- }
308
-
309
- function updateCenterOfMass(shape: Capsule) {
310
- shape.computedCenterOfMass.zero();
311
- }
312
-
313
- function updateVolume(shape: Capsule) {
314
- shape.computedVolume =
315
- (4 / 3) * Math.PI * shape.radius * shape.radius * shape.radius +
316
- Math.PI * shape.radius * shape.radius * shape.height;
317
- }
318
-
319
- function updateLocalBounds(shape: Capsule) {
320
- radiusVector.replicate(shape.radius);
321
- halfHeightOfCylinder.set({ x: 0, y: shape.getHalfHeightOfCylinder(), z: 0 });
322
- extent.addVectors(radiusVector, halfHeightOfCylinder);
323
- shape.computedAabb.min.negateVector(extent);
324
- shape.computedAabb.max.copy(extent);
325
- }
326
-
327
- function updateShape(shape: Capsule) {
328
- updateCopyForDiff(shape);
329
- updateCenterOfMass(shape);
330
- updateVolume(shape);
331
- updateLocalBounds(shape);
332
- }
@@ -1,288 +0,0 @@
1
- import {
2
- createClass,
3
- LazyReferenceType,
4
- MonomorphType,
5
- NumberType,
6
- PropertyDefinitionMap,
7
- PropertyDefinitionReference,
8
- ReferenceListType,
9
- } from "monomorph";
10
- import { ConvexShape, ConvexShapeInterface, Shape, ShapeCollectionInterface, ShapeType } from "./Shape";
11
- import type { World } from "../world";
12
- import { Aabb } from "./Aabb";
13
- import { BasicTransform } from "../math/BasicTransform";
14
- import { Vec3 } from "../math/vec3";
15
- import { Sphere } from "./Sphere";
16
- import { Box } from "./Box";
17
- import { Mat4 } from "../math/mat4";
18
- import { Face } from "../physics/manifold/Face";
19
- import { Mat3 } from "../math/mat3";
20
- import { Isometry } from "../math/isometry";
21
- import { Quat } from "../math/quat";
22
- import { TransformedShape } from "./TransformedShape";
23
- import ConvexHull from "./ConvexHull";
24
- import { Cylinder } from "./Cylinder";
25
- import { Capsule } from "./Capsule";
26
-
27
- const cumulativeInertia = /*@__PURE__*/ Mat3.create();
28
- const inertia = /*@__PURE__*/ Mat3.create();
29
- const rotationMatrix = /*@__PURE__*/ Mat3.create();
30
- const tempMatrixA = /*@__PURE__*/ Mat3.create();
31
- const tempMatrixB = /*@__PURE__*/ Mat3.create();
32
- const tempMatrixC = /*@__PURE__*/ Mat3.create();
33
-
34
- const innerBounds = /*@__PURE__*/ Aabb.create();
35
- const isometry = /*@__PURE__*/ Isometry.create();
36
-
37
- const centerOfMass = /*@__PURE__*/ Vec3.create();
38
- const transformedCenterOfMass = /*@__PURE__*/ Vec3.create();
39
-
40
- const transformSubshapeToCompound = /*@__PURE__*/ Isometry.create();
41
- const subShapeDirection = /*@__PURE__*/ Vec3.create();
42
- const subShapeToWorld = /*@__PURE__*/ Isometry.create();
43
-
44
- const compoundShapeProps = {
45
- computedCenterOfMass: MonomorphType(Vec3, {}, true),
46
- computedVolume: NumberType(0.0, true),
47
- computedAabb: MonomorphType(Aabb, {}, true),
48
- copyForDiff: LazyReferenceType((() => CompoundShape) as () => never) as PropertyDefinitionReference<
49
- CompoundShape | null,
50
- true
51
- >,
52
- shapes: ReferenceListType(TransformedShape),
53
- } as const satisfies PropertyDefinitionMap;
54
-
55
- const afterConstructorCode = `
56
- this.world = null;
57
- `;
58
-
59
- const tempTransform = /*@__PURE__*/ BasicTransform.create();
60
-
61
- export class CompoundShape
62
- extends createClass<CompoundShape, typeof compoundShapeProps>(compoundShapeProps, { afterConstructorCode })
63
- implements ShapeCollectionInterface
64
- {
65
- type: ShapeType.compoundShape = ShapeType.compoundShape;
66
-
67
- declare world: World | null;
68
-
69
- walkShapes(onShapeFound: (shape: ConvexShape, transform: BasicTransform) => boolean) {
70
- for (const shapeObj of this.shapes) {
71
- const shape = shapeObj.shape as ConvexShape;
72
- const transform = shapeObj.transform as BasicTransform;
73
- tempTransform.copy(transform);
74
- tempTransform.position.addVector(this.computedCenterOfMass);
75
-
76
- if (onShapeFound(shape as ConvexShape, tempTransform)) {
77
- return;
78
- }
79
- }
80
- }
81
-
82
- computeNumShapes() {
83
- return this.shapes.length;
84
- }
85
-
86
- getShapeAtIndex(index: number) {
87
- const shape = this.shapes.getAtIndex(index);
88
- if (shape === null) {
89
- throw new Error(`Shape at index ${index} not found in compound shape.`);
90
- }
91
- return shape;
92
- }
93
-
94
- computeSupportingFace(
95
- out: Face,
96
- subShapeID: number,
97
- direction: Vec3,
98
- scale: number,
99
- transformCompoundToWorld: Mat4
100
- ): void {
101
- // transform the direction from compound space to subshape space. we have the subshape's translation and rotation
102
- const { shape, transform } = this.getShapeAtIndex(subShapeID) as { shape: Shape; transform: BasicTransform };
103
- const translation = transform.position;
104
- const rotation = transform.rotation;
105
- transformSubshapeToCompound.fromRotationAndTranslation(rotation, translation);
106
- transformSubshapeToCompound.matrix.multiply3x3Transposed(subShapeDirection, direction);
107
- // get the subShapeToWorld transform which is just the product of the compoundToWorld and subshapeToCompound transforms
108
- subShapeToWorld.matrix.multiplyMatrices(transformCompoundToWorld, transformSubshapeToCompound.matrix);
109
- // TODO: account for subShapeID in case we allow recursive compound shapes
110
- shape.computeSupportingFace(out, subShapeID, subShapeDirection, scale, subShapeToWorld.matrix);
111
- }
112
-
113
- computeInertiaTensor(out: Mat3, totalMass: number): void {
114
- // init the cumulative mass and inertia
115
-
116
- let cumulativeMass = 0;
117
- cumulativeInertia.zero();
118
-
119
- const totalVolume = this.computedVolume;
120
-
121
- for (const shapeObj of this.shapes) {
122
- const shape = shapeObj.shape as ConvexShape;
123
- const transform = shapeObj.transform;
124
- const translation = transform.position;
125
- const rotation = transform.rotation;
126
-
127
- const volume = shape.computedVolume;
128
- const mass = (volume / totalVolume) * totalMass;
129
-
130
- // get the shape inertia tensor
131
- shape.computeInertiaTensor(inertia, mass);
132
-
133
- // rotate the inertia tensor
134
- rotationMatrix.fromQuat(rotation);
135
- inertia.multiplyMat3s(rotationMatrix, inertia);
136
- inertia.multiplyMat3RightTransposed(rotationMatrix);
137
-
138
- tempMatrixA.identity();
139
- tempMatrixA.multiplyByScalar(translation.dot(translation));
140
-
141
- tempMatrixB.outerProduct(translation, translation);
142
-
143
- tempMatrixC.subtractMatrices(tempMatrixA, tempMatrixB);
144
- tempMatrixC.multiplyByScalar(mass);
145
-
146
- inertia.addMatrix(tempMatrixC);
147
-
148
- // accumulate the mass and inertia
149
- cumulativeMass += mass;
150
- cumulativeInertia.addMatrix(inertia);
151
- }
152
-
153
- // return the cumulative inertia tensor
154
- out.copy(cumulativeInertia);
155
- }
156
-
157
- computeInverseInertiaTensor(out: Mat3, mass: number): void {
158
- this.computeInertiaTensor(out, mass);
159
- out.invert();
160
- }
161
-
162
- computeWorldBounds(out: Aabb, translation: Vec3, rotation: Quat): void {
163
- // TODO: apply rotation if shape's center of mass is not at the origin?
164
- isometry.fromRotationAndTranslation(rotation, translation);
165
- out.transformAabb(this.computedAabb, isometry);
166
- }
167
-
168
- computeSurfaceNormal(out: Vec3, inLocalSurfacePosition: Vec3, subShapeId?: number): void {
169
- if (subShapeId === undefined) {
170
- throw new Error("CompoundShape.computeSurfaceNormal requires a subShapeId");
171
- }
172
-
173
- const { shape, transform } = this.getShapeAtIndex(subShapeId) as { shape: ConvexShape; transform: BasicTransform };
174
-
175
- // transform the surface position from compound space to subshape space. we have the subshape's translation and rotation
176
- transformCompoundToSubshape.fromInverseRotationAndTranslation(transform.rotation, transform.position);
177
- subShapeSurfacePosition.transformVectorFromMat4(inLocalSurfacePosition, transformCompoundToSubshape.matrix);
178
-
179
- // get the surface normal in subshape space
180
- if (shape === undefined) {
181
- throw new Error(`CompoundShape.setView: shapeId ${subShapeId} not found in world`); // TODO: handle this better
182
- }
183
- shape.computeSurfaceNormal(out, subShapeSurfacePosition, subShapeId);
184
-
185
- // transform the surface normal from subshape space to compound space
186
- transformCompoundToSubshape.matrix.multiply3x3Transposed(out, out);
187
- }
188
-
189
- hasChanged() {
190
- // TODO: we don't support changes on transforms yet
191
- for (const shapeObj of this.shapes) {
192
- const hasChanged = (shapeObj.shape as ConvexShapeInterface).hasChanged();
193
- if (hasChanged) {
194
- return true;
195
- }
196
- }
197
- return false;
198
- }
199
-
200
- update() {
201
- updateShape(this);
202
- }
203
-
204
- commitChanges() {
205
- if (this.hasChanged()) {
206
- updateShape(this);
207
- // this.world?.updateBodyProperties();
208
- }
209
- }
210
- }
211
-
212
- function updateCenterOfMass(compound: CompoundShape) {
213
- // we simply average the centers of mass of the two shapes, applying the correct translation and rotation as we go
214
- compound.computedCenterOfMass.zero();
215
-
216
- for (const shapeObj of compound.shapes) {
217
- const shape = shapeObj.shape as ConvexShape;
218
- const transform = shapeObj.transform as BasicTransform;
219
-
220
- const translation = transform.position;
221
- const rotation = transform.rotation;
222
-
223
- transformedCenterOfMass.applyQuatThenAddToVector(translation, rotation, shape.computedCenterOfMass);
224
- // compound.computedCenterOfMass.addVectors(compound.computedCenterOfMass, transformedCenterOfMass);
225
- compound.computedCenterOfMass.addVector(transformedCenterOfMass);
226
- }
227
-
228
- const numShapes = compound.computeNumShapes();
229
- compound.computedCenterOfMass.scale(1 / numShapes);
230
- }
231
-
232
- const oldCreate = CompoundShape.create;
233
- CompoundShape.create = function () {
234
- const shape = oldCreate.apply(this, arguments as any);
235
- for (const shapeObj of shape.shapes) {
236
- const transform = shapeObj.transform as BasicTransform;
237
- transform.rotation.normalize();
238
- }
239
- updateShape(shape);
240
- return shape;
241
- };
242
-
243
- const transformCompoundToSubshape = /*@__PURE__*/ Isometry.create();
244
- const subShapeSurfacePosition = /*@__PURE__*/ Vec3.create();
245
-
246
- let volume = 0;
247
- function addVolumeCallback(shape: Sphere | Box | ConvexHull | Cylinder | Capsule, transform: BasicTransform) {
248
- volume += shape.computedVolume;
249
- return false;
250
- }
251
-
252
- function updateVolume(shape: CompoundShape) {
253
- volume = 0;
254
- shape.walkShapes(addVolumeCallback);
255
- shape.computedVolume = volume;
256
- }
257
-
258
- function updateLocalBounds(compound: CompoundShape) {
259
- compound.computedAabb.min.zero();
260
- compound.computedAabb.max.zero();
261
-
262
- for (const shapeObj of compound.shapes) {
263
- const shape = shapeObj.shape as ConvexShape;
264
- const transform = shapeObj.transform as BasicTransform;
265
-
266
- const translation = transform.position;
267
- const rotation = transform.rotation;
268
- isometry.fromRotationAndTranslation(rotation, translation);
269
- innerBounds.transformAabb(shape.computedAabb, isometry);
270
- compound.computedAabb.unionAabb(innerBounds);
271
- }
272
- }
273
-
274
- function updateCopyForDiff(shape: CompoundShape) {
275
- for (const shapeObj of shape.shapes) {
276
- const subShape = shapeObj.shape as ConvexShape;
277
- // @ts-ignore
278
- subShape.copyForDiff!.copy(subShape);
279
- subShape.copyForDiff!.copyForDiff = null;
280
- }
281
- }
282
-
283
- function updateShape(shape: CompoundShape) {
284
- updateCopyForDiff(shape);
285
- updateCenterOfMass(shape);
286
- updateVolume(shape);
287
- updateLocalBounds(shape);
288
- }