flutter3d
Showcase Changelog 38 packages API reference

Writing GLB: the source

since 0.7.0 Formats and I/O

Regions the guide quotes: document (line 20), write (line 41), report (line 65), check (line 94).

1/// Writing a GLB with `GltfWriter`, plain and with `compressGeometry`.
2///
3/// Quoted by `gltf_write.md` and shown whole in the Source tab.
4library;
5
6import 'package:flutter/widgets.dart';
7import 'package:flutter3d/flutter3d.dart';
8import 'package:flutter3d_showcase/src/demo/demo.dart';
9import 'package:vector_math/vector_math.dart';
11final class GltfWriteDemo extends ShowcaseDemo {
12 late final int _plainBytes;
13 late final int _compressedBytes;
14 late final bool _usedQuantization;
15 late final bool _usedReordering;
16 late final List<int> _magic;
18 @override
19 Scene build(DemoContext context) {
20 // A sphere with enough vertices that quantizing its attributes and
21 // reordering its triangles actually has something to save on.
22 final document = PlainModelDocument(
23 surfaces: <ModelSurface>[
24 ModelSurface(
25 mesh: SphereShape(segments: 48, rings: 24).build(),
26 materialIndex: 0,
27 ),
28 ],
29 materials: <SurfaceMaterial>[
30 SurfaceMaterial(
31 name: 'shell',
32 baseColor: Vector4(0.8, 0.5, 0.2, 1.0),
33 roughness: 0.5,
34 ),
35 ],
36 nodes: <ModelNode>[
37 ModelNode(surfaces: <int>[0]),
38 ],
39 );
41 final plain = GltfWriter(document).writeGlb();
42 final compressor = GltfWriter(document, compressGeometry: true);
43 final compressed = compressor.writeGlb();
45 _plainBytes = plain.length;
46 _compressedBytes = compressed.length;
47 _usedQuantization = compressor.usedGeometryQuantization;
48 _usedReordering = compressor.usedVertexCacheReordering;
49 _magic = plain.take(4).toList();
51 return Scene()
52 ..add(
53 MeshNode(
54 DeviceMesh.upload(context.device, document.surfaces.single.mesh),
55 Material(baseColor: Vector4(0.8, 0.5, 0.2, 1.0), roughness: 0.5),
56 name: 'shell',
57 ),
58 )
59 ..add(
60 LightNode(name: 'sun', intensity: 3.0)
61 ..setLocalForward(Vector3(-0.4, -1.0, -0.3)),
62 );
63 }
65 String _report() {
66 final magicText = String.fromCharCodes(_magic);
67 final ratio = _plainBytes / _compressedBytes;
68 return 'GLB magic: "$magicText"\n'
69 'plain: $_plainBytes bytes\n'
70 'compressed: $_compressedBytes bytes '
71 '(${ratio.toStringAsFixed(2)}x smaller)\n'
72 'used quantization: $_usedQuantization\n'
73 'used vertex-cache reordering: $_usedReordering';
74 }
76 @override
77 Widget? customBody(BuildContext buildContext, DemoContext context) =>
78 Container(
79 color: const Color(0xFF14161A),
80 padding: const EdgeInsets.all(24),
81 alignment: Alignment.topLeft,
82 child: DefaultTextStyle(
83 style: const TextStyle(
84 color: Color(0xFFE8E8EC),
85 fontSize: 16,
86 fontFamily: 'monospace',
87 ),
88 child: Text(_report()),
89 ),
90 );
92 @override
93 void verify(Scene scene, FrameResult frame) {
94 if (_magic.first != 0x67) {
95 throw StateError('the file does not start with the GLB magic');
96 }
97 if (_compressedBytes >= _plainBytes) {
98 throw StateError('compressGeometry did not shrink the file');
99 }
100 if (frame.drawCalls < 1) {
101 throw StateError('the sphere was not drawn');
102 }
103 }