]> git.tdb.fi Git - libs/demoscene.git/commitdiff
Initial files lifted from the Skrolliparty 2 demo
authorMikko Rasa <tdb@tdb.fi>
Wed, 13 Mar 2019 18:36:20 +0000 (20:36 +0200)
committerMikko Rasa <tdb@tdb.fi>
Wed, 13 Mar 2019 18:36:20 +0000 (20:36 +0200)
22 files changed:
.gitignore [new file with mode: 0644]
Build [new file with mode: 0644]
source/beatcounter.cpp [new file with mode: 0644]
source/beatcounter.h [new file with mode: 0644]
source/cameracontrol.cpp [new file with mode: 0644]
source/cameracontrol.h [new file with mode: 0644]
source/colorfade.cpp [new file with mode: 0644]
source/colorfade.h [new file with mode: 0644]
source/demo.cpp [new file with mode: 0644]
source/demo.h [new file with mode: 0644]
source/fadeoverlay.cpp [new file with mode: 0644]
source/fadeoverlay.h [new file with mode: 0644]
source/launcher.cpp [new file with mode: 0644]
source/launcher.h [new file with mode: 0644]
source/launchscreen.cpp [new file with mode: 0644]
source/launchscreen.h [new file with mode: 0644]
source/resources.cpp [new file with mode: 0644]
source/resources.h [new file with mode: 0644]
source/sequencer.cpp [new file with mode: 0644]
source/sequencer.h [new file with mode: 0644]
source/stage.cpp [new file with mode: 0644]
source/stage.h [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..0a95be1
--- /dev/null
@@ -0,0 +1,3 @@
+temp
+/libmspdemoscene.a
+/libmspdemoscene.so
diff --git a/Build b/Build
new file mode 100644 (file)
index 0000000..569ce99
--- /dev/null
+++ b/Build
@@ -0,0 +1,13 @@
+package "mspdemoscene"
+{
+       require "mspcore";
+       require "mspdatafile";
+       require "mspgui";
+       require "mspgl";
+       require "mspal";
+
+       library "mspdemoscene"
+       {
+               source "source";
+       };
+};
diff --git a/source/beatcounter.cpp b/source/beatcounter.cpp
new file mode 100644 (file)
index 0000000..cce1959
--- /dev/null
@@ -0,0 +1,22 @@
+#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);
+}
diff --git a/source/beatcounter.h b/source/beatcounter.h
new file mode 100644 (file)
index 0000000..856c8d7
--- /dev/null
@@ -0,0 +1,24 @@
+#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
diff --git a/source/cameracontrol.cpp b/source/cameracontrol.cpp
new file mode 100644 (file)
index 0000000..8b30627
--- /dev/null
@@ -0,0 +1,43 @@
+#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));
+}
diff --git a/source/cameracontrol.h b/source/cameracontrol.h
new file mode 100644 (file)
index 0000000..455bad6
--- /dev/null
@@ -0,0 +1,47 @@
+#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
diff --git a/source/colorfade.cpp b/source/colorfade.cpp
new file mode 100644 (file)
index 0000000..dfe7472
--- /dev/null
@@ -0,0 +1,25 @@
+#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);
+}
diff --git a/source/colorfade.h b/source/colorfade.h
new file mode 100644 (file)
index 0000000..ce2e0a6
--- /dev/null
@@ -0,0 +1,22 @@
+#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
diff --git a/source/demo.cpp b/source/demo.cpp
new file mode 100644 (file)
index 0000000..41e9641
--- /dev/null
@@ -0,0 +1,77 @@
+#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);
+}
diff --git a/source/demo.h b/source/demo.h
new file mode 100644 (file)
index 0000000..2f40cf8
--- /dev/null
@@ -0,0 +1,42 @@
+#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
diff --git a/source/fadeoverlay.cpp b/source/fadeoverlay.cpp
new file mode 100644 (file)
index 0000000..46ebd58
--- /dev/null
@@ -0,0 +1,34 @@
+#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)
+{ }
diff --git a/source/fadeoverlay.h b/source/fadeoverlay.h
new file mode 100644 (file)
index 0000000..65a9af2
--- /dev/null
@@ -0,0 +1,42 @@
+#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
diff --git a/source/launcher.cpp b/source/launcher.cpp
new file mode 100644 (file)
index 0000000..1563b70
--- /dev/null
@@ -0,0 +1,214 @@
+#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);
+}
diff --git a/source/launcher.h b/source/launcher.h
new file mode 100644 (file)
index 0000000..f607c7b
--- /dev/null
@@ -0,0 +1,84 @@
+#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
diff --git a/source/launchscreen.cpp b/source/launchscreen.cpp
new file mode 100644 (file)
index 0000000..4fd0883
--- /dev/null
@@ -0,0 +1,49 @@
+#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);
+}
diff --git a/source/launchscreen.h b/source/launchscreen.h
new file mode 100644 (file)
index 0000000..7c4a923
--- /dev/null
@@ -0,0 +1,29 @@
+#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
diff --git a/source/resources.cpp b/source/resources.cpp
new file mode 100644 (file)
index 0000000..dac473a
--- /dev/null
@@ -0,0 +1,22 @@
+#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);
+       }
+}
diff --git a/source/resources.h b/source/resources.h
new file mode 100644 (file)
index 0000000..3e0dbc9
--- /dev/null
@@ -0,0 +1,18 @@
+#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
diff --git a/source/sequencer.cpp b/source/sequencer.cpp
new file mode 100644 (file)
index 0000000..4acad43
--- /dev/null
@@ -0,0 +1,164 @@
+#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);
+}
diff --git a/source/sequencer.h b/source/sequencer.h
new file mode 100644 (file)
index 0000000..028c5d5
--- /dev/null
@@ -0,0 +1,80 @@
+#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
diff --git a/source/stage.cpp b/source/stage.cpp
new file mode 100644 (file)
index 0000000..ef5cac4
--- /dev/null
@@ -0,0 +1,25 @@
+#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);
+}
diff --git a/source/stage.h b/source/stage.h
new file mode 100644 (file)
index 0000000..f34e6ae
--- /dev/null
@@ -0,0 +1,29 @@
+#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