• 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 "SampleCode.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 #include "SkView.h"
19 
20 #include "sk_tool_utils.h"
21 
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         if (newVel.lengthSqd() > kMaxShipSpeed*kMaxShipSpeed) {
76             newVel.setLength(SkIntToScalar(kMaxShipSpeed));
77         }
78 
79         fShip.setVelocity(newVel);
80     }
81 
82 protected:
onDraw(SkCanvas * canvas)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
103 
104         this->drawLightDir(canvas, fBounds.centerX(), fBounds.centerY());
105 #endif
106 
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 
133             sk_sp<SkShader> normalMap = SkShader::MakeBitmapShader(fAtlas, SkShader::kClamp_TileMode,
134                     SkShader::kClamp_TileMode, &normalMat);
135             sk_sp<SkNormalSource> normalSource = SkNormalSource::MakeFromNormalMap(
136                     std::move(normalMap), m);
137             sk_sp<SkShader> diffuseShader = SkShader::MakeBitmapShader(fAtlas,
138                     SkShader::kClamp_TileMode, SkShader::kClamp_TileMode, &diffMat);
139             paint.setShader(SkLightingShader::Make(std::move(diffuseShader),
140                     std::move(normalSource), fLights));
141 
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 
onGetBounds()165     SkRect onGetBounds() override {
166         return fBounds;
167     }
168 
169 private:
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 
updateLights()182     void updateLights() {
183         SkLights::Builder builder;
184 
185         builder.add(SkLights::Light::MakeDirectional(
186                 SkColor3f::Make(1.0f, 1.0f, 1.0f), fLightDir));
187         builder.setAmbientLightColor(SkColor3f::Make(0.2f, 0.2f, 0.2f));
188 
189         fLights = builder.finish();
190     }
191 
192 #ifdef SK_DEBUG
193     // Draw a vector to the light
drawLightDir(SkCanvas * canvas,SkScalar centerX,SkScalar centerY)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 
213     // 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
MakeAtlas()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:
initAsteroid(SkRandom * rand,const SkRect & bounds,SkRect * diffTex,SkRect * normTex)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 
initShip(const SkRect & bounds,SkRect * diffTex,SkRect * normTex)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 
advance(const SkRect & bounds)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 
pos() const386         const SkPoint& pos() const { return fPosition; }
387 
rot() const388         SkScalar rot() const { return fRot; }
setRot(SkScalar rot)389         void setRot(SkScalar rot) { fRot = rot; }
390 
velocity() const391         const SkPoint& velocity() const { return fVelocity; }
setVelocity(const SkPoint & velocity)392         void setVelocity(const SkPoint& velocity) { fVelocity = velocity; }
393 
asRSXform() const394         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 
418 private:
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;
446     SkVector3       fLightDir;
447     sk_sp<SkLights> fLights;
448 
449     typedef SkDrawable INHERITED;
450 };
451 
452 class DrawLitAtlasView : public SampleView {
453 public:
DrawLitAtlasView()454     DrawLitAtlasView()
455         : fDrawable(new DrawLitAtlasDrawable(SkRect::MakeWH(640, 480))) {
456     }
457 
458 protected:
onQuery(SkEvent * evt)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;
483                 case 'o':
484                     fDrawable->rotateLight();
485                     this->inval(NULL);
486                     return true;
487                 default:
488                     break;
489             }
490         }
491         return this->INHERITED::onQuery(evt);
492     }
493 
onDrawContent(SkCanvas * canvas)494     void onDrawContent(SkCanvas* canvas) override {
495         canvas->drawDrawable(fDrawable.get());
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 
508 private:
509     sk_sp<DrawLitAtlasDrawable> fDrawable;
510 
511     typedef SampleView INHERITED;
512 };
513 
514 //////////////////////////////////////////////////////////////////////////////
515 
MyFactory()516 static SkView* MyFactory() { return new DrawLitAtlasView; }
517 static SkViewRegister reg(MyFactory);
518