blob: 252cf5e0163781dccd17b581789c71f08ce6f800 [file] [log] [blame]
Mike Reed69ace2a2020-01-11 15:57:14 -05001/*
2 * Copyright 2020 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
8#include "include/core/SkCanvas.h"
9#include "include/core/SkMatrix44.h"
10#include "include/core/SkPaint.h"
11#include "include/core/SkRRect.h"
Mike Reed75435872020-01-13 21:15:06 -050012#include "include/private/SkM44.h"
Mike Reed69ace2a2020-01-11 15:57:14 -050013#include "include/utils/Sk3D.h"
14#include "include/utils/SkRandom.h"
15#include "samplecode/Sample.h"
16#include "tools/Resources.h"
17
18static SkMatrix44 inv(const SkMatrix44& m) {
19 SkMatrix44 inverse;
20 SkAssertResult(m.invert(&inverse));
21 return inverse;
22}
23
Mike Reedee3216d2020-01-17 17:35:04 -050024static SkM44 inv(const SkM44& m) {
25 SkM44 inverse;
26 SkAssertResult(m.invert(&inverse));
27 return inverse;
28}
29
30static SkPoint project(const SkM44& m, SkV4 p) {
31 auto v = m * p;
32 return {v.x / v.w, v.y / v.w};
33}
34
Mike Reed69ace2a2020-01-11 15:57:14 -050035class Sample3DView : public Sample {
36protected:
37 float fNear = 0.05f;
38 float fFar = 4;
39 float fAngle = SK_ScalarPI / 12;
40
41 SkPoint3 fEye { 0, 0, 1.0f/tan(fAngle/2) - 1 };
42 SkPoint3 fCOA { 0, 0, 0 };
43 SkPoint3 fUp { 0, 1, 0 };
44
45 SkMatrix44 fRot;
46 SkPoint3 fTrans;
47
48 void rotate(float x, float y, float z) {
49 SkMatrix44 r;
50 if (x) {
51 r.setRotateAboutUnit(1, 0, 0, x);
52 } else if (y) {
53 r.setRotateAboutUnit(0, 1, 0, y);
54 } else {
55 r.setRotateAboutUnit(0, 0, 1, z);
56 }
57 fRot.postConcat(r);
58 }
59
60public:
Mike Reedee0a03a2020-01-14 16:44:47 -050061 void saveCamera(SkCanvas* canvas, const SkRect& area, SkScalar zscale) {
Mike Reed69ace2a2020-01-11 15:57:14 -050062 SkMatrix44 camera,
63 perspective,
Mike Reed69ace2a2020-01-11 15:57:14 -050064 viewport;
65
Mike Reed69ace2a2020-01-11 15:57:14 -050066 Sk3Perspective(&perspective, fNear, fFar, fAngle);
67 Sk3LookAt(&camera, fEye, fCOA, fUp);
Mike Reed75435872020-01-13 21:15:06 -050068 viewport.setScale(area.width()*0.5f, area.height()*0.5f, zscale)
69 .postTranslate(area.centerX(), area.centerY(), 0);
Mike Reed69ace2a2020-01-11 15:57:14 -050070
Mike Reedee3216d2020-01-17 17:35:04 -050071 // want "world" to be in our big coordinates (e.g. area), so apply this inverse
72 // as part of our "camera".
Mike Reedc43f2a02020-01-16 14:54:34 -050073 canvas->experimental_saveCamera(viewport * perspective, camera * inv(viewport));
Mike Reed69ace2a2020-01-11 15:57:14 -050074 }
75
76 bool onChar(SkUnichar uni) override {
77 float delta = SK_ScalarPI / 30;
78 switch (uni) {
79 case '8': this->rotate( delta, 0, 0); return true;
80 case '2': this->rotate(-delta, 0, 0); return true;
81 case '4': this->rotate(0, delta, 0); return true;
82 case '6': this->rotate(0, -delta, 0); return true;
83 case '-': this->rotate(0, 0, delta); return true;
84 case '+': this->rotate(0, 0, -delta); return true;
85
86 case 'i': fTrans.fZ += 0.1f; SkDebugf("z %g\n", fTrans.fZ); return true;
87 case 'k': fTrans.fZ -= 0.1f; SkDebugf("z %g\n", fTrans.fZ); return true;
88
89 case 'n': fNear += 0.1f; SkDebugf("near %g\n", fNear); return true;
90 case 'N': fNear -= 0.1f; SkDebugf("near %g\n", fNear); return true;
91 case 'f': fFar += 0.1f; SkDebugf("far %g\n", fFar); return true;
92 case 'F': fFar -= 0.1f; SkDebugf("far %g\n", fFar); return true;
93 default: break;
94 }
95 return false;
96 }
97};
98
Mike Reed69ace2a2020-01-11 15:57:14 -050099static SkMatrix44 RX(SkScalar rad) {
100 SkScalar c = SkScalarCos(rad), s = SkScalarSin(rad);
101 SkMatrix44 m;
102 m.set3x3(1, 0, 0,
103 0, c, s,
104 0,-s, c);
105 return m;
106}
107
108static SkMatrix44 RY(SkScalar rad) {
109 SkScalar c = SkScalarCos(rad), s = SkScalarSin(rad);
110 SkMatrix44 m;
111 m.set3x3( c, 0,-s,
112 0, 1, 0,
113 s, 0, c);
114 return m;
115}
116
117struct Face {
118 SkScalar fRx, fRy;
Mike Reedee3216d2020-01-17 17:35:04 -0500119 SkColor fColor;
Mike Reed69ace2a2020-01-11 15:57:14 -0500120
121 static SkMatrix44 T(SkScalar x, SkScalar y, SkScalar z) {
122 SkMatrix44 m;
123 m.setTranslate(x, y, z);
124 return m;
125 }
126
127 static SkMatrix44 R(SkScalar x, SkScalar y, SkScalar z, SkScalar rad) {
128 SkMatrix44 m;
129 m.setRotateAboutUnit(x, y, z, rad);
130 return m;
131 }
132
133 SkMatrix44 asM44(SkScalar scale) const {
134 return RY(fRy) * RX(fRx) * T(0, 0, scale);
135 }
136};
137
Mike Reed75435872020-01-13 21:15:06 -0500138static bool front(const SkM44& m) {
139 SkM44 m2;
Mike Reed69ace2a2020-01-11 15:57:14 -0500140 m.invert(&m2);
Mike Reed75435872020-01-13 21:15:06 -0500141 /*
142 * Classically we want to dot the transpose(inverse(ctm)) with our surface normal.
143 * In this case, the normal is known to be {0, 0, 1}, so we only actually need to look
144 * at the z-scale of the inverse (the transpose doesn't change the main diagonal, so
145 * no need to actually transpose).
146 */
147 return m2.atColMajor(10) > 0;
Mike Reed69ace2a2020-01-11 15:57:14 -0500148}
149
150const Face faces[] = {
Mike Reedee3216d2020-01-17 17:35:04 -0500151 { 0, 0, SK_ColorRED }, // front
152 { 0, SK_ScalarPI, SK_ColorGREEN }, // back
Mike Reed69ace2a2020-01-11 15:57:14 -0500153
Mike Reedee3216d2020-01-17 17:35:04 -0500154 { SK_ScalarPI/2, 0, SK_ColorBLUE }, // top
155 {-SK_ScalarPI/2, 0, SK_ColorCYAN }, // bottom
Mike Reed69ace2a2020-01-11 15:57:14 -0500156
Mike Reedee3216d2020-01-17 17:35:04 -0500157 { 0, SK_ScalarPI/2, SK_ColorMAGENTA }, // left
158 { 0,-SK_ScalarPI/2, SK_ColorYELLOW }, // right
Mike Reed69ace2a2020-01-11 15:57:14 -0500159};
160
Mike Reedb18e74d2020-01-16 13:58:22 -0500161#include "include/core/SkColorFilter.h"
162#include "include/effects/SkColorMatrix.h"
163
164static SkV3 normalize(SkV3 v) { return v * (1.0f / v.length()); }
165
166static SkColorMatrix comput_planar_lighting(SkCanvas* canvas, SkV3 lightDir) {
Mike Reedc43f2a02020-01-16 14:54:34 -0500167 SkM44 l2w = canvas->experimental_getLocalToWorld();
Mike Reedb18e74d2020-01-16 13:58:22 -0500168 auto normal = normalize(l2w * SkV3{0, 0, 1});
169 float dot = -normal * lightDir;
170
171 SkColorMatrix cm;
172 if (dot < 0) {
173 dot = 0;
174 }
175
176 float ambient = 0.5f;
177 float scale = ambient + dot;
178 cm.setScale(scale, scale, scale, 1);
179 return cm;
180}
181
182struct Light {
183 SkPoint fCenter;
184 SkPoint fEndPt;
185 SkScalar fRadius;
186 SkScalar fHeight;
187
188 bool hitTest(SkScalar x, SkScalar y) const {
189 auto xx = x - fCenter.fX;
190 auto yy = y - fCenter.fY;
191 return xx*xx + yy*yy <= fRadius*fRadius;
192 }
193
194 void update(SkScalar x, SkScalar y) {
195 auto xx = x - fCenter.fX;
196 auto yy = y - fCenter.fY;
197 auto len = SkScalarSqrt(xx*xx + yy*yy);
198 if (len > fRadius) {
199 xx *= fRadius / len;
200 yy *= fRadius / len;
201 }
202 fEndPt = {fCenter.fX + xx, fCenter.fY + yy};
203 }
204
205 SkV3 getDir() const {
206 auto pt = fEndPt - fCenter;
207 return normalize({pt.fX, pt.fY, -fHeight});
208 }
209
210 void draw(SkCanvas* canvas) {
211 SkPaint paint;
212 paint.setAntiAlias(true);
213 canvas->drawCircle(fCenter.fX, fCenter.fY, 5, paint);
214 paint.setStyle(SkPaint::kStroke_Style);
215 canvas->drawCircle(fCenter.fX, fCenter.fY, fRadius, paint);
216 paint.setColor(SK_ColorRED);
217 canvas->drawLine(fCenter.fX, fCenter.fY, fEndPt.fX, fEndPt.fY, paint);
218 }
219};
Mike Reed69ace2a2020-01-11 15:57:14 -0500220
221class SampleRR3D : public Sample3DView {
222 SkRRect fRR;
Mike Reedb18e74d2020-01-16 13:58:22 -0500223 Light fLight = {
224 {60, 60}, {60, 60}, 50, 10
225 };
Mike Reed69ace2a2020-01-11 15:57:14 -0500226 sk_sp<SkShader> fShader;
227
228 SkString name() override { return SkString("rrect3d"); }
229
230 void onOnceBeforeDraw() override {
231 fRR = SkRRect::MakeRectXY({20, 20, 380, 380}, 50, 50);
232 fShader = GetResourceAsImage("images/mandrill_128.png")
233 ->makeShader(SkMatrix::MakeScale(3, 3));
234 }
235
236 bool onChar(SkUnichar uni) override {
237 return this->Sample3DView::onChar(uni);
238 }
239
240 void drawContent(SkCanvas* canvas, const SkMatrix44& m) {
Mike Reed75435872020-01-13 21:15:06 -0500241 SkMatrix44 trans;
242 trans.setTranslate(200, 200, 0); // center of the rotation
243
Mike Reedd4d3b332020-01-16 16:34:34 -0500244 canvas->experimental_concat44(trans * fRot * m * inv(trans));
Mike Reed69ace2a2020-01-11 15:57:14 -0500245
Mike Reedc43f2a02020-01-16 14:54:34 -0500246 if (!front(canvas->experimental_getLocalToDevice())) {
Mike Reedb18e74d2020-01-16 13:58:22 -0500247 return;
248 }
249
Mike Reed69ace2a2020-01-11 15:57:14 -0500250 SkPaint paint;
Mike Reedc43f2a02020-01-16 14:54:34 -0500251 paint.setAlphaf(front(canvas->experimental_getLocalToDevice()) ? 1 : 0.25f);
Mike Reed69ace2a2020-01-11 15:57:14 -0500252 paint.setShader(fShader);
Mike Reedb18e74d2020-01-16 13:58:22 -0500253
254 SkColorMatrix cm = comput_planar_lighting(canvas, fLight.getDir());
255 paint.setColorFilter(SkColorFilters::Matrix(cm));
256
Mike Reed69ace2a2020-01-11 15:57:14 -0500257 canvas->drawRRect(fRR, paint);
258 }
259
260 void onDrawContent(SkCanvas* canvas) override {
Mike Reedb18e74d2020-01-16 13:58:22 -0500261 canvas->save();
Mike Reed69ace2a2020-01-11 15:57:14 -0500262 canvas->translate(400, 300);
263
Mike Reedee0a03a2020-01-14 16:44:47 -0500264 this->saveCamera(canvas, {0, 0, 400, 400}, 200);
Mike Reed75435872020-01-13 21:15:06 -0500265
Mike Reed69ace2a2020-01-11 15:57:14 -0500266 for (auto f : faces) {
267 SkAutoCanvasRestore acr(canvas, true);
Mike Reed75435872020-01-13 21:15:06 -0500268 this->drawContent(canvas, f.asM44(200));
Mike Reed69ace2a2020-01-11 15:57:14 -0500269 }
Mike Reedee0a03a2020-01-14 16:44:47 -0500270
271 canvas->restore();
Mike Reedb18e74d2020-01-16 13:58:22 -0500272 canvas->restore();
273
274 fLight.draw(canvas);
275 }
276
277 Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey modi) override {
278 if (fLight.hitTest(x, y)) {
279 return new Click();
280 }
281 return nullptr;
282 }
283 bool onClick(Click* click) override {
284 fLight.update(click->fCurr.fX, click->fCurr.fY);
285 return true;
Mike Reed69ace2a2020-01-11 15:57:14 -0500286 }
287};
288DEF_SAMPLE( return new SampleRR3D(); )
Mike Reedee3216d2020-01-17 17:35:04 -0500289
290#include "include/effects/SkRuntimeEffect.h"
291
292struct LightPos {
293 SkV4 fPos;
294 SkScalar fUIRadius;
295
296 bool hitTest(SkScalar x, SkScalar y) const {
297 auto xx = x - fPos.x;
298 auto yy = y - fPos.y;
299 return xx*xx + yy*yy <= fUIRadius*fUIRadius;
300 }
301
302 void update(SkScalar x, SkScalar y) {
303 fPos.x = x;
304 fPos.y = y;
305 }
306
307 void draw(SkCanvas* canvas) {
308 SkPaint paint;
309 paint.setAntiAlias(true);
310
311 SkAutoCanvasRestore acr(canvas, true);
312 canvas->experimental_concat44(SkM44::Translate(0, 0, fPos.z));
313 canvas->drawCircle(fPos.x, fPos.y, fUIRadius, paint);
314 }
315};
316
317class SamplePointLight3D : public Sample3DView {
318 SkRRect fRR;
319 LightPos fLight = {{200, 200, 800, 1}, 8};
320
321 sk_sp<SkShader> fShader;
322 sk_sp<SkRuntimeEffect> fEffect;
323
324 SkM44 fWorldToClick,
325 fClickToWorld;
326
327 SkString name() override { return SkString("pointlight3d"); }
328
329 void onOnceBeforeDraw() override {
330 fRR = SkRRect::MakeRectXY({20, 20, 380, 380}, 50, 50);
331 fShader = GetResourceAsImage("images/mandrill_128.png")
332 ->makeShader(SkMatrix::MakeScale(3, 3));
333
334 const char code[] = R"(
335 // in fragmentProcessor texture;
336 // color = sample(texture) * half(scale);
337
338 uniform float4x4 localToWorld;
339 uniform float3 lightPos;
340
Brian Osmanea7711d2020-01-21 16:04:32 -0500341 // TODO: Remove these helpers once all intrinsics work on the raster backend
342 float3 normalize_(float3 v) {
343 return v / sqrt(dot(v, v));
344 }
345
346 float max_(float a, float b) {
347 return a > b ? a : b;
348 }
349
Mike Reedee3216d2020-01-17 17:35:04 -0500350 void main(float x, float y, inout half4 color) {
351 float3 plane_pos = (localToWorld * float4(x, y, 0, 1)).xyz;
Brian Osmanea7711d2020-01-21 16:04:32 -0500352 float3 plane_norm = normalize_((localToWorld * float4(0, 0, 1, 0)).xyz);
353 float3 light_dir = normalize_(lightPos - plane_pos);
Mike Reedee3216d2020-01-17 17:35:04 -0500354 float ambient = 0.5;
355 float dp = dot(plane_norm, light_dir);
Brian Osmanea7711d2020-01-21 16:04:32 -0500356 float scale = ambient + max_(dp, 0);
Mike Reedee3216d2020-01-17 17:35:04 -0500357
358 color = color * half4(float4(scale, scale, scale, 1));
359 }
360 )";
361 auto [effect, error] = SkRuntimeEffect::Make(SkString(code));
362 if (!effect) {
363 SkDebugf("runtime error %s\n", error.c_str());
364 }
365 fEffect = effect;
366 }
367
368 bool onChar(SkUnichar uni) override {
369 switch (uni) {
Mike Reedee3216d2020-01-17 17:35:04 -0500370 case 'Z': fLight.fPos.z += 10; return true;
371 case 'z': fLight.fPos.z -= 10; return true;
372 }
373 return this->Sample3DView::onChar(uni);
374 }
375
376 void drawContent(SkCanvas* canvas, const SkMatrix44& m, SkColor color) {
377 SkMatrix44 trans;
378 trans.setTranslate(200, 200, 0); // center of the rotation
379
380 canvas->experimental_concat44(trans * fRot * m * inv(trans));
381
382 // wonder if the runtimeeffect can do this reject? (in a setup function)
383 if (!front(canvas->experimental_getLocalToDevice())) {
384 return;
385 }
386
387 struct Uniforms {
388 SkM44 fLocalToWorld;
389 SkV3 fLightPos;
390 } uni;
391 uni.fLocalToWorld = canvas->experimental_getLocalToWorld();
392 uni.fLightPos = {fLight.fPos.x, fLight.fPos.y, fLight.fPos.z};
393 sk_sp<SkData> data = SkData::MakeWithCopy(&uni, sizeof(uni));
394
395 SkPaint paint;
396 paint.setColor(color);
397 paint.setShader(fEffect->makeShader(data, &fShader, 0, nullptr, true));
398
399 canvas->drawRRect(fRR, paint);
400 }
401
402 void setClickToWorld(SkCanvas* canvas, const SkM44& clickM) {
403 auto l2d = canvas->experimental_getLocalToDevice();
404 fWorldToClick = inv(clickM) * l2d;
405 fClickToWorld = inv(fWorldToClick);
406 }
407
408 void onDrawContent(SkCanvas* canvas) override {
409 if (canvas->getGrContext() == nullptr) {
410 return;
411 }
412 SkM44 clickM = canvas->experimental_getLocalToDevice();
413
414 canvas->save();
415 canvas->translate(400, 300);
416
417 this->saveCamera(canvas, {0, 0, 400, 400}, 200);
418
419 this->setClickToWorld(canvas, clickM);
420
421 for (auto f : faces) {
422 SkAutoCanvasRestore acr(canvas, true);
423 this->drawContent(canvas, f.asM44(200), f.fColor);
424 }
425
426 fLight.draw(canvas);
427 canvas->restore();
428 canvas->restore();
429 }
430
431 Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey modi) override {
432 auto L = fWorldToClick * fLight.fPos;
433 SkPoint c = project(fClickToWorld, {x, y, L.z/L.w, 1});
434 if (fLight.hitTest(c.fX, c.fY)) {
435 return new Click();
436 }
437 return nullptr;
438 }
439 bool onClick(Click* click) override {
440 auto L = fWorldToClick * fLight.fPos;
441 SkPoint c = project(fClickToWorld, {click->fCurr.fX, click->fCurr.fY, L.z/L.w, 1});
442 fLight.update(c.fX, c.fY);
443 return true;
444 }
445};
446DEF_SAMPLE( return new SamplePointLight3D(); )
Mike Reedf0b7edf2020-01-18 14:21:12 -0500447
448#include "include/core/SkColorPriv.h"
449#include "include/core/SkSurface.h"
450
Mike Reedf0b7edf2020-01-18 14:21:12 -0500451class SampleBump3D : public Sample3DView {
452 SkRRect fRR;
453 LightPos fLight = {{200, 200, 800, 1}, 8};
454
455 sk_sp<SkShader> fBmpShader, fImgShader;
456 sk_sp<SkRuntimeEffect> fEffect;
457
458 SkM44 fWorldToClick,
459 fClickToWorld;
460
461 SkString name() override { return SkString("bump3d"); }
462
463 void onOnceBeforeDraw() override {
464 fRR = SkRRect::MakeRectXY({20, 20, 380, 380}, 50, 50);
465 auto img = GetResourceAsImage("images/brickwork-texture.jpg");
466 fImgShader = img->makeShader(SkMatrix::MakeScale(2, 2));
Mike Reed8c2ccc02020-01-21 09:50:56 -0500467 img = GetResourceAsImage("images/brickwork_normal-map.jpg");
468 fBmpShader = img->makeShader(SkMatrix::MakeScale(2, 2));
Mike Reedf0b7edf2020-01-18 14:21:12 -0500469
470 const char code[] = R"(
471 in fragmentProcessor color_map;
Mike Reed8c2ccc02020-01-21 09:50:56 -0500472 in fragmentProcessor normal_map;
Mike Reedf0b7edf2020-01-18 14:21:12 -0500473
474 uniform float4x4 localToWorld;
Mike Reed9f22f1f2020-01-20 20:18:14 -0500475 uniform float4x4 localToWorldAdjInv;
Mike Reedf0b7edf2020-01-18 14:21:12 -0500476 uniform float3 lightPos;
477
Mike Reed8c2ccc02020-01-21 09:50:56 -0500478 float3 convert_normal_sample(half4 c) {
479 float3 n = 2 * c.rgb - 1;
480 n.y = -n.y;
481 return n;
Mike Reedf0b7edf2020-01-18 14:21:12 -0500482 }
483
484 void main(float x, float y, inout half4 color) {
Mike Reed8c2ccc02020-01-21 09:50:56 -0500485 float3 norm = convert_normal_sample(sample(normal_map));
486 float3 plane_norm = normalize(localToWorld * float4(norm, 0)).xyz;
Mike Reedf0b7edf2020-01-18 14:21:12 -0500487
488 float3 plane_pos = (localToWorld * float4(x, y, 0, 1)).xyz;
Mike Reedf0b7edf2020-01-18 14:21:12 -0500489 float3 light_dir = normalize(lightPos - plane_pos);
Mike Reed8c2ccc02020-01-21 09:50:56 -0500490
491 float ambient = 0.2;
Mike Reedf0b7edf2020-01-18 14:21:12 -0500492 float dp = dot(plane_norm, light_dir);
Mike Reed8c2ccc02020-01-21 09:50:56 -0500493 float scale = min(ambient + max(dp, 0), 1);
Mike Reedf0b7edf2020-01-18 14:21:12 -0500494
495 color = sample(color_map) * half4(float4(scale, scale, scale, 1));
496 }
497 )";
498 auto [effect, error] = SkRuntimeEffect::Make(SkString(code));
499 if (!effect) {
500 SkDebugf("runtime error %s\n", error.c_str());
501 }
502 fEffect = effect;
503 }
504
505 bool onChar(SkUnichar uni) override {
506 switch (uni) {
507 case 'Z': fLight.fPos.z += 10; return true;
508 case 'z': fLight.fPos.z -= 10; return true;
509 }
510 return this->Sample3DView::onChar(uni);
511 }
512
513 void drawContent(SkCanvas* canvas, const SkMatrix44& m, SkColor color) {
514 SkMatrix44 trans;
515 trans.setTranslate(200, 200, 0); // center of the rotation
516
517 canvas->experimental_concat44(trans * fRot * m * inv(trans));
518
519 // wonder if the runtimeeffect can do this reject? (in a setup function)
520 if (!front(canvas->experimental_getLocalToDevice())) {
521 return;
522 }
523
Mike Reed9f22f1f2020-01-20 20:18:14 -0500524 auto adj_inv = [](const SkM44& m) {
525 SkM44 inv;
526 SkAssertResult(m.invert(&inv));
527 return inv.transpose();
528 };
529
Mike Reedf0b7edf2020-01-18 14:21:12 -0500530 struct Uniforms {
531 SkM44 fLocalToWorld;
Mike Reed9f22f1f2020-01-20 20:18:14 -0500532 SkM44 fLocalToWorldAdjInv;
Mike Reedf0b7edf2020-01-18 14:21:12 -0500533 SkV3 fLightPos;
534 } uni;
535 uni.fLocalToWorld = canvas->experimental_getLocalToWorld();
Mike Reed9f22f1f2020-01-20 20:18:14 -0500536 uni.fLocalToWorldAdjInv = adj_inv(uni.fLocalToWorld);
Mike Reedf0b7edf2020-01-18 14:21:12 -0500537 uni.fLightPos = {fLight.fPos.x, fLight.fPos.y, fLight.fPos.z};
538 sk_sp<SkData> data = SkData::MakeWithCopy(&uni, sizeof(uni));
539 sk_sp<SkShader> children[] = { fImgShader, fBmpShader };
540
541 SkPaint paint;
542 paint.setColor(color);
543 paint.setShader(fEffect->makeShader(data, children, 2, nullptr, true));
544
545 canvas->drawRRect(fRR, paint);
546 }
547
548 void setClickToWorld(SkCanvas* canvas, const SkM44& clickM) {
549 auto l2d = canvas->experimental_getLocalToDevice();
550 fWorldToClick = inv(clickM) * l2d;
551 fClickToWorld = inv(fWorldToClick);
552 }
553
554 void onDrawContent(SkCanvas* canvas) override {
555 if (canvas->getGrContext() == nullptr) {
556 return;
557 }
558 SkM44 clickM = canvas->experimental_getLocalToDevice();
559
560 canvas->save();
561 canvas->translate(400, 300);
562
563 this->saveCamera(canvas, {0, 0, 400, 400}, 200);
564
565 this->setClickToWorld(canvas, clickM);
566
567 for (auto f : faces) {
568 SkAutoCanvasRestore acr(canvas, true);
569 this->drawContent(canvas, f.asM44(200), f.fColor);
570 }
571
572 fLight.draw(canvas);
573 canvas->restore();
574 canvas->restore();
575 }
576
577 Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey modi) override {
578 auto L = fWorldToClick * fLight.fPos;
579 SkPoint c = project(fClickToWorld, {x, y, L.z/L.w, 1});
580 if (fLight.hitTest(c.fX, c.fY)) {
581 return new Click();
582 }
583 return nullptr;
584 }
585 bool onClick(Click* click) override {
586 auto L = fWorldToClick * fLight.fPos;
587 SkPoint c = project(fClickToWorld, {click->fCurr.fX, click->fCurr.fY, L.z/L.w, 1});
588 fLight.update(c.fX, c.fY);
589 return true;
590 }
591};
592DEF_SAMPLE( return new SampleBump3D(); )