Draco meshes: the source
Open the live demo · Read the guide · View on GitHub
Regions the guide quotes: bitstream (line 16), decode (line 35), rebuild (line 38), check (line 99).
1/// Decoding a real Draco bitstream with `decodeDraco`.
2///
3/// Quoted by `draco.md` and shown whole in the Source tab.
4library;
6import 'dart:convert';
8import 'package:flutter3d/flutter3d.dart';
9import 'package:flutter3d_showcase/src/demo/demo.dart';
10import 'package:vector_math/vector_math.dart';
12final class DracoDemo extends ShowcaseDemo {
13 late final DracoMesh _mesh;
14 late final MeshData _upload;
16 // A small edgebreaker-encoded mesh, the same fixture
17 // `flutter3d_core`'s own decoder test checks against an independently
18 // computed set of triangles. Edgebreaker connectivity carries no index
19 // buffer and no vertex order of its own: both come out however the
20 // encoder's traversal happened to walk the mesh.
21 static const String _fixtureBase64 =
22 'RFJBQ08CAgEBAAAAQDwBPAEBHx8BF2/btm3bfdu2bdv2ruu6ruu6ruu6rusC/wEz/wI1RAL/'
23 'AAAAAAABAAkDAAACAQEJAwABAwEBAQAPIwEHAQgBEAEBAQkBFxTABEaQRtBEhyBH5YsP2Cnb'
24 'LPkrs79pACDdXi8AUMmBEQBA+gYCgBrctAHA6m9DAYAfsbEGAFY/rh8AqMHMbwDgo5GqAQAA'
25 'wPwGAEAarh8AsMEaawDA9WtDAQAfsWkDANd/AwGADW4nBIDtV60BAMzAvQEADuOWBAB3O94D'
26 'AMLH6BYAyDZfeQAgfLS1AYC7jckFALodqhUAAACYXAAA3LG1AQC77ZUHAMjH6BYA0DbjPQCA'
27 'fNySAMBu/zMAEQKoACR0HgLAYSQDgAgnDQAJjQFAhEMFABpbAugdMwB4+DeAl5UAgFkjBKAM'
28 'CQFA0hECYAYaAUDU3gBgehgAMHsJwLdZAYCJMgCQUNIAiJAZAEhIVACcESYA4DADwId9A3CJ'
29 'CQBUW0IAyIgQACYtIQBjnBEAQwUOoCUAAAAA/z8AAAAgrr4AUBi/ACCuvgAwcz8OAAMBAQP/'
30 'B4E2/5+AAQGrAQP//////58BAf//////o4EBqwEBAQH/m4ATIJkSAFI2CrI77Ywk2kkIW/ru'
31 'gP8DAAD/AQAACg==';
34 Scene build(DemoContext context) {
35 final bytes = base64Decode(_fixtureBase64.replaceAll('\n', ''));
36 _mesh = decodeDraco(bytes);
38 // A decoded `DracoMesh` is not yet a `MeshData`: its attributes are
39 // keyed by Draco's own attribute id, not by name, so a caller picks
40 // out the ones it wants. This page draws positions only, with a flat
41 // normal recomputed per triangle.
42 final position = _mesh.attributes.values.firstWhere(
43 (attribute) => attribute.type == DracoAttributeType.position,
44 );
45 final builder = MeshBuilder(
46 VertexLayout.standard,
47 reserveVertices: _mesh.indices.length,
48 reserveIndices: _mesh.indices.length,
49 );
50 Vector3 pointAt(int index) => Vector3(
51 position.values[index * 3],
52 position.values[index * 3 + 1],
53 position.values[index * 3 + 2],
54 );
55 for (var t = 0; t + 2 < _mesh.indices.length; t += 3) {
56 final a = pointAt(_mesh.indices[t]);
57 final b = pointAt(_mesh.indices[t + 1]);
58 final c = pointAt(_mesh.indices[t + 2]);
59 final cross = (b - a).cross(c - a);
60 final normal = cross.length2 > 0.0
61 ? cross.normalized()
62 : Vector3(0, 0, 1);
63 final texcoord = Vector2.zero();
64 final i0 = builder.addVertex(
65 position: a,
66 normal: normal,
67 texcoord: texcoord,
68 );
69 final i1 = builder.addVertex(
70 position: b,
71 normal: normal,
72 texcoord: texcoord,
73 );
74 final i2 = builder.addVertex(
75 position: c,
76 normal: normal,
77 texcoord: texcoord,
78 );
79 builder.addTriangle(i0, i1, i2);
80 }
81 _upload = builder.build();
83 return Scene()
84 ..add(
85 MeshNode(
86 DeviceMesh.upload(context.device, _upload),
87 Material(baseColor: Vector4(0.75, 0.6, 0.5, 1.0), roughness: 0.6),
88 name: 'draco mesh',
89 ),
90 )
91 ..add(
92 LightNode(name: 'sun', intensity: 3.0)
93 ..setLocalForward(Vector3(-0.4, -1.0, -0.3)),
94 );
95 }
98 void verify(Scene scene, FrameResult frame) {
99 if (_mesh.indices.length != 180) {
100 throw StateError(
101 'this fixture always decodes to 60 triangles (180 corners); got '
102 '${_mesh.indices.length}',
103 );
104 }
105 if (_mesh.pointCount == 0) {
106 throw StateError('the decoded mesh has no points');
107 }
108 if (frame.drawCalls < 1) {
109 throw StateError('the decoded mesh was not drawn');
110 }
111 }
112}