@zylem/game-lib 0.6.3 → 0.6.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/actions.d.ts +5 -5
- package/dist/actions.js +196 -32
- package/dist/actions.js.map +1 -1
- package/dist/behavior/jumper-2d.d.ts +114 -0
- package/dist/behavior/jumper-2d.js +711 -0
- package/dist/behavior/jumper-2d.js.map +1 -0
- package/dist/behavior/platformer-3d.d.ts +14 -14
- package/dist/behavior/platformer-3d.js +347 -104
- package/dist/behavior/platformer-3d.js.map +1 -1
- package/dist/behavior/ricochet-2d.d.ts +4 -3
- package/dist/behavior/ricochet-2d.js +53 -22
- package/dist/behavior/ricochet-2d.js.map +1 -1
- package/dist/behavior/ricochet-3d.d.ts +117 -0
- package/dist/behavior/ricochet-3d.js +443 -0
- package/dist/behavior/ricochet-3d.js.map +1 -0
- package/dist/behavior/screen-visibility.d.ts +79 -0
- package/dist/behavior/screen-visibility.js +358 -0
- package/dist/behavior/screen-visibility.js.map +1 -0
- package/dist/behavior/screen-wrap.d.ts +4 -3
- package/dist/behavior/screen-wrap.js +100 -49
- package/dist/behavior/screen-wrap.js.map +1 -1
- package/dist/behavior/shooter-2d.d.ts +79 -0
- package/dist/behavior/shooter-2d.js +180 -0
- package/dist/behavior/shooter-2d.js.map +1 -0
- package/dist/behavior/thruster.d.ts +5 -4
- package/dist/behavior/thruster.js +133 -75
- package/dist/behavior/thruster.js.map +1 -1
- package/dist/behavior/top-down-movement.d.ts +56 -0
- package/dist/behavior/top-down-movement.js +125 -0
- package/dist/behavior/top-down-movement.js.map +1 -0
- package/dist/behavior/world-boundary-2d.d.ts +4 -3
- package/dist/behavior/world-boundary-2d.js +90 -36
- package/dist/behavior/world-boundary-2d.js.map +1 -1
- package/dist/behavior/world-boundary-3d.d.ts +76 -0
- package/dist/behavior/world-boundary-3d.js +274 -0
- package/dist/behavior/world-boundary-3d.js.map +1 -0
- package/dist/{behavior-descriptor-BWNWmIjv.d.ts → behavior-descriptor-BXnVR8Ki.d.ts} +22 -5
- package/dist/{blueprints-BWGz8fII.d.ts → blueprints-DmbK2dki.d.ts} +2 -2
- package/dist/camera-4XO5gbQH.d.ts +905 -0
- package/dist/camera.d.ts +1 -1
- package/dist/camera.js +876 -289
- package/dist/camera.js.map +1 -1
- package/dist/{composition-DrzFrbqI.d.ts → composition-BASvMKrW.d.ts} +1 -1
- package/dist/{core-DAkskq6Y.d.ts → core-CARRaS55.d.ts} +57 -14
- package/dist/core.d.ts +9 -8
- package/dist/core.js +4519 -1255
- package/dist/core.js.map +1 -1
- package/dist/{entities-DC9ce_vx.d.ts → entities-ChFirVL9.d.ts} +22 -28
- package/dist/entities.d.ts +4 -4
- package/dist/entities.js +1231 -314
- package/dist/entities.js.map +1 -1
- package/dist/{entity-BpbZqg19.d.ts → entity-vj-HTjzU.d.ts} +80 -11
- package/dist/{global-change-Dc8uCKi2.d.ts → global-change-2JvMaz44.d.ts} +1 -1
- package/dist/main.d.ts +718 -19
- package/dist/main.js +12129 -5959
- package/dist/main.js.map +1 -1
- package/dist/physics-pose-DCc4oE44.d.ts +25 -0
- package/dist/physics-protocol-BDD3P5W2.d.ts +200 -0
- package/dist/physics-worker.d.ts +21 -0
- package/dist/physics-worker.js +306 -0
- package/dist/physics-worker.js.map +1 -0
- package/dist/physics.d.ts +205 -0
- package/dist/physics.js +577 -0
- package/dist/physics.js.map +1 -0
- package/dist/{stage-types-BFsm3qsZ.d.ts → stage-types-C19IhuzA.d.ts} +253 -89
- package/dist/stage.d.ts +9 -8
- package/dist/stage.js +3782 -1041
- package/dist/stage.js.map +1 -1
- package/dist/sync-state-machine-CZyspBpj.d.ts +16 -0
- package/dist/{thruster-DhRaJnoL.d.ts → thruster-23lzoPZd.d.ts} +16 -8
- package/dist/world-DfgxoNMt.d.ts +105 -0
- package/package.json +25 -1
- package/dist/camera-B5e4c78l.d.ts +0 -468
- package/dist/world-Be5m1XC1.d.ts +0 -31
package/dist/entities.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/lib/entities/box.ts
|
|
2
|
-
import { Vector3 as
|
|
2
|
+
import { Vector3 as Vector39 } from "three";
|
|
3
3
|
|
|
4
4
|
// src/lib/entities/entity.ts
|
|
5
5
|
import { Mesh, ShaderMaterial, Group } from "three";
|
|
@@ -57,6 +57,8 @@ var BaseNode = class _BaseNode {
|
|
|
57
57
|
destroy: [],
|
|
58
58
|
cleanup: []
|
|
59
59
|
};
|
|
60
|
+
trackLifecycleRegistrations = false;
|
|
61
|
+
userLifecycleRegistrations = [];
|
|
60
62
|
constructor(args = []) {
|
|
61
63
|
const options = args.filter((arg) => !(arg instanceof _BaseNode)).reduce((acc, opt) => ({ ...acc, ...opt }), {});
|
|
62
64
|
this.options = options;
|
|
@@ -70,6 +72,7 @@ var BaseNode = class _BaseNode {
|
|
|
70
72
|
*/
|
|
71
73
|
onSetup(...callbacks) {
|
|
72
74
|
this.lifecycleCallbacks.setup.push(...callbacks);
|
|
75
|
+
this.recordLifecycleRegistration("onSetup", callbacks);
|
|
73
76
|
return this;
|
|
74
77
|
}
|
|
75
78
|
/**
|
|
@@ -77,6 +80,7 @@ var BaseNode = class _BaseNode {
|
|
|
77
80
|
*/
|
|
78
81
|
onLoaded(...callbacks) {
|
|
79
82
|
this.lifecycleCallbacks.loaded.push(...callbacks);
|
|
83
|
+
this.recordLifecycleRegistration("onLoaded", callbacks);
|
|
80
84
|
return this;
|
|
81
85
|
}
|
|
82
86
|
/**
|
|
@@ -84,6 +88,7 @@ var BaseNode = class _BaseNode {
|
|
|
84
88
|
*/
|
|
85
89
|
onUpdate(...callbacks) {
|
|
86
90
|
this.lifecycleCallbacks.update.push(...callbacks);
|
|
91
|
+
this.recordLifecycleRegistration("onUpdate", callbacks);
|
|
87
92
|
return this;
|
|
88
93
|
}
|
|
89
94
|
/**
|
|
@@ -91,6 +96,7 @@ var BaseNode = class _BaseNode {
|
|
|
91
96
|
*/
|
|
92
97
|
onDestroy(...callbacks) {
|
|
93
98
|
this.lifecycleCallbacks.destroy.push(...callbacks);
|
|
99
|
+
this.recordLifecycleRegistration("onDestroy", callbacks);
|
|
94
100
|
return this;
|
|
95
101
|
}
|
|
96
102
|
/**
|
|
@@ -98,6 +104,7 @@ var BaseNode = class _BaseNode {
|
|
|
98
104
|
*/
|
|
99
105
|
onCleanup(...callbacks) {
|
|
100
106
|
this.lifecycleCallbacks.cleanup.push(...callbacks);
|
|
107
|
+
this.recordLifecycleRegistration("onCleanup", callbacks);
|
|
101
108
|
return this;
|
|
102
109
|
}
|
|
103
110
|
/**
|
|
@@ -105,6 +112,7 @@ var BaseNode = class _BaseNode {
|
|
|
105
112
|
*/
|
|
106
113
|
prependSetup(...callbacks) {
|
|
107
114
|
this.lifecycleCallbacks.setup.unshift(...callbacks);
|
|
115
|
+
this.recordLifecycleRegistration("prependSetup", callbacks);
|
|
108
116
|
return this;
|
|
109
117
|
}
|
|
110
118
|
/**
|
|
@@ -112,6 +120,7 @@ var BaseNode = class _BaseNode {
|
|
|
112
120
|
*/
|
|
113
121
|
prependUpdate(...callbacks) {
|
|
114
122
|
this.lifecycleCallbacks.update.unshift(...callbacks);
|
|
123
|
+
this.recordLifecycleRegistration("prependUpdate", callbacks);
|
|
115
124
|
return this;
|
|
116
125
|
}
|
|
117
126
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -140,6 +149,42 @@ var BaseNode = class _BaseNode {
|
|
|
140
149
|
isComposite() {
|
|
141
150
|
return this.children.length > 0;
|
|
142
151
|
}
|
|
152
|
+
enableUserLifecycleTracking() {
|
|
153
|
+
this.trackLifecycleRegistrations = true;
|
|
154
|
+
}
|
|
155
|
+
replayUserLifecycleRegistrationsTo(target, wrap) {
|
|
156
|
+
for (const registration of this.userLifecycleRegistrations) {
|
|
157
|
+
const callbacks = wrap ? registration.callbacks.map((callback) => wrap(callback)) : [...registration.callbacks];
|
|
158
|
+
switch (registration.method) {
|
|
159
|
+
case "onSetup":
|
|
160
|
+
target.onSetup(...callbacks);
|
|
161
|
+
break;
|
|
162
|
+
case "onLoaded":
|
|
163
|
+
target.onLoaded(...callbacks);
|
|
164
|
+
break;
|
|
165
|
+
case "onUpdate":
|
|
166
|
+
target.onUpdate(...callbacks);
|
|
167
|
+
break;
|
|
168
|
+
case "onDestroy":
|
|
169
|
+
target.onDestroy(...callbacks);
|
|
170
|
+
break;
|
|
171
|
+
case "onCleanup":
|
|
172
|
+
target.onCleanup(...callbacks);
|
|
173
|
+
break;
|
|
174
|
+
case "prependSetup":
|
|
175
|
+
target.prependSetup(...callbacks);
|
|
176
|
+
break;
|
|
177
|
+
case "prependUpdate":
|
|
178
|
+
target.prependUpdate(...callbacks);
|
|
179
|
+
break;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
cloneChildrenInto(target) {
|
|
184
|
+
for (const child of this.children) {
|
|
185
|
+
target.add(cloneNode(child));
|
|
186
|
+
}
|
|
187
|
+
}
|
|
143
188
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
144
189
|
// Node lifecycle execution - runs internal + callback arrays
|
|
145
190
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -213,7 +258,25 @@ var BaseNode = class _BaseNode {
|
|
|
213
258
|
setOptions(options) {
|
|
214
259
|
this.options = { ...this.options, ...options };
|
|
215
260
|
}
|
|
261
|
+
recordLifecycleRegistration(method, callbacks) {
|
|
262
|
+
if (!this.trackLifecycleRegistrations || callbacks.length === 0) {
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
this.userLifecycleRegistrations.push({
|
|
266
|
+
method,
|
|
267
|
+
callbacks: [...callbacks]
|
|
268
|
+
});
|
|
269
|
+
}
|
|
216
270
|
};
|
|
271
|
+
function cloneNode(node) {
|
|
272
|
+
const maybeClone = node?.clone;
|
|
273
|
+
if (typeof maybeClone !== "function") {
|
|
274
|
+
throw new Error(
|
|
275
|
+
`Cannot clone child node "${node.name || node.uuid || "unknown"}": missing clone() support.`
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
return maybeClone.call(node);
|
|
279
|
+
}
|
|
217
280
|
|
|
218
281
|
// ../../node_modules/.pnpm/mitt@3.0.1/node_modules/mitt/dist/mitt.mjs
|
|
219
282
|
function mitt_default(n) {
|
|
@@ -285,10 +348,15 @@ function createTransformStore(initial) {
|
|
|
285
348
|
rotation: { x: 0, y: 0, z: 0, w: 1 },
|
|
286
349
|
velocity: { x: 0, y: 0, z: 0 },
|
|
287
350
|
angularVelocity: { x: 0, y: 0, z: 0 },
|
|
351
|
+
velocityChannels: {},
|
|
288
352
|
dirty: {
|
|
289
353
|
position: false,
|
|
290
354
|
rotation: false,
|
|
291
355
|
velocity: false,
|
|
356
|
+
velocityX: false,
|
|
357
|
+
velocityY: false,
|
|
358
|
+
velocityZ: false,
|
|
359
|
+
velocityChannels: false,
|
|
292
360
|
angularVelocity: false
|
|
293
361
|
}
|
|
294
362
|
};
|
|
@@ -305,39 +373,70 @@ function createTransformStore(initial) {
|
|
|
305
373
|
|
|
306
374
|
// src/lib/actions/capabilities/moveable.ts
|
|
307
375
|
import { Vector3 } from "three";
|
|
376
|
+
|
|
377
|
+
// src/lib/actions/capabilities/velocity-intents.ts
|
|
378
|
+
function setVelocityIntent(store, sourceId, vector, options = {}) {
|
|
379
|
+
const prev = store.velocityChannels[sourceId];
|
|
380
|
+
const mode = options.mode ?? prev?.mode ?? "replace";
|
|
381
|
+
const priority = options.priority ?? prev?.priority ?? 0;
|
|
382
|
+
const isAdditiveMerge = mode === "add" && prev?.mode === "add";
|
|
383
|
+
const x = vector.x == null ? prev?.x : isAdditiveMerge ? (prev?.x ?? 0) + vector.x : vector.x;
|
|
384
|
+
const y = vector.y == null ? prev?.y : isAdditiveMerge ? (prev?.y ?? 0) + vector.y : vector.y;
|
|
385
|
+
const z = vector.z == null ? prev?.z : isAdditiveMerge ? (prev?.z ?? 0) + vector.z : vector.z;
|
|
386
|
+
store.velocityChannels[sourceId] = {
|
|
387
|
+
x,
|
|
388
|
+
y,
|
|
389
|
+
z,
|
|
390
|
+
mode,
|
|
391
|
+
priority
|
|
392
|
+
};
|
|
393
|
+
store.dirty.velocityChannels = true;
|
|
394
|
+
}
|
|
395
|
+
function clearVelocityIntent(store, sourceId) {
|
|
396
|
+
if (!(sourceId in store.velocityChannels)) return;
|
|
397
|
+
delete store.velocityChannels[sourceId];
|
|
398
|
+
store.dirty.velocityChannels = true;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// src/lib/actions/capabilities/moveable.ts
|
|
308
402
|
function moveX(entity, delta) {
|
|
309
403
|
if (!entity.transformStore) return;
|
|
310
|
-
entity.transformStore
|
|
311
|
-
entity.transformStore.dirty.velocity = true;
|
|
404
|
+
setVelocityIntent(entity.transformStore, "actions", { x: delta }, { mode: "replace" });
|
|
312
405
|
}
|
|
313
406
|
function moveY(entity, delta) {
|
|
314
407
|
if (!entity.transformStore) return;
|
|
315
|
-
entity.transformStore
|
|
316
|
-
entity.transformStore.dirty.velocity = true;
|
|
408
|
+
setVelocityIntent(entity.transformStore, "actions", { y: delta }, { mode: "replace" });
|
|
317
409
|
}
|
|
318
410
|
function moveZ(entity, delta) {
|
|
319
411
|
if (!entity.transformStore) return;
|
|
320
|
-
entity.transformStore
|
|
321
|
-
entity.transformStore.dirty.velocity = true;
|
|
412
|
+
setVelocityIntent(entity.transformStore, "actions", { z: delta }, { mode: "replace" });
|
|
322
413
|
}
|
|
323
414
|
function moveXY(entity, deltaX, deltaY) {
|
|
324
415
|
if (!entity.transformStore) return;
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
416
|
+
setVelocityIntent(
|
|
417
|
+
entity.transformStore,
|
|
418
|
+
"actions",
|
|
419
|
+
{ x: deltaX, y: deltaY },
|
|
420
|
+
{ mode: "replace" }
|
|
421
|
+
);
|
|
328
422
|
}
|
|
329
423
|
function moveXZ(entity, deltaX, deltaZ) {
|
|
330
424
|
if (!entity.transformStore) return;
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
425
|
+
setVelocityIntent(
|
|
426
|
+
entity.transformStore,
|
|
427
|
+
"actions",
|
|
428
|
+
{ x: deltaX, z: deltaZ },
|
|
429
|
+
{ mode: "replace" }
|
|
430
|
+
);
|
|
334
431
|
}
|
|
335
432
|
function move(entity, vector) {
|
|
336
433
|
if (!entity.transformStore) return;
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
434
|
+
setVelocityIntent(
|
|
435
|
+
entity.transformStore,
|
|
436
|
+
"actions",
|
|
437
|
+
{ x: vector.x, y: vector.y, z: vector.z },
|
|
438
|
+
{ mode: "add" }
|
|
439
|
+
);
|
|
341
440
|
}
|
|
342
441
|
function resetVelocity(entity) {
|
|
343
442
|
if (!entity.body) return;
|
|
@@ -423,6 +522,17 @@ function makeMoveable(entity) {
|
|
|
423
522
|
|
|
424
523
|
// src/lib/actions/capabilities/rotatable.ts
|
|
425
524
|
import { Euler, Vector3 as Vector32, MathUtils, Quaternion as Quaternion2 } from "three";
|
|
525
|
+
function syncImmediateRotation(entity) {
|
|
526
|
+
if (!entity.group || !entity.transformStore) return;
|
|
527
|
+
entity.group.setRotationFromQuaternion(
|
|
528
|
+
new Quaternion2(
|
|
529
|
+
entity.transformStore.rotation.x,
|
|
530
|
+
entity.transformStore.rotation.y,
|
|
531
|
+
entity.transformStore.rotation.z,
|
|
532
|
+
entity.transformStore.rotation.w
|
|
533
|
+
)
|
|
534
|
+
);
|
|
535
|
+
}
|
|
426
536
|
function rotateInDirection(entity, moveVector) {
|
|
427
537
|
if (!entity.body) return;
|
|
428
538
|
const rotate = Math.atan2(-moveVector.x, moveVector.z);
|
|
@@ -432,9 +542,16 @@ function rotateYEuler(entity, amount) {
|
|
|
432
542
|
rotateEuler(entity, new Vector32(0, -amount, 0));
|
|
433
543
|
}
|
|
434
544
|
function rotateEuler(entity, rotation2) {
|
|
435
|
-
if (!entity.
|
|
436
|
-
const
|
|
437
|
-
|
|
545
|
+
if (!entity.transformStore) return;
|
|
546
|
+
const quat = new Quaternion2().setFromEuler(
|
|
547
|
+
new Euler(rotation2.x, rotation2.y, rotation2.z)
|
|
548
|
+
);
|
|
549
|
+
entity.transformStore.rotation.w = quat.w;
|
|
550
|
+
entity.transformStore.rotation.x = quat.x;
|
|
551
|
+
entity.transformStore.rotation.y = quat.y;
|
|
552
|
+
entity.transformStore.rotation.z = quat.z;
|
|
553
|
+
entity.transformStore.dirty.rotation = true;
|
|
554
|
+
syncImmediateRotation(entity);
|
|
438
555
|
}
|
|
439
556
|
function rotateY(entity, delta) {
|
|
440
557
|
if (!entity.transformStore) return;
|
|
@@ -451,6 +568,7 @@ function rotateY(entity, delta) {
|
|
|
451
568
|
entity.transformStore.rotation.y = newY;
|
|
452
569
|
entity.transformStore.rotation.z = newZ;
|
|
453
570
|
entity.transformStore.dirty.rotation = true;
|
|
571
|
+
syncImmediateRotation(entity);
|
|
454
572
|
}
|
|
455
573
|
function rotateX(entity, delta) {
|
|
456
574
|
if (!entity.transformStore) return;
|
|
@@ -467,6 +585,7 @@ function rotateX(entity, delta) {
|
|
|
467
585
|
entity.transformStore.rotation.y = newY;
|
|
468
586
|
entity.transformStore.rotation.z = newZ;
|
|
469
587
|
entity.transformStore.dirty.rotation = true;
|
|
588
|
+
syncImmediateRotation(entity);
|
|
470
589
|
}
|
|
471
590
|
function rotateZ(entity, delta) {
|
|
472
591
|
if (!entity.transformStore) return;
|
|
@@ -483,6 +602,8 @@ function rotateZ(entity, delta) {
|
|
|
483
602
|
entity.transformStore.rotation.y = newY;
|
|
484
603
|
entity.transformStore.rotation.z = newZ;
|
|
485
604
|
entity.transformStore.dirty.rotation = true;
|
|
605
|
+
entity._rotation2DAngle = (entity._rotation2DAngle ?? 0) + delta;
|
|
606
|
+
syncImmediateRotation(entity);
|
|
486
607
|
}
|
|
487
608
|
function setRotationY(entity, y) {
|
|
488
609
|
if (!entity.transformStore) return;
|
|
@@ -494,6 +615,7 @@ function setRotationY(entity, y) {
|
|
|
494
615
|
entity.transformStore.rotation.y = yComponent;
|
|
495
616
|
entity.transformStore.rotation.z = 0;
|
|
496
617
|
entity.transformStore.dirty.rotation = true;
|
|
618
|
+
syncImmediateRotation(entity);
|
|
497
619
|
}
|
|
498
620
|
function setRotationDegreesY(entity, y) {
|
|
499
621
|
if (!entity.body) return;
|
|
@@ -509,6 +631,7 @@ function setRotationX(entity, x) {
|
|
|
509
631
|
entity.transformStore.rotation.y = 0;
|
|
510
632
|
entity.transformStore.rotation.z = 0;
|
|
511
633
|
entity.transformStore.dirty.rotation = true;
|
|
634
|
+
syncImmediateRotation(entity);
|
|
512
635
|
}
|
|
513
636
|
function setRotationDegreesX(entity, x) {
|
|
514
637
|
if (!entity.body) return;
|
|
@@ -524,6 +647,8 @@ function setRotationZ(entity, z) {
|
|
|
524
647
|
entity.transformStore.rotation.y = 0;
|
|
525
648
|
entity.transformStore.rotation.z = zComponent;
|
|
526
649
|
entity.transformStore.dirty.rotation = true;
|
|
650
|
+
entity._rotation2DAngle = z;
|
|
651
|
+
syncImmediateRotation(entity);
|
|
527
652
|
}
|
|
528
653
|
function setRotationDegreesZ(entity, z) {
|
|
529
654
|
if (!entity.body) return;
|
|
@@ -537,6 +662,8 @@ function setRotation(entity, x, y, z) {
|
|
|
537
662
|
entity.transformStore.rotation.y = quat.y;
|
|
538
663
|
entity.transformStore.rotation.z = quat.z;
|
|
539
664
|
entity.transformStore.dirty.rotation = true;
|
|
665
|
+
entity._rotation2DAngle = z;
|
|
666
|
+
syncImmediateRotation(entity);
|
|
540
667
|
}
|
|
541
668
|
function setRotationDegrees(entity, x, y, z) {
|
|
542
669
|
if (!entity.body) return;
|
|
@@ -583,7 +710,7 @@ import {
|
|
|
583
710
|
RigidBodyDesc as RigidBodyDesc2,
|
|
584
711
|
RigidBodyType as RigidBodyType2
|
|
585
712
|
} from "@dimforge/rapier3d-compat";
|
|
586
|
-
import { Vector3 as
|
|
713
|
+
import { Vector3 as Vector35 } from "three";
|
|
587
714
|
|
|
588
715
|
// src/lib/collision/collision-builder.ts
|
|
589
716
|
import { ActiveCollisionTypes, ColliderDesc, RigidBodyDesc, RigidBodyType, Vector3 as Vector33 } from "@dimforge/rapier3d-compat";
|
|
@@ -645,10 +772,73 @@ var CollisionBuilder = class {
|
|
|
645
772
|
}
|
|
646
773
|
};
|
|
647
774
|
|
|
775
|
+
// src/lib/core/clone-utils.ts
|
|
776
|
+
import { Color, Vector2, Vector3 as Vector34 } from "three";
|
|
777
|
+
function isPlainObject(value) {
|
|
778
|
+
if (!value || typeof value !== "object") {
|
|
779
|
+
return false;
|
|
780
|
+
}
|
|
781
|
+
const proto = Object.getPrototypeOf(value);
|
|
782
|
+
return proto === Object.prototype || proto === null;
|
|
783
|
+
}
|
|
784
|
+
function deepCloneValue(value) {
|
|
785
|
+
if (value === null || value === void 0) {
|
|
786
|
+
return value;
|
|
787
|
+
}
|
|
788
|
+
if (typeof value === "function" || typeof value !== "object") {
|
|
789
|
+
return value;
|
|
790
|
+
}
|
|
791
|
+
if (value instanceof Color || value instanceof Vector2 || value instanceof Vector34) {
|
|
792
|
+
return value.clone();
|
|
793
|
+
}
|
|
794
|
+
if (value instanceof Float32Array) {
|
|
795
|
+
return new Float32Array(value);
|
|
796
|
+
}
|
|
797
|
+
if (Array.isArray(value)) {
|
|
798
|
+
return value.map((item) => deepCloneValue(item));
|
|
799
|
+
}
|
|
800
|
+
if (isPlainObject(value)) {
|
|
801
|
+
const cloned = {};
|
|
802
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
803
|
+
if (key === "_builders") {
|
|
804
|
+
continue;
|
|
805
|
+
}
|
|
806
|
+
cloned[key] = deepCloneValue(entry);
|
|
807
|
+
}
|
|
808
|
+
return cloned;
|
|
809
|
+
}
|
|
810
|
+
return value;
|
|
811
|
+
}
|
|
812
|
+
function deepMergeValues(base, overrides) {
|
|
813
|
+
const clonedBase = deepCloneValue(base);
|
|
814
|
+
if (!overrides) {
|
|
815
|
+
return clonedBase;
|
|
816
|
+
}
|
|
817
|
+
if (!isPlainObject(clonedBase) || !isPlainObject(overrides)) {
|
|
818
|
+
return deepCloneValue(overrides);
|
|
819
|
+
}
|
|
820
|
+
const result = clonedBase;
|
|
821
|
+
for (const [key, overrideValue] of Object.entries(overrides)) {
|
|
822
|
+
if (key === "_builders" || overrideValue === void 0) {
|
|
823
|
+
continue;
|
|
824
|
+
}
|
|
825
|
+
const currentValue = result[key];
|
|
826
|
+
if (isPlainObject(currentValue) && isPlainObject(overrideValue)) {
|
|
827
|
+
result[key] = deepMergeValues(currentValue, overrideValue);
|
|
828
|
+
continue;
|
|
829
|
+
}
|
|
830
|
+
result[key] = deepCloneValue(overrideValue);
|
|
831
|
+
}
|
|
832
|
+
return result;
|
|
833
|
+
}
|
|
834
|
+
|
|
648
835
|
// src/lib/entities/parts/collision-factories.ts
|
|
649
836
|
function isCollisionComponent(obj) {
|
|
650
837
|
return obj && obj.__kind === "collision";
|
|
651
838
|
}
|
|
839
|
+
function cloneCollisionComponent(component) {
|
|
840
|
+
return component.cloneComponent();
|
|
841
|
+
}
|
|
652
842
|
function buildBodyDesc(isStatic) {
|
|
653
843
|
const type = isStatic ? RigidBodyType2.Fixed : RigidBodyType2.Dynamic;
|
|
654
844
|
return new RigidBodyDesc2(type).setTranslation(0, 0, 0).setGravityScale(1).setCanSleep(false).setCcdEnabled(true);
|
|
@@ -670,57 +860,66 @@ function applyCollisionOptions(colliderDesc, opts) {
|
|
|
670
860
|
colliderDesc.setCollisionGroups(groupId << 16 | filter);
|
|
671
861
|
}
|
|
672
862
|
}
|
|
673
|
-
function makeComponent(colliderDesc, opts) {
|
|
863
|
+
function makeComponent(colliderDesc, opts, cloneComponentFactory) {
|
|
674
864
|
applyCollisionOptions(colliderDesc, opts);
|
|
675
865
|
return {
|
|
676
866
|
__kind: "collision",
|
|
677
867
|
bodyDesc: buildBodyDesc(opts.static ?? false),
|
|
678
|
-
colliderDesc
|
|
868
|
+
colliderDesc,
|
|
869
|
+
cloneComponent: cloneComponentFactory
|
|
679
870
|
};
|
|
680
871
|
}
|
|
681
872
|
function boxCollision(opts = {}) {
|
|
682
873
|
const size = opts.size ?? { x: 1, y: 1, z: 1 };
|
|
683
874
|
const desc = ColliderDesc2.cuboid(size.x / 2, size.y / 2, size.z / 2);
|
|
684
|
-
|
|
875
|
+
const clonedOpts = deepCloneValue(opts);
|
|
876
|
+
return makeComponent(desc, opts, () => boxCollision(clonedOpts));
|
|
685
877
|
}
|
|
686
878
|
function sphereCollision(opts = {}) {
|
|
687
879
|
const desc = ColliderDesc2.ball(opts.radius ?? 1);
|
|
688
|
-
|
|
880
|
+
const clonedOpts = deepCloneValue(opts);
|
|
881
|
+
return makeComponent(desc, opts, () => sphereCollision(clonedOpts));
|
|
689
882
|
}
|
|
690
883
|
function coneCollision(opts = {}) {
|
|
691
884
|
const radius = opts.radius ?? 1;
|
|
692
885
|
const height = opts.height ?? 2;
|
|
693
886
|
const desc = ColliderDesc2.cone(height / 2, radius);
|
|
694
|
-
|
|
887
|
+
const clonedOpts = deepCloneValue(opts);
|
|
888
|
+
return makeComponent(desc, opts, () => coneCollision(clonedOpts));
|
|
695
889
|
}
|
|
696
890
|
function pyramidCollision(opts = {}) {
|
|
697
891
|
const radius = opts.radius ?? 1;
|
|
698
892
|
const height = opts.height ?? 2;
|
|
699
893
|
const desc = ColliderDesc2.cone(height / 2, radius);
|
|
700
|
-
|
|
894
|
+
const clonedOpts = deepCloneValue(opts);
|
|
895
|
+
return makeComponent(desc, opts, () => pyramidCollision(clonedOpts));
|
|
701
896
|
}
|
|
702
897
|
function cylinderCollision(opts = {}) {
|
|
703
898
|
const radius = Math.max(opts.radiusTop ?? 1, opts.radiusBottom ?? 1);
|
|
704
899
|
const height = opts.height ?? 2;
|
|
705
900
|
const desc = ColliderDesc2.cylinder(height / 2, radius);
|
|
706
|
-
|
|
901
|
+
const clonedOpts = deepCloneValue(opts);
|
|
902
|
+
return makeComponent(desc, opts, () => cylinderCollision(clonedOpts));
|
|
707
903
|
}
|
|
708
904
|
function pillCollision(opts = {}) {
|
|
709
905
|
const radius = opts.radius ?? 0.5;
|
|
710
906
|
const length = opts.length ?? 1;
|
|
711
907
|
const desc = ColliderDesc2.capsule(length / 2, radius);
|
|
712
|
-
|
|
908
|
+
const clonedOpts = deepCloneValue(opts);
|
|
909
|
+
return makeComponent(desc, opts, () => pillCollision(clonedOpts));
|
|
713
910
|
}
|
|
714
911
|
function zoneCollision(opts = {}) {
|
|
715
912
|
const size = opts.size ?? { x: 1, y: 1, z: 1 };
|
|
716
913
|
const desc = ColliderDesc2.cuboid(size.x / 2, size.y / 2, size.z / 2);
|
|
717
914
|
desc.setSensor(true);
|
|
718
915
|
desc.activeCollisionTypes = ActiveCollisionTypes2.KINEMATIC_FIXED;
|
|
719
|
-
|
|
916
|
+
const effectiveOpts = { ...opts, static: opts.static ?? true, sensor: true };
|
|
917
|
+
const clonedOpts = deepCloneValue(effectiveOpts);
|
|
918
|
+
return makeComponent(desc, effectiveOpts, () => zoneCollision(clonedOpts));
|
|
720
919
|
}
|
|
721
920
|
|
|
722
921
|
// src/lib/entities/common.ts
|
|
723
|
-
import { Color, Vector3 as
|
|
922
|
+
import { Color as Color2, Vector3 as Vector36 } from "three";
|
|
724
923
|
|
|
725
924
|
// src/lib/graphics/shaders/vertex/object.shader.ts
|
|
726
925
|
var objectVertexShader = `
|
|
@@ -752,9 +951,9 @@ var standardShader = {
|
|
|
752
951
|
|
|
753
952
|
// src/lib/entities/common.ts
|
|
754
953
|
var commonDefaults = {
|
|
755
|
-
position: new
|
|
954
|
+
position: new Vector36(0, 0, 0),
|
|
756
955
|
material: {
|
|
757
|
-
color: new
|
|
956
|
+
color: new Color2("#ffffff"),
|
|
758
957
|
shader: standardShader
|
|
759
958
|
},
|
|
760
959
|
collision: {
|
|
@@ -773,6 +972,21 @@ function mergeArgs(args, defaults) {
|
|
|
773
972
|
}
|
|
774
973
|
|
|
775
974
|
// src/lib/entities/entity.ts
|
|
975
|
+
function isCollisionRegistrationOptions(value) {
|
|
976
|
+
return value != null && typeof value === "object";
|
|
977
|
+
}
|
|
978
|
+
function normalizeCollisionRegistrationOptions(options) {
|
|
979
|
+
return {
|
|
980
|
+
phase: options?.phase ?? "stay",
|
|
981
|
+
cooldownMs: options?.cooldownMs
|
|
982
|
+
};
|
|
983
|
+
}
|
|
984
|
+
function getCollisionNowMs() {
|
|
985
|
+
if (typeof performance !== "undefined" && typeof performance.now === "function") {
|
|
986
|
+
return performance.now();
|
|
987
|
+
}
|
|
988
|
+
return Date.now();
|
|
989
|
+
}
|
|
776
990
|
var GameEntity = class extends BaseNode {
|
|
777
991
|
behaviors = [];
|
|
778
992
|
group;
|
|
@@ -780,6 +994,7 @@ var GameEntity = class extends BaseNode {
|
|
|
780
994
|
materials;
|
|
781
995
|
bodyDesc = null;
|
|
782
996
|
body = null;
|
|
997
|
+
physicsAttached = false;
|
|
783
998
|
colliderDesc;
|
|
784
999
|
collider;
|
|
785
1000
|
custom = {};
|
|
@@ -793,7 +1008,9 @@ var GameEntity = class extends BaseNode {
|
|
|
793
1008
|
debugInfo = {};
|
|
794
1009
|
debugMaterial;
|
|
795
1010
|
collisionDelegate = {
|
|
796
|
-
collision: []
|
|
1011
|
+
collision: [],
|
|
1012
|
+
cooldowns: /* @__PURE__ */ new Map(),
|
|
1013
|
+
nextId: 0
|
|
797
1014
|
};
|
|
798
1015
|
collisionType;
|
|
799
1016
|
// Instancing support
|
|
@@ -807,6 +1024,9 @@ var GameEntity = class extends BaseNode {
|
|
|
807
1024
|
eventDelegate = new EventEmitterDelegate();
|
|
808
1025
|
// Behavior references (new ECS pattern)
|
|
809
1026
|
behaviorRefs = [];
|
|
1027
|
+
cloneFactory = null;
|
|
1028
|
+
trackAddedComponents = false;
|
|
1029
|
+
trackedComponents = [];
|
|
810
1030
|
// Transform store for batched physics updates (auto-created in create())
|
|
811
1031
|
transformStore;
|
|
812
1032
|
// Movement & rotation methods are assigned at runtime by makeTransformable.
|
|
@@ -865,8 +1085,10 @@ var GameEntity = class extends BaseNode {
|
|
|
865
1085
|
for (const component of components) {
|
|
866
1086
|
if (component instanceof Mesh) {
|
|
867
1087
|
this.addMeshComponent(component);
|
|
1088
|
+
this.trackComponent(component);
|
|
868
1089
|
} else if (isCollisionComponent(component)) {
|
|
869
1090
|
this.addCollisionComponent(component);
|
|
1091
|
+
this.trackComponent(component);
|
|
870
1092
|
} else {
|
|
871
1093
|
super.add(component);
|
|
872
1094
|
}
|
|
@@ -907,6 +1129,22 @@ var GameEntity = class extends BaseNode {
|
|
|
907
1129
|
this.colliderDescs.push(collision.colliderDesc);
|
|
908
1130
|
}
|
|
909
1131
|
}
|
|
1132
|
+
trackComponent(component) {
|
|
1133
|
+
if (!this.trackAddedComponents) {
|
|
1134
|
+
return;
|
|
1135
|
+
}
|
|
1136
|
+
if (component instanceof Mesh) {
|
|
1137
|
+
this.trackedComponents.push({
|
|
1138
|
+
kind: "mesh",
|
|
1139
|
+
component: cloneMeshComponent(component)
|
|
1140
|
+
});
|
|
1141
|
+
return;
|
|
1142
|
+
}
|
|
1143
|
+
this.trackedComponents.push({
|
|
1144
|
+
kind: "collision",
|
|
1145
|
+
component: cloneCollisionComponent(component)
|
|
1146
|
+
});
|
|
1147
|
+
}
|
|
910
1148
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
911
1149
|
// Actions API -- entity-scoped, self-contained stateful actions
|
|
912
1150
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -942,7 +1180,7 @@ var GameEntity = class extends BaseNode {
|
|
|
942
1180
|
store.velocity.x = 0;
|
|
943
1181
|
store.velocity.y = 0;
|
|
944
1182
|
store.velocity.z = 0;
|
|
945
|
-
store
|
|
1183
|
+
clearVelocityIntent(store, "actions");
|
|
946
1184
|
store.angularVelocity.x = 0;
|
|
947
1185
|
store.angularVelocity.y = 0;
|
|
948
1186
|
store.angularVelocity.z = 0;
|
|
@@ -958,7 +1196,7 @@ var GameEntity = class extends BaseNode {
|
|
|
958
1196
|
store.velocity.x = 0;
|
|
959
1197
|
store.velocity.y = 0;
|
|
960
1198
|
store.velocity.z = 0;
|
|
961
|
-
store
|
|
1199
|
+
clearVelocityIntent(store, "actions");
|
|
962
1200
|
store.angularVelocity.x = 0;
|
|
963
1201
|
store.angularVelocity.y = 0;
|
|
964
1202
|
store.angularVelocity.z = 0;
|
|
@@ -976,12 +1214,72 @@ var GameEntity = class extends BaseNode {
|
|
|
976
1214
|
this.name = this.options.name || "";
|
|
977
1215
|
return this;
|
|
978
1216
|
}
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
1217
|
+
clone(overrides) {
|
|
1218
|
+
if (!this.cloneFactory) {
|
|
1219
|
+
throw new Error(
|
|
1220
|
+
`Cannot clone ${this.constructor.name}: clone() is only supported for entities created by official factory helpers.`
|
|
1221
|
+
);
|
|
1222
|
+
}
|
|
1223
|
+
const clone = this.cloneFactory(
|
|
1224
|
+
deepMergeValues(this.options, overrides)
|
|
1225
|
+
);
|
|
1226
|
+
const behaviorAliases = this.copyBehaviorRefsTo(clone);
|
|
1227
|
+
const wrapCallback = (callback) => wrapBehaviorCallback(callback, behaviorAliases);
|
|
1228
|
+
this.replayUserLifecycleRegistrationsTo(clone, wrapCallback);
|
|
1229
|
+
const collisionRegistrations = this.getCollisionRegistrations();
|
|
1230
|
+
if (collisionRegistrations.length > 0) {
|
|
1231
|
+
for (const registration of collisionRegistrations) {
|
|
1232
|
+
clone.onCollision(
|
|
1233
|
+
wrapCallback(registration.callback),
|
|
1234
|
+
registration.options
|
|
1235
|
+
);
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
for (const trackedComponent of this.trackedComponents) {
|
|
1239
|
+
if (trackedComponent.kind === "mesh") {
|
|
1240
|
+
clone.add(cloneMeshComponent(trackedComponent.component));
|
|
1241
|
+
continue;
|
|
1242
|
+
}
|
|
1243
|
+
clone.add(cloneCollisionComponent(trackedComponent.component));
|
|
1244
|
+
}
|
|
1245
|
+
this.cloneChildrenInto(clone);
|
|
1246
|
+
return clone;
|
|
1247
|
+
}
|
|
1248
|
+
finalizeCloneSupport(factory) {
|
|
1249
|
+
this.cloneFactory = factory;
|
|
1250
|
+
this.enableUserLifecycleTracking();
|
|
1251
|
+
this.trackAddedComponents = true;
|
|
1252
|
+
return this;
|
|
1253
|
+
}
|
|
1254
|
+
onCollision(...args) {
|
|
1255
|
+
const existing = [...this.collisionDelegate.collision ?? []];
|
|
1256
|
+
let nextId = this.collisionDelegate.nextId ?? 0;
|
|
1257
|
+
if (args.length === 1 || args.length === 2 && (args[1] == null || isCollisionRegistrationOptions(args[1]))) {
|
|
1258
|
+
const callback = args[0];
|
|
1259
|
+
if (!callback) {
|
|
1260
|
+
return this;
|
|
1261
|
+
}
|
|
1262
|
+
const options = args[1];
|
|
1263
|
+
existing.push({
|
|
1264
|
+
id: nextId,
|
|
1265
|
+
callback,
|
|
1266
|
+
options: normalizeCollisionRegistrationOptions(options)
|
|
1267
|
+
});
|
|
1268
|
+
nextId += 1;
|
|
1269
|
+
this.collisionDelegate.collision = existing;
|
|
1270
|
+
this.collisionDelegate.nextId = nextId;
|
|
1271
|
+
return this;
|
|
1272
|
+
}
|
|
1273
|
+
for (const callback of args) {
|
|
1274
|
+
existing.push({
|
|
1275
|
+
id: nextId,
|
|
1276
|
+
callback,
|
|
1277
|
+
options: normalizeCollisionRegistrationOptions()
|
|
1278
|
+
});
|
|
1279
|
+
nextId += 1;
|
|
1280
|
+
}
|
|
1281
|
+
this.collisionDelegate.collision = existing;
|
|
1282
|
+
this.collisionDelegate.nextId = nextId;
|
|
985
1283
|
return this;
|
|
986
1284
|
}
|
|
987
1285
|
/**
|
|
@@ -1015,6 +1313,27 @@ var GameEntity = class extends BaseNode {
|
|
|
1015
1313
|
getBehaviorRefs() {
|
|
1016
1314
|
return this.behaviorRefs;
|
|
1017
1315
|
}
|
|
1316
|
+
getCollisionCallbacks() {
|
|
1317
|
+
return this.getCollisionRegistrations().map((registration) => registration.callback);
|
|
1318
|
+
}
|
|
1319
|
+
getCollisionRegistrations() {
|
|
1320
|
+
return (this.collisionDelegate.collision ?? []).map((registration) => ({
|
|
1321
|
+
id: registration.id,
|
|
1322
|
+
callback: registration.callback,
|
|
1323
|
+
options: { ...registration.options }
|
|
1324
|
+
}));
|
|
1325
|
+
}
|
|
1326
|
+
copyBehaviorRefsTo(target) {
|
|
1327
|
+
const aliases = /* @__PURE__ */ new Map();
|
|
1328
|
+
for (const ref of this.behaviorRefs) {
|
|
1329
|
+
target.use(ref.descriptor, deepCloneValue(ref.options));
|
|
1330
|
+
const targetRef = target.getBehaviorRefs().at(-1);
|
|
1331
|
+
if (targetRef) {
|
|
1332
|
+
aliases.set(ref, targetRef);
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
return aliases;
|
|
1336
|
+
}
|
|
1018
1337
|
/**
|
|
1019
1338
|
* Entity-specific setup - resets actions for a fresh stage session.
|
|
1020
1339
|
* (User callbacks are handled by BaseNode's lifecycleCallbacks.setup)
|
|
@@ -1068,12 +1387,29 @@ var GameEntity = class extends BaseNode {
|
|
|
1068
1387
|
this.debugMaterial = void 0;
|
|
1069
1388
|
this.group?.removeFromParent();
|
|
1070
1389
|
}
|
|
1071
|
-
_collision(other, globals
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1390
|
+
_collision(other, globals, dispatch = {
|
|
1391
|
+
phase: "stay",
|
|
1392
|
+
nowMs: getCollisionNowMs()
|
|
1393
|
+
}) {
|
|
1394
|
+
if (!this.collisionDelegate.collision?.length) {
|
|
1395
|
+
return;
|
|
1396
|
+
}
|
|
1397
|
+
const cooldowns = this.collisionDelegate.cooldowns ?? /* @__PURE__ */ new Map();
|
|
1398
|
+
this.collisionDelegate.cooldowns = cooldowns;
|
|
1399
|
+
for (const registration of this.collisionDelegate.collision) {
|
|
1400
|
+
if (registration.options.phase !== dispatch.phase) {
|
|
1401
|
+
continue;
|
|
1402
|
+
}
|
|
1403
|
+
const cooldownMs = registration.options.cooldownMs;
|
|
1404
|
+
if (cooldownMs != null) {
|
|
1405
|
+
const cooldownKey = `${registration.id}:${other.uuid}`;
|
|
1406
|
+
const lastTriggeredAt = cooldowns.get(cooldownKey);
|
|
1407
|
+
if (lastTriggeredAt != null && dispatch.nowMs - lastTriggeredAt < cooldownMs) {
|
|
1408
|
+
continue;
|
|
1409
|
+
}
|
|
1410
|
+
cooldowns.set(cooldownKey, dispatch.nowMs);
|
|
1411
|
+
}
|
|
1412
|
+
registration.callback({ entity: this, other, globals });
|
|
1077
1413
|
}
|
|
1078
1414
|
}
|
|
1079
1415
|
updateMaterials(params) {
|
|
@@ -1120,6 +1456,63 @@ var GameEntity = class extends BaseNode {
|
|
|
1120
1456
|
this.eventDelegate.dispose();
|
|
1121
1457
|
}
|
|
1122
1458
|
};
|
|
1459
|
+
function cloneMeshComponent(mesh) {
|
|
1460
|
+
const clonedMesh = mesh.clone(true);
|
|
1461
|
+
const originalNodes = [];
|
|
1462
|
+
const clonedNodes = [];
|
|
1463
|
+
mesh.traverse((node) => originalNodes.push(node));
|
|
1464
|
+
clonedMesh.traverse((node) => clonedNodes.push(node));
|
|
1465
|
+
for (let i = 0; i < originalNodes.length; i++) {
|
|
1466
|
+
const originalNode = originalNodes[i];
|
|
1467
|
+
const clonedNode = clonedNodes[i];
|
|
1468
|
+
if (originalNode?.geometry?.clone) {
|
|
1469
|
+
clonedNode.geometry = originalNode.geometry.clone();
|
|
1470
|
+
}
|
|
1471
|
+
if (originalNode?.material) {
|
|
1472
|
+
clonedNode.material = Array.isArray(originalNode.material) ? originalNode.material.map((material) => material.clone()) : originalNode.material.clone();
|
|
1473
|
+
}
|
|
1474
|
+
}
|
|
1475
|
+
return clonedMesh;
|
|
1476
|
+
}
|
|
1477
|
+
function wrapBehaviorCallback(callback, aliases) {
|
|
1478
|
+
if (aliases.size === 0) {
|
|
1479
|
+
return callback;
|
|
1480
|
+
}
|
|
1481
|
+
return ((...args) => {
|
|
1482
|
+
const previousValues = Array.from(aliases.entries()).map(([sourceRef, targetRef]) => ({
|
|
1483
|
+
sourceRef,
|
|
1484
|
+
options: sourceRef.options,
|
|
1485
|
+
fsm: sourceRef.fsm,
|
|
1486
|
+
hasFSM: Object.prototype.hasOwnProperty.call(sourceRef, "fsm"),
|
|
1487
|
+
targetRef
|
|
1488
|
+
}));
|
|
1489
|
+
for (const entry of previousValues) {
|
|
1490
|
+
entry.sourceRef.options = entry.targetRef.options;
|
|
1491
|
+
if (entry.targetRef.fsm === void 0) {
|
|
1492
|
+
delete entry.sourceRef.fsm;
|
|
1493
|
+
} else {
|
|
1494
|
+
entry.sourceRef.fsm = entry.targetRef.fsm;
|
|
1495
|
+
}
|
|
1496
|
+
}
|
|
1497
|
+
try {
|
|
1498
|
+
return callback(...args);
|
|
1499
|
+
} finally {
|
|
1500
|
+
for (let i = previousValues.length - 1; i >= 0; i--) {
|
|
1501
|
+
const entry = previousValues[i];
|
|
1502
|
+
entry.sourceRef.options = entry.options;
|
|
1503
|
+
if (!entry.hasFSM) {
|
|
1504
|
+
delete entry.sourceRef.fsm;
|
|
1505
|
+
} else {
|
|
1506
|
+
entry.sourceRef.fsm = entry.fsm;
|
|
1507
|
+
}
|
|
1508
|
+
}
|
|
1509
|
+
}
|
|
1510
|
+
});
|
|
1511
|
+
}
|
|
1512
|
+
function finalizeEntityCloneSupport(entity, factory) {
|
|
1513
|
+
entity.finalizeCloneSupport(factory);
|
|
1514
|
+
return entity;
|
|
1515
|
+
}
|
|
1123
1516
|
|
|
1124
1517
|
// src/lib/entities/delegates/debug.ts
|
|
1125
1518
|
import { MeshStandardMaterial, MeshBasicMaterial, MeshPhongMaterial } from "three";
|
|
@@ -1222,13 +1615,13 @@ import {
|
|
|
1222
1615
|
CapsuleGeometry,
|
|
1223
1616
|
ConeGeometry,
|
|
1224
1617
|
CylinderGeometry,
|
|
1225
|
-
Mesh as
|
|
1618
|
+
Mesh as Mesh3,
|
|
1226
1619
|
MeshStandardMaterial as MeshStandardMaterial3,
|
|
1227
1620
|
SphereGeometry
|
|
1228
1621
|
} from "three";
|
|
1229
1622
|
|
|
1230
1623
|
// src/lib/graphics/material.ts
|
|
1231
|
-
import { Color as
|
|
1624
|
+
import { Color as Color4, Vector2 as Vector23, Vector3 as Vector38 } from "three";
|
|
1232
1625
|
import {
|
|
1233
1626
|
MeshPhongMaterial as MeshPhongMaterial2,
|
|
1234
1627
|
MeshStandardMaterial as MeshStandardMaterial2,
|
|
@@ -1295,10 +1688,44 @@ var TextureLoaderAdapter = class {
|
|
|
1295
1688
|
|
|
1296
1689
|
// src/lib/core/loaders/gltf-loader.ts
|
|
1297
1690
|
import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
|
|
1691
|
+
import { DRACOLoader } from "three/addons/loaders/DRACOLoader.js";
|
|
1692
|
+
|
|
1693
|
+
// src/lib/core/loaders/model-clone.ts
|
|
1694
|
+
import { Mesh as Mesh2 } from "three";
|
|
1695
|
+
import { clone as cloneSkeleton } from "three/addons/utils/SkeletonUtils.js";
|
|
1696
|
+
function cloneModelObject(object) {
|
|
1697
|
+
const clonedObject = cloneSkeleton(object);
|
|
1698
|
+
const originalNodes = [];
|
|
1699
|
+
const clonedNodes = [];
|
|
1700
|
+
object.traverse((node) => originalNodes.push(node));
|
|
1701
|
+
clonedObject.traverse((node) => clonedNodes.push(node));
|
|
1702
|
+
for (let i = 0; i < originalNodes.length; i++) {
|
|
1703
|
+
const originalNode = originalNodes[i];
|
|
1704
|
+
const clonedNode = clonedNodes[i];
|
|
1705
|
+
if (!(originalNode instanceof Mesh2) || !(clonedNode instanceof Mesh2)) {
|
|
1706
|
+
continue;
|
|
1707
|
+
}
|
|
1708
|
+
if (originalNode.geometry?.clone) {
|
|
1709
|
+
clonedNode.geometry = originalNode.geometry.clone();
|
|
1710
|
+
}
|
|
1711
|
+
if (!originalNode.material) {
|
|
1712
|
+
continue;
|
|
1713
|
+
}
|
|
1714
|
+
clonedNode.material = Array.isArray(originalNode.material) ? originalNode.material.map((material) => material.clone()) : originalNode.material.clone();
|
|
1715
|
+
}
|
|
1716
|
+
return clonedObject;
|
|
1717
|
+
}
|
|
1718
|
+
|
|
1719
|
+
// src/lib/core/loaders/gltf-loader.ts
|
|
1720
|
+
var DRACO_DECODER_PATH = "https://www.gstatic.com/draco/v1/decoders/";
|
|
1298
1721
|
var GLTFLoaderAdapter = class {
|
|
1299
1722
|
loader;
|
|
1723
|
+
dracoLoader;
|
|
1300
1724
|
constructor() {
|
|
1725
|
+
this.dracoLoader = new DRACOLoader();
|
|
1726
|
+
this.dracoLoader.setDecoderPath(DRACO_DECODER_PATH);
|
|
1301
1727
|
this.loader = new GLTFLoader();
|
|
1728
|
+
this.loader.setDRACOLoader(this.dracoLoader);
|
|
1302
1729
|
}
|
|
1303
1730
|
isSupported(url) {
|
|
1304
1731
|
const ext = url.split(".").pop()?.toLowerCase();
|
|
@@ -1357,7 +1784,7 @@ var GLTFLoaderAdapter = class {
|
|
|
1357
1784
|
*/
|
|
1358
1785
|
clone(result) {
|
|
1359
1786
|
return {
|
|
1360
|
-
object: result.object
|
|
1787
|
+
object: cloneModelObject(result.object),
|
|
1361
1788
|
animations: result.animations?.map((anim) => anim.clone()),
|
|
1362
1789
|
gltf: result.gltf
|
|
1363
1790
|
};
|
|
@@ -1425,7 +1852,7 @@ var FBXLoaderAdapter = class {
|
|
|
1425
1852
|
*/
|
|
1426
1853
|
clone(result) {
|
|
1427
1854
|
return {
|
|
1428
|
-
object: result.object
|
|
1855
|
+
object: cloneModelObject(result.object),
|
|
1429
1856
|
animations: result.animations?.map((anim) => anim.clone())
|
|
1430
1857
|
};
|
|
1431
1858
|
}
|
|
@@ -1486,7 +1913,7 @@ var OBJLoaderAdapter = class {
|
|
|
1486
1913
|
*/
|
|
1487
1914
|
clone(result) {
|
|
1488
1915
|
return {
|
|
1489
|
-
object: result.object
|
|
1916
|
+
object: cloneModelObject(result.object),
|
|
1490
1917
|
animations: []
|
|
1491
1918
|
};
|
|
1492
1919
|
}
|
|
@@ -1922,6 +2349,14 @@ function isGLSLShader(shader) {
|
|
|
1922
2349
|
}
|
|
1923
2350
|
var MaterialBuilder = class _MaterialBuilder {
|
|
1924
2351
|
static batchMaterialMap = /* @__PURE__ */ new Map();
|
|
2352
|
+
/**
|
|
2353
|
+
* Clear the static batch material cache.
|
|
2354
|
+
* Should be called during game disposal to prevent stale material references
|
|
2355
|
+
* from persisting across demo/stage switches.
|
|
2356
|
+
*/
|
|
2357
|
+
static clearBatchCache() {
|
|
2358
|
+
_MaterialBuilder.batchMaterialMap.clear();
|
|
2359
|
+
}
|
|
1925
2360
|
materials = [];
|
|
1926
2361
|
/** Whether to use TSL/NodeMaterial (for WebGPU compatibility) */
|
|
1927
2362
|
useTSL;
|
|
@@ -1946,53 +2381,61 @@ var MaterialBuilder = class _MaterialBuilder {
|
|
|
1946
2381
|
}
|
|
1947
2382
|
}
|
|
1948
2383
|
build(options, entityType) {
|
|
1949
|
-
const { path, normalMap, repeat, color, shader, useTSL } = options;
|
|
2384
|
+
const { path, normalMap, repeat, color, shader, opacity, useTSL } = options;
|
|
1950
2385
|
const shouldUseTSL = useTSL ?? this.useTSL;
|
|
1951
2386
|
if (shader) {
|
|
1952
2387
|
if (isTSLShader(shader)) {
|
|
1953
|
-
this.setTSLShader(shader);
|
|
2388
|
+
this.setTSLShader(shader, opacity);
|
|
1954
2389
|
} else if (isGLSLShader(shader)) {
|
|
1955
2390
|
if (shouldUseTSL) {
|
|
1956
2391
|
console.warn("MaterialBuilder: GLSL shader provided but TSL mode requested. Using GLSL.");
|
|
1957
2392
|
}
|
|
1958
|
-
this.setShader(shader);
|
|
2393
|
+
this.setShader(shader, opacity);
|
|
1959
2394
|
}
|
|
1960
2395
|
} else if (path) {
|
|
1961
|
-
this.setTexture(path, repeat, shouldUseTSL);
|
|
2396
|
+
this.setTexture(path, repeat, shouldUseTSL, opacity);
|
|
1962
2397
|
}
|
|
1963
2398
|
if (color) {
|
|
1964
|
-
this.withColor(color, shouldUseTSL);
|
|
2399
|
+
this.withColor(color, shouldUseTSL, opacity);
|
|
1965
2400
|
}
|
|
1966
2401
|
if (this.materials.length === 0) {
|
|
1967
|
-
this.setColor(new
|
|
2402
|
+
this.setColor(new Color4("#ffffff"), shouldUseTSL, opacity);
|
|
1968
2403
|
}
|
|
1969
2404
|
if (normalMap && this.materials.length > 0) {
|
|
1970
2405
|
this.setNormalMap(normalMap, repeat);
|
|
1971
2406
|
}
|
|
1972
2407
|
this.batchMaterial(options, entityType);
|
|
1973
2408
|
}
|
|
1974
|
-
withColor(color, useTSL = false) {
|
|
1975
|
-
this.setColor(color, useTSL);
|
|
2409
|
+
withColor(color, useTSL = false, opacity) {
|
|
2410
|
+
this.setColor(color, useTSL, opacity);
|
|
1976
2411
|
return this;
|
|
1977
2412
|
}
|
|
1978
|
-
withShader(shader) {
|
|
1979
|
-
this.setShader(shader);
|
|
2413
|
+
withShader(shader, opacity) {
|
|
2414
|
+
this.setShader(shader, opacity);
|
|
1980
2415
|
return this;
|
|
1981
2416
|
}
|
|
1982
|
-
withTSLShader(shader) {
|
|
1983
|
-
this.setTSLShader(shader);
|
|
2417
|
+
withTSLShader(shader, opacity) {
|
|
2418
|
+
this.setTSLShader(shader, opacity);
|
|
1984
2419
|
return this;
|
|
1985
2420
|
}
|
|
2421
|
+
applyOpacity(material, opacity, forceTransparent = false) {
|
|
2422
|
+
if (opacity !== void 0) {
|
|
2423
|
+
material.opacity = opacity;
|
|
2424
|
+
}
|
|
2425
|
+
material.transparent = forceTransparent || opacity !== void 0 && opacity < 1;
|
|
2426
|
+
material.needsUpdate = true;
|
|
2427
|
+
}
|
|
1986
2428
|
/**
|
|
1987
2429
|
* Set texture - loads in background (deferred).
|
|
1988
2430
|
* Material is created immediately with null map, texture applies when loaded.
|
|
1989
2431
|
*/
|
|
1990
|
-
setTexture(texturePath = null, repeat = new
|
|
2432
|
+
setTexture(texturePath = null, repeat = new Vector23(1, 1), useTSL = false, opacity) {
|
|
1991
2433
|
if (!texturePath) {
|
|
1992
2434
|
return;
|
|
1993
2435
|
}
|
|
1994
2436
|
if (useTSL) {
|
|
1995
2437
|
const material = new MeshStandardNodeMaterial();
|
|
2438
|
+
this.applyOpacity(material, opacity);
|
|
1996
2439
|
this.materials.push(material);
|
|
1997
2440
|
assetManager.loadTexture(texturePath, {
|
|
1998
2441
|
clone: true,
|
|
@@ -2005,7 +2448,9 @@ var MaterialBuilder = class _MaterialBuilder {
|
|
|
2005
2448
|
});
|
|
2006
2449
|
} else {
|
|
2007
2450
|
const material = new MeshPhongMaterial2({
|
|
2008
|
-
map: null
|
|
2451
|
+
map: null,
|
|
2452
|
+
opacity: opacity ?? 1,
|
|
2453
|
+
transparent: opacity !== void 0 && opacity < 1
|
|
2009
2454
|
});
|
|
2010
2455
|
this.materials.push(material);
|
|
2011
2456
|
assetManager.loadTexture(texturePath, {
|
|
@@ -2022,7 +2467,7 @@ var MaterialBuilder = class _MaterialBuilder {
|
|
|
2022
2467
|
/**
|
|
2023
2468
|
* Set normal map for the current material
|
|
2024
2469
|
*/
|
|
2025
|
-
setNormalMap(normalMapPath, repeat = new
|
|
2470
|
+
setNormalMap(normalMapPath, repeat = new Vector23(1, 1)) {
|
|
2026
2471
|
const material = this.materials[this.materials.length - 1];
|
|
2027
2472
|
if (!material) return;
|
|
2028
2473
|
assetManager.loadTexture(normalMapPath, {
|
|
@@ -2046,17 +2491,20 @@ var MaterialBuilder = class _MaterialBuilder {
|
|
|
2046
2491
|
}
|
|
2047
2492
|
});
|
|
2048
2493
|
}
|
|
2049
|
-
setColor(color, useTSL = false) {
|
|
2494
|
+
setColor(color, useTSL = false, opacity) {
|
|
2050
2495
|
if (useTSL) {
|
|
2051
2496
|
const material = new MeshStandardNodeMaterial();
|
|
2052
2497
|
material.color = color;
|
|
2498
|
+
this.applyOpacity(material, opacity);
|
|
2053
2499
|
this.materials.push(material);
|
|
2054
2500
|
} else {
|
|
2055
2501
|
const material = new MeshStandardMaterial2({
|
|
2056
2502
|
color,
|
|
2057
2503
|
emissiveIntensity: 0.5,
|
|
2058
2504
|
lightMapIntensity: 0.5,
|
|
2059
|
-
fog: true
|
|
2505
|
+
fog: true,
|
|
2506
|
+
opacity: opacity ?? 1,
|
|
2507
|
+
transparent: opacity !== void 0 && opacity < 1
|
|
2060
2508
|
});
|
|
2061
2509
|
this.materials.push(material);
|
|
2062
2510
|
}
|
|
@@ -2064,34 +2512,37 @@ var MaterialBuilder = class _MaterialBuilder {
|
|
|
2064
2512
|
/**
|
|
2065
2513
|
* Set GLSL shader (WebGL only)
|
|
2066
2514
|
*/
|
|
2067
|
-
setShader(customShader) {
|
|
2515
|
+
setShader(customShader, opacity) {
|
|
2068
2516
|
const { fragment: fragment2, vertex } = customShader ?? standardShader;
|
|
2069
2517
|
const shader = new ShaderMaterial2({
|
|
2070
2518
|
uniforms: {
|
|
2071
|
-
iResolution: { value: new
|
|
2519
|
+
iResolution: { value: new Vector38(1, 1, 1) },
|
|
2072
2520
|
iTime: { value: 0 },
|
|
2073
2521
|
tDiffuse: { value: null },
|
|
2074
2522
|
tDepth: { value: null },
|
|
2075
2523
|
tNormal: { value: null },
|
|
2076
2524
|
normalMap: { value: null },
|
|
2077
|
-
lightDir: { value: new
|
|
2525
|
+
lightDir: { value: new Vector38(1, 1, 1) },
|
|
2078
2526
|
normalStrength: { value: 1 }
|
|
2079
2527
|
},
|
|
2080
2528
|
vertexShader: vertex,
|
|
2081
2529
|
fragmentShader: fragment2,
|
|
2082
|
-
transparent: true
|
|
2530
|
+
transparent: true,
|
|
2531
|
+
opacity: opacity ?? 1
|
|
2083
2532
|
});
|
|
2084
2533
|
this.materials.push(shader);
|
|
2085
2534
|
}
|
|
2086
2535
|
/**
|
|
2087
2536
|
* Set TSL shader (WebGPU compatible)
|
|
2088
2537
|
*/
|
|
2089
|
-
setTSLShader(tslShader) {
|
|
2538
|
+
setTSLShader(tslShader, opacity) {
|
|
2090
2539
|
const material = new MeshBasicNodeMaterial();
|
|
2091
2540
|
material.colorNode = tslShader.colorNode;
|
|
2092
|
-
|
|
2093
|
-
material
|
|
2094
|
-
|
|
2541
|
+
this.applyOpacity(
|
|
2542
|
+
material,
|
|
2543
|
+
opacity,
|
|
2544
|
+
tslShader.transparent
|
|
2545
|
+
);
|
|
2095
2546
|
this.materials.push(material);
|
|
2096
2547
|
}
|
|
2097
2548
|
};
|
|
@@ -2122,12 +2573,12 @@ function boxMesh(opts = {}) {
|
|
|
2122
2573
|
const size = opts.size ?? { x: 1, y: 1, z: 1 };
|
|
2123
2574
|
const geometry = new BoxGeometry(size.x, size.y, size.z);
|
|
2124
2575
|
const materials = buildMaterial(opts);
|
|
2125
|
-
return finalizeMesh(new
|
|
2576
|
+
return finalizeMesh(new Mesh3(geometry, materials.at(-1)), opts);
|
|
2126
2577
|
}
|
|
2127
2578
|
function sphereMesh(opts = {}) {
|
|
2128
2579
|
const geometry = new SphereGeometry(opts.radius ?? 1);
|
|
2129
2580
|
const materials = buildMaterial(opts);
|
|
2130
|
-
return finalizeMesh(new
|
|
2581
|
+
return finalizeMesh(new Mesh3(geometry, materials.at(-1)), opts);
|
|
2131
2582
|
}
|
|
2132
2583
|
function coneMesh(opts = {}) {
|
|
2133
2584
|
const geometry = new ConeGeometry(
|
|
@@ -2136,12 +2587,12 @@ function coneMesh(opts = {}) {
|
|
|
2136
2587
|
opts.radialSegments ?? 32
|
|
2137
2588
|
);
|
|
2138
2589
|
const materials = buildMaterial(opts);
|
|
2139
|
-
return finalizeMesh(new
|
|
2590
|
+
return finalizeMesh(new Mesh3(geometry, materials.at(-1)), opts);
|
|
2140
2591
|
}
|
|
2141
2592
|
function pyramidMesh(opts = {}) {
|
|
2142
2593
|
const geometry = new ConeGeometry(opts.radius ?? 1, opts.height ?? 2, 4);
|
|
2143
2594
|
const materials = buildMaterial(opts);
|
|
2144
|
-
return finalizeMesh(new
|
|
2595
|
+
return finalizeMesh(new Mesh3(geometry, materials.at(-1)), opts);
|
|
2145
2596
|
}
|
|
2146
2597
|
function cylinderMesh(opts = {}) {
|
|
2147
2598
|
const geometry = new CylinderGeometry(
|
|
@@ -2151,7 +2602,7 @@ function cylinderMesh(opts = {}) {
|
|
|
2151
2602
|
opts.radialSegments ?? 32
|
|
2152
2603
|
);
|
|
2153
2604
|
const materials = buildMaterial(opts);
|
|
2154
|
-
return finalizeMesh(new
|
|
2605
|
+
return finalizeMesh(new Mesh3(geometry, materials.at(-1)), opts);
|
|
2155
2606
|
}
|
|
2156
2607
|
function pillMesh(opts = {}) {
|
|
2157
2608
|
const geometry = new CapsuleGeometry(
|
|
@@ -2161,13 +2612,13 @@ function pillMesh(opts = {}) {
|
|
|
2161
2612
|
opts.radialSegments ?? 20
|
|
2162
2613
|
);
|
|
2163
2614
|
const materials = buildMaterial(opts);
|
|
2164
|
-
return finalizeMesh(new
|
|
2615
|
+
return finalizeMesh(new Mesh3(geometry, materials.at(-1)), opts);
|
|
2165
2616
|
}
|
|
2166
2617
|
|
|
2167
2618
|
// src/lib/entities/box.ts
|
|
2168
2619
|
var boxDefaults = {
|
|
2169
2620
|
...commonDefaults,
|
|
2170
|
-
size: new
|
|
2621
|
+
size: new Vector39(1, 1, 1)
|
|
2171
2622
|
};
|
|
2172
2623
|
var BOX_TYPE = /* @__PURE__ */ Symbol("Box");
|
|
2173
2624
|
var ZylemBox = class _ZylemBox extends GameEntity {
|
|
@@ -2200,7 +2651,10 @@ function createBox(...args) {
|
|
|
2200
2651
|
collisionFilter: options.collisionFilter
|
|
2201
2652
|
})
|
|
2202
2653
|
);
|
|
2203
|
-
return
|
|
2654
|
+
return finalizeEntityCloneSupport(
|
|
2655
|
+
entity,
|
|
2656
|
+
(cloneOptions) => createBox(cloneOptions ?? {})
|
|
2657
|
+
);
|
|
2204
2658
|
}
|
|
2205
2659
|
|
|
2206
2660
|
// src/lib/entities/sphere.ts
|
|
@@ -2239,12 +2693,15 @@ function createSphere(...args) {
|
|
|
2239
2693
|
collisionFilter: options.collisionFilter
|
|
2240
2694
|
})
|
|
2241
2695
|
);
|
|
2242
|
-
return
|
|
2696
|
+
return finalizeEntityCloneSupport(
|
|
2697
|
+
entity,
|
|
2698
|
+
(cloneOptions) => createSphere(cloneOptions ?? {})
|
|
2699
|
+
);
|
|
2243
2700
|
}
|
|
2244
2701
|
|
|
2245
2702
|
// src/lib/entities/sprite.ts
|
|
2246
2703
|
import { ColliderDesc as ColliderDesc3 } from "@dimforge/rapier3d-compat";
|
|
2247
|
-
import { Euler as Euler2, Group as Group3, Quaternion as Quaternion3, Vector3 as
|
|
2704
|
+
import { Euler as Euler2, Group as Group3, Quaternion as Quaternion3, Vector3 as Vector310 } from "three";
|
|
2248
2705
|
import {
|
|
2249
2706
|
TextureLoader as TextureLoader2,
|
|
2250
2707
|
SpriteMaterial,
|
|
@@ -2252,17 +2709,17 @@ import {
|
|
|
2252
2709
|
} from "three";
|
|
2253
2710
|
|
|
2254
2711
|
// src/lib/entities/builder.ts
|
|
2255
|
-
import { BufferGeometry as BufferGeometry2, Mesh as
|
|
2712
|
+
import { BufferGeometry as BufferGeometry2, Mesh as Mesh5, Color as Color5 } from "three";
|
|
2256
2713
|
|
|
2257
2714
|
// src/lib/graphics/mesh.ts
|
|
2258
|
-
import { Mesh as
|
|
2715
|
+
import { Mesh as Mesh4 } from "three";
|
|
2259
2716
|
var MeshBuilder = class {
|
|
2260
2717
|
_build(meshOptions, geometry, materials) {
|
|
2261
2718
|
const { batched, material } = meshOptions;
|
|
2262
2719
|
if (batched) {
|
|
2263
2720
|
console.warn("warning: mesh batching is not implemented");
|
|
2264
2721
|
}
|
|
2265
|
-
const mesh = new
|
|
2722
|
+
const mesh = new Mesh4(geometry, materials.at(-1));
|
|
2266
2723
|
mesh.position.set(0, 0, 0);
|
|
2267
2724
|
mesh.castShadow = true;
|
|
2268
2725
|
mesh.receiveShadow = true;
|
|
@@ -2315,7 +2772,7 @@ var EntityBuilder = class {
|
|
|
2315
2772
|
}
|
|
2316
2773
|
applyMaterialToGroup(group, materials) {
|
|
2317
2774
|
group.traverse((child) => {
|
|
2318
|
-
if (child instanceof
|
|
2775
|
+
if (child instanceof Mesh5) {
|
|
2319
2776
|
if (child.type === "SkinnedMesh" && materials[0] && !child.material.map) {
|
|
2320
2777
|
child.material = materials[0];
|
|
2321
2778
|
}
|
|
@@ -2349,7 +2806,7 @@ var EntityBuilder = class {
|
|
|
2349
2806
|
if (this.options.collisionType) {
|
|
2350
2807
|
entity.collisionType = this.options.collisionType;
|
|
2351
2808
|
}
|
|
2352
|
-
if (this.options.color instanceof
|
|
2809
|
+
if (this.options.color instanceof Color5) {
|
|
2353
2810
|
const applyColor = (material) => {
|
|
2354
2811
|
const anyMat = material;
|
|
2355
2812
|
if (anyMat && anyMat.color && anyMat.color.set) {
|
|
@@ -2366,7 +2823,7 @@ var EntityBuilder = class {
|
|
|
2366
2823
|
}
|
|
2367
2824
|
if (entity.group) {
|
|
2368
2825
|
entity.group.traverse((child) => {
|
|
2369
|
-
if (child instanceof
|
|
2826
|
+
if (child instanceof Mesh5 && child.material) {
|
|
2370
2827
|
const mat = child.material;
|
|
2371
2828
|
if (Array.isArray(mat)) mat.forEach(applyColor);
|
|
2372
2829
|
else applyColor(mat);
|
|
@@ -2409,7 +2866,8 @@ function createEntity(params) {
|
|
|
2409
2866
|
BuilderClass,
|
|
2410
2867
|
entityType,
|
|
2411
2868
|
MeshBuilderClass,
|
|
2412
|
-
CollisionBuilderClass
|
|
2869
|
+
CollisionBuilderClass,
|
|
2870
|
+
cloneFactory
|
|
2413
2871
|
} = params;
|
|
2414
2872
|
let builder = null;
|
|
2415
2873
|
let configuration;
|
|
@@ -2448,19 +2906,22 @@ function createEntity(params) {
|
|
|
2448
2906
|
if (!builder) {
|
|
2449
2907
|
throw new Error(`missing options for ${String(entityType)}, builder is not initialized.`);
|
|
2450
2908
|
}
|
|
2451
|
-
return
|
|
2909
|
+
return finalizeEntityCloneSupport(
|
|
2910
|
+
builder.build(),
|
|
2911
|
+
(options) => cloneFactory(options)
|
|
2912
|
+
);
|
|
2452
2913
|
}
|
|
2453
2914
|
|
|
2454
2915
|
// src/lib/entities/sprite.ts
|
|
2455
2916
|
var spriteDefaults = {
|
|
2456
2917
|
...commonDefaults,
|
|
2457
|
-
size: new
|
|
2918
|
+
size: new Vector310(1, 1, 1),
|
|
2458
2919
|
images: [],
|
|
2459
2920
|
animations: []
|
|
2460
2921
|
};
|
|
2461
2922
|
var SpriteCollisionBuilder = class extends EntityCollisionBuilder {
|
|
2462
2923
|
collider(options) {
|
|
2463
|
-
const size = options.collisionSize || options.size || new
|
|
2924
|
+
const size = options.collisionSize || options.size || new Vector310(1, 1, 1);
|
|
2464
2925
|
const half = { x: size.x / 2, y: size.y / 2, z: size.z / 2 };
|
|
2465
2926
|
let colliderDesc = ColliderDesc3.cuboid(half.x, half.y, half.z);
|
|
2466
2927
|
return colliderDesc;
|
|
@@ -2485,6 +2946,7 @@ var ZylemSprite = class _ZylemSprite extends GameEntity {
|
|
|
2485
2946
|
constructor(options) {
|
|
2486
2947
|
super();
|
|
2487
2948
|
this.options = { ...spriteDefaults, ...options };
|
|
2949
|
+
this.prependSetup(this.spriteSetup.bind(this));
|
|
2488
2950
|
this.prependUpdate(this.spriteUpdate.bind(this));
|
|
2489
2951
|
this.onCleanup(this.spriteDestroy.bind(this));
|
|
2490
2952
|
}
|
|
@@ -2503,6 +2965,7 @@ var ZylemSprite = class _ZylemSprite extends GameEntity {
|
|
|
2503
2965
|
}
|
|
2504
2966
|
createSpritesFromImages(images) {
|
|
2505
2967
|
const textureLoader = new TextureLoader2();
|
|
2968
|
+
const size = this.options.size ?? new Vector310(1, 1, 1);
|
|
2506
2969
|
images.forEach((image, index) => {
|
|
2507
2970
|
const spriteMap = textureLoader.load(image.file);
|
|
2508
2971
|
const material = new SpriteMaterial({
|
|
@@ -2511,6 +2974,8 @@ var ZylemSprite = class _ZylemSprite extends GameEntity {
|
|
|
2511
2974
|
});
|
|
2512
2975
|
const _sprite = new ThreeSprite(material);
|
|
2513
2976
|
_sprite.position.normalize();
|
|
2977
|
+
_sprite.scale.set(size.x, size.y, size.z);
|
|
2978
|
+
_sprite.visible = index === 0;
|
|
2514
2979
|
this.sprites.push(_sprite);
|
|
2515
2980
|
this.spriteMap.set(image.name, index);
|
|
2516
2981
|
});
|
|
@@ -2564,19 +3029,29 @@ var ZylemSprite = class _ZylemSprite extends GameEntity {
|
|
|
2564
3029
|
}
|
|
2565
3030
|
}
|
|
2566
3031
|
}
|
|
2567
|
-
|
|
3032
|
+
getCurrentRotationQuaternion() {
|
|
3033
|
+
if (this.transformStore?.dirty.rotation) {
|
|
3034
|
+
return this.transformStore.rotation;
|
|
3035
|
+
}
|
|
3036
|
+
return this.body?.rotation?.() ?? null;
|
|
3037
|
+
}
|
|
3038
|
+
syncSpriteMaterials() {
|
|
3039
|
+
const q = this.getCurrentRotationQuaternion();
|
|
2568
3040
|
this.sprites.forEach((_sprite) => {
|
|
2569
|
-
if (_sprite.material) {
|
|
2570
|
-
const
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
const euler = new Euler2().setFromQuaternion(quat, "XYZ");
|
|
2574
|
-
_sprite.material.rotation = euler.z;
|
|
2575
|
-
}
|
|
2576
|
-
_sprite.scale.set(this.options.size?.x ?? 1, this.options.size?.y ?? 1, this.options.size?.z ?? 1);
|
|
3041
|
+
if (_sprite.material && q) {
|
|
3042
|
+
const quat = new Quaternion3(q.x, q.y, q.z, q.w);
|
|
3043
|
+
const euler = new Euler2().setFromQuaternion(quat, "XYZ");
|
|
3044
|
+
_sprite.material.rotation = euler.z;
|
|
2577
3045
|
}
|
|
3046
|
+
_sprite.scale.set(this.options.size?.x ?? 1, this.options.size?.y ?? 1, this.options.size?.z ?? 1);
|
|
2578
3047
|
});
|
|
2579
3048
|
}
|
|
3049
|
+
spriteSetup(_params) {
|
|
3050
|
+
this.syncSpriteMaterials();
|
|
3051
|
+
}
|
|
3052
|
+
spriteUpdate(params) {
|
|
3053
|
+
this.syncSpriteMaterials();
|
|
3054
|
+
}
|
|
2580
3055
|
spriteDestroy(params) {
|
|
2581
3056
|
this.sprites.forEach((_sprite) => {
|
|
2582
3057
|
_sprite.removeFromParent();
|
|
@@ -2600,13 +3075,14 @@ function createSprite(...args) {
|
|
|
2600
3075
|
EntityClass: ZylemSprite,
|
|
2601
3076
|
BuilderClass: SpriteBuilder,
|
|
2602
3077
|
CollisionBuilderClass: SpriteCollisionBuilder,
|
|
2603
|
-
entityType: ZylemSprite.type
|
|
3078
|
+
entityType: ZylemSprite.type,
|
|
3079
|
+
cloneFactory: (options) => createSprite(options ?? {})
|
|
2604
3080
|
});
|
|
2605
3081
|
}
|
|
2606
3082
|
|
|
2607
3083
|
// src/lib/entities/plane.ts
|
|
2608
3084
|
import { ColliderDesc as ColliderDesc4 } from "@dimforge/rapier3d-compat";
|
|
2609
|
-
import { PlaneGeometry, Vector2 as
|
|
3085
|
+
import { PlaneGeometry, Vector2 as Vector24, Vector3 as Vector311 } from "three";
|
|
2610
3086
|
|
|
2611
3087
|
// src/lib/graphics/geometries/XZPlaneGeometry.ts
|
|
2612
3088
|
import { BufferGeometry as BufferGeometry3, Float32BufferAttribute } from "three";
|
|
@@ -2671,8 +3147,8 @@ var XZPlaneGeometry = class _XZPlaneGeometry extends BufferGeometry3 {
|
|
|
2671
3147
|
var DEFAULT_SUBDIVISIONS = 4;
|
|
2672
3148
|
var planeDefaults = {
|
|
2673
3149
|
...commonDefaults,
|
|
2674
|
-
tile: new
|
|
2675
|
-
repeat: new
|
|
3150
|
+
tile: new Vector24(10, 10),
|
|
3151
|
+
repeat: new Vector24(1, 1),
|
|
2676
3152
|
collision: {
|
|
2677
3153
|
static: true
|
|
2678
3154
|
},
|
|
@@ -2682,11 +3158,11 @@ var planeDefaults = {
|
|
|
2682
3158
|
};
|
|
2683
3159
|
var PlaneCollisionBuilder = class extends EntityCollisionBuilder {
|
|
2684
3160
|
collider(options) {
|
|
2685
|
-
const tile = options.tile ?? new
|
|
3161
|
+
const tile = options.tile ?? new Vector24(1, 1);
|
|
2686
3162
|
const subdivisions = options.subdivisions ?? DEFAULT_SUBDIVISIONS;
|
|
2687
|
-
const size = new
|
|
3163
|
+
const size = new Vector311(tile.x, 1, tile.y);
|
|
2688
3164
|
const heightData = options._builders?.meshBuilder?.heightData;
|
|
2689
|
-
const scale2 = new
|
|
3165
|
+
const scale2 = new Vector311(size.x, 1, size.z);
|
|
2690
3166
|
let colliderDesc = ColliderDesc4.heightfield(
|
|
2691
3167
|
subdivisions,
|
|
2692
3168
|
subdivisions,
|
|
@@ -2701,10 +3177,10 @@ var PlaneMeshBuilder = class extends EntityMeshBuilder {
|
|
|
2701
3177
|
columnsRows = /* @__PURE__ */ new Map();
|
|
2702
3178
|
subdivisions = DEFAULT_SUBDIVISIONS;
|
|
2703
3179
|
build(options) {
|
|
2704
|
-
const tile = options.tile ?? new
|
|
3180
|
+
const tile = options.tile ?? new Vector24(1, 1);
|
|
2705
3181
|
const subdivisions = options.subdivisions ?? DEFAULT_SUBDIVISIONS;
|
|
2706
3182
|
this.subdivisions = subdivisions;
|
|
2707
|
-
const size = new
|
|
3183
|
+
const size = new Vector311(tile.x, 1, tile.y);
|
|
2708
3184
|
const heightScale = options.heightScale ?? 1;
|
|
2709
3185
|
const geometry = new XZPlaneGeometry(size.x, size.z, subdivisions, subdivisions);
|
|
2710
3186
|
const vertexGeometry = new PlaneGeometry(size.x, size.z, subdivisions, subdivisions);
|
|
@@ -2773,12 +3249,13 @@ function createPlane(...args) {
|
|
|
2773
3249
|
BuilderClass: PlaneBuilder,
|
|
2774
3250
|
MeshBuilderClass: PlaneMeshBuilder,
|
|
2775
3251
|
CollisionBuilderClass: PlaneCollisionBuilder,
|
|
2776
|
-
entityType: ZylemPlane.type
|
|
3252
|
+
entityType: ZylemPlane.type,
|
|
3253
|
+
cloneFactory: (options) => createPlane(options ?? {})
|
|
2777
3254
|
});
|
|
2778
3255
|
}
|
|
2779
3256
|
|
|
2780
3257
|
// src/lib/entities/zone.ts
|
|
2781
|
-
import { Vector3 as
|
|
3258
|
+
import { Vector3 as Vector312 } from "three";
|
|
2782
3259
|
|
|
2783
3260
|
// src/lib/game/game-state.ts
|
|
2784
3261
|
import { proxy as proxy2, subscribe } from "valtio/vanilla";
|
|
@@ -2791,7 +3268,7 @@ var state = proxy2({
|
|
|
2791
3268
|
// src/lib/entities/zone.ts
|
|
2792
3269
|
var zoneDefaults = {
|
|
2793
3270
|
...commonDefaults,
|
|
2794
|
-
size: new
|
|
3271
|
+
size: new Vector312(1, 1, 1),
|
|
2795
3272
|
collision: {
|
|
2796
3273
|
static: true
|
|
2797
3274
|
}
|
|
@@ -2886,23 +3363,34 @@ function createZone(...args) {
|
|
|
2886
3363
|
collisionFilter: options.collisionFilter
|
|
2887
3364
|
})
|
|
2888
3365
|
);
|
|
2889
|
-
return
|
|
3366
|
+
return finalizeEntityCloneSupport(
|
|
3367
|
+
entity,
|
|
3368
|
+
(cloneOptions) => createZone(cloneOptions ?? {})
|
|
3369
|
+
);
|
|
2890
3370
|
}
|
|
2891
3371
|
|
|
2892
3372
|
// src/lib/entities/actor.ts
|
|
2893
3373
|
import { ActiveCollisionTypes as ActiveCollisionTypes3, ColliderDesc as ColliderDesc5 } from "@dimforge/rapier3d-compat";
|
|
2894
|
-
import {
|
|
3374
|
+
import {
|
|
3375
|
+
Box3,
|
|
3376
|
+
BufferAttribute,
|
|
3377
|
+
SkinnedMesh,
|
|
3378
|
+
MeshStandardMaterial as MeshStandardMaterial4,
|
|
3379
|
+
Group as Group4,
|
|
3380
|
+
Vector3 as Vector313,
|
|
3381
|
+
Matrix4
|
|
3382
|
+
} from "three";
|
|
2895
3383
|
|
|
2896
3384
|
// src/lib/core/entity-asset-loader.ts
|
|
2897
3385
|
var EntityAssetLoader = class {
|
|
2898
3386
|
/**
|
|
2899
3387
|
* Load a model file (FBX, GLTF, GLB, OBJ) using the asset manager
|
|
2900
3388
|
*/
|
|
2901
|
-
async loadFile(file) {
|
|
3389
|
+
async loadFile(file, options) {
|
|
2902
3390
|
const ext = file.split(".").pop()?.toLowerCase();
|
|
2903
3391
|
switch (ext) {
|
|
2904
3392
|
case "fbx": {
|
|
2905
|
-
const result = await assetManager.loadFBX(file);
|
|
3393
|
+
const result = await assetManager.loadFBX(file, options);
|
|
2906
3394
|
return {
|
|
2907
3395
|
object: result.object,
|
|
2908
3396
|
animation: result.animations?.[0]
|
|
@@ -2910,14 +3398,14 @@ var EntityAssetLoader = class {
|
|
|
2910
3398
|
}
|
|
2911
3399
|
case "gltf":
|
|
2912
3400
|
case "glb": {
|
|
2913
|
-
const result = await assetManager.loadGLTF(file);
|
|
3401
|
+
const result = await assetManager.loadGLTF(file, options);
|
|
2914
3402
|
return {
|
|
2915
3403
|
object: result.object,
|
|
2916
3404
|
gltf: result.gltf
|
|
2917
3405
|
};
|
|
2918
3406
|
}
|
|
2919
3407
|
case "obj": {
|
|
2920
|
-
const result = await assetManager.loadOBJ(file);
|
|
3408
|
+
const result = await assetManager.loadOBJ(file, options);
|
|
2921
3409
|
return {
|
|
2922
3410
|
object: result.object
|
|
2923
3411
|
};
|
|
@@ -2931,9 +3419,42 @@ var EntityAssetLoader = class {
|
|
|
2931
3419
|
// src/lib/entities/delegates/animation.ts
|
|
2932
3420
|
import {
|
|
2933
3421
|
AnimationMixer,
|
|
3422
|
+
Bone,
|
|
2934
3423
|
LoopOnce,
|
|
2935
3424
|
LoopRepeat
|
|
2936
3425
|
} from "three";
|
|
3426
|
+
function getTrackTargetName(trackName) {
|
|
3427
|
+
const propertyIndex = trackName.lastIndexOf(".");
|
|
3428
|
+
return propertyIndex >= 0 ? trackName.slice(0, propertyIndex) : trackName;
|
|
3429
|
+
}
|
|
3430
|
+
function shouldStripRootMotion(track, targetByName) {
|
|
3431
|
+
if (!track?.name?.endsWith(".position")) {
|
|
3432
|
+
return false;
|
|
3433
|
+
}
|
|
3434
|
+
const targetName = getTrackTargetName(track.name);
|
|
3435
|
+
const target = targetByName.get(targetName);
|
|
3436
|
+
if (target instanceof Bone) {
|
|
3437
|
+
return !(target.parent instanceof Bone);
|
|
3438
|
+
}
|
|
3439
|
+
return /hips\.position$/i.test(track.name) || /root\.position$/i.test(track.name);
|
|
3440
|
+
}
|
|
3441
|
+
function stripClipRootMotion(clip, targetByName) {
|
|
3442
|
+
for (const track of clip.tracks) {
|
|
3443
|
+
if (!shouldStripRootMotion(track, targetByName)) {
|
|
3444
|
+
continue;
|
|
3445
|
+
}
|
|
3446
|
+
if (!track.values || track.values.length < 3) {
|
|
3447
|
+
continue;
|
|
3448
|
+
}
|
|
3449
|
+
const baseX = track.values[0];
|
|
3450
|
+
const baseZ = track.values[2];
|
|
3451
|
+
for (let i = 0; i < track.values.length; i += 3) {
|
|
3452
|
+
track.values[i] = baseX;
|
|
3453
|
+
track.values[i + 2] = baseZ;
|
|
3454
|
+
}
|
|
3455
|
+
}
|
|
3456
|
+
return clip;
|
|
3457
|
+
}
|
|
2937
3458
|
var AnimationDelegate = class {
|
|
2938
3459
|
constructor(target) {
|
|
2939
3460
|
this.target = target;
|
|
@@ -2951,12 +3472,18 @@ var AnimationDelegate = class {
|
|
|
2951
3472
|
async loadAnimations(animations) {
|
|
2952
3473
|
if (!animations.length) return;
|
|
2953
3474
|
const results = await Promise.all(animations.map((a) => this._assetLoader.loadFile(a.path)));
|
|
3475
|
+
const targetByName = /* @__PURE__ */ new Map();
|
|
3476
|
+
this.target.traverse((node) => {
|
|
3477
|
+
if (node.name) {
|
|
3478
|
+
targetByName.set(node.name, node);
|
|
3479
|
+
}
|
|
3480
|
+
});
|
|
2954
3481
|
const loadedAnimations = [];
|
|
2955
3482
|
results.forEach((result, i) => {
|
|
2956
3483
|
if (result.animation) {
|
|
2957
3484
|
loadedAnimations.push({
|
|
2958
3485
|
key: animations[i].key || i.toString(),
|
|
2959
|
-
clip: result.animation
|
|
3486
|
+
clip: stripClipRootMotion(result.animation, targetByName)
|
|
2960
3487
|
});
|
|
2961
3488
|
}
|
|
2962
3489
|
});
|
|
@@ -3039,11 +3566,6 @@ var AnimationDelegate = class {
|
|
|
3039
3566
|
// src/lib/entities/actor.ts
|
|
3040
3567
|
var actorDefaults = {
|
|
3041
3568
|
...commonDefaults,
|
|
3042
|
-
collision: {
|
|
3043
|
-
static: false,
|
|
3044
|
-
size: new Vector312(0.5, 0.5, 0.5),
|
|
3045
|
-
position: new Vector312(0, 0, 0)
|
|
3046
|
-
},
|
|
3047
3569
|
material: {
|
|
3048
3570
|
shader: standardShader
|
|
3049
3571
|
},
|
|
@@ -3051,74 +3573,333 @@ var actorDefaults = {
|
|
|
3051
3573
|
models: [],
|
|
3052
3574
|
collisionShape: "capsule"
|
|
3053
3575
|
};
|
|
3576
|
+
function getActorScale(options) {
|
|
3577
|
+
return new Vector313(
|
|
3578
|
+
options.scale?.x ?? 1,
|
|
3579
|
+
options.scale?.y ?? 1,
|
|
3580
|
+
options.scale?.z ?? 1
|
|
3581
|
+
);
|
|
3582
|
+
}
|
|
3583
|
+
function getCollisionPosition(options) {
|
|
3584
|
+
const position2 = options.collision?.position;
|
|
3585
|
+
if (!position2) {
|
|
3586
|
+
return null;
|
|
3587
|
+
}
|
|
3588
|
+
return {
|
|
3589
|
+
x: position2.x ?? 0,
|
|
3590
|
+
y: position2.y ?? 0,
|
|
3591
|
+
z: position2.z ?? 0
|
|
3592
|
+
};
|
|
3593
|
+
}
|
|
3594
|
+
function hasExplicitCollisionSize(options) {
|
|
3595
|
+
return Boolean(options.collision?.size);
|
|
3596
|
+
}
|
|
3597
|
+
function warnDynamicTrimeshOnce(actor) {
|
|
3598
|
+
if (actor.hasWarnedDynamicTrimesh) {
|
|
3599
|
+
return;
|
|
3600
|
+
}
|
|
3601
|
+
actor.hasWarnedDynamicTrimesh = true;
|
|
3602
|
+
console.warn(
|
|
3603
|
+
`Actor "${actor.name || actor.uuid}" requested collisionShape="trimesh" on a dynamic body; falling back to bounds.`
|
|
3604
|
+
);
|
|
3605
|
+
}
|
|
3606
|
+
function resolveCollisionShape(shape, isStatic, onDynamicTrimesh) {
|
|
3607
|
+
const normalized = shape === "model" ? "bounds" : shape ?? "capsule";
|
|
3608
|
+
if (normalized === "trimesh" && !isStatic) {
|
|
3609
|
+
onDynamicTrimesh?.();
|
|
3610
|
+
return "bounds";
|
|
3611
|
+
}
|
|
3612
|
+
return normalized;
|
|
3613
|
+
}
|
|
3614
|
+
function tagTrimeshCollider(colliderDesc, vertices, indices) {
|
|
3615
|
+
colliderDesc.__zylemShapeData = {
|
|
3616
|
+
shape: "trimesh",
|
|
3617
|
+
vertices: Array.from(vertices),
|
|
3618
|
+
indices: Array.from(indices)
|
|
3619
|
+
};
|
|
3620
|
+
return colliderDesc;
|
|
3621
|
+
}
|
|
3622
|
+
function createCapsuleColliderFromDimensions(size, translation) {
|
|
3623
|
+
const fullWidth = size.x || 0.5;
|
|
3624
|
+
const fullHeight = size.y || 1;
|
|
3625
|
+
const fullDepth = size.z || 0.5;
|
|
3626
|
+
const radius = Math.max(fullWidth, fullDepth) / 2;
|
|
3627
|
+
const halfTotalHeight = fullHeight / 2;
|
|
3628
|
+
const halfCylinder = Math.max(0, halfTotalHeight - radius);
|
|
3629
|
+
const colliderDesc = ColliderDesc5.capsule(halfCylinder, radius);
|
|
3630
|
+
colliderDesc.setSensor(false);
|
|
3631
|
+
colliderDesc.setTranslation(translation.x, translation.y, translation.z);
|
|
3632
|
+
colliderDesc.activeCollisionTypes = ActiveCollisionTypes3.DEFAULT;
|
|
3633
|
+
return colliderDesc;
|
|
3634
|
+
}
|
|
3635
|
+
function createCapsuleCollider(options) {
|
|
3636
|
+
const size = options.collision?.size ?? options.size ?? { x: 0.5, y: 1, z: 0.5 };
|
|
3637
|
+
const fullHeight = size.y || 1;
|
|
3638
|
+
const collisionPosition = getCollisionPosition(options);
|
|
3639
|
+
return createCapsuleColliderFromDimensions(
|
|
3640
|
+
{
|
|
3641
|
+
x: size.x || 0.5,
|
|
3642
|
+
y: fullHeight,
|
|
3643
|
+
z: size.z || 0.5
|
|
3644
|
+
},
|
|
3645
|
+
collisionPosition ?? { x: 0, y: fullHeight / 2, z: 0 }
|
|
3646
|
+
);
|
|
3647
|
+
}
|
|
3648
|
+
function createExplicitBoxCollider(options) {
|
|
3649
|
+
const collisionSize = options.collision?.size;
|
|
3650
|
+
if (!collisionSize) {
|
|
3651
|
+
return null;
|
|
3652
|
+
}
|
|
3653
|
+
const halfWidth = collisionSize.x / 2;
|
|
3654
|
+
const halfHeight = collisionSize.y / 2;
|
|
3655
|
+
const halfDepth = collisionSize.z / 2;
|
|
3656
|
+
const colliderDesc = ColliderDesc5.cuboid(halfWidth, halfHeight, halfDepth);
|
|
3657
|
+
const collisionPosition = getCollisionPosition(options);
|
|
3658
|
+
colliderDesc.setSensor(false);
|
|
3659
|
+
colliderDesc.setTranslation(
|
|
3660
|
+
collisionPosition?.x ?? 0,
|
|
3661
|
+
collisionPosition?.y ?? halfHeight,
|
|
3662
|
+
collisionPosition?.z ?? 0
|
|
3663
|
+
);
|
|
3664
|
+
colliderDesc.activeCollisionTypes = ActiveCollisionTypes3.DEFAULT;
|
|
3665
|
+
return colliderDesc;
|
|
3666
|
+
}
|
|
3667
|
+
function computeActorSpaceMatrix(child, actorRoot, actorScale) {
|
|
3668
|
+
const actorRootInverse = actorRoot.matrixWorld.clone().invert();
|
|
3669
|
+
const actorSpaceMatrix = actorRootInverse.multiply(child.matrixWorld);
|
|
3670
|
+
return new Matrix4().makeScale(actorScale.x, actorScale.y, actorScale.z).multiply(actorSpaceMatrix);
|
|
3671
|
+
}
|
|
3672
|
+
function computeModelBounds(modelRoot, actorRoot, actorScale) {
|
|
3673
|
+
modelRoot.updateMatrixWorld(true);
|
|
3674
|
+
let foundMesh = false;
|
|
3675
|
+
const aggregated = new Box3();
|
|
3676
|
+
modelRoot.traverse((child) => {
|
|
3677
|
+
if (!child.isMesh) {
|
|
3678
|
+
return;
|
|
3679
|
+
}
|
|
3680
|
+
const mesh = child;
|
|
3681
|
+
const geometry = mesh.geometry;
|
|
3682
|
+
if (!geometry) {
|
|
3683
|
+
return;
|
|
3684
|
+
}
|
|
3685
|
+
let localBounds = null;
|
|
3686
|
+
if (mesh instanceof SkinnedMesh) {
|
|
3687
|
+
mesh.computeBoundingBox();
|
|
3688
|
+
localBounds = mesh.boundingBox?.clone() ?? null;
|
|
3689
|
+
} else {
|
|
3690
|
+
geometry.computeBoundingBox();
|
|
3691
|
+
localBounds = geometry.boundingBox?.clone() ?? null;
|
|
3692
|
+
}
|
|
3693
|
+
if (!localBounds) {
|
|
3694
|
+
return;
|
|
3695
|
+
}
|
|
3696
|
+
const transformedBounds = localBounds.applyMatrix4(computeActorSpaceMatrix(mesh, actorRoot, actorScale));
|
|
3697
|
+
if (!foundMesh) {
|
|
3698
|
+
aggregated.copy(transformedBounds);
|
|
3699
|
+
foundMesh = true;
|
|
3700
|
+
return;
|
|
3701
|
+
}
|
|
3702
|
+
aggregated.union(transformedBounds);
|
|
3703
|
+
});
|
|
3704
|
+
if (!foundMesh || aggregated.isEmpty()) {
|
|
3705
|
+
return null;
|
|
3706
|
+
}
|
|
3707
|
+
const size = new Vector313();
|
|
3708
|
+
const center = new Vector313();
|
|
3709
|
+
aggregated.getSize(size);
|
|
3710
|
+
aggregated.getCenter(center);
|
|
3711
|
+
return { size, center };
|
|
3712
|
+
}
|
|
3713
|
+
function computeModelTrimesh(modelRoot, actorRoot, actorScale) {
|
|
3714
|
+
modelRoot.updateMatrixWorld(true);
|
|
3715
|
+
const vertices = [];
|
|
3716
|
+
const indices = [];
|
|
3717
|
+
const bounds = new Box3();
|
|
3718
|
+
const point = new Vector313();
|
|
3719
|
+
let foundMesh = false;
|
|
3720
|
+
modelRoot.traverse((child) => {
|
|
3721
|
+
if (!child.isMesh) {
|
|
3722
|
+
return;
|
|
3723
|
+
}
|
|
3724
|
+
const mesh = child;
|
|
3725
|
+
const geometry = mesh.geometry;
|
|
3726
|
+
if (!geometry) {
|
|
3727
|
+
return;
|
|
3728
|
+
}
|
|
3729
|
+
const position2 = geometry.getAttribute("position");
|
|
3730
|
+
if (!(position2 instanceof BufferAttribute)) {
|
|
3731
|
+
return;
|
|
3732
|
+
}
|
|
3733
|
+
const transform = computeActorSpaceMatrix(mesh, actorRoot, actorScale);
|
|
3734
|
+
const vertexOffset = vertices.length / 3;
|
|
3735
|
+
for (let i = 0; i < position2.count; i++) {
|
|
3736
|
+
point.set(position2.getX(i), position2.getY(i), position2.getZ(i));
|
|
3737
|
+
point.applyMatrix4(transform);
|
|
3738
|
+
vertices.push(point.x, point.y, point.z);
|
|
3739
|
+
bounds.expandByPoint(point);
|
|
3740
|
+
}
|
|
3741
|
+
const index = geometry.getIndex();
|
|
3742
|
+
if (index) {
|
|
3743
|
+
for (let i = 0; i < index.count; i++) {
|
|
3744
|
+
indices.push(vertexOffset + index.getX(i));
|
|
3745
|
+
}
|
|
3746
|
+
} else {
|
|
3747
|
+
for (let i = 0; i < position2.count; i++) {
|
|
3748
|
+
indices.push(vertexOffset + i);
|
|
3749
|
+
}
|
|
3750
|
+
}
|
|
3751
|
+
foundMesh = true;
|
|
3752
|
+
});
|
|
3753
|
+
if (!foundMesh || vertices.length === 0 || indices.length === 0) {
|
|
3754
|
+
return null;
|
|
3755
|
+
}
|
|
3756
|
+
const center = new Vector313();
|
|
3757
|
+
bounds.getCenter(center);
|
|
3758
|
+
return {
|
|
3759
|
+
vertices: new Float32Array(vertices),
|
|
3760
|
+
indices: new Uint32Array(indices),
|
|
3761
|
+
center
|
|
3762
|
+
};
|
|
3763
|
+
}
|
|
3764
|
+
function createBoundsColliderFromModel(modelRoot, actorRoot, options) {
|
|
3765
|
+
const explicitCollider = createExplicitBoxCollider(options);
|
|
3766
|
+
if (explicitCollider) {
|
|
3767
|
+
return explicitCollider;
|
|
3768
|
+
}
|
|
3769
|
+
if (!modelRoot || !actorRoot) {
|
|
3770
|
+
return createCapsuleCollider(options);
|
|
3771
|
+
}
|
|
3772
|
+
const bounds = computeModelBounds(modelRoot, actorRoot, getActorScale(options));
|
|
3773
|
+
if (!bounds) {
|
|
3774
|
+
return createCapsuleCollider(options);
|
|
3775
|
+
}
|
|
3776
|
+
const colliderDesc = ColliderDesc5.cuboid(
|
|
3777
|
+
bounds.size.x / 2,
|
|
3778
|
+
bounds.size.y / 2,
|
|
3779
|
+
bounds.size.z / 2
|
|
3780
|
+
);
|
|
3781
|
+
const collisionPosition = getCollisionPosition(options);
|
|
3782
|
+
colliderDesc.setSensor(false);
|
|
3783
|
+
colliderDesc.setTranslation(
|
|
3784
|
+
collisionPosition?.x ?? bounds.center.x,
|
|
3785
|
+
collisionPosition?.y ?? bounds.center.y,
|
|
3786
|
+
collisionPosition?.z ?? bounds.center.z
|
|
3787
|
+
);
|
|
3788
|
+
colliderDesc.activeCollisionTypes = ActiveCollisionTypes3.DEFAULT;
|
|
3789
|
+
return colliderDesc;
|
|
3790
|
+
}
|
|
3791
|
+
function createCapsuleColliderFromModel(sizingRoot, sizingActorRoot, alignmentRoot, alignmentActorRoot, options) {
|
|
3792
|
+
if (hasExplicitCollisionSize(options)) {
|
|
3793
|
+
return createCapsuleCollider(options);
|
|
3794
|
+
}
|
|
3795
|
+
if (!sizingRoot || !sizingActorRoot) {
|
|
3796
|
+
return createCapsuleCollider(options);
|
|
3797
|
+
}
|
|
3798
|
+
const sizingBounds = computeModelBounds(
|
|
3799
|
+
sizingRoot,
|
|
3800
|
+
sizingActorRoot,
|
|
3801
|
+
getActorScale(options)
|
|
3802
|
+
);
|
|
3803
|
+
if (!sizingBounds) {
|
|
3804
|
+
return createCapsuleCollider(options);
|
|
3805
|
+
}
|
|
3806
|
+
const collisionPosition = getCollisionPosition(options);
|
|
3807
|
+
let translation = collisionPosition;
|
|
3808
|
+
if (!translation) {
|
|
3809
|
+
const alignmentBounds = alignmentRoot && alignmentActorRoot ? computeModelBounds(alignmentRoot, alignmentActorRoot, getActorScale(options)) : null;
|
|
3810
|
+
translation = alignmentBounds ? {
|
|
3811
|
+
x: alignmentBounds.center.x,
|
|
3812
|
+
y: alignmentBounds.center.y,
|
|
3813
|
+
z: alignmentBounds.center.z
|
|
3814
|
+
} : {
|
|
3815
|
+
x: sizingBounds.center.x,
|
|
3816
|
+
y: sizingBounds.center.y,
|
|
3817
|
+
z: sizingBounds.center.z
|
|
3818
|
+
};
|
|
3819
|
+
}
|
|
3820
|
+
return createCapsuleColliderFromDimensions(
|
|
3821
|
+
{
|
|
3822
|
+
x: sizingBounds.size.x,
|
|
3823
|
+
y: sizingBounds.size.y,
|
|
3824
|
+
z: sizingBounds.size.z
|
|
3825
|
+
},
|
|
3826
|
+
translation
|
|
3827
|
+
);
|
|
3828
|
+
}
|
|
3829
|
+
function createTrimeshColliderFromModel(modelRoot, actorRoot, options) {
|
|
3830
|
+
const explicitCollider = createExplicitBoxCollider(options);
|
|
3831
|
+
if (explicitCollider) {
|
|
3832
|
+
return explicitCollider;
|
|
3833
|
+
}
|
|
3834
|
+
if (!modelRoot || !actorRoot) {
|
|
3835
|
+
return createCapsuleCollider(options);
|
|
3836
|
+
}
|
|
3837
|
+
const trimesh = computeModelTrimesh(modelRoot, actorRoot, getActorScale(options));
|
|
3838
|
+
if (!trimesh) {
|
|
3839
|
+
return createCapsuleCollider(options);
|
|
3840
|
+
}
|
|
3841
|
+
const colliderDesc = tagTrimeshCollider(
|
|
3842
|
+
ColliderDesc5.trimesh(trimesh.vertices, trimesh.indices),
|
|
3843
|
+
trimesh.vertices,
|
|
3844
|
+
trimesh.indices
|
|
3845
|
+
);
|
|
3846
|
+
const collisionPosition = getCollisionPosition(options);
|
|
3847
|
+
if (collisionPosition) {
|
|
3848
|
+
colliderDesc.setTranslation(
|
|
3849
|
+
collisionPosition.x - trimesh.center.x,
|
|
3850
|
+
collisionPosition.y - trimesh.center.y,
|
|
3851
|
+
collisionPosition.z - trimesh.center.z
|
|
3852
|
+
);
|
|
3853
|
+
}
|
|
3854
|
+
colliderDesc.setSensor(false);
|
|
3855
|
+
colliderDesc.activeCollisionTypes = ActiveCollisionTypes3.DEFAULT;
|
|
3856
|
+
return colliderDesc;
|
|
3857
|
+
}
|
|
3858
|
+
function disposeObjectResources(object) {
|
|
3859
|
+
if (!object) {
|
|
3860
|
+
return;
|
|
3861
|
+
}
|
|
3862
|
+
object.traverse((child) => {
|
|
3863
|
+
if (!child.isMesh) {
|
|
3864
|
+
return;
|
|
3865
|
+
}
|
|
3866
|
+
const mesh = child;
|
|
3867
|
+
mesh.geometry?.dispose();
|
|
3868
|
+
if (Array.isArray(mesh.material)) {
|
|
3869
|
+
mesh.material.forEach((material) => material.dispose());
|
|
3870
|
+
} else {
|
|
3871
|
+
mesh.material?.dispose();
|
|
3872
|
+
}
|
|
3873
|
+
});
|
|
3874
|
+
}
|
|
3054
3875
|
var ActorCollisionBuilder = class extends EntityCollisionBuilder {
|
|
3055
3876
|
objectModel = null;
|
|
3877
|
+
collisionSource = null;
|
|
3056
3878
|
collisionShape = "capsule";
|
|
3057
3879
|
constructor(data) {
|
|
3058
3880
|
super();
|
|
3059
3881
|
this.objectModel = data.objectModel;
|
|
3882
|
+
this.collisionSource = data.collisionSource ?? data.objectModel ?? null;
|
|
3060
3883
|
this.collisionShape = data.collisionShape ?? "capsule";
|
|
3061
3884
|
}
|
|
3062
3885
|
collider(options) {
|
|
3063
|
-
|
|
3064
|
-
|
|
3886
|
+
const resolvedShape = resolveCollisionShape(
|
|
3887
|
+
this.collisionShape,
|
|
3888
|
+
Boolean(options.collision?.static)
|
|
3889
|
+
);
|
|
3890
|
+
if (resolvedShape === "bounds") {
|
|
3891
|
+
return createBoundsColliderFromModel(this.objectModel, this.objectModel, options);
|
|
3065
3892
|
}
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
colliderDesc.setSensor(false);
|
|
3077
|
-
colliderDesc.setTranslation(0, halfHeight + radius, 0);
|
|
3078
|
-
colliderDesc.activeCollisionTypes = ActiveCollisionTypes3.DEFAULT;
|
|
3079
|
-
return colliderDesc;
|
|
3080
|
-
}
|
|
3081
|
-
/**
|
|
3082
|
-
* Create a collider based on model geometry (works with Mesh and SkinnedMesh).
|
|
3083
|
-
* If collision.size and collision.position are provided, use those instead of computing from geometry.
|
|
3084
|
-
*/
|
|
3085
|
-
createColliderFromModel(objectModel, options) {
|
|
3086
|
-
const collisionSize = options.collision?.size;
|
|
3087
|
-
const collisionPosition = options.collision?.position;
|
|
3088
|
-
if (collisionSize) {
|
|
3089
|
-
const halfWidth = collisionSize.x / 2;
|
|
3090
|
-
const halfHeight = collisionSize.y / 2;
|
|
3091
|
-
const halfDepth = collisionSize.z / 2;
|
|
3092
|
-
let colliderDesc2 = ColliderDesc5.cuboid(halfWidth, halfHeight, halfDepth);
|
|
3093
|
-
colliderDesc2.setSensor(false);
|
|
3094
|
-
const posX = collisionPosition ? collisionPosition.x : 0;
|
|
3095
|
-
const posY = collisionPosition ? collisionPosition.y : halfHeight;
|
|
3096
|
-
const posZ = collisionPosition ? collisionPosition.z : 0;
|
|
3097
|
-
colliderDesc2.setTranslation(posX, posY, posZ);
|
|
3098
|
-
colliderDesc2.activeCollisionTypes = ActiveCollisionTypes3.DEFAULT;
|
|
3099
|
-
return colliderDesc2;
|
|
3100
|
-
}
|
|
3101
|
-
if (!objectModel) return this.createCapsuleCollider(options);
|
|
3102
|
-
let foundGeometry = null;
|
|
3103
|
-
objectModel.traverse((child) => {
|
|
3104
|
-
if (!foundGeometry && child.isMesh) {
|
|
3105
|
-
foundGeometry = child.geometry;
|
|
3106
|
-
}
|
|
3107
|
-
});
|
|
3108
|
-
if (!foundGeometry) return this.createCapsuleCollider(options);
|
|
3109
|
-
const geometry = foundGeometry;
|
|
3110
|
-
geometry.computeBoundingBox();
|
|
3111
|
-
const box = geometry.boundingBox;
|
|
3112
|
-
if (!box) return this.createCapsuleCollider(options);
|
|
3113
|
-
const height = box.max.y - box.min.y;
|
|
3114
|
-
const width = box.max.x - box.min.x;
|
|
3115
|
-
const depth = box.max.z - box.min.z;
|
|
3116
|
-
let colliderDesc = ColliderDesc5.cuboid(width / 2, height / 2, depth / 2);
|
|
3117
|
-
colliderDesc.setSensor(false);
|
|
3118
|
-
const centerY = (box.max.y + box.min.y) / 2;
|
|
3119
|
-
colliderDesc.setTranslation(0, centerY, 0);
|
|
3120
|
-
colliderDesc.activeCollisionTypes = ActiveCollisionTypes3.DEFAULT;
|
|
3121
|
-
return colliderDesc;
|
|
3893
|
+
if (resolvedShape === "trimesh") {
|
|
3894
|
+
return createTrimeshColliderFromModel(this.objectModel, this.objectModel, options);
|
|
3895
|
+
}
|
|
3896
|
+
return createCapsuleColliderFromModel(
|
|
3897
|
+
this.collisionSource,
|
|
3898
|
+
this.collisionSource,
|
|
3899
|
+
this.objectModel,
|
|
3900
|
+
this.objectModel,
|
|
3901
|
+
options
|
|
3902
|
+
);
|
|
3122
3903
|
}
|
|
3123
3904
|
};
|
|
3124
3905
|
var ActorBuilder = class extends EntityBuilder {
|
|
@@ -3130,96 +3911,228 @@ var ACTOR_TYPE = /* @__PURE__ */ Symbol("Actor");
|
|
|
3130
3911
|
var ZylemActor = class extends GameEntity {
|
|
3131
3912
|
static type = ACTOR_TYPE;
|
|
3132
3913
|
_object = null;
|
|
3914
|
+
_collisionSource = null;
|
|
3133
3915
|
_animationDelegate = null;
|
|
3134
3916
|
_modelFileNames = [];
|
|
3135
3917
|
_assetLoader = new EntityAssetLoader();
|
|
3918
|
+
_loadGeneration = 0;
|
|
3919
|
+
_loadStatus = "idle";
|
|
3136
3920
|
controlledRotation = false;
|
|
3921
|
+
hasWarnedDynamicTrimesh = false;
|
|
3137
3922
|
constructor(options) {
|
|
3138
3923
|
super();
|
|
3139
3924
|
this.options = { ...actorDefaults, ...options };
|
|
3140
3925
|
this.prependUpdate(this.actorUpdate.bind(this));
|
|
3141
3926
|
this.controlledRotation = true;
|
|
3142
3927
|
}
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
3928
|
+
create() {
|
|
3929
|
+
if (this._loadStatus === "idle" && (this.options.models?.length ?? 0) > 0) {
|
|
3930
|
+
this.load();
|
|
3931
|
+
}
|
|
3932
|
+
return super.create();
|
|
3933
|
+
}
|
|
3147
3934
|
load() {
|
|
3148
|
-
this.
|
|
3149
|
-
|
|
3935
|
+
if (this._loadStatus === "loading" || this._loadStatus === "loaded") {
|
|
3936
|
+
return;
|
|
3937
|
+
}
|
|
3938
|
+
this._modelFileNames = [...this.options.models || []];
|
|
3939
|
+
if (this._modelFileNames.length === 0) {
|
|
3940
|
+
return;
|
|
3941
|
+
}
|
|
3942
|
+
this.disposeRuntimeState(true);
|
|
3943
|
+
this._loadStatus = "loading";
|
|
3944
|
+
this.loadModelsDeferred(++this._loadGeneration);
|
|
3150
3945
|
}
|
|
3151
|
-
/**
|
|
3152
|
-
* Returns current data synchronously.
|
|
3153
|
-
* May return null values if loading is still in progress.
|
|
3154
|
-
*/
|
|
3155
3946
|
data() {
|
|
3156
3947
|
return {
|
|
3157
3948
|
animations: this._animationDelegate?.animations,
|
|
3158
3949
|
objectModel: this._object,
|
|
3950
|
+
collisionSource: this._collisionSource ?? this._object,
|
|
3159
3951
|
collisionShape: this.options.collisionShape
|
|
3160
3952
|
};
|
|
3161
3953
|
}
|
|
3954
|
+
needsDeferredModelCollision() {
|
|
3955
|
+
if (!this.requiresLoadedCollisionSource()) {
|
|
3956
|
+
return false;
|
|
3957
|
+
}
|
|
3958
|
+
return !this.getRuntimeCollisionSource();
|
|
3959
|
+
}
|
|
3960
|
+
synchronizeRuntimeCollider() {
|
|
3961
|
+
const resolvedShape = this.getResolvedCollisionShape();
|
|
3962
|
+
let colliderDesc;
|
|
3963
|
+
if (resolvedShape === "bounds") {
|
|
3964
|
+
colliderDesc = createBoundsColliderFromModel(
|
|
3965
|
+
this._object,
|
|
3966
|
+
this.group ?? this._object,
|
|
3967
|
+
this.options
|
|
3968
|
+
);
|
|
3969
|
+
} else if (resolvedShape === "trimesh") {
|
|
3970
|
+
colliderDesc = createTrimeshColliderFromModel(
|
|
3971
|
+
this._object,
|
|
3972
|
+
this.group ?? this._object,
|
|
3973
|
+
this.options
|
|
3974
|
+
);
|
|
3975
|
+
} else {
|
|
3976
|
+
const collisionSource = this.getRuntimeCollisionSource();
|
|
3977
|
+
colliderDesc = createCapsuleColliderFromModel(
|
|
3978
|
+
collisionSource,
|
|
3979
|
+
collisionSource,
|
|
3980
|
+
this._object,
|
|
3981
|
+
this._object,
|
|
3982
|
+
this.options
|
|
3983
|
+
);
|
|
3984
|
+
}
|
|
3985
|
+
this.colliderDesc = colliderDesc;
|
|
3986
|
+
if (this.colliderDescs.length > 0) {
|
|
3987
|
+
this.colliderDescs[0] = colliderDesc;
|
|
3988
|
+
} else {
|
|
3989
|
+
this.colliderDescs.push(colliderDesc);
|
|
3990
|
+
}
|
|
3991
|
+
}
|
|
3162
3992
|
actorUpdate(params) {
|
|
3163
3993
|
this._animationDelegate?.update(params.delta);
|
|
3164
3994
|
}
|
|
3165
|
-
/**
|
|
3166
|
-
* Clean up actor resources including animations, models, and groups
|
|
3167
|
-
*/
|
|
3168
3995
|
actorDestroy() {
|
|
3996
|
+
this._loadGeneration += 1;
|
|
3997
|
+
this._loadStatus = "idle";
|
|
3998
|
+
this.disposeRuntimeState(true);
|
|
3999
|
+
this._modelFileNames = [];
|
|
4000
|
+
}
|
|
4001
|
+
_cleanup(params) {
|
|
4002
|
+
super._cleanup(params);
|
|
4003
|
+
this.actorDestroy();
|
|
4004
|
+
}
|
|
4005
|
+
playAnimation(animationOptions) {
|
|
4006
|
+
this._animationDelegate?.playAnimation(animationOptions);
|
|
4007
|
+
}
|
|
4008
|
+
get object() {
|
|
4009
|
+
return this._object;
|
|
4010
|
+
}
|
|
4011
|
+
get collisionSource() {
|
|
4012
|
+
return this._collisionSource ?? this._object;
|
|
4013
|
+
}
|
|
4014
|
+
getDebugInfo() {
|
|
4015
|
+
const debugInfo = {
|
|
4016
|
+
type: "Actor",
|
|
4017
|
+
models: this._modelFileNames.length > 0 ? this._modelFileNames : "none",
|
|
4018
|
+
modelLoaded: !!this._object,
|
|
4019
|
+
scale: this.options.scale ? `${this.options.scale.x}, ${this.options.scale.y}, ${this.options.scale.z}` : "1, 1, 1",
|
|
4020
|
+
collisionShape: this.getResolvedCollisionShape()
|
|
4021
|
+
};
|
|
3169
4022
|
if (this._animationDelegate) {
|
|
3170
|
-
this._animationDelegate.
|
|
3171
|
-
|
|
4023
|
+
debugInfo.currentAnimation = this._animationDelegate.currentAnimationKey || "none";
|
|
4024
|
+
debugInfo.animationsCount = this.options.animations?.length || 0;
|
|
3172
4025
|
}
|
|
3173
4026
|
if (this._object) {
|
|
4027
|
+
let meshCount = 0;
|
|
4028
|
+
let vertexCount = 0;
|
|
3174
4029
|
this._object.traverse((child) => {
|
|
3175
|
-
if (child.isMesh) {
|
|
3176
|
-
|
|
3177
|
-
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
}
|
|
4030
|
+
if (!child.isMesh) {
|
|
4031
|
+
return;
|
|
4032
|
+
}
|
|
4033
|
+
meshCount++;
|
|
4034
|
+
const geometry = child.geometry;
|
|
4035
|
+
if (geometry?.attributes.position) {
|
|
4036
|
+
vertexCount += geometry.attributes.position.count;
|
|
3183
4037
|
}
|
|
3184
4038
|
});
|
|
3185
|
-
|
|
4039
|
+
debugInfo.meshCount = meshCount;
|
|
4040
|
+
debugInfo.vertexCount = vertexCount;
|
|
4041
|
+
}
|
|
4042
|
+
return debugInfo;
|
|
4043
|
+
}
|
|
4044
|
+
getResolvedCollisionShape() {
|
|
4045
|
+
return resolveCollisionShape(
|
|
4046
|
+
this.options.collisionShape,
|
|
4047
|
+
Boolean(this.options.collision?.static),
|
|
4048
|
+
() => warnDynamicTrimeshOnce(this)
|
|
4049
|
+
);
|
|
4050
|
+
}
|
|
4051
|
+
requiresLoadedCollisionSource() {
|
|
4052
|
+
const resolvedShape = this.getResolvedCollisionShape();
|
|
4053
|
+
if (resolvedShape === "bounds" || resolvedShape === "trimesh") {
|
|
4054
|
+
return !hasExplicitCollisionSize(this.options);
|
|
4055
|
+
}
|
|
4056
|
+
if (resolvedShape === "capsule") {
|
|
4057
|
+
return !hasExplicitCollisionSize(this.options) && this.getCollisionSourceSelection().file !== null;
|
|
4058
|
+
}
|
|
4059
|
+
return false;
|
|
4060
|
+
}
|
|
4061
|
+
getRuntimeCollisionSource() {
|
|
4062
|
+
const resolvedShape = this.getResolvedCollisionShape();
|
|
4063
|
+
if (resolvedShape === "bounds" || resolvedShape === "trimesh") {
|
|
4064
|
+
return this._object;
|
|
4065
|
+
}
|
|
4066
|
+
return this._collisionSource ?? this._object;
|
|
4067
|
+
}
|
|
4068
|
+
getCollisionSourceSelection() {
|
|
4069
|
+
const modelFile = this.options.models?.[0] ?? null;
|
|
4070
|
+
const animationFile = this.options.animations?.[0]?.path ?? null;
|
|
4071
|
+
if (animationFile) {
|
|
4072
|
+
return {
|
|
4073
|
+
file: animationFile,
|
|
4074
|
+
reuseVisibleModel: animationFile === modelFile
|
|
4075
|
+
};
|
|
4076
|
+
}
|
|
4077
|
+
return {
|
|
4078
|
+
file: modelFile,
|
|
4079
|
+
reuseVisibleModel: true
|
|
4080
|
+
};
|
|
4081
|
+
}
|
|
4082
|
+
disposeRuntimeState(clearGroup) {
|
|
4083
|
+
if (this._animationDelegate) {
|
|
4084
|
+
this._animationDelegate.dispose();
|
|
4085
|
+
this._animationDelegate = null;
|
|
4086
|
+
}
|
|
4087
|
+
const collisionSource = this._collisionSource;
|
|
4088
|
+
disposeObjectResources(this._object);
|
|
4089
|
+
if (collisionSource && collisionSource !== this._object) {
|
|
4090
|
+
disposeObjectResources(collisionSource);
|
|
3186
4091
|
}
|
|
3187
|
-
|
|
4092
|
+
this._object = null;
|
|
4093
|
+
this._collisionSource = null;
|
|
4094
|
+
if (clearGroup && this.group) {
|
|
3188
4095
|
this.group.clear();
|
|
3189
|
-
this.group =
|
|
4096
|
+
this.group = void 0;
|
|
3190
4097
|
}
|
|
3191
|
-
this._modelFileNames = [];
|
|
3192
4098
|
}
|
|
3193
|
-
|
|
3194
|
-
* Deferred loading - starts async load and updates entity when complete.
|
|
3195
|
-
* Called by synchronous load() method.
|
|
3196
|
-
*/
|
|
3197
|
-
loadModelsDeferred() {
|
|
3198
|
-
if (this._modelFileNames.length === 0) return;
|
|
4099
|
+
loadModelsDeferred(loadGeneration) {
|
|
3199
4100
|
this.dispatch("entity:model:loading", {
|
|
3200
4101
|
entityId: this.uuid,
|
|
3201
4102
|
files: this._modelFileNames
|
|
3202
4103
|
});
|
|
3203
|
-
const
|
|
3204
|
-
|
|
3205
|
-
|
|
3206
|
-
|
|
4104
|
+
const collisionSourceSelection = this.getCollisionSourceSelection();
|
|
4105
|
+
const promises = this._modelFileNames.map(
|
|
4106
|
+
(file) => this._assetLoader.loadFile(file, { clone: true })
|
|
4107
|
+
);
|
|
4108
|
+
const collisionSourcePromise = collisionSourceSelection.file && !collisionSourceSelection.reuseVisibleModel ? this._assetLoader.loadFile(collisionSourceSelection.file, { clone: true }) : Promise.resolve(null);
|
|
4109
|
+
Promise.all([Promise.all(promises), collisionSourcePromise]).then(([results, collisionSourceResult]) => {
|
|
4110
|
+
if (loadGeneration !== this._loadGeneration) {
|
|
4111
|
+
results.forEach((result) => disposeObjectResources(result.object ?? null));
|
|
4112
|
+
if (collisionSourceResult?.object) {
|
|
4113
|
+
disposeObjectResources(collisionSourceResult.object);
|
|
4114
|
+
}
|
|
4115
|
+
return;
|
|
3207
4116
|
}
|
|
4117
|
+
this._object = results[0]?.object ?? null;
|
|
4118
|
+
this._collisionSource = collisionSourceSelection.reuseVisibleModel ? this._object : collisionSourceResult?.object ?? this._object;
|
|
4119
|
+
this._loadStatus = this._object ? "loaded" : "idle";
|
|
3208
4120
|
let meshCount = 0;
|
|
3209
4121
|
if (this._object) {
|
|
3210
4122
|
this._object.traverse((child) => {
|
|
3211
|
-
if (child.isMesh)
|
|
4123
|
+
if (child.isMesh) {
|
|
4124
|
+
meshCount++;
|
|
4125
|
+
}
|
|
3212
4126
|
});
|
|
3213
4127
|
this.group = new Group4();
|
|
3214
|
-
this.group.
|
|
3215
|
-
this.group.scale.
|
|
3216
|
-
this.options.scale?.x || 1,
|
|
3217
|
-
this.options.scale?.y || 1,
|
|
3218
|
-
this.options.scale?.z || 1
|
|
3219
|
-
);
|
|
4128
|
+
this.group.add(this._object);
|
|
4129
|
+
this.group.scale.copy(getActorScale(this.options));
|
|
3220
4130
|
this.applyMaterialOverrides();
|
|
3221
4131
|
this._animationDelegate = new AnimationDelegate(this._object);
|
|
3222
|
-
this._animationDelegate.loadAnimations(this.options.animations || []).then(() => {
|
|
4132
|
+
void this._animationDelegate.loadAnimations(this.options.animations || []).then(() => {
|
|
4133
|
+
if (loadGeneration !== this._loadGeneration) {
|
|
4134
|
+
return;
|
|
4135
|
+
}
|
|
3223
4136
|
this.dispatch("entity:animation:loaded", {
|
|
3224
4137
|
entityId: this.uuid,
|
|
3225
4138
|
animationCount: this.options.animations?.length || 0
|
|
@@ -3231,72 +4144,61 @@ var ZylemActor = class extends GameEntity {
|
|
|
3231
4144
|
success: !!this._object,
|
|
3232
4145
|
meshCount
|
|
3233
4146
|
});
|
|
4147
|
+
}).catch((error) => {
|
|
4148
|
+
if (loadGeneration !== this._loadGeneration) {
|
|
4149
|
+
return;
|
|
4150
|
+
}
|
|
4151
|
+
this._loadStatus = "idle";
|
|
4152
|
+
console.error("Failed to load actor model", error);
|
|
4153
|
+
this.dispatch("entity:model:loaded", {
|
|
4154
|
+
entityId: this.uuid,
|
|
4155
|
+
success: false,
|
|
4156
|
+
meshCount: 0
|
|
4157
|
+
});
|
|
3234
4158
|
});
|
|
3235
4159
|
}
|
|
3236
|
-
playAnimation(animationOptions) {
|
|
3237
|
-
this._animationDelegate?.playAnimation(animationOptions);
|
|
3238
|
-
}
|
|
3239
|
-
/**
|
|
3240
|
-
* Apply material overrides from options to all meshes in the loaded model.
|
|
3241
|
-
* Only applies if material options are explicitly specified (not just defaults).
|
|
3242
|
-
*/
|
|
3243
4160
|
applyMaterialOverrides() {
|
|
3244
4161
|
const materialOptions = this.options.material;
|
|
3245
|
-
if (!materialOptions || !materialOptions.color && !materialOptions.path) {
|
|
4162
|
+
if (!materialOptions || !materialOptions.color && !materialOptions.path && materialOptions.opacity === void 0) {
|
|
4163
|
+
return;
|
|
4164
|
+
}
|
|
4165
|
+
if (!this._object) {
|
|
3246
4166
|
return;
|
|
3247
4167
|
}
|
|
3248
|
-
if (!this._object) return;
|
|
3249
4168
|
this._object.traverse((child) => {
|
|
3250
|
-
if (child.isMesh) {
|
|
3251
|
-
|
|
3252
|
-
if (materialOptions.color) {
|
|
3253
|
-
const newMaterial = new MeshStandardMaterial4({
|
|
3254
|
-
color: materialOptions.color,
|
|
3255
|
-
emissiveIntensity: 0.5,
|
|
3256
|
-
lightMapIntensity: 0.5,
|
|
3257
|
-
fog: true
|
|
3258
|
-
});
|
|
3259
|
-
mesh.castShadow = true;
|
|
3260
|
-
mesh.receiveShadow = true;
|
|
3261
|
-
mesh.material = newMaterial;
|
|
3262
|
-
}
|
|
4169
|
+
if (!child.isMesh) {
|
|
4170
|
+
return;
|
|
3263
4171
|
}
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
meshCount++;
|
|
3290
|
-
const geometry = child.geometry;
|
|
3291
|
-
if (geometry && geometry.attributes.position) {
|
|
3292
|
-
vertexCount += geometry.attributes.position.count;
|
|
3293
|
-
}
|
|
4172
|
+
const mesh = child;
|
|
4173
|
+
if (materialOptions.color) {
|
|
4174
|
+
const newMaterial = new MeshStandardMaterial4({
|
|
4175
|
+
color: materialOptions.color,
|
|
4176
|
+
emissiveIntensity: 0.5,
|
|
4177
|
+
lightMapIntensity: 0.5,
|
|
4178
|
+
fog: true,
|
|
4179
|
+
opacity: materialOptions.opacity ?? 1,
|
|
4180
|
+
transparent: materialOptions.opacity !== void 0 && materialOptions.opacity < 1
|
|
4181
|
+
});
|
|
4182
|
+
mesh.castShadow = true;
|
|
4183
|
+
mesh.receiveShadow = true;
|
|
4184
|
+
mesh.material = newMaterial;
|
|
4185
|
+
return;
|
|
4186
|
+
}
|
|
4187
|
+
if (materialOptions.opacity === void 0) {
|
|
4188
|
+
return;
|
|
4189
|
+
}
|
|
4190
|
+
const applyOpacity = (material) => {
|
|
4191
|
+
const clonedMaterial = material.clone();
|
|
4192
|
+
if ("opacity" in clonedMaterial && "transparent" in clonedMaterial) {
|
|
4193
|
+
const transparentMaterial = clonedMaterial;
|
|
4194
|
+
transparentMaterial.opacity = materialOptions.opacity;
|
|
4195
|
+
transparentMaterial.transparent = materialOptions.opacity < 1;
|
|
4196
|
+
transparentMaterial.needsUpdate = true;
|
|
3294
4197
|
}
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
}
|
|
3299
|
-
return debugInfo;
|
|
4198
|
+
return clonedMaterial;
|
|
4199
|
+
};
|
|
4200
|
+
mesh.material = Array.isArray(mesh.material) ? mesh.material.map(applyOpacity) : applyOpacity(mesh.material);
|
|
4201
|
+
});
|
|
3300
4202
|
}
|
|
3301
4203
|
};
|
|
3302
4204
|
function createActor(...args) {
|
|
@@ -3306,12 +4208,13 @@ function createActor(...args) {
|
|
|
3306
4208
|
EntityClass: ZylemActor,
|
|
3307
4209
|
BuilderClass: ActorBuilder,
|
|
3308
4210
|
CollisionBuilderClass: ActorCollisionBuilder,
|
|
3309
|
-
entityType: ZylemActor.type
|
|
4211
|
+
entityType: ZylemActor.type,
|
|
4212
|
+
cloneFactory: (options) => createActor(options ?? {})
|
|
3310
4213
|
});
|
|
3311
4214
|
}
|
|
3312
4215
|
|
|
3313
4216
|
// src/lib/entities/text.ts
|
|
3314
|
-
import { Color as
|
|
4217
|
+
import { Color as Color7, Group as Group5, Sprite as ThreeSprite2, SpriteMaterial as SpriteMaterial2, CanvasTexture, LinearFilter, Vector2 as Vector25, ClampToEdgeWrapping } from "three";
|
|
3315
4218
|
var textDefaults = {
|
|
3316
4219
|
position: void 0,
|
|
3317
4220
|
text: "",
|
|
@@ -3321,7 +4224,7 @@ var textDefaults = {
|
|
|
3321
4224
|
backgroundColor: null,
|
|
3322
4225
|
padding: 4,
|
|
3323
4226
|
stickToViewport: true,
|
|
3324
|
-
screenPosition: new
|
|
4227
|
+
screenPosition: new Vector25(24, 24),
|
|
3325
4228
|
zDistance: 1
|
|
3326
4229
|
};
|
|
3327
4230
|
var TextBuilder = class extends EntityBuilder {
|
|
@@ -3433,7 +4336,7 @@ var ZylemText = class _ZylemText extends GameEntity {
|
|
|
3433
4336
|
}
|
|
3434
4337
|
toCssColor(color) {
|
|
3435
4338
|
if (typeof color === "string") return color;
|
|
3436
|
-
const c = color instanceof
|
|
4339
|
+
const c = color instanceof Color7 ? color : new Color7(color);
|
|
3437
4340
|
return `#${c.getHexString()}`;
|
|
3438
4341
|
}
|
|
3439
4342
|
textSetup(params) {
|
|
@@ -3493,7 +4396,7 @@ var ZylemText = class _ZylemText extends GameEntity {
|
|
|
3493
4396
|
if (!this._sprite || !this._cameraRef) return;
|
|
3494
4397
|
const camera = this._cameraRef.camera;
|
|
3495
4398
|
const { width, height } = this.getResolution();
|
|
3496
|
-
const sp = this.options.screenPosition ?? new
|
|
4399
|
+
const sp = this.options.screenPosition ?? new Vector25(24, 24);
|
|
3497
4400
|
const { px, py } = this.getScreenPixels(sp, width, height);
|
|
3498
4401
|
const zDist = Math.max(1e-3, this.options.zDistance ?? 1);
|
|
3499
4402
|
const { worldHalfW, worldHalfH } = this.computeWorldExtents(camera, zDist);
|
|
@@ -3546,12 +4449,13 @@ function createText(...args) {
|
|
|
3546
4449
|
defaultConfig: { ...textDefaults },
|
|
3547
4450
|
EntityClass: ZylemText,
|
|
3548
4451
|
BuilderClass: TextBuilder,
|
|
3549
|
-
entityType: ZylemText.type
|
|
4452
|
+
entityType: ZylemText.type,
|
|
4453
|
+
cloneFactory: (options) => createText(options ?? {})
|
|
3550
4454
|
});
|
|
3551
4455
|
}
|
|
3552
4456
|
|
|
3553
4457
|
// src/lib/entities/rect.ts
|
|
3554
|
-
import { Color as
|
|
4458
|
+
import { Color as Color8, Group as Group6, Sprite as ThreeSprite3, SpriteMaterial as SpriteMaterial3, CanvasTexture as CanvasTexture2, LinearFilter as LinearFilter2, Vector2 as Vector26, ClampToEdgeWrapping as ClampToEdgeWrapping2, ShaderMaterial as ShaderMaterial3, Mesh as Mesh7, PlaneGeometry as PlaneGeometry2, Vector3 as Vector314 } from "three";
|
|
3555
4459
|
var rectDefaults = {
|
|
3556
4460
|
position: void 0,
|
|
3557
4461
|
width: 120,
|
|
@@ -3562,9 +4466,9 @@ var rectDefaults = {
|
|
|
3562
4466
|
radius: 0,
|
|
3563
4467
|
padding: 0,
|
|
3564
4468
|
stickToViewport: true,
|
|
3565
|
-
screenPosition: new
|
|
4469
|
+
screenPosition: new Vector26(24, 24),
|
|
3566
4470
|
zDistance: 1,
|
|
3567
|
-
anchor: new
|
|
4471
|
+
anchor: new Vector26(0, 0)
|
|
3568
4472
|
};
|
|
3569
4473
|
var RectBuilder = class extends EntityBuilder {
|
|
3570
4474
|
createEntity(options) {
|
|
@@ -3685,7 +4589,7 @@ var ZylemRect = class _ZylemRect extends GameEntity {
|
|
|
3685
4589
|
}
|
|
3686
4590
|
toCssColor(color) {
|
|
3687
4591
|
if (typeof color === "string") return color;
|
|
3688
|
-
const c = color instanceof
|
|
4592
|
+
const c = color instanceof Color8 ? color : new Color8(color);
|
|
3689
4593
|
return `#${c.getHexString()}`;
|
|
3690
4594
|
}
|
|
3691
4595
|
/**
|
|
@@ -3721,7 +4625,7 @@ var ZylemRect = class _ZylemRect extends GameEntity {
|
|
|
3721
4625
|
if (mat.uniforms?.tDiffuse) mat.uniforms.tDiffuse.value = this._texture;
|
|
3722
4626
|
if (mat.uniforms?.iResolution && this._canvas) mat.uniforms.iResolution.value.set(this._canvas.width, this._canvas.height, 1);
|
|
3723
4627
|
}
|
|
3724
|
-
this._mesh = new
|
|
4628
|
+
this._mesh = new Mesh7(new PlaneGeometry2(1, 1), mat);
|
|
3725
4629
|
this.group?.add(this._mesh);
|
|
3726
4630
|
this._sprite.visible = false;
|
|
3727
4631
|
}
|
|
@@ -3736,10 +4640,10 @@ var ZylemRect = class _ZylemRect extends GameEntity {
|
|
|
3736
4640
|
const desiredW = Math.max(2, Math.floor(width));
|
|
3737
4641
|
const desiredH = Math.max(2, Math.floor(height));
|
|
3738
4642
|
const changed = desiredW !== (this.options.width ?? 0) || desiredH !== (this.options.height ?? 0);
|
|
3739
|
-
this.options.screenPosition = new
|
|
4643
|
+
this.options.screenPosition = new Vector26(Math.floor(x), Math.floor(y));
|
|
3740
4644
|
this.options.width = desiredW;
|
|
3741
4645
|
this.options.height = desiredH;
|
|
3742
|
-
this.options.anchor = new
|
|
4646
|
+
this.options.anchor = new Vector26(0, 0);
|
|
3743
4647
|
if (changed) {
|
|
3744
4648
|
this.redrawRect();
|
|
3745
4649
|
}
|
|
@@ -3753,8 +4657,8 @@ var ZylemRect = class _ZylemRect extends GameEntity {
|
|
|
3753
4657
|
if (!this._sprite || !this._cameraRef) return;
|
|
3754
4658
|
const camera = this._cameraRef.camera;
|
|
3755
4659
|
const { width, height } = this.getResolution();
|
|
3756
|
-
const px = (this.options.screenPosition ?? new
|
|
3757
|
-
const py = (this.options.screenPosition ?? new
|
|
4660
|
+
const px = (this.options.screenPosition ?? new Vector26(24, 24)).x;
|
|
4661
|
+
const py = (this.options.screenPosition ?? new Vector26(24, 24)).y;
|
|
3758
4662
|
const zDist = Math.max(1e-3, this.options.zDistance ?? 1);
|
|
3759
4663
|
let worldHalfW = 1;
|
|
3760
4664
|
let worldHalfH = 1;
|
|
@@ -3785,7 +4689,7 @@ var ZylemRect = class _ZylemRect extends GameEntity {
|
|
|
3785
4689
|
this._sprite.scale.set(scaleX, scaleY, 1);
|
|
3786
4690
|
if (this._mesh) this._mesh.scale.set(scaleX, scaleY, 1);
|
|
3787
4691
|
}
|
|
3788
|
-
const anchor = this.options.anchor ?? new
|
|
4692
|
+
const anchor = this.options.anchor ?? new Vector26(0, 0);
|
|
3789
4693
|
const ax = Math.min(100, Math.max(0, anchor.x)) / 100;
|
|
3790
4694
|
const ay = Math.min(100, Math.max(0, anchor.y)) / 100;
|
|
3791
4695
|
const offsetX = (0.5 - ax) * scaleX;
|
|
@@ -3808,8 +4712,8 @@ var ZylemRect = class _ZylemRect extends GameEntity {
|
|
|
3808
4712
|
}
|
|
3809
4713
|
if (bounds.world) {
|
|
3810
4714
|
const { left, right, top, bottom, z = 0 } = bounds.world;
|
|
3811
|
-
const tl = this.worldToScreen(new
|
|
3812
|
-
const br = this.worldToScreen(new
|
|
4715
|
+
const tl = this.worldToScreen(new Vector314(left, top, z));
|
|
4716
|
+
const br = this.worldToScreen(new Vector314(right, bottom, z));
|
|
3813
4717
|
const x = Math.min(tl.x, br.x);
|
|
3814
4718
|
const y = Math.min(tl.y, br.y);
|
|
3815
4719
|
const width = Math.abs(br.x - tl.x);
|
|
@@ -3843,7 +4747,8 @@ function createRect(...args) {
|
|
|
3843
4747
|
defaultConfig: { ...rectDefaults },
|
|
3844
4748
|
EntityClass: ZylemRect,
|
|
3845
4749
|
BuilderClass: RectBuilder,
|
|
3846
|
-
entityType: ZylemRect.type
|
|
4750
|
+
entityType: ZylemRect.type,
|
|
4751
|
+
cloneFactory: (options) => createRect(options ?? {})
|
|
3847
4752
|
});
|
|
3848
4753
|
}
|
|
3849
4754
|
|
|
@@ -3894,7 +4799,10 @@ function createCone(...args) {
|
|
|
3894
4799
|
collisionFilter: options.collisionFilter
|
|
3895
4800
|
})
|
|
3896
4801
|
);
|
|
3897
|
-
return
|
|
4802
|
+
return finalizeEntityCloneSupport(
|
|
4803
|
+
entity,
|
|
4804
|
+
(cloneOptions) => createCone(cloneOptions ?? {})
|
|
4805
|
+
);
|
|
3898
4806
|
}
|
|
3899
4807
|
|
|
3900
4808
|
// src/lib/entities/pyramid.ts
|
|
@@ -3942,7 +4850,10 @@ function createPyramid(...args) {
|
|
|
3942
4850
|
collisionFilter: options.collisionFilter
|
|
3943
4851
|
})
|
|
3944
4852
|
);
|
|
3945
|
-
return
|
|
4853
|
+
return finalizeEntityCloneSupport(
|
|
4854
|
+
entity,
|
|
4855
|
+
(cloneOptions) => createPyramid(cloneOptions ?? {})
|
|
4856
|
+
);
|
|
3946
4857
|
}
|
|
3947
4858
|
|
|
3948
4859
|
// src/lib/entities/cylinder.ts
|
|
@@ -3997,7 +4908,10 @@ function createCylinder(...args) {
|
|
|
3997
4908
|
collisionFilter: options.collisionFilter
|
|
3998
4909
|
})
|
|
3999
4910
|
);
|
|
4000
|
-
return
|
|
4911
|
+
return finalizeEntityCloneSupport(
|
|
4912
|
+
entity,
|
|
4913
|
+
(cloneOptions) => createCylinder(cloneOptions ?? {})
|
|
4914
|
+
);
|
|
4001
4915
|
}
|
|
4002
4916
|
|
|
4003
4917
|
// src/lib/entities/pill.ts
|
|
@@ -4049,7 +4963,10 @@ function createPill(...args) {
|
|
|
4049
4963
|
collisionFilter: options.collisionFilter
|
|
4050
4964
|
})
|
|
4051
4965
|
);
|
|
4052
|
-
return
|
|
4966
|
+
return finalizeEntityCloneSupport(
|
|
4967
|
+
entity,
|
|
4968
|
+
(cloneOptions) => createPill(cloneOptions ?? {})
|
|
4969
|
+
);
|
|
4053
4970
|
}
|
|
4054
4971
|
export {
|
|
4055
4972
|
ZylemBox,
|