@perplexdotgg/bounce 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/bounce.d.ts +39501 -0
- package/build/bounce.js +17166 -0
- package/package.json +1 -1
- package/src/builders/ConvexHullBuilder.ts +0 -437
- package/src/builders/ConvexHullBuilder2d.ts +0 -344
- package/src/builders/ConvexHullBuilder3d.ts +0 -1689
- package/src/builders/HeightMapBuilder.ts +0 -414
- package/src/builders/TriangleMeshBuilder.ts +0 -92
- package/src/collision/CastShapesModule.ts +0 -184
- package/src/collision/CollideShapesModule.ts +0 -152
- package/src/collision/HeightMapCaster.ts +0 -38
- package/src/collision/HeightMapCollider.ts +0 -33
- package/src/collision/TriangleCaster.ts +0 -249
- package/src/collision/TriangleCollider.ts +0 -308
- package/src/collision/TriangleCollider2.ts +0 -379
- package/src/collision/activeEdge.ts +0 -146
- package/src/collision/cast/cast.ts +0 -139
- package/src/collision/cast/castCompoundVsCompound.ts +0 -59
- package/src/collision/cast/castCompoundVsConvex.ts +0 -116
- package/src/collision/cast/castConvexVsCompound.ts +0 -123
- package/src/collision/cast/castConvexVsConvex.ts +0 -213
- package/src/collision/cast/castConvexVsHeightMap.ts +0 -73
- package/src/collision/cast/castConvexVsTriangleMesh.ts +0 -56
- package/src/collision/cast/castRayVsCompound.ts +0 -44
- package/src/collision/cast/castRayVsConvex.ts +0 -45
- package/src/collision/cast/castRayVsHeightMap.ts +0 -58
- package/src/collision/cast/castRayVsTriangleMesh.ts +0 -58
- package/src/collision/closestPoints/closestPoints.ts +0 -23
- package/src/collision/closestPoints/computeBarycentricCoordinates2d.ts +0 -32
- package/src/collision/closestPoints/computeBarycentricCoordinates3d.ts +0 -81
- package/src/collision/closestPoints/computeClosestPointOnLine.ts +0 -30
- package/src/collision/closestPoints/computeClosestPointOnTetrahedron.ts +0 -96
- package/src/collision/closestPoints/computeClosestPointOnTriangle.ts +0 -195
- package/src/collision/closestPoints/isOriginOutsideOfPlane.ts +0 -25
- package/src/collision/closestPoints/isOriginOutsideOfTrianglePlanes.ts +0 -72
- package/src/collision/collide/collide.ts +0 -146
- package/src/collision/collide/collideCompoundVsCompound.ts +0 -60
- package/src/collision/collide/collideCompoundVsConvex.ts +0 -59
- package/src/collision/collide/collideCompoundVsHeightMap.ts +0 -73
- package/src/collision/collide/collideCompoundVsTriangleMesh.ts +0 -56
- package/src/collision/collide/collideConvexVsCompound.ts +0 -57
- package/src/collision/collide/collideConvexVsConvex.ts +0 -225
- package/src/collision/collide/collideConvexVsConvexImp.ts +0 -236
- package/src/collision/collide/collideConvexVsHeightMap.ts +0 -53
- package/src/collision/collide/collideConvexVsTriangleMesh.ts +0 -58
- package/src/collision/collide/collideHeightMapVsCompound.ts +0 -69
- package/src/collision/collide/collideHeightMapVsConvex.ts +0 -53
- package/src/collision/collide/collideSphereVsSphere.ts +0 -81
- package/src/collision/collide/collideTriangleMeshVsCompound.ts +0 -58
- package/src/collision/collide/collideTriangleMeshVsConvex.ts +0 -58
- package/src/collision/epa/EpaConvexHullBuilder.ts +0 -397
- package/src/collision/epa/StaticArray.ts +0 -154
- package/src/collision/epa/TriangleFactory.ts +0 -32
- package/src/collision/epa/arrays.ts +0 -99
- package/src/collision/epa/binaryHeap.ts +0 -82
- package/src/collision/epa/structs.ts +0 -227
- package/src/collision/gjk/GjkModule.ts +0 -864
- package/src/collision/gjk/PenetrationDepthModule.ts +0 -493
- package/src/collision/gjk/SupportPoints.ts +0 -50
- package/src/collision/imp/MinkowskiDifference.ts +0 -36
- package/src/collision/imp/computeExploredDistanceLowerUpperBound.ts +0 -40
- package/src/collision/imp/finalizeImpResult.ts +0 -69
- package/src/collision/imp/findContactImp.ts +0 -196
- package/src/collision/imp/imp.ts +0 -28
- package/src/collision/imp/incrementalMinimumDistanceExploreDirection.ts +0 -207
- package/src/collision/mpr/findPortal.ts +0 -152
- package/src/collision/mpr/mpr.ts +0 -29
- package/src/collision/mpr/updatePortal.ts +0 -52
- package/src/constraints/BaseConstraint.ts +0 -50
- package/src/constraints/ConstraintOptions.ts +0 -22
- package/src/constraints/ConstraintSolver.ts +0 -119
- package/src/constraints/DistanceConstraint.ts +0 -229
- package/src/constraints/FixedConstraint.ts +0 -203
- package/src/constraints/HingeConstraint.ts +0 -460
- package/src/constraints/PointConstraint.ts +0 -108
- package/src/constraints/components/AngleComponent.ts +0 -226
- package/src/constraints/components/AxisComponent.ts +0 -263
- package/src/constraints/components/HingeComponent.ts +0 -215
- package/src/constraints/components/Motor.ts +0 -36
- package/src/constraints/components/PointConstraintComponent.ts +0 -179
- package/src/constraints/components/RotationEulerComponent.ts +0 -139
- package/src/constraints/components/Spring.ts +0 -30
- package/src/constraints/components/SpringComponent.ts +0 -71
- package/src/constraints/types.ts +0 -6
- package/src/helpers.ts +0 -147
- package/src/index.ts +0 -50
- package/src/math/BasicTransform.ts +0 -19
- package/src/math/NumberValue.ts +0 -13
- package/src/math/isometry.ts +0 -64
- package/src/math/mat3.ts +0 -529
- package/src/math/mat4.ts +0 -588
- package/src/math/quat.ts +0 -193
- package/src/math/scalar.ts +0 -81
- package/src/math/tensor.ts +0 -17
- package/src/math/vec3.ts +0 -589
- package/src/math/vec4.ts +0 -10
- package/src/physics/Body.ts +0 -581
- package/src/physics/CollisionFilter.ts +0 -52
- package/src/physics/SleepModule.ts +0 -163
- package/src/physics/broadphase/BodyPairsModule.ts +0 -363
- package/src/physics/broadphase/BvhModule.ts +0 -237
- package/src/physics/broadphase/BvhTree.ts +0 -803
- package/src/physics/broadphase/ConstraintPairsModule.ts +0 -385
- package/src/physics/broadphase/TriangleMeshBvhTree.ts +0 -379
- package/src/physics/manifold/ContactManifold.ts +0 -227
- package/src/physics/manifold/ContactManifoldModule.ts +0 -623
- package/src/physics/manifold/Face.ts +0 -119
- package/src/physics/manifold/ManifoldCache.ts +0 -116
- package/src/physics/manifold/clipping/clipPolyVsEdge.ts +0 -131
- package/src/physics/manifold/clipping/clipPolyVsPlane.ts +0 -73
- package/src/physics/manifold/clipping/clipPolyVsPoly.ts +0 -72
- package/src/physics/narrowphase/CollideBodiesModule.ts +0 -755
- package/src/physics/solver/ContactConstraintModule.ts +0 -659
- package/src/physics/solver/ManifoldConstraint.ts +0 -420
- package/src/physics/solver/estimateCollisionResponse.ts +0 -146
- package/src/shape/Aabb.ts +0 -400
- package/src/shape/Box.ts +0 -231
- package/src/shape/Capsule.ts +0 -332
- package/src/shape/CompoundShape.ts +0 -288
- package/src/shape/Convex.ts +0 -130
- package/src/shape/ConvexHull.ts +0 -423
- package/src/shape/Cylinder.ts +0 -313
- package/src/shape/HeightMap.ts +0 -511
- package/src/shape/Line.ts +0 -14
- package/src/shape/Plane.ts +0 -116
- package/src/shape/Ray.ts +0 -81
- package/src/shape/Segment.ts +0 -25
- package/src/shape/Shape.ts +0 -77
- package/src/shape/Sphere.ts +0 -181
- package/src/shape/TransformedShape.ts +0 -51
- package/src/shape/Triangle.ts +0 -122
- package/src/shape/TriangleMesh.ts +0 -186
- package/src/types.ts +0 -1
- package/src/world.ts +0 -1335
- package/tests/BodyPairsModule.test.ts +0 -71
- package/tests/BvhTree.test.ts +0 -406
- package/tests/test.md +0 -642
- package/tests/vec3.test.ts +0 -12
|
@@ -1,379 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
createClass,
|
|
3
|
-
LazyReferenceListType,
|
|
4
|
-
LazyReferenceType,
|
|
5
|
-
MonomorphInstance,
|
|
6
|
-
MonomorphType,
|
|
7
|
-
NumberType,
|
|
8
|
-
PropertyDefinitionMap,
|
|
9
|
-
PropertyDefinitionReference,
|
|
10
|
-
PropertyDefinitionReferenceList,
|
|
11
|
-
ReferenceListType,
|
|
12
|
-
WithPool,
|
|
13
|
-
} from "monomorph";
|
|
14
|
-
import { TriangleMesh } from "../../shape/TriangleMesh";
|
|
15
|
-
import { Aabb } from "../../shape/Aabb";
|
|
16
|
-
import { Vec3 } from "../../math/vec3";
|
|
17
|
-
import { Triangle } from "../../shape/Triangle";
|
|
18
|
-
import { Ray } from "../../shape/Ray";
|
|
19
|
-
|
|
20
|
-
export interface TriangleMeshBvhTreeOptions {
|
|
21
|
-
//
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export interface TriangleMeshBvhTreeBuildParams {
|
|
25
|
-
mesh: TriangleMesh;
|
|
26
|
-
bvhTreeOptions: TriangleMeshBvhTreeOptions;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const bvhNodeProps = {
|
|
30
|
-
parent: LazyReferenceType((() => TriangleMeshBvhNode) as () => never) as PropertyDefinitionReference<TriangleMeshBvhNode | null, true>,
|
|
31
|
-
left: LazyReferenceType((() => TriangleMeshBvhNode) as () => never) as PropertyDefinitionReference<TriangleMeshBvhNode | null, true>,
|
|
32
|
-
right: LazyReferenceType((() => TriangleMeshBvhNode) as () => never) as PropertyDefinitionReference<TriangleMeshBvhNode | null, true>,
|
|
33
|
-
computedBounds: MonomorphType(Aabb, undefined, true),
|
|
34
|
-
depth: NumberType(0, true),
|
|
35
|
-
objects: LazyReferenceListType((() => Triangle) as () => never) as PropertyDefinitionReferenceList<Triangle, Triangle extends MonomorphInstance<infer P, infer I> ? P : never, Triangle extends MonomorphInstance<infer P, infer I> ? I : never>,
|
|
36
|
-
} as const satisfies PropertyDefinitionMap;
|
|
37
|
-
|
|
38
|
-
export class TriangleMeshBvhNode extends createClass<TriangleMeshBvhNode, typeof bvhNodeProps>(bvhNodeProps) {
|
|
39
|
-
/**
|
|
40
|
-
* if bvh node is an internal node, this will be an empty list
|
|
41
|
-
* if bvh node is a leaf node, this will contain refs to the objects in the leaf
|
|
42
|
-
*/
|
|
43
|
-
// objects: Triangle[] = [];
|
|
44
|
-
|
|
45
|
-
isLeaf() {
|
|
46
|
-
return this.left === null && this.right === null;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
isRoot() {
|
|
50
|
-
return this.parent === null;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function computeBounds(out: Aabb, objects: Triangle[]) {
|
|
55
|
-
out.copy(objects[0].computedBounds);
|
|
56
|
-
for (let i = 1; i < objects.length; i++) {
|
|
57
|
-
out.unionAabb(objects[i].computedBounds);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const centroidBounds = /*@__PURE__*/ Aabb.create();
|
|
62
|
-
const extent = /*@__PURE__*/ Vec3.create();
|
|
63
|
-
|
|
64
|
-
function sortByX(a: Triangle, b: Triangle) {
|
|
65
|
-
return a.computedBounds.centroid.x - b.computedBounds.centroid.x;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function sortByY(a: Triangle, b: Triangle) {
|
|
69
|
-
return a.computedBounds.centroid.y - b.computedBounds.centroid.y;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function sortByZ(a: Triangle, b: Triangle) {
|
|
73
|
-
return a.computedBounds.centroid.z - b.computedBounds.centroid.z;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const sortByAxisFunctions = {
|
|
77
|
-
0: sortByX,
|
|
78
|
-
1: sortByY,
|
|
79
|
-
2: sortByZ,
|
|
80
|
-
} as const;
|
|
81
|
-
|
|
82
|
-
function splitObjects(objects: Triangle[]) {
|
|
83
|
-
if (objects.length < 2) {
|
|
84
|
-
throw new Error("Cannot split fewer than 2 objects.");
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// expand centroid bounds
|
|
88
|
-
centroidBounds.min.fromArray([+Infinity, +Infinity, +Infinity]);
|
|
89
|
-
centroidBounds.max.fromArray([-Infinity, -Infinity, -Infinity]);
|
|
90
|
-
|
|
91
|
-
for (const obj of objects) {
|
|
92
|
-
centroidBounds.expandToPoint(obj.computedBounds.centroid);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
centroidBounds.computeExtents(extent);
|
|
96
|
-
const axis = centroidBounds.computeLargestAxis();
|
|
97
|
-
|
|
98
|
-
objects.sort(sortByAxisFunctions[axis]);
|
|
99
|
-
let mid = Math.floor(objects.length / 2);
|
|
100
|
-
if (mid <= 0 || mid >= objects.length) {
|
|
101
|
-
mid = 1;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
const leftObjects = objects.slice(0, mid);
|
|
105
|
-
const rightObjects = objects.slice(mid);
|
|
106
|
-
return [leftObjects, rightObjects] as [Triangle[], Triangle[]];
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
const triangleMeshBvhTreeProps = {
|
|
110
|
-
nodes: ReferenceListType(TriangleMeshBvhNode),
|
|
111
|
-
} as const satisfies PropertyDefinitionMap;
|
|
112
|
-
|
|
113
|
-
export class TriangleMeshBvhTree extends createClass<TriangleMeshBvhTree, typeof triangleMeshBvhTreeProps>(
|
|
114
|
-
triangleMeshBvhTreeProps
|
|
115
|
-
) {
|
|
116
|
-
// TODO: init this via constructor params
|
|
117
|
-
intersectStack: TriangleMeshBvhNode[] = [];
|
|
118
|
-
walkStack: TriangleMeshBvhNode[] = [];
|
|
119
|
-
|
|
120
|
-
build(params: TriangleMeshBvhTreeBuildParams, trianglePool: typeof Triangle.Pool) {
|
|
121
|
-
const objects: Triangle[] = [];
|
|
122
|
-
for (const triangle of params.mesh.triangles!) {
|
|
123
|
-
objects.push(triangle);
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
const root = this.nodes.create({
|
|
127
|
-
objects: {
|
|
128
|
-
pool: trianglePool,
|
|
129
|
-
},
|
|
130
|
-
});
|
|
131
|
-
this._buildRecursive(objects, root, null);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
get root() {
|
|
135
|
-
return this.nodes?.getAtIndex(0);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
_buildRecursive(objects: Triangle[], node: TriangleMeshBvhNode, parent: TriangleMeshBvhNode | null) {
|
|
139
|
-
node.parent = parent;
|
|
140
|
-
node.depth = parent ? parent.depth + 1 : 0;
|
|
141
|
-
computeBounds(node.computedBounds, objects);
|
|
142
|
-
|
|
143
|
-
if (objects.length <= 2) {
|
|
144
|
-
node.objects.length = 0;
|
|
145
|
-
for (const object of objects) {
|
|
146
|
-
node.objects.push(object as WithPool<Triangle>);
|
|
147
|
-
}
|
|
148
|
-
return;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
const [leftObjects, rightObjects] = splitObjects(objects);
|
|
152
|
-
|
|
153
|
-
if (leftObjects.length === 0 || rightObjects.length === 0) {
|
|
154
|
-
node.objects.length = 0;
|
|
155
|
-
for (const object of objects) {
|
|
156
|
-
node.objects.push(object as WithPool<Triangle>);
|
|
157
|
-
}
|
|
158
|
-
return;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
const left = this.nodes.create({
|
|
162
|
-
objects: {
|
|
163
|
-
pool: node.objects.pool,
|
|
164
|
-
},
|
|
165
|
-
});
|
|
166
|
-
const right = this.nodes.create({
|
|
167
|
-
objects: {
|
|
168
|
-
pool: node.objects.pool,
|
|
169
|
-
},
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
node.left = left;
|
|
173
|
-
node.right = right;
|
|
174
|
-
|
|
175
|
-
this._buildRecursive(leftObjects, left, node);
|
|
176
|
-
this._buildRecursive(rightObjects, right, node);
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
intersectAabb(onHit: (triangle: Triangle) => boolean, aabb: Aabb) {
|
|
180
|
-
// console.log("intersect body", body);
|
|
181
|
-
if (!this.root) {
|
|
182
|
-
return;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
const stack = this.intersectStack;
|
|
186
|
-
stack.length = 0;
|
|
187
|
-
stack.push(this.root);
|
|
188
|
-
|
|
189
|
-
while (stack.length > 0) {
|
|
190
|
-
const node = stack.pop();
|
|
191
|
-
|
|
192
|
-
if (!node) {
|
|
193
|
-
continue;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
// skip this node if it doesn't intersect the AABB
|
|
197
|
-
if (!node.computedBounds.intersectsAabb(aabb)) {
|
|
198
|
-
continue;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
// if the node is not a leaf, push its children onto the stack and continue
|
|
202
|
-
if (!node.isLeaf()) {
|
|
203
|
-
stack.push(node.left!);
|
|
204
|
-
stack.push(node.right!);
|
|
205
|
-
continue;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
// if the node is a leaf, check its objects
|
|
209
|
-
for (const object of node.objects) {
|
|
210
|
-
if (!aabb.intersectsAabb(object.computedBounds)) {
|
|
211
|
-
continue;
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
if (onHit(object)) {
|
|
215
|
-
return;
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
walk(onNode: (node: TriangleMeshBvhNode) => boolean) {
|
|
222
|
-
if (!this.root) {
|
|
223
|
-
return;
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
const stack = this.walkStack;
|
|
227
|
-
stack.length = 0;
|
|
228
|
-
stack.push(this.root);
|
|
229
|
-
|
|
230
|
-
while (stack.length > 0) {
|
|
231
|
-
const node = stack.pop();
|
|
232
|
-
|
|
233
|
-
if (!node) {
|
|
234
|
-
continue;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
if (onNode(node)) {
|
|
238
|
-
return;
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
if (!node.isLeaf()) {
|
|
242
|
-
stack.push(node.left!);
|
|
243
|
-
stack.push(node.right!);
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
computeMaxDepth() {
|
|
249
|
-
if (!this.root) {
|
|
250
|
-
return 0;
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
let maxDepth = 0;
|
|
254
|
-
this.walk(node => {
|
|
255
|
-
if (node.depth > maxDepth) {
|
|
256
|
-
maxDepth = node.depth;
|
|
257
|
-
}
|
|
258
|
-
return false;
|
|
259
|
-
});
|
|
260
|
-
return maxDepth;
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
castAabb(onHit: (triangle: Triangle) => boolean, bounds: Aabb, displacement: Vec3) {
|
|
264
|
-
// exit if the tree is empty
|
|
265
|
-
if (!this.root) {
|
|
266
|
-
return;
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
// aabb cast is done by the following method
|
|
270
|
-
// 1. shrink the shape aabb by its own extents down to a point
|
|
271
|
-
// 2. expand the block aabb by the extents of the shape aabb
|
|
272
|
-
// 3. cast the point by the displacement against the expanded block aabb (raycast vs aabb test)
|
|
273
|
-
|
|
274
|
-
bounds.computeCentroid(ray.origin);
|
|
275
|
-
ray.direction.normalizeVector(displacement);
|
|
276
|
-
ray.length = displacement.length();
|
|
277
|
-
|
|
278
|
-
bounds.computeHalfExtents(halfExtents);
|
|
279
|
-
|
|
280
|
-
const stack = this.intersectStack;
|
|
281
|
-
stack.length = 0;
|
|
282
|
-
stack.push(this.root);
|
|
283
|
-
|
|
284
|
-
while (stack.length > 0) {
|
|
285
|
-
const node = stack.pop();
|
|
286
|
-
|
|
287
|
-
if (!node) {
|
|
288
|
-
continue;
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
// skip branch if the node's expanded aabb does not intersect with the shape's ray
|
|
292
|
-
nodeBounds.copy(node.computedBounds);
|
|
293
|
-
nodeBounds.expandByVector(halfExtents);
|
|
294
|
-
if (ray.intersectsAabb(nodeBounds) === false) {
|
|
295
|
-
continue;
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
// if the node is not a leaf, push its children onto the stack and continue
|
|
299
|
-
if (!node.isLeaf()) {
|
|
300
|
-
stack.push(node.left!);
|
|
301
|
-
stack.push(node.right!);
|
|
302
|
-
continue;
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
// if the node is a leaf, check its objects
|
|
306
|
-
for (const body of node.objects) {
|
|
307
|
-
// expand the object aabb by the shape's aabb
|
|
308
|
-
// skip if bounds do not collide
|
|
309
|
-
bodyBounds.copy(body.computedBounds);
|
|
310
|
-
bodyBounds.expandByVector(halfExtents);
|
|
311
|
-
if (ray.intersectsAabb(bodyBounds) === false) {
|
|
312
|
-
continue;
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
// visit the body
|
|
316
|
-
if (onHit(body)) {
|
|
317
|
-
return;
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
castRay(onHit: (triangle: Triangle) => boolean, ray: Ray) {
|
|
324
|
-
// exit if the tree is empty
|
|
325
|
-
if (!this.root) {
|
|
326
|
-
return;
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
// aabb cast is done by the following method
|
|
330
|
-
// 1. shrink the shape aabb by its own extents down to a point
|
|
331
|
-
// 2. expand the block aabb by the extents of the shape aabb
|
|
332
|
-
// 3. cast the point by the displacement against the expanded block aabb (raycast vs aabb test)
|
|
333
|
-
|
|
334
|
-
const stack = this.intersectStack;
|
|
335
|
-
stack.length = 0;
|
|
336
|
-
stack.push(this.root);
|
|
337
|
-
|
|
338
|
-
while (stack.length > 0) {
|
|
339
|
-
const node = stack.pop();
|
|
340
|
-
|
|
341
|
-
if (!node) {
|
|
342
|
-
continue;
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
// skip branch if the node's expanded aabb does not intersect with the shape's ray
|
|
346
|
-
nodeBounds.copy(node.computedBounds);
|
|
347
|
-
if (ray.intersectsAabb(nodeBounds) === false) {
|
|
348
|
-
continue;
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
// if the node is not a leaf, push its children onto the stack and continue
|
|
352
|
-
if (!node.isLeaf()) {
|
|
353
|
-
stack.push(node.left!);
|
|
354
|
-
stack.push(node.right!);
|
|
355
|
-
continue;
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
// if the node is a leaf, check its objects
|
|
359
|
-
for (const body of node.objects) {
|
|
360
|
-
// expand the object aabb by the shape's aabb
|
|
361
|
-
// skip if bounds do not collide
|
|
362
|
-
bodyBounds.copy(body.computedBounds);
|
|
363
|
-
if (ray.intersectsAabb(bodyBounds) === false) {
|
|
364
|
-
continue;
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
// visit the body
|
|
368
|
-
if (onHit(body)) {
|
|
369
|
-
return;
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
const ray = /*@__PURE__*/ Ray.create();
|
|
377
|
-
const halfExtents = /*@__PURE__*/ Vec3.create();
|
|
378
|
-
const nodeBounds = /*@__PURE__*/ Aabb.create();
|
|
379
|
-
const bodyBounds = /*@__PURE__*/ Aabb.create();
|
|
@@ -1,227 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
createClass,
|
|
3
|
-
InputType,
|
|
4
|
-
LazyReferenceType,
|
|
5
|
-
MonomorphType,
|
|
6
|
-
NumberType,
|
|
7
|
-
PoolClass,
|
|
8
|
-
PropertyDefinitionMap,
|
|
9
|
-
PropertyDefinitionReference,
|
|
10
|
-
ReferenceListType,
|
|
11
|
-
ReferenceType,
|
|
12
|
-
} from "monomorph";
|
|
13
|
-
import { Body } from "../Body";
|
|
14
|
-
import { Vec3 } from "../../math/vec3";
|
|
15
|
-
import { Quat } from "../../math/quat";
|
|
16
|
-
import { Mat4 } from "../../math/mat4";
|
|
17
|
-
import { ReferenceFrame } from "../../constraints/BaseConstraint";
|
|
18
|
-
|
|
19
|
-
const localToWorldTransformA = /*@__PURE__*/ Mat4.create();
|
|
20
|
-
const point = /*@__PURE__*/ Vec3.create();
|
|
21
|
-
|
|
22
|
-
const contactManifoldProps = {
|
|
23
|
-
// meta data
|
|
24
|
-
bodyA: LazyReferenceType((() => Body) as () => never) as PropertyDefinitionReference<Body | null, true>,
|
|
25
|
-
bodyB: LazyReferenceType((() => Body) as () => never) as PropertyDefinitionReference<Body | null, true>,
|
|
26
|
-
subShapeIdA: NumberType(0),
|
|
27
|
-
subShapeIdB: NumberType(0),
|
|
28
|
-
nextContactManifold: LazyReferenceType((() => ContactManifold) as () => never) as PropertyDefinitionReference<
|
|
29
|
-
ContactManifold | null,
|
|
30
|
-
true
|
|
31
|
-
>,
|
|
32
|
-
// geometric data
|
|
33
|
-
baseTranslation: MonomorphType(Vec3),
|
|
34
|
-
firstWorldSpaceNormal: MonomorphType(Vec3),
|
|
35
|
-
worldSpaceNormal: MonomorphType(Vec3),
|
|
36
|
-
penetrationDepth: NumberType(0.0),
|
|
37
|
-
numContacts: NumberType(0),
|
|
38
|
-
contactPointsA: ReferenceListType(Vec3),
|
|
39
|
-
contactPointsB: ReferenceListType(Vec3),
|
|
40
|
-
lambdas: ReferenceListType(Vec3),
|
|
41
|
-
} as const satisfies PropertyDefinitionMap;
|
|
42
|
-
|
|
43
|
-
export class ContactManifold extends createClass<ContactManifold, typeof contactManifoldProps>(contactManifoldProps) {
|
|
44
|
-
get maxContacts(): number {
|
|
45
|
-
return this.contactPointsA!.maxLength;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
pushContactPoints(pointA: Vec3, pointB: Vec3): void {
|
|
49
|
-
if (this.numContacts >= this.maxContacts) {
|
|
50
|
-
throw new Error("contact manifold is full");
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
this.contactPointsA!.getAtIndex(this.numContacts)!.copy(pointA);
|
|
54
|
-
this.contactPointsB!.getAtIndex(this.numContacts)!.copy(pointB);
|
|
55
|
-
// this.lambdas!.getAtIndex(this.numContacts)!.copy(pointA);
|
|
56
|
-
this.numContacts++;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
get key(): string {
|
|
60
|
-
return createContactPairKey(this.bodyA!, this.bodyB!);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
copyLambda(other: ContactManifold, index: number): void {
|
|
64
|
-
this.lambdas.getAtIndex(index)!.copy(other.lambdas.getAtIndex(index)!);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
copyLambdas(other: ContactManifold): void {
|
|
68
|
-
for (let i = 0; i < this.numContacts; i++) {
|
|
69
|
-
this.lambdas.getAtIndex(i)!.copy(other.lambdas.getAtIndex(i)!);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
resetLambdas(): void {
|
|
74
|
-
for (let i = 0; i < this.numContacts; i++) {
|
|
75
|
-
this.lambdas.getAtIndex(i)!.zero();
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
getContactPointA(out: Vec3, index: number): void {
|
|
80
|
-
out.copy(this.contactPointsA.getAtIndex(index)!);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
getContactPointB(out: Vec3, index: number): void {
|
|
84
|
-
out.copy(this.contactPointsB.getAtIndex(index)!);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
setContactPointA(point: Vec3, index: number): void {
|
|
88
|
-
this.contactPointsA.getAtIndex(index)!.copy(point);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
setContactPointB(point: Vec3, index: number): void {
|
|
92
|
-
this.contactPointsB.getAtIndex(index)!.copy(point);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
clear() {
|
|
96
|
-
this.numContacts = 0;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* this function assumes that the contact points are stored in the local space of the corresponding body!
|
|
101
|
-
*/
|
|
102
|
-
getWorldContactPointA(out: Vec3, index: number): void {
|
|
103
|
-
const bodyA = this.bodyA!;
|
|
104
|
-
localToWorldTransformA.fromRotationTranslation(bodyA.orientation, bodyA.computedCenterOfMassPosition);
|
|
105
|
-
|
|
106
|
-
this.getContactPointA(out, index);
|
|
107
|
-
out.transformVectorFromMat4(out, localToWorldTransformA);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
getContactPointAInSpace(out: Vec3, index: number, space: ReferenceFrame): void {
|
|
111
|
-
if (space === ReferenceFrame.world) {
|
|
112
|
-
this.getWorldContactPointA(out, index);
|
|
113
|
-
} else {
|
|
114
|
-
this.getContactPointA(out, index);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
getCentroidA(out: Vec3): void {
|
|
119
|
-
out.zero();
|
|
120
|
-
|
|
121
|
-
for (let i = 0; i < this.numContacts; i++) {
|
|
122
|
-
this.getContactPointA(point, i);
|
|
123
|
-
out.addVectors(out, point);
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
out.scaleVector(out, 1 / this.numContacts);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
getWorldCentroidA(out: Vec3): void {
|
|
130
|
-
const bodyA = this.bodyA!;
|
|
131
|
-
localToWorldTransformA.fromRotationTranslation(bodyA.orientation, bodyA.computedCenterOfMassPosition);
|
|
132
|
-
|
|
133
|
-
this.getCentroidA(out);
|
|
134
|
-
out.transformVectorFromMat4(out, localToWorldTransformA);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
getCentroidAInSpace(out: Vec3, space: ReferenceFrame): void {
|
|
138
|
-
if (space === ReferenceFrame.world) {
|
|
139
|
-
this.getWorldCentroidA(out);
|
|
140
|
-
} else {
|
|
141
|
-
this.getCentroidA(out);
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
containsBody(body: Body): boolean {
|
|
146
|
-
return this.bodyA === body || this.bodyB === body;
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
export class ContactManifoldPool {
|
|
151
|
-
// the contact manifold pool
|
|
152
|
-
contactManifoldPool: PoolClass<ContactManifold>;
|
|
153
|
-
vec3Pool: PoolClass<Vec3>;
|
|
154
|
-
|
|
155
|
-
numMaxManifolds: number;
|
|
156
|
-
maxNumContacts: number;
|
|
157
|
-
|
|
158
|
-
constructor(numMaxManifolds: number, numMaxContacts: number) {
|
|
159
|
-
this.contactManifoldPool = new ContactManifold.Pool();
|
|
160
|
-
this.vec3Pool = new Vec3.Pool();
|
|
161
|
-
|
|
162
|
-
this.numMaxManifolds = numMaxManifolds;
|
|
163
|
-
this.maxNumContacts = numMaxContacts;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
createContactManifold(data: InputType<ContactManifold>): ContactManifold {
|
|
167
|
-
data = data ?? {};
|
|
168
|
-
|
|
169
|
-
if (data.contactPointsA) {
|
|
170
|
-
data.contactPointsA.pool = this.vec3Pool;
|
|
171
|
-
} else {
|
|
172
|
-
data.contactPointsA = { pool: this.vec3Pool };
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
if (data.contactPointsB) {
|
|
176
|
-
data.contactPointsB.pool = this.vec3Pool;
|
|
177
|
-
} else {
|
|
178
|
-
data.contactPointsB = { pool: this.vec3Pool };
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
if (data.lambdas) {
|
|
182
|
-
data.lambdas.pool = this.vec3Pool;
|
|
183
|
-
} else {
|
|
184
|
-
data.lambdas = { pool: this.vec3Pool };
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
const manifold = ContactManifold.create(data, this.contactManifoldPool);
|
|
188
|
-
|
|
189
|
-
// todo this behavior probably should change?
|
|
190
|
-
// currently, we create all the points once the first time, and never destroy them.
|
|
191
|
-
// they technically should be destroyed, but this is more efficient for this use case
|
|
192
|
-
// since we will be reusing the same manifolds over and over again, so we should just
|
|
193
|
-
// increment a numManifolds instead that is reset to 0
|
|
194
|
-
if (manifold.contactPointsA.length >= this.maxNumContacts) {
|
|
195
|
-
return manifold;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
for (let i = 0; i < this.maxNumContacts; i++) {
|
|
200
|
-
manifold.contactPointsA.create();
|
|
201
|
-
manifold.contactPointsB.create();
|
|
202
|
-
manifold.lambdas.create();
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
return manifold;
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
const contactPairProps = {
|
|
210
|
-
bodyA: LazyReferenceType((() => Body) as () => never) as PropertyDefinitionReference<Body | null, true>,
|
|
211
|
-
bodyB: LazyReferenceType((() => Body) as () => never) as PropertyDefinitionReference<Body | null, true>,
|
|
212
|
-
firstContactManifold: ReferenceType(ContactManifold),
|
|
213
|
-
translationAB: MonomorphType(Vec3),
|
|
214
|
-
rotationAB: MonomorphType(Quat),
|
|
215
|
-
} as const satisfies PropertyDefinitionMap;
|
|
216
|
-
|
|
217
|
-
export class ContactPair extends createClass<ContactPair, typeof contactPairProps>(contactPairProps) {
|
|
218
|
-
get key(): string {
|
|
219
|
-
return createContactPairKey(this.bodyA!, this.bodyB!);
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
export function createContactPairKey(bodyA: Body, bodyB: Body): string {
|
|
224
|
-
const keyA = `${bodyA.type}:${bodyA.index}`;
|
|
225
|
-
const keyB = `${bodyB.type}:${bodyB.index}`;
|
|
226
|
-
return keyA < keyB ? `${keyA}|${keyB}` : `${keyB}|${keyA}`;
|
|
227
|
-
}
|