blob: a4c4a20969eddc4a17eb2153cd34be6e01c2f56b [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 {
29 struct 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,
135 SkVector velocity, float spin, SkColor4f color, float frame);
136
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
146 0.0f); // sprite frame
147 }
148
Kevin Lubick269fe892019-03-06 09:32:55 -0500149 void update(double now);
Brian Osman7c979f52019-02-12 13:27:51 -0500150 void draw(SkCanvas* canvas);
151
Brian Osman9a8b8462019-09-19 10:06:36 -0400152 bool isAlive(bool includeSubEffects = true) const {
153 return (fState.fAge >= 0 && fState.fAge <= 1)
154 || (includeSubEffects && !fSubEffects.empty());
155 }
Brian Osmanb77d5022019-03-06 11:08:48 -0500156 int getCount() const { return fCount; }
Brian Osman7c979f52019-02-12 13:27:51 -0500157
Brian Osman2aa85df2019-08-30 10:59:47 -0400158 static void RegisterParticleTypes();
159
Brian Osman7c979f52019-02-12 13:27:51 -0500160private:
161 void setCapacity(int capacity);
162
Brian Osman9a8b8462019-09-19 10:06:36 -0400163 // Helpers to break down update
164 void advanceTime(double now);
165
166 void processEffectSpawnRequests(double now);
167 int runEffectScript(double now, const char* entry);
168
169 void processParticleSpawnRequests(double now, int start);
170 void runParticleScript(double now, const char* entry, int start, int count);
171
Brian Osman7c979f52019-02-12 13:27:51 -0500172 sk_sp<SkParticleEffectParams> fParams;
Brian Osman7c979f52019-02-12 13:27:51 -0500173
Brian Osman5c1f8eb2019-02-14 14:49:55 -0500174 SkRandom fRandom;
175
176 bool fLooping;
Brian Osman7c979f52019-02-12 13:27:51 -0500177 int fCount;
178 double fLastTime;
179 float fSpawnRemainder;
180
Brian Osmand46cb972019-09-12 16:25:52 -0400181 // Effect-associated values exposed to script. They are some mix of uniform and inout,
Brian Osman9a8b8462019-09-19 10:06:36 -0400182 // depending on whether we're executing per-effect or per-particle scripts.
Brian Osmand46cb972019-09-12 16:25:52 -0400183 struct EffectState {
184 float fDeltaTime;
185
186 // Above this line is always uniform. Below is uniform for particles, inout for effect.
187
188 float fAge;
189 float fLifetime;
190 int fLoopCount;
191 float fRate;
192 int fBurst;
193
194 // Properties that determine default values for new particles
195 SkPoint fPosition;
196 SkVector fHeading;
197 float fScale;
198 SkVector fVelocity;
199 float fSpin;
200 SkColor4f fColor;
201 float fFrame;
202 };
203 EffectState fState;
204
Brian Osmanfe491632019-07-25 15:14:50 -0400205 SkParticles fParticles;
206 SkAutoTMalloc<SkRandom> fStableRandoms;
Brian Osman7c979f52019-02-12 13:27:51 -0500207
208 // Cached
209 int fCapacity;
Brian Osman9a8b8462019-09-19 10:06:36 -0400210
211 // Private interface used by SkEffectBinding and SkEffectExternalValue to spawn sub effects
212 friend class SkEffectExternalValue;
213 struct SpawnRequest {
214 SpawnRequest(int index, bool loop, sk_sp<SkParticleEffectParams> params)
215 : fIndex(index)
216 , fLoop(loop)
217 , fParams(std::move(params)) {}
218
219 int fIndex;
220 bool fLoop;
221 sk_sp<SkParticleEffectParams> fParams;
222 };
223 void addSpawnRequest(int index, bool loop, sk_sp<SkParticleEffectParams> params) {
224 fSpawnRequests.emplace_back(index, loop, std::move(params));
225 }
226 SkTArray<SpawnRequest> fSpawnRequests;
227
228 SkTArray<sk_sp<SkParticleEffect>> fSubEffects;
Brian Osman7c979f52019-02-12 13:27:51 -0500229};
230
231#endif // SkParticleEffect_DEFINED