blob: fe26408e9ec26a0f5e6cf9312b72793507525778 [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
28namespace SkSL {
Brian Osman9b8b4552019-09-30 13:23:14 -040029 class ByteCode;
Brian Osmanfe491632019-07-25 15:14:50 -040030}
31
Brian Osman7c979f52019-02-12 13:27:51 -050032class SkParticleEffectParams : public SkRefCnt {
33public:
Brian Osmanfe491632019-07-25 15:14:50 -040034 SkParticleEffectParams();
35
Brian Osmand46cb972019-09-12 16:25:52 -040036 // Maximum number of particles per instance of the effect
37 int fMaxCount;
Brian Osman7c979f52019-02-12 13:27:51 -050038
Brian Osman3da607e2019-07-26 15:11:46 -040039 // What is drawn for each particle? (Image, shape, sprite sheet, etc.)
40 // See SkParticleDrawable::Make*
Brian Osman543d2e22019-02-15 14:29:38 -050041 sk_sp<SkParticleDrawable> fDrawable;
Brian Osman7c979f52019-02-12 13:27:51 -050042
Brian Osmand46cb972019-09-12 16:25:52 -040043 // Particle behavior is driven by two chunks of SkSL code. Effect functions are defined in
44 // fEffectCode, and get a mutable Effect struct:
Brian Osman3da607e2019-07-26 15:11:46 -040045 //
Brian Osmand46cb972019-09-12 16:25:52 -040046 // struct Effect {
Brian Osmand1846d82019-09-17 13:32:32 -040047 // float age; // Normalized age of the effect
48 // float lifetime; // Effect's duration, in seconds - script should set this in effectSpawn
49 // int loop; // Number of loops that have elapsed (0 on initial spawn)
50 // float rate; // Rate to generate new particles (particles / second)
51 // int burst; // Number of particles to emit in a single update
52 // // Set during spawn to emit that many at once on each loop
Brian Osmand46cb972019-09-12 16:25:52 -040053 //
54 // // Everything below this line controls the state of the effect, which is also the
55 // // default values for new particles.
56 // float2 pos = { 0, 0 }; // Local position
Brian Osman3da607e2019-07-26 15:11:46 -040057 // float2 dir = { 0, -1 }; // Heading. Should be a normalized vector.
58 // float scale = 1; // Size, normalized relative to the drawable's native size
59 // float2 vel = { 0, 0 }; // Linear velocity, in (units / second)
60 // float spin = 0; // Angular velocity, in (radians / second)
61 // float4 color = { 1, 1, 1, 1 }; // RGBA color
62 // float frame = 0; // Normalized sprite index for multi-frame drawables
63 // };
64 //
Brian Osmand46cb972019-09-12 16:25:52 -040065 // Particle functions are defined in fParticleCode, and get a mutable Particle struct, as well
66 // as a uniform copy of the current Effect, named 'effect'.
Brian Osman3da607e2019-07-26 15:11:46 -040067 //
Brian Osmand46cb972019-09-12 16:25:52 -040068 // struct Particle {
69 // float age;
70 // float lifetime;
71 // float2 pos;
72 // float2 dir;
73 // float scale;
74 // float2 vel;
75 // float spin;
76 // float4 color;
77 // float frame;
78 // };
79 //
80 // All functions have access to a global variable named 'rand'. Every read of 'rand' returns a
81 // random floating point value in [0, 1). For particle functions, the state is rewound after
82 // each update, so calls to 'rand' will return consistent values from one update to the next.
83 //
84 // Finally, there is one global uniform values available, 'dt'. This is a floating point
85 // number of seconds that have elapsed since the last update.
86 //
87 // Effect code should define two functions:
88 //
89 // 'void effectSpawn(inout Effect e)' is called when an instance of the effect is first
90 // created, and again at every loop point (if the effect is played with the looping flag).
91 //
92 // 'void effectUpdate(inout Effect e)' is called once per update to adjust properties of the
93 // effect (ie emitter).
94 //
95 // Particle code should also define two functions:
Brian Osman3da607e2019-07-26 15:11:46 -040096 //
97 // 'void spawn(inout Particle p)' is called once for each particle when it is first created,
98 // to set initial values. At a minimum, this should set 'lifetime' to the number of seconds
Brian Osmand1846d82019-09-17 13:32:32 -040099 // that the particle will exist. Other parameters will get default values from the effect.
Brian Osman3da607e2019-07-26 15:11:46 -0400100 //
101 // 'void update(inout Particle p)' is called for each particle on every call to the running
102 // SkParticleEffect's update() method. It can animate any of the particle's values. Note that
103 // the 'lifetime' field has a different meaning in 'update', and should not be used or changed.
Brian Osmand46cb972019-09-12 16:25:52 -0400104
105 SkString fEffectCode;
106 SkString fParticleCode;
Brian Osmanfe491632019-07-25 15:14:50 -0400107
Brian Osman3da607e2019-07-26 15:11:46 -0400108 // External objects accessible by the effect's SkSL code. Each binding is a name and particular
109 // kind of object. See SkParticleBinding::Make* for details.
Brian Osmanfe491632019-07-25 15:14:50 -0400110 SkTArray<sk_sp<SkParticleBinding>> fBindings;
Brian Osman7c979f52019-02-12 13:27:51 -0500111
112 void visitFields(SkFieldVisitor* v);
Brian Osmanfe491632019-07-25 15:14:50 -0400113
114private:
115 friend class SkParticleEffect;
116
117 // Cached
Brian Osmand46cb972019-09-12 16:25:52 -0400118 struct Program {
119 std::unique_ptr<SkSL::ByteCode> fByteCode;
120 SkTArray<std::unique_ptr<SkParticleExternalValue>> fExternalValues;
121 };
122
123 Program fEffectProgram;
124 Program fParticleProgram;
Brian Osmanfe491632019-07-25 15:14:50 -0400125
126 void rebuild();
Brian Osman7c979f52019-02-12 13:27:51 -0500127};
128
129class SkParticleEffect : public SkRefCnt {
130public:
Brian Osman5c1f8eb2019-02-14 14:49:55 -0500131 SkParticleEffect(sk_sp<SkParticleEffectParams> params, const SkRandom& random);
Brian Osman7c979f52019-02-12 13:27:51 -0500132
Brian Osman9a8b8462019-09-19 10:06:36 -0400133 // Start playing this effect, specifying initial values for the emitter's properties
134 void start(double now, bool looping, SkPoint position, SkVector heading, float scale,
Brian Osman559ffe42019-09-25 11:24:50 -0400135 SkVector velocity, float spin, SkColor4f color, float frame, uint32_t flags);
Brian Osman9a8b8462019-09-19 10:06:36 -0400136
137 // Start playing this effect, with default values for the emitter's properties
138 void start(double now, bool looping) {
139 this->start(now, looping,
140 { 0.0f, 0.0f }, // position
141 { 0.0f, -1.0f }, // heading
142 1.0f, // scale
143 { 0.0f, 0.0f }, // velocity
144 0.0f, // spin
145 { 1.0f, 1.0f, 1.0f, 1.0f }, // color
Brian Osman559ffe42019-09-25 11:24:50 -0400146 0.0f, // sprite frame
147 0); // flags
Brian Osman9a8b8462019-09-19 10:06:36 -0400148 }
149
Kevin Lubick269fe892019-03-06 09:32:55 -0500150 void update(double now);
Brian Osman7c979f52019-02-12 13:27:51 -0500151 void draw(SkCanvas* canvas);
152
Brian Osman9a8b8462019-09-19 10:06:36 -0400153 bool isAlive(bool includeSubEffects = true) const {
154 return (fState.fAge >= 0 && fState.fAge <= 1)
155 || (includeSubEffects && !fSubEffects.empty());
156 }
Brian Osmanb77d5022019-03-06 11:08:48 -0500157 int getCount() const { return fCount; }
Brian Osman7c979f52019-02-12 13:27:51 -0500158
Brian Osmandf182962019-10-15 10:27:59 -0400159 float getRate() const { return fState.fRate; }
160 int getBurst() const { return fState.fBurst; }
161 SkPoint getPosition() const { return fState.fPosition; }
162 SkVector getHeading() const { return fState.fHeading; }
163 float getScale() const { return fState.fScale; }
164 SkVector getVelocity() const { return fState.fVelocity; }
165 float getSpin() const { return fState.fSpin; }
166 SkColor4f getColor() const { return fState.fColor; }
167 float getFrame() const { return fState.fFrame; }
168 uint32_t getFlags() const { return fState.fFlags; }
169
170 void setRate (float r) { fState.fRate = r; }
171 void setBurst (int b) { fState.fBurst = b; }
172 void setPosition(SkPoint p) { fState.fPosition = p; }
173 void setHeading (SkVector h) { fState.fHeading = h; }
174 void setScale (float s) { fState.fScale = s; }
175 void setVelocity(SkVector v) { fState.fVelocity = v; }
176 void setSpin (float s) { fState.fSpin = s; }
177 void setColor (SkColor4f c) { fState.fColor = c; }
178 void setFrame (float f) { fState.fFrame = f; }
179 void setFlags (uint32_t f) { fState.fFlags = f; }
180
Brian Osman5b431132019-10-15 16:41:18 -0400181 const SkSL::ByteCode* effectCode() const { return fParams->fEffectProgram.fByteCode.get(); }
182 const SkSL::ByteCode* particleCode() const { return fParams->fParticleProgram.fByteCode.get(); }
183
184 float* effectUniforms() { return fEffectUniforms.data(); }
185 float* particleUniforms() { return fParticleUniforms.data(); }
186
Brian Osman2aa85df2019-08-30 10:59:47 -0400187 static void RegisterParticleTypes();
188
Brian Osman7c979f52019-02-12 13:27:51 -0500189private:
190 void setCapacity(int capacity);
191
Brian Osman9a8b8462019-09-19 10:06:36 -0400192 // Helpers to break down update
193 void advanceTime(double now);
194
195 void processEffectSpawnRequests(double now);
Brian Osmandf182962019-10-15 10:27:59 -0400196 void runEffectScript(double now, const char* entry);
Brian Osman9a8b8462019-09-19 10:06:36 -0400197
198 void processParticleSpawnRequests(double now, int start);
199 void runParticleScript(double now, const char* entry, int start, int count);
200
Brian Osman7c979f52019-02-12 13:27:51 -0500201 sk_sp<SkParticleEffectParams> fParams;
Brian Osman7c979f52019-02-12 13:27:51 -0500202
Brian Osman5c1f8eb2019-02-14 14:49:55 -0500203 SkRandom fRandom;
204
205 bool fLooping;
Brian Osman7c979f52019-02-12 13:27:51 -0500206 int fCount;
207 double fLastTime;
208 float fSpawnRemainder;
209
Brian Osman5b431132019-10-15 16:41:18 -0400210 // C++ version of the SkSL Effect struct. This is the inout parameter to per-effect scripts,
211 // and provided as a uniform (named 'effect') to the per-particle scripts.
Brian Osmand46cb972019-09-12 16:25:52 -0400212 struct EffectState {
Brian Osmand46cb972019-09-12 16:25:52 -0400213 float fAge;
214 float fLifetime;
215 int fLoopCount;
216 float fRate;
217 int fBurst;
218
219 // Properties that determine default values for new particles
220 SkPoint fPosition;
221 SkVector fHeading;
222 float fScale;
223 SkVector fVelocity;
224 float fSpin;
225 SkColor4f fColor;
226 float fFrame;
Brian Osman559ffe42019-09-25 11:24:50 -0400227 uint32_t fFlags;
Brian Osmand46cb972019-09-12 16:25:52 -0400228 };
229 EffectState fState;
230
Brian Osmanfe491632019-07-25 15:14:50 -0400231 SkParticles fParticles;
232 SkAutoTMalloc<SkRandom> fStableRandoms;
Brian Osman7c979f52019-02-12 13:27:51 -0500233
234 // Cached
235 int fCapacity;
Brian Osman5b431132019-10-15 16:41:18 -0400236 SkTArray<float, true> fEffectUniforms;
237 SkTArray<float, true> fParticleUniforms;
Brian Osman9a8b8462019-09-19 10:06:36 -0400238
239 // Private interface used by SkEffectBinding and SkEffectExternalValue to spawn sub effects
240 friend class SkEffectExternalValue;
241 struct SpawnRequest {
242 SpawnRequest(int index, bool loop, sk_sp<SkParticleEffectParams> params)
243 : fIndex(index)
244 , fLoop(loop)
245 , fParams(std::move(params)) {}
246
247 int fIndex;
248 bool fLoop;
249 sk_sp<SkParticleEffectParams> fParams;
250 };
251 void addSpawnRequest(int index, bool loop, sk_sp<SkParticleEffectParams> params) {
252 fSpawnRequests.emplace_back(index, loop, std::move(params));
253 }
254 SkTArray<SpawnRequest> fSpawnRequests;
255
256 SkTArray<sk_sp<SkParticleEffect>> fSubEffects;
Brian Osman7c979f52019-02-12 13:27:51 -0500257};
258
259#endif // SkParticleEffect_DEFINED