1 | /* |
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 | |
11 | #include "include/core/SkColor.h" |
12 | #include "include/core/SkPoint.h" |
13 | #include "include/core/SkRefCnt.h" |
14 | #include "include/core/SkString.h" |
15 | #include "include/private/SkTArray.h" |
16 | #include "include/private/SkTemplates.h" |
17 | #include "modules/particles/include/SkParticleData.h" |
18 | |
19 | #include <memory> |
20 | |
21 | class SkCanvas; |
22 | class SkFieldVisitor; |
23 | class SkParticleBinding; |
24 | class SkParticleDrawable; |
25 | class SkParticleExternalValue; |
26 | |
27 | namespace skresources { |
28 | class ResourceProvider; |
29 | } |
30 | |
31 | namespace SkSL { |
32 | class ByteCode; |
33 | } |
34 | |
35 | class SkParticleEffectParams : public SkRefCnt { |
36 | public: |
37 | SkParticleEffectParams(); |
38 | |
39 | // Maximum number of particles per instance of the effect |
40 | int fMaxCount; |
41 | |
42 | // What is drawn for each particle? (Image, shape, sprite sheet, etc.) |
43 | // See SkParticleDrawable::Make* |
44 | sk_sp<SkParticleDrawable> fDrawable; |
45 | |
46 | // Particle behavior is driven by two chunks of SkSL code. Effect functions are defined in |
47 | // fEffectCode, and get a mutable Effect struct: |
48 | // |
49 | // struct Effect { |
50 | // 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 |
56 | // |
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 |
60 | // 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 |
66 | // uint flags = 0; // Arbitrary state for use by script |
67 | // uint seed = 0; // Random seed, used with rand() (see below) |
68 | // }; |
69 | // |
70 | // 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'. |
72 | // |
73 | // 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; |
83 | // uint flags; |
84 | // uint seed; |
85 | // }; |
86 | // |
87 | // 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. |
92 | // |
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: |
105 | // |
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 |
108 | // that the particle will exist. Other parameters will get default values from the effect. |
109 | // |
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. |
113 | |
114 | SkString fEffectCode; |
115 | SkString fParticleCode; |
116 | |
117 | // 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. |
119 | SkTArray<sk_sp<SkParticleBinding>> fBindings; |
120 | |
121 | void visitFields(SkFieldVisitor* v); |
122 | |
123 | // Load/compute cached resources |
124 | void prepare(const skresources::ResourceProvider*); |
125 | |
126 | private: |
127 | friend class SkParticleEffect; |
128 | |
129 | // Cached |
130 | struct Program { |
131 | std::unique_ptr<SkSL::ByteCode> fByteCode; |
132 | SkTArray<std::unique_ptr<SkParticleExternalValue>> fExternalValues; |
133 | }; |
134 | |
135 | Program fEffectProgram; |
136 | Program fParticleProgram; |
137 | }; |
138 | |
139 | class SkParticleEffect : public SkRefCnt { |
140 | public: |
141 | SkParticleEffect(sk_sp<SkParticleEffectParams> params); |
142 | |
143 | // 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, |
145 | SkVector velocity, float spin, SkColor4f color, float frame, uint32_t flags, |
146 | uint32_t seed); |
147 | |
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 |
157 | 0.0f, // sprite frame |
158 | 0, // flags |
159 | 0); // seed |
160 | } |
161 | |
162 | void update(double now); |
163 | void draw(SkCanvas* canvas); |
164 | |
165 | bool isAlive(bool includeSubEffects = true) const { |
166 | return (fState.fAge >= 0 && fState.fAge <= 1) |
167 | || (includeSubEffects && !fSubEffects.empty()); |
168 | } |
169 | int getCount() const { return fCount; } |
170 | |
171 | 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 | |
193 | const SkSL::ByteCode* effectCode() const { return fParams->fEffectProgram.fByteCode.get(); } |
194 | const SkSL::ByteCode* particleCode() const { return fParams->fParticleProgram.fByteCode.get(); } |
195 | |
196 | float* effectUniforms() { return fEffectUniforms.data(); } |
197 | float* particleUniforms() { return fParticleUniforms.data(); } |
198 | |
199 | static void RegisterParticleTypes(); |
200 | |
201 | private: |
202 | void setCapacity(int capacity); |
203 | |
204 | // Helpers to break down update |
205 | void advanceTime(double now); |
206 | |
207 | void processEffectSpawnRequests(double now); |
208 | void runEffectScript(double now, const char* entry); |
209 | |
210 | void processParticleSpawnRequests(double now, int start); |
211 | void runParticleScript(double now, const char* entry, int start, int count); |
212 | |
213 | sk_sp<SkParticleEffectParams> fParams; |
214 | |
215 | bool fLooping; |
216 | int fCount; |
217 | double fLastTime; |
218 | float fSpawnRemainder; |
219 | |
220 | // 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. |
222 | struct EffectState { |
223 | 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; |
237 | uint32_t fFlags; |
238 | uint32_t fRandom; |
239 | }; |
240 | EffectState fState; |
241 | |
242 | SkParticles fParticles; |
243 | SkAutoTMalloc<float> fStableRandoms; |
244 | |
245 | // Cached |
246 | int fCapacity; |
247 | SkTArray<float, true> fEffectUniforms; |
248 | SkTArray<float, true> fParticleUniforms; |
249 | |
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; |
268 | }; |
269 | |
270 | #endif // SkParticleEffect_DEFINED |
271 | |