@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,36 @@
1
+ import { createClass, MonomorphType, NumberType, PropertyDefinitionMap } from "monomorph";
2
+ import { Spring, SpringMode } from "./Spring";
3
+
4
+ export const enum MotorMode {
5
+ Off,
6
+ Position,
7
+ Velocity,
8
+ }
9
+
10
+ const motorProps = {
11
+ spring: MonomorphType(Spring, { mode: SpringMode.UseFrequency, damping: 1.0, frequency: 2.0, stiffness: 2.0 }),
12
+ minForce: NumberType(-Infinity),
13
+ maxForce: NumberType(+Infinity),
14
+ minTorque: NumberType(-Infinity),
15
+ maxTorque: NumberType(+Infinity),
16
+ mode: NumberType(MotorMode.Off),
17
+ } as const satisfies PropertyDefinitionMap;
18
+
19
+ export class Motor extends createClass<Motor, typeof motorProps>(motorProps) {
20
+ reset(): void {
21
+ this.spring.reset();
22
+ this.spring.mode = SpringMode.UseFrequency;
23
+ this.spring.damping = 1.0;
24
+ this.spring.frequency = 2.0;
25
+ this.spring.stiffness = 2.0;
26
+ this.minForce = -Infinity;
27
+ this.maxForce = +Infinity;
28
+ this.minTorque = -Infinity;
29
+ this.maxTorque = +Infinity;
30
+ this.mode = MotorMode.Off;
31
+ }
32
+
33
+ isValid(): boolean {
34
+ return this.spring.isValid() && this.minForce <= this.maxForce && this.minTorque <= this.maxTorque;
35
+ }
36
+ }
@@ -0,0 +1,179 @@
1
+ import {
2
+ createClass,
3
+ LazyReferenceType,
4
+ MonomorphType,
5
+ PropertyDefinitionMap,
6
+ PropertyDefinitionReference,
7
+ } from "monomorph";
8
+ import { ConstraintOptions } from "../ConstraintOptions";
9
+ import { Vec3 } from "../../math/vec3";
10
+ import { Mat3 } from "../../math/mat3";
11
+ import type { World } from "../../world";
12
+ import { Body, BodyType } from "../../physics/Body";
13
+
14
+ const pointConstraintComponentProps = {
15
+ bodyA: LazyReferenceType((() => Body) as () => never) as PropertyDefinitionReference<Body | null, true>,
16
+ bodyB: LazyReferenceType((() => Body) as () => never) as PropertyDefinitionReference<Body | null, true>,
17
+ options: MonomorphType(ConstraintOptions, {
18
+ positionBaumgarte: 0.8,
19
+ velocityBaumgarte: 1.0,
20
+ strength: 1.0,
21
+ }),
22
+ momentArmA: MonomorphType(Vec3, undefined, true),
23
+ momentArmB: MonomorphType(Vec3, undefined, true),
24
+ effectiveMass: MonomorphType(Mat3, undefined, true),
25
+ effectiveInverseInertiaA: MonomorphType(Mat3, undefined, true),
26
+ effectiveInverseInertiaB: MonomorphType(Mat3, undefined, true),
27
+ totalLambda: MonomorphType(Vec3, undefined, true),
28
+ } as const satisfies PropertyDefinitionMap;
29
+
30
+ export class PointConstraintComponent extends createClass<
31
+ PointConstraintComponent,
32
+ typeof pointConstraintComponentProps
33
+ >(pointConstraintComponentProps) {
34
+ declare world: World;
35
+
36
+ deactivate(): void {
37
+ this.effectiveMass.zero();
38
+ this.totalLambda.zero();
39
+ }
40
+
41
+ isActive(): boolean {
42
+ // TODO: should probably just track using an isActive flag
43
+ return !this.effectiveMass.isExactlyZero();
44
+ }
45
+
46
+ applyDirectImpulse(impulse: Vec3): void {
47
+ if (impulse.isExactlyZero()) {
48
+ return;
49
+ }
50
+
51
+ const bodyA = this.bodyA!;
52
+ const bodyB = this.bodyB!;
53
+
54
+ if (bodyA.type === BodyType.dynamic) {
55
+ bodyA.linearVelocity.subScaledVector(impulse, bodyA.inverseMass);
56
+ bodyA.angularVelocity.subRotatedVector(impulse, this.effectiveInverseInertiaA);
57
+ }
58
+
59
+ if (bodyB.type === BodyType.dynamic) {
60
+ bodyB.linearVelocity.addScaledVector(impulse, bodyB.inverseMass);
61
+ bodyB.angularVelocity.addRotatedVector(impulse, this.effectiveInverseInertiaB);
62
+ }
63
+ }
64
+
65
+ setup(localMomentArmA: Vec3, localMomentArmB: Vec3): void {
66
+ const bodyA = this.bodyA!;
67
+ const bodyB = this.bodyB!;
68
+
69
+ rotateLocalToWorldA.fromQuat(bodyA!.orientation);
70
+ rotateLocalToWorldB.fromQuat(bodyB!.orientation);
71
+
72
+ // calculate the moment arms in world space
73
+ this.momentArmA.transformVectorFromMat3(localMomentArmA, rotateLocalToWorldA);
74
+ this.momentArmB.transformVectorFromMat3(localMomentArmB, rotateLocalToWorldB);
75
+
76
+ // calculate the effective mass
77
+ let totalInverseMass = 0;
78
+
79
+ inverseEffectiveMass.zero();
80
+
81
+ if (bodyA.type === BodyType.dynamic) {
82
+ totalInverseMass += bodyA.inverseMass;
83
+
84
+ bodyA.computeInverseInertiaTensor(inverseInertia);
85
+ crossMatrix.asSkewSymmetricMatrix(this.momentArmA);
86
+ this.effectiveInverseInertiaA.multiplyMatrices(inverseInertia, crossMatrix);
87
+ tempMatrix.multiplyMatrices(crossMatrix, inverseInertia);
88
+ tempMatrix.multiplyMat3RightTransposed(crossMatrix);
89
+ inverseEffectiveMass.addMatrices(inverseEffectiveMass, tempMatrix);
90
+ }
91
+
92
+ if (bodyB.type === BodyType.dynamic) {
93
+ totalInverseMass += bodyB.inverseMass;
94
+
95
+ bodyB.computeInverseInertiaTensor(inverseInertia);
96
+ crossMatrix.asSkewSymmetricMatrix(this.momentArmB);
97
+ this.effectiveInverseInertiaB.multiplyMatrices(inverseInertia, crossMatrix);
98
+ tempMatrix.multiplyMatrices(crossMatrix, inverseInertia);
99
+ tempMatrix.multiplyMat3RightTransposed(crossMatrix);
100
+ inverseEffectiveMass.addMatrices(inverseEffectiveMass, tempMatrix);
101
+ }
102
+
103
+ // add the inverse mass
104
+
105
+ totalInverseMassMatrix.identity();
106
+ totalInverseMassMatrix.multiplyMatrixByScalar(totalInverseMassMatrix, totalInverseMass);
107
+ inverseEffectiveMass.addMatrices(inverseEffectiveMass, totalInverseMassMatrix);
108
+
109
+ const isValid = this.effectiveMass.invertMat3(inverseEffectiveMass);
110
+ if (isValid === null) {
111
+ this.deactivate();
112
+ }
113
+ this.effectiveMass.multiplyByScalar(this.options.strength);
114
+ }
115
+
116
+ warmStart(warmStartImpulseRatio: number): void {
117
+ this.totalLambda.scale(warmStartImpulseRatio);
118
+ this.applyDirectImpulse(this.totalLambda);
119
+ }
120
+
121
+ solveVelocity(): void {
122
+ const bodyA = this.bodyA!;
123
+ const bodyB = this.bodyB!;
124
+
125
+ // compute the partial lambda for current velocity
126
+ bodyA.computePointVelocity(pointVelocityA, this.momentArmA);
127
+ bodyB.computePointVelocity(pointVelocityB, this.momentArmB);
128
+ lambda.subtractVectors(pointVelocityA, pointVelocityB);
129
+ lambda.scale(this.options.velocityBaumgarte);
130
+ lambda.transformVectorFromMat3(lambda, this.effectiveMass);
131
+
132
+ // accumulate the lambda
133
+ this.totalLambda.addVector(lambda);
134
+
135
+ // apply the lambda
136
+ this.applyDirectImpulse(lambda);
137
+ }
138
+
139
+ solvePosition(): void {
140
+ const bodyA = this.bodyA!;
141
+ const bodyB = this.bodyB!;
142
+
143
+ separation.subtractVectors(bodyB.computedCenterOfMassPosition, bodyA.computedCenterOfMassPosition);
144
+ separation.addVector(this.momentArmB);
145
+ separation.subtractVector(this.momentArmA);
146
+
147
+ // case: no separation
148
+ if (separation.isNearZero(1e-18)) {
149
+ return;
150
+ }
151
+
152
+ // case: separation
153
+
154
+ lambda.scaleVector(separation, -this.options.positionBaumgarte);
155
+ lambda.transformVectorFromMat3(lambda, this.effectiveMass);
156
+
157
+ if (bodyA.type === BodyType.dynamic) {
158
+ bodyA.computedCenterOfMassPosition.subScaledVector(lambda, bodyA.inverseMass);
159
+ bodyA.subRotationPositionImpulse(lambda, this.effectiveInverseInertiaA);
160
+ }
161
+
162
+ if (bodyB.type === BodyType.dynamic) {
163
+ bodyB.computedCenterOfMassPosition.addScaledVector(lambda, bodyB.inverseMass);
164
+ bodyB.addRotationPositionImpulse(lambda, this.effectiveInverseInertiaB);
165
+ }
166
+ }
167
+ }
168
+
169
+ const rotateLocalToWorldA = /*@__PURE__*/ Mat3.create();
170
+ const rotateLocalToWorldB = /*@__PURE__*/ Mat3.create();
171
+ const inverseEffectiveMass = /*@__PURE__*/ Mat3.create();
172
+ const inverseInertia = /*@__PURE__*/ Mat3.create();
173
+ const crossMatrix = /*@__PURE__*/ Mat3.create();
174
+ const tempMatrix = /*@__PURE__*/ Mat3.create();
175
+ const totalInverseMassMatrix = /*@__PURE__*/ Mat3.create();
176
+ const pointVelocityA = /*@__PURE__*/ Vec3.create();
177
+ const pointVelocityB = /*@__PURE__*/ Vec3.create();
178
+ const separation = /*@__PURE__*/ Vec3.create();
179
+ const lambda = /*@__PURE__*/ Vec3.create();
@@ -0,0 +1,139 @@
1
+ import {
2
+ createClass,
3
+ LazyReferenceType,
4
+ MonomorphType,
5
+ PropertyDefinitionMap,
6
+ PropertyDefinitionReference,
7
+ } from "monomorph";
8
+ import { ConstraintOptions } from "../ConstraintOptions";
9
+ import { Mat3 } from "../../math/mat3";
10
+ import { Vec3 } from "../../math/vec3";
11
+ import { Body, BodyType } from "../../physics/Body";
12
+ import type { World } from "../../world";
13
+ import { Quat } from "../../math/quat";
14
+
15
+ const rotationEulerComponentProps = {
16
+ bodyA: LazyReferenceType((() => Body) as () => never) as PropertyDefinitionReference<Body | null, true>,
17
+ bodyB: LazyReferenceType((() => Body) as () => never) as PropertyDefinitionReference<Body | null, true>,
18
+ options: MonomorphType(ConstraintOptions, {
19
+ positionBaumgarte: 0.8,
20
+ velocityBaumgarte: 1.0,
21
+ strength: 1.0,
22
+ }),
23
+ mInvI1: MonomorphType(Mat3, undefined, true),
24
+ mInvI2: MonomorphType(Mat3, undefined, true),
25
+ effectiveMassRotationComponent: MonomorphType(Mat3, undefined, true),
26
+ totalLambdaRotationComponent: MonomorphType(Vec3, undefined, true),
27
+ } as const satisfies PropertyDefinitionMap;
28
+
29
+ export class RotationEulerComponent extends createClass<RotationEulerComponent, typeof rotationEulerComponentProps>(
30
+ rotationEulerComponentProps
31
+ ) {
32
+ declare world: World;
33
+
34
+ deactivate(): void {
35
+ this.effectiveMassRotationComponent.zero();
36
+ this.totalLambdaRotationComponent.zero();
37
+ }
38
+
39
+ isActive(): boolean {
40
+ return !this.effectiveMassRotationComponent.isExactlyZero();
41
+ }
42
+
43
+ applyDirectImpulse(impulse: Vec3): void {
44
+ if (impulse.isExactlyZero()) {
45
+ return;
46
+ }
47
+
48
+ const bodyA = this.bodyA!;
49
+ const bodyB = this.bodyB!;
50
+
51
+ if (bodyA.type === BodyType.dynamic) {
52
+ bodyA.angularVelocity.subRotatedVector(impulse, this.mInvI1);
53
+ }
54
+
55
+ if (bodyB.type === BodyType.dynamic) {
56
+ bodyB.angularVelocity.addRotatedVector(impulse, this.mInvI2);
57
+ }
58
+ }
59
+
60
+ setup(): void {
61
+ const bodyA = this.bodyA!;
62
+ const bodyB = this.bodyB!;
63
+
64
+ if (bodyA.type === BodyType.dynamic) {
65
+ bodyA.computeInverseInertiaTensor(this.mInvI1);
66
+ } else {
67
+ this.mInvI1.zero();
68
+ }
69
+
70
+ if (bodyB.type === BodyType.dynamic) {
71
+ bodyB.computeInverseInertiaTensor(this.mInvI2);
72
+ } else {
73
+ this.mInvI2.zero();
74
+ }
75
+
76
+ this.effectiveMassRotationComponent.addMatrices(this.mInvI1, this.mInvI2);
77
+ const isValid = this.effectiveMassRotationComponent.invertMat3(this.effectiveMassRotationComponent);
78
+ if (isValid === null) {
79
+ this.deactivate();
80
+ }
81
+ this.effectiveMassRotationComponent.multiplyByScalar(this.options.strength);
82
+ }
83
+
84
+ warmStart(warmStartImpulseRatio: number): void {
85
+ this.totalLambdaRotationComponent.scale(warmStartImpulseRatio);
86
+ this.applyDirectImpulse(this.totalLambdaRotationComponent);
87
+ }
88
+
89
+ solveVelocity(): void {
90
+ const bodyA = this.bodyA!;
91
+ const bodyB = this.bodyB!;
92
+
93
+ // compute the partial lambda for current velocity
94
+ lambda.subtractVectors(bodyA!.angularVelocity, bodyB!.angularVelocity);
95
+ lambda.scale(this.options.velocityBaumgarte);
96
+ lambda.transformVectorFromMat3(lambda, this.effectiveMassRotationComponent);
97
+
98
+ this.totalLambdaRotationComponent.addVector(lambda);
99
+ this.applyDirectImpulse(lambda);
100
+ }
101
+
102
+ solvePosition(inverseInitialRotationAToB: Quat): void {
103
+ const bodyA = this.bodyA!;
104
+ const bodyB = this.bodyB!;
105
+
106
+ inverseRotationA.conjugateQuat(bodyA.orientation);
107
+
108
+ quaternionError.multiplyQuats(bodyB.orientation, inverseInitialRotationAToB);
109
+ quaternionError.multiplyQuats(quaternionError, inverseRotationA);
110
+
111
+ // TODO: this only works if error is small, handle large errors
112
+ quaternionError.intoEuler(eulerError);
113
+
114
+ if (eulerError.isNearZero(1e-18)) {
115
+ return;
116
+ }
117
+
118
+ lambda.scaleVector(eulerError, -this.options.positionBaumgarte);
119
+ lambda.transformVectorFromMat3(lambda, this.effectiveMassRotationComponent);
120
+
121
+ if (bodyA.type === BodyType.dynamic) {
122
+ angularImpulse.transformVectorFromMat3(lambda, this.mInvI1);
123
+ angularImpulse.scaleVector(angularImpulse, 1);
124
+ bodyA.subRotationDelta(angularImpulse);
125
+ }
126
+
127
+ if (bodyB.type === BodyType.dynamic) {
128
+ angularImpulse.transformVectorFromMat3(lambda, this.mInvI2);
129
+ angularImpulse.scaleVector(angularImpulse, 1);
130
+ bodyB.addRotationDelta(angularImpulse);
131
+ }
132
+ }
133
+ }
134
+
135
+ const lambda = /*@__PURE__*/ Vec3.create();
136
+ const quaternionError = /*@__PURE__*/ Quat.create();
137
+ const inverseRotationA = /*@__PURE__*/ Quat.create();
138
+ const eulerError = /*@__PURE__*/ Vec3.create();
139
+ const angularImpulse = /*@__PURE__*/ Vec3.create();
@@ -0,0 +1,30 @@
1
+ import { createClass, NumberType, PropertyDefinitionMap } from "monomorph";
2
+
3
+ export const enum SpringMode {
4
+ UseFrequency,
5
+ UseStiffness,
6
+ }
7
+
8
+ const springProps = {
9
+ mode: NumberType(SpringMode.UseFrequency),
10
+ damping: NumberType(0),
11
+ frequency: NumberType(0),
12
+ stiffness: NumberType(0),
13
+ } as const satisfies PropertyDefinitionMap;
14
+
15
+ export class Spring extends createClass<Spring, typeof springProps>(springProps) {
16
+ reset(): void {
17
+ this.mode = SpringMode.UseFrequency;
18
+ this.frequency = 0;
19
+ this.damping = 0;
20
+ this.stiffness = 0;
21
+ }
22
+
23
+ hasStiffness(): boolean {
24
+ return this.frequency > 0;
25
+ }
26
+
27
+ isValid(): boolean {
28
+ return this.frequency >= 0 && this.damping >= 0 && this.stiffness >= 0;
29
+ }
30
+ }
@@ -0,0 +1,71 @@
1
+ import { createClass, NumberType, PropertyDefinitionMap } from "monomorph";
2
+ import { squared } from "../../math/scalar";
3
+
4
+ const springComponentProps = {
5
+ bias: NumberType(0),
6
+ softness: NumberType(0),
7
+ } as const satisfies PropertyDefinitionMap;
8
+
9
+ export class SpringComponent extends createClass<SpringComponent, typeof springComponentProps>(springComponentProps) {
10
+ getBias(lambda: number): number {
11
+ return this.softness * lambda + this.bias;
12
+ }
13
+
14
+ isActive(): boolean {
15
+ return this.softness !== 0;
16
+ }
17
+
18
+ setupSpringProperties(
19
+ deltaTime: number,
20
+ inverseEffectiveMass: number,
21
+ bias: number,
22
+ constraint: number,
23
+ stiffness: number,
24
+ damping: number
25
+ ): number {
26
+ this.softness = 1 / (deltaTime * (damping * deltaTime * stiffness));
27
+ this.bias = bias + deltaTime * stiffness * this.softness * constraint;
28
+ return 1 / (inverseEffectiveMass + this.softness);
29
+ }
30
+
31
+ setupSpringPropertiesWithBias(bias: number): void {
32
+ this.bias = bias;
33
+ this.softness = 0;
34
+ }
35
+
36
+ setupWithFrequency(
37
+ deltaTime: number,
38
+ inverseEffectiveMass: number,
39
+ bias: number,
40
+ constraint: number,
41
+ frequency: number,
42
+ damping: number
43
+ ): number {
44
+ const effectiveMass = 1 / inverseEffectiveMass;
45
+ if (frequency > 0) {
46
+ const omega = 2 * Math.PI * frequency;
47
+ const k = effectiveMass * squared(omega);
48
+ const c = 2 * effectiveMass * damping * omega;
49
+ return this.setupSpringProperties(deltaTime, inverseEffectiveMass, bias, constraint, k, c);
50
+ } else {
51
+ this.setupSpringPropertiesWithBias(bias);
52
+ return effectiveMass;
53
+ }
54
+ }
55
+
56
+ setupWithStiffness(
57
+ deltaTime: number,
58
+ inverseEffectiveMass: number,
59
+ bias: number,
60
+ constraint: number,
61
+ stiffness: number,
62
+ damping: number
63
+ ): number {
64
+ if (stiffness > 0) {
65
+ return this.setupSpringProperties(deltaTime, inverseEffectiveMass, bias, constraint, stiffness, damping);
66
+ } else {
67
+ this.setupSpringPropertiesWithBias(bias);
68
+ return 1 / inverseEffectiveMass;
69
+ }
70
+ }
71
+ }
@@ -0,0 +1,6 @@
1
+ import { DistanceConstraint } from "./DistanceConstraint";
2
+ import { FixedConstraint } from "./FixedConstraint";
3
+ import { HingeConstraint } from "./HingeConstraint";
4
+ import { PointConstraint } from "./PointConstraint";
5
+
6
+ export type Constraint = PointConstraint | DistanceConstraint | FixedConstraint | HingeConstraint;
package/src/helpers.ts ADDED
@@ -0,0 +1,147 @@
1
+ import { Monomorph, MonomorphInstance, PoolClass, PropertyDefinitionMap } from "monomorph";
2
+
3
+ export function assert(condition: boolean, message: string) {
4
+ if (!condition) {
5
+ throw new Error(message);
6
+ }
7
+ }
8
+
9
+ export function destroyAllInstancesInPool<T extends PoolClass<MonomorphInstance<P, I>>, P extends PropertyDefinitionMap = T extends PoolClass<MonomorphInstance<infer iP, infer iI>> ? iP : never, I extends any = T extends PoolClass<MonomorphInstance<infer iP, infer iI>> ? iI : never>(pool: T) {
10
+ for (let i = 0; i < pool.array.length; i++) {
11
+ const instance = pool.array[i];
12
+
13
+ if (!instance) {
14
+ continue;
15
+ }
16
+
17
+ if (instance.isDestroyed()) {
18
+ continue;
19
+ }
20
+
21
+ instance.destroy();
22
+ }
23
+ }
24
+
25
+ export class ArrayPool<T extends { index: number }> {
26
+ pool: PoolClass<T>;
27
+ array: T[];
28
+ _count: number;
29
+ _capacity: number;
30
+
31
+ constructor(theClass: any, capacity: number) {
32
+ this._count = 0;
33
+ this._capacity = capacity;
34
+ this.pool = new theClass.Pool(capacity);
35
+
36
+ // preallocate the pool with instances
37
+ for (let i = 0; i < capacity; i++) {
38
+ theClass.create(undefined, this.pool);
39
+ }
40
+ this.array = this.pool.array;
41
+ }
42
+
43
+ get count() {
44
+ return this._count;
45
+ }
46
+
47
+ get capacity() {
48
+ return this._capacity;
49
+ }
50
+
51
+ create() {
52
+ if (this._count >= this.capacity) {
53
+ return null;
54
+ }
55
+ const instance = this.array[this._count];
56
+ instance.index = this._count;
57
+ this._count++;
58
+ return instance;
59
+ }
60
+
61
+ remove(instance: T) {
62
+ // get indices of instances to swap
63
+ const index = instance.index;
64
+ const lastIndex = this._count - 1;
65
+
66
+ if (index === lastIndex) {
67
+ // if the instance is the last one, just decrement the count
68
+ this._count--;
69
+ return;
70
+ }
71
+
72
+ // swap instances
73
+ this.array[index] = this.array[lastIndex];
74
+ this.array[lastIndex] = instance;
75
+
76
+ // swap their indices
77
+ this.array[index].index = index;
78
+ this.array[lastIndex].index = lastIndex;
79
+
80
+ // decrement the count
81
+ this._count--;
82
+ }
83
+
84
+ clear() {
85
+ this._count = 0;
86
+ }
87
+ }
88
+
89
+ export function coerceVec3Argument<S extends any>(obj: S, key: keyof S) {
90
+ let value = obj[key] as any;
91
+ if (!value) {
92
+ return;
93
+ }
94
+ if (Array.isArray(value)) {
95
+ value = { x: value[0], y: value[1], z: value[2] };
96
+ }
97
+ obj[key] = value;
98
+ }
99
+
100
+ export function coerceQuatArgument<S extends any>(obj: S, key: keyof S) {
101
+ let value = obj[key] as any;
102
+ if (!value) {
103
+ return;
104
+ }
105
+ if (Array.isArray(value)) {
106
+ if (Array.length === 3) {
107
+ let x = value[0] * (180 / Math.PI);
108
+ let y = value[1] * (180 / Math.PI);
109
+ let z = value[2] * (180 / Math.PI);
110
+ let halfToRad = Math.PI / 360;
111
+ x *= halfToRad;
112
+ z *= halfToRad;
113
+ y *= halfToRad;
114
+ let sx = Math.sin(x);
115
+ let cx = Math.cos(x);
116
+ let sy = Math.sin(y);
117
+ let cy = Math.cos(y);
118
+ let sz = Math.sin(z);
119
+ let cz = Math.cos(z);
120
+ value = { x: 0, y: 0, z: 0, w: 0 };
121
+ value.x = sx * cy * cz + cx * sy * sz;
122
+ value.y = cx * sy * cz - sx * cy * sz;
123
+ value.z = cx * cy * sz + sx * sy * cz;
124
+ value.w = cx * cy * cz - sx * sy * sz;
125
+ }
126
+ } else if (!("w" in value)) {
127
+ let x = value.x * (180 / Math.PI);
128
+ let y = value.y * (180 / Math.PI);
129
+ let z = value.z * (180 / Math.PI);
130
+ let halfToRad = Math.PI / 360;
131
+ x *= halfToRad;
132
+ z *= halfToRad;
133
+ y *= halfToRad;
134
+ let sx = Math.sin(x);
135
+ let cx = Math.cos(x);
136
+ let sy = Math.sin(y);
137
+ let cy = Math.cos(y);
138
+ let sz = Math.sin(z);
139
+ let cz = Math.cos(z);
140
+ value = { x: 0, y: 0, z: 0, w: 0 };
141
+ value.x = sx * cy * cz + cx * sy * sz;
142
+ value.y = cx * sy * cz - sx * cy * sz;
143
+ value.z = cx * cy * sz + sx * sy * cz;
144
+ value.w = cx * cy * cz - sx * sy * sz;
145
+ }
146
+ obj[key] = value;
147
+ }
package/src/index.ts ADDED
@@ -0,0 +1,50 @@
1
+ export { Mat3 } from "./math/mat3";
2
+ export { Line } from "./shape/Line";
3
+ export { Mat4 } from "./math/mat4";
4
+ export { Plane } from "./shape/Plane";
5
+ export { type CastRayResult } from "./physics/narrowphase/CollideBodiesModule" ;
6
+ export { QueryPrecision } from "./collision/cast/cast";
7
+ export { Ray } from "./shape/Ray";
8
+ export { baseConstraintProps, ReferenceFrame } from "./constraints/BaseConstraint";
9
+ export { type Constraint } from "./constraints/types";
10
+ export { ContactManifold } from "./physics/manifold/ContactManifold";
11
+ export { FixedConstraint } from "./constraints/FixedConstraint";
12
+ export { PointConstraint } from "./constraints/PointConstraint";
13
+ export { DistanceConstraint } from "./constraints/DistanceConstraint";
14
+ export { HingeConstraint } from "./constraints/HingeConstraint";
15
+ export * as CollisionFilter from "./physics/CollisionFilter";
16
+ export { AllFlag, NoneFlag } from "./physics/CollisionFilter";
17
+ export { Isometry } from "./math/isometry";
18
+ export { clamp, squared, degreesToRadians } from "./math/scalar";
19
+ export { PolygonConvexSupport, SupportMode, ConvexRadiusObject } from "./shape/Convex";
20
+ export { destroyAllInstancesInPool } from "./helpers";
21
+ export { NumberValue } from "./math/NumberValue";
22
+ export { GjkCastShapeResult, GjkModule } from "./collision/gjk/GjkModule";
23
+ export { ColliderType, Body, BodyType } from "./physics/Body";
24
+ export { Face } from "./physics/manifold/Face";
25
+ export { Capsule } from "./shape/Capsule";
26
+ export { default as ConvexHull } from "./shape/ConvexHull";
27
+ export { CastCollector, type CastSettings, CastResult } from "./collision/cast/cast";
28
+ export { Aabb } from "./shape/Aabb";
29
+ export { BvhTree, type BvhTreeOptions, BvhNode } from "./physics/broadphase/BvhTree";
30
+ export { BodyPairsModule, BodyPairNode } from "./physics/broadphase/BodyPairsModule";
31
+ export { CoefficientFunctionType } from "./physics/solver/ContactConstraintModule";
32
+ export {
33
+ ActiveEdgeMode,
34
+ CollectFacesMode,
35
+ BackFaceMode,
36
+ type CollisionSettings,
37
+ CollisionCollector,
38
+ CollisionResult,
39
+ } from "./collision/collide/collide";
40
+ export { Box } from "./shape/Box";
41
+ export { PenetrationDepthModule } from "./collision/gjk/PenetrationDepthModule";
42
+ export { CollideShapesModule } from "./collision/CollideShapesModule";
43
+ export { ShapeType, type Shape, type ConvexShape } from "./shape/Shape";
44
+ export { Quat } from "./math/quat";
45
+ export { Vec3 } from "./math/vec3";
46
+ export { Sphere } from "./shape/Sphere";
47
+ export { World } from "./world";
48
+ export { CompoundShape } from "./shape/CompoundShape";
49
+ export { BasicTransform } from "./math/BasicTransform";
50
+ export { TransformedShape } from "./shape/TransformedShape";
@@ -0,0 +1,19 @@
1
+ import { createClass, MonomorphType, NumberType, PropertyDefinitionMap } from "monomorph";
2
+ import { Quat } from "./quat";
3
+ import { Vec3 } from "./vec3";
4
+
5
+ const basicTransformProps = {
6
+ position: MonomorphType(Vec3),
7
+ rotation: MonomorphType(Quat),
8
+ scale: NumberType(1.0),
9
+ } as const satisfies PropertyDefinitionMap;
10
+
11
+ export class BasicTransform extends createClass<BasicTransform, typeof basicTransformProps>(basicTransformProps) {
12
+ equals(other: BasicTransform, tolerance: number = 1e-6) {
13
+ return (
14
+ this.position.equals(other.position, tolerance) &&
15
+ this.rotation.equals(other.rotation, tolerance) &&
16
+ Math.abs(this.scale - other.scale) < tolerance
17
+ );
18
+ }
19
+ }
@@ -0,0 +1,13 @@
1
+ import { createClass, NumberType, PropertyDefinitionMap } from "monomorph";
2
+
3
+ const numberValueProps = {
4
+ value: NumberType(0.0),
5
+ } as const satisfies PropertyDefinitionMap;
6
+
7
+ export class NumberValue extends createClass<NumberValue, typeof numberValueProps>(numberValueProps) {
8
+ toObject() {
9
+ return {
10
+ value: this.value,
11
+ };
12
+ }
13
+ }