Loading a level
Open the live demo · Read the source · View on GitHub
LevelLoader is the bridge between a level document and something drawable:
brushes become mesh nodes, lights become light nodes, and the result is a
LoadedLevel with its own scene, collision world and a list of anything
that went wrong along the way.
Step 1: A document #
A floor and a light, built in code rather than read from a file.
static Level _document() => Level(
name: 'room',
brushes: <Brush>[
Brush(centre: Vector3(0, -0.5, 0), size: Vector3(4, 1, 4)),
],
lights: <LevelLight>[
LevelLight(position: Vector3(0, 3, 0), intensity: 5.0, range: 10.0),
],
);
Step 2: Build it #
LevelLoader.build does everything load does except finding the
document, which is what lets a level built in memory — an editor's unsaved
document, a page like this one — go through the same path a bundled level
does.
_loaded = await const LevelLoader().build(
_document(),
device: context.device,
registry: EntityRegistry(const <EntityKind>[]),
);
Step 3: Share repeated meshes #
SharedMeshes uploads a shape once and hands the same DeviceMesh back to
anything that asks for it again, so three torch posts of the same size cost
one upload.
// Three torch posts that all ask the cache for the same box, uploaded
// once and reused by all three nodes.
_shared = SharedMeshes(context.device);
for (var i = 0; i < 3; i++) {
final mesh = _shared.box(Vector3(0.2, 1.0, 0.2));
final post = MeshNode(
mesh,
Material(name: 'post', baseColor: Vector4(0.4, 0.3, 0.2, 1.0)),
)..setPosition(-1.0 + i.toDouble(), 0.0, 1.5);
_loaded.scene.add(post);
}
A clean document loads with an empty issues list; a level with a missing
texture or an unknown entity type would still load, with the problem named
there instead of stopping the level from playing.