From 7740c7dc62fadf6f8b39d7fb3e5b421938e27d48 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Mon, 7 Oct 2024 13:14:50 +0200 Subject: [PATCH] drip effect rework * made effect time-defendant (instead of framerate dependent) * made effect a bit slower * fixed physics * made dropping more random (avoids skipped rows) * drop traces look better now * made bounce more realistic * prevent off-screen drawing --- wled00/FX.cpp | 67 ++++++++++++++++++++++++++++++++++++--------------- wled00/wled.h | 2 +- 2 files changed, 49 insertions(+), 20 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index a93c2843..d345ff79 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -3713,6 +3713,17 @@ uint16_t mode_exploding_fireworks(void) static const char _data_FX_MODE_EXPLODING_FIREWORKS[] PROGMEM = "Fireworks 1D@Gravity,Firing side;!,!;!;12;pal=11,ix=128"; + +//SparkDrop type is used for drip +typedef struct __attribute__ ((packed)) SparkDrop { + float pos; + float boost; // speed "kick" when dropping + float vel; + uint32_t aux; // aux variable (RGBW color) + uint16_t col; + uint8_t colIndex; +} sparkdrop; + /* * Drip Effect * ported of: https://www.youtube.com/watch?v=sru2fXh4r7k @@ -3723,20 +3734,24 @@ uint16_t mode_drip(void) //allocate segment data uint16_t strips = SEGMENT.nrOfVStrips(); const int maxNumDrops = 4; - uint16_t dataSize = sizeof(spark) * maxNumDrops; + uint16_t dataSize = sizeof(sparkdrop) * maxNumDrops; if (!SEGENV.allocateData(dataSize * strips)) return mode_static(); //allocation failed - Spark* drops = reinterpret_cast(SEGENV.data); + SparkDrop* drops = reinterpret_cast(SEGENV.data); - if (SEGENV.call == 0) SEGMENT.fill(BLACK); // WLEDMM clear LEDs at startup + if (SEGENV.call == 0) { + SEGMENT.fill(BLACK); // WLEDMM clear LEDs at startup + SEGENV.step = strip.now; // initial time + } if (!SEGMENT.check2) SEGMENT.fill(SEGCOLOR(1)); struct virtualStrip { - static void runStrip(uint16_t stripNr, Spark* drops) { + static void runStrip(uint16_t stripNr, SparkDrop* drops, float deltaTime) { // WLEDMM added deltaTime uint8_t numDrops = 1 + (SEGMENT.intensity >> 6); // 255>>6 = 3 - float gravity = -0.0005f - (float(SEGMENT.speed)/35000.0f); //increased gravity (50000 to 35000) - gravity *= min(max(1, SEGLEN-1), 255); //WLEDMM speed limit 255 + float theSpeed = (SEGMENT.speed * SEGMENT.speed) / 255.0f; // WLEDMM + float gravity = -0.0002f - theSpeed/42000.0f; //gravity // WLEDMM adjusted + gravity *= min(max(1, SEGLEN-1), 255); // WLEDMM speed limit 255 const int sourcedrop = 12; for (int j=0;j255) drops[j].col=255; - SEGMENT.setPixelColor(indexToVStrip(uint16_t(drops[j].pos), stripNr), color_blend(BLACK,dropColor,drops[j].col)); + int intPos = max(0.0f, roundf(drops[j].pos)); // WLEDMM round it first + SEGMENT.setPixelColor(int(indexToVStrip(intPos, stripNr)), color_blend(BLACK,dropColor,drops[j].col)); - drops[j].col += map(SEGMENT.custom1, 0, 255, 1, 6); // swelling + unsigned swell = map(SEGMENT.custom1, 0, 255, 1, 6); // swelling + drops[j].col += swell; + if (drops[j].boost < 4.0f) drops[j].boost += 0.012f * float(swell); // increase mass when swelling uint32_t fallrate = (drops[j].col * (1 + SEGMENT.custom1 * SEGMENT.custom1)) / 192; // WLEDMM specific if (random16() <= (fallrate / 10)) { // random drop => 1% ... 20% probalibity drops[j].colIndex=2; //fall drops[j].col=255; + drops[j].vel = gravity * 2.0f * drops[j].boost; // WLEDMM initial kick } } if (drops[j].colIndex > 1) { // falling if (drops[j].pos > 0.01f) { // fall until end of segment - drops[j].pos += drops[j].vel; + drops[j].pos += drops[j].vel * deltaTime; if (drops[j].pos < 0) drops[j].pos = 0; - drops[j].vel += gravity; // gravity is negative + drops[j].vel += gravity * deltaTime; // gravity is negative - for (int i=1;i<7-drops[j].colIndex;i++) { // some minor math so we don't expand bouncing droplets - int intPos = roundf(drops[j].pos) +i; // WLEDMM round it first + int maxLen = 8 + SEGMENT.speed/64; + for (int i=1; i < maxLen-drops[j].colIndex; i++) { // some minor math so we don't expand bouncing droplets + int intPos = roundf(drops[j].pos + float(i)); // WLEDMM round it first + if ((intPos >= SEGLEN) || (intPos < 0)) break; // WLEDMM skip off-screen pixels uint16_t pos = constrain(intPos, 0, SEGLEN-1); //this is BAD, returns a pos >= SEGLEN occasionally // WLEDMM bad cast to uint16_t removed SEGMENT.setPixelColor(indexToVStrip(pos, stripNr), color_blend(BLACK,dropColor,drops[j].col/i)); //spread pixel with fade while falling } if (drops[j].colIndex > 2) { // during bounce, some water is on the floor - SEGMENT.setPixelColor(indexToVStrip(0, stripNr), color_blend(dropColor,BLACK, (2 * drops[j].col)/3)); // WLEDMM reduced brightness + SEGMENT.addPixelColor(indexToVStrip(0, stripNr), color_blend(dropColor,BLACK, drops[j].col*4)); // WLEDMM darker } } else { // we hit bottom if (drops[j].colIndex > 2) { // already hit once, so back to forming @@ -3785,8 +3807,11 @@ uint16_t mode_drip(void) } else { if (drops[j].colIndex==2) { // init bounce - drops[j].vel = -drops[j].vel/4;// reverse velocity with damping - drops[j].pos += drops[j].vel; + // reverse velocity with damping + if (SEGLEN > 16) drops[j].vel = -drops[j].vel/3.5f; + else drops[j].vel = -drops[j].vel/4.5f; + // do bounce + drops[j].pos += drops[j].vel * deltaTime * drops[j].boost*0.5f; } drops[j].col = sourcedrop*2; drops[j].colIndex = 5; // bouncing @@ -3797,12 +3822,16 @@ uint16_t mode_drip(void) } }; + // WLEDMM calculate time passed + uint32_t millisPassed = min(max(1UL, strip.now - SEGENV.step), 180UL); // constrain between 1 and 180 + SEGENV.step = strip.now; + float deltaTime = float(millisPassed) / 20.0f; // base speed 50 FPS for (int stripNr=0; stripNr