@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,308 @@
1
+ import { Isometry } from "../math/isometry";
2
+ import { Mat3 } from "../math/mat3";
3
+ import { Vec3 } from "../math/vec3";
4
+ import { Face } from "../physics/manifold/Face";
5
+ import { Aabb } from "../shape/Aabb";
6
+ import { ConvexRadiusObject, SupportMode } from "../shape/Convex";
7
+ import { ConvexShape } from "../shape/Shape";
8
+ import { Triangle } from "../shape/Triangle";
9
+ import { fixNormal } from "./activeEdge";
10
+ import { PenetrationDepth, PenetrationDepthModule } from "./gjk/PenetrationDepthModule";
11
+ import {
12
+ ActiveEdgeMode,
13
+ BackFaceMode,
14
+ CollisionCollector,
15
+ CollisionResult,
16
+ CollisionSettings,
17
+ CollisionStatus,
18
+ } from "./collide/collide";
19
+ import { Body } from "../physics/Body";
20
+
21
+ const result = /*@__PURE__*/ CollisionResult.create({
22
+ faceA: {
23
+ numVertices: 0,
24
+ buffer: { pool: new Vec3.Pool(32), maxLength: 32 },
25
+ },
26
+ faceB: {
27
+ numVertices: 0,
28
+ buffer: { pool: new Vec3.Pool(32), maxLength: 32 },
29
+ },
30
+ });
31
+ const isometryBToA = /*@__PURE__*/ Isometry.create();
32
+ const localBoundsA = /*@__PURE__*/ Aabb.create();
33
+ const shapeB = /*@__PURE__*/ Triangle.create();
34
+ const convexA = /*@__PURE__*/ ConvexRadiusObject.create();
35
+ const convexB = /*@__PURE__*/ ConvexRadiusObject.create();
36
+ const rotationMatrixA = /*@__PURE__*/ Mat3.create();
37
+ const positionA = /*@__PURE__*/ Vec3.create();
38
+ const positionB = /*@__PURE__*/ Vec3.create();
39
+
40
+ const triangleNormal = /*@__PURE__*/ Vec3.create();
41
+ const triangleBounds = /*@__PURE__*/ Aabb.create();
42
+ const pointA = /*@__PURE__*/ Vec3.create();
43
+ const pointB = /*@__PURE__*/ Vec3.create();
44
+ const penetrationAxis = /*@__PURE__*/ Vec3.create();
45
+ const gjkResult = /*@__PURE__*/ PenetrationDepth.create();
46
+ const pointAB = /*@__PURE__*/ Vec3.create();
47
+ const activeEdgeMovementDirectionWorld = /*@__PURE__*/ Vec3.create();
48
+ const activeEdgeMovementDirectionLocal = /*@__PURE__*/ Vec3.create();
49
+ const contactNormal = /*@__PURE__*/ Vec3.create();
50
+ const negatedPenetrationAxis = /*@__PURE__*/ Vec3.create();
51
+ const negatedTriangleNormal = /*@__PURE__*/ Vec3.create();
52
+ const penetrationAxisWorld = /*@__PURE__*/ Vec3.create();
53
+
54
+ export class TriangleCollider {
55
+ declare penetrationDepthModule: PenetrationDepthModule;
56
+ declare shapeA: ConvexShape;
57
+ declare shapeB: Triangle;
58
+ declare isometryA: Isometry;
59
+ declare isometryB: Isometry;
60
+ declare settings: CollisionSettings;
61
+ declare collector: CollisionCollector;
62
+ declare subShapeAId: number;
63
+ bodyA: Body | null = null;
64
+ bodyB: Body | null = null;
65
+
66
+ initialize(
67
+ penetrationDepthModule: PenetrationDepthModule,
68
+ shapeA: ConvexShape,
69
+ isometryA: Isometry,
70
+ isometryB: Isometry,
71
+ settings: CollisionSettings,
72
+ collector: CollisionCollector,
73
+ subShapeAId: number = 0,
74
+ bodyA: Body | null = null,
75
+ bodyB: Body | null = null
76
+ ): void {
77
+ this.penetrationDepthModule = penetrationDepthModule;
78
+ this.shapeA = shapeA;
79
+ this.isometryA = isometryA;
80
+ this.isometryB = isometryB;
81
+ this.settings = settings;
82
+ this.collector = collector;
83
+ this.subShapeAId = subShapeAId;
84
+ this.bodyA = bodyA;
85
+ this.bodyB = bodyB;
86
+
87
+ // get the isometry from B to A
88
+ isometryBToA.matrix.invertMatrix(isometryA.matrix);
89
+ isometryBToA.matrix.multiplyMatrix(isometryB.matrix);
90
+
91
+ // get the rotation matrix of A
92
+ rotationMatrixA.fromMat4(isometryA.matrix);
93
+
94
+ // get the positions
95
+ isometryA.matrix.getTranslation(positionA);
96
+ isometryB.matrix.getTranslation(positionB);
97
+
98
+ // get the local bounds of A
99
+ localBoundsA.copy(shapeA.computedAabb);
100
+ localBoundsA.expand(settings.maxSeparation);
101
+ }
102
+
103
+ collide(triangle: Triangle, activeEdges: number, triangleId: number, bodyA?: Body | null, bodyB?: Body | null): void {
104
+ result.reset();
105
+ // @ts-ignore
106
+ result.bodyA = bodyA || this.bodyA;
107
+ // @ts-ignore
108
+ result.bodyB = bodyB || this.bodyB;
109
+ const settings = this.settings;
110
+ const shapeA = this.shapeA;
111
+ const isometryA = this.isometryA;
112
+ const subShapeAId = this.subShapeAId;
113
+ const collector = this.collector;
114
+ const penetrationDepthModule = this.penetrationDepthModule;
115
+
116
+ // get the triangle in A space
117
+ shapeB.copy(triangle);
118
+ shapeB.transform(isometryBToA.matrix);
119
+
120
+ // get the triangle normal
121
+
122
+ shapeB.computeNormal(triangleNormal);
123
+ // shapeB.getUnnormalizedNormal(triangleNormal);
124
+
125
+ // TODO: handle backface mode
126
+ const back_facing = triangleNormal.dot(triangle.a) > 0;
127
+ if (settings.backFaceMode === BackFaceMode.IgnoreBackFaces && back_facing) {
128
+ // console.log("miss: ignoring back-facing triangle");
129
+ collector.addMiss();
130
+ return;
131
+ }
132
+
133
+ // get triangle bounds
134
+
135
+ shapeB.computeLocalBounds(triangleBounds);
136
+
137
+ // collide bounds
138
+ if (localBoundsA.intersectsAabb(triangleBounds) === false) {
139
+ // console.log("miss: bounds do not intersect");
140
+ collector.addMiss();
141
+ return;
142
+ }
143
+
144
+ // get the support shapes
145
+ const supportA = shapeA.computeSupportShape(SupportMode.ExcludeConvexRadius, 1);
146
+ const supportB = shapeB.computeSupportShape(SupportMode.ExcludeConvexRadius, 1);
147
+
148
+ // get convex radii, including the max separation distance
149
+ const convexRadiusA = supportA.getConvexRadius() + settings.maxSeparation;
150
+ const convexRadiusB = supportB.getConvexRadius();
151
+
152
+ // init the pass-through results
153
+
154
+ // init the penetration axis as the triangle normal
155
+ penetrationAxis.negateVector(triangleNormal);
156
+
157
+ // init the gjk result
158
+
159
+ // run gjk
160
+ penetrationDepthModule.getPenetrationDepthStepGjk(
161
+ gjkResult,
162
+ supportA,
163
+ supportB,
164
+ convexRadiusA,
165
+ convexRadiusB,
166
+ penetrationAxis,
167
+ settings.collisionTolerance
168
+ );
169
+
170
+ switch (gjkResult.status) {
171
+ case CollisionStatus.Colliding: {
172
+ // pass through the results
173
+ pointA.copy(gjkResult.pointA);
174
+ pointB.copy(gjkResult.pointB);
175
+ penetrationAxis.copy(gjkResult.penetrationAxis);
176
+ break;
177
+ }
178
+
179
+ case CollisionStatus.NotColliding: {
180
+ // definitive no penetration
181
+ result.hasContact = false;
182
+ // console.log("miss: no hit from gjk");
183
+ collector.addMiss();
184
+ return;
185
+ }
186
+
187
+ case CollisionStatus.Indeterminate: {
188
+ // need to run IMP
189
+
190
+ // get the support shapes
191
+ const supportA = shapeA.computeSupportShape(SupportMode.IncludeConvexRadius, 1);
192
+ const supportB = shapeB.computeSupportShape(SupportMode.IncludeConvexRadius, 1);
193
+
194
+ // // get the convex shapes
195
+ // convexA.object = supportA;
196
+ // convexA.radius = settings.maxSeparation;
197
+
198
+ // convexB.object = supportB;
199
+ // convexB.radius = 0;
200
+ convexA.setData(settings.maxSeparation, supportA);
201
+ convexB.setData(0, supportB);
202
+
203
+ // init the imp result
204
+
205
+ const hitFound = penetrationDepthModule.getPenetrationDepthStepEPA(
206
+ convexA,
207
+ convexB,
208
+ settings.collisionTolerance,
209
+ penetrationAxis,
210
+ pointA,
211
+ pointB
212
+ );
213
+
214
+ if (!hitFound) {
215
+ // console.log("miss: no hit from EPA");
216
+ result.hasContact = false;
217
+ collector.addMiss();
218
+ return;
219
+ }
220
+ }
221
+ }
222
+
223
+ // we have a collision
224
+
225
+ // get the penetration depth
226
+
227
+ // check if penetration is bigger than early out fraction
228
+ pointAB.subtractVectors(pointB, pointA);
229
+ const penetrationDepth = pointAB.length() - settings.maxSeparation;
230
+ if (-penetrationDepth >= collector.getEarlyOutFraction()) {
231
+ result.hasContact = false;
232
+ collector.addMiss();
233
+ return;
234
+ }
235
+
236
+ // reduce pointA by the max separation distance along the penetration axis
237
+ const penetrationAxisLength = penetrationAxis.length();
238
+ if (penetrationAxisLength > 0) {
239
+ pointA.addScaled(penetrationAxis, -(settings.maxSeparation / penetrationAxisLength));
240
+ }
241
+
242
+ // TODO: handle active edge detection
243
+ if (settings.activeEdgeMode === ActiveEdgeMode.CollideOnlyWithActive && activeEdges !== 0b111) {
244
+ activeEdgeMovementDirectionWorld.x = settings.activeEdgeMovementDirectionX;
245
+ activeEdgeMovementDirectionWorld.y = settings.activeEdgeMovementDirectionY;
246
+ activeEdgeMovementDirectionWorld.z = settings.activeEdgeMovementDirectionZ;
247
+
248
+ // convert the active edge velocity hint to local space
249
+ isometryA.matrix.multiply3x3Transposed(activeEdgeMovementDirectionLocal, activeEdgeMovementDirectionWorld);
250
+
251
+ // update the penetration axis to account for active edges
252
+ // note that we flip the triangle normal as the penetration axis is pointing towards the triangle instead of away
253
+ negatedPenetrationAxis.negateVector(penetrationAxis);
254
+ negatedTriangleNormal.negateVector(triangleNormal);
255
+
256
+ fixNormal(
257
+ contactNormal,
258
+ shapeB.a,
259
+ shapeB.b,
260
+ shapeB.c,
261
+ // back_facing ? triangleNormal : negatedTriangleNormal,
262
+ // triangleNormal,
263
+ negatedTriangleNormal,
264
+ activeEdges,
265
+ pointB,
266
+ penetrationAxis,
267
+ activeEdgeMovementDirectionLocal
268
+ );
269
+
270
+ // update the penetration axis
271
+ penetrationAxis.copy(contactNormal);
272
+ }
273
+
274
+ // convert results from A space to world space
275
+ result.hasContact = true;
276
+ result.subShapeIdA = subShapeAId;
277
+ result.subShapeIdB = triangleId;
278
+ result.isBackFace = back_facing;
279
+
280
+ // convert to world space
281
+ pointA.transformByMat4(isometryA.matrix);
282
+ pointB.transformByMat4(isometryA.matrix);
283
+ isometryA.matrix.multiply3x3(penetrationAxisWorld, penetrationAxis);
284
+
285
+ result.contactPointA.copy(pointA);
286
+ result.contactPointB.copy(pointB);
287
+
288
+ // get the normal in world space
289
+ result.normalA.copy(penetrationAxisWorld);
290
+ result.normalB.negateVector(result.normalA);
291
+
292
+ result.penetration = penetrationDepth;
293
+ result.hasContact = true;
294
+
295
+ // add the world space surface normal of the triangle
296
+ result.surfaceNormalA.zero();
297
+ result.surfaceNormalB.transformVectorFromMat3(triangleNormal, rotationMatrixA);
298
+
299
+ // normalize the normals
300
+ result.normalA.normalize();
301
+ result.normalB.normalize();
302
+
303
+ // console.log("hit: triangle collider hit");
304
+
305
+ // add the hit
306
+ collector.addHit(result);
307
+ }
308
+ }
@@ -0,0 +1,379 @@
1
+ import { Isometry } from "../math/isometry";
2
+ import { Mat3 } from "../math/mat3";
3
+ import { Vec3 } from "../math/vec3";
4
+ import { Face } from "../physics/manifold/Face";
5
+ import { Aabb } from "../shape/Aabb";
6
+ import { ConvexRadiusObject, SupportMode } from "../shape/Convex";
7
+ import { ConvexShape } from "../shape/Shape";
8
+ import { Triangle } from "../shape/Triangle";
9
+ import { fixNormal } from "./activeEdge";
10
+ import { PenetrationDepth, PenetrationDepthModule } from "./gjk/PenetrationDepthModule";
11
+ import {
12
+ ActiveEdgeMode,
13
+ BackFaceMode,
14
+ CollectFacesMode,
15
+ CollisionCollector,
16
+ CollisionResult,
17
+ CollisionSettings,
18
+ CollisionStatus,
19
+ } from "./collide/collide";
20
+ import { Body } from "../physics/Body";
21
+
22
+ const result = /*@__PURE__*/ CollisionResult.create({
23
+ faceA: {
24
+ numVertices: 0,
25
+ buffer: { pool: new Vec3.Pool(32), maxLength: 32 },
26
+ },
27
+ faceB: {
28
+ numVertices: 0,
29
+ buffer: { pool: new Vec3.Pool(32), maxLength: 32 },
30
+ },
31
+ });
32
+ const isometryBToA = /*@__PURE__*/ Isometry.create();
33
+ const localBoundsA = /*@__PURE__*/ Aabb.create();
34
+ const shapeB = /*@__PURE__*/ Triangle.create();
35
+ const convexA = /*@__PURE__*/ ConvexRadiusObject.create();
36
+ const convexB = /*@__PURE__*/ ConvexRadiusObject.create();
37
+ const rotationMatrixA = /*@__PURE__*/ Mat3.create();
38
+ const positionA = /*@__PURE__*/ Vec3.create();
39
+ const positionB = /*@__PURE__*/ Vec3.create();
40
+
41
+ const centerA = /*@__PURE__*/ Vec3.create();
42
+ const centerB = /*@__PURE__*/ Vec3.create();
43
+
44
+ const triangleNormal = /*@__PURE__*/ Vec3.create();
45
+ const triangleBounds = /*@__PURE__*/ Aabb.create();
46
+ const pointA = /*@__PURE__*/ Vec3.create();
47
+ const pointB = /*@__PURE__*/ Vec3.create();
48
+ const penetrationAxis = /*@__PURE__*/ Vec3.create();
49
+ const gjkResult = /*@__PURE__*/ PenetrationDepth.create();
50
+ const pointAB = /*@__PURE__*/ Vec3.create();
51
+ const activeEdgeMovementDirectionWorld = /*@__PURE__*/ Vec3.create();
52
+ const activeEdgeMovementDirectionLocal = /*@__PURE__*/ Vec3.create();
53
+ const contactNormal = /*@__PURE__*/ Vec3.create();
54
+ const negatedPenetrationAxis = /*@__PURE__*/ Vec3.create();
55
+ const negatedTriangleNormal = /*@__PURE__*/ Vec3.create();
56
+ const penetrationAxisWorld = /*@__PURE__*/ Vec3.create();
57
+
58
+ export class TriangleCollider2 {
59
+ declare penetrationDepthModule: PenetrationDepthModule;
60
+ declare shapeA: ConvexShape;
61
+ declare shapeB: Triangle;
62
+ declare isometryA: Isometry;
63
+ declare isometryB: Isometry;
64
+ declare settings: CollisionSettings;
65
+ declare collector: CollisionCollector;
66
+ declare subShapeAId: number;
67
+ bodyA: Body | null = null;
68
+ bodyB: Body | null = null;
69
+
70
+ initialize(
71
+ penetrationDepthModule: PenetrationDepthModule,
72
+ shapeA: ConvexShape,
73
+ isometryA: Isometry,
74
+ isometryB: Isometry,
75
+ settings: CollisionSettings,
76
+ collector: CollisionCollector,
77
+ subShapeAId: number = 0,
78
+ bodyA: Body | null = null,
79
+ bodyB: Body | null = null
80
+ ): void {
81
+ this.penetrationDepthModule = penetrationDepthModule;
82
+ this.shapeA = shapeA;
83
+ this.isometryA = isometryA;
84
+ this.isometryB = isometryB;
85
+ this.settings = settings;
86
+ this.collector = collector;
87
+ this.subShapeAId = subShapeAId;
88
+ this.bodyA = bodyA;
89
+ this.bodyB = bodyB;
90
+
91
+ // console.log(isometryA.matrix.toObject());
92
+ // console.log(isometryB.matrix.toObject());
93
+ // console.log(settings);
94
+ // console.log(subShapeAId);
95
+
96
+ // get the isometry from B to A
97
+ isometryBToA.matrix.invertMatrix(isometryA.matrix);
98
+ isometryBToA.matrix.multiplyMatrices(isometryBToA.matrix, isometryB.matrix);
99
+ // isometryBToA.matrix.log("isometryBToA");
100
+
101
+ // get the rotation matrix of A
102
+ rotationMatrixA.fromMat4(isometryA.matrix);
103
+
104
+ // get the positions
105
+ isometryA.matrix.getTranslation(positionA);
106
+ isometryB.matrix.getTranslation(positionB);
107
+
108
+ // get the local bounds of A
109
+ localBoundsA.copy(shapeA.computedAabb);
110
+ localBoundsA.expand(settings.maxSeparation);
111
+ }
112
+
113
+ collide(
114
+ triangle: Triangle,
115
+ activeEdges: number,
116
+ triangleId: number,
117
+ bodyA: Body | null = null,
118
+ bodyB: Body | null = null,
119
+ swapResults: boolean = false
120
+ ): void {
121
+ result.reset();
122
+ // @ts-ignore
123
+ result.bodyA = bodyA || this.bodyA;
124
+ // @ts-ignore
125
+ result.bodyB = bodyB || this.bodyB;
126
+ const settings = this.settings;
127
+ const shapeA = this.shapeA;
128
+ const isometryA = this.isometryA;
129
+ const subShapeAId = this.subShapeAId;
130
+ const collector = this.collector;
131
+ const penetrationDepthModule = this.penetrationDepthModule;
132
+
133
+ // get the triangle in A space
134
+ shapeB.copy(triangle);
135
+ shapeB.transform(isometryBToA.matrix);
136
+ // console.log(triangleId, triangle.toObject(), shapeB.toObject());
137
+
138
+ // get the triangle normal
139
+ shapeB.computeUnnormalizedNormal(triangleNormal);
140
+ // shapeB.getNormal(triangleNormal);
141
+
142
+ // TODO: handle backface mode
143
+ // const back_facing = triangleNormal.dot(triangle.a) > 0;
144
+ const back_facing = triangleNormal.dot(shapeB.a) > 0;
145
+ if (settings.backFaceMode === BackFaceMode.IgnoreBackFaces && back_facing) {
146
+ // this.world.debug && console.log("triangle miss, ignore back faces", triangleId);
147
+ // console.log("miss: ignoring back-facing triangle");
148
+ collector.addMiss();
149
+ return;
150
+ }
151
+
152
+ // get triangle bounds
153
+ shapeB.computeLocalBounds(triangleBounds);
154
+
155
+ // collide bounds
156
+ if (localBoundsA.intersectsAabb(triangleBounds) === false) {
157
+ // this.world.debug && console.log("triangle miss, bounds not intersecting", triangleId);
158
+ // console.log("miss: bounds do not intersect");
159
+ collector.addMiss();
160
+ // console.log("exit because bounds don't overlap");
161
+ return;
162
+ }
163
+
164
+ // get the support shapes
165
+ const supportA = shapeA.computeSupportShape(SupportMode.ExcludeConvexRadius, 1);
166
+ const supportB = shapeB.computeSupportShape(SupportMode.ExcludeConvexRadius, 1);
167
+
168
+ // init the pass-through results
169
+ // init the penetration axis as the triangle normal
170
+ // vec3.negate(penetrationAxis, triangleNormal);
171
+
172
+ // penetrationAxis.set([0, +1, 0]);
173
+
174
+ isometryA.matrix.getTranslation(centerA);
175
+ shapeB.computeCentroid(centerB);
176
+ penetrationAxis.subtractVectors(centerB, centerA);
177
+
178
+ penetrationAxis.normalize();
179
+
180
+ // init the gjk result
181
+ // run gjk
182
+ // if (this.world.debug) {
183
+ // debugger;
184
+ // }
185
+ penetrationDepthModule.getPenetrationDepthStepGjk(
186
+ gjkResult,
187
+ supportA,
188
+ supportB,
189
+ supportA.getConvexRadius() + settings.maxSeparation,
190
+ supportB.getConvexRadius(),
191
+ penetrationAxis,
192
+ settings.collisionTolerance
193
+ );
194
+
195
+ switch (gjkResult.status) {
196
+ case CollisionStatus.Colliding: {
197
+ // pass through the results
198
+ pointA.copy(gjkResult.pointA);
199
+ pointB.copy(gjkResult.pointB);
200
+ penetrationAxis.copy(gjkResult.penetrationAxis);
201
+ // console.log("found collision with gjk");
202
+ break;
203
+ }
204
+
205
+ case CollisionStatus.NotColliding: {
206
+ // this.world.debug && console.log("triangle miss, not colliding via gjk", triangleId);
207
+ // definitive no penetration
208
+ // console.log("miss: no hit from gjk");
209
+ collector.addMiss();
210
+ // console.log("exit because gjk says not colliding");
211
+ return;
212
+ }
213
+
214
+ case CollisionStatus.Indeterminate: {
215
+ // console.log("starting imp");
216
+ // need to run IMP
217
+
218
+ // get the support shapes
219
+ const supportA = shapeA.computeSupportShape(SupportMode.IncludeConvexRadius, 1);
220
+ const supportB = shapeB.computeSupportShape(SupportMode.IncludeConvexRadius, 1);
221
+
222
+ // get the convex shapes
223
+ // const convexA = this.convexA;
224
+ // convexA.object = supportA;
225
+ // convexA.radius = settings.maxSeparation;
226
+
227
+ // const convexB = this.convexB;
228
+ // convexB.object = supportB;
229
+ // convexB.radius = 0;
230
+
231
+ convexA.setData(settings.maxSeparation, supportA);
232
+ convexB.setData(0, supportB);
233
+
234
+ const hitFound = penetrationDepthModule.getPenetrationDepthStepEPA(
235
+ convexA,
236
+ convexB,
237
+ settings.collisionTolerance,
238
+ penetrationAxis,
239
+ pointA,
240
+ pointB
241
+ );
242
+
243
+ if (!hitFound) {
244
+ result.hasContact = false;
245
+ collector.addMiss();
246
+ return;
247
+ }
248
+ }
249
+ }
250
+
251
+ // we have a collision
252
+
253
+ // // check if penetration axis is similar to triangle normal, if it is, negate it to make it consistent whether back-facing or not
254
+ // if (penetrationAxis.dot(triangleNormal) > 0) {
255
+ // vec3.negate(penetrationAxis, penetrationAxis);
256
+ // }
257
+
258
+ // if (this.world.debugger) {
259
+ // this.world.debugger.drawShape({
260
+ // type: "arrow",
261
+ // group: "foo",
262
+ // origin: pointA,
263
+ // direction: penetrationAxis,
264
+ // length: 10,
265
+ // radius: 0.1,
266
+ // color: "black",
267
+ // });
268
+ // }
269
+
270
+ // check if penetration is bigger than early out fraction
271
+ pointAB.subtractVectors(pointB, pointA);
272
+ const penetrationDepth = pointAB.length() - settings.maxSeparation;
273
+ if (-penetrationDepth >= collector.getEarlyOutFraction()) {
274
+ result.hasContact = false;
275
+ collector.addMiss();
276
+ return;
277
+ }
278
+
279
+ // reduce pointA by the max separation distance along the penetration axis
280
+ const penetrationAxisLength = penetrationAxis.length();
281
+ if (penetrationAxisLength > 0) {
282
+ pointA.addScaled(penetrationAxis, -(settings.maxSeparation / penetrationAxisLength));
283
+ }
284
+
285
+ // TODO: handle active edge detection
286
+ if (settings.activeEdgeMode === ActiveEdgeMode.CollideOnlyWithActive && activeEdges !== 0b111) {
287
+ activeEdgeMovementDirectionWorld.x = settings.activeEdgeMovementDirectionX;
288
+ activeEdgeMovementDirectionWorld.y = settings.activeEdgeMovementDirectionY;
289
+ activeEdgeMovementDirectionWorld.z = settings.activeEdgeMovementDirectionZ;
290
+
291
+ // convert the active edge velocity hint to local space
292
+ isometryA.matrix.multiply3x3Transposed(activeEdgeMovementDirectionLocal, activeEdgeMovementDirectionWorld);
293
+
294
+ // update the penetration axis to account for active edges
295
+ // note that we flip the triangle normal as the penetration axis is pointing towards the triangle instead of away
296
+ negatedPenetrationAxis.negateVector(penetrationAxis);
297
+ negatedTriangleNormal.negateVector(triangleNormal);
298
+
299
+ fixNormal(
300
+ contactNormal,
301
+ shapeB.a,
302
+ shapeB.b,
303
+ shapeB.c,
304
+ // back_facing ? triangleNormal : negatedTriangleNormal,
305
+ // triangleNormal,
306
+ negatedTriangleNormal,
307
+ activeEdges,
308
+ pointB,
309
+ penetrationAxis,
310
+ activeEdgeMovementDirectionLocal
311
+ );
312
+
313
+ // update the penetration axis
314
+ penetrationAxis.copy(contactNormal);
315
+ }
316
+
317
+ // convert results from A space to world space
318
+ result.hasContact = true;
319
+ result.subShapeIdA = subShapeAId;
320
+ result.subShapeIdB = triangleId;
321
+ result.isBackFace = back_facing;
322
+
323
+ // this.faceA.clear();
324
+ // this.faceB.clear();
325
+ // result.faceA = this.faceA;
326
+ // result.faceB = this.faceB;
327
+
328
+ // if (settings.collectFacesMode === CollectFacesMode.CollectFaces) {
329
+ // // const negatedPenetrationAxis = Vec3.global(
330
+ // // this.world,
331
+ // // "CollideConvexAndTriangles2.collide.negatedPenetrationAxis"
332
+ // // );
333
+ // negatedPenetrationAxis.negateVector(penetrationAxis);
334
+ // shapeA.computeSupportingFace(result.faceA, subShapeAId, negatedPenetrationAxis, 1, isometryA.matrix);
335
+ // shapeB.computeSupportingFace(result.faceB, triangleId, negatedPenetrationAxis, 1, isometryA.matrix);
336
+ // }
337
+
338
+ // convert to world space
339
+ pointA.transformByMat4(isometryA.matrix);
340
+ pointB.transformByMat4(isometryA.matrix);
341
+ isometryA.matrix.multiply3x3(penetrationAxisWorld, penetrationAxis);
342
+
343
+ result.contactPointA.copy(pointA);
344
+ result.contactPointB.copy(pointB);
345
+
346
+ // get the normal in world space
347
+ result.normalA.copy(penetrationAxisWorld);
348
+ result.normalB.negateVector(result.normalA);
349
+
350
+ result.penetration = penetrationDepth;
351
+ result.hasContact = true;
352
+
353
+ // add the world space surface normal of the triangle
354
+ result.surfaceNormalA.zero();
355
+ result.surfaceNormalB.transformVectorFromMat3(triangleNormal, rotationMatrixA);
356
+
357
+ // normalize the normals
358
+ result.normalA.normalize();
359
+ result.normalB.normalize();
360
+
361
+ // console.log("collision found: processed results");
362
+ // console.log("result.contactPointA", result.contactPointA.toArray());
363
+ // console.log("result.contactPointB", result.contactPointB.toArray());
364
+ // console.log("result.normalA", result.normalA.toArray());
365
+ // console.log("result.penetration", result.penetration);
366
+ // result.faceA.log("result.faceA");
367
+ // result.faceB.log("result.faceB");
368
+
369
+ if (swapResults) {
370
+ result.swap();
371
+ }
372
+
373
+ // console.log("hit: triangle collider hit");
374
+
375
+ // add the hit
376
+ // this.world.debug && console.log("triangle hit", triangleId);
377
+ collector.addHit(result);
378
+ }
379
+ }