aboutsummaryrefslogtreecommitdiffstats
path: root/samplecode/SampleLitAtlas.cpp
diff options
context:
space:
mode:
authorrobertphillips <robertphillips@google.com>2016-06-03 10:12:08 -0700
committerCommit bot <commit-bot@chromium.org>2016-06-03 10:12:08 -0700
commitadf5afa628adb62a0ad451d07ef1442381a0ee20 (patch)
treec1d9f39b45e0e98cc335b61fb58ec9e3322f5c1f /samplecode/SampleLitAtlas.cpp
parent5823846397066377b65e011a42becd2d665110ad (diff)
downloadplatform_external_skqp-adf5afa628adb62a0ad451d07ef1442381a0ee20.tar.gz
platform_external_skqp-adf5afa628adb62a0ad451d07ef1442381a0ee20.tar.bz2
platform_external_skqp-adf5afa628adb62a0ad451d07ef1442381a0ee20.zip
Add SampleApp slide with animating lightmapped objects & transparency
This is pulled out of the drawLitAtlas CL (may it someday land). It does nicely demonstrate animating normal mapped objects and normal maps combined with partially transparent diffuse textures. It is a crude Asteroids game. GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2026393005 TBR=reed@google.com Review-Url: https://codereview.chromium.org/2026393005
Diffstat (limited to 'samplecode/SampleLitAtlas.cpp')
-rw-r--r--samplecode/SampleLitAtlas.cpp467
1 files changed, 467 insertions, 0 deletions
diff --git a/samplecode/SampleLitAtlas.cpp b/samplecode/SampleLitAtlas.cpp
new file mode 100644
index 0000000000..4f989af1ae
--- /dev/null
+++ b/samplecode/SampleLitAtlas.cpp
@@ -0,0 +1,467 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SampleCode.h"
+#include "SkAnimTimer.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkDrawable.h"
+#include "SkLightingShader.h"
+#include "SkLights.h"
+#include "SkRandom.h"
+#include "SkRSXform.h"
+
+#include "sk_tool_utils.h"
+
+class DrawLitAtlasDrawable : public SkDrawable {
+public:
+ DrawLitAtlasDrawable(const SkRect& r)
+ : fBounds(r)
+ , fUseColors(false) {
+ fAtlas = MakeAtlas();
+
+ SkRandom rand;
+ for (int i = 0; i < kNumAsteroids; ++i) {
+ fAsteroids[i].initAsteroid(&rand, fBounds, &fDiffTex[i], &fNormTex[i]);
+ }
+
+ fShip.initShip(fBounds, &fDiffTex[kNumAsteroids], &fNormTex[kNumAsteroids]);
+
+ SkLights::Builder builder;
+
+ builder.add(SkLights::Light(SkColor3f::Make(1.0f, 1.0f, 1.0f),
+ SkVector3::Make(1.0f, 0.0f, 0.0f)));
+ builder.add(SkLights::Light(SkColor3f::Make(0.2f, 0.2f, 0.2f)));
+
+ fLights = builder.finish();
+ }
+
+ void toggleUseColors() {
+ fUseColors = !fUseColors;
+ }
+
+ void left() {
+ SkScalar newRot = SkScalarMod(fShip.rot() + (2*SK_ScalarPI - SK_ScalarPI/32.0f),
+ 2 * SK_ScalarPI);
+ fShip.setRot(newRot);
+ }
+
+ void right() {
+ SkScalar newRot = SkScalarMod(fShip.rot() + SK_ScalarPI/32.0f, 2 * SK_ScalarPI);
+ fShip.setRot(newRot);
+ }
+
+ void thrust() {
+ SkScalar c;
+ SkScalar s = SkScalarSinCos(fShip.rot(), &c);
+
+ SkVector newVel = fShip.velocity();
+ newVel.fX += s;
+ newVel.fY += -c;
+
+ if (newVel.lengthSqd() > kMaxShipSpeed*kMaxShipSpeed) {
+ newVel.setLength(SkIntToScalar(kMaxShipSpeed));
+ }
+
+ fShip.setVelocity(newVel);
+ }
+
+protected:
+ void onDraw(SkCanvas* canvas) override {
+ SkRSXform xforms[kNumAsteroids+kNumShips];
+ SkColor colors[kNumAsteroids+kNumShips];
+
+ for (int i = 0; i < kNumAsteroids; ++i) {
+ fAsteroids[i].advance(fBounds);
+ xforms[i] = fAsteroids[i].asRSXform();
+ if (fUseColors) {
+ colors[i] = SkColorSetARGB(0xFF, 0xFF, 0xFF, 0xFF);
+ }
+ }
+
+ fShip.advance(fBounds);
+ xforms[kNumAsteroids] = fShip.asRSXform();
+ if (fUseColors) {
+ colors[kNumAsteroids] = SkColorSetARGB(0xFF, 0xFF, 0xFF, 0xFF);
+ }
+
+#ifdef SK_DEBUG
+ canvas->drawBitmap(fAtlas, 0, 0); // just to see the atlas
+#endif
+
+#if 0
+ // TODO: revitalize when drawLitAtlas API lands
+ SkPaint paint;
+ paint.setFilterQuality(kLow_SkFilterQuality);
+
+ const SkRect cull = this->getBounds();
+ const SkColor* colorsPtr = fUseColors ? colors : NULL;
+
+ canvas->drawLitAtlas(fAtlas, xforms, fDiffTex, fNormTex, colorsPtr, kNumAsteroids+1,
+ SkXfermode::kModulate_Mode, &cull, &paint, fLights);
+#else
+ SkMatrix diffMat, normalMat;
+
+ for (int i = 0; i < kNumAsteroids+1; ++i) {
+ colors[i] = colors[i] & 0xFF000000; // to silence compilers
+ SkPaint paint;
+
+ SkRect r = fDiffTex[i];
+ r.offsetTo(0, 0);
+
+ diffMat.setRectToRect(fDiffTex[i], r, SkMatrix::kFill_ScaleToFit);
+ normalMat.setRectToRect(fNormTex[i], r, SkMatrix::kFill_ScaleToFit);
+
+ SkMatrix m;
+ m.setRSXform(xforms[i]);
+
+ // TODO: correctly pull out the pure rotation
+ SkVector invNormRotation = { m[SkMatrix::kMScaleX], m[SkMatrix::kMSkewY] };
+
+ paint.setShader(SkLightingShader::Make(fAtlas, fAtlas, fLights,
+ invNormRotation, &diffMat, &normalMat));
+
+ canvas->save();
+ canvas->setMatrix(m);
+ canvas->drawRect(r, paint);
+ canvas->restore();
+ }
+#endif
+
+#ifdef SK_DEBUG
+ {
+ SkPaint paint;
+ paint.setColor(SK_ColorRED);
+
+ for (int i = 0; i < kNumAsteroids; ++i) {
+ canvas->drawCircle(fAsteroids[i].pos().x(), fAsteroids[i].pos().y(), 2, paint);
+ }
+ canvas->drawCircle(fShip.pos().x(), fShip.pos().y(), 2, paint);
+
+ paint.setStyle(SkPaint::kStroke_Style);
+ canvas->drawRect(this->getBounds(), paint);
+ }
+#endif
+ }
+
+ SkRect onGetBounds() override {
+ return fBounds;
+ }
+
+private:
+
+ enum ObjType {
+ kBigAsteroid_ObjType = 0,
+ kMedAsteroid_ObjType,
+ kSmAsteroid_ObjType,
+ kShip_ObjType,
+
+ kLast_ObjType = kShip_ObjType
+ };
+
+ static const int kObjTypeCount = kLast_ObjType + 1;
+
+ // Create the mixed diffuse & normal atlas
+ //
+ // big color circle | big normal hemi
+ // ------------------------------------
+ // med color circle | med normal pyra
+ // ------------------------------------
+ // sm color circle | sm normal hemi
+ // ------------------------------------
+ // big ship | big tetra normal
+ static SkBitmap MakeAtlas() {
+
+ SkBitmap atlas;
+ atlas.allocN32Pixels(kAtlasWidth, kAtlasHeight);
+
+ for (int y = 0; y < kAtlasHeight; ++y) {
+ int x = 0;
+ for ( ; x < kBigSize+kPad; ++x) {
+ *atlas.getAddr32(x, y) = SK_ColorTRANSPARENT;
+ }
+ for ( ; x < kAtlasWidth; ++x) {
+ *atlas.getAddr32(x, y) = SkPackARGB32(0xFF, 0x88, 0x88, 0xFF);
+ }
+ }
+
+ // big asteroid
+ {
+ SkPoint bigCenter = SkPoint::Make(kDiffXOff + kBigSize/2.0f, kBigYOff + kBigSize/2.0f);
+
+ for (int y = kBigYOff; y < kBigYOff+kBigSize; ++y) {
+ for (int x = kDiffXOff; x < kDiffXOff+kBigSize; ++x) {
+ SkScalar distSq = (x - bigCenter.fX) * (x - bigCenter.fX) +
+ (y - bigCenter.fY) * (y - bigCenter.fY);
+ if (distSq > kBigSize*kBigSize/4.0f) {
+ *atlas.getAddr32(x, y) = SkPreMultiplyARGB(0, 0, 0, 0);
+ } else {
+ *atlas.getAddr32(x, y) = SkPackARGB32(0xFF, 0xFF, 0, 0);
+ }
+ }
+ }
+
+ sk_tool_utils::create_hemi_normal_map(&atlas,
+ SkIRect::MakeXYWH(kNormXOff, kBigYOff,
+ kBigSize, kBigSize));
+ }
+
+ // medium asteroid
+ {
+ for (int y = kMedYOff; y < kMedYOff+kMedSize; ++y) {
+ for (int x = kDiffXOff; x < kDiffXOff+kMedSize; ++x) {
+ *atlas.getAddr32(x, y) = SkPackARGB32(0xFF, 0, 0xFF, 0);
+ }
+ }
+
+ sk_tool_utils::create_frustum_normal_map(&atlas,
+ SkIRect::MakeXYWH(kNormXOff, kMedYOff,
+ kMedSize, kMedSize));
+ }
+
+ // small asteroid
+ {
+ SkPoint smCenter = SkPoint::Make(kDiffXOff + kSmSize/2.0f, kSmYOff + kSmSize/2.0f);
+
+ for (int y = kSmYOff; y < kSmYOff+kSmSize; ++y) {
+ for (int x = kDiffXOff; x < kDiffXOff+kSmSize; ++x) {
+ SkScalar distSq = (x - smCenter.fX) * (x - smCenter.fX) +
+ (y - smCenter.fY) * (y - smCenter.fY);
+ if (distSq > kSmSize*kSmSize/4.0f) {
+ *atlas.getAddr32(x, y) = SkPreMultiplyARGB(0, 0, 0, 0);
+ } else {
+ *atlas.getAddr32(x, y) = SkPackARGB32(0xFF, 0, 0, 0xFF);
+ }
+ }
+ }
+
+ sk_tool_utils::create_hemi_normal_map(&atlas,
+ SkIRect::MakeXYWH(kNormXOff, kSmYOff,
+ kSmSize, kSmSize));
+ }
+
+ // ship
+ {
+ SkScalar shipMidLine = kDiffXOff + kMedSize/2.0f;
+
+ for (int y = kShipYOff; y < kShipYOff+kMedSize; ++y) {
+ SkScalar scaledY = (y - kShipYOff)/(float)kMedSize; // 0..1
+
+ for (int x = kDiffXOff; x < kDiffXOff+kMedSize; ++x) {
+ SkScalar scaledX;
+
+ if (x < shipMidLine) {
+ scaledX = 1.0f - (x - kDiffXOff)/(kMedSize/2.0f); // 0..1
+ } else {
+ scaledX = (x - shipMidLine)/(kMedSize/2.0f); // 0..1
+ }
+
+ if (scaledX < scaledY) {
+ *atlas.getAddr32(x, y) = SkPackARGB32(0xFF, 0, 0xFF, 0xFF);
+ } else {
+ *atlas.getAddr32(x, y) = SkPackARGB32(0, 0, 0, 0);
+ }
+ }
+ }
+
+ sk_tool_utils::create_tetra_normal_map(&atlas,
+ SkIRect::MakeXYWH(kNormXOff, kShipYOff,
+ kMedSize, kMedSize));
+ }
+
+ return atlas;
+ }
+
+ class ObjectRecord {
+ public:
+ void initAsteroid(SkRandom *rand, const SkRect& bounds,
+ SkRect* diffTex, SkRect* normTex) {
+ static const SkScalar gMaxSpeeds[3] = { 1, 2, 5 }; // smaller asteroids can go faster
+ static const SkScalar gYOffs[3] = { kBigYOff, kMedYOff, kSmYOff };
+ static const SkScalar gSizes[3] = { kBigSize, kMedSize, kSmSize };
+
+ static unsigned int asteroidType = 0;
+ fObjType = static_cast<ObjType>(asteroidType++ % 3);
+
+ fPosition.set(bounds.fLeft + rand->nextUScalar1() * bounds.width(),
+ bounds.fTop + rand->nextUScalar1() * bounds.height());
+ fVelocity.fX = rand->nextSScalar1();
+ fVelocity.fY = sqrt(1.0f - fVelocity.fX * fVelocity.fX);
+ SkASSERT(SkScalarNearlyEqual(fVelocity.length(), 1.0f));
+ fVelocity *= gMaxSpeeds[fObjType];
+ fRot = 0;
+ fDeltaRot = rand->nextSScalar1() / 32;
+
+ diffTex->setXYWH(SkIntToScalar(kDiffXOff), gYOffs[fObjType],
+ gSizes[fObjType], gSizes[fObjType]);
+ normTex->setXYWH(SkIntToScalar(kNormXOff), gYOffs[fObjType],
+ gSizes[fObjType], gSizes[fObjType]);
+ }
+
+ void initShip(const SkRect& bounds, SkRect* diffTex, SkRect* normTex) {
+ fObjType = kShip_ObjType;
+ fPosition.set(bounds.centerX(), bounds.centerY());
+ fVelocity = SkVector::Make(0.0f, 0.0f);
+ fRot = 0.0f;
+ fDeltaRot = 0.0f;
+
+ diffTex->setXYWH(SkIntToScalar(kDiffXOff), SkIntToScalar(kShipYOff),
+ SkIntToScalar(kMedSize), SkIntToScalar(kMedSize));
+ normTex->setXYWH(SkIntToScalar(kNormXOff), SkIntToScalar(kShipYOff),
+ SkIntToScalar(kMedSize), SkIntToScalar(kMedSize));
+ }
+
+ void advance(const SkRect& bounds) {
+ fPosition += fVelocity;
+ if (fPosition.fX > bounds.right()) {
+ SkASSERT(fVelocity.fX > 0);
+ fVelocity.fX = -fVelocity.fX;
+ } else if (fPosition.fX < bounds.left()) {
+ SkASSERT(fVelocity.fX < 0);
+ fVelocity.fX = -fVelocity.fX;
+ }
+ if (fPosition.fY > bounds.bottom()) {
+ if (fVelocity.fY > 0) {
+ fVelocity.fY = -fVelocity.fY;
+ }
+ } else if (fPosition.fY < bounds.top()) {
+ if (fVelocity.fY < 0) {
+ fVelocity.fY = -fVelocity.fY;
+ }
+ }
+
+ fRot += fDeltaRot;
+ fRot = SkScalarMod(fRot, 2 * SK_ScalarPI);
+ }
+
+ const SkPoint& pos() const { return fPosition; }
+
+ SkScalar rot() const { return fRot; }
+ void setRot(SkScalar rot) { fRot = rot; }
+
+ const SkPoint& velocity() const { return fVelocity; }
+ void setVelocity(const SkPoint& velocity) { fVelocity = velocity; }
+
+ SkRSXform asRSXform() const {
+ static const SkScalar gHalfSizes[kObjTypeCount] = {
+ SkScalarHalf(kBigSize),
+ SkScalarHalf(kMedSize),
+ SkScalarHalf(kSmSize),
+ SkScalarHalf(kMedSize),
+ };
+
+ return SkRSXform::MakeFromRadians(1.0f, fRot, fPosition.x(), fPosition.y(),
+ gHalfSizes[fObjType],
+ gHalfSizes[fObjType]);
+ }
+
+ private:
+ ObjType fObjType;
+ SkPoint fPosition;
+ SkVector fVelocity;
+ SkScalar fRot; // In radians.
+ SkScalar fDeltaRot; // In radiands. Not used by ship.
+ };
+
+
+
+
+private:
+ static const int kNumLights = 2;
+ static const int kNumAsteroids = 6;
+ static const int kNumShips = 1;
+
+ static const int kBigSize = 128;
+ static const int kMedSize = 64;
+ static const int kSmSize = 32;
+ static const int kPad = 1;
+ static const int kAtlasWidth = kBigSize + kBigSize + 2 * kPad; // 2 pads in the middle
+ static const int kAtlasHeight = kBigSize + kMedSize + kSmSize + kMedSize + 3 * kPad;
+
+ static const int kDiffXOff = 0;
+ static const int kNormXOff = kBigSize + 2 * kPad;
+
+ static const int kBigYOff = 0;
+ static const int kMedYOff = kBigSize + kPad;
+ static const int kSmYOff = kMedYOff + kMedSize + kPad;
+ static const int kShipYOff = kSmYOff + kSmSize + kPad;
+ static const int kMaxShipSpeed = 5;
+
+ SkBitmap fAtlas;
+ ObjectRecord fAsteroids[kNumAsteroids];
+ ObjectRecord fShip;
+ SkRect fDiffTex[kNumAsteroids+kNumShips];
+ SkRect fNormTex[kNumAsteroids+kNumShips];
+ SkRect fBounds;
+ bool fUseColors;
+ sk_sp<SkLights> fLights;
+
+ typedef SkDrawable INHERITED;
+};
+
+class DrawLitAtlasView : public SampleView {
+public:
+ DrawLitAtlasView()
+ : fDrawable(new DrawLitAtlasDrawable(SkRect::MakeWH(640, 480))) {
+ }
+
+protected:
+ bool onQuery(SkEvent* evt) override {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "DrawLitAtlas");
+ return true;
+ }
+ SkUnichar uni;
+ if (SampleCode::CharQ(*evt, &uni)) {
+ switch (uni) {
+ case 'C':
+ fDrawable->toggleUseColors();
+ this->inval(NULL);
+ return true;
+ case 'j':
+ fDrawable->left();
+ this->inval(NULL);
+ return true;
+ case 'k':
+ fDrawable->thrust();
+ this->inval(NULL);
+ return true;
+ case 'l':
+ fDrawable->right();
+ this->inval(NULL);
+ return true;
+ default:
+ break;
+ }
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ void onDrawContent(SkCanvas* canvas) override {
+ canvas->drawDrawable(fDrawable);
+ this->inval(NULL);
+ }
+
+#if 0
+ // TODO: switch over to use this for our animation
+ bool onAnimate(const SkAnimTimer& timer) override {
+ SkScalar angle = SkDoubleToScalar(fmod(timer.secs() * 360 / 24, 360));
+ fAnimatingDrawable->setSweep(angle);
+ return true;
+ }
+#endif
+
+private:
+ SkAutoTUnref<DrawLitAtlasDrawable> fDrawable;
+
+ typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new DrawLitAtlasView; }
+static SkViewRegister reg(MyFactory);