Flutter Engine
The Flutter Engine
MeshSlide.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2023 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
21#include "tools/DecodeUtils.h"
22#include "tools/Resources.h"
24#include "tools/viewer/Slide.h"
25
26#include <cmath>
27#include <cstddef>
28#include <cstdint>
29#include <iterator>
30#include <vector>
31
32#include "imgui.h"
33
34namespace {
35
36float lerp(float a, float b, float t) {
37 return a + (b - a)*t;
38}
39
42
43 // Normalize to 1x1 for UV sampling.
44 const auto lm = SkMatrix::Scale(1.0f/img->width(), 1.0f/img->height());
45
49 &lm);
50}
51
52static constexpr struct ShaderFactory {
53 const char* fName;
54 sk_sp<SkShader> (*fBuild)();
55} gShaderFactories[] = {
56 {
57 "Img (Mandrill)",
58 []() ->sk_sp<SkShader> { return make_image_shader("images/mandrill_512.png"); }
59 },
60 {
61 "Img (Baby Tux)",
62 []() ->sk_sp<SkShader> { return make_image_shader("images/baby_tux.png"); }
63 },
64 {
65 "Img (Brickwork)",
66 []() ->sk_sp<SkShader> { return make_image_shader("images/brickwork-texture.jpg"); }
67 },
68 {
69 "Radial Gradient",
70 []() ->sk_sp<SkShader> {
71 static constexpr SkColor gColors[] = {
73 };
74 return SkGradientShader::MakeRadial({0.5f, 0.5f}, 0.5f, gColors, nullptr,
76 }
77 },
78 {
79 "Colors",
80 []() ->sk_sp<SkShader> { return nullptr; }
81 },
82};
83
84static constexpr struct VertexAnimator {
85 const char* fName;
86 void (*fAanimate)(const std::vector<SkPoint>& uv, float t, std::vector<SkPoint>& out);
87} gVertexAnimators[] = {
88 {
89 "Cylinderator",
90 // Simulate a cylinder rolling sideways across the 1x1 uv space.
91 [](const std::vector<SkPoint>& uvs, float t, std::vector<SkPoint>& out) {
92 static constexpr float kCylRadius = .2f;
93
94 const auto cyl_pos = t;
95
96 for (size_t i = 0; i < uvs.size(); ++i) {
97 const auto& uv = uvs[i];
98
99 if (uv.fX <= cyl_pos) {
100 out[i] = uv;
101 continue;
102 }
103
104 const auto arc_len = uv.fX - cyl_pos,
105 arc_ang = arc_len/kCylRadius;
106
107 out[i] = SkPoint{
108 cyl_pos + std::sin(arc_ang)*kCylRadius,
109 uv.fY,
110 };
111 }
112 },
113 },
114 {
115 "Squircillator",
116 // Pull all vertices towards center, proportionally, such that the outer square edge
117 // is mapped to a circle for t == 1.
118 [](const std::vector<SkPoint>& uvs, float t, std::vector<SkPoint>& out) {
119 for (size_t i = 0; i < uvs.size(); ++i) {
120 // remap to [-.5,.5]
121 const auto uv = (uvs[i] - SkPoint{0.5,0.5});
122
123 // Distance from center to outer edge for the line pasing through uv.
124 const auto d = uv.length()*0.5f/std::max(std::abs(uv.fX), std::abs(uv.fY));
125 // Scale needed to pull the outer edge to the r=0.5 circle at t == 1.
126 const auto s = lerp(1, (0.5f / d), t);
127
128 out[i] = uv*s + SkPoint{0.5, 0.5};
129 }
130 },
131 },
132 {
133 "Twirlinator",
134 // Rotate vertices proportional to their distance to center.
135 [](const std::vector<SkPoint>& uvs, float t, std::vector<SkPoint>& out) {
136 static constexpr float kMaxRotate = SK_FloatPI*4;
137
138 for (size_t i = 0; i < uvs.size(); ++i) {
139 // remap to [-.5,.5]
140 const auto uv = (uvs[i] - SkPoint{0.5,0.5});
141 const auto angle = kMaxRotate * t * uv.length();
142
143 out[i] = SkMatrix::RotateRad(angle).mapPoint(uv) + SkPoint{0.5, 0.5};
144 }
145 },
146 },
147 {
148 "Wigglynator",
149 [](const std::vector<SkPoint>& uvs, float t, std::vector<SkPoint>& out) {
150 const float radius = t*0.2f/(std::sqrt(uvs.size()) - 1);
151 for (size_t i = 0; i < uvs.size(); ++i) {
152 const float phase = i*SK_FloatPI*0.31f,
153 angle = phase + t*SK_FloatPI*2;
154 out[i] = uvs[i] + SkVector{
155 radius*std::cos(angle),
156 radius*std::sin(angle),
157 };
158 }
159 },
160 },
161 {
162 "None",
163 [](const std::vector<SkPoint>& uvs, float, std::vector<SkPoint>& out) { out = uvs; },
164 },
165};
166
167class MeshSlide final : public Slide {
168public:
169 MeshSlide() : fTimeMapper({0.5f, 0}, {0.5f, 1}) { fName = "Mesh"; }
170
171 void load(SkScalar w, SkScalar h) override {
172 fSize = {w, h};
173
174 this->initMesh(256);
175 this->initShader(gShaderFactories[0]);
176 }
177
178 void resize(SkScalar w, SkScalar h) override { fSize = {w, h}; }
179
180 void draw(SkCanvas* canvas) override {
181 SkAutoCanvasRestore acr(canvas, true);
182
183 SkPaint p;
184 p.setAntiAlias(true);
185 p.setColor(SK_ColorWHITE);
186
187 static constexpr float kMeshFraction = 0.85f;
188 const float mesh_size = std::min(fSize.fWidth, fSize.fHeight)*kMeshFraction;
189
190 canvas->translate((fSize.fWidth - mesh_size) * 0.5f,
191 (fSize.fHeight - mesh_size) * 0.5f);
192 canvas->scale(mesh_size, mesh_size);
193
195 fVertices.size(),
196 fVertices.data(),
197 fShader ? fUVs.data() : nullptr,
198 fShader ? nullptr : fColors.data(),
199 fIndices.size(),
200 fIndices.data());
201 p.setShader(fShader);
202 canvas->drawVertices(verts, SkBlendMode::kModulate, p);
203
204 if (fShowMesh) {
205 p.setShader(nullptr);
206 p.setColor(SK_ColorBLUE);
207 p.setStroke(true);
208 p.setStrokeWidth(0.5f/mesh_size);
209
210 SkASSERT(fIndices.size() % 6 == 0);
211 for (auto i = fIndices.cbegin(); i < fIndices.cend(); i += 6) {
212 canvas->drawLine(fVertices[i[0]], fVertices[i[1]], p);
213 canvas->drawLine(fVertices[i[1]], fVertices[i[2]], p);
214 canvas->drawLine(fVertices[i[2]], fVertices[i[0]], p);
215 canvas->drawLine(fVertices[i[3]], fVertices[i[4]], p);
216 canvas->drawLine(fVertices[i[4]], fVertices[i[5]], p);
217 canvas->drawLine(fVertices[i[5]], fVertices[i[3]], p);
218 }
219
220 p.setStrokeCap(SkPaint::kRound_Cap);
221 p.setStrokeWidth(5/mesh_size);
222 canvas->drawPoints(SkCanvas::kPoints_PointMode, fVertices.size(), fVertices.data(), p);
223 }
224
225 this->drawControls();
226 }
227
228 bool animate(double nanos) override {
229 if (!fTimeBase) {
230 fTimeBase = nanos;
231 }
232
233 // Oscillating between 0..1
234 const float t =
235 std::abs((std::fmod((nanos - fTimeBase)*0.000000001*fAnimationSpeed, 2) - 1));
236
237 // Add some easing
238 fCurrentAnimator->fAanimate(fUVs, fTimeMapper.computeYFromX(t), fVertices);
239
240 return true;
241 }
242
243private:
244 void initMesh(size_t vertex_count) {
245 // Generate an NxN mesh. For simplicity, we keep the vertices in normalized space
246 // (1x1 same as UVs), and scale the mesh up when rendering.
247 const auto n = static_cast<size_t>(std::sqrt(vertex_count));
248 SkASSERT(n > 0);
249 SkASSERT(n == std::sqrt(vertex_count));
250
251 fVertices.resize(vertex_count);
252 fUVs.resize(vertex_count);
253 fColors.resize(vertex_count);
254 for (size_t i = 0; i < vertex_count; ++i) {
255 fVertices[i] = fUVs[i] = {
256 static_cast<float>(i % n) / (n - 1),
257 static_cast<float>(i / n) / (n - 1),
258 };
259 fColors[i] = SkColorSetRGB(!!(i%2)*255,
260 !!(i%3)*255,
261 !!((i+1)%3)*255);
262 }
263
264 // Trivial triangle tessellation pattern:
265 //
266 // *---*---*
267 // | /|\ |
268 // | / | \ |
269 // |/ | \|
270 // *---*---*
271 // |\ | /|
272 // | \ | / |
273 // | \|/ |
274 // *---*---*
275 const size_t triangle_count = 2*(n - 1)*(n - 1),
276 index_count = 3*triangle_count;
277
278 fIndices.clear();
279 fIndices.reserve(index_count);
280 for (size_t i = 0; i < n - 1; ++i) {
281 for (size_t j = 0; j < n - 1; ++j) {
282 const auto row_0_idx = j*n + i,
283 row_1_idx = row_0_idx + n,
284 off_0 = (i + j) % 2,
285 off_1 = 1 - off_0;
286
287 fIndices.push_back(row_0_idx + 0);
288 fIndices.push_back(row_0_idx + 1);
289 fIndices.push_back(row_1_idx + off_0);
290
291 fIndices.push_back(row_0_idx + off_1);
292 fIndices.push_back(row_1_idx + 1);
293 fIndices.push_back(row_1_idx + 0);
294 }
295 }
296
297 SkASSERT(fIndices.size() == index_count);
298 }
299
300 void initShader(const ShaderFactory& fact) {
301 fShader = fact.fBuild();
302 fCurrentShaderFactory = &fact;
303 }
304
305 void drawControls() {
306 ImGui::Begin("Mesh Options");
307
308 if (ImGui::BeginCombo("Texture", fCurrentShaderFactory->fName)) {
309 for (const auto& fact : gShaderFactories) {
310 const auto is_selected = (fCurrentShaderFactory->fName == fact.fName);
311 if (ImGui::Selectable(fact.fName) && !is_selected) {
312 this->initShader(fact);
313 }
314 if (is_selected) {
315 ImGui::SetItemDefaultFocus();
316 }
317 }
318 ImGui::EndCombo();
319 }
320
321 if (ImGui::BeginCombo("Animator", fCurrentAnimator->fName)) {
322 for (const auto& anim : gVertexAnimators) {
323 const auto is_selected = (fCurrentAnimator->fName == anim.fName);
324 if (ImGui::Selectable(anim.fName) && !is_selected) {
325 fCurrentAnimator = &anim;
326 fTimeBase = 0;
327 }
328 if (is_selected) {
329 ImGui::SetItemDefaultFocus();
330 }
331 }
332 ImGui::EndCombo();
333 }
334
335 static constexpr struct {
336 const char* fLabel;
337 size_t fCount;
338 } gSizeInfo[] = {
339 { "4x4", 16 },
340 { "8x8", 64 },
341 { "16x16", 256 },
342 { "32x32", 1024 },
343 { "64x64", 4096 },
344 { "128x128", 16384 },
345 };
346 ImGui::SliderInt("Mesh Size",
347 &fMeshSizeSelector,
348 0, std::size(gSizeInfo) - 1,
349 gSizeInfo[fMeshSizeSelector].fLabel);
350 if (fVertices.size() != gSizeInfo[fMeshSizeSelector].fCount) {
351 this->initMesh(gSizeInfo[fMeshSizeSelector].fCount);
352 }
353
354 ImGui::SliderFloat("Speed", &fAnimationSpeed, 0.25, 4, "%.2f");
355
356 ImGui::Checkbox("Show mesh", &fShowMesh);
357
358 ImGui::End();
359 }
360
361 SkSize fSize;
362 sk_sp<SkShader> fShader;
363 std::vector<SkPoint> fVertices,
364 fUVs;
365 std::vector<SkColor> fColors;
366 std::vector<uint16_t> fIndices;
367
368 double fTimeBase = 0;
369 const SkCubicMap fTimeMapper;
370
371 // UI stuff
372 const ShaderFactory* fCurrentShaderFactory = &gShaderFactories[0];
373 const VertexAnimator* fCurrentAnimator = &gVertexAnimators[0];
374 int fMeshSizeSelector = 2;
375 float fAnimationSpeed = 1.f;
376 bool fShowMesh = false;
377};
378
379} // anonymous namespace
380
381DEF_SLIDE(return new MeshSlide{};)
const char * fName
SkPoint lerp(const SkPoint &a, const SkPoint &b, float t)
static const SkColor gColors[]
#define SkASSERT(cond)
Definition: SkAssert.h:116
@ kModulate
r = s*d
uint32_t SkColor
Definition: SkColor.h:37
#define SkColorSetRGB(r, g, b)
Definition: SkColor.h:57
constexpr SkColor SK_ColorBLUE
Definition: SkColor.h:135
constexpr SkColor SK_ColorRED
Definition: SkColor.h:126
constexpr SkColor SK_ColorGREEN
Definition: SkColor.h:131
constexpr SkColor SK_ColorWHITE
Definition: SkColor.h:122
constexpr float SK_FloatPI
static SkString resource(SkPDFResourceType type, int index)
static SkPDFIndirectReference make_image_shader(SkPDFDocument *doc, SkMatrix finalMatrix, SkTileMode tileModesX, SkTileMode tileModesY, SkRect bBox, const SkImage *image, SkColor4f paintColor)
Definition: SkPDFShader.cpp:84
#define DEF_SLIDE(code)
Definition: Slide.h:25
void drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint &paint)
Definition: SkCanvas.cpp:1710
void translate(SkScalar dx, SkScalar dy)
Definition: SkCanvas.cpp:1278
void drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1, const SkPaint &paint)
Definition: SkCanvas.cpp:2700
void scale(SkScalar sx, SkScalar sy)
Definition: SkCanvas.cpp:1289
void drawVertices(const SkVertices *vertices, SkBlendMode mode, const SkPaint &paint)
Definition: SkCanvas.cpp:1720
@ kPoints_PointMode
draw each point separately
Definition: SkCanvas.h:1241
static sk_sp< SkShader > MakeRadial(const SkPoint &center, SkScalar radius, const SkColor colors[], const SkScalar pos[], int count, SkTileMode mode, uint32_t flags=0, const SkMatrix *localMatrix=nullptr)
int width() const
Definition: SkImage.h:285
sk_sp< SkShader > makeShader(SkTileMode tmx, SkTileMode tmy, const SkSamplingOptions &, const SkMatrix *localMatrix=nullptr) const
Definition: SkImage.cpp:179
int height() const
Definition: SkImage.h:291
static SkMatrix Scale(SkScalar sx, SkScalar sy)
Definition: SkMatrix.h:75
static SkMatrix RotateRad(SkScalar rad)
Definition: SkMatrix.h:114
SkPoint mapPoint(SkPoint pt) const
Definition: SkMatrix.h:1374
@ kRound_Cap
adds circle
Definition: SkPaint.h:335
static sk_sp< SkVertices > MakeCopy(VertexMode mode, int vertexCount, const SkPoint positions[], const SkPoint texs[], const SkColor colors[], int indexCount, const uint16_t indices[])
Definition: SkVertices.cpp:200
@ kTriangles_VertexMode
Definition: SkVertices.h:31
Definition: Slide.h:29
virtual void resize(SkScalar winWidth, SkScalar winHeight)
Definition: Slide.h:41
virtual void load(SkScalar winWidth, SkScalar winHeight)
Definition: Slide.h:40
virtual bool animate(double nanos)
Definition: Slide.h:39
virtual void draw(SkCanvas *canvas)=0
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE auto & d
Definition: main.cc:19
float SkScalar
Definition: extension.cpp:12
static bool b
struct MyStruct s
struct MyStruct a[10]
static float max(float r, float g, float b)
Definition: hsl.cpp:49
static float min(float r, float g, float b)
Definition: hsl.cpp:48
sk_sp< SkImage > GetResourceAsImage(const char *resource)
Definition: DecodeUtils.h:25
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
SkSamplingOptions(SkFilterMode::kLinear))
SIN Vec< N, float > abs(const Vec< N, float > &x)
Definition: SkVx.h:707
SIN Vec< N, float > sqrt(const Vec< N, float > &x)
Definition: SkVx.h:706
SkScalar w
SkScalar h
float fY
y-axis value
Definition: SkPoint_impl.h:165
Definition: SkSize.h:52