2D Fireworks (enhanced 1D version).
Transposed Fire 2012 on matrix.
This commit is contained in:
parent
8c31904838
commit
e7c9b5a4f0
105
wled00/FX.cpp
105
wled00/FX.cpp
@ -1927,46 +1927,47 @@ static const char *_data_FX_MODE_PALETTE PROGMEM = "Palette@!,;1,2,3;!";
|
|||||||
|
|
||||||
uint16_t WS2812FX::mode_fire_2012()
|
uint16_t WS2812FX::mode_fire_2012()
|
||||||
{
|
{
|
||||||
|
uint16_t height = SEGMENT.virtualWidth(); // same as SEGLEN on 1D
|
||||||
|
uint16_t width = SEGMENT.virtualHeight(); // they are actually transposed for the effect purpose to support 1D as well as 2D
|
||||||
uint32_t it = now >> 5; //div 32
|
uint32_t it = now >> 5; //div 32
|
||||||
uint16_t nFlames = SEGMENT.virtualHeight();
|
uint16_t q = width>>2; // a quarter of segment height
|
||||||
uint16_t q = nFlames>>2; // a quarter of segment height
|
|
||||||
|
|
||||||
if (!SEGENV.allocateData(SEGLEN*nFlames)) return mode_static(); //allocation failed
|
if (!SEGENV.allocateData(height*width)) return mode_static(); //allocation failed
|
||||||
|
|
||||||
byte* heat = SEGENV.data;
|
byte* heat = SEGENV.data;
|
||||||
|
|
||||||
if (it != SEGENV.step) {
|
if (it != SEGENV.step) {
|
||||||
SEGENV.step = it;
|
SEGENV.step = it;
|
||||||
uint8_t ignition = max(3,SEGLEN/10); // ignition area: 10% of segment length or minimum 3 pixels
|
uint8_t ignition = max(3,height/10); // ignition area: 10% of segment length or minimum 3 pixels
|
||||||
|
|
||||||
for (uint16_t f = 0; f < nFlames; f++) {
|
for (uint16_t f = 0; f < width; f++) {
|
||||||
// Step 1. Cool down every cell a little
|
// Step 1. Cool down every cell a little
|
||||||
for (uint16_t i = 0; i < SEGLEN; i++) {
|
for (uint16_t i = 0; i < height; i++) {
|
||||||
uint8_t cool = (((20 + SEGMENT.speed /3) * 10) / SEGLEN);
|
uint8_t cool = (((20 + SEGMENT.speed /3) * 10) / height);
|
||||||
// 2D enhancement: cool sides of the flame a bit more
|
// 2D enhancement: cool sides of the flame a bit more
|
||||||
if (nFlames>5) {
|
if (width>5) {
|
||||||
if (f < q) qadd8(cool, (uint16_t)((cool * (q-f))/nFlames)); // cool segment sides a bit more
|
if (f < q) cool = qadd8(cool, (uint16_t)((cool * (q-f))/width)); // cool segment sides a bit more
|
||||||
if (f > 3*q) qadd8(cool, (uint16_t)((cool * (nFlames-f))/nFlames)); // cool segment sides a bit more
|
if (f > 3*q) cool = qadd8(cool, (uint16_t)((cool * (width-f))/width)); // cool segment sides a bit more
|
||||||
}
|
}
|
||||||
uint8_t temp = qsub8(heat[i+SEGLEN*f], random8(0, cool + 2));
|
uint8_t temp = qsub8(heat[i+height*f], random8(0, cool + 2));
|
||||||
heat[i+SEGLEN*f] = (temp==0 && i<ignition) ? 16 : temp; // prevent ignition area from becoming black
|
heat[i+height*f] = (temp==0 && i<ignition) ? 16 : temp; // prevent ignition area from becoming black
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 2. Heat from each cell drifts 'up' and diffuses a little
|
// Step 2. Heat from each cell drifts 'up' and diffuses a little
|
||||||
for (uint16_t k= SEGLEN -1; k > 1; k--) {
|
for (uint16_t k= height -1; k > 1; k--) {
|
||||||
heat[k+SEGLEN*f] = (heat[k+SEGLEN*f - 1] + (heat[k+SEGLEN*f - 2]<<1) ) / 3; // heat[k-2] multiplied by 2
|
heat[k+height*f] = (heat[k+height*f - 1] + (heat[k+height*f - 2]<<1) ) / 3; // heat[k-2] multiplied by 2
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 3. Randomly ignite new 'sparks' of heat near the bottom
|
// Step 3. Randomly ignite new 'sparks' of heat near the bottom
|
||||||
if (random8() <= SEGMENT.intensity) {
|
if (random8() <= SEGMENT.intensity) {
|
||||||
uint8_t y = random8(ignition);
|
uint8_t y = random8(ignition);
|
||||||
if (y < SEGLEN) heat[y+SEGLEN*f] = qadd8(heat[y+SEGLEN*f], random8(160,255));
|
if (y < height) heat[y+height*f] = qadd8(heat[y+height*f], random8(160,255));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 4. Map from heat cells to LED colors
|
// Step 4. Map from heat cells to LED colors
|
||||||
for (uint16_t j = 0; j < SEGLEN; j++) {
|
for (uint16_t j = 0; j < height; j++) {
|
||||||
CRGB color = ColorFromPalette(currentPalette, MIN(heat[j+SEGLEN*f],240), 255, LINEARBLEND);
|
CRGB color = ColorFromPalette(currentPalette, MIN(heat[j+height*f],240), 255, LINEARBLEND);
|
||||||
if (isMatrix) setPixelColorXY(j, f, color);
|
if (isMatrix) setPixelColorXY(f, j, color);
|
||||||
else setPixelColor(j, color);
|
else setPixelColor(j, color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2881,8 +2882,8 @@ static const char *_data_FX_MODE_GLITTER PROGMEM = "Glitter";
|
|||||||
//each needs 11 bytes
|
//each needs 11 bytes
|
||||||
//Spark type is used for popcorn, 1D fireworks, and drip
|
//Spark type is used for popcorn, 1D fireworks, and drip
|
||||||
typedef struct Spark {
|
typedef struct Spark {
|
||||||
float pos;
|
float pos, posX;
|
||||||
float vel;
|
float vel, velX;
|
||||||
uint16_t col;
|
uint16_t col;
|
||||||
uint8_t colIndex;
|
uint8_t colIndex;
|
||||||
} spark;
|
} spark;
|
||||||
@ -3174,9 +3175,13 @@ static const char *_data_FX_MODE_STARBURST PROGMEM = "Fireworks Starburst";
|
|||||||
/*
|
/*
|
||||||
* Exploding fireworks effect
|
* Exploding fireworks effect
|
||||||
* adapted from: http://www.anirama.com/1000leds/1d-fireworks/
|
* adapted from: http://www.anirama.com/1000leds/1d-fireworks/
|
||||||
|
* adapted for 2D WLED by blazoncek (Blaz Kristan)
|
||||||
*/
|
*/
|
||||||
uint16_t WS2812FX::mode_exploding_fireworks(void)
|
uint16_t WS2812FX::mode_exploding_fireworks(void)
|
||||||
{
|
{
|
||||||
|
uint16_t height = SEGMENT.virtualWidth(); // same as SEGLEN on 1D
|
||||||
|
uint16_t width = SEGMENT.virtualHeight(); // they are actually transposed for the effect purpose to support 1D as well as 2D
|
||||||
|
|
||||||
//allocate segment data
|
//allocate segment data
|
||||||
uint16_t maxData = FAIR_DATA_PER_SEG; //ESP8266: 256 ESP32: 640
|
uint16_t maxData = FAIR_DATA_PER_SEG; //ESP8266: 256 ESP32: 640
|
||||||
uint8_t segs = getActiveSegmentsNum();
|
uint8_t segs = getActiveSegmentsNum();
|
||||||
@ -3184,7 +3189,7 @@ uint16_t WS2812FX::mode_exploding_fireworks(void)
|
|||||||
if (segs <= (MAX_NUM_SEGMENTS /4)) maxData *= 2; //ESP8266: 1024 if <= 4 segs ESP32: 2560 if <= 8 segs
|
if (segs <= (MAX_NUM_SEGMENTS /4)) maxData *= 2; //ESP8266: 1024 if <= 4 segs ESP32: 2560 if <= 8 segs
|
||||||
int maxSparks = maxData / sizeof(spark); //ESP8266: max. 21/42/85 sparks/seg, ESP32: max. 53/106/213 sparks/seg
|
int maxSparks = maxData / sizeof(spark); //ESP8266: max. 21/42/85 sparks/seg, ESP32: max. 53/106/213 sparks/seg
|
||||||
|
|
||||||
uint16_t numSparks = min(2 + (SEGLEN >> 1), maxSparks);
|
uint16_t numSparks = min(2 + (height >> 1), maxSparks);
|
||||||
uint16_t dataSize = sizeof(spark) * numSparks;
|
uint16_t dataSize = sizeof(spark) * numSparks;
|
||||||
if (!SEGENV.allocateData(dataSize + sizeof(float))) return mode_static(); //allocation failed
|
if (!SEGENV.allocateData(dataSize + sizeof(float))) return mode_static(); //allocation failed
|
||||||
float *dying_gravity = reinterpret_cast<float*>(SEGENV.data + dataSize);
|
float *dying_gravity = reinterpret_cast<float*>(SEGENV.data + dataSize);
|
||||||
@ -3195,38 +3200,38 @@ uint16_t WS2812FX::mode_exploding_fireworks(void)
|
|||||||
SEGENV.aux1 = dataSize;
|
SEGENV.aux1 = dataSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
fill(BLACK);
|
//fill(BLACK);
|
||||||
|
fade_out(252);
|
||||||
bool actuallyReverse = SEGMENT.getOption(SEG_OPTION_REVERSED);
|
|
||||||
//have fireworks start in either direction based on intensity
|
|
||||||
SEGMENT.setOption(SEG_OPTION_REVERSED, SEGENV.step);
|
|
||||||
|
|
||||||
Spark* sparks = reinterpret_cast<Spark*>(SEGENV.data);
|
Spark* sparks = reinterpret_cast<Spark*>(SEGENV.data);
|
||||||
Spark* flare = sparks; //first spark is flare data
|
Spark* flare = sparks; //first spark is flare data
|
||||||
|
|
||||||
float gravity = -0.0004 - (SEGMENT.speed/800000.0); // m/s/s
|
float gravity = -0.0004 - (SEGMENT.speed/800000.0); // m/s/s
|
||||||
gravity *= SEGLEN;
|
gravity *= height;
|
||||||
|
|
||||||
if (SEGENV.aux0 < 2) { //FLARE
|
if (SEGENV.aux0 < 2) { //FLARE
|
||||||
if (SEGENV.aux0 == 0) { //init flare
|
if (SEGENV.aux0 == 0) { //init flare
|
||||||
flare->pos = 0;
|
flare->pos = 0;
|
||||||
|
flare->posX = isMatrix ? random16(width) : (SEGMENT.intensity > random8()); // will enable random firing side on 1D
|
||||||
uint16_t peakHeight = 75 + random8(180); //0-255
|
uint16_t peakHeight = 75 + random8(180); //0-255
|
||||||
peakHeight = (peakHeight * (SEGLEN -1)) >> 8;
|
peakHeight = (peakHeight * (height -1)) >> 8;
|
||||||
flare->vel = sqrt(-2.0 * gravity * peakHeight);
|
flare->vel = sqrt(-2.0 * gravity * peakHeight);
|
||||||
|
flare->velX = isMatrix ? (random8(6)-3)/23 : 0; // no X velocity on 1D
|
||||||
flare->col = 255; //brightness
|
flare->col = 255; //brightness
|
||||||
|
|
||||||
SEGENV.aux0 = 1;
|
SEGENV.aux0 = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// launch
|
// launch
|
||||||
if (flare->vel > 12 * gravity) {
|
if (flare->vel > 12 * gravity) {
|
||||||
// flare
|
// flare
|
||||||
setPixelColor(int(flare->pos),flare->col,flare->col,flare->col);
|
if (isMatrix) setPixelColorXY(int(flare->posX), int(flare->pos), flare->col, flare->col, flare->col);
|
||||||
|
else setPixelColor(int(flare->posX) ? height-int(flare->pos)-1 : int(flare->pos), flare->col, flare->col, flare->col);
|
||||||
flare->pos += flare->vel;
|
flare->pos += flare->vel;
|
||||||
flare->pos = constrain(flare->pos, 0, SEGLEN-1);
|
flare->posX += flare->velX;
|
||||||
flare->vel += gravity;
|
flare->pos = constrain(flare->pos, 0, height-1);
|
||||||
flare->col -= 2;
|
flare->posX = constrain(flare->posX, 0, width-isMatrix);
|
||||||
|
flare->vel += gravity;
|
||||||
|
flare->col -= 2;
|
||||||
} else {
|
} else {
|
||||||
SEGENV.aux0 = 2; // ready to explode
|
SEGENV.aux0 = 2; // ready to explode
|
||||||
}
|
}
|
||||||
@ -3243,13 +3248,16 @@ uint16_t WS2812FX::mode_exploding_fireworks(void)
|
|||||||
// initialize sparks
|
// initialize sparks
|
||||||
if (SEGENV.aux0 == 2) {
|
if (SEGENV.aux0 == 2) {
|
||||||
for (int i = 1; i < nSparks; i++) {
|
for (int i = 1; i < nSparks; i++) {
|
||||||
sparks[i].pos = flare->pos;
|
sparks[i].pos = flare->pos;
|
||||||
sparks[i].vel = (float(random16(0, 20000)) / 10000.0) - 0.9; // from -0.9 to 1.1
|
sparks[i].posX = flare->posX;
|
||||||
sparks[i].col = 345;//abs(sparks[i].vel * 750.0); // set colors before scaling velocity to keep them bright
|
sparks[i].vel = (float(random16(0, 20000)) / 10000.0) - 0.9; // from -0.9 to 1.1
|
||||||
|
sparks[i].velX = isMatrix ? (float(random16(0, 20000)) / 10000.0) - 0.9 : 0; // from -0.9 to 1.1
|
||||||
|
sparks[i].col = 345;//abs(sparks[i].vel * 750.0); // set colors before scaling velocity to keep them bright
|
||||||
//sparks[i].col = constrain(sparks[i].col, 0, 345);
|
//sparks[i].col = constrain(sparks[i].col, 0, 345);
|
||||||
sparks[i].colIndex = random8();
|
sparks[i].colIndex = random8();
|
||||||
sparks[i].vel *= flare->pos/SEGLEN; // proportional to height
|
sparks[i].vel *= flare->pos/height; // proportional to height
|
||||||
sparks[i].vel *= -gravity *50;
|
sparks[i].velX *= isMatrix ? flare->posX/width : 0; // proportional to width
|
||||||
|
sparks[i].vel *= -gravity *50;
|
||||||
}
|
}
|
||||||
//sparks[1].col = 345; // this will be our known spark
|
//sparks[1].col = 345; // this will be our known spark
|
||||||
*dying_gravity = gravity/2;
|
*dying_gravity = gravity/2;
|
||||||
@ -3258,11 +3266,14 @@ uint16_t WS2812FX::mode_exploding_fireworks(void)
|
|||||||
|
|
||||||
if (sparks[1].col > 4) {//&& sparks[1].pos > 0) { // as long as our known spark is lit, work with all the sparks
|
if (sparks[1].col > 4) {//&& sparks[1].pos > 0) { // as long as our known spark is lit, work with all the sparks
|
||||||
for (int i = 1; i < nSparks; i++) {
|
for (int i = 1; i < nSparks; i++) {
|
||||||
sparks[i].pos += sparks[i].vel;
|
sparks[i].pos += sparks[i].vel;
|
||||||
sparks[i].vel += *dying_gravity;
|
sparks[i].posX += sparks[i].velX;
|
||||||
|
sparks[i].vel += *dying_gravity;
|
||||||
|
sparks[i].velX += isMatrix ? *dying_gravity : 0;
|
||||||
if (sparks[i].col > 3) sparks[i].col -= 4;
|
if (sparks[i].col > 3) sparks[i].col -= 4;
|
||||||
|
|
||||||
if (sparks[i].pos > 0 && sparks[i].pos < SEGLEN) {
|
if (sparks[i].pos > 0 && sparks[i].pos < height) {
|
||||||
|
if (isMatrix && !(sparks[i].posX > 0 && sparks[i].posX < width)) continue;
|
||||||
uint16_t prog = sparks[i].col;
|
uint16_t prog = sparks[i].col;
|
||||||
uint32_t spColor = (SEGMENT.palette) ? color_wheel(sparks[i].colIndex) : SEGCOLOR(0);
|
uint32_t spColor = (SEGMENT.palette) ? color_wheel(sparks[i].colIndex) : SEGCOLOR(0);
|
||||||
CRGB c = CRGB::Black; //HeatColor(sparks[i].col);
|
CRGB c = CRGB::Black; //HeatColor(sparks[i].col);
|
||||||
@ -3274,10 +3285,11 @@ uint16_t WS2812FX::mode_exploding_fireworks(void)
|
|||||||
c.g = qsub8(c.g, cooling);
|
c.g = qsub8(c.g, cooling);
|
||||||
c.b = qsub8(c.b, cooling * 2);
|
c.b = qsub8(c.b, cooling * 2);
|
||||||
}
|
}
|
||||||
setPixelColor(int(sparks[i].pos), c.red, c.green, c.blue);
|
if (isMatrix) setPixelColorXY(int(sparks[i].posX), int(sparks[i].pos), c.red, c.green, c.blue);
|
||||||
|
else setPixelColor(int(sparks[i].posX) ? height-int(sparks[i].pos)-1 : int(sparks[i].pos), c.red, c.green, c.blue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*dying_gravity *= .99; // as sparks burn out they fall slower
|
*dying_gravity *= .8; // as sparks burn out they fall slower
|
||||||
} else {
|
} else {
|
||||||
SEGENV.aux0 = 6 + random8(10); //wait for this many frames
|
SEGENV.aux0 = 6 + random8(10); //wait for this many frames
|
||||||
}
|
}
|
||||||
@ -3285,12 +3297,9 @@ uint16_t WS2812FX::mode_exploding_fireworks(void)
|
|||||||
SEGENV.aux0--;
|
SEGENV.aux0--;
|
||||||
if (SEGENV.aux0 < 4) {
|
if (SEGENV.aux0 < 4) {
|
||||||
SEGENV.aux0 = 0; //back to flare
|
SEGENV.aux0 = 0; //back to flare
|
||||||
SEGENV.step = actuallyReverse ^ (SEGMENT.intensity > random8()); //decide firing side
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SEGMENT.setOption(SEG_OPTION_REVERSED, actuallyReverse);
|
|
||||||
|
|
||||||
return FRAMETIME;
|
return FRAMETIME;
|
||||||
}
|
}
|
||||||
#undef MAX_SPARKS
|
#undef MAX_SPARKS
|
||||||
|
Loading…
Reference in New Issue
Block a user