Mesh editing overlays: the source
Open the live demo · Read the guide · View on GitHub
Regions the guide quotes: contributor (line 28), surface (line 35), camera (line 59), batches (line 77), controls (line 136), check (line 155).
1/// A modeller-style overlay drawn against the scene depth buffer.
2library;
4import 'dart:math' as math;
6import 'package:flutter3d/flutter3d.dart';
7import 'package:flutter3d_showcase/src/demo/demo.dart';
8import 'package:vector_math/vector_math.dart';
10final class MeshOverlayDemo extends ShowcaseDemo {
11 static const double _viewportHeight = 720;
13 late final MeshOverlay _overlay;
14 bool _vertices = true;
15 bool _face = true;
16 bool _through = true;
19 void configureView(DemoContext context) {
20 context.orbit
21 ..distance = 6.2
22 ..pitch = 0.28
23 ..yaw = 0.62;
24 }
27 Scene build(DemoContext context) {
28 _overlay = context.renderer.addContributor(
29 MeshOverlay(
30 vertexShader: context.renderer.debugLineVertexShader,
31 fragmentShader: context.renderer.debugLineFragmentShader,
32 ),
33 );
35 final MeshNode cube = MeshNode(
36 DeviceMesh.upload(
37 context.device,
38 CuboidShape(size: Vector3.all(2.0)).build(),
39 ),
40 Material(
41 name: 'editable surface',
42 baseColor: Vector4(0.16, 0.32, 0.52, 1.0),
43 roughness: 0.58,
44 ),
45 name: 'editable cube',
46 );
47 final Scene scene = Scene()
48 ..ambientColor = Vector3(0.45, 0.52, 0.68)
49 ..ambientIntensity = 0.16
50 ..add(cube)
51 ..add(
52 LightNode(name: 'key', intensity: 3.6)
53 ..setLocalForward(Vector3(-0.45, -0.8, -0.35)),
54 );
55 _rebuild(context);
56 return scene;
57 }
59 void _rebuild(DemoContext context) {
60 final Matrix4 cameraWorld = context.camera.worldMatrix;
61 final values = cameraWorld.storage;
62 final Vector3 right = Vector3(values[0], values[1], values[2]);
63 final Vector3 up = Vector3(values[4], values[5], values[6]);
64 final double fov =
65 context.camera.projection.verticalFieldOfView ?? math.pi / 4;
66 _overlay
67 ..clear()
68 ..lookFrom(
69 eye: context.camera.readWorldPosition(),
70 right: right,
71 up: up,
72 pixel: 2.0 * math.tan(fov * 0.5) / _viewportHeight,
73 );
74 _writeGeometry();
75 }
77 void _writeGeometry() {
78 final List<Vector3> corners = <Vector3>[
79 for (final double z in <double>[-1, 1])
80 for (final double y in <double>[-1, 1])
81 for (final double x in <double>[-1, 1]) Vector3(x, y, z),
82 ];
83 final Vector4 edgeColour = Vector4(0.2, 0.82, 1.0, 1.0);
84 for (final (int a, int b) in <(int, int)>[
85 (0, 1),
86 (0, 2),
87 (1, 3),
88 (2, 3),
89 (4, 5),
90 (4, 6),
91 (5, 7),
92 (6, 7),
93 (0, 4),
94 (1, 5),
95 (2, 6),
96 (3, 7),
97 ]) {
98 _overlay.edge(corners[a], corners[b], edgeColour);
99 }
100 if (_vertices) {
101 for (final Vector3 corner in corners) {
102 _overlay.point(corner, Vector4(1.0, 0.72, 0.12, 1.0), size: 11);
103 }
104 }
105 if (_face) {
106 final Vector4 selected = Vector4(0.2, 1.0, 0.45, 1.0);
107 _overlay
108 ..wash(corners[4], corners[5], corners[7], selected)
109 ..wash(corners[4], corners[7], corners[6], selected)
110 ..ribbon(corners[4], corners[7], selected, width: 5);
111 }
112 if (_through) {
113 _overlay.throughGeometry(() {
114 _overlay
115 ..edge(
116 Vector3(-1.6, 0.0, 0.0),
117 Vector3(1.6, 0.0, 0.0),
118 Vector4(0.78, 0.42, 1.0, 1.0),
119 )
120 ..ribbon(
121 Vector3(0.0, -1.6, 0.0),
122 Vector3(0.0, 1.6, 0.0),
123 Vector4(0.78, 0.42, 1.0, 1.0),
124 width: 7,
125 )
126 ..point(Vector3(0.0, 1.6, 0.0), Vector4(0.78, 0.42, 1.0, 1.0));
127 });
128 }
129 }
132 void update(DemoContext context, double dt) => _rebuild(context);
135 List<DemoControl> controls(DemoContext context) => <DemoControl>[
136 ToggleControl(
137 'Vertex handles',
138 value: () => _vertices,
139 onChanged: (bool value) => _vertices = value,
140 ),
141 ToggleControl(
142 'Selected face',
143 value: () => _face,
144 onChanged: (bool value) => _face = value,
145 ),
146 ToggleControl(
147 'Through-mesh gizmo',
148 value: () => _through,
149 onChanged: (bool value) => _through = value,
150 ),
151 ];
154 void verify(Scene scene, FrameResult frame) {
155 if (_overlay.lines.vertexCount != 24 ||
156 _overlay.handles.vertexCount != 54 ||
157 _overlay.fill.vertexCount != 6 ||
158 _overlay.linesThrough.isEmpty ||
159 _overlay.handlesThrough.isEmpty ||
160 frame.drawCalls < 6) {
161 throw StateError('the mesh overlay batches did not reach the frame');
162 }
163 }
164}