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 #include <vector>
21 
22 class SkCanvas;
23 class SkFieldVisitor;
24 class SkParticleBinding;
25 class SkParticleDrawable;
26 struct SkParticleProgram;
27 
28 namespace skresources {
29     class ResourceProvider;
30 }  // namespace skresources
31 
32 namespace SkSL {
33     class ExternalFunction;
34     struct UniformInfo;
35 }  // namespace SkSL
36 
37 class SkParticleEffectParams : public SkRefCnt {
38 public:
39     SkParticleEffectParams();
40 
41     // Maximum number of particles per instance of the effect
42     int   fMaxCount;
43 
44     // What is drawn for each particle? (Image, shape, sprite sheet, etc.)
45     // See SkParticleDrawable::Make*
46     sk_sp<SkParticleDrawable> fDrawable;
47 
48     // Particle behavior is driven by SkSL code. Effect functions get a mutable Effect struct:
49     //
50     // struct Effect {
51     //   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
57     //
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
61     //   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     //   float  seed  = 0;               // Random value, used with rand() (see below)
68     // };
69     //
70     // Particle functions get a mutable Particle struct, as well as a uniform copy of the current
71     // 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     //   float  seed;
84     // };
85     //
86     // All functions have access to a global function named 'rand'. It takes a float seed value,
87     // which it uses and updates (using a PRNG). It returns a random floating point value in [0, 1].
88     // Typical usage is to pass the particle or effect's seed value to rand.
89     // For particle functions, the seed is rewound after each update, so calls to 'rand(p.seed)'
90     // will return consistent values from one update to the next.
91     //
92     // Finally, there is one global uniform values available, 'dt'. This is a floating point
93     // number of seconds that have elapsed since the last update.
94     //
95     // There are four functions that can be defined in fCode:
96     //
97     // 'void effectSpawn(inout Effect e)' is called when an instance of the effect is first
98     // created, and again at every loop point (if the effect is played with the looping flag).
99     //
100     // 'void effectUpdate(inout Effect e)' is called once per update to adjust properties of the
101     // effect (ie emitter).
102     //
103     // 'void spawn(inout Particle p)' is called once for each particle when it is first created,
104     // to set initial values. At a minimum, this should set 'lifetime' to the number of seconds
105     // that the particle will exist. Other parameters will get default values from the effect.
106     //
107     // 'void update(inout Particle p)' is called for each particle on every call to the running
108     // SkParticleEffect's update() method. It can animate any of the particle's values. Note that
109     // the 'lifetime' field has a different meaning in 'update', and should not be used or changed.
110 
111     SkString fCode;
112 
113     // External objects accessible by the effect's SkSL code. Each binding is a name and particular
114     // kind of object. See SkParticleBinding::Make* for details.
115     SkTArray<sk_sp<SkParticleBinding>> fBindings;
116 
117     void visitFields(SkFieldVisitor* v);
118 
119     // Load/compute cached resources
120     void prepare(const skresources::ResourceProvider*);
121 
122 private:
123     friend class SkParticleEffect;
124 
125     std::unique_ptr<SkParticleProgram> fProgram;
126 };
127 
128 class SkParticleEffect : public SkRefCnt {
129 public:
130     SkParticleEffect(sk_sp<SkParticleEffectParams> params);
131 
132     // Start playing this effect, specifying initial values for the emitter's properties
133     void start(double now, bool looping, SkPoint position, SkVector heading, float scale,
134                SkVector velocity, float spin, SkColor4f color, float frame, float seed);
135 
136     // Start playing this effect, with default values for the emitter's properties
start(double now,bool looping)137     void start(double now, bool looping) {
138         this->start(now, looping,
139                     { 0.0f, 0.0f },              // position
140                     { 0.0f, -1.0f },             // heading
141                     1.0f,                        // scale
142                     { 0.0f, 0.0f },              // velocity
143                     0.0f,                        // spin
144                     { 1.0f, 1.0f, 1.0f, 1.0f },  // color
145                     0.0f,                        // sprite frame
146                     0.0f);                       // seed
147     }
148 
149     void update(double now);
150     void draw(SkCanvas* canvas);
151 
isAlive()152     bool isAlive() const { return (fState.fAge >= 0 && fState.fAge <= 1); }
getCount()153     int getCount() const { return fCount; }
154 
getRate()155     float     getRate()     const { return fState.fRate;     }
getBurst()156     int       getBurst()    const { return fState.fBurst;    }
getPosition()157     SkPoint   getPosition() const { return fState.fPosition; }
getHeading()158     SkVector  getHeading()  const { return fState.fHeading;  }
getScale()159     float     getScale()    const { return fState.fScale;    }
getVelocity()160     SkVector  getVelocity() const { return fState.fVelocity; }
getSpin()161     float     getSpin()     const { return fState.fSpin;     }
getColor()162     SkColor4f getColor()    const { return fState.fColor;    }
getFrame()163     float     getFrame()    const { return fState.fFrame;    }
164 
setRate(float r)165     void setRate    (float     r) { fState.fRate     = r; }
setBurst(int b)166     void setBurst   (int       b) { fState.fBurst    = b; }
setPosition(SkPoint p)167     void setPosition(SkPoint   p) { fState.fPosition = p; }
setHeading(SkVector h)168     void setHeading (SkVector  h) { fState.fHeading  = h; }
setScale(float s)169     void setScale   (float     s) { fState.fScale    = s; }
setVelocity(SkVector v)170     void setVelocity(SkVector  v) { fState.fVelocity = v; }
setSpin(float s)171     void setSpin    (float     s) { fState.fSpin     = s; }
setColor(SkColor4f c)172     void setColor   (SkColor4f c) { fState.fColor    = c; }
setFrame(float f)173     void setFrame   (float     f) { fState.fFrame    = f; }
174 
175     const SkSL::UniformInfo* uniformInfo() const;
uniformData()176     float* uniformData() { return fUniforms.data(); }
177 
178     // Sets named uniform to the data in 'val'. 'count' must be equal to the total number of floats
179     // in the uniform (eg, the number of elements in a vector). Returns false if the uniform isn't
180     // found, or if count is incorrect. Returns true if the value is changed successfully.
181     bool setUniform(const char* name, const float* val, int count);
182 
183     static void RegisterParticleTypes();
184 
185 private:
186     void setCapacity(int capacity);
187     void updateStorage();
188 
189     // Helpers to break down update
190     void advanceTime(double now);
191 
192     enum class EntryPoint {
193         kSpawn,
194         kUpdate,
195     };
196 
197     void runEffectScript(EntryPoint entryPoint);
198     void runParticleScript(EntryPoint entryPoint, int start, int count);
199 
200     sk_sp<SkParticleEffectParams> fParams;
201 
202     bool   fLooping;
203     int    fCount;
204     double fLastTime;
205     float  fSpawnRemainder;
206 
207     // C++ version of the SkSL Effect struct. This is the inout parameter to per-effect scripts,
208     // and provided as a uniform (named 'effect') to all scripts.
209     struct EffectState {
210         float fAge;
211         float fLifetime;
212         int   fLoopCount;
213         float fRate;
214         int   fBurst;
215 
216         // Properties that determine default values for new particles
217         SkPoint   fPosition;
218         SkVector  fHeading;
219         float     fScale;
220         SkVector  fVelocity;
221         float     fSpin;
222         SkColor4f fColor;
223         float     fFrame;
224         float     fRandom;
225     };
226     EffectState fState;
227 
228     SkParticles          fParticles;
229     SkAutoTMalloc<float> fStableRandoms;
230 
231     // Cached
232     int fCapacity = 0;
233     SkTArray<float, true> fUniforms;
234 
235     friend struct SkParticleProgram;
236 };
237 
238 #endif // SkParticleEffect_DEFINED
239