blob: b19ce2f635878587d95295dfc9c7549abc3001b1 [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"
Brian Osmanfe491632019-07-25 15:14:50 -040019
20#include <memory>
Brian Osman7c979f52019-02-12 13:27:51 -050021
Brian Osman7c979f52019-02-12 13:27:51 -050022class SkCanvas;
Brian Osman2aa85df2019-08-30 10:59:47 -040023class SkFieldVisitor;
24class SkParticleBinding;
Brian Osman543d2e22019-02-15 14:29:38 -050025class SkParticleDrawable;
Brian Osmanfe491632019-07-25 15:14:50 -040026class SkParticleExternalValue;
27
Brian Osmand12f2782019-11-27 10:34:18 -050028namespace skresources {
29 class ResourceProvider;
30}
31
Brian Osmanfe491632019-07-25 15:14:50 -040032namespace SkSL {
Brian Osman9b8b4552019-09-30 13:23:14 -040033 class ByteCode;
Brian Osmanfe491632019-07-25 15:14:50 -040034}
35
Brian Osman7c979f52019-02-12 13:27:51 -050036class SkParticleEffectParams : public SkRefCnt {
37public:
Brian Osmanfe491632019-07-25 15:14:50 -040038 SkParticleEffectParams();
39
Brian Osmand46cb972019-09-12 16:25:52 -040040 // Maximum number of particles per instance of the effect
41 int fMaxCount;
Brian Osman7c979f52019-02-12 13:27:51 -050042
Brian Osman3da607e2019-07-26 15:11:46 -040043 // What is drawn for each particle? (Image, shape, sprite sheet, etc.)
44 // See SkParticleDrawable::Make*
Brian Osman543d2e22019-02-15 14:29:38 -050045 sk_sp<SkParticleDrawable> fDrawable;
Brian Osman7c979f52019-02-12 13:27:51 -050046
Brian Osmand46cb972019-09-12 16:25:52 -040047 // Particle behavior is driven by two chunks of SkSL code. Effect functions are defined in
48 // fEffectCode, and get a mutable Effect struct:
Brian Osman3da607e2019-07-26 15:11:46 -040049 //
Brian Osmand46cb972019-09-12 16:25:52 -040050 // struct Effect {
Brian Osmand1846d82019-09-17 13:32:32 -040051 // float age; // Normalized age of the effect
52 // float lifetime; // Effect's duration, in seconds - script should set this in effectSpawn
53 // int loop; // Number of loops that have elapsed (0 on initial spawn)
54 // float rate; // Rate to generate new particles (particles / second)
55 // int burst; // Number of particles to emit in a single update
56 // // Set during spawn to emit that many at once on each loop
Brian Osmand46cb972019-09-12 16:25:52 -040057 //
58 // // Everything below this line controls the state of the effect, which is also the
59 // // default values for new particles.
60 // float2 pos = { 0, 0 }; // Local position
Brian Osman3da607e2019-07-26 15:11:46 -040061 // float2 dir = { 0, -1 }; // Heading. Should be a normalized vector.
62 // float scale = 1; // Size, normalized relative to the drawable's native size
63 // float2 vel = { 0, 0 }; // Linear velocity, in (units / second)
64 // float spin = 0; // Angular velocity, in (radians / second)
65 // float4 color = { 1, 1, 1, 1 }; // RGBA color
66 // float frame = 0; // Normalized sprite index for multi-frame drawables
67 // };
68 //
Brian Osmand46cb972019-09-12 16:25:52 -040069 // Particle functions are defined in fParticleCode, and get a mutable Particle struct, as well
70 // as a uniform copy of the current Effect, named 'effect'.
Brian Osman3da607e2019-07-26 15:11:46 -040071 //
Brian Osmand46cb972019-09-12 16:25:52 -040072 // struct Particle {
73 // float age;
74 // float lifetime;
75 // float2 pos;
76 // float2 dir;
77 // float scale;
78 // float2 vel;
79 // float spin;
80 // float4 color;
81 // float frame;
82 // };
83 //
84 // All functions have access to a global variable named 'rand'. Every read of 'rand' returns a
85 // random floating point value in [0, 1). For particle functions, the state is rewound after
86 // each update, so calls to 'rand' will return consistent values from one update to the next.
87 //
88 // Finally, there is one global uniform values available, 'dt'. This is a floating point
89 // number of seconds that have elapsed since the last update.
90 //
91 // Effect code should define two functions:
92 //
93 // 'void effectSpawn(inout Effect e)' is called when an instance of the effect is first
94 // created, and again at every loop point (if the effect is played with the looping flag).
95 //
96 // 'void effectUpdate(inout Effect e)' is called once per update to adjust properties of the
97 // effect (ie emitter).
98 //
99 // Particle code should also define two functions:
Brian Osman3da607e2019-07-26 15:11:46 -0400100 //
101 // 'void spawn(inout Particle p)' is called once for each particle when it is first created,
102 // to set initial values. At a minimum, this should set 'lifetime' to the number of seconds
Brian Osmand1846d82019-09-17 13:32:32 -0400103 // that the particle will exist. Other parameters will get default values from the effect.
Brian Osman3da607e2019-07-26 15:11:46 -0400104 //
105 // 'void update(inout Particle p)' is called for each particle on every call to the running
106 // SkParticleEffect's update() method. It can animate any of the particle's values. Note that
107 // the 'lifetime' field has a different meaning in 'update', and should not be used or changed.
Brian Osmand46cb972019-09-12 16:25:52 -0400108
109 SkString fEffectCode;
110 SkString fParticleCode;
Brian Osmanfe491632019-07-25 15:14:50 -0400111
Brian Osman3da607e2019-07-26 15:11:46 -0400112 // External objects accessible by the effect's SkSL code. Each binding is a name and particular
113 // kind of object. See SkParticleBinding::Make* for details.
Brian Osmanfe491632019-07-25 15:14:50 -0400114 SkTArray<sk_sp<SkParticleBinding>> fBindings;
Brian Osman7c979f52019-02-12 13:27:51 -0500115
116 void visitFields(SkFieldVisitor* v);
Brian Osmanfe491632019-07-25 15:14:50 -0400117
Brian Osman9dac0d82019-12-02 16:52:51 -0500118 // Load/compute cached resources
119 void prepare(const skresources::ResourceProvider*);
120
Brian Osmanfe491632019-07-25 15:14:50 -0400121private:
122 friend class SkParticleEffect;
123
124 // Cached
Brian Osmand46cb972019-09-12 16:25:52 -0400125 struct Program {
Brian Osmanb08cc022020-04-02 11:38:40 -0400126 std::unique_ptr<SkSL::ByteCode> fByteCode;
Brian Osmand46cb972019-09-12 16:25:52 -0400127 SkTArray<std::unique_ptr<SkParticleExternalValue>> fExternalValues;
128 };
129
Brian Osmanb08cc022020-04-02 11:38:40 -0400130 Program fEffectProgram;
131 Program fParticleProgram;
Brian Osman7c979f52019-02-12 13:27:51 -0500132};
133
134class SkParticleEffect : public SkRefCnt {
135public:
Brian Osman9dac0d82019-12-02 16:52:51 -0500136 SkParticleEffect(sk_sp<SkParticleEffectParams> params, const SkRandom& random);
Brian Osman7c979f52019-02-12 13:27:51 -0500137
Brian Osman9a8b8462019-09-19 10:06:36 -0400138 // Start playing this effect, specifying initial values for the emitter's properties
139 void start(double now, bool looping, SkPoint position, SkVector heading, float scale,
Brian Osman559ffe42019-09-25 11:24:50 -0400140 SkVector velocity, float spin, SkColor4f color, float frame, uint32_t flags);
Brian Osman9a8b8462019-09-19 10:06:36 -0400141
142 // Start playing this effect, with default values for the emitter's properties
143 void start(double now, bool looping) {
144 this->start(now, looping,
145 { 0.0f, 0.0f }, // position
146 { 0.0f, -1.0f }, // heading
147 1.0f, // scale
148 { 0.0f, 0.0f }, // velocity
149 0.0f, // spin
150 { 1.0f, 1.0f, 1.0f, 1.0f }, // color
Brian Osman559ffe42019-09-25 11:24:50 -0400151 0.0f, // sprite frame
152 0); // flags
Brian Osman9a8b8462019-09-19 10:06:36 -0400153 }
154
Kevin Lubick269fe892019-03-06 09:32:55 -0500155 void update(double now);
Brian Osman7c979f52019-02-12 13:27:51 -0500156 void draw(SkCanvas* canvas);
157
Brian Osman9a8b8462019-09-19 10:06:36 -0400158 bool isAlive(bool includeSubEffects = true) const {
159 return (fState.fAge >= 0 && fState.fAge <= 1)
160 || (includeSubEffects && !fSubEffects.empty());
161 }
Brian Osmanb77d5022019-03-06 11:08:48 -0500162 int getCount() const { return fCount; }
Brian Osman7c979f52019-02-12 13:27:51 -0500163
Brian Osmandf182962019-10-15 10:27:59 -0400164 float getRate() const { return fState.fRate; }
165 int getBurst() const { return fState.fBurst; }
166 SkPoint getPosition() const { return fState.fPosition; }
167 SkVector getHeading() const { return fState.fHeading; }
168 float getScale() const { return fState.fScale; }
169 SkVector getVelocity() const { return fState.fVelocity; }
170 float getSpin() const { return fState.fSpin; }
171 SkColor4f getColor() const { return fState.fColor; }
172 float getFrame() const { return fState.fFrame; }
173 uint32_t getFlags() const { return fState.fFlags; }
174
175 void setRate (float r) { fState.fRate = r; }
176 void setBurst (int b) { fState.fBurst = b; }
177 void setPosition(SkPoint p) { fState.fPosition = p; }
178 void setHeading (SkVector h) { fState.fHeading = h; }
179 void setScale (float s) { fState.fScale = s; }
180 void setVelocity(SkVector v) { fState.fVelocity = v; }
181 void setSpin (float s) { fState.fSpin = s; }
182 void setColor (SkColor4f c) { fState.fColor = c; }
183 void setFrame (float f) { fState.fFrame = f; }
184 void setFlags (uint32_t f) { fState.fFlags = f; }
185
Brian Osmanb08cc022020-04-02 11:38:40 -0400186 const SkSL::ByteCode* effectCode() const { return fParams->fEffectProgram.fByteCode.get(); }
187 const SkSL::ByteCode* particleCode() const { return fParams->fParticleProgram.fByteCode.get(); }
Brian Osman5b431132019-10-15 16:41:18 -0400188
189 float* effectUniforms() { return fEffectUniforms.data(); }
190 float* particleUniforms() { return fParticleUniforms.data(); }
191
Brian Osman2aa85df2019-08-30 10:59:47 -0400192 static void RegisterParticleTypes();
193
Brian Osman7c979f52019-02-12 13:27:51 -0500194private:
195 void setCapacity(int capacity);
196
Brian Osman9a8b8462019-09-19 10:06:36 -0400197 // Helpers to break down update
198 void advanceTime(double now);
199
200 void processEffectSpawnRequests(double now);
Brian Osmandf182962019-10-15 10:27:59 -0400201 void runEffectScript(double now, const char* entry);
Brian Osman9a8b8462019-09-19 10:06:36 -0400202
203 void processParticleSpawnRequests(double now, int start);
204 void runParticleScript(double now, const char* entry, int start, int count);
205
Brian Osmand12f2782019-11-27 10:34:18 -0500206 sk_sp<SkParticleEffectParams> fParams;
Brian Osman7c979f52019-02-12 13:27:51 -0500207
Brian Osman5c1f8eb2019-02-14 14:49:55 -0500208 SkRandom fRandom;
209
210 bool fLooping;
Brian Osman7c979f52019-02-12 13:27:51 -0500211 int fCount;
212 double fLastTime;
213 float fSpawnRemainder;
214
Brian Osman5b431132019-10-15 16:41:18 -0400215 // C++ version of the SkSL Effect struct. This is the inout parameter to per-effect scripts,
216 // and provided as a uniform (named 'effect') to the per-particle scripts.
Brian Osmand46cb972019-09-12 16:25:52 -0400217 struct EffectState {
Brian Osmand46cb972019-09-12 16:25:52 -0400218 float fAge;
219 float fLifetime;
220 int fLoopCount;
221 float fRate;
222 int fBurst;
223
224 // Properties that determine default values for new particles
225 SkPoint fPosition;
226 SkVector fHeading;
227 float fScale;
228 SkVector fVelocity;
229 float fSpin;
230 SkColor4f fColor;
231 float fFrame;
Brian Osman559ffe42019-09-25 11:24:50 -0400232 uint32_t fFlags;
Brian Osmand46cb972019-09-12 16:25:52 -0400233 };
234 EffectState fState;
235
Brian Osmanfe491632019-07-25 15:14:50 -0400236 SkParticles fParticles;
237 SkAutoTMalloc<SkRandom> fStableRandoms;
Brian Osman7c979f52019-02-12 13:27:51 -0500238
239 // Cached
240 int fCapacity;
Brian Osman5b431132019-10-15 16:41:18 -0400241 SkTArray<float, true> fEffectUniforms;
242 SkTArray<float, true> fParticleUniforms;
Brian Osman9a8b8462019-09-19 10:06:36 -0400243
244 // Private interface used by SkEffectBinding and SkEffectExternalValue to spawn sub effects
245 friend class SkEffectExternalValue;
246 struct SpawnRequest {
247 SpawnRequest(int index, bool loop, sk_sp<SkParticleEffectParams> params)
248 : fIndex(index)
249 , fLoop(loop)
250 , fParams(std::move(params)) {}
251
252 int fIndex;
253 bool fLoop;
254 sk_sp<SkParticleEffectParams> fParams;
255 };
256 void addSpawnRequest(int index, bool loop, sk_sp<SkParticleEffectParams> params) {
257 fSpawnRequests.emplace_back(index, loop, std::move(params));
258 }
259 SkTArray<SpawnRequest> fSpawnRequests;
260
261 SkTArray<sk_sp<SkParticleEffect>> fSubEffects;
Brian Osman7c979f52019-02-12 13:27:51 -0500262};
263
264#endif // SkParticleEffect_DEFINED