blob: b28eac831158f3c905343918a1c74b9f19a1ae02 [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"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050017#include "include/utils/SkRandom.h"
Brian Osmanfe491632019-07-25 15:14:50 -040018#include "modules/particles/include/SkParticleData.h"
Ethan Nicholasb962eff2020-01-23 16:49:41 -050019#include "src/sksl/SkSLInterpreter.h"
Brian Osmanfe491632019-07-25 15:14:50 -040020
21#include <memory>
Brian Osman7c979f52019-02-12 13:27:51 -050022
Brian Osman7c979f52019-02-12 13:27:51 -050023class SkCanvas;
Brian Osman2aa85df2019-08-30 10:59:47 -040024class SkFieldVisitor;
25class SkParticleBinding;
Brian Osman543d2e22019-02-15 14:29:38 -050026class SkParticleDrawable;
Brian Osmanfe491632019-07-25 15:14:50 -040027class SkParticleExternalValue;
28
Ethan Nicholasb962eff2020-01-23 16:49:41 -050029static constexpr int INTERPRETER_WIDTH = 8;
30
Brian Osmand12f2782019-11-27 10:34:18 -050031namespace skresources {
32 class ResourceProvider;
33}
34
Brian Osmanfe491632019-07-25 15:14:50 -040035namespace SkSL {
Brian Osman9b8b4552019-09-30 13:23:14 -040036 class ByteCode;
Brian Osmanfe491632019-07-25 15:14:50 -040037}
38
Brian Osman7c979f52019-02-12 13:27:51 -050039class SkParticleEffectParams : public SkRefCnt {
40public:
Brian Osmanfe491632019-07-25 15:14:50 -040041 SkParticleEffectParams();
42
Brian Osmand46cb972019-09-12 16:25:52 -040043 // Maximum number of particles per instance of the effect
44 int fMaxCount;
Brian Osman7c979f52019-02-12 13:27:51 -050045
Brian Osman3da607e2019-07-26 15:11:46 -040046 // What is drawn for each particle? (Image, shape, sprite sheet, etc.)
47 // See SkParticleDrawable::Make*
Brian Osman543d2e22019-02-15 14:29:38 -050048 sk_sp<SkParticleDrawable> fDrawable;
Brian Osman7c979f52019-02-12 13:27:51 -050049
Brian Osmand46cb972019-09-12 16:25:52 -040050 // Particle behavior is driven by two chunks of SkSL code. Effect functions are defined in
51 // fEffectCode, and get a mutable Effect struct:
Brian Osman3da607e2019-07-26 15:11:46 -040052 //
Brian Osmand46cb972019-09-12 16:25:52 -040053 // struct Effect {
Brian Osmand1846d82019-09-17 13:32:32 -040054 // float age; // Normalized age of the effect
55 // float lifetime; // Effect's duration, in seconds - script should set this in effectSpawn
56 // int loop; // Number of loops that have elapsed (0 on initial spawn)
57 // float rate; // Rate to generate new particles (particles / second)
58 // int burst; // Number of particles to emit in a single update
59 // // Set during spawn to emit that many at once on each loop
Brian Osmand46cb972019-09-12 16:25:52 -040060 //
61 // // Everything below this line controls the state of the effect, which is also the
62 // // default values for new particles.
63 // float2 pos = { 0, 0 }; // Local position
Brian Osman3da607e2019-07-26 15:11:46 -040064 // float2 dir = { 0, -1 }; // Heading. Should be a normalized vector.
65 // float scale = 1; // Size, normalized relative to the drawable's native size
66 // float2 vel = { 0, 0 }; // Linear velocity, in (units / second)
67 // float spin = 0; // Angular velocity, in (radians / second)
68 // float4 color = { 1, 1, 1, 1 }; // RGBA color
69 // float frame = 0; // Normalized sprite index for multi-frame drawables
70 // };
71 //
Brian Osmand46cb972019-09-12 16:25:52 -040072 // Particle functions are defined in fParticleCode, and get a mutable Particle struct, as well
73 // as a uniform copy of the current Effect, named 'effect'.
Brian Osman3da607e2019-07-26 15:11:46 -040074 //
Brian Osmand46cb972019-09-12 16:25:52 -040075 // struct Particle {
76 // float age;
77 // float lifetime;
78 // float2 pos;
79 // float2 dir;
80 // float scale;
81 // float2 vel;
82 // float spin;
83 // float4 color;
84 // float frame;
85 // };
86 //
87 // All functions have access to a global variable named 'rand'. Every read of 'rand' returns a
88 // random floating point value in [0, 1). For particle functions, the state is rewound after
89 // each update, so calls to 'rand' will return consistent values from one update to the next.
90 //
91 // Finally, there is one global uniform values available, 'dt'. This is a floating point
92 // number of seconds that have elapsed since the last update.
93 //
94 // Effect code should define two functions:
95 //
96 // 'void effectSpawn(inout Effect e)' is called when an instance of the effect is first
97 // created, and again at every loop point (if the effect is played with the looping flag).
98 //
99 // 'void effectUpdate(inout Effect e)' is called once per update to adjust properties of the
100 // effect (ie emitter).
101 //
102 // Particle code should also define two functions:
Brian Osman3da607e2019-07-26 15:11:46 -0400103 //
104 // 'void spawn(inout Particle p)' is called once for each particle when it is first created,
105 // to set initial values. At a minimum, this should set 'lifetime' to the number of seconds
Brian Osmand1846d82019-09-17 13:32:32 -0400106 // that the particle will exist. Other parameters will get default values from the effect.
Brian Osman3da607e2019-07-26 15:11:46 -0400107 //
108 // 'void update(inout Particle p)' is called for each particle on every call to the running
109 // SkParticleEffect's update() method. It can animate any of the particle's values. Note that
110 // the 'lifetime' field has a different meaning in 'update', and should not be used or changed.
Brian Osmand46cb972019-09-12 16:25:52 -0400111
112 SkString fEffectCode;
113 SkString fParticleCode;
Brian Osmanfe491632019-07-25 15:14:50 -0400114
Brian Osman3da607e2019-07-26 15:11:46 -0400115 // External objects accessible by the effect's SkSL code. Each binding is a name and particular
116 // kind of object. See SkParticleBinding::Make* for details.
Brian Osmanfe491632019-07-25 15:14:50 -0400117 SkTArray<sk_sp<SkParticleBinding>> fBindings;
Brian Osman7c979f52019-02-12 13:27:51 -0500118
119 void visitFields(SkFieldVisitor* v);
Brian Osmanfe491632019-07-25 15:14:50 -0400120
Brian Osman9dac0d82019-12-02 16:52:51 -0500121 // Load/compute cached resources
122 void prepare(const skresources::ResourceProvider*);
123
Brian Osmanfe491632019-07-25 15:14:50 -0400124private:
125 friend class SkParticleEffect;
126
127 // Cached
Ethan Nicholasb962eff2020-01-23 16:49:41 -0500128 template<int width>
Brian Osmand46cb972019-09-12 16:25:52 -0400129 struct Program {
Ethan Nicholasb962eff2020-01-23 16:49:41 -0500130 std::unique_ptr<SkSL::Interpreter<width>> fInterpreter;
Brian Osmand46cb972019-09-12 16:25:52 -0400131 SkTArray<std::unique_ptr<SkParticleExternalValue>> fExternalValues;
132 };
133
Ethan Nicholasb962eff2020-01-23 16:49:41 -0500134 // for performance it would be better to run this with a Program<1>, but for code-size reasons
135 // we stick to INTERPRETER_WIDTH
136 Program<INTERPRETER_WIDTH> fEffectProgram;
137 Program<INTERPRETER_WIDTH> fParticleProgram;
Brian Osman7c979f52019-02-12 13:27:51 -0500138};
139
140class SkParticleEffect : public SkRefCnt {
141public:
Brian Osman9dac0d82019-12-02 16:52:51 -0500142 SkParticleEffect(sk_sp<SkParticleEffectParams> params, const SkRandom& random);
Brian Osman7c979f52019-02-12 13:27:51 -0500143
Brian Osman9a8b8462019-09-19 10:06:36 -0400144 // Start playing this effect, specifying initial values for the emitter's properties
145 void start(double now, bool looping, SkPoint position, SkVector heading, float scale,
Brian Osman559ffe42019-09-25 11:24:50 -0400146 SkVector velocity, float spin, SkColor4f color, float frame, uint32_t flags);
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
158 0); // flags
Brian Osman9a8b8462019-09-19 10:06:36 -0400159 }
160
Kevin Lubick269fe892019-03-06 09:32:55 -0500161 void update(double now);
Brian Osman7c979f52019-02-12 13:27:51 -0500162 void draw(SkCanvas* canvas);
163
Brian Osman9a8b8462019-09-19 10:06:36 -0400164 bool isAlive(bool includeSubEffects = true) const {
165 return (fState.fAge >= 0 && fState.fAge <= 1)
166 || (includeSubEffects && !fSubEffects.empty());
167 }
Brian Osmanb77d5022019-03-06 11:08:48 -0500168 int getCount() const { return fCount; }
Brian Osman7c979f52019-02-12 13:27:51 -0500169
Brian Osmandf182962019-10-15 10:27:59 -0400170 float getRate() const { return fState.fRate; }
171 int getBurst() const { return fState.fBurst; }
172 SkPoint getPosition() const { return fState.fPosition; }
173 SkVector getHeading() const { return fState.fHeading; }
174 float getScale() const { return fState.fScale; }
175 SkVector getVelocity() const { return fState.fVelocity; }
176 float getSpin() const { return fState.fSpin; }
177 SkColor4f getColor() const { return fState.fColor; }
178 float getFrame() const { return fState.fFrame; }
179 uint32_t getFlags() const { return fState.fFlags; }
180
181 void setRate (float r) { fState.fRate = r; }
182 void setBurst (int b) { fState.fBurst = b; }
183 void setPosition(SkPoint p) { fState.fPosition = p; }
184 void setHeading (SkVector h) { fState.fHeading = h; }
185 void setScale (float s) { fState.fScale = s; }
186 void setVelocity(SkVector v) { fState.fVelocity = v; }
187 void setSpin (float s) { fState.fSpin = s; }
188 void setColor (SkColor4f c) { fState.fColor = c; }
189 void setFrame (float f) { fState.fFrame = f; }
190 void setFlags (uint32_t f) { fState.fFlags = f; }
191
Ethan Nicholasb962eff2020-01-23 16:49:41 -0500192 const SkSL::ByteCode* effectCode() const {
193 return fParams->fEffectProgram.fInterpreter ?
194 &fParams->fEffectProgram.fInterpreter->getCode() :
195 nullptr;
196 }
197
198 const SkSL::ByteCode* particleCode() const {
199 return fParams->fParticleProgram.fInterpreter ?
200 &fParams->fParticleProgram.fInterpreter->getCode() :
201 nullptr;
202 }
Brian Osman5b431132019-10-15 16:41:18 -0400203
204 float* effectUniforms() { return fEffectUniforms.data(); }
205 float* particleUniforms() { return fParticleUniforms.data(); }
206
Brian Osman2aa85df2019-08-30 10:59:47 -0400207 static void RegisterParticleTypes();
208
Brian Osman7c979f52019-02-12 13:27:51 -0500209private:
210 void setCapacity(int capacity);
211
Brian Osman9a8b8462019-09-19 10:06:36 -0400212 // Helpers to break down update
213 void advanceTime(double now);
214
215 void processEffectSpawnRequests(double now);
Brian Osmandf182962019-10-15 10:27:59 -0400216 void runEffectScript(double now, const char* entry);
Brian Osman9a8b8462019-09-19 10:06:36 -0400217
218 void processParticleSpawnRequests(double now, int start);
219 void runParticleScript(double now, const char* entry, int start, int count);
220
Brian Osmand12f2782019-11-27 10:34:18 -0500221 sk_sp<SkParticleEffectParams> fParams;
Brian Osman7c979f52019-02-12 13:27:51 -0500222
Brian Osman5c1f8eb2019-02-14 14:49:55 -0500223 SkRandom fRandom;
224
225 bool fLooping;
Brian Osman7c979f52019-02-12 13:27:51 -0500226 int fCount;
227 double fLastTime;
228 float fSpawnRemainder;
229
Brian Osman5b431132019-10-15 16:41:18 -0400230 // C++ version of the SkSL Effect struct. This is the inout parameter to per-effect scripts,
231 // and provided as a uniform (named 'effect') to the per-particle scripts.
Brian Osmand46cb972019-09-12 16:25:52 -0400232 struct EffectState {
Brian Osmand46cb972019-09-12 16:25:52 -0400233 float fAge;
234 float fLifetime;
235 int fLoopCount;
236 float fRate;
237 int fBurst;
238
239 // Properties that determine default values for new particles
240 SkPoint fPosition;
241 SkVector fHeading;
242 float fScale;
243 SkVector fVelocity;
244 float fSpin;
245 SkColor4f fColor;
246 float fFrame;
Brian Osman559ffe42019-09-25 11:24:50 -0400247 uint32_t fFlags;
Brian Osmand46cb972019-09-12 16:25:52 -0400248 };
249 EffectState fState;
250
Brian Osmanfe491632019-07-25 15:14:50 -0400251 SkParticles fParticles;
252 SkAutoTMalloc<SkRandom> fStableRandoms;
Brian Osman7c979f52019-02-12 13:27:51 -0500253
254 // Cached
255 int fCapacity;
Brian Osman5b431132019-10-15 16:41:18 -0400256 SkTArray<float, true> fEffectUniforms;
257 SkTArray<float, true> fParticleUniforms;
Brian Osman9a8b8462019-09-19 10:06:36 -0400258
259 // Private interface used by SkEffectBinding and SkEffectExternalValue to spawn sub effects
260 friend class SkEffectExternalValue;
261 struct SpawnRequest {
262 SpawnRequest(int index, bool loop, sk_sp<SkParticleEffectParams> params)
263 : fIndex(index)
264 , fLoop(loop)
265 , fParams(std::move(params)) {}
266
267 int fIndex;
268 bool fLoop;
269 sk_sp<SkParticleEffectParams> fParams;
270 };
271 void addSpawnRequest(int index, bool loop, sk_sp<SkParticleEffectParams> params) {
272 fSpawnRequests.emplace_back(index, loop, std::move(params));
273 }
274 SkTArray<SpawnRequest> fSpawnRequests;
275
276 SkTArray<sk_sp<SkParticleEffect>> fSubEffects;
Brian Osman7c979f52019-02-12 13:27:51 -0500277};
278
279#endif // SkParticleEffect_DEFINED