@gg-web-engine/core 0.0.58 → 0.0.60
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/2d/components/rendering/i-camera-2d.component.d.ts +5 -0
- package/dist/2d/components/rendering/i-visual-scene-2d.component.d.ts +1 -2
- package/dist/2d/entities/renderer-2d.entity.d.ts +2 -1
- package/dist/2d/gg-2d-world.d.ts +5 -1
- package/dist/2d/gg-2d-world.js +57 -2
- package/dist/2d/index.d.ts +3 -0
- package/dist/2d/index.js +3 -0
- package/dist/2d/level-loader.d.ts +101 -0
- package/dist/2d/level-loader.js +88 -0
- package/dist/2d/loader.d.ts +11 -0
- package/dist/2d/loader.js +10 -0
- package/dist/3d/components/rendering/{i-camera.component.d.ts → i-camera-3d.component.d.ts} +1 -1
- package/dist/3d/components/rendering/i-camera-3d.component.js +1 -0
- package/dist/3d/components/rendering/i-renderer-3d.component.d.ts +0 -1
- package/dist/3d/components/rendering/i-visual-scene-3d.component.d.ts +1 -2
- package/dist/3d/entities/camera-3d.entity.d.ts +24 -0
- package/dist/3d/entities/camera-3d.entity.js +35 -0
- package/dist/3d/entities/renderer-3d.entity.d.ts +0 -5
- package/dist/3d/entities/renderer-3d.entity.js +0 -15
- package/dist/3d/gg-3d-world.d.ts +2 -2
- package/dist/3d/gg-3d-world.js +73 -0
- package/dist/3d/index.d.ts +3 -1
- package/dist/3d/index.js +3 -1
- package/dist/3d/level-loader.d.ts +300 -0
- package/dist/3d/level-loader.js +267 -0
- package/dist/3d/loader.d.ts +44 -3
- package/dist/3d/loader.js +41 -4
- package/dist/3d/models/gg-meta.d.ts +10 -0
- package/dist/3d/models/gg-meta.js +2 -1
- package/dist/base/blueprint/blueprint-node.d.ts +75 -0
- package/dist/base/blueprint/blueprint-node.js +51 -0
- package/dist/base/blueprint/blueprint.d.ts +123 -0
- package/dist/base/blueprint/blueprint.js +86 -0
- package/dist/base/blueprint/nodes/remove-entity.node.d.ts +26 -0
- package/dist/base/blueprint/nodes/remove-entity.node.js +29 -0
- package/dist/base/clock/pausable-clock.d.ts +12 -0
- package/dist/base/clock/pausable-clock.js +24 -0
- package/dist/base/components/rendering/i-renderer.component.d.ts +1 -0
- package/dist/base/components/rendering/i-visual-scene.component.d.ts +2 -0
- package/dist/base/entities/group.entity.d.ts +17 -0
- package/dist/base/entities/group.entity.js +19 -0
- package/dist/base/entities/i-entity.d.ts +11 -0
- package/dist/base/entities/i-entity.js +28 -0
- package/dist/base/entities/i-renderer.entity.d.ts +7 -1
- package/dist/base/entities/i-renderer.entity.js +15 -0
- package/dist/base/gg-world.d.ts +13 -0
- package/dist/base/gg-world.js +83 -1
- package/dist/base/index.d.ts +5 -0
- package/dist/base/index.js +5 -0
- package/dist/base/inputs/mouse.input.js +1 -0
- package/dist/base/level-loader.d.ts +217 -0
- package/dist/base/level-loader.js +258 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +10 -9
- package/tsconfig.json +4 -1
- package/blender_exporter/build_blender_scene.py +0 -179
- package/blender_exporter/spherical_to_cube_texture.py +0 -116
- /package/dist/{3d/components/rendering/i-camera.component.js → 2d/components/rendering/i-camera-2d.component.js} +0 -0
|
@@ -3,11 +3,12 @@ import { GgWorld, GgWorldTypeDocVPatch, VisualTypeDocRepo } from '../gg-world';
|
|
|
3
3
|
import { BehaviorSubject, Observable } from 'rxjs';
|
|
4
4
|
import { Point2 } from '../models/points';
|
|
5
5
|
import { RendererOptions } from '../components/rendering/i-renderer.component';
|
|
6
|
+
import { IPositionable } from '../interfaces/i-positionable';
|
|
6
7
|
/**
|
|
7
8
|
* Represents a base class for a renderer entity.
|
|
8
9
|
* @class
|
|
9
10
|
*/
|
|
10
|
-
export declare abstract class IRendererEntity<D, R, VTypeDoc extends VisualTypeDocRepo<D, R> = VisualTypeDocRepo<D, R>> extends IEntity<D, R, GgWorldTypeDocVPatch<D, R, VTypeDoc>> {
|
|
11
|
+
export declare abstract class IRendererEntity<D, R, VTypeDoc extends VisualTypeDocRepo<D, R> = VisualTypeDocRepo<D, R>> extends IEntity<D, R, GgWorldTypeDocVPatch<D, R, VTypeDoc>> implements IPositionable<D, R> {
|
|
11
12
|
readonly renderer: VTypeDoc['renderer'];
|
|
12
13
|
readonly tickOrder = TickOrder.RENDERING;
|
|
13
14
|
/** Represents the current size of the renderer. */
|
|
@@ -23,6 +24,11 @@ export declare abstract class IRendererEntity<D, R, VTypeDoc extends VisualTypeD
|
|
|
23
24
|
*/
|
|
24
25
|
get rendererSize(): Point2 | null;
|
|
25
26
|
get rendererOptions(): RendererOptions;
|
|
27
|
+
get camera(): VTypeDoc['camera'];
|
|
28
|
+
get position(): D;
|
|
29
|
+
set position(value: D);
|
|
30
|
+
get rotation(): R;
|
|
31
|
+
set rotation(value: R);
|
|
26
32
|
/** get flag whether renderer shows physics debugger view */
|
|
27
33
|
get physicsDebugViewActive(): boolean;
|
|
28
34
|
/** turns on/off physics debugger view for this renderer */
|
|
@@ -23,6 +23,21 @@ export class IRendererEntity extends IEntity {
|
|
|
23
23
|
get rendererOptions() {
|
|
24
24
|
return this.renderer.rendererOptions;
|
|
25
25
|
}
|
|
26
|
+
get camera() {
|
|
27
|
+
return this.renderer.camera;
|
|
28
|
+
}
|
|
29
|
+
get position() {
|
|
30
|
+
return this.camera.position;
|
|
31
|
+
}
|
|
32
|
+
set position(value) {
|
|
33
|
+
this.renderer.camera.position = value;
|
|
34
|
+
}
|
|
35
|
+
get rotation() {
|
|
36
|
+
return this.renderer.camera.rotation;
|
|
37
|
+
}
|
|
38
|
+
set rotation(value) {
|
|
39
|
+
this.renderer.camera.rotation = value;
|
|
40
|
+
}
|
|
26
41
|
/** get flag whether renderer shows physics debugger view */
|
|
27
42
|
get physicsDebugViewActive() {
|
|
28
43
|
return this.renderer.physicsDebugViewActive;
|
package/dist/base/gg-world.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ export type VisualTypeDocRepo<D, R> = {
|
|
|
6
6
|
displayObject: IDisplayObjectComponent<D, R>;
|
|
7
7
|
renderer: IRendererComponent<D, R>;
|
|
8
8
|
rendererExtraOpts: {};
|
|
9
|
+
camera: IPositionable<D, R>;
|
|
9
10
|
};
|
|
10
11
|
export type PhysicsTypeDocRepo<D, R> = {
|
|
11
12
|
factory: unknown;
|
|
@@ -32,6 +33,8 @@ export type GgWorldSceneTypeDocVPatch<D, R, VTypeDoc extends VisualTypeDocRepo2D
|
|
|
32
33
|
export type GgWorldSceneTypeDocPPatch<D, R, PTypeDoc extends PhysicsTypeDocRepo<D, R>, PW extends IPhysicsWorldComponent<D, R, PTypeDoc> | null> = Omit<GgWorldSceneTypeRepo<D, R>, 'physicsWorld'> & {
|
|
33
34
|
physicsWorld: PW;
|
|
34
35
|
};
|
|
36
|
+
export type TypeDocOf<W extends GgWorld<any, any>> = W extends GgWorld<infer D, infer R, infer TypeDoc> ? TypeDoc : never;
|
|
37
|
+
export type SceneTypeDocOf<W extends GgWorld<any, any>> = W extends GgWorld<infer D, infer R, infer TypeDoc, infer SceneTypeDoc> ? SceneTypeDoc : never;
|
|
35
38
|
export declare abstract class GgWorld<D, R, TypeDoc extends GgWorldTypeDocRepo<D, R> = GgWorldTypeDocRepo<D, R>, SceneTypeDoc extends GgWorldSceneTypeRepo<D, R, TypeDoc> = GgWorldSceneTypeRepo<D, R, TypeDoc>> {
|
|
36
39
|
private static default_name_counter;
|
|
37
40
|
private static _documentWorlds;
|
|
@@ -67,6 +70,16 @@ export declare abstract class GgWorld<D, R, TypeDoc extends GgWorldTypeDocRepo<D
|
|
|
67
70
|
position?: D, rotation?: R, material?: unknown): IPositionable<D, R> & IRenderableEntity<D, R, TypeDoc>;
|
|
68
71
|
addEntity(entity: IEntity): void;
|
|
69
72
|
removeEntity(entity: IEntity, dispose?: boolean): void;
|
|
73
|
+
/**
|
|
74
|
+
* Find an entity anywhere in the world by name. `children` is a flat list of every entity ever
|
|
75
|
+
* added via `addEntity` (nested entities included - `addChildren`/`onSpawned` cascade into it
|
|
76
|
+
* too), so this is a plain linear scan, not a tree walk; to search inside one particular
|
|
77
|
+
* entity's own subtree instead, use `IEntity.getChildEntityByName`.
|
|
78
|
+
* @param name - The entity's `name`
|
|
79
|
+
* @returns The first entity found with that name (insertion order), if more than one shares it
|
|
80
|
+
* @throws if no entity in the world has that name
|
|
81
|
+
*/
|
|
82
|
+
getEntityByName<T extends IEntity = IEntity>(name: string): T;
|
|
70
83
|
private onGgStaticInitialized;
|
|
71
84
|
protected registerConsoleCommands(ggstatic: {
|
|
72
85
|
registerConsoleCommand: (world: GgWorld<any, any> | null, command: string, handler: (...args: string[]) => Promise<string>, doc?: string) => void;
|
package/dist/base/gg-world.js
CHANGED
|
@@ -134,7 +134,13 @@ export class GgWorld {
|
|
|
134
134
|
this.disposed$.complete();
|
|
135
135
|
}
|
|
136
136
|
addEntity(entity) {
|
|
137
|
-
if (
|
|
137
|
+
if (entity.world === this) {
|
|
138
|
+
// Already a member of this world - e.g. reparented (via addChildren) after having been
|
|
139
|
+
// added directly, as level-loaded entities are. Not an error: just a no-op, since
|
|
140
|
+
// addChildren already updated the parent/children bookkeeping before calling back in here.
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
if (entity.world) {
|
|
138
144
|
console.warn('Trying to spawn entity, which is already spawned');
|
|
139
145
|
return;
|
|
140
146
|
}
|
|
@@ -156,6 +162,22 @@ export class GgWorld {
|
|
|
156
162
|
entity.dispose();
|
|
157
163
|
}
|
|
158
164
|
}
|
|
165
|
+
/**
|
|
166
|
+
* Find an entity anywhere in the world by name. `children` is a flat list of every entity ever
|
|
167
|
+
* added via `addEntity` (nested entities included - `addChildren`/`onSpawned` cascade into it
|
|
168
|
+
* too), so this is a plain linear scan, not a tree walk; to search inside one particular
|
|
169
|
+
* entity's own subtree instead, use `IEntity.getChildEntityByName`.
|
|
170
|
+
* @param name - The entity's `name`
|
|
171
|
+
* @returns The first entity found with that name (insertion order), if more than one shares it
|
|
172
|
+
* @throws if no entity in the world has that name
|
|
173
|
+
*/
|
|
174
|
+
getEntityByName(name) {
|
|
175
|
+
const found = this.children.find(e => e.name === name);
|
|
176
|
+
if (!found) {
|
|
177
|
+
throw new Error(`No entity named "${name}" found in the world`);
|
|
178
|
+
}
|
|
179
|
+
return found;
|
|
180
|
+
}
|
|
159
181
|
onGgStaticInitialized() {
|
|
160
182
|
window.removeEventListener('ggstatic_added', this.onGgStaticInitialized);
|
|
161
183
|
this.registerConsoleCommands(window.ggstatic);
|
|
@@ -174,6 +196,19 @@ export class GgWorld {
|
|
|
174
196
|
}
|
|
175
197
|
return this.worldClock.tickRateLimit.toString();
|
|
176
198
|
}), 'args: [ int? ]; Get current tick rate limit of selected world clock or set it. 0 means no limit applied');
|
|
199
|
+
ggstatic.registerConsoleCommand(this, 'step', (...args) => __awaiter(this, void 0, void 0, function* () {
|
|
200
|
+
if (!this.worldClock.isPaused) {
|
|
201
|
+
throw new Error('World must be paused first (run "timescale 0") before it can be stepped');
|
|
202
|
+
}
|
|
203
|
+
const ms = args[0] === undefined ? 1000 / 120 : +args[0];
|
|
204
|
+
if (isNaN(ms) || ms <= 0) {
|
|
205
|
+
throw new Error('usage: step [ms]; ms must be a positive number');
|
|
206
|
+
}
|
|
207
|
+
this.worldClock.step(ms);
|
|
208
|
+
return `stepped ${ms} ms`;
|
|
209
|
+
}), 'args: [ float? ]; Advance a paused world clock by exactly one tick of the given duration ' +
|
|
210
|
+
'in milliseconds (default 8, i.e. 1000/120). Only works while the world is paused via ' +
|
|
211
|
+
'"timescale 0"; rejects otherwise');
|
|
177
212
|
ggstatic.registerConsoleCommand(this, 'renderers', () => __awaiter(this, void 0, void 0, function* () {
|
|
178
213
|
return this.renderers.map(r => r.name).join('\n');
|
|
179
214
|
}), 'no args; Print all renderers in selected world');
|
|
@@ -236,6 +271,53 @@ export class GgWorld {
|
|
|
236
271
|
}), 'args: [ int?, avg|peak? ]; Measure how much time was spent per ' +
|
|
237
272
|
'entity in world. Arguments are samples amount (20 by default) and "peak" or "avg" choice, both arguments are ' +
|
|
238
273
|
'optional. "avg" report sorts entities by average time consumed, "peak" records highest value for each entity');
|
|
274
|
+
ggstatic.registerConsoleCommand(this, 'entities', (...args) => __awaiter(this, void 0, void 0, function* () {
|
|
275
|
+
var _a;
|
|
276
|
+
const filter = (_a = args[0]) === null || _a === void 0 ? void 0 : _a.toLowerCase();
|
|
277
|
+
const list = this.children.filter(e => !filter || e.name.toLowerCase().includes(filter));
|
|
278
|
+
if (list.length === 0) {
|
|
279
|
+
return '<span style="color:#aaa">(no entities)</span>';
|
|
280
|
+
}
|
|
281
|
+
return list
|
|
282
|
+
.map(e => `<span style='color:yellow'>${e.name}</span>\t<span style='color:#aaa'>${e.constructor.name}</span>`)
|
|
283
|
+
.join('\n');
|
|
284
|
+
}), 'args: [ string? ]; List all entities in this world (name and class), optionally filtered by ' +
|
|
285
|
+
'a case-insensitive substring of the name. Use "entity <name>" to inspect one of them');
|
|
286
|
+
ggstatic.registerConsoleCommand(this, 'entity', (...args) => __awaiter(this, void 0, void 0, function* () {
|
|
287
|
+
const name = args[0];
|
|
288
|
+
if (!name) {
|
|
289
|
+
throw new Error('usage: entity <name>; use "entities" to list available names');
|
|
290
|
+
}
|
|
291
|
+
const entity = this.getEntityByName(name);
|
|
292
|
+
const lines = [
|
|
293
|
+
`class: ${entity.constructor.name}`,
|
|
294
|
+
`active: ${entity.active}`,
|
|
295
|
+
`parent: ${entity.parent ? entity.parent.name : '(none)'}`,
|
|
296
|
+
];
|
|
297
|
+
if ('visible' in entity) {
|
|
298
|
+
lines.push(`visible: ${entity.visible}`);
|
|
299
|
+
}
|
|
300
|
+
if ('position' in entity) {
|
|
301
|
+
lines.push(`position: ${JSON.stringify(entity.position)}`);
|
|
302
|
+
}
|
|
303
|
+
if ('rotation' in entity) {
|
|
304
|
+
lines.push(`rotation: ${JSON.stringify(entity.rotation)}`);
|
|
305
|
+
}
|
|
306
|
+
lines.push(`children: ${entity.children.length === 0 ? '(none)' : entity.children.map(c => c.name).join(', ')}`);
|
|
307
|
+
return lines.join('\n');
|
|
308
|
+
}), 'args: [ string ]; Print class, position/rotation (if any) and children of one entity. Use ' +
|
|
309
|
+
'"entities" to list available names, "set_position"/"set_rotation" to move it');
|
|
310
|
+
ggstatic.registerConsoleCommand(this, 'remove', (...args) => __awaiter(this, void 0, void 0, function* () {
|
|
311
|
+
const name = args[0];
|
|
312
|
+
if (!name) {
|
|
313
|
+
throw new Error('usage: remove <name> [dispose=0|1]');
|
|
314
|
+
}
|
|
315
|
+
const entity = this.getEntityByName(name);
|
|
316
|
+
const dispose = args[1] === undefined ? true : args[1] === '1';
|
|
317
|
+
this.removeEntity(entity, dispose);
|
|
318
|
+
return `removed "${name}"`;
|
|
319
|
+
}), 'args: [ string, 0|1? ]; Remove the named entity from this world, disposing it by default. ' +
|
|
320
|
+
'Pass 0 as second arg to detach without disposing (e.g. before re-adding it elsewhere)');
|
|
239
321
|
}
|
|
240
322
|
}
|
|
241
323
|
GgWorld.default_name_counter = 0;
|
package/dist/base/index.d.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
export * from './blueprint/blueprint';
|
|
2
|
+
export * from './blueprint/blueprint-node';
|
|
3
|
+
export * from './blueprint/nodes/remove-entity.node';
|
|
1
4
|
export * from './clock/global-clock';
|
|
2
5
|
export * from './clock/i-clock';
|
|
3
6
|
export * from './clock/pausable-clock';
|
|
@@ -14,6 +17,7 @@ export * from './data-structures/bitmask';
|
|
|
14
17
|
export * from './data-structures/graph';
|
|
15
18
|
export * from './entities/controllers/animation-mixer';
|
|
16
19
|
export * from './entities/controllers/inline-controller';
|
|
20
|
+
export * from './entities/group.entity';
|
|
17
21
|
export * from './entities/i-entity';
|
|
18
22
|
export * from './entities/i-renderer.entity';
|
|
19
23
|
export * from './entities/i-renderable.entity';
|
|
@@ -36,3 +40,4 @@ export * from './math/matrix4';
|
|
|
36
40
|
export * from './math/splines';
|
|
37
41
|
export * from './pipes/gg-elastic.pipe';
|
|
38
42
|
export * from './gg-world';
|
|
43
|
+
export * from './level-loader';
|
package/dist/base/index.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
export * from './blueprint/blueprint';
|
|
2
|
+
export * from './blueprint/blueprint-node';
|
|
3
|
+
export * from './blueprint/nodes/remove-entity.node';
|
|
1
4
|
export * from './clock/global-clock';
|
|
2
5
|
export * from './clock/i-clock';
|
|
3
6
|
export * from './clock/pausable-clock';
|
|
@@ -14,6 +17,7 @@ export * from './data-structures/bitmask';
|
|
|
14
17
|
export * from './data-structures/graph';
|
|
15
18
|
export * from './entities/controllers/animation-mixer';
|
|
16
19
|
export * from './entities/controllers/inline-controller';
|
|
20
|
+
export * from './entities/group.entity';
|
|
17
21
|
export * from './entities/i-entity';
|
|
18
22
|
export * from './entities/i-renderer.entity';
|
|
19
23
|
export * from './entities/i-renderable.entity';
|
|
@@ -36,3 +40,4 @@ export * from './math/matrix4';
|
|
|
36
40
|
export * from './math/splines';
|
|
37
41
|
export * from './pipes/gg-elastic.pipe';
|
|
38
42
|
export * from './gg-world';
|
|
43
|
+
export * from './level-loader';
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { GgWorld, GgWorldTypeDocRepo } from './gg-world';
|
|
2
|
+
import { GroupEntity } from './entities/group.entity';
|
|
3
|
+
import { BlueprintJson, BlueprintNodeFactory } from './blueprint/blueprint';
|
|
4
|
+
/**
|
|
5
|
+
* A function that turns per-entity JSON settings into a spawned `IEntity` (e.g. a primitive body,
|
|
6
|
+
* a trigger, a camera). Registered against a class alias via {@link LevelLoader.registerClass}.
|
|
7
|
+
* May be `async`/return a `Promise` (e.g. the built-in `"Glb"` 3D class, which fetches a model) -
|
|
8
|
+
* {@link LevelLoader.loadLevel} awaits every generator before moving to the next entity. A
|
|
9
|
+
* generator that returns anything other than an `IEntity` (including `null`/`undefined`) has its
|
|
10
|
+
* result discarded - see {@link LevelLoader.loadLevel}.
|
|
11
|
+
* @template D - The position type
|
|
12
|
+
* @template R - The rotation type
|
|
13
|
+
* @template TypeDoc - The type document repository
|
|
14
|
+
* @template Settings - The settings object type
|
|
15
|
+
* @template W - The world type
|
|
16
|
+
*/
|
|
17
|
+
export type EntityGenerator<D, R, TypeDoc extends GgWorldTypeDocRepo<D, R>, Settings = any, W = GgWorld<D, R, TypeDoc>> = (world: W, settings: Settings) => any;
|
|
18
|
+
/**
|
|
19
|
+
* A level/scene, serializable as a single JSON document (e.g. to be hosted as a static file and
|
|
20
|
+
* loaded via {@link LevelLoader.loadLevelFromUrl}).
|
|
21
|
+
*/
|
|
22
|
+
export interface LevelJson {
|
|
23
|
+
/**
|
|
24
|
+
* Entities in the level
|
|
25
|
+
*/
|
|
26
|
+
entities: EntityJson[];
|
|
27
|
+
/**
|
|
28
|
+
* Blueprint graphs available to this level's entities, keyed by name - referenced from an
|
|
29
|
+
* `EntityJson.events` entry to run a blueprint whenever the named observable on that entity
|
|
30
|
+
* fires. See {@link BlueprintJson} and the `gg-engine-level-json` skill's "Blueprints" section.
|
|
31
|
+
*/
|
|
32
|
+
blueprints?: Record<string, BlueprintJson>;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* JSON description of a single entity in a level. `position`/`rotation` are left untyped here
|
|
36
|
+
* since their shape depends on the dimensionality (`Point2`/`number` for 2D, `Point3`/`Point4`
|
|
37
|
+
* for 3D) of whichever `LevelLoader` subclass parses this JSON.
|
|
38
|
+
*/
|
|
39
|
+
export interface EntityJson {
|
|
40
|
+
/**
|
|
41
|
+
* Class alias for the entity, matching a class registered via `registerClass`. Built-in
|
|
42
|
+
* primitive shapes (box/sphere/square/circle/...) all share the single `"Primitive"` alias and
|
|
43
|
+
* are distinguished by `shape` instead of by a per-shape class - e.g. `{ class: "Primitive",
|
|
44
|
+
* shape: "BOX" }` rather than `{ class: "BOX" }`. Apps register their own aliases (e.g.
|
|
45
|
+
* `"ShapeSpawner"`) the same way the dimensionality-specific `LevelLoader` subclasses register
|
|
46
|
+
* their built-ins, via `registerClass`.
|
|
47
|
+
*/
|
|
48
|
+
class: string;
|
|
49
|
+
/**
|
|
50
|
+
* Shape identifier for the built-in `"Primitive"` entity class (e.g. `"Box"`, `"Circle"`) - see
|
|
51
|
+
* the dimensionality-specific `LevelLoader` subclass (`Gg2dLevelLoader`/`Gg3dLevelLoader`) for
|
|
52
|
+
* the supported values. Ignored for any other `class`.
|
|
53
|
+
*/
|
|
54
|
+
shape?: string;
|
|
55
|
+
/**
|
|
56
|
+
* Position of the entity
|
|
57
|
+
*/
|
|
58
|
+
position?: any;
|
|
59
|
+
/**
|
|
60
|
+
* Rotation of the entity
|
|
61
|
+
*/
|
|
62
|
+
rotation?: any;
|
|
63
|
+
/**
|
|
64
|
+
* Name of the entity. `loadLevel` sets the generator's returned `IEntity`'s `.name` to this
|
|
65
|
+
* (overriding whatever default the generator gave it), so it can be found afterwards with
|
|
66
|
+
* `GgWorld.getEntityByName`/`IEntity.getChildEntityByName`. Moot if the generator doesn't return
|
|
67
|
+
* an `IEntity` - that result is discarded (with a console warning) before naming is applied.
|
|
68
|
+
*/
|
|
69
|
+
name?: string;
|
|
70
|
+
/**
|
|
71
|
+
* Configuration for the entity, passed to its generator alongside position/rotation/name
|
|
72
|
+
*/
|
|
73
|
+
config?: any;
|
|
74
|
+
/**
|
|
75
|
+
* Maps an observable property name on this entity's generated `IEntity` (e.g. `Trigger3dEntity`'s
|
|
76
|
+
* `"onEntityEntered"`) to what should run whenever that observable fires - see
|
|
77
|
+
* {@link EntityEventBinding}. `loadLevel` subscribes to the observable and triggers a fresh
|
|
78
|
+
* `Blueprint` instance (via its `"in"` entry point) with whatever value it emits, each time it
|
|
79
|
+
* fires - see `LevelLoader.loadLevel` and the `gg-engine-level-json` skill's "Blueprints"
|
|
80
|
+
* section. Silently ignored (with a console warning) if the binding can't be resolved to a
|
|
81
|
+
* blueprint, or the named property isn't an `Observable`.
|
|
82
|
+
*/
|
|
83
|
+
events?: Record<string, EntityEventBinding>;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* What an `EntityJson.events` entry runs. Either:
|
|
87
|
+
* - a plain `string` - first tried as a key into the level's top-level `blueprints` map (a named,
|
|
88
|
+
* possibly multi-node graph); if not found there, tried as a blueprint node type alias
|
|
89
|
+
* registered via `registerBlueprintNode` (e.g. the built-in `"RemoveEntity"`) instead, with no
|
|
90
|
+
* settings - shorthand for the single-node form below with `settings` omitted.
|
|
91
|
+
* - `{ type, settings? }` - a single built-in/registered blueprint node used directly as the
|
|
92
|
+
* handler, with inline `settings`, no `blueprints` entry needed at all - e.g.
|
|
93
|
+
* `{ "type": "RemoveEntity", "settings": { "dispose": true } }`. Only node types registered with
|
|
94
|
+
* a default input pin (every built-in one is - see `registerBlueprintNode`) support this form;
|
|
95
|
+
* others require a full graph declared in `blueprints` instead, addressing the desired input pin
|
|
96
|
+
* explicitly via `inputs`.
|
|
97
|
+
*/
|
|
98
|
+
export type EntityEventBinding = string | {
|
|
99
|
+
type: string;
|
|
100
|
+
settings?: Record<string, any>;
|
|
101
|
+
};
|
|
102
|
+
/**
|
|
103
|
+
* Base class for level loaders: parses a {@link LevelJson} document into world entities by
|
|
104
|
+
* dispatching each `EntityJson.class` to a generator function registered with {@link registerClass}.
|
|
105
|
+
*
|
|
106
|
+
* A generator is required to return an `IEntity`. Every `IEntity` a generator produces is parented
|
|
107
|
+
* under one {@link GroupEntity} per `loadLevel`/`loadLevelFromUrl` call (added to the world
|
|
108
|
+
* immediately, and handed back once loading completes) - so a whole level can be torn down in one
|
|
109
|
+
* shot with `world.removeEntity(level, true)`, which cascades removal/disposal to every child, and
|
|
110
|
+
* any named entity can be found afterwards with `level.getChildEntityByName(name)`. If a generator
|
|
111
|
+
* returns anything other than an `IEntity` (including `null`/`undefined`), `loadLevel` logs a
|
|
112
|
+
* `console.warn` and skips that entity - it's never parented, named, or tracked.
|
|
113
|
+
* @template D - The position type
|
|
114
|
+
* @template R - The rotation type
|
|
115
|
+
* @template TypeDoc - The type document repository
|
|
116
|
+
*/
|
|
117
|
+
export declare abstract class LevelLoader<D, R, TypeDoc extends GgWorldTypeDocRepo<D, R>> {
|
|
118
|
+
protected readonly world: GgWorld<D, R, TypeDoc>;
|
|
119
|
+
/**
|
|
120
|
+
* Map of class aliases to generator functions
|
|
121
|
+
*/
|
|
122
|
+
protected generators: Map<string, EntityGenerator<D, R, TypeDoc, any, any>>;
|
|
123
|
+
/**
|
|
124
|
+
* Map of blueprint node type aliases to node factory functions - see {@link registerBlueprintNode}.
|
|
125
|
+
*/
|
|
126
|
+
protected blueprintNodes: Map<string, BlueprintNodeFactory<D, R, TypeDoc>>;
|
|
127
|
+
/**
|
|
128
|
+
* Map of blueprint node type aliases to their default input pin name, for node types registered
|
|
129
|
+
* with one - see {@link registerBlueprintNode}.
|
|
130
|
+
*/
|
|
131
|
+
protected blueprintNodeDefaultInputs: Map<string, string>;
|
|
132
|
+
/**
|
|
133
|
+
* Constructor
|
|
134
|
+
* @param world - The world instance
|
|
135
|
+
*/
|
|
136
|
+
constructor(world: GgWorld<D, R, TypeDoc>);
|
|
137
|
+
/**
|
|
138
|
+
* Register a generator function for a class alias
|
|
139
|
+
* @param classAlias - The class alias
|
|
140
|
+
* @param generator - The generator function
|
|
141
|
+
*/
|
|
142
|
+
registerClass<Settings, W = any>(classAlias: string, generator: EntityGenerator<D, R, TypeDoc, Settings, W>): void;
|
|
143
|
+
/**
|
|
144
|
+
* Register a {@link BlueprintNode} factory for a node type alias, so a `BlueprintJson`'s
|
|
145
|
+
* `nodes` can reference it by `type` (e.g. the built-in `"RemoveEntity"`, registered by every
|
|
146
|
+
* `LevelLoader` out of the box). Same pattern as {@link registerClass}, one level down (node
|
|
147
|
+
* types within a blueprint graph, rather than entity classes within a level).
|
|
148
|
+
* @param typeAlias - The node type alias
|
|
149
|
+
* @param factory - Builds a node instance from its baked-in settings
|
|
150
|
+
* @param defaultInputPin - This node type's sole "trigger me" input pin name, if it has one
|
|
151
|
+
* canonical one (e.g. `"RemoveEntity"`'s `"entity"`). Enables the node type to be used directly
|
|
152
|
+
* as an `EntityJson.events` binding (`{ "eventName": "TypeAlias" }` or
|
|
153
|
+
* `{ "eventName": { "type": "TypeAlias", "settings": {...} } }`) without declaring a full
|
|
154
|
+
* `BlueprintJson` graph in `blueprints` - see {@link EntityEventBinding}. Omit for a node type
|
|
155
|
+
* with zero or multiple input pins, or one with no single obviously-correct default; it remains
|
|
156
|
+
* usable from a full graph either way.
|
|
157
|
+
*/
|
|
158
|
+
registerBlueprintNode(typeAlias: string, factory: BlueprintNodeFactory<D, R, TypeDoc>, defaultInputPin?: string): void;
|
|
159
|
+
/**
|
|
160
|
+
* Load a level from an already-parsed JSON document. Every `IEntity` the level's entities
|
|
161
|
+
* produce is parented under - and, on failure, torn down along with - the returned
|
|
162
|
+
* {@link GroupEntity}, already added to the world.
|
|
163
|
+
* @param levelJson - The level JSON
|
|
164
|
+
* @param levelName - Optional name for the returned group entity (e.g. so a debugger/console
|
|
165
|
+
* listing entities by name shows something more meaningful than the default auto-generated one)
|
|
166
|
+
* @returns The level's root group entity
|
|
167
|
+
*/
|
|
168
|
+
loadLevel(levelJson: LevelJson, levelName?: string): Promise<GroupEntity<D, R, TypeDoc>>;
|
|
169
|
+
/**
|
|
170
|
+
* Resolve `eventBinding` (see {@link EntityEventBinding}) to a `BlueprintJson`, instantiate a
|
|
171
|
+
* fresh `Blueprint` from it, and subscribe it to `entity[eventName]` so every value that
|
|
172
|
+
* observable emits triggers the blueprint's `"in"` entry point. Wrapped in a
|
|
173
|
+
* `BlueprintBindingEntity` so the subscription (and the blueprint's own node state) is torn down
|
|
174
|
+
* automatically once that entity is disposed - the caller parents the returned entity under the
|
|
175
|
+
* level's group for that reason.
|
|
176
|
+
* @param entity - The entity carrying the observable property
|
|
177
|
+
* @param eventName - Name of the observable property on `entity`
|
|
178
|
+
* @param eventBinding - What to run - a `blueprints` name, a bare node type alias, or `{ type,
|
|
179
|
+
* settings? }`
|
|
180
|
+
* @param blueprints - The level's top-level blueprint map, if any
|
|
181
|
+
* @returns The binding entity to parent under the level, or `undefined` if `eventBinding`
|
|
182
|
+
* couldn't be resolved or the named property isn't an `Observable` (both logged via
|
|
183
|
+
* `console.warn`)
|
|
184
|
+
*/
|
|
185
|
+
private bindEvent;
|
|
186
|
+
/**
|
|
187
|
+
* Turn an `EntityEventBinding` into a `BlueprintJson` to run. An object form (`{ type,
|
|
188
|
+
* settings? }`) always builds a single-node inline graph via {@link inlineNodeBlueprint}. A
|
|
189
|
+
* string form is tried first as a key into `blueprints` (a named, possibly multi-node graph),
|
|
190
|
+
* then - if not found there - as a bare node type alias, same as the object form with no
|
|
191
|
+
* settings.
|
|
192
|
+
* @param eventName - Name of the observable property being bound, for warning messages
|
|
193
|
+
* @param eventBinding - The binding to resolve
|
|
194
|
+
* @param blueprints - The level's top-level blueprint map, if any
|
|
195
|
+
* @returns The resolved graph, or `undefined` (logged via `console.warn`) if it couldn't be
|
|
196
|
+
*/
|
|
197
|
+
private resolveEventBlueprint;
|
|
198
|
+
/**
|
|
199
|
+
* Build a single-node `BlueprintJson` wrapping one blueprint node type, wired so the node's
|
|
200
|
+
* registered default input pin (see {@link registerBlueprintNode}) is reachable as `"in"` - what
|
|
201
|
+
* powers the `EntityEventBinding` shorthand that skips declaring a `blueprints` entry entirely.
|
|
202
|
+
* @param eventName - Name of the observable property being bound, for warning messages
|
|
203
|
+
* @param nodeType - The blueprint node type alias
|
|
204
|
+
* @param settings - Settings to bake into the node, if any
|
|
205
|
+
* @returns The single-node graph, or `undefined` (logged via `console.warn`) if `nodeType` isn't
|
|
206
|
+
* registered, or was registered without a default input pin
|
|
207
|
+
*/
|
|
208
|
+
private inlineNodeBlueprint;
|
|
209
|
+
/**
|
|
210
|
+
* Fetch a level JSON document hosted at `url` and load it, so a whole level/scene can be
|
|
211
|
+
* shipped and consumed as a single static JSON file.
|
|
212
|
+
* @param url - URL (or path) of the level JSON document
|
|
213
|
+
* @param levelName - Optional name for the returned group entity, see {@link loadLevel}
|
|
214
|
+
* @returns The level's root group entity
|
|
215
|
+
*/
|
|
216
|
+
loadLevelFromUrl(url: string, levelName?: string): Promise<GroupEntity<D, R, TypeDoc>>;
|
|
217
|
+
}
|