• 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 "Sample.h"
9 #include "SkAnimTimer.h"
10 #include "SkBitmapProcShader.h"
11 #include "SkCanvas.h"
12 #include "SkDrawable.h"
13 #include "SkLightingShader.h"
14 #include "SkLights.h"
15 #include "SkNormalSource.h"
16 #include "SkRandom.h"
17 #include "SkRSXform.h"
18 
19 #include "sk_tool_utils.h"
20 
21 // A crude normal mapped asteroids-like sample
22 class DrawLitAtlasDrawable : public SkDrawable {
23 public:
DrawLitAtlasDrawable(const SkRect & r)24     DrawLitAtlasDrawable(const SkRect& r)
25             : fBounds(r)
26             , fUseColors(false)
27             , fLightDir(SkVector3::Make(1.0f, 0.0f, 0.0f)) {
28         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 
37         this->updateLights();
38     }
39 
toggleUseColors()40     void toggleUseColors() {
41         fUseColors = !fUseColors;
42     }
43 
rotateLight()44     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 
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 c;
69         SkScalar s = SkScalarSinCos(fShip.rot(), &c);
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 = SkShader::MakeBitmapShader(fAtlas, SkShader::kClamp_TileMode,
135                     SkShader::kClamp_TileMode, &normalMat);
136             sk_sp<SkNormalSource> normalSource = SkNormalSource::MakeFromNormalMap(
137                     std::move(normalMap), m);
138             sk_sp<SkShader> diffuseShader = SkShader::MakeBitmapShader(fAtlas,
139                     SkShader::kClamp_TileMode, SkShader::kClamp_TileMode, &diffMat);
140             paint.setShader(SkLightingShader::Make(std::move(diffuseShader),
141                     std::move(normalSource), fLights));
142 
143             canvas->save();
144                 canvas->setMatrix(m);
145                 canvas->drawRect(r, paint);
146             canvas->restore();
147         }
148 #endif
149 
150 #ifdef SK_DEBUG
151         {
152             SkPaint paint;
153             paint.setColor(SK_ColorRED);
154 
155             for (int i = 0; i < kNumAsteroids; ++i) {
156                 canvas->drawCircle(fAsteroids[i].pos().x(), fAsteroids[i].pos().y(), 2, paint);
157             }
158             canvas->drawCircle(fShip.pos().x(), fShip.pos().y(), 2, paint);
159 
160             paint.setStyle(SkPaint::kStroke_Style);
161             canvas->drawRect(this->getBounds(), paint);
162         }
163 #endif
164     }
165 
onGetBounds()166     SkRect onGetBounds() override {
167         return fBounds;
168     }
169 
170 private:
171 
172     enum ObjType {
173         kBigAsteroid_ObjType = 0,
174         kMedAsteroid_ObjType,
175         kSmAsteroid_ObjType,
176         kShip_ObjType,
177 
178         kLast_ObjType = kShip_ObjType
179     };
180 
181     static const int kObjTypeCount = kLast_ObjType + 1;
182 
updateLights()183     void updateLights() {
184         SkLights::Builder builder;
185 
186         builder.add(SkLights::Light::MakeDirectional(
187                 SkColor3f::Make(1.0f, 1.0f, 1.0f), fLightDir));
188         builder.setAmbientLightColor(SkColor3f::Make(0.2f, 0.2f, 0.2f));
189 
190         fLights = builder.finish();
191     }
192 
193 #ifdef SK_DEBUG
194     // Draw a vector to the light
drawLightDir(SkCanvas * canvas,SkScalar centerX,SkScalar centerY)195     void drawLightDir(SkCanvas* canvas, SkScalar centerX, SkScalar centerY) {
196         static const int kBgLen = 30;
197         static const int kSmLen = 5;
198 
199         // TODO: change the lighting coordinate system to be right handed
200         SkPoint p1 = SkPoint::Make(centerX + kBgLen * fLightDir.fX,
201                                    centerY - kBgLen * fLightDir.fY);
202         SkPoint p2 = SkPoint::Make(centerX + (kBgLen-kSmLen) * fLightDir.fX,
203                                    centerY - (kBgLen-kSmLen) * fLightDir.fY);
204 
205         SkPaint p;
206         canvas->drawLine(centerX, centerY, p1.fX, p1.fY, p);
207         canvas->drawLine(p1.fX, p1.fY,
208                          p2.fX - kSmLen * fLightDir.fY, p2.fY - kSmLen * fLightDir.fX, p);
209         canvas->drawLine(p1.fX, p1.fY,
210                          p2.fX + kSmLen * fLightDir.fY, p2.fY + kSmLen * fLightDir.fX, p);
211     }
212 #endif
213 
214     // Create the mixed diffuse & normal atlas
215     //
216     //    big color circle  |  big normal hemi
217     //    ------------------------------------
218     //    med color circle  |  med normal pyra
219     //    ------------------------------------
220     //    sm color circle   |   sm normal hemi
221     //    ------------------------------------
222     //    big ship          | big tetra normal
MakeAtlas()223     static SkBitmap MakeAtlas() {
224 
225         SkBitmap atlas;
226         atlas.allocN32Pixels(kAtlasWidth, kAtlasHeight);
227 
228         for (int y = 0; y < kAtlasHeight; ++y) {
229             int x = 0;
230             for ( ; x < kBigSize+kPad; ++x) {
231                 *atlas.getAddr32(x, y) = SK_ColorTRANSPARENT;
232             }
233             for ( ; x < kAtlasWidth; ++x) {
234                 *atlas.getAddr32(x, y) = SkPackARGB32(0xFF, 0x88, 0x88, 0xFF);
235             }
236         }
237 
238         // big asteroid
239         {
240             SkPoint bigCenter = SkPoint::Make(kDiffXOff + kBigSize/2.0f, kBigYOff + kBigSize/2.0f);
241 
242             for (int y = kBigYOff; y < kBigYOff+kBigSize; ++y) {
243                 for (int x = kDiffXOff; x < kDiffXOff+kBigSize; ++x) {
244                     SkScalar distSq = (x - bigCenter.fX) * (x - bigCenter.fX) +
245                                       (y - bigCenter.fY) * (y - bigCenter.fY);
246                     if (distSq > kBigSize*kBigSize/4.0f) {
247                         *atlas.getAddr32(x, y) = SkPreMultiplyARGB(0, 0, 0, 0);
248                     } else {
249                         *atlas.getAddr32(x, y) = SkPackARGB32(0xFF, 0xFF, 0, 0);
250                     }
251                 }
252             }
253 
254             sk_tool_utils::create_hemi_normal_map(&atlas,
255                                                   SkIRect::MakeXYWH(kNormXOff, kBigYOff,
256                                                                     kBigSize, kBigSize));
257         }
258 
259         // medium asteroid
260         {
261             for (int y = kMedYOff; y < kMedYOff+kMedSize; ++y) {
262                 for (int x = kDiffXOff; x < kDiffXOff+kMedSize; ++x) {
263                     *atlas.getAddr32(x, y) = SkPackARGB32(0xFF, 0, 0xFF, 0);
264                 }
265             }
266 
267             sk_tool_utils::create_frustum_normal_map(&atlas,
268                                                      SkIRect::MakeXYWH(kNormXOff, kMedYOff,
269                                                                        kMedSize, kMedSize));
270         }
271 
272         // small asteroid
273         {
274             SkPoint smCenter = SkPoint::Make(kDiffXOff + kSmSize/2.0f, kSmYOff + kSmSize/2.0f);
275 
276             for (int y = kSmYOff; y < kSmYOff+kSmSize; ++y) {
277                 for (int x = kDiffXOff; x < kDiffXOff+kSmSize; ++x) {
278                     SkScalar distSq = (x - smCenter.fX) * (x - smCenter.fX) +
279                                       (y - smCenter.fY) * (y - smCenter.fY);
280                     if (distSq > kSmSize*kSmSize/4.0f) {
281                         *atlas.getAddr32(x, y) = SkPreMultiplyARGB(0, 0, 0, 0);
282                     } else {
283                         *atlas.getAddr32(x, y) = SkPackARGB32(0xFF, 0, 0, 0xFF);
284                     }
285                 }
286             }
287 
288             sk_tool_utils::create_hemi_normal_map(&atlas,
289                                                   SkIRect::MakeXYWH(kNormXOff, kSmYOff,
290                                                                     kSmSize, kSmSize));
291         }
292 
293         // ship
294         {
295             SkScalar shipMidLine = kDiffXOff + kMedSize/2.0f;
296 
297             for (int y = kShipYOff; y < kShipYOff+kMedSize; ++y) {
298                 SkScalar scaledY = (y - kShipYOff)/(float)kMedSize; // 0..1
299 
300                 for (int x = kDiffXOff; x < kDiffXOff+kMedSize; ++x) {
301                     SkScalar scaledX;
302 
303                     if (x < shipMidLine) {
304                         scaledX = 1.0f - (x - kDiffXOff)/(kMedSize/2.0f); // 0..1
305                     } else {
306                         scaledX = (x - shipMidLine)/(kMedSize/2.0f);      // 0..1
307                     }
308 
309                     if (scaledX < scaledY) {
310                         *atlas.getAddr32(x, y) = SkPackARGB32(0xFF, 0, 0xFF, 0xFF);
311                     } else {
312                         *atlas.getAddr32(x, y) = SkPackARGB32(0, 0, 0, 0);
313                     }
314                 }
315             }
316 
317             sk_tool_utils::create_tetra_normal_map(&atlas,
318                                                    SkIRect::MakeXYWH(kNormXOff, kShipYOff,
319                                                                      kMedSize, kMedSize));
320         }
321 
322         return atlas;
323     }
324 
325     class ObjectRecord {
326     public:
initAsteroid(SkRandom * rand,const SkRect & bounds,SkRect * diffTex,SkRect * normTex)327         void initAsteroid(SkRandom *rand, const SkRect& bounds,
328                           SkRect* diffTex, SkRect* normTex) {
329             static const SkScalar gMaxSpeeds[3] = { 1, 2, 5 }; // smaller asteroids can go faster
330             static const SkScalar gYOffs[3] = { kBigYOff, kMedYOff, kSmYOff };
331             static const SkScalar gSizes[3] = { kBigSize, kMedSize, kSmSize };
332 
333             static unsigned int asteroidType = 0;
334             fObjType = static_cast<ObjType>(asteroidType++ % 3);
335 
336             fPosition.set(bounds.fLeft + rand->nextUScalar1() * bounds.width(),
337                           bounds.fTop + rand->nextUScalar1() * bounds.height());
338             fVelocity.fX = rand->nextSScalar1();
339             fVelocity.fY = sqrt(1.0f - fVelocity.fX * fVelocity.fX);
340             SkASSERT(SkScalarNearlyEqual(fVelocity.length(), 1.0f));
341             fVelocity *= gMaxSpeeds[fObjType];
342             fRot = 0;
343             fDeltaRot = rand->nextSScalar1() / 32;
344 
345             diffTex->setXYWH(SkIntToScalar(kDiffXOff), gYOffs[fObjType],
346                              gSizes[fObjType], gSizes[fObjType]);
347             normTex->setXYWH(SkIntToScalar(kNormXOff), gYOffs[fObjType],
348                              gSizes[fObjType], gSizes[fObjType]);
349         }
350 
initShip(const SkRect & bounds,SkRect * diffTex,SkRect * normTex)351         void initShip(const SkRect& bounds, SkRect* diffTex, SkRect* normTex) {
352             fObjType = kShip_ObjType;
353             fPosition.set(bounds.centerX(), bounds.centerY());
354             fVelocity = SkVector::Make(0.0f, 0.0f);
355             fRot = 0.0f;
356             fDeltaRot = 0.0f;
357 
358             diffTex->setXYWH(SkIntToScalar(kDiffXOff), SkIntToScalar(kShipYOff),
359                              SkIntToScalar(kMedSize), SkIntToScalar(kMedSize));
360             normTex->setXYWH(SkIntToScalar(kNormXOff), SkIntToScalar(kShipYOff),
361                              SkIntToScalar(kMedSize), SkIntToScalar(kMedSize));
362         }
363 
advance(const SkRect & bounds)364         void advance(const SkRect& bounds) {
365             fPosition += fVelocity;
366             if (fPosition.fX > bounds.right()) {
367                 SkASSERT(fVelocity.fX > 0);
368                 fVelocity.fX = -fVelocity.fX;
369             } else if (fPosition.fX < bounds.left()) {
370                 SkASSERT(fVelocity.fX < 0);
371                 fVelocity.fX = -fVelocity.fX;
372             }
373             if (fPosition.fY > bounds.bottom()) {
374                 if (fVelocity.fY > 0) {
375                     fVelocity.fY = -fVelocity.fY;
376                 }
377             } else if (fPosition.fY < bounds.top()) {
378                 if (fVelocity.fY < 0) {
379                     fVelocity.fY = -fVelocity.fY;
380                 }
381             }
382 
383             fRot += fDeltaRot;
384             fRot = SkScalarMod(fRot, 2 * SK_ScalarPI);
385         }
386 
pos() const387         const SkPoint& pos() const { return fPosition; }
388 
rot() const389         SkScalar rot() const { return fRot; }
setRot(SkScalar rot)390         void setRot(SkScalar rot) { fRot = rot; }
391 
velocity() const392         const SkPoint& velocity() const { return fVelocity; }
setVelocity(const SkPoint & velocity)393         void setVelocity(const SkPoint& velocity) { fVelocity = velocity; }
394 
asRSXform() const395         SkRSXform asRSXform() const {
396             static const SkScalar gHalfSizes[kObjTypeCount] = {
397                 SkScalarHalf(kBigSize),
398                 SkScalarHalf(kMedSize),
399                 SkScalarHalf(kSmSize),
400                 SkScalarHalf(kMedSize),
401             };
402 
403             return SkRSXform::MakeFromRadians(1.0f, fRot, fPosition.x(), fPosition.y(),
404                                               gHalfSizes[fObjType],
405                                               gHalfSizes[fObjType]);
406         }
407 
408     private:
409         ObjType     fObjType;
410         SkPoint     fPosition;
411         SkVector    fVelocity;
412         SkScalar    fRot;        // In radians.
413         SkScalar    fDeltaRot;   // In radiands. Not used by ship.
414     };
415 
416 private:
417     static const int kNumLights = 2;
418     static const int kNumAsteroids = 6;
419     static const int kNumShips = 1;
420 
421     static const int kBigSize = 128;
422     static const int kMedSize = 64;
423     static const int kSmSize = 32;
424     static const int kPad = 1;
425     static const int kAtlasWidth = kBigSize + kBigSize + 2 * kPad; // 2 pads in the middle
426     static const int kAtlasHeight = kBigSize + kMedSize + kSmSize + kMedSize + 3 * kPad;
427 
428     static const int kDiffXOff = 0;
429     static const int kNormXOff = kBigSize + 2 * kPad;
430 
431     static const int kBigYOff = 0;
432     static const int kMedYOff = kBigSize + kPad;
433     static const int kSmYOff = kMedYOff + kMedSize + kPad;
434     static const int kShipYOff = kSmYOff + kSmSize + kPad;
435     static const int kMaxShipSpeed = 5;
436 
437     SkBitmap        fAtlas;
438     ObjectRecord    fAsteroids[kNumAsteroids];
439     ObjectRecord    fShip;
440     SkRect          fDiffTex[kNumAsteroids+kNumShips];
441     SkRect          fNormTex[kNumAsteroids+kNumShips];
442     SkRect          fBounds;
443     bool            fUseColors;
444     SkVector3       fLightDir;
445     sk_sp<SkLights> fLights;
446 
447     typedef SkDrawable INHERITED;
448 };
449 
450 class DrawLitAtlasView : public Sample {
451 public:
DrawLitAtlasView()452     DrawLitAtlasView() : fDrawable(new DrawLitAtlasDrawable(SkRect::MakeWH(640, 480))) {}
453 
454 protected:
onQuery(Sample::Event * evt)455     bool onQuery(Sample::Event* evt) override {
456         if (Sample::TitleQ(*evt)) {
457             Sample::TitleR(evt, "DrawLitAtlas");
458             return true;
459         }
460         SkUnichar uni;
461         if (Sample::CharQ(*evt, &uni)) {
462             switch (uni) {
463                 case 'C':
464                     fDrawable->toggleUseColors();
465                     return true;
466                 case 'j':
467                     fDrawable->left();
468                     return true;
469                 case 'k':
470                     fDrawable->thrust();
471                     return true;
472                 case 'l':
473                     fDrawable->right();
474                     return true;
475                 case 'o':
476                     fDrawable->rotateLight();
477                     return true;
478                 default:
479                     break;
480             }
481         }
482         return this->INHERITED::onQuery(evt);
483     }
484 
onDrawContent(SkCanvas * canvas)485     void onDrawContent(SkCanvas* canvas) override {
486         canvas->drawDrawable(fDrawable.get());
487     }
488 
onAnimate(const SkAnimTimer & timer)489     bool onAnimate(const SkAnimTimer& timer) override {
490         return true;
491     }
492 
493 private:
494     sk_sp<DrawLitAtlasDrawable> fDrawable;
495 
496     typedef Sample INHERITED;
497 };
498 
499 //////////////////////////////////////////////////////////////////////////////
500 
501 DEF_SAMPLE( return new DrawLitAtlasView(); )
502