flutter3d
Showcase Changelog 38 packages API reference

glTF and GLB: the source

since 0.1.0 Formats and I/O

Regions the guide quotes: write (line 23), load (line 51), upload (line 58), check (line 95).

1/// Reading a real GLB with `GltfLoader`, into meshes this page draws.
2///
3/// Quoted by `gltf_load.md` and shown whole in the Source tab.
4library;
5
6import 'package:flutter3d/flutter3d.dart';
7import 'package:flutter3d_showcase/src/demo/demo.dart';
8import 'package:vector_math/vector_math.dart';
9
10final class GltfLoadDemo extends ShowcaseDemo {
11 late final GltfAsset _asset;
13 @override
14 void configureView(DemoContext context) {
15 context.orbit
16 ..distance = 3.0
17 ..pitch = 0.35
18 ..yaw = 0.6;
19 }
21 @override
22 Future<void> prepare(DemoContext context) async {
23 // A real GLB, written by this engine's own `GltfWriter` so this page
24 // needs no bundled asset to read: two surfaces, one material each.
25 final source = PlainModelDocument(
26 surfaces: <ModelSurface>[
27 ModelSurface(
28 mesh: CuboidShape(size: Vector3.all(0.9)).build(),
29 materialIndex: 0,
30 ),
31 ModelSurface(
32 mesh: SphereShape(segments: 32, rings: 16).build(),
33 materialIndex: 1,
34 ),
35 ],
36 materials: <SurfaceMaterial>[
37 SurfaceMaterial(baseColor: Vector4(0.8, 0.4, 0.3, 1.0)),
38 SurfaceMaterial(
39 baseColor: Vector4(0.3, 0.6, 0.8, 1.0),
40 metallic: 0.8,
41 roughness: 0.3,
42 ),
43 ],
44 nodes: <ModelNode>[
45 ModelNode(surfaces: <int>[0]),
46 ModelNode(surfaces: <int>[1], translation: Vector3(1.6, 0.0, 0.0)),
47 ],
48 );
49 final bytes = GltfWriter(source).writeGlb();
51 // `GltfLoader.load` reads the container, walks its accessors and
52 // materials, and hands back a `GltfAsset`, whatever wrote the file.
53 _asset = await GltfLoader().load(bytes);
54 }
56 @override
57 Scene build(DemoContext context) {
58 // `GltfAsset` is a `ModelDocument`: a list of surfaces, each with an
59 // index into the document's materials, plus a node hierarchy that
60 // places them. This page uploads each surface by hand rather than
61 // through `ModelAsset`, to keep the loader itself the whole story; the
62 // `model-asset` page shows the upload path a real application uses,
63 // textures included.
64 final scene = Scene();
65 for (final ModelNode node in _asset.nodes) {
66 for (final int surfaceIndex in node.surfaces) {
67 final ModelSurface surface = _asset.surfaces[surfaceIndex];
68 final SurfaceMaterial? material = surface.materialIndex == null
69 ? null
70 : _asset.materials[surface.materialIndex!];
71 scene.add(
72 MeshNode(
73 DeviceMesh.upload(context.device, surface.mesh),
74 Material(
75 name: material?.name,
76 baseColor: material?.baseColor ?? Vector4(0.8, 0.8, 0.8, 1.0),
77 metallic: material?.metallic ?? 0.0,
78 roughness: material?.roughness ?? 0.6,
79 ),
80 name: surface.name,
81 )..setPositionFrom(node.translation),
82 );
83 }
84 }
86 scene.add(
87 LightNode(name: 'sun', intensity: 3.0)
88 ..setLocalForward(Vector3(-0.4, -1.0, -0.3)),
89 );
90 return scene;
91 }
93 @override
94 void verify(Scene scene, FrameResult frame) {
95 if (_asset.surfaces.length != 2) {
96 throw StateError('the GLB decoded to the wrong number of surfaces');
97 }
98 if (_asset.vertexCount == 0) {
99 throw StateError('the decoded mesh has no vertices');
100 }
101 if (frame.drawCalls < 2) {
102 throw StateError('both surfaces were not drawn');
103 }
104 }