@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
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
import { LevelLoader } from '../base/level-loader';
|
|
2
|
+
import { Gg3dWorld, Gg3dWorldTypeDocRepo } from './gg-3d-world';
|
|
3
|
+
import { AxisDirection3, Point3, Point4 } from '../base';
|
|
4
|
+
import { DisplayObject3dOpts } from './factories';
|
|
5
|
+
import { Body3DOptions } from './models/body-options';
|
|
6
|
+
import { Shape3DDescriptor } from './models/shapes';
|
|
7
|
+
import { GgCarProperties } from './entities/gg-car/gg-car.entity';
|
|
8
|
+
import { RVEntityAxleOptions, RVEntitySharedWheelOptions } from './entities/raycast-vehicle-3d.entity';
|
|
9
|
+
import { MapGraphNodeType } from './entities/map-graph-3d.entity';
|
|
10
|
+
/**
|
|
11
|
+
* Shape names accepted by the built-in `"Primitive"` entity class in a 3D level JSON, via the
|
|
12
|
+
* sibling `shape` field on the entity (e.g. `{ class: "Primitive", shape: "BOX" }`) - the same
|
|
13
|
+
* `Shape3DDescriptor['shape']` values used at the engine API level, so no translation is needed
|
|
14
|
+
* between a level JSON and `Gg3dWorld.addPrimitiveRigidBody`.
|
|
15
|
+
*/
|
|
16
|
+
export type Primitive3DShapeName = Shape3DDescriptor['shape'];
|
|
17
|
+
/**
|
|
18
|
+
* Settings shared by every primitive entity (Box, Sphere, Plane, Capsule, Cylinder, Cone)
|
|
19
|
+
*/
|
|
20
|
+
export interface Primitive3DSettings {
|
|
21
|
+
/**
|
|
22
|
+
* Which primitive shape to construct
|
|
23
|
+
*/
|
|
24
|
+
shape: Primitive3DShapeName;
|
|
25
|
+
/**
|
|
26
|
+
* Position of the primitive
|
|
27
|
+
*/
|
|
28
|
+
position?: Point3;
|
|
29
|
+
/**
|
|
30
|
+
* Rotation of the primitive
|
|
31
|
+
*/
|
|
32
|
+
rotation?: Point4;
|
|
33
|
+
/**
|
|
34
|
+
* Dimensions of the primitive (for Box)
|
|
35
|
+
*/
|
|
36
|
+
dimensions?: Point3;
|
|
37
|
+
/**
|
|
38
|
+
* Radius of the primitive (for Sphere, Capsule, Cylinder, Cone)
|
|
39
|
+
*/
|
|
40
|
+
radius?: number;
|
|
41
|
+
/**
|
|
42
|
+
* Height of the primitive (for Cylinder, Cone)
|
|
43
|
+
*/
|
|
44
|
+
height?: number;
|
|
45
|
+
/**
|
|
46
|
+
* Centers distance of the primitive (for Capsule)
|
|
47
|
+
*/
|
|
48
|
+
centersDistance?: number;
|
|
49
|
+
/**
|
|
50
|
+
* Material options for the primitive
|
|
51
|
+
*/
|
|
52
|
+
material?: DisplayObject3dOpts<any>;
|
|
53
|
+
/**
|
|
54
|
+
* Physics body options, merged over sensible defaults
|
|
55
|
+
*/
|
|
56
|
+
body?: Partial<Body3DOptions>;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Settings for a trigger entity
|
|
60
|
+
*/
|
|
61
|
+
export interface Trigger3DSettings {
|
|
62
|
+
/**
|
|
63
|
+
* Position of the trigger
|
|
64
|
+
*/
|
|
65
|
+
position?: Point3;
|
|
66
|
+
/**
|
|
67
|
+
* Rotation of the trigger
|
|
68
|
+
*/
|
|
69
|
+
rotation?: Point4;
|
|
70
|
+
/**
|
|
71
|
+
* Dimensions of the trigger
|
|
72
|
+
*/
|
|
73
|
+
dimensions: Point3;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Settings for a camera entity
|
|
77
|
+
*/
|
|
78
|
+
export interface Camera3DSettings {
|
|
79
|
+
/**
|
|
80
|
+
* Position of the camera
|
|
81
|
+
*/
|
|
82
|
+
position?: Point3;
|
|
83
|
+
/**
|
|
84
|
+
* Rotation of the camera
|
|
85
|
+
*/
|
|
86
|
+
rotation?: Point4;
|
|
87
|
+
/**
|
|
88
|
+
* Field of view in degrees
|
|
89
|
+
*/
|
|
90
|
+
fov?: number;
|
|
91
|
+
/**
|
|
92
|
+
* Aspect ratio (width / height)
|
|
93
|
+
*/
|
|
94
|
+
aspectRatio?: number;
|
|
95
|
+
/**
|
|
96
|
+
* Near and far frustum planes
|
|
97
|
+
*/
|
|
98
|
+
frustrum?: {
|
|
99
|
+
near: number;
|
|
100
|
+
far: number;
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Settings for a `"GgCar"` wheel's optional visual mesh. A level JSON has no way to reference an
|
|
105
|
+
* existing display object component (unlike programmatic `RVEntityProperties`, whose
|
|
106
|
+
* `WheelDisplayOptions.displayObject` takes one directly) - instead, supplying `display` at all
|
|
107
|
+
* makes the `"GgCar"` generator build one itself via `visualScene.factory.createCylinder`, sized
|
|
108
|
+
* to that wheel's own (or its axle/shared settings') `tyreRadius`/`tyreWidth`. Omit `display`
|
|
109
|
+
* entirely (on both the wheel and whatever it inherits from) to leave that wheel invisible
|
|
110
|
+
* (physics-only), same as omitting `WheelDisplayOptions.displayObject` does programmatically.
|
|
111
|
+
*/
|
|
112
|
+
export interface GgCarWheelDisplaySettings {
|
|
113
|
+
material?: DisplayObject3dOpts<any>;
|
|
114
|
+
wheelObjectDirection?: AxisDirection3;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* JSON-friendly counterpart of `RVEntitySharedWheelOptions`: identical except `display` is a
|
|
118
|
+
* {@link GgCarWheelDisplaySettings} descriptor instead of a ready-made `WheelDisplayOptions`.
|
|
119
|
+
*/
|
|
120
|
+
export type GgCarSharedWheelSettings = Omit<RVEntitySharedWheelOptions, 'display'> & {
|
|
121
|
+
display?: GgCarWheelDisplaySettings;
|
|
122
|
+
};
|
|
123
|
+
/**
|
|
124
|
+
* JSON-friendly counterpart of `RVEntityAxleOptions`, for the `"GgCar"` class's `wheelBase.front`/
|
|
125
|
+
* `wheelBase.rear`.
|
|
126
|
+
*/
|
|
127
|
+
export type GgCarAxleSettings = Pick<RVEntityAxleOptions, 'halfAxleWidth' | 'axlePosition' | 'axleHeight'> & GgCarSharedWheelSettings;
|
|
128
|
+
/**
|
|
129
|
+
* JSON-friendly counterpart of one `RVEntityProperties['wheelOptions']` element, for the
|
|
130
|
+
* `"GgCar"` class's `wheelOptions` array.
|
|
131
|
+
*/
|
|
132
|
+
export type GgCarWheelSettings = GgCarSharedWheelSettings & {
|
|
133
|
+
isLeft: boolean;
|
|
134
|
+
isFront: boolean;
|
|
135
|
+
position: Point3;
|
|
136
|
+
};
|
|
137
|
+
/**
|
|
138
|
+
* Fields of `GgCarProperties` that don't vary between its `wheelBase`/`wheelOptions` shapes -
|
|
139
|
+
* carried over into {@link GgCar3DSettings} as-is (already plain JSON-serializable data).
|
|
140
|
+
*/
|
|
141
|
+
export interface GgCar3DCommonSettings {
|
|
142
|
+
suspension: GgCarProperties['suspension'];
|
|
143
|
+
tractionBias: GgCarProperties['tractionBias'];
|
|
144
|
+
mpsToRpmFactor?: GgCarProperties['mpsToRpmFactor'];
|
|
145
|
+
engine: GgCarProperties['engine'];
|
|
146
|
+
brake: GgCarProperties['brake'];
|
|
147
|
+
transmission: GgCarProperties['transmission'];
|
|
148
|
+
maxSteerAngle: GgCarProperties['maxSteerAngle'];
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Settings for the built-in `"GgCar"` entity class (3D only): builds a box-shaped chassis rigid
|
|
152
|
+
* body (+ optional matching display box) and a full `GgCarEntity` on top of it - the procedural
|
|
153
|
+
* counterpart of the GLB-driven car construction apps do by hand (see `examples/fly-city-three-ammo`'s
|
|
154
|
+
* `GameFactory.generateCar`), for a car whose chassis/wheels are plain primitives rather than
|
|
155
|
+
* loaded meshes.
|
|
156
|
+
*/
|
|
157
|
+
export type GgCar3DSettings = GgCar3DCommonSettings & {
|
|
158
|
+
position?: Point3;
|
|
159
|
+
rotation?: Point4;
|
|
160
|
+
/**
|
|
161
|
+
* The chassis's box collider/mesh. `body` is merged over a default dynamic body (same shape as
|
|
162
|
+
* `Primitive3DSettings.body`, but with `mass: 800` instead of `1`, since a `mass: 1` chassis is
|
|
163
|
+
* unrealistically light for a car).
|
|
164
|
+
*/
|
|
165
|
+
chassis: {
|
|
166
|
+
dimensions: Point3;
|
|
167
|
+
material?: DisplayObject3dOpts<any>;
|
|
168
|
+
body?: Partial<Body3DOptions>;
|
|
169
|
+
};
|
|
170
|
+
} & ({
|
|
171
|
+
wheelBase: {
|
|
172
|
+
shared?: GgCarSharedWheelSettings;
|
|
173
|
+
front: GgCarAxleSettings;
|
|
174
|
+
rear: GgCarAxleSettings;
|
|
175
|
+
};
|
|
176
|
+
wheelOptions?: undefined;
|
|
177
|
+
sharedWheelOptions?: undefined;
|
|
178
|
+
} | {
|
|
179
|
+
wheelOptions: GgCarWheelSettings[];
|
|
180
|
+
sharedWheelOptions?: GgCarSharedWheelSettings;
|
|
181
|
+
wheelBase?: undefined;
|
|
182
|
+
});
|
|
183
|
+
/**
|
|
184
|
+
* JSON-friendly counterpart of `MapGraphNodeType`: identical except `loadOptions` may be omitted
|
|
185
|
+
* (defaulting to `{}`) rather than required, since most nodes need none of it.
|
|
186
|
+
*/
|
|
187
|
+
export type MapGraphNodeJson = Omit<MapGraphNodeType, 'loadOptions'> & {
|
|
188
|
+
loadOptions?: MapGraphNodeType['loadOptions'];
|
|
189
|
+
};
|
|
190
|
+
/**
|
|
191
|
+
* Settings for the built-in `"MapGraph"` entity class (3D only): builds a `MapGraph` from plain
|
|
192
|
+
* node data and wraps it in a ready-to-use `MapGraph3dEntity`. `graph` mirrors the two
|
|
193
|
+
* `MapGraph` factory methods - a flat (optionally closed-loop) path via `nodes`, or a rectangular
|
|
194
|
+
* `grid` - since both already take plain-data node arrays. The resulting entity doesn't implement
|
|
195
|
+
* `IPositionable3d` (each node carries its own absolute `position`/`rotation`), so there's no
|
|
196
|
+
* `position`/`rotation` field here - and its `loaderCursor$` still needs to be driven at runtime
|
|
197
|
+
* from whatever entity's position should determine which nodes are loaded (see
|
|
198
|
+
* `gg-engine-level-json` skill's "MapGraph" section).
|
|
199
|
+
*/
|
|
200
|
+
export interface MapGraph3DSettings {
|
|
201
|
+
graph: {
|
|
202
|
+
type?: 'array';
|
|
203
|
+
nodes: MapGraphNodeJson[];
|
|
204
|
+
closed?: boolean;
|
|
205
|
+
} | {
|
|
206
|
+
type: 'grid';
|
|
207
|
+
grid: MapGraphNodeJson[][];
|
|
208
|
+
};
|
|
209
|
+
/** Depth in the graph to load - see `Gg3dMapGraphEntityOptions.loadDepth` (default `5`) */
|
|
210
|
+
loadDepth?: number;
|
|
211
|
+
/** Extra unload-delay depth - see `Gg3dMapGraphEntityOptions.inertia` (default `0`) */
|
|
212
|
+
inertia?: number;
|
|
213
|
+
/** Max nodes loaded per tick - see `Gg3dMapGraphEntityOptions.maxNodesLoadingPerTick` (default `1`) */
|
|
214
|
+
maxNodesLoadingPerTick?: number;
|
|
215
|
+
/** Ticks/second of the internal load-scheduling clock - see `MapGraph3dEntity.loadRateLimit` (default `1`) */
|
|
216
|
+
loadRateLimit?: number;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* 3D level loader: registers the built-in primitive/trigger/camera/car/map-graph entity classes
|
|
220
|
+
* and dispatches `LevelJson` entities to them (or to custom classes registered via
|
|
221
|
+
* `registerClass`).
|
|
222
|
+
* @template TypeDoc - The type document repository
|
|
223
|
+
*/
|
|
224
|
+
export declare class Gg3dLevelLoader<TypeDoc extends Gg3dWorldTypeDocRepo = Gg3dWorldTypeDocRepo> extends LevelLoader<Point3, Point4, TypeDoc> {
|
|
225
|
+
protected readonly world: Gg3dWorld<TypeDoc>;
|
|
226
|
+
constructor(world: Gg3dWorld<TypeDoc>);
|
|
227
|
+
/**
|
|
228
|
+
* Register the built-in classes for primitives, triggers, and cameras
|
|
229
|
+
*/
|
|
230
|
+
private registerDefaultClasses;
|
|
231
|
+
/**
|
|
232
|
+
* Turn a `Primitive3DSettings` (`shape` plus shape-specific fields) into the `Shape3DDescriptor`
|
|
233
|
+
* consumed by `Gg3dWorld.addPrimitiveRigidBody`.
|
|
234
|
+
* @param settings - The primitive settings, as parsed from a `"Primitive"` entity's `shape` +
|
|
235
|
+
* `config`
|
|
236
|
+
* @returns The shape descriptor
|
|
237
|
+
*/
|
|
238
|
+
private buildShapeDescriptor;
|
|
239
|
+
/**
|
|
240
|
+
* Create a primitive entity (both display object and physics body) from a shape descriptor.
|
|
241
|
+
* `Shape3DDescriptor` (no mesh-only segment options) is used for both the visual and the
|
|
242
|
+
* physics representation, same as `Gg3dWorld.addPrimitiveRigidBody`'s own shortcut methods.
|
|
243
|
+
* @param world - The world instance
|
|
244
|
+
* @param shape - The shape descriptor
|
|
245
|
+
* @param settings - Position/rotation/material/body settings shared by all primitives
|
|
246
|
+
* @returns The created entity
|
|
247
|
+
*/
|
|
248
|
+
private createPrimitive;
|
|
249
|
+
/**
|
|
250
|
+
* Create a trigger entity: a `Trigger3dEntity` wrapping the raw physics trigger component, so
|
|
251
|
+
* the app can subscribe to `onEntityEntered`/`onEntityLeft` without any extra wiring - see the
|
|
252
|
+
* base `LevelLoader` docs for how it's parented for level-lifecycle cleanup.
|
|
253
|
+
* @param world - The world instance
|
|
254
|
+
* @param settings - The trigger settings
|
|
255
|
+
* @returns The created trigger entity
|
|
256
|
+
*/
|
|
257
|
+
private createTrigger;
|
|
258
|
+
/**
|
|
259
|
+
* Create a camera entity: a `Camera3dEntity` wrapping the raw camera component, not attached to
|
|
260
|
+
* any renderer/canvas (a level JSON has no notion of one - `world.addRenderer(entity.camera,
|
|
261
|
+
* canvas)` once the app has a canvas). Parented under the level's group entity, so it's torn
|
|
262
|
+
* down along with the rest of the level - put a camera meant to outlive a level swap in a
|
|
263
|
+
* separate, never-unloaded level instead (see `gg-engine-level-json`'s "Camera" section).
|
|
264
|
+
* @param world - The world instance
|
|
265
|
+
* @param settings - The camera settings
|
|
266
|
+
* @returns The created camera entity
|
|
267
|
+
*/
|
|
268
|
+
private createCamera;
|
|
269
|
+
/**
|
|
270
|
+
* Build a `WheelDisplayOptions` for one `"GgCar"` wheel/axle from its (already shared-merged)
|
|
271
|
+
* settings, via `visualScene.factory.createCylinder`. Returns `undefined` (no visual wheel,
|
|
272
|
+
* physics-only) if `display` wasn't specified at all, or there's no visual scene to build one
|
|
273
|
+
* against.
|
|
274
|
+
* @param world - The world instance
|
|
275
|
+
* @param wheelSettings - The wheel's own settings, already merged over whatever it inherits
|
|
276
|
+
* from `wheelBase.shared`/`sharedWheelOptions`
|
|
277
|
+
* @returns The resolved display options, or `undefined`
|
|
278
|
+
*/
|
|
279
|
+
private resolveWheelDisplay;
|
|
280
|
+
/**
|
|
281
|
+
* Create a `"GgCar"` entity: a box chassis rigid body (+ optional matching display box) wrapped
|
|
282
|
+
* in a full `GgCarEntity`, with each wheel's optional visual mesh built from its settings (see
|
|
283
|
+
* {@link resolveWheelDisplay}) rather than referencing an existing display object component,
|
|
284
|
+
* which a level JSON has no way to do.
|
|
285
|
+
* @param world - The world instance
|
|
286
|
+
* @param settings - The car settings
|
|
287
|
+
* @returns The created car entity
|
|
288
|
+
*/
|
|
289
|
+
private createGgCar;
|
|
290
|
+
/**
|
|
291
|
+
* Create a `"MapGraph"` entity: a `MapGraph` built from plain node data (a flat/looped path or
|
|
292
|
+
* a rectangular grid, see {@link MapGraph3DSettings}), wrapped in a ready-to-use
|
|
293
|
+
* `MapGraph3dEntity`. The app still has to drive `loaderCursor$` itself once the level is
|
|
294
|
+
* loaded - see `gg-engine-level-json`'s "MapGraph" section.
|
|
295
|
+
* @param world - The world instance
|
|
296
|
+
* @param settings - The map graph settings
|
|
297
|
+
* @returns The created map graph entity
|
|
298
|
+
*/
|
|
299
|
+
private createMapGraph;
|
|
300
|
+
}
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
2
|
+
var t = {};
|
|
3
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
4
|
+
t[p] = s[p];
|
|
5
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
6
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
7
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
8
|
+
t[p[i]] = s[p[i]];
|
|
9
|
+
}
|
|
10
|
+
return t;
|
|
11
|
+
};
|
|
12
|
+
import { LevelLoader } from '../base/level-loader';
|
|
13
|
+
import { Trigger3dEntity } from './entities/trigger-3d.entity';
|
|
14
|
+
import { Camera3dEntity } from './entities/camera-3d.entity';
|
|
15
|
+
import { GgCarEntity } from './entities/gg-car/gg-car.entity';
|
|
16
|
+
import { MapGraph, MapGraph3dEntity, } from './entities/map-graph-3d.entity';
|
|
17
|
+
const defaultBodyOptions = {
|
|
18
|
+
dynamic: true,
|
|
19
|
+
mass: 1,
|
|
20
|
+
restitution: 0.2,
|
|
21
|
+
friction: 0.5,
|
|
22
|
+
ownCollisionGroups: 'all',
|
|
23
|
+
interactWithCollisionGroups: 'all',
|
|
24
|
+
};
|
|
25
|
+
const defaultCarChassisBodyOptions = Object.assign(Object.assign({}, defaultBodyOptions), { mass: 800 });
|
|
26
|
+
/** Fallback tyre size used only to size a `"GgCar"` wheel's auto-generated cylinder mesh when
|
|
27
|
+
* neither the wheel nor its shared wheel settings specify one - independent of the physics
|
|
28
|
+
* wheel's own default (`RaycastVehicle3dEntity`'s internal `wheeelDefaults`) applied when a
|
|
29
|
+
* wheel's `tyreRadius`/`tyreWidth` is left unset entirely. */
|
|
30
|
+
const defaultWheelDisplaySize = { tyreRadius: 0.4, tyreWidth: 0.3 };
|
|
31
|
+
/**
|
|
32
|
+
* 3D level loader: registers the built-in primitive/trigger/camera/car/map-graph entity classes
|
|
33
|
+
* and dispatches `LevelJson` entities to them (or to custom classes registered via
|
|
34
|
+
* `registerClass`).
|
|
35
|
+
* @template TypeDoc - The type document repository
|
|
36
|
+
*/
|
|
37
|
+
export class Gg3dLevelLoader extends LevelLoader {
|
|
38
|
+
constructor(world) {
|
|
39
|
+
super(world);
|
|
40
|
+
this.world = world;
|
|
41
|
+
this.registerDefaultClasses();
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Register the built-in classes for primitives, triggers, and cameras
|
|
45
|
+
*/
|
|
46
|
+
registerDefaultClasses() {
|
|
47
|
+
this.registerClass('Primitive', (world, settings) => this.createPrimitive(world, this.buildShapeDescriptor(settings), settings));
|
|
48
|
+
this.registerClass('Trigger', this.createTrigger.bind(this));
|
|
49
|
+
this.registerClass('Camera', this.createCamera.bind(this));
|
|
50
|
+
this.registerClass('GgCar', this.createGgCar.bind(this));
|
|
51
|
+
this.registerClass('MapGraph', this.createMapGraph.bind(this));
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Turn a `Primitive3DSettings` (`shape` plus shape-specific fields) into the `Shape3DDescriptor`
|
|
55
|
+
* consumed by `Gg3dWorld.addPrimitiveRigidBody`.
|
|
56
|
+
* @param settings - The primitive settings, as parsed from a `"Primitive"` entity's `shape` +
|
|
57
|
+
* `config`
|
|
58
|
+
* @returns The shape descriptor
|
|
59
|
+
*/
|
|
60
|
+
buildShapeDescriptor(settings) {
|
|
61
|
+
switch (settings.shape) {
|
|
62
|
+
case 'BOX':
|
|
63
|
+
if (!settings.dimensions) {
|
|
64
|
+
throw new Error('Dimensions are required for BOX primitive');
|
|
65
|
+
}
|
|
66
|
+
return { shape: 'BOX', dimensions: settings.dimensions };
|
|
67
|
+
case 'SPHERE':
|
|
68
|
+
if (settings.radius === undefined) {
|
|
69
|
+
throw new Error('Radius is required for SPHERE primitive');
|
|
70
|
+
}
|
|
71
|
+
return { shape: 'SPHERE', radius: settings.radius };
|
|
72
|
+
case 'PLANE':
|
|
73
|
+
return { shape: 'PLANE' };
|
|
74
|
+
case 'CAPSULE':
|
|
75
|
+
if (settings.radius === undefined) {
|
|
76
|
+
throw new Error('Radius is required for CAPSULE primitive');
|
|
77
|
+
}
|
|
78
|
+
if (settings.centersDistance === undefined) {
|
|
79
|
+
throw new Error('Centers distance is required for CAPSULE primitive');
|
|
80
|
+
}
|
|
81
|
+
return { shape: 'CAPSULE', radius: settings.radius, centersDistance: settings.centersDistance };
|
|
82
|
+
case 'CYLINDER':
|
|
83
|
+
if (settings.radius === undefined) {
|
|
84
|
+
throw new Error('Radius is required for CYLINDER primitive');
|
|
85
|
+
}
|
|
86
|
+
if (settings.height === undefined) {
|
|
87
|
+
throw new Error('Height is required for CYLINDER primitive');
|
|
88
|
+
}
|
|
89
|
+
return { shape: 'CYLINDER', radius: settings.radius, height: settings.height };
|
|
90
|
+
case 'CONE':
|
|
91
|
+
if (settings.radius === undefined) {
|
|
92
|
+
throw new Error('Radius is required for CONE primitive');
|
|
93
|
+
}
|
|
94
|
+
if (settings.height === undefined) {
|
|
95
|
+
throw new Error('Height is required for CONE primitive');
|
|
96
|
+
}
|
|
97
|
+
return { shape: 'CONE', radius: settings.radius, height: settings.height };
|
|
98
|
+
default:
|
|
99
|
+
throw new Error(`Unknown primitive shape "${settings.shape}"`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Create a primitive entity (both display object and physics body) from a shape descriptor.
|
|
104
|
+
* `Shape3DDescriptor` (no mesh-only segment options) is used for both the visual and the
|
|
105
|
+
* physics representation, same as `Gg3dWorld.addPrimitiveRigidBody`'s own shortcut methods.
|
|
106
|
+
* @param world - The world instance
|
|
107
|
+
* @param shape - The shape descriptor
|
|
108
|
+
* @param settings - Position/rotation/material/body settings shared by all primitives
|
|
109
|
+
* @returns The created entity
|
|
110
|
+
*/
|
|
111
|
+
createPrimitive(world, shape, settings) {
|
|
112
|
+
const { position, rotation, material, body } = settings;
|
|
113
|
+
return world.addPrimitiveRigidBody({ shape, body: Object.assign(Object.assign({}, defaultBodyOptions), body) }, position, rotation, material);
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Create a trigger entity: a `Trigger3dEntity` wrapping the raw physics trigger component, so
|
|
117
|
+
* the app can subscribe to `onEntityEntered`/`onEntityLeft` without any extra wiring - see the
|
|
118
|
+
* base `LevelLoader` docs for how it's parented for level-lifecycle cleanup.
|
|
119
|
+
* @param world - The world instance
|
|
120
|
+
* @param settings - The trigger settings
|
|
121
|
+
* @returns The created trigger entity
|
|
122
|
+
*/
|
|
123
|
+
createTrigger(world, settings) {
|
|
124
|
+
var _a;
|
|
125
|
+
const { position, rotation, dimensions } = settings;
|
|
126
|
+
const shape = { shape: 'BOX', dimensions };
|
|
127
|
+
const trigger = (_a = world.physicsWorld) === null || _a === void 0 ? void 0 : _a.factory.createTrigger(shape, { position, rotation });
|
|
128
|
+
if (!trigger) {
|
|
129
|
+
return undefined;
|
|
130
|
+
}
|
|
131
|
+
const entity = new Trigger3dEntity(trigger);
|
|
132
|
+
if (position) {
|
|
133
|
+
entity.position = position;
|
|
134
|
+
}
|
|
135
|
+
if (rotation) {
|
|
136
|
+
entity.rotation = rotation;
|
|
137
|
+
}
|
|
138
|
+
return entity;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Create a camera entity: a `Camera3dEntity` wrapping the raw camera component, not attached to
|
|
142
|
+
* any renderer/canvas (a level JSON has no notion of one - `world.addRenderer(entity.camera,
|
|
143
|
+
* canvas)` once the app has a canvas). Parented under the level's group entity, so it's torn
|
|
144
|
+
* down along with the rest of the level - put a camera meant to outlive a level swap in a
|
|
145
|
+
* separate, never-unloaded level instead (see `gg-engine-level-json`'s "Camera" section).
|
|
146
|
+
* @param world - The world instance
|
|
147
|
+
* @param settings - The camera settings
|
|
148
|
+
* @returns The created camera entity
|
|
149
|
+
*/
|
|
150
|
+
createCamera(world, settings) {
|
|
151
|
+
var _a;
|
|
152
|
+
const { position, rotation, fov, aspectRatio, frustrum } = settings;
|
|
153
|
+
const camera = (_a = world.visualScene) === null || _a === void 0 ? void 0 : _a.factory.createPerspectiveCamera({ fov, aspectRatio, frustrum });
|
|
154
|
+
if (!camera) {
|
|
155
|
+
return undefined;
|
|
156
|
+
}
|
|
157
|
+
const entity = new Camera3dEntity(camera);
|
|
158
|
+
if (position) {
|
|
159
|
+
entity.position = position;
|
|
160
|
+
}
|
|
161
|
+
if (rotation) {
|
|
162
|
+
entity.rotation = rotation;
|
|
163
|
+
}
|
|
164
|
+
return entity;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Build a `WheelDisplayOptions` for one `"GgCar"` wheel/axle from its (already shared-merged)
|
|
168
|
+
* settings, via `visualScene.factory.createCylinder`. Returns `undefined` (no visual wheel,
|
|
169
|
+
* physics-only) if `display` wasn't specified at all, or there's no visual scene to build one
|
|
170
|
+
* against.
|
|
171
|
+
* @param world - The world instance
|
|
172
|
+
* @param wheelSettings - The wheel's own settings, already merged over whatever it inherits
|
|
173
|
+
* from `wheelBase.shared`/`sharedWheelOptions`
|
|
174
|
+
* @returns The resolved display options, or `undefined`
|
|
175
|
+
*/
|
|
176
|
+
resolveWheelDisplay(world, wheelSettings) {
|
|
177
|
+
var _a, _b;
|
|
178
|
+
if (!wheelSettings.display || !world.visualScene) {
|
|
179
|
+
return undefined;
|
|
180
|
+
}
|
|
181
|
+
const { tyreRadius = defaultWheelDisplaySize.tyreRadius, tyreWidth = defaultWheelDisplaySize.tyreWidth } = wheelSettings;
|
|
182
|
+
return {
|
|
183
|
+
displayObject: world.visualScene.factory.createCylinder(tyreRadius, tyreWidth, (_a = wheelSettings.display.material) !== null && _a !== void 0 ? _a : {}),
|
|
184
|
+
wheelObjectDirection: (_b = wheelSettings.display.wheelObjectDirection) !== null && _b !== void 0 ? _b : 'x',
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Create a `"GgCar"` entity: a box chassis rigid body (+ optional matching display box) wrapped
|
|
189
|
+
* in a full `GgCarEntity`, with each wheel's optional visual mesh built from its settings (see
|
|
190
|
+
* {@link resolveWheelDisplay}) rather than referencing an existing display object component,
|
|
191
|
+
* which a level JSON has no way to do.
|
|
192
|
+
* @param world - The world instance
|
|
193
|
+
* @param settings - The car settings
|
|
194
|
+
* @returns The created car entity
|
|
195
|
+
*/
|
|
196
|
+
createGgCar(world, settings) {
|
|
197
|
+
var _a, _b, _c;
|
|
198
|
+
if (!world.physicsWorld) {
|
|
199
|
+
return undefined;
|
|
200
|
+
}
|
|
201
|
+
const { position, rotation, chassis, wheelBase, wheelOptions, sharedWheelOptions } = settings, rest = __rest(settings, ["position", "rotation", "chassis", "wheelBase", "wheelOptions", "sharedWheelOptions"]);
|
|
202
|
+
if (!(chassis === null || chassis === void 0 ? void 0 : chassis.dimensions)) {
|
|
203
|
+
throw new Error('Chassis dimensions are required for GgCar class');
|
|
204
|
+
}
|
|
205
|
+
if (!wheelBase && !wheelOptions) {
|
|
206
|
+
throw new Error('Either "wheelBase" or "wheelOptions" is required for GgCar class');
|
|
207
|
+
}
|
|
208
|
+
const chassisBody = world.physicsWorld.factory.createRigidBody({
|
|
209
|
+
shape: { shape: 'BOX', dimensions: chassis.dimensions },
|
|
210
|
+
body: Object.assign(Object.assign({}, defaultCarChassisBodyOptions), chassis.body),
|
|
211
|
+
});
|
|
212
|
+
const chassis3D = (_c = (_a = world.visualScene) === null || _a === void 0 ? void 0 : _a.factory.createBox(chassis.dimensions, (_b = chassis.material) !== null && _b !== void 0 ? _b : {})) !== null && _c !== void 0 ? _c : null;
|
|
213
|
+
const carProperties = wheelBase
|
|
214
|
+
? Object.assign(Object.assign({}, rest), { wheelBase: {
|
|
215
|
+
shared: Object.assign(Object.assign({}, wheelBase.shared), { display: undefined }),
|
|
216
|
+
front: Object.assign(Object.assign({}, wheelBase.front), { display: this.resolveWheelDisplay(world, Object.assign(Object.assign({}, wheelBase.shared), wheelBase.front)) }),
|
|
217
|
+
rear: Object.assign(Object.assign({}, wheelBase.rear), { display: this.resolveWheelDisplay(world, Object.assign(Object.assign({}, wheelBase.shared), wheelBase.rear)) }),
|
|
218
|
+
} }) : Object.assign(Object.assign({}, rest), { wheelOptions: wheelOptions.map(wheel => (Object.assign(Object.assign({}, wheel), { display: this.resolveWheelDisplay(world, Object.assign(Object.assign({}, sharedWheelOptions), wheel)) }))), sharedWheelOptions: sharedWheelOptions && Object.assign(Object.assign({}, sharedWheelOptions), { display: undefined }) });
|
|
219
|
+
const entity = new GgCarEntity(carProperties, chassis3D, world.physicsWorld.factory.createRaycastVehicle(chassisBody));
|
|
220
|
+
if (position) {
|
|
221
|
+
entity.position = position;
|
|
222
|
+
}
|
|
223
|
+
if (rotation) {
|
|
224
|
+
entity.rotation = rotation;
|
|
225
|
+
}
|
|
226
|
+
return entity;
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Create a `"MapGraph"` entity: a `MapGraph` built from plain node data (a flat/looped path or
|
|
230
|
+
* a rectangular grid, see {@link MapGraph3DSettings}), wrapped in a ready-to-use
|
|
231
|
+
* `MapGraph3dEntity`. The app still has to drive `loaderCursor$` itself once the level is
|
|
232
|
+
* loaded - see `gg-engine-level-json`'s "MapGraph" section.
|
|
233
|
+
* @param world - The world instance
|
|
234
|
+
* @param settings - The map graph settings
|
|
235
|
+
* @returns The created map graph entity
|
|
236
|
+
*/
|
|
237
|
+
createMapGraph(world, settings) {
|
|
238
|
+
var _a, _b, _c;
|
|
239
|
+
const { graph, loadDepth, inertia, maxNodesLoadingPerTick, loadRateLimit } = settings;
|
|
240
|
+
if (!graph) {
|
|
241
|
+
throw new Error('"graph" is required for MapGraph class');
|
|
242
|
+
}
|
|
243
|
+
const normalizeNode = (node) => {
|
|
244
|
+
var _a;
|
|
245
|
+
return (Object.assign(Object.assign({}, node), { loadOptions: (_a = node.loadOptions) !== null && _a !== void 0 ? _a : {} }));
|
|
246
|
+
};
|
|
247
|
+
let mapGraph;
|
|
248
|
+
if (graph.type === 'grid') {
|
|
249
|
+
if (!((_a = graph.grid) === null || _a === void 0 ? void 0 : _a.length)) {
|
|
250
|
+
throw new Error('"graph.grid" must be a non-empty grid for MapGraph class');
|
|
251
|
+
}
|
|
252
|
+
mapGraph = MapGraph.fromMapSquareGrid(graph.grid.map(row => row.map(normalizeNode)));
|
|
253
|
+
}
|
|
254
|
+
else {
|
|
255
|
+
if (!((_b = graph.nodes) === null || _b === void 0 ? void 0 : _b.length)) {
|
|
256
|
+
throw new Error('"graph.nodes" must be a non-empty array for MapGraph class');
|
|
257
|
+
}
|
|
258
|
+
mapGraph = MapGraph.fromMapArray(graph.nodes.map(normalizeNode), (_c = graph.closed) !== null && _c !== void 0 ? _c : false);
|
|
259
|
+
}
|
|
260
|
+
const options = Object.assign(Object.assign(Object.assign({}, (loadDepth !== undefined ? { loadDepth } : {})), (inertia !== undefined ? { inertia } : {})), (maxNodesLoadingPerTick !== undefined ? { maxNodesLoadingPerTick } : {}));
|
|
261
|
+
const entity = new MapGraph3dEntity(mapGraph, options);
|
|
262
|
+
if (loadRateLimit !== undefined) {
|
|
263
|
+
entity.loadRateLimit = loadRateLimit;
|
|
264
|
+
}
|
|
265
|
+
return entity;
|
|
266
|
+
}
|
|
267
|
+
}
|
package/dist/3d/loader.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { Gg3dWorld, Gg3dWorldTypeDocRepo } from './gg-3d-world';
|
|
|
2
2
|
import { GgMeta } from './models/gg-meta';
|
|
3
3
|
import { Entity3d } from './entities/entity-3d';
|
|
4
4
|
import { Point3, Point4 } from '../base';
|
|
5
|
+
import { Gg3dLevelLoader } from './level-loader';
|
|
5
6
|
export declare enum CachingStrategy {
|
|
6
7
|
Nothing = 0,
|
|
7
8
|
Files = 1,
|
|
@@ -28,11 +29,51 @@ export type LoadResult<TypeDoc extends Gg3dWorldTypeDocRepo = Gg3dWorldTypeDocRe
|
|
|
28
29
|
export type LoadResultWithProps<TypeDoc extends Gg3dWorldTypeDocRepo = Gg3dWorldTypeDocRepo> = LoadResult<TypeDoc> & {
|
|
29
30
|
props?: LoadResult<TypeDoc>[];
|
|
30
31
|
};
|
|
31
|
-
|
|
32
|
-
|
|
32
|
+
/**
|
|
33
|
+
* Settings for the built-in `"Glb"` level entity class (3D only): loads a GG GLB+meta pair via
|
|
34
|
+
* `Gg3dLoader.loadGgGlb` and returns every entity it produces (the model itself, plus any nested
|
|
35
|
+
* props/scenes) grouped under one `GroupEntity`.
|
|
36
|
+
*/
|
|
37
|
+
export interface Glb3DSettings {
|
|
38
|
+
/**
|
|
39
|
+
* Path (URL or path prefix, without extension) to the `.glb`/`.meta` pair - passed straight
|
|
40
|
+
* through to `loadGgGlb`
|
|
41
|
+
*/
|
|
42
|
+
path: string;
|
|
43
|
+
/**
|
|
44
|
+
* Position of the loaded model
|
|
45
|
+
*/
|
|
46
|
+
position?: Point3;
|
|
47
|
+
/**
|
|
48
|
+
* Rotation of the loaded model
|
|
49
|
+
*/
|
|
50
|
+
rotation?: Point4;
|
|
51
|
+
/**
|
|
52
|
+
* Caching strategy, see `CachingStrategy`. Defaults to `CachingStrategy.Nothing`, same as
|
|
53
|
+
* `loadGgGlb` itself.
|
|
54
|
+
*/
|
|
55
|
+
cachingStrategy?: CachingStrategy;
|
|
56
|
+
/**
|
|
57
|
+
* Whether to also load dummies flagged as props/scenes. Defaults to `true`, same as `loadGgGlb`.
|
|
58
|
+
*/
|
|
59
|
+
loadProps?: boolean;
|
|
60
|
+
/**
|
|
61
|
+
* Path where to find prop scenes, if different from `path`'s own directory
|
|
62
|
+
*/
|
|
63
|
+
propsPath?: string;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Full 3D loader exposed as `Gg3dWorld.loader`: GLB+meta asset loading (`loadGgGlb` and friends)
|
|
67
|
+
* layered on top of `Gg3dLevelLoader`, so `registerClass`/`loadLevel`/`loadLevelFromUrl`/
|
|
68
|
+
* `getEntityByName` are all available directly on `world.loader`. Also registers a `"Glb"` level
|
|
69
|
+
* entity class (see `Glb3DSettings`) so a level JSON can place a GLB model declaratively, the same
|
|
70
|
+
* way it places primitives/triggers/cameras.
|
|
71
|
+
* @template TypeDoc - The type document repository
|
|
72
|
+
*/
|
|
73
|
+
export declare class Gg3dLoader<TypeDoc extends Gg3dWorldTypeDocRepo = Gg3dWorldTypeDocRepo> extends Gg3dLevelLoader<TypeDoc> {
|
|
33
74
|
readonly filesCache: Map<string, [ArrayBuffer, GgMeta] | Promise<[ArrayBuffer, GgMeta]>>;
|
|
34
75
|
readonly loadResultCache: Map<string, LoadResourcesResult<TypeDoc> | Promise<LoadResourcesResult<TypeDoc>>>;
|
|
35
|
-
constructor(world: Gg3dWorld);
|
|
76
|
+
constructor(world: Gg3dWorld<TypeDoc>);
|
|
36
77
|
loadGgGlbFiles(path: string, useCache?: boolean): Promise<[ArrayBuffer, GgMeta]>;
|
|
37
78
|
loadGgGlbResources(path: string, cachingStrategy?: CachingStrategy): Promise<LoadResourcesResult<TypeDoc>>;
|
|
38
79
|
loadGgGlb(path: string, options?: Partial<LoadOptions>): Promise<LoadResultWithProps<TypeDoc>>;
|