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