SampleLitAtlas.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488
  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. #include "include/core/SkCanvas.h"
  8. #include "include/core/SkDrawable.h"
  9. #include "include/core/SkRSXform.h"
  10. #include "include/utils/SkRandom.h"
  11. #include "samplecode/Sample.h"
  12. #include "src/core/SkNormalSource.h"
  13. #include "src/shaders/SkBitmapProcShader.h"
  14. #include "src/shaders/SkLightingShader.h"
  15. #include "src/shaders/SkLights.h"
  16. #include "tools/ToolUtils.h"
  17. // A crude normal mapped asteroids-like sample
  18. class DrawLitAtlasDrawable : public SkDrawable {
  19. public:
  20. DrawLitAtlasDrawable(const SkRect& r)
  21. : fBounds(r)
  22. , fUseColors(false)
  23. , fLightDir(SkVector3::Make(1.0f, 0.0f, 0.0f)) {
  24. fAtlas = MakeAtlas();
  25. SkRandom rand;
  26. for (int i = 0; i < kNumAsteroids; ++i) {
  27. fAsteroids[i].initAsteroid(&rand, fBounds, &fDiffTex[i], &fNormTex[i]);
  28. }
  29. fShip.initShip(fBounds, &fDiffTex[kNumAsteroids], &fNormTex[kNumAsteroids]);
  30. this->updateLights();
  31. }
  32. void toggleUseColors() {
  33. fUseColors = !fUseColors;
  34. }
  35. void rotateLight() {
  36. SkScalar r = SK_ScalarPI / 6.0f,
  37. s = SkScalarSin(r),
  38. c = SkScalarCos(r);
  39. SkScalar newX = c * fLightDir.fX - s * fLightDir.fY;
  40. SkScalar newY = s * fLightDir.fX + c * fLightDir.fY;
  41. fLightDir.set(newX, newY, 0.0f);
  42. this->updateLights();
  43. }
  44. void left() {
  45. SkScalar newRot = SkScalarMod(fShip.rot() + (2*SK_ScalarPI - SK_ScalarPI/32.0f),
  46. 2 * SK_ScalarPI);
  47. fShip.setRot(newRot);
  48. }
  49. void right() {
  50. SkScalar newRot = SkScalarMod(fShip.rot() + SK_ScalarPI/32.0f, 2 * SK_ScalarPI);
  51. fShip.setRot(newRot);
  52. }
  53. void thrust() {
  54. SkScalar s = SkScalarSin(fShip.rot()),
  55. c = SkScalarCos(fShip.rot());
  56. SkVector newVel = fShip.velocity();
  57. newVel.fX += s;
  58. newVel.fY += -c;
  59. SkScalar len = newVel.length();
  60. if (len > kMaxShipSpeed) {
  61. newVel.setLength(SkIntToScalar(kMaxShipSpeed));
  62. }
  63. fShip.setVelocity(newVel);
  64. }
  65. protected:
  66. void onDraw(SkCanvas* canvas) override {
  67. SkRSXform xforms[kNumAsteroids+kNumShips];
  68. SkColor colors[kNumAsteroids+kNumShips];
  69. for (int i = 0; i < kNumAsteroids; ++i) {
  70. fAsteroids[i].advance(fBounds);
  71. xforms[i] = fAsteroids[i].asRSXform();
  72. if (fUseColors) {
  73. colors[i] = SkColorSetARGB(0xFF, 0xFF, 0xFF, 0xFF);
  74. }
  75. }
  76. fShip.advance(fBounds);
  77. xforms[kNumAsteroids] = fShip.asRSXform();
  78. if (fUseColors) {
  79. colors[kNumAsteroids] = SkColorSetARGB(0xFF, 0xFF, 0xFF, 0xFF);
  80. }
  81. #ifdef SK_DEBUG
  82. canvas->drawBitmap(fAtlas, 0, 0); // just to see the atlas
  83. this->drawLightDir(canvas, fBounds.centerX(), fBounds.centerY());
  84. #endif
  85. #if 0
  86. // TODO: revitalize when drawLitAtlas API lands
  87. SkPaint paint;
  88. paint.setFilterQuality(kLow_SkFilterQuality);
  89. const SkRect cull = this->getBounds();
  90. const SkColor* colorsPtr = fUseColors ? colors : NULL;
  91. canvas->drawLitAtlas(fAtlas, xforms, fDiffTex, fNormTex, colorsPtr, kNumAsteroids+1,
  92. SkXfermode::kModulate_Mode, &cull, &paint, fLights);
  93. #else
  94. SkMatrix diffMat, normalMat;
  95. for (int i = 0; i < kNumAsteroids+1; ++i) {
  96. colors[i] = colors[i] & 0xFF000000; // to silence compilers
  97. SkPaint paint;
  98. SkRect r = fDiffTex[i];
  99. r.offsetTo(0, 0);
  100. diffMat.setRectToRect(fDiffTex[i], r, SkMatrix::kFill_ScaleToFit);
  101. normalMat.setRectToRect(fNormTex[i], r, SkMatrix::kFill_ScaleToFit);
  102. SkMatrix m;
  103. m.setRSXform(xforms[i]);
  104. sk_sp<SkShader> normalMap = fAtlas.makeShader(&normalMat);
  105. sk_sp<SkNormalSource> normalSource = SkNormalSource::MakeFromNormalMap(
  106. std::move(normalMap), m);
  107. sk_sp<SkShader> diffuseShader = fAtlas.makeShader(&diffMat);
  108. paint.setShader(SkLightingShader::Make(std::move(diffuseShader),
  109. std::move(normalSource), fLights));
  110. canvas->save();
  111. canvas->setMatrix(m);
  112. canvas->drawRect(r, paint);
  113. canvas->restore();
  114. }
  115. #endif
  116. #ifdef SK_DEBUG
  117. {
  118. SkPaint paint;
  119. paint.setColor(SK_ColorRED);
  120. for (int i = 0; i < kNumAsteroids; ++i) {
  121. canvas->drawCircle(fAsteroids[i].pos().x(), fAsteroids[i].pos().y(), 2, paint);
  122. }
  123. canvas->drawCircle(fShip.pos().x(), fShip.pos().y(), 2, paint);
  124. paint.setStyle(SkPaint::kStroke_Style);
  125. canvas->drawRect(this->getBounds(), paint);
  126. }
  127. #endif
  128. }
  129. SkRect onGetBounds() override {
  130. return fBounds;
  131. }
  132. private:
  133. enum ObjType {
  134. kBigAsteroid_ObjType = 0,
  135. kMedAsteroid_ObjType,
  136. kSmAsteroid_ObjType,
  137. kShip_ObjType,
  138. kLast_ObjType = kShip_ObjType
  139. };
  140. static const int kObjTypeCount = kLast_ObjType + 1;
  141. void updateLights() {
  142. SkLights::Builder builder;
  143. builder.add(SkLights::Light::MakeDirectional(
  144. SkColor3f::Make(1.0f, 1.0f, 1.0f), fLightDir));
  145. builder.setAmbientLightColor(SkColor3f::Make(0.2f, 0.2f, 0.2f));
  146. fLights = builder.finish();
  147. }
  148. #ifdef SK_DEBUG
  149. // Draw a vector to the light
  150. void drawLightDir(SkCanvas* canvas, SkScalar centerX, SkScalar centerY) {
  151. static const int kBgLen = 30;
  152. static const int kSmLen = 5;
  153. // TODO: change the lighting coordinate system to be right handed
  154. SkPoint p1 = SkPoint::Make(centerX + kBgLen * fLightDir.fX,
  155. centerY - kBgLen * fLightDir.fY);
  156. SkPoint p2 = SkPoint::Make(centerX + (kBgLen-kSmLen) * fLightDir.fX,
  157. centerY - (kBgLen-kSmLen) * fLightDir.fY);
  158. SkPaint p;
  159. canvas->drawLine(centerX, centerY, p1.fX, p1.fY, p);
  160. canvas->drawLine(p1.fX, p1.fY,
  161. p2.fX - kSmLen * fLightDir.fY, p2.fY - kSmLen * fLightDir.fX, p);
  162. canvas->drawLine(p1.fX, p1.fY,
  163. p2.fX + kSmLen * fLightDir.fY, p2.fY + kSmLen * fLightDir.fX, p);
  164. }
  165. #endif
  166. // Create the mixed diffuse & normal atlas
  167. //
  168. // big color circle | big normal hemi
  169. // ------------------------------------
  170. // med color circle | med normal pyra
  171. // ------------------------------------
  172. // sm color circle | sm normal hemi
  173. // ------------------------------------
  174. // big ship | big tetra normal
  175. static SkBitmap MakeAtlas() {
  176. SkBitmap atlas;
  177. atlas.allocN32Pixels(kAtlasWidth, kAtlasHeight);
  178. for (int y = 0; y < kAtlasHeight; ++y) {
  179. int x = 0;
  180. for ( ; x < kBigSize+kPad; ++x) {
  181. *atlas.getAddr32(x, y) = SK_ColorTRANSPARENT;
  182. }
  183. for ( ; x < kAtlasWidth; ++x) {
  184. *atlas.getAddr32(x, y) = SkPackARGB32(0xFF, 0x88, 0x88, 0xFF);
  185. }
  186. }
  187. // big asteroid
  188. {
  189. SkPoint bigCenter = SkPoint::Make(kDiffXOff + kBigSize/2.0f, kBigYOff + kBigSize/2.0f);
  190. for (int y = kBigYOff; y < kBigYOff+kBigSize; ++y) {
  191. for (int x = kDiffXOff; x < kDiffXOff+kBigSize; ++x) {
  192. SkScalar distSq = (x - bigCenter.fX) * (x - bigCenter.fX) +
  193. (y - bigCenter.fY) * (y - bigCenter.fY);
  194. if (distSq > kBigSize*kBigSize/4.0f) {
  195. *atlas.getAddr32(x, y) = SkPreMultiplyARGB(0, 0, 0, 0);
  196. } else {
  197. *atlas.getAddr32(x, y) = SkPackARGB32(0xFF, 0xFF, 0, 0);
  198. }
  199. }
  200. }
  201. ToolUtils::create_hemi_normal_map(
  202. &atlas, SkIRect::MakeXYWH(kNormXOff, kBigYOff, kBigSize, kBigSize));
  203. }
  204. // medium asteroid
  205. {
  206. for (int y = kMedYOff; y < kMedYOff+kMedSize; ++y) {
  207. for (int x = kDiffXOff; x < kDiffXOff+kMedSize; ++x) {
  208. *atlas.getAddr32(x, y) = SkPackARGB32(0xFF, 0, 0xFF, 0);
  209. }
  210. }
  211. ToolUtils::create_frustum_normal_map(
  212. &atlas, SkIRect::MakeXYWH(kNormXOff, kMedYOff, kMedSize, kMedSize));
  213. }
  214. // small asteroid
  215. {
  216. SkPoint smCenter = SkPoint::Make(kDiffXOff + kSmSize/2.0f, kSmYOff + kSmSize/2.0f);
  217. for (int y = kSmYOff; y < kSmYOff+kSmSize; ++y) {
  218. for (int x = kDiffXOff; x < kDiffXOff+kSmSize; ++x) {
  219. SkScalar distSq = (x - smCenter.fX) * (x - smCenter.fX) +
  220. (y - smCenter.fY) * (y - smCenter.fY);
  221. if (distSq > kSmSize*kSmSize/4.0f) {
  222. *atlas.getAddr32(x, y) = SkPreMultiplyARGB(0, 0, 0, 0);
  223. } else {
  224. *atlas.getAddr32(x, y) = SkPackARGB32(0xFF, 0, 0, 0xFF);
  225. }
  226. }
  227. }
  228. ToolUtils::create_hemi_normal_map(
  229. &atlas, SkIRect::MakeXYWH(kNormXOff, kSmYOff, kSmSize, kSmSize));
  230. }
  231. // ship
  232. {
  233. SkScalar shipMidLine = kDiffXOff + kMedSize/2.0f;
  234. for (int y = kShipYOff; y < kShipYOff+kMedSize; ++y) {
  235. SkScalar scaledY = (y - kShipYOff)/(float)kMedSize; // 0..1
  236. for (int x = kDiffXOff; x < kDiffXOff+kMedSize; ++x) {
  237. SkScalar scaledX;
  238. if (x < shipMidLine) {
  239. scaledX = 1.0f - (x - kDiffXOff)/(kMedSize/2.0f); // 0..1
  240. } else {
  241. scaledX = (x - shipMidLine)/(kMedSize/2.0f); // 0..1
  242. }
  243. if (scaledX < scaledY) {
  244. *atlas.getAddr32(x, y) = SkPackARGB32(0xFF, 0, 0xFF, 0xFF);
  245. } else {
  246. *atlas.getAddr32(x, y) = SkPackARGB32(0, 0, 0, 0);
  247. }
  248. }
  249. }
  250. ToolUtils::create_tetra_normal_map(
  251. &atlas, SkIRect::MakeXYWH(kNormXOff, kShipYOff, kMedSize, kMedSize));
  252. }
  253. return atlas;
  254. }
  255. class ObjectRecord {
  256. public:
  257. void initAsteroid(SkRandom *rand, const SkRect& bounds,
  258. SkRect* diffTex, SkRect* normTex) {
  259. static const SkScalar gMaxSpeeds[3] = { 1, 2, 5 }; // smaller asteroids can go faster
  260. static const SkScalar gYOffs[3] = { kBigYOff, kMedYOff, kSmYOff };
  261. static const SkScalar gSizes[3] = { kBigSize, kMedSize, kSmSize };
  262. static unsigned int asteroidType = 0;
  263. fObjType = static_cast<ObjType>(asteroidType++ % 3);
  264. fPosition.set(bounds.fLeft + rand->nextUScalar1() * bounds.width(),
  265. bounds.fTop + rand->nextUScalar1() * bounds.height());
  266. fVelocity.fX = rand->nextSScalar1();
  267. fVelocity.fY = sqrt(1.0f - fVelocity.fX * fVelocity.fX);
  268. SkASSERT(SkScalarNearlyEqual(fVelocity.length(), 1.0f));
  269. fVelocity *= gMaxSpeeds[fObjType];
  270. fRot = 0;
  271. fDeltaRot = rand->nextSScalar1() / 32;
  272. diffTex->setXYWH(SkIntToScalar(kDiffXOff), gYOffs[fObjType],
  273. gSizes[fObjType], gSizes[fObjType]);
  274. normTex->setXYWH(SkIntToScalar(kNormXOff), gYOffs[fObjType],
  275. gSizes[fObjType], gSizes[fObjType]);
  276. }
  277. void initShip(const SkRect& bounds, SkRect* diffTex, SkRect* normTex) {
  278. fObjType = kShip_ObjType;
  279. fPosition.set(bounds.centerX(), bounds.centerY());
  280. fVelocity = SkVector::Make(0.0f, 0.0f);
  281. fRot = 0.0f;
  282. fDeltaRot = 0.0f;
  283. diffTex->setXYWH(SkIntToScalar(kDiffXOff), SkIntToScalar(kShipYOff),
  284. SkIntToScalar(kMedSize), SkIntToScalar(kMedSize));
  285. normTex->setXYWH(SkIntToScalar(kNormXOff), SkIntToScalar(kShipYOff),
  286. SkIntToScalar(kMedSize), SkIntToScalar(kMedSize));
  287. }
  288. void advance(const SkRect& bounds) {
  289. fPosition += fVelocity;
  290. if (fPosition.fX > bounds.right()) {
  291. SkASSERT(fVelocity.fX > 0);
  292. fVelocity.fX = -fVelocity.fX;
  293. } else if (fPosition.fX < bounds.left()) {
  294. SkASSERT(fVelocity.fX < 0);
  295. fVelocity.fX = -fVelocity.fX;
  296. }
  297. if (fPosition.fY > bounds.bottom()) {
  298. if (fVelocity.fY > 0) {
  299. fVelocity.fY = -fVelocity.fY;
  300. }
  301. } else if (fPosition.fY < bounds.top()) {
  302. if (fVelocity.fY < 0) {
  303. fVelocity.fY = -fVelocity.fY;
  304. }
  305. }
  306. fRot += fDeltaRot;
  307. fRot = SkScalarMod(fRot, 2 * SK_ScalarPI);
  308. }
  309. const SkPoint& pos() const { return fPosition; }
  310. SkScalar rot() const { return fRot; }
  311. void setRot(SkScalar rot) { fRot = rot; }
  312. const SkPoint& velocity() const { return fVelocity; }
  313. void setVelocity(const SkPoint& velocity) { fVelocity = velocity; }
  314. SkRSXform asRSXform() const {
  315. static const SkScalar gHalfSizes[kObjTypeCount] = {
  316. SkScalarHalf(kBigSize),
  317. SkScalarHalf(kMedSize),
  318. SkScalarHalf(kSmSize),
  319. SkScalarHalf(kMedSize),
  320. };
  321. return SkRSXform::MakeFromRadians(1.0f, fRot, fPosition.x(), fPosition.y(),
  322. gHalfSizes[fObjType],
  323. gHalfSizes[fObjType]);
  324. }
  325. private:
  326. ObjType fObjType;
  327. SkPoint fPosition;
  328. SkVector fVelocity;
  329. SkScalar fRot; // In radians.
  330. SkScalar fDeltaRot; // In radiands. Not used by ship.
  331. };
  332. private:
  333. static const int kNumLights = 2;
  334. static const int kNumAsteroids = 6;
  335. static const int kNumShips = 1;
  336. static const int kBigSize = 128;
  337. static const int kMedSize = 64;
  338. static const int kSmSize = 32;
  339. static const int kPad = 1;
  340. static const int kAtlasWidth = kBigSize + kBigSize + 2 * kPad; // 2 pads in the middle
  341. static const int kAtlasHeight = kBigSize + kMedSize + kSmSize + kMedSize + 3 * kPad;
  342. static const int kDiffXOff = 0;
  343. static const int kNormXOff = kBigSize + 2 * kPad;
  344. static const int kBigYOff = 0;
  345. static const int kMedYOff = kBigSize + kPad;
  346. static const int kSmYOff = kMedYOff + kMedSize + kPad;
  347. static const int kShipYOff = kSmYOff + kSmSize + kPad;
  348. static const int kMaxShipSpeed = 5;
  349. SkBitmap fAtlas;
  350. ObjectRecord fAsteroids[kNumAsteroids];
  351. ObjectRecord fShip;
  352. SkRect fDiffTex[kNumAsteroids+kNumShips];
  353. SkRect fNormTex[kNumAsteroids+kNumShips];
  354. SkRect fBounds;
  355. bool fUseColors;
  356. SkVector3 fLightDir;
  357. sk_sp<SkLights> fLights;
  358. typedef SkDrawable INHERITED;
  359. };
  360. class DrawLitAtlasView : public Sample {
  361. public:
  362. DrawLitAtlasView() : fDrawable(new DrawLitAtlasDrawable(SkRect::MakeWH(640, 480))) {}
  363. protected:
  364. SkString name() override { return SkString("DrawLitAtlas"); }
  365. bool onChar(SkUnichar uni) override {
  366. switch (uni) {
  367. case 'C':
  368. fDrawable->toggleUseColors();
  369. return true;
  370. case 'j':
  371. fDrawable->left();
  372. return true;
  373. case 'k':
  374. fDrawable->thrust();
  375. return true;
  376. case 'l':
  377. fDrawable->right();
  378. return true;
  379. case 'o':
  380. fDrawable->rotateLight();
  381. return true;
  382. default:
  383. break;
  384. }
  385. return false;
  386. }
  387. void onDrawContent(SkCanvas* canvas) override {
  388. canvas->drawDrawable(fDrawable.get());
  389. }
  390. bool onAnimate(double nanos) override { return true; }
  391. private:
  392. sk_sp<DrawLitAtlasDrawable> fDrawable;
  393. typedef Sample INHERITED;
  394. };
  395. //////////////////////////////////////////////////////////////////////////////
  396. DEF_SAMPLE( return new DrawLitAtlasView(); )