--- /dev/null
+temp
+/libmspdemoscene.a
+/libmspdemoscene.so
--- /dev/null
+package "mspdemoscene"
+{
+ require "mspcore";
+ require "mspdatafile";
+ require "mspgui";
+ require "mspgl";
+ require "mspal";
+
+ library "mspdemoscene"
+ {
+ source "source";
+ };
+};
--- /dev/null
+#include <msp/strings/format.h>
+#include "beatcounter.h"
+
+using namespace Msp;
+
+BeatCounter::BeatCounter(DataFile::Collection &resources):
+ text(resources.get<GL::Font>("ikarius-48.font"), &resources.get<GL::Technique>("basic_text.tech")),
+ instance(text)
+{
+ text.set_alignment(GL::Text::CENTER);
+ instance.set_matrix(GL::Matrix::scaling(0.02f));
+}
+
+void BeatCounter::beat(int b)
+{
+ text.set_text(format("%d / %d", b/4, b%4));
+}
+
+void BeatCounter::render(GL::Renderer &renderer, const GL::Tag &tag) const
+{
+ instance.render(renderer, tag);
+}
--- /dev/null
+#ifndef MSP_DEMOSCENE_BEATCOUNTER_H_
+#define MSP_DEMOSCENE_BEATCOUNTER_H_
+
+#include <msp/datafile/collection.h>
+#include <msp/gl/objectinstance.h>
+#include <msp/gl/renderable.h>
+#include <msp/gl/text.h>
+#include "sequencer.h"
+
+class BeatCounter: public Msp::GL::Renderable, public Sequencer::Action
+{
+private:
+ Msp::GL::Text text;
+ Msp::GL::ObjectInstance instance;
+
+public:
+ BeatCounter(Msp::DataFile::Collection &);
+
+ virtual void beat(int);
+
+ virtual void render(Msp::GL::Renderer &, const Msp::GL::Tag &) const;
+};
+
+#endif
--- /dev/null
+#include "cameracontrol.h"
+
+using namespace Msp;
+
+void CameraControl::set_camera(const GL::Camera &c)
+{
+ camera.set_object_matrix(c.get_object_matrix());
+ camera.set_up_direction(c.get_up_direction());
+ camera.set_field_of_view(c.get_field_of_view());
+ camera.set_depth_clip(c.get_near_clip(), c.get_far_clip());
+}
+
+void CameraControl::animate_camera(const GL::Animation &anim, float speed)
+{
+ anim_player.play(camera, anim, speed);
+}
+
+void CameraControl::tick(float, float d)
+{
+ anim_player.tick(d*Time::sec);
+}
+
+
+CameraControl::SetCamera::SetCamera(CameraControl &cc, const GL::Camera &c):
+ control(cc),
+ camera(c)
+{ }
+
+void CameraControl::SetCamera::start(float, float)
+{
+ control.set_camera(camera);
+}
+
+
+CameraControl::AnimateCamera::AnimateCamera(CameraControl &cc, const GL::Animation &a):
+ control(cc),
+ anim(a)
+{ }
+
+void CameraControl::AnimateCamera::start(float, float d)
+{
+ control.animate_camera(anim, (d ? (anim.get_duration()/Time::sec)/d : 1.0f));
+}
--- /dev/null
+#ifndef MSP_DEMOSCENE_CAMERACONTROL_H_
+#define MSP_DEMOSCENE_CAMERACONTROL_H_
+
+#include <msp/gl/animationplayer.h>
+#include <msp/gl/camera.h>
+#include "sequencer.h"
+
+class CameraControl: public Sequencer::Action
+{
+public:
+ class SetCamera: public Sequencer::Action
+ {
+ private:
+ CameraControl &control;
+ const Msp::GL::Camera &camera;
+
+ public:
+ SetCamera(CameraControl &, const Msp::GL::Camera &);
+
+ virtual void start(float, float);
+ };
+
+ class AnimateCamera: public Sequencer::Action
+ {
+ private:
+ CameraControl &control;
+ const Msp::GL::Animation &anim;
+
+ public:
+ AnimateCamera(CameraControl &, const Msp::GL::Animation &);
+
+ virtual void start(float, float);
+ };
+
+private:
+ Msp::GL::Camera camera;
+ Msp::GL::AnimationPlayer anim_player;
+
+public:
+ void set_camera(const Msp::GL::Camera &);
+ void animate_camera(const Msp::GL::Animation &, float);
+ Msp::GL::Camera &get_camera() { return camera; }
+
+ virtual void tick(float, float);
+};
+
+#endif
--- /dev/null
+#include "colorfade.h"
+
+using namespace Msp;
+
+ColorFadeAction::ColorFadeAction(const GL::Color &c):
+ Sequencer::InterpolationAction(true),
+ end_color(c)
+{ }
+
+void ColorFadeAction::start(float b, float d)
+{
+ start_color = get_color();
+ if(!start_color.a)
+ start_color = GL::Color(end_color.r, end_color.g, end_color.b, 0.0f);
+ if(!end_color.a)
+ end_color = GL::Color(start_color.r, start_color.g, start_color.b, 0.0f);
+ InterpolationAction::start(b, d);
+}
+
+void ColorFadeAction::interpolate(float t, float)
+{
+ GL::Color c = start_color*(1-t)+end_color*t;
+ c.a = start_color.a*(1-t)+end_color.a*t;
+ set_color(c);
+}
--- /dev/null
+#ifndef MSP_DEMOSCENE_COLORFADE_H_
+#define MSP_DEMOSCENE_COLORFADE_H_
+
+#include <msp/gl/color.h>
+#include "sequencer.h"
+
+class ColorFadeAction: public Sequencer::InterpolationAction
+{
+protected:
+ Msp::GL::Color start_color;
+ Msp::GL::Color end_color;
+
+ ColorFadeAction(const Msp::GL::Color &);
+
+public:
+ virtual void start(float, float);
+ virtual void interpolate(float, float);
+ virtual const Msp::GL::Color &get_color() const = 0;
+ virtual void set_color(const Msp::GL::Color &) = 0;
+};
+
+#endif
--- /dev/null
+#include <msp/time/utils.h>
+#include "demo.h"
+
+using namespace std;
+using namespace Msp;
+
+Demo::Demo(Graphics::Window &window, Graphics::GLContext &gl_ctx, DataFile::Collection &r):
+ resources(r),
+ view(window, gl_ctx),
+ music_source(0),
+ streamer(0),
+ music_io(0),
+ music_decoder(0)
+{
+ sequencer.signal_finished.connect(signal_finished);
+}
+
+Demo::~Demo()
+{
+ delete streamer;
+ delete music_decoder;
+ delete music_io;
+ delete music_source;
+}
+
+void Demo::set_fixed_framerate(float fps)
+{
+ frame_interval = Time::sec/fps;
+}
+
+void Demo::enable_music()
+{
+ if(!music_source)
+ music_source = new AL::Source;
+ if(!streamer)
+ streamer = new AL::Streamer(*music_source);
+}
+
+void Demo::play_music(const string &fn)
+{
+ if(!streamer)
+ return;
+
+ streamer->stop();
+ delete music_decoder;
+ delete music_io;
+ music_io = resources.open_raw(fn);
+ music_decoder = AL::SoundDecoder::open_io(*music_io);
+ float beat = sequencer.get_current_beat();
+ if(beat)
+ {
+ float time = beat*60/sequencer.get_beats_per_minute();
+ unsigned bytes_per_sec = music_decoder->get_frequency()*get_unit_size(music_decoder->get_format());
+ music_decoder->seek(time*bytes_per_sec);
+ }
+ streamer->play(*music_decoder);
+}
+
+void Demo::tick()
+{
+ Time::TimeStamp t = Time::now();
+ Time::TimeDelta dt = (last_tick ? frame_interval ? frame_interval : t-last_tick : Time::zero);
+ last_tick = t;
+
+ if(frame_interval)
+ next_frame = t+frame_interval;
+
+ sequencer.tick(dt);
+ if(streamer)
+ streamer->tick();
+ view.render();
+}
+
+void Demo::seek(const Time::TimeDelta &pos)
+{
+ sequencer.seek(pos*sequencer.get_beats_per_minute()/Time::min);
+}
--- /dev/null
+#ifndef MSP_DEMOSCENE_DEMO_H_
+#define MSP_DEMOSCENE_DEMO_H_
+
+#include <sigc++/signal.h>
+#include <msp/al/sounddecoder.h>
+#include <msp/al/source.h>
+#include <msp/al/streamer.h>
+#include <msp/datafile/collection.h>
+#include <msp/gl/windowview.h>
+#include "sequencer.h"
+
+class Demo
+{
+public:
+ sigc::signal<void> signal_finished;
+
+protected:
+ Sequencer sequencer;
+ Msp::DataFile::Collection &resources;
+ Msp::GL::WindowView view;
+ Msp::AL::Source *music_source;
+ Msp::AL::Streamer *streamer;
+ Msp::IO::Seekable *music_io;
+ Msp::AL::SoundDecoder *music_decoder;
+ Msp::Time::TimeDelta frame_interval;
+ Msp::Time::TimeStamp last_tick;
+ Msp::Time::TimeStamp next_frame;
+
+ Demo(Msp::Graphics::Window &, Msp::Graphics::GLContext &, Msp::DataFile::Collection &);
+public:
+ virtual ~Demo();
+
+ void set_fixed_framerate(float);
+ const Msp::Time::TimeStamp &get_next_frame_time() const { return next_frame; }
+ void enable_music();
+ void play_music(const std::string &);
+ virtual void tick();
+ void seek(const Msp::Time::TimeDelta &);
+};
+
+
+#endif
--- /dev/null
+#include <msp/datafile/collection.h>
+#include <msp/gl/renderer.h>
+#include <msp/gl/texture2d.h>
+#include "fadeoverlay.h"
+
+using namespace Msp;
+
+FadeOverlay::FadeOverlay(DataFile::Collection &resources):
+ mesh(get_fullscreen_quad()),
+ shprog(resources.get<GL::Program>("fade.glsl")),
+ color(0.0f, 0.0f, 0.0f, 0.0f)
+{
+ shdata.uniform("color", color);
+}
+
+void FadeOverlay::set_color(const GL::Color &c)
+{
+ color = c;
+ shdata.uniform("color", color);
+}
+
+void FadeOverlay::render(GL::Renderer &renderer, const GL::Texture2D &color_buf, const GL::Texture2D &)
+{
+ GL::Renderer::Push push(renderer);
+ renderer.set_shader_program(&shprog, &shdata);
+ renderer.set_texture(&color_buf);
+ mesh.draw(renderer);
+}
+
+
+FadeOverlay::Fade::Fade(FadeOverlay &o, const GL::Color &c):
+ ColorFadeAction(c),
+ overlay(o)
+{ }
--- /dev/null
+#ifndef MSP_DEMOSCENE_FADEOVERLAY_H_
+#define MSP_DEMOSCENE_FADEOVERLAY_H_
+
+#include <msp/gl/color.h>
+#include <msp/gl/mesh.h>
+#include <msp/gl/postprocessor.h>
+#include <msp/gl/program.h>
+#include <msp/gl/programdata.h>
+#include "colorfade.h"
+#include "sequencer.h"
+
+class FadeOverlay: public Msp::GL::PostProcessor
+{
+public:
+ class Fade: public ColorFadeAction
+ {
+ private:
+ FadeOverlay &overlay;
+
+ public:
+ Fade(FadeOverlay &, const Msp::GL::Color &);
+
+ virtual const Msp::GL::Color &get_color() const { return overlay.color; }
+ virtual void set_color(const Msp::GL::Color &c) { overlay.set_color(c); }
+ };
+
+private:
+ const Msp::GL::Mesh &mesh;
+ const Msp::GL::Program &shprog;
+ Msp::GL::ProgramData shdata;
+ Msp::GL::Color color;
+
+public:
+ FadeOverlay(Msp::DataFile::Collection &);
+
+ void set_content(Msp::GL::Renderable *);
+ void set_color(const Msp::GL::Color &);
+
+ virtual void render(Msp::GL::Renderer &, const Msp::GL::Texture2D &, const Msp::GL::Texture2D &);
+};
+
+#endif
--- /dev/null
+#include <cmath>
+#include <msp/core/getopt.h>
+#include <msp/input/keys.h>
+#include <msp/io/console.h>
+#include <msp/io/file.h>
+#include <msp/strings/regex.h>
+#include <msp/time/utils.h>
+#include "demo.h"
+#include "launcher.h"
+#include "launchscreen.h"
+
+using namespace std;
+using namespace Msp;
+
+LauncherBase::Options::Options(Graphics::Display &dpy, int argc, char **argv):
+ start_fullscreen(-1),
+ framerate(0),
+ no_music(false),
+ no_vsync(false)
+{
+ unsigned fullscreen = 0;
+ string fullscreen_size;
+ unsigned windowed = 0;
+ string windowed_size;
+ float seek_seconds = 0.0f;
+
+ GetOpt getopt;
+ getopt.add_option('f', "fullscreen", fullscreen_size, GetOpt::OPTIONAL_ARG).bind_seen_count(fullscreen).set_help("Start in fullscreen mode", "SIZE");
+ getopt.add_option('w', "windowed", windowed_size, GetOpt::OPTIONAL_ARG).bind_seen_count(windowed).set_help("Start in windowed mode without delay", "SIZE");
+#ifdef WITH_DEVELOPER
+ getopt.add_option('s', "seek", seek_seconds, GetOpt::REQUIRED_ARG).set_help("Start at TIME seconds", "TIME");
+ getopt.add_option("record-frames", frame_dump_fn, GetOpt::REQUIRED_ARG).set_help("Record raw frame data to FILE", "FILE");
+#endif
+ getopt.add_option("no-music", no_music, GetOpt::NO_ARG).set_help("Disable music playback");
+ getopt.add_option("fps", framerate, GetOpt::REQUIRED_ARG).set_help("Run at NUM fps", "NUM");
+ getopt.add_option("no-vsync", no_vsync, GetOpt::NO_ARG).set_help("Disable vertical sync");
+ getopt(argc, argv);
+
+ if(fullscreen && windowed)
+ throw usage_error("--fullscreen and --windowed are exclusive");
+
+ if(fullscreen)
+ start_fullscreen = 1;
+ else if(windowed)
+ start_fullscreen = 0;
+
+ const Graphics::VideoMode &desktop_mode = dpy.get_desktop_mode();
+ if(!windowed_size.empty())
+ parse_size(windowed_size, win_opts);
+ else if(desktop_mode.width>1920 && desktop_mode.height>1080)
+ {
+ win_opts.width = 1920;
+ win_opts.height = 1080;
+ }
+ else
+ {
+ win_opts.width = 1280;
+ win_opts.height = 720;
+ }
+
+ if(!fullscreen_size.empty())
+ parse_size(fullscreen_size, fullscreen_opts);
+ else
+ {
+ fullscreen_opts.width = desktop_mode.width;
+ fullscreen_opts.height = desktop_mode.height;
+ }
+ fullscreen_opts.fullscreen = true;
+ fullscreen_opts.fullscreen_monitor = desktop_mode.monitor;
+ fullscreen_opts.fullscreen_exclusive = false;
+
+ seek = seek_seconds*Time::sec;
+}
+
+void LauncherBase::Options::parse_size(const string &size, Graphics::WindowOptions &win_opts)
+{
+ static Regex r_size("^([0-9]+)x([0-9]+)$");
+ RegMatch m = r_size.match(size);
+ if(!m)
+ throw invalid_argument("Options::parse_size");
+
+ win_opts.width = lexical_cast<unsigned>(m[1].str);
+ win_opts.height = lexical_cast<unsigned>(m[2].str);
+}
+
+
+LauncherBase::LauncherBase(int argc, char **argv):
+ options(display, argc, argv),
+ window(display, options.start_fullscreen>0 ? options.fullscreen_opts : options.win_opts),
+ gl_context(window),
+ keyboard(window),
+ al_device(options.no_music ? 0 : new AL::Device),
+ al_context(options.no_music ? 0 : new AL::Context(*al_device)),
+ launch_screen(0),
+ demo(0),
+ frame_dump(0),
+ frame_size(0),
+ frame_dump_buffer(0)
+{
+ gl_context.set_swap_interval(options.no_vsync ? 0 : 1);
+ window.signal_close.connect(sigc::bind(sigc::mem_fun(this, &LauncherBase::exit), 0));
+ keyboard.signal_button_press.connect(sigc::bind_return(sigc::mem_fun(this, &LauncherBase::key_press), false));
+}
+
+LauncherBase::~LauncherBase()
+{
+ delete launch_screen;
+ delete demo;
+ delete[] frame_dump_buffer;
+ delete al_context;
+ delete al_device;
+}
+
+void LauncherBase::start()
+{
+ window.show();
+ if(options.start_fullscreen>=0)
+ start_demo(options.start_fullscreen);
+ else
+ {
+ launch_screen = new LaunchScreen(get_resources());
+ timeout = Time::now()+5*Time::sec;
+ }
+}
+
+void LauncherBase::tick()
+{
+ if(demo)
+ tick_demo();
+ else if(launch_screen)
+ tick_launch_screen();
+}
+
+void LauncherBase::tick_demo()
+{
+ Time::TimeStamp t = Time::now();
+ if(timeout)
+ {
+ if(t<timeout)
+ return;
+ timeout = Time::TimeStamp();
+ }
+
+ if(!options.no_vsync && t<demo->get_next_frame_time())
+ return;
+
+ display.tick();
+ demo->tick();
+
+ if(frame_dump)
+ {
+ glReadPixels(0, 0, window.get_width(), window.get_height(), GL_RGB, GL_UNSIGNED_BYTE, frame_dump_buffer);
+ frame_dump->write(frame_dump_buffer, frame_size);
+ }
+}
+
+void LauncherBase::tick_launch_screen()
+{
+ Time::TimeStamp t = Time::now();
+ Time::TimeDelta time_left = timeout-t;
+ if(time_left>Time::zero)
+ launch_screen->set_countdown(ceil(time_left/Time::sec));
+ else
+ start_demo(true);
+
+ display.tick();
+ launch_screen->render();
+ gl_context.swap_buffers();
+
+ if(demo)
+ {
+ delete launch_screen;
+ launch_screen = 0;
+ }
+}
+
+void LauncherBase::start_demo(bool fullscreen)
+{
+ if(fullscreen)
+ {
+ window.reconfigure(options.fullscreen_opts);
+ window.show_cursor(false);
+ timeout = Time::now()+Time::sec;
+ }
+ else
+ timeout = Time::TimeStamp();
+
+ demo = create_demo();
+ if(!options.no_music)
+ demo->enable_music();
+ if(options.framerate)
+ demo->set_fixed_framerate(options.framerate);
+ if(options.seek)
+ demo->seek(options.seek);
+ demo->signal_finished.connect(sigc::bind(sigc::mem_fun(this, &LauncherBase::exit), 0));
+
+ if(!options.frame_dump_fn.empty())
+ {
+ if(options.frame_dump_fn=="-")
+ frame_dump = &IO::cout;
+ else
+ frame_dump = new IO::File(options.frame_dump_fn, IO::M_WRITE);
+ frame_size = window.get_width()*window.get_height()*3;
+ frame_dump_buffer = new char[frame_size];
+ }
+}
+
+void LauncherBase::key_press(unsigned key)
+{
+ if(key==Input::KEY_ENTER && !demo)
+ start_demo(false);
+ else if(key==Input::KEY_ESC)
+ exit(0);
+}
--- /dev/null
+#ifndef MSP_DEMOSCENE_LAUNCHER_H_
+#define MSP_DEMOSCENE_LAUNCHER_H_
+
+#include <msp/al/context.h>
+#include <msp/al/device.h>
+#include <msp/core/application.h>
+#include <msp/datafile/collection.h>
+#include <msp/graphics/display.h>
+#include <msp/graphics/glcontext.h>
+#include <msp/graphics/window.h>
+#include <msp/input/keyboard.h>
+#include <msp/io/base.h>
+#include <msp/time/timedelta.h>
+#include <msp/time/timestamp.h>
+
+class Demo;
+class LaunchScreen;
+
+class LauncherBase
+{
+protected:
+ struct Options
+ {
+ Msp::Graphics::WindowOptions win_opts;
+ Msp::Graphics::WindowOptions fullscreen_opts;
+ int start_fullscreen;
+ std::string frame_dump_fn;
+ float framerate;
+ Msp::Time::TimeDelta seek;
+ bool no_music;
+ bool no_vsync;
+
+ Options(Msp::Graphics::Display &, int, char **);
+
+ static void parse_size(const std::string &, Msp::Graphics::WindowOptions &);
+ };
+
+ Msp::Graphics::Display display;
+ Options options;
+ Msp::Graphics::Window window;
+ Msp::Graphics::GLContext gl_context;
+ Msp::Input::Keyboard keyboard;
+ Msp::AL::Device *al_device;
+ Msp::AL::Context *al_context;
+
+ LaunchScreen *launch_screen;
+ Msp::Time::TimeStamp timeout;
+
+ Demo *demo;
+ Msp::Time::TimeStamp next_frame;
+ Msp::IO::Base *frame_dump;
+ unsigned frame_size;
+ char *frame_dump_buffer;
+
+public:
+ LauncherBase(int, char **);
+ ~LauncherBase();
+
+protected:
+ virtual Msp::DataFile::Collection &get_resources() = 0;
+ virtual Demo *create_demo() = 0;
+ virtual void start();
+ virtual void tick();
+ void tick_launch_screen();
+ virtual void tick_demo();
+
+ void start_demo(bool);
+ void key_press(unsigned);
+
+ virtual void exit(int) = 0;
+};
+
+template<typename T>
+class Launcher: public Msp::RegisteredApplication<T>, public LauncherBase
+{
+protected:
+ Launcher(int argc, char **argv): LauncherBase(argc, argv) { }
+
+ virtual int main() { start(); return Msp::Application::main(); }
+ virtual void tick() { LauncherBase::tick(); }
+ virtual void exit(int c) { Msp::Application::exit(c); }
+};
+
+#endif
--- /dev/null
+#include <msp/gl/blend.h>
+#include <msp/gl/framebuffer.h>
+#include <msp/gl/renderer.h>
+#include <msp/strings/format.h>
+#include "launchscreen.h"
+
+using namespace Msp;
+
+LaunchScreen::LaunchScreen(DataFile::Collection &resources):
+ font(resources.get<GL::Font>("ikarius-48.font")),
+ tech(resources.get<GL::Technique>("basic_text.tech")),
+ countdown(font, &tech),
+ countdown_value(0),
+ enter_prompt(font, &tech),
+ esc_prompt(font, &tech)
+{
+ countdown.set_alignment(GL::Text::CENTER);
+
+ enter_prompt.set_alignment(GL::Text::CENTER);
+ enter_prompt.set_text("Press ENTER now to remain windowed");
+
+ esc_prompt.set_alignment(GL::Text::CENTER);
+ esc_prompt.set_text("Press ESC at any time to exit");
+
+ camera.set_orthographic(1920, 1080);
+ camera.set_depth_clip(-1, 1);
+}
+
+void LaunchScreen::set_countdown(unsigned c)
+{
+ if(c!=countdown_value)
+ {
+ countdown_value = c;
+ countdown.set_text(format("Going fullscreen in %d...", countdown_value));
+ }
+}
+
+void LaunchScreen::render()
+{
+ GL::Framebuffer::system().clear(GL::COLOR_BUFFER_BIT);
+ GL::Bind blend(GL::Blend::alpha());
+ GL::Renderer renderer(&camera);
+ renderer.transform(GL::Matrix().scale(48).translate(0, 1.2, 0));
+ countdown.render(renderer);
+ renderer.transform(GL::Matrix::translation(0, -1.2, 0));
+ enter_prompt.render(renderer);
+ renderer.transform(GL::Matrix::translation(0, -1.2, 0));
+ esc_prompt.render(renderer);
+}
--- /dev/null
+#ifndef MSP_DEMOSCENE_LAUNCHSCREEN_H_
+#define MSP_DEMOSCENE_LAUNCHSCREEN_H_
+
+#include <msp/datafile/collection.h>
+#include <msp/gl/camera.h>
+#include <msp/gl/font.h>
+#include <msp/gl/technique.h>
+#include <msp/gl/text.h>
+
+class LaunchScreen
+{
+private:
+ const Msp::GL::Font &font;
+ const Msp::GL::Technique &tech;
+ Msp::GL::Text countdown;
+ unsigned countdown_value;
+ Msp::GL::Text enter_prompt;
+ Msp::GL::Text esc_prompt;
+ Msp::GL::Camera camera;
+
+public:
+ LaunchScreen(Msp::DataFile::Collection &);
+
+ void set_countdown(unsigned);
+
+ void render();
+};
+
+#endif
--- /dev/null
+#include <msp/fs/stat.h>
+#include "resources.h"
+
+using namespace std;
+using namespace Msp;
+
+Resources::Resources(const string &pack_name)
+{
+ set_default_texture_anisotropy(8);
+ set_srgb_conversion(true);
+
+ if(FS::exists("data"))
+ {
+ dir_source.add_directory("data");
+ add_source(dir_source);
+ }
+ else
+ {
+ pack_source.add_pack_file(pack_name);
+ add_source(pack_source);
+ }
+}
--- /dev/null
+#ifndef MSP_DEMOSCENE_RESOURCES_H_
+#define MSP_DEMOSCENE_RESOURCES_H_
+
+#include <msp/datafile/directorysource.h>
+#include <msp/datafile/packsource.h>
+#include <msp/gl/resources.h>
+
+class Resources: public Msp::GL::Resources
+{
+private:
+ Msp::DataFile::DirectorySource dir_source;
+ Msp::DataFile::PackSource pack_source;
+
+public:
+ Resources(const std::string &);
+};
+
+#endif
--- /dev/null
+#include <cmath>
+#include <msp/core/algorithm.h>
+#include "sequencer.h"
+
+using namespace std;
+using namespace Msp;
+
+Sequencer::Sequencer(float bpm):
+ started(false),
+ beat(0),
+ next_event(0)
+{
+ set_beats_per_minute(bpm);
+}
+
+void Sequencer::set_beats_per_minute(float bpm)
+{
+ secs_per_beat = Time::min/bpm;
+}
+
+void Sequencer::add_static_action(Action &act)
+{
+ static_actions.push_back(&act);
+}
+
+void Sequencer::add_action(Action &act, float sb, float eb)
+{
+ if(sb<0 || sb>eb)
+ throw invalid_argument("Sequencer::add_action");
+
+ Segment seq_act;
+ seq_act.action = &act;
+ seq_act.start_beat = sb;
+ seq_act.end_beat = eb;
+ auto i = find_if(segments, [=](const Segment &s){ return s.start_beat>sb; });
+ segments.insert(i, seq_act);
+}
+
+void Sequencer::start()
+{
+ started = true;
+ end = begin = segments.begin();
+ for(auto a: static_actions)
+ a->start(0, numeric_limits<float>::max());
+ float start_beat = beat;
+ beat = -1;
+ advance_to(start_beat);
+}
+
+void Sequencer::seek(float b)
+{
+ if(b<beat)
+ throw logic_error("Sequencer::seek");
+
+ if(started)
+ advance_to(b);
+ else
+ beat = b;
+}
+
+void Sequencer::tick(const Time::TimeDelta &dt)
+{
+ if(!started)
+ start();
+ if(begin==segments.end())
+ return;
+
+ advance_to(beat+dt/secs_per_beat);
+}
+
+void Sequencer::advance_to(float target_beat)
+{
+ while(target_beat>next_event)
+ advance_to(next_event);
+
+ float prev_beat = beat;
+ beat = target_beat;
+
+ while(end!=segments.end() && end->start_beat<=beat)
+ ++end;
+
+ int ibeat = static_cast<int>(floor(beat));
+ bool do_beat = (ibeat!=static_cast<int>(floor(prev_beat)));
+
+ for(auto a: static_actions)
+ {
+ if(do_beat)
+ a->beat(ibeat);
+ a->tick(beat, beat-prev_beat);
+ }
+
+ for(auto i=begin; i!=end; ++i)
+ {
+ if(i->start_beat>prev_beat)
+ i->action->start(i->start_beat, i->end_beat-i->start_beat);
+ if(do_beat)
+ i->action->beat(ibeat);
+ if(i->end_beat>=prev_beat)
+ {
+ float tick_beat = min(beat, i->end_beat);
+ i->action->tick(tick_beat, tick_beat-max(prev_beat, i->start_beat));
+ if(i->end_beat<=beat)
+ i->action->end(i->end_beat);
+ }
+ }
+
+ while(begin!=end && begin->end_beat<=beat)
+ ++begin;
+
+ if(target_beat>=next_event)
+ update_next_event();
+
+ if(begin==segments.end())
+ signal_finished.emit();
+}
+
+void Sequencer::update_next_event()
+{
+ next_event = numeric_limits<float>::max();
+
+ if(end!=segments.end())
+ next_event = min(next_event, end->start_beat);
+
+ for(auto i=begin; i!=end; ++i)
+ if(i->end_beat>beat)
+ next_event = min(next_event, i->end_beat);
+}
+
+
+Sequencer::InterpolationAction::InterpolationAction(bool h):
+ hermite(h),
+ start_beat(0),
+ duration(0)
+{ }
+
+void Sequencer::InterpolationAction::start(float b, float d)
+{
+ start_beat = b;
+ duration = d;
+ interpolate(0.0f, 0.0f);
+}
+
+void Sequencer::InterpolationAction::tick(float b, float d)
+{
+ if(duration)
+ {
+ float t = (b-start_beat)/duration;
+ float dt = d/duration;
+ if(hermite)
+ {
+ dt = t-dt;
+ t = (3-2*t)*t*t;
+ dt = t-(3-2*dt)*dt*dt;
+ }
+ interpolate(t, dt);
+ }
+ else
+ interpolate(1.0f, 1.0f);
+}
+
+void Sequencer::InterpolationAction::end(float)
+{
+ interpolate(1.0f, 0.0f);
+}
--- /dev/null
+#ifndef MSP_DEMOSCENE_SEQUENCER_H_
+#define MSP_DEMOSCENE_SEQUENCER_H_
+
+#include <vector>
+#include <sigc++/signal.h>
+#include <msp/time/timedelta.h>
+#include <msp/time/timestamp.h>
+
+class Sequencer
+{
+public:
+ class Action
+ {
+ protected:
+ Action() { }
+ public:
+ virtual ~Action() { }
+
+ virtual void start(float, float) { }
+ virtual void beat(int) { }
+ virtual void tick(float, float) { }
+ virtual void end(float) { }
+ };
+
+ class InterpolationAction: public Action
+ {
+ protected:
+ bool hermite;
+ float start_beat;
+ float duration;
+
+ InterpolationAction(bool = false);
+
+ public:
+ virtual void start(float, float);
+ virtual void tick(float, float);
+ virtual void end(float);
+ virtual void interpolate(float, float) { }
+ };
+
+private:
+ struct Segment
+ {
+ Action *action;
+ float start_beat;
+ float end_beat;
+ };
+
+public:
+ sigc::signal<void> signal_finished;
+
+private:
+ Msp::Time::TimeDelta secs_per_beat;
+ std::vector<Action *> static_actions;
+ std::vector<Segment> segments;
+ std::vector<Segment>::const_iterator begin;
+ std::vector<Segment>::const_iterator end;
+ bool started;
+ float beat;
+ float next_event;
+
+public:
+ Sequencer(float = 120.0f);
+
+ void set_beats_per_minute(float);
+ float get_beats_per_minute() const { return Msp::Time::min/secs_per_beat; }
+ void add_static_action(Action &);
+ void add_action(Action &, float, float);
+
+ void start();
+ void seek(float);
+ void tick(const Msp::Time::TimeDelta &);
+private:
+ void advance_to(float);
+ void update_next_event();
+public:
+ float get_current_beat() const { return beat; }
+};
+
+#endif
--- /dev/null
+#include <msp/gl/view.h>
+#include "stage.h"
+
+using namespace Msp;
+
+Stage::Stage():
+ pipeline(0)
+{ }
+
+Stage::~Stage()
+{
+ delete pipeline;
+}
+
+
+Stage::UseInView::UseInView(GL::View &v, Stage &s):
+ view(v),
+ stage(s)
+{ }
+
+void Stage::UseInView::start(float, float)
+{
+ view.set_camera(&stage.camera_control.get_camera());
+ view.set_content(stage.pipeline);
+}
--- /dev/null
+#ifndef MSP_DEMOSCENE_STAGE_H_
+#define MSP_DEMOSCENE_STAGE_H_
+
+#include <msp/gl/pipeline.h>
+#include "cameracontrol.h"
+#include "sequencer.h"
+
+struct Stage
+{
+ class UseInView: public Sequencer::Action
+ {
+ private:
+ Msp::GL::View &view;
+ Stage &stage;
+
+ public:
+ UseInView(Msp::GL::View &, Stage &);
+
+ virtual void start(float, float);
+ };
+
+ Msp::GL::Pipeline *pipeline;
+ CameraControl camera_control;
+
+ Stage();
+ ~Stage();
+};
+
+#endif