| 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 |  | 
|---|