Add SkParticleValue to allow further customization of curve behavior
All curves (and path affectors) are driven by an SkParticleValue. The
value can derive its value from the current defaults (age of particle
or effect), or explicitly choose the other one, a random value, or any
other particle value. Values can be range adjusted and support repeat,
clamp, and mirror tiling.
Also fixed some more issues related to resource path in the slide GUI.
Bug: skia:
Change-Id: I4755018d5b57ae2d5ec400d541055ca4fb542978
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/196760
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 076a1aa..044c0f4 100644
--- a/modules/particles/include/SkCurve.h
+++ b/modules/particles/include/SkCurve.h
@@ -9,6 +9,7 @@
#define SkCurve_DEFINED
#include "SkColor.h"
+#include "SkParticleData.h"
#include "SkScalar.h"
#include "SkTArray.h"
@@ -73,10 +74,12 @@
fSegments.push_back().setConstant(c);
}
- // Evaluate this curve at x, using random for curves that have ranged or bidirectional segments.
- SkScalar eval(SkScalar x, SkRandom& random) const;
+ SkScalar eval(const SkParticleUpdateParams& params, SkParticleState& ps) const;
void visitFields(SkFieldVisitor* v);
+ // Parameters that determine our x-value during evaluation
+ SkParticleValue fInput;
+
// It should always be true that (fXValues.count() + 1) == fSegments.count()
SkTArray<SkScalar, true> fXValues;
SkTArray<SkCurveSegment, true> fSegments;
@@ -96,7 +99,7 @@
}
}
- SkColor4f eval(SkScalar x, SkRandom& random) const;
+ SkColor4f eval(SkScalar x, SkScalar t) const;
void visitFields(SkFieldVisitor* v);
void setConstant(SkColor4f c) {
@@ -117,9 +120,10 @@
fSegments.push_back().setConstant(c);
}
- SkColor4f eval(SkScalar x, SkRandom& random) const;
+ SkColor4f eval(const SkParticleUpdateParams& params, SkParticleState& ps) const;
void visitFields(SkFieldVisitor* v);
+ SkParticleValue fInput;
SkTArray<SkScalar, true> fXValues;
SkTArray<SkColorCurveSegment, true> fSegments;
};
diff --git a/modules/particles/include/SkParticleAffector.h b/modules/particles/include/SkParticleAffector.h
index 0efc54b..634d555 100644
--- a/modules/particles/include/SkParticleAffector.h
+++ b/modules/particles/include/SkParticleAffector.h
@@ -10,24 +10,17 @@
#include "SkReflected.h"
+#include "SkParticleData.h"
#include "SkPoint.h"
struct SkColorCurve;
struct SkCurve;
-struct SkParticleState;
-struct SkParticleUpdateParams;
-
-enum SkParticleFrame {
- kWorld_ParticleFrame, // "Up" is { 0, -1 }
- kLocal_ParticleFrame, // "Up" is particle's heading
- kVelocity_ParticleFrame, // "Up" is particle's direction of travel
-};
class SkParticleAffector : public SkReflected {
public:
REFLECTED_ABSTRACT(SkParticleAffector, SkReflected)
- void apply(SkParticleUpdateParams& params, SkParticleState ps[], int count);
+ void apply(const SkParticleUpdateParams& params, SkParticleState ps[], int count);
void visitFields(SkFieldVisitor* v) override;
static void RegisterAffectorTypes();
@@ -53,7 +46,7 @@
static sk_sp<SkParticleAffector> MakeColor(const SkColorCurve& curve);
private:
- virtual void onApply(SkParticleUpdateParams& params, SkParticleState ps[], int count) = 0;
+ virtual void onApply(const SkParticleUpdateParams& params, SkParticleState ps[], int count) = 0;
bool fEnabled = true;
};
diff --git a/modules/particles/include/SkParticleData.h b/modules/particles/include/SkParticleData.h
index e0fa771..906f811 100644
--- a/modules/particles/include/SkParticleData.h
+++ b/modules/particles/include/SkParticleData.h
@@ -11,12 +11,25 @@
#include "SkColor.h"
#include "SkPoint.h"
#include "SkRandom.h"
+#include "SkReflected.h"
#include "SkRSXform.h"
/*
* Various structs used to communicate particle information among emitters, affectors, etc.
*/
+enum SkParticleFrame {
+ kWorld_ParticleFrame, // "Up" is { 0, -1 }
+ kLocal_ParticleFrame, // "Up" is particle's heading
+ kVelocity_ParticleFrame, // "Up" is particle's direction of travel
+};
+
+static constexpr SkFieldVisitor::EnumStringMapping gParticleFrameMapping[] = {
+ { kWorld_ParticleFrame, "World" },
+ { kLocal_ParticleFrame, "Local" },
+ { kVelocity_ParticleFrame, "Velocity" },
+};
+
struct SkParticlePose {
SkPoint fPosition;
SkVector fHeading;
@@ -44,10 +57,83 @@
SkColor4f fColor;
SkScalar fFrame; // Parameter to drawable for animated sprites, etc.
SkRandom fRandom;
+
+ SkVector getFrameHeading(SkParticleFrame frame) const {
+ switch (frame) {
+ case kLocal_ParticleFrame:
+ return fPose.fHeading;
+ case kVelocity_ParticleFrame: {
+ SkVector heading = fVelocity.fLinear;
+ if (!heading.normalize()) {
+ heading.set(0, -1);
+ }
+ return heading;
+ }
+ case kWorld_ParticleFrame:
+ default:
+ return SkVector{ 0, -1 };
+ }
+ }
};
struct SkParticleUpdateParams {
float fDeltaTime;
+ float fEffectAge;
+ int fAgeSource;
+};
+
+/**
+ * SkParticleValue selects a specific value to be used when evaluating a curve, position on a path,
+ * or any other affector that needs a scalar float input. An SkParticleValue starts with a source
+ * value taken from the state of the effect or particle. That can be adjusted using a scale and
+ * bias, and then reduced into the desired range (typically [0, 1]) via a chosen tile mode.
+ */
+struct SkParticleValue {
+ enum Source {
+ // Either the particle or effect age, depending on spawn or update
+ kAge_Source,
+
+ kRandom_Source,
+ kParticleAge_Source,
+ kEffectAge_Source,
+ kPositionX_Source,
+ kPositionY_Source,
+ kHeadingX_Source,
+ kHeadingY_Source,
+ kScale_Source,
+ kVelocityX_Source,
+ kVelocityY_Source,
+ kRotation_Source,
+ kColorR_Source,
+ kColorG_Source,
+ kColorB_Source,
+ kColorA_Source,
+ kSpriteFrame_Source,
+ };
+
+ enum TileMode {
+ kClamp_TileMode,
+ kRepeat_TileMode,
+ kMirror_TileMode,
+ };
+
+ void visitFields(SkFieldVisitor* v);
+ float eval(const SkParticleUpdateParams& params, SkParticleState& ps) const;
+
+ int fSource = kAge_Source;
+ int fFrame = kWorld_ParticleFrame;
+ int fTileMode = kRepeat_TileMode;
+
+ // We map fLeft -> 0 and fRight -> 1. This is easier to work with and reason about.
+ float fLeft = 0.0f;
+ float fRight = 1.0f;
+
+ // Cached from the above
+ float fScale = 1.0f;
+ float fBias = 0.0f;
+
+private:
+ float getSourceValue(const SkParticleUpdateParams& params, SkParticleState& ps) const;
};
#endif // SkParticleData_DEFINED
diff --git a/modules/particles/include/SkReflected.h b/modules/particles/include/SkReflected.h
index 0904a31..a7d787c 100644
--- a/modules/particles/include/SkReflected.h
+++ b/modules/particles/include/SkReflected.h
@@ -9,13 +9,13 @@
#define SkReflected_DEFINED
#include "SkColor.h"
-#include "SkCurve.h"
#include "SkRefCnt.h"
#include "SkString.h"
#include "SkTArray.h"
#include <string.h>
+struct SkCurve;
class SkFieldVisitor;
class SkRandom;
@@ -158,11 +158,7 @@
virtual void visit(const char*, int&, const EnumStringMapping*, int count) = 0;
// Specific virtual signature for SkCurve, to allow for heavily customized UI in SkGuiVisitor.
- virtual void visit(const char* name, SkCurve& c) {
- this->enterObject(name);
- c.visitFields(this);
- this->exitObject();
- }
+ virtual void visit(const char* name, SkCurve& c);
// 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.