From 35332818fc6bad98fe77831de2c51a11326e31aa Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Wed, 13 Mar 2019 20:36:20 +0200 Subject: [PATCH] Initial files lifted from the Skrolliparty 2 demo --- .gitignore | 3 + Build | 13 +++ source/beatcounter.cpp | 22 ++++ source/beatcounter.h | 24 +++++ source/cameracontrol.cpp | 43 ++++++++ source/cameracontrol.h | 47 +++++++++ source/colorfade.cpp | 25 +++++ source/colorfade.h | 22 ++++ source/demo.cpp | 77 ++++++++++++++ source/demo.h | 42 ++++++++ source/fadeoverlay.cpp | 34 +++++++ source/fadeoverlay.h | 42 ++++++++ source/launcher.cpp | 214 +++++++++++++++++++++++++++++++++++++++ source/launcher.h | 84 +++++++++++++++ source/launchscreen.cpp | 49 +++++++++ source/launchscreen.h | 29 ++++++ source/resources.cpp | 22 ++++ source/resources.h | 18 ++++ source/sequencer.cpp | 164 ++++++++++++++++++++++++++++++ source/sequencer.h | 80 +++++++++++++++ source/stage.cpp | 25 +++++ source/stage.h | 29 ++++++ 22 files changed, 1108 insertions(+) create mode 100644 .gitignore create mode 100644 Build create mode 100644 source/beatcounter.cpp create mode 100644 source/beatcounter.h create mode 100644 source/cameracontrol.cpp create mode 100644 source/cameracontrol.h create mode 100644 source/colorfade.cpp create mode 100644 source/colorfade.h create mode 100644 source/demo.cpp create mode 100644 source/demo.h create mode 100644 source/fadeoverlay.cpp create mode 100644 source/fadeoverlay.h create mode 100644 source/launcher.cpp create mode 100644 source/launcher.h create mode 100644 source/launchscreen.cpp create mode 100644 source/launchscreen.h create mode 100644 source/resources.cpp create mode 100644 source/resources.h create mode 100644 source/sequencer.cpp create mode 100644 source/sequencer.h create mode 100644 source/stage.cpp create mode 100644 source/stage.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0a95be1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +temp +/libmspdemoscene.a +/libmspdemoscene.so diff --git a/Build b/Build new file mode 100644 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 index 0000000..cce1959 --- /dev/null +++ b/source/beatcounter.cpp @@ -0,0 +1,22 @@ +#include +#include "beatcounter.h" + +using namespace Msp; + +BeatCounter::BeatCounter(DataFile::Collection &resources): + text(resources.get("ikarius-48.font"), &resources.get("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 index 0000000..856c8d7 --- /dev/null +++ b/source/beatcounter.h @@ -0,0 +1,24 @@ +#ifndef MSP_DEMOSCENE_BEATCOUNTER_H_ +#define MSP_DEMOSCENE_BEATCOUNTER_H_ + +#include +#include +#include +#include +#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 index 0000000..8b30627 --- /dev/null +++ b/source/cameracontrol.cpp @@ -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 index 0000000..455bad6 --- /dev/null +++ b/source/cameracontrol.h @@ -0,0 +1,47 @@ +#ifndef MSP_DEMOSCENE_CAMERACONTROL_H_ +#define MSP_DEMOSCENE_CAMERACONTROL_H_ + +#include +#include +#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 index 0000000..dfe7472 --- /dev/null +++ b/source/colorfade.cpp @@ -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 index 0000000..ce2e0a6 --- /dev/null +++ b/source/colorfade.h @@ -0,0 +1,22 @@ +#ifndef MSP_DEMOSCENE_COLORFADE_H_ +#define MSP_DEMOSCENE_COLORFADE_H_ + +#include +#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 index 0000000..41e9641 --- /dev/null +++ b/source/demo.cpp @@ -0,0 +1,77 @@ +#include +#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 index 0000000..2f40cf8 --- /dev/null +++ b/source/demo.h @@ -0,0 +1,42 @@ +#ifndef MSP_DEMOSCENE_DEMO_H_ +#define MSP_DEMOSCENE_DEMO_H_ + +#include +#include +#include +#include +#include +#include +#include "sequencer.h" + +class Demo +{ +public: + sigc::signal 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 index 0000000..46ebd58 --- /dev/null +++ b/source/fadeoverlay.cpp @@ -0,0 +1,34 @@ +#include +#include +#include +#include "fadeoverlay.h" + +using namespace Msp; + +FadeOverlay::FadeOverlay(DataFile::Collection &resources): + mesh(get_fullscreen_quad()), + shprog(resources.get("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 index 0000000..65a9af2 --- /dev/null +++ b/source/fadeoverlay.h @@ -0,0 +1,42 @@ +#ifndef MSP_DEMOSCENE_FADEOVERLAY_H_ +#define MSP_DEMOSCENE_FADEOVERLAY_H_ + +#include +#include +#include +#include +#include +#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 index 0000000..1563b70 --- /dev/null +++ b/source/launcher.cpp @@ -0,0 +1,214 @@ +#include +#include +#include +#include +#include +#include +#include +#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(m[1].str); + win_opts.height = lexical_cast(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(tget_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 index 0000000..f607c7b --- /dev/null +++ b/source/launcher.h @@ -0,0 +1,84 @@ +#ifndef MSP_DEMOSCENE_LAUNCHER_H_ +#define MSP_DEMOSCENE_LAUNCHER_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 +class Launcher: public Msp::RegisteredApplication, 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 index 0000000..4fd0883 --- /dev/null +++ b/source/launchscreen.cpp @@ -0,0 +1,49 @@ +#include +#include +#include +#include +#include "launchscreen.h" + +using namespace Msp; + +LaunchScreen::LaunchScreen(DataFile::Collection &resources): + font(resources.get("ikarius-48.font")), + tech(resources.get("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 index 0000000..7c4a923 --- /dev/null +++ b/source/launchscreen.h @@ -0,0 +1,29 @@ +#ifndef MSP_DEMOSCENE_LAUNCHSCREEN_H_ +#define MSP_DEMOSCENE_LAUNCHSCREEN_H_ + +#include +#include +#include +#include +#include + +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 index 0000000..dac473a --- /dev/null +++ b/source/resources.cpp @@ -0,0 +1,22 @@ +#include +#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 index 0000000..3e0dbc9 --- /dev/null +++ b/source/resources.h @@ -0,0 +1,18 @@ +#ifndef MSP_DEMOSCENE_RESOURCES_H_ +#define MSP_DEMOSCENE_RESOURCES_H_ + +#include +#include +#include + +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 index 0000000..4acad43 --- /dev/null +++ b/source/sequencer.cpp @@ -0,0 +1,164 @@ +#include +#include +#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::max()); + float start_beat = beat; + beat = -1; + advance_to(start_beat); +} + +void Sequencer::seek(float b) +{ + if(bnext_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(floor(beat)); + bool do_beat = (ibeat!=static_cast(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::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 index 0000000..028c5d5 --- /dev/null +++ b/source/sequencer.h @@ -0,0 +1,80 @@ +#ifndef MSP_DEMOSCENE_SEQUENCER_H_ +#define MSP_DEMOSCENE_SEQUENCER_H_ + +#include +#include +#include +#include + +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 signal_finished; + +private: + Msp::Time::TimeDelta secs_per_beat; + std::vector static_actions; + std::vector segments; + std::vector::const_iterator begin; + std::vector::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 index 0000000..ef5cac4 --- /dev/null +++ b/source/stage.cpp @@ -0,0 +1,25 @@ +#include +#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 index 0000000..f34e6ae --- /dev/null +++ b/source/stage.h @@ -0,0 +1,29 @@ +#ifndef MSP_DEMOSCENE_STAGE_H_ +#define MSP_DEMOSCENE_STAGE_H_ + +#include +#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 -- 2.43.0