blob: 657ebab4455be4f325ba345f3ad931b6616d5f34 [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"
Mike Reed46f5c5f2020-02-20 15:42:29 -05009#include "include/core/SkM44.h"
Mike Reed69ace2a2020-01-11 15:57:14 -050010#include "include/core/SkPaint.h"
11#include "include/core/SkRRect.h"
Brian Osmanaa9983a2020-04-02 16:49:59 -040012#include "include/core/SkVertices.h"
Mike Reed69ace2a2020-01-11 15:57:14 -050013#include "include/utils/SkRandom.h"
14#include "samplecode/Sample.h"
15#include "tools/Resources.h"
16
Mike Reede5809952020-01-25 20:42:51 -050017struct VSphere {
Mike Reed415cace2020-02-21 12:03:49 -050018 SkV2 fCenter;
Mike Reede5809952020-01-25 20:42:51 -050019 SkScalar fRadius;
20
Mike Reed415cace2020-02-21 12:03:49 -050021 VSphere(SkV2 center, SkScalar radius) : fCenter(center), fRadius(radius) {}
Mike Reede5809952020-01-25 20:42:51 -050022
Mike Reed415cace2020-02-21 12:03:49 -050023 bool contains(SkV2 v) const {
Mike Reede5809952020-01-25 20:42:51 -050024 return (v - fCenter).length() <= fRadius;
25 }
26
Mike Reed415cace2020-02-21 12:03:49 -050027 SkV2 pinLoc(SkV2 p) const {
Mike Reed2d4a28e2020-01-25 22:39:43 -050028 auto v = p - fCenter;
29 if (v.length() > fRadius) {
30 v *= (fRadius / v.length());
31 }
32 return fCenter + v;
33 }
34
Mike Reed415cace2020-02-21 12:03:49 -050035 SkV3 computeUnitV3(SkV2 v) const {
Mike Reede5809952020-01-25 20:42:51 -050036 v = (v - fCenter) * (1 / fRadius);
37 SkScalar len2 = v.lengthSquared();
38 if (len2 > 1) {
Mike Reede1a81ba2020-02-19 17:40:32 -050039 v = v.normalize();
Mike Reede5809952020-01-25 20:42:51 -050040 len2 = 1;
41 }
42 SkScalar z = SkScalarSqrt(1 - len2);
43 return {v.x, v.y, z};
44 }
45
Mike Reed52638372020-02-19 13:30:06 -050046 struct RotateInfo {
47 SkV3 fAxis;
48 SkScalar fAngle;
49 };
50
Mike Reed415cace2020-02-21 12:03:49 -050051 RotateInfo computeRotationInfo(SkV2 a, SkV2 b) const {
Mike Reede5809952020-01-25 20:42:51 -050052 SkV3 u = this->computeUnitV3(a);
53 SkV3 v = this->computeUnitV3(b);
54 SkV3 axis = u.cross(v);
Mike Reed23823662020-02-19 17:07:04 -050055 SkScalar length = axis.length();
Mike Reede5809952020-01-25 20:42:51 -050056
Mike Reed23823662020-02-19 17:07:04 -050057 if (!SkScalarNearlyZero(length)) {
58 return {axis * (1.0f / length), acos(u.dot(v))};
Mike Reede5809952020-01-25 20:42:51 -050059 }
Mike Reed52638372020-02-19 13:30:06 -050060 return {{0, 0, 0}, 0};
61 }
62
Mike Reed415cace2020-02-21 12:03:49 -050063 SkM44 computeRotation(SkV2 a, SkV2 b) const {
Mike Reed52638372020-02-19 13:30:06 -050064 auto [axis, angle] = this->computeRotationInfo(a, b);
65 return SkM44::Rotate(axis, angle);
Mike Reede5809952020-01-25 20:42:51 -050066 }
67};
68
Mike Reedee3216d2020-01-17 17:35:04 -050069static SkM44 inv(const SkM44& m) {
70 SkM44 inverse;
71 SkAssertResult(m.invert(&inverse));
72 return inverse;
73}
74
Mike Reed69ace2a2020-01-11 15:57:14 -050075class Sample3DView : public Sample {
76protected:
77 float fNear = 0.05f;
78 float fFar = 4;
79 float fAngle = SK_ScalarPI / 12;
80
Mike Reed00a97642020-01-25 18:42:23 -050081 SkV3 fEye { 0, 0, 1.0f/tan(fAngle/2) - 1 };
82 SkV3 fCOA { 0, 0, 0 };
83 SkV3 fUp { 0, 1, 0 };
Mike Reed69ace2a2020-01-11 15:57:14 -050084
Mike Reed7fe6ee32020-04-09 12:35:09 -040085 enum {
86 kCameraID = 42,
87 };
88
Mike Reed69ace2a2020-01-11 15:57:14 -050089public:
Mike Reedee0a03a2020-01-14 16:44:47 -050090 void saveCamera(SkCanvas* canvas, const SkRect& area, SkScalar zscale) {
Mike Reed00a97642020-01-25 18:42:23 -050091 SkM44 camera = Sk3LookAt(fEye, fCOA, fUp),
92 perspective = Sk3Perspective(fNear, fFar, fAngle),
93 viewport = SkM44::Translate(area.centerX(), area.centerY(), 0) *
94 SkM44::Scale(area.width()*0.5f, area.height()*0.5f, zscale);
Mike Reed69ace2a2020-01-11 15:57:14 -050095
Mike Reedee3216d2020-01-17 17:35:04 -050096 // want "world" to be in our big coordinates (e.g. area), so apply this inverse
97 // as part of our "camera".
Mike Reed7fe6ee32020-04-09 12:35:09 -040098 canvas->save();
99 canvas->concat(viewport * perspective * camera * inv(viewport));
100 canvas->markCTM(kCameraID);
101 }
102
103 SkM44 localToWorld(SkCanvas* canvas) {
104 SkM44 camera;
105 SkAssertResult(canvas->findMarkedCTM(kCameraID, &camera));
Mike Reed73e33442020-04-09 14:03:25 -0400106 return inv(camera) * canvas->getLocalToDevice();
Mike Reed69ace2a2020-01-11 15:57:14 -0500107 }
Mike Reed69ace2a2020-01-11 15:57:14 -0500108};
109
Mike Reed69ace2a2020-01-11 15:57:14 -0500110struct Face {
111 SkScalar fRx, fRy;
Mike Reedee3216d2020-01-17 17:35:04 -0500112 SkColor fColor;
Mike Reed69ace2a2020-01-11 15:57:14 -0500113
Mike Reed00a97642020-01-25 18:42:23 -0500114 static SkM44 T(SkScalar x, SkScalar y, SkScalar z) {
115 return SkM44::Translate(x, y, z);
Mike Reed78184a32020-01-25 03:20:18 +0000116 }
117
Mike Reed00a97642020-01-25 18:42:23 -0500118 static SkM44 R(SkV3 axis, SkScalar rad) {
119 return SkM44::Rotate(axis, rad);
Mike Reed78184a32020-01-25 03:20:18 +0000120 }
121
Mike Reed00a97642020-01-25 18:42:23 -0500122 SkM44 asM44(SkScalar scale) const {
123 return R({0,1,0}, fRy) * R({1,0,0}, fRx) * T(0, 0, scale);
Mike Reed69ace2a2020-01-11 15:57:14 -0500124 }
125};
126
Mike Reed75435872020-01-13 21:15:06 -0500127static bool front(const SkM44& m) {
Florin Malitad5899162020-02-04 10:06:24 -0500128 SkM44 m2(SkM44::kUninitialized_Constructor);
129 if (!m.invert(&m2)) {
130 m2.setIdentity();
131 }
Mike Reed75435872020-01-13 21:15:06 -0500132 /*
133 * Classically we want to dot the transpose(inverse(ctm)) with our surface normal.
134 * In this case, the normal is known to be {0, 0, 1}, so we only actually need to look
135 * at the z-scale of the inverse (the transpose doesn't change the main diagonal, so
136 * no need to actually transpose).
137 */
Mike Reed07d32b42020-01-23 11:06:20 -0500138 return m2.rc(2,2) > 0;
Mike Reed69ace2a2020-01-11 15:57:14 -0500139}
140
141const Face faces[] = {
Mike Reedee3216d2020-01-17 17:35:04 -0500142 { 0, 0, SK_ColorRED }, // front
143 { 0, SK_ScalarPI, SK_ColorGREEN }, // back
Mike Reed69ace2a2020-01-11 15:57:14 -0500144
Mike Reedee3216d2020-01-17 17:35:04 -0500145 { SK_ScalarPI/2, 0, SK_ColorBLUE }, // top
146 {-SK_ScalarPI/2, 0, SK_ColorCYAN }, // bottom
Mike Reed69ace2a2020-01-11 15:57:14 -0500147
Mike Reedee3216d2020-01-17 17:35:04 -0500148 { 0, SK_ScalarPI/2, SK_ColorMAGENTA }, // left
149 { 0,-SK_ScalarPI/2, SK_ColorYELLOW }, // right
Mike Reed69ace2a2020-01-11 15:57:14 -0500150};
151
Mike Reedee3216d2020-01-17 17:35:04 -0500152#include "include/effects/SkRuntimeEffect.h"
153
Mike Reed2d4a28e2020-01-25 22:39:43 -0500154struct LightOnSphere {
Mike Reed415cace2020-02-21 12:03:49 -0500155 SkV2 fLoc;
Mike Reed2d4a28e2020-01-25 22:39:43 -0500156 SkScalar fDistance;
157 SkScalar fRadius;
158
159 SkV3 computeWorldPos(const VSphere& s) const {
160 return s.computeUnitV3(fLoc) * fDistance;
161 }
162
163 void draw(SkCanvas* canvas) const {
164 SkPaint paint;
165 paint.setAntiAlias(true);
166 paint.setColor(SK_ColorWHITE);
167 canvas->drawCircle(fLoc.x, fLoc.y, fRadius + 2, paint);
168 paint.setColor(SK_ColorBLACK);
169 canvas->drawCircle(fLoc.x, fLoc.y, fRadius, paint);
170 }
171};
172
Mike Reed52638372020-02-19 13:30:06 -0500173#include "include/core/SkTime.h"
174
175class RotateAnimator {
176 SkV3 fAxis = {0, 0, 0};
177 SkScalar fAngle = 0,
178 fPrevAngle = 1234567;
179 double fNow = 0,
180 fPrevNow = 0;
181
182 SkScalar fAngleSpeed = 0,
183 fAngleSign = 1;
184
185 static constexpr double kSlowDown = 4;
186 static constexpr SkScalar kMaxSpeed = 16;
187
188public:
189 void update(SkV3 axis, SkScalar angle) {
190 if (angle != fPrevAngle) {
191 fPrevAngle = fAngle;
192 fAngle = angle;
193
194 fPrevNow = fNow;
195 fNow = SkTime::GetSecs();
196
197 fAxis = axis;
198 }
199 }
200
201 SkM44 rotation() {
202 if (fAngleSpeed > 0) {
203 double now = SkTime::GetSecs();
204 double dtime = now - fPrevNow;
205 fPrevNow = now;
206 double delta = fAngleSign * fAngleSpeed * dtime;
207 fAngle += delta;
208 fAngleSpeed -= kSlowDown * dtime;
209 if (fAngleSpeed < 0) {
210 fAngleSpeed = 0;
211 }
212 }
213 return SkM44::Rotate(fAxis, fAngle);
214
215 }
216
217 void start() {
218 if (fPrevNow != fNow) {
219 fAngleSpeed = (fAngle - fPrevAngle) / (fNow - fPrevNow);
220 fAngleSign = fAngleSpeed < 0 ? -1 : 1;
221 fAngleSpeed = std::min(kMaxSpeed, std::abs(fAngleSpeed));
222 } else {
223 fAngleSpeed = 0;
224 }
225 fPrevNow = SkTime::GetSecs();
226 fAngle = 0;
227 }
228
229 void reset() {
230 fAngleSpeed = 0;
231 fAngle = 0;
232 fPrevAngle = 1234567;
233 }
Mike Reedfab61982020-02-21 13:25:12 -0500234
235 bool isAnimating() const { return fAngleSpeed != 0; }
Mike Reed52638372020-02-19 13:30:06 -0500236};
237
Mike Reedcfee8ee2020-02-18 13:05:45 -0500238class SampleCubeBase : public Sample3DView {
Mike Reed2d4a28e2020-01-25 22:39:43 -0500239 enum {
240 DX = 400,
241 DY = 300
242 };
243
Mike Reedf0b7edf2020-01-18 14:21:12 -0500244 SkM44 fWorldToClick,
245 fClickToWorld;
246
Mike Reed52638372020-02-19 13:30:06 -0500247 SkM44 fRotation; // part of model
248
249 RotateAnimator fRotateAnimator;
Mike Reede5809952020-01-25 20:42:51 -0500250
Mike Reedcfee8ee2020-02-18 13:05:45 -0500251protected:
252 enum Flags {
253 kCanRunOnCPU = 1 << 0,
254 kShowLightDome = 1 << 1,
255 };
256
257 LightOnSphere fLight = {{200 + DX, 200 + DY}, 800, 12};
258
259 VSphere fSphere;
260 Flags fFlags;
261
Mike Reede5809952020-01-25 20:42:51 -0500262public:
Mike Reedcfee8ee2020-02-18 13:05:45 -0500263 SampleCubeBase(Flags flags)
264 : fSphere({200 + DX, 200 + DY}, 400)
265 , fFlags(flags)
266 {}
Mike Reedf0b7edf2020-01-18 14:21:12 -0500267
268 bool onChar(SkUnichar uni) override {
269 switch (uni) {
Mike Reed2d4a28e2020-01-25 22:39:43 -0500270 case 'Z': fLight.fDistance += 10; return true;
271 case 'z': fLight.fDistance -= 10; return true;
Mike Reedf0b7edf2020-01-18 14:21:12 -0500272 }
273 return this->Sample3DView::onChar(uni);
274 }
275
Mike Reedcfee8ee2020-02-18 13:05:45 -0500276 virtual void drawContent(SkCanvas* canvas, SkColor, int index, bool drawFront) = 0;
Mike Reedf0b7edf2020-01-18 14:21:12 -0500277
278 void setClickToWorld(SkCanvas* canvas, const SkM44& clickM) {
Mike Reed46f5c5f2020-02-20 15:42:29 -0500279 auto l2d = canvas->getLocalToDevice();
Mike Reedf0b7edf2020-01-18 14:21:12 -0500280 fWorldToClick = inv(clickM) * l2d;
281 fClickToWorld = inv(fWorldToClick);
282 }
283
284 void onDrawContent(SkCanvas* canvas) override {
Mike Reedcfee8ee2020-02-18 13:05:45 -0500285 if (!canvas->getGrContext() && !(fFlags & kCanRunOnCPU)) {
Mike Reedf0b7edf2020-01-18 14:21:12 -0500286 return;
287 }
Mike Reed46f5c5f2020-02-20 15:42:29 -0500288 SkM44 clickM = canvas->getLocalToDevice();
Mike Reedf0b7edf2020-01-18 14:21:12 -0500289
290 canvas->save();
Mike Reed2d4a28e2020-01-25 22:39:43 -0500291 canvas->translate(DX, DY);
Mike Reedf0b7edf2020-01-18 14:21:12 -0500292
293 this->saveCamera(canvas, {0, 0, 400, 400}, 200);
294
295 this->setClickToWorld(canvas, clickM);
296
Mike Reedcfee8ee2020-02-18 13:05:45 -0500297 for (bool drawFront : {false, true}) {
298 int index = 0;
299 for (auto f : faces) {
300 SkAutoCanvasRestore acr(canvas, true);
301
302 SkM44 trans = SkM44::Translate(200, 200, 0); // center of the rotation
Mike Reed52638372020-02-19 13:30:06 -0500303 SkM44 m = fRotateAnimator.rotation() * fRotation * f.asM44(200);
Mike Reedcfee8ee2020-02-18 13:05:45 -0500304
Mike Reed3ef77dd2020-04-06 10:41:09 -0400305 canvas->concat(trans * m * inv(trans));
Mike Reedcfee8ee2020-02-18 13:05:45 -0500306 this->drawContent(canvas, f.fColor, index++, drawFront);
307 }
Mike Reedf0b7edf2020-01-18 14:21:12 -0500308 }
309
Mike Reede5809952020-01-25 20:42:51 -0500310 canvas->restore(); // camera
311 canvas->restore(); // center the content in the window
312
Mike Reedcfee8ee2020-02-18 13:05:45 -0500313 if (fFlags & kShowLightDome){
314 fLight.draw(canvas);
315
Mike Reede5809952020-01-25 20:42:51 -0500316 SkPaint paint;
Mike Reed2d4a28e2020-01-25 22:39:43 -0500317 paint.setAntiAlias(true);
Mike Reede5809952020-01-25 20:42:51 -0500318 paint.setStyle(SkPaint::kStroke_Style);
Mike Reed2d4a28e2020-01-25 22:39:43 -0500319 paint.setColor(0x40FF0000);
Mike Reede5809952020-01-25 20:42:51 -0500320 canvas->drawCircle(fSphere.fCenter.x, fSphere.fCenter.y, fSphere.fRadius, paint);
321 canvas->drawLine(fSphere.fCenter.x, fSphere.fCenter.y - fSphere.fRadius,
322 fSphere.fCenter.x, fSphere.fCenter.y + fSphere.fRadius, paint);
323 canvas->drawLine(fSphere.fCenter.x - fSphere.fRadius, fSphere.fCenter.y,
324 fSphere.fCenter.x + fSphere.fRadius, fSphere.fCenter.y, paint);
325 }
Mike Reedf0b7edf2020-01-18 14:21:12 -0500326 }
327
328 Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey modi) override {
Mike Reed415cace2020-02-21 12:03:49 -0500329 SkV2 p = fLight.fLoc - SkV2{x, y};
Mike Reed2d4a28e2020-01-25 22:39:43 -0500330 if (p.length() <= fLight.fRadius) {
Mike Reede5809952020-01-25 20:42:51 -0500331 Click* c = new Click();
332 c->fMeta.setS32("type", 0);
333 return c;
334 }
335 if (fSphere.contains({x, y})) {
336 Click* c = new Click();
337 c->fMeta.setS32("type", 1);
Mike Reed52638372020-02-19 13:30:06 -0500338
339 fRotation = fRotateAnimator.rotation() * fRotation;
340 fRotateAnimator.reset();
Mike Reede5809952020-01-25 20:42:51 -0500341 return c;
Mike Reedf0b7edf2020-01-18 14:21:12 -0500342 }
343 return nullptr;
344 }
345 bool onClick(Click* click) override {
Mike Reed2d4a28e2020-01-25 22:39:43 -0500346#if 0
347 auto L = fWorldToClick * fLight.fPos;
348 SkPoint c = project(fClickToWorld, {click->fCurr.fX, click->fCurr.fY, L.z/L.w, 1});
349 fLight.update(c.fX, c.fY);
350#endif
Mike Reede5809952020-01-25 20:42:51 -0500351 if (click->fMeta.hasS32("type", 0)) {
Mike Reed2d4a28e2020-01-25 22:39:43 -0500352 fLight.fLoc = fSphere.pinLoc({click->fCurr.fX, click->fCurr.fY});
Mike Reede5809952020-01-25 20:42:51 -0500353 return true;
354 }
355 if (click->fMeta.hasS32("type", 1)) {
356 if (click->fState == skui::InputState::kUp) {
Mike Reed52638372020-02-19 13:30:06 -0500357 fRotation = fRotateAnimator.rotation() * fRotation;
358 fRotateAnimator.start();
Mike Reede5809952020-01-25 20:42:51 -0500359 } else {
Mike Reed52638372020-02-19 13:30:06 -0500360 auto [axis, angle] = fSphere.computeRotationInfo(
361 {click->fOrig.fX, click->fOrig.fY},
362 {click->fCurr.fX, click->fCurr.fY});
363 fRotateAnimator.update(axis, angle);
Mike Reede5809952020-01-25 20:42:51 -0500364 }
365 return true;
366 }
Mike Reedf0b7edf2020-01-18 14:21:12 -0500367 return true;
368 }
Mike Reed52638372020-02-19 13:30:06 -0500369
370 bool onAnimate(double nanos) override {
Mike Reedfab61982020-02-21 13:25:12 -0500371 return fRotateAnimator.isAnimating();
Mike Reed52638372020-02-19 13:30:06 -0500372 }
373
374private:
375 typedef Sample3DView INHERITED;
Mike Reedf0b7edf2020-01-18 14:21:12 -0500376};
Mike Reedcfee8ee2020-02-18 13:05:45 -0500377
378class SampleBump3D : public SampleCubeBase {
379 sk_sp<SkShader> fBmpShader, fImgShader;
380 sk_sp<SkRuntimeEffect> fEffect;
381 SkRRect fRR;
382
383public:
384 SampleBump3D() : SampleCubeBase(kShowLightDome) {}
385
386 SkString name() override { return SkString("bump3d"); }
387
388 void onOnceBeforeDraw() override {
389 fRR = SkRRect::MakeRectXY({20, 20, 380, 380}, 50, 50);
390 auto img = GetResourceAsImage("images/brickwork-texture.jpg");
391 fImgShader = img->makeShader(SkMatrix::MakeScale(2, 2));
392 img = GetResourceAsImage("images/brickwork_normal-map.jpg");
393 fBmpShader = img->makeShader(SkMatrix::MakeScale(2, 2));
394
395 const char code[] = R"(
396 in fragmentProcessor color_map;
397 in fragmentProcessor normal_map;
398
399 uniform float4x4 localToWorld;
400 uniform float4x4 localToWorldAdjInv;
401 uniform float3 lightPos;
402
403 float3 convert_normal_sample(half4 c) {
404 float3 n = 2 * c.rgb - 1;
405 n.y = -n.y;
406 return n;
407 }
408
409 void main(float2 p, inout half4 color) {
410 float3 norm = convert_normal_sample(sample(normal_map, p));
Brian Osman613deb02020-04-02 15:55:37 -0400411 float3 plane_norm = normalize(localToWorldAdjInv * float4(norm, 0)).xyz;
Mike Reedcfee8ee2020-02-18 13:05:45 -0500412
413 float3 plane_pos = (localToWorld * float4(p, 0, 1)).xyz;
414 float3 light_dir = normalize(lightPos - plane_pos);
415
416 float ambient = 0.2;
417 float dp = dot(plane_norm, light_dir);
418 float scale = min(ambient + max(dp, 0), 1);
419
420 color = sample(color_map, p) * half4(float4(scale, scale, scale, 1));
421 }
422 )";
423 auto [effect, error] = SkRuntimeEffect::Make(SkString(code));
424 if (!effect) {
425 SkDebugf("runtime error %s\n", error.c_str());
426 }
427 fEffect = effect;
428 }
429
430 void drawContent(SkCanvas* canvas, SkColor color, int index, bool drawFront) override {
Mike Reed46f5c5f2020-02-20 15:42:29 -0500431 if (!drawFront || !front(canvas->getLocalToDevice())) {
Mike Reedcfee8ee2020-02-18 13:05:45 -0500432 return;
433 }
434
435 auto adj_inv = [](const SkM44& m) {
Brian Osman613deb02020-04-02 15:55:37 -0400436 // Normals need to be transformed by the inverse-transpose of the upper-left 3x3 portion
437 // (scale + rotate) of the local to world matrix. (If the local to world only has
438 // uniform scale, we can use its upper-left 3x3 directly, but we don't know if that's
439 // the case here, so go the extra mile.)
440 SkM44 rot_scale(m.rc(0, 0), m.rc(0, 1), m.rc(0, 2), 0,
441 m.rc(1, 0), m.rc(1, 1), m.rc(1, 2), 0,
442 m.rc(2, 0), m.rc(2, 1), m.rc(2, 2), 0,
443 0, 0, 0, 1);
444 SkM44 inv(SkM44::kUninitialized_Constructor);
445 SkAssertResult(rot_scale.invert(&inv));
Mike Reedcfee8ee2020-02-18 13:05:45 -0500446 return inv.transpose();
447 };
448
449 struct Uniforms {
450 SkM44 fLocalToWorld;
451 SkM44 fLocalToWorldAdjInv;
452 SkV3 fLightPos;
453 } uni;
Mike Reed7fe6ee32020-04-09 12:35:09 -0400454 uni.fLocalToWorld = this->localToWorld(canvas);
Mike Reedcfee8ee2020-02-18 13:05:45 -0500455 uni.fLocalToWorldAdjInv = adj_inv(uni.fLocalToWorld);
456 uni.fLightPos = fLight.computeWorldPos(fSphere);
457
458 sk_sp<SkData> data = SkData::MakeWithCopy(&uni, sizeof(uni));
459 sk_sp<SkShader> children[] = { fImgShader, fBmpShader };
460
461 SkPaint paint;
462 paint.setColor(color);
463 paint.setShader(fEffect->makeShader(data, children, 2, nullptr, true));
464
465 canvas->drawRRect(fRR, paint);
466 }
467};
Mike Reede5809952020-01-25 20:42:51 -0500468DEF_SAMPLE( return new SampleBump3D; )
Mike Reedcfee8ee2020-02-18 13:05:45 -0500469
Brian Osmanaa9983a2020-04-02 16:49:59 -0400470class SampleVerts3D : public SampleCubeBase {
471 sk_sp<SkRuntimeEffect> fEffect;
472 sk_sp<SkVertices> fVertices;
473
474public:
475 SampleVerts3D() : SampleCubeBase(kShowLightDome) {}
476
477 SkString name() override { return SkString("verts3d"); }
478
479 void onOnceBeforeDraw() override {
480 using Attr = SkVertices::Attribute;
481 Attr attrs[] = {
482 Attr(Attr::Type::kFloat3, Attr::Usage::kNormalVector),
483 };
484
485 SkVertices::Builder builder(SkVertices::kTriangleFan_VertexMode, 66, 0, attrs, 1);
486
487 SkPoint* pos = builder.positions();
488 SkV3* nrm = (SkV3*)builder.customData();
489
490 SkPoint center = { 200, 200 };
491 SkScalar radius = 200;
492
493 pos[0] = center;
494 nrm[0] = { 0, 0, 1 };
495
496 for (int i = 0; i < 65; ++i) {
497 SkScalar t = (i / 64.0f) * 2 * SK_ScalarPI;
498 SkScalar s = SkScalarSin(t),
499 c = SkScalarCos(t);
500 pos[i + 1] = center + SkPoint { c * radius, s * radius };
501 nrm[i + 1] = { c, s, 0 };
502 }
503
504 fVertices = builder.detach();
505
506 const char code[] = R"(
507 varying float3 vtx_normal;
508 uniform float4x4 localToWorld;
509 uniform float3 lightPos;
510
511 void main(float2 p, inout half4 color) {
512 float3 norm = normalize(vtx_normal);
513 float3 plane_norm = normalize(localToWorld * float4(norm, 0)).xyz;
514
515 float3 plane_pos = (localToWorld * float4(p, 0, 1)).xyz;
516 float3 light_dir = normalize(lightPos - plane_pos);
517
518 float ambient = 0.2;
519 float dp = dot(plane_norm, light_dir);
520 float scale = min(ambient + max(dp, 0), 1);
521
522 color = half4(0.7, 0.9, 0.3, 1) * half4(float4(scale, scale, scale, 1));
523 }
524 )";
525 auto [effect, error] = SkRuntimeEffect::Make(SkString(code));
526 if (!effect) {
527 SkDebugf("runtime error %s\n", error.c_str());
528 }
529 fEffect = effect;
530 }
531
532 void drawContent(SkCanvas* canvas, SkColor color, int index, bool drawFront) override {
533 if (!drawFront || !front(canvas->getLocalToDevice())) {
534 return;
535 }
536
537 struct Uniforms {
538 SkM44 fLocalToWorld;
539 SkV3 fLightPos;
540 } uni;
Mike Reed7fe6ee32020-04-09 12:35:09 -0400541 uni.fLocalToWorld = this->localToWorld(canvas);
Brian Osmanaa9983a2020-04-02 16:49:59 -0400542 uni.fLightPos = fLight.computeWorldPos(fSphere);
543
544 sk_sp<SkData> data = SkData::MakeWithCopy(&uni, sizeof(uni));
545
546 SkPaint paint;
547 paint.setColor(color);
548 paint.setShader(fEffect->makeShader(data, nullptr, 0, nullptr, true));
549
550 canvas->drawVertices(fVertices, paint);
551 }
552};
553DEF_SAMPLE( return new SampleVerts3D; )
554
Mike Reedcfee8ee2020-02-18 13:05:45 -0500555#include "modules/skottie/include/Skottie.h"
556
557class SampleSkottieCube : public SampleCubeBase {
558 sk_sp<skottie::Animation> fAnim[6];
559
560public:
561 SampleSkottieCube() : SampleCubeBase(kCanRunOnCPU) {}
562
563 SkString name() override { return SkString("skottie3d"); }
564
565 void onOnceBeforeDraw() override {
566 const char* files[] = {
567 "skottie/skottie-chained-mattes.json",
568 "skottie/skottie-gradient-ramp.json",
569 "skottie/skottie_sample_2.json",
570 "skottie/skottie-3d-3planes.json",
571 "skottie/skottie-text-animator-4.json",
572 "skottie/skottie-motiontile-effect-phase.json",
573
574 };
575 for (unsigned i = 0; i < SK_ARRAY_COUNT(files); ++i) {
576 if (auto stream = GetResourceAsStream(files[i])) {
577 fAnim[i] = skottie::Animation::Make(stream.get());
578 }
579 }
580 }
581
582 void drawContent(SkCanvas* canvas, SkColor color, int index, bool drawFront) override {
Mike Reed46f5c5f2020-02-20 15:42:29 -0500583 if (!drawFront || !front(canvas->getLocalToDevice())) {
Mike Reedcfee8ee2020-02-18 13:05:45 -0500584 return;
585 }
586
587 SkPaint paint;
588 paint.setColor(color);
589 SkRect r = {0, 0, 400, 400};
590 canvas->drawRect(r, paint);
591 fAnim[index]->render(canvas, &r);
592 }
593
594 bool onAnimate(double nanos) override {
595 for (auto& anim : fAnim) {
596 SkScalar dur = anim->duration();
597 SkScalar t = fmod(1e-9 * nanos, dur) / dur;
598 anim->seek(t);
599 }
600 return true;
601 }
602};
603DEF_SAMPLE( return new SampleSkottieCube; )