babylon-box3d 0.1.0 → 0.2.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.
@@ -0,0 +1,2602 @@
1
+ /* babylon-box3d 0.2.0 | MIT | https://github.com/Pryme8/babylon-box3d */
2
+ "use strict";
3
+ var BABYLONBOX3D = (() => {
4
+ var __create = Object.create;
5
+ var __defProp = Object.defineProperty;
6
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
7
+ var __getOwnPropNames = Object.getOwnPropertyNames;
8
+ var __getProtoOf = Object.getPrototypeOf;
9
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
10
+ var __commonJS = (cb, mod) => function __require() {
11
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
12
+ };
13
+ var __export = (target, all) => {
14
+ for (var name in all)
15
+ __defProp(target, name, { get: all[name], enumerable: true });
16
+ };
17
+ var __copyProps = (to, from, except, desc) => {
18
+ if (from && typeof from === "object" || typeof from === "function") {
19
+ for (let key of __getOwnPropNames(from))
20
+ if (!__hasOwnProp.call(to, key) && key !== except)
21
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
22
+ }
23
+ return to;
24
+ };
25
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
26
+ // If the importer is in node compatibility mode or this is not an ESM
27
+ // file that has been converted to a CommonJS file using a Babel-
28
+ // compatible transform (i.e. "__esModule" has not been set), then set
29
+ // "default" to the CommonJS "module.exports" for node compatibility.
30
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
31
+ mod
32
+ ));
33
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
34
+
35
+ // babylon-global:@babylonjs/core/Maths/math.vector.js
36
+ var require_math_vector = __commonJS({
37
+ "babylon-global:@babylonjs/core/Maths/math.vector.js"(exports, module) {
38
+ if (typeof BABYLON === "undefined") {
39
+ throw new Error("babylon.box3d.js needs babylon.js loaded first");
40
+ }
41
+ module.exports = BABYLON;
42
+ }
43
+ });
44
+
45
+ // babylon-global:@babylonjs/core/Physics/v2/IPhysicsEnginePlugin.js
46
+ var require_IPhysicsEnginePlugin = __commonJS({
47
+ "babylon-global:@babylonjs/core/Physics/v2/IPhysicsEnginePlugin.js"(exports, module) {
48
+ if (typeof BABYLON === "undefined") {
49
+ throw new Error("babylon.box3d.js needs babylon.js loaded first");
50
+ }
51
+ module.exports = BABYLON;
52
+ }
53
+ });
54
+
55
+ // babylon-global:@babylonjs/core/Physics/physicsRaycastResult.js
56
+ var require_physicsRaycastResult = __commonJS({
57
+ "babylon-global:@babylonjs/core/Physics/physicsRaycastResult.js"(exports, module) {
58
+ if (typeof BABYLON === "undefined") {
59
+ throw new Error("babylon.box3d.js needs babylon.js loaded first");
60
+ }
61
+ module.exports = BABYLON;
62
+ }
63
+ });
64
+
65
+ // babylon-global:@babylonjs/core/Misc/logger.js
66
+ var require_logger = __commonJS({
67
+ "babylon-global:@babylonjs/core/Misc/logger.js"(exports, module) {
68
+ if (typeof BABYLON === "undefined") {
69
+ throw new Error("babylon.box3d.js needs babylon.js loaded first");
70
+ }
71
+ module.exports = BABYLON;
72
+ }
73
+ });
74
+
75
+ // babylon-global:@babylonjs/core/Culling/boundingBox.js
76
+ var require_boundingBox = __commonJS({
77
+ "babylon-global:@babylonjs/core/Culling/boundingBox.js"(exports, module) {
78
+ if (typeof BABYLON === "undefined") {
79
+ throw new Error("babylon.box3d.js needs babylon.js loaded first");
80
+ }
81
+ module.exports = BABYLON;
82
+ }
83
+ });
84
+
85
+ // babylon-global:@babylonjs/core/Meshes/mesh.js
86
+ var require_mesh = __commonJS({
87
+ "babylon-global:@babylonjs/core/Meshes/mesh.js"(exports, module) {
88
+ if (typeof BABYLON === "undefined") {
89
+ throw new Error("babylon.box3d.js needs babylon.js loaded first");
90
+ }
91
+ module.exports = BABYLON;
92
+ }
93
+ });
94
+
95
+ // babylon-global:@babylonjs/core/Meshes/instancedMesh.js
96
+ var require_instancedMesh = __commonJS({
97
+ "babylon-global:@babylonjs/core/Meshes/instancedMesh.js"(exports, module) {
98
+ if (typeof BABYLON === "undefined") {
99
+ throw new Error("babylon.box3d.js needs babylon.js loaded first");
100
+ }
101
+ module.exports = BABYLON;
102
+ }
103
+ });
104
+
105
+ // babylon-global:@babylonjs/core/Buffers/buffer.js
106
+ var require_buffer = __commonJS({
107
+ "babylon-global:@babylonjs/core/Buffers/buffer.js"(exports, module) {
108
+ if (typeof BABYLON === "undefined") {
109
+ throw new Error("babylon.box3d.js needs babylon.js loaded first");
110
+ }
111
+ module.exports = BABYLON;
112
+ }
113
+ });
114
+
115
+ // babylon-global:@babylonjs/core/Misc/observable.js
116
+ var require_observable = __commonJS({
117
+ "babylon-global:@babylonjs/core/Misc/observable.js"(exports, module) {
118
+ if (typeof BABYLON === "undefined") {
119
+ throw new Error("babylon.box3d.js needs babylon.js loaded first");
120
+ }
121
+ module.exports = BABYLON;
122
+ }
123
+ });
124
+
125
+ // src/index.ts
126
+ var index_exports = {};
127
+ __export(index_exports, {
128
+ BOX3D_HEIGHT_FIELD_HOLE: () => BOX3D_HEIGHT_FIELD_HOLE,
129
+ Box3D: () => wasm_default,
130
+ Box3DPlugin: () => Box3DPlugin,
131
+ Box3DWheelJoint: () => Box3DWheelJoint
132
+ });
133
+
134
+ // src/box3dPlugin.ts
135
+ var import_math_vector = __toESM(require_math_vector(), 1);
136
+ var import_IPhysicsEnginePlugin = __toESM(require_IPhysicsEnginePlugin(), 1);
137
+ var import_physicsRaycastResult = __toESM(require_physicsRaycastResult(), 1);
138
+ var import_logger = __toESM(require_logger(), 1);
139
+ var import_boundingBox = __toESM(require_boundingBox(), 1);
140
+ var import_mesh = __toESM(require_mesh(), 1);
141
+ var import_instancedMesh = __toESM(require_instancedMesh(), 1);
142
+ var import_buffer = __toESM(require_buffer(), 1);
143
+ var import_observable = __toESM(require_observable(), 1);
144
+ var BOX3D_HEIGHT_FIELD_HOLE = 255;
145
+ var Box3DBodyData = class {
146
+ constructor(slot) {
147
+ this.slot = slot;
148
+ this.userMassProps = {};
149
+ /** Box3D angular motion locks currently set on the body, bits x, y, z */
150
+ this.angularLocks = 0;
151
+ /** Havok's EventType bits: 1 collision started, 2 collision continued, 4 collision finished */
152
+ this.eventMask = 0;
153
+ this.motionType = import_IPhysicsEnginePlugin.PhysicsMotionType.STATIC;
154
+ /** Babylon activation control; ALWAYS_INACTIVE parks the body as a static Box3D body, see setActivationControl */
155
+ this.activation = import_IPhysicsEnginePlugin.PhysicsActivationControl.SIMULATION_CONTROLLED;
156
+ }
157
+ };
158
+ var Box3DShapeData = class {
159
+ constructor(slot, type) {
160
+ this.slot = slot;
161
+ this.type = type;
162
+ this.material = {};
163
+ /** bodies currently instantiating this shape, refreshed when the description changes */
164
+ this.users = /* @__PURE__ */ new Set();
165
+ /** for containers: child shapes, so changes propagate to users */
166
+ this.children = [];
167
+ this.parents = /* @__PURE__ */ new Set();
168
+ }
169
+ };
170
+ var Box3DConstraintData = class {
171
+ constructor() {
172
+ this.joints = [];
173
+ this.pairs = [];
174
+ this.type = import_IPhysicsEnginePlugin.PhysicsConstraintType.LOCK;
175
+ this.jointType = 0;
176
+ this.enabled = true;
177
+ this.collisions = false;
178
+ this.frames = new Float32Array(14);
179
+ this.length = 0;
180
+ this.axes = /* @__PURE__ */ new Map();
181
+ /** SIX_DOF only */
182
+ this.plan = null;
183
+ }
184
+ };
185
+ var Box3DWheelJoint = class {
186
+ /**
187
+ * @internal
188
+ */
189
+ constructor(_plugin, _slot) {
190
+ this._plugin = _plugin;
191
+ this._slot = _slot;
192
+ }
193
+ /**
194
+ * Sets the target angular speed of the spin motor.
195
+ * @param speed radians per second
196
+ */
197
+ setSpinSpeed(speed) {
198
+ this._plugin._wheelCall("_bx_WheelJoint_SetSpinMotorSpeed", this._slot, speed);
199
+ }
200
+ /**
201
+ * Sets the maximum torque the spin motor can apply.
202
+ * @param torque newton meters
203
+ */
204
+ setMaxSpinTorque(torque) {
205
+ this._plugin._wheelCall("_bx_WheelJoint_SetMaxSpinTorque", this._slot, torque);
206
+ }
207
+ /**
208
+ * Enables or disables the spin motor (disabled wheels roll freely).
209
+ * @param enabled true to enable
210
+ */
211
+ enableSpinMotor(enabled) {
212
+ this._plugin._wheelCall("_bx_WheelJoint_EnableSpinMotor", this._slot, enabled ? 1 : 0);
213
+ }
214
+ /**
215
+ * Sets the target steering angle.
216
+ * @param radians steering angle
217
+ */
218
+ setSteeringAngle(radians) {
219
+ this._plugin._wheelCall("_bx_WheelJoint_SetTargetSteeringAngle", this._slot, radians);
220
+ }
221
+ /**
222
+ * Tunes the suspension spring.
223
+ * @param hertz stiffness
224
+ * @param dampingRatio damping ratio
225
+ */
226
+ setSuspension(hertz, dampingRatio) {
227
+ this._plugin._wheelCall("_bx_WheelJoint_SetSuspension", this._slot, hertz, dampingRatio);
228
+ }
229
+ /**
230
+ * Removes the joint.
231
+ */
232
+ dispose() {
233
+ this._plugin._destroyExtraJoint(this._slot);
234
+ this._slot = 0;
235
+ }
236
+ };
237
+ var LockedInertiaRatio = 1e5;
238
+ function DiagonalizeInertia(tensor, moments, orientation) {
239
+ const a = [
240
+ [tensor[0], tensor[3], tensor[4]],
241
+ [tensor[3], tensor[1], tensor[5]],
242
+ [tensor[4], tensor[5], tensor[2]]
243
+ ];
244
+ orientation.set(0, 0, 0, 1);
245
+ moments.set(tensor[0], tensor[1], tensor[2]);
246
+ const q = [0, 0, 0, 1];
247
+ for (let sweep = 0; sweep < 24; sweep++) {
248
+ const [x, y, z, w] = q;
249
+ const r = [
250
+ [1 - 2 * (y * y + z * z), 2 * (x * y - z * w), 2 * (x * z + y * w)],
251
+ [2 * (x * y + z * w), 1 - 2 * (x * x + z * z), 2 * (y * z - x * w)],
252
+ [2 * (x * z - y * w), 2 * (y * z + x * w), 1 - 2 * (x * x + y * y)]
253
+ ];
254
+ const d = [0, 1, 2].map((i) => [0, 1, 2].map((j) => [0, 1, 2].reduce((sum, k3) => sum + r[k3][i] * [0, 1, 2].reduce((s2, l) => s2 + a[k3][l] * r[l][j], 0), 0)));
255
+ moments.set(d[0][0], d[1][1], d[2][2]);
256
+ orientation.set(q[0], q[1], q[2], q[3]);
257
+ const offDiagonal = [Math.abs(d[1][2]), Math.abs(d[0][2]), Math.abs(d[0][1])];
258
+ const k = offDiagonal.indexOf(Math.max(...offDiagonal));
259
+ const k1 = (k + 1) % 3;
260
+ const k2 = (k + 2) % 3;
261
+ const off = d[k1][k2];
262
+ const scale = Math.abs(d[0][0]) + Math.abs(d[1][1]) + Math.abs(d[2][2]) + 1e-30;
263
+ if (Math.abs(off) <= 1e-9 * scale) {
264
+ return;
265
+ }
266
+ let theta = (d[k2][k2] - d[k1][k1]) / (2 * off);
267
+ const sign = theta > 0 ? 1 : -1;
268
+ theta *= sign;
269
+ const t = sign / (theta + (theta < 1e6 ? Math.sqrt(theta * theta + 1) : theta));
270
+ const c = 1 / Math.sqrt(t * t + 1);
271
+ if (c === 1) {
272
+ return;
273
+ }
274
+ const jacobi = [0, 0, 0, 0];
275
+ jacobi[k] = -sign * Math.sqrt((1 - c) / 2);
276
+ jacobi[3] = Math.sqrt(1 - jacobi[k] * jacobi[k]);
277
+ const [qx, qy, qz, qw] = q;
278
+ const [jx, jy, jz, jw] = jacobi;
279
+ q[0] = qw * jx + qx * jw + qy * jz - qz * jy;
280
+ q[1] = qw * jy - qx * jz + qy * jw + qz * jx;
281
+ q[2] = qw * jz + qx * jy - qy * jx + qz * jw;
282
+ q[3] = qw * jw - qx * jx - qy * jy - qz * jz;
283
+ const length = Math.hypot(q[0], q[1], q[2], q[3]) || 1;
284
+ for (let i = 0; i < 4; i++) {
285
+ q[i] /= length;
286
+ }
287
+ }
288
+ }
289
+ function ComposeInertia(moments, orientation) {
290
+ const { x, y, z, w } = orientation;
291
+ const r = [
292
+ [1 - 2 * (y * y + z * z), 2 * (x * y - z * w), 2 * (x * z + y * w)],
293
+ [2 * (x * y + z * w), 1 - 2 * (x * x + z * z), 2 * (y * z - x * w)],
294
+ [2 * (x * z - y * w), 2 * (y * z + x * w), 1 - 2 * (x * x + y * y)]
295
+ ];
296
+ const at = (i, j) => r[i][0] * moments[0] * r[j][0] + r[i][1] * moments[1] * r[j][1] + r[i][2] * moments[2] * r[j][2];
297
+ return [at(0, 0), at(1, 1), at(2, 2), at(0, 1), at(0, 2), at(1, 2)];
298
+ }
299
+ var MOVE_STRIDE = 9;
300
+ var CONTACT_STRIDE = 12;
301
+ var SENSOR_STRIDE = 5;
302
+ var RAY_STRIDE = 11;
303
+ var MeshGeometryAccumulator = class {
304
+ constructor(_collectIndices, _flipWinding) {
305
+ this._collectIndices = _collectIndices;
306
+ this._flipWinding = _flipWinding;
307
+ this._vertices = [];
308
+ this._indices = [];
309
+ }
310
+ addNodeMeshes(mesh, includeChildren) {
311
+ mesh.computeWorldMatrix(true);
312
+ const rootScaled = import_math_vector.TmpVectors.Matrix[0];
313
+ import_math_vector.Matrix.ScalingToRef(mesh.absoluteScaling.x, mesh.absoluteScaling.y, mesh.absoluteScaling.z, rootScaled);
314
+ if (mesh instanceof import_mesh.Mesh) {
315
+ this._addMesh(mesh, rootScaled);
316
+ } else if (mesh instanceof import_instancedMesh.InstancedMesh) {
317
+ this._addMesh(mesh.sourceMesh, rootScaled);
318
+ }
319
+ if (includeChildren) {
320
+ const worldToRoot = import_math_vector.TmpVectors.Matrix[1];
321
+ mesh.computeWorldMatrix().invertToRef(worldToRoot);
322
+ const worldToRootScaled = import_math_vector.TmpVectors.Matrix[2];
323
+ worldToRoot.multiplyToRef(rootScaled, worldToRootScaled);
324
+ const children = mesh.getChildMeshes(false).filter((m) => !m.physicsBody);
325
+ for (const m of children) {
326
+ const childToWorld = m.computeWorldMatrix();
327
+ const childToRootScaled = import_math_vector.TmpVectors.Matrix[3];
328
+ childToWorld.multiplyToRef(worldToRootScaled, childToRootScaled);
329
+ if (m instanceof import_mesh.Mesh) {
330
+ this._addMesh(m, childToRootScaled);
331
+ } else if (m instanceof import_instancedMesh.InstancedMesh) {
332
+ this._addMesh(m.sourceMesh, childToRootScaled);
333
+ }
334
+ }
335
+ }
336
+ }
337
+ _addMesh(mesh, meshToRoot) {
338
+ const vertexData = mesh.getVerticesData(import_buffer.VertexBuffer.PositionKind) || [];
339
+ const numVerts = vertexData.length / 3;
340
+ const indexOffset = this._vertices.length / 3;
341
+ const p = import_math_vector.TmpVectors.Vector3[0];
342
+ for (let v = 0; v < numVerts; v++) {
343
+ p.set(vertexData[v * 3 + 0], vertexData[v * 3 + 1], vertexData[v * 3 + 2]);
344
+ import_math_vector.Vector3.TransformCoordinatesToRef(p, meshToRoot, p);
345
+ this._vertices.push(p.x, p.y, p.z);
346
+ }
347
+ if (this._collectIndices) {
348
+ const meshIndices = mesh.getIndices();
349
+ if (meshIndices) {
350
+ for (let i = 0; i < meshIndices.length; i += 3) {
351
+ if (this._flipWinding) {
352
+ this._indices.push(meshIndices[i + 2] + indexOffset, meshIndices[i + 1] + indexOffset, meshIndices[i + 0] + indexOffset);
353
+ } else {
354
+ this._indices.push(meshIndices[i + 0] + indexOffset, meshIndices[i + 1] + indexOffset, meshIndices[i + 2] + indexOffset);
355
+ }
356
+ }
357
+ }
358
+ }
359
+ }
360
+ get vertexCount() {
361
+ return this._vertices.length / 3;
362
+ }
363
+ get triangleCount() {
364
+ return this._indices.length / 3;
365
+ }
366
+ get vertices() {
367
+ return this._vertices;
368
+ }
369
+ get indices() {
370
+ return this._indices;
371
+ }
372
+ };
373
+ var RequiredNativeExports = [
374
+ "_bx_Body_AllowFastRotation",
375
+ "_bx_Body_ApplyAngularImpulse",
376
+ "_bx_Body_ApplyForce",
377
+ "_bx_Body_ApplyLinearImpulse",
378
+ "_bx_Body_ApplyMassFromShapes",
379
+ "_bx_Body_ApplyTorque",
380
+ "_bx_Body_ComputeShapeMassData",
381
+ "_bx_Body_EnableSleep",
382
+ "_bx_Body_GetAABB",
383
+ "_bx_Body_GetAngularDamping",
384
+ "_bx_Body_GetAngularVelocity",
385
+ "_bx_Body_GetGravityScale",
386
+ "_bx_Body_GetLinearDamping",
387
+ "_bx_Body_GetLinearVelocity",
388
+ "_bx_Body_GetMassData",
389
+ "_bx_Body_GetTransform",
390
+ "_bx_Body_SetAngularDamping",
391
+ "_bx_Body_SetAngularVelocity",
392
+ "_bx_Body_SetAwake",
393
+ "_bx_Body_SetEventFlags",
394
+ "_bx_Body_SetGravityScale",
395
+ "_bx_Body_SetLinearDamping",
396
+ "_bx_Body_SetLinearVelocity",
397
+ "_bx_Body_SetMassDataFull",
398
+ "_bx_Body_SetMotionLocks",
399
+ "_bx_Body_SetShape",
400
+ "_bx_Body_SetTargetTransform",
401
+ "_bx_Body_SetTransform",
402
+ "_bx_Body_SetType",
403
+ "_bx_Body_SyncShapeDesc",
404
+ "_bx_ComputeAlignedFrameB",
405
+ "_bx_ContactEventsPtr",
406
+ "_bx_CreateBody",
407
+ "_bx_CreateJoint",
408
+ "_bx_CreateWorld",
409
+ "_bx_DebugIndexCount",
410
+ "_bx_DebugIndicesPtr",
411
+ "_bx_DebugPositionsPtr",
412
+ "_bx_DestroyBody",
413
+ "_bx_DestroyJoint",
414
+ "_bx_DestroyWorld",
415
+ "_bx_GetRejectedSlotCount",
416
+ "_bx_Joint_EnableLimit",
417
+ "_bx_Joint_EnableMotor",
418
+ "_bx_Joint_EnableSpring",
419
+ "_bx_Joint_IsValid",
420
+ "_bx_Joint_SetCollideConnected",
421
+ "_bx_Joint_SetConstraintTuning",
422
+ "_bx_Joint_SetLimits",
423
+ "_bx_Joint_SetMaxMotorForce",
424
+ "_bx_Joint_SetMotorSpeed",
425
+ "_bx_Joint_SetSphericalMotor",
426
+ "_bx_Joint_SetSphericalTarget",
427
+ "_bx_Joint_SetSpring",
428
+ "_bx_Joint_SetTarget",
429
+ "_bx_Joint_SetTwistLimits",
430
+ "_bx_Joint_UpdateMotorFrame",
431
+ "_bx_Joint_WakeBodies",
432
+ "_bx_MoveEventsPtr",
433
+ "_bx_RayHitsPtr",
434
+ "_bx_Scratch",
435
+ "_bx_SensorEventsPtr",
436
+ "_bx_ShapeDesc_AddChild",
437
+ "_bx_ShapeDesc_BuildDebugGeometry",
438
+ "_bx_ShapeDesc_CreateBox",
439
+ "_bx_ShapeDesc_CreateCapsule",
440
+ "_bx_ShapeDesc_CreateContainer",
441
+ "_bx_ShapeDesc_CreateCylinder",
442
+ "_bx_ShapeDesc_CreateHeightField",
443
+ "_bx_ShapeDesc_CreateHull",
444
+ "_bx_ShapeDesc_CreateMesh",
445
+ "_bx_ShapeDesc_CreateSphere",
446
+ "_bx_ShapeDesc_Destroy",
447
+ "_bx_ShapeDesc_GetAABB",
448
+ "_bx_ShapeDesc_GetCategoryBits",
449
+ "_bx_ShapeDesc_GetChildCount",
450
+ "_bx_ShapeDesc_GetDensity",
451
+ "_bx_ShapeDesc_GetFriction",
452
+ "_bx_ShapeDesc_GetMaskBits",
453
+ "_bx_ShapeDesc_GetRestitution",
454
+ "_bx_ShapeDesc_RemoveChild",
455
+ "_bx_ShapeDesc_SetDensity",
456
+ "_bx_ShapeDesc_SetFilter",
457
+ "_bx_ShapeDesc_SetGroup",
458
+ "_bx_ShapeDesc_SetMaterial",
459
+ "_bx_ShapeDesc_SetRollingResistance",
460
+ "_bx_ShapeDesc_SetSensor",
461
+ "_bx_World_CastRay",
462
+ "_bx_World_EnableContinuous",
463
+ "_bx_World_EnableSleeping",
464
+ "_bx_World_Explode",
465
+ "_bx_World_GetContactEvents",
466
+ "_bx_World_GetMaximumLinearSpeed",
467
+ "_bx_World_GetMoveEvents",
468
+ "_bx_World_GetSensorEvents",
469
+ "_bx_World_GetStats",
470
+ "_bx_World_SetGravity",
471
+ "_bx_World_SetMaximumLinearSpeed",
472
+ "_bx_World_Step"
473
+ ];
474
+ var Box3DPlugin = class {
475
+ /**
476
+ * Creates a Box3D plugin.
477
+ * @param _useDeltaForWorldStep step the world with the frame delta (true) or a fixed time step (false)
478
+ * @param box3dModule the resolved Box3D WASM module (await Box3DModule())
479
+ */
480
+ constructor(_useDeltaForWorldStep = true, box3dModule) {
481
+ this._useDeltaForWorldStep = _useDeltaForWorldStep;
482
+ /** Name of the plugin */
483
+ this.name = "Box3DPlugin";
484
+ /** Number of solver sub steps per world step. Box3D recommends 4. */
485
+ this.subStepCount = 4;
486
+ /** Wall clock time of the last world step in milliseconds (JavaScript side, includes the event sync). */
487
+ this.lastStepTimeMs = 0;
488
+ this._worldSlot = 0;
489
+ this._fixedTimeStep = 1 / 60;
490
+ this._timeStep = 1 / 60;
491
+ this._maxAngularVelocity = 4 * Math.PI * 10;
492
+ this._bodies = [];
493
+ this._shapes = [];
494
+ this._bodyCollisionObservable = /* @__PURE__ */ new Map();
495
+ this._bodyCollisionEndedObservable = /* @__PURE__ */ new Map();
496
+ this._touchedInstanceMeshes = /* @__PURE__ */ new Set();
497
+ /** spherical joints whose velocity motor target has to be re-expressed in world space every step */
498
+ this._motorJoints = /* @__PURE__ */ new Set();
499
+ this._tmpVec3 = [new import_math_vector.Vector3(), new import_math_vector.Vector3(), new import_math_vector.Vector3(), new import_math_vector.Vector3()];
500
+ this._tmpQuat = [new import_math_vector.Quaternion(), new import_math_vector.Quaternion(), new import_math_vector.Quaternion()];
501
+ this._warned = /* @__PURE__ */ new Set();
502
+ /** the shim's rejected slot counter as of the last step, see executeStep */
503
+ this._rejectedSlots = 0;
504
+ /** Observable for collision started and continued events */
505
+ this.onCollisionObservable = new import_observable.Observable();
506
+ /** Observable for collision ended events */
507
+ this.onCollisionEndedObservable = new import_observable.Observable();
508
+ /** Observable for trigger entered and exited events */
509
+ this.onTriggerCollisionObservable = new import_observable.Observable();
510
+ if (!box3dModule) {
511
+ throw new Error("Box3D module is required: pass the awaited result of the Box3D module factory.");
512
+ }
513
+ const missing = RequiredNativeExports.filter((name) => typeof box3dModule[name] !== "function");
514
+ if (missing.length > 0) {
515
+ throw new Error(
516
+ `Box3DPlugin: the Box3D module is missing ${missing.length} entry point${missing.length === 1 ? "" : "s"} this build needs (${missing.slice(0, 3).join(", ")}${missing.length > 3 ? ", ..." : ""}). box3d.js and box3d.wasm have to come from the same build of this package: check what locateFile points at and clear any copied or cached box3d.wasm.`
517
+ );
518
+ }
519
+ this._b3 = box3dModule;
520
+ this.world = box3dModule;
521
+ this._worldSlot = this._b3._bx_CreateWorld(0, -9.81, 0);
522
+ if (!this._worldSlot) {
523
+ throw new Error("Box3D: could not create a world (max worlds reached?)");
524
+ }
525
+ }
526
+ // ----------------------------------------------------------------------------------------
527
+ // helpers
528
+ // ----------------------------------------------------------------------------------------
529
+ _scratch() {
530
+ return new Float32Array(this._b3.HEAPF32.buffer, this._b3._bx_Scratch(), 64);
531
+ }
532
+ _warnOnce(key, message) {
533
+ if (!this._warned.has(key)) {
534
+ this._warned.add(key);
535
+ import_logger.Logger.Warn(`Box3DPlugin: ${message}`);
536
+ }
537
+ }
538
+ _getPluginReference(body, instanceIndex) {
539
+ return body._pluginDataInstances?.length ? body._pluginDataInstances[instanceIndex ?? 0] : body._pluginData;
540
+ }
541
+ _applyToBodyOrInstances(body, fn, instanceIndex) {
542
+ if (body._pluginDataInstances?.length > 0 && instanceIndex === void 0) {
543
+ for (const data of body._pluginDataInstances) {
544
+ fn(data);
545
+ }
546
+ } else {
547
+ fn(this._getPluginReference(body, instanceIndex));
548
+ }
549
+ }
550
+ _motionTypeToNative(motionType) {
551
+ switch (motionType) {
552
+ case import_IPhysicsEnginePlugin.PhysicsMotionType.STATIC:
553
+ return 0;
554
+ case import_IPhysicsEnginePlugin.PhysicsMotionType.ANIMATED:
555
+ return 1;
556
+ default:
557
+ return 2;
558
+ }
559
+ }
560
+ _nodeTransformToRef(node, position, orientation) {
561
+ if (node.parent) {
562
+ node.computeWorldMatrix(true);
563
+ position.copyFrom(node.absolutePosition);
564
+ orientation.copyFrom(node.absoluteRotationQuaternion);
565
+ return;
566
+ }
567
+ position.copyFrom(node.position);
568
+ if (node.rotationQuaternion) {
569
+ orientation.copyFrom(node.rotationQuaternion);
570
+ } else {
571
+ import_math_vector.Quaternion.FromEulerAnglesToRef(node.rotation.x, node.rotation.y, node.rotation.z, orientation);
572
+ }
573
+ }
574
+ // ----------------------------------------------------------------------------------------
575
+ // world
576
+ // ----------------------------------------------------------------------------------------
577
+ setGravity(gravity) {
578
+ this._b3._bx_World_SetGravity(this._worldSlot, gravity.x, gravity.y, gravity.z);
579
+ }
580
+ setTimeStep(timeStep) {
581
+ this._fixedTimeStep = timeStep;
582
+ }
583
+ getTimeStep() {
584
+ return this._fixedTimeStep;
585
+ }
586
+ executeStep(delta, physicsBodies) {
587
+ for (const physicsBody of physicsBodies) {
588
+ if (physicsBody.disablePreStep) {
589
+ continue;
590
+ }
591
+ this.setPhysicsBodyTransformation(physicsBody, physicsBody.transformNode);
592
+ }
593
+ for (const joint of this._motorJoints) {
594
+ if (this._b3._bx_Joint_IsValid(joint)) {
595
+ this._b3._bx_Joint_UpdateMotorFrame(joint);
596
+ } else {
597
+ this._motorJoints.delete(joint);
598
+ }
599
+ }
600
+ const deltaTime = this._useDeltaForWorldStep ? delta : this._fixedTimeStep;
601
+ this._timeStep = deltaTime;
602
+ const start = performance.now();
603
+ this._b3._bx_World_Step(this._worldSlot, deltaTime, this.subStepCount);
604
+ const moveCount = this._b3._bx_World_GetMoveEvents(this._worldSlot);
605
+ if (moveCount > 0) {
606
+ const movePointer = this._b3._bx_MoveEventsPtr();
607
+ let buffer = new Float32Array(this._b3.HEAPF32.buffer, movePointer, moveCount * MOVE_STRIDE);
608
+ for (let i = 0; i < moveCount; i++) {
609
+ if (buffer.buffer !== this._b3.HEAPF32.buffer) {
610
+ buffer = new Float32Array(this._b3.HEAPF32.buffer, movePointer, moveCount * MOVE_STRIDE);
611
+ }
612
+ const offset = i * MOVE_STRIDE;
613
+ const ref = this._bodies[buffer[offset]];
614
+ if (!ref || ref.body.disableSync) {
615
+ continue;
616
+ }
617
+ this._syncBodyFromBuffer(ref.body, ref.index, buffer, offset + 1);
618
+ }
619
+ for (const mesh of this._touchedInstanceMeshes) {
620
+ mesh.thinInstanceBufferUpdated("matrix");
621
+ }
622
+ this._touchedInstanceMeshes.clear();
623
+ }
624
+ this._notifyCollisions();
625
+ this._notifyTriggers();
626
+ this.lastStepTimeMs = performance.now() - start;
627
+ const rejected = this._b3._bx_GetRejectedSlotCount();
628
+ if (rejected > this._rejectedSlots) {
629
+ this._rejectedSlots = rejected;
630
+ this._warnOnce(
631
+ "rejected-slots",
632
+ `the native module did not recognize ${rejected} of the handles it was given, and those calls did nothing. Something is using a body, shape or joint after it was destroyed, or box3d.js and box3d.wasm are from different builds.`
633
+ );
634
+ }
635
+ }
636
+ getPluginVersion() {
637
+ return 2;
638
+ }
639
+ setVelocityLimits(maxLinearVelocity, maxAngularVelocity) {
640
+ this._b3._bx_World_SetMaximumLinearSpeed(this._worldSlot, maxLinearVelocity);
641
+ this._maxAngularVelocity = maxAngularVelocity;
642
+ }
643
+ getMaxLinearVelocity() {
644
+ return this._b3._bx_World_GetMaximumLinearSpeed(this._worldSlot);
645
+ }
646
+ getMaxAngularVelocity() {
647
+ return this._maxAngularVelocity;
648
+ }
649
+ /**
650
+ * Box3D counters and timings for the current world.
651
+ * @returns body, shape, contact, joint and island counts plus the last step time in milliseconds
652
+ */
653
+ getStats() {
654
+ this._b3._bx_World_GetStats(this._worldSlot);
655
+ const s = this._scratch();
656
+ return { bodies: s[0], shapes: s[1], contacts: s[2], joints: s[3], islands: s[4], stepMs: s[5], collideMs: s[6], solveMs: s[7] };
657
+ }
658
+ /**
659
+ * Enables or disables island sleeping.
660
+ * @param enabled true to let resting bodies sleep
661
+ */
662
+ setSleepingEnabled(enabled) {
663
+ this._b3._bx_World_EnableSleeping(this._worldSlot, enabled ? 1 : 0);
664
+ }
665
+ /**
666
+ * Enables or disables continuous collision against static geometry.
667
+ * @param enabled true to enable continuous collision
668
+ */
669
+ setContinuousEnabled(enabled) {
670
+ this._b3._bx_World_EnableContinuous(this._worldSlot, enabled ? 1 : 0);
671
+ }
672
+ // ----------------------------------------------------------------------------------------
673
+ // bodies
674
+ // ----------------------------------------------------------------------------------------
675
+ _createNativeBody(motionType, position, orientation, startAsleep) {
676
+ const slot = this._b3._bx_CreateBody(
677
+ this._worldSlot,
678
+ this._motionTypeToNative(motionType),
679
+ position.x,
680
+ position.y,
681
+ position.z,
682
+ orientation.x,
683
+ orientation.y,
684
+ orientation.z,
685
+ orientation.w,
686
+ startAsleep ? 0 : 1
687
+ );
688
+ const data = new Box3DBodyData(slot);
689
+ data.motionType = motionType;
690
+ return data;
691
+ }
692
+ initBody(body, motionType, position, orientation) {
693
+ const data = this._createNativeBody(motionType, position, orientation, body.startAsleep);
694
+ body._pluginData = data;
695
+ this._bodies[data.slot] = { body, index: 0, data };
696
+ }
697
+ initBodyInstances(body, motionType, mesh) {
698
+ const instancesCount = mesh._thinInstanceDataStorage?.instancesCount ?? 0;
699
+ const matrixData = mesh._thinInstanceDataStorage?.matrixData;
700
+ if (!matrixData) {
701
+ return;
702
+ }
703
+ const shape = body.shape?._pluginData;
704
+ if (instancesCount > 8 && shape && this._bakesMeshCopies(shape)) {
705
+ this._warnOnce(
706
+ "instanced-mesh-child",
707
+ "a mesh inside a container has no local transform in Box3D, so every instance gets its own baked copy of the mesh data; put the mesh on the body directly, or bake the offset into the mesh, to share it."
708
+ );
709
+ }
710
+ this._createOrUpdateBodyInstances(body, motionType, matrixData, 0, instancesCount, false);
711
+ }
712
+ _createOrUpdateBodyInstances(body, motionType, matrixData, startIndex, endIndex, update) {
713
+ const rotation = import_math_vector.TmpVectors.Quaternion[0];
714
+ const rotationMatrix = import_math_vector.TmpVectors.Matrix[0];
715
+ const position = import_math_vector.TmpVectors.Vector3[0];
716
+ for (let i = startIndex; i < endIndex; i++) {
717
+ position.set(matrixData[i * 16 + 12], matrixData[i * 16 + 13], matrixData[i * 16 + 14]);
718
+ import_math_vector.Matrix.FromArrayToRef(matrixData, i * 16, rotationMatrix);
719
+ rotationMatrix.decompose(void 0, rotation, void 0);
720
+ if (update) {
721
+ const data = body._pluginDataInstances[i];
722
+ this._b3._bx_Body_SetTransform(data.slot, position.x, position.y, position.z, rotation.x, rotation.y, rotation.z, rotation.w);
723
+ } else {
724
+ const data = this._createNativeBody(motionType, position, rotation, body.startAsleep);
725
+ const sibling = body._pluginDataInstances[0];
726
+ if (sibling) {
727
+ data.userMassProps = sibling.userMassProps;
728
+ data.eventMask = sibling.eventMask;
729
+ this._applyEventFlags(data);
730
+ if (sibling.activation !== import_IPhysicsEnginePlugin.PhysicsActivationControl.SIMULATION_CONTROLLED) {
731
+ data.activation = sibling.activation;
732
+ this._applyNativeMotionType(data);
733
+ this._b3._bx_Body_EnableSleep(data.slot, sibling.activation === import_IPhysicsEnginePlugin.PhysicsActivationControl.ALWAYS_ACTIVE ? 0 : 1);
734
+ }
735
+ }
736
+ body._pluginDataInstances.push(data);
737
+ this._bodies[data.slot] = { body, index: i, data };
738
+ }
739
+ }
740
+ }
741
+ updateBodyInstances(body, mesh) {
742
+ const instancesCount = mesh._thinInstanceDataStorage?.instancesCount ?? 0;
743
+ const matrixData = mesh._thinInstanceDataStorage?.matrixData;
744
+ if (!matrixData) {
745
+ return;
746
+ }
747
+ const pluginInstancesCount = body._pluginDataInstances.length;
748
+ const motionType = this.getMotionType(body);
749
+ if (instancesCount > pluginInstancesCount) {
750
+ this._createOrUpdateBodyInstances(body, motionType, matrixData, pluginInstancesCount, instancesCount, false);
751
+ const shape = body.shape;
752
+ if (shape && shape._pluginData) {
753
+ for (let i = pluginInstancesCount; i < instancesCount; i++) {
754
+ this._setBodyShape(body._pluginDataInstances[i], shape._pluginData);
755
+ }
756
+ }
757
+ } else if (instancesCount < pluginInstancesCount) {
758
+ const instancesToRemove = pluginInstancesCount - instancesCount;
759
+ for (let i = 0; i < instancesToRemove; i++) {
760
+ const data = body._pluginDataInstances.pop();
761
+ this._destroyBodyData(data);
762
+ }
763
+ this._createOrUpdateBodyInstances(body, motionType, matrixData, 0, instancesCount, true);
764
+ }
765
+ }
766
+ _destroyBodyData(data) {
767
+ if (!data) {
768
+ return;
769
+ }
770
+ for (const shape of this._shapes) {
771
+ shape?.users.delete(data);
772
+ }
773
+ this._bodyCollisionObservable.delete(data.slot);
774
+ this._bodyCollisionEndedObservable.delete(data.slot);
775
+ this._bodies[data.slot] = void 0;
776
+ this._b3._bx_DestroyBody(data.slot);
777
+ data.slot = 0;
778
+ }
779
+ removeBody(body) {
780
+ if (body._pluginDataInstances?.length) {
781
+ for (const data of body._pluginDataInstances) {
782
+ this._destroyBodyData(data);
783
+ }
784
+ }
785
+ if (body._pluginData) {
786
+ this._destroyBodyData(body._pluginData);
787
+ }
788
+ }
789
+ sync(body) {
790
+ this.syncTransform(body, body.transformNode);
791
+ }
792
+ _syncBodyFromBuffer(body, index, buffer, offset) {
793
+ const node = body.transformNode;
794
+ if (body._pluginDataInstances.length) {
795
+ const m = node;
796
+ const matrixData = m._thinInstanceDataStorage?.matrixData;
797
+ if (!matrixData) {
798
+ return;
799
+ }
800
+ const scale = this._tmpVec3[0];
801
+ const base = index * 16;
802
+ scale.set(
803
+ Math.hypot(matrixData[base], matrixData[base + 1], matrixData[base + 2]),
804
+ Math.hypot(matrixData[base + 4], matrixData[base + 5], matrixData[base + 6]),
805
+ Math.hypot(matrixData[base + 8], matrixData[base + 9], matrixData[base + 10])
806
+ );
807
+ const quat = this._tmpQuat[0];
808
+ quat.set(buffer[offset + 3], buffer[offset + 4], buffer[offset + 5], buffer[offset + 6]);
809
+ const position = this._tmpVec3[1];
810
+ position.set(buffer[offset], buffer[offset + 1], buffer[offset + 2]);
811
+ const matrix = import_math_vector.TmpVectors.Matrix[0];
812
+ import_math_vector.Matrix.ComposeToRef(scale, quat, position, matrix);
813
+ matrix.copyToArray(matrixData, base);
814
+ this._touchedInstanceMeshes.add(m);
815
+ return;
816
+ }
817
+ this._applyTransformToNode(node, buffer[offset], buffer[offset + 1], buffer[offset + 2], buffer[offset + 3], buffer[offset + 4], buffer[offset + 5], buffer[offset + 6]);
818
+ }
819
+ _applyTransformToNode(node, px, py, pz, qx, qy, qz, qw) {
820
+ const quat = this._tmpQuat[0];
821
+ quat.set(qx, qy, qz, qw);
822
+ const parent = node.parent;
823
+ if (parent && !parent.getWorldMatrix().isIdentity()) {
824
+ parent.computeWorldMatrix(true);
825
+ const savedScaling = this._tmpVec3[2];
826
+ savedScaling.copyFrom(node.scaling);
827
+ const finalTransform = import_math_vector.TmpVectors.Matrix[0];
828
+ const finalTranslation = this._tmpVec3[3];
829
+ finalTranslation.set(px, py, pz);
830
+ import_math_vector.Matrix.ComposeToRef(node.absoluteScaling, quat, finalTranslation, finalTransform);
831
+ const parentInverse = import_math_vector.TmpVectors.Matrix[1];
832
+ parent.getWorldMatrix().invertToRef(parentInverse);
833
+ const localTransform = import_math_vector.TmpVectors.Matrix[2];
834
+ finalTransform.multiplyToRef(parentInverse, localTransform);
835
+ localTransform.decomposeToTransformNode(node);
836
+ node.rotationQuaternion?.normalize();
837
+ node.scaling.copyFrom(savedScaling);
838
+ } else {
839
+ node.position.set(px, py, pz);
840
+ if (node.rotationQuaternion) {
841
+ node.rotationQuaternion.copyFrom(quat);
842
+ } else {
843
+ quat.toEulerAnglesToRef(node.rotation);
844
+ }
845
+ }
846
+ }
847
+ syncTransform(body, transformNode) {
848
+ if (body._pluginDataInstances.length) {
849
+ const m = transformNode;
850
+ const matrixData = m._thinInstanceDataStorage?.matrixData;
851
+ if (!matrixData) {
852
+ return;
853
+ }
854
+ for (let i = 0; i < body._pluginDataInstances.length; i++) {
855
+ const data2 = body._pluginDataInstances[i];
856
+ this._b3._bx_Body_GetTransform(data2.slot);
857
+ this._syncBodyFromBuffer(body, i, this._scratch(), 0);
858
+ }
859
+ m.thinInstanceBufferUpdated("matrix");
860
+ this._touchedInstanceMeshes.delete(m);
861
+ return;
862
+ }
863
+ const data = body._pluginData;
864
+ if (!data || !data.slot) {
865
+ return;
866
+ }
867
+ this._b3._bx_Body_GetTransform(data.slot);
868
+ const s = this._scratch();
869
+ this._applyTransformToNode(transformNode, s[0], s[1], s[2], s[3], s[4], s[5], s[6]);
870
+ }
871
+ /**
872
+ * Pushes the transform node's pose into the physics body (called during the pre step).
873
+ * @param body the physics body
874
+ * @param node the transform node to read from
875
+ */
876
+ setPhysicsBodyTransformation(body, node) {
877
+ const prestep = body.getPrestepType();
878
+ if (prestep == import_IPhysicsEnginePlugin.PhysicsPrestepType.TELEPORT) {
879
+ if (body._pluginDataInstances.length > 0) {
880
+ const m = node;
881
+ const matrixData = m._thinInstanceDataStorage?.matrixData;
882
+ if (!matrixData) {
883
+ return;
884
+ }
885
+ this._createOrUpdateBodyInstances(body, body.getMotionType(), matrixData, 0, body._pluginDataInstances.length, true);
886
+ return;
887
+ }
888
+ const data = body._pluginData;
889
+ const position = this._tmpVec3[0];
890
+ const orientation = this._tmpQuat[0];
891
+ this._nodeTransformToRef(node, position, orientation);
892
+ this._b3._bx_Body_SetTransform(data.slot, position.x, position.y, position.z, orientation.x, orientation.y, orientation.z, orientation.w);
893
+ } else if (prestep == import_IPhysicsEnginePlugin.PhysicsPrestepType.ACTION) {
894
+ this.setTargetTransform(body, node.absolutePosition, node.absoluteRotationQuaternion);
895
+ } else if (prestep == import_IPhysicsEnginePlugin.PhysicsPrestepType.DISABLED) {
896
+ import_logger.Logger.Warn("Prestep type is set to DISABLED. Unable to set physics body transformation.");
897
+ }
898
+ }
899
+ setTargetTransform(body, position, rotation, instanceIndex) {
900
+ this._applyToBodyOrInstances(
901
+ body,
902
+ (data) => {
903
+ this._b3._bx_Body_SetTargetTransform(data.slot, position.x, position.y, position.z, rotation.x, rotation.y, rotation.z, rotation.w, this._timeStep);
904
+ },
905
+ instanceIndex
906
+ );
907
+ }
908
+ /** True when the shape puts a mesh inside a container, which Box3D can only do by baking a copy per body. */
909
+ _bakesMeshCopies(data, visited = /* @__PURE__ */ new Set()) {
910
+ if (visited.has(data)) {
911
+ return false;
912
+ }
913
+ visited.add(data);
914
+ return data.children.some((child) => child.type === import_IPhysicsEnginePlugin.PhysicsShapeType.MESH || this._bakesMeshCopies(child, visited));
915
+ }
916
+ _setBodyShape(data, shape) {
917
+ for (const existing of this._shapes) {
918
+ existing?.users.delete(data);
919
+ }
920
+ this._b3._bx_Body_SetShape(data.slot, shape ? shape.slot : 0);
921
+ if (shape) {
922
+ shape.users.add(data);
923
+ }
924
+ this._applyEventFlags(data);
925
+ this._internalUpdateMassProperties(data);
926
+ }
927
+ setShape(body, shape) {
928
+ const shapeData = shape && shape._pluginData ? shape._pluginData : null;
929
+ this._applyToBodyOrInstances(body, (data) => this._setBodyShape(data, shapeData));
930
+ }
931
+ getShape(body) {
932
+ return body.shape ?? null;
933
+ }
934
+ getShapeType(shape) {
935
+ return shape.type ?? shape._pluginData?.type ?? import_IPhysicsEnginePlugin.PhysicsShapeType.CONTAINER;
936
+ }
937
+ /** Turns the body's event mask into Box3D's two shape flags, so a body only pays for the events it asked for. */
938
+ _applyEventFlags(data) {
939
+ const touch = data.eventMask & (1 /* COLLISION_STARTED */ | 4 /* COLLISION_FINISHED */) ? 1 : 0;
940
+ const hit = data.eventMask & 2 /* COLLISION_CONTINUED */ ? 1 : 0;
941
+ this._b3._bx_Body_SetEventFlags(data.slot, touch, hit);
942
+ }
943
+ setEventMask(body, eventMask, instanceIndex) {
944
+ this._applyToBodyOrInstances(
945
+ body,
946
+ (data) => {
947
+ data.eventMask = eventMask;
948
+ this._applyEventFlags(data);
949
+ },
950
+ instanceIndex
951
+ );
952
+ }
953
+ getEventMask(body, instanceIndex) {
954
+ return this._getPluginReference(body, instanceIndex).eventMask;
955
+ }
956
+ setMotionType(body, motionType, instanceIndex) {
957
+ this._applyToBodyOrInstances(
958
+ body,
959
+ (data) => {
960
+ data.motionType = motionType;
961
+ this._applyNativeMotionType(data);
962
+ this._internalUpdateMassProperties(data);
963
+ },
964
+ instanceIndex
965
+ );
966
+ }
967
+ /** The Box3D body type: the Babylon motion type, unless the body is parked by ALWAYS_INACTIVE. */
968
+ _applyNativeMotionType(data) {
969
+ const native = data.activation === import_IPhysicsEnginePlugin.PhysicsActivationControl.ALWAYS_INACTIVE ? 0 : this._motionTypeToNative(data.motionType);
970
+ this._b3._bx_Body_SetType(data.slot, native);
971
+ }
972
+ /**
973
+ * Babylon's activation control, matched to Havok's behaviour with Box3D sleeping.
974
+ * ALWAYS_ACTIVE turns sleeping off for the body, SIMULATION_CONTROLLED turns it back on. ALWAYS_INACTIVE parks the
975
+ * body: in Havok such a body ignores impulses and velocity changes, is not woken by anything that touches it and
976
+ * still blocks other bodies, which Box3D sleeping alone cannot do, so it becomes a static Box3D body until the
977
+ * control changes. Coming back it is left asleep, like Havok, until something wakes it.
978
+ * @param body the physics body
979
+ * @param controlMode how the body may be activated and deactivated
980
+ * @param instanceIndex optional thin instance index
981
+ */
982
+ setActivationControl(body, controlMode, instanceIndex) {
983
+ this._applyToBodyOrInstances(
984
+ body,
985
+ (data) => {
986
+ if (data.activation === controlMode) {
987
+ return;
988
+ }
989
+ const wasInactive = data.activation === import_IPhysicsEnginePlugin.PhysicsActivationControl.ALWAYS_INACTIVE;
990
+ data.activation = controlMode;
991
+ if (controlMode === import_IPhysicsEnginePlugin.PhysicsActivationControl.ALWAYS_INACTIVE) {
992
+ this._b3._bx_Body_SetLinearVelocity(data.slot, 0, 0, 0);
993
+ this._b3._bx_Body_SetAngularVelocity(data.slot, 0, 0, 0);
994
+ this._applyNativeMotionType(data);
995
+ return;
996
+ }
997
+ this._applyNativeMotionType(data);
998
+ this._internalUpdateMassProperties(data);
999
+ this._b3._bx_Body_EnableSleep(data.slot, controlMode === import_IPhysicsEnginePlugin.PhysicsActivationControl.ALWAYS_ACTIVE ? 0 : 1);
1000
+ if (wasInactive && controlMode === import_IPhysicsEnginePlugin.PhysicsActivationControl.SIMULATION_CONTROLLED) {
1001
+ this._b3._bx_Body_SetAwake(data.slot, 0);
1002
+ }
1003
+ },
1004
+ instanceIndex
1005
+ );
1006
+ }
1007
+ getMotionType(body, instanceIndex) {
1008
+ return this._getPluginReference(body, instanceIndex).motionType;
1009
+ }
1010
+ /**
1011
+ * Reads Box3D's mass data and converts it to Babylon's form: the inertia is the principal moments of the tensor
1012
+ * divided by the mass (Babylon's inertia is per unit mass) and the orientation rotates the principal axes into
1013
+ * body space.
1014
+ */
1015
+ _readMassData(data) {
1016
+ if (data.motionType === import_IPhysicsEnginePlugin.PhysicsMotionType.DYNAMIC && data.activation !== import_IPhysicsEnginePlugin.PhysicsActivationControl.ALWAYS_INACTIVE) {
1017
+ this._b3._bx_Body_GetMassData(data.slot);
1018
+ } else {
1019
+ this._b3._bx_Body_ComputeShapeMassData(data.slot);
1020
+ }
1021
+ const s = this._scratch();
1022
+ const mass = s[0];
1023
+ const principal = new import_math_vector.Vector3();
1024
+ const orientation = new import_math_vector.Quaternion();
1025
+ DiagonalizeInertia([s[4], s[5], s[6], s[7], s[8], s[9]], principal, orientation);
1026
+ if (mass > 0) {
1027
+ principal.scaleInPlace(1 / mass);
1028
+ }
1029
+ return {
1030
+ mass,
1031
+ centerOfMass: new import_math_vector.Vector3(s[1], s[2], s[3]),
1032
+ inertia: principal,
1033
+ inertiaOrientation: orientation
1034
+ };
1035
+ }
1036
+ /** The mass properties of the body: what the user asked for, filled in from the shapes where they said nothing. */
1037
+ _resolveMassProperties(data) {
1038
+ const computed = this._readMassData(data);
1039
+ const user = data.userMassProps ?? {};
1040
+ const mass = user.mass !== void 0 && user.mass > 0 ? user.mass : computed.mass;
1041
+ return {
1042
+ mass,
1043
+ centerOfMass: user.centerOfMass ?? computed.centerOfMass,
1044
+ inertia: user.inertia ?? computed.inertia,
1045
+ inertiaOrientation: user.inertiaOrientation ?? (user.inertia ? import_math_vector.Quaternion.Identity() : computed.inertiaOrientation)
1046
+ };
1047
+ }
1048
+ _internalUpdateMassProperties(data) {
1049
+ if (data.motionType !== import_IPhysicsEnginePlugin.PhysicsMotionType.DYNAMIC || data.activation === import_IPhysicsEnginePlugin.PhysicsActivationControl.ALWAYS_INACTIVE) {
1050
+ this._applyMotionLocks(data, false, false, false);
1051
+ return;
1052
+ }
1053
+ this._b3._bx_Body_ApplyMassFromShapes(data.slot);
1054
+ const user = data.userMassProps;
1055
+ const hasUser = !!user && (user.mass !== void 0 || !!user.centerOfMass || !!user.inertia || !!user.inertiaOrientation);
1056
+ if (!hasUser) {
1057
+ this._applyMotionLocks(data, false, false, false);
1058
+ return;
1059
+ }
1060
+ const props = this._resolveMassProperties(data);
1061
+ const mass = props.mass > 0 ? props.mass : 1;
1062
+ const inertia = props.inertia;
1063
+ const orientation = props.inertiaOrientation;
1064
+ const locked = [inertia.x <= 0, inertia.y <= 0, inertia.z <= 0];
1065
+ const largest = Math.max(inertia.x, inertia.y, inertia.z, 1e-4) * LockedInertiaRatio;
1066
+ const moments = [locked[0] ? largest : inertia.x, locked[1] ? largest : inertia.y, locked[2] ? largest : inertia.z].map((value) => value * mass);
1067
+ const tensor = ComposeInertia(moments, orientation);
1068
+ this._b3._bx_Body_SetMassDataFull(
1069
+ data.slot,
1070
+ mass,
1071
+ props.centerOfMass.x,
1072
+ props.centerOfMass.y,
1073
+ props.centerOfMass.z,
1074
+ tensor[0],
1075
+ tensor[1],
1076
+ tensor[2],
1077
+ tensor[3],
1078
+ tensor[4],
1079
+ tensor[5]
1080
+ );
1081
+ this._applyLockedAxes(data, locked, orientation);
1082
+ }
1083
+ /**
1084
+ * Box3D locks are world space, Babylon's zero inertia components are body local. Locking two of them leaves a single
1085
+ * free axis, and if that axis is world aligned the body can only ever spin about it, so the world locks are exact
1086
+ * and stay exact. Everything else relies on the large moment above.
1087
+ */
1088
+ _applyLockedAxes(data, locked, orientation) {
1089
+ const count = locked.filter(Boolean).length;
1090
+ if (count === 3) {
1091
+ this._applyMotionLocks(data, true, true, true);
1092
+ return;
1093
+ }
1094
+ if (count !== 2) {
1095
+ this._applyMotionLocks(data, false, false, false);
1096
+ return;
1097
+ }
1098
+ this._b3._bx_Body_GetTransform(data.slot);
1099
+ const s = this._scratch();
1100
+ const bodyRotation = this._tmpQuat[0].set(s[3], s[4], s[5], s[6]);
1101
+ const free = locked.indexOf(false);
1102
+ const axis = this._tmpVec3[0].set(free === 0 ? 1 : 0, free === 1 ? 1 : 0, free === 2 ? 1 : 0);
1103
+ axis.applyRotationQuaternionInPlace(orientation).applyRotationQuaternionInPlace(bodyRotation);
1104
+ const components = [Math.abs(axis.x), Math.abs(axis.y), Math.abs(axis.z)];
1105
+ const dominant = components.indexOf(Math.max(...components));
1106
+ if (components[dominant] > 0.9999) {
1107
+ this._applyMotionLocks(data, dominant !== 0, dominant !== 1, dominant !== 2);
1108
+ } else {
1109
+ this._applyMotionLocks(data, false, false, false);
1110
+ }
1111
+ }
1112
+ _applyMotionLocks(data, x, y, z) {
1113
+ const locks = (x ? 1 : 0) | (y ? 2 : 0) | (z ? 4 : 0);
1114
+ if (locks === data.angularLocks) {
1115
+ return;
1116
+ }
1117
+ data.angularLocks = locks;
1118
+ this._b3._bx_Body_SetMotionLocks(data.slot, 0, 0, 0, x ? 1 : 0, y ? 1 : 0, z ? 1 : 0);
1119
+ }
1120
+ computeMassProperties(body, instanceIndex) {
1121
+ const data = this._getPluginReference(body, instanceIndex);
1122
+ this._b3._bx_Body_ApplyMassFromShapes(data.slot);
1123
+ const computed = this._readMassData(data);
1124
+ this._internalUpdateMassProperties(data);
1125
+ return computed;
1126
+ }
1127
+ setMassProperties(body, massProps, instanceIndex) {
1128
+ this._applyToBodyOrInstances(
1129
+ body,
1130
+ (data) => {
1131
+ data.userMassProps = massProps;
1132
+ this._internalUpdateMassProperties(data);
1133
+ },
1134
+ instanceIndex
1135
+ );
1136
+ }
1137
+ getMassProperties(body, instanceIndex) {
1138
+ return this._resolveMassProperties(this._getPluginReference(body, instanceIndex));
1139
+ }
1140
+ setLinearDamping(body, damping, instanceIndex) {
1141
+ this._applyToBodyOrInstances(body, (data) => this._b3._bx_Body_SetLinearDamping(data.slot, damping), instanceIndex);
1142
+ }
1143
+ getLinearDamping(body, instanceIndex) {
1144
+ return this._b3._bx_Body_GetLinearDamping(this._getPluginReference(body, instanceIndex).slot);
1145
+ }
1146
+ setAngularDamping(body, damping, instanceIndex) {
1147
+ this._applyToBodyOrInstances(body, (data) => this._b3._bx_Body_SetAngularDamping(data.slot, damping), instanceIndex);
1148
+ }
1149
+ getAngularDamping(body, instanceIndex) {
1150
+ return this._b3._bx_Body_GetAngularDamping(this._getPluginReference(body, instanceIndex).slot);
1151
+ }
1152
+ setLinearVelocity(body, linVel, instanceIndex) {
1153
+ this._applyToBodyOrInstances(body, (data) => this._b3._bx_Body_SetLinearVelocity(data.slot, linVel.x, linVel.y, linVel.z), instanceIndex);
1154
+ }
1155
+ getLinearVelocityToRef(body, linVel, instanceIndex) {
1156
+ this._b3._bx_Body_GetLinearVelocity(this._getPluginReference(body, instanceIndex).slot);
1157
+ const s = this._scratch();
1158
+ linVel.set(s[0], s[1], s[2]);
1159
+ }
1160
+ applyImpulse(body, impulse, location, instanceIndex) {
1161
+ this._applyToBodyOrInstances(
1162
+ body,
1163
+ (data) => this._b3._bx_Body_ApplyLinearImpulse(data.slot, impulse.x, impulse.y, impulse.z, location.x, location.y, location.z),
1164
+ instanceIndex
1165
+ );
1166
+ }
1167
+ applyAngularImpulse(body, angularImpulse, instanceIndex) {
1168
+ this._applyToBodyOrInstances(body, (data) => this._b3._bx_Body_ApplyAngularImpulse(data.slot, angularImpulse.x, angularImpulse.y, angularImpulse.z), instanceIndex);
1169
+ }
1170
+ applyForce(body, force, location, instanceIndex) {
1171
+ this._applyToBodyOrInstances(body, (data) => this._b3._bx_Body_ApplyForce(data.slot, force.x, force.y, force.z, location.x, location.y, location.z), instanceIndex);
1172
+ }
1173
+ applyTorque(body, torque, instanceIndex) {
1174
+ this._applyToBodyOrInstances(body, (data) => this._b3._bx_Body_ApplyTorque(data.slot, torque.x, torque.y, torque.z), instanceIndex);
1175
+ }
1176
+ setAngularVelocity(body, angVel, instanceIndex) {
1177
+ this._applyToBodyOrInstances(body, (data) => this._b3._bx_Body_SetAngularVelocity(data.slot, angVel.x, angVel.y, angVel.z), instanceIndex);
1178
+ }
1179
+ getAngularVelocityToRef(body, angVel, instanceIndex) {
1180
+ this._b3._bx_Body_GetAngularVelocity(this._getPluginReference(body, instanceIndex).slot);
1181
+ const s = this._scratch();
1182
+ angVel.set(s[0], s[1], s[2]);
1183
+ }
1184
+ getBodyGeometry(body) {
1185
+ const shape = body.shape?._pluginData;
1186
+ if (!shape) {
1187
+ return { positions: [], indices: [] };
1188
+ }
1189
+ const vertexCount = this._b3._bx_ShapeDesc_BuildDebugGeometry(shape.slot);
1190
+ const indexCount = this._b3._bx_DebugIndexCount();
1191
+ if (!vertexCount || !indexCount) {
1192
+ return { positions: [], indices: [] };
1193
+ }
1194
+ const positions = new Float32Array(this._b3.HEAPF32.buffer, this._b3._bx_DebugPositionsPtr(), vertexCount * 3).slice();
1195
+ const indices = new Uint32Array(this._b3.HEAPU32.buffer, this._b3._bx_DebugIndicesPtr(), indexCount).slice();
1196
+ return { positions, indices };
1197
+ }
1198
+ disposeBody(body) {
1199
+ this.removeBody(body);
1200
+ body._pluginData = void 0;
1201
+ body._pluginDataInstances.length = 0;
1202
+ }
1203
+ setCollisionCallbackEnabled(body, enabled, instanceIndex) {
1204
+ this.setEventMask(body, enabled ? 7 /* ALL */ : 0, instanceIndex);
1205
+ }
1206
+ setCollisionEndedCallbackEnabled(body, enabled, instanceIndex) {
1207
+ this._applyToBodyOrInstances(
1208
+ body,
1209
+ (data) => {
1210
+ data.eventMask = enabled ? data.eventMask | 4 /* COLLISION_FINISHED */ : data.eventMask & ~4 /* COLLISION_FINISHED */;
1211
+ this._applyEventFlags(data);
1212
+ },
1213
+ instanceIndex
1214
+ );
1215
+ }
1216
+ getCollisionObservable(body, instanceIndex) {
1217
+ const slot = this._getPluginReference(body, instanceIndex).slot;
1218
+ let observable = this._bodyCollisionObservable.get(slot);
1219
+ if (!observable) {
1220
+ observable = new import_observable.Observable();
1221
+ this._bodyCollisionObservable.set(slot, observable);
1222
+ }
1223
+ return observable;
1224
+ }
1225
+ getCollisionEndedObservable(body, instanceIndex) {
1226
+ const slot = this._getPluginReference(body, instanceIndex).slot;
1227
+ let observable = this._bodyCollisionEndedObservable.get(slot);
1228
+ if (!observable) {
1229
+ observable = new import_observable.Observable();
1230
+ this._bodyCollisionEndedObservable.set(slot, observable);
1231
+ }
1232
+ return observable;
1233
+ }
1234
+ setGravityFactor(body, factor, instanceIndex) {
1235
+ this._applyToBodyOrInstances(body, (data) => this._b3._bx_Body_SetGravityScale(data.slot, factor), instanceIndex);
1236
+ }
1237
+ getGravityFactor(body, instanceIndex) {
1238
+ return this._b3._bx_Body_GetGravityScale(this._getPluginReference(body, instanceIndex).slot);
1239
+ }
1240
+ // ----------------------------------------------------------------------------------------
1241
+ // shapes
1242
+ // ----------------------------------------------------------------------------------------
1243
+ _registerShape(shape, slot, type) {
1244
+ if (!slot) {
1245
+ throw new Error("Box3DPlugin: shape creation failed (degenerate geometry?)");
1246
+ }
1247
+ const data = new Box3DShapeData(slot, type);
1248
+ shape._pluginData = data;
1249
+ this._shapes[slot] = data;
1250
+ return data;
1251
+ }
1252
+ _withFloatBuffer(values, fn) {
1253
+ const ptr = this._b3._malloc(values.length * 4);
1254
+ new Float32Array(this._b3.HEAPF32.buffer, ptr, values.length).set(values);
1255
+ try {
1256
+ return fn(ptr);
1257
+ } finally {
1258
+ this._b3._free(ptr);
1259
+ }
1260
+ }
1261
+ /** Copies bytes into the module for the length of `fn`; with no bytes, `fn` gets a null pointer. */
1262
+ _withByteBuffer(values, fn) {
1263
+ if (!values || values.length === 0) {
1264
+ return fn(0);
1265
+ }
1266
+ const ptr = this._b3._malloc(values.length);
1267
+ new Uint8Array(this._b3.HEAPU8.buffer, ptr, values.length).set(values);
1268
+ try {
1269
+ return fn(ptr);
1270
+ } finally {
1271
+ this._b3._free(ptr);
1272
+ }
1273
+ }
1274
+ _withIntBuffer(values, fn) {
1275
+ const ptr = this._b3._malloc(values.length * 4);
1276
+ new Int32Array(this._b3.HEAP32.buffer, ptr, values.length).set(values);
1277
+ try {
1278
+ return fn(ptr);
1279
+ } finally {
1280
+ this._b3._free(ptr);
1281
+ }
1282
+ }
1283
+ _createOptionsFromGroundMesh(options) {
1284
+ const mesh = options.groundMesh;
1285
+ if (!mesh) {
1286
+ return;
1287
+ }
1288
+ const pos = mesh.getVerticesData(import_buffer.VertexBuffer.PositionKind);
1289
+ const transform = mesh.computeWorldMatrix(true);
1290
+ const transformed = [];
1291
+ const p = import_math_vector.TmpVectors.Vector3[0];
1292
+ for (let i = 0; i < pos.length; i += 3) {
1293
+ import_math_vector.Vector3.FromArrayToRef(pos, i, p);
1294
+ import_math_vector.Vector3.TransformCoordinatesToRef(p, transform, p);
1295
+ p.toArray(transformed, i);
1296
+ }
1297
+ const arraySize = ~~(Math.sqrt(transformed.length / 3) - 1);
1298
+ const boundingInfo = mesh.getBoundingInfo();
1299
+ const dim = Math.min(boundingInfo.boundingBox.extendSizeWorld.x, boundingInfo.boundingBox.extendSizeWorld.z);
1300
+ const minX = boundingInfo.boundingBox.minimumWorld.x;
1301
+ const minY = boundingInfo.boundingBox.minimumWorld.y;
1302
+ const minZ = boundingInfo.boundingBox.minimumWorld.z;
1303
+ const matrix = new Float32Array((arraySize + 1) * (arraySize + 1));
1304
+ const elementSize = dim * 2 / arraySize;
1305
+ matrix.fill(minY);
1306
+ for (let i = 0; i < transformed.length; i += 3) {
1307
+ const x = Math.round((transformed[i + 0] - minX) / elementSize);
1308
+ const z = arraySize - Math.round((transformed[i + 2] - minZ) / elementSize);
1309
+ matrix[z * (arraySize + 1) + x] = transformed[i + 1] - minY;
1310
+ }
1311
+ options.numHeightFieldSamplesX = arraySize + 1;
1312
+ options.numHeightFieldSamplesZ = arraySize + 1;
1313
+ options.heightFieldSizeX = boundingInfo.boundingBox.extendSizeWorld.x * 2;
1314
+ options.heightFieldSizeZ = boundingInfo.boundingBox.extendSizeWorld.z * 2;
1315
+ options.heightFieldData = matrix;
1316
+ }
1317
+ initShape(shape, type, options) {
1318
+ const b3 = this._b3;
1319
+ const center = options.center ?? import_math_vector.Vector3.ZeroReadOnly;
1320
+ const rotation = options.rotation ?? import_math_vector.Quaternion.Identity();
1321
+ switch (type) {
1322
+ case import_IPhysicsEnginePlugin.PhysicsShapeType.SPHERE: {
1323
+ const radius = options.radius ?? 1;
1324
+ this._registerShape(shape, b3._bx_ShapeDesc_CreateSphere(center.x, center.y, center.z, radius), type);
1325
+ break;
1326
+ }
1327
+ case import_IPhysicsEnginePlugin.PhysicsShapeType.CAPSULE: {
1328
+ const a = options.pointA ?? import_math_vector.Vector3.ZeroReadOnly;
1329
+ const b = options.pointB ?? import_math_vector.Vector3.UpReadOnly;
1330
+ this._registerShape(shape, b3._bx_ShapeDesc_CreateCapsule(a.x, a.y, a.z, b.x, b.y, b.z, options.radius ?? 0), type);
1331
+ break;
1332
+ }
1333
+ case import_IPhysicsEnginePlugin.PhysicsShapeType.BOX: {
1334
+ const extents = options.extents ?? import_math_vector.Vector3.OneReadOnly;
1335
+ this._registerShape(
1336
+ shape,
1337
+ b3._bx_ShapeDesc_CreateBox(extents.x * 0.5, extents.y * 0.5, extents.z * 0.5, center.x, center.y, center.z, rotation.x, rotation.y, rotation.z, rotation.w),
1338
+ type
1339
+ );
1340
+ break;
1341
+ }
1342
+ case import_IPhysicsEnginePlugin.PhysicsShapeType.CYLINDER: {
1343
+ const a = options.pointA ?? import_math_vector.Vector3.ZeroReadOnly;
1344
+ const b = options.pointB ?? import_math_vector.Vector3.UpReadOnly;
1345
+ const axis = b.subtract(a);
1346
+ const height = axis.length();
1347
+ const mid = a.add(b).scaleInPlace(0.5);
1348
+ const q = this._tmpQuat[0];
1349
+ if (height > 1e-6) {
1350
+ axis.scaleInPlace(1 / height);
1351
+ import_math_vector.Quaternion.FromUnitVectorsToRef(import_math_vector.Vector3.UpReadOnly, axis, q);
1352
+ } else {
1353
+ q.set(0, 0, 0, 1);
1354
+ }
1355
+ this._registerShape(shape, b3._bx_ShapeDesc_CreateCylinder(height, options.radius ?? 0, 16, mid.x, mid.y, mid.z, q.x, q.y, q.z, q.w), type);
1356
+ break;
1357
+ }
1358
+ case import_IPhysicsEnginePlugin.PhysicsShapeType.CONVEX_HULL:
1359
+ case import_IPhysicsEnginePlugin.PhysicsShapeType.MESH: {
1360
+ const mesh = options.mesh;
1361
+ if (!mesh) {
1362
+ throw new Error("No mesh provided to create physics shape.");
1363
+ }
1364
+ const needIndices = type === import_IPhysicsEnginePlugin.PhysicsShapeType.MESH;
1365
+ const flipWinding = !mesh.getScene().useRightHandedSystem;
1366
+ const accum = new MeshGeometryAccumulator(needIndices, flipWinding);
1367
+ accum.addNodeMeshes(mesh, !!options.includeChildMeshes);
1368
+ if (type === import_IPhysicsEnginePlugin.PhysicsShapeType.CONVEX_HULL) {
1369
+ const slot = this._withFloatBuffer(accum.vertices, (ptr) => b3._bx_ShapeDesc_CreateHull(ptr, accum.vertexCount));
1370
+ this._registerShape(shape, slot, type);
1371
+ } else {
1372
+ const slot = this._withFloatBuffer(
1373
+ accum.vertices,
1374
+ (vptr) => this._withIntBuffer(accum.indices, (iptr) => b3._bx_ShapeDesc_CreateMesh(vptr, accum.vertexCount, iptr, accum.triangleCount, 1, 1, 1, 0, 0))
1375
+ );
1376
+ this._registerShape(shape, slot, type);
1377
+ }
1378
+ break;
1379
+ }
1380
+ case import_IPhysicsEnginePlugin.PhysicsShapeType.HEIGHTFIELD: {
1381
+ if (options.groundMesh) {
1382
+ this._createOptionsFromGroundMesh(options);
1383
+ }
1384
+ const nx = options.numHeightFieldSamplesX;
1385
+ const nz = options.numHeightFieldSamplesZ;
1386
+ if (!nx || !nz || !options.heightFieldSizeX || !options.heightFieldSizeZ || !options.heightFieldData) {
1387
+ throw new Error("Missing required heightfield parameters");
1388
+ }
1389
+ const heights = new Float32Array(nx * nz);
1390
+ for (let x = 0; x < nx; x++) {
1391
+ for (let z = 0; z < nz; z++) {
1392
+ heights[z * nx + x] = options.heightFieldData[(nz - 1 - z) * nx + x];
1393
+ }
1394
+ }
1395
+ const scaleX = options.heightFieldSizeX / (nx - 1);
1396
+ const scaleZ = options.heightFieldSizeZ / (nz - 1);
1397
+ const cellsX = nx - 1;
1398
+ const cellsZ = nz - 1;
1399
+ const source = options.heightFieldMaterials;
1400
+ if (source && source.length !== cellsX * cellsZ) {
1401
+ throw new Error(`heightFieldMaterials needs ${cellsX * cellsZ} cells (${cellsX} \xD7 ${cellsZ}), got ${source.length}`);
1402
+ }
1403
+ const materials = source ? new Uint8Array(cellsX * cellsZ) : null;
1404
+ if (source && materials) {
1405
+ for (let x = 0; x < cellsX; x++) {
1406
+ for (let z = 0; z < cellsZ; z++) {
1407
+ materials[z * cellsX + x] = source[(cellsZ - 1 - z) * cellsX + x];
1408
+ }
1409
+ }
1410
+ }
1411
+ const slot = this._withFloatBuffer(
1412
+ heights,
1413
+ (heightsPtr) => this._withByteBuffer(
1414
+ materials,
1415
+ (materialsPtr) => b3._bx_ShapeDesc_CreateHeightField(
1416
+ heightsPtr,
1417
+ materialsPtr,
1418
+ nx,
1419
+ nz,
1420
+ scaleX,
1421
+ 1,
1422
+ scaleZ,
1423
+ 0,
1424
+ -options.heightFieldSizeX * 0.5,
1425
+ 0,
1426
+ -options.heightFieldSizeZ * 0.5
1427
+ )
1428
+ )
1429
+ );
1430
+ this._registerShape(shape, slot, type);
1431
+ break;
1432
+ }
1433
+ case import_IPhysicsEnginePlugin.PhysicsShapeType.CONTAINER: {
1434
+ this._registerShape(shape, b3._bx_ShapeDesc_CreateContainer(), type);
1435
+ break;
1436
+ }
1437
+ default:
1438
+ throw new Error("Unsupported Shape Type.");
1439
+ }
1440
+ }
1441
+ // Re-instantiates the shape on every body that uses it (or uses a container holding it). Only needed for geometry
1442
+ // and child changes; filters, materials and density are applied in place, see _syncShapeUsers.
1443
+ _refreshShapeUsers(data, visited = /* @__PURE__ */ new Set()) {
1444
+ if (visited.has(data)) {
1445
+ return;
1446
+ }
1447
+ visited.add(data);
1448
+ for (const body of data.users) {
1449
+ this._b3._bx_Body_SetShape(body.slot, data.slot);
1450
+ this._applyEventFlags(body);
1451
+ this._internalUpdateMassProperties(body);
1452
+ }
1453
+ for (const parent of data.parents) {
1454
+ this._refreshShapeUsers(parent, visited);
1455
+ }
1456
+ }
1457
+ /**
1458
+ * Pushes changed description properties into the Box3D shapes that are already on the bodies using this shape,
1459
+ * directly or through a container. Rebuilding them instead would drop their contacts and re-apply the body mass.
1460
+ */
1461
+ _syncShapeUsers(data, what, visited = /* @__PURE__ */ new Set(), changed = data) {
1462
+ if (visited.has(data)) {
1463
+ return;
1464
+ }
1465
+ visited.add(data);
1466
+ for (const body of data.users) {
1467
+ this._b3._bx_Body_SyncShapeDesc(body.slot, changed.slot, what);
1468
+ if (what & 4 /* DENSITY */) {
1469
+ this._internalUpdateMassProperties(body);
1470
+ }
1471
+ }
1472
+ for (const parent of data.parents) {
1473
+ this._syncShapeUsers(parent, what, visited, changed);
1474
+ }
1475
+ }
1476
+ setShapeFilterMembershipMask(shape, membershipMask) {
1477
+ const data = shape._pluginData;
1478
+ const collide = this._b3._bx_ShapeDesc_GetMaskBits(data.slot);
1479
+ this._b3._bx_ShapeDesc_SetFilter(data.slot, membershipMask >>> 0, collide >>> 0);
1480
+ this._syncShapeUsers(data, 1 /* FILTER */);
1481
+ }
1482
+ getShapeFilterMembershipMask(shape) {
1483
+ return this._b3._bx_ShapeDesc_GetCategoryBits(shape._pluginData.slot);
1484
+ }
1485
+ setShapeFilterCollideMask(shape, collideMask) {
1486
+ const data = shape._pluginData;
1487
+ const membership = this._b3._bx_ShapeDesc_GetCategoryBits(data.slot);
1488
+ this._b3._bx_ShapeDesc_SetFilter(data.slot, membership >>> 0, collideMask >>> 0);
1489
+ this._syncShapeUsers(data, 1 /* FILTER */);
1490
+ }
1491
+ getShapeFilterCollideMask(shape) {
1492
+ return this._b3._bx_ShapeDesc_GetMaskBits(shape._pluginData.slot);
1493
+ }
1494
+ setMaterial(shape, material) {
1495
+ const data = shape._pluginData;
1496
+ data.material = material;
1497
+ this._b3._bx_ShapeDesc_SetMaterial(data.slot, material.friction ?? 0.5, material.restitution ?? 0);
1498
+ this._syncShapeUsers(data, 2 /* MATERIAL */);
1499
+ }
1500
+ getMaterial(shape) {
1501
+ const data = shape._pluginData;
1502
+ return {
1503
+ friction: this._b3._bx_ShapeDesc_GetFriction(data.slot),
1504
+ staticFriction: data.material.staticFriction,
1505
+ restitution: this._b3._bx_ShapeDesc_GetRestitution(data.slot),
1506
+ frictionCombine: data.material.frictionCombine,
1507
+ restitutionCombine: data.material.restitutionCombine
1508
+ };
1509
+ }
1510
+ setDensity(shape, density) {
1511
+ const data = shape._pluginData;
1512
+ this._b3._bx_ShapeDesc_SetDensity(data.slot, density);
1513
+ this._syncShapeUsers(data, 4 /* DENSITY */);
1514
+ }
1515
+ getDensity(shape) {
1516
+ return this._b3._bx_ShapeDesc_GetDensity(shape._pluginData.slot);
1517
+ }
1518
+ addChild(shape, newChild, translation, rotation, scale) {
1519
+ const parent = shape._pluginData;
1520
+ const child = newChild._pluginData;
1521
+ const t = translation ?? import_math_vector.Vector3.ZeroReadOnly;
1522
+ const r = rotation ?? import_math_vector.Quaternion.Identity();
1523
+ const s = scale ?? import_math_vector.Vector3.OneReadOnly;
1524
+ this._b3._bx_ShapeDesc_AddChild(parent.slot, child.slot, t.x, t.y, t.z, r.x, r.y, r.z, r.w, s.x, s.y, s.z);
1525
+ parent.children.push(child);
1526
+ child.parents.add(parent);
1527
+ this._refreshShapeUsers(parent);
1528
+ }
1529
+ removeChild(shape, childIndex) {
1530
+ const parent = shape._pluginData;
1531
+ this._b3._bx_ShapeDesc_RemoveChild(parent.slot, childIndex);
1532
+ const [child] = parent.children.splice(childIndex, 1);
1533
+ if (child && !parent.children.includes(child)) {
1534
+ child.parents.delete(parent);
1535
+ }
1536
+ this._refreshShapeUsers(parent);
1537
+ }
1538
+ getNumChildren(shape) {
1539
+ return this._b3._bx_ShapeDesc_GetChildCount(shape._pluginData.slot);
1540
+ }
1541
+ getBoundingBox(shape) {
1542
+ this._b3._bx_ShapeDesc_GetAABB(shape._pluginData.slot);
1543
+ const s = this._scratch();
1544
+ import_math_vector.TmpVectors.Vector3[0].set(s[0], s[1], s[2]);
1545
+ import_math_vector.TmpVectors.Vector3[1].set(s[3], s[4], s[5]);
1546
+ return new import_boundingBox.BoundingBox(import_math_vector.TmpVectors.Vector3[0], import_math_vector.TmpVectors.Vector3[1], import_math_vector.Matrix.IdentityReadOnly);
1547
+ }
1548
+ getBodyBoundingBox(body) {
1549
+ const data = this._getPluginReference(body);
1550
+ this._b3._bx_Body_GetAABB(data.slot);
1551
+ const s = this._scratch();
1552
+ import_math_vector.TmpVectors.Vector3[0].set(s[0], s[1], s[2]);
1553
+ import_math_vector.TmpVectors.Vector3[1].set(s[3], s[4], s[5]);
1554
+ return new import_boundingBox.BoundingBox(import_math_vector.TmpVectors.Vector3[0], import_math_vector.TmpVectors.Vector3[1], import_math_vector.Matrix.IdentityReadOnly);
1555
+ }
1556
+ disposeShape(shape) {
1557
+ const data = shape._pluginData;
1558
+ if (!data) {
1559
+ return;
1560
+ }
1561
+ for (const parent of data.parents) {
1562
+ const index = parent.children.indexOf(data);
1563
+ if (index >= 0) {
1564
+ parent.children.splice(index, 1);
1565
+ this._b3._bx_ShapeDesc_RemoveChild(parent.slot, index);
1566
+ }
1567
+ }
1568
+ for (const child of data.children) {
1569
+ child.parents.delete(data);
1570
+ }
1571
+ this._shapes[data.slot] = void 0;
1572
+ this._b3._bx_ShapeDesc_Destroy(data.slot);
1573
+ shape._pluginData = void 0;
1574
+ }
1575
+ setTrigger(shape, isTrigger) {
1576
+ const data = shape._pluginData;
1577
+ this._b3._bx_ShapeDesc_SetSensor(data.slot, isTrigger ? 1 : 0);
1578
+ this._refreshShapeUsers(data);
1579
+ }
1580
+ // ----------------------------------------------------------------------------------------
1581
+ // constraints
1582
+ // ----------------------------------------------------------------------------------------
1583
+ _frameToBuffer(buffer, offset, pivot, xAxis, yAxis, zAxis) {
1584
+ const m = import_math_vector.TmpVectors.Matrix[0];
1585
+ import_math_vector.Matrix.FromValuesToRef(xAxis.x, xAxis.y, xAxis.z, 0, yAxis.x, yAxis.y, yAxis.z, 0, zAxis.x, zAxis.y, zAxis.z, 0, 0, 0, 0, 1, m);
1586
+ const q = this._tmpQuat[2];
1587
+ import_math_vector.Quaternion.FromRotationMatrixToRef(m, q);
1588
+ q.normalize();
1589
+ buffer[offset] = pivot.x;
1590
+ buffer[offset + 1] = pivot.y;
1591
+ buffer[offset + 2] = pivot.z;
1592
+ buffer[offset + 3] = q.x;
1593
+ buffer[offset + 4] = q.y;
1594
+ buffer[offset + 5] = q.z;
1595
+ buffer[offset + 6] = q.w;
1596
+ }
1597
+ _buildFrames(cdata, constraint, bodyA, bodyB) {
1598
+ const options = constraint.options;
1599
+ const pivotA = options.pivotA ?? import_math_vector.Vector3.ZeroReadOnly;
1600
+ const pivotB = options.pivotB ?? import_math_vector.Vector3.ZeroReadOnly;
1601
+ const axisA = (options.axisA ?? import_math_vector.Vector3.RightReadOnly).normalizeToNew();
1602
+ const axisB = (options.axisB ?? import_math_vector.Vector3.RightReadOnly).normalizeToNew();
1603
+ const perpA = options.perpAxisA ? options.perpAxisA.normalizeToNew() : axisA.getNormalToRef(new import_math_vector.Vector3());
1604
+ const perpB = options.perpAxisB ? options.perpAxisB.normalizeToNew() : axisB.getNormalToRef(new import_math_vector.Vector3());
1605
+ perpA.subtractInPlace(axisA.scale(import_math_vector.Vector3.Dot(axisA, perpA))).normalize();
1606
+ perpB.subtractInPlace(axisB.scale(import_math_vector.Vector3.Dot(axisB, perpB))).normalize();
1607
+ const thirdA = import_math_vector.Vector3.Cross(axisA, perpA);
1608
+ const thirdB = import_math_vector.Vector3.Cross(axisB, perpB);
1609
+ if (!constraint._initOptions) {
1610
+ constraint._initOptions = {
1611
+ axisA: axisA.clone(),
1612
+ axisB: axisB.clone(),
1613
+ perpAxisA: perpA.clone(),
1614
+ perpAxisB: perpB.clone(),
1615
+ pivotA: pivotA.clone(),
1616
+ pivotB: pivotB.clone()
1617
+ };
1618
+ }
1619
+ const frames = cdata.frames;
1620
+ if (cdata.plan) {
1621
+ const basis = cdata.plan.basis;
1622
+ if (basis) {
1623
+ const basisA = [axisA, perpA, thirdA];
1624
+ const basisB = [axisB, perpB, thirdB];
1625
+ this._frameToBuffer(frames, 0, pivotA, basisA[basis[0]], basisA[basis[1]], basisA[basis[2]]);
1626
+ this._frameToBuffer(frames, 7, pivotB, basisB[basis[0]], basisB[basis[1]], basisB[basis[2]]);
1627
+ } else {
1628
+ frames.set([pivotA.x, pivotA.y, pivotA.z, 0, 0, 0, 1, pivotB.x, pivotB.y, pivotB.z, 0, 0, 0, 1]);
1629
+ }
1630
+ return;
1631
+ }
1632
+ switch (cdata.type) {
1633
+ case import_IPhysicsEnginePlugin.PhysicsConstraintType.HINGE:
1634
+ case import_IPhysicsEnginePlugin.PhysicsConstraintType.BALL_AND_SOCKET:
1635
+ this._frameToBuffer(frames, 0, pivotA, perpA, import_math_vector.Vector3.Cross(axisA, perpA), axisA);
1636
+ this._frameToBuffer(frames, 7, pivotB, perpB, import_math_vector.Vector3.Cross(axisB, perpB), axisB);
1637
+ break;
1638
+ case import_IPhysicsEnginePlugin.PhysicsConstraintType.PRISMATIC:
1639
+ case import_IPhysicsEnginePlugin.PhysicsConstraintType.SLIDER:
1640
+ this._frameToBuffer(frames, 0, pivotA, axisA, perpA, thirdA);
1641
+ this._frameToBuffer(frames, 7, pivotB, axisB, perpB, thirdB);
1642
+ break;
1643
+ case import_IPhysicsEnginePlugin.PhysicsConstraintType.LOCK: {
1644
+ this._b3._bx_Body_GetTransform(bodyA.slot);
1645
+ let s = this._scratch();
1646
+ const qA = this._tmpQuat[0];
1647
+ qA.set(s[3], s[4], s[5], s[6]);
1648
+ this._b3._bx_Body_GetTransform(bodyB.slot);
1649
+ s = this._scratch();
1650
+ const qB = this._tmpQuat[1];
1651
+ qB.set(s[3], s[4], s[5], s[6]);
1652
+ const rel = qB.invert().multiply(qA).normalize();
1653
+ frames.set([pivotA.x, pivotA.y, pivotA.z, 0, 0, 0, 1, pivotB.x, pivotB.y, pivotB.z, rel.x, rel.y, rel.z, rel.w]);
1654
+ break;
1655
+ }
1656
+ default:
1657
+ frames.set([pivotA.x, pivotA.y, pivotA.z, 0, 0, 0, 1, pivotB.x, pivotB.y, pivotB.z, 0, 0, 0, 1]);
1658
+ break;
1659
+ }
1660
+ }
1661
+ _createJoint(cdata, bodyA, bodyB) {
1662
+ this._scratch().set(cdata.frames);
1663
+ const joint = this._b3._bx_CreateJoint(this._worldSlot, cdata.jointType, bodyA.slot, bodyB.slot, cdata.collisions ? 1 : 0, cdata.length);
1664
+ if (!joint) {
1665
+ import_logger.Logger.Warn("Box3DPlugin: joint creation failed.");
1666
+ return 0;
1667
+ }
1668
+ this._applyAxisState(cdata, joint, bodyA, bodyB);
1669
+ return joint;
1670
+ }
1671
+ /** Reads Physics6DoFConstraint.limits into the axis table with Havok's rules. */
1672
+ _readSixDofLimits(cdata, limits) {
1673
+ for (const limit of limits ?? []) {
1674
+ const state = this._axisState(cdata, limit.axis);
1675
+ if (limit.minLimit === 0 && limit.maxLimit === 0) {
1676
+ state.mode = import_IPhysicsEnginePlugin.PhysicsConstraintAxisLimitMode.LOCKED;
1677
+ state.min = 0;
1678
+ state.max = 0;
1679
+ } else if (limit.minLimit !== void 0 || limit.maxLimit !== void 0) {
1680
+ state.mode = import_IPhysicsEnginePlugin.PhysicsConstraintAxisLimitMode.LIMITED;
1681
+ state.min = limit.minLimit ?? -Infinity;
1682
+ state.max = limit.maxLimit ?? Infinity;
1683
+ }
1684
+ state.stiffness = limit.stiffness ?? 0;
1685
+ state.damping = limit.damping ?? 0;
1686
+ }
1687
+ }
1688
+ /**
1689
+ * Maps the six axes of a SIX_DOF constraint onto one Box3D joint, following Havok's semantics: an axis that is not
1690
+ * listed is free, 0/0 is locked, anything else is limited. The constraint frame is [axis, perpAxis, axis x perpAxis];
1691
+ * ANGULAR_X rotates about axis, ANGULAR_Y about perpAxis and ANGULAR_Z about axis x perpAxis, all measured as the
1692
+ * rotation of the child frame relative to the parent frame (right handed, like Havok).
1693
+ * - all linear locked, all angular locked: weld
1694
+ * - all linear locked, one angular axis open: revolute about it, with its limits (one sided ranges work)
1695
+ * - all linear locked, two or three angular axes open: spherical, twist limits from ANGULAR_X and one symmetric
1696
+ * cone from ANGULAR_Y/Z (Box3D has no elliptical cone, the larger swing range is used)
1697
+ * - two linear locked, angular locked: prismatic along the open linear axis
1698
+ * - only LINEAR_DISTANCE constrained: distance joint (a spring when min == max and a stiffness is set)
1699
+ * - nothing constrained: filter joint (collision filtering only)
1700
+ * Anything else warns once and uses the closest of these.
1701
+ */
1702
+ _planSixDof(cdata) {
1703
+ const Axis = import_IPhysicsEnginePlugin.PhysicsConstraintAxis;
1704
+ const Mode = import_IPhysicsEnginePlugin.PhysicsConstraintAxisLimitMode;
1705
+ const mode = (axis) => cdata.axes.get(axis)?.mode ?? Mode.FREE;
1706
+ const range = (axis) => {
1707
+ const state = cdata.axes.get(axis);
1708
+ return state.mode === Mode.LOCKED ? [0, 0] : [Math.min(state.min, state.max), Math.max(state.min, state.max)];
1709
+ };
1710
+ const clamp = (value, limit) => Math.max(-limit, Math.min(limit, value));
1711
+ const maxAngle = 0.99 * Math.PI;
1712
+ const maxLength = 1e5;
1713
+ const plan = {
1714
+ jointType: 0 /* WELD */,
1715
+ basis: [0, 1, 2],
1716
+ primaryAxis: null,
1717
+ coneAngle: -1,
1718
+ twist: null,
1719
+ limit: null,
1720
+ spring: false,
1721
+ stiffness: 0,
1722
+ damping: 0,
1723
+ angularStiffness: false
1724
+ };
1725
+ for (const [axis, state] of cdata.axes) {
1726
+ if ((state.stiffness > 0 || state.damping > 0) && state.stiffness >= plan.stiffness) {
1727
+ plan.stiffness = state.stiffness;
1728
+ plan.damping = state.damping;
1729
+ plan.angularStiffness = axis === Axis.ANGULAR_X || axis === Axis.ANGULAR_Y || axis === Axis.ANGULAR_Z;
1730
+ }
1731
+ }
1732
+ const linear = [Axis.LINEAR_X, Axis.LINEAR_Y, Axis.LINEAR_Z];
1733
+ const angular = [Axis.ANGULAR_X, Axis.ANGULAR_Y, Axis.ANGULAR_Z];
1734
+ const linearLocked = linear.filter((a) => mode(a) === Mode.LOCKED).length;
1735
+ const linearFree = linear.filter((a) => mode(a) === Mode.FREE).length;
1736
+ const angularOpen = angular.filter((a) => mode(a) !== Mode.LOCKED);
1737
+ const angularFree = angular.filter((a) => mode(a) === Mode.FREE).length;
1738
+ const distanceMode = mode(Axis.LINEAR_DISTANCE);
1739
+ if (distanceMode !== Mode.FREE) {
1740
+ if (linearFree === 3 && angularFree === 3) {
1741
+ const [lower, upper] = range(Axis.LINEAR_DISTANCE);
1742
+ plan.jointType = 4 /* DISTANCE */;
1743
+ plan.basis = null;
1744
+ plan.primaryAxis = Axis.LINEAR_DISTANCE;
1745
+ plan.limit = [Math.min(Math.max(0, lower), maxLength), Math.min(Math.max(0, upper), maxLength)];
1746
+ plan.spring = plan.stiffness > 0 && Math.abs(upper - lower) < 1e-6 && isFinite(lower);
1747
+ return plan;
1748
+ }
1749
+ this._warnOnce("sixdof-distance", "SIX_DOF: LINEAR_DISTANCE combined with other constrained axes is not supported with Box3D, the distance limit is ignored.");
1750
+ }
1751
+ let pointJoint = linearLocked === 3;
1752
+ if (!pointJoint && linearLocked !== 2 && linearFree === 0) {
1753
+ this._warnOnce("sixdof-linear-limited", "SIX_DOF: two or more limited linear axes are treated as locked with Box3D.");
1754
+ pointJoint = true;
1755
+ }
1756
+ if (pointJoint) {
1757
+ if (angularOpen.length === 0) {
1758
+ return plan;
1759
+ }
1760
+ if (angularOpen.length === 1) {
1761
+ const axis = angularOpen[0];
1762
+ plan.jointType = 2 /* REVOLUTE */;
1763
+ plan.basis = [
1764
+ [1, 2, 0],
1765
+ [2, 0, 1],
1766
+ [0, 1, 2]
1767
+ ][angular.indexOf(axis)];
1768
+ plan.primaryAxis = axis;
1769
+ if (mode(axis) === Mode.LIMITED) {
1770
+ const [lower, upper] = range(axis);
1771
+ plan.limit = [clamp(lower, maxAngle), clamp(upper, maxAngle)];
1772
+ }
1773
+ return plan;
1774
+ }
1775
+ plan.jointType = 1 /* SPHERICAL */;
1776
+ plan.basis = [1, 2, 0];
1777
+ if (mode(Axis.ANGULAR_X) !== Mode.FREE) {
1778
+ const [lower, upper] = range(Axis.ANGULAR_X);
1779
+ plan.twist = [clamp(lower, maxAngle), clamp(upper, maxAngle)];
1780
+ }
1781
+ const swingModes = [mode(Axis.ANGULAR_Y), mode(Axis.ANGULAR_Z)];
1782
+ if (swingModes.some((m) => m !== Mode.FREE)) {
1783
+ const extents = [];
1784
+ let asymmetric = false;
1785
+ for (const axis of [Axis.ANGULAR_Y, Axis.ANGULAR_Z]) {
1786
+ if (mode(axis) === Mode.FREE) {
1787
+ continue;
1788
+ }
1789
+ const [lower, upper] = range(axis);
1790
+ extents.push(Math.max(Math.abs(lower), Math.abs(upper)));
1791
+ asymmetric || (asymmetric = Math.abs(lower + upper) > 1e-4);
1792
+ }
1793
+ const cone = Math.max(...extents);
1794
+ if (isFinite(cone)) {
1795
+ if (swingModes.includes(Mode.FREE) || Math.abs(extents[0] - extents[1]) > 1e-4 || asymmetric) {
1796
+ this._warnOnce(
1797
+ "sixdof-cone",
1798
+ "SIX_DOF: Box3D has one symmetric swing cone, ANGULAR_Y and ANGULAR_Z use the larger of their ranges."
1799
+ );
1800
+ }
1801
+ if (cone > Math.PI / 2) {
1802
+ this._warnOnce("sixdof-cone-90", "SIX_DOF: Box3D cone limits are at most 90 degrees, larger swing ranges are clamped.");
1803
+ }
1804
+ plan.coneAngle = Math.min(cone, Math.PI / 2);
1805
+ }
1806
+ }
1807
+ return plan;
1808
+ }
1809
+ if (linearLocked === 2) {
1810
+ const slide = linear.find((a) => mode(a) !== Mode.LOCKED);
1811
+ plan.jointType = 3 /* PRISMATIC */;
1812
+ plan.basis = [
1813
+ [0, 1, 2],
1814
+ [1, 2, 0],
1815
+ [2, 0, 1]
1816
+ ][linear.indexOf(slide)];
1817
+ plan.primaryAxis = slide;
1818
+ if (mode(slide) === Mode.LIMITED) {
1819
+ const [lower, upper] = range(slide);
1820
+ plan.limit = [clamp(lower, maxLength), clamp(upper, maxLength)];
1821
+ }
1822
+ if (angularOpen.length) {
1823
+ this._warnOnce("sixdof-prismatic", "SIX_DOF: a sliding axis with rotation is not supported with Box3D, rotation is locked.");
1824
+ }
1825
+ return plan;
1826
+ }
1827
+ plan.jointType = 5 /* FILTER */;
1828
+ plan.basis = null;
1829
+ if (linearFree !== 3 || angularFree !== 3) {
1830
+ this._warnOnce("sixdof-unsupported", "SIX_DOF: this combination of free and limited axes is not supported with Box3D, the bodies are left unconstrained.");
1831
+ }
1832
+ return plan;
1833
+ }
1834
+ /** Box3D spring settings for a Havok spring constant and damping on a body pair. */
1835
+ _springToHertz(stiffness, damping, angular, bodyA, bodyB) {
1836
+ const inverse = (data) => {
1837
+ if (data.motionType !== import_IPhysicsEnginePlugin.PhysicsMotionType.DYNAMIC) {
1838
+ return 0;
1839
+ }
1840
+ this._b3._bx_Body_GetMassData(data.slot);
1841
+ const s = this._scratch();
1842
+ const value = angular ? (s[4] + s[5] + s[6]) / 3 : s[0];
1843
+ return value > 0 ? 1 / value : 0;
1844
+ };
1845
+ const inverseSum = inverse(bodyA) + inverse(bodyB);
1846
+ const mass = inverseSum > 0 ? 1 / inverseSum : 1;
1847
+ const k = Math.max(stiffness, 0);
1848
+ return {
1849
+ hertz: Math.sqrt(k / mass) / (2 * Math.PI),
1850
+ dampingRatio: k > 0 ? damping / (2 * Math.sqrt(k * mass)) : 1
1851
+ };
1852
+ }
1853
+ initConstraint(constraint, body, childBody, instanceIndex, childInstanceIndex) {
1854
+ const type = constraint.type;
1855
+ const options = constraint.options;
1856
+ if (!type || !options) {
1857
+ import_logger.Logger.Warn("No constraint type or options. Constraint is invalid.");
1858
+ return;
1859
+ }
1860
+ if (body._pluginDataInstances.length > 0 && instanceIndex === void 0 || childBody._pluginDataInstances.length > 0 && childInstanceIndex === void 0) {
1861
+ import_logger.Logger.Warn("Body is instanced but no instance index was specified. Constraint will not be applied.");
1862
+ return;
1863
+ }
1864
+ const firstPair = !constraint._pluginData;
1865
+ const cdata = constraint._pluginData ?? new Box3DConstraintData();
1866
+ constraint._pluginData = cdata;
1867
+ cdata.type = type;
1868
+ if (firstPair) {
1869
+ cdata.collisions = !!options.collision;
1870
+ }
1871
+ switch (type) {
1872
+ case import_IPhysicsEnginePlugin.PhysicsConstraintType.BALL_AND_SOCKET:
1873
+ cdata.jointType = 1 /* SPHERICAL */;
1874
+ break;
1875
+ case import_IPhysicsEnginePlugin.PhysicsConstraintType.HINGE:
1876
+ cdata.jointType = 2 /* REVOLUTE */;
1877
+ break;
1878
+ case import_IPhysicsEnginePlugin.PhysicsConstraintType.PRISMATIC:
1879
+ case import_IPhysicsEnginePlugin.PhysicsConstraintType.SLIDER:
1880
+ cdata.jointType = 3 /* PRISMATIC */;
1881
+ break;
1882
+ case import_IPhysicsEnginePlugin.PhysicsConstraintType.LOCK:
1883
+ cdata.jointType = 0 /* WELD */;
1884
+ break;
1885
+ case import_IPhysicsEnginePlugin.PhysicsConstraintType.DISTANCE:
1886
+ cdata.jointType = 4 /* DISTANCE */;
1887
+ break;
1888
+ case import_IPhysicsEnginePlugin.PhysicsConstraintType.SIX_DOF:
1889
+ if (firstPair) {
1890
+ this._readSixDofLimits(cdata, constraint.limits);
1891
+ }
1892
+ cdata.plan = this._planSixDof(cdata);
1893
+ cdata.jointType = cdata.plan.jointType;
1894
+ break;
1895
+ default:
1896
+ import_logger.Logger.Warn("Box3DPlugin: unknown constraint type " + type);
1897
+ return;
1898
+ }
1899
+ const bodyA = this._getPluginReference(body, instanceIndex);
1900
+ const bodyB = this._getPluginReference(childBody, childInstanceIndex);
1901
+ this._buildFrames(cdata, constraint, bodyA, bodyB);
1902
+ this._updateJointLength(cdata, options.maxDistance, bodyA, bodyB);
1903
+ const joint = cdata.enabled ? this._createJoint(cdata, bodyA, bodyB) : 0;
1904
+ cdata.joints.push(joint);
1905
+ cdata.pairs.push({ parent: body, parentIndex: instanceIndex ?? 0, child: childBody, childIndex: childInstanceIndex ?? 0, parentData: bodyA, childData: bodyB });
1906
+ }
1907
+ /** Length parameter passed to Box3D's distance joint at creation (the rest length of its spring). */
1908
+ _updateJointLength(cdata, maxDistance, bodyA, bodyB) {
1909
+ if (cdata.type === import_IPhysicsEnginePlugin.PhysicsConstraintType.DISTANCE) {
1910
+ cdata.length = maxDistance ?? 0;
1911
+ } else if (cdata.plan?.jointType === 4 /* DISTANCE */ && cdata.plan.limit) {
1912
+ cdata.length = cdata.plan.limit[0];
1913
+ } else {
1914
+ return;
1915
+ }
1916
+ if (cdata.length <= 0) {
1917
+ cdata.length = Math.max(0.01, this._currentPivotDistance(cdata, bodyA, bodyB));
1918
+ }
1919
+ }
1920
+ _currentPivotDistance(cdata, bodyA, bodyB) {
1921
+ const f = cdata.frames;
1922
+ const worldA = this._tmpVec3[0];
1923
+ const worldB = this._tmpVec3[1];
1924
+ this._b3._bx_Body_GetTransform(bodyA.slot);
1925
+ let s = this._scratch();
1926
+ this._tmpQuat[0].set(s[3], s[4], s[5], s[6]);
1927
+ worldA.set(f[0], f[1], f[2]).applyRotationQuaternionInPlace(this._tmpQuat[0]).addInPlaceFromFloats(s[0], s[1], s[2]);
1928
+ this._b3._bx_Body_GetTransform(bodyB.slot);
1929
+ s = this._scratch();
1930
+ this._tmpQuat[0].set(s[3], s[4], s[5], s[6]);
1931
+ worldB.set(f[7], f[8], f[9]).applyRotationQuaternionInPlace(this._tmpQuat[0]).addInPlaceFromFloats(s[0], s[1], s[2]);
1932
+ return import_math_vector.Vector3.Distance(worldA, worldB);
1933
+ }
1934
+ setEnabled(constraint, isEnabled) {
1935
+ const cdata = constraint._pluginData;
1936
+ if (!cdata || cdata.enabled === isEnabled) {
1937
+ return;
1938
+ }
1939
+ cdata.enabled = isEnabled;
1940
+ this._recreateJoints(cdata);
1941
+ }
1942
+ _destroyJoint(joint) {
1943
+ this._motorJoints.delete(joint);
1944
+ this._b3._bx_DestroyJoint(joint);
1945
+ }
1946
+ _recreateJoints(cdata) {
1947
+ for (let i = 0; i < cdata.pairs.length; i++) {
1948
+ if (cdata.joints[i]) {
1949
+ this._destroyJoint(cdata.joints[i]);
1950
+ }
1951
+ const pair = cdata.pairs[i];
1952
+ cdata.joints[i] = cdata.enabled && pair.parentData.slot && pair.childData.slot ? this._createJoint(cdata, pair.parentData, pair.childData) : 0;
1953
+ }
1954
+ }
1955
+ getEnabled(constraint) {
1956
+ return constraint._pluginData?.enabled ?? false;
1957
+ }
1958
+ setCollisionsEnabled(constraint, isEnabled) {
1959
+ const cdata = constraint._pluginData;
1960
+ cdata.collisions = isEnabled;
1961
+ for (const joint of cdata.joints) {
1962
+ if (joint) {
1963
+ this._b3._bx_Joint_SetCollideConnected(joint, isEnabled ? 1 : 0);
1964
+ }
1965
+ }
1966
+ }
1967
+ getCollisionsEnabled(constraint) {
1968
+ return constraint._pluginData?.collisions ?? false;
1969
+ }
1970
+ // The one axis box3d exposes for this constraint type.
1971
+ _primaryAxis(cdata) {
1972
+ switch (cdata.type) {
1973
+ case import_IPhysicsEnginePlugin.PhysicsConstraintType.HINGE:
1974
+ return import_IPhysicsEnginePlugin.PhysicsConstraintAxis.ANGULAR_X;
1975
+ case import_IPhysicsEnginePlugin.PhysicsConstraintType.PRISMATIC:
1976
+ case import_IPhysicsEnginePlugin.PhysicsConstraintType.SLIDER:
1977
+ return import_IPhysicsEnginePlugin.PhysicsConstraintAxis.LINEAR_X;
1978
+ case import_IPhysicsEnginePlugin.PhysicsConstraintType.DISTANCE:
1979
+ return import_IPhysicsEnginePlugin.PhysicsConstraintAxis.LINEAR_DISTANCE;
1980
+ case import_IPhysicsEnginePlugin.PhysicsConstraintType.BALL_AND_SOCKET:
1981
+ return import_IPhysicsEnginePlugin.PhysicsConstraintAxis.ANGULAR_Y;
1982
+ case import_IPhysicsEnginePlugin.PhysicsConstraintType.SIX_DOF:
1983
+ return cdata.plan?.primaryAxis ?? null;
1984
+ default:
1985
+ return null;
1986
+ }
1987
+ }
1988
+ _isAngularAxis(axis) {
1989
+ return axis === import_IPhysicsEnginePlugin.PhysicsConstraintAxis.ANGULAR_X || axis === import_IPhysicsEnginePlugin.PhysicsConstraintAxis.ANGULAR_Y || axis === import_IPhysicsEnginePlugin.PhysicsConstraintAxis.ANGULAR_Z;
1990
+ }
1991
+ _isPrimaryAxis(cdata, axis) {
1992
+ if (cdata.type === import_IPhysicsEnginePlugin.PhysicsConstraintType.BALL_AND_SOCKET || cdata.plan?.jointType === 1 /* SPHERICAL */) {
1993
+ return this._isAngularAxis(axis);
1994
+ }
1995
+ return axis === this._primaryAxis(cdata);
1996
+ }
1997
+ _axisState(cdata, axis) {
1998
+ let state = cdata.axes.get(axis);
1999
+ if (!state) {
2000
+ state = {
2001
+ mode: import_IPhysicsEnginePlugin.PhysicsConstraintAxisLimitMode.FREE,
2002
+ min: 0,
2003
+ max: 0,
2004
+ motor: import_IPhysicsEnginePlugin.PhysicsConstraintMotorType.NONE,
2005
+ target: 0,
2006
+ maxForce: 0,
2007
+ friction: 0,
2008
+ stiffness: 0,
2009
+ damping: 0
2010
+ };
2011
+ cdata.axes.set(axis, state);
2012
+ }
2013
+ return state;
2014
+ }
2015
+ _applyAxisState(cdata, joint, bodyA, bodyB) {
2016
+ const b3 = this._b3;
2017
+ if (cdata.plan) {
2018
+ this._applySixDofPlan(cdata, joint, bodyA, bodyB);
2019
+ b3._bx_Joint_WakeBodies(joint);
2020
+ return;
2021
+ }
2022
+ const primary = this._primaryAxis(cdata);
2023
+ if (primary === null) {
2024
+ return;
2025
+ }
2026
+ for (const [axis, state] of cdata.axes) {
2027
+ if (!this._isPrimaryAxis(cdata, axis)) {
2028
+ continue;
2029
+ }
2030
+ if (cdata.type === import_IPhysicsEnginePlugin.PhysicsConstraintType.BALL_AND_SOCKET) {
2031
+ if (axis === import_IPhysicsEnginePlugin.PhysicsConstraintAxis.ANGULAR_X) {
2032
+ b3._bx_Joint_SetTwistLimits(joint, state.mode === import_IPhysicsEnginePlugin.PhysicsConstraintAxisLimitMode.FREE ? 0 : 1, state.min, state.max);
2033
+ continue;
2034
+ }
2035
+ }
2036
+ const limited = state.mode !== import_IPhysicsEnginePlugin.PhysicsConstraintAxisLimitMode.FREE;
2037
+ if (limited) {
2038
+ const min = state.mode === import_IPhysicsEnginePlugin.PhysicsConstraintAxisLimitMode.LOCKED ? 0 : state.min;
2039
+ const max = state.mode === import_IPhysicsEnginePlugin.PhysicsConstraintAxisLimitMode.LOCKED ? 0 : state.max;
2040
+ b3._bx_Joint_SetLimits(joint, min, max);
2041
+ }
2042
+ b3._bx_Joint_EnableLimit(joint, limited ? 1 : 0);
2043
+ if (cdata.type !== import_IPhysicsEnginePlugin.PhysicsConstraintType.BALL_AND_SOCKET) {
2044
+ this._applyMotor(joint, state);
2045
+ }
2046
+ }
2047
+ if (cdata.type === import_IPhysicsEnginePlugin.PhysicsConstraintType.BALL_AND_SOCKET) {
2048
+ this._applySphericalMotor(cdata, joint);
2049
+ }
2050
+ b3._bx_Joint_WakeBodies(joint);
2051
+ }
2052
+ /**
2053
+ * Box3D's spherical joint has one motor for the whole joint: a relative angular velocity, and one spring towards a
2054
+ * target rotation. Babylon has a motor per axis, so the three angular axes are combined into one target in the
2055
+ * joint frame (ANGULAR_X about frame z, ANGULAR_Y about frame x, ANGULAR_Z about frame y).
2056
+ */
2057
+ _applySphericalMotor(cdata, joint) {
2058
+ const b3 = this._b3;
2059
+ const twist = cdata.axes.get(import_IPhysicsEnginePlugin.PhysicsConstraintAxis.ANGULAR_X);
2060
+ const swing1 = cdata.axes.get(import_IPhysicsEnginePlugin.PhysicsConstraintAxis.ANGULAR_Y);
2061
+ const swing2 = cdata.axes.get(import_IPhysicsEnginePlugin.PhysicsConstraintAxis.ANGULAR_Z);
2062
+ const states = [twist, swing1, swing2];
2063
+ const velocity = (state) => state?.motor === import_IPhysicsEnginePlugin.PhysicsConstraintMotorType.VELOCITY ? state.target : 0;
2064
+ const hasVelocity = states.some((state) => state?.motor === import_IPhysicsEnginePlugin.PhysicsConstraintMotorType.VELOCITY);
2065
+ const hasPosition = states.some((state) => state?.motor === import_IPhysicsEnginePlugin.PhysicsConstraintMotorType.POSITION);
2066
+ const maxTorque = Math.max(...states.map((state) => state?.motor === import_IPhysicsEnginePlugin.PhysicsConstraintMotorType.VELOCITY ? state.maxForce : 0));
2067
+ b3._bx_Joint_SetSphericalMotor(joint, hasVelocity ? 1 : 0, velocity(swing1), velocity(swing2), velocity(twist), maxTorque);
2068
+ const target = velocity(swing1) !== 0 || velocity(swing2) !== 0 || velocity(twist) !== 0;
2069
+ if (hasVelocity && target) {
2070
+ this._motorJoints.add(joint);
2071
+ } else {
2072
+ this._motorJoints.delete(joint);
2073
+ }
2074
+ b3._bx_Joint_EnableSpring(joint, hasPosition ? 1 : 0);
2075
+ if (hasPosition) {
2076
+ const position = (state) => state?.motor === import_IPhysicsEnginePlugin.PhysicsConstraintMotorType.POSITION ? state.target : 0;
2077
+ const rotation = this._tmpQuat[0];
2078
+ import_math_vector.Quaternion.RotationYawPitchRollToRef(0, 0, position(twist), rotation);
2079
+ const swing = this._tmpQuat[1];
2080
+ import_math_vector.Quaternion.RotationYawPitchRollToRef(position(swing2), position(swing1), 0, swing);
2081
+ rotation.multiplyInPlace(swing);
2082
+ b3._bx_Joint_SetSpring(joint, 5, 1);
2083
+ b3._bx_Joint_SetSphericalTarget(joint, rotation.x, rotation.y, rotation.z, rotation.w);
2084
+ }
2085
+ }
2086
+ _applyMotor(joint, state) {
2087
+ const b3 = this._b3;
2088
+ switch (state.motor) {
2089
+ case import_IPhysicsEnginePlugin.PhysicsConstraintMotorType.VELOCITY:
2090
+ b3._bx_Joint_EnableSpring(joint, 0);
2091
+ b3._bx_Joint_EnableMotor(joint, 1);
2092
+ b3._bx_Joint_SetMotorSpeed(joint, state.target);
2093
+ b3._bx_Joint_SetMaxMotorForce(joint, state.maxForce);
2094
+ break;
2095
+ case import_IPhysicsEnginePlugin.PhysicsConstraintMotorType.POSITION:
2096
+ b3._bx_Joint_EnableMotor(joint, 0);
2097
+ b3._bx_Joint_EnableSpring(joint, 1);
2098
+ b3._bx_Joint_SetSpring(joint, 5, 1);
2099
+ b3._bx_Joint_SetTarget(joint, state.target);
2100
+ break;
2101
+ default:
2102
+ b3._bx_Joint_EnableMotor(joint, 0);
2103
+ b3._bx_Joint_EnableSpring(joint, 0);
2104
+ break;
2105
+ }
2106
+ }
2107
+ /** Applies a SIX_DOF plan's limits, springs and motors to one of its Box3D joints. */
2108
+ _applySixDofPlan(cdata, joint, bodyA, bodyB) {
2109
+ const b3 = this._b3;
2110
+ const plan = cdata.plan;
2111
+ switch (plan.jointType) {
2112
+ case 1 /* SPHERICAL */:
2113
+ if (plan.coneAngle >= 0) {
2114
+ b3._bx_Joint_SetLimits(joint, -plan.coneAngle, plan.coneAngle);
2115
+ }
2116
+ b3._bx_Joint_EnableLimit(joint, plan.coneAngle >= 0 ? 1 : 0);
2117
+ b3._bx_Joint_SetTwistLimits(joint, plan.twist ? 1 : 0, plan.twist ? plan.twist[0] : 0, plan.twist ? plan.twist[1] : 0);
2118
+ break;
2119
+ case 2 /* REVOLUTE */:
2120
+ case 3 /* PRISMATIC */:
2121
+ if (plan.limit) {
2122
+ b3._bx_Joint_SetLimits(joint, plan.limit[0], plan.limit[1]);
2123
+ }
2124
+ b3._bx_Joint_EnableLimit(joint, plan.limit ? 1 : 0);
2125
+ break;
2126
+ case 4 /* DISTANCE */:
2127
+ if (plan.spring) {
2128
+ const spring = this._springToHertz(plan.stiffness, plan.damping, false, bodyA, bodyB);
2129
+ b3._bx_Joint_EnableLimit(joint, 0);
2130
+ b3._bx_Joint_EnableSpring(joint, 1);
2131
+ b3._bx_Joint_SetSpring(joint, spring.hertz, spring.dampingRatio);
2132
+ b3._bx_Joint_SetTarget(joint, cdata.length);
2133
+ } else {
2134
+ b3._bx_Joint_SetLimits(joint, plan.limit[0], plan.limit[1]);
2135
+ b3._bx_Joint_EnableLimit(joint, 1);
2136
+ b3._bx_Joint_EnableSpring(joint, 1);
2137
+ b3._bx_Joint_SetSpring(joint, 0, 0);
2138
+ }
2139
+ break;
2140
+ default:
2141
+ break;
2142
+ }
2143
+ if (plan.stiffness > 0 && !plan.spring) {
2144
+ this._warnOnce(
2145
+ "sixdof-softness",
2146
+ "SIX_DOF: limit stiffness and damping are approximated with Box3D's per joint constraint softness, which applies to every axis of the joint."
2147
+ );
2148
+ const soft = this._springToHertz(plan.stiffness, plan.damping, plan.angularStiffness, bodyA, bodyB);
2149
+ b3._bx_Joint_SetConstraintTuning(joint, soft.hertz, soft.dampingRatio);
2150
+ }
2151
+ if (plan.jointType === 1 /* SPHERICAL */) {
2152
+ this._applySphericalMotor(cdata, joint);
2153
+ } else if (plan.primaryAxis !== null && plan.jointType !== 4 /* DISTANCE */) {
2154
+ const state = cdata.axes.get(plan.primaryAxis);
2155
+ if (state) {
2156
+ this._applyMotor(joint, state);
2157
+ }
2158
+ }
2159
+ }
2160
+ _setAxis(constraint, axis, apply) {
2161
+ const cdata = constraint._pluginData;
2162
+ if (!cdata) {
2163
+ return;
2164
+ }
2165
+ const state = this._axisState(cdata, axis);
2166
+ const previousMotor = { motor: state.motor, target: state.target, maxForce: state.maxForce };
2167
+ apply(state);
2168
+ if (cdata.plan) {
2169
+ const plan = this._planSixDof(cdata);
2170
+ const rebuild = plan.jointType !== cdata.plan.jointType || String(plan.basis) !== String(cdata.plan.basis);
2171
+ cdata.plan = plan;
2172
+ cdata.jointType = plan.jointType;
2173
+ const motorChanged = previousMotor.motor !== state.motor || previousMotor.target !== state.target || previousMotor.maxForce !== state.maxForce;
2174
+ if (motorChanged && state.motor !== import_IPhysicsEnginePlugin.PhysicsConstraintMotorType.NONE && !this._isPrimaryAxis(cdata, axis)) {
2175
+ this._warnOnce(`axis-motor-${axis}`, `SIX_DOF: a motor on axis ${axis} has no effect with the Box3D joint this constraint maps to.`);
2176
+ }
2177
+ const first = cdata.pairs[0];
2178
+ if (first) {
2179
+ this._updateJointLength(cdata, constraint.options.maxDistance, first.parentData, first.childData);
2180
+ }
2181
+ if (rebuild) {
2182
+ if (first) {
2183
+ this._buildFrames(cdata, constraint, first.parentData, first.childData);
2184
+ }
2185
+ this._recreateJoints(cdata);
2186
+ return;
2187
+ }
2188
+ } else if (!this._isPrimaryAxis(cdata, axis)) {
2189
+ this._warnOnce(`axis-${cdata.type}-${axis}`, `axis ${axis} is not configurable on constraint type ${cdata.type} with Box3D; the value is stored but has no effect.`);
2190
+ return;
2191
+ }
2192
+ for (let i = 0; i < cdata.joints.length; i++) {
2193
+ if (cdata.joints[i]) {
2194
+ this._applyAxisState(cdata, cdata.joints[i], cdata.pairs[i].parentData, cdata.pairs[i].childData);
2195
+ }
2196
+ }
2197
+ }
2198
+ setAxisFriction(constraint, axis, friction) {
2199
+ const cdata = constraint._pluginData;
2200
+ if (cdata) {
2201
+ this._axisState(cdata, axis).friction = friction;
2202
+ }
2203
+ }
2204
+ getAxisFriction(constraint, axis) {
2205
+ return constraint._pluginData?.axes.get(axis)?.friction ?? null;
2206
+ }
2207
+ setAxisMode(constraint, axis, limitMode) {
2208
+ this._setAxis(constraint, axis, (state) => state.mode = limitMode);
2209
+ }
2210
+ getAxisMode(constraint, axis) {
2211
+ return constraint._pluginData?.axes.get(axis)?.mode ?? null;
2212
+ }
2213
+ setAxisMinLimit(constraint, axis, minLimit) {
2214
+ this._setAxis(constraint, axis, (state) => {
2215
+ state.min = minLimit;
2216
+ if (state.mode === import_IPhysicsEnginePlugin.PhysicsConstraintAxisLimitMode.FREE) {
2217
+ state.mode = import_IPhysicsEnginePlugin.PhysicsConstraintAxisLimitMode.LIMITED;
2218
+ }
2219
+ });
2220
+ }
2221
+ getAxisMinLimit(constraint, axis) {
2222
+ return constraint._pluginData?.axes.get(axis)?.min ?? null;
2223
+ }
2224
+ setAxisMaxLimit(constraint, axis, limit) {
2225
+ this._setAxis(constraint, axis, (state) => {
2226
+ state.max = limit;
2227
+ if (state.mode === import_IPhysicsEnginePlugin.PhysicsConstraintAxisLimitMode.FREE) {
2228
+ state.mode = import_IPhysicsEnginePlugin.PhysicsConstraintAxisLimitMode.LIMITED;
2229
+ }
2230
+ });
2231
+ }
2232
+ getAxisMaxLimit(constraint, axis) {
2233
+ return constraint._pluginData?.axes.get(axis)?.max ?? null;
2234
+ }
2235
+ setAxisMotorType(constraint, axis, motorType) {
2236
+ this._setAxis(constraint, axis, (state) => state.motor = motorType);
2237
+ }
2238
+ getAxisMotorType(constraint, axis) {
2239
+ return constraint._pluginData?.axes.get(axis)?.motor ?? null;
2240
+ }
2241
+ setAxisMotorTarget(constraint, axis, target) {
2242
+ this._setAxis(constraint, axis, (state) => state.target = target);
2243
+ }
2244
+ getAxisMotorTarget(constraint, axis) {
2245
+ return constraint._pluginData?.axes.get(axis)?.target ?? null;
2246
+ }
2247
+ setAxisMotorMaxForce(constraint, axis, maxForce) {
2248
+ this._setAxis(constraint, axis, (state) => state.maxForce = maxForce);
2249
+ }
2250
+ getAxisMotorMaxForce(constraint, axis) {
2251
+ return constraint._pluginData?.axes.get(axis)?.maxForce ?? null;
2252
+ }
2253
+ disposeConstraint(constraint) {
2254
+ const cdata = constraint._pluginData;
2255
+ if (!cdata) {
2256
+ return;
2257
+ }
2258
+ for (const joint of cdata.joints) {
2259
+ if (joint) {
2260
+ this._destroyJoint(joint);
2261
+ }
2262
+ }
2263
+ cdata.joints.length = 0;
2264
+ cdata.pairs.length = 0;
2265
+ }
2266
+ getBodiesUsingConstraint(constraint) {
2267
+ const cdata = constraint._pluginData;
2268
+ if (!cdata) {
2269
+ return [];
2270
+ }
2271
+ return cdata.pairs.map((p) => ({ parentBody: p.parent, parentBodyIndex: p.parentIndex, childBody: p.child, childBodyIndex: p.childIndex }));
2272
+ }
2273
+ addConstraint(body, childBody, constraint, instanceIndex, childInstanceIndex) {
2274
+ this.initConstraint(constraint, body, childBody, instanceIndex, childInstanceIndex);
2275
+ }
2276
+ // ----------------------------------------------------------------------------------------
2277
+ // Box3D specific extras
2278
+ // ----------------------------------------------------------------------------------------
2279
+ /**
2280
+ * Applies a radial impulse to every shape within the radius, like a grenade.
2281
+ * @param position world position of the explosion
2282
+ * @param radius radius of the explosion
2283
+ * @param impulsePerArea impulse per surface area of the affected shapes
2284
+ * @param falloff distance beyond the radius over which the impulse fades to zero
2285
+ */
2286
+ explode(position, radius, impulsePerArea, falloff = 0) {
2287
+ this._b3._bx_World_Explode(this._worldSlot, position.x, position.y, position.z, radius, falloff, impulsePerArea);
2288
+ }
2289
+ /**
2290
+ * Box2D style collision group. Shapes sharing a negative group never collide with each other,
2291
+ * shapes sharing a positive group always collide. Zero means no group.
2292
+ * @param shape the shape
2293
+ * @param groupIndex the group index
2294
+ */
2295
+ setShapeFilterGroup(shape, groupIndex) {
2296
+ const data = shape._pluginData;
2297
+ this._b3._bx_ShapeDesc_SetGroup(data.slot, groupIndex | 0);
2298
+ this._syncShapeUsers(data, 1 /* FILTER */);
2299
+ }
2300
+ /**
2301
+ * Rolling resistance for spheres and capsules (Babylon materials have no equivalent).
2302
+ * @param shape the shape
2303
+ * @param value resistance, typically in [0, 1]
2304
+ */
2305
+ setShapeRollingResistance(shape, value) {
2306
+ const data = shape._pluginData;
2307
+ this._b3._bx_ShapeDesc_SetRollingResistance(data.slot, value);
2308
+ this._syncShapeUsers(data, 2 /* MATERIAL */);
2309
+ }
2310
+ /**
2311
+ * Lets a body spin faster than the default angular speed cap (wheels).
2312
+ * @param body the body
2313
+ * @param allow true to allow fast rotation
2314
+ * @param instanceIndex optional thin instance index
2315
+ */
2316
+ setAllowFastRotation(body, allow, instanceIndex) {
2317
+ this._applyToBodyOrInstances(body, (data) => this._b3._bx_Body_AllowFastRotation(data.slot, allow ? 1 : 0), instanceIndex);
2318
+ }
2319
+ /**
2320
+ * Creates a Box3D wheel joint between a chassis and a wheel body.
2321
+ * @param chassis the chassis body
2322
+ * @param wheel the wheel body
2323
+ * @param options wheel joint options
2324
+ * @returns a handle used to drive and steer the wheel
2325
+ */
2326
+ createWheelJoint(chassis, wheel, options) {
2327
+ const s = this._scratch();
2328
+ const axisA = (options.axisA ?? import_math_vector.Vector3.UpReadOnly).normalizeToNew();
2329
+ const axleA = (options.axleA ?? import_math_vector.Vector3.Forward()).normalizeToNew();
2330
+ axleA.subtractInPlace(axisA.scale(import_math_vector.Vector3.Dot(axisA, axleA))).normalize();
2331
+ this._frameToBuffer(s, 0, options.pivotA, axisA, import_math_vector.Vector3.Cross(axleA, axisA), axleA);
2332
+ const pivotB = options.pivotB ?? import_math_vector.Vector3.ZeroReadOnly;
2333
+ s[7] = pivotB.x;
2334
+ s[8] = pivotB.y;
2335
+ s[9] = pivotB.z;
2336
+ const chassisData = this._getPluginReference(chassis);
2337
+ const wheelData = this._getPluginReference(wheel);
2338
+ this._b3._bx_ComputeAlignedFrameB(chassisData.slot, wheelData.slot);
2339
+ const susp = options.suspensionLimits ?? [-0.2, 0.2];
2340
+ const steer = options.steeringLimits ?? [-Math.PI / 4, Math.PI / 4];
2341
+ s.set(
2342
+ [
2343
+ 1,
2344
+ options.suspensionHertz ?? 4,
2345
+ options.suspensionDampingRatio ?? 0.7,
2346
+ 1,
2347
+ susp[0],
2348
+ susp[1],
2349
+ options.enableSpinMotor ? 1 : 0,
2350
+ options.maxSpinTorque ?? 5,
2351
+ 0,
2352
+ options.enableSteering ? 1 : 0,
2353
+ options.steeringHertz ?? 10,
2354
+ options.steeringDampingRatio ?? 0.7,
2355
+ 0,
2356
+ options.maxSteeringTorque ?? 5,
2357
+ 1,
2358
+ steer[0],
2359
+ steer[1]
2360
+ ],
2361
+ 14
2362
+ );
2363
+ const slot = this._b3._bx_CreateJoint(this._worldSlot, 6 /* WHEEL */, chassisData.slot, wheelData.slot, 0, 0);
2364
+ if (!slot) {
2365
+ throw new Error("Box3DPlugin: wheel joint creation failed");
2366
+ }
2367
+ return new Box3DWheelJoint(this, slot);
2368
+ }
2369
+ /**
2370
+ * Creates a Box3D parallel joint: a spring that keeps a local axis of bodyB parallel to an axis
2371
+ * of bodyA, for example to keep a vehicle upright without locking its motion.
2372
+ * @param bodyA first body (often a static body)
2373
+ * @param bodyB body to align
2374
+ * @param axisA axis in bodyA local space
2375
+ * @param axisB axis in bodyB local space
2376
+ * @param hertz spring stiffness
2377
+ * @param dampingRatio spring damping
2378
+ * @param maxTorque maximum spring torque
2379
+ * @returns a joint slot that can be passed to disposeExtraJoint
2380
+ */
2381
+ createParallelJoint(bodyA, bodyB, axisA, axisB, hertz = 1, dampingRatio = 1, maxTorque = 34e37) {
2382
+ const s = this._scratch();
2383
+ const a = axisA.normalizeToNew();
2384
+ const pa = a.getNormalToRef(new import_math_vector.Vector3());
2385
+ this._frameToBuffer(s, 0, import_math_vector.Vector3.ZeroReadOnly, pa, import_math_vector.Vector3.Cross(a, pa), a);
2386
+ const b = axisB.normalizeToNew();
2387
+ const pb = b.getNormalToRef(new import_math_vector.Vector3());
2388
+ this._frameToBuffer(s, 7, import_math_vector.Vector3.ZeroReadOnly, pb, import_math_vector.Vector3.Cross(b, pb), b);
2389
+ s.set([hertz, dampingRatio, maxTorque], 14);
2390
+ return this._b3._bx_CreateJoint(this._worldSlot, 7 /* PARALLEL */, this._getPluginReference(bodyA).slot, this._getPluginReference(bodyB).slot, 1, 0);
2391
+ }
2392
+ /**
2393
+ * Destroys a joint created by createParallelJoint.
2394
+ * @param slot the joint slot
2395
+ */
2396
+ disposeExtraJoint(slot) {
2397
+ this._destroyExtraJoint(slot);
2398
+ }
2399
+ /**
2400
+ * @internal
2401
+ */
2402
+ _destroyExtraJoint(slot) {
2403
+ if (slot) {
2404
+ this._destroyJoint(slot);
2405
+ }
2406
+ }
2407
+ /**
2408
+ * @internal
2409
+ */
2410
+ _wheelCall(fn, slot, ...args) {
2411
+ if (slot) {
2412
+ this._b3[fn](slot, ...args);
2413
+ }
2414
+ }
2415
+ // ----------------------------------------------------------------------------------------
2416
+ // queries
2417
+ // ----------------------------------------------------------------------------------------
2418
+ raycast(from, to, result, query) {
2419
+ const results = Array.isArray(result) ? result : [result];
2420
+ for (const r of results) {
2421
+ r.reset(from, to);
2422
+ }
2423
+ const membership = (query?.membership ?? ~0) >>> 0;
2424
+ const collideWith = (query?.collideWith ?? ~0) >>> 0;
2425
+ const ignore = query?.ignoreBody ? query.ignoreBody._pluginData?.slot ?? 0 : 0;
2426
+ const closestOnly = results.length === 1;
2427
+ const count = this._b3._bx_World_CastRay(
2428
+ this._worldSlot,
2429
+ from.x,
2430
+ from.y,
2431
+ from.z,
2432
+ to.x - from.x,
2433
+ to.y - from.y,
2434
+ to.z - from.z,
2435
+ membership,
2436
+ collideWith,
2437
+ ignore,
2438
+ query?.shouldHitTriggers ? 1 : 0,
2439
+ closestOnly ? 1 : 0
2440
+ );
2441
+ if (count <= 0) {
2442
+ return;
2443
+ }
2444
+ const buffer = new Float32Array(this._b3.HEAPF32.buffer, this._b3._bx_RayHitsPtr(), count * RAY_STRIDE);
2445
+ const hits = [];
2446
+ for (let i = 0; i < count; i++) {
2447
+ hits.push(i);
2448
+ }
2449
+ hits.sort((a, b) => buffer[a * RAY_STRIDE + 6] - buffer[b * RAY_STRIDE + 6]);
2450
+ if (!results.length) {
2451
+ for (let i = 0; i < count; i++) {
2452
+ const r = new import_physicsRaycastResult.PhysicsRaycastResult();
2453
+ r.reset(from, to);
2454
+ results.push(r);
2455
+ }
2456
+ }
2457
+ const n = Math.min(count, results.length);
2458
+ for (let i = 0; i < n; i++) {
2459
+ const o = hits[i] * RAY_STRIDE;
2460
+ const r = results[i];
2461
+ const bodyRef = this._bodies[buffer[o + 7]];
2462
+ r.body = bodyRef?.body;
2463
+ r.bodyIndex = bodyRef?.index;
2464
+ r.shape = bodyRef?.body.shape ?? void 0;
2465
+ r.setHitData({ x: buffer[o + 3], y: buffer[o + 4], z: buffer[o + 5] }, { x: buffer[o], y: buffer[o + 1], z: buffer[o + 2] }, buffer[o + 9]);
2466
+ r.calculateHitDistance();
2467
+ }
2468
+ }
2469
+ // ----------------------------------------------------------------------------------------
2470
+ // events
2471
+ // ----------------------------------------------------------------------------------------
2472
+ /**
2473
+ * Copies `count` event records out of wasm memory and resolves their two body slots (at `slotOffset`) to the plugin's
2474
+ * body references. Observers run user code that can grow the wasm memory, which detaches every HEAPF32 view (creating
2475
+ * bodies or shapes, casting rays), or dispose bodies and hand their slots to new ones, so nothing is read from wasm
2476
+ * memory or the slot table once the first observer has been called.
2477
+ */
2478
+ _readEvents(pointer, count, stride, slotOffset) {
2479
+ const records = new Float32Array(this._b3.HEAPF32.buffer, pointer, count * stride).slice();
2480
+ const refs = new Array(count * 2);
2481
+ for (let i = 0; i < count; i++) {
2482
+ refs[i * 2] = this._bodies[records[i * stride + slotOffset]];
2483
+ refs[i * 2 + 1] = this._bodies[records[i * stride + slotOffset + 1]];
2484
+ }
2485
+ return { records, refs };
2486
+ }
2487
+ _notifyCollisions() {
2488
+ const count = this._b3._bx_World_GetContactEvents(this._worldSlot);
2489
+ if (!count) {
2490
+ return;
2491
+ }
2492
+ const { records: buffer, refs } = this._readEvents(this._b3._bx_ContactEventsPtr(), count, CONTACT_STRIDE, 3);
2493
+ for (let i = 0; i < count; i++) {
2494
+ const o = i * CONTACT_STRIDE;
2495
+ const kind = buffer[o];
2496
+ const refA = refs[i * 2];
2497
+ const refB = refs[i * 2 + 1];
2498
+ if (!refA || !refB || !refA.data.slot || !refB.data.slot) {
2499
+ continue;
2500
+ }
2501
+ const bit = kind === 0 ? 1 /* COLLISION_STARTED */ : kind === 1 ? 4 /* COLLISION_FINISHED */ : 2 /* COLLISION_CONTINUED */;
2502
+ if (((refA.data.eventMask | refB.data.eventMask) & bit) === 0) {
2503
+ continue;
2504
+ }
2505
+ if (kind === 1) {
2506
+ const ended = {
2507
+ collider: refA.body,
2508
+ colliderIndex: refA.index,
2509
+ collidedAgainst: refB.body,
2510
+ collidedAgainstIndex: refB.index,
2511
+ type: import_IPhysicsEnginePlugin.PhysicsEventType.COLLISION_FINISHED
2512
+ };
2513
+ this.onCollisionEndedObservable.notifyObservers(ended);
2514
+ this._bodyCollisionEndedObservable.get(refA.data.slot)?.notifyObservers(ended);
2515
+ const endedB = this._bodyCollisionEndedObservable.get(refB.data.slot);
2516
+ if (endedB) {
2517
+ endedB.notifyObservers({ ...ended, collider: refB.body, colliderIndex: refB.index, collidedAgainst: refA.body, collidedAgainstIndex: refA.index });
2518
+ }
2519
+ continue;
2520
+ }
2521
+ const type = kind === 0 ? import_IPhysicsEnginePlugin.PhysicsEventType.COLLISION_STARTED : import_IPhysicsEnginePlugin.PhysicsEventType.COLLISION_CONTINUED;
2522
+ const point = new import_math_vector.Vector3(buffer[o + 5], buffer[o + 6], buffer[o + 7]);
2523
+ const normal = new import_math_vector.Vector3(buffer[o + 8], buffer[o + 9], buffer[o + 10]);
2524
+ const info = {
2525
+ collider: refA.body,
2526
+ colliderIndex: refA.index,
2527
+ collidedAgainst: refB.body,
2528
+ collidedAgainstIndex: refB.index,
2529
+ type,
2530
+ point,
2531
+ normal,
2532
+ distance: 0,
2533
+ impulse: buffer[o + 11]
2534
+ };
2535
+ this.onCollisionObservable.notifyObservers(info);
2536
+ if (this._bodyCollisionObservable.size) {
2537
+ this._bodyCollisionObservable.get(refA.data.slot)?.notifyObservers(info);
2538
+ const obsB = this._bodyCollisionObservable.get(refB.data.slot);
2539
+ if (obsB) {
2540
+ obsB.notifyObservers({
2541
+ ...info,
2542
+ collider: refB.body,
2543
+ colliderIndex: refB.index,
2544
+ collidedAgainst: refA.body,
2545
+ collidedAgainstIndex: refA.index,
2546
+ normal: normal ? normal.negate() : null
2547
+ });
2548
+ }
2549
+ }
2550
+ }
2551
+ }
2552
+ _notifyTriggers() {
2553
+ const count = this._b3._bx_World_GetSensorEvents(this._worldSlot);
2554
+ if (!count || !this.onTriggerCollisionObservable.hasObservers()) {
2555
+ return;
2556
+ }
2557
+ const { records: buffer, refs } = this._readEvents(this._b3._bx_SensorEventsPtr(), count, SENSOR_STRIDE, 3);
2558
+ for (let i = 0; i < count; i++) {
2559
+ const o = i * SENSOR_STRIDE;
2560
+ const refA = refs[i * 2];
2561
+ const refB = refs[i * 2 + 1];
2562
+ if (!refA || !refB || !refA.data.slot || !refB.data.slot) {
2563
+ continue;
2564
+ }
2565
+ this.onTriggerCollisionObservable.notifyObservers({
2566
+ collider: refA.body,
2567
+ colliderIndex: refA.index,
2568
+ collidedAgainst: refB.body,
2569
+ collidedAgainstIndex: refB.index,
2570
+ type: buffer[o] === 0 ? import_IPhysicsEnginePlugin.PhysicsEventType.TRIGGER_ENTERED : import_IPhysicsEnginePlugin.PhysicsEventType.TRIGGER_EXITED
2571
+ });
2572
+ }
2573
+ }
2574
+ dispose() {
2575
+ this._motorJoints.clear();
2576
+ this.onCollisionObservable.clear();
2577
+ this.onCollisionEndedObservable.clear();
2578
+ this.onTriggerCollisionObservable.clear();
2579
+ this._bodyCollisionObservable.clear();
2580
+ this._bodyCollisionEndedObservable.clear();
2581
+ for (const shape of this._shapes) {
2582
+ if (shape) {
2583
+ this._b3._bx_ShapeDesc_Destroy(shape.slot);
2584
+ }
2585
+ }
2586
+ this._shapes.length = 0;
2587
+ this._bodies.length = 0;
2588
+ if (this._worldSlot) {
2589
+ this._b3._bx_DestroyWorld(this._worldSlot);
2590
+ this._worldSlot = 0;
2591
+ }
2592
+ }
2593
+ };
2594
+
2595
+ // box3d-global:babylon-box3d/wasm
2596
+ if (typeof Box3D === "undefined") {
2597
+ throw new Error("babylon.box3d.js needs lib/umd/box3d.umd.js loaded first");
2598
+ }
2599
+ var wasm_default = Box3D;
2600
+ return __toCommonJS(index_exports);
2601
+ })();
2602
+ //# sourceMappingURL=babylon.box3d.js.map