Files
WLED/usermods/user_fx/user_fx.cpp
Damian Schneider 6b70c6ae91 Remove FRAMETIME return value from all FX (#5314)
* remove "return FRAMETIME" from all FX, fix timing for some FX

- all FX now render every frame, no more "speed up" during transitions

* fix missing return by adding FS_FALLBACK_STATIC macro

* add FX_FALLBACK_STATIC also to user_fx

* remove obsolete seg.next_time
2026-02-09 07:57:49 +01:00

141 lines
4.6 KiB
C++

#include "wled.h"
// for information how FX metadata strings work see https://kno.wled.ge/interfaces/json-api/#effect-metadata
// static effect, used if an effect fails to initialize
static void mode_static(void) {
SEGMENT.fill(SEGCOLOR(0));
}
#define FX_FALLBACK_STATIC { mode_static(); return; }
// If you define configuration options in your class and need to reference them in your effect function, add them here.
// If you only need to use them in your class you can define them as class members instead.
// bool myConfigValue = false;
/////////////////////////
// User FX functions //
/////////////////////////
// Diffusion Fire: fire effect intended for 2D setups smaller than 16x16
static void mode_diffusionfire(void) {
if (!strip.isMatrix || !SEGMENT.is2D())
FX_FALLBACK_STATIC; // not a 2D set-up
const int cols = SEG_W;
const int rows = SEG_H;
const auto XY = [&](int x, int y) { return x + y * cols; };
const uint8_t refresh_hz = map(SEGMENT.speed, 0, 255, 20, 80);
const unsigned refresh_ms = 1000 / refresh_hz;
const int16_t diffusion = map(SEGMENT.custom1, 0, 255, 0, 100);
const uint8_t spark_rate = SEGMENT.intensity;
const uint8_t turbulence = SEGMENT.custom2;
unsigned dataSize = cols * rows; // SEGLEN (virtual length) is equivalent to vWidth()*vHeight() for 2D
if (!SEGENV.allocateData(dataSize))
FX_FALLBACK_STATIC; // allocation failed
if (SEGENV.call == 0) {
SEGMENT.fill(BLACK);
SEGENV.step = 0;
}
if ((strip.now - SEGENV.step) >= refresh_ms) {
// Keep for ≤~1 KiB; otherwise consider heap or reuse SEGENV.data as scratch.
uint8_t tmp_row[cols];
SEGENV.step = strip.now;
// scroll up
for (unsigned y = 1; y < rows; y++)
for (unsigned x = 0; x < cols; x++) {
unsigned src = XY(x, y);
unsigned dst = XY(x, y - 1);
SEGENV.data[dst] = SEGENV.data[src];
}
if (hw_random8() > turbulence) {
// create new sparks at bottom row
for (unsigned x = 0; x < cols; x++) {
uint8_t p = hw_random8();
if (p < spark_rate) {
unsigned dst = XY(x, rows - 1);
SEGENV.data[dst] = 255;
}
}
}
// diffuse
for (unsigned y = 0; y < rows; y++) {
for (unsigned x = 0; x < cols; x++) {
unsigned v = SEGENV.data[XY(x, y)];
if (x > 0) {
v += SEGENV.data[XY(x - 1, y)];
}
if (x < (cols - 1)) {
v += SEGENV.data[XY(x + 1, y)];
}
tmp_row[x] = min(255, (int)(v * 100 / (300 + diffusion)));
}
for (unsigned x = 0; x < cols; x++) {
SEGENV.data[XY(x, y)] = tmp_row[x];
if (SEGMENT.check1) {
uint32_t color = SEGMENT.color_from_palette(tmp_row[x], true, false, 0);
SEGMENT.setPixelColorXY(x, y, color);
} else {
uint32_t base = SEGCOLOR(0);
SEGMENT.setPixelColorXY(x, y, color_fade(base, tmp_row[x]));
}
}
}
}
}
static const char _data_FX_MODE_DIFFUSIONFIRE[] PROGMEM = "Diffusion Fire@!,Spark rate,Diffusion Speed,Turbulence,,Use palette;;Color;;2;pal=35";
/////////////////////
// UserMod Class //
/////////////////////
class UserFxUsermod : public Usermod {
private:
public:
void setup() override {
strip.addEffect(255, &mode_diffusionfire, _data_FX_MODE_DIFFUSIONFIRE);
////////////////////////////////////////
// add your effect function(s) here //
////////////////////////////////////////
// use id=255 for all custom user FX (the final id is assigned when adding the effect)
// strip.addEffect(255, &mode_your_effect, _data_FX_MODE_YOUR_EFFECT);
// strip.addEffect(255, &mode_your_effect2, _data_FX_MODE_YOUR_EFFECT2);
// strip.addEffect(255, &mode_your_effect3, _data_FX_MODE_YOUR_EFFECT3);
}
///////////////////////////////////////////////////////////////////////////////////////////////
// If you want configuration options in the usermod settings page, implement these methods //
///////////////////////////////////////////////////////////////////////////////////////////////
// void addToConfig(JsonObject& root) override
// {
// JsonObject top = root.createNestedObject(FPSTR("User FX"));
// top["myConfigValue"] = myConfigValue;
// }
// bool readFromConfig(JsonObject& root) override
// {
// JsonObject top = root[FPSTR("User FX")];
// bool configComplete = !top.isNull();
// configComplete &= getJsonValue(top["myConfigValue"], myConfigValue);
// return configComplete;
// }
void loop() override {} // nothing to do in the loop
uint16_t getId() override { return USERMOD_ID_USER_FX; }
};
static UserFxUsermod user_fx;
REGISTER_USERMOD(user_fx);