| Index: samplecode/SampleLitAtlas.cpp
|
| diff --git a/samplecode/SampleLitAtlas.cpp b/samplecode/SampleLitAtlas.cpp
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..dace6e52fbb282e066887e33777bf29ea7ae7a47
|
| --- /dev/null
|
| +++ b/samplecode/SampleLitAtlas.cpp
|
| @@ -0,0 +1,430 @@
|
| +/*
|
| + * Copyright 2015 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 "SkLight.h"
|
| +#include "SkRandom.h"
|
| +#include "SkRSXform.h"
|
| +#include "SkSurface.h"
|
| +
|
| +#include "sk_tool_utils.h"
|
| +
|
| +
|
| +class DrawLitAtlasDrawable : public SkDrawable {
|
| +public:
|
| + DrawLitAtlasDrawable(const SkRect& r)
|
| + : fBounds(r)
|
| + , fUseColors(false) {
|
| + fAtlas.reset(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]);
|
| +
|
| + fLights[0] = SkLight(SkColor3f::Make(1.0f, 1.0f, 1.0f),
|
| + SkVector3::Make(1.0f, 0.0f, 0.0f));
|
| + fLights[1] = SkLight(SkColor3f::Make(0.2f, 0.2f, 0.2f));
|
| + }
|
| +
|
| + 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;
|
| +
|
| + 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);
|
| + }
|
| +
|
| + SkPaint paint;
|
| + paint.setFilterQuality(kLow_SkFilterQuality);
|
| +
|
| + const SkRect cull = this->getBounds();
|
| + const SkColor* colorsPtr = fUseColors ? colors : NULL;
|
| +#ifdef SK_DEBUG
|
| + canvas->drawImage(fAtlas, 0, 0); // just to see the atlas
|
| +#endif
|
| +
|
| + canvas->drawLitAtlas(fAtlas, xforms, fDiffTex, fNormTex, colorsPtr, kNumAsteroids+1,
|
| + SkXfermode::kModulate_Mode, &cull, &paint, fLights, kNumLights);
|
| +
|
| +#ifdef SK_DEBUG
|
| + 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(cull, 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 SkImage* 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 SkImage::NewFromBitmap(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 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 gYOffs[kObjTypeCount] = {
|
| + kBigYOff,
|
| + kMedYOff,
|
| + kSmYOff,
|
| + kShipYOff
|
| + };
|
| +
|
| + 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;
|
| +
|
| + SkAutoTUnref<SkImage> fAtlas;
|
| + ObjectRecord fAsteroids[kNumAsteroids];
|
| + ObjectRecord fShip;
|
| + SkRect fDiffTex[kNumAsteroids+kNumShips];
|
| + SkRect fNormTex[kNumAsteroids+kNumShips];
|
| + SkRect fBounds;
|
| + bool fUseColors;
|
| + SkLight fLights[kNumLights];
|
| +
|
| + 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);
|
|
|