@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.
- package/LICENSE +21 -0
- package/README.md +30 -0
- package/package.json +32 -0
- package/src/builders/ConvexHullBuilder.ts +437 -0
- package/src/builders/ConvexHullBuilder2d.ts +344 -0
- package/src/builders/ConvexHullBuilder3d.ts +1689 -0
- package/src/builders/HeightMapBuilder.ts +414 -0
- package/src/builders/TriangleMeshBuilder.ts +92 -0
- package/src/collision/CastShapesModule.ts +184 -0
- package/src/collision/CollideShapesModule.ts +152 -0
- package/src/collision/HeightMapCaster.ts +38 -0
- package/src/collision/HeightMapCollider.ts +33 -0
- package/src/collision/TriangleCaster.ts +249 -0
- package/src/collision/TriangleCollider.ts +308 -0
- package/src/collision/TriangleCollider2.ts +379 -0
- package/src/collision/activeEdge.ts +146 -0
- package/src/collision/cast/cast.ts +139 -0
- package/src/collision/cast/castCompoundVsCompound.ts +59 -0
- package/src/collision/cast/castCompoundVsConvex.ts +116 -0
- package/src/collision/cast/castConvexVsCompound.ts +123 -0
- package/src/collision/cast/castConvexVsConvex.ts +213 -0
- package/src/collision/cast/castConvexVsHeightMap.ts +73 -0
- package/src/collision/cast/castConvexVsTriangleMesh.ts +56 -0
- package/src/collision/cast/castRayVsCompound.ts +44 -0
- package/src/collision/cast/castRayVsConvex.ts +45 -0
- package/src/collision/cast/castRayVsHeightMap.ts +58 -0
- package/src/collision/cast/castRayVsTriangleMesh.ts +58 -0
- package/src/collision/closestPoints/closestPoints.ts +23 -0
- package/src/collision/closestPoints/computeBarycentricCoordinates2d.ts +32 -0
- package/src/collision/closestPoints/computeBarycentricCoordinates3d.ts +81 -0
- package/src/collision/closestPoints/computeClosestPointOnLine.ts +30 -0
- package/src/collision/closestPoints/computeClosestPointOnTetrahedron.ts +96 -0
- package/src/collision/closestPoints/computeClosestPointOnTriangle.ts +195 -0
- package/src/collision/closestPoints/isOriginOutsideOfPlane.ts +25 -0
- package/src/collision/closestPoints/isOriginOutsideOfTrianglePlanes.ts +72 -0
- package/src/collision/collide/collide.ts +146 -0
- package/src/collision/collide/collideCompoundVsCompound.ts +60 -0
- package/src/collision/collide/collideCompoundVsConvex.ts +59 -0
- package/src/collision/collide/collideCompoundVsHeightMap.ts +73 -0
- package/src/collision/collide/collideCompoundVsTriangleMesh.ts +56 -0
- package/src/collision/collide/collideConvexVsCompound.ts +57 -0
- package/src/collision/collide/collideConvexVsConvex.ts +225 -0
- package/src/collision/collide/collideConvexVsConvexImp.ts +236 -0
- package/src/collision/collide/collideConvexVsHeightMap.ts +53 -0
- package/src/collision/collide/collideConvexVsTriangleMesh.ts +58 -0
- package/src/collision/collide/collideHeightMapVsCompound.ts +69 -0
- package/src/collision/collide/collideHeightMapVsConvex.ts +53 -0
- package/src/collision/collide/collideSphereVsSphere.ts +81 -0
- package/src/collision/collide/collideTriangleMeshVsCompound.ts +58 -0
- package/src/collision/collide/collideTriangleMeshVsConvex.ts +58 -0
- package/src/collision/epa/EpaConvexHullBuilder.ts +397 -0
- package/src/collision/epa/StaticArray.ts +154 -0
- package/src/collision/epa/TriangleFactory.ts +32 -0
- package/src/collision/epa/arrays.ts +99 -0
- package/src/collision/epa/binaryHeap.ts +82 -0
- package/src/collision/epa/structs.ts +227 -0
- package/src/collision/gjk/GjkModule.ts +864 -0
- package/src/collision/gjk/PenetrationDepthModule.ts +493 -0
- package/src/collision/gjk/SupportPoints.ts +50 -0
- package/src/collision/imp/MinkowskiDifference.ts +36 -0
- package/src/collision/imp/computeExploredDistanceLowerUpperBound.ts +40 -0
- package/src/collision/imp/finalizeImpResult.ts +69 -0
- package/src/collision/imp/findContactImp.ts +196 -0
- package/src/collision/imp/imp.ts +28 -0
- package/src/collision/imp/incrementalMinimumDistanceExploreDirection.ts +207 -0
- package/src/collision/mpr/findPortal.ts +152 -0
- package/src/collision/mpr/mpr.ts +29 -0
- package/src/collision/mpr/updatePortal.ts +52 -0
- package/src/constraints/BaseConstraint.ts +50 -0
- package/src/constraints/ConstraintOptions.ts +22 -0
- package/src/constraints/ConstraintSolver.ts +119 -0
- package/src/constraints/DistanceConstraint.ts +229 -0
- package/src/constraints/FixedConstraint.ts +203 -0
- package/src/constraints/HingeConstraint.ts +460 -0
- package/src/constraints/PointConstraint.ts +108 -0
- package/src/constraints/components/AngleComponent.ts +226 -0
- package/src/constraints/components/AxisComponent.ts +263 -0
- package/src/constraints/components/HingeComponent.ts +215 -0
- package/src/constraints/components/Motor.ts +36 -0
- package/src/constraints/components/PointConstraintComponent.ts +179 -0
- package/src/constraints/components/RotationEulerComponent.ts +139 -0
- package/src/constraints/components/Spring.ts +30 -0
- package/src/constraints/components/SpringComponent.ts +71 -0
- package/src/constraints/types.ts +6 -0
- package/src/helpers.ts +147 -0
- package/src/index.ts +50 -0
- package/src/math/BasicTransform.ts +19 -0
- package/src/math/NumberValue.ts +13 -0
- package/src/math/isometry.ts +64 -0
- package/src/math/mat3.ts +529 -0
- package/src/math/mat4.ts +588 -0
- package/src/math/quat.ts +193 -0
- package/src/math/scalar.ts +81 -0
- package/src/math/tensor.ts +17 -0
- package/src/math/vec3.ts +589 -0
- package/src/math/vec4.ts +10 -0
- package/src/physics/Body.ts +581 -0
- package/src/physics/CollisionFilter.ts +52 -0
- package/src/physics/SleepModule.ts +163 -0
- package/src/physics/broadphase/BodyPairsModule.ts +363 -0
- package/src/physics/broadphase/BvhModule.ts +237 -0
- package/src/physics/broadphase/BvhTree.ts +803 -0
- package/src/physics/broadphase/ConstraintPairsModule.ts +385 -0
- package/src/physics/broadphase/TriangleMeshBvhTree.ts +379 -0
- package/src/physics/manifold/ContactManifold.ts +227 -0
- package/src/physics/manifold/ContactManifoldModule.ts +623 -0
- package/src/physics/manifold/Face.ts +119 -0
- package/src/physics/manifold/ManifoldCache.ts +116 -0
- package/src/physics/manifold/clipping/clipPolyVsEdge.ts +131 -0
- package/src/physics/manifold/clipping/clipPolyVsPlane.ts +73 -0
- package/src/physics/manifold/clipping/clipPolyVsPoly.ts +72 -0
- package/src/physics/narrowphase/CollideBodiesModule.ts +755 -0
- package/src/physics/solver/ContactConstraintModule.ts +659 -0
- package/src/physics/solver/ManifoldConstraint.ts +420 -0
- package/src/physics/solver/estimateCollisionResponse.ts +146 -0
- package/src/shape/Aabb.ts +400 -0
- package/src/shape/Box.ts +231 -0
- package/src/shape/Capsule.ts +332 -0
- package/src/shape/CompoundShape.ts +288 -0
- package/src/shape/Convex.ts +130 -0
- package/src/shape/ConvexHull.ts +423 -0
- package/src/shape/Cylinder.ts +313 -0
- package/src/shape/HeightMap.ts +511 -0
- package/src/shape/Line.ts +14 -0
- package/src/shape/Plane.ts +116 -0
- package/src/shape/Ray.ts +81 -0
- package/src/shape/Segment.ts +25 -0
- package/src/shape/Shape.ts +77 -0
- package/src/shape/Sphere.ts +181 -0
- package/src/shape/TransformedShape.ts +51 -0
- package/src/shape/Triangle.ts +122 -0
- package/src/shape/TriangleMesh.ts +186 -0
- package/src/types.ts +1 -0
- package/src/world.ts +1335 -0
- package/tests/BodyPairsModule.test.ts +71 -0
- package/tests/BvhTree.test.ts +406 -0
- package/tests/test.md +642 -0
- package/tests/vec3.test.ts +12 -0
- package/tsconfig.json +20 -0
- package/vite.config.js +40 -0
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
import { createClass, MonomorphType, PropertyDefinitionMap } from "monomorph";
|
|
2
|
+
import { Vec3 } from "../math/vec3";
|
|
3
|
+
import { Face } from "../physics/manifold/Face";
|
|
4
|
+
import { Isometry } from "../math/isometry";
|
|
5
|
+
import { Segment } from "./Segment";
|
|
6
|
+
import { Ray } from "./Ray";
|
|
7
|
+
import { Quat } from "../math/quat";
|
|
8
|
+
|
|
9
|
+
const epsilon = 1e-7;
|
|
10
|
+
const newMin = /*@__PURE__*/ Vec3.create();
|
|
11
|
+
const newMax = /*@__PURE__*/ Vec3.create();
|
|
12
|
+
const col = /*@__PURE__*/ Vec3.create();
|
|
13
|
+
const a = /*@__PURE__*/ Vec3.create();
|
|
14
|
+
const b = /*@__PURE__*/ Vec3.create();
|
|
15
|
+
|
|
16
|
+
interface Vec3Like {
|
|
17
|
+
x: number;
|
|
18
|
+
y: number;
|
|
19
|
+
z: number;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const aabbProps = {
|
|
23
|
+
min: MonomorphType(Vec3),
|
|
24
|
+
max: MonomorphType(Vec3),
|
|
25
|
+
centroid: MonomorphType(Vec3, undefined, true),
|
|
26
|
+
} as const satisfies PropertyDefinitionMap;
|
|
27
|
+
|
|
28
|
+
export class Aabb extends createClass<Aabb, typeof aabbProps>(aabbProps) {
|
|
29
|
+
computeSupport(out: Vec3, direction: Vec3) {
|
|
30
|
+
out.x = direction.x < 0 ? this.min.x : this.max.x;
|
|
31
|
+
out.y = direction.y < 0 ? this.min.y : this.max.y;
|
|
32
|
+
out.z = direction.z < 0 ? this.min.z : this.max.z;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
computeSupportingFace(out: Face, inDirection: Vec3): void {
|
|
36
|
+
const absX = Math.abs(inDirection.x);
|
|
37
|
+
const absY = Math.abs(inDirection.y);
|
|
38
|
+
const absZ = Math.abs(inDirection.z);
|
|
39
|
+
|
|
40
|
+
const axis = absX > absY ? (absX > absZ ? 0 : 2) : absY > absZ ? 1 : 2;
|
|
41
|
+
const component = inDirection.getComponentAtIndex(axis);
|
|
42
|
+
if (component < 0) {
|
|
43
|
+
switch (axis) {
|
|
44
|
+
case 0: {
|
|
45
|
+
out.pushVertexValues(this.max.x, this.min.y, this.min.z);
|
|
46
|
+
out.pushVertexValues(this.max.x, this.max.y, this.min.z);
|
|
47
|
+
out.pushVertexValues(this.max.x, this.max.y, this.max.z);
|
|
48
|
+
out.pushVertexValues(this.max.x, this.min.y, this.max.z);
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
case 1: {
|
|
53
|
+
out.pushVertexValues(this.min.x, this.max.y, this.min.z);
|
|
54
|
+
out.pushVertexValues(this.min.x, this.max.y, this.max.z);
|
|
55
|
+
out.pushVertexValues(this.max.x, this.max.y, this.max.z);
|
|
56
|
+
out.pushVertexValues(this.max.x, this.max.y, this.min.z);
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
case 2: {
|
|
61
|
+
out.pushVertexValues(this.min.x, this.min.y, this.max.z);
|
|
62
|
+
out.pushVertexValues(this.max.x, this.min.y, this.max.z);
|
|
63
|
+
out.pushVertexValues(this.max.x, this.max.y, this.max.z);
|
|
64
|
+
out.pushVertexValues(this.min.x, this.max.y, this.max.z);
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
} else {
|
|
69
|
+
switch (axis) {
|
|
70
|
+
case 0: {
|
|
71
|
+
out.pushVertexValues(this.min.x, this.min.y, this.min.z);
|
|
72
|
+
out.pushVertexValues(this.min.x, this.min.y, this.max.z);
|
|
73
|
+
out.pushVertexValues(this.min.x, this.max.y, this.max.z);
|
|
74
|
+
out.pushVertexValues(this.min.x, this.max.y, this.min.z);
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
case 1: {
|
|
79
|
+
out.pushVertexValues(this.min.x, this.min.y, this.min.z);
|
|
80
|
+
out.pushVertexValues(this.max.x, this.min.y, this.min.z);
|
|
81
|
+
out.pushVertexValues(this.max.x, this.min.y, this.max.z);
|
|
82
|
+
out.pushVertexValues(this.min.x, this.min.y, this.max.z);
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
case 2: {
|
|
87
|
+
out.pushVertexValues(this.min.x, this.min.y, this.min.z);
|
|
88
|
+
out.pushVertexValues(this.min.x, this.max.y, this.min.z);
|
|
89
|
+
out.pushVertexValues(this.max.x, this.max.y, this.min.z);
|
|
90
|
+
out.pushVertexValues(this.max.x, this.min.y, this.min.z);
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
translate(translation: Vec3): void {
|
|
98
|
+
this.min.addVector(translation);
|
|
99
|
+
this.max.addVector(translation);
|
|
100
|
+
this.computeCentroid(this.centroid);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
translateAabb(aabb: Aabb, translation: Vec3): Aabb {
|
|
104
|
+
this.min.addVectors(aabb.min, translation);
|
|
105
|
+
this.max.addVectors(aabb.max, translation);
|
|
106
|
+
this.computeCentroid(this.centroid);
|
|
107
|
+
return this;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
transform(transform: Isometry) {
|
|
111
|
+
// start with the translation of the matrix
|
|
112
|
+
transform.matrix.getTranslation(newMin);
|
|
113
|
+
transform.matrix.getTranslation(newMax);
|
|
114
|
+
|
|
115
|
+
// mow find the extreme points by considering the product of the min and max with each column of inMatrix
|
|
116
|
+
for (let c = 0; c < 3; ++c) {
|
|
117
|
+
transform.matrix.getColumn3(col, c);
|
|
118
|
+
|
|
119
|
+
const minComponent = this.min.getComponentAtIndex(c);
|
|
120
|
+
const maxComponent = this.max.getComponentAtIndex(c);
|
|
121
|
+
|
|
122
|
+
a.scaleVector(col, minComponent);
|
|
123
|
+
b.scaleVector(col, maxComponent);
|
|
124
|
+
|
|
125
|
+
newMin.x += Math.min(a.x, b.x);
|
|
126
|
+
newMin.y += Math.min(a.y, b.y);
|
|
127
|
+
newMin.z += Math.min(a.z, b.z);
|
|
128
|
+
|
|
129
|
+
newMax.x += Math.max(a.x, b.x);
|
|
130
|
+
newMax.y += Math.max(a.y, b.y);
|
|
131
|
+
newMax.z += Math.max(a.z, b.z);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// set the new bounding box
|
|
135
|
+
this.min.copy(newMin);
|
|
136
|
+
this.max.copy(newMax);
|
|
137
|
+
this.computeCentroid(this.centroid);
|
|
138
|
+
|
|
139
|
+
return this;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
transformAabb(aabb: Aabb, transform: Isometry) {
|
|
143
|
+
// start with the translation of the matrix
|
|
144
|
+
transform.matrix.getTranslation(newMin);
|
|
145
|
+
transform.matrix.getTranslation(newMax);
|
|
146
|
+
|
|
147
|
+
// now find the extreme points by considering the product of the min and max with each column of inMatrix
|
|
148
|
+
for (let c = 0; c < 3; ++c) {
|
|
149
|
+
transform.matrix.getColumn3(col, c);
|
|
150
|
+
|
|
151
|
+
const minComponent = aabb.min.getComponentAtIndex(c);
|
|
152
|
+
const maxComponent = aabb.max.getComponentAtIndex(c);
|
|
153
|
+
|
|
154
|
+
a.scaleVector(col, minComponent);
|
|
155
|
+
b.scaleVector(col, maxComponent);
|
|
156
|
+
|
|
157
|
+
newMin.x += Math.min(a.x, b.x);
|
|
158
|
+
newMin.y += Math.min(a.y, b.y);
|
|
159
|
+
newMin.z += Math.min(a.z, b.z);
|
|
160
|
+
|
|
161
|
+
newMax.x += Math.max(a.x, b.x);
|
|
162
|
+
newMax.y += Math.max(a.y, b.y);
|
|
163
|
+
newMax.z += Math.max(a.z, b.z);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// set the new bounding box
|
|
167
|
+
this.min.copy(newMin);
|
|
168
|
+
this.max.copy(newMax);
|
|
169
|
+
this.computeCentroid(this.centroid);
|
|
170
|
+
|
|
171
|
+
return this;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
intersectsAabb(aabb: Aabb) {
|
|
175
|
+
return (
|
|
176
|
+
this.min.x <= aabb.max.x &&
|
|
177
|
+
this.max.x >= aabb.min.x &&
|
|
178
|
+
this.min.y <= aabb.max.y &&
|
|
179
|
+
this.max.y >= aabb.min.y &&
|
|
180
|
+
this.min.z <= aabb.max.z &&
|
|
181
|
+
this.max.z >= aabb.min.z
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
unionAabb(aabb: Aabb) {
|
|
186
|
+
this.min.minVectors(this.min, aabb.min);
|
|
187
|
+
this.max.maxVectors(this.max, aabb.max);
|
|
188
|
+
this.computeCentroid(this.centroid);
|
|
189
|
+
return this;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
unionAabbs(boxA: Aabb, boxB: Aabb) {
|
|
193
|
+
this.min.minVectors(boxA.min, boxB.min);
|
|
194
|
+
this.max.maxVectors(boxA.max, boxB.max);
|
|
195
|
+
this.computeCentroid(this.centroid);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
expand(value: number) {
|
|
199
|
+
this.min.x -= value;
|
|
200
|
+
this.min.y -= value;
|
|
201
|
+
this.min.z -= value;
|
|
202
|
+
this.max.x += value;
|
|
203
|
+
this.max.y += value;
|
|
204
|
+
this.max.z += value;
|
|
205
|
+
this.computeCentroid(this.centroid);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
expandAabb(aabb: Aabb, value: number) {
|
|
209
|
+
this.min.x = aabb.min.x - value;
|
|
210
|
+
this.min.y = aabb.min.y - value;
|
|
211
|
+
this.min.z = aabb.min.z - value;
|
|
212
|
+
this.max.x = aabb.max.x + value;
|
|
213
|
+
this.max.y = aabb.max.y + value;
|
|
214
|
+
this.max.z = aabb.max.z + value;
|
|
215
|
+
this.computeCentroid(this.centroid);
|
|
216
|
+
return this;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
setExtents(min: Vec3Like, max: Vec3Like) {
|
|
220
|
+
this.min.set(min);
|
|
221
|
+
this.max.set(max);
|
|
222
|
+
this.computeCentroid(this.centroid);
|
|
223
|
+
return this;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
computeSurfaceArea() {
|
|
227
|
+
const dx = this.max.x - this.min.x;
|
|
228
|
+
const dy = this.max.y - this.min.y;
|
|
229
|
+
const dz = this.max.z - this.min.z;
|
|
230
|
+
return 2 * (dx * dy + dy * dz + dz * dx);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
computeExtents(out: Vec3) {
|
|
234
|
+
out.x = this.max.x - this.min.x;
|
|
235
|
+
out.y = this.max.y - this.min.y;
|
|
236
|
+
out.z = this.max.z - this.min.z;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
computeLargestAxis() {
|
|
240
|
+
const dx = this.max.x - this.min.x;
|
|
241
|
+
const dy = this.max.y - this.min.y;
|
|
242
|
+
const dz = this.max.z - this.min.z;
|
|
243
|
+
|
|
244
|
+
if (dx >= dy && dx >= dz) {
|
|
245
|
+
return 0;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
if (dy >= dx && dy >= dz) {
|
|
249
|
+
return 1;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return 2;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
computeLargestComponent() {
|
|
256
|
+
const dx = this.max.x - this.min.x;
|
|
257
|
+
const dy = this.max.y - this.min.y;
|
|
258
|
+
const dz = this.max.z - this.min.z;
|
|
259
|
+
|
|
260
|
+
if (dx >= dy && dx >= dz) {
|
|
261
|
+
return "x";
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
if (dy >= dx && dy >= dz) {
|
|
265
|
+
return "y";
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return "z";
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
computeCentroid(out: Vec3) {
|
|
272
|
+
out.x = (this.min.x + this.max.x) * 0.5;
|
|
273
|
+
out.y = (this.min.y + this.max.y) * 0.5;
|
|
274
|
+
out.z = (this.min.z + this.max.z) * 0.5;
|
|
275
|
+
return out;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
computeHalfExtents(out: Vec3) {
|
|
279
|
+
out.subtractVectors(this.max, this.min);
|
|
280
|
+
out.scaleVector(out, 0.5);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
expandToPoint(point: Vec3) {
|
|
284
|
+
this.min.minVector(point);
|
|
285
|
+
this.max.maxVector(point);
|
|
286
|
+
return this;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
enclosesAabb(aabb: Aabb) {
|
|
290
|
+
return (
|
|
291
|
+
this.min.x < aabb.min.x &&
|
|
292
|
+
this.max.x > aabb.max.x &&
|
|
293
|
+
this.min.y < aabb.min.y &&
|
|
294
|
+
this.max.y > aabb.max.y &&
|
|
295
|
+
this.min.z < aabb.min.z &&
|
|
296
|
+
this.max.z > aabb.max.z
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
expandByVector(vector: Vec3) {
|
|
301
|
+
this.min.x -= vector.x;
|
|
302
|
+
this.min.y -= vector.y;
|
|
303
|
+
this.min.z -= vector.z;
|
|
304
|
+
this.max.x += vector.x;
|
|
305
|
+
this.max.y += vector.y;
|
|
306
|
+
this.max.z += vector.z;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
toString() {
|
|
310
|
+
return `Aabb(min: ${[this.min.x, this.min.y, this.min.z].join(", ")}, max: ${[
|
|
311
|
+
this.max.x,
|
|
312
|
+
this.max.y,
|
|
313
|
+
this.max.z,
|
|
314
|
+
].join(", ")})`;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
intersectsRay(ray: Ray) {
|
|
318
|
+
segment.setFromRay(ray);
|
|
319
|
+
return this.intersectsSegment(segment);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* from Christer Ericson's book "Real-Time Collision Detection", "5.3.3 Intersecting Ray or Segment Against Box"
|
|
324
|
+
*/
|
|
325
|
+
intersectsSegment(segment: Segment) {
|
|
326
|
+
this.computeCentroid(aabbCenter);
|
|
327
|
+
this.computeHalfExtents(aabbHalfExtents);
|
|
328
|
+
|
|
329
|
+
segment.computeCenter(segmentCenter);
|
|
330
|
+
segment.computeHalfExtents(segmentHalfExtents);
|
|
331
|
+
|
|
332
|
+
// translate the segment to the aabb space
|
|
333
|
+
segmentCenter.subtractVectors(segmentCenter, aabbCenter);
|
|
334
|
+
|
|
335
|
+
// test the 6 separating axes
|
|
336
|
+
|
|
337
|
+
// try the aabb axes first
|
|
338
|
+
|
|
339
|
+
// for any given axis, if the projection of the segment center onto the axis is greater than the sum of the half extents of the aabb and the segment, then the aabb and the segment do not intersect
|
|
340
|
+
let adx = Math.abs(segmentHalfExtents.x);
|
|
341
|
+
if (Math.abs(segmentCenter.x) > aabbHalfExtents.x + adx) {
|
|
342
|
+
return false;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
let ady = Math.abs(segmentHalfExtents.y);
|
|
346
|
+
if (Math.abs(segmentCenter.y) > aabbHalfExtents.y + ady) {
|
|
347
|
+
return false;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
let adz = Math.abs(segmentHalfExtents.z);
|
|
351
|
+
if (Math.abs(segmentCenter.z) > aabbHalfExtents.z + adz) {
|
|
352
|
+
return false;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// try the segment cross aabb axes next
|
|
356
|
+
|
|
357
|
+
// an epsilon is added to the half extents to counteract floating point errors when segment is near parallel to an axis
|
|
358
|
+
adx += epsilon;
|
|
359
|
+
ady += epsilon;
|
|
360
|
+
adz += epsilon;
|
|
361
|
+
|
|
362
|
+
if (
|
|
363
|
+
Math.abs(segmentCenter.y * segmentHalfExtents.z - segmentCenter.z * segmentHalfExtents.y) >
|
|
364
|
+
aabbHalfExtents.y * adz + aabbHalfExtents.z * ady
|
|
365
|
+
) {
|
|
366
|
+
return false;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
if (
|
|
370
|
+
Math.abs(segmentCenter.z * segmentHalfExtents.x - segmentCenter.x * segmentHalfExtents.z) >
|
|
371
|
+
aabbHalfExtents.x * adz + aabbHalfExtents.z * adx
|
|
372
|
+
) {
|
|
373
|
+
return false;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
if (
|
|
377
|
+
Math.abs(segmentCenter.x * segmentHalfExtents.y - segmentCenter.y * segmentHalfExtents.x) >
|
|
378
|
+
aabbHalfExtents.x * ady + aabbHalfExtents.y * adx
|
|
379
|
+
) {
|
|
380
|
+
return false;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// if the segment passes all the tests, then it intersects the aabb
|
|
384
|
+
|
|
385
|
+
return true;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
toObject() {
|
|
389
|
+
return {
|
|
390
|
+
min: this.min.toObject(),
|
|
391
|
+
max: this.max.toObject(),
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
const segment = /*@__PURE__*/ Segment.create();
|
|
397
|
+
const aabbCenter = /*@__PURE__*/ Vec3.create();
|
|
398
|
+
const aabbHalfExtents = /*@__PURE__*/ Vec3.create();
|
|
399
|
+
const segmentCenter = /*@__PURE__*/ Vec3.create();
|
|
400
|
+
const segmentHalfExtents = /*@__PURE__*/ Vec3.create();
|
package/src/shape/Box.ts
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
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 { ConvexShapeInterface, ShapeType } from "./Shape";
|
|
11
|
+
import { Quat } from "../math/quat";
|
|
12
|
+
import { Aabb } from "./Aabb";
|
|
13
|
+
import { Face } from "../physics/manifold/Face";
|
|
14
|
+
import { Mat4 } from "../math/mat4";
|
|
15
|
+
import { Mat3 } from "../math/mat3";
|
|
16
|
+
import { Isometry } from "../math/isometry";
|
|
17
|
+
import { SupportMode, SupportShapeWithConvexRadius } from "./Convex";
|
|
18
|
+
import type { World } from "../world";
|
|
19
|
+
|
|
20
|
+
const scaledHalfExtent = /*@__PURE__*/ Vec3.create();
|
|
21
|
+
const halfExtent = /*@__PURE__*/ Vec3.create();
|
|
22
|
+
const convexRadiusVector = /*@__PURE__*/ Vec3.create();
|
|
23
|
+
const reducedHalfExtent = /*@__PURE__*/ Vec3.create();
|
|
24
|
+
const negatedScaledHalfExtent = /*@__PURE__*/ Vec3.create();
|
|
25
|
+
const box = /*@__PURE__*/ Aabb.create();
|
|
26
|
+
|
|
27
|
+
const isometry = /*@__PURE__*/ Isometry.create();
|
|
28
|
+
|
|
29
|
+
const absLocalSurfacePosition = /*@__PURE__*/ Vec3.create();
|
|
30
|
+
const tempVector = /*@__PURE__*/ Vec3.create();
|
|
31
|
+
|
|
32
|
+
const defaultConvexRadius = 0.05; // Default convex radius for box shapes
|
|
33
|
+
|
|
34
|
+
const boxSupportProps = {
|
|
35
|
+
convexRadius: NumberType(0.0),
|
|
36
|
+
// TODO: cannot used undefined default for this, possibly due to it being used as a deeply nested prop
|
|
37
|
+
computedAabb: MonomorphType(Aabb, {}, true),
|
|
38
|
+
} as const satisfies PropertyDefinitionMap;
|
|
39
|
+
|
|
40
|
+
export class BoxSupport extends createClass<BoxSupport, typeof boxSupportProps>(boxSupportProps) {
|
|
41
|
+
computeSupport(out: Vec3, direction: Vec3): void {
|
|
42
|
+
this.computedAabb.computeSupport(out, direction);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
getConvexRadius(): number {
|
|
46
|
+
return this.convexRadius;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const boxProps = {
|
|
51
|
+
computedCenterOfMass: MonomorphType(Vec3, undefined, true),
|
|
52
|
+
computedVolume: NumberType(0.0, true),
|
|
53
|
+
computedAabb: MonomorphType(Aabb, undefined, true),
|
|
54
|
+
convexRadius: NumberType(0.0),
|
|
55
|
+
width: NumberType(0.0),
|
|
56
|
+
height: NumberType(0.0),
|
|
57
|
+
depth: NumberType(0.0),
|
|
58
|
+
boxSupport: MonomorphType(BoxSupport, undefined, true),
|
|
59
|
+
copyForDiff: LazyReferenceType((() => Box) as () => never) as PropertyDefinitionReference<Box | null, true>,
|
|
60
|
+
} as const satisfies PropertyDefinitionMap;
|
|
61
|
+
|
|
62
|
+
const afterConstructorCode = `
|
|
63
|
+
this.world = null;
|
|
64
|
+
`;
|
|
65
|
+
|
|
66
|
+
export class Box
|
|
67
|
+
extends createClass<Box, typeof boxProps>(boxProps, { afterConstructorCode })
|
|
68
|
+
implements ConvexShapeInterface
|
|
69
|
+
{
|
|
70
|
+
type: ShapeType.box = ShapeType.box;
|
|
71
|
+
|
|
72
|
+
declare world: World | null;
|
|
73
|
+
|
|
74
|
+
computeSupportShape(mode: SupportMode, scale: number): SupportShapeWithConvexRadius {
|
|
75
|
+
// Scale our half extents
|
|
76
|
+
halfExtent.x = this.width * 0.5;
|
|
77
|
+
halfExtent.y = this.height * 0.5;
|
|
78
|
+
halfExtent.z = this.depth * 0.5;
|
|
79
|
+
scaledHalfExtent.scaleVector(halfExtent, scale);
|
|
80
|
+
|
|
81
|
+
switch (mode) {
|
|
82
|
+
case SupportMode.IncludeConvexRadius: {
|
|
83
|
+
// Make box out of our half extents
|
|
84
|
+
const out = this.boxSupport;
|
|
85
|
+
out.computedAabb.min.negateVector(scaledHalfExtent);
|
|
86
|
+
out.computedAabb.max.copy(scaledHalfExtent);
|
|
87
|
+
out.convexRadius = 0;
|
|
88
|
+
return out;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
case SupportMode.ExcludeConvexRadius: {
|
|
92
|
+
// Reduce the box by our convex radius
|
|
93
|
+
// TODO: could this be a bug in their code? this never lets convex radius grow past the default
|
|
94
|
+
const convexRadius = Math.min(this.convexRadius * Math.abs(scale), defaultConvexRadius);
|
|
95
|
+
convexRadiusVector.replicate(convexRadius);
|
|
96
|
+
reducedHalfExtent.subtractVectors(scaledHalfExtent, convexRadiusVector);
|
|
97
|
+
|
|
98
|
+
const out = this.boxSupport;
|
|
99
|
+
out.computedAabb.min.negateVector(reducedHalfExtent);
|
|
100
|
+
out.computedAabb.max.copy(reducedHalfExtent);
|
|
101
|
+
out.convexRadius = convexRadius;
|
|
102
|
+
return out;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
default: {
|
|
106
|
+
throw new Error(`Invalid support mode: ${mode}`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
computeSupportingFace(
|
|
112
|
+
out: Face,
|
|
113
|
+
subShapeID: number,
|
|
114
|
+
direction: Vec3,
|
|
115
|
+
scale: number,
|
|
116
|
+
centerOfMassTransform: Mat4
|
|
117
|
+
): void {
|
|
118
|
+
scale = Math.abs(scale);
|
|
119
|
+
|
|
120
|
+
halfExtent.set({ x: this.width * 0.5, y: this.height * 0.5, z: this.depth * 0.5 });
|
|
121
|
+
|
|
122
|
+
scaledHalfExtent.scaleVector(halfExtent, scale);
|
|
123
|
+
|
|
124
|
+
negatedScaledHalfExtent.negateVector(scaledHalfExtent);
|
|
125
|
+
|
|
126
|
+
box.set({ min: negatedScaledHalfExtent, max: scaledHalfExtent });
|
|
127
|
+
box.computeSupportingFace(out, direction);
|
|
128
|
+
|
|
129
|
+
if (out.numVertices > 4) {
|
|
130
|
+
throw new Error("Box face has more than 4 vertices");
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// transform to world space
|
|
134
|
+
out.transform(centerOfMassTransform);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
computeInertiaTensor(out: Mat3, mass: number): void {
|
|
138
|
+
const widthSquared = this.width * this.width;
|
|
139
|
+
const heightSquared = this.height * this.height;
|
|
140
|
+
const depthSquared = this.depth * this.depth;
|
|
141
|
+
|
|
142
|
+
out.fromArray([
|
|
143
|
+
(mass * (heightSquared + depthSquared)) / 12,
|
|
144
|
+
0,
|
|
145
|
+
0,
|
|
146
|
+
0,
|
|
147
|
+
(mass * (widthSquared + depthSquared)) / 12,
|
|
148
|
+
0,
|
|
149
|
+
0,
|
|
150
|
+
0,
|
|
151
|
+
(mass * (widthSquared + heightSquared)) / 12,
|
|
152
|
+
]);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
computeInverseInertiaTensor(out: Mat3, mass: number): void {
|
|
156
|
+
this.computeInertiaTensor(out, mass);
|
|
157
|
+
out.invert();
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
computeWorldBounds(out: Aabb, translation: Vec3, rotation: Quat): void {
|
|
161
|
+
// TODO: apply rotation if shape's center of mass is not at the origin?
|
|
162
|
+
isometry.fromRotationAndTranslation(rotation, translation);
|
|
163
|
+
out.transformAabb(this.computedAabb, isometry);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
hasChanged() {
|
|
167
|
+
return (
|
|
168
|
+
this.copyForDiff !== null &&
|
|
169
|
+
(this.width !== this.copyForDiff.width ||
|
|
170
|
+
this.height !== this.copyForDiff.height ||
|
|
171
|
+
this.depth !== this.copyForDiff.depth)
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
commitChanges() {
|
|
176
|
+
if (this.hasChanged()) {
|
|
177
|
+
updateShape(this);
|
|
178
|
+
// this.world?.updateBodyProperties();
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
computeSurfaceNormal(out: Vec3, inLocalSurfacePosition: Vec3): void {
|
|
183
|
+
// Get component that is closest to the surface of the box
|
|
184
|
+
|
|
185
|
+
absLocalSurfacePosition.absVector(inLocalSurfacePosition);
|
|
186
|
+
halfExtent.set({ x: this.width * 0.5, y: this.height * 0.5, z: this.depth * 0.5 });
|
|
187
|
+
tempVector.subtractVectors(absLocalSurfacePosition, halfExtent);
|
|
188
|
+
tempVector.absVector(tempVector);
|
|
189
|
+
const index = tempVector.computeLowestComponentIndex();
|
|
190
|
+
|
|
191
|
+
// Calculate normal
|
|
192
|
+
out.zero();
|
|
193
|
+
const component = inLocalSurfacePosition.getComponentAtIndex(index);
|
|
194
|
+
out.setComponentAtIndex(index, component > 0 ? 1 : -1);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const oldCreate = Box.create;
|
|
199
|
+
Box.create = function () {
|
|
200
|
+
const shape = oldCreate.apply(this, arguments as any);
|
|
201
|
+
updateShape(shape);
|
|
202
|
+
return shape;
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
function updateCopyForDiff(shape: Box) {
|
|
206
|
+
if (shape.copyForDiff) {
|
|
207
|
+
shape.copyForDiff.copy(shape);
|
|
208
|
+
shape.copyForDiff.copyForDiff = null;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function updateCenterOfMass(shape: Box) {
|
|
213
|
+
shape.computedCenterOfMass.zero();
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function updateVolume(shape: Box) {
|
|
217
|
+
shape.computedVolume = shape.width * shape.height * shape.depth;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function updateLocalBounds(shape: Box) {
|
|
221
|
+
halfExtent.set({ x: shape.width * 0.5, y: shape.height * 0.5, z: shape.depth * 0.5 });
|
|
222
|
+
shape.computedAabb.min.negateVector(halfExtent);
|
|
223
|
+
shape.computedAabb.max.copy(halfExtent);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function updateShape(shape: Box) {
|
|
227
|
+
updateCopyForDiff(shape);
|
|
228
|
+
updateCenterOfMass(shape);
|
|
229
|
+
updateVolume(shape);
|
|
230
|
+
updateLocalBounds(shape);
|
|
231
|
+
}
|