blob: 3895c2052544af927dcef82e4bbe814176585d35 [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
Brian Osman548de742020-04-24 12:02:25 -040085 const char* kLocalToWorld = "local_to_world";
Mike Reed7fe6ee32020-04-09 12:35:09 -040086
Mike Reed69ace2a2020-01-11 15:57:14 -050087public:
Brian Osman19854662020-04-21 14:47:07 -040088 void concatCamera(SkCanvas* canvas, const SkRect& area, SkScalar zscale) {
Mike Reed00a97642020-01-25 18:42:23 -050089 SkM44 camera = Sk3LookAt(fEye, fCOA, fUp),
90 perspective = Sk3Perspective(fNear, fFar, fAngle),
91 viewport = SkM44::Translate(area.centerX(), area.centerY(), 0) *
92 SkM44::Scale(area.width()*0.5f, area.height()*0.5f, zscale);
Mike Reed69ace2a2020-01-11 15:57:14 -050093
Mike Reed7fe6ee32020-04-09 12:35:09 -040094 canvas->concat(viewport * perspective * camera * inv(viewport));
Mike Reed7fe6ee32020-04-09 12:35:09 -040095 }
Mike Reed69ace2a2020-01-11 15:57:14 -050096};
97
Mike Reed69ace2a2020-01-11 15:57:14 -050098struct Face {
99 SkScalar fRx, fRy;
Mike Reedee3216d2020-01-17 17:35:04 -0500100 SkColor fColor;
Mike Reed69ace2a2020-01-11 15:57:14 -0500101
Mike Reed00a97642020-01-25 18:42:23 -0500102 static SkM44 T(SkScalar x, SkScalar y, SkScalar z) {
103 return SkM44::Translate(x, y, z);
Mike Reed78184a32020-01-25 03:20:18 +0000104 }
105
Mike Reed00a97642020-01-25 18:42:23 -0500106 static SkM44 R(SkV3 axis, SkScalar rad) {
107 return SkM44::Rotate(axis, rad);
Mike Reed78184a32020-01-25 03:20:18 +0000108 }
109
Mike Reed00a97642020-01-25 18:42:23 -0500110 SkM44 asM44(SkScalar scale) const {
111 return R({0,1,0}, fRy) * R({1,0,0}, fRx) * T(0, 0, scale);
Mike Reed69ace2a2020-01-11 15:57:14 -0500112 }
113};
114
Mike Reed75435872020-01-13 21:15:06 -0500115static bool front(const SkM44& m) {
Florin Malitad5899162020-02-04 10:06:24 -0500116 SkM44 m2(SkM44::kUninitialized_Constructor);
117 if (!m.invert(&m2)) {
118 m2.setIdentity();
119 }
Mike Reed75435872020-01-13 21:15:06 -0500120 /*
121 * Classically we want to dot the transpose(inverse(ctm)) with our surface normal.
122 * In this case, the normal is known to be {0, 0, 1}, so we only actually need to look
123 * at the z-scale of the inverse (the transpose doesn't change the main diagonal, so
124 * no need to actually transpose).
125 */
Mike Reed07d32b42020-01-23 11:06:20 -0500126 return m2.rc(2,2) > 0;
Mike Reed69ace2a2020-01-11 15:57:14 -0500127}
128
129const Face faces[] = {
Mike Reedee3216d2020-01-17 17:35:04 -0500130 { 0, 0, SK_ColorRED }, // front
131 { 0, SK_ScalarPI, SK_ColorGREEN }, // back
Mike Reed69ace2a2020-01-11 15:57:14 -0500132
Mike Reedee3216d2020-01-17 17:35:04 -0500133 { SK_ScalarPI/2, 0, SK_ColorBLUE }, // top
134 {-SK_ScalarPI/2, 0, SK_ColorCYAN }, // bottom
Mike Reed69ace2a2020-01-11 15:57:14 -0500135
Mike Reedee3216d2020-01-17 17:35:04 -0500136 { 0, SK_ScalarPI/2, SK_ColorMAGENTA }, // left
137 { 0,-SK_ScalarPI/2, SK_ColorYELLOW }, // right
Mike Reed69ace2a2020-01-11 15:57:14 -0500138};
139
Mike Reedee3216d2020-01-17 17:35:04 -0500140#include "include/effects/SkRuntimeEffect.h"
141
Mike Reed2d4a28e2020-01-25 22:39:43 -0500142struct LightOnSphere {
Mike Reed415cace2020-02-21 12:03:49 -0500143 SkV2 fLoc;
Mike Reed2d4a28e2020-01-25 22:39:43 -0500144 SkScalar fDistance;
145 SkScalar fRadius;
146
147 SkV3 computeWorldPos(const VSphere& s) const {
148 return s.computeUnitV3(fLoc) * fDistance;
149 }
150
151 void draw(SkCanvas* canvas) const {
152 SkPaint paint;
153 paint.setAntiAlias(true);
154 paint.setColor(SK_ColorWHITE);
155 canvas->drawCircle(fLoc.x, fLoc.y, fRadius + 2, paint);
156 paint.setColor(SK_ColorBLACK);
157 canvas->drawCircle(fLoc.x, fLoc.y, fRadius, paint);
158 }
159};
160
Mike Reed52638372020-02-19 13:30:06 -0500161#include "include/core/SkTime.h"
162
163class RotateAnimator {
164 SkV3 fAxis = {0, 0, 0};
165 SkScalar fAngle = 0,
166 fPrevAngle = 1234567;
167 double fNow = 0,
168 fPrevNow = 0;
169
170 SkScalar fAngleSpeed = 0,
171 fAngleSign = 1;
172
173 static constexpr double kSlowDown = 4;
174 static constexpr SkScalar kMaxSpeed = 16;
175
176public:
177 void update(SkV3 axis, SkScalar angle) {
178 if (angle != fPrevAngle) {
179 fPrevAngle = fAngle;
180 fAngle = angle;
181
182 fPrevNow = fNow;
183 fNow = SkTime::GetSecs();
184
185 fAxis = axis;
186 }
187 }
188
189 SkM44 rotation() {
190 if (fAngleSpeed > 0) {
191 double now = SkTime::GetSecs();
192 double dtime = now - fPrevNow;
193 fPrevNow = now;
194 double delta = fAngleSign * fAngleSpeed * dtime;
195 fAngle += delta;
196 fAngleSpeed -= kSlowDown * dtime;
197 if (fAngleSpeed < 0) {
198 fAngleSpeed = 0;
199 }
200 }
201 return SkM44::Rotate(fAxis, fAngle);
202
203 }
204
205 void start() {
206 if (fPrevNow != fNow) {
207 fAngleSpeed = (fAngle - fPrevAngle) / (fNow - fPrevNow);
208 fAngleSign = fAngleSpeed < 0 ? -1 : 1;
209 fAngleSpeed = std::min(kMaxSpeed, std::abs(fAngleSpeed));
210 } else {
211 fAngleSpeed = 0;
212 }
213 fPrevNow = SkTime::GetSecs();
214 fAngle = 0;
215 }
216
217 void reset() {
218 fAngleSpeed = 0;
219 fAngle = 0;
220 fPrevAngle = 1234567;
221 }
Mike Reedfab61982020-02-21 13:25:12 -0500222
223 bool isAnimating() const { return fAngleSpeed != 0; }
Mike Reed52638372020-02-19 13:30:06 -0500224};
225
Mike Reedcfee8ee2020-02-18 13:05:45 -0500226class SampleCubeBase : public Sample3DView {
Mike Reed2d4a28e2020-01-25 22:39:43 -0500227 enum {
228 DX = 400,
229 DY = 300
230 };
231
Mike Reed52638372020-02-19 13:30:06 -0500232 SkM44 fRotation; // part of model
233
234 RotateAnimator fRotateAnimator;
Mike Reede5809952020-01-25 20:42:51 -0500235
Mike Reedcfee8ee2020-02-18 13:05:45 -0500236protected:
237 enum Flags {
238 kCanRunOnCPU = 1 << 0,
239 kShowLightDome = 1 << 1,
240 };
241
242 LightOnSphere fLight = {{200 + DX, 200 + DY}, 800, 12};
243
244 VSphere fSphere;
245 Flags fFlags;
246
Mike Reede5809952020-01-25 20:42:51 -0500247public:
Mike Reedcfee8ee2020-02-18 13:05:45 -0500248 SampleCubeBase(Flags flags)
249 : fSphere({200 + DX, 200 + DY}, 400)
250 , fFlags(flags)
251 {}
Mike Reedf0b7edf2020-01-18 14:21:12 -0500252
253 bool onChar(SkUnichar uni) override {
254 switch (uni) {
Mike Reed2d4a28e2020-01-25 22:39:43 -0500255 case 'Z': fLight.fDistance += 10; return true;
256 case 'z': fLight.fDistance -= 10; return true;
Mike Reedf0b7edf2020-01-18 14:21:12 -0500257 }
258 return this->Sample3DView::onChar(uni);
259 }
260
Mike Reedcfee8ee2020-02-18 13:05:45 -0500261 virtual void drawContent(SkCanvas* canvas, SkColor, int index, bool drawFront) = 0;
Mike Reedf0b7edf2020-01-18 14:21:12 -0500262
Mike Reedf0b7edf2020-01-18 14:21:12 -0500263 void onDrawContent(SkCanvas* canvas) override {
Robert Phillips30ebcf72020-07-09 13:25:17 -0400264 if (!canvas->recordingContext() && !(fFlags & kCanRunOnCPU)) {
Mike Reedf0b7edf2020-01-18 14:21:12 -0500265 return;
266 }
Mike Reedf0b7edf2020-01-18 14:21:12 -0500267
268 canvas->save();
Mike Reed2d4a28e2020-01-25 22:39:43 -0500269 canvas->translate(DX, DY);
Mike Reedf0b7edf2020-01-18 14:21:12 -0500270
Brian Osman19854662020-04-21 14:47:07 -0400271 this->concatCamera(canvas, {0, 0, 400, 400}, 200);
Mike Reedf0b7edf2020-01-18 14:21:12 -0500272
Mike Reedcfee8ee2020-02-18 13:05:45 -0500273 for (bool drawFront : {false, true}) {
274 int index = 0;
275 for (auto f : faces) {
276 SkAutoCanvasRestore acr(canvas, true);
277
278 SkM44 trans = SkM44::Translate(200, 200, 0); // center of the rotation
Mike Reed52638372020-02-19 13:30:06 -0500279 SkM44 m = fRotateAnimator.rotation() * fRotation * f.asM44(200);
Mike Reedcfee8ee2020-02-18 13:05:45 -0500280
Brian Osman19854662020-04-21 14:47:07 -0400281 canvas->concat(trans);
282
283 // "World" space - content is centered at the origin, in device scale (+-200)
Brian Osman548de742020-04-24 12:02:25 -0400284 canvas->markCTM(kLocalToWorld);
Brian Osman19854662020-04-21 14:47:07 -0400285
286 canvas->concat(m * inv(trans));
Mike Reedcfee8ee2020-02-18 13:05:45 -0500287 this->drawContent(canvas, f.fColor, index++, drawFront);
288 }
Mike Reedf0b7edf2020-01-18 14:21:12 -0500289 }
290
Brian Osman19854662020-04-21 14:47:07 -0400291 canvas->restore(); // camera & center the content in the window
Mike Reede5809952020-01-25 20:42:51 -0500292
Mike Reedcfee8ee2020-02-18 13:05:45 -0500293 if (fFlags & kShowLightDome){
294 fLight.draw(canvas);
295
Mike Reede5809952020-01-25 20:42:51 -0500296 SkPaint paint;
Mike Reed2d4a28e2020-01-25 22:39:43 -0500297 paint.setAntiAlias(true);
Mike Reede5809952020-01-25 20:42:51 -0500298 paint.setStyle(SkPaint::kStroke_Style);
Mike Reed2d4a28e2020-01-25 22:39:43 -0500299 paint.setColor(0x40FF0000);
Mike Reede5809952020-01-25 20:42:51 -0500300 canvas->drawCircle(fSphere.fCenter.x, fSphere.fCenter.y, fSphere.fRadius, paint);
301 canvas->drawLine(fSphere.fCenter.x, fSphere.fCenter.y - fSphere.fRadius,
302 fSphere.fCenter.x, fSphere.fCenter.y + fSphere.fRadius, paint);
303 canvas->drawLine(fSphere.fCenter.x - fSphere.fRadius, fSphere.fCenter.y,
304 fSphere.fCenter.x + fSphere.fRadius, fSphere.fCenter.y, paint);
305 }
Mike Reedf0b7edf2020-01-18 14:21:12 -0500306 }
307
308 Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey modi) override {
Mike Reed415cace2020-02-21 12:03:49 -0500309 SkV2 p = fLight.fLoc - SkV2{x, y};
Mike Reed2d4a28e2020-01-25 22:39:43 -0500310 if (p.length() <= fLight.fRadius) {
Mike Reede5809952020-01-25 20:42:51 -0500311 Click* c = new Click();
312 c->fMeta.setS32("type", 0);
313 return c;
314 }
315 if (fSphere.contains({x, y})) {
316 Click* c = new Click();
317 c->fMeta.setS32("type", 1);
Mike Reed52638372020-02-19 13:30:06 -0500318
319 fRotation = fRotateAnimator.rotation() * fRotation;
320 fRotateAnimator.reset();
Mike Reede5809952020-01-25 20:42:51 -0500321 return c;
Mike Reedf0b7edf2020-01-18 14:21:12 -0500322 }
323 return nullptr;
324 }
325 bool onClick(Click* click) override {
Mike Reede5809952020-01-25 20:42:51 -0500326 if (click->fMeta.hasS32("type", 0)) {
Mike Reed2d4a28e2020-01-25 22:39:43 -0500327 fLight.fLoc = fSphere.pinLoc({click->fCurr.fX, click->fCurr.fY});
Mike Reede5809952020-01-25 20:42:51 -0500328 return true;
329 }
330 if (click->fMeta.hasS32("type", 1)) {
331 if (click->fState == skui::InputState::kUp) {
Mike Reed52638372020-02-19 13:30:06 -0500332 fRotation = fRotateAnimator.rotation() * fRotation;
333 fRotateAnimator.start();
Mike Reede5809952020-01-25 20:42:51 -0500334 } else {
Mike Reed52638372020-02-19 13:30:06 -0500335 auto [axis, angle] = fSphere.computeRotationInfo(
336 {click->fOrig.fX, click->fOrig.fY},
337 {click->fCurr.fX, click->fCurr.fY});
338 fRotateAnimator.update(axis, angle);
Mike Reede5809952020-01-25 20:42:51 -0500339 }
340 return true;
341 }
Mike Reedf0b7edf2020-01-18 14:21:12 -0500342 return true;
343 }
Mike Reed52638372020-02-19 13:30:06 -0500344
345 bool onAnimate(double nanos) override {
Mike Reedfab61982020-02-21 13:25:12 -0500346 return fRotateAnimator.isAnimating();
Mike Reed52638372020-02-19 13:30:06 -0500347 }
348
349private:
350 typedef Sample3DView INHERITED;
Mike Reedf0b7edf2020-01-18 14:21:12 -0500351};
Mike Reedcfee8ee2020-02-18 13:05:45 -0500352
353class SampleBump3D : public SampleCubeBase {
354 sk_sp<SkShader> fBmpShader, fImgShader;
355 sk_sp<SkRuntimeEffect> fEffect;
356 SkRRect fRR;
357
358public:
Brian Osmanad43e542020-06-19 15:02:35 -0400359 SampleBump3D() : SampleCubeBase(Flags(kCanRunOnCPU | kShowLightDome)) {}
Mike Reedcfee8ee2020-02-18 13:05:45 -0500360
361 SkString name() override { return SkString("bump3d"); }
362
363 void onOnceBeforeDraw() override {
364 fRR = SkRRect::MakeRectXY({20, 20, 380, 380}, 50, 50);
365 auto img = GetResourceAsImage("images/brickwork-texture.jpg");
Mike Reed1f607332020-05-21 12:11:27 -0400366 fImgShader = img->makeShader(SkMatrix::Scale(2, 2));
Mike Reedcfee8ee2020-02-18 13:05:45 -0500367 img = GetResourceAsImage("images/brickwork_normal-map.jpg");
Mike Reed1f607332020-05-21 12:11:27 -0400368 fBmpShader = img->makeShader(SkMatrix::Scale(2, 2));
Mike Reedcfee8ee2020-02-18 13:05:45 -0500369
370 const char code[] = R"(
Brian Osman28590d52020-03-23 16:59:08 -0400371 in shader color_map;
372 in shader normal_map;
Mike Reedcfee8ee2020-02-18 13:05:45 -0500373
Brian Osmanf59a9612020-04-15 14:18:13 -0400374 layout (marker=local_to_world) uniform float4x4 localToWorld;
375 layout (marker=normals(local_to_world)) uniform float4x4 localToWorldAdjInv;
Mike Reedcfee8ee2020-02-18 13:05:45 -0500376 uniform float3 lightPos;
377
378 float3 convert_normal_sample(half4 c) {
379 float3 n = 2 * c.rgb - 1;
380 n.y = -n.y;
381 return n;
382 }
383
384 void main(float2 p, inout half4 color) {
385 float3 norm = convert_normal_sample(sample(normal_map, p));
Brian Osman613deb02020-04-02 15:55:37 -0400386 float3 plane_norm = normalize(localToWorldAdjInv * float4(norm, 0)).xyz;
Mike Reedcfee8ee2020-02-18 13:05:45 -0500387
388 float3 plane_pos = (localToWorld * float4(p, 0, 1)).xyz;
389 float3 light_dir = normalize(lightPos - plane_pos);
390
391 float ambient = 0.2;
392 float dp = dot(plane_norm, light_dir);
393 float scale = min(ambient + max(dp, 0), 1);
394
395 color = sample(color_map, p) * half4(float4(scale, scale, scale, 1));
396 }
397 )";
398 auto [effect, error] = SkRuntimeEffect::Make(SkString(code));
399 if (!effect) {
400 SkDebugf("runtime error %s\n", error.c_str());
401 }
402 fEffect = effect;
403 }
404
405 void drawContent(SkCanvas* canvas, SkColor color, int index, bool drawFront) override {
Mike Reed46f5c5f2020-02-20 15:42:29 -0500406 if (!drawFront || !front(canvas->getLocalToDevice())) {
Mike Reedcfee8ee2020-02-18 13:05:45 -0500407 return;
408 }
409
Brian Osmand9bde072020-04-15 14:18:13 -0400410 SkRuntimeShaderBuilder builder(fEffect);
411 builder.input("lightPos") = fLight.computeWorldPos(fSphere);
412 // localToWorld matrices are automatically populated, via layout(marker)
Mike Reedcfee8ee2020-02-18 13:05:45 -0500413
Brian Osmand9bde072020-04-15 14:18:13 -0400414 builder.child("color_map") = fImgShader;
415 builder.child("normal_map") = fBmpShader;
Mike Reedcfee8ee2020-02-18 13:05:45 -0500416
417 SkPaint paint;
418 paint.setColor(color);
Brian Osmand9bde072020-04-15 14:18:13 -0400419 paint.setShader(builder.makeShader(nullptr, true));
Mike Reedcfee8ee2020-02-18 13:05:45 -0500420
421 canvas->drawRRect(fRR, paint);
422 }
423};
Mike Reede5809952020-01-25 20:42:51 -0500424DEF_SAMPLE( return new SampleBump3D; )
Mike Reedcfee8ee2020-02-18 13:05:45 -0500425
Brian Osmanaa9983a2020-04-02 16:49:59 -0400426class SampleVerts3D : public SampleCubeBase {
427 sk_sp<SkRuntimeEffect> fEffect;
428 sk_sp<SkVertices> fVertices;
429
430public:
431 SampleVerts3D() : SampleCubeBase(kShowLightDome) {}
432
433 SkString name() override { return SkString("verts3d"); }
434
435 void onOnceBeforeDraw() override {
436 using Attr = SkVertices::Attribute;
437 Attr attrs[] = {
438 Attr(Attr::Type::kFloat3, Attr::Usage::kNormalVector),
439 };
440
441 SkVertices::Builder builder(SkVertices::kTriangleFan_VertexMode, 66, 0, attrs, 1);
442
443 SkPoint* pos = builder.positions();
444 SkV3* nrm = (SkV3*)builder.customData();
445
446 SkPoint center = { 200, 200 };
447 SkScalar radius = 200;
448
449 pos[0] = center;
450 nrm[0] = { 0, 0, 1 };
451
452 for (int i = 0; i < 65; ++i) {
453 SkScalar t = (i / 64.0f) * 2 * SK_ScalarPI;
454 SkScalar s = SkScalarSin(t),
455 c = SkScalarCos(t);
456 pos[i + 1] = center + SkPoint { c * radius, s * radius };
457 nrm[i + 1] = { c, s, 0 };
458 }
459
460 fVertices = builder.detach();
461
462 const char code[] = R"(
463 varying float3 vtx_normal;
Brian Osmand9bde072020-04-15 14:18:13 -0400464
465 layout (marker=local_to_world) uniform float4x4 localToWorld;
466 layout (marker=normals(local_to_world)) uniform float4x4 localToWorldAdjInv;
Brian Osmanaa9983a2020-04-02 16:49:59 -0400467 uniform float3 lightPos;
468
469 void main(float2 p, inout half4 color) {
470 float3 norm = normalize(vtx_normal);
Brian Osmand9bde072020-04-15 14:18:13 -0400471 float3 plane_norm = normalize(localToWorldAdjInv * float4(norm, 0)).xyz;
Brian Osmanaa9983a2020-04-02 16:49:59 -0400472
473 float3 plane_pos = (localToWorld * float4(p, 0, 1)).xyz;
474 float3 light_dir = normalize(lightPos - plane_pos);
475
476 float ambient = 0.2;
477 float dp = dot(plane_norm, light_dir);
478 float scale = min(ambient + max(dp, 0), 1);
479
480 color = half4(0.7, 0.9, 0.3, 1) * half4(float4(scale, scale, scale, 1));
481 }
482 )";
483 auto [effect, error] = SkRuntimeEffect::Make(SkString(code));
484 if (!effect) {
485 SkDebugf("runtime error %s\n", error.c_str());
486 }
487 fEffect = effect;
488 }
489
490 void drawContent(SkCanvas* canvas, SkColor color, int index, bool drawFront) override {
491 if (!drawFront || !front(canvas->getLocalToDevice())) {
492 return;
493 }
494
Brian Osmand9bde072020-04-15 14:18:13 -0400495 SkRuntimeShaderBuilder builder(fEffect);
496 builder.input("lightPos") = fLight.computeWorldPos(fSphere);
Brian Osmanaa9983a2020-04-02 16:49:59 -0400497
498 SkPaint paint;
499 paint.setColor(color);
Brian Osmand9bde072020-04-15 14:18:13 -0400500 paint.setShader(builder.makeShader(nullptr, true));
Brian Osmanaa9983a2020-04-02 16:49:59 -0400501
502 canvas->drawVertices(fVertices, paint);
503 }
504};
505DEF_SAMPLE( return new SampleVerts3D; )
506
Mike Reedcfee8ee2020-02-18 13:05:45 -0500507#include "modules/skottie/include/Skottie.h"
508
509class SampleSkottieCube : public SampleCubeBase {
510 sk_sp<skottie::Animation> fAnim[6];
511
512public:
513 SampleSkottieCube() : SampleCubeBase(kCanRunOnCPU) {}
514
515 SkString name() override { return SkString("skottie3d"); }
516
517 void onOnceBeforeDraw() override {
518 const char* files[] = {
519 "skottie/skottie-chained-mattes.json",
520 "skottie/skottie-gradient-ramp.json",
521 "skottie/skottie_sample_2.json",
522 "skottie/skottie-3d-3planes.json",
523 "skottie/skottie-text-animator-4.json",
524 "skottie/skottie-motiontile-effect-phase.json",
525
526 };
527 for (unsigned i = 0; i < SK_ARRAY_COUNT(files); ++i) {
528 if (auto stream = GetResourceAsStream(files[i])) {
529 fAnim[i] = skottie::Animation::Make(stream.get());
530 }
531 }
532 }
533
534 void drawContent(SkCanvas* canvas, SkColor color, int index, bool drawFront) override {
Mike Reed46f5c5f2020-02-20 15:42:29 -0500535 if (!drawFront || !front(canvas->getLocalToDevice())) {
Mike Reedcfee8ee2020-02-18 13:05:45 -0500536 return;
537 }
538
539 SkPaint paint;
540 paint.setColor(color);
541 SkRect r = {0, 0, 400, 400};
542 canvas->drawRect(r, paint);
543 fAnim[index]->render(canvas, &r);
544 }
545
546 bool onAnimate(double nanos) override {
547 for (auto& anim : fAnim) {
548 SkScalar dur = anim->duration();
549 SkScalar t = fmod(1e-9 * nanos, dur) / dur;
550 anim->seek(t);
551 }
552 return true;
553 }
554};
555DEF_SAMPLE( return new SampleSkottieCube; )