/*
* 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 "SkPictureRecorder.h"
#include "SkShadowPaintFilterCanvas.h"
#include "SkShadowShader.h"
#include "SkSurface.h"
#ifdef SK_EXPERIMENTAL_SHADOWING
class ShadowingView : public SampleView {
public:
ShadowingView()
: fSceneChanged(true)
, fLightsChanged(true)
, fMoveLight(false)
, fClearShadowMaps(false)
, fSelectedRectID(-1)
, fSelectedSliderID(-1)
, fLightDepth(400.0f) {
this->setBGColor(0xFFCCCCCC);
this->updateLights(100, 100);
fTestRects[0].fColor = 0xFFEE8888;
fTestRects[0].fDepth = 80;
fTestRects[0].fGeometry = SkRect::MakeLTRB(300,200,350,250);
fTestRects[1].fColor = 0xFF88EE88;
fTestRects[1].fDepth = 160;
fTestRects[1].fGeometry = SkRect::MakeLTRB(200,300,250,350);
fTestRects[2].fColor = 0xFF8888EE;
fTestRects[2].fDepth = 240;
fTestRects[2].fGeometry = SkRect::MakeLTRB(100,100,150,150);
fSliders[0].fGeometry = SkRect::MakeLTRB(20, 400, 30, 420);
fSliders[0].fOffset = 0.0f;
fSliders[0].fScale = 0.1f;
fSliders[1].fGeometry = SkRect::MakeLTRB(100, 420, 110, 440);
fSliders[1].fOffset = 0.0f;
fSliders[1].fScale = 10.0f;
fSliders[2].fGeometry = SkRect::MakeLTRB(0, 440, 10, 460);
fSliders[2].fOffset = 0.0f;
fSliders[2].fScale = 0.0025f;
fShadowParams.fShadowRadius = 4.0f;
fShadowParams.fBiasingConstant = 0.3f;
fShadowParams.fMinVariance = 2048; // we need a higher min variance for point lights
fShadowParams.fType = SkShadowParams::kNoBlur_ShadowType;
}
protected:
bool onQuery(SkEvent *evt) override {
if (SampleCode::TitleQ(*evt)) {
SampleCode::TitleR(evt, "shadowing");
return true;
}
SkUnichar uni;
if (SampleCode::CharQ(*evt, &uni)) {
switch (uni) {
case 'L':
fMoveLight = !fMoveLight;
break;
case 'd':
// Raster generated shadow maps have their origin in the UL corner
// GPU shadow maps can have an arbitrary origin.
// We override the 'd' keypress so that when the device is cycled,
// the shadow maps will be re-generated according to the new backend.
fClearShadowMaps = true;
break;
case 'q':
fLightDepth += 5.0f;
fMoveLight = true;
break;
case 'B':
if (SkShadowParams::kVariance_ShadowType == fShadowParams.fType) {
fShadowParams.fType = SkShadowParams::kNoBlur_ShadowType;
} else if (SkShadowParams::kNoBlur_ShadowType ==
fShadowParams.fType) {
fShadowParams.fType = SkShadowParams::kVariance_ShadowType;
}
fLightsChanged = true;
break;
case 'w':
fLightDepth -= 5.0f;
fMoveLight = true;
break;
default:
break;
}
}
return this->INHERITED::onQuery(evt);
}
sk_sp<SkPicture> makeTestPicture(int width, int height) {
SkPictureRecorder recorder;
// LONG RANGE TODO: eventually add SkBBHFactory (bounding box factory)
SkCanvas* canvas = recorder.beginRecording(SkRect::MakeIWH(width, height));
SkASSERT(canvas->getTotalMatrix().isIdentity());
SkPaint paint;
paint.setColor(SK_ColorGRAY);
// LONG RANGE TODO: tag occluders
// LONG RANGE TODO: track number of IDs we need (hopefully less than 256)
// and determinate the mapping from z to id
// universal receiver, "ground"
canvas->drawRect(SkRect::MakeIWH(width, height), paint);
for (int i = 0; i < kNumTestRects; i++) {
paint.setColor(fTestRects[i].fColor);
if (i == 0) {
canvas->translateZ(fTestRects[0].fDepth);
} else {
canvas->translateZ(fTestRects[i].fDepth - fTestRects[i-1].fDepth);
}
canvas->drawRect(fTestRects[i].fGeometry, paint);
}
return recorder.finishRecordingAsPicture();
}
void onDrawContent(SkCanvas *canvas) override {
if (fSceneChanged) {
fPicture = this->makeTestPicture(kWidth, kHeight);
}
if (fSceneChanged || fLightsChanged || fClearShadowMaps) {
for (int i = 0; i < fLights->numLights(); i++) {
fLights->light(i).setShadowMap(nullptr);
}
fSceneChanged = false;
fLightsChanged = false;
fClearShadowMaps = false;
}
canvas->setLights(fLights);
canvas->drawShadowedPicture(fPicture, nullptr, nullptr, fShadowParams);
for (int i = 0; i < kNumSliders; i++) {
SkPaint paint;
paint.setColor(SK_ColorBLACK);
canvas->drawRect(fSliders[i].fGeometry, paint);
}
}
SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned modi) override {
return new SkView::Click(this);
}
void updateLights(int x, int y) {
SkLights::Builder builder;
builder.add(SkLights::Light::MakePoint(SkColor3f::Make(1.0f, 1.0f, 1.0f),
SkVector3::Make(x,
kHeight - y,
fLightDepth),
400, true));
fLights = builder.finish();
}
void updateFromSelectedSlider() {
SkScalar newValue = fSliders[fSelectedSliderID].fGeometry.fLeft *
fSliders[fSelectedSliderID].fScale +
fSliders[fSelectedSliderID].fOffset;
switch (fSelectedSliderID) {
case 0:
fShadowParams.fShadowRadius = newValue;
break;
case 1:
fShadowParams.fMinVariance = newValue;
break;
case 2:
fShadowParams.fBiasingConstant = newValue;
break;
default:
break;
}
}
bool onClick(Click *click) override {
SkScalar x = click->fCurr.fX;
SkScalar y = click->fCurr.fY;
SkScalar dx = x - click->fPrev.fX;
SkScalar dy = y - click->fPrev.fY;
if (fMoveLight) {
if (dx != 0 || dy != 0) {
this->updateLights(x, y);
fLightsChanged = true;
this->inval(nullptr);
}
return true;
}
if (click->fState == Click::State::kUp_State) {
fSelectedRectID = -1;
fSelectedSliderID = -1;
return true;
}
if (fSelectedRectID > -1) {
fTestRects[fSelectedRectID].fGeometry.offset(dx, dy);
fSceneChanged = true;
this->inval(nullptr);
return true;
}
if (fSelectedSliderID > -1) {
fSliders[fSelectedSliderID].fGeometry.offset(dx, 0);
this->updateFromSelectedSlider();
fLightsChanged = true;
this->inval(nullptr);
return true;
}
// assume last elements are highest
for (int i = kNumTestRects - 1; i >= 0; i--) {
if (fTestRects[i].fGeometry.contains(SkRect::MakeXYWH(x, y, 1, 1))) {
fSelectedRectID = i;
fTestRects[i].fGeometry.offset(dx, dy);
fSceneChanged = true;
this->inval(nullptr);
break;
}
}
for (int i = 0; i <= kNumSliders; i++) {
if (fSliders[i].fGeometry.contains(SkRect::MakeXYWH(x, y, 1, 1))) {
fSelectedSliderID = i;
fSliders[i].fGeometry.offset(dx, 0);
this->updateFromSelectedSlider();
fLightsChanged = true;
this->inval(nullptr);
break;
}
}
return true;
}
private:
static constexpr int kNumTestRects = 3;
static constexpr int kNumSliders = 3;
static const int kWidth = 400;
static const int kHeight = 400;
bool fSceneChanged;
bool fLightsChanged;
bool fMoveLight;
bool fClearShadowMaps;
struct {
SkRect fGeometry;
int fDepth;
SkColor fColor;
} fTestRects[kNumTestRects];
int fSelectedRectID;
struct {
SkRect fGeometry;
SkScalar fOffset;
SkScalar fScale;
} fSliders[kNumSliders];
int fSelectedSliderID;
SkScalar fLightDepth;
sk_sp<SkPicture> fPicture;
SkShadowParams fShadowParams;
sk_sp<SkLights> fLights;
typedef SampleView INHERITED;
};
//////////////////////////////////////////////////////////////////////////////
static SkView* MyFactory() { return new ShadowingView; }
static SkViewRegister reg(MyFactory);
#endif