diff --git a/examples/cpp/.gitignore b/examples/cpp/.gitignore index bb3371f031f43db3624ce95e55186cd2feafeefa..50b27b4e720c250d23a81c014c6aa210ff189ded 100644 --- a/examples/cpp/.gitignore +++ b/examples/cpp/.gitignore @@ -6,4 +6,5 @@ rings_2d spokes dot mixer +looper particle_trail diff --git a/examples/cpp/Makefile b/examples/cpp/Makefile index 2eb77f186d2db187f860309098248e584c303a60..fffa0d29fb839b15a6b2577def5deb7532c376e4 100644 --- a/examples/cpp/Makefile +++ b/examples/cpp/Makefile @@ -1,23 +1,24 @@ -PROGRAMS = simple rings spokes dot particle_trail mixer +PROGRAMS = simple rings spokes dot particle_trail mixer looper # Important optimization options -CFLAGS = -O3 -ffast-math -fno-rtti +CXXFLAGS = -O3 -ffast-math -fno-rtti # Standard libraries -LFLAGS = -lm -lstdc++ -lpthread +#LDFLAGS = -lm -lstdc++ -lpthread +LDFLAGS = -lm -lpthread # Debugging -CFLAGS += -g -Wall -LFLAGS += -g +CXXFLAGS += -g -Wall +LDFLAGS += -g # Annoying warnings on by default on Mac OS -CFLAGS += -Wno-tautological-constant-out-of-range-compare -Wno-gnu-static-float-init +CXXFLAGS += -Wno-tautological-constant-out-of-range-compare -Wno-gnu-static-float-init all: $(PROGRAMS) .cpp: - $(CC) $(CFLAGS) $< -o $@ $(LFLAGS) + $(CXX) $(CXXFLAGS) $< -o $@ $(LDFLAGS) .PHONY: clean all diff --git a/examples/cpp/dot.cpp b/examples/cpp/dot.cpp index 8cfacc00d78f46e633a183e45b52b57ea89c94ba..f10918407fbabb41d70894b23ec306d5804d4f34 100644 --- a/examples/cpp/dot.cpp +++ b/examples/cpp/dot.cpp @@ -6,7 +6,7 @@ int main(int argc, char **argv) EffectRunner r; DotEffect e("data/dot.png"); - r.setEffect(&e); + r.addEffect(&e); // Defaults, overridable with command line options r.setLayout("../layouts/grid32x16z.json"); diff --git a/examples/cpp/lib/brightness.h b/examples/cpp/lib/brightness.h index 072a2d8c7e9240b0c66d97e52abd69bda60fdb4b..5843d2aa289a0ce430a4203e07397c4dcd4af38b 100644 --- a/examples/cpp/lib/brightness.h +++ b/examples/cpp/lib/brightness.h @@ -51,7 +51,7 @@ public: float getTotalBrightnessDelta() const; virtual void beginFrame(const FrameInfo& f); - virtual void endFrame(const FrameInfo& f); + virtual bool endFrame(const FrameInfo& f); virtual void debug(const DebugInfo& f); virtual void shader(Vec3& rgb, const PixelInfo& p) const; @@ -236,9 +236,10 @@ inline void Brightness::beginFrame(const FrameInfo& f) latestAverage = avg; } -inline void Brightness::endFrame(const FrameInfo& f) +inline bool Brightness::endFrame(const FrameInfo& f) { next.endFrame(f); + return Effect::endFrame(f); } inline void Brightness::debug(const DebugInfo& d) diff --git a/examples/cpp/lib/effect.h b/examples/cpp/lib/effect.h index 62de4e3a7bd28a9ecf1cfe5f46c1d48a55f5ed89..5c9f10d31abf517fb02ab16ba4d30f4088b810df 100644 --- a/examples/cpp/lib/effect.h +++ b/examples/cpp/lib/effect.h @@ -27,11 +27,11 @@ #pragma once -#include <math.h> +#include <cmath> #include <unistd.h> #include <vector> -#include <string.h> -#include <stdlib.h> +#include <cstring> +#include <cstdlib> #include "nanoflann.h" // Tiny KD-tree library #include "svl/SVL.h" @@ -71,7 +71,7 @@ public: // Optional begin/end frame callbacks virtual void beginFrame(const FrameInfo& f); - virtual void endFrame(const FrameInfo& f); + virtual bool endFrame(const FrameInfo& f); // Optional callback, invoked once per second when verbose mode is enabled. // This can print parameters out to the console. @@ -176,6 +176,10 @@ public: EffectRunner &runner; }; + + Effect(): number_frames(0), frame_count(0) {} + unsigned long number_frames; + unsigned long frame_count; }; @@ -302,10 +306,21 @@ inline Effect::DebugInfo::DebugInfo(EffectRunner &runner) : runner(runner) {} -inline void Effect::beginFrame(const FrameInfo &f) {} -inline void Effect::endFrame(const FrameInfo &f) {} -inline void Effect::debug(const DebugInfo &f) {} -inline void Effect::postProcess(const Vec3& rgb, const PixelInfo& p) {} +inline void Effect::beginFrame( const FrameInfo & ) {} +inline bool Effect::endFrame( const FrameInfo & ) +{ + if( number_frames ) + { + if( frame_count++ > number_frames ) + { + frame_count = 0; + return true; + } + } + return false; +} +inline void Effect::debug( const DebugInfo & ) {} +inline void Effect::postProcess( const Vec3&, const PixelInfo& ) {} static inline float sq(float a) diff --git a/examples/cpp/lib/effect_mixer.h b/examples/cpp/lib/effect_mixer.h index 413f11645bc6b0f28e96ca03ddbd0ea0b22c9c8e..4113f20b173f1f91d26155e85a0ba829371406f0 100644 --- a/examples/cpp/lib/effect_mixer.h +++ b/examples/cpp/lib/effect_mixer.h @@ -63,7 +63,7 @@ public: virtual void shader(Vec3& rgb, const PixelInfo& p) const; virtual void postProcess(const Vec3& rgb, const PixelInfo& p); virtual void beginFrame(const FrameInfo& f); - virtual void endFrame(const FrameInfo& f); + virtual bool endFrame(const FrameInfo& f); virtual void debug(const DebugInfo& d); private: @@ -223,11 +223,13 @@ inline void EffectMixer::postProcess(const Vec3& rgb, const PixelInfo& p) } } -inline void EffectMixer::endFrame(const FrameInfo& f) +inline bool EffectMixer::endFrame(const FrameInfo& f) { + bool lastFrame = false; for (unsigned i = 0; i < channels.size(); ++i) { - channels[i].effect->endFrame(f); + lastFrame |= channels[i].effect->endFrame(f); } + return lastFrame | Effect::endFrame(f); } inline void EffectMixer::debug(const DebugInfo& d) diff --git a/examples/cpp/lib/effect_runner.h b/examples/cpp/lib/effect_runner.h index 347a848f14af5a8ce2b56278b95c7fb6a4a1573e..bee028a6413cbbb547d6a766be7bf574dd4e34dd 100644 --- a/examples/cpp/lib/effect_runner.h +++ b/examples/cpp/lib/effect_runner.h @@ -27,15 +27,15 @@ #pragma once -#include <math.h> +#include <cmath> #include <unistd.h> #include <algorithm> #include <vector> #include <sys/time.h> -#include <stdio.h> -#include <string.h> -#include <stdlib.h> -#include <time.h> +#include <cstdio> +#include <cstring> +#include <cstdlib> +#include <ctime> #include "effect.h" #include "opc_client.h" @@ -52,6 +52,7 @@ public: bool setServer(const char *hostport); bool setLayout(const char *filename); void setEffect(Effect* effect); + void addEffect(Effect* effect); void setMaxFrameRate(float fps); void setVerbose(bool verbose = true); @@ -76,6 +77,7 @@ public: struct FrameStatus { float timeDelta; bool debugOutput; + bool lastFrame; }; // Main loop body @@ -87,7 +89,7 @@ public: // Simple argument parsing and optional main loop bool parseArguments(int argc, char **argv); - int main(int argc, char **argv); + int main(int argc, char **argv, bool loop = true); protected: // Extensibility for argument parsing @@ -99,6 +101,7 @@ private: OPCClient opc; rapidjson::Document layout; Effect *effect; + std::vector<Effect*> effects; std::vector<uint8_t> frameBuffer; Effect::FrameInfo frameInfo; @@ -123,7 +126,12 @@ private: inline EffectRunner::EffectRunner() - : effect(0), + : opc(), + layout(), + effect(0), + effects(), + frameBuffer(), + frameInfo(), minTimeDelta(0), currentDelay(0), filteredTimeDelta(0), @@ -197,9 +205,16 @@ inline bool EffectRunner::hasLayout() const inline void EffectRunner::setEffect(Effect *effect) { + effects.clear(); + effects.push_back( effect ); this->effect = effect; } +inline void EffectRunner::addEffect( Effect *effect ) +{ + effects.push_back( effect ); +} + inline Effect* EffectRunner::getEffect() const { return effect; @@ -237,9 +252,12 @@ inline float EffectRunner::getPercentBusy() const inline void EffectRunner::run() { - while (true) { - doFrame(); + FrameStatus status; + do + { + status = doFrame(); } + while( !status.lastFrame ); } inline EffectRunner::FrameStatus EffectRunner::doFrame() @@ -265,8 +283,9 @@ inline EffectRunner::FrameStatus EffectRunner::doFrame(float timeDelta) FrameStatus frameStatus; // Effects may get a modified view of time - frameStatus.timeDelta = frameInfo.timeDelta = timeDelta * speed; + frameStatus.timeDelta = frameInfo.timeDelta = timeDelta * speed; frameStatus.debugOutput = false; + frameStatus.lastFrame = false; jitterStatsMin = std::min(jitterStatsMin, frameStatus.timeDelta); jitterStatsMax = std::max(jitterStatsMax, frameStatus.timeDelta); @@ -296,7 +315,7 @@ inline EffectRunner::FrameStatus EffectRunner::doFrame(float timeDelta) opc.write(frameBuffer); } - effect->endFrame(frameInfo); + frameStatus.lastFrame = effect->endFrame(frameInfo); } // Low-pass filter for timeDelta, to estimate our frame rate @@ -370,13 +389,22 @@ inline bool EffectRunner::parseArguments(int argc, char **argv) return true; } -inline int EffectRunner::main(int argc, char **argv) +inline int EffectRunner::main(int argc, char **argv, bool loop) { if (!parseArguments(argc, argv)) { return 1; } - run(); + do + { + for( std::vector<Effect*>::iterator i( effects.begin() ); + i != effects.end(); ++i ) + { + effect = *i; + run(); + } + } + while( loop ); return 0; } diff --git a/examples/cpp/looper.cpp b/examples/cpp/looper.cpp new file mode 100644 index 0000000000000000000000000000000000000000..235958e7fc554d720047c37f529028c86fee5ec0 --- /dev/null +++ b/examples/cpp/looper.cpp @@ -0,0 +1,41 @@ +#include "lib/effect_runner.h" +#include "dot.h" +#include "particle_trail.h" +#include "rings.h" +#include "spokes.h" + +int main(int argc, char **argv) +{ + EffectRunner r; + + int fps = 300; + for( int i = 1; i < argc; ++i ) + { + if (!strcmp(argv[i], "-fps") && (i+1 < argc)) { + fps = atoi(argv[++i]); + if (fps <= 0) { + fprintf(stderr, "Invalid frame rate\n"); + return 1; + } + } + } + + DotEffect dot("data/dot.png"); + ParticleTrailEffect trail; + RingsEffect rings("data/glass.png"); + SpokesEffect spokes; + dot.number_frames = fps * 3; + trail.number_frames = fps * 3; + rings.number_frames = fps * 3; + spokes.number_frames = fps * 3; + + r.addEffect(&dot); + r.addEffect(&trail); + r.addEffect(&rings); + r.addEffect(&spokes); + + // Defaults, overridable with command line options + r.setLayout("../layouts/grid32x16z.json"); + + return r.main(argc, argv); +} diff --git a/examples/cpp/particle_trail.cpp b/examples/cpp/particle_trail.cpp index d0b3ff67ee731771b18456ba0351ed496bd0786b..168434106015e3eb393f95494489fac240eb765f 100644 --- a/examples/cpp/particle_trail.cpp +++ b/examples/cpp/particle_trail.cpp @@ -5,7 +5,7 @@ int main(int argc, char **argv) { EffectRunner r; ParticleTrailEffect e; - r.setEffect(&e); + r.addEffect(&e); r.setLayout("../layouts/grid32x16z.json"); return r.main(argc, argv); } diff --git a/examples/cpp/particle_trail.h b/examples/cpp/particle_trail.h index 597361ee32daaba82cbb03f8746a96c260a77157..5f142beff23c0b9b7a0a78445be2e94bcde90d15 100644 --- a/examples/cpp/particle_trail.h +++ b/examples/cpp/particle_trail.h @@ -2,7 +2,7 @@ #pragma once -#include <math.h> +#include <cmath> #include "lib/color.h" #include "lib/effect.h" #include "lib/particle.h" diff --git a/examples/cpp/rings.cpp b/examples/cpp/rings.cpp index f72b564275e3db7928f8a559eb51862a4bc06c72..9604b3172344c41eb9c8aa00a1c6a0aecda28561 100644 --- a/examples/cpp/rings.cpp +++ b/examples/cpp/rings.cpp @@ -6,7 +6,7 @@ int main(int argc, char **argv) RingsEffect e("data/glass.png"); EffectRunner r; - r.setEffect(&e); + r.addEffect(&e); r.setLayout("../layouts/grid32x16z.json"); return r.main(argc, argv); diff --git a/examples/cpp/rings.h b/examples/cpp/rings.h index b6ae179f89991ab161980fdc2d26bbfccdb69df1..31cc2bc59905bd32f91d16da13ee7f39408c52c1 100644 --- a/examples/cpp/rings.h +++ b/examples/cpp/rings.h @@ -12,9 +12,9 @@ #pragma once -#include <math.h> -#include <time.h> -#include <stdlib.h> +#include <cmath> +#include <ctime> +#include <cstdlib> #include "lib/color.h" #include "lib/effect.h" #include "lib/noise.h" @@ -176,7 +176,7 @@ public: } } - virtual void endFrame(const FrameInfo &f) + virtual bool endFrame(const FrameInfo &f) { // Per-frame brightness calculations. // Adjust threshold in brightness-determining noise function, in order @@ -205,6 +205,7 @@ public: if (step < -thresholdStepLimit) step = -thresholdStepLimit; threshold += step; } + return Effect::endFrame(f); } virtual void debug(const DebugInfo &di) diff --git a/examples/cpp/simple.cpp b/examples/cpp/simple.cpp index b93dbbbabbedabfe883da573c139bb916f9aeeab..f65242c1c685638017a9ef15893e3bd828061337 100644 --- a/examples/cpp/simple.cpp +++ b/examples/cpp/simple.cpp @@ -34,11 +34,11 @@ int main(int argc, char **argv) EffectRunner r; MyEffect e; - r.setEffect(&e); + r.addEffect(&e); // Defaults, overridable with command line options r.setMaxFrameRate(100); r.setLayout("../layouts/grid32x16z.json"); return r.main(argc, argv); -} \ No newline at end of file +} diff --git a/examples/cpp/spokes.cpp b/examples/cpp/spokes.cpp index 3576d0015e7b711fa27660672252508a1aa33821..3af6952aef9a43641a33c694d3f61978aaeb8737 100644 --- a/examples/cpp/spokes.cpp +++ b/examples/cpp/spokes.cpp @@ -10,7 +10,7 @@ int main(int argc, char **argv) br.set(0.2); EffectRunner r; - r.setEffect(&br); + r.addEffect(&br); r.setLayout("../layouts/grid32x16z.json"); return r.main(argc, argv); } diff --git a/examples/cpp/spokes.h b/examples/cpp/spokes.h index ac784693399b0f1b7d192c9eebaa745cda0c7b51..acab839ef891aa60de4d79e22b4912e0f9668fa3 100644 --- a/examples/cpp/spokes.h +++ b/examples/cpp/spokes.h @@ -6,7 +6,7 @@ #pragma once -#include <math.h> +#include <cmath> #include "lib/color.h" #include "lib/effect.h" #include "lib/noise.h"