Skip to content

Commit

Permalink
added particle WrapUpdate function, added spray FX, some bugfixes
Browse files Browse the repository at this point in the history
  • Loading branch information
DedeHai committed Feb 17, 2024
1 parent 11a84c1 commit 241b080
Show file tree
Hide file tree
Showing 4 changed files with 182 additions and 9 deletions.
136 changes: 129 additions & 7 deletions wled00/FX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7889,8 +7889,13 @@ uint16_t mode_particlerotatingspray(void)

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

#ifdef ESP8266
const uint32_t numParticles = 150; // maximum number of particles
#else
const uint32_t numParticles = 500; // maximum number of particles
#endif

const uint32_t numParticles = 400;
const uint8_t numSprays = 8; // maximum number of sprays

PSparticle *particles;
Expand Down Expand Up @@ -7922,7 +7927,7 @@ uint16_t mode_particlerotatingspray(void)
spray[i].source.hue = random8();
spray[i].source.sat = 255; // set saturation
spray[i].source.x = (cols * PS_P_RADIUS) / 2; // center
spray[i].source.y = (cols * PS_P_RADIUS) / 2; // center
spray[i].source.y = (rows * PS_P_RADIUS) / 2; // center
spray[i].source.vx = 0;
spray[i].source.vy = 0;
spray[i].maxLife = 400;
Expand Down Expand Up @@ -8192,7 +8197,7 @@ static const char _data_FX_MODE_PARTICLEFIREWORKS[] PROGMEM = "Particle Firework
* by DedeHai (Damian Schneider)
*/

uint16_t mode_particlespray(void)
uint16_t mode_particlevolcano(void)
{

if (SEGLEN == 1)
Expand Down Expand Up @@ -8322,7 +8327,7 @@ uint16_t mode_particlespray(void)

return FRAMETIME;
}
static const char _data_FX_MODE_PARTICLESPRAY[] PROGMEM = "Particle Volcano@Moving Speed,Intensity,Particle Speed,Bouncyness,Nozzle Size,Color by Age,Bounce X,Collisions;;!;012;pal=35,sx=0,ix=160,c1=100,c2=160,c3=10,o1=1,o2=1,o3=1";
static const char _data_FX_MODE_PARTICLEVOLCANO[] PROGMEM = "Particle Volcano@Moving Speed,Intensity,Particle Speed,Bouncyness,Nozzle Size,Color by Age,Bounce X,Collisions;;!;012;pal=35,sx=0,ix=160,c1=100,c2=160,c3=10,o1=1,o2=1,o3=1";

// good default values for sliders: 100,200,190, 45
/*syntax for json configuration string:
Expand Down Expand Up @@ -9314,7 +9319,123 @@ uint16_t mode_particleattractor(void)
static const char _data_FX_MODE_PARTICLEATTRACTOR[] PROGMEM = "Particle Attractor@Center Mass,Particles,Emit Speed,Collision Strength,Friction,Bounce,Trails,Swallow;;!;012;pal=9,sx=100,ix=82,c1=190,c2=210,o1=0,o2=0,o3=0";


//TODO: animation idea: just one spray, sliders set x position, y position, speed, intensity and spray angle. ticks set wrap, bounce, gravity? evtl noch life time koppeln mit speed?


uint16_t mode_particlespray(void)
{

if (SEGLEN == 1)
return mode_static();

const uint16_t cols = strip.isMatrix ? SEGMENT.virtualWidth() : 1;
const uint16_t rows = strip.isMatrix ? SEGMENT.virtualHeight() : SEGMENT.virtualLength();
// particle system x dimension
const uint16_t PS_MAX_X = (cols * PS_P_RADIUS - 1);
const uint16_t PS_MAX_Y = (rows * PS_P_RADIUS - 1);

const uint32_t numParticles = 450;
const uint8_t numSprays = 1;
uint8_t percycle = numSprays; // maximum number of particles emitted per cycle

PSparticle *particles;
PSpointsource *spray;

// allocate memory and divide it into proper pointers, max is 32k for all segments.
uint32_t dataSize = sizeof(PSparticle) * numParticles;
dataSize += sizeof(PSpointsource) * (numSprays);
if (!SEGENV.allocateData(dataSize))
return mode_static(); // allocation failed; //allocation failed

// DEBUG_PRINT(F("particle datasize = "));
// DEBUG_PRINTLN(dataSize);

spray = reinterpret_cast<PSpointsource *>(SEGENV.data);
// calculate the end of the spray data and assign it as the data pointer for the particles:
particles = reinterpret_cast<PSparticle *>(spray + numSprays); // cast the data array into a particle pointer

uint32_t i = 0;
uint32_t j = 0;

if (SEGMENT.call == 0) // initialization
{
for (i = 0; i < numParticles; i++)
{
particles[i].ttl = 0;
}
for (i = 0; i < numSprays; i++)
{
spray[i].source.hue = random8();
spray[i].source.sat = 255; // set full saturation
spray[i].source.x = (cols * PS_P_RADIUS) / (numSprays + 1) * (i + 1);
spray[i].source.y = 5; // just above the lower edge, if zero, particles already 'bounce' at start and loose speed.
spray[i].source.vx = 0;
spray[i].maxLife = 300; // lifetime in frames
spray[i].minLife = 20;
spray[i].source.collide = true; // seeded particles will collide
spray[i].vx = 0; // emitting speed
spray[i].vy = 0; // emitting speed
spray[i].var = 10;
}
// SEGMENT.palette = 35; //fire palette
}

// change source emitting color from time to time
if (SEGMENT.call % (11 - (SEGMENT.intensity / 25)) == 0) // every nth frame, cycle color and emit particles
{
for (i = 0; i < numSprays; i++)
{
spray[i].source.hue++; // = random8(); //change hue of spray source
//spray[i].var = SEGMENT.custom3; // emiting variation = nozzle size (custom 3 goes from 0-32)
spray[i].source.x = map(SEGMENT.custom1,0,255,0,PS_MAX_X);
spray[i].source.y = map(SEGMENT.custom2,0,255,0,PS_MAX_Y);
}

i = 0;
j = 0;
for (i = 0; i < numParticles; i++)
{
if (particles[i].ttl == 0) // find a dead particle
{
// spray[j].source.hue = random8(); //set random color for each particle (using palette)
Emitter_Angle_emit(&spray[j], &particles[i], SEGMENT.custom3<<3, SEGMENT.speed>>2);
j = (j + 1) % numSprays;
if (percycle-- == 0)
{
break; // quit loop if all particles of this round emitted
}
}
}
}

uint8_t hardness = 200;

if (SEGMENT.check3) // collisions enabled
detectCollisions(particles, numParticles, hardness);


for (i = 0; i < numParticles; i++)
{
//particles[i].hue = min((uint16_t)220, particles[i].ttl);
if (SEGMENT.check1) //use gravity
Particle_Gravity_update(&particles[i], SEGMENT.check2, SEGMENT.check2 == 0, true, hardness);
else //bounce particles
{
if(SEGMENT.check2) //wrap x
Particle_Wrap_update(&particles[i], true, false);
else //bounce
Particle_Bounce_update(&particles[i], hardness);
}
}

SEGMENT.fill(BLACK); // clear the matrix

// render the particles
ParticleSys_render(particles, numParticles, SEGMENT.check2, false);

return FRAMETIME;
}
static const char _data_FX_MODE_PARTICLESPRAY[] PROGMEM = "Particle Spray@Particle Speed,Intensity,X Position,Y Position,Angle,Gravity,WrapX/Bounce,Collisions;;!;012;pal=0,sx=180,ix=200,c1=220,c2=30,c3=12,o1=1,o2=0,o3=1";
// TODO: animation idea: just one spray, sliders set x position, y position, speed, intensity and spray angle. ticks set wrap/bounce, gravity? evtl noch life time koppeln mit speed?

#endif // WLED_DISABLE_2D

Expand Down Expand Up @@ -9558,7 +9679,7 @@ void WS2812FX::setupEffectData() {
addEffect(FX_MODE_2DAKEMI, &mode_2DAkemi, _data_FX_MODE_2DAKEMI); // audio


addEffect(FX_MODE_PARTICLESPRAY, &mode_particlespray, _data_FX_MODE_PARTICLESPRAY);
addEffect(FX_MODE_PARTICLEVOLCANO, &mode_particlevolcano, _data_FX_MODE_PARTICLEVOLCANO);
addEffect(FX_MODE_PARTICLEFIRE, &mode_particlefire, _data_FX_MODE_PARTICLEFIRE);
addEffect(FX_MODE_PARTICLEFIREWORKS, &mode_particlefireworks, _data_FX_MODE_PARTICLEFIREWORKS);
addEffect(FX_MODE_PARTICLEROTATINGSPRAY, &mode_particlerotatingspray, _data_FX_MODE_PARTICLEROTATINGSPRAY);
Expand All @@ -9569,7 +9690,8 @@ void WS2812FX::setupEffectData() {
//experimental
addEffect(FX_MODE_PARTICLEWATERFALL, &mode_particlewaterfall, _data_FX_MODE_PARTICLEWATERFALL);
addEffect(FX_MODE_PARTICLEIMPACT, &mode_particleimpact, _data_FX_MODE_PARTICLEIMPACT);
addEffect(FX_MODE_PARTICLEATTRACTOR, &mode_particleattractor, _data_FX_MODE_PARTICLEATTRACTOR);
addEffect(FX_MODE_PARTICLEATTRACTOR, &mode_particleattractor, _data_FX_MODE_PARTICLEATTRACTOR);
addEffect(FX_MODE_PARTICLESPRAY, &mode_particlespray, _data_FX_MODE_PARTICLESPRAY);

#endif // WLED_DISABLE_2D

Expand Down
5 changes: 3 additions & 2 deletions wled00/FX.h
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,7 @@
#define FX_MODE_WAVESINS 184
#define FX_MODE_ROCKTAVES 185
#define FX_MODE_2DAKEMI 186
#define FX_MODE_PARTICLESPRAY 187
#define FX_MODE_PARTICLEVOLCANO 187
#define FX_MODE_PARTICLEFIRE 188
#define FX_MODE_PARTICLEFIREWORKS 189
#define FX_MODE_PARTICLEROTATINGSPRAY 190
Expand All @@ -328,7 +328,8 @@
#define FX_MODE_PARTICLEATTRACTOR 194
#define FX_MODE_PARTICLEIMPACT 195
#define FX_MODE_PARTICLEWATERFALL 196
#define MODE_COUNT 197
#define FX_MODE_PARTICLESPRAY 197
#define MODE_COUNT 198

typedef enum mapping1D2D {
M12_Pixels = 0,
Expand Down
49 changes: 49 additions & 0 deletions wled00/FXparticleSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,55 @@ void Particle_Bounce_update(PSparticle *part, const uint8_t hardness) // bounces
}
}

void Particle_Wrap_update(PSparticle *part, bool wrapX, bool wrapY) // particle moves, decays and dies (age or out of matrix), if wrap is set, pixels leaving the matrix reappear on other side
{
// Matrix dimension
const uint16_t cols = strip.isMatrix ? SEGMENT.virtualWidth() : 1;
const uint16_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);

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

// apply velocity
int32_t newX, newY;
newX = part->x + (int16_t)part->vx;
newY = part->y + (int16_t)part->vy;

part->outofbounds = 0; // reset out of bounds (in case particle was created outside the matrix and is now moving into view)
// x direction, handle wraparound
if (wrapX)
{
newX = newX % (PS_MAX_X + 1);
if (newX < 0)
newX = PS_MAX_X - newX;
}
else if ((part->x <= 0) || (part->x >= PS_MAX_X)) // check if particle is out of bounds
{
part->outofbounds = 1;
}
part->x = newX; // set new position

if (wrapY)
{
newY = newY % (PS_MAX_Y + 1);
if (newY < 0)
newY = PS_MAX_Y - newY;
}
else if ((part->y <= 0) || (part->y >= PS_MAX_Y)) // check if particle is out of bounds
{
part->outofbounds = 1;
}
part->y = newY; // set new position
}

}

void Particle_Gravity_update(PSparticle *part, bool wrapX, bool bounceX, bool bounceY, const uint8_t hardness) // particle moves, decays and dies (age or out of matrix), if wrapX is set, pixels leaving in x direction reappear on other side, hardness is surface hardness for bouncing (127 means 50% speed lost each bounce)
{

Expand Down
1 change: 1 addition & 0 deletions wled00/FXparticleSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ void Emitter_Angle_emit(PSpointsource *emitter, PSparticle *part, uint8_t angle,
void Particle_attractor(PSparticle *particle, PSparticle *attractor, uint8_t *counter, uint8_t strength, bool swallow);
void Particle_Move_update(PSparticle *part);
void Particle_Bounce_update(PSparticle *part, const uint8_t hardness);
void Particle_Wrap_update(PSparticle *part, bool wrapX, bool wrapY);
void Particle_Gravity_update(PSparticle *part, bool wrapX, bool bounceX, bool bounceY, const uint8_t hardness);
void ParticleSys_render(PSparticle *particles, uint32_t numParticles, bool wrapX, bool wrapY);
void FireParticle_update(PSparticle *part, bool wrapX, bool WrapY);
Expand Down

0 comments on commit 241b080

Please sign in to comment.