Skip to content

Commit

Permalink
Fixed Speed limit
Browse files Browse the repository at this point in the history
limiting speed was incorrect, leading to overflows. fixed this.
also fixed bugs in GEQ, removed some debug stuff, added FPS limit to fire (just en experiment)
  • Loading branch information
DedeHai committed Apr 6, 2024
1 parent 0a251ae commit 5e2dca3
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 82 deletions.
40 changes: 24 additions & 16 deletions wled00/FX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7910,8 +7910,8 @@ uint16_t mode_particlevortex(void)
for (i = 0; i < numSprays; i++)
{
PartSys->sources[i].source.sat = 255; // set saturation
PartSys->sources[i].source.x = (PartSys->maxX - PS_P_HALFRADIUS + 1) >> 1; // center
PartSys->sources[i].source.y = (PartSys->maxY - PS_P_HALFRADIUS + 1) >> 1; // center
PartSys->sources[i].source.x = (PartSys->maxX + 1) >> 1; // center
PartSys->sources[i].source.y = (PartSys->maxY + 1) >> 1; // center
PartSys->sources[i].source.vx = 0;
PartSys->sources[i].source.vy = 0;
PartSys->sources[i].maxLife = 900;
Expand Down Expand Up @@ -8294,7 +8294,7 @@ uint16_t mode_particlefire(void)
{
if (!initParticleSystem(PartSys))
return mode_static(); // allocation failed; //allocation failed
Serial.println("fireinit done");
// Serial.println("fireinit done");
SEGMENT.aux0 = rand(); // aux0 is wind position (index) in the perlin noise
// initialize the flame sprays
numFlames = PartSys->numSources;
Expand Down Expand Up @@ -8379,7 +8379,7 @@ uint16_t mode_particlefire(void)
{
//int32_t curl = ((int16_t)inoise8(PartSys->particles[i].x , PartSys->particles[i].y >> 1, SEGMENT.step<<2 ) - 127);
//int32_t curl = ((int16_t)inoise8(PartSys->particles[i].x, PartSys->particles[i].y , SEGMENT.step << 4) - 127);
int32_t curl = ((int16_t)inoise8(PartSys->particles[i].x, PartSys->particles[i].y , SEGMENT.step << 4) - 127); //-> this is good!
int32_t curl = ((int32_t)inoise8(PartSys->particles[i].x, PartSys->particles[i].y , SEGMENT.step << 4) - 127); //-> this is good!

//int32_t curl = ((int16_t)inoise8(PartSys->particles[i].x>>1, SEGMENT.step<<5) - 127);
// curl = ((curl * PartSys->particles[i].y) / PartSys->maxY); //'curl' stronger at the top
Expand Down Expand Up @@ -8424,13 +8424,21 @@ uint16_t mode_particlefire(void)
}
Serial.println("B");
}*/

if(SEGMENT.check1) //low fps is enabled
{
static uint32_t lastcall; //!!! put this in heap
while(millis()-lastcall < 25)
{
yield();
}
lastcall = millis();
}

PartSys->updateFire(SEGMENT.intensity); // update and render the fire

return FRAMETIME;
}
static const char _data_FX_MODE_PARTICLEFIRE[] PROGMEM = "PS Fire@Speed,Intensity,Base Heat,Wind,Spread,,Cylinder,Turbulence;;!;2;pal=35,sx=130,ix=120,c1=110,c2=128,c3=22,o1=0";
static const char _data_FX_MODE_PARTICLEFIRE[] PROGMEM = "PS Fire@Speed,Intensity,Base Heat,Wind,Spread,FPS Limit,Cylinder,Turbulence;;!;2;pal=35,sx=130,ix=120,c1=110,c2=128,c3=22,o1=0";

/*
PS Ballpit: particles falling down, user can enable these three options: X-wraparound, side bounce, ground bounce
Expand Down Expand Up @@ -8551,7 +8559,7 @@ uint16_t mode_particlewaterfall(void)

if (PartSys == NULL)
{
Serial.println("ERROR: paticle system not found, nullpointer");
DEBUG_PRINT(F("ERROR: FX PartSys nullpointer"));
return mode_static(); // something went wrong, no data! (TODO: ask how to handle this so it always works)
}
// Particle System settings
Expand Down Expand Up @@ -8623,7 +8631,7 @@ uint16_t mode_particlebox(void)

if (PartSys == NULL)
{
Serial.println("ERROR: paticle system not found, nullpointer");
DEBUG_PRINT(F("ERROR: FX PartSys nullpointer"));
return mode_static(); // something went wrong, no data!
}

Expand Down Expand Up @@ -8819,7 +8827,7 @@ uint16_t mode_particleimpact(void)

if (PartSys == NULL)
{
Serial.println("ERROR: paticle system not found, nullpointer");
DEBUG_PRINT(F("ERROR: FX PartSys nullpointer"));
return mode_static(); // something went wrong, no data! (TODO: ask how to handle this so it always works)
}

Expand Down Expand Up @@ -8961,7 +8969,7 @@ uint16_t mode_particleattractor(void)

if (PartSys == NULL)
{
Serial.println("ERROR: paticle system not found, nullpointer");
DEBUG_PRINT(F("ERROR: FX PartSys nullpointer"));
return mode_static(); // something went wrong, no data! (TODO: ask how to handle this so it always works)
}
// Particle System settings
Expand Down Expand Up @@ -9130,7 +9138,7 @@ uint16_t mode_particleGEQ(void)

if (PartSys == NULL)
{
Serial.println("ERROR: paticle system not found, nullpointer");
DEBUG_PRINT(F("ERROR: FX PartSys nullpointer"));
return mode_static(); // something went wrong, no data! (TODO: ask how to handle this so it always works)
}

Expand All @@ -9142,7 +9150,7 @@ uint16_t mode_particleGEQ(void)
PartSys->setBounceY(SEGMENT.check3);
PartSys->enableParticleCollisions(false);
PartSys->setWallHardness(SEGMENT.custom2);
PartSys->enableGravity(true, SEGMENT.custom3<<1); //set gravity strength
PartSys->enableGravity(true, SEGMENT.custom3<<2); //set gravity strength

um_data_t *um_data;
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE))
Expand All @@ -9166,7 +9174,7 @@ uint16_t mode_particleGEQ(void)
for (bin = 0; bin < 16; bin++)
{
uint32_t xposition = binwidth*bin + (binwidth>>1); // emit position according to frequency band
uint8_t emitspeed = 5 + (((uint32_t)fftResult[bin]*(uint32_t)SEGMENT.speed)>>9); // emit speed according to loudness of band
uint8_t emitspeed = ((uint32_t)fftResult[bin] * (uint32_t)SEGMENT.speed) >> 9; // emit speed according to loudness of band (127 max!)
emitparticles = 0;

if (fftResult[bin] > threshold)
Expand All @@ -9187,10 +9195,10 @@ uint16_t mode_particleGEQ(void)
if (PartSys->particles[i].ttl == 0) // find a dead particle
{
//set particle properties
PartSys->particles[i].ttl = map(SEGMENT.intensity, 0,255, emitspeed>>1, emitspeed + random16(emitspeed)) ; // set particle alive, particle lifespan is in number of frames
PartSys->particles[i].ttl = 20 + map(SEGMENT.intensity, 0,255, emitspeed>>1, emitspeed + random16(emitspeed)) ; // set particle alive, particle lifespan is in number of frames
PartSys->particles[i].x = xposition + random16(binwidth) - (binwidth>>1); //position randomly, deviating half a bin width
PartSys->particles[i].y = 0; //start at the bottom
PartSys->particles[i].vx = random16(SEGMENT.custom1>>1)-(SEGMENT.custom1>>2) ; //x-speed variation
PartSys->particles[i].y = PS_P_RADIUS; //tart at the bottom (PS_P_RADIUS is minimum position a particle is fully in frame)
PartSys->particles[i].vx = random16(SEGMENT.custom1>>1)-(SEGMENT.custom1>>2) ; //x-speed variation: +/- custom1/4
PartSys->particles[i].vy = emitspeed;
PartSys->particles[i].hue = (bin<<4) + random16(17) - 8; // color from palette according to bin
PartSys->particles[i].sat = 255; // set saturation
Expand Down
112 changes: 50 additions & 62 deletions wled00/FXparticleSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@

ParticleSystem::ParticleSystem(uint16_t width, uint16_t height, uint16_t numberofparticles, uint16_t numberofsources)
{
Serial.println("PS Constructor");
//Serial.println("PS Constructor");
numSources = numberofsources;
numParticles = numberofparticles; // set number of particles in the array
usedParticles = numberofparticles; // use all particles by default
Expand All @@ -69,7 +69,7 @@ ParticleSystem::ParticleSystem(uint16_t width, uint16_t height, uint16_t numbero
Serial.println(particles[i].y);
}
}*/
Serial.println("PS Constructor done");
//Serial.println("PS Constructor done");
}

//update function applies gravity, moves the particles, handles collisions and renders the particles
Expand Down Expand Up @@ -285,9 +285,12 @@ void ParticleSystem::particleMoveUpdate(PSparticle &part, PSsettings &options)
{
if (newY < PS_P_RADIUS) // bounce at bottom
{
part.vy = -part.vy; // invert speed
part.vy = (part.vy * wallHardness) / 255; // reduce speed as energy is lost on non-hard surface
newY = PS_P_RADIUS;
if(part.vy < -10)
{
part.vy = -part.vy; // invert speed
part.vy = ((int32_t)part.vy * wallHardness) / 255; // reduce speed as energy is lost on non-hard surface
newY = PS_P_RADIUS;
}
}
else
{
Expand All @@ -299,7 +302,7 @@ void ParticleSystem::particleMoveUpdate(PSparticle &part, PSsettings &options)
else
{
part.vy = -part.vy; // invert speed
part.vy = (part.vy * wallHardness) / 255; // reduce speed as energy is lost on non-hard surface
part.vy = ((int32_t)part.vy * wallHardness) / 255; // reduce speed as energy is lost on non-hard surface
newY = maxY - PS_P_RADIUS;
}
}
Expand Down Expand Up @@ -341,8 +344,8 @@ void ParticleSystem::applyForce(PSparticle *part, uint32_t numparticles, int8_t
uint8_t ycounter = (*counter) >> 4; // upper four bits

// velocity increase
int32_t dvx = calcForce_dV(xforce, &xcounter);
int32_t dvy = calcForce_dV(yforce, &ycounter);
int32_t dvx = calcForce_dv(xforce, &xcounter);
int32_t dvy = calcForce_dv(yforce, &ycounter);

// save counter values back
*counter |= xcounter & 0x0F; // write lower four bits, make sure not to write more than 4 bits
Expand All @@ -352,30 +355,18 @@ void ParticleSystem::applyForce(PSparticle *part, uint32_t numparticles, int8_t
int32_t i = 0;
if (dvx != 0)
{
if (numparticles == 1) // for single particle, skip the for loop to make it faster
for (i = 0; i < numparticles; i++)
{
part[0].vx = part[0].vx + dvx > PS_P_MAXSPEED ? PS_P_MAXSPEED : part[0].vx + dvx; // limit the force, this is faster than min or if/else
}
else
{
for (i = 0; i < numparticles; i++)
{
// note: not checking if particle is dead is faster as most are usually alive and if few are alive, rendering is faster so no speed penalty
part[i].vx = part[i].vx + dvx > PS_P_MAXSPEED ? PS_P_MAXSPEED : part[i].vx + dvx;
}
// note: not checking if particle is dead is faster as most are usually alive and if few are alive, rendering is faster so no speed penalty
part[i].vx = limitSpeed((int32_t)particles[i].vx + dvx);
}
}
if (dvy != 0)
{
if (numparticles == 1) // for single particle, skip the for loop to make it faster
part[0].vy = part[0].vy + dvy > PS_P_MAXSPEED ? PS_P_MAXSPEED : part[0].vy + dvy;
else
for (i = 0; i < numparticles; i++)
{
for (i = 0; i < numparticles; i++)
{
part[i].vy = part[i].vy + dvy > PS_P_MAXSPEED ? PS_P_MAXSPEED : part[i].vy + dvy;
}
}
part[i].vy = limitSpeed((int32_t)particles[i].vy + dvy);
}
}
}

Expand All @@ -386,8 +377,8 @@ void ParticleSystem::applyForce(PSparticle *part, uint32_t numparticles, int8_t
for (uint i = 0; i < numparticles; i++)
{
// note: not checking if particle is dead is faster as most are usually alive and if few are alive, rendering is faster so no speed penalty
part[i].vx = part[i].vx + xforce > PS_P_MAXSPEED ? PS_P_MAXSPEED : part[i].vx + xforce;
part[i].vy = part[i].vy + yforce > PS_P_MAXSPEED ? PS_P_MAXSPEED : part[i].vy + yforce;
part[i].vx = limitSpeed((int32_t)part[i].vx + (int32_t)xforce);
part[i].vy = limitSpeed((int32_t)part[i].vy + (int32_t)yforce);
}
}

Expand Down Expand Up @@ -415,27 +406,17 @@ void ParticleSystem::applyAngleForce(PSparticle *part, uint32_t numparticles, ui
// apply gravity to a group of particles
// faster than apply force since direction is always down and counter is fixed for all particles
// caller needs to provide a 8bit counter that holds its value between calls
// force is in 4.4 fixed point notation so force=16 means apply v+1 each frame default of 8 is every other frame (gives good results), force above 127 are VERY strong
void ParticleSystem::applyGravity(PSparticle *part, uint32_t numarticles, uint8_t force, uint8_t *counter)
// force is in 3.4 fixed point notation so force=16 means apply v+1 each frame default of 8 is every other frame (gives good results)
// positive force means down
void ParticleSystem::applyGravity(PSparticle *part, uint32_t numarticles, int8_t force, uint8_t *counter)
{
int32_t dv; // velocity increase
if (force > 15)
dv = (force >> 4); // apply the 4 MSBs
else
dv = 1;

*counter += force;

if (*counter > 15)
{
*counter -= 16;
// apply force to all used particles
for (uint32_t i = 0; i < numarticles; i++)
{
// note: not checking if particle is dead is faster as most are usually alive and if few are alive, rendering is fast anyways
particles[i].vy = particles[i].vy - dv > PS_P_MAXSPEED ? PS_P_MAXSPEED : particles[i].vy - dv; // limit the force, this is faster than min or if/else
}
int32_t dv = calcForce_dv(force, counter);
for (uint32_t i = 0; i < numarticles; i++)
{
// note: not checking if particle is dead is faster as most are usually alive and if few are alive, rendering is fast anyways
part[i].vy = limitSpeed((int32_t)particles[i].vy - dv);
}

}

//apply gravity using PS global gforce
Expand All @@ -445,6 +426,7 @@ void ParticleSystem::applyGravity(PSparticle *part, uint32_t numarticles, uint8_
}

//apply gravity to single particle using system settings (use this for sources)
//function does not increment gravity counter, if gravity setting is disabled, this cannot be used
void ParticleSystem::applyGravity(PSparticle *part)
{
int32_t dv; // velocity increase
Expand All @@ -454,8 +436,8 @@ void ParticleSystem::applyGravity(PSparticle *part)
dv = 1;

if (gforcecounter + gforce > 15) //counter is updated in global update when applying gravity
{
part->vy = part->vy - dv > PS_P_MAXSPEED ? PS_P_MAXSPEED : part->vy - dv; // limit the force, this is faster than min or if/else
{
part->vy = limitSpeed((int32_t)part->vy - dv);
}
}

Expand Down Expand Up @@ -1093,11 +1075,11 @@ int32_t ParticleSystem::wraparound(int32_t p, int32_t maxvalue)

//calculate the delta speed (dV) value and update the counter for force calculation (is used several times, function saves on codesize)
//force is in 3.4 fixedpoint notation, +/-127
int32_t ParticleSystem::calcForce_dV(int8_t force, uint8_t* counter)
int32_t ParticleSystem::calcForce_dv(int8_t force, uint8_t* counter)
{
// for small forces, need to use a delay counter
int32_t force_abs = abs(force); // absolute value (faster than lots of if's only 7 instructions)
int32_t dv = 0;
int32_t dv;
// for small forces, need to use a delay counter, apply force only if it overflows
if (force_abs < 16)
{
Expand All @@ -1115,6 +1097,12 @@ int32_t ParticleSystem::calcForce_dV(int8_t force, uint8_t* counter)
return dv;
}

//limit speed to prevent overflows
int32_t ParticleSystem::limitSpeed(int32_t speed)
{
return speed > PS_P_MAXSPEED ? PS_P_MAXSPEED : (speed < -PS_P_MAXSPEED ? -PS_P_MAXSPEED : speed);
}

// allocate memory for the 2D array in one contiguous block and set values to zero
CRGB **ParticleSystem::allocate2Dbuffer(uint32_t cols, uint32_t rows)
{
Expand Down Expand Up @@ -1207,33 +1195,33 @@ bool allocateParticleSystemMemory(uint16_t numparticles, uint16_t numsources, ui
requiredmemory += sizeof(PSparticle) * numparticles;
requiredmemory += sizeof(PSsource) * numsources;
requiredmemory += additionalbytes;
Serial.print("allocating: ");
Serial.print(requiredmemory);
Serial.println("Bytes");
Serial.print("allocating for segment at");
Serial.println((uintptr_t)SEGMENT.data);
//Serial.print("allocating: ");
//Serial.print(requiredmemory);
//Serial.println("Bytes");
//Serial.print("allocating for segment at");
//Serial.println((uintptr_t)SEGMENT.data);
return(SEGMENT.allocateData(requiredmemory));
}

// initialize Particle System, allocate additional bytes if needed (pointer to those bytes can be read from particle system class: PSdataEnd)
bool initParticleSystem(ParticleSystem *&PartSys, uint16_t additionalbytes)
{
Serial.println("PS init function");
//Serial.println("PS init function");
uint32_t numparticles = calculateNumberOfParticles();
uint32_t numsources = calculateNumberOfSources();
if (!allocateParticleSystemMemory(numparticles, numsources, additionalbytes))
{
DEBUG_PRINT(F("PS init failed: memory depleted"));
return false;
}
Serial.print("segment.data ptr");
Serial.println((uintptr_t)(SEGMENT.data));
//Serial.print("segment.data ptr");
//Serial.println((uintptr_t)(SEGMENT.data));
uint16_t cols = strip.isMatrix ? SEGMENT.virtualWidth() : 1;
uint16_t rows = strip.isMatrix ? SEGMENT.virtualHeight() : SEGMENT.virtualLength();
Serial.println("calling constructor");
//Serial.println("calling constructor");
PartSys = new (SEGMENT.data) ParticleSystem(cols, rows, numparticles, numsources); // particle system constructor TODO: why does VS studio thinkt this is bad?
Serial.print("PS pointer at ");
Serial.println((uintptr_t)PartSys);
//Serial.print("PS pointer at ");
//Serial.println((uintptr_t)PartSys);
return true;
}

Expand Down
9 changes: 5 additions & 4 deletions wled00/FXparticleSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
#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_MINSURFACEHARDNESS 128 //minimum hardness used in collision impulse calculation, below this hardness, particles become sticky
#define PS_P_MAXSPEED 200 //maximum speed a particle can have
#define PS_P_MAXSPEED 120 //maximum speed a particle can have (vx/vy is int8)

//struct for a single particle
typedef struct {
Expand Down Expand Up @@ -105,7 +105,7 @@ class ParticleSystem
void particleMoveUpdate(PSparticle &part, PSsettings &options);

//particle physics
void applyGravity(PSparticle *part, uint32_t numarticles, uint8_t force, uint8_t *counter);
void applyGravity(PSparticle *part, uint32_t numarticles, int8_t force, uint8_t *counter);
void applyGravity(PSparticle *part, uint32_t numarticles, uint8_t *counter); //use global gforce
void applyGravity(PSparticle *part); //use global system settings
void applyForce(PSparticle *part, uint32_t numparticles, int8_t xforce, int8_t yforce, uint8_t *counter);
Expand Down Expand Up @@ -153,15 +153,16 @@ class ParticleSystem
//utility functions
void updatePSpointers(); // update the data pointers to current segment data space
int32_t wraparound(int32_t w, int32_t maxvalue);
int32_t calcForce_dV(int8_t force, uint8_t *counter);
int32_t calcForce_dv(int8_t force, uint8_t *counter);
int32_t limitSpeed(int32_t speed);
CRGB **allocate2Dbuffer(uint32_t cols, uint32_t rows);

// note: variables that are accessed often are 32bit for speed
uint32_t emitIndex; // index to count through particles to emit so searching for dead pixels is faster
int32_t collisionHardness;
int32_t wallHardness;
uint8_t gforcecounter; //counter for global gravity
uint8_t gforce; //gravity strength, default is 8
int8_t gforce; //gravity strength, default is 8 (negative is allowed)
uint8_t collisioncounter; //counter to handle collisions
};

Expand Down

0 comments on commit 5e2dca3

Please sign in to comment.