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