blob: 922d36420feec0e9c9d76c070ac3d954201db94f [file] [log] [blame]
Brian Osman7c979f52019-02-12 13:27:51 -05001/*
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#include "SkParticleEffect.h"
9
10#include "Resources.h"
11#include "SkAnimTimer.h"
12#include "SkCanvas.h"
13#include "SkColorData.h"
Brian Osman7c979f52019-02-12 13:27:51 -050014#include "SkPaint.h"
15#include "SkParticleAffector.h"
Brian Osman543d2e22019-02-15 14:29:38 -050016#include "SkParticleDrawable.h"
Brian Osman7c979f52019-02-12 13:27:51 -050017#include "SkParticleEmitter.h"
18#include "SkReflected.h"
19#include "SkRSXform.h"
20
Brian Osman7c979f52019-02-12 13:27:51 -050021void SkParticleEffectParams::visitFields(SkFieldVisitor* v) {
22 v->visit("MaxCount", fMaxCount);
Brian Osman5c1f8eb2019-02-14 14:49:55 -050023 v->visit("Duration", fEffectDuration);
Brian Osman7c979f52019-02-12 13:27:51 -050024 v->visit("Rate", fRate);
25 v->visit("Life", fLifetime);
Brian Osman7c979f52019-02-12 13:27:51 -050026
Brian Osman543d2e22019-02-15 14:29:38 -050027 v->visit("Drawable", fDrawable);
Brian Osman7c979f52019-02-12 13:27:51 -050028 v->visit("Emitter", fEmitter);
29
Brian Osman5c1f8eb2019-02-14 14:49:55 -050030 v->visit("Spawn", fSpawnAffectors);
31 v->visit("Update", fUpdateAffectors);
Brian Osman7c979f52019-02-12 13:27:51 -050032}
33
Brian Osman5c1f8eb2019-02-14 14:49:55 -050034SkParticleEffect::SkParticleEffect(sk_sp<SkParticleEffectParams> params, const SkRandom& random)
Brian Osman7c979f52019-02-12 13:27:51 -050035 : fParams(std::move(params))
Brian Osman5c1f8eb2019-02-14 14:49:55 -050036 , fRandom(random)
37 , fLooping(false)
38 , fSpawnTime(-1.0)
Brian Osman7c979f52019-02-12 13:27:51 -050039 , fCount(0)
Brian Osman5c1f8eb2019-02-14 14:49:55 -050040 , fLastTime(-1.0)
Brian Osman7c979f52019-02-12 13:27:51 -050041 , fSpawnRemainder(0.0f) {
42 this->setCapacity(fParams->fMaxCount);
Brian Osman7c979f52019-02-12 13:27:51 -050043}
44
Brian Osman5c1f8eb2019-02-14 14:49:55 -050045void SkParticleEffect::start(const SkAnimTimer& timer, bool looping) {
46 fCount = 0;
47 fLastTime = fSpawnTime = timer.secs();
48 fSpawnRemainder = 0.0f;
49 fLooping = looping;
50}
51
52void SkParticleEffect::update(const SkAnimTimer& timer) {
Brian Osman543d2e22019-02-15 14:29:38 -050053 if (!timer.isRunning() || !this->isAlive() || !fParams->fDrawable) {
Brian Osman7c979f52019-02-12 13:27:51 -050054 return;
55 }
56
Brian Osmand8e1ee92019-02-20 14:33:49 -050057 double now = timer.secs();
58 float deltaTime = static_cast<float>(now - fLastTime);
59 if (deltaTime < 0.0f) {
60 return;
61 }
62 fLastTime = now;
63
Brian Osman7c979f52019-02-12 13:27:51 -050064 // Handle user edits to fMaxCount
65 if (fParams->fMaxCount != fCapacity) {
66 this->setCapacity(fParams->fMaxCount);
67 }
68
Brian Osman7c979f52019-02-12 13:27:51 -050069 SkParticleUpdateParams updateParams;
70 updateParams.fDeltaTime = deltaTime;
Brian Osman5c1f8eb2019-02-14 14:49:55 -050071 updateParams.fRandom = &fRandom;
Brian Osman7c979f52019-02-12 13:27:51 -050072
Brian Osmand8e1ee92019-02-20 14:33:49 -050073 // Advance age for existing particles, and remove any that have reached their end of life
Brian Osman7c979f52019-02-12 13:27:51 -050074 for (int i = 0; i < fCount; ++i) {
Brian Osmand8e1ee92019-02-20 14:33:49 -050075 fParticles[i].fAge += fParticles[i].fInvLifetime * deltaTime;
76 if (fParticles[i].fAge > 1.0f) {
Brian Osman7c979f52019-02-12 13:27:51 -050077 // NOTE: This is fast, but doesn't preserve drawing order. Could be a problem...
Brian Osman125daa42019-02-20 12:25:20 -050078 fParticles[i] = fParticles[fCount - 1];
79 fStableRandoms[i] = fStableRandoms[fCount - 1];
Brian Osman7c979f52019-02-12 13:27:51 -050080 --i;
81 --fCount;
Brian Osman5c1f8eb2019-02-14 14:49:55 -050082 }
83 }
84
85 // Spawn new particles
86 float desired = fParams->fRate * deltaTime + fSpawnRemainder;
87 int numToSpawn = sk_float_round2int(desired);
88 fSpawnRemainder = desired - numToSpawn;
89 numToSpawn = SkTPin(numToSpawn, 0, fParams->fMaxCount - fCount);
90 if (fParams->fEmitter) {
Brian Osman125daa42019-02-20 12:25:20 -050091 // This isn't "particle" t, it's effect t.
Brian Osmand8e1ee92019-02-20 14:33:49 -050092 float t = static_cast<float>((now - fSpawnTime) / fParams->fEffectDuration);
93 t = fLooping ? fmodf(t, 1.0f) : SkTPin(t, 0.0f, 1.0f);
Brian Osman5c1f8eb2019-02-14 14:49:55 -050094
Brian Osman8b6283f2019-02-14 16:55:21 -050095 for (int i = 0; i < numToSpawn; ++i) {
Brian Osman125daa42019-02-20 12:25:20 -050096 // Mutate our SkRandom so each particle definitely gets a different stable generator
97 fRandom.nextU();
98
Brian Osmand8e1ee92019-02-20 14:33:49 -050099 // Temporarily set our age to the *effect* age, so spawn affectors are driven by that
100 fParticles[fCount].fAge = t;
101 fParticles[fCount].fInvLifetime =
102 sk_ieee_float_divide(1.0f, fParams->fLifetime.eval(t, fRandom));
Brian Osman125daa42019-02-20 12:25:20 -0500103 fParticles[fCount].fPose = fParams->fEmitter->emit(fRandom);
104 fParticles[fCount].fVelocity.fLinear = { 0.0f, 0.0f };
105 fParticles[fCount].fVelocity.fAngular = 0.0f;
106 fParticles[fCount].fColor = { 1.0f, 1.0f, 1.0f, 1.0f };
107 fParticles[fCount].fFrame = 0.0f;
Brian Osman8b6283f2019-02-14 16:55:21 -0500108
Brian Osman125daa42019-02-20 12:25:20 -0500109 fParticles[fCount].fStableRandom = fStableRandoms[fCount] = fRandom;
Brian Osman8b6283f2019-02-14 16:55:21 -0500110 fCount++;
111 }
112
Brian Osmand8e1ee92019-02-20 14:33:49 -0500113 // Apply spawn affectors, then reset our age to 0 (the *particle* age)
Brian Osman14a67a32019-02-25 14:30:44 -0500114 for (auto affector : fParams->fSpawnAffectors) {
115 if (affector) {
116 affector->apply(updateParams, fParticles + (fCount - numToSpawn), numToSpawn);
Brian Osman5c1f8eb2019-02-14 14:49:55 -0500117 }
Brian Osman14a67a32019-02-25 14:30:44 -0500118 }
119 for (int i = fCount - numToSpawn; i < fCount; ++i) {
Brian Osmand8e1ee92019-02-20 14:33:49 -0500120 fParticles[i].fAge = 0.0f;
Brian Osman5c1f8eb2019-02-14 14:49:55 -0500121 }
122 }
123
Brian Osman125daa42019-02-20 12:25:20 -0500124 // Restore all stable random generators so update affectors get consistent behavior each frame
125 for (int i = 0; i < fCount; ++i) {
126 fParticles[i].fStableRandom = fStableRandoms[i];
127 }
128
Brian Osman5c1f8eb2019-02-14 14:49:55 -0500129 // Apply update rules
Brian Osman14a67a32019-02-25 14:30:44 -0500130 for (auto affector : fParams->fUpdateAffectors) {
131 if (affector) {
132 affector->apply(updateParams, fParticles, fCount);
Brian Osman7c979f52019-02-12 13:27:51 -0500133 }
Brian Osman14a67a32019-02-25 14:30:44 -0500134 }
Brian Osman7c979f52019-02-12 13:27:51 -0500135
Brian Osman14a67a32019-02-25 14:30:44 -0500136 // Do fixed-function update work (integration of position and orientation)
137 for (int i = 0; i < fCount; ++i) {
Brian Osman125daa42019-02-20 12:25:20 -0500138 fParticles[i].fPose.fPosition += fParticles[i].fVelocity.fLinear * deltaTime;
Brian Osman7c979f52019-02-12 13:27:51 -0500139
Brian Osman125daa42019-02-20 12:25:20 -0500140 SkScalar c, s = SkScalarSinCos(fParticles[i].fVelocity.fAngular * deltaTime, &c);
141 SkVector oldHeading = fParticles[i].fPose.fHeading;
142 fParticles[i].fPose.fHeading = { oldHeading.fX * c - oldHeading.fY * s,
143 oldHeading.fX * s + oldHeading.fY * c };
Brian Osman7c979f52019-02-12 13:27:51 -0500144 }
Brian Osman5c1f8eb2019-02-14 14:49:55 -0500145
146 // Mark effect as dead if we've reached the end (and are not looping)
147 if (!fLooping && (now - fSpawnTime) > fParams->fEffectDuration) {
148 fSpawnTime = -1.0;
149 }
Brian Osman7c979f52019-02-12 13:27:51 -0500150}
151
152void SkParticleEffect::draw(SkCanvas* canvas) {
Brian Osman543d2e22019-02-15 14:29:38 -0500153 if (this->isAlive() && fParams->fDrawable) {
Brian Osman5c1f8eb2019-02-14 14:49:55 -0500154 SkPaint paint;
155 paint.setFilterQuality(SkFilterQuality::kMedium_SkFilterQuality);
Brian Osman125daa42019-02-20 12:25:20 -0500156 fParams->fDrawable->draw(canvas, fParticles.get(), fCount, &paint);
Brian Osman5c1f8eb2019-02-14 14:49:55 -0500157 }
Brian Osman7c979f52019-02-12 13:27:51 -0500158}
159
160void SkParticleEffect::setCapacity(int capacity) {
161 fParticles.realloc(capacity);
Brian Osman125daa42019-02-20 12:25:20 -0500162 fStableRandoms.realloc(capacity);
Brian Osman7c979f52019-02-12 13:27:51 -0500163
164 fCapacity = capacity;
165 fCount = SkTMin(fCount, fCapacity);
166}