Loading into a scene
Open the live demo · Read the source · View on GitHub
GltfLoader, ObjLoader and every other reader in this engine produce a
ModelDocument: geometry, materials and a hierarchy, still on the CPU.
ModelAsset.fromDocument is the one path from there onto a GPU, whatever
format the document came from.
Step 1: A decoded document #
This page builds its own rather than reading a file, but the shape is exactly what a real loader hands back: surfaces, a material, and a node naming each one.
// A decoded document, the shape every format in this package produces.
// `ModelAsset.fromDocument` does not care which one wrote it.
final _document = PlainModelDocument(
surfaces: <ModelSurface>[
ModelSurface(
mesh: SphereShape(segments: 32, rings: 16).build(),
materialIndex: 0,
),
],
materials: <SurfaceMaterial>[
SurfaceMaterial(
name: 'shell',
baseColor: Vector4(0.75, 0.4, 0.6, 1.0),
metallic: 0.2,
roughness: 0.4,
),
],
nodes: <ModelNode>[
ModelNode(surfaces: <int>[0]),
],
);
Step 2: Upload it once #
Meshes and images are shared by identity, so a document that reuses one
mesh across several surfaces uploads it once. Materials come out as the
engine's own Material, with textures resolved and bound.
// Meshes and images are uploaded once, deduplicated by identity; a
// document's materials become the engine's own `Material`, ready to be
// placed as many times as a scene wants.
_asset = await ModelAsset.fromDocument(_document, device: context.device);
Step 3: Place it in a scene #
An asset is immutable and GPU-resident; instantiate is what puts it
somewhere. Calling it twice makes two independent instances that share
the same uploaded mesh, which is the reason the asset and the instance
are two different things: loading a model twice would upload it twice.
// `instantiate` rebuilds the asset's hierarchy as scene nodes and
// returns a `ModelInstance`: the node it hangs from, one scene node a
// decoded node, and a skeleton or an animation player when the asset
// carries one.
final scene = Scene();
_instance = _asset.instantiate(scene);
Step 4: Check the claim #
The asset has to have actually uploaded something, instantiate has to
have added a mesh to the scene, and the frame has to have drawn it.
if (_asset.vertexCount == 0) {
throw StateError('the asset uploaded no vertices');
}
if (_instance == null || _instance!.meshes.isEmpty) {
throw StateError('instantiate did not add a mesh to the scene');
}
if (frame.drawCalls < 1) {
throw StateError('the instance was not drawn');
}