• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 "include/core/SkCanvas.h"
9 #include "include/core/SkDrawable.h"
10 #include "include/core/SkRSXform.h"
11 #include "include/utils/SkRandom.h"
12 #include "samplecode/Sample.h"
13 #include "src/core/SkNormalSource.h"
14 #include "src/shaders/SkBitmapProcShader.h"
15 #include "src/shaders/SkLightingShader.h"
16 #include "src/shaders/SkLights.h"
17 
18 #include "tools/ToolUtils.h"
19 
20 // A crude normal mapped asteroids-like sample
21 class DrawLitAtlasDrawable : public SkDrawable {
22 public:
DrawLitAtlasDrawable(const SkRect & r)23     DrawLitAtlasDrawable(const SkRect& r)
24             : fBounds(r)
25             , fUseColors(false)
26             , fLightDir(SkVector3::Make(1.0f, 0.0f, 0.0f)) {
27         fAtlas = MakeAtlas();
28 
29         SkRandom rand;
30         for (int i = 0; i < kNumAsteroids; ++i) {
31             fAsteroids[i].initAsteroid(&rand, fBounds, &fDiffTex[i], &fNormTex[i]);
32         }
33 
34         fShip.initShip(fBounds, &fDiffTex[kNumAsteroids], &fNormTex[kNumAsteroids]);
35 
36         this->updateLights();
37     }
38 
toggleUseColors()39     void toggleUseColors() {
40         fUseColors = !fUseColors;
41     }
42 
rotateLight()43     void rotateLight() {
44         SkScalar r = SK_ScalarPI / 6.0f,
45                  s = SkScalarSin(r),
46                  c = SkScalarCos(r);
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 
left()56     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 
right()62     void right() {
63         SkScalar newRot = SkScalarMod(fShip.rot() + SK_ScalarPI/32.0f, 2 * SK_ScalarPI);
64         fShip.setRot(newRot);
65     }
66 
thrust()67     void thrust() {
68         SkScalar s = SkScalarSin(fShip.rot()),
69                  c = SkScalarCos(fShip.rot());
70 
71         SkVector newVel = fShip.velocity();
72         newVel.fX += s;
73         newVel.fY += -c;
74 
75         SkScalar len = newVel.length();
76         if (len > kMaxShipSpeed) {
77             newVel.setLength(SkIntToScalar(kMaxShipSpeed));
78         }
79 
80         fShip.setVelocity(newVel);
81     }
82 
83 protected:
onDraw(SkCanvas * canvas)84     void onDraw(SkCanvas* canvas) override {
85         SkRSXform xforms[kNumAsteroids+kNumShips];
86         SkColor colors[kNumAsteroids+kNumShips];
87 
88         for (int i = 0; i < kNumAsteroids; ++i) {
89             fAsteroids[i].advance(fBounds);
90             xforms[i] = fAsteroids[i].asRSXform();
91             if (fUseColors) {
92                 colors[i] = SkColorSetARGB(0xFF, 0xFF, 0xFF, 0xFF);
93             }
94         }
95 
96         fShip.advance(fBounds);
97         xforms[kNumAsteroids] = fShip.asRSXform();
98         if (fUseColors) {
99             colors[kNumAsteroids] = SkColorSetARGB(0xFF, 0xFF, 0xFF, 0xFF);
100         }
101 
102 #ifdef SK_DEBUG
103         canvas->drawBitmap(fAtlas, 0, 0); // just to see the atlas
104 
105         this->drawLightDir(canvas, fBounds.centerX(), fBounds.centerY());
106 #endif
107 
108 #if 0
109         // TODO: revitalize when drawLitAtlas API lands
110         SkPaint paint;
111         paint.setFilterQuality(kLow_SkFilterQuality);
112 
113         const SkRect cull = this->getBounds();
114         const SkColor* colorsPtr = fUseColors ? colors : NULL;
115 
116         canvas->drawLitAtlas(fAtlas, xforms, fDiffTex, fNormTex, colorsPtr, kNumAsteroids+1,
117                              SkXfermode::kModulate_Mode, &cull, &paint, fLights);
118 #else
119         SkMatrix diffMat, normalMat;
120 
121         for (int i = 0; i < kNumAsteroids+1; ++i) {
122             colors[i] = colors[i] & 0xFF000000; // to silence compilers
123             SkPaint paint;
124 
125             SkRect r = fDiffTex[i];
126             r.offsetTo(0, 0);
127 
128             diffMat.setRectToRect(fDiffTex[i], r, SkMatrix::kFill_ScaleToFit);
129             normalMat.setRectToRect(fNormTex[i], r, SkMatrix::kFill_ScaleToFit);
130 
131             SkMatrix m;
132             m.setRSXform(xforms[i]);
133 
134             sk_sp<SkShader> normalMap = fAtlas.makeShader(&normalMat);
135             sk_sp<SkNormalSource> normalSource = SkNormalSource::MakeFromNormalMap(
136                     std::move(normalMap), m);
137             sk_sp<SkShader> diffuseShader = fAtlas.makeShader(&diffMat);
138             paint.setShader(SkLightingShader::Make(std::move(diffuseShader),
139                     std::move(normalSource), fLights));
140 
141             canvas->save();
142                 canvas->setMatrix(m);
143                 canvas->drawRect(r, paint);
144             canvas->restore();
145         }
146 #endif
147 
148 #ifdef SK_DEBUG
149         {
150             SkPaint paint;
151             paint.setColor(SK_ColorRED);
152 
153             for (int i = 0; i < kNumAsteroids; ++i) {
154                 canvas->drawCircle(fAsteroids[i].pos().x(), fAsteroids[i].pos().y(), 2, paint);
155             }
156             canvas->drawCircle(fShip.pos().x(), fShip.pos().y(), 2, paint);
157 
158             paint.setStyle(SkPaint::kStroke_Style);
159             canvas->drawRect(this->getBounds(), paint);
160         }
161 #endif
162     }
163 
onGetBounds()164     SkRect onGetBounds() override {
165         return fBounds;
166     }
167 
168 private:
169 
170     enum ObjType {
171         kBigAsteroid_ObjType = 0,
172         kMedAsteroid_ObjType,
173         kSmAsteroid_ObjType,
174         kShip_ObjType,
175 
176         kLast_ObjType = kShip_ObjType
177     };
178 
179     static const int kObjTypeCount = kLast_ObjType + 1;
180 
updateLights()181     void updateLights() {
182         SkLights::Builder builder;
183 
184         builder.add(SkLights::Light::MakeDirectional(
185                 SkColor3f::Make(1.0f, 1.0f, 1.0f), fLightDir));
186         builder.setAmbientLightColor(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
drawLightDir(SkCanvas * canvas,SkScalar centerX,SkScalar centerY)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 
212     // 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
MakeAtlas()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             ToolUtils::create_hemi_normal_map(
253                     &atlas, SkIRect::MakeXYWH(kNormXOff, kBigYOff, kBigSize, kBigSize));
254         }
255 
256         // medium asteroid
257         {
258             for (int y = kMedYOff; y < kMedYOff+kMedSize; ++y) {
259                 for (int x = kDiffXOff; x < kDiffXOff+kMedSize; ++x) {
260                     *atlas.getAddr32(x, y) = SkPackARGB32(0xFF, 0, 0xFF, 0);
261                 }
262             }
263 
264             ToolUtils::create_frustum_normal_map(
265                     &atlas, SkIRect::MakeXYWH(kNormXOff, kMedYOff, kMedSize, kMedSize));
266         }
267 
268         // small asteroid
269         {
270             SkPoint smCenter = SkPoint::Make(kDiffXOff + kSmSize/2.0f, kSmYOff + kSmSize/2.0f);
271 
272             for (int y = kSmYOff; y < kSmYOff+kSmSize; ++y) {
273                 for (int x = kDiffXOff; x < kDiffXOff+kSmSize; ++x) {
274                     SkScalar distSq = (x - smCenter.fX) * (x - smCenter.fX) +
275                                       (y - smCenter.fY) * (y - smCenter.fY);
276                     if (distSq > kSmSize*kSmSize/4.0f) {
277                         *atlas.getAddr32(x, y) = SkPreMultiplyARGB(0, 0, 0, 0);
278                     } else {
279                         *atlas.getAddr32(x, y) = SkPackARGB32(0xFF, 0, 0, 0xFF);
280                     }
281                 }
282             }
283 
284             ToolUtils::create_hemi_normal_map(
285                     &atlas, SkIRect::MakeXYWH(kNormXOff, kSmYOff, kSmSize, kSmSize));
286         }
287 
288         // ship
289         {
290             SkScalar shipMidLine = kDiffXOff + kMedSize/2.0f;
291 
292             for (int y = kShipYOff; y < kShipYOff+kMedSize; ++y) {
293                 SkScalar scaledY = (y - kShipYOff)/(float)kMedSize; // 0..1
294 
295                 for (int x = kDiffXOff; x < kDiffXOff+kMedSize; ++x) {
296                     SkScalar scaledX;
297 
298                     if (x < shipMidLine) {
299                         scaledX = 1.0f - (x - kDiffXOff)/(kMedSize/2.0f); // 0..1
300                     } else {
301                         scaledX = (x - shipMidLine)/(kMedSize/2.0f);      // 0..1
302                     }
303 
304                     if (scaledX < scaledY) {
305                         *atlas.getAddr32(x, y) = SkPackARGB32(0xFF, 0, 0xFF, 0xFF);
306                     } else {
307                         *atlas.getAddr32(x, y) = SkPackARGB32(0, 0, 0, 0);
308                     }
309                 }
310             }
311 
312             ToolUtils::create_tetra_normal_map(
313                     &atlas, SkIRect::MakeXYWH(kNormXOff, kShipYOff, kMedSize, kMedSize));
314         }
315 
316         return atlas;
317     }
318 
319     class ObjectRecord {
320     public:
initAsteroid(SkRandom * rand,const SkRect & bounds,SkRect * diffTex,SkRect * normTex)321         void initAsteroid(SkRandom *rand, const SkRect& bounds,
322                           SkRect* diffTex, SkRect* normTex) {
323             static const SkScalar gMaxSpeeds[3] = { 1, 2, 5 }; // smaller asteroids can go faster
324             static const SkScalar gYOffs[3] = { kBigYOff, kMedYOff, kSmYOff };
325             static const SkScalar gSizes[3] = { kBigSize, kMedSize, kSmSize };
326 
327             static unsigned int asteroidType = 0;
328             fObjType = static_cast<ObjType>(asteroidType++ % 3);
329 
330             fPosition.set(bounds.fLeft + rand->nextUScalar1() * bounds.width(),
331                           bounds.fTop + rand->nextUScalar1() * bounds.height());
332             fVelocity.fX = rand->nextSScalar1();
333             fVelocity.fY = sqrt(1.0f - fVelocity.fX * fVelocity.fX);
334             SkASSERT(SkScalarNearlyEqual(fVelocity.length(), 1.0f));
335             fVelocity *= gMaxSpeeds[fObjType];
336             fRot = 0;
337             fDeltaRot = rand->nextSScalar1() / 32;
338 
339             diffTex->setXYWH(SkIntToScalar(kDiffXOff), gYOffs[fObjType],
340                              gSizes[fObjType], gSizes[fObjType]);
341             normTex->setXYWH(SkIntToScalar(kNormXOff), gYOffs[fObjType],
342                              gSizes[fObjType], gSizes[fObjType]);
343         }
344 
initShip(const SkRect & bounds,SkRect * diffTex,SkRect * normTex)345         void initShip(const SkRect& bounds, SkRect* diffTex, SkRect* normTex) {
346             fObjType = kShip_ObjType;
347             fPosition.set(bounds.centerX(), bounds.centerY());
348             fVelocity = SkVector::Make(0.0f, 0.0f);
349             fRot = 0.0f;
350             fDeltaRot = 0.0f;
351 
352             diffTex->setXYWH(SkIntToScalar(kDiffXOff), SkIntToScalar(kShipYOff),
353                              SkIntToScalar(kMedSize), SkIntToScalar(kMedSize));
354             normTex->setXYWH(SkIntToScalar(kNormXOff), SkIntToScalar(kShipYOff),
355                              SkIntToScalar(kMedSize), SkIntToScalar(kMedSize));
356         }
357 
advance(const SkRect & bounds)358         void advance(const SkRect& bounds) {
359             fPosition += fVelocity;
360             if (fPosition.fX > bounds.right()) {
361                 SkASSERT(fVelocity.fX > 0);
362                 fVelocity.fX = -fVelocity.fX;
363             } else if (fPosition.fX < bounds.left()) {
364                 SkASSERT(fVelocity.fX < 0);
365                 fVelocity.fX = -fVelocity.fX;
366             }
367             if (fPosition.fY > bounds.bottom()) {
368                 if (fVelocity.fY > 0) {
369                     fVelocity.fY = -fVelocity.fY;
370                 }
371             } else if (fPosition.fY < bounds.top()) {
372                 if (fVelocity.fY < 0) {
373                     fVelocity.fY = -fVelocity.fY;
374                 }
375             }
376 
377             fRot += fDeltaRot;
378             fRot = SkScalarMod(fRot, 2 * SK_ScalarPI);
379         }
380 
pos() const381         const SkPoint& pos() const { return fPosition; }
382 
rot() const383         SkScalar rot() const { return fRot; }
setRot(SkScalar rot)384         void setRot(SkScalar rot) { fRot = rot; }
385 
velocity() const386         const SkPoint& velocity() const { return fVelocity; }
setVelocity(const SkPoint & velocity)387         void setVelocity(const SkPoint& velocity) { fVelocity = velocity; }
388 
asRSXform() const389         SkRSXform asRSXform() const {
390             static const SkScalar gHalfSizes[kObjTypeCount] = {
391                 SkScalarHalf(kBigSize),
392                 SkScalarHalf(kMedSize),
393                 SkScalarHalf(kSmSize),
394                 SkScalarHalf(kMedSize),
395             };
396 
397             return SkRSXform::MakeFromRadians(1.0f, fRot, fPosition.x(), fPosition.y(),
398                                               gHalfSizes[fObjType],
399                                               gHalfSizes[fObjType]);
400         }
401 
402     private:
403         ObjType     fObjType;
404         SkPoint     fPosition;
405         SkVector    fVelocity;
406         SkScalar    fRot;        // In radians.
407         SkScalar    fDeltaRot;   // In radiands. Not used by ship.
408     };
409 
410 private:
411     static const int kNumLights = 2;
412     static const int kNumAsteroids = 6;
413     static const int kNumShips = 1;
414 
415     static const int kBigSize = 128;
416     static const int kMedSize = 64;
417     static const int kSmSize = 32;
418     static const int kPad = 1;
419     static const int kAtlasWidth = kBigSize + kBigSize + 2 * kPad; // 2 pads in the middle
420     static const int kAtlasHeight = kBigSize + kMedSize + kSmSize + kMedSize + 3 * kPad;
421 
422     static const int kDiffXOff = 0;
423     static const int kNormXOff = kBigSize + 2 * kPad;
424 
425     static const int kBigYOff = 0;
426     static const int kMedYOff = kBigSize + kPad;
427     static const int kSmYOff = kMedYOff + kMedSize + kPad;
428     static const int kShipYOff = kSmYOff + kSmSize + kPad;
429     static const int kMaxShipSpeed = 5;
430 
431     SkBitmap        fAtlas;
432     ObjectRecord    fAsteroids[kNumAsteroids];
433     ObjectRecord    fShip;
434     SkRect          fDiffTex[kNumAsteroids+kNumShips];
435     SkRect          fNormTex[kNumAsteroids+kNumShips];
436     SkRect          fBounds;
437     bool            fUseColors;
438     SkVector3       fLightDir;
439     sk_sp<SkLights> fLights;
440 
441     typedef SkDrawable INHERITED;
442 };
443 
444 class DrawLitAtlasView : public Sample {
445 public:
DrawLitAtlasView()446     DrawLitAtlasView() : fDrawable(new DrawLitAtlasDrawable(SkRect::MakeWH(640, 480))) {}
447 
448 protected:
name()449     SkString name() override { return SkString("DrawLitAtlas"); }
450 
onChar(SkUnichar uni)451     bool onChar(SkUnichar uni) override {
452             switch (uni) {
453                 case 'C':
454                     fDrawable->toggleUseColors();
455                     return true;
456                 case 'j':
457                     fDrawable->left();
458                     return true;
459                 case 'k':
460                     fDrawable->thrust();
461                     return true;
462                 case 'l':
463                     fDrawable->right();
464                     return true;
465                 case 'o':
466                     fDrawable->rotateLight();
467                     return true;
468                 default:
469                     break;
470             }
471             return false;
472     }
473 
onDrawContent(SkCanvas * canvas)474     void onDrawContent(SkCanvas* canvas) override {
475         canvas->drawDrawable(fDrawable.get());
476     }
477 
onAnimate(double nanos)478     bool onAnimate(double nanos) override { return true; }
479 
480 private:
481     sk_sp<DrawLitAtlasDrawable> fDrawable;
482 
483     typedef Sample INHERITED;
484 };
485 
486 //////////////////////////////////////////////////////////////////////////////
487 
488 DEF_SAMPLE( return new DrawLitAtlasView(); )
489