@kvyverse/world-runtime 0.1.0 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +78 -60
- package/dist/{BallCollider-8ZrxReEd.js → BallCollider-3L5vOrcL.js} +2 -2
- package/dist/{CapsuleCollider-bynyf6Mc.js → CapsuleCollider-CBTsR-Ys.js} +2 -2
- package/dist/ChatModule-2tiWS_Yr.js +411 -0
- package/dist/{Collider-4_rCTHVA.js → Collider-CPUCG3RA.js} +2 -2
- package/dist/{ConeCollider-DtRZmgK8.js → ConeCollider-C3XU3Y9X.js} +2 -2
- package/dist/{ConvexMeshCollider-BSmDM4o3.js → ConvexMeshCollider-DzNV4HTO.js} +1 -1
- package/dist/{CuboidCollider-CJvq7--s.js → CuboidCollider-Dp1DZoFM.js} +1 -1
- package/dist/{CylinderCollider-CQPSh4tu.js → CylinderCollider-C0h_nhB5.js} +2 -2
- package/dist/{FpsCameraController-BE2EirRj.js → FpsCameraController-2GNWJM5D.js} +1 -1
- package/dist/RapierDebugRenderer-BQy_m-3C.js +73 -0
- package/dist/{RapierFpsPlayerController-VsNfQl6v.js → RapierFpsPlayerController-DthD8dw9.js} +5 -22
- package/dist/{RapierPhysics-C_3b7QOn.js → RapierPhysics-zLwVZpwF.js} +33 -15
- package/dist/{Rigidbody-b7QNF4ev.js → Rigidbody-DLhN8ddv.js} +12 -1
- package/dist/World.d.ts +2 -2
- package/dist/WorldRuntime.d.ts +13 -4
- package/dist/addons/AddonsRuntime.d.ts +35 -0
- package/dist/addons/lib-registry.d.ts +34 -0
- package/dist/addons/types.d.ts +28 -0
- package/dist/behaviours/physics/BallCollider.d.ts +1 -1
- package/dist/behaviours/physics/CapsuleCollider.d.ts +1 -1
- package/dist/behaviours/physics/ConeCollider.d.ts +1 -1
- package/dist/behaviours/physics/ConvexMeshCollider.d.ts +1 -1
- package/dist/behaviours/physics/CuboidCollider.d.ts +1 -1
- package/dist/behaviours/physics/CylinderCollider.d.ts +1 -1
- package/dist/behaviours/physics/HalfspaceCollider.d.ts +1 -1
- package/dist/behaviours/physics/_required-patch.d.ts +10 -6
- package/dist/chat.module-descriptor-BTsep-Kq.js +107 -0
- package/dist/{get-scale-from-obj-QKv3Fig5.js → get-scale-from-obj-B7JlVPni.js} +1 -1
- package/dist/index.d.ts +21 -13
- package/dist/index.js +441 -155
- package/dist/modules/InputSystemModule.d.ts +14 -32
- package/dist/modules/chat/ChatDom.d.ts +100 -0
- package/dist/modules/chat/ChatModule.d.ts +36 -0
- package/dist/modules/chat/chat.module-descriptor.d.ts +43 -0
- package/dist/modules/contracts/addons.module-api.d.ts +15 -0
- package/dist/modules/contracts/chat.module-api.d.ts +52 -0
- package/dist/modules/contracts/controls.module-api.d.ts +58 -0
- package/dist/modules/contracts/html.module-api.d.ts +9 -1
- package/dist/modules/contracts/input.module-api.d.ts +11 -11
- package/dist/modules/contracts/module-events.d.ts +17 -0
- package/dist/modules/contracts/rapier.module-api.d.ts +13 -1
- package/dist/modules/controls/ControlsModule.d.ts +45 -0
- package/dist/modules/controls/KeyboardControlsDriver.d.ts +12 -0
- package/dist/modules/index.d.ts +30 -3
- package/dist/modules/optional-module-descriptor.d.ts +36 -0
- package/dist/modules/optional-modules.d.ts +11 -0
- package/dist/modules/physics/RapierDebugRenderer.d.ts +19 -0
- package/dist/{behaviours/physics/rapier-addon-core → modules/physics}/RapierPhysics.d.ts +13 -3
- package/dist/modules/physics/physics.module-descriptor.d.ts +16 -0
- package/dist/modules/registry.d.ts +7 -3
- package/dist/rendering/SceneBloom.d.ts +1 -1
- package/dist/rendering/renderer-apply.d.ts +1 -1
- package/dist/{settings → rendering}/types.d.ts +1 -12
- package/dist/types.d.ts +10 -3
- package/dist/version.gen.d.ts +1 -1
- package/package.json +9 -1
- package/dist/RapierDebugRenderer-sURZDLP4.js +0 -55
- package/dist/behaviours/physics/rapier-addon-core/RapierDebugRenderer.d.ts +0 -17
- package/dist/modules/contracts/rapierDebug.module-api.d.ts +0 -5
- package/dist/rolldown-runtime-D7D4PA-g.js +0 -13
- package/dist/settings/AddonsRuntime.d.ts +0 -19
- package/dist/settings/lib-loaders.d.ts +0 -7
- package/dist/settings/lib-registry.d.ts +0 -24
- package/dist/settings/module-registry.d.ts +0 -25
- /package/dist/{settings → addons}/externals.d.ts +0 -0
- /package/dist/behaviours/physics/{rapier-addon-core/Collider.d.ts → Collider.d.ts} +0 -0
- /package/dist/behaviours/physics/{rapier-addon-core/get-scale-from-obj.d.ts → get-scale-from-obj.d.ts} +0 -0
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { n as getRadiusScale, t as getHalfheightRadiusScale } from "./get-scale-from-obj-
|
|
1
|
+
import { a as OPTIONAL_MODULE_IDS, i as normalizeChat, n as CHAT_INPUT_MODES, o as OptionalModuleId, r as chatModuleDescriptor } from "./chat.module-descriptor-BTsep-Kq.js";
|
|
2
|
+
import { n as getRadiusScale, t as getHalfheightRadiusScale } from "./get-scale-from-obj-B7JlVPni.js";
|
|
3
3
|
import * as THREE from "three/webgpu";
|
|
4
4
|
import { BufferGeometryLoader, LoadingManager, TextureLoader } from "three/webgpu";
|
|
5
5
|
import * as ThreeStartNS from "three-start";
|
|
@@ -7,10 +7,10 @@ import { ContextModule, ThreeContextEvents, ThreeStart } from "three-start";
|
|
|
7
7
|
import * as TSL from "three/tsl";
|
|
8
8
|
import { emissive, mrt, output } from "three/tsl";
|
|
9
9
|
import van from "vanjs-core";
|
|
10
|
-
import EventEmitter from "eventemitter3";
|
|
11
10
|
import { Howl, Howler } from "howler";
|
|
12
11
|
import { mergeGeometries } from "three/examples/jsm/utils/BufferGeometryUtils.js";
|
|
13
12
|
import { clone } from "three/examples/jsm/utils/SkeletonUtils.js";
|
|
13
|
+
import EventEmitter from "eventemitter3";
|
|
14
14
|
//#region packages/world-runtime/src/world-events.ts
|
|
15
15
|
/**
|
|
16
16
|
* World lifecycle events, emitted on `game.emitter` (`world.events` in scripts)
|
|
@@ -40,8 +40,8 @@ var World = class extends ThreeStart {
|
|
|
40
40
|
super(options);
|
|
41
41
|
this._runtime = _runtime;
|
|
42
42
|
}
|
|
43
|
-
/** Asset URL by project path — same resolving as `getUrl` in scripts. */
|
|
44
|
-
getUrl = (
|
|
43
|
+
/** Asset URL by project path or resource — same resolving as `getUrl` in scripts. */
|
|
44
|
+
getUrl = (source) => this._runtime.getUrl(source);
|
|
45
45
|
/** World lifecycle event bus — the same one as `world.events` in scripts. */
|
|
46
46
|
get events() {
|
|
47
47
|
return this._runtime.emitter;
|
|
@@ -268,6 +268,241 @@ function finiteOr(value, fallback) {
|
|
|
268
268
|
return typeof value === "number" && Number.isFinite(value) ? value : fallback;
|
|
269
269
|
}
|
|
270
270
|
//#endregion
|
|
271
|
+
//#region packages/world-runtime/src/modules/controls/KeyboardControlsDriver.ts
|
|
272
|
+
/** A diagonal must not be faster than a straight line. */
|
|
273
|
+
var DIAGONAL = Math.SQRT1_2;
|
|
274
|
+
/**
|
|
275
|
+
* Keyboard driver for `controls`: WASD/arrows, shift to run, Space to jump.
|
|
276
|
+
* Reads key state from the `input` module and binds no listeners of its own.
|
|
277
|
+
*/
|
|
278
|
+
var KeyboardControlsDriver = class {
|
|
279
|
+
_input;
|
|
280
|
+
kind = "keyboard";
|
|
281
|
+
constructor(_input) {
|
|
282
|
+
this._input = _input;
|
|
283
|
+
}
|
|
284
|
+
read(state) {
|
|
285
|
+
const key = this._input.isKeyDown;
|
|
286
|
+
let x = 0;
|
|
287
|
+
let y = 0;
|
|
288
|
+
if (key("KeyW") || key("ArrowUp")) y += 1;
|
|
289
|
+
if (key("KeyS") || key("ArrowDown")) y -= 1;
|
|
290
|
+
if (key("KeyD") || key("ArrowRight")) x += 1;
|
|
291
|
+
if (key("KeyA") || key("ArrowLeft")) x -= 1;
|
|
292
|
+
if (x !== 0 && y !== 0) {
|
|
293
|
+
x *= DIAGONAL;
|
|
294
|
+
y *= DIAGONAL;
|
|
295
|
+
}
|
|
296
|
+
state.move.set(x, y);
|
|
297
|
+
state.force = x !== 0 || y !== 0 ? 1 : 0;
|
|
298
|
+
state.run = this._input.shiftKey;
|
|
299
|
+
state.jump = key("Space");
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
//#endregion
|
|
303
|
+
//#region packages/world-runtime/src/modules/controls/ControlsModule.ts
|
|
304
|
+
/**
|
|
305
|
+
* The player's movement intent — what they want to do, whatever the hardware is.
|
|
306
|
+
* Input sources register as drivers (`addDriver`); the module polls them once a
|
|
307
|
+
* frame and decides which one is driving. Details — `./README.md`.
|
|
308
|
+
*/
|
|
309
|
+
var ControlsModule = class extends ContextModule {
|
|
310
|
+
/** x — strafe, y — forward. Reused vector: copy it before storing. */
|
|
311
|
+
move = new THREE.Vector2();
|
|
312
|
+
_drivers = [];
|
|
313
|
+
_state = {
|
|
314
|
+
move: new THREE.Vector2(),
|
|
315
|
+
force: 0,
|
|
316
|
+
run: false,
|
|
317
|
+
jump: false
|
|
318
|
+
};
|
|
319
|
+
_keyboard = null;
|
|
320
|
+
_active = null;
|
|
321
|
+
_source = "keyboard";
|
|
322
|
+
_force = 0;
|
|
323
|
+
_run = false;
|
|
324
|
+
_jump = false;
|
|
325
|
+
onAwake() {
|
|
326
|
+
this._keyboard = new KeyboardControlsDriver(this.modules.input);
|
|
327
|
+
this.addDriver(this._keyboard);
|
|
328
|
+
}
|
|
329
|
+
/** Speed multiplier: 1 is a normal walk, higher means a stronger stick tilt. */
|
|
330
|
+
get force() {
|
|
331
|
+
return this._force;
|
|
332
|
+
}
|
|
333
|
+
/** Run modifier: shift, a stick pushed past its ring, a UI button. */
|
|
334
|
+
get run() {
|
|
335
|
+
return this._run;
|
|
336
|
+
}
|
|
337
|
+
/** Kind of the device driving right now. */
|
|
338
|
+
get source() {
|
|
339
|
+
return this._source;
|
|
340
|
+
}
|
|
341
|
+
get drivers() {
|
|
342
|
+
return this._drivers;
|
|
343
|
+
}
|
|
344
|
+
/** The built-in keyboard driver — detach it with `removeDriver`. */
|
|
345
|
+
get keyboard() {
|
|
346
|
+
if (!this._keyboard) throw new Error("controls: keyboard driver is not ready yet");
|
|
347
|
+
return this._keyboard;
|
|
348
|
+
}
|
|
349
|
+
addDriver(driver) {
|
|
350
|
+
if (!this._drivers.includes(driver)) this._drivers.push(driver);
|
|
351
|
+
return () => this.removeDriver(driver);
|
|
352
|
+
}
|
|
353
|
+
removeDriver(driver) {
|
|
354
|
+
const index = this._drivers.indexOf(driver);
|
|
355
|
+
if (index < 0) return;
|
|
356
|
+
this._drivers.splice(index, 1);
|
|
357
|
+
if (this._active === driver) this._active = null;
|
|
358
|
+
}
|
|
359
|
+
jump() {
|
|
360
|
+
this.emit("jump");
|
|
361
|
+
}
|
|
362
|
+
onUpdate() {
|
|
363
|
+
const driver = this._readActiveDriver();
|
|
364
|
+
this._apply(driver);
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* The active driver keeps the channel while it produces input — otherwise the
|
|
368
|
+
* keyboard and a stick would steal control from each other every frame. Once it
|
|
369
|
+
* goes quiet, the first driver with input takes over. The state is left filled
|
|
370
|
+
* by the winner, so nobody is read twice.
|
|
371
|
+
*/
|
|
372
|
+
_readActiveDriver() {
|
|
373
|
+
if (this._active && this._readDriver(this._active)) return this._active;
|
|
374
|
+
for (const driver of this._drivers) {
|
|
375
|
+
if (driver === this._active) continue;
|
|
376
|
+
if (this._readDriver(driver)) return driver;
|
|
377
|
+
}
|
|
378
|
+
this._resetState();
|
|
379
|
+
return null;
|
|
380
|
+
}
|
|
381
|
+
_readDriver(driver) {
|
|
382
|
+
this._resetState();
|
|
383
|
+
driver.read(this._state);
|
|
384
|
+
const { move, force, jump } = this._state;
|
|
385
|
+
return move.x !== 0 || move.y !== 0 || force > 0 || jump;
|
|
386
|
+
}
|
|
387
|
+
_resetState() {
|
|
388
|
+
const state = this._state;
|
|
389
|
+
state.move.set(0, 0);
|
|
390
|
+
state.force = 0;
|
|
391
|
+
state.run = false;
|
|
392
|
+
state.jump = false;
|
|
393
|
+
}
|
|
394
|
+
_apply(driver) {
|
|
395
|
+
const state = this._state;
|
|
396
|
+
this._active = driver ?? this._active;
|
|
397
|
+
const source = this._active?.kind ?? this._source;
|
|
398
|
+
if (source !== this._source) {
|
|
399
|
+
this._source = source;
|
|
400
|
+
this.emit("sourcechanged", source);
|
|
401
|
+
}
|
|
402
|
+
if (!this.move.equals(state.move)) {
|
|
403
|
+
this.move.copy(state.move);
|
|
404
|
+
this.emit("movechanged", this.move);
|
|
405
|
+
}
|
|
406
|
+
if (this._force !== state.force) {
|
|
407
|
+
this._force = state.force;
|
|
408
|
+
this.emit("forcechanged", state.force);
|
|
409
|
+
}
|
|
410
|
+
this._run = state.run;
|
|
411
|
+
if (state.jump && !this._jump) this.jump();
|
|
412
|
+
this._jump = state.jump;
|
|
413
|
+
}
|
|
414
|
+
};
|
|
415
|
+
//#endregion
|
|
416
|
+
//#region packages/world-runtime/src/behaviours/physics/_required-patch.ts
|
|
417
|
+
var lazyRapierPhysicsModule = lazy(async () => (await import("./RapierPhysics-zLwVZpwF.js")).default);
|
|
418
|
+
/** The only place the physics module is created — called by the registry
|
|
419
|
+
* descriptor. The module itself takes rapier from `ctx.modules.addons` on awake. */
|
|
420
|
+
async function createRapierPhysicsModule(config) {
|
|
421
|
+
return new (await (lazyRapierPhysicsModule.get()))({ debug: config.debug });
|
|
422
|
+
}
|
|
423
|
+
/**
|
|
424
|
+
* Registers physics on demand: a built-in physics behaviour declares it via
|
|
425
|
+
* `meta.requiredPatch`, so a world with a collider on the scene gets
|
|
426
|
+
* `ctx.modules.rapier` even without a line in `settings.addons.json`.
|
|
427
|
+
* Idempotent, and strictly before `start()` — three-start forbids `addModules`
|
|
428
|
+
* afterwards.
|
|
429
|
+
*/
|
|
430
|
+
async function applyRapierPhysicsPatch(starter) {
|
|
431
|
+
if (starter.ctx.modules.rapier) return;
|
|
432
|
+
const descriptor = physicsModuleDescriptor;
|
|
433
|
+
const module = await descriptor.create(descriptor.defaultConfig());
|
|
434
|
+
starter.addModules({ rapier: module });
|
|
435
|
+
}
|
|
436
|
+
//#endregion
|
|
437
|
+
//#region packages/world-runtime/src/modules/physics/physics.module-descriptor.ts
|
|
438
|
+
var PHYSICS_ENGINES = ["rapier"];
|
|
439
|
+
/** Physics defaults: the rapier engine, debug off. */
|
|
440
|
+
function normalizePhysics(value) {
|
|
441
|
+
if (!value) return null;
|
|
442
|
+
if (typeof value === "string") return {
|
|
443
|
+
engine: value,
|
|
444
|
+
debug: false
|
|
445
|
+
};
|
|
446
|
+
return {
|
|
447
|
+
engine: value.engine,
|
|
448
|
+
debug: value.debug ?? false
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
var physicsModuleDescriptor = {
|
|
452
|
+
id: OptionalModuleId.Physics,
|
|
453
|
+
normalize: (raw) => normalizePhysics(raw),
|
|
454
|
+
defaultConfig: () => ({
|
|
455
|
+
engine: "rapier",
|
|
456
|
+
debug: false
|
|
457
|
+
}),
|
|
458
|
+
create: (config) => createRapierPhysicsModule(config),
|
|
459
|
+
schema: {
|
|
460
|
+
description: "Physics engine module. false | \"rapier\" | { engine, debug }.",
|
|
461
|
+
oneOf: [
|
|
462
|
+
{
|
|
463
|
+
type: "boolean",
|
|
464
|
+
enum: [false]
|
|
465
|
+
},
|
|
466
|
+
{
|
|
467
|
+
type: "string",
|
|
468
|
+
enum: [...PHYSICS_ENGINES]
|
|
469
|
+
},
|
|
470
|
+
{
|
|
471
|
+
type: "object",
|
|
472
|
+
additionalProperties: false,
|
|
473
|
+
required: ["engine"],
|
|
474
|
+
properties: {
|
|
475
|
+
engine: {
|
|
476
|
+
type: "string",
|
|
477
|
+
enum: [...PHYSICS_ENGINES]
|
|
478
|
+
},
|
|
479
|
+
debug: {
|
|
480
|
+
type: "boolean",
|
|
481
|
+
description: "Draw collider debug lines from the start (the renderer is loaded on demand)."
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
]
|
|
486
|
+
}
|
|
487
|
+
};
|
|
488
|
+
//#endregion
|
|
489
|
+
//#region packages/world-runtime/src/modules/optional-modules.ts
|
|
490
|
+
/**
|
|
491
|
+
* Table of contents for the world's pluggable modules: what can be enabled from
|
|
492
|
+
* `settings.addons.json → modules`. The descriptors themselves live next to
|
|
493
|
+
* their module code; this is only the list.
|
|
494
|
+
*
|
|
495
|
+
* Add a descriptor here and the module shows up both in the runtime and in the
|
|
496
|
+
* config's JSON schema (the platform builds it from `descriptor.schema`).
|
|
497
|
+
*/
|
|
498
|
+
var OPTIONAL_MODULES = [physicsModuleDescriptor, chatModuleDescriptor];
|
|
499
|
+
var DESCRIPTOR_BY_ID = new Map(OPTIONAL_MODULES.map((m) => [m.id, m]));
|
|
500
|
+
function getOptionalModuleDescriptor(id) {
|
|
501
|
+
const descriptor = DESCRIPTOR_BY_ID.get(id);
|
|
502
|
+
if (!descriptor) throw new Error(`Unknown world module "${id}"`);
|
|
503
|
+
return descriptor;
|
|
504
|
+
}
|
|
505
|
+
//#endregion
|
|
271
506
|
//#region packages/world-runtime/src/modules/is-mobile-device.ts
|
|
272
507
|
function isMobileDevice() {
|
|
273
508
|
if (typeof navigator === "undefined" || typeof window === "undefined") return false;
|
|
@@ -336,46 +571,6 @@ var HtmlHudModule = class extends ContextModule {
|
|
|
336
571
|
};
|
|
337
572
|
//#endregion
|
|
338
573
|
//#region packages/world-runtime/src/modules/InputSystemModule.ts
|
|
339
|
-
var PlayerInput = class extends EventEmitter {
|
|
340
|
-
_forward = 0;
|
|
341
|
-
_lateral = 0;
|
|
342
|
-
_force = 1;
|
|
343
|
-
/**
|
|
344
|
-
* @default 0
|
|
345
|
-
*/
|
|
346
|
-
get forward() {
|
|
347
|
-
return this._forward;
|
|
348
|
-
}
|
|
349
|
-
/**
|
|
350
|
-
* @default 0
|
|
351
|
-
*/
|
|
352
|
-
get lateral() {
|
|
353
|
-
return this._lateral;
|
|
354
|
-
}
|
|
355
|
-
/**
|
|
356
|
-
* @default 1
|
|
357
|
-
*/
|
|
358
|
-
get force() {
|
|
359
|
-
return this._force;
|
|
360
|
-
}
|
|
361
|
-
setDir(forward, lateral) {
|
|
362
|
-
this._forward = forward;
|
|
363
|
-
this._lateral = lateral;
|
|
364
|
-
this.emit("dirchanged", forward, lateral);
|
|
365
|
-
}
|
|
366
|
-
setForce(force) {
|
|
367
|
-
this._force = force;
|
|
368
|
-
this.emit("forcechanged", force);
|
|
369
|
-
}
|
|
370
|
-
jump() {
|
|
371
|
-
this.emit("jump");
|
|
372
|
-
}
|
|
373
|
-
/**
|
|
374
|
-
* 0 is keyboard, 1 is joystick
|
|
375
|
-
* @default 0
|
|
376
|
-
*/
|
|
377
|
-
mode = 0;
|
|
378
|
-
};
|
|
379
574
|
var InputSystemModule = class extends ContextModule {
|
|
380
575
|
_dom;
|
|
381
576
|
_keys = /* @__PURE__ */ new Set();
|
|
@@ -421,12 +616,30 @@ var InputSystemModule = class extends ContextModule {
|
|
|
421
616
|
isKeyDown = (key) => {
|
|
422
617
|
return this._keys.has(key);
|
|
423
618
|
};
|
|
424
|
-
|
|
619
|
+
_captureKeyboard = true;
|
|
620
|
+
/**
|
|
621
|
+
* The game listens to the keyboard on the whole document, not just the canvas
|
|
622
|
+
* (otherwise any HTML over the world would swallow input). `false` hands the
|
|
623
|
+
* keyboard entirely to the UI: the game sees no keys and held ones are reset.
|
|
624
|
+
* @default true
|
|
625
|
+
*/
|
|
626
|
+
get captureKeyboard() {
|
|
627
|
+
return this._captureKeyboard;
|
|
628
|
+
}
|
|
629
|
+
set captureKeyboard(value) {
|
|
630
|
+
if (this._captureKeyboard === value) return;
|
|
631
|
+
this._captureKeyboard = value;
|
|
632
|
+
if (!value) {
|
|
633
|
+
this.resetKeys();
|
|
634
|
+
this.emit("keyschanged");
|
|
635
|
+
}
|
|
636
|
+
}
|
|
425
637
|
onAwake() {
|
|
426
638
|
const ctx = this.ctx;
|
|
427
639
|
this._dom = ctx.renderer.domElement;
|
|
428
640
|
window.addEventListener("resize", this.onWindowResize);
|
|
429
641
|
window.addEventListener("scroll", this.onWindowScroll);
|
|
642
|
+
window.addEventListener("blur", this.onBlur);
|
|
430
643
|
ctx.on(ThreeContextEvents.Resized, this.onDomResize);
|
|
431
644
|
const on = this._dom.addEventListener.bind(this._dom);
|
|
432
645
|
on("focus", this.onFocus);
|
|
@@ -436,13 +649,15 @@ var InputSystemModule = class extends ContextModule {
|
|
|
436
649
|
on("pointermove", this.onPointerMove);
|
|
437
650
|
on("pointerup", this.onPointerUp);
|
|
438
651
|
on("pointerleave", this.onPointerLeave);
|
|
439
|
-
|
|
440
|
-
|
|
652
|
+
document.addEventListener("keydown", this.onKeyDown);
|
|
653
|
+
document.addEventListener("keyup", this.onKeyUp);
|
|
654
|
+
document.addEventListener("focusin", this.onDocumentFocusIn);
|
|
441
655
|
}
|
|
442
656
|
/** three-start `ContextModule` has no `onDestroy`; teardown is explicit. */
|
|
443
657
|
dispose() {
|
|
444
658
|
window.removeEventListener("resize", this.onWindowResize);
|
|
445
659
|
window.removeEventListener("scroll", this.onWindowScroll);
|
|
660
|
+
window.removeEventListener("blur", this.onBlur);
|
|
446
661
|
this.ctx.off(ThreeContextEvents.Resized, this.onDomResize);
|
|
447
662
|
const off = this._dom.removeEventListener.bind(this._dom);
|
|
448
663
|
off("focus", this.onFocus);
|
|
@@ -452,8 +667,9 @@ var InputSystemModule = class extends ContextModule {
|
|
|
452
667
|
off("pointermove", this.onPointerMove);
|
|
453
668
|
off("pointerup", this.onPointerUp);
|
|
454
669
|
off("pointerleave", this.onPointerLeave);
|
|
455
|
-
|
|
456
|
-
|
|
670
|
+
document.removeEventListener("keydown", this.onKeyDown);
|
|
671
|
+
document.removeEventListener("keyup", this.onKeyUp);
|
|
672
|
+
document.removeEventListener("focusin", this.onDocumentFocusIn);
|
|
457
673
|
}
|
|
458
674
|
onWindowResize = () => {
|
|
459
675
|
this.updateDomBoundingClientRect();
|
|
@@ -472,15 +688,28 @@ var InputSystemModule = class extends ContextModule {
|
|
|
472
688
|
this.resetKeys();
|
|
473
689
|
this.resetPointers();
|
|
474
690
|
};
|
|
691
|
+
onDocumentFocusIn = (event) => {
|
|
692
|
+
if (!isUiKeyboardTarget(event.target)) return;
|
|
693
|
+
this.resetKeys();
|
|
694
|
+
this.emit("keyschanged");
|
|
695
|
+
};
|
|
696
|
+
/** The key belongs to the game, not to the UI. */
|
|
697
|
+
acceptsKey(event) {
|
|
698
|
+
return this._captureKeyboard && !isUiKeyboardTarget(event.target);
|
|
699
|
+
}
|
|
475
700
|
onKeyDown = (event) => {
|
|
701
|
+
if (!this.acceptsKey(event)) return;
|
|
476
702
|
this.updFnKeys(event);
|
|
477
703
|
this._keys.add(event.code);
|
|
478
704
|
this.emit("keyschanged");
|
|
705
|
+
this.emit("keydown", event);
|
|
479
706
|
};
|
|
480
707
|
onKeyUp = (event) => {
|
|
708
|
+
if (!this.acceptsKey(event)) return;
|
|
481
709
|
this.updFnKeys(event);
|
|
482
710
|
this._keys.delete(event.code);
|
|
483
711
|
this.emit("keyschanged");
|
|
712
|
+
this.emit("keyup", event);
|
|
484
713
|
};
|
|
485
714
|
onPointerEnter = (_) => {};
|
|
486
715
|
onPointerDown = (event) => {
|
|
@@ -577,6 +806,21 @@ var InputSystemModule = class extends ContextModule {
|
|
|
577
806
|
return this._domBoundingClientRect;
|
|
578
807
|
}
|
|
579
808
|
};
|
|
809
|
+
/**
|
|
810
|
+
* Focus is in a text field (or contenteditable) — the keyboard belongs to the UI
|
|
811
|
+
* and the game does not see it. Engine plumbing: nobody has to think about it,
|
|
812
|
+
* `isKeyDown` simply stays quiet while the player types.
|
|
813
|
+
*/
|
|
814
|
+
function isUiKeyboardTarget(target) {
|
|
815
|
+
if (!(target instanceof HTMLElement)) return false;
|
|
816
|
+
if (target.isContentEditable) return true;
|
|
817
|
+
switch (target.tagName) {
|
|
818
|
+
case "INPUT":
|
|
819
|
+
case "TEXTAREA":
|
|
820
|
+
case "SELECT": return true;
|
|
821
|
+
default: return false;
|
|
822
|
+
}
|
|
823
|
+
}
|
|
580
824
|
//#endregion
|
|
581
825
|
//#region packages/world-runtime/src/modules/SoundsModule.ts
|
|
582
826
|
var SoundsModule = class extends ContextModule {
|
|
@@ -599,39 +843,36 @@ var UtilsModule = class extends ContextModule {
|
|
|
599
843
|
};
|
|
600
844
|
//#endregion
|
|
601
845
|
//#region packages/world-runtime/src/modules/index.ts
|
|
602
|
-
/**
|
|
603
|
-
|
|
604
|
-
|
|
846
|
+
/**
|
|
847
|
+
* Assembles the world's ctx modules: core ones always, pluggable ones per config.
|
|
848
|
+
* Pluggable classes are pulled dynamically, so a disabled module never reaches
|
|
849
|
+
* the bundle. Call strictly before `start()` and with addons already loaded: the
|
|
850
|
+
* physics module takes rapier from `ctx.modules.addons`.
|
|
851
|
+
*
|
|
852
|
+
* A module the scene needs without a line in the config (physics when a physics
|
|
853
|
+
* behaviour is present) is added separately — through the behaviour's
|
|
854
|
+
* `meta.requiredPatch`, with the same descriptor and idempotently.
|
|
855
|
+
*/
|
|
856
|
+
async function createWorldModules(mode = "prod", options) {
|
|
857
|
+
const modules = {
|
|
858
|
+
addons: options.addons,
|
|
859
|
+
controls: new ControlsModule(),
|
|
605
860
|
env: new EnvModule(mode),
|
|
606
861
|
html: new HtmlHudModule(),
|
|
607
862
|
input: new InputSystemModule(),
|
|
608
863
|
sounds: new SoundsModule(),
|
|
609
864
|
utils: new UtilsModule()
|
|
610
865
|
};
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
var lazyRapierDebugModule = lazy(async () => (await import("./RapierDebugRenderer-sURZDLP4.js")).default);
|
|
622
|
-
/**
|
|
623
|
-
* Registers rapier modules before bootstrap: three-start forbids `addModules`
|
|
624
|
-
* after `start()`. Idempotent — a repeated call sees the module in ctx and exits.
|
|
625
|
-
*/
|
|
626
|
-
async function applyRapierPhysicsPatch(starter) {
|
|
627
|
-
const RAPIER = await lazyRapier.get();
|
|
628
|
-
const RapierPhysicsModule = await lazyRapierPhysicsModule.get();
|
|
629
|
-
const RapierDebugRenderer = await lazyRapierDebugModule.get();
|
|
630
|
-
if (RapierPhysics.findInCtx(starter.ctx)) return;
|
|
631
|
-
starter.addModules({
|
|
632
|
-
rapier: new RapierPhysicsModule(RAPIER),
|
|
633
|
-
rapierDebug: new RapierDebugRenderer()
|
|
634
|
-
});
|
|
866
|
+
for (const descriptor of OPTIONAL_MODULES) {
|
|
867
|
+
const config = descriptor.normalize(options.modules?.[descriptor.id]);
|
|
868
|
+
if (!config) continue;
|
|
869
|
+
try {
|
|
870
|
+
modules[descriptor.id] = await descriptor.create(config);
|
|
871
|
+
} catch (error) {
|
|
872
|
+
console.error(`[modules] failed to create "${descriptor.id}"`, error);
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
return modules;
|
|
635
876
|
}
|
|
636
877
|
//#endregion
|
|
637
878
|
//#region packages/world-runtime/src/behaviours/registry.ts
|
|
@@ -664,7 +905,7 @@ var entries = [
|
|
|
664
905
|
{
|
|
665
906
|
uuid: "fpscameracontroller",
|
|
666
907
|
name: "FpsCameraController",
|
|
667
|
-
load: () => loadDefault(import("./FpsCameraController-
|
|
908
|
+
load: () => loadDefault(import("./FpsCameraController-2GNWJM5D.js"))
|
|
668
909
|
},
|
|
669
910
|
{
|
|
670
911
|
uuid: "sightinteractablearea",
|
|
@@ -679,43 +920,43 @@ var entries = [
|
|
|
679
920
|
{
|
|
680
921
|
uuid: "rrb",
|
|
681
922
|
name: "Rigidbody",
|
|
682
|
-
load: () => loadDefault(import("./Rigidbody-
|
|
923
|
+
load: () => loadDefault(import("./Rigidbody-DLhN8ddv.js").then((n) => n.n)),
|
|
683
924
|
requiredPatch: applyRapierPhysicsPatch
|
|
684
925
|
},
|
|
685
926
|
{
|
|
686
927
|
uuid: "rapierballcol",
|
|
687
928
|
name: "BallCollider",
|
|
688
|
-
load: () => loadDefault(import("./BallCollider-
|
|
929
|
+
load: () => loadDefault(import("./BallCollider-3L5vOrcL.js")),
|
|
689
930
|
requiredPatch: applyRapierPhysicsPatch
|
|
690
931
|
},
|
|
691
932
|
{
|
|
692
933
|
uuid: "rapiercapscol",
|
|
693
934
|
name: "CapsuleCollider",
|
|
694
|
-
load: () => loadDefault(import("./CapsuleCollider-
|
|
935
|
+
load: () => loadDefault(import("./CapsuleCollider-CBTsR-Ys.js")),
|
|
695
936
|
requiredPatch: applyRapierPhysicsPatch
|
|
696
937
|
},
|
|
697
938
|
{
|
|
698
939
|
uuid: "rapierconecol",
|
|
699
940
|
name: "ConeCollider",
|
|
700
|
-
load: () => loadDefault(import("./ConeCollider-
|
|
941
|
+
load: () => loadDefault(import("./ConeCollider-C3XU3Y9X.js")),
|
|
701
942
|
requiredPatch: applyRapierPhysicsPatch
|
|
702
943
|
},
|
|
703
944
|
{
|
|
704
945
|
uuid: "rapierconvxcol",
|
|
705
946
|
name: "ConvexMeshCollider",
|
|
706
|
-
load: () => loadDefault(import("./ConvexMeshCollider-
|
|
947
|
+
load: () => loadDefault(import("./ConvexMeshCollider-DzNV4HTO.js")),
|
|
707
948
|
requiredPatch: applyRapierPhysicsPatch
|
|
708
949
|
},
|
|
709
950
|
{
|
|
710
951
|
uuid: "rapiercubcol",
|
|
711
952
|
name: "CuboidCollider",
|
|
712
|
-
load: () => loadDefault(import("./CuboidCollider-
|
|
953
|
+
load: () => loadDefault(import("./CuboidCollider-Dp1DZoFM.js")),
|
|
713
954
|
requiredPatch: applyRapierPhysicsPatch
|
|
714
955
|
},
|
|
715
956
|
{
|
|
716
957
|
uuid: "rapiercylcol",
|
|
717
958
|
name: "CylinderCollider",
|
|
718
|
-
load: () => loadDefault(import("./CylinderCollider-
|
|
959
|
+
load: () => loadDefault(import("./CylinderCollider-C0h_nhB5.js")),
|
|
719
960
|
requiredPatch: applyRapierPhysicsPatch
|
|
720
961
|
},
|
|
721
962
|
{
|
|
@@ -727,7 +968,7 @@ var entries = [
|
|
|
727
968
|
{
|
|
728
969
|
uuid: "rapierfpsplayercontroller",
|
|
729
970
|
name: "RapierFpsPlayerController",
|
|
730
|
-
load: () => loadDefault(import("./RapierFpsPlayerController-
|
|
971
|
+
load: () => loadDefault(import("./RapierFpsPlayerController-DthD8dw9.js")),
|
|
731
972
|
requiredPatch: applyRapierPhysicsPatch
|
|
732
973
|
}
|
|
733
974
|
];
|
|
@@ -803,20 +1044,25 @@ function createFont(url, family, mime, name) {
|
|
|
803
1044
|
return source;
|
|
804
1045
|
}
|
|
805
1046
|
//#endregion
|
|
806
|
-
//#region packages/world-runtime/src/
|
|
1047
|
+
//#region packages/world-runtime/src/addons/lib-registry.ts
|
|
807
1048
|
/**
|
|
808
1049
|
* Registry of built-in libraries enabled via `settings.addons.json → libs`.
|
|
809
1050
|
*
|
|
810
1051
|
* Separates two keys: `id` (short config key under `libs`) and `requireKey`
|
|
811
|
-
* (what the user writes in `require(...)` in scripts
|
|
812
|
-
*
|
|
1052
|
+
* (what the user writes in `require(...)` in scripts — and the import specifier
|
|
1053
|
+
* importers are built from). Single source for:
|
|
1054
|
+
* - game: the app's importer map (`core/game/settings/optional-imports`),
|
|
1055
|
+
* - export: the `imports.libs` block the build emits into the world module,
|
|
813
1056
|
* - editor: JSON schema (`libs` props) + require typings (`AddonsRequireTypings`).
|
|
814
1057
|
*
|
|
815
|
-
* Adding a lib = add an entry here (+
|
|
1058
|
+
* Adding a lib = add an entry here (+ importer in the app, + d.ts loader in editor).
|
|
1059
|
+
* The package itself never imports these — see `./optional-imports.ts`.
|
|
816
1060
|
*/
|
|
817
1061
|
var LibId = /* @__PURE__ */ function(LibId) {
|
|
818
1062
|
LibId["Tween"] = "tween";
|
|
819
1063
|
LibId["Anime"] = "anime";
|
|
1064
|
+
LibId["Nipplejs"] = "nipplejs";
|
|
1065
|
+
LibId["Stats"] = "stats";
|
|
820
1066
|
return LibId;
|
|
821
1067
|
}({});
|
|
822
1068
|
var LIBS = {
|
|
@@ -828,7 +1074,20 @@ var LIBS = {
|
|
|
828
1074
|
["anime"]: {
|
|
829
1075
|
id: "anime",
|
|
830
1076
|
requireKey: "animejs/animation",
|
|
1077
|
+
packageName: "animejs",
|
|
831
1078
|
label: "Anime.js (animate)"
|
|
1079
|
+
},
|
|
1080
|
+
["nipplejs"]: {
|
|
1081
|
+
id: "nipplejs",
|
|
1082
|
+
requireKey: "nipplejs",
|
|
1083
|
+
label: "nipplejs (on-screen joystick)",
|
|
1084
|
+
defaultExport: true
|
|
1085
|
+
},
|
|
1086
|
+
["stats"]: {
|
|
1087
|
+
id: "stats",
|
|
1088
|
+
requireKey: "stats.js",
|
|
1089
|
+
label: "Stats.js (fps overlay)",
|
|
1090
|
+
defaultExport: true
|
|
832
1091
|
}
|
|
833
1092
|
};
|
|
834
1093
|
var LIB_IDS = Object.values(LibId);
|
|
@@ -837,47 +1096,71 @@ function libByRequireKey(requireKey) {
|
|
|
837
1096
|
return LIB_DESCRIPTORS.find((l) => l.requireKey === requireKey);
|
|
838
1097
|
}
|
|
839
1098
|
//#endregion
|
|
840
|
-
//#region packages/world-runtime/src/
|
|
1099
|
+
//#region packages/world-runtime/src/addons/AddonsRuntime.ts
|
|
841
1100
|
/**
|
|
842
|
-
*
|
|
843
|
-
*
|
|
844
|
-
*
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
//#endregion
|
|
851
|
-
//#region packages/world-runtime/src/settings/AddonsRuntime.ts
|
|
852
|
-
/**
|
|
853
|
-
* Runtime loading of addons (game-only): enabled built-in libs + external
|
|
854
|
-
* scripts (externals). Exposes them to user scripts via `resolve(key)`
|
|
855
|
-
* (used in `ScriptsRuntime.require`). Replaces the old
|
|
856
|
-
* `GameLoader.loadExternalsAsync` / `getExternalLib`.
|
|
1101
|
+
* External dependencies of the world: built-in libs, externals and the physics
|
|
1102
|
+
* engine. Loaded before the world starts; scripts reach them through
|
|
1103
|
+
* `resolve(key)` (`require`), the physics module through `rapier`.
|
|
1104
|
+
*
|
|
1105
|
+
* This is the `addons` ctx module, but it also works without a ctx: launcher
|
|
1106
|
+
* worlds have no 3d context while `require` still has to work, so there the
|
|
1107
|
+
* instance is used directly. How to import packages is up to the runtime owner
|
|
1108
|
+
* (see `WorldImports`).
|
|
857
1109
|
*/
|
|
858
|
-
var AddonsRuntime = class {
|
|
1110
|
+
var AddonsRuntime = class extends ContextModule {
|
|
1111
|
+
_imports;
|
|
859
1112
|
/** libs — by requireKey (`@tweenjs/tween.js`), externals — by accessor (`Tone`). */
|
|
860
1113
|
_byKey = /* @__PURE__ */ new Map();
|
|
861
1114
|
_scriptTags = [];
|
|
862
|
-
|
|
1115
|
+
_rapier = null;
|
|
1116
|
+
constructor(_imports = {}) {
|
|
1117
|
+
super();
|
|
1118
|
+
this._imports = _imports;
|
|
1119
|
+
}
|
|
1120
|
+
/** `physics` — whether the world needs rapier (config or a physics behaviour). */
|
|
1121
|
+
async load(addons, options = {}) {
|
|
863
1122
|
await this._loadLibs(addons.libs);
|
|
864
1123
|
await this._loadExternals(addons.externals);
|
|
1124
|
+
if (options.physics) await this._loadRapier();
|
|
865
1125
|
}
|
|
866
1126
|
/** Resolves a require key: enabled lib or external accessor; undefined otherwise. */
|
|
867
1127
|
resolve(key) {
|
|
868
1128
|
return this._byKey.get(key);
|
|
869
1129
|
}
|
|
1130
|
+
/** The physics engine, loaded and initialized; `null` in worlds without physics. */
|
|
1131
|
+
get rapier() {
|
|
1132
|
+
return this._rapier;
|
|
1133
|
+
}
|
|
870
1134
|
async _loadLibs(libs) {
|
|
871
1135
|
if (!libs) return;
|
|
872
1136
|
for (const id of LIB_IDS) {
|
|
873
1137
|
if (!libs[id]) continue;
|
|
1138
|
+
const load = this._imports.libs?.[id];
|
|
1139
|
+
if (!load) {
|
|
1140
|
+
console.error(`[addons] lib "${id}" is enabled but has no importer`);
|
|
1141
|
+
continue;
|
|
1142
|
+
}
|
|
874
1143
|
try {
|
|
875
|
-
this._byKey.set(LIBS[id].requireKey, await
|
|
1144
|
+
this._byKey.set(LIBS[id].requireKey, unwrapLib(id, await load()));
|
|
876
1145
|
} catch (e) {
|
|
877
1146
|
console.error(`[addons] failed to load lib "${id}"`, e);
|
|
878
1147
|
}
|
|
879
1148
|
}
|
|
880
1149
|
}
|
|
1150
|
+
async _loadRapier() {
|
|
1151
|
+
const load = this._imports.rapier;
|
|
1152
|
+
if (!load) {
|
|
1153
|
+
console.error("[addons] the world needs physics but has no importer for \"@dimforge/rapier3d-compat\"");
|
|
1154
|
+
return;
|
|
1155
|
+
}
|
|
1156
|
+
try {
|
|
1157
|
+
const rapier = await load();
|
|
1158
|
+
await rapier.init();
|
|
1159
|
+
this._rapier = rapier;
|
|
1160
|
+
} catch (e) {
|
|
1161
|
+
console.error("[addons] failed to load rapier", e);
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
881
1164
|
async _loadExternals(externals) {
|
|
882
1165
|
if (!externals) return;
|
|
883
1166
|
for (const ext of externals) try {
|
|
@@ -907,36 +1190,14 @@ var AddonsRuntime = class {
|
|
|
907
1190
|
for (const tag of this._scriptTags) tag.remove();
|
|
908
1191
|
this._scriptTags.length = 0;
|
|
909
1192
|
this._byKey.clear();
|
|
1193
|
+
this._rapier = null;
|
|
910
1194
|
}
|
|
911
1195
|
};
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
* Feature toggles (chat/joystick/debugPerf) are NOT here — they live in
|
|
918
|
-
* `settings.json` (see `../features/feature-registry`).
|
|
919
|
-
*
|
|
920
|
-
* The only module so far is physics.
|
|
921
|
-
* Installation lives in game (`game/settings/module-installers`).
|
|
922
|
-
*/
|
|
923
|
-
var ModuleId = /* @__PURE__ */ function(ModuleId) {
|
|
924
|
-
ModuleId["Physics"] = "physics";
|
|
925
|
-
return ModuleId;
|
|
926
|
-
}({});
|
|
927
|
-
var PHYSICS_ENGINES = ["rapier"];
|
|
928
|
-
var MODULE_IDS = Object.values(ModuleId);
|
|
929
|
-
/** Normalizes the physics config to an object (or null if disabled). */
|
|
930
|
-
function normalizePhysics(value) {
|
|
931
|
-
if (!value) return null;
|
|
932
|
-
if (typeof value === "string") return {
|
|
933
|
-
engine: value,
|
|
934
|
-
debug: false
|
|
935
|
-
};
|
|
936
|
-
return {
|
|
937
|
-
engine: value.engine,
|
|
938
|
-
debug: value.debug ?? false
|
|
939
|
-
};
|
|
1196
|
+
/** A package like nipplejs exposes its API as the default export — that is what
|
|
1197
|
+
* `require` should hand back. */
|
|
1198
|
+
function unwrapLib(id, module) {
|
|
1199
|
+
if (!LIBS[id].defaultExport) return module;
|
|
1200
|
+
return module.default ?? module;
|
|
940
1201
|
}
|
|
941
1202
|
//#endregion
|
|
942
1203
|
//#region packages/world-runtime/src/WorldRuntime.ts
|
|
@@ -949,17 +1210,24 @@ var WorldRuntime = class {
|
|
|
949
1210
|
options;
|
|
950
1211
|
/** World lifecycle event bus (`world.events` in scripts). */
|
|
951
1212
|
emitter = new EventEmitter();
|
|
952
|
-
_addons
|
|
1213
|
+
_addons;
|
|
953
1214
|
_executedScripts = /* @__PURE__ */ new Map();
|
|
954
1215
|
_builtins = /* @__PURE__ */ new Map();
|
|
955
1216
|
_styleElements = [];
|
|
956
1217
|
_resources = {};
|
|
1218
|
+
/** Reverse index for `getUrl(resource)` — play mode resolves objects too. */
|
|
1219
|
+
_pathByResource = /* @__PURE__ */ new Map();
|
|
957
1220
|
_ctx = null;
|
|
958
1221
|
_bloom = null;
|
|
959
1222
|
_scriptContext = null;
|
|
960
1223
|
constructor(definition, options) {
|
|
961
1224
|
this.definition = definition;
|
|
962
1225
|
this.options = options;
|
|
1226
|
+
this._addons = new AddonsRuntime(definition.imports);
|
|
1227
|
+
}
|
|
1228
|
+
/** The world's external dependencies; also the `addons` ctx module. */
|
|
1229
|
+
get addons() {
|
|
1230
|
+
return this._addons;
|
|
963
1231
|
}
|
|
964
1232
|
/** ctx appears at the 3d-context creation step; unavailable to scripts before that. */
|
|
965
1233
|
get ctx() {
|
|
@@ -974,14 +1242,23 @@ var WorldRuntime = class {
|
|
|
974
1242
|
setBloom(bloom) {
|
|
975
1243
|
this._bloom = bloom;
|
|
976
1244
|
}
|
|
977
|
-
/** @internal Built-in libs and
|
|
1245
|
+
/** @internal Built-in libs, externals and the physics engine, if the world needs one. */
|
|
978
1246
|
async loadAddons() {
|
|
979
|
-
|
|
980
|
-
|
|
1247
|
+
await this._addons.load(this.definition.addons ?? {}, { physics: this._usesPhysics() });
|
|
1248
|
+
}
|
|
1249
|
+
/** Physics is on in the config, or a built-in behaviour on the scene needs it. */
|
|
1250
|
+
_usesPhysics() {
|
|
1251
|
+
if (normalizePhysics(this.definition.addons?.modules?.physics)) return true;
|
|
1252
|
+
return (this.definition.usedBuiltins ?? []).some((uuid) => getBuiltinBehaviour(uuid)?.requiredPatch);
|
|
981
1253
|
}
|
|
982
1254
|
/** @internal World resources by asset path — result of `loadAssets`. */
|
|
983
1255
|
setResources(resources) {
|
|
984
1256
|
this._resources = resources;
|
|
1257
|
+
this._pathByResource.clear();
|
|
1258
|
+
for (const path in resources) {
|
|
1259
|
+
const resource = resources[path];
|
|
1260
|
+
if (resource !== null && typeof resource === "object") this._pathByResource.set(resource, path);
|
|
1261
|
+
}
|
|
985
1262
|
}
|
|
986
1263
|
reportProgress(stage, details) {
|
|
987
1264
|
const progress = {
|
|
@@ -990,9 +1267,11 @@ var WorldRuntime = class {
|
|
|
990
1267
|
};
|
|
991
1268
|
this.options.onProgress?.(progress);
|
|
992
1269
|
}
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
1270
|
+
/** Accepts an asset path or a resource returned by `require` — like play mode. */
|
|
1271
|
+
getUrl = (source) => {
|
|
1272
|
+
const path = typeof source === "string" ? source : this._pathByResource.get(source);
|
|
1273
|
+
const url = path === void 0 ? void 0 : this.definition.assets[path];
|
|
1274
|
+
if (url === void 0) throw new Error(`World runtime: unknown asset "${typeof source === "string" ? source : path ?? "<unknown resource>"}"`);
|
|
996
1275
|
if (isAbsoluteUrl(url)) return url;
|
|
997
1276
|
return `${this.options.assetsBaseUrl ?? ""}${url}`;
|
|
998
1277
|
};
|
|
@@ -1032,11 +1311,6 @@ var WorldRuntime = class {
|
|
|
1032
1311
|
console.error(`Required patch of "${uuid}" failed`, error);
|
|
1033
1312
|
}
|
|
1034
1313
|
}
|
|
1035
|
-
if (normalizePhysics(this.definition.addons?.modules?.physics)) try {
|
|
1036
|
-
await applyRapierPhysicsPatch(starter);
|
|
1037
|
-
} catch (error) {
|
|
1038
|
-
console.error("Physics module patch failed", error);
|
|
1039
|
-
}
|
|
1040
1314
|
}
|
|
1041
1315
|
/** Addon (built-in lib / external) → script by path → asset resource. Same
|
|
1042
1316
|
* resolve order as in play mode. */
|
|
@@ -1050,7 +1324,8 @@ var WorldRuntime = class {
|
|
|
1050
1324
|
};
|
|
1051
1325
|
/**
|
|
1052
1326
|
* Runtime spawn: a prefab (build-time factory) is assembled anew, a regular
|
|
1053
|
-
* object is cloned.
|
|
1327
|
+
* object is cloned. The instance is only created and returned — adding it to
|
|
1328
|
+
* the scene hierarchy is up to the caller (`ctx.scene.add(obj)`).
|
|
1054
1329
|
*/
|
|
1055
1330
|
instantiate = (source, options) => {
|
|
1056
1331
|
const object = typeof source === "function" ? source() : clone(source);
|
|
@@ -1144,6 +1419,10 @@ var WorldRuntime = class {
|
|
|
1144
1419
|
get ctx() {
|
|
1145
1420
|
return runtime.ctx;
|
|
1146
1421
|
},
|
|
1422
|
+
/** Short path to the chat module, same as in play mode. */
|
|
1423
|
+
get chat() {
|
|
1424
|
+
return runtime.ctx.modules.chat;
|
|
1425
|
+
},
|
|
1147
1426
|
events: this.emitter,
|
|
1148
1427
|
emitter: this.emitter,
|
|
1149
1428
|
rendering: { get bloom() {
|
|
@@ -1155,7 +1434,11 @@ var WorldRuntime = class {
|
|
|
1155
1434
|
getUrl: this.getUrl,
|
|
1156
1435
|
THREE,
|
|
1157
1436
|
TSL,
|
|
1158
|
-
van
|
|
1437
|
+
van,
|
|
1438
|
+
/** Physics engine global; null in worlds without physics. */
|
|
1439
|
+
get RAPIER() {
|
|
1440
|
+
return runtime.addons.rapier;
|
|
1441
|
+
}
|
|
1159
1442
|
};
|
|
1160
1443
|
return this._scriptContext;
|
|
1161
1444
|
}
|
|
@@ -1177,7 +1460,7 @@ function isAbsoluteUrl(url) {
|
|
|
1177
1460
|
}
|
|
1178
1461
|
//#endregion
|
|
1179
1462
|
//#region packages/world-runtime/src/version.gen.ts
|
|
1180
|
-
var RUNTIME_VERSION = "0.
|
|
1463
|
+
var RUNTIME_VERSION = "0.3.2";
|
|
1181
1464
|
//#endregion
|
|
1182
1465
|
//#region packages/world-runtime/src/version.ts
|
|
1183
1466
|
/**
|
|
@@ -1243,7 +1526,10 @@ async function createWorld(definition, runtime) {
|
|
|
1243
1526
|
scene: new THREE.Scene(),
|
|
1244
1527
|
autoInitRenderer: false
|
|
1245
1528
|
}, runtime);
|
|
1246
|
-
world.addModules(createWorldModules("prod"
|
|
1529
|
+
world.addModules(await createWorldModules("prod", {
|
|
1530
|
+
addons: runtime.addons,
|
|
1531
|
+
modules: runtime.definition.addons?.modules
|
|
1532
|
+
}));
|
|
1247
1533
|
applyRendererConfig(renderer, config);
|
|
1248
1534
|
if (config.bloom?.enabled) {
|
|
1249
1535
|
if (config.bloom.mode === "emissive") enableSceneEmissiveMrt(world.ctx.scenePass);
|
|
@@ -1678,7 +1964,7 @@ var UNIFORM_DEFAULTS = {
|
|
|
1678
1964
|
texture: () => getDefaultResource(DefaultResourceId.Texture)
|
|
1679
1965
|
};
|
|
1680
1966
|
//#endregion
|
|
1681
|
-
//#region packages/world-runtime/src/
|
|
1967
|
+
//#region packages/world-runtime/src/addons/externals.ts
|
|
1682
1968
|
var EXTERNAL_TYPES = ["umd", "module"];
|
|
1683
1969
|
//#endregion
|
|
1684
1970
|
//#region packages/world-runtime/src/rendering/renderer-options.ts
|
|
@@ -1704,4 +1990,4 @@ var TONE_MAPPING_NAMES = [
|
|
|
1704
1990
|
"Neutral"
|
|
1705
1991
|
];
|
|
1706
1992
|
//#endregion
|
|
1707
|
-
export { AddonsRuntime, AssetLoaders, AudioSource, BLOOM_MODES, BUILTIN_BEHAVIOURS, BUILTIN_BEHAVIOUR_LIST, DEFAULT_RESOURCE_IDS, DefaultResourceId, EXTERNAL_TYPES, EnvModule, FontSource, HtmlHudModule, InputSystemModule, LIBS, LIB_DESCRIPTORS, LIB_IDS, LOADING_MANAGER, LibId,
|
|
1993
|
+
export { AddonsRuntime, AssetLoaders, AudioSource, BLOOM_MODES, BUILTIN_BEHAVIOURS, BUILTIN_BEHAVIOUR_LIST, CHAT_INPUT_MODES, DEFAULT_RESOURCE_IDS, DefaultResourceId, EXTERNAL_TYPES, EnvModule, FontSource, HtmlHudModule, InputSystemModule, LIBS, LIB_DESCRIPTORS, LIB_IDS, LOADING_MANAGER, LibId, OPTIONAL_MODULES, OPTIONAL_MODULE_IDS, OptionalModuleId, PHYSICS_ENGINES, RUNTIME_VERSION, SHADOW_MAP_TYPES, SceneBloom, SoundsModule, TONE_MAPPING_NAMES, TslNode, UNIFORM_DEFAULTS, UNIFORM_TYPES, UtilsModule, VideoSource, WORLD_FORMAT_VERSION, World, WorldEvent, applyRapierPhysicsPatch, applyRendererConfig, applyTextureDefaults, collectModelSubResources, configureAssetLoaders, createAudio, createFont, createTslNode, createUniformNode, createVideo, createWorldModules, defineWorld, enableSceneEmissiveMrt, fontFormatFromMime, getBuiltinBehaviour, getDefaultResource, getHalfheightRadiusScale, getOptionalModuleDescriptor, getRadiusScale, isDefaultResourceId, isMobileDevice, isUniformType, libByRequireKey, loadGeometry, loadHdr, loadModel, loadTexture, normalizeChat, normalizePhysics, parseGeometryJson, parseHdr, parseModel, setColor, uniformType };
|