| Index: samplecode/SampleLitAtlas.cpp
|
| diff --git a/samplecode/SampleLitAtlas.cpp b/samplecode/SampleLitAtlas.cpp
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..4f989af1ae026f5fdb7a9511bcf0d24a2cc3668d
|
| --- /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);
|
|
|