Added snowfall animation

This commit is contained in:
Thomas Kolb 2019-12-15 21:39:03 +01:00
parent 8e9124c884
commit aa56e36f3f
4 changed files with 253 additions and 1 deletions

View file

@ -3,3 +3,4 @@
#include "ConnectingAnimation.h"
#include "ConnectionEstablishedAnimation.h"
#include "FireAnimation.h"
#include "SnowfallAnimation.h"

View file

@ -14,13 +14,15 @@ class AnimationController
enum DefaultAnimation {
FIRE_HOT = 0,
FIRE_COLD = 1,
SNOWFALL = 2,
NUM_DEFAULT_ANIMATIONS
};
static const constexpr std::array<const char*, NUM_DEFAULT_ANIMATIONS> AnimationNames{
"Hot Fire",
"Cold Fire"
"Cold Fire",
"Snowfall"
};
AnimationController(Fader *fader);

View file

@ -0,0 +1,58 @@
#pragma once
#include <random>
#include <list>
#include "Animation.h"
#include "fasttrigon.h"
class SnowfallAnimation : public Animation
{
public:
SnowfallAnimation(Fader *fader);
void loop(uint64_t frame) override;
void stop(void) override
{
m_stopping = true;
}
void reset(void) override;
private:
class SnowFlake
{
public:
SnowFlake(Fader *fader, int32_t phase, int32_t vertSpeed, int32_t baseStrip);
void move(void);
bool has_fallen(void) const;
void render(void) const;
int32_t cur_strip(void) const;
private:
Fader *m_fader;
int32_t m_phase;
int32_t m_phaseIncPerFrame;
int32_t m_vertSpeed; // in 1/256 LEDs
int32_t m_radius; // in 1/256 LEDs
int32_t m_radiusSq; // in 1/256 LEDs
int32_t m_basePosStrip; // in 1/256 strips (for speed only)
int32_t m_posStrip; // in 1/256 strips
int32_t m_posLED; // in 1/256 LEDs
};
std::list<SnowFlake> m_snowFlakes;
std::vector<uint32_t> m_snowLevel; // level for each strip in 1/256 LEDs
std::default_random_engine m_gen;
bool m_stopping;
};

View file

@ -0,0 +1,191 @@
#include <algorithm>
#include "Animation/SnowfallAnimation.h"
/********* SnowFlake *********/
SnowfallAnimation::SnowFlake::SnowFlake(Fader *fader, int32_t phase, int32_t vertSpeed, int32_t baseStrip)
: m_fader(fader),
m_phase(phase),
m_phaseIncPerFrame(fasttrigon::LUT_SIZE/60),
m_vertSpeed(vertSpeed),
m_radius(282), // 1.1 * 256
m_radiusSq(310),
m_basePosStrip(baseStrip*256),
m_posStrip(baseStrip*256),
m_posLED(fader->modules_per_strip()*256 + m_radius*2)
{}
void SnowfallAnimation::SnowFlake::move(void)
{
m_phase += m_phaseIncPerFrame;
if(m_phase > fasttrigon::LUT_SIZE) {
m_phase -= fasttrigon::LUT_SIZE;
}
m_posStrip = m_basePosStrip + 2*fasttrigon::fastsin(m_phase);
m_posLED -= m_vertSpeed;
}
bool SnowfallAnimation::SnowFlake::has_fallen(void) const
{
return m_posLED < -(2*m_radius);
}
int32_t SnowfallAnimation::SnowFlake::cur_strip(void) const
{
int32_t strip = m_posStrip / 256;
uint32_t nstrip = m_fader->strips();
while(strip < 0) {
strip += nstrip;
}
while(strip >= nstrip) {
strip -= nstrip;
}
return strip;
}
void SnowfallAnimation::SnowFlake::render(void) const
{
int32_t min_strip = (m_posStrip - m_radius) / 256;
int32_t max_strip = (m_posStrip + m_radius + 256) / 256;
int32_t min_led = (m_posLED - m_radius) / 256;
int32_t max_led = (m_posLED + m_radius + 256) / 256;
for(int32_t strip = min_strip; strip <= max_strip; strip++) {
int32_t finestrip = strip * 256;
for(int32_t led = min_led; led <= max_led; led++) {
if(led < 0 || led >= m_fader->modules_per_strip()) {
continue;
}
int32_t fineled = led * 256;
int32_t diststrip = finestrip - m_posStrip;
int32_t distled = fineled - m_posLED;
int32_t distsq = (diststrip*diststrip + distled*distled) / 256;
// brightness target range: 0..255
int32_t brightness = 256 - 256 * distsq / m_radiusSq;
if(brightness < 0) {
continue; // this pixel is too far away -> no effect
}
int32_t normstrip = strip;
int32_t nstrips = m_fader->strips();
while(normstrip < 0) {
normstrip += nstrips;
}
while(normstrip >= nstrips) {
normstrip -= nstrips;
}
m_fader->add_color(normstrip, led,
Fader::Color{
0,
0,
static_cast<int16_t>(32*brightness/256),
static_cast<int16_t>(48*brightness/256)
});
}
}
}
/********* SnowfallAnimation *********/
SnowfallAnimation::SnowfallAnimation(Fader *fader)
: Animation(fader),
m_snowLevel(fader->strips(), 0)
{
reset();
}
void SnowfallAnimation::loop(uint64_t frame)
{
std::size_t nled = m_fader->modules_per_strip();
std::size_t nstrip = m_fader->strips();
// update snow level on ground from fallen snow flakes
for(auto &flake: m_snowFlakes) {
if(flake.has_fallen()) {
m_snowLevel[flake.cur_strip()] += 65536/5;
}
}
// remove fallen snow flakes
m_snowFlakes.remove_if(
[](const SnowFlake &flake) { return flake.has_fallen(); });
if(!m_stopping) {
// spawn new snowflakes
std::uniform_int_distribution<uint32_t> spawnRNG(0, 999);
if(spawnRNG(m_gen) < 50) {
std::uniform_int_distribution<int32_t> phaseRNG(0, fasttrigon::LUT_SIZE-1);
std::uniform_int_distribution<int32_t> vertSpeedRNG(13, 38); // 0.05..0.15
std::uniform_int_distribution<int32_t> baseStripRNG(0, nstrip-1);
m_snowFlakes.emplace_back(SnowFlake{
m_fader,
phaseRNG(m_gen),
vertSpeedRNG(m_gen),
baseStripRNG(m_gen)
});
}
}
// clear the frame buffer
m_fader->set_color(Fader::Color{0, 0, 0, 0});
// move snow flakes and render
for(auto &flake: m_snowFlakes) {
flake.move();
flake.render();
}
// render snow on the ground and melt it
for(size_t strip = 0; strip < nstrip; strip++) {
size_t led = 0;
size_t level = m_snowLevel[strip];
size_t brightness;
while(level > 0 && led < nled) {
if(level >= 65536) {
brightness = 65536;
} else {
brightness = level;
}
m_fader->add_color(strip, led,
Fader::Color{
0,
static_cast<int16_t>(8 * brightness / 65535),
static_cast<int16_t>(32 * brightness / 65535),
static_cast<int16_t>(12 * brightness / 65535)
});
level -= brightness;
led++;
}
// "melt" the snow
m_snowLevel[strip] = m_snowLevel[strip] * 999 / 1000;
}
m_fader->update();
if(m_stopping && m_snowFlakes.empty()) {
m_running = false;
}
}
void SnowfallAnimation::reset(void)
{
m_stopping = false;
m_running = true;
}