@perplexdotgg/bounce 1.0.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 (140) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +30 -0
  3. package/package.json +32 -0
  4. package/src/builders/ConvexHullBuilder.ts +437 -0
  5. package/src/builders/ConvexHullBuilder2d.ts +344 -0
  6. package/src/builders/ConvexHullBuilder3d.ts +1689 -0
  7. package/src/builders/HeightMapBuilder.ts +414 -0
  8. package/src/builders/TriangleMeshBuilder.ts +92 -0
  9. package/src/collision/CastShapesModule.ts +184 -0
  10. package/src/collision/CollideShapesModule.ts +152 -0
  11. package/src/collision/HeightMapCaster.ts +38 -0
  12. package/src/collision/HeightMapCollider.ts +33 -0
  13. package/src/collision/TriangleCaster.ts +249 -0
  14. package/src/collision/TriangleCollider.ts +308 -0
  15. package/src/collision/TriangleCollider2.ts +379 -0
  16. package/src/collision/activeEdge.ts +146 -0
  17. package/src/collision/cast/cast.ts +139 -0
  18. package/src/collision/cast/castCompoundVsCompound.ts +59 -0
  19. package/src/collision/cast/castCompoundVsConvex.ts +116 -0
  20. package/src/collision/cast/castConvexVsCompound.ts +123 -0
  21. package/src/collision/cast/castConvexVsConvex.ts +213 -0
  22. package/src/collision/cast/castConvexVsHeightMap.ts +73 -0
  23. package/src/collision/cast/castConvexVsTriangleMesh.ts +56 -0
  24. package/src/collision/cast/castRayVsCompound.ts +44 -0
  25. package/src/collision/cast/castRayVsConvex.ts +45 -0
  26. package/src/collision/cast/castRayVsHeightMap.ts +58 -0
  27. package/src/collision/cast/castRayVsTriangleMesh.ts +58 -0
  28. package/src/collision/closestPoints/closestPoints.ts +23 -0
  29. package/src/collision/closestPoints/computeBarycentricCoordinates2d.ts +32 -0
  30. package/src/collision/closestPoints/computeBarycentricCoordinates3d.ts +81 -0
  31. package/src/collision/closestPoints/computeClosestPointOnLine.ts +30 -0
  32. package/src/collision/closestPoints/computeClosestPointOnTetrahedron.ts +96 -0
  33. package/src/collision/closestPoints/computeClosestPointOnTriangle.ts +195 -0
  34. package/src/collision/closestPoints/isOriginOutsideOfPlane.ts +25 -0
  35. package/src/collision/closestPoints/isOriginOutsideOfTrianglePlanes.ts +72 -0
  36. package/src/collision/collide/collide.ts +146 -0
  37. package/src/collision/collide/collideCompoundVsCompound.ts +60 -0
  38. package/src/collision/collide/collideCompoundVsConvex.ts +59 -0
  39. package/src/collision/collide/collideCompoundVsHeightMap.ts +73 -0
  40. package/src/collision/collide/collideCompoundVsTriangleMesh.ts +56 -0
  41. package/src/collision/collide/collideConvexVsCompound.ts +57 -0
  42. package/src/collision/collide/collideConvexVsConvex.ts +225 -0
  43. package/src/collision/collide/collideConvexVsConvexImp.ts +236 -0
  44. package/src/collision/collide/collideConvexVsHeightMap.ts +53 -0
  45. package/src/collision/collide/collideConvexVsTriangleMesh.ts +58 -0
  46. package/src/collision/collide/collideHeightMapVsCompound.ts +69 -0
  47. package/src/collision/collide/collideHeightMapVsConvex.ts +53 -0
  48. package/src/collision/collide/collideSphereVsSphere.ts +81 -0
  49. package/src/collision/collide/collideTriangleMeshVsCompound.ts +58 -0
  50. package/src/collision/collide/collideTriangleMeshVsConvex.ts +58 -0
  51. package/src/collision/epa/EpaConvexHullBuilder.ts +397 -0
  52. package/src/collision/epa/StaticArray.ts +154 -0
  53. package/src/collision/epa/TriangleFactory.ts +32 -0
  54. package/src/collision/epa/arrays.ts +99 -0
  55. package/src/collision/epa/binaryHeap.ts +82 -0
  56. package/src/collision/epa/structs.ts +227 -0
  57. package/src/collision/gjk/GjkModule.ts +864 -0
  58. package/src/collision/gjk/PenetrationDepthModule.ts +493 -0
  59. package/src/collision/gjk/SupportPoints.ts +50 -0
  60. package/src/collision/imp/MinkowskiDifference.ts +36 -0
  61. package/src/collision/imp/computeExploredDistanceLowerUpperBound.ts +40 -0
  62. package/src/collision/imp/finalizeImpResult.ts +69 -0
  63. package/src/collision/imp/findContactImp.ts +196 -0
  64. package/src/collision/imp/imp.ts +28 -0
  65. package/src/collision/imp/incrementalMinimumDistanceExploreDirection.ts +207 -0
  66. package/src/collision/mpr/findPortal.ts +152 -0
  67. package/src/collision/mpr/mpr.ts +29 -0
  68. package/src/collision/mpr/updatePortal.ts +52 -0
  69. package/src/constraints/BaseConstraint.ts +50 -0
  70. package/src/constraints/ConstraintOptions.ts +22 -0
  71. package/src/constraints/ConstraintSolver.ts +119 -0
  72. package/src/constraints/DistanceConstraint.ts +229 -0
  73. package/src/constraints/FixedConstraint.ts +203 -0
  74. package/src/constraints/HingeConstraint.ts +460 -0
  75. package/src/constraints/PointConstraint.ts +108 -0
  76. package/src/constraints/components/AngleComponent.ts +226 -0
  77. package/src/constraints/components/AxisComponent.ts +263 -0
  78. package/src/constraints/components/HingeComponent.ts +215 -0
  79. package/src/constraints/components/Motor.ts +36 -0
  80. package/src/constraints/components/PointConstraintComponent.ts +179 -0
  81. package/src/constraints/components/RotationEulerComponent.ts +139 -0
  82. package/src/constraints/components/Spring.ts +30 -0
  83. package/src/constraints/components/SpringComponent.ts +71 -0
  84. package/src/constraints/types.ts +6 -0
  85. package/src/helpers.ts +147 -0
  86. package/src/index.ts +50 -0
  87. package/src/math/BasicTransform.ts +19 -0
  88. package/src/math/NumberValue.ts +13 -0
  89. package/src/math/isometry.ts +64 -0
  90. package/src/math/mat3.ts +529 -0
  91. package/src/math/mat4.ts +588 -0
  92. package/src/math/quat.ts +193 -0
  93. package/src/math/scalar.ts +81 -0
  94. package/src/math/tensor.ts +17 -0
  95. package/src/math/vec3.ts +589 -0
  96. package/src/math/vec4.ts +10 -0
  97. package/src/physics/Body.ts +581 -0
  98. package/src/physics/CollisionFilter.ts +52 -0
  99. package/src/physics/SleepModule.ts +163 -0
  100. package/src/physics/broadphase/BodyPairsModule.ts +363 -0
  101. package/src/physics/broadphase/BvhModule.ts +237 -0
  102. package/src/physics/broadphase/BvhTree.ts +803 -0
  103. package/src/physics/broadphase/ConstraintPairsModule.ts +385 -0
  104. package/src/physics/broadphase/TriangleMeshBvhTree.ts +379 -0
  105. package/src/physics/manifold/ContactManifold.ts +227 -0
  106. package/src/physics/manifold/ContactManifoldModule.ts +623 -0
  107. package/src/physics/manifold/Face.ts +119 -0
  108. package/src/physics/manifold/ManifoldCache.ts +116 -0
  109. package/src/physics/manifold/clipping/clipPolyVsEdge.ts +131 -0
  110. package/src/physics/manifold/clipping/clipPolyVsPlane.ts +73 -0
  111. package/src/physics/manifold/clipping/clipPolyVsPoly.ts +72 -0
  112. package/src/physics/narrowphase/CollideBodiesModule.ts +755 -0
  113. package/src/physics/solver/ContactConstraintModule.ts +659 -0
  114. package/src/physics/solver/ManifoldConstraint.ts +420 -0
  115. package/src/physics/solver/estimateCollisionResponse.ts +146 -0
  116. package/src/shape/Aabb.ts +400 -0
  117. package/src/shape/Box.ts +231 -0
  118. package/src/shape/Capsule.ts +332 -0
  119. package/src/shape/CompoundShape.ts +288 -0
  120. package/src/shape/Convex.ts +130 -0
  121. package/src/shape/ConvexHull.ts +423 -0
  122. package/src/shape/Cylinder.ts +313 -0
  123. package/src/shape/HeightMap.ts +511 -0
  124. package/src/shape/Line.ts +14 -0
  125. package/src/shape/Plane.ts +116 -0
  126. package/src/shape/Ray.ts +81 -0
  127. package/src/shape/Segment.ts +25 -0
  128. package/src/shape/Shape.ts +77 -0
  129. package/src/shape/Sphere.ts +181 -0
  130. package/src/shape/TransformedShape.ts +51 -0
  131. package/src/shape/Triangle.ts +122 -0
  132. package/src/shape/TriangleMesh.ts +186 -0
  133. package/src/types.ts +1 -0
  134. package/src/world.ts +1335 -0
  135. package/tests/BodyPairsModule.test.ts +71 -0
  136. package/tests/BvhTree.test.ts +406 -0
  137. package/tests/test.md +642 -0
  138. package/tests/vec3.test.ts +12 -0
  139. package/tsconfig.json +20 -0
  140. package/vite.config.js +40 -0
@@ -0,0 +1,313 @@
1
+ import {
2
+ createClass,
3
+ LazyReferenceType,
4
+ MonomorphType,
5
+ NumberType,
6
+ PropertyDefinitionMap,
7
+ PropertyDefinitionReference,
8
+ } from "monomorph";
9
+ import { squared } from "../math/scalar";
10
+ import { Vec3 } from "../math/vec3";
11
+ import { Face } from "../physics/manifold/Face";
12
+ import { Mat4 } from "../math/mat4";
13
+ import { Aabb } from "./Aabb";
14
+ import { ConvexShapeInterface, ShapeType } from "./Shape";
15
+ import type { World } from "../world";
16
+ import { Isometry } from "../math/isometry";
17
+ import { SupportMode, SupportShapeWithConvexRadius } from "./Convex";
18
+ import { Mat3 } from "../math/mat3";
19
+ import { Quat } from "../math/quat";
20
+
21
+ // Approximation of top face with 8 vertices
22
+ const topFace: [number, number, number][] = [
23
+ [0.0, 1.0, 1.0],
24
+ [Math.sin(Math.PI / 4), 1.0, Math.sin(Math.PI / 4)],
25
+ [1.0, 1.0, 0.0],
26
+ [Math.sin(Math.PI / 4), 1.0, -Math.sin(Math.PI / 4)],
27
+ [0.0, 1.0, -1.0],
28
+ [-Math.sin(Math.PI / 4), 1.0, -Math.sin(Math.PI / 4)],
29
+ [-1.0, 1.0, 0.0],
30
+ [-Math.sin(Math.PI / 4), 1.0, Math.sin(Math.PI / 4)],
31
+ ];
32
+
33
+ // Approximation of bottom face with 8 vertices
34
+ const bottomFace: [number, number, number][] = [
35
+ [0.0, -1.0, 1.0],
36
+ [-Math.sin(Math.PI / 4), -1.0, Math.sin(Math.PI / 4)],
37
+ [-1.0, -1.0, 0.0],
38
+ [-Math.sin(Math.PI / 4), -1.0, -Math.sin(Math.PI / 4)],
39
+ [0.0, -1.0, -1.0],
40
+ [Math.sin(Math.PI / 4), -1.0, -Math.sin(Math.PI / 4)],
41
+ [1.0, -1.0, 0.0],
42
+ [Math.sin(Math.PI / 4), -1.0, Math.sin(Math.PI / 4)],
43
+ ];
44
+
45
+ const local_surface_position_xz = /*@__PURE__*/ Vec3.create();
46
+ const abs_scale = /*@__PURE__*/ Vec3.create();
47
+ const vertex = /*@__PURE__*/ Vec3.create();
48
+ const multiplier = /*@__PURE__*/ Vec3.create();
49
+ const transform = /*@__PURE__*/ Mat4.create();
50
+ const isometry = /*@__PURE__*/ Isometry.create();
51
+
52
+ const cylinderSupportShapeProps = {
53
+ halfHeight: NumberType(0),
54
+ radius: NumberType(0),
55
+ convexRadius: NumberType(0),
56
+ } as const satisfies PropertyDefinitionMap;
57
+
58
+ export class CylinderSupportShape extends createClass<CylinderSupportShape, typeof cylinderSupportShapeProps>(
59
+ cylinderSupportShapeProps
60
+ ) {
61
+ getConvexRadius(): number {
62
+ return this.convexRadius;
63
+ }
64
+
65
+ computeSupport(out: Vec3, direction: Vec3): void {
66
+ // Support mapping, taken from:
67
+ // A Fast and Robust GJK Implementation for Collision Detection of Convex Objects - Gino van den Bergen
68
+ // page 8
69
+ const x = direction.x;
70
+ const y = direction.y;
71
+ const z = direction.z;
72
+ const o = Math.sqrt(squared(x) + squared(z));
73
+
74
+ if (o > 0.0) {
75
+ out.x = (this.radius * x) / o;
76
+ out.y = Math.sign(y) * this.halfHeight;
77
+ out.z = (this.radius * z) / o;
78
+ } else {
79
+ out.x = 0;
80
+ out.y = Math.sign(y) * this.halfHeight;
81
+ out.z = 0;
82
+ }
83
+ }
84
+
85
+ computeSupportingFace(out: Face, subShapeID: number, direction: Vec3, scale: number, centerOfMassTransform: Mat4) {
86
+ throw new Error("Method not implemented.");
87
+ }
88
+ }
89
+
90
+ const cylinderProps = {
91
+ computedCenterOfMass: MonomorphType(Vec3, undefined, true),
92
+ computedVolume: NumberType(0.0, true),
93
+ computedAabb: MonomorphType(Aabb, undefined, true),
94
+ radius: NumberType(0),
95
+ halfHeight: NumberType(0),
96
+ convexRadius: NumberType(0),
97
+ translation: MonomorphType(Vec3, undefined, true),
98
+ supportShape: MonomorphType(CylinderSupportShape, undefined, true),
99
+ copyForDiff: LazyReferenceType((() => Cylinder) as () => never) as PropertyDefinitionReference<Cylinder | null, true>,
100
+ } as const satisfies PropertyDefinitionMap;
101
+
102
+ const afterConstructorCode = `
103
+ this.world = null;
104
+ `;
105
+
106
+ export class Cylinder
107
+ extends createClass<Cylinder, typeof cylinderProps>(cylinderProps, { afterConstructorCode })
108
+ implements ConvexShapeInterface
109
+ {
110
+ type: ShapeType.cylinder = ShapeType.cylinder;
111
+ declare world: World | null;
112
+
113
+ intersectsAabb(aabb: Aabb): boolean {
114
+ throw new Error("Method not implemented.");
115
+ }
116
+
117
+ computeSupport(out: Vec3, direction: Vec3): void {
118
+ throw new Error("Method not implemented.");
119
+ }
120
+
121
+ getConvexRadius(): number {
122
+ return this.convexRadius;
123
+ }
124
+
125
+ computeSurfaceNormal(out: Vec3, inLocalSurfacePosition: Vec3, subShapeId?: number): void {
126
+ // Calculate distance to infinite cylinder surface
127
+
128
+ local_surface_position_xz.fromArray([inLocalSurfacePosition.x, 0, inLocalSurfacePosition.z]);
129
+ const local_surface_position_xz_len = local_surface_position_xz.length();
130
+ const distance_to_curved_surface = Math.abs(local_surface_position_xz_len - this.radius);
131
+
132
+ // Calculate distance to top or bottom plane
133
+ const distance_to_top_or_bottom = Math.abs(Math.abs(inLocalSurfacePosition.y) - this.halfHeight);
134
+
135
+ // Return normal according to closest surface
136
+ if (distance_to_curved_surface < distance_to_top_or_bottom) {
137
+ out.copy(local_surface_position_xz).scale(1 / local_surface_position_xz_len);
138
+ } else {
139
+ out.fromArray([0, inLocalSurfacePosition.y > 0 ? +1 : -1, 0]);
140
+ }
141
+ }
142
+
143
+ computeSupportShape(mode: SupportMode, scale: number): SupportShapeWithConvexRadius {
144
+ abs_scale.replicate(Math.abs(scale));
145
+ const scale_xz = abs_scale.x;
146
+ const scale_y = abs_scale.y;
147
+ const scaled_half_height = scale_y * this.halfHeight;
148
+ const scaled_radius = scale_xz * this.radius;
149
+ const scaled_convex_radius = this.convexRadius * scale;
150
+
151
+ const supportShape = this.supportShape;
152
+
153
+ switch (mode) {
154
+ case SupportMode.IncludeConvexRadius: {
155
+ supportShape.set({ halfHeight: scaled_half_height, radius: scaled_radius, convexRadius: 0.0 });
156
+ break;
157
+ }
158
+
159
+ case SupportMode.ExcludeConvexRadius: {
160
+ supportShape.set({
161
+ halfHeight: scaled_half_height - scaled_convex_radius,
162
+ radius: scaled_radius - scaled_convex_radius,
163
+ convexRadius: scaled_convex_radius,
164
+ });
165
+ break;
166
+ }
167
+
168
+ default: {
169
+ throw new Error("Invalid mode");
170
+ }
171
+ }
172
+
173
+ return supportShape;
174
+ }
175
+
176
+ computeSupportingFace(
177
+ out: Face,
178
+ subShapeID: number,
179
+ direction: Vec3,
180
+ scale: number,
181
+ centerOfMassTransform: Mat4
182
+ ): void {
183
+ out.clear();
184
+
185
+ // Get scaled cylinder
186
+
187
+ abs_scale.replicate(Math.abs(scale));
188
+ const scale_xz = abs_scale.x;
189
+ const scale_y = abs_scale.y;
190
+ const scaled_half_height = scale_y * this.halfHeight;
191
+ const scaled_radius = scale_xz * this.radius;
192
+
193
+ const x = direction.x;
194
+ const y = direction.y;
195
+ const z = direction.z;
196
+ const o = Math.sqrt(squared(x) + squared(z));
197
+
198
+ // If o / |y| > scaled_radius / scaled_half_height, we're hitting the side
199
+ if (o * scaled_half_height > scaled_radius * Math.abs(y)) {
200
+ // Hitting side
201
+ const f = -scaled_radius / o;
202
+ const vx = x * f;
203
+ const vz = z * f;
204
+
205
+ vertex.fromArray([vx, +scaled_half_height, vz]);
206
+ out.pushVertex(vertex);
207
+
208
+ vertex.fromArray([vx, -scaled_half_height, vz]);
209
+ out.pushVertex(vertex);
210
+
211
+ out.transform(centerOfMassTransform);
212
+ // out.log("hitting side, cylinder supporting face");
213
+ } else {
214
+ // // Hitting top or bottom
215
+
216
+ multiplier.fromArray([+scaled_radius, +scaled_half_height, +scaled_radius]);
217
+ // if (y < 0) {
218
+ // multiplier.fromArray([+scaled_radius, +scaled_half_height, +scaled_radius]);
219
+ // } else {
220
+ // multiplier.fromArray([-scaled_radius, -scaled_half_height, -scaled_radius]);
221
+ // }
222
+
223
+ const theFace = y < 0 ? topFace : bottomFace;
224
+
225
+ for (const topFaceVertex of theFace) {
226
+ vertex.fromArray(topFaceVertex);
227
+ out.pushVertex(vertex);
228
+ }
229
+
230
+ transform.copy(centerOfMassTransform).preScaled(multiplier);
231
+ out.transform(transform);
232
+ // out.log("hitting top or bottom, cylinder supporting face");
233
+ }
234
+
235
+ // console.log("cylinder supporting face with vertex count", out.vertexCount);
236
+ }
237
+
238
+ computeInertiaTensor(out: Mat3, mass: number): void {
239
+ const radiusSquared = squared(this.radius);
240
+ const height = this.halfHeight * 2;
241
+ const yyInertia = radiusSquared * mass * 0.5;
242
+ const xxInertia = yyInertia * 0.5 + (mass * height * height) / 12;
243
+ const zzInertia = xxInertia;
244
+ out.fromArray([xxInertia, 0, 0, 0, yyInertia, 0, 0, 0, zzInertia]);
245
+ }
246
+
247
+ computeInverseInertiaTensor(out: Mat3, mass: number): void {
248
+ this.computeInertiaTensor(out, mass);
249
+ out.invert();
250
+ }
251
+
252
+ computeWorldBounds(out: Aabb, translation: Vec3, rotation: Quat): void {
253
+ // TODO: apply rotation if shape's center of mass is not at the origin?
254
+ isometry.fromRotationAndTranslation(rotation, translation);
255
+ out.transformAabb(this.computedAabb, isometry);
256
+ }
257
+
258
+ get isConvex(): boolean {
259
+ return true;
260
+ }
261
+
262
+ hasChanged() {
263
+ return (
264
+ this.copyForDiff !== null &&
265
+ (this.radius !== this.copyForDiff.radius || this.halfHeight !== this.copyForDiff.halfHeight)
266
+ );
267
+ }
268
+
269
+ commitChanges() {
270
+ if (this.hasChanged()) {
271
+ updateShape(this);
272
+ // this.world?.updateBodyProperties();
273
+ }
274
+ }
275
+ }
276
+
277
+ const oldCreate = Cylinder.create;
278
+ Cylinder.create = function () {
279
+ const shape = oldCreate.apply(this, arguments as any);
280
+ updateShape(shape);
281
+ return shape;
282
+ };
283
+
284
+ function updateVolume(shape: Cylinder) {
285
+ shape.computedVolume = Math.PI * squared(shape.radius) * shape.halfHeight * 2;
286
+ }
287
+
288
+ function updateLocalBounds(shape: Cylinder) {
289
+ shape.computedAabb.min.x = -shape.radius;
290
+ shape.computedAabb.min.y = -shape.halfHeight;
291
+ shape.computedAabb.min.z = -shape.radius;
292
+ shape.computedAabb.max.x = +shape.radius;
293
+ shape.computedAabb.max.y = +shape.halfHeight;
294
+ shape.computedAabb.max.z = +shape.radius;
295
+ }
296
+
297
+ function updateCopyForDiff(shape: Cylinder) {
298
+ if (shape.copyForDiff) {
299
+ shape.copyForDiff.copy(shape);
300
+ shape.copyForDiff.copyForDiff = null;
301
+ }
302
+ }
303
+
304
+ function updateCenterOfMass(shape: Cylinder) {
305
+ shape.computedCenterOfMass.zero();
306
+ }
307
+
308
+ function updateShape(shape: Cylinder) {
309
+ updateCopyForDiff(shape);
310
+ updateCenterOfMass(shape);
311
+ updateVolume(shape);
312
+ updateLocalBounds(shape);
313
+ }