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