mirror of
https://github.com/wled/WLED.git
synced 2026-03-13 08:29:49 +08:00
Introduce comet effect usermod with fire particle system (#5347)
* Introduce comet effect usermod with fire particle system * Introduce comet effect usermod with fire particle system
This commit is contained in:
123
usermods/PS_Comet/PS_Comet.cpp
Normal file
123
usermods/PS_Comet/PS_Comet.cpp
Normal file
@@ -0,0 +1,123 @@
|
||||
#include "wled.h"
|
||||
#include "FXparticleSystem.h"
|
||||
|
||||
unsigned long nextCometCreationTime = 0;
|
||||
|
||||
#define FX_FALLBACK_STATIC { SEGMENT.fill(SEGCOLOR(0)); return; }
|
||||
// Use UINT32_MAX - 1 for the "no comet" case so we can add 1 later and not have it overflow
|
||||
#define NULL_INDEX UINT32_MAX - 1
|
||||
|
||||
///////////////////////
|
||||
// Effect Function //
|
||||
///////////////////////
|
||||
|
||||
void mode_pscomet() {
|
||||
ParticleSystem2D *PartSys = nullptr;
|
||||
uint32_t i;
|
||||
|
||||
if (SEGMENT.call == 0) { // Initialization
|
||||
// Try to allocate one comet for every column
|
||||
if (!initParticleSystem2D(PartSys, SEGMENT.vWidth())) {
|
||||
FX_FALLBACK_STATIC; // Allocation failed or not 2D
|
||||
}
|
||||
PartSys->setMotionBlur(170); // Enable motion blur
|
||||
PartSys->setParticleSize(0); // Allow small comets to be a single pixel wide
|
||||
}
|
||||
else {
|
||||
PartSys = reinterpret_cast<ParticleSystem2D *>(SEGENV.data); // If not first call, use existing data
|
||||
}
|
||||
if (PartSys == nullptr || SEGMENT.vHeight() < 2 || SEGMENT.vWidth() < 2) {
|
||||
FX_FALLBACK_STATIC;
|
||||
}
|
||||
|
||||
PartSys->updateSystem(); // Update system properties (dimensions and data pointers)
|
||||
|
||||
auto has_fallen_off_screen = [PartSys](uint32_t particleIndex) {
|
||||
return particleIndex < PartSys->numSources
|
||||
? PartSys->sources[particleIndex].source.y < PartSys->maxY * -1
|
||||
: true;
|
||||
};
|
||||
|
||||
// This will be SEGMENT.vWidth() unless the particle system had insufficient memory
|
||||
uint32_t numComets = PartSys->numSources;
|
||||
// Pick a random column for a new comet to spawn, but reset it to null if it's not time yet or there's already a
|
||||
// comet nearby
|
||||
uint32_t chosenIndex = hw_random(numComets);
|
||||
if (
|
||||
strip.now < nextCometCreationTime
|
||||
|| !has_fallen_off_screen(chosenIndex - 1)
|
||||
|| !has_fallen_off_screen(chosenIndex)
|
||||
|| !has_fallen_off_screen(chosenIndex + 1)
|
||||
) {
|
||||
chosenIndex = NULL_INDEX;
|
||||
} else {
|
||||
uint16_t cometFrequencyDelay = 2040 - (SEGMENT.intensity << 3);
|
||||
nextCometCreationTime = strip.now + cometFrequencyDelay + hw_random16(cometFrequencyDelay);
|
||||
}
|
||||
uint8_t canLargeCometSpawn =
|
||||
// Slider 3 determines % of large comets with extra particle sources on their sides
|
||||
SEGMENT.custom1 > hw_random8(254)
|
||||
&& chosenIndex != 0
|
||||
&& chosenIndex != numComets - 1;
|
||||
uint8_t fallingSpeed = 1 + (SEGMENT.speed >> 2);
|
||||
|
||||
// Update the comets
|
||||
for (i = 0; i < numComets; i++) {
|
||||
auto& source = PartSys->sources[i];
|
||||
auto& sourceParticle = source.source;
|
||||
|
||||
if (!has_fallen_off_screen(i)) {
|
||||
// Active comets fall downwards and emit flames
|
||||
sourceParticle.y -= fallingSpeed;
|
||||
source.vy = (SEGMENT.speed >> 5) - fallingSpeed; // Emitting speed (upwards)
|
||||
PartSys->flameEmit(PartSys->sources[i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
bool isChosenComet = i == chosenIndex;
|
||||
bool isChosenSideComet =
|
||||
canLargeCometSpawn &&
|
||||
(i == chosenIndex - 1 || i == chosenIndex + 1);
|
||||
|
||||
// Chosen comets respawn at the top
|
||||
if (isChosenComet || isChosenSideComet) {
|
||||
// Map the comet index into an output pixel index
|
||||
sourceParticle.x = i * PartSys->maxX / (SEGMENT.vWidth() - 1);
|
||||
// Spawn a bit above the top to avoid popping into view
|
||||
sourceParticle.y = PartSys->maxY + (2 * fallingSpeed);
|
||||
if (isChosenComet) {
|
||||
// Slider 4 controls comet length via particle lifetime and fire intensity adjustments
|
||||
source.maxLife = 16 + (SEGMENT.custom2 >> 2);
|
||||
source.minLife = source.maxLife >> 1;
|
||||
sourceParticle.ttl = 16 - (SEGMENT.custom2 >> 4);
|
||||
} else {
|
||||
// Side comets have fixed length
|
||||
source.maxLife = 18;
|
||||
source.minLife = 14;
|
||||
sourceParticle.ttl = 16;
|
||||
// Shift side comets up by 1 pixel
|
||||
sourceParticle.y += 2 * PartSys->maxY / (SEGMENT.vHeight() - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Slider 4 controls comet length via particle lifetime and fire intensity adjustments
|
||||
PartSys->updateFire(max(255U - SEGMENT.custom2, 45U));
|
||||
}
|
||||
static const char _data_FX_MODE_PSCOMET[] PROGMEM = "PS Comet@Falling Speed,Comet Frequency,Large Comet Probability,Comet Length;;!;2;pal=35,sx=128,ix=255,c1=32,c2=128";
|
||||
|
||||
/////////////////////
|
||||
// UserMod Class //
|
||||
/////////////////////
|
||||
|
||||
class PSCometUsermod : public Usermod {
|
||||
public:
|
||||
void setup() override {
|
||||
strip.addEffect(255, &mode_pscomet, _data_FX_MODE_PSCOMET);
|
||||
}
|
||||
|
||||
void loop() override {}
|
||||
};
|
||||
|
||||
static PSCometUsermod ps_comet;
|
||||
REGISTER_USERMOD(ps_comet);
|
||||
25
usermods/PS_Comet/README.md
Normal file
25
usermods/PS_Comet/README.md
Normal file
@@ -0,0 +1,25 @@
|
||||
## Description
|
||||
|
||||
A 2D falling comet effect similar to "Matrix" but with a fire particle simulation to enhance the comet trail visuals. Works with custom color palettes, defaulting to "Fire". Supports "small" and "large" comets which are 1px and 3px wide respectively.
|
||||
|
||||
Demo: [https://imgur.com/a/i1v5WAy](https://imgur.com/a/i1v5WAy)
|
||||
|
||||
## Installation
|
||||
|
||||
To activate the usermod, add the following line to your platformio_override.ini
|
||||
```ini
|
||||
custom_usermods = ps_comet
|
||||
```
|
||||
Or if you are already using a usermod, append ps_comet to the list
|
||||
```ini
|
||||
custom_usermods = audioreactive ps_comet
|
||||
```
|
||||
|
||||
You should now see "PS Comet" appear in your effect list.
|
||||
|
||||
## Parameters
|
||||
|
||||
1. **Falling Speed** sets how fast the comets fall
|
||||
2. **Comet Frequency** determines how many comets are on screen at a time
|
||||
3. **Large Comet Probability** determines how often large 3px wide comets spawn
|
||||
4. **Comet Length** sets how far comet trails stretch vertically
|
||||
4
usermods/PS_Comet/library.json
Normal file
4
usermods/PS_Comet/library.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"name": "PS Comet",
|
||||
"build": { "libArchive": false }
|
||||
}
|
||||
Reference in New Issue
Block a user