Skip to content

Commit 5e2dca3

Browse files
committed
Fixed Speed limit
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)
1 parent 0a251ae commit 5e2dca3

File tree

3 files changed

+79
-82
lines changed

3 files changed

+79
-82
lines changed

wled00/FX.cpp

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7910,8 +7910,8 @@ uint16_t mode_particlevortex(void)
79107910
for (i = 0; i < numSprays; i++)
79117911
{
79127912
PartSys->sources[i].source.sat = 255; // set saturation
7913-
PartSys->sources[i].source.x = (PartSys->maxX - PS_P_HALFRADIUS + 1) >> 1; // center
7914-
PartSys->sources[i].source.y = (PartSys->maxY - PS_P_HALFRADIUS + 1) >> 1; // center
7913+
PartSys->sources[i].source.x = (PartSys->maxX + 1) >> 1; // center
7914+
PartSys->sources[i].source.y = (PartSys->maxY + 1) >> 1; // center
79157915
PartSys->sources[i].source.vx = 0;
79167916
PartSys->sources[i].source.vy = 0;
79177917
PartSys->sources[i].maxLife = 900;
@@ -8294,7 +8294,7 @@ uint16_t mode_particlefire(void)
82948294
{
82958295
if (!initParticleSystem(PartSys))
82968296
return mode_static(); // allocation failed; //allocation failed
8297-
Serial.println("fireinit done");
8297+
// Serial.println("fireinit done");
82988298
SEGMENT.aux0 = rand(); // aux0 is wind position (index) in the perlin noise
82998299
// initialize the flame sprays
83008300
numFlames = PartSys->numSources;
@@ -8379,7 +8379,7 @@ uint16_t mode_particlefire(void)
83798379
{
83808380
//int32_t curl = ((int16_t)inoise8(PartSys->particles[i].x , PartSys->particles[i].y >> 1, SEGMENT.step<<2 ) - 127);
83818381
//int32_t curl = ((int16_t)inoise8(PartSys->particles[i].x, PartSys->particles[i].y , SEGMENT.step << 4) - 127);
8382-
int32_t curl = ((int16_t)inoise8(PartSys->particles[i].x, PartSys->particles[i].y , SEGMENT.step << 4) - 127); //-> this is good!
8382+
int32_t curl = ((int32_t)inoise8(PartSys->particles[i].x, PartSys->particles[i].y , SEGMENT.step << 4) - 127); //-> this is good!
83838383

83848384
//int32_t curl = ((int16_t)inoise8(PartSys->particles[i].x>>1, SEGMENT.step<<5) - 127);
83858385
// curl = ((curl * PartSys->particles[i].y) / PartSys->maxY); //'curl' stronger at the top
@@ -8424,13 +8424,21 @@ uint16_t mode_particlefire(void)
84248424
}
84258425
Serial.println("B");
84268426
}*/
8427-
8427+
if(SEGMENT.check1) //low fps is enabled
8428+
{
8429+
static uint32_t lastcall; //!!! put this in heap
8430+
while(millis()-lastcall < 25)
8431+
{
8432+
yield();
8433+
}
8434+
lastcall = millis();
8435+
}
84288436

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

84318439
return FRAMETIME;
84328440
}
8433-
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";
8441+
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";
84348442

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

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

86248632
if (PartSys == NULL)
86258633
{
8626-
Serial.println("ERROR: paticle system not found, nullpointer");
8634+
DEBUG_PRINT(F("ERROR: FX PartSys nullpointer"));
86278635
return mode_static(); // something went wrong, no data!
86288636
}
86298637

@@ -8819,7 +8827,7 @@ uint16_t mode_particleimpact(void)
88198827

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

@@ -8961,7 +8969,7 @@ uint16_t mode_particleattractor(void)
89618969

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

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

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

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

91729180
if (fftResult[bin] > threshold)
@@ -9187,10 +9195,10 @@ uint16_t mode_particleGEQ(void)
91879195
if (PartSys->particles[i].ttl == 0) // find a dead particle
91889196
{
91899197
//set particle properties
9190-
PartSys->particles[i].ttl = map(SEGMENT.intensity, 0,255, emitspeed>>1, emitspeed + random16(emitspeed)) ; // set particle alive, particle lifespan is in number of frames
9198+
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
91919199
PartSys->particles[i].x = xposition + random16(binwidth) - (binwidth>>1); //position randomly, deviating half a bin width
9192-
PartSys->particles[i].y = 0; //start at the bottom
9193-
PartSys->particles[i].vx = random16(SEGMENT.custom1>>1)-(SEGMENT.custom1>>2) ; //x-speed variation
9200+
PartSys->particles[i].y = PS_P_RADIUS; //tart at the bottom (PS_P_RADIUS is minimum position a particle is fully in frame)
9201+
PartSys->particles[i].vx = random16(SEGMENT.custom1>>1)-(SEGMENT.custom1>>2) ; //x-speed variation: +/- custom1/4
91949202
PartSys->particles[i].vy = emitspeed;
91959203
PartSys->particles[i].hue = (bin<<4) + random16(17) - 8; // color from palette according to bin
91969204
PartSys->particles[i].sat = 255; // set saturation

wled00/FXparticleSystem.cpp

Lines changed: 50 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747

4848
ParticleSystem::ParticleSystem(uint16_t width, uint16_t height, uint16_t numberofparticles, uint16_t numberofsources)
4949
{
50-
Serial.println("PS Constructor");
50+
//Serial.println("PS Constructor");
5151
numSources = numberofsources;
5252
numParticles = numberofparticles; // set number of particles in the array
5353
usedParticles = numberofparticles; // use all particles by default
@@ -69,7 +69,7 @@ ParticleSystem::ParticleSystem(uint16_t width, uint16_t height, uint16_t numbero
6969
Serial.println(particles[i].y);
7070
}
7171
}*/
72-
Serial.println("PS Constructor done");
72+
//Serial.println("PS Constructor done");
7373
}
7474

7575
//update function applies gravity, moves the particles, handles collisions and renders the particles
@@ -285,9 +285,12 @@ void ParticleSystem::particleMoveUpdate(PSparticle &part, PSsettings &options)
285285
{
286286
if (newY < PS_P_RADIUS) // bounce at bottom
287287
{
288-
part.vy = -part.vy; // invert speed
289-
part.vy = (part.vy * wallHardness) / 255; // reduce speed as energy is lost on non-hard surface
290-
newY = PS_P_RADIUS;
288+
if(part.vy < -10)
289+
{
290+
part.vy = -part.vy; // invert speed
291+
part.vy = ((int32_t)part.vy * wallHardness) / 255; // reduce speed as energy is lost on non-hard surface
292+
newY = PS_P_RADIUS;
293+
}
291294
}
292295
else
293296
{
@@ -299,7 +302,7 @@ void ParticleSystem::particleMoveUpdate(PSparticle &part, PSsettings &options)
299302
else
300303
{
301304
part.vy = -part.vy; // invert speed
302-
part.vy = (part.vy * wallHardness) / 255; // reduce speed as energy is lost on non-hard surface
305+
part.vy = ((int32_t)part.vy * wallHardness) / 255; // reduce speed as energy is lost on non-hard surface
303306
newY = maxY - PS_P_RADIUS;
304307
}
305308
}
@@ -341,8 +344,8 @@ void ParticleSystem::applyForce(PSparticle *part, uint32_t numparticles, int8_t
341344
uint8_t ycounter = (*counter) >> 4; // upper four bits
342345

343346
// velocity increase
344-
int32_t dvx = calcForce_dV(xforce, &xcounter);
345-
int32_t dvy = calcForce_dV(yforce, &ycounter);
347+
int32_t dvx = calcForce_dv(xforce, &xcounter);
348+
int32_t dvy = calcForce_dv(yforce, &ycounter);
346349

347350
// save counter values back
348351
*counter |= xcounter & 0x0F; // write lower four bits, make sure not to write more than 4 bits
@@ -352,30 +355,18 @@ void ParticleSystem::applyForce(PSparticle *part, uint32_t numparticles, int8_t
352355
int32_t i = 0;
353356
if (dvx != 0)
354357
{
355-
if (numparticles == 1) // for single particle, skip the for loop to make it faster
358+
for (i = 0; i < numparticles; i++)
356359
{
357-
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
358-
}
359-
else
360-
{
361-
for (i = 0; i < numparticles; i++)
362-
{
363-
// 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
364-
part[i].vx = part[i].vx + dvx > PS_P_MAXSPEED ? PS_P_MAXSPEED : part[i].vx + dvx;
365-
}
360+
// 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
361+
part[i].vx = limitSpeed((int32_t)particles[i].vx + dvx);
366362
}
367363
}
368364
if (dvy != 0)
369365
{
370-
if (numparticles == 1) // for single particle, skip the for loop to make it faster
371-
part[0].vy = part[0].vy + dvy > PS_P_MAXSPEED ? PS_P_MAXSPEED : part[0].vy + dvy;
372-
else
366+
for (i = 0; i < numparticles; i++)
373367
{
374-
for (i = 0; i < numparticles; i++)
375-
{
376-
part[i].vy = part[i].vy + dvy > PS_P_MAXSPEED ? PS_P_MAXSPEED : part[i].vy + dvy;
377-
}
378-
}
368+
part[i].vy = limitSpeed((int32_t)particles[i].vy + dvy);
369+
}
379370
}
380371
}
381372

@@ -386,8 +377,8 @@ void ParticleSystem::applyForce(PSparticle *part, uint32_t numparticles, int8_t
386377
for (uint i = 0; i < numparticles; i++)
387378
{
388379
// 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
389-
part[i].vx = part[i].vx + xforce > PS_P_MAXSPEED ? PS_P_MAXSPEED : part[i].vx + xforce;
390-
part[i].vy = part[i].vy + yforce > PS_P_MAXSPEED ? PS_P_MAXSPEED : part[i].vy + yforce;
380+
part[i].vx = limitSpeed((int32_t)part[i].vx + (int32_t)xforce);
381+
part[i].vy = limitSpeed((int32_t)part[i].vy + (int32_t)yforce);
391382
}
392383
}
393384

@@ -415,27 +406,17 @@ void ParticleSystem::applyAngleForce(PSparticle *part, uint32_t numparticles, ui
415406
// apply gravity to a group of particles
416407
// faster than apply force since direction is always down and counter is fixed for all particles
417408
// caller needs to provide a 8bit counter that holds its value between calls
418-
// 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
419-
void ParticleSystem::applyGravity(PSparticle *part, uint32_t numarticles, uint8_t force, uint8_t *counter)
409+
// 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)
410+
// positive force means down
411+
void ParticleSystem::applyGravity(PSparticle *part, uint32_t numarticles, int8_t force, uint8_t *counter)
420412
{
421-
int32_t dv; // velocity increase
422-
if (force > 15)
423-
dv = (force >> 4); // apply the 4 MSBs
424-
else
425-
dv = 1;
426-
427-
*counter += force;
428-
429-
if (*counter > 15)
430-
{
431-
*counter -= 16;
432-
// apply force to all used particles
433-
for (uint32_t i = 0; i < numarticles; i++)
434-
{
435-
// note: not checking if particle is dead is faster as most are usually alive and if few are alive, rendering is fast anyways
436-
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
437-
}
413+
int32_t dv = calcForce_dv(force, counter);
414+
for (uint32_t i = 0; i < numarticles; i++)
415+
{
416+
// note: not checking if particle is dead is faster as most are usually alive and if few are alive, rendering is fast anyways
417+
part[i].vy = limitSpeed((int32_t)particles[i].vy - dv);
438418
}
419+
439420
}
440421

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

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

456438
if (gforcecounter + gforce > 15) //counter is updated in global update when applying gravity
457-
{
458-
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
439+
{
440+
part->vy = limitSpeed((int32_t)part->vy - dv);
459441
}
460442
}
461443

@@ -1093,11 +1075,11 @@ int32_t ParticleSystem::wraparound(int32_t p, int32_t maxvalue)
10931075

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

1100+
//limit speed to prevent overflows
1101+
int32_t ParticleSystem::limitSpeed(int32_t speed)
1102+
{
1103+
return speed > PS_P_MAXSPEED ? PS_P_MAXSPEED : (speed < -PS_P_MAXSPEED ? -PS_P_MAXSPEED : speed);
1104+
}
1105+
11181106
// allocate memory for the 2D array in one contiguous block and set values to zero
11191107
CRGB **ParticleSystem::allocate2Dbuffer(uint32_t cols, uint32_t rows)
11201108
{
@@ -1207,33 +1195,33 @@ bool allocateParticleSystemMemory(uint16_t numparticles, uint16_t numsources, ui
12071195
requiredmemory += sizeof(PSparticle) * numparticles;
12081196
requiredmemory += sizeof(PSsource) * numsources;
12091197
requiredmemory += additionalbytes;
1210-
Serial.print("allocating: ");
1211-
Serial.print(requiredmemory);
1212-
Serial.println("Bytes");
1213-
Serial.print("allocating for segment at");
1214-
Serial.println((uintptr_t)SEGMENT.data);
1198+
//Serial.print("allocating: ");
1199+
//Serial.print(requiredmemory);
1200+
//Serial.println("Bytes");
1201+
//Serial.print("allocating for segment at");
1202+
//Serial.println((uintptr_t)SEGMENT.data);
12151203
return(SEGMENT.allocateData(requiredmemory));
12161204
}
12171205

12181206
// initialize Particle System, allocate additional bytes if needed (pointer to those bytes can be read from particle system class: PSdataEnd)
12191207
bool initParticleSystem(ParticleSystem *&PartSys, uint16_t additionalbytes)
12201208
{
1221-
Serial.println("PS init function");
1209+
//Serial.println("PS init function");
12221210
uint32_t numparticles = calculateNumberOfParticles();
12231211
uint32_t numsources = calculateNumberOfSources();
12241212
if (!allocateParticleSystemMemory(numparticles, numsources, additionalbytes))
12251213
{
12261214
DEBUG_PRINT(F("PS init failed: memory depleted"));
12271215
return false;
12281216
}
1229-
Serial.print("segment.data ptr");
1230-
Serial.println((uintptr_t)(SEGMENT.data));
1217+
//Serial.print("segment.data ptr");
1218+
//Serial.println((uintptr_t)(SEGMENT.data));
12311219
uint16_t cols = strip.isMatrix ? SEGMENT.virtualWidth() : 1;
12321220
uint16_t rows = strip.isMatrix ? SEGMENT.virtualHeight() : SEGMENT.virtualLength();
1233-
Serial.println("calling constructor");
1221+
//Serial.println("calling constructor");
12341222
PartSys = new (SEGMENT.data) ParticleSystem(cols, rows, numparticles, numsources); // particle system constructor TODO: why does VS studio thinkt this is bad?
1235-
Serial.print("PS pointer at ");
1236-
Serial.println((uintptr_t)PartSys);
1223+
//Serial.print("PS pointer at ");
1224+
//Serial.println((uintptr_t)PartSys);
12371225
return true;
12381226
}
12391227

wled00/FXparticleSystem.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
#define PS_P_SURFACE 12 // shift: 2^PS_P_SURFACE = (PS_P_RADIUS)^2
4545
#define PS_P_HARDRADIUS 80 //hard surface radius of a particle, used for collision detection proximity
4646
#define PS_P_MINSURFACEHARDNESS 128 //minimum hardness used in collision impulse calculation, below this hardness, particles become sticky
47-
#define PS_P_MAXSPEED 200 //maximum speed a particle can have
47+
#define PS_P_MAXSPEED 120 //maximum speed a particle can have (vx/vy is int8)
4848

4949
//struct for a single particle
5050
typedef struct {
@@ -105,7 +105,7 @@ class ParticleSystem
105105
void particleMoveUpdate(PSparticle &part, PSsettings &options);
106106

107107
//particle physics
108-
void applyGravity(PSparticle *part, uint32_t numarticles, uint8_t force, uint8_t *counter);
108+
void applyGravity(PSparticle *part, uint32_t numarticles, int8_t force, uint8_t *counter);
109109
void applyGravity(PSparticle *part, uint32_t numarticles, uint8_t *counter); //use global gforce
110110
void applyGravity(PSparticle *part); //use global system settings
111111
void applyForce(PSparticle *part, uint32_t numparticles, int8_t xforce, int8_t yforce, uint8_t *counter);
@@ -153,15 +153,16 @@ class ParticleSystem
153153
//utility functions
154154
void updatePSpointers(); // update the data pointers to current segment data space
155155
int32_t wraparound(int32_t w, int32_t maxvalue);
156-
int32_t calcForce_dV(int8_t force, uint8_t *counter);
156+
int32_t calcForce_dv(int8_t force, uint8_t *counter);
157+
int32_t limitSpeed(int32_t speed);
157158
CRGB **allocate2Dbuffer(uint32_t cols, uint32_t rows);
158159

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

0 commit comments

Comments
 (0)