Particles: Better integration for ResourceProvider
This untangles some of the dirty state tracking and dynamic rebuilding support (that's only needed for the GUI editor), so the core code is more streamlined. It also paves the way for feeding the RP to bindings. Change-Id: I208ec59622154fdb2845c3ae8f7efb070d1abfc7 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/257476 Reviewed-by: Brian Osman <brianosman@google.com> Commit-Queue: Brian Osman <brianosman@google.com>
This commit is contained in:
parent
9fe91522ae
commit
9dac0d8216
@ -114,11 +114,9 @@ EMSCRIPTEN_BINDINGS(Particles) {
|
||||
skjson::DOM dom(json.c_str(), json.length());
|
||||
SkFromJsonVisitor fromJson(dom.root());
|
||||
params->visitFields(&fromJson);
|
||||
return sk_sp<SkParticleEffect>(new SkParticleEffect(
|
||||
std::move(params),
|
||||
skresources::DataURIResourceProviderProxy::Make(
|
||||
ParticleAssetProvider::Make(std::move(assets))),
|
||||
r));
|
||||
params->prepare(skresources::DataURIResourceProviderProxy::Make(
|
||||
ParticleAssetProvider::Make(std::move(assets))).get());
|
||||
return sk_sp<SkParticleEffect>(new SkParticleEffect(std::move(params), r));
|
||||
}));
|
||||
constant("particles", true);
|
||||
|
||||
|
@ -18,6 +18,10 @@ class SkParticleEffect;
|
||||
class SkParticleEffectParams;
|
||||
class SkRandom;
|
||||
|
||||
namespace skresources {
|
||||
class ResourceProvider;
|
||||
}
|
||||
|
||||
namespace SkSL {
|
||||
class Compiler;
|
||||
}
|
||||
@ -47,7 +51,9 @@ public:
|
||||
REFLECTED_ABSTRACT(SkParticleBinding, SkReflected)
|
||||
|
||||
void visitFields(SkFieldVisitor* v) override;
|
||||
|
||||
virtual std::unique_ptr<SkParticleExternalValue> toValue(SkSL::Compiler&) = 0;
|
||||
virtual void prepare(const skresources::ResourceProvider*) = 0;
|
||||
|
||||
static void RegisterBindingTypes();
|
||||
|
||||
|
@ -21,8 +21,9 @@ class SkParticleDrawable : public SkReflected {
|
||||
public:
|
||||
REFLECTED_ABSTRACT(SkParticleDrawable, SkReflected)
|
||||
|
||||
virtual void draw(const skresources::ResourceProvider* resourceProvider, SkCanvas* canvas,
|
||||
const SkParticles& particles, int count, const SkPaint& paint) = 0;
|
||||
virtual void draw(SkCanvas* canvas, const SkParticles& particles, int count,
|
||||
const SkPaint& paint) = 0;
|
||||
virtual void prepare(const skresources::ResourceProvider* resourceProvider) = 0;
|
||||
|
||||
static void RegisterDrawableTypes();
|
||||
|
||||
|
@ -115,6 +115,9 @@ public:
|
||||
|
||||
void visitFields(SkFieldVisitor* v);
|
||||
|
||||
// Load/compute cached resources
|
||||
void prepare(const skresources::ResourceProvider*);
|
||||
|
||||
private:
|
||||
friend class SkParticleEffect;
|
||||
|
||||
@ -126,14 +129,11 @@ private:
|
||||
|
||||
Program fEffectProgram;
|
||||
Program fParticleProgram;
|
||||
|
||||
void rebuild();
|
||||
};
|
||||
|
||||
class SkParticleEffect : public SkRefCnt {
|
||||
public:
|
||||
SkParticleEffect(sk_sp<SkParticleEffectParams> params,
|
||||
sk_sp<skresources::ResourceProvider> rp, const SkRandom& random);
|
||||
SkParticleEffect(sk_sp<SkParticleEffectParams> params, const SkRandom& random);
|
||||
|
||||
// Start playing this effect, specifying initial values for the emitter's properties
|
||||
void start(double now, bool looping, SkPoint position, SkVector heading, float scale,
|
||||
@ -204,7 +204,6 @@ private:
|
||||
void runParticleScript(double now, const char* entry, int start, int count);
|
||||
|
||||
sk_sp<SkParticleEffectParams> fParams;
|
||||
sk_sp<skresources::ResourceProvider> fResourceProvider;
|
||||
|
||||
SkRandom fRandom;
|
||||
|
||||
|
@ -42,27 +42,8 @@ public:
|
||||
fWriter.appendString(name, s.c_str());
|
||||
}
|
||||
}
|
||||
void visit(const char* name, int& i, const EnumStringMapping* map, int count) override {
|
||||
fWriter.appendString(name, EnumToString(i, map, count));
|
||||
}
|
||||
|
||||
// Compound types
|
||||
void visit(const char* name, SkPoint& p) override {
|
||||
fWriter.beginObject(name, false);
|
||||
fWriter.appendFloat("x", p.fX);
|
||||
fWriter.appendFloat("y", p.fY);
|
||||
fWriter.endObject();
|
||||
}
|
||||
|
||||
void visit(const char* name, SkColor4f& c) override {
|
||||
fWriter.beginArray(name, false);
|
||||
fWriter.appendFloat(c.fR);
|
||||
fWriter.appendFloat(c.fG);
|
||||
fWriter.appendFloat(c.fB);
|
||||
fWriter.appendFloat(c.fA);
|
||||
fWriter.endArray();
|
||||
}
|
||||
|
||||
void visit(sk_sp<SkReflected>& e, const SkReflected::Type* baseType) override {
|
||||
fWriter.appendString("Type", e ? e->getType()->fName : "Null");
|
||||
}
|
||||
@ -115,29 +96,6 @@ public:
|
||||
TryParse(get(name), s);
|
||||
}
|
||||
}
|
||||
void visit(const char* name, int& i, const EnumStringMapping* map, int count) override {
|
||||
SkString str;
|
||||
if (TryParse(get(name), str)) {
|
||||
i = StringToEnum(str.c_str(), map, count);
|
||||
}
|
||||
}
|
||||
|
||||
void visit(const char* name, SkPoint& p) override {
|
||||
if (const skjson::ObjectValue* obj = get(name)) {
|
||||
TryParse((*obj)["x"], p.fX);
|
||||
TryParse((*obj)["y"], p.fY);
|
||||
}
|
||||
}
|
||||
|
||||
void visit(const char* name, SkColor4f& c) override {
|
||||
const skjson::ArrayValue* arr = get(name);
|
||||
if (arr && arr->size() == 4) {
|
||||
TryParse((*arr)[0], c.fR);
|
||||
TryParse((*arr)[1], c.fG);
|
||||
TryParse((*arr)[2], c.fB);
|
||||
TryParse((*arr)[3], c.fA);
|
||||
}
|
||||
}
|
||||
|
||||
void visit(sk_sp<SkReflected>& e, const SkReflected::Type* baseType) override {
|
||||
const skjson::StringValue* typeString = get("Type");
|
||||
|
@ -128,8 +128,8 @@ private:
|
||||
* SkReflected types, and of types that implement the visitFields() function.
|
||||
*
|
||||
* Classes implementing the interface must supply implementations of virtual functions that visit
|
||||
* basic types (float, int, bool, SkString, etc...), as well as helper methods for entering the
|
||||
* scope of an object or array.
|
||||
* basic types (float, int, bool, SkString), as well as helper methods for entering the scope of
|
||||
* an object or array.
|
||||
*
|
||||
* All visit functions supply a field name, and a non-constant reference to an actual field.
|
||||
* This allows visitors to serialize or deserialize collections of objects, or perform edits on
|
||||
@ -152,28 +152,9 @@ public:
|
||||
virtual void visit(const char*, bool&) = 0;
|
||||
virtual void visit(const char*, SkString&) = 0;
|
||||
|
||||
virtual void visit(const char*, SkPoint&) = 0;
|
||||
virtual void visit(const char*, SkColor4f&) = 0;
|
||||
|
||||
// Accommodation for enums, where caller can supply a value <-> string map
|
||||
struct EnumStringMapping {
|
||||
int fValue;
|
||||
const char* fName;
|
||||
};
|
||||
virtual void visit(const char*, int&, const EnumStringMapping*, int count) = 0;
|
||||
|
||||
// Default visit function for structs with no special behavior. It is assumed that any such
|
||||
// struct implements visitFields(SkFieldVisitor*) to recursively visit each of its fields.
|
||||
template <typename T>
|
||||
void visit(const char* name, T& value) {
|
||||
this->enterObject(name);
|
||||
value.visitFields(this);
|
||||
this->exitObject();
|
||||
}
|
||||
|
||||
// Specialization for SkTArrays. In conjunction with the enterArray/exitArray virtuals, this
|
||||
// allows visitors to resize an array (for deserialization), and apply a single edit operation
|
||||
// (remove or move a single element). Each element of the array is visited as normal.
|
||||
// (remove a single element). Each element of the array is visited as normal.
|
||||
template <typename T, bool MEM_MOVE>
|
||||
void visit(const char* name, SkTArray<T, MEM_MOVE>& arr) {
|
||||
arr.resize_back(this->enterArray(name, arr.count()));
|
||||
@ -214,7 +195,6 @@ protected:
|
||||
enum class Verb {
|
||||
kNone,
|
||||
kRemove,
|
||||
kMoveForward,
|
||||
};
|
||||
|
||||
Verb fVerb = Verb::kNone;
|
||||
@ -231,32 +211,11 @@ protected:
|
||||
}
|
||||
arr.pop_back();
|
||||
break;
|
||||
case Verb::kMoveForward:
|
||||
if (fIndex > 0 && fIndex < arr.count()) {
|
||||
std::swap(arr[fIndex - 1], arr[fIndex]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static const char* EnumToString(int value, const EnumStringMapping* map, int count) {
|
||||
for (int i = 0; i < count; ++i) {
|
||||
if (map[i].fValue == value) {
|
||||
return map[i].fName;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
static int StringToEnum(const char* str, const EnumStringMapping* map, int count) {
|
||||
for (int i = 0; i < count; ++i) {
|
||||
if (0 == strcmp(str, map[i].fName)) {
|
||||
return map[i].fValue;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private:
|
||||
virtual void enterObject(const char* name) = 0;
|
||||
virtual void exitObject() = 0;
|
||||
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "include/utils/SkTextUtils.h"
|
||||
#include "modules/particles/include/SkParticleEffect.h"
|
||||
#include "modules/particles/include/SkReflected.h"
|
||||
#include "modules/skresources/include/SkResources.h"
|
||||
#include "src/sksl/SkSLCompiler.h"
|
||||
|
||||
void SkParticleBinding::visitFields(SkFieldVisitor* v) {
|
||||
@ -64,6 +65,10 @@ public:
|
||||
new SkEffectExternalValue(fName.c_str(), compiler, fParams));
|
||||
}
|
||||
|
||||
void prepare(const skresources::ResourceProvider* resourceProvider) override {
|
||||
fParams->prepare(resourceProvider);
|
||||
}
|
||||
|
||||
private:
|
||||
sk_sp<SkParticleEffectParams> fParams;
|
||||
};
|
||||
@ -121,21 +126,13 @@ class SkPathBinding : public SkParticleBinding {
|
||||
public:
|
||||
SkPathBinding(const char* name = "", const char* path = "")
|
||||
: SkParticleBinding(name)
|
||||
, fPath(path) {
|
||||
this->rebuild();
|
||||
}
|
||||
, fPath(path) {}
|
||||
|
||||
REFLECTED(SkPathBinding, SkParticleBinding)
|
||||
|
||||
void visitFields(SkFieldVisitor* v) override {
|
||||
SkString oldPath = fPath;
|
||||
|
||||
SkParticleBinding::visitFields(v);
|
||||
v->visit("Path", fPath);
|
||||
|
||||
if (fPath != oldPath) {
|
||||
this->rebuild();
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<SkParticleExternalValue> toValue(SkSL::Compiler& compiler) override {
|
||||
@ -143,16 +140,16 @@ public:
|
||||
new SkPathExternalValue(fName.c_str(), compiler, &fContours));
|
||||
}
|
||||
|
||||
private:
|
||||
SkString fPath;
|
||||
|
||||
void rebuild() {
|
||||
void prepare(const skresources::ResourceProvider*) override {
|
||||
SkPath path;
|
||||
if (SkParsePath::FromSVGString(fPath.c_str(), &path)) {
|
||||
fContours.rebuild(path);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
SkString fPath;
|
||||
|
||||
// Cached
|
||||
SkPathContours fContours;
|
||||
};
|
||||
@ -162,23 +159,14 @@ public:
|
||||
SkTextBinding(const char* name = "", const char* text = "", SkScalar fontSize = 96)
|
||||
: SkParticleBinding(name)
|
||||
, fText(text)
|
||||
, fFontSize(fontSize) {
|
||||
this->rebuild();
|
||||
}
|
||||
, fFontSize(fontSize) {}
|
||||
|
||||
REFLECTED(SkTextBinding, SkParticleBinding)
|
||||
|
||||
void visitFields(SkFieldVisitor* v) override {
|
||||
SkString oldText = fText;
|
||||
SkScalar oldSize = fFontSize;
|
||||
|
||||
SkParticleBinding::visitFields(v);
|
||||
v->visit("Text", fText);
|
||||
v->visit("FontSize", fFontSize);
|
||||
|
||||
if (fText != oldText || fFontSize != oldSize) {
|
||||
this->rebuild();
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<SkParticleExternalValue> toValue(SkSL::Compiler& compiler) override {
|
||||
@ -186,11 +174,7 @@ public:
|
||||
new SkPathExternalValue(fName.c_str(), compiler, &fContours));
|
||||
}
|
||||
|
||||
private:
|
||||
SkString fText;
|
||||
SkScalar fFontSize;
|
||||
|
||||
void rebuild() {
|
||||
void prepare(const skresources::ResourceProvider*) override {
|
||||
if (fText.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
@ -201,6 +185,10 @@ private:
|
||||
fContours.rebuild(path);
|
||||
}
|
||||
|
||||
private:
|
||||
SkString fText;
|
||||
SkScalar fFontSize;
|
||||
|
||||
// Cached
|
||||
SkPathContours fContours;
|
||||
};
|
||||
|
@ -72,16 +72,14 @@ struct DrawAtlasArrays {
|
||||
|
||||
class SkCircleDrawable : public SkParticleDrawable {
|
||||
public:
|
||||
SkCircleDrawable(int radius = 1)
|
||||
: fRadius(radius) {
|
||||
this->rebuild();
|
||||
}
|
||||
SkCircleDrawable(int radius = 1) : fRadius(radius) {}
|
||||
|
||||
REFLECTED(SkCircleDrawable, SkParticleDrawable)
|
||||
|
||||
void draw(const skresources::ResourceProvider* /* unused */, SkCanvas* canvas,
|
||||
const SkParticles& particles, int count, const SkPaint& paint) override {
|
||||
SkPoint center = { SkIntToScalar(fRadius), SkIntToScalar(fRadius) };
|
||||
void draw(SkCanvas* canvas, const SkParticles& particles, int count,
|
||||
const SkPaint& paint) override {
|
||||
int r = SkTMax(fRadius, 1);
|
||||
SkPoint center = { SkIntToScalar(r), SkIntToScalar(r) };
|
||||
DrawAtlasArrays arrays(particles, count, center);
|
||||
for (int i = 0; i < count; ++i) {
|
||||
arrays.fRects[i].setIWH(fImage->width(), fImage->height());
|
||||
@ -90,21 +88,20 @@ public:
|
||||
count, SkBlendMode::kModulate, nullptr, &paint);
|
||||
}
|
||||
|
||||
void prepare(const skresources::ResourceProvider*) override {
|
||||
int r = SkTMax(fRadius, 1);
|
||||
if (!fImage || fImage->width() != 2 * r) {
|
||||
fImage = make_circle_image(r);
|
||||
}
|
||||
}
|
||||
|
||||
void visitFields(SkFieldVisitor* v) override {
|
||||
v->visit("Radius", fRadius);
|
||||
this->rebuild();
|
||||
}
|
||||
|
||||
private:
|
||||
int fRadius;
|
||||
|
||||
void rebuild() {
|
||||
fRadius = SkTMax(fRadius, 1);
|
||||
if (!fImage || fImage->width() != 2 * fRadius) {
|
||||
fImage = make_circle_image(fRadius);
|
||||
}
|
||||
}
|
||||
|
||||
// Cached
|
||||
sk_sp<SkImage> fImage;
|
||||
};
|
||||
@ -116,60 +113,33 @@ public:
|
||||
: fPath(path)
|
||||
, fName(name)
|
||||
, fCols(cols)
|
||||
, fRows(rows)
|
||||
, fDirty(true) {}
|
||||
, fRows(rows) {}
|
||||
|
||||
REFLECTED(SkImageDrawable, SkParticleDrawable)
|
||||
|
||||
void draw(const skresources::ResourceProvider* resourceProvider, SkCanvas* canvas,
|
||||
const SkParticles& particles, int count, const SkPaint& paint) override {
|
||||
this->rebuildIfDirty(resourceProvider);
|
||||
|
||||
SkRect baseRect = SkRect::MakeWH(static_cast<float>(fImage->width()) / fCols,
|
||||
static_cast<float>(fImage->height()) / fRows);
|
||||
void draw(SkCanvas* canvas, const SkParticles& particles, int count,
|
||||
const SkPaint& paint) override {
|
||||
int cols = SkTMax(fCols, 1),
|
||||
rows = SkTMax(fRows, 1);
|
||||
SkRect baseRect = SkRect::MakeWH(static_cast<float>(fImage->width()) / cols,
|
||||
static_cast<float>(fImage->height()) / rows);
|
||||
SkPoint center = { baseRect.width() * 0.5f, baseRect.height() * 0.5f };
|
||||
DrawAtlasArrays arrays(particles, count, center);
|
||||
|
||||
int frameCount = fCols * fRows;
|
||||
int frameCount = cols * rows;
|
||||
float* spriteFrames = particles.fData[SkParticles::kSpriteFrame].get();
|
||||
for (int i = 0; i < count; ++i) {
|
||||
int frame = static_cast<int>(spriteFrames[i] * frameCount + 0.5f);
|
||||
frame = SkTPin(frame, 0, frameCount - 1);
|
||||
int row = frame / fCols;
|
||||
int col = frame % fCols;
|
||||
int row = frame / cols;
|
||||
int col = frame % cols;
|
||||
arrays.fRects[i] = baseRect.makeOffset(col * baseRect.width(), row * baseRect.height());
|
||||
}
|
||||
canvas->drawAtlas(fImage, arrays.fXforms.get(), arrays.fRects.get(), arrays.fColors.get(),
|
||||
count, SkBlendMode::kModulate, nullptr, &paint);
|
||||
}
|
||||
|
||||
void visitFields(SkFieldVisitor* v) override {
|
||||
SkString oldPath = fPath,
|
||||
oldName = fName;
|
||||
|
||||
v->visit("Path", fPath);
|
||||
v->visit("Name", fName);
|
||||
v->visit("Columns", fCols);
|
||||
v->visit("Rows", fRows);
|
||||
|
||||
fCols = SkTMax(fCols, 1);
|
||||
fRows = SkTMax(fRows, 1);
|
||||
if (oldPath != fPath || oldName != fName) {
|
||||
fDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
SkString fPath;
|
||||
SkString fName;
|
||||
int fCols;
|
||||
int fRows;
|
||||
|
||||
void rebuildIfDirty(const skresources::ResourceProvider* resourceProvider) {
|
||||
if (!fDirty) {
|
||||
return;
|
||||
}
|
||||
|
||||
void prepare(const skresources::ResourceProvider* resourceProvider) override {
|
||||
fImage.reset();
|
||||
if (auto asset = resourceProvider->loadImageAsset(fPath.c_str(), fName.c_str(), nullptr)) {
|
||||
fImage = asset->getFrame(0);
|
||||
@ -178,12 +148,23 @@ private:
|
||||
SkDebugf("Could not load image \"%s:%s\"\n", fPath.c_str(), fName.c_str());
|
||||
fImage = make_circle_image(1);
|
||||
}
|
||||
fDirty = false;
|
||||
}
|
||||
|
||||
void visitFields(SkFieldVisitor* v) override {
|
||||
v->visit("Path", fPath);
|
||||
v->visit("Name", fName);
|
||||
v->visit("Columns", fCols);
|
||||
v->visit("Rows", fRows);
|
||||
}
|
||||
|
||||
private:
|
||||
SkString fPath;
|
||||
SkString fName;
|
||||
int fCols;
|
||||
int fRows;
|
||||
|
||||
// Cached
|
||||
sk_sp<SkImage> fImage;
|
||||
bool fDirty;
|
||||
};
|
||||
|
||||
void SkParticleDrawable::RegisterDrawableTypes() {
|
||||
|
@ -100,30 +100,26 @@ SkParticleEffectParams::SkParticleEffectParams()
|
||||
: fMaxCount(128)
|
||||
, fDrawable(nullptr)
|
||||
, fEffectCode(kDefaultEffectCode)
|
||||
, fParticleCode(kDefaultParticleCode) {
|
||||
this->rebuild();
|
||||
}
|
||||
, fParticleCode(kDefaultParticleCode) {}
|
||||
|
||||
void SkParticleEffectParams::visitFields(SkFieldVisitor* v) {
|
||||
SkString oldEffectCode = fEffectCode;
|
||||
SkString oldParticleCode = fParticleCode;
|
||||
|
||||
v->visit("MaxCount", fMaxCount);
|
||||
|
||||
v->visit("Drawable", fDrawable);
|
||||
|
||||
v->visit("EffectCode", fEffectCode);
|
||||
v->visit("Code", fParticleCode);
|
||||
|
||||
v->visit("Bindings", fBindings);
|
||||
|
||||
// TODO: Or, if any change to binding metadata?
|
||||
if (fParticleCode != oldParticleCode || fEffectCode != oldEffectCode) {
|
||||
this->rebuild();
|
||||
}
|
||||
}
|
||||
|
||||
void SkParticleEffectParams::rebuild() {
|
||||
void SkParticleEffectParams::prepare(const skresources::ResourceProvider* resourceProvider) {
|
||||
for (auto& binding : fBindings) {
|
||||
if (binding) {
|
||||
binding->prepare(resourceProvider);
|
||||
}
|
||||
}
|
||||
if (fDrawable) {
|
||||
fDrawable->prepare(resourceProvider);
|
||||
}
|
||||
|
||||
auto buildProgram = [this](const SkSL::String& code, Program* p) {
|
||||
SkSL::Compiler compiler;
|
||||
SkSL::Program::Settings settings;
|
||||
@ -169,11 +165,8 @@ void SkParticleEffectParams::rebuild() {
|
||||
buildProgram(particleCode, &fParticleProgram);
|
||||
}
|
||||
|
||||
SkParticleEffect::SkParticleEffect(sk_sp<SkParticleEffectParams> params,
|
||||
sk_sp<skresources::ResourceProvider> rp,
|
||||
const SkRandom& random)
|
||||
SkParticleEffect::SkParticleEffect(sk_sp<SkParticleEffectParams> params, const SkRandom& random)
|
||||
: fParams(std::move(params))
|
||||
, fResourceProvider(std::move(rp))
|
||||
, fRandom(random)
|
||||
, fLooping(false)
|
||||
, fCount(0)
|
||||
@ -218,7 +211,7 @@ void SkParticleEffect::start(double now, bool looping, SkPoint position, SkVecto
|
||||
void SkParticleEffect::processEffectSpawnRequests(double now) {
|
||||
for (const auto& spawnReq : fSpawnRequests) {
|
||||
sk_sp<SkParticleEffect> newEffect(new SkParticleEffect(std::move(spawnReq.fParams),
|
||||
fResourceProvider, fRandom));
|
||||
fRandom));
|
||||
fRandom.nextU();
|
||||
|
||||
newEffect->start(now, spawnReq.fLoop, fState.fPosition, fState.fHeading, fState.fScale,
|
||||
@ -249,7 +242,6 @@ void SkParticleEffect::processParticleSpawnRequests(double now, int start) {
|
||||
for (const auto& spawnReq : fSpawnRequests) {
|
||||
int idx = start + spawnReq.fIndex;
|
||||
sk_sp<SkParticleEffect> newEffect(new SkParticleEffect(std::move(spawnReq.fParams),
|
||||
fResourceProvider,
|
||||
fParticles.fRandom[idx]));
|
||||
newEffect->start(now, spawnReq.fLoop,
|
||||
{ data[SkParticles::kPositionX ][idx],
|
||||
@ -472,7 +464,7 @@ void SkParticleEffect::draw(SkCanvas* canvas) {
|
||||
if (this->isAlive(false) && fParams->fDrawable) {
|
||||
SkPaint paint;
|
||||
paint.setFilterQuality(SkFilterQuality::kMedium_SkFilterQuality);
|
||||
fParams->fDrawable->draw(fResourceProvider.get(), canvas, fParticles, fCount, paint);
|
||||
fParams->fDrawable->draw(canvas, fParticles, fCount, paint);
|
||||
}
|
||||
|
||||
for (const auto& subEffect : fSubEffects) {
|
||||
|
@ -58,17 +58,16 @@ public:
|
||||
fTreeStack.push_back(true);
|
||||
}
|
||||
|
||||
#define IF_OPEN(WIDGET) if (fTreeStack.back()) { WIDGET; }
|
||||
|
||||
void visit(const char* name, float& f) override {
|
||||
IF_OPEN(ImGui::DragFloat(item(name), &f))
|
||||
fDirty = (fTreeStack.back() && ImGui::DragFloat(item(name), &f)) || fDirty;
|
||||
}
|
||||
void visit(const char* name, int& i) override {
|
||||
IF_OPEN(ImGui::DragInt(item(name), &i))
|
||||
fDirty = (fTreeStack.back() && ImGui::DragInt(item(name), &i)) || fDirty;
|
||||
}
|
||||
void visit(const char* name, bool& b) override {
|
||||
IF_OPEN(ImGui::Checkbox(item(name), &b))
|
||||
fDirty = (fTreeStack.back() && ImGui::Checkbox(item(name), &b)) || fDirty;
|
||||
}
|
||||
|
||||
void visit(const char* name, SkString& s) override {
|
||||
if (fTreeStack.back()) {
|
||||
int lines = count_lines(s);
|
||||
@ -76,48 +75,26 @@ public:
|
||||
if (lines > 1) {
|
||||
ImGui::LabelText("##Label", "%s", name);
|
||||
ImVec2 boxSize(-1.0f, ImGui::GetTextLineHeight() * (lines + 1));
|
||||
ImGui::InputTextMultiline(item(name), s.writable_str(), s.size() + 1, boxSize,
|
||||
flags, InputTextCallback, &s);
|
||||
fDirty = ImGui::InputTextMultiline(item(name), s.writable_str(), s.size() + 1,
|
||||
boxSize, flags, InputTextCallback, &s)
|
||||
|| fDirty;
|
||||
} else {
|
||||
ImGui::InputText(item(name), s.writable_str(), s.size() + 1, flags,
|
||||
InputTextCallback, &s);
|
||||
fDirty = ImGui::InputText(item(name), s.writable_str(), s.size() + 1, flags,
|
||||
InputTextCallback, &s)
|
||||
|| fDirty;
|
||||
}
|
||||
}
|
||||
}
|
||||
void visit(const char* name, int& i, const EnumStringMapping* map, int count) override {
|
||||
if (fTreeStack.back()) {
|
||||
const char* curStr = EnumToString(i, map, count);
|
||||
if (ImGui::BeginCombo(item(name), curStr ? curStr : "Unknown")) {
|
||||
for (int j = 0; j < count; ++j) {
|
||||
if (ImGui::Selectable(map[j].fName, i == map[j].fValue)) {
|
||||
i = map[j].fValue;
|
||||
}
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void visit(const char* name, SkPoint& p) override {
|
||||
if (fTreeStack.back()) {
|
||||
ImGui::DragFloat2(item(name), &p.fX);
|
||||
gDragPoints.push_back(&p);
|
||||
}
|
||||
}
|
||||
void visit(const char* name, SkColor4f& c) override {
|
||||
IF_OPEN(ImGui::ColorEdit4(item(name), c.vec()))
|
||||
}
|
||||
|
||||
#undef IF_OPEN
|
||||
|
||||
void visit(sk_sp<SkReflected>& e, const SkReflected::Type* baseType) override {
|
||||
if (fTreeStack.back()) {
|
||||
const SkReflected::Type* curType = e ? e->getType() : nullptr;
|
||||
if (ImGui::BeginCombo("Type", curType ? curType->fName : "Null")) {
|
||||
auto visitType = [baseType, curType, &e](const SkReflected::Type* t) {
|
||||
auto visitType = [baseType, curType, &e, this](const SkReflected::Type* t) {
|
||||
if (t->fFactory && (t == baseType || t->isDerivedFrom(baseType)) &&
|
||||
ImGui::Selectable(t->fName, curType == t)) {
|
||||
e = t->fFactory();
|
||||
fDirty = true;
|
||||
}
|
||||
};
|
||||
SkReflected::VisitTypes(visitType);
|
||||
@ -151,6 +128,7 @@ public:
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("+")) {
|
||||
++count;
|
||||
fDirty = true;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
@ -163,6 +141,8 @@ public:
|
||||
return edit;
|
||||
}
|
||||
|
||||
bool fDirty = false;
|
||||
|
||||
private:
|
||||
const char* item(const char* name) {
|
||||
if (name) {
|
||||
@ -179,16 +159,7 @@ private:
|
||||
if (ImGui::Button("X")) {
|
||||
edit.fVerb = ArrayEdit::Verb::kRemove;
|
||||
edit.fIndex = index;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("^")) {
|
||||
edit.fVerb = ArrayEdit::Verb::kMoveForward;
|
||||
edit.fIndex = index;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("v")) {
|
||||
edit.fVerb = ArrayEdit::Verb::kMoveForward;
|
||||
edit.fIndex = index + 1;
|
||||
fDirty = true;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
|
||||
@ -223,6 +194,7 @@ void ParticlesSlide::loadEffects(const char* dirname) {
|
||||
skjson::DOM dom(static_cast<const char*>(fileData->data()), fileData->size());
|
||||
SkFromJsonVisitor fromJson(dom.root());
|
||||
effect.fParams->visitFields(&fromJson);
|
||||
effect.fParams->prepare(fResourceProvider.get());
|
||||
fLoaded.push_back(effect);
|
||||
}
|
||||
}
|
||||
@ -283,7 +255,7 @@ void ParticlesSlide::draw(SkCanvas* canvas) {
|
||||
ImGui::PushID(i);
|
||||
if (fAnimated && ImGui::Button("Play")) {
|
||||
sk_sp<SkParticleEffect> effect(new SkParticleEffect(fLoaded[i].fParams,
|
||||
fResourceProvider, fRandom));
|
||||
fRandom));
|
||||
effect->start(fAnimationTime, looped);
|
||||
fRunning.push_back({ fPlayPosition, fLoaded[i].fName, effect, false });
|
||||
fRandom.nextU();
|
||||
@ -296,6 +268,10 @@ void ParticlesSlide::draw(SkCanvas* canvas) {
|
||||
if (ImGui::TreeNode("##Details")) {
|
||||
fLoaded[i].fParams->visitFields(&gui);
|
||||
ImGui::TreePop();
|
||||
if (gui.fDirty) {
|
||||
fLoaded[i].fParams->prepare(fResourceProvider.get());
|
||||
gui.fDirty = false;
|
||||
}
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user