diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 3d580bb9a5..bcd1ab8e55 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -4875,6 +4875,78 @@ uint16_t mode_FlowStripe(void) { } // mode_FlowStripe() static const char _data_FX_MODE_FLOWSTRIPE[] PROGMEM = "Flow Stripe@Hue speed,Effect speed;;!;pal=11"; +/* + Shimmer effect: moves a gradient with optional modulators across the strip at a given interval, up to 60 seconds + It can be used as an overlay to other effects or standalone + by DedeHai (Damian Schneider), based on idea from @Charming-Lime (#4905) +*/ +uint16_t mode_shimmer() { + if(!SEGENV.allocateData(sizeof(uint32_t))) { return mode_static(); } + uint32_t* lastTime = reinterpret_cast(SEGENV.data); + + uint32_t radius = (SEGMENT.custom1 * SEGLEN >> 7) + 1; // [1, 2*SEGLEN+1] pixels + uint32_t traversalDistance = (SEGLEN + 2 * radius) << 8; // total subpixels to cross, 1 pixel = 256 subpixels + uint32_t traversalTime = 200 + (255 - SEGMENT.speed) * 80; // [200, 20600] ms + uint32_t speed = ((traversalDistance << 5) / traversalTime); // subpixels/512ms + int32_t position = static_cast(SEGENV.step); // current position in subpixels + uint16_t inputstate = (uint16_t(SEGMENT.intensity) << 8) | uint16_t(SEGMENT.custom1); // current user input state + + // init + if (SEGENV.call == 0 || inputstate != SEGENV.aux1) { + position = -(radius << 8); + SEGENV.aux0 = 0; // aux0 is pause timer + *lastTime = strip.now; + SEGENV.aux1 = inputstate; // save user input state + } + + if(SEGMENT.speed) { + uint32_t deltaTime = (strip.now - *lastTime) & 0x7F; // clamp to 127ms to avoid overflows. note: speed*deltaTime can still overflow for segments > ~10k pixels + *lastTime = strip.now; + + if (SEGENV.aux0 > 0) { + SEGENV.aux0 = (SEGENV.aux0 > deltaTime) ? SEGENV.aux0 - deltaTime : 0; + } else { + // calculate movement step and update position + int32_t step = 1 + ((speed * deltaTime) >> 5); // subpixels moved this frame. note >>5 as speed is in subpixels/512ms + position += step; + int endposition = (SEGLEN + radius) << 8; + if (position > endposition) { + SEGENV.aux0 = SEGMENT.intensity * 236; // [0, 60180] ms pause + if(SEGMENT.check3) SEGENV.aux0 = hw_random(SEGENV.aux0 + 1000); // randomise interval, +1 second to affect low intensity values + position = -(radius << 8); // reset to start position (out of frame) + } + SEGENV.step = (uint32_t)position; // save back + } + + if (SEGMENT.check2) + position = (SEGLEN << 8) - position; // invert position (and direction) + } else { + position = (SEGLEN << 7); // at speed=0, make it static in the center (this enables to use modulators only) + } + + for (int i = 0; i < SEGLEN; i++) { + uint32_t dist = abs(position - (i << 8)); + if (dist < (radius << 8)) { + uint32_t color = SEGMENT.color_from_palette(i * 255 / SEGLEN, false, false, 0); + uint8_t blend = dist / radius; // linear gradient note: dist is in subpixels, radius in pixels, result is [0, 255] since dist < radius*256 + if (SEGMENT.custom2) { + uint8_t modVal; // modulation value + if (SEGMENT.check1) { + modVal = (sin16_t((i * SEGMENT.custom2 << 6) + (strip.now * SEGMENT.custom3 << 5)) >> 8) + 128; // sine modulation: regular "Zebra" stripes + } else { + modVal = perlin16((i * SEGMENT.custom2 << 7), strip.now * SEGMENT.custom3 << 5) >> 8; // perlin noise modulation + } + color = color_fade(color, modVal, true); // dim by modulator value + } + SEGMENT.setPixelColor(i, color_blend(color, SEGCOLOR(1), blend)); // blend to background color + } else { + SEGMENT.setPixelColor(i, SEGCOLOR(1)); + } + } + + return FRAMETIME; +} +static const char _data_FX_MODE_SHIMMER[] PROGMEM = "Shimmer@Speed,Interval,Size,Granular,Flow,Zebra,Reverse,Sporadic;Fx,Bg,Cx;!;1;pal=15,sx=220,ix=10,c2=0,c3=0"; #ifndef WLED_DISABLE_2D /////////////////////////////////////////////////////////////////////////////// @@ -10805,6 +10877,7 @@ void WS2812FX::setupEffectData() { addEffect(FX_MODE_FLOWSTRIPE, &mode_FlowStripe, _data_FX_MODE_FLOWSTRIPE); addEffect(FX_MODE_WAVESINS, &mode_wavesins, _data_FX_MODE_WAVESINS); addEffect(FX_MODE_ROCKTAVES, &mode_rocktaves, _data_FX_MODE_ROCKTAVES); + addEffect(FX_MODE_SHIMMER, &mode_shimmer, _data_FX_MODE_SHIMMER); // --- 2D effects --- #ifndef WLED_DISABLE_2D diff --git a/wled00/FX.h b/wled00/FX.h index 097c857caf..d80f93abec 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -317,6 +317,7 @@ extern byte realtimeMode; // used in getMappedPixelIndex() #define FX_MODE_DJLIGHT 159 #define FX_MODE_2DFUNKYPLANK 160 //#define FX_MODE_2DCENTERBARS 161 +#define FX_MODE_SHIMMER 161 // gap fill, non SR 1D effect #define FX_MODE_2DPULSER 162 #define FX_MODE_BLURZ 163 #define FX_MODE_2DDRIFT 164