blob: 964b6c168fa8edf2c29ad8992d52c3036397cb77 [file] [log] [blame]
Brian Osman7c979f52019-02-12 13:27:51 -05001/*
2* Copyright 2019 Google LLC
3*
4* Use of this source code is governed by a BSD-style license that can be
5* found in the LICENSE file.
6*/
7
8#ifndef SkParticleEffect_DEFINED
9#define SkParticleEffect_DEFINED
10
Brian Osmand46cb972019-09-12 16:25:52 -040011#include "include/core/SkColor.h"
12#include "include/core/SkPoint.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050013#include "include/core/SkRefCnt.h"
Brian Osmanfe491632019-07-25 15:14:50 -040014#include "include/core/SkString.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050015#include "include/private/SkTArray.h"
Brian Osmanfe491632019-07-25 15:14:50 -040016#include "include/private/SkTemplates.h"
Brian Osmanfe491632019-07-25 15:14:50 -040017#include "modules/particles/include/SkParticleData.h"
Brian Osmanfe491632019-07-25 15:14:50 -040018
19#include <memory>
Brian Osman7c979f52019-02-12 13:27:51 -050020
Brian Osman7c979f52019-02-12 13:27:51 -050021class SkCanvas;
Brian Osman2aa85df2019-08-30 10:59:47 -040022class SkFieldVisitor;
23class SkParticleBinding;
Brian Osman543d2e22019-02-15 14:29:38 -050024class SkParticleDrawable;
Brian Osmanfe491632019-07-25 15:14:50 -040025class SkParticleExternalValue;
26
Brian Osmand12f2782019-11-27 10:34:18 -050027namespace skresources {
28 class ResourceProvider;
29}
30
Brian Osmanfe491632019-07-25 15:14:50 -040031namespace SkSL {
Brian Osman9b8b4552019-09-30 13:23:14 -040032 class ByteCode;
Brian Osmanfe491632019-07-25 15:14:50 -040033}
34
Brian Osman7c979f52019-02-12 13:27:51 -050035class SkParticleEffectParams : public SkRefCnt {
36public:
Brian Osmanfe491632019-07-25 15:14:50 -040037 SkParticleEffectParams();
38
Brian Osmand46cb972019-09-12 16:25:52 -040039 // Maximum number of particles per instance of the effect
40 int fMaxCount;
Brian Osman7c979f52019-02-12 13:27:51 -050041
Brian Osman3da607e2019-07-26 15:11:46 -040042 // What is drawn for each particle? (Image, shape, sprite sheet, etc.)
43 // See SkParticleDrawable::Make*
Brian Osman543d2e22019-02-15 14:29:38 -050044 sk_sp<SkParticleDrawable> fDrawable;
Brian Osman7c979f52019-02-12 13:27:51 -050045
Brian Osmand46cb972019-09-12 16:25:52 -040046 // Particle behavior is driven by two chunks of SkSL code. Effect functions are defined in
47 // fEffectCode, and get a mutable Effect struct:
Brian Osman3da607e2019-07-26 15:11:46 -040048 //
Brian Osmand46cb972019-09-12 16:25:52 -040049 // struct Effect {
Brian Osmand1846d82019-09-17 13:32:32 -040050 // float age; // Normalized age of the effect
51 // float lifetime; // Effect's duration, in seconds - script should set this in effectSpawn
52 // int loop; // Number of loops that have elapsed (0 on initial spawn)
53 // float rate; // Rate to generate new particles (particles / second)
54 // int burst; // Number of particles to emit in a single update
55 // // Set during spawn to emit that many at once on each loop
Brian Osmand46cb972019-09-12 16:25:52 -040056 //
57 // // Everything below this line controls the state of the effect, which is also the
58 // // default values for new particles.
59 // float2 pos = { 0, 0 }; // Local position
Brian Osman3da607e2019-07-26 15:11:46 -040060 // float2 dir = { 0, -1 }; // Heading. Should be a normalized vector.
61 // float scale = 1; // Size, normalized relative to the drawable's native size
62 // float2 vel = { 0, 0 }; // Linear velocity, in (units / second)
63 // float spin = 0; // Angular velocity, in (radians / second)
64 // float4 color = { 1, 1, 1, 1 }; // RGBA color
65 // float frame = 0; // Normalized sprite index for multi-frame drawables
Brian Osman95c26ef2020-02-10 13:45:22 -050066 // uint flags = 0; // Arbitrary state for use by script
67 // uint seed = 0; // Random seed, used with rand() (see below)
Brian Osman3da607e2019-07-26 15:11:46 -040068 // };
69 //
Brian Osmand46cb972019-09-12 16:25:52 -040070 // Particle functions are defined in fParticleCode, and get a mutable Particle struct, as well
71 // as a uniform copy of the current Effect, named 'effect'.
Brian Osman3da607e2019-07-26 15:11:46 -040072 //
Brian Osmand46cb972019-09-12 16:25:52 -040073 // struct Particle {
74 // float age;
75 // float lifetime;
76 // float2 pos;
77 // float2 dir;
78 // float scale;
79 // float2 vel;
80 // float spin;
81 // float4 color;
82 // float frame;
Brian Osman95c26ef2020-02-10 13:45:22 -050083 // uint flags;
84 // uint seed;
Brian Osmand46cb972019-09-12 16:25:52 -040085 // };
86 //
Brian Osman95c26ef2020-02-10 13:45:22 -050087 // All functions have access to a global function named 'rand'. It takes a uint seed value,
88 // which it uses and updates (using a linear congruential RNG). It returns a random floating
89 // point value in [0, 1]. Typical usage is to pass the particle or effect's seed value to rand.
90 // For particle functions, the seed is rewound after each update, so calls to 'rand(p.seed)'
91 // will return consistent values from one update to the next.
Brian Osmand46cb972019-09-12 16:25:52 -040092 //
93 // Finally, there is one global uniform values available, 'dt'. This is a floating point
94 // number of seconds that have elapsed since the last update.
95 //
96 // Effect code should define two functions:
97 //
98 // 'void effectSpawn(inout Effect e)' is called when an instance of the effect is first
99 // created, and again at every loop point (if the effect is played with the looping flag).
100 //
101 // 'void effectUpdate(inout Effect e)' is called once per update to adjust properties of the
102 // effect (ie emitter).
103 //
104 // Particle code should also define two functions:
Brian Osman3da607e2019-07-26 15:11:46 -0400105 //
106 // 'void spawn(inout Particle p)' is called once for each particle when it is first created,
107 // to set initial values. At a minimum, this should set 'lifetime' to the number of seconds
Brian Osmand1846d82019-09-17 13:32:32 -0400108 // that the particle will exist. Other parameters will get default values from the effect.
Brian Osman3da607e2019-07-26 15:11:46 -0400109 //
110 // 'void update(inout Particle p)' is called for each particle on every call to the running
111 // SkParticleEffect's update() method. It can animate any of the particle's values. Note that
112 // the 'lifetime' field has a different meaning in 'update', and should not be used or changed.
Brian Osmand46cb972019-09-12 16:25:52 -0400113
114 SkString fEffectCode;
115 SkString fParticleCode;
Brian Osmanfe491632019-07-25 15:14:50 -0400116
Brian Osman3da607e2019-07-26 15:11:46 -0400117 // External objects accessible by the effect's SkSL code. Each binding is a name and particular
118 // kind of object. See SkParticleBinding::Make* for details.
Brian Osmanfe491632019-07-25 15:14:50 -0400119 SkTArray<sk_sp<SkParticleBinding>> fBindings;
Brian Osman7c979f52019-02-12 13:27:51 -0500120
121 void visitFields(SkFieldVisitor* v);
Brian Osmanfe491632019-07-25 15:14:50 -0400122
Brian Osman9dac0d82019-12-02 16:52:51 -0500123 // Load/compute cached resources
124 void prepare(const skresources::ResourceProvider*);
125
Brian Osmanfe491632019-07-25 15:14:50 -0400126private:
127 friend class SkParticleEffect;
128
129 // Cached
Brian Osmand46cb972019-09-12 16:25:52 -0400130 struct Program {
Brian Osmanb08cc022020-04-02 11:38:40 -0400131 std::unique_ptr<SkSL::ByteCode> fByteCode;
Brian Osmand46cb972019-09-12 16:25:52 -0400132 SkTArray<std::unique_ptr<SkParticleExternalValue>> fExternalValues;
133 };
134
Brian Osmanb08cc022020-04-02 11:38:40 -0400135 Program fEffectProgram;
136 Program fParticleProgram;
Brian Osman7c979f52019-02-12 13:27:51 -0500137};
138
139class SkParticleEffect : public SkRefCnt {
140public:
Brian Osman95c26ef2020-02-10 13:45:22 -0500141 SkParticleEffect(sk_sp<SkParticleEffectParams> params);
Brian Osman7c979f52019-02-12 13:27:51 -0500142
Brian Osman9a8b8462019-09-19 10:06:36 -0400143 // Start playing this effect, specifying initial values for the emitter's properties
144 void start(double now, bool looping, SkPoint position, SkVector heading, float scale,
Brian Osman95c26ef2020-02-10 13:45:22 -0500145 SkVector velocity, float spin, SkColor4f color, float frame, uint32_t flags,
146 uint32_t seed);
Brian Osman9a8b8462019-09-19 10:06:36 -0400147
148 // Start playing this effect, with default values for the emitter's properties
149 void start(double now, bool looping) {
150 this->start(now, looping,
151 { 0.0f, 0.0f }, // position
152 { 0.0f, -1.0f }, // heading
153 1.0f, // scale
154 { 0.0f, 0.0f }, // velocity
155 0.0f, // spin
156 { 1.0f, 1.0f, 1.0f, 1.0f }, // color
Brian Osman559ffe42019-09-25 11:24:50 -0400157 0.0f, // sprite frame
Brian Osman95c26ef2020-02-10 13:45:22 -0500158 0, // flags
159 0); // seed
Brian Osman9a8b8462019-09-19 10:06:36 -0400160 }
161
Kevin Lubick269fe892019-03-06 09:32:55 -0500162 void update(double now);
Brian Osman7c979f52019-02-12 13:27:51 -0500163 void draw(SkCanvas* canvas);
164
Brian Osman9a8b8462019-09-19 10:06:36 -0400165 bool isAlive(bool includeSubEffects = true) const {
166 return (fState.fAge >= 0 && fState.fAge <= 1)
167 || (includeSubEffects && !fSubEffects.empty());
168 }
Brian Osmanb77d5022019-03-06 11:08:48 -0500169 int getCount() const { return fCount; }
Brian Osman7c979f52019-02-12 13:27:51 -0500170
Brian Osmandf182962019-10-15 10:27:59 -0400171 float getRate() const { return fState.fRate; }
172 int getBurst() const { return fState.fBurst; }
173 SkPoint getPosition() const { return fState.fPosition; }
174 SkVector getHeading() const { return fState.fHeading; }
175 float getScale() const { return fState.fScale; }
176 SkVector getVelocity() const { return fState.fVelocity; }
177 float getSpin() const { return fState.fSpin; }
178 SkColor4f getColor() const { return fState.fColor; }
179 float getFrame() const { return fState.fFrame; }
180 uint32_t getFlags() const { return fState.fFlags; }
181
182 void setRate (float r) { fState.fRate = r; }
183 void setBurst (int b) { fState.fBurst = b; }
184 void setPosition(SkPoint p) { fState.fPosition = p; }
185 void setHeading (SkVector h) { fState.fHeading = h; }
186 void setScale (float s) { fState.fScale = s; }
187 void setVelocity(SkVector v) { fState.fVelocity = v; }
188 void setSpin (float s) { fState.fSpin = s; }
189 void setColor (SkColor4f c) { fState.fColor = c; }
190 void setFrame (float f) { fState.fFrame = f; }
191 void setFlags (uint32_t f) { fState.fFlags = f; }
192
Brian Osmanb08cc022020-04-02 11:38:40 -0400193 const SkSL::ByteCode* effectCode() const { return fParams->fEffectProgram.fByteCode.get(); }
194 const SkSL::ByteCode* particleCode() const { return fParams->fParticleProgram.fByteCode.get(); }
Brian Osman5b431132019-10-15 16:41:18 -0400195
196 float* effectUniforms() { return fEffectUniforms.data(); }
197 float* particleUniforms() { return fParticleUniforms.data(); }
198
Brian Osman2aa85df2019-08-30 10:59:47 -0400199 static void RegisterParticleTypes();
200
Brian Osman7c979f52019-02-12 13:27:51 -0500201private:
202 void setCapacity(int capacity);
203
Brian Osman9a8b8462019-09-19 10:06:36 -0400204 // Helpers to break down update
205 void advanceTime(double now);
206
207 void processEffectSpawnRequests(double now);
Brian Osmandf182962019-10-15 10:27:59 -0400208 void runEffectScript(double now, const char* entry);
Brian Osman9a8b8462019-09-19 10:06:36 -0400209
210 void processParticleSpawnRequests(double now, int start);
211 void runParticleScript(double now, const char* entry, int start, int count);
212
Brian Osmand12f2782019-11-27 10:34:18 -0500213 sk_sp<SkParticleEffectParams> fParams;
Brian Osman7c979f52019-02-12 13:27:51 -0500214
Brian Osman5c1f8eb2019-02-14 14:49:55 -0500215 bool fLooping;
Brian Osman7c979f52019-02-12 13:27:51 -0500216 int fCount;
217 double fLastTime;
218 float fSpawnRemainder;
219
Brian Osman5b431132019-10-15 16:41:18 -0400220 // C++ version of the SkSL Effect struct. This is the inout parameter to per-effect scripts,
221 // and provided as a uniform (named 'effect') to the per-particle scripts.
Brian Osmand46cb972019-09-12 16:25:52 -0400222 struct EffectState {
Brian Osmand46cb972019-09-12 16:25:52 -0400223 float fAge;
224 float fLifetime;
225 int fLoopCount;
226 float fRate;
227 int fBurst;
228
229 // Properties that determine default values for new particles
230 SkPoint fPosition;
231 SkVector fHeading;
232 float fScale;
233 SkVector fVelocity;
234 float fSpin;
235 SkColor4f fColor;
236 float fFrame;
Brian Osman559ffe42019-09-25 11:24:50 -0400237 uint32_t fFlags;
Brian Osman95c26ef2020-02-10 13:45:22 -0500238 uint32_t fRandom;
Brian Osmand46cb972019-09-12 16:25:52 -0400239 };
240 EffectState fState;
241
Brian Osman95c26ef2020-02-10 13:45:22 -0500242 SkParticles fParticles;
243 SkAutoTMalloc<float> fStableRandoms;
Brian Osman7c979f52019-02-12 13:27:51 -0500244
245 // Cached
246 int fCapacity;
Brian Osman5b431132019-10-15 16:41:18 -0400247 SkTArray<float, true> fEffectUniforms;
248 SkTArray<float, true> fParticleUniforms;
Brian Osman9a8b8462019-09-19 10:06:36 -0400249
250 // Private interface used by SkEffectBinding and SkEffectExternalValue to spawn sub effects
251 friend class SkEffectExternalValue;
252 struct SpawnRequest {
253 SpawnRequest(int index, bool loop, sk_sp<SkParticleEffectParams> params)
254 : fIndex(index)
255 , fLoop(loop)
256 , fParams(std::move(params)) {}
257
258 int fIndex;
259 bool fLoop;
260 sk_sp<SkParticleEffectParams> fParams;
261 };
262 void addSpawnRequest(int index, bool loop, sk_sp<SkParticleEffectParams> params) {
263 fSpawnRequests.emplace_back(index, loop, std::move(params));
264 }
265 SkTArray<SpawnRequest> fSpawnRequests;
266
267 SkTArray<sk_sp<SkParticleEffect>> fSubEffects;
Brian Osman7c979f52019-02-12 13:27:51 -0500268};
269
270#endif // SkParticleEffect_DEFINED