@perplexdotgg/bounce 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (140) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +30 -0
  3. package/package.json +32 -0
  4. package/src/builders/ConvexHullBuilder.ts +437 -0
  5. package/src/builders/ConvexHullBuilder2d.ts +344 -0
  6. package/src/builders/ConvexHullBuilder3d.ts +1689 -0
  7. package/src/builders/HeightMapBuilder.ts +414 -0
  8. package/src/builders/TriangleMeshBuilder.ts +92 -0
  9. package/src/collision/CastShapesModule.ts +184 -0
  10. package/src/collision/CollideShapesModule.ts +152 -0
  11. package/src/collision/HeightMapCaster.ts +38 -0
  12. package/src/collision/HeightMapCollider.ts +33 -0
  13. package/src/collision/TriangleCaster.ts +249 -0
  14. package/src/collision/TriangleCollider.ts +308 -0
  15. package/src/collision/TriangleCollider2.ts +379 -0
  16. package/src/collision/activeEdge.ts +146 -0
  17. package/src/collision/cast/cast.ts +139 -0
  18. package/src/collision/cast/castCompoundVsCompound.ts +59 -0
  19. package/src/collision/cast/castCompoundVsConvex.ts +116 -0
  20. package/src/collision/cast/castConvexVsCompound.ts +123 -0
  21. package/src/collision/cast/castConvexVsConvex.ts +213 -0
  22. package/src/collision/cast/castConvexVsHeightMap.ts +73 -0
  23. package/src/collision/cast/castConvexVsTriangleMesh.ts +56 -0
  24. package/src/collision/cast/castRayVsCompound.ts +44 -0
  25. package/src/collision/cast/castRayVsConvex.ts +45 -0
  26. package/src/collision/cast/castRayVsHeightMap.ts +58 -0
  27. package/src/collision/cast/castRayVsTriangleMesh.ts +58 -0
  28. package/src/collision/closestPoints/closestPoints.ts +23 -0
  29. package/src/collision/closestPoints/computeBarycentricCoordinates2d.ts +32 -0
  30. package/src/collision/closestPoints/computeBarycentricCoordinates3d.ts +81 -0
  31. package/src/collision/closestPoints/computeClosestPointOnLine.ts +30 -0
  32. package/src/collision/closestPoints/computeClosestPointOnTetrahedron.ts +96 -0
  33. package/src/collision/closestPoints/computeClosestPointOnTriangle.ts +195 -0
  34. package/src/collision/closestPoints/isOriginOutsideOfPlane.ts +25 -0
  35. package/src/collision/closestPoints/isOriginOutsideOfTrianglePlanes.ts +72 -0
  36. package/src/collision/collide/collide.ts +146 -0
  37. package/src/collision/collide/collideCompoundVsCompound.ts +60 -0
  38. package/src/collision/collide/collideCompoundVsConvex.ts +59 -0
  39. package/src/collision/collide/collideCompoundVsHeightMap.ts +73 -0
  40. package/src/collision/collide/collideCompoundVsTriangleMesh.ts +56 -0
  41. package/src/collision/collide/collideConvexVsCompound.ts +57 -0
  42. package/src/collision/collide/collideConvexVsConvex.ts +225 -0
  43. package/src/collision/collide/collideConvexVsConvexImp.ts +236 -0
  44. package/src/collision/collide/collideConvexVsHeightMap.ts +53 -0
  45. package/src/collision/collide/collideConvexVsTriangleMesh.ts +58 -0
  46. package/src/collision/collide/collideHeightMapVsCompound.ts +69 -0
  47. package/src/collision/collide/collideHeightMapVsConvex.ts +53 -0
  48. package/src/collision/collide/collideSphereVsSphere.ts +81 -0
  49. package/src/collision/collide/collideTriangleMeshVsCompound.ts +58 -0
  50. package/src/collision/collide/collideTriangleMeshVsConvex.ts +58 -0
  51. package/src/collision/epa/EpaConvexHullBuilder.ts +397 -0
  52. package/src/collision/epa/StaticArray.ts +154 -0
  53. package/src/collision/epa/TriangleFactory.ts +32 -0
  54. package/src/collision/epa/arrays.ts +99 -0
  55. package/src/collision/epa/binaryHeap.ts +82 -0
  56. package/src/collision/epa/structs.ts +227 -0
  57. package/src/collision/gjk/GjkModule.ts +864 -0
  58. package/src/collision/gjk/PenetrationDepthModule.ts +493 -0
  59. package/src/collision/gjk/SupportPoints.ts +50 -0
  60. package/src/collision/imp/MinkowskiDifference.ts +36 -0
  61. package/src/collision/imp/computeExploredDistanceLowerUpperBound.ts +40 -0
  62. package/src/collision/imp/finalizeImpResult.ts +69 -0
  63. package/src/collision/imp/findContactImp.ts +196 -0
  64. package/src/collision/imp/imp.ts +28 -0
  65. package/src/collision/imp/incrementalMinimumDistanceExploreDirection.ts +207 -0
  66. package/src/collision/mpr/findPortal.ts +152 -0
  67. package/src/collision/mpr/mpr.ts +29 -0
  68. package/src/collision/mpr/updatePortal.ts +52 -0
  69. package/src/constraints/BaseConstraint.ts +50 -0
  70. package/src/constraints/ConstraintOptions.ts +22 -0
  71. package/src/constraints/ConstraintSolver.ts +119 -0
  72. package/src/constraints/DistanceConstraint.ts +229 -0
  73. package/src/constraints/FixedConstraint.ts +203 -0
  74. package/src/constraints/HingeConstraint.ts +460 -0
  75. package/src/constraints/PointConstraint.ts +108 -0
  76. package/src/constraints/components/AngleComponent.ts +226 -0
  77. package/src/constraints/components/AxisComponent.ts +263 -0
  78. package/src/constraints/components/HingeComponent.ts +215 -0
  79. package/src/constraints/components/Motor.ts +36 -0
  80. package/src/constraints/components/PointConstraintComponent.ts +179 -0
  81. package/src/constraints/components/RotationEulerComponent.ts +139 -0
  82. package/src/constraints/components/Spring.ts +30 -0
  83. package/src/constraints/components/SpringComponent.ts +71 -0
  84. package/src/constraints/types.ts +6 -0
  85. package/src/helpers.ts +147 -0
  86. package/src/index.ts +50 -0
  87. package/src/math/BasicTransform.ts +19 -0
  88. package/src/math/NumberValue.ts +13 -0
  89. package/src/math/isometry.ts +64 -0
  90. package/src/math/mat3.ts +529 -0
  91. package/src/math/mat4.ts +588 -0
  92. package/src/math/quat.ts +193 -0
  93. package/src/math/scalar.ts +81 -0
  94. package/src/math/tensor.ts +17 -0
  95. package/src/math/vec3.ts +589 -0
  96. package/src/math/vec4.ts +10 -0
  97. package/src/physics/Body.ts +581 -0
  98. package/src/physics/CollisionFilter.ts +52 -0
  99. package/src/physics/SleepModule.ts +163 -0
  100. package/src/physics/broadphase/BodyPairsModule.ts +363 -0
  101. package/src/physics/broadphase/BvhModule.ts +237 -0
  102. package/src/physics/broadphase/BvhTree.ts +803 -0
  103. package/src/physics/broadphase/ConstraintPairsModule.ts +385 -0
  104. package/src/physics/broadphase/TriangleMeshBvhTree.ts +379 -0
  105. package/src/physics/manifold/ContactManifold.ts +227 -0
  106. package/src/physics/manifold/ContactManifoldModule.ts +623 -0
  107. package/src/physics/manifold/Face.ts +119 -0
  108. package/src/physics/manifold/ManifoldCache.ts +116 -0
  109. package/src/physics/manifold/clipping/clipPolyVsEdge.ts +131 -0
  110. package/src/physics/manifold/clipping/clipPolyVsPlane.ts +73 -0
  111. package/src/physics/manifold/clipping/clipPolyVsPoly.ts +72 -0
  112. package/src/physics/narrowphase/CollideBodiesModule.ts +755 -0
  113. package/src/physics/solver/ContactConstraintModule.ts +659 -0
  114. package/src/physics/solver/ManifoldConstraint.ts +420 -0
  115. package/src/physics/solver/estimateCollisionResponse.ts +146 -0
  116. package/src/shape/Aabb.ts +400 -0
  117. package/src/shape/Box.ts +231 -0
  118. package/src/shape/Capsule.ts +332 -0
  119. package/src/shape/CompoundShape.ts +288 -0
  120. package/src/shape/Convex.ts +130 -0
  121. package/src/shape/ConvexHull.ts +423 -0
  122. package/src/shape/Cylinder.ts +313 -0
  123. package/src/shape/HeightMap.ts +511 -0
  124. package/src/shape/Line.ts +14 -0
  125. package/src/shape/Plane.ts +116 -0
  126. package/src/shape/Ray.ts +81 -0
  127. package/src/shape/Segment.ts +25 -0
  128. package/src/shape/Shape.ts +77 -0
  129. package/src/shape/Sphere.ts +181 -0
  130. package/src/shape/TransformedShape.ts +51 -0
  131. package/src/shape/Triangle.ts +122 -0
  132. package/src/shape/TriangleMesh.ts +186 -0
  133. package/src/types.ts +1 -0
  134. package/src/world.ts +1335 -0
  135. package/tests/BodyPairsModule.test.ts +71 -0
  136. package/tests/BvhTree.test.ts +406 -0
  137. package/tests/test.md +642 -0
  138. package/tests/vec3.test.ts +12 -0
  139. package/tsconfig.json +20 -0
  140. package/vite.config.js +40 -0
@@ -0,0 +1,581 @@
1
+ import {
2
+ BooleanType,
3
+ createClass,
4
+ LazyReferenceType,
5
+ MonomorphType,
6
+ NumberType,
7
+ PropertyDefinition,
8
+ PropertyDefinitionMap,
9
+ PropertyDefinitionReference,
10
+ ReferenceType,
11
+ WithPool,
12
+ } from "monomorph";
13
+ import { CoefficientFunctionType } from "./solver/ContactConstraintModule";
14
+ import { Vec3 } from "../math/vec3";
15
+ import { Quat } from "../math/quat";
16
+ import { Sphere } from "../shape/Sphere";
17
+ import { Face } from "./manifold/Face";
18
+ import { Mat4 } from "../math/mat4";
19
+ import { transformTensor } from "../math/tensor";
20
+ import { Mat3 } from "../math/mat3";
21
+ import { Box } from "../shape/Box";
22
+ import { Shape, ShapeType } from "../shape/Shape";
23
+ import { Aabb } from "../shape/Aabb";
24
+ import type { World } from "../world";
25
+ import { BodyPairEdge } from "./broadphase/BodyPairsModule";
26
+ import { BvhNode } from "./broadphase/BvhTree";
27
+ import { CompoundShape } from "../shape/CompoundShape";
28
+ import ConvexHull from "../shape/ConvexHull";
29
+ import { Capsule } from "../shape/Capsule";
30
+ import { HeightMap } from "../shape/HeightMap";
31
+ import { Cylinder } from "../shape/Cylinder";
32
+ import { TriangleMesh } from "../shape/TriangleMesh";
33
+ import { Isometry } from "../math/isometry";
34
+ import { AllFlag } from "./CollisionFilter";
35
+ import { coerceQuatArgument, coerceVec3Argument } from "../helpers";
36
+ import { ConstraintPairEdge } from "./broadphase/ConstraintPairsModule";
37
+
38
+ const centerOfMassTransform = /*@__PURE__*/ Mat4.create();
39
+ const shapeCenterOfMassInWorldSpace = /*@__PURE__*/ Vec3.create();
40
+ const negatedBaseOffset = /*@__PURE__*/ Vec3.create();
41
+ const localDirection = /*@__PURE__*/ Vec3.create();
42
+
43
+ const rotationVector = /*@__PURE__*/ Vec3.create();
44
+ const axis = /*@__PURE__*/ Vec3.create();
45
+ const rotationQuaternion = /*@__PURE__*/ Quat.create();
46
+
47
+ const quaternionDelta = /*@__PURE__*/ Quat.create();
48
+
49
+ const accumulatedForce = /*@__PURE__*/ Vec3.create();
50
+ const accelerationDueToForces = /*@__PURE__*/ Vec3.create();
51
+ const accelerationDueToGravity = /*@__PURE__*/ Vec3.create();
52
+ const linearAcceleration = /*@__PURE__*/ Vec3.create();
53
+ const angularAcceleration = /*@__PURE__*/ Vec3.create();
54
+
55
+ const worldToLocalTransform = /*@__PURE__*/ Isometry.create();
56
+ const inLocalSurfacePosition = /*@__PURE__*/ Vec3.create();
57
+
58
+ export const enum BodyType {
59
+ dynamic,
60
+ kinematic,
61
+ static,
62
+ }
63
+
64
+ export const enum ColliderType {
65
+ hasIntersectionOnly,
66
+ findContactManifoldOnly,
67
+ resolveContact,
68
+ attractor,
69
+ }
70
+
71
+ const bodyProps = {
72
+ type: NumberType(BodyType.dynamic) as PropertyDefinition<BodyType, true>,
73
+ position: MonomorphType(Vec3),
74
+ orientation: MonomorphType(Quat),
75
+ linearVelocity: MonomorphType(Vec3),
76
+ angularVelocity: MonomorphType(Vec3),
77
+ computedCenterOfMassPosition: MonomorphType(Vec3, undefined, true),
78
+ computedBounds: MonomorphType(Aabb, undefined, true),
79
+ previousPosition: MonomorphType(Vec3, undefined, true),
80
+ previousOrientation: MonomorphType(Quat, undefined, true),
81
+ isSleeping: BooleanType(false, true),
82
+ timeWithoutMoving: NumberType(0.0, true),
83
+ friction: NumberType(0.0),
84
+ restitution: NumberType(0.0),
85
+ frictionFunction: NumberType(CoefficientFunctionType.average) as PropertyDefinition<CoefficientFunctionType, true>,
86
+ restitutionFunction: NumberType(CoefficientFunctionType.average) as PropertyDefinition<CoefficientFunctionType, true>,
87
+ mass: NumberType(0.0),
88
+ density: NumberType(0.0),
89
+ inverseMass: NumberType(0.0, true),
90
+ computedLocalInverseInertia: MonomorphType(Mat3, undefined, true),
91
+ computedWorldInverseInertia: MonomorphType(Mat3, undefined, true),
92
+ colliderType: NumberType(ColliderType.resolveContact) as PropertyDefinition<ColliderType, true>,
93
+ gravityScale: NumberType(1.0),
94
+ linearForces: MonomorphType(Vec3, undefined, true),
95
+ angularForces: MonomorphType(Vec3, undefined, true),
96
+ copyForDiff: LazyReferenceType((() => Body) as () => never) as PropertyDefinitionReference<Body | null, true>,
97
+ firstPotentialPairEdge: LazyReferenceType((() => BodyPairEdge) as () => never) as PropertyDefinitionReference<
98
+ BodyPairEdge | null,
99
+ true
100
+ >,
101
+ belongsToGroups: NumberType(AllFlag),
102
+ collidesWithGroups: NumberType(AllFlag),
103
+ node: LazyReferenceType((() => BvhNode) as () => never) as PropertyDefinitionReference<BvhNode | null, true>,
104
+ shapeType: NumberType(0) as PropertyDefinition<ShapeType, true>,
105
+ // we need to do "as 'shape' ..." otherwise they are just 'string' and not the actual specific keys
106
+ ['shape' + ShapeType.box as 'shape0']: ReferenceType(Box, undefined, true),
107
+ ['shape' + ShapeType.capsule as 'shape1']: ReferenceType(Capsule, undefined, true),
108
+ ['shape' + ShapeType.compoundShape as 'shape2']: ReferenceType(CompoundShape, undefined, true),
109
+ ['shape' + ShapeType.convexHull as 'shape3']: ReferenceType(ConvexHull, undefined, true),
110
+ ['shape' + ShapeType.cylinder as 'shape4']: ReferenceType(Cylinder, undefined, true),
111
+ ['shape' + ShapeType.heightMap as 'shape5']: ReferenceType(HeightMap, undefined, true),
112
+ ['shape' + ShapeType.sphere as 'shape6']: ReferenceType(Sphere, undefined, true),
113
+ ['shape' + ShapeType.triangleMesh as 'shape7']: ReferenceType(TriangleMesh, undefined, true),
114
+ isSleepingEnabled: BooleanType(true),
115
+ linearDamping: NumberType(-1.0),
116
+ angularDamping: NumberType(-1.0),
117
+ firstPotentialConstraintPairEdge: LazyReferenceType(
118
+ (() => ConstraintPairEdge) as () => never
119
+ ) as PropertyDefinitionReference<ConstraintPairEdge | null, true>,
120
+ visitGeneration: NumberType(0, true),
121
+ } as const satisfies PropertyDefinitionMap;
122
+
123
+ const afterConstructorCode = `
124
+ this.world = null;
125
+ `;
126
+
127
+ export class Body extends createClass<Body, typeof bodyProps>(bodyProps, { afterConstructorCode }) {
128
+ declare world: World | null;
129
+
130
+ get shape(): Shape | null {
131
+ return this['shape' + this.shapeType as keyof typeof this] as Shape | null;
132
+ }
133
+
134
+ set shape(value: Shape) {
135
+ (this['shape' + this.shapeType as keyof typeof this] as (Shape | null)) = null;
136
+ (this['shape' + value.type as keyof typeof this] as (Shape | null)) = value;
137
+ this.shapeType = value.type;
138
+ updateBody(this);
139
+ }
140
+
141
+ // physics integration functions
142
+
143
+ /**
144
+ * integrates the linear velocity of a body
145
+ */
146
+ applyLinearVelocityIntegration(timeStepSizeSeconds: number) {
147
+ this.previousPosition.copy(this.computedCenterOfMassPosition);
148
+ this.computedCenterOfMassPosition.addScaled(this.linearVelocity, timeStepSizeSeconds);
149
+ }
150
+
151
+ applyAngularVelocityIntegration(timeStepSizeSeconds: number) {
152
+ rotationVector.scaleVector(this.angularVelocity, timeStepSizeSeconds);
153
+
154
+ // if the rotation is very small, just skip it
155
+ const angle = rotationVector.length();
156
+ if (angle > 1e-6) {
157
+ // create a quaternion using axis angle from the rotation vector
158
+
159
+ axis.scaleVector(rotationVector, 1 / angle);
160
+
161
+ rotationQuaternion.setAxisAngle(axis, angle);
162
+
163
+ // multiply the orientation by the rotation quaternion
164
+ this.previousOrientation.copy(this.orientation);
165
+ this.orientation.multiplyQuats(rotationQuaternion, this.orientation);
166
+
167
+ // normalize the orientation
168
+ this.orientation.normalize();
169
+ }
170
+ }
171
+
172
+ /**
173
+ * applies gravity to a body
174
+ */
175
+ applyAccelerationIntegration(
176
+ timeStepSizeSeconds: number,
177
+ gravity: Vec3,
178
+ isGravityEnabled: boolean,
179
+ linearDamping: number,
180
+ angularDamping: number,
181
+ maxLinearSpeed: number,
182
+ maxLinearSpeedSquared: number,
183
+ maxAngularSpeed: number,
184
+ maxAngularSpeedSquared: number
185
+ ) {
186
+ accelerationDueToForces.scaleVector(this.linearForces, this.inverseMass);
187
+ accelerationDueToGravity.scaleVector(gravity, isGravityEnabled ? this.gravityScale : 0);
188
+ linearAcceleration.addVectors(accelerationDueToForces, accelerationDueToGravity);
189
+ this.linearVelocity.addScaled(linearAcceleration, timeStepSizeSeconds);
190
+ this.dampLinearVelocity(timeStepSizeSeconds, this.linearDamping === -1 ? linearDamping : this.linearDamping);
191
+ this.clampLinearVelocity(maxLinearSpeed, maxLinearSpeedSquared);
192
+
193
+ this.computeEffectiveInverseInertiaVector(angularAcceleration, this.orientation, this.angularForces);
194
+ this.angularVelocity.addScaled(angularAcceleration, timeStepSizeSeconds);
195
+ this.dampAngularVelocity(timeStepSizeSeconds, this.angularDamping === -1 ? angularDamping : this.angularDamping);
196
+ this.clampAngularVelocity(maxAngularSpeed, maxAngularSpeedSquared);
197
+ }
198
+
199
+ // physics solve functions
200
+
201
+ subRotationDelta(rotationDelta: Vec3): void {
202
+ const angle = rotationDelta.length();
203
+ if (angle <= 1e-6) {
204
+ return;
205
+ }
206
+
207
+ axis.scaleVector(rotationDelta, 1 / angle);
208
+ quaternionDelta.setAxisAngle(axis, -angle);
209
+ this.orientation.multiplyQuats(quaternionDelta, this.orientation);
210
+ this.orientation.normalize();
211
+ }
212
+
213
+ addRotationDelta(rotationDelta: Vec3): void {
214
+ const angle = rotationDelta.length();
215
+ if (angle <= 1e-6) {
216
+ return;
217
+ }
218
+
219
+ axis.scaleVector(rotationDelta, 1 / angle);
220
+ quaternionDelta.setAxisAngle(axis, +angle);
221
+ this.orientation.multiplyQuats(quaternionDelta, this.orientation);
222
+ this.orientation.normalize();
223
+ }
224
+
225
+ addRotationPositionImpulse(impulse: Vec3, inverseEffectiveMass: Mat3) {
226
+ rotationDelta.transformVectorFromMat3(impulse, inverseEffectiveMass);
227
+ this.addRotationDelta(rotationDelta);
228
+ }
229
+
230
+ subRotationPositionImpulse(impulse: Vec3, inverseEffectiveMass: Mat3) {
231
+ rotationDelta.transformVectorFromMat3(impulse, inverseEffectiveMass);
232
+ this.subRotationDelta(rotationDelta);
233
+ }
234
+
235
+ computePointVelocity(out: Vec3, point: Vec3) {
236
+ out.crossVectors(point, this.angularVelocity);
237
+ out.subtractVectors(this.linearVelocity, out);
238
+ }
239
+
240
+ computeEffectiveInverseInertiaVector(out: Vec3, rotation: Quat, axis: Vec3): void {
241
+ // mask out the degrees of freedom that are not allowed
242
+
243
+ // TODO: this is a placeholder, we should mask out the degrees of freedom that are not allowed
244
+ angularDofsMask.set({ x: 1, y: 1, z: 1 });
245
+
246
+ maskedAxis.multiplyVectors(axis, angularDofsMask);
247
+
248
+ // multiply the vector by the inverse inertia
249
+
250
+ rotationMatrix.fromQuat(rotation); // TODO: add inertia rotation at some point
251
+
252
+ inverseRotationMatrix.transposeMatrix(rotationMatrix); // TODO: is transpose okay here? or invert?
253
+
254
+ rotatedMaskedAxis.transformVectorFromMat3(maskedAxis, inverseRotationMatrix);
255
+ rotatedMaskedAxis.transformVectorFromMat3(rotatedMaskedAxis, this.computedLocalInverseInertia);
256
+ rotatedMaskedAxis.transformVectorFromMat3(rotatedMaskedAxis, rotationMatrix);
257
+
258
+ out.multiplyVectors(rotatedMaskedAxis, angularDofsMask);
259
+ }
260
+
261
+ // TODO: avoid transforms when not necessary (eg, no change in orientation, no change in computedLocalInverseInertia)
262
+ computeInverseInertiaTensor(out: Mat3): void {
263
+ transformTensor(this.computedWorldInverseInertia, this.computedLocalInverseInertia, this.orientation);
264
+ out.copy(this.computedWorldInverseInertia);
265
+ }
266
+
267
+ computeVelocityOfPointRelativeToCenterOfMass(out: Vec3, point: Vec3): void {
268
+ out.crossVectors(this.angularVelocity, point);
269
+ out.addVector(this.linearVelocity);
270
+ }
271
+
272
+ computeInverseEffectiveMass(direction: Vec3) {
273
+ angularInverseMass.transformVectorFromMat3(direction, this.computedWorldInverseInertia);
274
+ return this.inverseMass + angularInverseMass.dot(direction);
275
+ }
276
+
277
+ // velocity transformation functions
278
+
279
+ dampLinearVelocity(timeStepSizeSeconds: number, linearDamping: number) {
280
+ this.linearVelocity.scale(Math.max(0, 1 - linearDamping * timeStepSizeSeconds));
281
+ }
282
+
283
+ dampAngularVelocity(timeStepSizeSeconds: number, angularDamping: number) {
284
+ this.angularVelocity.scale(Math.max(0, 1 - angularDamping * timeStepSizeSeconds));
285
+ }
286
+
287
+ clampLinearVelocity(maxSpeed: number, maxSpeedSquared: number) {
288
+ const squaredLinearSpeed = this.linearVelocity.squaredLength();
289
+ if (squaredLinearSpeed > maxSpeedSquared) {
290
+ this.linearVelocity.scale(maxSpeed / Math.sqrt(squaredLinearSpeed));
291
+ }
292
+ }
293
+
294
+ clampAngularVelocity(maxSpeed: number, maxSpeedSquared: number) {
295
+ const squaredAngularSpeed = this.angularVelocity.squaredLength();
296
+ if (squaredAngularSpeed > maxSpeedSquared) {
297
+ this.angularVelocity.scale(maxSpeed / Math.sqrt(squaredAngularSpeed));
298
+ }
299
+ }
300
+
301
+ // physics collision functions
302
+
303
+ /**
304
+ * direction is in world space
305
+ */
306
+ computeSupportingFace(out: Face, subShapeId: number, direction: Vec3, baseOffset: Vec3): void {
307
+ if (!this.shape) {
308
+ throw new Error("Body does not have a shape");
309
+ }
310
+
311
+ centerOfMassTransform.fromRotationTranslation(this.orientation, this.computedCenterOfMassPosition);
312
+ negatedBaseOffset.negateVector(baseOffset);
313
+ centerOfMassTransform.postTranslated(negatedBaseOffset);
314
+ centerOfMassTransform.multiply3x3Transposed(localDirection, direction);
315
+ this.shape.computeSupportingFace(out, subShapeId, localDirection, 1, centerOfMassTransform);
316
+ }
317
+
318
+ computeWorldSpaceSurfaceNormal(outWorldSpaceNormal: Vec3, inWorldSpacePosition: Vec3, subShapeId?: number): void {
319
+ // get world to local transform
320
+ worldToLocalTransform.fromInverseRotationAndTranslation(this.orientation, this.computedCenterOfMassPosition);
321
+
322
+ // transform position from world space to local space
323
+ inLocalSurfacePosition.transformVectorFromMat4(inWorldSpacePosition, worldToLocalTransform.matrix); // local space position
324
+
325
+ // get surface normal in local space
326
+ this.shape!.computeSurfaceNormal(outWorldSpaceNormal, inLocalSurfacePosition, subShapeId);
327
+
328
+ // transform surface normal from local space to world space
329
+ worldToLocalTransform.inverseRotateVector(outWorldSpaceNormal, outWorldSpaceNormal);
330
+
331
+ // normalize surface normal
332
+ outWorldSpaceNormal.normalize();
333
+ }
334
+
335
+ // diff functions
336
+
337
+ hasChanged() {
338
+ return (
339
+ this.copyForDiff !== null &&
340
+ (this.position.notEquals(this.copyForDiff.position) ||
341
+ this.orientation.notEquals(this.copyForDiff.orientation) ||
342
+ this.mass !== this.copyForDiff.mass ||
343
+ this.density !== this.copyForDiff.density ||
344
+ this.shape !== this.copyForDiff.shape ||
345
+ this.type !== this.copyForDiff.type)
346
+ );
347
+ }
348
+
349
+ commitChanges() {
350
+ if (this.hasChanged()) {
351
+ updateBody(this);
352
+ }
353
+ }
354
+
355
+ // impulse and force functions
356
+
357
+ applyLinearForce(force: Vec3) {
358
+ if (this.type !== BodyType.dynamic) {
359
+ return;
360
+ }
361
+
362
+ this.linearForces.addVectors(this.linearForces, force);
363
+
364
+ if (this.isSleeping) {
365
+ this.isSleeping = false;
366
+ this.timeWithoutMoving = 0;
367
+ }
368
+ }
369
+
370
+ applyAngularForce(force: Vec3) {
371
+ if (this.type !== BodyType.dynamic) {
372
+ return;
373
+ }
374
+
375
+ this.angularForces.addVectors(this.angularForces, force);
376
+
377
+ if (this.isSleeping) {
378
+ this.isSleeping = false;
379
+ this.timeWithoutMoving = 0;
380
+ }
381
+ }
382
+
383
+ applyForce(force: Vec3, point: Vec3, useLocalFrame: boolean = false) {
384
+ if (this.type !== BodyType.dynamic) {
385
+ return;
386
+ }
387
+
388
+ if (useLocalFrame) {
389
+ isometry.fromRotationAndTranslation(this.orientation, this.computedCenterOfMassPosition);
390
+ forcePointInWorldSpace.transformVectorFromMat4(point, isometry.matrix);
391
+ }
392
+
393
+ momentArm.subtractVectors(forcePointInWorldSpace, this.computedCenterOfMassPosition);
394
+ torque.crossVectors(momentArm, force);
395
+ this.angularForces.addVectors(this.angularForces, torque);
396
+
397
+ this.linearForces.addVectors(this.linearForces, force);
398
+
399
+ if (this.isSleeping) {
400
+ this.isSleeping = false;
401
+ this.timeWithoutMoving = 0;
402
+ }
403
+ }
404
+
405
+ applyLinearImpulse(impulse: Vec3) {
406
+ if (this.type !== BodyType.dynamic) {
407
+ return;
408
+ }
409
+
410
+ deltaVelocity.scaleVector(impulse, this.inverseMass);
411
+ this.linearVelocity.addVectors(this.linearVelocity, deltaVelocity);
412
+ this.clampLinearVelocity(this.world!.options.maxLinearSpeed, this.world!.maxLinearSpeedSquared);
413
+
414
+ if (this.isSleeping) {
415
+ this.isSleeping = false;
416
+ this.timeWithoutMoving = 0;
417
+ }
418
+ }
419
+
420
+ applyAngularImpulse(impulse: Vec3) {
421
+ if (this.type !== BodyType.dynamic) {
422
+ return;
423
+ }
424
+
425
+ deltaSpin.transformVectorFromMat3(impulse, this.computedWorldInverseInertia);
426
+ this.angularVelocity.addVectors(this.angularVelocity, deltaSpin);
427
+ this.clampAngularVelocity(this.world!.options.maxAngularSpeed, this.world!.maxAngularSpeedSquared);
428
+
429
+ if (this.isSleeping) {
430
+ this.isSleeping = false;
431
+ this.timeWithoutMoving = 0;
432
+ }
433
+ }
434
+
435
+ applyImpulse(impulse: Vec3, point: Vec3, useLocalFrame: boolean = false) {
436
+ if (this.type !== BodyType.dynamic) {
437
+ return;
438
+ }
439
+
440
+ if (useLocalFrame) {
441
+ isometry.fromRotationAndTranslation(this.orientation, this.computedCenterOfMassPosition);
442
+ impulsePointInWorldSpace.transformVectorFromMat4(point, isometry.matrix);
443
+ }
444
+
445
+ impulsePointInWorldSpace.copy(point);
446
+ deltaVelocity.scaleVector(impulse, this.inverseMass);
447
+ this.linearVelocity.addVectors(this.linearVelocity, deltaVelocity);
448
+ this.clampLinearVelocity(this.world!.options.maxLinearSpeed, this.world!.maxLinearSpeedSquared);
449
+
450
+ momentArm.subtractVectors(impulsePointInWorldSpace, this.computedCenterOfMassPosition);
451
+ torque.crossVectors(momentArm, impulse);
452
+ deltaSpin.transformVectorFromMat3(torque, this.computedWorldInverseInertia);
453
+ this.angularVelocity.addVectors(this.angularVelocity, deltaSpin);
454
+ this.clampAngularVelocity(this.world!.options.maxAngularSpeed, this.world!.maxAngularSpeedSquared);
455
+
456
+ if (this.isSleeping) {
457
+ this.isSleeping = false;
458
+ this.timeWithoutMoving = 0;
459
+ }
460
+ }
461
+
462
+ clearLinearForces() {
463
+ this.linearForces.zero();
464
+ }
465
+
466
+ clearAngularForces() {
467
+ this.angularForces.zero();
468
+ }
469
+
470
+ clearForces() {
471
+ this.linearForces.zero();
472
+ this.angularForces.zero();
473
+ }
474
+
475
+ // sleep functions
476
+
477
+ isReadyToSleep(sleepTimeThreshold: number) {
478
+ return this.type !== BodyType.dynamic || this.timeWithoutMoving >= sleepTimeThreshold;
479
+ }
480
+
481
+ sleep() {
482
+ if (this.type === BodyType.static) {
483
+ return;
484
+ }
485
+ this.isSleeping = true;
486
+ }
487
+
488
+ wakeUp() {
489
+ this.world!.sleepModule.wakeBodyUp(this);
490
+ }
491
+
492
+ markBodyAsDirty() {
493
+ this.world!.broadphase.markDynamicBodyAsDirty(this as WithPool<this>);
494
+ }
495
+ }
496
+ const forcePointInWorldSpace = /*@__PURE__*/ Vec3.create();
497
+ const impulsePointInWorldSpace = /*@__PURE__*/ Vec3.create();
498
+ const angularDofsMask = /*@__PURE__*/ Vec3.create();
499
+ const maskedAxis = /*@__PURE__*/ Vec3.create();
500
+ const rotationMatrix = /*@__PURE__*/ Mat3.create();
501
+ const inverseRotationMatrix = /*@__PURE__*/ Mat3.create();
502
+ const rotatedMaskedAxis = /*@__PURE__*/ Vec3.create();
503
+
504
+ const deltaSpin = /*@__PURE__*/ Vec3.create();
505
+ const torque = /*@__PURE__*/ Vec3.create();
506
+ const momentArm = /*@__PURE__*/ Vec3.create();
507
+ const deltaVelocity = /*@__PURE__*/ Vec3.create();
508
+ const angularInverseMass = /*@__PURE__*/ Vec3.create();
509
+ const rotationDelta = /*@__PURE__*/ Vec3.create();
510
+ const isometry = /*@__PURE__*/ Isometry.create();
511
+
512
+ const oldCreate = Body.create;
513
+ Body.create = function () {
514
+ const data = arguments[0];
515
+ coerceVec3Argument(data, "position");
516
+ coerceQuatArgument(data, "orientation");
517
+ const body = oldCreate.apply(this, arguments as any);
518
+ body.orientation.normalize();
519
+ body.previousPosition.copy(body.position);
520
+ body.previousOrientation.copy(body.orientation);
521
+
522
+ if (data.shape) {
523
+ body.shape = data.shape;
524
+ }
525
+ return body;
526
+ };
527
+
528
+ function updateCenterOfMassPosition(body: Body) {
529
+ shapeCenterOfMassInWorldSpace.copy(body.shape!.computedCenterOfMass);
530
+ shapeCenterOfMassInWorldSpace.transformByQuat(body.orientation);
531
+ body.computedCenterOfMassPosition.addVectors(body.position, shapeCenterOfMassInWorldSpace);
532
+ }
533
+
534
+ export function updateWorldBounds(body: Body) {
535
+ body.shape!.computeWorldBounds(body.computedBounds, body.computedCenterOfMassPosition, body.orientation);
536
+ }
537
+
538
+ function updateMassProperties(body: Body) {
539
+ if (body.type === BodyType.static) {
540
+ body.density = Infinity;
541
+ body.mass = Infinity;
542
+ body.inverseMass = 0;
543
+ body.computedLocalInverseInertia.zero();
544
+ body.computedWorldInverseInertia.zero();
545
+ return;
546
+ }
547
+
548
+ if (!body.shape) {
549
+ throw new Error("DynamicBody.computeMassProperties: shape must be set");
550
+ }
551
+
552
+ const volume = body.shape.computedVolume;
553
+
554
+ // use default density if neither is specified
555
+ if (body.mass === 0 && body.density === 0) {
556
+ body.density = 1000;
557
+ body.mass = volume * body.density;
558
+ }
559
+ // derive mass if density is specified
560
+ else if (body.mass === 0) {
561
+ body.mass = volume * body.density;
562
+ }
563
+ // derive density if mass is specified
564
+ else if (body.density === 0) {
565
+ body.density = body.mass / volume;
566
+ }
567
+ // derive mass if both are specified
568
+ else {
569
+ body.mass = volume * body.density;
570
+ }
571
+
572
+ body.inverseMass = 1 / body.mass;
573
+ body.shape.computeInverseInertiaTensor(body.computedLocalInverseInertia, body.mass);
574
+ transformTensor(body.computedWorldInverseInertia, body.computedLocalInverseInertia, body.orientation);
575
+ }
576
+
577
+ export function updateBody(body: Body) {
578
+ updateMassProperties(body);
579
+ updateCenterOfMassPosition(body);
580
+ updateWorldBounds(body);
581
+ }
@@ -0,0 +1,52 @@
1
+ export const NoneFlag = 0;
2
+ export const AllFlag = ~(~0 << 31);
3
+
4
+ export function createBitFlags<T extends readonly string[]>(
5
+ keys: T
6
+ ): {
7
+ [K in T[number] | "None" | "All"]: number;
8
+ } {
9
+ const result: any = {};
10
+
11
+ result["None"] = 0;
12
+ result["All"] = ~0;
13
+
14
+ let index = 0;
15
+ for (const key of keys) {
16
+ result[key] = 1 << index++;
17
+ }
18
+
19
+ return result;
20
+ }
21
+
22
+ export function shouldPairCollide(groupA: number, maskA: number, groupB: number, maskB: number): boolean {
23
+ return (groupA & maskB) !== 0 && (groupB & maskA) !== 0;
24
+ }
25
+
26
+ export function shouldCollide(maskA: number, groupB: number): boolean {
27
+ return (groupB & maskA) !== 0;
28
+ }
29
+
30
+ export function addFlag(flag: number, value: number): number {
31
+ return flag | value;
32
+ }
33
+
34
+ export function removeFlag(flag: number, value: number): number {
35
+ return flag & ~value;
36
+ }
37
+
38
+ export function toggleFlag(flag: number, value: number): number {
39
+ return flag ^ value;
40
+ }
41
+
42
+ export function hasFlag(flag: number, value: number): boolean {
43
+ return (flag & value) !== 0;
44
+ }
45
+
46
+ export function doesNotHaveFlag(flag: number, value: number): boolean {
47
+ return (flag & value) === 0;
48
+ }
49
+
50
+ export function setFlags(...values: number[]): number {
51
+ return values.reduce((acc, value) => acc | value, 0);
52
+ }