@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,152 @@
|
|
|
1
|
+
import { Isometry } from "../math/isometry";
|
|
2
|
+
import { Shape, ShapeType } from "../shape/Shape";
|
|
3
|
+
import { PenetrationDepthModule } from "./gjk/PenetrationDepthModule";
|
|
4
|
+
import { CollisionCollector, CollisionSettings } from "./collide/collide";
|
|
5
|
+
import { collideSphereVsSphere } from "./collide/collideSphereVsSphere";
|
|
6
|
+
import { collideConvexVsConvex } from "./collide/collideConvexVsConvex";
|
|
7
|
+
import { collideCompoundVsConvex } from "./collide/collideCompoundVsConvex";
|
|
8
|
+
import { collideConvexVsCompound } from "./collide/collideConvexVsCompound";
|
|
9
|
+
import { collideCompoundVsCompound } from "./collide/collideCompoundVsCompound";
|
|
10
|
+
import { collideConvexVsHeightMap } from "./collide/collideConvexVsHeightMap";
|
|
11
|
+
import { collideCompoundVsHeightMap } from "./collide/collideCompoundVsHeightMap";
|
|
12
|
+
import { collideHeightMapVsConvex } from "./collide/collideHeightMapVsConvex";
|
|
13
|
+
import { collideHeightMapVsCompound } from "./collide/collideHeightMapVsCompound";
|
|
14
|
+
import { collideConvexVsTriangleMesh } from "./collide/collideConvexVsTriangleMesh";
|
|
15
|
+
import { collideTriangleMeshVsConvex } from "./collide/collideTriangleMeshVsConvex";
|
|
16
|
+
import { collideTriangleMeshVsCompound } from "./collide/collideTriangleMeshVsCompound";
|
|
17
|
+
import { collideCompoundVsTriangleMesh } from "./collide/collideCompoundVsTriangleMesh";
|
|
18
|
+
import { Body } from "../physics/Body";
|
|
19
|
+
|
|
20
|
+
function noOp() {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const collideShapesFns = {
|
|
25
|
+
[ShapeType.sphere]: {
|
|
26
|
+
[ShapeType.box]: collideConvexVsConvex,
|
|
27
|
+
[ShapeType.capsule]: collideConvexVsConvex,
|
|
28
|
+
[ShapeType.convexHull]: collideConvexVsConvex,
|
|
29
|
+
[ShapeType.compoundShape]: collideConvexVsCompound,
|
|
30
|
+
[ShapeType.cylinder]: collideConvexVsConvex,
|
|
31
|
+
[ShapeType.heightMap]: collideConvexVsHeightMap,
|
|
32
|
+
[ShapeType.sphere]: collideSphereVsSphere,
|
|
33
|
+
[ShapeType.triangleMesh]: collideConvexVsTriangleMesh,
|
|
34
|
+
},
|
|
35
|
+
[ShapeType.box]: {
|
|
36
|
+
[ShapeType.box]: collideConvexVsConvex,
|
|
37
|
+
[ShapeType.capsule]: collideConvexVsConvex,
|
|
38
|
+
[ShapeType.compoundShape]: collideConvexVsCompound,
|
|
39
|
+
[ShapeType.convexHull]: collideConvexVsConvex,
|
|
40
|
+
[ShapeType.cylinder]: collideConvexVsConvex,
|
|
41
|
+
[ShapeType.heightMap]: collideConvexVsHeightMap,
|
|
42
|
+
[ShapeType.sphere]: collideConvexVsConvex,
|
|
43
|
+
[ShapeType.triangleMesh]: collideConvexVsTriangleMesh,
|
|
44
|
+
},
|
|
45
|
+
[ShapeType.convexHull]: {
|
|
46
|
+
[ShapeType.box]: collideConvexVsConvex,
|
|
47
|
+
[ShapeType.capsule]: collideConvexVsConvex,
|
|
48
|
+
[ShapeType.compoundShape]: collideConvexVsCompound,
|
|
49
|
+
[ShapeType.convexHull]: collideConvexVsConvex,
|
|
50
|
+
[ShapeType.cylinder]: collideConvexVsConvex,
|
|
51
|
+
[ShapeType.heightMap]: collideConvexVsHeightMap,
|
|
52
|
+
[ShapeType.sphere]: collideConvexVsConvex,
|
|
53
|
+
[ShapeType.triangleMesh]: collideConvexVsTriangleMesh,
|
|
54
|
+
},
|
|
55
|
+
[ShapeType.compoundShape]: {
|
|
56
|
+
[ShapeType.box]: collideCompoundVsConvex,
|
|
57
|
+
[ShapeType.capsule]: collideCompoundVsConvex,
|
|
58
|
+
[ShapeType.compoundShape]: collideCompoundVsCompound,
|
|
59
|
+
[ShapeType.convexHull]: collideCompoundVsConvex,
|
|
60
|
+
[ShapeType.cylinder]: collideCompoundVsConvex,
|
|
61
|
+
[ShapeType.heightMap]: collideCompoundVsHeightMap,
|
|
62
|
+
[ShapeType.sphere]: collideCompoundVsConvex,
|
|
63
|
+
[ShapeType.triangleMesh]: collideCompoundVsTriangleMesh,
|
|
64
|
+
},
|
|
65
|
+
[ShapeType.cylinder]: {
|
|
66
|
+
[ShapeType.box]: collideConvexVsConvex,
|
|
67
|
+
[ShapeType.capsule]: collideConvexVsConvex,
|
|
68
|
+
[ShapeType.compoundShape]: collideConvexVsCompound,
|
|
69
|
+
[ShapeType.convexHull]: collideConvexVsConvex,
|
|
70
|
+
[ShapeType.cylinder]: collideConvexVsConvex,
|
|
71
|
+
[ShapeType.heightMap]: collideConvexVsHeightMap,
|
|
72
|
+
[ShapeType.sphere]: collideConvexVsConvex,
|
|
73
|
+
[ShapeType.triangleMesh]: collideConvexVsTriangleMesh,
|
|
74
|
+
},
|
|
75
|
+
[ShapeType.capsule]: {
|
|
76
|
+
[ShapeType.box]: collideConvexVsConvex,
|
|
77
|
+
[ShapeType.capsule]: collideConvexVsConvex,
|
|
78
|
+
[ShapeType.compoundShape]: collideConvexVsCompound,
|
|
79
|
+
[ShapeType.convexHull]: collideConvexVsConvex,
|
|
80
|
+
[ShapeType.cylinder]: collideConvexVsConvex,
|
|
81
|
+
[ShapeType.heightMap]: collideConvexVsHeightMap,
|
|
82
|
+
[ShapeType.sphere]: collideConvexVsConvex,
|
|
83
|
+
[ShapeType.triangleMesh]: collideConvexVsTriangleMesh,
|
|
84
|
+
},
|
|
85
|
+
[ShapeType.heightMap]: {
|
|
86
|
+
[ShapeType.box]: collideHeightMapVsConvex,
|
|
87
|
+
[ShapeType.capsule]: collideHeightMapVsConvex,
|
|
88
|
+
[ShapeType.compoundShape]: collideHeightMapVsCompound,
|
|
89
|
+
[ShapeType.convexHull]: collideHeightMapVsConvex,
|
|
90
|
+
[ShapeType.cylinder]: collideHeightMapVsConvex,
|
|
91
|
+
[ShapeType.heightMap]: noOp,
|
|
92
|
+
[ShapeType.sphere]: collideHeightMapVsConvex,
|
|
93
|
+
[ShapeType.triangleMesh]: noOp,
|
|
94
|
+
},
|
|
95
|
+
[ShapeType.triangleMesh]: {
|
|
96
|
+
[ShapeType.box]: collideTriangleMeshVsConvex,
|
|
97
|
+
[ShapeType.capsule]: collideTriangleMeshVsConvex,
|
|
98
|
+
[ShapeType.compoundShape]: collideTriangleMeshVsCompound,
|
|
99
|
+
[ShapeType.convexHull]: collideTriangleMeshVsConvex,
|
|
100
|
+
[ShapeType.cylinder]: collideTriangleMeshVsConvex,
|
|
101
|
+
[ShapeType.heightMap]: noOp,
|
|
102
|
+
[ShapeType.sphere]: collideTriangleMeshVsConvex,
|
|
103
|
+
[ShapeType.triangleMesh]: noOp,
|
|
104
|
+
},
|
|
105
|
+
} as const;
|
|
106
|
+
|
|
107
|
+
export class CollideShapesModule {
|
|
108
|
+
penetrationDepthModule: PenetrationDepthModule;
|
|
109
|
+
|
|
110
|
+
constructor(penetrationDepthModule: PenetrationDepthModule) {
|
|
111
|
+
this.penetrationDepthModule = penetrationDepthModule;
|
|
112
|
+
}
|
|
113
|
+
// collides two shapes that implement the ShapeWithType interface, using the query map
|
|
114
|
+
// the shapes must be in local space
|
|
115
|
+
// the isometries must transform the shapes into world space
|
|
116
|
+
// the results are in world space
|
|
117
|
+
collideShapes(
|
|
118
|
+
collector: CollisionCollector,
|
|
119
|
+
shapeA: Shape,
|
|
120
|
+
isometryA: Isometry,
|
|
121
|
+
shapeB: Shape,
|
|
122
|
+
isometryB: Isometry,
|
|
123
|
+
settings: CollisionSettings,
|
|
124
|
+
bodyA: Body | null,
|
|
125
|
+
bodyB: Body | null,
|
|
126
|
+
subShapeIdA: number = 0,
|
|
127
|
+
subShapeIdB: number = 0
|
|
128
|
+
): void {
|
|
129
|
+
const keyA = shapeA.type as keyof typeof collideShapesFns;
|
|
130
|
+
const keyB = shapeB.type as keyof (typeof collideShapesFns)[keyof typeof collideShapesFns];
|
|
131
|
+
const collisionFunction = collideShapesFns[keyA][keyB];
|
|
132
|
+
if (!collisionFunction) {
|
|
133
|
+
throw new Error(`no collision function found for shapes with type ${keyA} and ${keyB}`);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return collisionFunction(
|
|
137
|
+
this.penetrationDepthModule,
|
|
138
|
+
collector,
|
|
139
|
+
// @ts-ignore
|
|
140
|
+
shapeA,
|
|
141
|
+
shapeB,
|
|
142
|
+
isometryA,
|
|
143
|
+
isometryB,
|
|
144
|
+
settings,
|
|
145
|
+
bodyA,
|
|
146
|
+
bodyB,
|
|
147
|
+
undefined,
|
|
148
|
+
subShapeIdA,
|
|
149
|
+
subShapeIdB
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Vec3 } from "../math/vec3";
|
|
2
|
+
import { Body } from "../physics/Body";
|
|
3
|
+
import { Aabb } from "../shape/Aabb";
|
|
4
|
+
import { HeightMapVisitor } from "../shape/HeightMap";
|
|
5
|
+
import { Ray } from "../shape/Ray";
|
|
6
|
+
import { Triangle } from "../shape/Triangle";
|
|
7
|
+
import { TriangleCaster } from "./TriangleCaster";
|
|
8
|
+
|
|
9
|
+
export class HeightMapCaster implements HeightMapVisitor {
|
|
10
|
+
halfExtents: Vec3;
|
|
11
|
+
ray: Ray;
|
|
12
|
+
triangleCaster: TriangleCaster;
|
|
13
|
+
bodyA: Body | null = null;
|
|
14
|
+
bodyB: Body | null = null;
|
|
15
|
+
|
|
16
|
+
constructor(triangleCaster: TriangleCaster) {
|
|
17
|
+
this.triangleCaster = triangleCaster;
|
|
18
|
+
this.halfExtents = Vec3.create() as Vec3;
|
|
19
|
+
this.ray = Ray.create() as Ray;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
initialize(halfExtents: Vec3, ray: Ray, bodyA: Body | null, bodyB: Body | null): void {
|
|
23
|
+
this.halfExtents.copy(halfExtents);
|
|
24
|
+
this.ray.copy(ray);
|
|
25
|
+
this.bodyA = bodyA;
|
|
26
|
+
this.bodyB = bodyB;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
shouldVisitBlock(block: Aabb): boolean {
|
|
30
|
+
// expand the block aabb by the extents of the shape aabb
|
|
31
|
+
block.expandByVector(this.halfExtents);
|
|
32
|
+
return this.ray.intersectsAabb(block);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
visitTriangle(triangle: Triangle, activeEdges: number, triangleId: number): void {
|
|
36
|
+
this.triangleCaster.cast(triangle, activeEdges, triangleId, this.bodyA, this.bodyB);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Body } from "../physics/Body";
|
|
2
|
+
import { Aabb } from "../shape/Aabb";
|
|
3
|
+
import { HeightMapVisitor } from "../shape/HeightMap";
|
|
4
|
+
import { Triangle } from "../shape/Triangle";
|
|
5
|
+
import { TriangleCollider } from "./TriangleCollider";
|
|
6
|
+
|
|
7
|
+
export class HeightMapCollider implements HeightMapVisitor {
|
|
8
|
+
bodyA: Body | null;
|
|
9
|
+
bodyB: Body | null;
|
|
10
|
+
triangleCollider: TriangleCollider;
|
|
11
|
+
shapeABoundsInBSpace: Aabb;
|
|
12
|
+
|
|
13
|
+
constructor() {
|
|
14
|
+
this.shapeABoundsInBSpace = Aabb.create() as Aabb;
|
|
15
|
+
this.triangleCollider = new TriangleCollider();
|
|
16
|
+
this.bodyA = null;
|
|
17
|
+
this.bodyB = null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
initialize(shapeABoundsInBSpace: Aabb, bodyA: Body | null, bodyB: Body | null): void {
|
|
21
|
+
this.shapeABoundsInBSpace.copy(shapeABoundsInBSpace);
|
|
22
|
+
this.bodyA = bodyA;
|
|
23
|
+
this.bodyB = bodyB;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
shouldVisitBlock(block: Aabb): boolean {
|
|
27
|
+
return this.shapeABoundsInBSpace.intersectsAabb(block);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
visitTriangle(triangle: Triangle, activeEdges: number, triangleId: number): void {
|
|
31
|
+
this.triangleCollider.collide(triangle, activeEdges, triangleId, this.bodyA, this.bodyB);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import { Isometry } from "../math/isometry";
|
|
2
|
+
import { Mat3 } from "../math/mat3";
|
|
3
|
+
import { NumberValue } from "../math/NumberValue";
|
|
4
|
+
import { Vec3 } from "../math/vec3";
|
|
5
|
+
import { Body } from "../physics/Body";
|
|
6
|
+
import { SupportMode } from "../shape/Convex";
|
|
7
|
+
import { ConvexShape } from "../shape/Shape";
|
|
8
|
+
import { Triangle } from "../shape/Triangle";
|
|
9
|
+
import { fixNormal } from "./activeEdge";
|
|
10
|
+
import { CastCollector, CastResult, CastSettings } from "./cast/cast";
|
|
11
|
+
import { ActiveEdgeMode, BackFaceMode } from "./collide/collide";
|
|
12
|
+
import { GjkCastShapeResult, GjkModule } from "./gjk/GjkModule";
|
|
13
|
+
import { PenetrationDepthModule } from "./gjk/PenetrationDepthModule";
|
|
14
|
+
|
|
15
|
+
const triangleNormal = /*@__PURE__*/ Vec3.create();
|
|
16
|
+
const negatedTriangleNormal = /*@__PURE__*/ Vec3.create();
|
|
17
|
+
const pointA = /*@__PURE__*/ Vec3.create();
|
|
18
|
+
const pointB = /*@__PURE__*/ Vec3.create();
|
|
19
|
+
const penetrationAxis = /*@__PURE__*/ Vec3.create();
|
|
20
|
+
const lambda = /*@__PURE__*/ NumberValue.create();
|
|
21
|
+
const castResult = /*@__PURE__*/ GjkCastShapeResult.create();
|
|
22
|
+
const activeEdgeMovementDirectionLocal = /*@__PURE__*/ Vec3.create();
|
|
23
|
+
const contactNormal = /*@__PURE__*/ Vec3.create();
|
|
24
|
+
const result = /*@__PURE__*/ CastResult.create();
|
|
25
|
+
const rotationMatrixB = /*@__PURE__*/ Mat3.create();
|
|
26
|
+
const penetrationDepthModule = /*@__PURE__*/ new PenetrationDepthModule(new GjkModule());
|
|
27
|
+
const activeEdgeMovementDirection = /*@__PURE__*/ Vec3.create();
|
|
28
|
+
|
|
29
|
+
export class TriangleCaster {
|
|
30
|
+
declare penetrationDepthModule: PenetrationDepthModule;
|
|
31
|
+
|
|
32
|
+
declare settings: CastSettings;
|
|
33
|
+
declare collector: CastCollector;
|
|
34
|
+
declare shapeA: ConvexShape;
|
|
35
|
+
bodyA: Body | null = null;
|
|
36
|
+
bodyB: Body | null = null;
|
|
37
|
+
shapeB: Triangle;
|
|
38
|
+
negatedOffset: Vec3;
|
|
39
|
+
offsetIsometryA: Isometry;
|
|
40
|
+
offsetIsometryB: Isometry;
|
|
41
|
+
inverseOffsetIsometryB: Isometry;
|
|
42
|
+
offsetIsometryAToB: Isometry;
|
|
43
|
+
displacementInB: Vec3;
|
|
44
|
+
rotateBToWorld: Mat3;
|
|
45
|
+
rotateWorldToB: Mat3;
|
|
46
|
+
|
|
47
|
+
constructor() {
|
|
48
|
+
this.shapeB = Triangle.create() as Triangle;
|
|
49
|
+
this.negatedOffset = Vec3.create() as Vec3;
|
|
50
|
+
this.offsetIsometryA = Isometry.create() as Isometry;
|
|
51
|
+
this.offsetIsometryB = Isometry.create() as Isometry;
|
|
52
|
+
this.inverseOffsetIsometryB = Isometry.create() as Isometry;
|
|
53
|
+
this.offsetIsometryAToB = Isometry.create() as Isometry;
|
|
54
|
+
this.displacementInB = Vec3.create() as Vec3;
|
|
55
|
+
this.rotateBToWorld = Mat3.create() as Mat3;
|
|
56
|
+
this.rotateWorldToB = Mat3.create() as Mat3;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
initialize(
|
|
60
|
+
settings: CastSettings,
|
|
61
|
+
collector: CastCollector,
|
|
62
|
+
shapeA: ConvexShape,
|
|
63
|
+
isometryA: Isometry,
|
|
64
|
+
isometryB: Isometry,
|
|
65
|
+
displacement: Vec3,
|
|
66
|
+
offset: Vec3,
|
|
67
|
+
bodyA: Body | null = null,
|
|
68
|
+
bodyB: Body | null = null
|
|
69
|
+
): void {
|
|
70
|
+
// so far, everything is in world space (as in, the shapes are in local space, and the isometries transform from their local space to world space, respectively)
|
|
71
|
+
// but we want to cast in B space, so convert everything to B space in the same style as CastModule.castConvexVsConvexWorld
|
|
72
|
+
this.settings = settings;
|
|
73
|
+
this.collector = collector;
|
|
74
|
+
this.shapeA = shapeA;
|
|
75
|
+
this.bodyA = bodyA;
|
|
76
|
+
this.bodyB = bodyB;
|
|
77
|
+
|
|
78
|
+
const negatedOffset = this.negatedOffset;
|
|
79
|
+
const offsetIsometryA = this.offsetIsometryA;
|
|
80
|
+
const offsetIsometryB = this.offsetIsometryB;
|
|
81
|
+
const inverseOffsetIsometryB = this.inverseOffsetIsometryB;
|
|
82
|
+
const offsetIsometryAToB = this.offsetIsometryAToB;
|
|
83
|
+
const displacementInB = this.displacementInB;
|
|
84
|
+
|
|
85
|
+
// translate A and B by the offset, for example to move them both to origin
|
|
86
|
+
negatedOffset.negateVector(offset);
|
|
87
|
+
offsetIsometryA.matrix.postTranslatedMatrix(isometryA.matrix, negatedOffset);
|
|
88
|
+
offsetIsometryB.matrix.postTranslatedMatrix(isometryB.matrix, negatedOffset);
|
|
89
|
+
|
|
90
|
+
// get a transformA in the space of B
|
|
91
|
+
inverseOffsetIsometryB.matrix.invertMatrix(offsetIsometryB.matrix);
|
|
92
|
+
offsetIsometryAToB.matrix.multiplyMatrices(inverseOffsetIsometryB.matrix, offsetIsometryA.matrix);
|
|
93
|
+
|
|
94
|
+
// get the rotation matrix of B
|
|
95
|
+
this.rotateBToWorld.fromMat4(isometryB.matrix);
|
|
96
|
+
|
|
97
|
+
// get the rotation matrix of world to B
|
|
98
|
+
this.rotateWorldToB.invertMat3(this.rotateBToWorld);
|
|
99
|
+
|
|
100
|
+
// get displacement in the space of B
|
|
101
|
+
inverseOffsetIsometryB.matrix.multiply3x3(displacementInB, displacement);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
cast(triangle: Triangle, activeEdges: number, triangleId: number, bodyA: Body | null, bodyB: Body | null): void {
|
|
105
|
+
// cast in B space, return in world space
|
|
106
|
+
const ioCollector = this.collector;
|
|
107
|
+
const inShapeA = this.shapeA;
|
|
108
|
+
const inShapeB = this.shapeB;
|
|
109
|
+
const isometryAToB = this.offsetIsometryAToB;
|
|
110
|
+
const isometryB = this.offsetIsometryB;
|
|
111
|
+
const inScaleA = 1;
|
|
112
|
+
const inScaleB = 1;
|
|
113
|
+
const displacementInB = this.displacementInB;
|
|
114
|
+
const inShapeCastSettings = this.settings;
|
|
115
|
+
const rotateBToWorld = this.rotateBToWorld;
|
|
116
|
+
const rotateWorldToB = this.rotateWorldToB;
|
|
117
|
+
|
|
118
|
+
// get shape B in B space
|
|
119
|
+
inShapeB.copy(triangle);
|
|
120
|
+
|
|
121
|
+
// TODO: scale the shapes
|
|
122
|
+
|
|
123
|
+
// get the triangle normal in B space
|
|
124
|
+
|
|
125
|
+
inShapeB.computeNormal(triangleNormal);
|
|
126
|
+
negatedTriangleNormal.negateVector(triangleNormal);
|
|
127
|
+
|
|
128
|
+
// TODO: handle backface mode
|
|
129
|
+
const back_facing = triangleNormal.dot(displacementInB) > 0;
|
|
130
|
+
if (inShapeCastSettings.backFaceModeTriangles === BackFaceMode.IgnoreBackFaces && back_facing) {
|
|
131
|
+
ioCollector.addMiss();
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// TODO: handle bounds casting
|
|
136
|
+
|
|
137
|
+
// get the support shapes
|
|
138
|
+
const supportMode = inShapeCastSettings.useShrunkenShapeAndConvexRadius
|
|
139
|
+
? SupportMode.ExcludeConvexRadius
|
|
140
|
+
: SupportMode.IncludeConvexRadius;
|
|
141
|
+
const supportA = inShapeA.computeSupportShape(supportMode, inScaleA);
|
|
142
|
+
const supportB = inShapeB.computeSupportShape(supportMode, inScaleB);
|
|
143
|
+
|
|
144
|
+
// get the convex radii
|
|
145
|
+
const convexRadiusA = supportA.getConvexRadius();
|
|
146
|
+
const convexRadiusB = supportB.getConvexRadius();
|
|
147
|
+
|
|
148
|
+
// init pass-through results
|
|
149
|
+
|
|
150
|
+
// init the gjk cast shape result
|
|
151
|
+
|
|
152
|
+
castResult.lambda = ioCollector.getEarlyOutFraction();
|
|
153
|
+
penetrationDepthModule.castShape(
|
|
154
|
+
castResult,
|
|
155
|
+
isometryAToB,
|
|
156
|
+
displacementInB,
|
|
157
|
+
inShapeCastSettings.collisionTolerance,
|
|
158
|
+
inShapeCastSettings.penetrationTolerance,
|
|
159
|
+
supportA,
|
|
160
|
+
supportB,
|
|
161
|
+
convexRadiusA,
|
|
162
|
+
convexRadiusB,
|
|
163
|
+
inShapeCastSettings.returnDeepestPoint
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
// exit if no hit found
|
|
167
|
+
if (castResult.isHitFound === false) {
|
|
168
|
+
this.collector.addMiss();
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// update pass-through results
|
|
173
|
+
pointA.copy(castResult.pointA);
|
|
174
|
+
pointB.copy(castResult.pointB);
|
|
175
|
+
penetrationAxis.copy(castResult.separatingAxis);
|
|
176
|
+
// vec3.normalize(penetrationAxis, castResult.separatingAxis);
|
|
177
|
+
lambda.value = castResult.lambda;
|
|
178
|
+
|
|
179
|
+
// check active edge mode
|
|
180
|
+
if (inShapeCastSettings.activeEdgeMode === ActiveEdgeMode.CollideOnlyWithActive && activeEdges !== 0b111) {
|
|
181
|
+
// debugger;
|
|
182
|
+
// convert the active edge velocity hint to local space
|
|
183
|
+
|
|
184
|
+
activeEdgeMovementDirection.x = inShapeCastSettings.activeEdgeMovementDirectionX;
|
|
185
|
+
activeEdgeMovementDirection.y = inShapeCastSettings.activeEdgeMovementDirectionY;
|
|
186
|
+
activeEdgeMovementDirection.z = inShapeCastSettings.activeEdgeMovementDirectionZ;
|
|
187
|
+
|
|
188
|
+
activeEdgeMovementDirectionLocal.transformVectorFromMat3(activeEdgeMovementDirection, rotateWorldToB);
|
|
189
|
+
|
|
190
|
+
// update the contact normal to account for active edges
|
|
191
|
+
// note that we flip the triangle normal as the penetration axis is pointing towards the triangle instead of away
|
|
192
|
+
|
|
193
|
+
fixNormal(
|
|
194
|
+
contactNormal,
|
|
195
|
+
inShapeB.a,
|
|
196
|
+
inShapeB.b,
|
|
197
|
+
inShapeB.c,
|
|
198
|
+
back_facing ? triangleNormal : negatedTriangleNormal,
|
|
199
|
+
activeEdges,
|
|
200
|
+
pointB,
|
|
201
|
+
penetrationAxis,
|
|
202
|
+
activeEdgeMovementDirectionLocal
|
|
203
|
+
);
|
|
204
|
+
|
|
205
|
+
// update the penetration axis
|
|
206
|
+
penetrationAxis.copy(contactNormal);
|
|
207
|
+
// vec3.negate(penetrationAxis, contactNormal);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// convert to world space
|
|
211
|
+
pointA.transformVectorFromMat4(pointA, isometryB.matrix);
|
|
212
|
+
pointB.transformVectorFromMat4(pointB, isometryB.matrix);
|
|
213
|
+
penetrationAxis.transformVectorFromMat3(penetrationAxis, rotateBToWorld);
|
|
214
|
+
|
|
215
|
+
// create the result
|
|
216
|
+
|
|
217
|
+
result.bodyA = bodyA;
|
|
218
|
+
result.bodyB = bodyB;
|
|
219
|
+
result.subShapeIdA = 0;
|
|
220
|
+
result.subShapeIdB = triangleId;
|
|
221
|
+
result.fraction = lambda.value;
|
|
222
|
+
result.isBackFaceHit = false;
|
|
223
|
+
result.contactPointA.copy(pointA);
|
|
224
|
+
result.contactPointB.copy(pointB);
|
|
225
|
+
result.normalA.copy(penetrationAxis);
|
|
226
|
+
result.normalB.negateVector(penetrationAxis);
|
|
227
|
+
result.penetration = pointA.distance(pointB);
|
|
228
|
+
|
|
229
|
+
// early out if this hit is deeper than the collector's early out value
|
|
230
|
+
if (result.fraction === 0 && -result.penetration >= ioCollector.getEarlyOutFraction()) {
|
|
231
|
+
ioCollector.addMiss(); // TODO: should we classify as a miss?
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// set surface normals on result
|
|
236
|
+
result.surfaceNormalA.zero();
|
|
237
|
+
|
|
238
|
+
// surface normal B should be the triangle's normal in world space
|
|
239
|
+
|
|
240
|
+
rotationMatrixB.fromMat4(isometryB.matrix);
|
|
241
|
+
result.surfaceNormalB.transformVectorFromMat3(triangleNormal, rotationMatrixB);
|
|
242
|
+
|
|
243
|
+
// TODO: handle faces
|
|
244
|
+
|
|
245
|
+
// add the result to the collector
|
|
246
|
+
// ioCollector.earlyOutFraction = result.fraction;
|
|
247
|
+
ioCollector.addHit(result);
|
|
248
|
+
}
|
|
249
|
+
}
|