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
22class SkCanvas;
23class SkFieldVisitor;
24class SkParticleBinding;
25class SkParticleDrawable;
26struct SkParticleProgram;
27
28namespace skresources {
29    class ResourceProvider;
30}  // namespace skresources
31
32namespace SkSL {
33    class ExternalFunction;
34    struct UniformInfo;
35}  // namespace SkSL
36
37class SkParticleEffectParams : public SkRefCnt {
38public:
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
122private:
123    friend class SkParticleEffect;
124
125    std::unique_ptr<SkParticleProgram> fProgram;
126};
127
128class SkParticleEffect : public SkRefCnt {
129public:
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
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
152    bool isAlive() const { return (fState.fAge >= 0 && fState.fAge <= 1); }
153    int getCount() const { return fCount; }
154
155    float     getRate()     const { return fState.fRate;     }
156    int       getBurst()    const { return fState.fBurst;    }
157    SkPoint   getPosition() const { return fState.fPosition; }
158    SkVector  getHeading()  const { return fState.fHeading;  }
159    float     getScale()    const { return fState.fScale;    }
160    SkVector  getVelocity() const { return fState.fVelocity; }
161    float     getSpin()     const { return fState.fSpin;     }
162    SkColor4f getColor()    const { return fState.fColor;    }
163    float     getFrame()    const { return fState.fFrame;    }
164
165    void setRate    (float     r) { fState.fRate     = r; }
166    void setBurst   (int       b) { fState.fBurst    = b; }
167    void setPosition(SkPoint   p) { fState.fPosition = p; }
168    void setHeading (SkVector  h) { fState.fHeading  = h; }
169    void setScale   (float     s) { fState.fScale    = s; }
170    void setVelocity(SkVector  v) { fState.fVelocity = v; }
171    void setSpin    (float     s) { fState.fSpin     = s; }
172    void setColor   (SkColor4f c) { fState.fColor    = c; }
173    void setFrame   (float     f) { fState.fFrame    = f; }
174
175    const SkSL::UniformInfo* uniformInfo() const;
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
185private:
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