blob: 5f62e2abfb1cfedd236dc3c2dae5ca7e8c6d7223 [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
185 builder.add(SkLights::Light(SkColor3f::Make(1.0f, 1.0f, 1.0f), fLightDir));
186 builder.add(SkLights::Light(SkColor3f::Make(0.2f, 0.2f, 0.2f)));
187
188 fLights = builder.finish();
189 }
190
191#ifdef SK_DEBUG
192 // Draw a vector to the light
193 void drawLightDir(SkCanvas* canvas, SkScalar centerX, SkScalar centerY) {
194 static const int kBgLen = 30;
195 static const int kSmLen = 5;
196
197 // TODO: change the lighting coordinate system to be right handed
198 SkPoint p1 = SkPoint::Make(centerX + kBgLen * fLightDir.fX,
199 centerY - kBgLen * fLightDir.fY);
200 SkPoint p2 = SkPoint::Make(centerX + (kBgLen-kSmLen) * fLightDir.fX,
201 centerY - (kBgLen-kSmLen) * fLightDir.fY);
202
203 SkPaint p;
204 canvas->drawLine(centerX, centerY, p1.fX, p1.fY, p);
205 canvas->drawLine(p1.fX, p1.fY,
206 p2.fX - kSmLen * fLightDir.fY, p2.fY - kSmLen * fLightDir.fX, p);
207 canvas->drawLine(p1.fX, p1.fY,
208 p2.fX + kSmLen * fLightDir.fY, p2.fY + kSmLen * fLightDir.fX, p);
209 }
210#endif
211
robertphillipsadf5afa2016-06-03 10:12:08 -0700212 // Create the mixed diffuse & normal atlas
213 //
214 // big color circle | big normal hemi
215 // ------------------------------------
216 // med color circle | med normal pyra
217 // ------------------------------------
218 // sm color circle | sm normal hemi
219 // ------------------------------------
220 // big ship | big tetra normal
221 static SkBitmap MakeAtlas() {
222
223 SkBitmap atlas;
224 atlas.allocN32Pixels(kAtlasWidth, kAtlasHeight);
225
226 for (int y = 0; y < kAtlasHeight; ++y) {
227 int x = 0;
228 for ( ; x < kBigSize+kPad; ++x) {
229 *atlas.getAddr32(x, y) = SK_ColorTRANSPARENT;
230 }
231 for ( ; x < kAtlasWidth; ++x) {
232 *atlas.getAddr32(x, y) = SkPackARGB32(0xFF, 0x88, 0x88, 0xFF);
233 }
234 }
235
236 // big asteroid
237 {
238 SkPoint bigCenter = SkPoint::Make(kDiffXOff + kBigSize/2.0f, kBigYOff + kBigSize/2.0f);
239
240 for (int y = kBigYOff; y < kBigYOff+kBigSize; ++y) {
241 for (int x = kDiffXOff; x < kDiffXOff+kBigSize; ++x) {
242 SkScalar distSq = (x - bigCenter.fX) * (x - bigCenter.fX) +
243 (y - bigCenter.fY) * (y - bigCenter.fY);
244 if (distSq > kBigSize*kBigSize/4.0f) {
245 *atlas.getAddr32(x, y) = SkPreMultiplyARGB(0, 0, 0, 0);
246 } else {
247 *atlas.getAddr32(x, y) = SkPackARGB32(0xFF, 0xFF, 0, 0);
248 }
249 }
250 }
251
252 sk_tool_utils::create_hemi_normal_map(&atlas,
253 SkIRect::MakeXYWH(kNormXOff, kBigYOff,
254 kBigSize, kBigSize));
255 }
256
257 // medium asteroid
258 {
259 for (int y = kMedYOff; y < kMedYOff+kMedSize; ++y) {
260 for (int x = kDiffXOff; x < kDiffXOff+kMedSize; ++x) {
261 *atlas.getAddr32(x, y) = SkPackARGB32(0xFF, 0, 0xFF, 0);
262 }
263 }
264
265 sk_tool_utils::create_frustum_normal_map(&atlas,
266 SkIRect::MakeXYWH(kNormXOff, kMedYOff,
267 kMedSize, kMedSize));
268 }
269
270 // small asteroid
271 {
272 SkPoint smCenter = SkPoint::Make(kDiffXOff + kSmSize/2.0f, kSmYOff + kSmSize/2.0f);
273
274 for (int y = kSmYOff; y < kSmYOff+kSmSize; ++y) {
275 for (int x = kDiffXOff; x < kDiffXOff+kSmSize; ++x) {
276 SkScalar distSq = (x - smCenter.fX) * (x - smCenter.fX) +
277 (y - smCenter.fY) * (y - smCenter.fY);
278 if (distSq > kSmSize*kSmSize/4.0f) {
279 *atlas.getAddr32(x, y) = SkPreMultiplyARGB(0, 0, 0, 0);
280 } else {
281 *atlas.getAddr32(x, y) = SkPackARGB32(0xFF, 0, 0, 0xFF);
282 }
283 }
284 }
285
286 sk_tool_utils::create_hemi_normal_map(&atlas,
287 SkIRect::MakeXYWH(kNormXOff, kSmYOff,
288 kSmSize, kSmSize));
289 }
290
291 // ship
292 {
293 SkScalar shipMidLine = kDiffXOff + kMedSize/2.0f;
294
295 for (int y = kShipYOff; y < kShipYOff+kMedSize; ++y) {
296 SkScalar scaledY = (y - kShipYOff)/(float)kMedSize; // 0..1
297
298 for (int x = kDiffXOff; x < kDiffXOff+kMedSize; ++x) {
299 SkScalar scaledX;
300
301 if (x < shipMidLine) {
302 scaledX = 1.0f - (x - kDiffXOff)/(kMedSize/2.0f); // 0..1
303 } else {
304 scaledX = (x - shipMidLine)/(kMedSize/2.0f); // 0..1
305 }
306
307 if (scaledX < scaledY) {
308 *atlas.getAddr32(x, y) = SkPackARGB32(0xFF, 0, 0xFF, 0xFF);
309 } else {
310 *atlas.getAddr32(x, y) = SkPackARGB32(0, 0, 0, 0);
311 }
312 }
313 }
314
315 sk_tool_utils::create_tetra_normal_map(&atlas,
316 SkIRect::MakeXYWH(kNormXOff, kShipYOff,
317 kMedSize, kMedSize));
318 }
319
320 return atlas;
321 }
322
323 class ObjectRecord {
324 public:
325 void initAsteroid(SkRandom *rand, const SkRect& bounds,
326 SkRect* diffTex, SkRect* normTex) {
327 static const SkScalar gMaxSpeeds[3] = { 1, 2, 5 }; // smaller asteroids can go faster
328 static const SkScalar gYOffs[3] = { kBigYOff, kMedYOff, kSmYOff };
329 static const SkScalar gSizes[3] = { kBigSize, kMedSize, kSmSize };
330
331 static unsigned int asteroidType = 0;
332 fObjType = static_cast<ObjType>(asteroidType++ % 3);
333
334 fPosition.set(bounds.fLeft + rand->nextUScalar1() * bounds.width(),
335 bounds.fTop + rand->nextUScalar1() * bounds.height());
336 fVelocity.fX = rand->nextSScalar1();
337 fVelocity.fY = sqrt(1.0f - fVelocity.fX * fVelocity.fX);
338 SkASSERT(SkScalarNearlyEqual(fVelocity.length(), 1.0f));
339 fVelocity *= gMaxSpeeds[fObjType];
340 fRot = 0;
341 fDeltaRot = rand->nextSScalar1() / 32;
342
343 diffTex->setXYWH(SkIntToScalar(kDiffXOff), gYOffs[fObjType],
344 gSizes[fObjType], gSizes[fObjType]);
345 normTex->setXYWH(SkIntToScalar(kNormXOff), gYOffs[fObjType],
346 gSizes[fObjType], gSizes[fObjType]);
347 }
348
349 void initShip(const SkRect& bounds, SkRect* diffTex, SkRect* normTex) {
350 fObjType = kShip_ObjType;
351 fPosition.set(bounds.centerX(), bounds.centerY());
352 fVelocity = SkVector::Make(0.0f, 0.0f);
353 fRot = 0.0f;
354 fDeltaRot = 0.0f;
355
356 diffTex->setXYWH(SkIntToScalar(kDiffXOff), SkIntToScalar(kShipYOff),
357 SkIntToScalar(kMedSize), SkIntToScalar(kMedSize));
358 normTex->setXYWH(SkIntToScalar(kNormXOff), SkIntToScalar(kShipYOff),
359 SkIntToScalar(kMedSize), SkIntToScalar(kMedSize));
360 }
361
362 void advance(const SkRect& bounds) {
363 fPosition += fVelocity;
364 if (fPosition.fX > bounds.right()) {
365 SkASSERT(fVelocity.fX > 0);
366 fVelocity.fX = -fVelocity.fX;
367 } else if (fPosition.fX < bounds.left()) {
368 SkASSERT(fVelocity.fX < 0);
369 fVelocity.fX = -fVelocity.fX;
370 }
371 if (fPosition.fY > bounds.bottom()) {
372 if (fVelocity.fY > 0) {
373 fVelocity.fY = -fVelocity.fY;
374 }
375 } else if (fPosition.fY < bounds.top()) {
376 if (fVelocity.fY < 0) {
377 fVelocity.fY = -fVelocity.fY;
378 }
379 }
380
381 fRot += fDeltaRot;
382 fRot = SkScalarMod(fRot, 2 * SK_ScalarPI);
383 }
384
385 const SkPoint& pos() const { return fPosition; }
386
387 SkScalar rot() const { return fRot; }
388 void setRot(SkScalar rot) { fRot = rot; }
389
390 const SkPoint& velocity() const { return fVelocity; }
391 void setVelocity(const SkPoint& velocity) { fVelocity = velocity; }
392
393 SkRSXform asRSXform() const {
394 static const SkScalar gHalfSizes[kObjTypeCount] = {
395 SkScalarHalf(kBigSize),
396 SkScalarHalf(kMedSize),
397 SkScalarHalf(kSmSize),
398 SkScalarHalf(kMedSize),
399 };
400
401 return SkRSXform::MakeFromRadians(1.0f, fRot, fPosition.x(), fPosition.y(),
402 gHalfSizes[fObjType],
403 gHalfSizes[fObjType]);
404 }
405
406 private:
407 ObjType fObjType;
408 SkPoint fPosition;
409 SkVector fVelocity;
410 SkScalar fRot; // In radians.
411 SkScalar fDeltaRot; // In radiands. Not used by ship.
412 };
413
414
415
416
417private:
418 static const int kNumLights = 2;
419 static const int kNumAsteroids = 6;
420 static const int kNumShips = 1;
421
422 static const int kBigSize = 128;
423 static const int kMedSize = 64;
424 static const int kSmSize = 32;
425 static const int kPad = 1;
426 static const int kAtlasWidth = kBigSize + kBigSize + 2 * kPad; // 2 pads in the middle
427 static const int kAtlasHeight = kBigSize + kMedSize + kSmSize + kMedSize + 3 * kPad;
428
429 static const int kDiffXOff = 0;
430 static const int kNormXOff = kBigSize + 2 * kPad;
431
432 static const int kBigYOff = 0;
433 static const int kMedYOff = kBigSize + kPad;
434 static const int kSmYOff = kMedYOff + kMedSize + kPad;
435 static const int kShipYOff = kSmYOff + kSmSize + kPad;
436 static const int kMaxShipSpeed = 5;
437
438 SkBitmap fAtlas;
439 ObjectRecord fAsteroids[kNumAsteroids];
440 ObjectRecord fShip;
441 SkRect fDiffTex[kNumAsteroids+kNumShips];
442 SkRect fNormTex[kNumAsteroids+kNumShips];
443 SkRect fBounds;
444 bool fUseColors;
robertphillips862dae52016-06-03 15:48:16 -0700445 SkVector3 fLightDir;
robertphillipsadf5afa2016-06-03 10:12:08 -0700446 sk_sp<SkLights> fLights;
447
448 typedef SkDrawable INHERITED;
449};
450
451class DrawLitAtlasView : public SampleView {
452public:
453 DrawLitAtlasView()
454 : fDrawable(new DrawLitAtlasDrawable(SkRect::MakeWH(640, 480))) {
455 }
456
457protected:
458 bool onQuery(SkEvent* evt) override {
459 if (SampleCode::TitleQ(*evt)) {
460 SampleCode::TitleR(evt, "DrawLitAtlas");
461 return true;
462 }
463 SkUnichar uni;
464 if (SampleCode::CharQ(*evt, &uni)) {
465 switch (uni) {
466 case 'C':
467 fDrawable->toggleUseColors();
468 this->inval(NULL);
469 return true;
470 case 'j':
471 fDrawable->left();
472 this->inval(NULL);
473 return true;
474 case 'k':
475 fDrawable->thrust();
476 this->inval(NULL);
477 return true;
478 case 'l':
479 fDrawable->right();
480 this->inval(NULL);
481 return true;
robertphillips862dae52016-06-03 15:48:16 -0700482 case 'o':
483 fDrawable->rotateLight();
484 this->inval(NULL);
485 return true;
robertphillipsadf5afa2016-06-03 10:12:08 -0700486 default:
487 break;
488 }
489 }
490 return this->INHERITED::onQuery(evt);
491 }
492
493 void onDrawContent(SkCanvas* canvas) override {
494 canvas->drawDrawable(fDrawable);
495 this->inval(NULL);
496 }
497
498#if 0
499 // TODO: switch over to use this for our animation
500 bool onAnimate(const SkAnimTimer& timer) override {
501 SkScalar angle = SkDoubleToScalar(fmod(timer.secs() * 360 / 24, 360));
502 fAnimatingDrawable->setSweep(angle);
503 return true;
504 }
505#endif
506
507private:
508 SkAutoTUnref<DrawLitAtlasDrawable> fDrawable;
509
510 typedef SampleView INHERITED;
511};
512
513//////////////////////////////////////////////////////////////////////////////
514
515static SkView* MyFactory() { return new DrawLitAtlasView; }
516static SkViewRegister reg(MyFactory);