@perplexdotgg/bounce 1.0.0 → 1.0.1
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
|
@@ -1,659 +0,0 @@
|
|
|
1
|
-
import { Body, BodyType, ColliderType } from "../Body";
|
|
2
|
-
import { ContactManifold } from "../manifold/ContactManifold";
|
|
3
|
-
import { ContactConstraint, ManifoldConstraint, ManifoldConstraintPool } from "./ManifoldConstraint";
|
|
4
|
-
import { ContactManifoldModule } from "../manifold/ContactManifoldModule";
|
|
5
|
-
import { average, clamp, squared } from "../../math/scalar";
|
|
6
|
-
import { Isometry } from "../../math/isometry";
|
|
7
|
-
import { Mat3 } from "../../math/mat3";
|
|
8
|
-
import { Mat4 } from "../../math/mat4";
|
|
9
|
-
import { createContactPairKey } from "../manifold/ContactManifold";
|
|
10
|
-
import { Vec3 } from "../../math/vec3";
|
|
11
|
-
import { destroyAllInstancesInPool } from "../../helpers";
|
|
12
|
-
import type { World } from "../../world";
|
|
13
|
-
import { WithPool } from "monomorph";
|
|
14
|
-
|
|
15
|
-
function getCombinedFriction(bodyA: Body, bodyB: Body, fallbackFrictionFunction: CoefficientFunctionType): number {
|
|
16
|
-
const frictionFunction =
|
|
17
|
-
bodyA.frictionFunction !== bodyB.frictionFunction
|
|
18
|
-
? fallbackFrictionFunction
|
|
19
|
-
: (bodyA.frictionFunction as keyof typeof coefficientFunctions);
|
|
20
|
-
return coefficientFunctions[frictionFunction](bodyA.friction, bodyB.friction);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export const enum CoefficientFunctionType {
|
|
24
|
-
min,
|
|
25
|
-
max,
|
|
26
|
-
average,
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const coefficientFunctions = {
|
|
30
|
-
[CoefficientFunctionType.min]: Math.min,
|
|
31
|
-
[CoefficientFunctionType.max]: Math.max,
|
|
32
|
-
[CoefficientFunctionType.average]: average,
|
|
33
|
-
} as const;
|
|
34
|
-
|
|
35
|
-
const contactPointPreserveLambdaMaxDistanceSquared = squared(0.01); // 1 cm
|
|
36
|
-
|
|
37
|
-
const inverseIsometryB = /*@__PURE__*/ Isometry.create();
|
|
38
|
-
const inverseInertiaTensorA = /*@__PURE__*/ Mat3.create();
|
|
39
|
-
const inverseInertiaTensorB = /*@__PURE__*/ Mat3.create();
|
|
40
|
-
const worldToLocalA = /*@__PURE__*/ Mat4.create();
|
|
41
|
-
const worldToLocalB = /*@__PURE__*/ Mat4.create();
|
|
42
|
-
|
|
43
|
-
const worldContactPointA = /*@__PURE__*/ Vec3.create();
|
|
44
|
-
const worldContactPointB = /*@__PURE__*/ Vec3.create();
|
|
45
|
-
const oldContactPointA = /*@__PURE__*/ Vec3.create();
|
|
46
|
-
const oldContactPointB = /*@__PURE__*/ Vec3.create();
|
|
47
|
-
const combinedWorldContactPoint = /*@__PURE__*/ Vec3.create();
|
|
48
|
-
const worldMomentArmA = /*@__PURE__*/ Vec3.create();
|
|
49
|
-
const worldMomentArmB = /*@__PURE__*/ Vec3.create();
|
|
50
|
-
const contactVelocityA = /*@__PURE__*/ Vec3.create();
|
|
51
|
-
const contactVelocityB = /*@__PURE__*/ Vec3.create();
|
|
52
|
-
const contactVelocityAB = /*@__PURE__*/ Vec3.create();
|
|
53
|
-
const penetrationVector = /*@__PURE__*/ Vec3.create();
|
|
54
|
-
const worldSpaceSurfaceVelocity = /*@__PURE__*/ Vec3.create();
|
|
55
|
-
|
|
56
|
-
const transformLocalToWorldA = /*@__PURE__*/ Isometry.create();
|
|
57
|
-
const transformLocalToWorldB = /*@__PURE__*/ Isometry.create();
|
|
58
|
-
|
|
59
|
-
const worldPointA = /*@__PURE__*/ Vec3.create();
|
|
60
|
-
const worldPointB = /*@__PURE__*/ Vec3.create();
|
|
61
|
-
|
|
62
|
-
const transformAToWorld = /*@__PURE__*/ Mat4.create();
|
|
63
|
-
const transformBToWorld = /*@__PURE__*/ Mat4.create();
|
|
64
|
-
|
|
65
|
-
const averageWorldContactPoint = /*@__PURE__*/ Vec3.create();
|
|
66
|
-
const momentArmA = /*@__PURE__*/ Vec3.create();
|
|
67
|
-
const momentArmB = /*@__PURE__*/ Vec3.create();
|
|
68
|
-
|
|
69
|
-
const positionDeltaA = /*@__PURE__*/ Vec3.create();
|
|
70
|
-
const rotationDeltaA = /*@__PURE__*/ Vec3.create();
|
|
71
|
-
const positionDeltaB = /*@__PURE__*/ Vec3.create();
|
|
72
|
-
const rotationDeltaB = /*@__PURE__*/ Vec3.create();
|
|
73
|
-
|
|
74
|
-
function getCombinedRestitution(
|
|
75
|
-
bodyA: Body,
|
|
76
|
-
bodyB: Body,
|
|
77
|
-
fallbackRestitutionFunction: CoefficientFunctionType
|
|
78
|
-
): number {
|
|
79
|
-
const restitutionFunction =
|
|
80
|
-
bodyA.restitutionFunction !== bodyB.restitutionFunction
|
|
81
|
-
? fallbackRestitutionFunction
|
|
82
|
-
: (bodyA.restitutionFunction as keyof typeof coefficientFunctions);
|
|
83
|
-
return coefficientFunctions[restitutionFunction](bodyA.restitution, bodyB.restitution);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
export class ContactConstraintModule {
|
|
87
|
-
world: World;
|
|
88
|
-
manifoldConstraints: ManifoldConstraintPool;
|
|
89
|
-
contactManifoldModule: ContactManifoldModule;
|
|
90
|
-
|
|
91
|
-
constructor(world: World, contactManifoldModule: ContactManifoldModule) {
|
|
92
|
-
this.world = world;
|
|
93
|
-
this.contactManifoldModule = contactManifoldModule;
|
|
94
|
-
this.manifoldConstraints = new ManifoldConstraintPool(
|
|
95
|
-
this.contactManifoldModule.currentManifoldCache.options.maxContactManifolds,
|
|
96
|
-
4
|
|
97
|
-
);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
warmStartConstraints(): void {
|
|
101
|
-
// warm start the contact constraints
|
|
102
|
-
for (const constraint of this.manifoldConstraints.manifoldConstraintPool) {
|
|
103
|
-
constraint.warmStart();
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
solveFrictionVelocityConstraints(
|
|
108
|
-
manifoldConstraint: ManifoldConstraint,
|
|
109
|
-
transformLocalToWorldA: Isometry,
|
|
110
|
-
transformLocalToWorldB: Isometry
|
|
111
|
-
): void {
|
|
112
|
-
// update the tangents
|
|
113
|
-
// TODO: is this needed during solving?
|
|
114
|
-
manifoldConstraint.updateTangentDirections();
|
|
115
|
-
|
|
116
|
-
for (let i = 0; i < manifoldConstraint.numContacts; i++) {
|
|
117
|
-
const contactConstraint = manifoldConstraint.contactConstraints!.getAtIndex(i)!;
|
|
118
|
-
// solve friction constraints if active
|
|
119
|
-
if (contactConstraint.tangentConstraint.isActive() || contactConstraint.bitangentConstraint.isActive()) {
|
|
120
|
-
let tangentLambda = contactConstraint.tangentConstraint.getTotalLambda(
|
|
121
|
-
manifoldConstraint.bodyA!,
|
|
122
|
-
manifoldConstraint.bodyB!,
|
|
123
|
-
manifoldConstraint.worldSpaceTangent
|
|
124
|
-
);
|
|
125
|
-
let bitangentLambda = contactConstraint.bitangentConstraint.getTotalLambda(
|
|
126
|
-
manifoldConstraint.bodyA!,
|
|
127
|
-
manifoldConstraint.bodyB!,
|
|
128
|
-
manifoldConstraint.worldSpaceBitangent
|
|
129
|
-
);
|
|
130
|
-
const totalLambdaSquared = squared(tangentLambda) + squared(bitangentLambda);
|
|
131
|
-
|
|
132
|
-
const maxLambda = manifoldConstraint.friction * contactConstraint.normalConstraint.totalLambda;
|
|
133
|
-
|
|
134
|
-
if (totalLambdaSquared > squared(maxLambda)) {
|
|
135
|
-
const scale = maxLambda / Math.sqrt(totalLambdaSquared);
|
|
136
|
-
tangentLambda *= scale;
|
|
137
|
-
bitangentLambda *= scale;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
worldPointA.transformVectorFromMat4(contactConstraint.localPositionA, transformLocalToWorldA.matrix);
|
|
141
|
-
worldPointB.transformVectorFromMat4(contactConstraint.localPositionB, transformLocalToWorldB.matrix);
|
|
142
|
-
|
|
143
|
-
contactConstraint.tangentConstraint.applyImpulse(
|
|
144
|
-
manifoldConstraint.bodyA!,
|
|
145
|
-
manifoldConstraint.bodyB!,
|
|
146
|
-
manifoldConstraint.inverseMassA,
|
|
147
|
-
manifoldConstraint.inverseMassB,
|
|
148
|
-
manifoldConstraint.worldSpaceTangent,
|
|
149
|
-
tangentLambda
|
|
150
|
-
);
|
|
151
|
-
|
|
152
|
-
contactConstraint.bitangentConstraint.applyImpulse(
|
|
153
|
-
manifoldConstraint.bodyA!,
|
|
154
|
-
manifoldConstraint.bodyB!,
|
|
155
|
-
manifoldConstraint.inverseMassA,
|
|
156
|
-
manifoldConstraint.inverseMassB,
|
|
157
|
-
manifoldConstraint.worldSpaceBitangent,
|
|
158
|
-
bitangentLambda
|
|
159
|
-
);
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
solveNonPenetrationVelocityConstraints(
|
|
165
|
-
manifoldConstraint: ManifoldConstraint,
|
|
166
|
-
transformLocalToWorldA: Isometry,
|
|
167
|
-
transformLocalToWorldB: Isometry
|
|
168
|
-
): void {
|
|
169
|
-
for (let i = 0; i < manifoldConstraint.numContacts; i++) {
|
|
170
|
-
const contactConstraint = manifoldConstraint.contactConstraints!.getAtIndex(i)!;
|
|
171
|
-
|
|
172
|
-
let totalLambda = contactConstraint.normalConstraint.getTotalLambda(
|
|
173
|
-
manifoldConstraint.bodyA!,
|
|
174
|
-
manifoldConstraint.bodyB!,
|
|
175
|
-
manifoldConstraint.worldSpaceNormal
|
|
176
|
-
);
|
|
177
|
-
|
|
178
|
-
totalLambda = clamp(totalLambda, 0, Infinity);
|
|
179
|
-
|
|
180
|
-
worldPointA.transformVectorFromMat4(contactConstraint.localPositionA, transformLocalToWorldA.matrix);
|
|
181
|
-
worldPointB.transformVectorFromMat4(contactConstraint.localPositionB, transformLocalToWorldB.matrix);
|
|
182
|
-
|
|
183
|
-
// apply the normal impulse
|
|
184
|
-
contactConstraint.normalConstraint.applyImpulse(
|
|
185
|
-
manifoldConstraint.bodyA!,
|
|
186
|
-
manifoldConstraint.bodyB!,
|
|
187
|
-
manifoldConstraint.inverseMassA,
|
|
188
|
-
manifoldConstraint.inverseMassB,
|
|
189
|
-
manifoldConstraint.worldSpaceNormal,
|
|
190
|
-
totalLambda
|
|
191
|
-
);
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
solveVelocityConstraint(constraint: ManifoldConstraint): void {
|
|
196
|
-
transformLocalToWorldA.fromRotationAndTranslation(
|
|
197
|
-
constraint.bodyA!.orientation,
|
|
198
|
-
constraint.bodyA!.computedCenterOfMassPosition
|
|
199
|
-
);
|
|
200
|
-
transformLocalToWorldB.fromRotationAndTranslation(
|
|
201
|
-
constraint.bodyB!.orientation,
|
|
202
|
-
constraint.bodyB!.computedCenterOfMassPosition
|
|
203
|
-
);
|
|
204
|
-
|
|
205
|
-
this.solveFrictionVelocityConstraints(constraint, transformLocalToWorldA, transformLocalToWorldB);
|
|
206
|
-
this.solveNonPenetrationVelocityConstraints(constraint, transformLocalToWorldA, transformLocalToWorldB);
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
solveVelocityConstraintsStep(): void {
|
|
210
|
-
for (const constraint of this.manifoldConstraints.manifoldConstraintPool) {
|
|
211
|
-
this.solveVelocityConstraint(constraint);
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
updateNonPenetrationConstraint(
|
|
216
|
-
constraint: ContactConstraint,
|
|
217
|
-
bodyA: Body,
|
|
218
|
-
bodyB: Body,
|
|
219
|
-
inverseMassA: number,
|
|
220
|
-
inverseMassB: number,
|
|
221
|
-
worldContactPointA: Vec3,
|
|
222
|
-
worldContactPointB: Vec3,
|
|
223
|
-
worldSpaceNormal: Vec3
|
|
224
|
-
): void {
|
|
225
|
-
averageWorldContactPoint.averageOfVectors(worldContactPointA, worldContactPointB);
|
|
226
|
-
momentArmA.subtractVectors(averageWorldContactPoint, bodyA.computedCenterOfMassPosition);
|
|
227
|
-
momentArmB.subtractVectors(averageWorldContactPoint, bodyB.computedCenterOfMassPosition);
|
|
228
|
-
|
|
229
|
-
constraint.normalConstraint.initializeWithMassOverride(
|
|
230
|
-
bodyA,
|
|
231
|
-
bodyB,
|
|
232
|
-
inverseMassA,
|
|
233
|
-
inverseMassB,
|
|
234
|
-
momentArmA,
|
|
235
|
-
momentArmB,
|
|
236
|
-
worldSpaceNormal
|
|
237
|
-
);
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
solvePositionConstraintWithMassOverride(
|
|
241
|
-
constraint: ContactConstraint,
|
|
242
|
-
bodyA: Body,
|
|
243
|
-
bodyB: Body,
|
|
244
|
-
inverseMassA: number,
|
|
245
|
-
inverseMassB: number,
|
|
246
|
-
direction: Vec3,
|
|
247
|
-
constraintValue: number,
|
|
248
|
-
baumgarteValue: number,
|
|
249
|
-
worldContactPointA: Vec3,
|
|
250
|
-
worldContactPointB: Vec3
|
|
251
|
-
): void {
|
|
252
|
-
// do nothing if no constraint or spring is active
|
|
253
|
-
if (constraintValue === 0 || constraint.normalConstraint.isSpringActive()) {
|
|
254
|
-
return;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
const lambda = -constraint.normalConstraint.effectiveMass * baumgarteValue * constraintValue;
|
|
258
|
-
|
|
259
|
-
if (bodyA.type === BodyType.dynamic) {
|
|
260
|
-
positionDeltaA.scaleVector(direction, -lambda * inverseMassA);
|
|
261
|
-
bodyA.world?.broadphase.markDynamicBodyAsDirty(bodyA as WithPool<Body>);
|
|
262
|
-
bodyA.computedCenterOfMassPosition.addVectors(bodyA.computedCenterOfMassPosition, positionDeltaA);
|
|
263
|
-
|
|
264
|
-
rotationDeltaA.scaleVector(constraint.normalConstraint.mInvI1_R1PlusUxAxis, lambda);
|
|
265
|
-
bodyA.subRotationDelta(rotationDeltaA);
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
if (bodyB.type === BodyType.dynamic) {
|
|
269
|
-
positionDeltaB.scaleVector(direction, +lambda * inverseMassB);
|
|
270
|
-
bodyB.world?.broadphase.markDynamicBodyAsDirty(bodyB as WithPool<Body>);
|
|
271
|
-
bodyB.computedCenterOfMassPosition.addVectors(bodyB.computedCenterOfMassPosition, positionDeltaB);
|
|
272
|
-
|
|
273
|
-
rotationDeltaB.scaleVector(constraint.normalConstraint.mInvI2_R2xAxis, lambda);
|
|
274
|
-
bodyB.addRotationDelta(rotationDeltaB);
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
solvePositionConstraint(
|
|
279
|
-
manifoldConstraint: ManifoldConstraint,
|
|
280
|
-
penetrationSlop: number,
|
|
281
|
-
maxPenetrationDistance: number,
|
|
282
|
-
baumgarte: number
|
|
283
|
-
): void {
|
|
284
|
-
// get body transforms
|
|
285
|
-
transformAToWorld.fromRotationTranslation(
|
|
286
|
-
manifoldConstraint.bodyA!.orientation,
|
|
287
|
-
manifoldConstraint.bodyA!.computedCenterOfMassPosition
|
|
288
|
-
);
|
|
289
|
-
transformBToWorld.fromRotationTranslation(
|
|
290
|
-
manifoldConstraint.bodyB!.orientation,
|
|
291
|
-
manifoldConstraint.bodyB!.computedCenterOfMassPosition
|
|
292
|
-
);
|
|
293
|
-
|
|
294
|
-
// recompute the world contact points from the cached local contact points
|
|
295
|
-
|
|
296
|
-
for (let i = 0; i < manifoldConstraint.numContacts; i++) {
|
|
297
|
-
const contactConstraint = manifoldConstraint.contactConstraints!.getAtIndex(i)!;
|
|
298
|
-
worldContactPointA.transformVectorFromMat4(contactConstraint.localPositionA, transformAToWorld);
|
|
299
|
-
worldContactPointB.transformVectorFromMat4(contactConstraint.localPositionB, transformBToWorld);
|
|
300
|
-
|
|
301
|
-
penetrationVector.subtractVectors(worldContactPointB, worldContactPointA);
|
|
302
|
-
|
|
303
|
-
// negative if penetrated, positive if separated
|
|
304
|
-
const separation = Math.max(
|
|
305
|
-
penetrationVector.dot(manifoldConstraint.worldSpaceNormal) + penetrationSlop,
|
|
306
|
-
-maxPenetrationDistance
|
|
307
|
-
);
|
|
308
|
-
|
|
309
|
-
// only solve if separation is negative (penetrated)
|
|
310
|
-
if (separation < 0) {
|
|
311
|
-
this.updateNonPenetrationConstraint(
|
|
312
|
-
contactConstraint,
|
|
313
|
-
manifoldConstraint.bodyA!,
|
|
314
|
-
manifoldConstraint.bodyB!,
|
|
315
|
-
manifoldConstraint.inverseMassA,
|
|
316
|
-
manifoldConstraint.inverseMassB,
|
|
317
|
-
worldContactPointA,
|
|
318
|
-
worldContactPointB,
|
|
319
|
-
manifoldConstraint.worldSpaceNormal
|
|
320
|
-
);
|
|
321
|
-
|
|
322
|
-
this.solvePositionConstraintWithMassOverride(
|
|
323
|
-
contactConstraint,
|
|
324
|
-
manifoldConstraint.bodyA!,
|
|
325
|
-
manifoldConstraint.bodyB!,
|
|
326
|
-
manifoldConstraint.inverseMassA,
|
|
327
|
-
manifoldConstraint.inverseMassB,
|
|
328
|
-
manifoldConstraint.worldSpaceNormal,
|
|
329
|
-
separation,
|
|
330
|
-
baumgarte,
|
|
331
|
-
worldContactPointA,
|
|
332
|
-
worldContactPointB
|
|
333
|
-
);
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
solvePositionConstraintsStep(penetrationSlop: number, maxPenetrationDistance: number, baumgarte: number) {
|
|
339
|
-
for (const constraint of this.manifoldConstraints.manifoldConstraintPool) {
|
|
340
|
-
this.solvePositionConstraint(constraint, penetrationSlop, maxPenetrationDistance, baumgarte);
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
cacheLambdas(): void {
|
|
345
|
-
// copy the total lambdas from each manifold constraint to the corresponding cached manifold
|
|
346
|
-
const cache = this.contactManifoldModule.currentManifoldCache;
|
|
347
|
-
|
|
348
|
-
for (const constraint of this.manifoldConstraints.manifoldConstraintPool) {
|
|
349
|
-
const manifold = cache.manifoldMap.get(constraint.key);
|
|
350
|
-
if (manifold === undefined) {
|
|
351
|
-
throw new Error(`expected to find manifold ${constraint.key} in cache`);
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
constraint.copyLambdasToManifold(manifold);
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
clearContactConstraints(): void {
|
|
359
|
-
destroyAllInstancesInPool(this.manifoldConstraints.manifoldConstraintPool);
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
addContactConstraints(
|
|
363
|
-
bodyA: Body,
|
|
364
|
-
bodyB: Body,
|
|
365
|
-
manifold: ContactManifold,
|
|
366
|
-
timeStepSizeSeconds: number,
|
|
367
|
-
isWarmStartingEnabled: boolean,
|
|
368
|
-
minVelocityForElasticContact: number
|
|
369
|
-
) {
|
|
370
|
-
// ensure the world space normal is normalized
|
|
371
|
-
manifold.worldSpaceNormal.normalize();
|
|
372
|
-
// console.log("adding contact constraint", manifold.worldSpaceNormal.toObject());
|
|
373
|
-
|
|
374
|
-
// #v-ifdef DEV
|
|
375
|
-
// assert that the direction is normalized within some tolerance
|
|
376
|
-
if (manifold.worldSpaceNormal.isNormalized(1e-5) === false) {
|
|
377
|
-
throw new Error(`expected normalized manifold normal, got ${manifold.worldSpaceNormal} instead`);
|
|
378
|
-
}
|
|
379
|
-
// #v-endif
|
|
380
|
-
|
|
381
|
-
const cachedManifold = this.contactManifoldModule.currentManifoldCache.manifolds.createContactManifold({
|
|
382
|
-
bodyA: bodyA,
|
|
383
|
-
bodyB: bodyB,
|
|
384
|
-
worldSpaceNormal: manifold.worldSpaceNormal,
|
|
385
|
-
firstWorldSpaceNormal: manifold.firstWorldSpaceNormal,
|
|
386
|
-
penetrationDepth: manifold.penetrationDepth,
|
|
387
|
-
nextContactManifold: manifold.nextContactManifold,
|
|
388
|
-
numContacts: manifold.numContacts,
|
|
389
|
-
subShapeIdA: manifold.subShapeIdA,
|
|
390
|
-
subShapeIdB: manifold.subShapeIdB,
|
|
391
|
-
});
|
|
392
|
-
|
|
393
|
-
this.contactManifoldModule.currentManifoldCache.manifoldMap.set(cachedManifold.key, cachedManifold);
|
|
394
|
-
|
|
395
|
-
// for the cached manifold, we store the worldSpaceNormal in the space of bodyB
|
|
396
|
-
|
|
397
|
-
inverseIsometryB.fromInverseRotationAndTranslation(bodyB.orientation, bodyB.computedCenterOfMassPosition);
|
|
398
|
-
inverseIsometryB.matrix.multiply3x3(cachedManifold.worldSpaceNormal, manifold.worldSpaceNormal);
|
|
399
|
-
cachedManifold.worldSpaceNormal.normalize();
|
|
400
|
-
|
|
401
|
-
const previousManifold = this.contactManifoldModule.previousManifoldCache.manifoldMap.get(cachedManifold.key);
|
|
402
|
-
// check if the manifold exists in the previous cache
|
|
403
|
-
if (previousManifold !== undefined) {
|
|
404
|
-
// persistent contact
|
|
405
|
-
// TODO: get the previous lambdas per constraint and use them to warm start the constraints
|
|
406
|
-
cachedManifold.copyLambdas(previousManifold);
|
|
407
|
-
} else {
|
|
408
|
-
// new contact
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
// no constraints if either body does not resolve contacts
|
|
412
|
-
if (bodyA.colliderType !== ColliderType.resolveContact && bodyB.colliderType !== ColliderType.resolveContact) {
|
|
413
|
-
return;
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
// no constraints if neither body is dynamic
|
|
417
|
-
if (bodyA.type !== BodyType.dynamic && bodyB.type !== BodyType.dynamic) {
|
|
418
|
-
return;
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
const manifoldConstraint = this.manifoldConstraints.createManifoldConstraint({
|
|
422
|
-
bodyA: bodyA,
|
|
423
|
-
bodyB: bodyB,
|
|
424
|
-
subShapeIdA: manifold.subShapeIdA,
|
|
425
|
-
subShapeIdB: manifold.subShapeIdB,
|
|
426
|
-
friction: getCombinedFriction(bodyA, bodyB, CoefficientFunctionType.average),
|
|
427
|
-
restitution: getCombinedRestitution(bodyA, bodyB, CoefficientFunctionType.average),
|
|
428
|
-
inverseMassA: bodyA.inverseMass,
|
|
429
|
-
inverseMassB: bodyB.inverseMass,
|
|
430
|
-
numContacts: 0,
|
|
431
|
-
});
|
|
432
|
-
|
|
433
|
-
// TODO: which normal should we use, A or B?
|
|
434
|
-
manifoldConstraint.worldSpaceNormal.normalizeVector(manifold.worldSpaceNormal);
|
|
435
|
-
manifoldConstraint.updateTangentDirections();
|
|
436
|
-
manifoldConstraint.numContacts = manifold.numContacts;
|
|
437
|
-
|
|
438
|
-
// get mass properties
|
|
439
|
-
|
|
440
|
-
bodyA.computeInverseInertiaTensor(inverseInertiaTensorA);
|
|
441
|
-
bodyB.computeInverseInertiaTensor(inverseInertiaTensorB);
|
|
442
|
-
|
|
443
|
-
// get the world to local transform for each body
|
|
444
|
-
|
|
445
|
-
worldToLocalA.fromInverseRotationAndTranslation(bodyA.orientation, bodyA.computedCenterOfMassPosition);
|
|
446
|
-
worldToLocalB.fromInverseRotationAndTranslation(bodyB.orientation, bodyB.computedCenterOfMassPosition);
|
|
447
|
-
|
|
448
|
-
// iterate over the contacts and add the constraints
|
|
449
|
-
for (let i = 0; i < manifold.numContacts; i++) {
|
|
450
|
-
this.addContactConstraint(
|
|
451
|
-
bodyA,
|
|
452
|
-
bodyB,
|
|
453
|
-
manifold,
|
|
454
|
-
manifoldConstraint,
|
|
455
|
-
inverseInertiaTensorA,
|
|
456
|
-
inverseInertiaTensorB,
|
|
457
|
-
worldToLocalA,
|
|
458
|
-
worldToLocalB,
|
|
459
|
-
cachedManifold,
|
|
460
|
-
i,
|
|
461
|
-
timeStepSizeSeconds,
|
|
462
|
-
isWarmStartingEnabled,
|
|
463
|
-
minVelocityForElasticContact
|
|
464
|
-
);
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
// set the new cached manifold as the head of the current manifold cache
|
|
468
|
-
const contactPairKey = createContactPairKey(manifold.bodyA!, manifold.bodyB!);
|
|
469
|
-
const contactPair = this.contactManifoldModule.currentManifoldCache.pairMap.get(contactPairKey);
|
|
470
|
-
|
|
471
|
-
if (contactPair === undefined) {
|
|
472
|
-
throw new Error("contact pair not found");
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
// TODO: implement manifold list
|
|
476
|
-
const currentFirstManifold = contactPair.firstContactManifold;
|
|
477
|
-
if (currentFirstManifold === null) {
|
|
478
|
-
// case: no current first manifold, simply set the new manifold as the first
|
|
479
|
-
contactPair.firstContactManifold = cachedManifold;
|
|
480
|
-
} else {
|
|
481
|
-
// case: there is a current first manifold, set the current first as the next of the new manifold, and set the new manifold as the new first
|
|
482
|
-
cachedManifold.nextContactManifold = currentFirstManifold;
|
|
483
|
-
contactPair.firstContactManifold = cachedManifold;
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
contactPair.firstContactManifold = cachedManifold;
|
|
487
|
-
|
|
488
|
-
// reset the current cached manifold's lambdas
|
|
489
|
-
cachedManifold.resetLambdas();
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
addContactConstraint(
|
|
493
|
-
bodyA: Body,
|
|
494
|
-
bodyB: Body,
|
|
495
|
-
manifold: ContactManifold,
|
|
496
|
-
manifoldConstraint: ManifoldConstraint,
|
|
497
|
-
inverseInertiaTensorA: Mat3,
|
|
498
|
-
inverseInertiaTensorB: Mat3,
|
|
499
|
-
worldToLocalA: Mat4,
|
|
500
|
-
worldToLocalB: Mat4,
|
|
501
|
-
cachedManifold: ContactManifold,
|
|
502
|
-
index: number,
|
|
503
|
-
timeStepSizeSeconds: number,
|
|
504
|
-
isWarmStartingEnabled: boolean,
|
|
505
|
-
minVelocityForElasticContact: number
|
|
506
|
-
): void {
|
|
507
|
-
const contactConstraint = manifoldConstraint.contactConstraints!.getAtIndex(index)!;
|
|
508
|
-
// init the constraint directions
|
|
509
|
-
|
|
510
|
-
// init the constraint local contact points
|
|
511
|
-
|
|
512
|
-
manifold.getContactPointA(worldContactPointA, index);
|
|
513
|
-
worldContactPointA.addVector(manifold.baseTranslation);
|
|
514
|
-
manifold.getContactPointB(worldContactPointB, index);
|
|
515
|
-
worldContactPointB.addVector(manifold.baseTranslation);
|
|
516
|
-
|
|
517
|
-
contactConstraint.localPositionA.transformVectorFromMat4(worldContactPointA, worldToLocalA);
|
|
518
|
-
contactConstraint.localPositionB.transformVectorFromMat4(worldContactPointB, worldToLocalB);
|
|
519
|
-
|
|
520
|
-
// init the lambdas to zero
|
|
521
|
-
contactConstraint.normalConstraint.totalLambda = 0;
|
|
522
|
-
contactConstraint.tangentConstraint.totalLambda = 0;
|
|
523
|
-
contactConstraint.bitangentConstraint.totalLambda = 0;
|
|
524
|
-
|
|
525
|
-
// check if we have a close contact point from last update
|
|
526
|
-
const oldManifold = this.contactManifoldModule.previousManifoldCache.manifoldMap.get(manifold.key);
|
|
527
|
-
if (oldManifold !== undefined) {
|
|
528
|
-
// check if any of the contact points are close enough to be considered the same contact
|
|
529
|
-
|
|
530
|
-
for (let i = 0; i < oldManifold.numContacts; i++) {
|
|
531
|
-
oldManifold.getContactPointA(oldContactPointA, index);
|
|
532
|
-
oldManifold.getContactPointB(oldContactPointB, index);
|
|
533
|
-
|
|
534
|
-
if (
|
|
535
|
-
contactConstraint.localPositionA.isClose(oldContactPointA, contactPointPreserveLambdaMaxDistanceSquared) &&
|
|
536
|
-
contactConstraint.localPositionB.isClose(oldContactPointB, contactPointPreserveLambdaMaxDistanceSquared)
|
|
537
|
-
) {
|
|
538
|
-
if (isWarmStartingEnabled) {
|
|
539
|
-
contactConstraint.copyLambdaFromManifold(oldManifold, index);
|
|
540
|
-
}
|
|
541
|
-
break;
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
// store the contact points for next frame
|
|
547
|
-
cachedManifold.setContactPointA(contactConstraint.localPositionA, index);
|
|
548
|
-
cachedManifold.setContactPointB(contactConstraint.localPositionB, index);
|
|
549
|
-
|
|
550
|
-
// 1. init the velocity constraint
|
|
551
|
-
// wcp.TemplatedCalculateFrictionAndNonPenetrationConstraintProperties<Type1, Type2>(deltaTime, bodyA, bodyB, inverseMassA, inverseMassB, inverseInertiaA, inverseInertiaB,
|
|
552
|
-
// worldContactPointA, worldContactPointB, worldSpaceNormal, worldSpaceTangent, worldSpaceBitangent, settings, mPhysicsSettings.mMinVelocityForRestitution);
|
|
553
|
-
|
|
554
|
-
// init the world moment arms
|
|
555
|
-
|
|
556
|
-
combinedWorldContactPoint.averageOfVectors(worldContactPointA, worldContactPointB);
|
|
557
|
-
worldMomentArmA.subtractVectors(combinedWorldContactPoint, bodyA.computedCenterOfMassPosition);
|
|
558
|
-
worldMomentArmB.subtractVectors(combinedWorldContactPoint, bodyB.computedCenterOfMassPosition);
|
|
559
|
-
|
|
560
|
-
// init the contact point velocities
|
|
561
|
-
|
|
562
|
-
bodyA.computeVelocityOfPointRelativeToCenterOfMass(contactVelocityA, worldMomentArmA);
|
|
563
|
-
bodyB.computeVelocityOfPointRelativeToCenterOfMass(contactVelocityB, worldMomentArmB);
|
|
564
|
-
contactVelocityAB.subtractVectors(contactVelocityB, contactVelocityA);
|
|
565
|
-
|
|
566
|
-
// init the relative contact point speed along the normal
|
|
567
|
-
const normalSpeed = contactVelocityAB.dot(manifoldConstraint.worldSpaceNormal);
|
|
568
|
-
|
|
569
|
-
// init the contact penetration along the normal (positive -> penetrated, negative -> separated)
|
|
570
|
-
|
|
571
|
-
penetrationVector.subtractVectors(worldContactPointA, worldContactPointB);
|
|
572
|
-
const penetration = penetrationVector.dot(manifoldConstraint.worldSpaceNormal);
|
|
573
|
-
|
|
574
|
-
// #v-ifdef DEV
|
|
575
|
-
// assert that penetration is positive, TODO: handle negative penetration (speculative contacts)
|
|
576
|
-
if (penetration < 0) {
|
|
577
|
-
// throw new Error("got here without being in contact, handling of speculative contacts not implemented yet");
|
|
578
|
-
}
|
|
579
|
-
// #v-endif
|
|
580
|
-
|
|
581
|
-
// init the speculative contact velocity bias
|
|
582
|
-
const speculativeContactVelocityBias = Math.max(0, -penetration / timeStepSizeSeconds);
|
|
583
|
-
|
|
584
|
-
// init velocity bias (handles restitution)
|
|
585
|
-
let normalVelocityBias: number;
|
|
586
|
-
|
|
587
|
-
const isVelocityBigEnoughForRestitution =
|
|
588
|
-
manifoldConstraint.restitution > 0 &&
|
|
589
|
-
normalSpeed < -minVelocityForElasticContact &&
|
|
590
|
-
normalSpeed < -speculativeContactVelocityBias;
|
|
591
|
-
|
|
592
|
-
if (isVelocityBigEnoughForRestitution) {
|
|
593
|
-
if (normalSpeed < -speculativeContactVelocityBias) {
|
|
594
|
-
normalVelocityBias = manifoldConstraint.restitution * normalSpeed;
|
|
595
|
-
} else {
|
|
596
|
-
normalVelocityBias = speculativeContactVelocityBias;
|
|
597
|
-
}
|
|
598
|
-
} else {
|
|
599
|
-
normalVelocityBias = speculativeContactVelocityBias;
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
// init the normal constraint
|
|
603
|
-
contactConstraint.normalConstraint.initialize(
|
|
604
|
-
bodyA,
|
|
605
|
-
bodyB,
|
|
606
|
-
manifoldConstraint.inverseMassA,
|
|
607
|
-
manifoldConstraint.inverseMassB,
|
|
608
|
-
inverseInertiaTensorA,
|
|
609
|
-
inverseInertiaTensorB,
|
|
610
|
-
worldMomentArmA,
|
|
611
|
-
worldMomentArmB,
|
|
612
|
-
manifoldConstraint.worldSpaceNormal,
|
|
613
|
-
normalVelocityBias
|
|
614
|
-
);
|
|
615
|
-
|
|
616
|
-
// init the tangent constraints
|
|
617
|
-
if (manifoldConstraint.friction === 0) {
|
|
618
|
-
contactConstraint.tangentConstraint.effectiveMass = 0;
|
|
619
|
-
contactConstraint.tangentConstraint.totalLambda = 0;
|
|
620
|
-
|
|
621
|
-
contactConstraint.bitangentConstraint.effectiveMass = 0;
|
|
622
|
-
contactConstraint.bitangentConstraint.totalLambda = 0;
|
|
623
|
-
return;
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
// get the surface velocity relative to the tangents
|
|
627
|
-
// TODO: this world space velocity is a placeholder for something that a user can pass in through settings
|
|
628
|
-
|
|
629
|
-
worldSpaceSurfaceVelocity.zero();
|
|
630
|
-
const tangentSpeed = worldSpaceSurfaceVelocity.dot(manifoldConstraint.worldSpaceTangent);
|
|
631
|
-
const bitangentSpeed = worldSpaceSurfaceVelocity.dot(manifoldConstraint.worldSpaceBitangent);
|
|
632
|
-
|
|
633
|
-
// init the tangent constraints
|
|
634
|
-
contactConstraint.tangentConstraint.initialize(
|
|
635
|
-
bodyA,
|
|
636
|
-
bodyB,
|
|
637
|
-
manifoldConstraint.inverseMassA,
|
|
638
|
-
manifoldConstraint.inverseMassB,
|
|
639
|
-
inverseInertiaTensorA,
|
|
640
|
-
inverseInertiaTensorB,
|
|
641
|
-
worldMomentArmA,
|
|
642
|
-
worldMomentArmB,
|
|
643
|
-
manifoldConstraint.worldSpaceTangent,
|
|
644
|
-
tangentSpeed
|
|
645
|
-
);
|
|
646
|
-
contactConstraint.bitangentConstraint.initialize(
|
|
647
|
-
bodyA,
|
|
648
|
-
bodyB,
|
|
649
|
-
manifoldConstraint.inverseMassA,
|
|
650
|
-
manifoldConstraint.inverseMassB,
|
|
651
|
-
inverseInertiaTensorA,
|
|
652
|
-
inverseInertiaTensorB,
|
|
653
|
-
worldMomentArmA,
|
|
654
|
-
worldMomentArmB,
|
|
655
|
-
manifoldConstraint.worldSpaceBitangent,
|
|
656
|
-
bitangentSpeed
|
|
657
|
-
);
|
|
658
|
-
}
|
|
659
|
-
}
|