#include #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 + FASTTRIGON_8BIT(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(32*brightness/256), static_cast(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 spawnRNG(0, 999); if(spawnRNG(m_gen) < 50) { std::uniform_int_distribution phaseRNG(0, fasttrigon::LUT_SIZE-1); std::uniform_int_distribution vertSpeedRNG(13, 38); // 0.05..0.15 std::uniform_int_distribution 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(8 * brightness / 65535), static_cast(32 * brightness / 65535), static_cast(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; }