blob: d1f7b6486b75acf83e69ee95d51ba4d9f6050937 [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 "SkReflected.h"
18#include "SkRSXform.h"
19
Brian Osman7c979f52019-02-12 13:27:51 -050020void SkParticleEffectParams::visitFields(SkFieldVisitor* v) {
21 v->visit("MaxCount", fMaxCount);
Brian Osman5c1f8eb2019-02-14 14:49:55 -050022 v->visit("Duration", fEffectDuration);
Brian Osman7c979f52019-02-12 13:27:51 -050023 v->visit("Rate", fRate);
24 v->visit("Life", fLifetime);
Brian Osman7c979f52019-02-12 13:27:51 -050025
Brian Osman543d2e22019-02-15 14:29:38 -050026 v->visit("Drawable", fDrawable);
Brian Osman7c979f52019-02-12 13:27:51 -050027
Brian Osman5c1f8eb2019-02-14 14:49:55 -050028 v->visit("Spawn", fSpawnAffectors);
29 v->visit("Update", fUpdateAffectors);
Brian Osman7c979f52019-02-12 13:27:51 -050030}
31
Brian Osman5c1f8eb2019-02-14 14:49:55 -050032SkParticleEffect::SkParticleEffect(sk_sp<SkParticleEffectParams> params, const SkRandom& random)
Brian Osman7c979f52019-02-12 13:27:51 -050033 : fParams(std::move(params))
Brian Osman5c1f8eb2019-02-14 14:49:55 -050034 , fRandom(random)
35 , fLooping(false)
36 , fSpawnTime(-1.0)
Brian Osman7c979f52019-02-12 13:27:51 -050037 , fCount(0)
Brian Osman5c1f8eb2019-02-14 14:49:55 -050038 , fLastTime(-1.0)
Brian Osman7c979f52019-02-12 13:27:51 -050039 , fSpawnRemainder(0.0f) {
40 this->setCapacity(fParams->fMaxCount);
Brian Osman7c979f52019-02-12 13:27:51 -050041}
42
Brian Osman5c1f8eb2019-02-14 14:49:55 -050043void SkParticleEffect::start(const SkAnimTimer& timer, bool looping) {
44 fCount = 0;
45 fLastTime = fSpawnTime = timer.secs();
46 fSpawnRemainder = 0.0f;
47 fLooping = looping;
48}
49
50void SkParticleEffect::update(const SkAnimTimer& timer) {
Brian Osman543d2e22019-02-15 14:29:38 -050051 if (!timer.isRunning() || !this->isAlive() || !fParams->fDrawable) {
Brian Osman7c979f52019-02-12 13:27:51 -050052 return;
53 }
54
Brian Osmand8e1ee92019-02-20 14:33:49 -050055 double now = timer.secs();
56 float deltaTime = static_cast<float>(now - fLastTime);
57 if (deltaTime < 0.0f) {
58 return;
59 }
60 fLastTime = now;
61
Brian Osman7c979f52019-02-12 13:27:51 -050062 // Handle user edits to fMaxCount
63 if (fParams->fMaxCount != fCapacity) {
64 this->setCapacity(fParams->fMaxCount);
65 }
66
Brian Osman7c979f52019-02-12 13:27:51 -050067 SkParticleUpdateParams updateParams;
68 updateParams.fDeltaTime = deltaTime;
Brian Osman7c979f52019-02-12 13:27:51 -050069
Brian Osmand8e1ee92019-02-20 14:33:49 -050070 // Advance age for existing particles, and remove any that have reached their end of life
Brian Osman7c979f52019-02-12 13:27:51 -050071 for (int i = 0; i < fCount; ++i) {
Brian Osmand8e1ee92019-02-20 14:33:49 -050072 fParticles[i].fAge += fParticles[i].fInvLifetime * deltaTime;
73 if (fParticles[i].fAge > 1.0f) {
Brian Osman7c979f52019-02-12 13:27:51 -050074 // NOTE: This is fast, but doesn't preserve drawing order. Could be a problem...
Brian Osman125daa42019-02-20 12:25:20 -050075 fParticles[i] = fParticles[fCount - 1];
76 fStableRandoms[i] = fStableRandoms[fCount - 1];
Brian Osman7c979f52019-02-12 13:27:51 -050077 --i;
78 --fCount;
Brian Osman5c1f8eb2019-02-14 14:49:55 -050079 }
80 }
81
82 // Spawn new particles
83 float desired = fParams->fRate * deltaTime + fSpawnRemainder;
84 int numToSpawn = sk_float_round2int(desired);
85 fSpawnRemainder = desired - numToSpawn;
86 numToSpawn = SkTPin(numToSpawn, 0, fParams->fMaxCount - fCount);
Brian Osman3d76d1b2019-02-28 15:48:05 -050087 if (numToSpawn) {
Brian Osman125daa42019-02-20 12:25:20 -050088 // This isn't "particle" t, it's effect t.
Brian Osmand8e1ee92019-02-20 14:33:49 -050089 float t = static_cast<float>((now - fSpawnTime) / fParams->fEffectDuration);
90 t = fLooping ? fmodf(t, 1.0f) : SkTPin(t, 0.0f, 1.0f);
Brian Osman5c1f8eb2019-02-14 14:49:55 -050091
Brian Osman8b6283f2019-02-14 16:55:21 -050092 for (int i = 0; i < numToSpawn; ++i) {
Brian Osman125daa42019-02-20 12:25:20 -050093 // Mutate our SkRandom so each particle definitely gets a different stable generator
94 fRandom.nextU();
95
Brian Osmand8e1ee92019-02-20 14:33:49 -050096 // Temporarily set our age to the *effect* age, so spawn affectors are driven by that
97 fParticles[fCount].fAge = t;
98 fParticles[fCount].fInvLifetime =
99 sk_ieee_float_divide(1.0f, fParams->fLifetime.eval(t, fRandom));
Brian Osman3d76d1b2019-02-28 15:48:05 -0500100 fParticles[fCount].fPose.fPosition = { 0.0f, 0.0f };
101 fParticles[fCount].fPose.fHeading = { 0.0f, -1.0f };
102 fParticles[fCount].fPose.fScale = 1.0f;
Brian Osman125daa42019-02-20 12:25:20 -0500103 fParticles[fCount].fVelocity.fLinear = { 0.0f, 0.0f };
104 fParticles[fCount].fVelocity.fAngular = 0.0f;
105 fParticles[fCount].fColor = { 1.0f, 1.0f, 1.0f, 1.0f };
106 fParticles[fCount].fFrame = 0.0f;
Brian Osman8b6283f2019-02-14 16:55:21 -0500107
Brian Osmane5d532e2019-02-26 14:58:40 -0500108 fParticles[fCount].fRandom = fStableRandoms[fCount] = fRandom;
Brian Osman8b6283f2019-02-14 16:55:21 -0500109 fCount++;
110 }
111
Brian Osmand8e1ee92019-02-20 14:33:49 -0500112 // Apply spawn affectors, then reset our age to 0 (the *particle* age)
Brian Osman14a67a32019-02-25 14:30:44 -0500113 for (auto affector : fParams->fSpawnAffectors) {
114 if (affector) {
115 affector->apply(updateParams, fParticles + (fCount - numToSpawn), numToSpawn);
Brian Osman5c1f8eb2019-02-14 14:49:55 -0500116 }
Brian Osman14a67a32019-02-25 14:30:44 -0500117 }
118 for (int i = fCount - numToSpawn; i < fCount; ++i) {
Brian Osmand8e1ee92019-02-20 14:33:49 -0500119 fParticles[i].fAge = 0.0f;
Brian Osman5c1f8eb2019-02-14 14:49:55 -0500120 }
121 }
122
Brian Osman125daa42019-02-20 12:25:20 -0500123 // Restore all stable random generators so update affectors get consistent behavior each frame
124 for (int i = 0; i < fCount; ++i) {
Brian Osmane5d532e2019-02-26 14:58:40 -0500125 fParticles[i].fRandom = fStableRandoms[i];
Brian Osman125daa42019-02-20 12:25:20 -0500126 }
127
Brian Osman5c1f8eb2019-02-14 14:49:55 -0500128 // Apply update rules
Brian Osman14a67a32019-02-25 14:30:44 -0500129 for (auto affector : fParams->fUpdateAffectors) {
130 if (affector) {
131 affector->apply(updateParams, fParticles, fCount);
Brian Osman7c979f52019-02-12 13:27:51 -0500132 }
Brian Osman14a67a32019-02-25 14:30:44 -0500133 }
Brian Osman7c979f52019-02-12 13:27:51 -0500134
Brian Osman14a67a32019-02-25 14:30:44 -0500135 // Do fixed-function update work (integration of position and orientation)
136 for (int i = 0; i < fCount; ++i) {
Brian Osman125daa42019-02-20 12:25:20 -0500137 fParticles[i].fPose.fPosition += fParticles[i].fVelocity.fLinear * deltaTime;
Brian Osman7c979f52019-02-12 13:27:51 -0500138
Brian Osman125daa42019-02-20 12:25:20 -0500139 SkScalar c, s = SkScalarSinCos(fParticles[i].fVelocity.fAngular * deltaTime, &c);
140 SkVector oldHeading = fParticles[i].fPose.fHeading;
141 fParticles[i].fPose.fHeading = { oldHeading.fX * c - oldHeading.fY * s,
142 oldHeading.fX * s + oldHeading.fY * c };
Brian Osman7c979f52019-02-12 13:27:51 -0500143 }
Brian Osman5c1f8eb2019-02-14 14:49:55 -0500144
145 // Mark effect as dead if we've reached the end (and are not looping)
146 if (!fLooping && (now - fSpawnTime) > fParams->fEffectDuration) {
147 fSpawnTime = -1.0;
148 }
Brian Osman7c979f52019-02-12 13:27:51 -0500149}
150
151void SkParticleEffect::draw(SkCanvas* canvas) {
Brian Osman543d2e22019-02-15 14:29:38 -0500152 if (this->isAlive() && fParams->fDrawable) {
Brian Osman5c1f8eb2019-02-14 14:49:55 -0500153 SkPaint paint;
154 paint.setFilterQuality(SkFilterQuality::kMedium_SkFilterQuality);
Brian Osman125daa42019-02-20 12:25:20 -0500155 fParams->fDrawable->draw(canvas, fParticles.get(), fCount, &paint);
Brian Osman5c1f8eb2019-02-14 14:49:55 -0500156 }
Brian Osman7c979f52019-02-12 13:27:51 -0500157}
158
159void SkParticleEffect::setCapacity(int capacity) {
160 fParticles.realloc(capacity);
Brian Osman125daa42019-02-20 12:25:20 -0500161 fStableRandoms.realloc(capacity);
Brian Osman7c979f52019-02-12 13:27:51 -0500162
163 fCapacity = capacity;
164 fCount = SkTMin(fCount, fCapacity);
165}