@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/LICENSE +21 -0
- package/README.md +95 -0
- package/dist/index.d.mts +782 -0
- package/dist/index.d.ts +782 -0
- package/dist/index.js +1243 -0
- package/dist/index.mjs +1229 -0
- package/package.json +72 -0
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
|
+
});
|