Skip to content

Commit

Permalink
slight speed improvements in fire, like 1-2FPS
Browse files Browse the repository at this point in the history
  • Loading branch information
DedeHai committed Mar 23, 2024
1 parent 913e910 commit 306a850
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 39 deletions.
81 changes: 45 additions & 36 deletions wled00/FXparticleSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -500,21 +500,23 @@ void ParticleSys_render(PSparticle *particles, uint32_t numParticles, bool wrapX
void FireParticle_update(PSparticle *part, bool wrapX)
{
// Matrix dimension
const uint16_t cols = strip.isMatrix ? SEGMENT.virtualWidth() : 1;
const uint16_t rows = strip.isMatrix ? SEGMENT.virtualHeight() : SEGMENT.virtualLength();
const int32_t cols = strip.isMatrix ? SEGMENT.virtualWidth() : 1;
const int32_t rows = strip.isMatrix ? SEGMENT.virtualHeight() : SEGMENT.virtualLength();

// particle box dimensions
const uint16_t PS_MAX_X(cols * PS_P_RADIUS - 1);
const uint16_t PS_MAX_Y(rows * PS_P_RADIUS - 1);
const int32_t PS_MAX_X=(cols * (uint32_t)PS_P_RADIUS - 1);
const int32_t PS_MAX_Y=(rows * (uint32_t)PS_P_RADIUS - 1);



if (part->ttl > 0)
{
// age
part->ttl--;

// apply velocity
part->x = part->x + (int16_t)part->vx;
part->y = part->y + (int16_t)part->vy + (part->ttl >> 4); // younger particles move faster upward as they are hotter, used for fire
part->x = part->x + (int32_t)part->vx;
part->y = part->y + (int32_t)part->vy + (part->ttl >> 4); // younger particles move faster upward as they are hotter, used for fire

part->outofbounds = 0;
// check if particle is out of bounds, wrap around to other side if wrapping is enabled
Expand Down Expand Up @@ -550,49 +552,57 @@ void FireParticle_update(PSparticle *part, bool wrapX)
void ParticleSys_renderParticleFire(PSparticle *particles, uint32_t numParticles, bool wrapX)
{

const uint16_t cols = strip.isMatrix ? SEGMENT.virtualWidth() : 1;
const uint16_t rows = strip.isMatrix ? SEGMENT.virtualHeight() : SEGMENT.virtualLength();
const uint32_t cols = strip.isMatrix ? SEGMENT.virtualWidth() : 1;
const uint32_t rows = strip.isMatrix ? SEGMENT.virtualHeight() : SEGMENT.virtualLength();

int32_t x, y;
uint8_t dx, dy;
uint32_t dx, dy;
uint32_t tempVal;
uint32_t i;

// go over particles and update matrix cells on the way
// note: some pixels (the x+1 ones) can be out of bounds, it is probably faster than to check that for every pixel as this only happens on the right border (and nothing bad happens as this is checked down the road)
for (i = 0; i < numParticles; i++)
{
if (particles[i].ttl == 0 || particles[i].outofbounds)

if (particles[i].outofbounds)
{
continue;
}

if (particles[i].ttl == 0)
{
continue;
}



dx = (uint8_t)((uint16_t)particles[i].x % (uint16_t)PS_P_RADIUS);
dy = (uint8_t)((uint16_t)particles[i].y % (uint16_t)PS_P_RADIUS);

x = (uint8_t)((uint16_t)particles[i].x / (uint16_t)PS_P_RADIUS); // compiler should optimize to bit shift
y = (uint8_t)((uint16_t)particles[i].y / (uint16_t)PS_P_RADIUS);
x = (uint8_t)((uint16_t)particles[i].x >> PS_P_RADIUS_SHIFT); // divide by PS_P_RADIUS which is 64, so can bitshift (compiler may not optimize automatically)
y = (uint8_t)((uint16_t)particles[i].y >> PS_P_RADIUS_SHIFT);

if (dx < (PS_P_RADIUS >> 1)) // jump to next physical pixel if half of virtual pixel size is reached
{
x--; // shift left
dx = dx + (PS_P_RADIUS >> 1); // add half a radius
dx += PS_P_RADIUS >> 1; // add half a radius
}
else // if jump has ocurred, fade pixel
{
// adjust dx so pixel fades
dx = dx - (PS_P_RADIUS >> 1);
dx -= PS_P_RADIUS >> 1;
}

if (dy < (PS_P_RADIUS >> 1)) // jump to next physical pixel if half of virtual pixel size is reached
{
y--; // shift row
dy = dy + (PS_P_RADIUS >> 1);
dy += PS_P_RADIUS >> 1;
}
else
{
// adjust dy so pixel fades
dy = dy - (PS_P_RADIUS >> 1);
dy -= PS_P_RADIUS >> 1;
}

if (wrapX)
Expand All @@ -605,56 +615,55 @@ void ParticleSys_renderParticleFire(PSparticle *particles, uint32_t numParticles

// calculate brightness values for all six pixels representing a particle using linear interpolation
// bottom left
if (x < cols && x >=0 && y < rows && y >=0)
//if (x < cols && x >=0 && y < rows && y >=0)
{
tempVal = (((uint32_t)((PS_P_RADIUS)-dx) * ((PS_P_RADIUS)-dy) * (uint32_t)particles[i].ttl) >> PS_P_SURFACE);
PartMatrix_addHeat(x, y, tempVal);
PartMatrix_addHeat(x + 1, y, tempVal); // shift particle by 1 pixel to the right and add heat again (makes flame wider without using more particles)
PartMatrix_addHeat(x, y, tempVal, rows);
PartMatrix_addHeat(x + 1, y, tempVal, rows); // shift particle by 1 pixel to the right and add heat again (makes flame wider without using more particles)
}
// bottom right;
x++;
if (wrapX)
{ // wrap it to the other side if required
if (x >= cols)
//if (x >= cols)
x = x % cols; // in case the right half of particle render is out of frame, wrap it (note: on microcontrollers with hardware division, the if statement is not really needed)
}
if (x < cols && y < rows && y >= 0)
//if (x < cols && y < rows && y >= 0)
{
tempVal = (((uint32_t)dx * ((PS_P_RADIUS)-dy) * (uint32_t)particles[i].ttl) >> PS_P_SURFACE);
PartMatrix_addHeat(x, y, tempVal);
PartMatrix_addHeat(x + 1, y, tempVal); // shift particle by 1 pixel to the right and add heat again (makes flame wider without using more particles)
PartMatrix_addHeat(x, y, tempVal, rows);
PartMatrix_addHeat(x + 1, y, tempVal, rows); // shift particle by 1 pixel to the right and add heat again (makes flame wider without using more particles)
}
// top right
y++;
if (x < cols && y < rows)
//if (x < cols && y < rows)
{
tempVal = (((uint32_t)dx * dy * (uint32_t)particles[i].ttl) >> PS_P_SURFACE); //
PartMatrix_addHeat(x, y, tempVal);
PartMatrix_addHeat(x + 1, y, tempVal); // shift particle by 1 pixel to the right and add heat again (makes flame wider without using more particles)
PartMatrix_addHeat(x, y, tempVal, rows);
PartMatrix_addHeat(x + 1, y, tempVal, rows); // shift particle by 1 pixel to the right and add heat again (makes flame wider without using more particles)
}
// top left
x--;
if (wrapX)
{ // wrap it to the other side if required
if (x < 0)
{ // left half of particle render is out of frame, wrap it
if (x < 0) // left half of particle render is out of frame, wrap it
x = cols - 1;
}

}
if (x < cols && x >= 0 && y < rows)
//if (x < cols && x >= 0 && y < rows)
{
tempVal = (((uint32_t)((PS_P_RADIUS)-dx) * dy * (uint32_t)particles[i].ttl) >> PS_P_SURFACE);
PartMatrix_addHeat(x, y, tempVal);
PartMatrix_addHeat(x + 1, y, tempVal); // shift particle by 1 pixel to the right and add heat again (makes flame wider without using more particles)
PartMatrix_addHeat(x, y, tempVal, rows);
PartMatrix_addHeat(x + 1, y, tempVal, rows); // shift particle by 1 pixel to the right and add heat again (makes flame wider without using more particles)
}
}
}

// adds 'heat' to red color channel, if it overflows, add it to next color channel
void PartMatrix_addHeat(uint8_t col, uint8_t row, uint16_t heat)
void PartMatrix_addHeat(uint8_t col, uint8_t row, uint32_t heat, uint32_t rows)
{

const uint32_t rows = strip.isMatrix ? SEGMENT.virtualHeight() : SEGMENT.virtualLength();
//const uint32_t rows = strip.isMatrix ? SEGMENT.virtualHeight() : SEGMENT.virtualLength();

CRGB currentcolor = SEGMENT.getPixelColorXY(col, rows - row - 1); // read current matrix color (flip y axis)
uint32_t newcolorvalue;
Expand All @@ -665,9 +674,9 @@ void PartMatrix_addHeat(uint8_t col, uint8_t row, uint16_t heat)
heat = heat << 3; // need to take a larger value to scale ttl value of particle to a good heat value that decays fast enough

// i=0 is normal red fire, i=1 is green fire, i=2 is blue fire
uint8_t i = (colormode & 0x07) >> 1;
uint32_t i = (colormode & 0x07) >> 1;
i = i % 3;
int8_t increment = (colormode & 0x01) + 1; // 0 (or 3) means only one single color for the flame, 1 is normal, 2 is alternate color modes
uint32_t increment = (colormode & 0x01) + 1; // 0 (or 3) means only one single color for the flame, 1 is normal, 2 is alternate color modes
if (currentcolor[i] < 255)
{
newcolorvalue = (uint16_t)currentcolor[i] + heat; // add heat, check if it overflows
Expand Down
8 changes: 5 additions & 3 deletions wled00/FXparticleSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,11 @@
#include <stdint.h>

//particle dimensions (subpixel division)
#define PS_P_RADIUS 64 //subpixel size, each pixel is divided by this for particle movement
#define PS_P_RADIUS 64 //subpixel size, each pixel is divided by this for particle movement, if this value is changed, also change the shift defines (next two lines)
#define PS_P_RADIUS_SHIFT 6 // shift for RADIUS
#define PS_P_SURFACE 12 // shift: 2^PS_P_SURFACE = (PS_P_RADIUS)^2
#define PS_P_HARDRADIUS 80 //hard surface radius of a particle, used for collision detection proximity
#define PS_P_SURFACE 12 //shift: 2^PS_P_SURFACE = (PS_P_RADIUS)^2



//todo: can add bitfields to add in more stuff, but accessing bitfields is slower than direct memory access!
Expand Down Expand Up @@ -93,7 +95,7 @@ void Particle_Gravity_update(PSparticle *part, bool wrapX, bool bounceX, bool bo
void ParticleSys_render(PSparticle *particles, uint32_t numParticles, bool wrapX, bool wrapY);
void FireParticle_update(PSparticle *part, bool wrapX = false);
void ParticleSys_renderParticleFire(PSparticle *particles, uint32_t numParticles, bool wrapX);
void PartMatrix_addHeat(uint8_t col, uint8_t row, uint16_t heat);
void PartMatrix_addHeat(uint8_t col, uint8_t row, uint32_t heat, uint32_t rows);
void detectCollisions(PSparticle *particles, uint32_t numparticles, uint8_t hardness);
void handleCollision(PSparticle *particle1, PSparticle *particle2, const uint8_t hardness);
void applyFriction(PSparticle *particle, uint8_t coefficient);

0 comments on commit 306a850

Please sign in to comment.