flutter3d
Showcase Changelog 38 packages API reference

Splats under a budget: the source

since 0.8.0 Scene and geometry

Regions the guide quotes: cloud (line 34), tree (line 64), contributor (line 66), budget (line 76), slider (line 81), check (line 93).

1/// A generated cloud of Gaussians cut into a tree of levels of detail and
2/// drawn under a splat budget.
3///
4/// Quoted by `splat_budget.md` and shown whole in the Source tab.
5library;
6
7import 'dart:math' as math;
8import 'dart:typed_data';
9
10import 'package:flutter3d/flutter3d.dart';
11import 'package:flutter3d_showcase/src/demo/demo.dart';
12import 'package:vector_math/vector_math.dart';
14final class SplatBudgetDemo extends ShowcaseDemo {
15 /// Splats around the ring, and around the tube at each step of the ring.
16 static const int _around = 60;
17 static const int _tube = 40;
19 double budget = 300.0;
21 late final SplatOctree _tree;
22 late final SplatLod _lod;
24 @override
25 void configureView(DemoContext context) {
26 context.orbit
27 ..distance = 5.0
28 ..pitch = 0.6
29 ..yaw = 0.3;
30 }
32 @override
33 Scene build(DemoContext context) {
34 const int count = _around * _tube;
35 final Float32List centres = Float32List(count * 3);
36 final Float32List colours = Float32List(count * 4);
37 final Float32List scales = Float32List(count * 3);
38 final Float32List rotations = Float32List(count * 4);
39 for (var i = 0; i < count; i++) {
40 final double a = (i ~/ _tube) / _around * 2.0 * math.pi;
41 final double b = (i % _tube) / _tube * 2.0 * math.pi;
42 final double ring = 1.4 + 0.45 * math.cos(b);
43 centres.setAll(i * 3, <double>[
44 ring * math.cos(a),
45 0.45 * math.sin(b),
46 ring * math.sin(a),
47 ]);
48 colours.setAll(i * 4, <double>[
49 0.5 + 0.5 * math.cos(a),
50 0.5 + 0.5 * math.sin(b),
51 0.5 - 0.5 * math.cos(a),
52 0.9,
53 ]);
54 scales.setAll(i * 3, <double>[0.05, 0.05, 0.05]);
55 rotations[i * 4 + 3] = 1.0;
56 }
57 final SplatCloud cloud = SplatCloud(
58 centres: centres,
59 colours: colours,
60 scales: scales,
61 rotations: rotations,
62 );
64 _tree = buildSplatOctree(cloud, leafCapacity: 64, grid: 4);
66 _lod = SplatLod(_tree, budget: budget.round());
67 context.renderer.addContributor(SplatContributor.lod(_lod));
69 return Scene()
70 ..ambientColor = Vector3(0.4, 0.46, 0.6)
71 ..ambientIntensity = 0.24;
72 }
74 @override
75 void update(DemoContext context, double dt) {
76 _lod.budget = budget.round();
77 }
79 @override
80 List<DemoControl> controls(DemoContext context) => <DemoControl>[
81 SliderControl(
82 'Splat budget',
83 min: _tree.nodes.first.splatCount.toDouble(),
84 max: _tree.leafSplatCount.toDouble(),
85 value: () => budget,
86 onChanged: (double v) => budget = v,
87 format: (double v) => '${v.round()} of ${_tree.leafSplatCount}',
88 ),
89 ];
91 @override
92 void verify(Scene scene, FrameResult frame) {
93 if (_tree.leafSplatCount != _around * _tube ||
94 _lod.cut.isEmpty ||
95 _lod.cutSplatCount > _lod.budget ||
96 _lod.cutSplatCount >= _tree.leafSplatCount ||
97 frame.drawCalls < 1) {
98 throw StateError('the tree was not drawn as a cut under its budget');
99 }
100 }