Commit 09cfb059 authored by Viktor Lidholt's avatar Viktor Lidholt

Merge pull request #300 from vlidholt/master

Optimizations for Particle Systems
parents 6d717593 59b0e328
...@@ -20,25 +20,6 @@ class ColorSequence { ...@@ -20,25 +20,6 @@ class ColorSequence {
colorStops = new List<double>.from(sequence.colorStops); colorStops = new List<double>.from(sequence.colorStops);
} }
ColorSequence.copyWithVariance(ColorSequence sequence, int alphaVar, int redVar, int greenVar, int blueVar) {
colors = new List<Color>();
colorStops = new List<double>.from(sequence.colorStops);
for (Color color in sequence.colors) {
int aDelta = ((randomDouble() * 2.0 - 1.0) * alphaVar).toInt();
int rDelta = ((randomDouble() * 2.0 - 1.0) * redVar).toInt();
int gDelta = ((randomDouble() * 2.0 - 1.0) * greenVar).toInt();
int bDelta = ((randomDouble() * 2.0 - 1.0) * blueVar).toInt();
int aNew = (color.alpha + aDelta).clamp(0, 255);
int rNew = (color.red + rDelta).clamp(0, 255);
int gNew = (color.green + gDelta).clamp(0, 255);
int bNew = (color.blue + bDelta).clamp(0, 255);
colors.add(new Color.fromARGB(aNew, rNew, gNew, bNew));
}
}
Color colorAtPosition(double pos) { Color colorAtPosition(double pos) {
assert(pos >= 0.0 && pos <= 1.0); assert(pos >= 0.0 && pos <= 1.0);
......
...@@ -16,12 +16,18 @@ class _Particle { ...@@ -16,12 +16,18 @@ class _Particle {
double timeToLive; double timeToLive;
Vector2 dir; Vector2 dir;
double radialAccel;
double tangentialAccel; _ParticleAccelerations accelerations;
Float64List simpleColorSequence;
ColorSequence colorSequence; ColorSequence colorSequence;
} }
class _ParticleAccelerations {
double radialAccel;
double tangentialAccel;
}
class ParticleSystem extends Node { class ParticleSystem extends Node {
...@@ -76,10 +82,12 @@ class ParticleSystem extends Node { ...@@ -76,10 +82,12 @@ class ParticleSystem extends Node {
List<_Particle> _particles; List<_Particle> _particles;
double _emitCounter; double _emitCounter;
// Not yet used:
// double _elapsedTime;
int _numEmittedParticles = 0; int _numEmittedParticles = 0;
static Paint _paint = new Paint()
..setFilterQuality(FilterQuality.low)
..isAntiAlias = false;
ParticleSystem(this.texture, ParticleSystem(this.texture,
{this.life: 1.5, {this.life: 1.5,
this.lifeVar: 1.0, this.lifeVar: 1.0,
...@@ -154,6 +162,7 @@ class ParticleSystem extends Node { ...@@ -154,6 +162,7 @@ class ParticleSystem extends Node {
// Update the particle // Update the particle
if (particle.accelerations != null) {
// Radial acceleration // Radial acceleration
Vector2 radial; Vector2 radial;
if (particle.pos[0] != 0 || particle.pos[1] != 0) { if (particle.pos[0] != 0 || particle.pos[1] != 0) {
...@@ -162,18 +171,26 @@ class ParticleSystem extends Node { ...@@ -162,18 +171,26 @@ class ParticleSystem extends Node {
radial = new Vector2.zero(); radial = new Vector2.zero();
} }
Vector2 tangential = new Vector2.copy(radial); Vector2 tangential = new Vector2.copy(radial);
radial.scale(particle.radialAccel); radial.scale(particle.accelerations.radialAccel);
// Tangential acceleration // Tangential acceleration
double newY = tangential.x; double newY = tangential.x;
tangential.x = -tangential.y; tangential.x = -tangential.y;
tangential.y = newY; tangential.y = newY;
tangential.scale(particle.tangentialAccel); tangential.scale(particle.accelerations.tangentialAccel);
// (gravity + radial + tangential) * dt // (gravity + radial + tangential) * dt
Vector2 accel = (gravity + radial + tangential).scale(dt); Vector2 accel = (gravity + radial + tangential).scale(dt);
particle.dir += accel; particle.dir += accel;
particle.pos += new Vector2.copy(particle.dir).scale(dt); } else if (gravity[0] != 0.0 || gravity[1] != 0) {
// gravity
Vector2 accel = gravity.scale(dt);
particle.dir += accel;
}
// Update particle position
particle.pos[0] += particle.dir[0] * dt;
particle.pos[1] += particle.dir[1] * dt;
// Size // Size
particle.size = math.max(particle.size + particle.deltaSize * dt, 0.0); particle.size = math.max(particle.size + particle.deltaSize * dt, 0.0);
...@@ -182,8 +199,14 @@ class ParticleSystem extends Node { ...@@ -182,8 +199,14 @@ class ParticleSystem extends Node {
particle.rotation += particle.deltaRotation * dt; particle.rotation += particle.deltaRotation * dt;
// Color // Color
if (particle.simpleColorSequence != null) {
for (int i = 0; i < 4; i++) {
particle.simpleColorSequence[i] += particle.simpleColorSequence[i + 4] * dt;
}
} else {
particle.colorPos = math.min(particle.colorPos + particle.deltaColorPos * dt, 1.0); particle.colorPos = math.min(particle.colorPos + particle.deltaColorPos * dt, 1.0);
} }
}
if (autoRemoveOnFinish && _particles.length == 0 && _numEmittedParticles > 0) { if (autoRemoveOnFinish && _particles.length == 0 && _numEmittedParticles > 0) {
if (parent != null) removeFromParent(); if (parent != null) removeFromParent();
...@@ -218,18 +241,50 @@ class ParticleSystem extends Node { ...@@ -218,18 +241,50 @@ class ParticleSystem extends Node {
double speedFinal = speed + speedVar * randomSignedDouble(); double speedFinal = speed + speedVar * randomSignedDouble();
particle.dir = dirVector.scale(speedFinal); particle.dir = dirVector.scale(speedFinal);
// Accelerations
if (radialAcceleration != 0.0 || radialAccelerationVar != 0.0 ||
tangentialAcceleration != 0.0 || tangentialAccelerationVar != 0.0) {
particle.accelerations = new _ParticleAccelerations();
// Radial acceleration // Radial acceleration
particle.radialAccel = radialAcceleration + radialAccelerationVar * randomSignedDouble(); particle.accelerations.radialAccel = radialAcceleration + radialAccelerationVar * randomSignedDouble();
// Tangential acceleration // Tangential acceleration
particle.tangentialAccel = tangentialAcceleration + tangentialAccelerationVar * randomSignedDouble(); particle.accelerations.tangentialAccel = tangentialAcceleration + tangentialAccelerationVar * randomSignedDouble();
}
// Color // Color
particle.colorPos = 0.0; particle.colorPos = 0.0;
particle.deltaColorPos = 1.0 / particle.timeToLive; particle.deltaColorPos = 1.0 / particle.timeToLive;
if (alphaVar != 0 || redVar != 0 || greenVar != 0 || blueVar != 0) { if (alphaVar != 0 || redVar != 0 || greenVar != 0 || blueVar != 0) {
particle.colorSequence = new ColorSequence.copyWithVariance(colorSequence, alphaVar, redVar, greenVar, blueVar); particle.colorSequence = _ColorSequenceUtil.copyWithVariance(colorSequence, alphaVar, redVar, greenVar, blueVar);
}
// Optimizes the case where there are only two colors in the sequence
if (colorSequence.colors.length == 2) {
Color startColor;
Color endColor;
if (particle.colorSequence != null) {
startColor = particle.colorSequence.colors[0];
endColor = particle.colorSequence.colors[1];
} else {
startColor = colorSequence.colors[0];
endColor = colorSequence.colors[1];
}
// First 4 elements are start ARGB, last 4 are delta ARGB
particle.simpleColorSequence = new Float64List(8);
particle.simpleColorSequence[0] = startColor.alpha.toDouble();
particle.simpleColorSequence[1] = startColor.red.toDouble();
particle.simpleColorSequence[2] = startColor.green.toDouble();
particle.simpleColorSequence[3] = startColor.blue.toDouble();
particle.simpleColorSequence[4] = (endColor.alpha.toDouble() - startColor.alpha.toDouble()) / particle.timeToLive;
particle.simpleColorSequence[5] = (endColor.red.toDouble() - startColor.red.toDouble()) / particle.timeToLive;
particle.simpleColorSequence[6] = (endColor.green.toDouble() - startColor.green.toDouble()) / particle.timeToLive;
particle.simpleColorSequence[7] = (endColor.blue.toDouble() - startColor.blue.toDouble()) / particle.timeToLive;
} }
_particles.add(particle); _particles.add(particle);
...@@ -242,6 +297,8 @@ class ParticleSystem extends Node { ...@@ -242,6 +297,8 @@ class ParticleSystem extends Node {
List<Rect> rects = []; List<Rect> rects = [];
List<Color> colors = []; List<Color> colors = [];
_paint.setTransferMode(transferMode);
for (_Particle particle in _particles) { for (_Particle particle in _particles) {
// Transform // Transform
double scos; double scos;
...@@ -250,9 +307,12 @@ class ParticleSystem extends Node { ...@@ -250,9 +307,12 @@ class ParticleSystem extends Node {
double extraRotation = GameMath.atan2(particle.dir[1], particle.dir[0]); double extraRotation = GameMath.atan2(particle.dir[1], particle.dir[0]);
scos = math.cos(convertDegrees2Radians(particle.rotation) + extraRotation) * particle.size; scos = math.cos(convertDegrees2Radians(particle.rotation) + extraRotation) * particle.size;
ssin = math.sin(convertDegrees2Radians(particle.rotation) + extraRotation) * particle.size; ssin = math.sin(convertDegrees2Radians(particle.rotation) + extraRotation) * particle.size;
} else { } else if (particle.rotation != 0.0) {
scos = math.cos(convertDegrees2Radians(particle.rotation)) * particle.size; scos = math.cos(convertDegrees2Radians(particle.rotation)) * particle.size;
ssin = math.sin(convertDegrees2Radians(particle.rotation)) * particle.size; ssin = math.sin(convertDegrees2Radians(particle.rotation)) * particle.size;
} else {
scos = particle.size;
ssin = 0.0;
} }
RSTransform transform = new RSTransform(scos, ssin, particle.pos[0], particle.pos[1]); RSTransform transform = new RSTransform(scos, ssin, particle.pos[0], particle.pos[1]);
transforms.add(transform); transforms.add(transform);
...@@ -262,6 +322,14 @@ class ParticleSystem extends Node { ...@@ -262,6 +322,14 @@ class ParticleSystem extends Node {
rects.add(rect); rects.add(rect);
// Color // Color
if (particle.simpleColorSequence != null) {
Color particleColor = new Color.fromARGB(
particle.simpleColorSequence[0].toInt().clamp(0, 255),
particle.simpleColorSequence[1].toInt().clamp(0, 255),
particle.simpleColorSequence[2].toInt().clamp(0, 255),
particle.simpleColorSequence[3].toInt().clamp(0, 255));
colors.add(particleColor);
} else {
Color particleColor; Color particleColor;
if (particle.colorSequence != null) { if (particle.colorSequence != null) {
particleColor = particle.colorSequence.colorAtPosition(particle.colorPos); particleColor = particle.colorSequence.colorAtPosition(particle.colorPos);
...@@ -270,11 +338,39 @@ class ParticleSystem extends Node { ...@@ -270,11 +338,39 @@ class ParticleSystem extends Node {
} }
colors.add(particleColor); colors.add(particleColor);
} }
}
Paint paint = new Paint()..setTransferMode(transferMode)
..setFilterQuality(FilterQuality.low) // All Skia examples do this.
..isAntiAlias = false; // Antialiasing breaks SkCanvas.drawAtlas?
canvas.drawAtlas(texture.image, transforms, rects, colors, canvas.drawAtlas(texture.image, transforms, rects, colors,
TransferMode.modulate, null, paint); TransferMode.modulate, null, _paint);
}
}
class _ColorSequenceUtil {
static ColorSequence copyWithVariance(
ColorSequence sequence,
int alphaVar,
int redVar,
int greenVar,
int blueVar
) {
ColorSequence copy = new ColorSequence.copy(sequence);
int i = 0;
for (Color color in sequence.colors) {
int aDelta = ((randomDouble() * 2.0 - 1.0) * alphaVar).toInt();
int rDelta = ((randomDouble() * 2.0 - 1.0) * redVar).toInt();
int gDelta = ((randomDouble() * 2.0 - 1.0) * greenVar).toInt();
int bDelta = ((randomDouble() * 2.0 - 1.0) * blueVar).toInt();
int aNew = (color.alpha + aDelta).clamp(0, 255);
int rNew = (color.red + rDelta).clamp(0, 255);
int gNew = (color.green + gDelta).clamp(0, 255);
int bNew = (color.blue + bDelta).clamp(0, 255);
copy.colors[i] = new Color.fromARGB(aNew, rNew, gNew, bNew);
i++;
}
return copy;
} }
} }
...@@ -31,6 +31,10 @@ class SpriteBox extends RenderBox { ...@@ -31,6 +31,10 @@ class SpriteBox extends RenderBox {
void set rootNode (NodeWithSize value) { void set rootNode (NodeWithSize value) {
if (value == _rootNode) return; if (value == _rootNode) return;
// Ensure that the root node has a size
assert(value.size.width > 0);
assert(value.size.height > 0);
// Remove sprite box references // Remove sprite box references
if (_rootNode != null) _removeSpriteBoxReference(_rootNode); if (_rootNode != null) _removeSpriteBoxReference(_rootNode);
...@@ -334,9 +338,9 @@ class SpriteBox extends RenderBox { ...@@ -334,9 +338,9 @@ class SpriteBox extends RenderBox {
_frameRate = 1.0/delta; _frameRate = 1.0/delta;
// Print frame rate // // Print frame rate
if (_numFrames % 60 == 0) // if (_numFrames % 60 == 0)
print("delta: $delta fps: $_frameRate"); // print("delta: $delta fps: $_frameRate");
_runActions(_rootNode, delta); _runActions(_rootNode, delta);
_callUpdate(_rootNode, delta); _callUpdate(_rootNode, delta);
......
...@@ -57,26 +57,26 @@ class GameMath { ...@@ -57,26 +57,26 @@ class GameMath {
if (x >= 0) { if (x >= 0) {
if (y >= 0) { if (y >= 0) {
if (x >= y) if (x >= y)
return _atan2.ppy[(_Atan2Constants.size * y / x + 0.5).floor()]; return _atan2.ppy[(_Atan2Constants.size * y / x + 0.5).toInt()];
else else
return _atan2.ppx[(_Atan2Constants.size * x / y + 0.5).floor()]; return _atan2.ppx[(_Atan2Constants.size * x / y + 0.5).toInt()];
} else { } else {
if (x >= -y) if (x >= -y)
return _atan2.pny[(_Atan2Constants.ezis * y / x + 0.5).floor()]; return _atan2.pny[(_Atan2Constants.ezis * y / x + 0.5).toInt()];
else else
return _atan2.pnx[(_Atan2Constants.ezis * x / y + 0.5).floor()]; return _atan2.pnx[(_Atan2Constants.ezis * x / y + 0.5).toInt()];
} }
} else { } else {
if (y >= 0) { if (y >= 0) {
if (-x >= y) if (-x >= y)
return _atan2.npy[(_Atan2Constants.ezis * y / x + 0.5).floor()]; return _atan2.npy[(_Atan2Constants.ezis * y / x + 0.5).toInt()];
else else
return _atan2.npx[(_Atan2Constants.ezis * x / y + 0.5).floor()]; return _atan2.npx[(_Atan2Constants.ezis * x / y + 0.5).toInt()];
} else { } else {
if (x <= y) if (x <= y)
return _atan2.nny[(_Atan2Constants.size * y / x + 0.5).floor()]; return _atan2.nny[(_Atan2Constants.size * y / x + 0.5).toInt()];
else else
return _atan2.nnx[(_Atan2Constants.size * x / y + 0.5).floor()]; return _atan2.nnx[(_Atan2Constants.size * x / y + 0.5).toInt()];
} }
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment