Refactor and further generalization of particle model
- Collapsed the per-particle data into a single struct, and
use that to communicate with drawables, too. Let the drawables
manage allocation of xforms, colors, etc. Helpful for non-atlas
drawables, and just to keep the effect code simpler.
- Having all of the params in a single struct allows us to move
the remaining animated behaviors into affectors (color/frame).
- Added SkColorCurve, which works like SkCurve for SkColor4f.
Use that to create a color affector (rather than simple
start/end colors in the effect params).
- Also put the stable random in SkParticleState. This is going
to be necessary if/when we change affectors to operate on all
particles (rather than one at a time). Still need to move t
value into the particle struct (or eval it from the lifetime
params on demand).
Change-Id: Icf39116acbfd5d6e8eb91e9affbd8898d106211d
Reviewed-on: https://skia-review.googlesource.com/c/193473
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
diff --git a/modules/particles/include/SkCurve.h b/modules/particles/include/SkCurve.h
index 58c8e2f..b3c0ca6 100644
--- a/modules/particles/include/SkCurve.h
+++ b/modules/particles/include/SkCurve.h
@@ -8,6 +8,7 @@
#ifndef SkCurve_DEFINED
#define SkCurve_DEFINED
+#include "SkColor.h"
#include "SkScalar.h"
#include "SkTArray.h"
@@ -78,4 +79,46 @@
SkTArray<SkCurveSegment, true> fSegments;
};
+/**
+ * SkColorCurve is similar to SkCurve, but keyframes 4D values - specifically colors. Because
+ * negative colors rarely make sense, SkColorCurves do not support bidirectional segments, but
+ * support all other features (including cubic interpolation).
+ */
+
+struct SkColorCurveSegment {
+ SkColorCurveSegment() {
+ for (int i = 0; i < 4; ++i) {
+ fMin[i] = { 1.0f, 1.0f, 1.0f, 1.0f };
+ fMax[i] = { 1.0f, 1.0f, 1.0f, 1.0f };
+ }
+ }
+
+ SkColor4f eval(SkScalar x, SkRandom& random) const;
+ void visitFields(SkFieldVisitor* v);
+
+ void setConstant(SkColor4f c) {
+ fConstant = true;
+ fRanged = false;
+ fMin[0] = c;
+ }
+
+ SkColor4f fMin[4];
+ SkColor4f fMax[4];
+
+ bool fConstant = true;
+ bool fRanged = false;
+};
+
+struct SkColorCurve {
+ SkColorCurve(SkColor4f c = { 1.0f, 1.0f, 1.0f, 1.0f }) {
+ fSegments.push_back().setConstant(c);
+ }
+
+ SkColor4f eval(SkScalar x, SkRandom& random) const;
+ void visitFields(SkFieldVisitor* v);
+
+ SkTArray<SkScalar, true> fXValues;
+ SkTArray<SkColorCurveSegment, true> fSegments;
+};
+
#endif // SkCurve_DEFINED
diff --git a/modules/particles/include/SkParticleAffector.h b/modules/particles/include/SkParticleAffector.h
index 82570db..7de9fbe 100644
--- a/modules/particles/include/SkParticleAffector.h
+++ b/modules/particles/include/SkParticleAffector.h
@@ -12,15 +12,16 @@
#include "SkPoint.h"
+struct SkColorCurve;
struct SkCurve;
-struct SkParticlePoseAndVelocity;
+struct SkParticleState;
struct SkParticleUpdateParams;
class SkParticleAffector : public SkReflected {
public:
REFLECTED_ABSTRACT(SkParticleAffector, SkReflected)
- virtual void apply(SkParticleUpdateParams& params, SkParticlePoseAndVelocity& pv) = 0;
+ virtual void apply(SkParticleUpdateParams& params, SkParticleState& ps) = 0;
static void RegisterAffectorTypes();
@@ -31,7 +32,9 @@
SkScalar invSquare);
static sk_sp<SkParticleAffector> MakeOrientAlongVelocity();
- static sk_sp<SkParticleAffector> MakeSizeAffector(const SkCurve& curve);
+ static sk_sp<SkParticleAffector> MakeSize(const SkCurve& curve);
+ static sk_sp<SkParticleAffector> MakeFrame(const SkCurve& curve);
+ static sk_sp<SkParticleAffector> MakeColor(const SkColorCurve& curve);
};
#endif // SkParticleAffector_DEFINED
diff --git a/modules/particles/include/SkParticleData.h b/modules/particles/include/SkParticleData.h
index b99576b..54e0244 100644
--- a/modules/particles/include/SkParticleData.h
+++ b/modules/particles/include/SkParticleData.h
@@ -8,6 +8,7 @@
#ifndef SkParticleData_DEFINED
#define SkParticleData_DEFINED
+#include "SkColor.h"
#include "SkPoint.h"
#include "SkRandom.h"
#include "SkRSXform.h"
@@ -35,14 +36,18 @@
SkScalar fAngular;
};
-struct SkParticlePoseAndVelocity {
+struct SkParticleState {
+ double fTimeOfBirth;
+ double fTimeOfDeath;
SkParticlePose fPose;
SkParticleVelocity fVelocity;
+ SkColor4f fColor;
+ SkScalar fFrame; // Parameter to drawable for animated sprites, etc.
+ SkRandom fStableRandom;
};
struct SkParticleUpdateParams {
SkRandom* fRandom;
- SkRandom* fStableRandom;
float fDeltaTime;
float fParticleT;
};
diff --git a/modules/particles/include/SkParticleDrawable.h b/modules/particles/include/SkParticleDrawable.h
index aedfdb1..a47b24d 100644
--- a/modules/particles/include/SkParticleDrawable.h
+++ b/modules/particles/include/SkParticleDrawable.h
@@ -10,20 +10,17 @@
#include "SkReflected.h"
-#include "SkColor.h"
-
class SkCanvas;
+struct SkParticleState;
class SkPaint;
-struct SkRSXform;
class SkString;
class SkParticleDrawable : public SkReflected {
public:
REFLECTED_ABSTRACT(SkParticleDrawable, SkReflected)
- virtual void draw(SkCanvas* canvas, const SkRSXform xform[], const float tex[],
- const SkColor colors[], int count, const SkPaint* paint) = 0;
- virtual SkPoint center() const = 0;
+ virtual void draw(SkCanvas* canvas, const SkParticleState particles[], int count,
+ const SkPaint* paint) = 0;
static void RegisterDrawableTypes();
diff --git a/modules/particles/include/SkParticleEffect.h b/modules/particles/include/SkParticleEffect.h
index 7397976..c636409 100644
--- a/modules/particles/include/SkParticleEffect.h
+++ b/modules/particles/include/SkParticleEffect.h
@@ -22,7 +22,6 @@
class SkParticleAffector;
class SkParticleDrawable;
class SkParticleEmitter;
-struct SkRSXform;
class SkParticleEffectParams : public SkRefCnt {
public:
@@ -30,8 +29,6 @@
float fEffectDuration = 1.0f;
float fRate = 8.0f;
SkCurve fLifetime = 1.0f;
- SkColor4f fStartColor = { 1.0f, 1.0f, 1.0f, 1.0f };
- SkColor4f fEndColor = { 1.0f, 1.0f, 1.0f, 1.0f };
// Drawable (image, sprite sheet, etc.)
sk_sp<SkParticleDrawable> fDrawable;
@@ -63,15 +60,6 @@
private:
void setCapacity(int capacity);
- struct Particle {
- double fTimeOfBirth;
- double fTimeOfDeath;
- SkRandom fStableRandom;
-
- // Texture coord rects and colors are stored in parallel arrays for drawAtlas.
- SkParticlePoseAndVelocity fPV;
- };
-
sk_sp<SkParticleEffectParams> fParams;
SkRandom fRandom;
@@ -83,10 +71,8 @@
double fLastTime;
float fSpawnRemainder;
- SkAutoTMalloc<Particle> fParticles;
- SkAutoTMalloc<SkRSXform> fXforms;
- SkAutoTMalloc<float> fFrames;
- SkAutoTMalloc<SkColor> fColors;
+ SkAutoTMalloc<SkParticleState> fParticles;
+ SkAutoTMalloc<SkRandom> fStableRandoms;
// Cached
int fCapacity;
diff --git a/modules/particles/src/SkCurve.cpp b/modules/particles/src/SkCurve.cpp
index ac3d983..aa03853 100644
--- a/modules/particles/src/SkCurve.cpp
+++ b/modules/particles/src/SkCurve.cpp
@@ -15,6 +15,15 @@
return pts[0]*ix*ix*ix + pts[1]*3*ix*ix*x + pts[2]*3*ix*x*x + pts[3]*x*x*x;
}
+static SkColor4f operator+(SkColor4f c1, SkColor4f c2) {
+ return { c1.fR + c2.fR, c1.fG + c2.fG, c1.fB + c2.fB, c1.fA + c2.fA };
+}
+
+static SkColor4f eval_cubic(const SkColor4f* pts, SkScalar x) {
+ SkScalar ix = (1 - x);
+ return pts[0]*(ix*ix*ix) + pts[1]*(3*ix*ix*x) + pts[2]*(3*ix*x*x) + pts[3]*(x*x*x);
+}
+
SkScalar SkCurveSegment::eval(SkScalar x, SkRandom& random) const {
SkScalar result = fConstant ? fMin[0] : eval_cubic(fMin, x);
if (fRanged) {
@@ -93,3 +102,59 @@
}
}
}
+
+SkColor4f SkColorCurveSegment::eval(SkScalar x, SkRandom& random) const {
+ SkColor4f result = fConstant ? fMin[0] : eval_cubic(fMin, x);
+ if (fRanged) {
+ result = result +
+ ((fConstant ? fMax[0] : eval_cubic(fMax, x)) + (result * -1)) * random.nextF();
+ }
+ return result;
+}
+
+void SkColorCurveSegment::visitFields(SkFieldVisitor* v) {
+ v->visit("Constant", fConstant);
+ v->visit("Ranged", fRanged);
+ v->visit("A0", fMin[0]);
+ v->visit("B0", fMin[1]);
+ v->visit("C0", fMin[2]);
+ v->visit("D0", fMin[3]);
+ v->visit("A1", fMax[0]);
+ v->visit("B1", fMax[1]);
+ v->visit("C1", fMax[2]);
+ v->visit("D1", fMax[3]);
+}
+
+SkColor4f SkColorCurve::eval(SkScalar x, SkRandom& random) const {
+ SkASSERT(fSegments.count() == fXValues.count() + 1);
+
+ int i = 0;
+ for (; i < fXValues.count(); ++i) {
+ if (x <= fXValues[i]) {
+ break;
+ }
+ }
+
+ SkScalar rangeMin = (i == 0) ? 0.0f : fXValues[i - 1];
+ SkScalar rangeMax = (i == fXValues.count()) ? 1.0f : fXValues[i];
+ SkScalar segmentX = (x - rangeMin) / (rangeMax - rangeMin);
+ if (!SkScalarIsFinite(segmentX)) {
+ segmentX = rangeMin;
+ }
+ SkASSERT(0.0f <= segmentX && segmentX <= 1.0f);
+ return fSegments[i].eval(segmentX, random);
+}
+
+void SkColorCurve::visitFields(SkFieldVisitor* v) {
+ v->visit("XValues", fXValues);
+ v->visit("Segments", fSegments);
+
+ // Validate and fixup
+ if (fSegments.empty()) {
+ fSegments.push_back().setConstant(SkColor4f{ 1.0f, 1.0f, 1.0f, 1.0f });
+ }
+ fXValues.resize_back(fSegments.count() - 1);
+ for (int i = 0; i < fXValues.count(); ++i) {
+ fXValues[i] = SkTPin(fXValues[i], i > 0 ? fXValues[i - 1] : 0.0f, 1.0f);
+ }
+}
diff --git a/modules/particles/src/SkParticleAffector.cpp b/modules/particles/src/SkParticleAffector.cpp
index 24a84d1..7cdf0cb 100644
--- a/modules/particles/src/SkParticleAffector.cpp
+++ b/modules/particles/src/SkParticleAffector.cpp
@@ -22,15 +22,15 @@
REFLECTED(SkLinearVelocityAffector, SkParticleAffector)
- void apply(SkParticleUpdateParams& params, SkParticlePoseAndVelocity& pv) override {
- float angle = fAngle.eval(params.fParticleT, *params.fStableRandom);
+ void apply(SkParticleUpdateParams& params, SkParticleState& ps) override {
+ float angle = fAngle.eval(params.fParticleT, ps.fStableRandom);
SkScalar c, s = SkScalarSinCos(SkDegreesToRadians(angle), &c);
- float strength = fStrength.eval(params.fParticleT, *params.fStableRandom);
+ float strength = fStrength.eval(params.fParticleT, ps.fStableRandom);
SkVector force = { c * strength, s * strength };
if (fForce) {
- pv.fVelocity.fLinear += force * params.fDeltaTime;
+ ps.fVelocity.fLinear += force * params.fDeltaTime;
} else {
- pv.fVelocity.fLinear = force;
+ ps.fVelocity.fLinear = force;
}
}
@@ -54,11 +54,11 @@
REFLECTED(SkPointForceAffector, SkParticleAffector)
- void apply(SkParticleUpdateParams& params, SkParticlePoseAndVelocity& pv) override {
- SkVector toPoint = fPoint - pv.fPose.fPosition;
+ void apply(SkParticleUpdateParams& params, SkParticleState& ps) override {
+ SkVector toPoint = fPoint - ps.fPose.fPosition;
SkScalar lenSquare = toPoint.dot(toPoint);
toPoint.normalize();
- pv.fVelocity.fLinear += toPoint * (fConstant + (fInvSquare/lenSquare)) * params.fDeltaTime;
+ ps.fVelocity.fLinear += toPoint * (fConstant + (fInvSquare/lenSquare)) * params.fDeltaTime;
}
void visitFields(SkFieldVisitor* v) override {
@@ -79,12 +79,12 @@
REFLECTED(SkOrientAlongVelocityAffector, SkParticleAffector)
- void apply(SkParticleUpdateParams& params, SkParticlePoseAndVelocity& pv) override {
- SkVector heading = pv.fVelocity.fLinear;
+ void apply(SkParticleUpdateParams& params, SkParticleState& ps) override {
+ SkVector heading = ps.fVelocity.fLinear;
if (!heading.normalize()) {
heading.set(0, -1);
}
- pv.fPose.fHeading = heading;
+ ps.fPose.fHeading = heading;
}
void visitFields(SkFieldVisitor*) override {}
@@ -96,8 +96,8 @@
REFLECTED(SkSizeAffector, SkParticleAffector)
- void apply(SkParticleUpdateParams& params, SkParticlePoseAndVelocity& pv) override {
- pv.fPose.fScale = fCurve.eval(params.fParticleT, *params.fStableRandom);
+ void apply(SkParticleUpdateParams& params, SkParticleState& ps) override {
+ ps.fPose.fScale = fCurve.eval(params.fParticleT, ps.fStableRandom);
}
void visitFields(SkFieldVisitor* v) override {
@@ -108,12 +108,51 @@
SkCurve fCurve;
};
+class SkFrameAffector : public SkParticleAffector {
+public:
+ SkFrameAffector(const SkCurve& curve = 1.0f) : fCurve(curve) {}
+
+ REFLECTED(SkFrameAffector, SkParticleAffector)
+
+ void apply(SkParticleUpdateParams& params, SkParticleState& ps) override {
+ ps.fFrame = fCurve.eval(params.fParticleT, ps.fStableRandom);
+ }
+
+ void visitFields(SkFieldVisitor* v) override {
+ v->visit("Curve", fCurve);
+ }
+
+private:
+ SkCurve fCurve;
+};
+
+class SkColorAffector : public SkParticleAffector {
+public:
+ SkColorAffector(const SkColorCurve& curve = SkColor4f{ 1.0f, 1.0f, 1.0f, 1.0f })
+ : fCurve(curve) {}
+
+ REFLECTED(SkColorAffector, SkParticleAffector)
+
+ void apply(SkParticleUpdateParams& params, SkParticleState& ps) override {
+ ps.fColor = fCurve.eval(params.fParticleT, ps.fStableRandom);
+ }
+
+ void visitFields(SkFieldVisitor* v) override {
+ v->visit("Curve", fCurve);
+ }
+
+private:
+ SkColorCurve fCurve;
+};
+
void SkParticleAffector::RegisterAffectorTypes() {
REGISTER_REFLECTED(SkParticleAffector);
REGISTER_REFLECTED(SkLinearVelocityAffector);
REGISTER_REFLECTED(SkPointForceAffector);
REGISTER_REFLECTED(SkOrientAlongVelocityAffector);
REGISTER_REFLECTED(SkSizeAffector);
+ REGISTER_REFLECTED(SkFrameAffector);
+ REGISTER_REFLECTED(SkColorAffector);
}
sk_sp<SkParticleAffector> SkParticleAffector::MakeLinearVelocity(const SkCurve& angle,
@@ -131,6 +170,14 @@
return sk_sp<SkParticleAffector>(new SkOrientAlongVelocityAffector());
}
-sk_sp<SkParticleAffector> SkParticleAffector::MakeSizeAffector(const SkCurve& curve) {
+sk_sp<SkParticleAffector> SkParticleAffector::MakeSize(const SkCurve& curve) {
return sk_sp<SkParticleAffector>(new SkSizeAffector(curve));
}
+
+sk_sp<SkParticleAffector> SkParticleAffector::MakeFrame(const SkCurve& curve) {
+ return sk_sp<SkParticleAffector>(new SkFrameAffector(curve));
+}
+
+sk_sp<SkParticleAffector> SkParticleAffector::MakeColor(const SkColorCurve& curve) {
+ return sk_sp<SkParticleAffector>(new SkColorAffector(curve));
+}
diff --git a/modules/particles/src/SkParticleDrawable.cpp b/modules/particles/src/SkParticleDrawable.cpp
index fb0c150..0adc87d 100644
--- a/modules/particles/src/SkParticleDrawable.cpp
+++ b/modules/particles/src/SkParticleDrawable.cpp
@@ -12,6 +12,7 @@
#include "SkCanvas.h"
#include "SkImage.h"
#include "SkPaint.h"
+#include "SkParticleData.h"
#include "SkRect.h"
#include "SkSurface.h"
#include "SkString.h"
@@ -27,6 +28,22 @@
return surface->makeImageSnapshot();
}
+struct DrawAtlasArrays {
+ DrawAtlasArrays(const SkParticleState particles[], int count, SkPoint center)
+ : fXforms(count)
+ , fRects(count)
+ , fColors(count) {
+ for (int i = 0; i < count; ++i) {
+ fXforms[i] = particles[i].fPose.asRSXform(center);
+ fColors[i] = particles[i].fColor.toSkColor();
+ }
+ }
+
+ SkAutoTMalloc<SkRSXform> fXforms;
+ SkAutoTMalloc<SkRect> fRects;
+ SkAutoTMalloc<SkColor> fColors;
+};
+
class SkCircleDrawable : public SkParticleDrawable {
public:
SkCircleDrawable(int radius = 1)
@@ -36,18 +53,15 @@
REFLECTED(SkCircleDrawable, SkParticleDrawable)
- void draw(SkCanvas* canvas, const SkRSXform xform[], const float tex[], const SkColor colors[],
- int count, const SkPaint* paint) override {
- SkAutoTMalloc<SkRect> texRects(count);
+ void draw(SkCanvas* canvas, const SkParticleState particles[], int count,
+ const SkPaint* paint) override {
+ SkPoint center = { SkIntToScalar(fRadius), SkIntToScalar(fRadius) };
+ DrawAtlasArrays arrays(particles, count, center);
for (int i = 0; i < count; ++i) {
- texRects[i].set(0.0f, 0.0f, fImage->width(), fImage->height());
+ arrays.fRects[i].set(0.0f, 0.0f, fImage->width(), fImage->height());
}
- canvas->drawAtlas(fImage, xform, texRects.get(), colors, count,
- SkBlendMode::kModulate, nullptr, paint);
- }
-
- SkPoint center() const override {
- return { SkIntToScalar(fRadius), SkIntToScalar(fRadius) };
+ canvas->drawAtlas(fImage, arrays.fXforms.get(), arrays.fRects.get(), arrays.fColors.get(),
+ count, SkBlendMode::kModulate, nullptr, paint);
}
void visitFields(SkFieldVisitor* v) override {
@@ -80,27 +94,22 @@
REFLECTED(SkImageDrawable, SkParticleDrawable)
- void draw(SkCanvas* canvas, const SkRSXform xform[], const float tex[], const SkColor colors[],
- int count, const SkPaint* paint) override {
- SkAutoTMalloc<SkRect> texRects(count);
-
+ void draw(SkCanvas* canvas, const SkParticleState particles[], int count,
+ const SkPaint* paint) override {
SkRect baseRect = getBaseRect();
- int frameCount = fCols * fRows;
+ SkPoint center = { baseRect.width() * 0.5f, baseRect.height() * 0.5f };
+ DrawAtlasArrays arrays(particles, count, center);
+ int frameCount = fCols * fRows;
for (int i = 0; i < count; ++i) {
- int frame = static_cast<int>(tex[i] * frameCount + 0.5f);
+ int frame = static_cast<int>(particles[i].fFrame * frameCount + 0.5f);
frame = SkTPin(frame, 0, frameCount - 1);
int row = frame / fCols;
int col = frame % fCols;
- texRects[i] = baseRect.makeOffset(col * baseRect.width(), row * baseRect.height());
+ arrays.fRects[i] = baseRect.makeOffset(col * baseRect.width(), row * baseRect.height());
}
- canvas->drawAtlas(fImage, xform, texRects.get(), colors, count,
- SkBlendMode::kModulate, nullptr, paint);
- }
-
- SkPoint center() const override {
- SkRect baseRect = getBaseRect();
- return { baseRect.width() * 0.5f, baseRect.height() * 0.5f };
+ canvas->drawAtlas(fImage, arrays.fXforms.get(), arrays.fRects.get(), arrays.fColors.get(),
+ count, SkBlendMode::kModulate, nullptr, paint);
}
void visitFields(SkFieldVisitor* v) override {
diff --git a/modules/particles/src/SkParticleEffect.cpp b/modules/particles/src/SkParticleEffect.cpp
index 39b8dd6..4dfb7c2 100644
--- a/modules/particles/src/SkParticleEffect.cpp
+++ b/modules/particles/src/SkParticleEffect.cpp
@@ -11,7 +11,6 @@
#include "SkAnimTimer.h"
#include "SkCanvas.h"
#include "SkColorData.h"
-#include "SkNx.h"
#include "SkPaint.h"
#include "SkParticleAffector.h"
#include "SkParticleDrawable.h"
@@ -24,8 +23,6 @@
v->visit("Duration", fEffectDuration);
v->visit("Rate", fRate);
v->visit("Life", fLifetime);
- v->visit("StartColor", fStartColor);
- v->visit("EndColor", fEndColor);
v->visit("Drawable", fDrawable);
v->visit("Emitter", fEmitter);
@@ -66,9 +63,6 @@
float deltaTime = static_cast<float>(now - fLastTime);
fLastTime = now;
- Sk4f startColor = Sk4f::Load(fParams->fStartColor.vec());
- Sk4f colorScale = Sk4f::Load(fParams->fEndColor.vec()) - startColor;
-
SkParticleUpdateParams updateParams;
updateParams.fDeltaTime = deltaTime;
updateParams.fRandom = &fRandom;
@@ -77,9 +71,8 @@
for (int i = 0; i < fCount; ++i) {
if (now > fParticles[i].fTimeOfDeath) {
// NOTE: This is fast, but doesn't preserve drawing order. Could be a problem...
- fParticles[i] = fParticles[fCount - 1];
- fFrames[i] = fFrames[fCount - 1];
- fColors[i] = fColors[fCount - 1];
+ fParticles[i] = fParticles[fCount - 1];
+ fStableRandoms[i] = fStableRandoms[fCount - 1];
--i;
--fCount;
}
@@ -91,24 +84,24 @@
fSpawnRemainder = desired - numToSpawn;
numToSpawn = SkTPin(numToSpawn, 0, fParams->fMaxCount - fCount);
if (fParams->fEmitter) {
- // No, this isn't "stable", but spawn affectors are only run once anyway.
- // Would it ever make sense to give the same random to all particles spawned on a given
- // frame? Having a hard time thinking when that would be useful.
- updateParams.fStableRandom = &fRandom;
- // ... and this isn't "particle" t, it's effect t.
+ // This isn't "particle" t, it's effect t.
double t = (now - fSpawnTime) / fParams->fEffectDuration;
updateParams.fParticleT = static_cast<float>(fLooping ? fmod(t, 1.0) : SkTPin(t, 0.0, 1.0));
for (int i = 0; i < numToSpawn; ++i) {
+ // Mutate our SkRandom so each particle definitely gets a different stable generator
+ fRandom.nextU();
+
fParticles[fCount].fTimeOfBirth = now;
fParticles[fCount].fTimeOfDeath = now + fParams->fLifetime.eval(updateParams.fParticleT,
fRandom);
- fParticles[fCount].fPV.fPose = fParams->fEmitter->emit(fRandom);
- fParticles[fCount].fPV.fVelocity.fLinear = { 0.0f, 0.0f };
- fParticles[fCount].fPV.fVelocity.fAngular = 0.0f;
+ fParticles[fCount].fPose = fParams->fEmitter->emit(fRandom);
+ fParticles[fCount].fVelocity.fLinear = { 0.0f, 0.0f };
+ fParticles[fCount].fVelocity.fAngular = 0.0f;
+ fParticles[fCount].fColor = { 1.0f, 1.0f, 1.0f, 1.0f };
+ fParticles[fCount].fFrame = 0.0f;
- fParticles[fCount].fStableRandom = fRandom;
- fFrames[fCount] = 0.0f;
+ fParticles[fCount].fStableRandom = fStableRandoms[fCount] = fRandom;
fCount++;
}
@@ -116,47 +109,37 @@
for (int i = fCount - numToSpawn; i < fCount; ++i) {
for (auto affector : fParams->fSpawnAffectors) {
if (affector) {
- affector->apply(updateParams, fParticles[i].fPV);
+ affector->apply(updateParams, fParticles[i]);
}
}
}
}
+ // Restore all stable random generators so update affectors get consistent behavior each frame
+ for (int i = 0; i < fCount; ++i) {
+ fParticles[i].fStableRandom = fStableRandoms[i];
+ }
+
// Apply update rules
for (int i = 0; i < fCount; ++i) {
// Compute fraction of lifetime that's elapsed
- float t = static_cast<float>((now - fParticles[i].fTimeOfBirth) /
- (fParticles[i].fTimeOfDeath - fParticles[i].fTimeOfBirth));
-
- SkRandom stableRandom = fParticles[i].fStableRandom;
- updateParams.fStableRandom = &stableRandom;
- updateParams.fParticleT = t;
-
- // Set sprite frame by lifetime (TODO: Remove, add affector)
- fFrames[i] = t;
-
- // Set color by lifetime
- fColors[i] = Sk4f_toL32(swizzle_rb(startColor + (colorScale * t)));
+ updateParams.fParticleT =
+ static_cast<float>((now - fParticles[i].fTimeOfBirth) /
+ (fParticles[i].fTimeOfDeath - fParticles[i].fTimeOfBirth));
for (auto affector : fParams->fUpdateAffectors) {
if (affector) {
- affector->apply(updateParams, fParticles[i].fPV);
+ affector->apply(updateParams, fParticles[i]);
}
}
// Integrate position / orientation
- fParticles[i].fPV.fPose.fPosition += fParticles[i].fPV.fVelocity.fLinear * deltaTime;
+ fParticles[i].fPose.fPosition += fParticles[i].fVelocity.fLinear * deltaTime;
- SkScalar c, s = SkScalarSinCos(fParticles[i].fPV.fVelocity.fAngular * deltaTime, &c);
- SkVector oldHeading = fParticles[i].fPV.fPose.fHeading;
- fParticles[i].fPV.fPose.fHeading = { oldHeading.fX * c - oldHeading.fY * s,
- oldHeading.fX * s + oldHeading.fY * c };
- }
-
- // Re-generate all xforms
- SkPoint ofs = fParams->fDrawable ? fParams->fDrawable->center() : SkPoint{ 0.0f, 0.0f };
- for (int i = 0; i < fCount; ++i) {
- fXforms[i] = fParticles[i].fPV.fPose.asRSXform(ofs);
+ SkScalar c, s = SkScalarSinCos(fParticles[i].fVelocity.fAngular * deltaTime, &c);
+ SkVector oldHeading = fParticles[i].fPose.fHeading;
+ fParticles[i].fPose.fHeading = { oldHeading.fX * c - oldHeading.fY * s,
+ oldHeading.fX * s + oldHeading.fY * c };
}
// Mark effect as dead if we've reached the end (and are not looping)
@@ -169,16 +152,13 @@
if (this->isAlive() && fParams->fDrawable) {
SkPaint paint;
paint.setFilterQuality(SkFilterQuality::kMedium_SkFilterQuality);
- fParams->fDrawable->draw(
- canvas, fXforms.get(), fFrames.get(), fColors.get(), fCount, &paint);
+ fParams->fDrawable->draw(canvas, fParticles.get(), fCount, &paint);
}
}
void SkParticleEffect::setCapacity(int capacity) {
fParticles.realloc(capacity);
- fXforms.realloc(capacity);
- fFrames.realloc(capacity);
- fColors.realloc(capacity);
+ fStableRandoms.realloc(capacity);
fCapacity = capacity;
fCount = SkTMin(fCount, fCapacity);