123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488 |
- /*
- * 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 "include/core/SkCanvas.h"
- #include "include/core/SkDrawable.h"
- #include "include/core/SkRSXform.h"
- #include "include/utils/SkRandom.h"
- #include "samplecode/Sample.h"
- #include "src/core/SkNormalSource.h"
- #include "src/shaders/SkBitmapProcShader.h"
- #include "src/shaders/SkLightingShader.h"
- #include "src/shaders/SkLights.h"
- #include "tools/ToolUtils.h"
- // A crude normal mapped asteroids-like sample
- class DrawLitAtlasDrawable : public SkDrawable {
- public:
- DrawLitAtlasDrawable(const SkRect& r)
- : fBounds(r)
- , fUseColors(false)
- , fLightDir(SkVector3::Make(1.0f, 0.0f, 0.0f)) {
- 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]);
- this->updateLights();
- }
- void toggleUseColors() {
- fUseColors = !fUseColors;
- }
- void rotateLight() {
- SkScalar r = SK_ScalarPI / 6.0f,
- s = SkScalarSin(r),
- c = SkScalarCos(r);
- SkScalar newX = c * fLightDir.fX - s * fLightDir.fY;
- SkScalar newY = s * fLightDir.fX + c * fLightDir.fY;
- fLightDir.set(newX, newY, 0.0f);
- this->updateLights();
- }
- 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 s = SkScalarSin(fShip.rot()),
- c = SkScalarCos(fShip.rot());
- SkVector newVel = fShip.velocity();
- newVel.fX += s;
- newVel.fY += -c;
- SkScalar len = newVel.length();
- if (len > 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
- this->drawLightDir(canvas, fBounds.centerX(), fBounds.centerY());
- #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]);
- sk_sp<SkShader> normalMap = fAtlas.makeShader(&normalMat);
- sk_sp<SkNormalSource> normalSource = SkNormalSource::MakeFromNormalMap(
- std::move(normalMap), m);
- sk_sp<SkShader> diffuseShader = fAtlas.makeShader(&diffMat);
- paint.setShader(SkLightingShader::Make(std::move(diffuseShader),
- std::move(normalSource), fLights));
- 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;
- void updateLights() {
- SkLights::Builder builder;
- builder.add(SkLights::Light::MakeDirectional(
- SkColor3f::Make(1.0f, 1.0f, 1.0f), fLightDir));
- builder.setAmbientLightColor(SkColor3f::Make(0.2f, 0.2f, 0.2f));
- fLights = builder.finish();
- }
- #ifdef SK_DEBUG
- // Draw a vector to the light
- void drawLightDir(SkCanvas* canvas, SkScalar centerX, SkScalar centerY) {
- static const int kBgLen = 30;
- static const int kSmLen = 5;
- // TODO: change the lighting coordinate system to be right handed
- SkPoint p1 = SkPoint::Make(centerX + kBgLen * fLightDir.fX,
- centerY - kBgLen * fLightDir.fY);
- SkPoint p2 = SkPoint::Make(centerX + (kBgLen-kSmLen) * fLightDir.fX,
- centerY - (kBgLen-kSmLen) * fLightDir.fY);
- SkPaint p;
- canvas->drawLine(centerX, centerY, p1.fX, p1.fY, p);
- canvas->drawLine(p1.fX, p1.fY,
- p2.fX - kSmLen * fLightDir.fY, p2.fY - kSmLen * fLightDir.fX, p);
- canvas->drawLine(p1.fX, p1.fY,
- p2.fX + kSmLen * fLightDir.fY, p2.fY + kSmLen * fLightDir.fX, p);
- }
- #endif
- // 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);
- }
- }
- }
- ToolUtils::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);
- }
- }
- ToolUtils::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);
- }
- }
- }
- ToolUtils::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);
- }
- }
- }
- ToolUtils::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;
- SkVector3 fLightDir;
- sk_sp<SkLights> fLights;
- typedef SkDrawable INHERITED;
- };
- class DrawLitAtlasView : public Sample {
- public:
- DrawLitAtlasView() : fDrawable(new DrawLitAtlasDrawable(SkRect::MakeWH(640, 480))) {}
- protected:
- SkString name() override { return SkString("DrawLitAtlas"); }
- bool onChar(SkUnichar uni) override {
- switch (uni) {
- case 'C':
- fDrawable->toggleUseColors();
- return true;
- case 'j':
- fDrawable->left();
- return true;
- case 'k':
- fDrawable->thrust();
- return true;
- case 'l':
- fDrawable->right();
- return true;
- case 'o':
- fDrawable->rotateLight();
- return true;
- default:
- break;
- }
- return false;
- }
- void onDrawContent(SkCanvas* canvas) override {
- canvas->drawDrawable(fDrawable.get());
- }
- bool onAnimate(double nanos) override { return true; }
- private:
- sk_sp<DrawLitAtlasDrawable> fDrawable;
- typedef Sample INHERITED;
- };
- //////////////////////////////////////////////////////////////////////////////
- DEF_SAMPLE( return new DrawLitAtlasView(); )
|