@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,30 @@
1
+ import { Vec3 } from "../../math/vec3";
2
+ import { BarycentricCoordinatesResult, ClosestPointResult } from "./closestPoints";
3
+ import { computeBarycentricCoordinates2d } from "./computeBarycentricCoordinates2d";
4
+
5
+ const barycentric = /*@__PURE__*/ BarycentricCoordinatesResult.create();
6
+
7
+ export function computeClosestPointOnLine(
8
+ outClosestPoints: ClosestPointResult,
9
+ a: Vec3,
10
+ b: Vec3,
11
+ squaredTolerance: number = 1e-10
12
+ ) {
13
+ computeBarycentricCoordinates2d(barycentric, a, b, squaredTolerance);
14
+
15
+ if (barycentric.v <= 0.0) {
16
+ // a is closest point
17
+ outClosestPoints.point.copy(a);
18
+ outClosestPoints.pointSet = 0b0001;
19
+ } else if (barycentric.u <= 0.0) {
20
+ // b is closest point
21
+ outClosestPoints.point.copy(b);
22
+ outClosestPoints.pointSet = 0b0010;
23
+ } else {
24
+ // Closest point lies on line ab
25
+ outClosestPoints.point.zero();
26
+ outClosestPoints.point.addScaled(a, barycentric.u);
27
+ outClosestPoints.point.addScaled(b, barycentric.v);
28
+ outClosestPoints.pointSet = 0b0011;
29
+ }
30
+ }
@@ -0,0 +1,96 @@
1
+ import { Vec3 } from "../../math/vec3";
2
+ import { Vec4 } from "../../math/vec4";
3
+ import { ClosestPointResult } from "./closestPoints";
4
+ import { computeClosestPointOnTriangle } from "./computeClosestPointOnTriangle";
5
+ import { isOriginOutsideOfTrianglePlanes } from "./isOriginOutsideOfTrianglePlanes";
6
+
7
+ const otherResult = /*@__PURE__*/ ClosestPointResult.create();
8
+ const originOutOfPlanes = /*@__PURE__*/ Vec4.create();
9
+
10
+ export function computeClosestPointOnTetrahedron(
11
+ result: ClosestPointResult,
12
+ inA: Vec3,
13
+ inB: Vec3,
14
+ inC: Vec3,
15
+ inD: Vec3,
16
+ mustIncludeD: boolean = false,
17
+ tolerance: number = 1e-5
18
+ ): void {
19
+ const squaredTolerance = tolerance * tolerance;
20
+
21
+ // Taken from: Real-Time Collision Detection - Christer Ericson (Section: Closest Point on Tetrahedron to Point)
22
+ // With p = 0
23
+
24
+ // Start out assuming point inside all halfspaces, so closest to itself
25
+ result.pointSet = 0b1111;
26
+ result.point.zero();
27
+
28
+ let bestDistanceSquared = Infinity;
29
+
30
+ // Determine for each of the faces of the tetrahedron if the origin is in front of the plane
31
+ isOriginOutsideOfTrianglePlanes(originOutOfPlanes, inA, inB, inC, inD, tolerance);
32
+
33
+ // If point outside face abc then compute closest point on abc
34
+ if (originOutOfPlanes.x) {
35
+ // OriginOutsideOfPlane(inA, inB, inC, inD)
36
+ if (mustIncludeD) {
37
+ // If the closest point must include D then ABC cannot be closest but the closest point
38
+ // cannot be an interior point either so we return A as closest point
39
+ result.pointSet = 0b0001;
40
+ result.point.copy(inA);
41
+ } else {
42
+ // Test the face normally
43
+ computeClosestPointOnTriangle(result, inA, inB, inC, false, squaredTolerance);
44
+ }
45
+ bestDistanceSquared = result.point.squaredLength();
46
+ }
47
+
48
+ // Repeat test for face acd
49
+ if (originOutOfPlanes.y) {
50
+ // OriginOutsideOfPlane(inA, inC, inD, inB)
51
+ otherResult.pointSet = 0;
52
+ otherResult.point.zero();
53
+ computeClosestPointOnTriangle(otherResult, inA, inC, inD, mustIncludeD, squaredTolerance);
54
+ const distanceSquared = otherResult.point.squaredLength();
55
+ if (distanceSquared < bestDistanceSquared) {
56
+ bestDistanceSquared = distanceSquared;
57
+ result.point.copy(otherResult.point);
58
+ result.pointSet = (otherResult.pointSet & 0b0001) + ((otherResult.pointSet & 0b0110) << 1);
59
+ }
60
+ }
61
+
62
+ // Repeat test for face adb
63
+ if (originOutOfPlanes.z) {
64
+ // OriginOutsideOfPlane(inA, inD, inB, inC)
65
+
66
+ // Keep original vertex order, it doesn't matter if the triangle is facing inward or outward
67
+ // and it improves consistency for GJK which will always add a new vertex D and keep the closest
68
+ // feature from the previous iteration in ABC
69
+ otherResult.pointSet = 0;
70
+ otherResult.point.zero();
71
+ computeClosestPointOnTriangle(otherResult, inA, inB, inD, mustIncludeD, squaredTolerance);
72
+ const distanceSquared = otherResult.point.squaredLength();
73
+ if (distanceSquared < bestDistanceSquared) {
74
+ bestDistanceSquared = distanceSquared;
75
+ result.point.copy(otherResult.point);
76
+ result.pointSet = (otherResult.pointSet & 0b0011) + ((otherResult.pointSet & 0b0100) << 1);
77
+ }
78
+ }
79
+
80
+ // Repeat test for face bdc
81
+ if (originOutOfPlanes.w) {
82
+ // OriginOutsideOfPlane(inB, inD, inC, inA)
83
+
84
+ // Keep original vertex order, it doesn't matter if the triangle is facing inward or outward
85
+ // and it improves consistency for GJK which will always add a new vertex D and keep the closest
86
+ // feature from the previous iteration in ABC
87
+ otherResult.pointSet = 0;
88
+ otherResult.point.zero();
89
+ computeClosestPointOnTriangle(otherResult, inB, inC, inD, mustIncludeD, squaredTolerance);
90
+ const distanceSquared = otherResult.point.squaredLength();
91
+ if (distanceSquared < bestDistanceSquared) {
92
+ result.point.copy(otherResult.point);
93
+ result.pointSet = otherResult.pointSet << 1;
94
+ }
95
+ }
96
+ }
@@ -0,0 +1,195 @@
1
+ import { clamp } from "../../math/scalar";
2
+ import { Vec3 } from "../../math/vec3";
3
+ import { ClosestPointResult } from "./closestPoints";
4
+
5
+ const ac = /*@__PURE__*/ Vec3.create();
6
+ const ab = /*@__PURE__*/ Vec3.create();
7
+ const bc = /*@__PURE__*/ Vec3.create();
8
+ const a = /*@__PURE__*/ Vec3.create();
9
+ const c = /*@__PURE__*/ Vec3.create();
10
+ const n = /*@__PURE__*/ Vec3.create();
11
+ const ap = /*@__PURE__*/ Vec3.create();
12
+ const bp = /*@__PURE__*/ Vec3.create();
13
+ const cp = /*@__PURE__*/ Vec3.create();
14
+ const closestPoint = /*@__PURE__*/ Vec3.create();
15
+ const q = /*@__PURE__*/ Vec3.create();
16
+ const tempVector = /*@__PURE__*/ Vec3.create();
17
+
18
+ export function computeClosestPointOnTriangle(
19
+ outClosestPoint: ClosestPointResult,
20
+ inA: Vec3,
21
+ inB: Vec3,
22
+ inC: Vec3,
23
+ mustIncludeC: boolean = false,
24
+ squaredTolerance: number = 1e-10
25
+ ): void {
26
+ // Taken from: Real-Time Collision Detection - Christer Ericson (Section: Closest Point on Triangle to Point)
27
+ // With p = 0
28
+
29
+ // The most accurate normal is calculated by using the two shortest edges
30
+ // See: https://box2d.org/posts/2014/01/troublesome-triangle/
31
+ // The difference in normals is most pronounced when one edge is much smaller than the others (in which case the other 2 must have roughly the same length).
32
+ // Therefore we can suffice by just picking the shortest from 2 edges and use that with the 3rd edge to calculate the normal.
33
+ // We first check which of the edges is shorter and if bc is shorter than ac then we swap a with c to a is always on the shortest edge
34
+
35
+ ac.subtractVectors(inC, inA);
36
+ bc.subtractVectors(inC, inB);
37
+ const swapAC = bc.dot(bc) < ac.dot(ac);
38
+
39
+ a.copy(swapAC ? inC : inA);
40
+ c.copy(swapAC ? inA : inC);
41
+
42
+ // Calculate normal
43
+ ab.subtractVectors(inB, a);
44
+ ac.subtractVectors(c, a);
45
+ n.crossVectors(ab, ac);
46
+ const normalLengthSquared = n.squaredLength();
47
+
48
+ // Check degenerate
49
+ if (normalLengthSquared < 1.0e-11) {
50
+ // Square(FLT_EPSILON) was too small and caused numerical problems, see test case TestCollideParallelTriangleVsCapsule
51
+
52
+ // Degenerate, fallback to vertices and edges
53
+
54
+ // Start with vertex C being the closest
55
+ let closestSet = 0b0100;
56
+ closestPoint.copy(inC);
57
+ let bestDistanceSquared = inC.squaredLength();
58
+
59
+ // If the closest point must include C then A or B cannot be closest
60
+ // Note that we test vertices first because we want to prefer a closest vertex over a closest edge (this results in an outSet with fewer bits set)
61
+ if (!mustIncludeC) {
62
+ // Try vertex A
63
+ const aLengthSquared = inA.squaredLength();
64
+ if (aLengthSquared < bestDistanceSquared) {
65
+ closestSet = 0b0001;
66
+ closestPoint.copy(inA);
67
+ bestDistanceSquared = aLengthSquared;
68
+ }
69
+
70
+ // Try vertex B
71
+ const bLengthSquared = inB.squaredLength();
72
+ if (bLengthSquared < bestDistanceSquared) {
73
+ closestSet = 0b0010;
74
+ closestPoint.copy(inB);
75
+ bestDistanceSquared = bLengthSquared;
76
+ }
77
+ }
78
+
79
+ // Edge AC
80
+ const acLengthSquared = ac.squaredLength();
81
+ if (acLengthSquared > squaredTolerance) {
82
+ const v = clamp(-a.dot(ac) / acLengthSquared, 0.0, 1.0);
83
+ q.addScaledToVector(a, ac, v);
84
+ const distanceSquared = q.squaredLength();
85
+ if (distanceSquared < bestDistanceSquared) {
86
+ closestSet = 0b0101;
87
+ closestPoint.copy(q);
88
+ bestDistanceSquared = distanceSquared;
89
+ }
90
+ }
91
+
92
+ // Edge BC
93
+ bc.subtractVectors(inC, inB);
94
+ const bcLengthSquared = bc.squaredLength();
95
+ if (bcLengthSquared > squaredTolerance) {
96
+ const v = clamp(-inB.dot(bc) / bcLengthSquared, 0.0, 1.0);
97
+ q.addScaledToVector(inB, bc, v);
98
+ const distanceSquared = q.squaredLength();
99
+ if (distanceSquared < bestDistanceSquared) {
100
+ closestSet = 0b0110;
101
+ closestPoint.copy(q);
102
+ bestDistanceSquared = distanceSquared;
103
+ }
104
+ }
105
+
106
+ // If the closest point must include C then AB cannot be closest
107
+ if (!mustIncludeC) {
108
+ // Edge AB
109
+ ab.subtractVectors(inB, inA);
110
+ const abLengthSquared = ab.squaredLength();
111
+ if (abLengthSquared > squaredTolerance) {
112
+ const v = clamp(-inA.dot(ab) / abLengthSquared, 0.0, 1.0);
113
+ q.addScaledToVector(inA, ab, v);
114
+ const distanceSquared = q.squaredLength();
115
+ if (distanceSquared < bestDistanceSquared) {
116
+ closestSet = 0b0011;
117
+ closestPoint.copy(q);
118
+ bestDistanceSquared = distanceSquared;
119
+ }
120
+ }
121
+ }
122
+
123
+ outClosestPoint.pointSet = closestSet;
124
+ outClosestPoint.point.copy(closestPoint);
125
+ return;
126
+ }
127
+
128
+ // Check if P in vertex region outside A
129
+ ap.negateVector(a);
130
+ const d1 = ab.dot(ap);
131
+ const d2 = ac.dot(ap);
132
+ if (d1 <= 0.0 && d2 <= 0.0) {
133
+ outClosestPoint.pointSet = swapAC ? 0b0100 : 0b0001;
134
+ outClosestPoint.point.copy(a); // barycentric coordinates (1,0,0)
135
+ return;
136
+ }
137
+
138
+ // Check if P in vertex region outside B
139
+ bp.negateVector(inB);
140
+ const d3 = ab.dot(bp);
141
+ const d4 = ac.dot(bp);
142
+ if (d3 >= 0.0 && d4 <= d3) {
143
+ outClosestPoint.pointSet = 0b0010;
144
+ outClosestPoint.point.copy(inB); // barycentric coordinates (0,1,0)
145
+ return;
146
+ }
147
+
148
+ // Check if P in edge region of AB, if so return projection of P onto AB
149
+ if (d1 * d4 <= d3 * d2 && d1 >= 0.0 && d3 <= 0.0) {
150
+ const v = d1 / (d1 - d3);
151
+ outClosestPoint.pointSet = swapAC ? 0b0110 : 0b0011;
152
+ outClosestPoint.point.addScaledToVector(a, ab, v); // barycentric coordinates (1-v,v,0)
153
+ return;
154
+ }
155
+
156
+ // Check if P in vertex region outside C
157
+ cp.negateVector(c);
158
+ const d5 = ab.dot(cp);
159
+ const d6 = ac.dot(cp);
160
+ if (d6 >= 0.0 && d5 <= d6) {
161
+ outClosestPoint.pointSet = swapAC ? 0b0001 : 0b0100;
162
+ outClosestPoint.point.copy(c); // barycentric coordinates (0,0,1)
163
+ return;
164
+ }
165
+
166
+ // Check if P in edge region of AC, if so return projection of P onto AC
167
+ if (d5 * d2 <= d1 * d6 && d2 >= 0.0 && d6 <= 0.0) {
168
+ const w = d2 / (d2 - d6);
169
+ outClosestPoint.pointSet = 0b0101;
170
+ outClosestPoint.point.addScaledToVector(a, ac, w); // barycentric coordinates (1-w,0,w)
171
+ return;
172
+ }
173
+
174
+ // Check if P in edge region of BC, if so return projection of P onto BC
175
+ const diff_d4_d3 = d4 - d3;
176
+ const diff_d5_d6 = d5 - d6;
177
+ if (d3 * d6 <= d5 * d4 && diff_d4_d3 >= 0.0 && diff_d5_d6 >= 0.0) {
178
+ const w = diff_d4_d3 / (diff_d4_d3 + diff_d5_d6);
179
+ outClosestPoint.pointSet = swapAC ? 0b0011 : 0b0110;
180
+ tempVector.subtractVectors(c, inB);
181
+ outClosestPoint.point.addScaledToVector(inB, tempVector, w); // barycentric coordinates (0,1-w,w)
182
+ return;
183
+ }
184
+
185
+ // P inside face region.
186
+ // Here we deviate from Christer Ericson's article to improve accuracy.
187
+ // Determine distance between triangle and origin: distance = (centroid - origin) . normal / |normal|
188
+ // Closest point to origin is then: distance . normal / |normal|
189
+ // Note that this way of calculating the closest point is much more accurate than first calculating barycentric coordinates
190
+ // and then calculating the closest point based on those coordinates.
191
+ outClosestPoint.pointSet = 0b0111;
192
+ tempVector.addVectors(a, inB);
193
+ tempVector.addVectors(tempVector, c);
194
+ outClosestPoint.point.scaleVector(n, tempVector.dot(n) / (3 * normalLengthSquared));
195
+ }
@@ -0,0 +1,25 @@
1
+ import { Vec3 } from "../../math/vec3";
2
+
3
+ const n = /*@__PURE__*/ Vec3.create();
4
+ const ab = /*@__PURE__*/ Vec3.create();
5
+ const ac = /*@__PURE__*/ Vec3.create();
6
+ const ad = /*@__PURE__*/ Vec3.create();
7
+
8
+ export function isOriginOutsideOfPlane(a: Vec3, b: Vec3, c: Vec3, d: Vec3, tolerance: number = 1e-5): boolean {
9
+ // Taken from: Real-Time Collision Detection - Christer Ericson (Section: Closest Point on Tetrahedron to Point)
10
+ // With p = 0
11
+
12
+ // Test if point p and d lie on opposite sides of plane through abc
13
+ ab.subtractVectors(b, a);
14
+ ac.subtractVectors(c, a);
15
+ ad.subtractVectors(d, a);
16
+
17
+ n.crossVectors(ab, ac);
18
+ const signP = a.dot(n); // [AP AB AC]
19
+ const signD = ad.dot(n); // [AD AB AC]
20
+
21
+ // Points on opposite sides if expression signs are the same
22
+ // Note that we left out the minus sign in signp so we need to check > 0 instead of < 0 as in Christer's book
23
+ // We compare against a small negative value to allow for a little bit of slop in the calculations
24
+ return signP * signD > -tolerance;
25
+ }
@@ -0,0 +1,72 @@
1
+ import { Vec3 } from "../../math/vec3";
2
+ import { Vec4 } from "../../math/vec4";
3
+
4
+ const ab = /*@__PURE__*/ Vec3.create();
5
+ const ac = /*@__PURE__*/ Vec3.create();
6
+ const ad = /*@__PURE__*/ Vec3.create();
7
+ const bd = /*@__PURE__*/ Vec3.create();
8
+ const bc = /*@__PURE__*/ Vec3.create();
9
+ const ab_cross_ac = /*@__PURE__*/ Vec3.create();
10
+ const ac_cross_ad = /*@__PURE__*/ Vec3.create();
11
+ const ad_cross_ab = /*@__PURE__*/ Vec3.create();
12
+ const bd_cross_bc = /*@__PURE__*/ Vec3.create();
13
+ const signP = /*@__PURE__*/ Vec4.create();
14
+ const signD = /*@__PURE__*/ Vec4.create();
15
+
16
+ export function isOriginOutsideOfTrianglePlanes(
17
+ outResult: Vec4,
18
+ a: Vec3,
19
+ b: Vec3,
20
+ c: Vec3,
21
+ d: Vec3,
22
+ tolerance: number = 1e-5
23
+ ): void {
24
+ ab.subtractVectors(b, a);
25
+ ac.subtractVectors(c, a);
26
+ ad.subtractVectors(d, a);
27
+ bd.subtractVectors(d, b);
28
+ bc.subtractVectors(c, b);
29
+
30
+ ab_cross_ac.crossVectors(ab, ac);
31
+ ac_cross_ad.crossVectors(ac, ad);
32
+ ad_cross_ab.crossVectors(ad, ab);
33
+ bd_cross_bc.crossVectors(bd, bc);
34
+
35
+ // For each plane get the side on which the origin is
36
+ signP.x = a.dot(ab_cross_ac); // ABC
37
+ signP.y = a.dot(ac_cross_ad); // ACD
38
+ signP.z = a.dot(ad_cross_ab); // ADB
39
+ signP.w = b.dot(bd_cross_bc); // BDC
40
+
41
+ // For each plane get the side that is outside (determined by the 4th point)
42
+ signD.x = ad.dot(ab_cross_ac); // D
43
+ signD.y = ab.dot(ac_cross_ad); // B
44
+ signD.z = ac.dot(ad_cross_ab); // C
45
+ signD.w = -ab.dot(bd_cross_bc); // A
46
+
47
+ // handle all signD components are positive
48
+ const allPositive = signD.x > 0 && signD.y > 0 && signD.z > 0 && signD.w > 0;
49
+ if (allPositive) {
50
+ outResult.x = signP.x >= -tolerance ? 1 : 0;
51
+ outResult.y = signP.y >= -tolerance ? 1 : 0;
52
+ outResult.z = signP.z >= -tolerance ? 1 : 0;
53
+ outResult.w = signP.w >= -tolerance ? 1 : 0;
54
+ return;
55
+ }
56
+
57
+ // handle all signD components are negative
58
+ const allNegative = signD.x < 0 && signD.y < 0 && signD.z < 0 && signD.w < 0;
59
+ if (allNegative) {
60
+ outResult.x = signP.x <= tolerance ? 1 : 0;
61
+ outResult.y = signP.y <= tolerance ? 1 : 0;
62
+ outResult.z = signP.z <= tolerance ? 1 : 0;
63
+ outResult.w = signP.w <= tolerance ? 1 : 0;
64
+ return;
65
+ }
66
+
67
+ // handle mixed signs, degenerate tetrahedron
68
+ outResult.x = 1;
69
+ outResult.y = 1;
70
+ outResult.z = 1;
71
+ outResult.w = 1;
72
+ }
@@ -0,0 +1,146 @@
1
+ import {
2
+ BooleanType,
3
+ createClass,
4
+ LazyReferenceType,
5
+ MonomorphType,
6
+ NumberType,
7
+ PropertyDefinitionMap,
8
+ PropertyDefinitionReference,
9
+ } from "monomorph";
10
+ import { Vec3 } from "../../math/vec3";
11
+ import { Face } from "../../physics/manifold/Face";
12
+ import { Body } from "../../physics/Body";
13
+
14
+ export const enum CollisionStatus {
15
+ NotColliding,
16
+ Colliding,
17
+ Indeterminate,
18
+ }
19
+
20
+ const collisionResultProps = {
21
+ status: NumberType(0),
22
+ hasContact: BooleanType(false),
23
+ penetration: NumberType(0.0),
24
+ contactPointA: MonomorphType(Vec3),
25
+ contactPointB: MonomorphType(Vec3),
26
+ normalA: MonomorphType(Vec3),
27
+ normalB: MonomorphType(Vec3),
28
+ momentArmA: MonomorphType(Vec3),
29
+ momentArmB: MonomorphType(Vec3),
30
+ surfaceNormalA: MonomorphType(Vec3),
31
+ surfaceNormalB: MonomorphType(Vec3),
32
+ bodyA: LazyReferenceType((() => Body) as () => never) as PropertyDefinitionReference<Body | null, true>,
33
+ bodyB: LazyReferenceType((() => Body) as () => never) as PropertyDefinitionReference<Body | null, true>,
34
+ subShapeIdA: NumberType(0),
35
+ subShapeIdB: NumberType(0),
36
+ isBackFace: BooleanType(false),
37
+ faceA: MonomorphType(Face),
38
+ faceB: MonomorphType(Face),
39
+ } as const satisfies PropertyDefinitionMap;
40
+
41
+ export class CollisionResult extends createClass<CollisionResult, typeof collisionResultProps>(collisionResultProps) {
42
+ reset() {
43
+ this.status = CollisionStatus.Indeterminate;
44
+ this.hasContact = false;
45
+ this.penetration = 0;
46
+ this.bodyA = null;
47
+ this.bodyB = null;
48
+ this.subShapeIdA = 0;
49
+ this.subShapeIdB = 0;
50
+ this.isBackFace = false;
51
+ this.faceA.clear();
52
+ this.faceB.clear();
53
+ }
54
+
55
+ /**
56
+ * swaps the A and B data
57
+ */
58
+ swap() {
59
+ this.contactPointA.swap(this.contactPointB);
60
+ this.normalA.swap(this.normalB);
61
+ this.momentArmA.swap(this.momentArmB);
62
+ this.surfaceNormalA.swap(this.surfaceNormalB);
63
+
64
+ const tempBody = this.bodyA;
65
+ this.bodyA = this.bodyB;
66
+ this.bodyB = tempBody;
67
+
68
+ const tempSubShapeId = this.subShapeIdA;
69
+ this.subShapeIdA = this.subShapeIdB;
70
+ this.subShapeIdB = tempSubShapeId;
71
+
72
+ // this.faceA.swap(this.faceB);
73
+ const tempFace = this.faceA;
74
+ this.faceA = this.faceB;
75
+ this.faceB = tempFace;
76
+ }
77
+ }
78
+
79
+ export class CollisionCollector {
80
+ static initialEarlyOutFraction: number = Infinity;
81
+ static shouldEarlyOutFraction: number = -Infinity;
82
+ earlyOutFraction: number = CollisionCollector.initialEarlyOutFraction;
83
+ body2: Body | null = null;
84
+
85
+ getEarlyOutFraction(): number {
86
+ return CollisionCollector.initialEarlyOutFraction;
87
+ // return this.earlyOutFraction;
88
+ // return CollisionCollector.shouldEarlyOutFraction;
89
+ }
90
+
91
+ getPositiveEarlyOutFraction(): number {
92
+ return Math.max(Number.MIN_VALUE, this.earlyOutFraction);
93
+ }
94
+
95
+ reset() {
96
+ this.earlyOutFraction = CollisionCollector.initialEarlyOutFraction;
97
+ this.body2 = null;
98
+
99
+ return this;
100
+ }
101
+
102
+ addHit(result: CollisionResult): void {}
103
+
104
+ addMiss(): void {}
105
+ }
106
+
107
+ export const enum CollectFacesMode {
108
+ CollectFaces,
109
+ NoFaces,
110
+ }
111
+
112
+ export const enum ActiveEdgeMode {
113
+ CollideOnlyWithActive,
114
+ CollideWithAll,
115
+ }
116
+
117
+ export const enum BackFaceMode {
118
+ IgnoreBackFaces,
119
+ CollideWithBackFaces,
120
+ }
121
+
122
+ export interface CollisionSettings {
123
+ maxSeparation: number;
124
+ collisionTolerance: number;
125
+ activeEdgeMode: ActiveEdgeMode;
126
+ collectFacesMode: CollectFacesMode;
127
+ penetrationTolerance: number;
128
+ activeEdgeMovementDirectionX: number;
129
+ activeEdgeMovementDirectionY: number;
130
+ activeEdgeMovementDirectionZ: number;
131
+ backFaceMode: BackFaceMode;
132
+ }
133
+
134
+ export function createDefaultCollisionSettings() {
135
+ return {
136
+ maxSeparation: 0.0,
137
+ collisionTolerance: 1e-4,
138
+ activeEdgeMode: ActiveEdgeMode.CollideOnlyWithActive,
139
+ collectFacesMode: CollectFacesMode.NoFaces,
140
+ penetrationTolerance: 1e-4,
141
+ activeEdgeMovementDirectionX: 0.0,
142
+ activeEdgeMovementDirectionY: 0.0,
143
+ activeEdgeMovementDirectionZ: 0.0,
144
+ backFaceMode: BackFaceMode.IgnoreBackFaces,
145
+ } satisfies CollisionSettings;
146
+ }
@@ -0,0 +1,60 @@
1
+ import { Isometry } from "../../math/isometry";
2
+ import { Vec3 } from "../../math/vec3";
3
+ import { PenetrationDepthModule } from "../gjk/PenetrationDepthModule";
4
+ import { CollisionCollector, CollisionSettings } from "./collide";
5
+ import { CompoundShape } from "../../shape/CompoundShape";
6
+ import { collideConvexVsConvex } from "./collideConvexVsConvex";
7
+ import { Body } from "../../physics/Body";
8
+ import { ConvexShape } from "../../shape/Shape";
9
+
10
+ const transformSubShapeToA = /*@__PURE__*/ Isometry.create();
11
+ const transformSubShapeAToWorld = /*@__PURE__*/ Isometry.create();
12
+
13
+ const transformSubShapeToB = /*@__PURE__*/ Isometry.create();
14
+ const transformSubShapeBToWorld = /*@__PURE__*/ Isometry.create();
15
+
16
+ // the results are in world space
17
+ export function collideCompoundVsCompound(
18
+ penetrationDepthModule: PenetrationDepthModule,
19
+ collector: CollisionCollector,
20
+ shapeA: CompoundShape,
21
+ shapeB: CompoundShape,
22
+ isometryA: Isometry,
23
+ isometryB: Isometry,
24
+ settings: CollisionSettings,
25
+ bodyA: Body | null,
26
+ bodyB: Body | null,
27
+ initialDirection?: Vec3,
28
+ subShapeIdA: number = 0,
29
+ subShapeIdB: number = 0
30
+ ): void {
31
+ let indexA = 0;
32
+ for (const { shape: subShapeA, transform: transformA } of shapeA.shapes) {
33
+ transformSubShapeToA.fromRotationAndTranslation(transformA.rotation, transformA.position);
34
+ transformSubShapeAToWorld.matrix.multiplyMatrices(isometryA.matrix, transformSubShapeToA.matrix);
35
+
36
+ let indexB = 0;
37
+ for (const { shape: subShapeB, transform: transformB } of shapeB.shapes) {
38
+ transformSubShapeToB.fromRotationAndTranslation(transformB.rotation, transformB.position);
39
+ transformSubShapeBToWorld.matrix.multiplyMatrices(isometryB.matrix, transformSubShapeToB.matrix);
40
+
41
+ collideConvexVsConvex(
42
+ penetrationDepthModule,
43
+ collector,
44
+ subShapeA as ConvexShape,
45
+ subShapeB as ConvexShape,
46
+ transformSubShapeAToWorld,
47
+ transformSubShapeBToWorld,
48
+ settings,
49
+ bodyA,
50
+ bodyB,
51
+ initialDirection,
52
+ indexA,
53
+ indexB
54
+ );
55
+
56
+ indexB++;
57
+ }
58
+ indexA++;
59
+ }
60
+ }
@@ -0,0 +1,59 @@
1
+ import { Isometry } from "../../math/isometry";
2
+ import { Vec3 } from "../../math/vec3";
3
+ import { PenetrationDepthModule } from "../gjk/PenetrationDepthModule";
4
+ import { CollisionCollector, CollisionSettings } from "./collide";
5
+ import { ConvexWithcomputeSupportShape } from "../../shape/Convex";
6
+ import { CompoundShape } from "../../shape/CompoundShape";
7
+ import { collideConvexVsConvex } from "./collideConvexVsConvex";
8
+ import { Body } from "../../physics/Body";
9
+ import { ConvexShape } from "../../shape/Shape";
10
+ import { BasicTransform } from "../../math/BasicTransform";
11
+
12
+ const transformSubShapeToA = /*@__PURE__*/ Isometry.create();
13
+ const transformSubShapeToWorld = /*@__PURE__*/ Isometry.create();
14
+
15
+ // the results are in world space
16
+ export function collideCompoundVsConvex(
17
+ penetrationDepthModule: PenetrationDepthModule,
18
+ collector: CollisionCollector,
19
+ shapeA: CompoundShape,
20
+ shapeB: ConvexWithcomputeSupportShape,
21
+ isometryA: Isometry,
22
+ isometryB: Isometry,
23
+ settings: CollisionSettings,
24
+ bodyA: Body | null,
25
+ bodyB: Body | null,
26
+ initialDirection?: Vec3,
27
+ subShapeIdA: number = 0,
28
+ subShapeIdB: number = 0
29
+ ): void {
30
+ let index = 0;
31
+ for (const shapeObj of shapeA.shapes) {
32
+ const shape = shapeObj.shape as ConvexShape;
33
+ const transform = shapeObj.transform as BasicTransform;
34
+ const translation = transform.position;
35
+ const rotation = transform.rotation;
36
+
37
+ // set the isometries
38
+ transformSubShapeToA.fromRotationAndTranslation(rotation, translation);
39
+ transformSubShapeToWorld.matrix.multiplyMatrices(isometryA.matrix, transformSubShapeToA.matrix);
40
+
41
+ // collide the shapes. TODO: this should be convex vs convex for now, but may be expanded in the future
42
+ collideConvexVsConvex(
43
+ penetrationDepthModule,
44
+ collector,
45
+ shape,
46
+ shapeB,
47
+ transformSubShapeToWorld,
48
+ isometryB,
49
+ settings,
50
+ bodyA,
51
+ bodyB,
52
+ initialDirection,
53
+ index,
54
+ 0
55
+ );
56
+
57
+ index++;
58
+ }
59
+ }