blob: cb49bf38723721495746bc65e2f0d08ea3e93429 [file] [log] [blame]
robertphillipsadf5afa2016-06-03 10:12:08 -07001/*
2 * Copyright 2016 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 "SampleCode.h"
9#include "SkAnimTimer.h"
dvonbeck5b794fa2016-07-06 13:58:36 -070010#include "SkBitmapProcShader.h"
robertphillipsadf5afa2016-06-03 10:12:08 -070011#include "SkCanvas.h"
12#include "SkDrawable.h"
13#include "SkLightingShader.h"
14#include "SkLights.h"
dvonbeck5b794fa2016-07-06 13:58:36 -070015#include "SkNormalSource.h"
robertphillipsadf5afa2016-06-03 10:12:08 -070016#include "SkRandom.h"
17#include "SkRSXform.h"
dvonbeck5b794fa2016-07-06 13:58:36 -070018#include "SkView.h"
robertphillipsadf5afa2016-06-03 10:12:08 -070019
20#include "sk_tool_utils.h"
21
22class DrawLitAtlasDrawable : public SkDrawable {
23public:
24 DrawLitAtlasDrawable(const SkRect& r)
25 : fBounds(r)
robertphillips862dae52016-06-03 15:48:16 -070026 , fUseColors(false)
27 , fLightDir(SkVector3::Make(1.0f, 0.0f, 0.0f)) {
robertphillipsadf5afa2016-06-03 10:12:08 -070028 fAtlas = MakeAtlas();
29
30 SkRandom rand;
31 for (int i = 0; i < kNumAsteroids; ++i) {
32 fAsteroids[i].initAsteroid(&rand, fBounds, &fDiffTex[i], &fNormTex[i]);
33 }
34
35 fShip.initShip(fBounds, &fDiffTex[kNumAsteroids], &fNormTex[kNumAsteroids]);
36
robertphillips862dae52016-06-03 15:48:16 -070037 this->updateLights();
robertphillipsadf5afa2016-06-03 10:12:08 -070038 }
39
40 void toggleUseColors() {
41 fUseColors = !fUseColors;
42 }
43
robertphillips862dae52016-06-03 15:48:16 -070044 void rotateLight() {
45 SkScalar c;
46 SkScalar s = SkScalarSinCos(SK_ScalarPI/6.0f, &c);
47
48 SkScalar newX = c * fLightDir.fX - s * fLightDir.fY;
49 SkScalar newY = s * fLightDir.fX + c * fLightDir.fY;
50
51 fLightDir.set(newX, newY, 0.0f);
52
53 this->updateLights();
54 }
55
robertphillipsadf5afa2016-06-03 10:12:08 -070056 void left() {
57 SkScalar newRot = SkScalarMod(fShip.rot() + (2*SK_ScalarPI - SK_ScalarPI/32.0f),
58 2 * SK_ScalarPI);
59 fShip.setRot(newRot);
60 }
61
62 void right() {
63 SkScalar newRot = SkScalarMod(fShip.rot() + SK_ScalarPI/32.0f, 2 * SK_ScalarPI);
64 fShip.setRot(newRot);
65 }
66
67 void thrust() {
68 SkScalar c;
69 SkScalar s = SkScalarSinCos(fShip.rot(), &c);
70
71 SkVector newVel = fShip.velocity();
72 newVel.fX += s;
73 newVel.fY += -c;
74
75 if (newVel.lengthSqd() > kMaxShipSpeed*kMaxShipSpeed) {
76 newVel.setLength(SkIntToScalar(kMaxShipSpeed));
77 }
78
79 fShip.setVelocity(newVel);
80 }
81
82protected:
83 void onDraw(SkCanvas* canvas) override {
84 SkRSXform xforms[kNumAsteroids+kNumShips];
85 SkColor colors[kNumAsteroids+kNumShips];
86
87 for (int i = 0; i < kNumAsteroids; ++i) {
88 fAsteroids[i].advance(fBounds);
89 xforms[i] = fAsteroids[i].asRSXform();
90 if (fUseColors) {
91 colors[i] = SkColorSetARGB(0xFF, 0xFF, 0xFF, 0xFF);
92 }
93 }
94
95 fShip.advance(fBounds);
96 xforms[kNumAsteroids] = fShip.asRSXform();
97 if (fUseColors) {
98 colors[kNumAsteroids] = SkColorSetARGB(0xFF, 0xFF, 0xFF, 0xFF);
99 }
100
101#ifdef SK_DEBUG
102 canvas->drawBitmap(fAtlas, 0, 0); // just to see the atlas
robertphillips862dae52016-06-03 15:48:16 -0700103
104 this->drawLightDir(canvas, fBounds.centerX(), fBounds.centerY());
105#endif
robertphillipsadf5afa2016-06-03 10:12:08 -0700106
107#if 0
108 // TODO: revitalize when drawLitAtlas API lands
109 SkPaint paint;
110 paint.setFilterQuality(kLow_SkFilterQuality);
111
112 const SkRect cull = this->getBounds();
113 const SkColor* colorsPtr = fUseColors ? colors : NULL;
114
115 canvas->drawLitAtlas(fAtlas, xforms, fDiffTex, fNormTex, colorsPtr, kNumAsteroids+1,
116 SkXfermode::kModulate_Mode, &cull, &paint, fLights);
117#else
118 SkMatrix diffMat, normalMat;
119
120 for (int i = 0; i < kNumAsteroids+1; ++i) {
121 colors[i] = colors[i] & 0xFF000000; // to silence compilers
122 SkPaint paint;
123
124 SkRect r = fDiffTex[i];
125 r.offsetTo(0, 0);
126
127 diffMat.setRectToRect(fDiffTex[i], r, SkMatrix::kFill_ScaleToFit);
128 normalMat.setRectToRect(fNormTex[i], r, SkMatrix::kFill_ScaleToFit);
129
130 SkMatrix m;
131 m.setRSXform(xforms[i]);
132
reed1ec04d92016-08-05 12:07:41 -0700133 sk_sp<SkShader> normalMap = SkShader::MakeBitmapShader(fAtlas, SkShader::kClamp_TileMode,
134 SkShader::kClamp_TileMode, &normalMat);
dvonbeck5b794fa2016-07-06 13:58:36 -0700135 sk_sp<SkNormalSource> normalSource = SkNormalSource::MakeFromNormalMap(
136 std::move(normalMap), m);
reed320a40d2016-08-02 06:12:06 -0700137 sk_sp<SkShader> diffuseShader = SkShader::MakeBitmapShader(fAtlas,
dvonbeck6af677f2016-07-10 18:38:33 -0700138 SkShader::kClamp_TileMode, SkShader::kClamp_TileMode, &diffMat);
139 paint.setShader(SkLightingShader::Make(std::move(diffuseShader),
140 std::move(normalSource), fLights));
robertphillipsadf5afa2016-06-03 10:12:08 -0700141
142 canvas->save();
143 canvas->setMatrix(m);
144 canvas->drawRect(r, paint);
145 canvas->restore();
146 }
147#endif
148
149#ifdef SK_DEBUG
150 {
151 SkPaint paint;
152 paint.setColor(SK_ColorRED);
153
154 for (int i = 0; i < kNumAsteroids; ++i) {
155 canvas->drawCircle(fAsteroids[i].pos().x(), fAsteroids[i].pos().y(), 2, paint);
156 }
157 canvas->drawCircle(fShip.pos().x(), fShip.pos().y(), 2, paint);
158
159 paint.setStyle(SkPaint::kStroke_Style);
160 canvas->drawRect(this->getBounds(), paint);
161 }
162#endif
163 }
164
165 SkRect onGetBounds() override {
166 return fBounds;
167 }
168
169private:
170
171 enum ObjType {
172 kBigAsteroid_ObjType = 0,
173 kMedAsteroid_ObjType,
174 kSmAsteroid_ObjType,
175 kShip_ObjType,
176
177 kLast_ObjType = kShip_ObjType
178 };
179
180 static const int kObjTypeCount = kLast_ObjType + 1;
181
robertphillips862dae52016-06-03 15:48:16 -0700182 void updateLights() {
183 SkLights::Builder builder;
184
vjiaoblack772b5ee2016-08-12 11:38:47 -0700185 builder.add(SkLights::Light::MakeDirectional(
186 SkColor3f::Make(1.0f, 1.0f, 1.0f), fLightDir));
187 builder.add(SkLights::Light::MakeAmbient(SkColor3f::Make(0.2f, 0.2f, 0.2f)));
robertphillips862dae52016-06-03 15:48:16 -0700188
189 fLights = builder.finish();
190 }
191
192#ifdef SK_DEBUG
193 // Draw a vector to the light
194 void drawLightDir(SkCanvas* canvas, SkScalar centerX, SkScalar centerY) {
195 static const int kBgLen = 30;
196 static const int kSmLen = 5;
197
198 // TODO: change the lighting coordinate system to be right handed
199 SkPoint p1 = SkPoint::Make(centerX + kBgLen * fLightDir.fX,
200 centerY - kBgLen * fLightDir.fY);
201 SkPoint p2 = SkPoint::Make(centerX + (kBgLen-kSmLen) * fLightDir.fX,
202 centerY - (kBgLen-kSmLen) * fLightDir.fY);
203
204 SkPaint p;
205 canvas->drawLine(centerX, centerY, p1.fX, p1.fY, p);
206 canvas->drawLine(p1.fX, p1.fY,
207 p2.fX - kSmLen * fLightDir.fY, p2.fY - kSmLen * fLightDir.fX, p);
208 canvas->drawLine(p1.fX, p1.fY,
209 p2.fX + kSmLen * fLightDir.fY, p2.fY + kSmLen * fLightDir.fX, p);
210 }
211#endif
212
robertphillipsadf5afa2016-06-03 10:12:08 -0700213 // Create the mixed diffuse & normal atlas
214 //
215 // big color circle | big normal hemi
216 // ------------------------------------
217 // med color circle | med normal pyra
218 // ------------------------------------
219 // sm color circle | sm normal hemi
220 // ------------------------------------
221 // big ship | big tetra normal
222 static SkBitmap MakeAtlas() {
223
224 SkBitmap atlas;
225 atlas.allocN32Pixels(kAtlasWidth, kAtlasHeight);
226
227 for (int y = 0; y < kAtlasHeight; ++y) {
228 int x = 0;
229 for ( ; x < kBigSize+kPad; ++x) {
230 *atlas.getAddr32(x, y) = SK_ColorTRANSPARENT;
231 }
232 for ( ; x < kAtlasWidth; ++x) {
233 *atlas.getAddr32(x, y) = SkPackARGB32(0xFF, 0x88, 0x88, 0xFF);
234 }
235 }
236
237 // big asteroid
238 {
239 SkPoint bigCenter = SkPoint::Make(kDiffXOff + kBigSize/2.0f, kBigYOff + kBigSize/2.0f);
240
241 for (int y = kBigYOff; y < kBigYOff+kBigSize; ++y) {
242 for (int x = kDiffXOff; x < kDiffXOff+kBigSize; ++x) {
243 SkScalar distSq = (x - bigCenter.fX) * (x - bigCenter.fX) +
244 (y - bigCenter.fY) * (y - bigCenter.fY);
245 if (distSq > kBigSize*kBigSize/4.0f) {
246 *atlas.getAddr32(x, y) = SkPreMultiplyARGB(0, 0, 0, 0);
247 } else {
248 *atlas.getAddr32(x, y) = SkPackARGB32(0xFF, 0xFF, 0, 0);
249 }
250 }
251 }
252
253 sk_tool_utils::create_hemi_normal_map(&atlas,
254 SkIRect::MakeXYWH(kNormXOff, kBigYOff,
255 kBigSize, kBigSize));
256 }
257
258 // medium asteroid
259 {
260 for (int y = kMedYOff; y < kMedYOff+kMedSize; ++y) {
261 for (int x = kDiffXOff; x < kDiffXOff+kMedSize; ++x) {
262 *atlas.getAddr32(x, y) = SkPackARGB32(0xFF, 0, 0xFF, 0);
263 }
264 }
265
266 sk_tool_utils::create_frustum_normal_map(&atlas,
267 SkIRect::MakeXYWH(kNormXOff, kMedYOff,
268 kMedSize, kMedSize));
269 }
270
271 // small asteroid
272 {
273 SkPoint smCenter = SkPoint::Make(kDiffXOff + kSmSize/2.0f, kSmYOff + kSmSize/2.0f);
274
275 for (int y = kSmYOff; y < kSmYOff+kSmSize; ++y) {
276 for (int x = kDiffXOff; x < kDiffXOff+kSmSize; ++x) {
277 SkScalar distSq = (x - smCenter.fX) * (x - smCenter.fX) +
278 (y - smCenter.fY) * (y - smCenter.fY);
279 if (distSq > kSmSize*kSmSize/4.0f) {
280 *atlas.getAddr32(x, y) = SkPreMultiplyARGB(0, 0, 0, 0);
281 } else {
282 *atlas.getAddr32(x, y) = SkPackARGB32(0xFF, 0, 0, 0xFF);
283 }
284 }
285 }
286
287 sk_tool_utils::create_hemi_normal_map(&atlas,
288 SkIRect::MakeXYWH(kNormXOff, kSmYOff,
289 kSmSize, kSmSize));
290 }
291
292 // ship
293 {
294 SkScalar shipMidLine = kDiffXOff + kMedSize/2.0f;
295
296 for (int y = kShipYOff; y < kShipYOff+kMedSize; ++y) {
297 SkScalar scaledY = (y - kShipYOff)/(float)kMedSize; // 0..1
298
299 for (int x = kDiffXOff; x < kDiffXOff+kMedSize; ++x) {
300 SkScalar scaledX;
301
302 if (x < shipMidLine) {
303 scaledX = 1.0f - (x - kDiffXOff)/(kMedSize/2.0f); // 0..1
304 } else {
305 scaledX = (x - shipMidLine)/(kMedSize/2.0f); // 0..1
306 }
307
308 if (scaledX < scaledY) {
309 *atlas.getAddr32(x, y) = SkPackARGB32(0xFF, 0, 0xFF, 0xFF);
310 } else {
311 *atlas.getAddr32(x, y) = SkPackARGB32(0, 0, 0, 0);
312 }
313 }
314 }
315
316 sk_tool_utils::create_tetra_normal_map(&atlas,
317 SkIRect::MakeXYWH(kNormXOff, kShipYOff,
318 kMedSize, kMedSize));
319 }
320
321 return atlas;
322 }
323
324 class ObjectRecord {
325 public:
326 void initAsteroid(SkRandom *rand, const SkRect& bounds,
327 SkRect* diffTex, SkRect* normTex) {
328 static const SkScalar gMaxSpeeds[3] = { 1, 2, 5 }; // smaller asteroids can go faster
329 static const SkScalar gYOffs[3] = { kBigYOff, kMedYOff, kSmYOff };
330 static const SkScalar gSizes[3] = { kBigSize, kMedSize, kSmSize };
331
332 static unsigned int asteroidType = 0;
333 fObjType = static_cast<ObjType>(asteroidType++ % 3);
334
335 fPosition.set(bounds.fLeft + rand->nextUScalar1() * bounds.width(),
336 bounds.fTop + rand->nextUScalar1() * bounds.height());
337 fVelocity.fX = rand->nextSScalar1();
338 fVelocity.fY = sqrt(1.0f - fVelocity.fX * fVelocity.fX);
339 SkASSERT(SkScalarNearlyEqual(fVelocity.length(), 1.0f));
340 fVelocity *= gMaxSpeeds[fObjType];
341 fRot = 0;
342 fDeltaRot = rand->nextSScalar1() / 32;
343
344 diffTex->setXYWH(SkIntToScalar(kDiffXOff), gYOffs[fObjType],
345 gSizes[fObjType], gSizes[fObjType]);
346 normTex->setXYWH(SkIntToScalar(kNormXOff), gYOffs[fObjType],
347 gSizes[fObjType], gSizes[fObjType]);
348 }
349
350 void initShip(const SkRect& bounds, SkRect* diffTex, SkRect* normTex) {
351 fObjType = kShip_ObjType;
352 fPosition.set(bounds.centerX(), bounds.centerY());
353 fVelocity = SkVector::Make(0.0f, 0.0f);
354 fRot = 0.0f;
355 fDeltaRot = 0.0f;
356
357 diffTex->setXYWH(SkIntToScalar(kDiffXOff), SkIntToScalar(kShipYOff),
358 SkIntToScalar(kMedSize), SkIntToScalar(kMedSize));
359 normTex->setXYWH(SkIntToScalar(kNormXOff), SkIntToScalar(kShipYOff),
360 SkIntToScalar(kMedSize), SkIntToScalar(kMedSize));
361 }
362
363 void advance(const SkRect& bounds) {
364 fPosition += fVelocity;
365 if (fPosition.fX > bounds.right()) {
366 SkASSERT(fVelocity.fX > 0);
367 fVelocity.fX = -fVelocity.fX;
368 } else if (fPosition.fX < bounds.left()) {
369 SkASSERT(fVelocity.fX < 0);
370 fVelocity.fX = -fVelocity.fX;
371 }
372 if (fPosition.fY > bounds.bottom()) {
373 if (fVelocity.fY > 0) {
374 fVelocity.fY = -fVelocity.fY;
375 }
376 } else if (fPosition.fY < bounds.top()) {
377 if (fVelocity.fY < 0) {
378 fVelocity.fY = -fVelocity.fY;
379 }
380 }
381
382 fRot += fDeltaRot;
383 fRot = SkScalarMod(fRot, 2 * SK_ScalarPI);
384 }
385
386 const SkPoint& pos() const { return fPosition; }
387
388 SkScalar rot() const { return fRot; }
389 void setRot(SkScalar rot) { fRot = rot; }
390
391 const SkPoint& velocity() const { return fVelocity; }
392 void setVelocity(const SkPoint& velocity) { fVelocity = velocity; }
393
394 SkRSXform asRSXform() const {
395 static const SkScalar gHalfSizes[kObjTypeCount] = {
396 SkScalarHalf(kBigSize),
397 SkScalarHalf(kMedSize),
398 SkScalarHalf(kSmSize),
399 SkScalarHalf(kMedSize),
400 };
401
402 return SkRSXform::MakeFromRadians(1.0f, fRot, fPosition.x(), fPosition.y(),
403 gHalfSizes[fObjType],
404 gHalfSizes[fObjType]);
405 }
406
407 private:
408 ObjType fObjType;
409 SkPoint fPosition;
410 SkVector fVelocity;
411 SkScalar fRot; // In radians.
412 SkScalar fDeltaRot; // In radiands. Not used by ship.
413 };
414
415
416
417
418private:
419 static const int kNumLights = 2;
420 static const int kNumAsteroids = 6;
421 static const int kNumShips = 1;
422
423 static const int kBigSize = 128;
424 static const int kMedSize = 64;
425 static const int kSmSize = 32;
426 static const int kPad = 1;
427 static const int kAtlasWidth = kBigSize + kBigSize + 2 * kPad; // 2 pads in the middle
428 static const int kAtlasHeight = kBigSize + kMedSize + kSmSize + kMedSize + 3 * kPad;
429
430 static const int kDiffXOff = 0;
431 static const int kNormXOff = kBigSize + 2 * kPad;
432
433 static const int kBigYOff = 0;
434 static const int kMedYOff = kBigSize + kPad;
435 static const int kSmYOff = kMedYOff + kMedSize + kPad;
436 static const int kShipYOff = kSmYOff + kSmSize + kPad;
437 static const int kMaxShipSpeed = 5;
438
439 SkBitmap fAtlas;
440 ObjectRecord fAsteroids[kNumAsteroids];
441 ObjectRecord fShip;
442 SkRect fDiffTex[kNumAsteroids+kNumShips];
443 SkRect fNormTex[kNumAsteroids+kNumShips];
444 SkRect fBounds;
445 bool fUseColors;
robertphillips862dae52016-06-03 15:48:16 -0700446 SkVector3 fLightDir;
robertphillipsadf5afa2016-06-03 10:12:08 -0700447 sk_sp<SkLights> fLights;
448
449 typedef SkDrawable INHERITED;
450};
451
452class DrawLitAtlasView : public SampleView {
453public:
454 DrawLitAtlasView()
455 : fDrawable(new DrawLitAtlasDrawable(SkRect::MakeWH(640, 480))) {
456 }
457
458protected:
459 bool onQuery(SkEvent* evt) override {
460 if (SampleCode::TitleQ(*evt)) {
461 SampleCode::TitleR(evt, "DrawLitAtlas");
462 return true;
463 }
464 SkUnichar uni;
465 if (SampleCode::CharQ(*evt, &uni)) {
466 switch (uni) {
467 case 'C':
468 fDrawable->toggleUseColors();
469 this->inval(NULL);
470 return true;
471 case 'j':
472 fDrawable->left();
473 this->inval(NULL);
474 return true;
475 case 'k':
476 fDrawable->thrust();
477 this->inval(NULL);
478 return true;
479 case 'l':
480 fDrawable->right();
481 this->inval(NULL);
482 return true;
robertphillips862dae52016-06-03 15:48:16 -0700483 case 'o':
484 fDrawable->rotateLight();
485 this->inval(NULL);
486 return true;
robertphillipsadf5afa2016-06-03 10:12:08 -0700487 default:
488 break;
489 }
490 }
491 return this->INHERITED::onQuery(evt);
492 }
493
494 void onDrawContent(SkCanvas* canvas) override {
495 canvas->drawDrawable(fDrawable);
496 this->inval(NULL);
497 }
498
499#if 0
500 // TODO: switch over to use this for our animation
501 bool onAnimate(const SkAnimTimer& timer) override {
502 SkScalar angle = SkDoubleToScalar(fmod(timer.secs() * 360 / 24, 360));
503 fAnimatingDrawable->setSweep(angle);
504 return true;
505 }
506#endif
507
508private:
509 SkAutoTUnref<DrawLitAtlasDrawable> fDrawable;
510
511 typedef SampleView INHERITED;
512};
513
514//////////////////////////////////////////////////////////////////////////////
515
516static SkView* MyFactory() { return new DrawLitAtlasView; }
517static SkViewRegister reg(MyFactory);