@frsource/babylon-box3d 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,1243 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ Box3DPlugin: () => Box3DPlugin
24
+ });
25
+ module.exports = __toCommonJS(index_exports);
26
+
27
+ // box3dPlugin.ts
28
+ var import_core = require("@babylonjs/core");
29
+ var DEFAULT_SUB_STEP_COUNT = 4;
30
+ function toB3Vec(v) {
31
+ return { x: v.x, y: v.y, z: v.z };
32
+ }
33
+ function fromB3Vec(v, out) {
34
+ if (out) {
35
+ out.set(v.x, v.y, v.z);
36
+ return out;
37
+ }
38
+ return new import_core.Vector3(v.x, v.y, v.z);
39
+ }
40
+ function toB3Quat(q) {
41
+ return { x: q.x, y: q.y, z: q.z, w: q.w };
42
+ }
43
+ function fromB3Quat(q, out) {
44
+ if (out) {
45
+ out.set(q.x, q.y, q.z, q.w);
46
+ return out;
47
+ }
48
+ return new import_core.Quaternion(q.x, q.y, q.z, q.w);
49
+ }
50
+ function quaternionFromXAxis(axis) {
51
+ const from = import_core.Vector3.Right();
52
+ const to = axis.normalizeToNew();
53
+ const dot = import_core.Vector3.Dot(from, to);
54
+ if (dot > 1 - 1e-6) {
55
+ return import_core.Quaternion.Identity();
56
+ }
57
+ if (dot < -1 + 1e-6) {
58
+ const perp = Math.abs(from.y) < 0.99 ? import_core.Vector3.Up() : import_core.Vector3.Forward();
59
+ const axisOfRotation2 = import_core.Vector3.Cross(from, perp).normalize();
60
+ return import_core.Quaternion.RotationAxis(axisOfRotation2, Math.PI);
61
+ }
62
+ const axisOfRotation = import_core.Vector3.Cross(from, to);
63
+ const s = Math.sqrt((1 + dot) * 2);
64
+ const invS = 1 / s;
65
+ return new import_core.Quaternion(
66
+ axisOfRotation.x * invS,
67
+ axisOfRotation.y * invS,
68
+ axisOfRotation.z * invS,
69
+ s * 0.5
70
+ ).normalize();
71
+ }
72
+ var Box3DBodyPluginData = class {
73
+ constructor(native) {
74
+ this.native = native;
75
+ this.shape = null;
76
+ this.attachments = [];
77
+ this.motionType = import_core.PhysicsMotionType.STATIC;
78
+ this.eventMask = 0;
79
+ this.collisionCBEnabled = false;
80
+ this.collisionEndedCBEnabled = false;
81
+ this.onCollisionObservable = new import_core.Observable();
82
+ this.onCollisionEndedObservable = new import_core.Observable();
83
+ }
84
+ };
85
+ var Box3DShapeDescriptor = class {
86
+ constructor(type, parameters) {
87
+ this.type = type;
88
+ this.parameters = parameters;
89
+ this.filterMembershipMask = 1;
90
+ this.filterCollideMask = 4294967295;
91
+ this.material = { friction: 0.5, restitution: 0 };
92
+ this.density = 1;
93
+ this.isTrigger = false;
94
+ this.children = [];
95
+ }
96
+ };
97
+ var Box3DConstraintPluginData = class {
98
+ constructor(type, bodyPair, buildJoint) {
99
+ this.type = type;
100
+ this.bodyPair = bodyPair;
101
+ this.buildJoint = buildJoint;
102
+ this.native = null;
103
+ this.enabled = true;
104
+ this.collideConnected = false;
105
+ this.axisMotorType = /* @__PURE__ */ new Map();
106
+ this.axisMotorTarget = /* @__PURE__ */ new Map();
107
+ this.axisMotorMaxForce = /* @__PURE__ */ new Map();
108
+ this.axisLimitMode = /* @__PURE__ */ new Map();
109
+ this.axisMinLimit = /* @__PURE__ */ new Map();
110
+ this.axisMaxLimit = /* @__PURE__ */ new Map();
111
+ this.axisFriction = /* @__PURE__ */ new Map();
112
+ }
113
+ rebuild() {
114
+ if (this.native && this.native.isValid()) {
115
+ this.native.destroy(true);
116
+ }
117
+ this.native = this.enabled ? this.buildJoint(this) : null;
118
+ }
119
+ };
120
+ var Box3DPlugin = class {
121
+ /**
122
+ * @param box3d - The resolved box3d-wasm module, e.g. `await Box3D()`.
123
+ * @param worldOptions - Forwarded to box3d-wasm's `World` constructor (gravity, sleep, continuous collision, thread count, ...).
124
+ */
125
+ constructor(box3d, worldOptions) {
126
+ this.name = "Box3D";
127
+ this.onCollisionObservable = new import_core.Observable();
128
+ this.onCollisionEndedObservable = new import_core.Observable();
129
+ this.onTriggerCollisionObservable = new import_core.Observable();
130
+ this._timeStep = 1 / 60;
131
+ this._subStepCount = DEFAULT_SUB_STEP_COUNT;
132
+ this._maxLinearVelocity = Infinity;
133
+ this._maxAngularVelocity = Infinity;
134
+ this._bodies = /* @__PURE__ */ new Map();
135
+ this._shapeOwners = /* @__PURE__ */ new Map();
136
+ this._warnedAboutInstancing = false;
137
+ this.world = new box3d.World(worldOptions);
138
+ }
139
+ setGravity(gravity) {
140
+ this.world.setGravity(toB3Vec(gravity));
141
+ }
142
+ setTimeStep(timeStep) {
143
+ this._timeStep = timeStep;
144
+ }
145
+ getTimeStep() {
146
+ return this._timeStep;
147
+ }
148
+ /** Number of box3d solver sub-steps per `executeStep` call. Defaults to 4, matching box3d's own samples. */
149
+ setSubStepCount(subStepCount) {
150
+ this._subStepCount = subStepCount;
151
+ }
152
+ getPluginVersion() {
153
+ return 2;
154
+ }
155
+ setVelocityLimits(maxLinearVelocity, maxAngularVelocity) {
156
+ this._maxLinearVelocity = maxLinearVelocity;
157
+ this._maxAngularVelocity = maxAngularVelocity;
158
+ }
159
+ getMaxLinearVelocity() {
160
+ return this._maxLinearVelocity;
161
+ }
162
+ getMaxAngularVelocity() {
163
+ return this._maxAngularVelocity;
164
+ }
165
+ executeStep(delta, bodies) {
166
+ for (const body of bodies) {
167
+ if (!body.disablePreStep) {
168
+ this._prestep(body);
169
+ }
170
+ }
171
+ this.world.step(delta, this._subStepCount);
172
+ if (Number.isFinite(this._maxLinearVelocity)) {
173
+ this._clampLinearVelocities(bodies);
174
+ }
175
+ for (const body of bodies) {
176
+ if (!body.disableSync) {
177
+ this.sync(body);
178
+ }
179
+ }
180
+ this._dispatchEvents();
181
+ }
182
+ _clampLinearVelocities(bodies) {
183
+ const max = this._maxLinearVelocity;
184
+ for (const body of bodies) {
185
+ const data = this._getPluginData(body);
186
+ const v = data.native.getLinearVelocity();
187
+ const speed = Math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z);
188
+ if (speed > max && speed > 0) {
189
+ const scale = max / speed;
190
+ data.native.setLinearVelocity({
191
+ x: v.x * scale,
192
+ y: v.y * scale,
193
+ z: v.z * scale
194
+ });
195
+ }
196
+ }
197
+ }
198
+ /** Pushes kinematic/animated transform-node motion into box3d before stepping. Dynamic bodies are driven by the solver and skip this. */
199
+ _prestep(body) {
200
+ const data = body._pluginData;
201
+ if (!data || data.motionType === import_core.PhysicsMotionType.DYNAMIC) {
202
+ return;
203
+ }
204
+ const node = body.transformNode;
205
+ const position = node.position;
206
+ const rotation = node.rotationQuaternion ?? import_core.Quaternion.FromEulerVector(node.rotation);
207
+ data.native.setTargetTransform(
208
+ { position: toB3Vec(position), rotation: toB3Quat(rotation) },
209
+ this._timeStep,
210
+ true
211
+ );
212
+ }
213
+ // -------------------------------------------------------------------
214
+ // Body
215
+ // -------------------------------------------------------------------
216
+ initBody(body, motionType, position, orientation) {
217
+ const native = this.world.createBody({
218
+ type: toBox3DBodyType(motionType),
219
+ position: toB3Vec(position),
220
+ rotation: toB3Quat(orientation),
221
+ isAwake: !body.startAsleep
222
+ });
223
+ const data = new Box3DBodyPluginData(native);
224
+ data.motionType = motionType;
225
+ body._pluginData = data;
226
+ this._bodies.set(body, data);
227
+ }
228
+ initBodyInstances(body, motionType, mesh) {
229
+ if (!this._warnedAboutInstancing) {
230
+ this._warnedAboutInstancing = true;
231
+ console.warn(
232
+ "Box3DPlugin: box3d-wasm has no batched rigid body instancing; only the master mesh transform is simulated."
233
+ );
234
+ }
235
+ const position = mesh.position.clone();
236
+ const rotation = mesh.rotationQuaternion?.clone() ?? import_core.Quaternion.FromEulerVector(mesh.rotation);
237
+ this.initBody(body, motionType, position, rotation);
238
+ }
239
+ updateBodyInstances(_body, _mesh) {
240
+ }
241
+ removeBody(body) {
242
+ const data = this._bodies.get(body);
243
+ if (!data) {
244
+ return;
245
+ }
246
+ this._destroyAttachments(data);
247
+ if (data.native.isValid()) {
248
+ data.native.destroy();
249
+ }
250
+ this._bodies.delete(body);
251
+ }
252
+ sync(body) {
253
+ this.syncTransform(body, body.transformNode);
254
+ }
255
+ syncTransform(body, transformNode) {
256
+ const data = body._pluginData;
257
+ if (!data || data.motionType !== import_core.PhysicsMotionType.DYNAMIC) {
258
+ return;
259
+ }
260
+ fromB3Vec(data.native.getPosition(), transformNode.position);
261
+ transformNode.rotationQuaternion = fromB3Quat(
262
+ data.native.getRotation(),
263
+ transformNode.rotationQuaternion ?? new import_core.Quaternion()
264
+ );
265
+ }
266
+ setShape(body, shape) {
267
+ const data = this._getPluginData(body);
268
+ this._destroyAttachments(data);
269
+ data.shape = shape;
270
+ if (shape) {
271
+ this._attachShape(
272
+ body,
273
+ data,
274
+ shape,
275
+ import_core.Vector3.Zero(),
276
+ import_core.Quaternion.Identity(),
277
+ import_core.Vector3.One()
278
+ );
279
+ data.native.applyMassFromShapes();
280
+ }
281
+ }
282
+ getShape(body) {
283
+ return this._getPluginData(body).shape;
284
+ }
285
+ getShapeType(shape) {
286
+ return this._getShapeData(shape).type;
287
+ }
288
+ setEventMask(body, eventMask) {
289
+ this._getPluginData(body).eventMask = eventMask;
290
+ }
291
+ getEventMask(body) {
292
+ return this._getPluginData(body).eventMask;
293
+ }
294
+ setMotionType(body, motionType) {
295
+ const data = this._getPluginData(body);
296
+ data.motionType = motionType;
297
+ data.native.setType(toBox3DBodyType(motionType));
298
+ }
299
+ getMotionType(body) {
300
+ return this._getPluginData(body).motionType;
301
+ }
302
+ computeMassProperties(body) {
303
+ const data = this._getPluginData(body);
304
+ data.native.applyMassFromShapes();
305
+ return {
306
+ mass: data.native.getMass(),
307
+ centerOfMass: fromB3Vec(data.native.getLocalCenterOfMass())
308
+ };
309
+ }
310
+ setMassProperties(body, massProps) {
311
+ const data = this._getPluginData(body);
312
+ if (massProps.inertia || massProps.inertiaOrientation || massProps.centerOfMass) {
313
+ console.warn(
314
+ "Box3DPlugin: box3d-wasm derives inertia and center of mass from shape density; only `mass` can be overridden."
315
+ );
316
+ }
317
+ if (massProps.mass === void 0) {
318
+ return;
319
+ }
320
+ data.native.applyMassFromShapes();
321
+ const currentMass = data.native.getMass();
322
+ if (currentMass <= 0 || data.attachments.length === 0) {
323
+ return;
324
+ }
325
+ const scale = massProps.mass / currentMass;
326
+ for (const attachment of data.attachments) {
327
+ attachment.native.setDensity(
328
+ attachment.native.getDensity() * scale,
329
+ false
330
+ );
331
+ }
332
+ data.native.applyMassFromShapes();
333
+ }
334
+ getMassProperties(body) {
335
+ const data = this._getPluginData(body);
336
+ return {
337
+ mass: data.native.getMass(),
338
+ centerOfMass: fromB3Vec(data.native.getLocalCenterOfMass())
339
+ };
340
+ }
341
+ setLinearDamping(body, damping) {
342
+ this._getPluginData(body).native.setLinearDamping(damping);
343
+ }
344
+ getLinearDamping(body) {
345
+ return this._getPluginData(body).native.getLinearDamping();
346
+ }
347
+ setAngularDamping(body, damping) {
348
+ this._getPluginData(body).native.setAngularDamping(damping);
349
+ }
350
+ getAngularDamping(body) {
351
+ return this._getPluginData(body).native.getAngularDamping();
352
+ }
353
+ setLinearVelocity(body, linVel) {
354
+ this._getPluginData(body).native.setLinearVelocity(toB3Vec(linVel));
355
+ }
356
+ getLinearVelocityToRef(body, linVel) {
357
+ fromB3Vec(this._getPluginData(body).native.getLinearVelocity(), linVel);
358
+ }
359
+ applyImpulse(body, impulse, location) {
360
+ this._getPluginData(body).native.applyLinearImpulse(
361
+ toB3Vec(impulse),
362
+ toB3Vec(location),
363
+ true
364
+ );
365
+ }
366
+ applyAngularImpulse(body, angularImpulse) {
367
+ this._getPluginData(body).native.applyAngularImpulse(
368
+ toB3Vec(angularImpulse),
369
+ true
370
+ );
371
+ }
372
+ applyForce(body, force, location) {
373
+ this._getPluginData(body).native.applyForce(
374
+ toB3Vec(force),
375
+ toB3Vec(location),
376
+ true
377
+ );
378
+ }
379
+ applyTorque(body, torque) {
380
+ this._getPluginData(body).native.applyTorque(toB3Vec(torque), true);
381
+ }
382
+ setAngularVelocity(body, angVel) {
383
+ this._getPluginData(body).native.setAngularVelocity(toB3Vec(angVel));
384
+ }
385
+ getAngularVelocityToRef(body, angVel) {
386
+ fromB3Vec(this._getPluginData(body).native.getAngularVelocity(), angVel);
387
+ }
388
+ getBodyGeometry(_body) {
389
+ return {};
390
+ }
391
+ disposeBody(body) {
392
+ const data = this._bodies.get(body);
393
+ if (!data) {
394
+ return;
395
+ }
396
+ data.onCollisionObservable.clear();
397
+ data.onCollisionEndedObservable.clear();
398
+ this.removeBody(body);
399
+ body._pluginData = null;
400
+ }
401
+ setCollisionCallbackEnabled(body, enabled) {
402
+ const data = this._getPluginData(body);
403
+ data.collisionCBEnabled = enabled;
404
+ }
405
+ setCollisionEndedCallbackEnabled(body, enabled) {
406
+ const data = this._getPluginData(body);
407
+ data.collisionEndedCBEnabled = enabled;
408
+ }
409
+ addConstraint(body, childBody, constraint) {
410
+ this.initConstraint(constraint, body, childBody);
411
+ }
412
+ getCollisionObservable(body) {
413
+ return this._getPluginData(body).onCollisionObservable;
414
+ }
415
+ getCollisionEndedObservable(body) {
416
+ return this._getPluginData(body).onCollisionEndedObservable;
417
+ }
418
+ setGravityFactor(body, factor) {
419
+ this._getPluginData(body).native.setGravityScale(factor);
420
+ }
421
+ getGravityFactor(body) {
422
+ return this._getPluginData(body).native.getGravityScale();
423
+ }
424
+ setTargetTransform(body, position, rotation) {
425
+ this._getPluginData(body).native.setTargetTransform(
426
+ { position: toB3Vec(position), rotation: toB3Quat(rotation) },
427
+ this._timeStep,
428
+ true
429
+ );
430
+ }
431
+ _getPluginData(body) {
432
+ const data = body._pluginData;
433
+ if (!data) {
434
+ throw new Error(
435
+ "Box3DPlugin: PhysicsBody has not been initialized by this plugin."
436
+ );
437
+ }
438
+ return data;
439
+ }
440
+ _destroyAttachments(data) {
441
+ for (const attachment of data.attachments) {
442
+ this._shapeOwners.delete(attachment.native.getUserData());
443
+ if (attachment.native.isValid()) {
444
+ attachment.native.destroy(false);
445
+ }
446
+ }
447
+ data.attachments = [];
448
+ }
449
+ // -------------------------------------------------------------------
450
+ // Shape
451
+ // -------------------------------------------------------------------
452
+ initShape(shape, type, options) {
453
+ if (type === import_core.PhysicsShapeType.MESH || type === import_core.PhysicsShapeType.HEIGHTFIELD || type === import_core.PhysicsShapeType.CYLINDER) {
454
+ throw new Error(
455
+ `Box3DPlugin: shape type ${import_core.PhysicsShapeType[type]} is not supported by box3d-wasm.`
456
+ );
457
+ }
458
+ shape._pluginData = new Box3DShapeDescriptor(type, options);
459
+ }
460
+ setShapeFilterMembershipMask(shape, membershipMask) {
461
+ this._getShapeData(shape).filterMembershipMask = membershipMask;
462
+ this._refreshShapeFilters(shape);
463
+ }
464
+ getShapeFilterMembershipMask(shape) {
465
+ return this._getShapeData(shape).filterMembershipMask;
466
+ }
467
+ setShapeFilterCollideMask(shape, collideMask) {
468
+ this._getShapeData(shape).filterCollideMask = collideMask;
469
+ this._refreshShapeFilters(shape);
470
+ }
471
+ getShapeFilterCollideMask(shape) {
472
+ return this._getShapeData(shape).filterCollideMask;
473
+ }
474
+ setMaterial(shape, material) {
475
+ const data = this._getShapeData(shape);
476
+ data.material = material;
477
+ for (const native of this._nativeShapesOf(shape)) {
478
+ native.setFriction(material.friction ?? 0.5);
479
+ native.setRestitution(material.restitution ?? 0);
480
+ }
481
+ }
482
+ getMaterial(shape) {
483
+ return this._getShapeData(shape).material;
484
+ }
485
+ setDensity(shape, density) {
486
+ const data = this._getShapeData(shape);
487
+ data.density = density;
488
+ for (const native of this._nativeShapesOf(shape)) {
489
+ native.setDensity(density, true);
490
+ }
491
+ }
492
+ getDensity(shape) {
493
+ return this._getShapeData(shape).density;
494
+ }
495
+ addChild(shape, newChild, translation, rotation, scale) {
496
+ const data = this._getShapeData(shape);
497
+ data.children.push({
498
+ shape: newChild,
499
+ translation: translation?.clone() ?? import_core.Vector3.Zero(),
500
+ rotation: rotation?.clone() ?? import_core.Quaternion.Identity(),
501
+ scale: scale?.clone() ?? import_core.Vector3.One()
502
+ });
503
+ this._reattachOwners(shape);
504
+ }
505
+ removeChild(shape, childIndex) {
506
+ this._getShapeData(shape).children.splice(childIndex, 1);
507
+ this._reattachOwners(shape);
508
+ }
509
+ getNumChildren(shape) {
510
+ return this._getShapeData(shape).children.length;
511
+ }
512
+ getBoundingBox(shape) {
513
+ const { min, max } = computeLocalBounds(this._getShapeData(shape));
514
+ return new import_core.BoundingBox(min, max);
515
+ }
516
+ getBodyBoundingBox(body) {
517
+ const aabb = this._getPluginData(body).native.computeAABB();
518
+ return new import_core.BoundingBox(
519
+ fromB3Vec(aabb.lowerBound),
520
+ fromB3Vec(aabb.upperBound)
521
+ );
522
+ }
523
+ disposeShape(shape) {
524
+ for (const [owner] of this._ownersOfShape(shape)) {
525
+ const attachmentIndex = owner.attachments.findIndex(
526
+ (a) => a.shape === shape
527
+ );
528
+ if (attachmentIndex < 0) {
529
+ continue;
530
+ }
531
+ const attachment = owner.attachments.splice(attachmentIndex, 1)[0];
532
+ if (!attachment) {
533
+ continue;
534
+ }
535
+ this._shapeOwners.delete(attachment.native.getUserData());
536
+ if (attachment.native.isValid()) {
537
+ attachment.native.destroy(true);
538
+ }
539
+ }
540
+ shape._pluginData = null;
541
+ }
542
+ setTrigger(shape, isTrigger) {
543
+ const data = this._getShapeData(shape);
544
+ data.isTrigger = isTrigger;
545
+ this._reattachOwners(shape);
546
+ }
547
+ _getShapeData(shape) {
548
+ const data = shape._pluginData;
549
+ if (!data) {
550
+ throw new Error(
551
+ "Box3DPlugin: PhysicsShape has not been initialized by this plugin."
552
+ );
553
+ }
554
+ return data;
555
+ }
556
+ _nativeShapesOf(shape) {
557
+ const result = [];
558
+ for (const data of this._bodies.values()) {
559
+ for (const attachment of data.attachments) {
560
+ if (attachment.shape === shape) {
561
+ result.push(attachment.native);
562
+ }
563
+ }
564
+ }
565
+ return result;
566
+ }
567
+ *_ownersOfShape(shape) {
568
+ for (const [body, data] of this._bodies.entries()) {
569
+ if (data.attachments.some((a) => a.shape === shape)) {
570
+ yield [data, body];
571
+ }
572
+ }
573
+ }
574
+ /** Re-creates every body's attachment tree that (transitively) references `shape`, e.g. after `addChild`/`removeChild`/`setTrigger`. */
575
+ _reattachOwners(shape) {
576
+ for (const [body, data] of this._bodies.entries()) {
577
+ if (data.shape && shapeTreeContains(data.shape, shape)) {
578
+ this._destroyAttachments(data);
579
+ this._attachShape(
580
+ body,
581
+ data,
582
+ data.shape,
583
+ import_core.Vector3.Zero(),
584
+ import_core.Quaternion.Identity(),
585
+ import_core.Vector3.One()
586
+ );
587
+ data.native.applyMassFromShapes();
588
+ }
589
+ }
590
+ }
591
+ _refreshShapeFilters(shape) {
592
+ const data = this._getShapeData(shape);
593
+ for (const native of this._nativeShapesOf(shape)) {
594
+ native.setFilter({
595
+ categoryBits: data.filterMembershipMask,
596
+ maskBits: data.filterCollideMask
597
+ });
598
+ }
599
+ }
600
+ /** Recursively creates native box3d shapes for `shape` (and, for CONTAINER shapes, its children) on `bodyData.native`, composing local transforms along the way. */
601
+ _attachShape(owner, bodyData, shape, parentT, parentR, parentS) {
602
+ const data = this._getShapeData(shape);
603
+ if (data.type !== import_core.PhysicsShapeType.CONTAINER) {
604
+ const native = createNativeShape(
605
+ bodyData.native,
606
+ data,
607
+ parentT,
608
+ parentR,
609
+ parentS
610
+ );
611
+ native.setUserData(nextShapeTag());
612
+ native.enableContactEvents(true);
613
+ native.enableHitEvents(true);
614
+ native.enableSensorEvents(true);
615
+ native.setFilter({
616
+ categoryBits: data.filterMembershipMask,
617
+ maskBits: data.filterCollideMask
618
+ });
619
+ bodyData.attachments.push({ shape, native });
620
+ this._shapeOwners.set(native.getUserData(), { body: owner, shape });
621
+ }
622
+ for (const child of data.children) {
623
+ const t = parentT.add(
624
+ import_core.Vector3.TransformCoordinates(
625
+ child.translation.multiply(parentS),
626
+ Matrix_fromQuaternion(parentR)
627
+ )
628
+ );
629
+ const r = parentR.multiply(child.rotation);
630
+ const s = parentS.multiply(child.scale);
631
+ this._attachShape(owner, bodyData, child.shape, t, r, s);
632
+ }
633
+ }
634
+ // -------------------------------------------------------------------
635
+ // Constraint
636
+ // -------------------------------------------------------------------
637
+ initConstraint(constraint, body, childBody) {
638
+ const bodyDataA = this._getPluginData(body);
639
+ const bodyDataB = this._getPluginData(childBody);
640
+ const options = constraint.options;
641
+ const bodyPair = {
642
+ parentBody: body,
643
+ parentBodyIndex: 0,
644
+ childBody,
645
+ childBodyIndex: 0
646
+ };
647
+ const buildJoint = createJointFactory(
648
+ this.world,
649
+ bodyDataA.native,
650
+ bodyDataB.native,
651
+ constraint.type,
652
+ options
653
+ );
654
+ const pluginData = new Box3DConstraintPluginData(
655
+ constraint.type,
656
+ bodyPair,
657
+ buildJoint
658
+ );
659
+ pluginData.collideConnected = options.collision ?? false;
660
+ constraint._pluginData = pluginData;
661
+ pluginData.rebuild();
662
+ }
663
+ setEnabled(constraint, isEnabled) {
664
+ const data = this._getConstraintData(constraint);
665
+ data.enabled = isEnabled;
666
+ data.rebuild();
667
+ }
668
+ getEnabled(constraint) {
669
+ return this._getConstraintData(constraint).enabled;
670
+ }
671
+ setCollisionsEnabled(constraint, isEnabled) {
672
+ const data = this._getConstraintData(constraint);
673
+ data.collideConnected = isEnabled;
674
+ data.native?.setCollideConnected(isEnabled);
675
+ }
676
+ getCollisionsEnabled(constraint) {
677
+ return this._getConstraintData(constraint).collideConnected;
678
+ }
679
+ setAxisFriction(constraint, axis, friction) {
680
+ const data = this._getConstraintData(constraint);
681
+ data.axisFriction.set(axis, friction);
682
+ data.rebuild();
683
+ }
684
+ getAxisFriction(constraint, axis) {
685
+ return this._getConstraintData(constraint).axisFriction.get(axis) ?? null;
686
+ }
687
+ setAxisMode(constraint, axis, limitMode) {
688
+ const data = this._getConstraintData(constraint);
689
+ data.axisLimitMode.set(axis, limitMode);
690
+ data.rebuild();
691
+ }
692
+ getAxisMode(constraint, axis) {
693
+ return this._getConstraintData(constraint).axisLimitMode.get(axis) ?? null;
694
+ }
695
+ setAxisMinLimit(constraint, axis, minLimit) {
696
+ const data = this._getConstraintData(constraint);
697
+ data.axisMinLimit.set(axis, minLimit);
698
+ data.rebuild();
699
+ }
700
+ getAxisMinLimit(constraint, axis) {
701
+ return this._getConstraintData(constraint).axisMinLimit.get(axis) ?? null;
702
+ }
703
+ setAxisMaxLimit(constraint, axis, limit) {
704
+ const data = this._getConstraintData(constraint);
705
+ data.axisMaxLimit.set(axis, limit);
706
+ data.rebuild();
707
+ }
708
+ getAxisMaxLimit(constraint, axis) {
709
+ return this._getConstraintData(constraint).axisMaxLimit.get(axis) ?? null;
710
+ }
711
+ setAxisMotorType(constraint, axis, motorType) {
712
+ const data = this._getConstraintData(constraint);
713
+ data.axisMotorType.set(axis, motorType);
714
+ data.rebuild();
715
+ }
716
+ getAxisMotorType(constraint, axis) {
717
+ return this._getConstraintData(constraint).axisMotorType.get(axis) ?? null;
718
+ }
719
+ setAxisMotorTarget(constraint, axis, target) {
720
+ const data = this._getConstraintData(constraint);
721
+ data.axisMotorTarget.set(axis, target);
722
+ data.rebuild();
723
+ }
724
+ getAxisMotorTarget(constraint, axis) {
725
+ return this._getConstraintData(constraint).axisMotorTarget.get(axis) ?? null;
726
+ }
727
+ setAxisMotorMaxForce(constraint, axis, maxForce) {
728
+ const data = this._getConstraintData(constraint);
729
+ data.axisMotorMaxForce.set(axis, maxForce);
730
+ data.rebuild();
731
+ }
732
+ getAxisMotorMaxForce(constraint, axis) {
733
+ return this._getConstraintData(constraint).axisMotorMaxForce.get(axis) ?? null;
734
+ }
735
+ disposeConstraint(constraint) {
736
+ const data = this._getConstraintData(constraint);
737
+ if (data.native?.isValid()) {
738
+ data.native.destroy(true);
739
+ }
740
+ constraint._pluginData = null;
741
+ }
742
+ getBodiesUsingConstraint(constraint) {
743
+ return [this._getConstraintData(constraint).bodyPair];
744
+ }
745
+ _getConstraintData(constraint) {
746
+ const data = constraint._pluginData;
747
+ if (!data) {
748
+ throw new Error(
749
+ "Box3DPlugin: PhysicsConstraint has not been initialized by this plugin."
750
+ );
751
+ }
752
+ return data;
753
+ }
754
+ // -------------------------------------------------------------------
755
+ // Raycast
756
+ // -------------------------------------------------------------------
757
+ raycast(from, to, result, query) {
758
+ const target = Array.isArray(result) ? result[0] : result;
759
+ if (!target) {
760
+ return;
761
+ }
762
+ target.reset(from, to);
763
+ const translation = to.subtract(from);
764
+ const hit = this.world.castRayClosest(toB3Vec(from), toB3Vec(translation), {
765
+ categoryBits: query?.membership,
766
+ maskBits: query?.collideWith
767
+ });
768
+ if (!hit.hit || hit.shapeUserData === void 0) {
769
+ return;
770
+ }
771
+ const owner = this._shapeOwners.get(hit.shapeUserData);
772
+ if (query?.ignoreBody && owner?.body === query.ignoreBody) {
773
+ return;
774
+ }
775
+ target.body = owner?.body;
776
+ target.shape = owner?.shape;
777
+ if (hit.normal && hit.point) {
778
+ target.setHitData(hit.normal, hit.point);
779
+ }
780
+ if (hit.fraction !== void 0) {
781
+ target.setHitDistance(hit.fraction * translation.length());
782
+ }
783
+ }
784
+ dispose() {
785
+ for (const body of [...this._bodies.keys()]) {
786
+ this.disposeBody(body);
787
+ }
788
+ this._shapeOwners.clear();
789
+ this.onCollisionObservable.clear();
790
+ this.onCollisionEndedObservable.clear();
791
+ this.onTriggerCollisionObservable.clear();
792
+ if (this.world.isValid()) {
793
+ this.world.destroy();
794
+ }
795
+ }
796
+ // -------------------------------------------------------------------
797
+ // Event dispatch
798
+ // -------------------------------------------------------------------
799
+ _dispatchEvents() {
800
+ const contacts = this.world.getContactEvents();
801
+ for (const begin of contacts.begin) {
802
+ this._notifyCollision(
803
+ begin.shapeUserDataA,
804
+ begin.shapeUserDataB,
805
+ import_core.PhysicsEventType.COLLISION_STARTED,
806
+ null,
807
+ null,
808
+ 0
809
+ );
810
+ }
811
+ for (const end of contacts.end) {
812
+ if (end.shapeUserDataA === null || end.shapeUserDataB === null) {
813
+ continue;
814
+ }
815
+ this._notifyCollisionEnded(end.shapeUserDataA, end.shapeUserDataB);
816
+ }
817
+ for (const hit of contacts.hit) {
818
+ this._notifyCollision(
819
+ hit.shapeUserDataA,
820
+ hit.shapeUserDataB,
821
+ import_core.PhysicsEventType.COLLISION_CONTINUED,
822
+ fromB3Vec(hit.point),
823
+ fromB3Vec(hit.normal),
824
+ hit.approachSpeed
825
+ );
826
+ }
827
+ const sensors = this.world.getSensorEvents();
828
+ for (const begin of sensors.begin) {
829
+ this._notifyTrigger(
830
+ begin.sensorUserData,
831
+ begin.visitorUserData,
832
+ import_core.PhysicsEventType.TRIGGER_ENTERED
833
+ );
834
+ }
835
+ for (const end of sensors.end) {
836
+ if (end.sensorUserData === null || end.visitorUserData === null) {
837
+ continue;
838
+ }
839
+ this._notifyTrigger(
840
+ end.sensorUserData,
841
+ end.visitorUserData,
842
+ import_core.PhysicsEventType.TRIGGER_EXITED
843
+ );
844
+ }
845
+ }
846
+ _notifyCollision(tagA, tagB, type, point, normal, impulse) {
847
+ const ownerA = this._shapeOwners.get(tagA);
848
+ const ownerB = this._shapeOwners.get(tagB);
849
+ if (!ownerA || !ownerB) {
850
+ return;
851
+ }
852
+ const dataA = this._bodies.get(ownerA.body);
853
+ const dataB = this._bodies.get(ownerB.body);
854
+ const event = {
855
+ collider: ownerA.body,
856
+ collidedAgainst: ownerB.body,
857
+ colliderIndex: 0,
858
+ collidedAgainstIndex: 0,
859
+ type,
860
+ point,
861
+ normal,
862
+ distance: 0,
863
+ impulse
864
+ };
865
+ if (dataA?.collisionCBEnabled) {
866
+ dataA.onCollisionObservable.notifyObservers(event);
867
+ }
868
+ if (dataB?.collisionCBEnabled) {
869
+ dataB.onCollisionObservable.notifyObservers({
870
+ ...event,
871
+ collider: ownerB.body,
872
+ collidedAgainst: ownerA.body
873
+ });
874
+ }
875
+ this.onCollisionObservable.notifyObservers(event);
876
+ }
877
+ _notifyCollisionEnded(tagA, tagB) {
878
+ const ownerA = this._shapeOwners.get(tagA);
879
+ const ownerB = this._shapeOwners.get(tagB);
880
+ if (!ownerA || !ownerB) {
881
+ return;
882
+ }
883
+ const dataA = this._bodies.get(ownerA.body);
884
+ const dataB = this._bodies.get(ownerB.body);
885
+ const event = {
886
+ collider: ownerA.body,
887
+ collidedAgainst: ownerB.body,
888
+ colliderIndex: 0,
889
+ collidedAgainstIndex: 0,
890
+ type: import_core.PhysicsEventType.COLLISION_FINISHED
891
+ };
892
+ if (dataA?.collisionEndedCBEnabled) {
893
+ dataA.onCollisionEndedObservable.notifyObservers(event);
894
+ }
895
+ if (dataB?.collisionEndedCBEnabled) {
896
+ dataB.onCollisionEndedObservable.notifyObservers({
897
+ ...event,
898
+ collider: ownerB.body,
899
+ collidedAgainst: ownerA.body
900
+ });
901
+ }
902
+ this.onCollisionEndedObservable.notifyObservers(event);
903
+ }
904
+ _notifyTrigger(sensorTag, visitorTag, type) {
905
+ const sensorOwner = this._shapeOwners.get(sensorTag);
906
+ const visitorOwner = this._shapeOwners.get(visitorTag);
907
+ if (!sensorOwner || !visitorOwner) {
908
+ return;
909
+ }
910
+ this.onTriggerCollisionObservable.notifyObservers({
911
+ collider: sensorOwner.body,
912
+ collidedAgainst: visitorOwner.body,
913
+ colliderIndex: 0,
914
+ collidedAgainstIndex: 0,
915
+ type
916
+ });
917
+ }
918
+ };
919
+ function toBox3DBodyType(motionType) {
920
+ switch (motionType) {
921
+ case import_core.PhysicsMotionType.DYNAMIC:
922
+ return "dynamic";
923
+ case import_core.PhysicsMotionType.ANIMATED:
924
+ return "kinematic";
925
+ case import_core.PhysicsMotionType.STATIC:
926
+ default:
927
+ return "static";
928
+ }
929
+ }
930
+ var _nextShapeTag = 1;
931
+ function nextShapeTag() {
932
+ return _nextShapeTag++;
933
+ }
934
+ function Matrix_fromQuaternion(q) {
935
+ return q.toRotationMatrix(new import_core.Matrix());
936
+ }
937
+ function shapeTreeContains(root, target) {
938
+ if (root === target) {
939
+ return true;
940
+ }
941
+ const data = root._pluginData;
942
+ if (!data) {
943
+ return false;
944
+ }
945
+ return data.children.some(
946
+ (child) => shapeTreeContains(child.shape, target)
947
+ );
948
+ }
949
+ function computeLocalBounds(descriptor) {
950
+ const p = descriptor.parameters;
951
+ switch (descriptor.type) {
952
+ case import_core.PhysicsShapeType.BOX: {
953
+ const extents = p.extents ?? new import_core.Vector3(1, 1, 1);
954
+ const center = p.center ?? import_core.Vector3.Zero();
955
+ const half = extents.scale(0.5);
956
+ return { min: center.subtract(half), max: center.add(half) };
957
+ }
958
+ case import_core.PhysicsShapeType.SPHERE: {
959
+ const radius = p.radius ?? 0.5;
960
+ const center = p.center ?? import_core.Vector3.Zero();
961
+ const r = new import_core.Vector3(radius, radius, radius);
962
+ return { min: center.subtract(r), max: center.add(r) };
963
+ }
964
+ case import_core.PhysicsShapeType.CAPSULE: {
965
+ const radius = p.radius ?? 0.5;
966
+ const a = p.pointA ?? new import_core.Vector3(0, -0.5, 0);
967
+ const b = p.pointB ?? new import_core.Vector3(0, 0.5, 0);
968
+ const min = import_core.Vector3.Minimize(a, b).subtractFromFloats(
969
+ radius,
970
+ radius,
971
+ radius
972
+ );
973
+ const max = import_core.Vector3.Maximize(a, b).addInPlaceFromFloats(
974
+ radius,
975
+ radius,
976
+ radius
977
+ );
978
+ return { min, max };
979
+ }
980
+ case import_core.PhysicsShapeType.CONVEX_HULL: {
981
+ const points = extractHullPoints(p);
982
+ let min = points[0]?.clone() ?? import_core.Vector3.Zero();
983
+ let max = points[0]?.clone() ?? import_core.Vector3.Zero();
984
+ for (const point of points) {
985
+ min = import_core.Vector3.Minimize(min, point);
986
+ max = import_core.Vector3.Maximize(max, point);
987
+ }
988
+ return { min, max };
989
+ }
990
+ case import_core.PhysicsShapeType.CONTAINER: {
991
+ let min = new import_core.Vector3(Infinity, Infinity, Infinity);
992
+ let max = new import_core.Vector3(-Infinity, -Infinity, -Infinity);
993
+ for (const child of descriptor.children) {
994
+ const childData = child.shape._pluginData;
995
+ if (!childData) {
996
+ continue;
997
+ }
998
+ const childBounds = computeLocalBounds(childData);
999
+ const worldMin = import_core.Vector3.TransformCoordinates(
1000
+ childBounds.min.multiply(child.scale),
1001
+ Matrix_fromQuaternion(child.rotation)
1002
+ ).add(child.translation);
1003
+ const worldMax = import_core.Vector3.TransformCoordinates(
1004
+ childBounds.max.multiply(child.scale),
1005
+ Matrix_fromQuaternion(child.rotation)
1006
+ ).add(child.translation);
1007
+ min = import_core.Vector3.Minimize(min, import_core.Vector3.Minimize(worldMin, worldMax));
1008
+ max = import_core.Vector3.Maximize(max, import_core.Vector3.Maximize(worldMin, worldMax));
1009
+ }
1010
+ return { min, max };
1011
+ }
1012
+ default:
1013
+ return { min: import_core.Vector3.Zero(), max: import_core.Vector3.Zero() };
1014
+ }
1015
+ }
1016
+ function extractHullPoints(parameters) {
1017
+ if (!parameters.mesh) {
1018
+ return [];
1019
+ }
1020
+ const positions = parameters.mesh.getVerticesData(import_core.VertexBuffer.PositionKind);
1021
+ if (!positions) {
1022
+ return [];
1023
+ }
1024
+ const points = [];
1025
+ for (let i = 0; i < positions.length; i += 3) {
1026
+ points.push(
1027
+ new import_core.Vector3(
1028
+ positions[i] ?? 0,
1029
+ positions[i + 1] ?? 0,
1030
+ positions[i + 2] ?? 0
1031
+ )
1032
+ );
1033
+ }
1034
+ return points;
1035
+ }
1036
+ function createNativeShape(nativeBody, descriptor, t, r, s) {
1037
+ const p = descriptor.parameters;
1038
+ const shared = {
1039
+ density: descriptor.density,
1040
+ friction: descriptor.material.friction ?? 0.5,
1041
+ restitution: descriptor.material.restitution ?? 0,
1042
+ isSensor: descriptor.isTrigger,
1043
+ filter: {
1044
+ categoryBits: descriptor.filterMembershipMask,
1045
+ maskBits: descriptor.filterCollideMask
1046
+ }
1047
+ };
1048
+ if (Math.abs(s.x - s.y) > 1e-4 || Math.abs(s.y - s.z) > 1e-4) {
1049
+ console.warn(
1050
+ "Box3DPlugin: non-uniform shape scaling is not supported; using the average scale factor."
1051
+ );
1052
+ }
1053
+ const uniformScale = (s.x + s.y + s.z) / 3;
1054
+ switch (descriptor.type) {
1055
+ case import_core.PhysicsShapeType.BOX: {
1056
+ const extents = (p.extents ?? new import_core.Vector3(1, 1, 1)).scale(uniformScale);
1057
+ const center = (p.center ?? import_core.Vector3.Zero()).multiply(s);
1058
+ const localT = t.add(
1059
+ import_core.Vector3.TransformCoordinates(center, Matrix_fromQuaternion(r))
1060
+ );
1061
+ const localR = r.multiply(p.rotation ?? import_core.Quaternion.Identity());
1062
+ return nativeBody.createBox({
1063
+ ...shared,
1064
+ halfExtents: toB3Vec(extents.scale(0.5)),
1065
+ offset: toB3Vec(localT),
1066
+ rotation: toB3Quat(localR)
1067
+ });
1068
+ }
1069
+ case import_core.PhysicsShapeType.SPHERE: {
1070
+ const center = (p.center ?? import_core.Vector3.Zero()).multiply(s);
1071
+ const localT = t.add(
1072
+ import_core.Vector3.TransformCoordinates(center, Matrix_fromQuaternion(r))
1073
+ );
1074
+ return nativeBody.createSphere({
1075
+ ...shared,
1076
+ radius: (p.radius ?? 0.5) * uniformScale,
1077
+ center: toB3Vec(localT)
1078
+ });
1079
+ }
1080
+ case import_core.PhysicsShapeType.CAPSULE: {
1081
+ const a = (p.pointA ?? new import_core.Vector3(0, -0.5, 0)).multiply(s);
1082
+ const b = (p.pointB ?? new import_core.Vector3(0, 0.5, 0)).multiply(s);
1083
+ const worldA = t.add(
1084
+ import_core.Vector3.TransformCoordinates(a, Matrix_fromQuaternion(r))
1085
+ );
1086
+ const worldB = t.add(
1087
+ import_core.Vector3.TransformCoordinates(b, Matrix_fromQuaternion(r))
1088
+ );
1089
+ return nativeBody.createCapsule({
1090
+ ...shared,
1091
+ radius: (p.radius ?? 0.5) * uniformScale,
1092
+ center1: toB3Vec(worldA),
1093
+ center2: toB3Vec(worldB)
1094
+ });
1095
+ }
1096
+ case import_core.PhysicsShapeType.CONVEX_HULL: {
1097
+ const localPoints = extractHullPoints(p);
1098
+ const worldPoints = localPoints.map(
1099
+ (point) => t.add(
1100
+ import_core.Vector3.TransformCoordinates(
1101
+ point.multiply(s),
1102
+ Matrix_fromQuaternion(r)
1103
+ )
1104
+ )
1105
+ );
1106
+ return nativeBody.createHull({
1107
+ ...shared,
1108
+ points: worldPoints.map(toB3Vec)
1109
+ });
1110
+ }
1111
+ default:
1112
+ throw new Error(
1113
+ `Box3DPlugin: shape type ${import_core.PhysicsShapeType[descriptor.type]} is not supported by box3d-wasm.`
1114
+ );
1115
+ }
1116
+ }
1117
+ function createJointFactory(world, bodyA, bodyB, type, options) {
1118
+ const anchorA = options.pivotA ? toB3Vec(options.pivotA) : void 0;
1119
+ const anchorB = options.pivotB ? toB3Vec(options.pivotB) : void 0;
1120
+ return (data) => {
1121
+ const base = { anchorA, anchorB, collideConnected: data.collideConnected };
1122
+ switch (type) {
1123
+ case import_core.PhysicsConstraintType.BALL_AND_SOCKET: {
1124
+ const twist = readLimit(data, import_core.PhysicsConstraintAxis.ANGULAR_X);
1125
+ const motorSpeed = data.axisMotorTarget.get(
1126
+ import_core.PhysicsConstraintAxis.ANGULAR_X
1127
+ );
1128
+ return world.createSphericalJoint(bodyA, bodyB, {
1129
+ ...base,
1130
+ enableTwistLimit: twist !== null,
1131
+ lowerTwistAngle: twist?.min,
1132
+ upperTwistAngle: twist?.max,
1133
+ enableMotor: motorSpeed !== void 0,
1134
+ motorVelocity: motorSpeed !== void 0 ? { x: motorSpeed, y: 0, z: 0 } : void 0,
1135
+ maxMotorTorque: data.axisMotorMaxForce.get(
1136
+ import_core.PhysicsConstraintAxis.ANGULAR_X
1137
+ )
1138
+ });
1139
+ }
1140
+ case import_core.PhysicsConstraintType.DISTANCE: {
1141
+ const limit = readLimit(data, import_core.PhysicsConstraintAxis.LINEAR_DISTANCE);
1142
+ const maxDistance = options.maxDistance ?? limit?.max ?? 0;
1143
+ return world.createDistanceJoint(bodyA, bodyB, {
1144
+ ...base,
1145
+ length: maxDistance,
1146
+ enableLimit: true,
1147
+ minLength: limit?.min ?? 0,
1148
+ maxLength: maxDistance,
1149
+ enableMotor: data.axisMotorType.get(import_core.PhysicsConstraintAxis.LINEAR_DISTANCE) !== void 0,
1150
+ motorSpeed: data.axisMotorTarget.get(
1151
+ import_core.PhysicsConstraintAxis.LINEAR_DISTANCE
1152
+ ),
1153
+ maxMotorForce: data.axisMotorMaxForce.get(
1154
+ import_core.PhysicsConstraintAxis.LINEAR_DISTANCE
1155
+ )
1156
+ });
1157
+ }
1158
+ case import_core.PhysicsConstraintType.HINGE: {
1159
+ const localFrameA = {
1160
+ rotation: toB3Quat(
1161
+ quaternionFromXAxis(options.axisA ?? import_core.Vector3.Right())
1162
+ )
1163
+ };
1164
+ const localFrameB = {
1165
+ rotation: toB3Quat(
1166
+ quaternionFromXAxis(options.axisB ?? import_core.Vector3.Right())
1167
+ )
1168
+ };
1169
+ const limit = readLimit(data, import_core.PhysicsConstraintAxis.ANGULAR_X);
1170
+ const motorType = data.axisMotorType.get(
1171
+ import_core.PhysicsConstraintAxis.ANGULAR_X
1172
+ );
1173
+ const friction = data.axisFriction.get(import_core.PhysicsConstraintAxis.ANGULAR_X);
1174
+ const useFrictionAsMotor = !motorType && friction !== void 0;
1175
+ return world.createRevoluteJoint(bodyA, bodyB, {
1176
+ ...base,
1177
+ localFrameA,
1178
+ localFrameB,
1179
+ enableLimit: limit !== null,
1180
+ lowerAngle: limit?.min,
1181
+ upperAngle: limit?.max,
1182
+ enableMotor: motorType !== void 0 || useFrictionAsMotor,
1183
+ motorSpeed: useFrictionAsMotor ? 0 : data.axisMotorTarget.get(import_core.PhysicsConstraintAxis.ANGULAR_X),
1184
+ maxMotorTorque: useFrictionAsMotor ? friction : data.axisMotorMaxForce.get(import_core.PhysicsConstraintAxis.ANGULAR_X)
1185
+ });
1186
+ }
1187
+ case import_core.PhysicsConstraintType.SLIDER:
1188
+ case import_core.PhysicsConstraintType.PRISMATIC: {
1189
+ const localFrameA = {
1190
+ rotation: toB3Quat(
1191
+ quaternionFromXAxis(options.axisA ?? import_core.Vector3.Right())
1192
+ )
1193
+ };
1194
+ const localFrameB = {
1195
+ rotation: toB3Quat(
1196
+ quaternionFromXAxis(options.axisB ?? import_core.Vector3.Right())
1197
+ )
1198
+ };
1199
+ const limit = readLimit(data, import_core.PhysicsConstraintAxis.LINEAR_X);
1200
+ return world.createPrismaticJoint(bodyA, bodyB, {
1201
+ ...base,
1202
+ localFrameA,
1203
+ localFrameB,
1204
+ enableLimit: limit !== null,
1205
+ lowerTranslation: limit?.min,
1206
+ upperTranslation: limit?.max,
1207
+ enableMotor: data.axisMotorType.get(import_core.PhysicsConstraintAxis.LINEAR_X) !== void 0,
1208
+ motorSpeed: data.axisMotorTarget.get(import_core.PhysicsConstraintAxis.LINEAR_X),
1209
+ maxMotorForce: data.axisMotorMaxForce.get(
1210
+ import_core.PhysicsConstraintAxis.LINEAR_X
1211
+ )
1212
+ });
1213
+ }
1214
+ case import_core.PhysicsConstraintType.LOCK:
1215
+ case import_core.PhysicsConstraintType.SIX_DOF: {
1216
+ if (type === import_core.PhysicsConstraintType.SIX_DOF) {
1217
+ console.warn(
1218
+ "Box3DPlugin: SIX_DOF constraints are approximated with a weld joint; per-axis limits/motors are tracked but not enforced."
1219
+ );
1220
+ }
1221
+ return world.createWeldJoint(bodyA, bodyB, base);
1222
+ }
1223
+ default:
1224
+ throw new Error(
1225
+ `Box3DPlugin: constraint type ${import_core.PhysicsConstraintType[type]} is not supported.`
1226
+ );
1227
+ }
1228
+ };
1229
+ }
1230
+ function readLimit(data, axis) {
1231
+ const mode = data.axisLimitMode.get(axis);
1232
+ if (mode === void 0 || mode === import_core.PhysicsConstraintAxisLimitMode.FREE) {
1233
+ return null;
1234
+ }
1235
+ if (mode === import_core.PhysicsConstraintAxisLimitMode.LOCKED) {
1236
+ return { min: 0, max: 0 };
1237
+ }
1238
+ return { min: data.axisMinLimit.get(axis), max: data.axisMaxLimit.get(axis) };
1239
+ }
1240
+ // Annotate the CommonJS export names for ESM import in node:
1241
+ 0 && (module.exports = {
1242
+ Box3DPlugin
1243
+ });