diff --git a/BPmono.ttf b/BPmono.ttf new file mode 100755 index 0000000..e6bb690 Binary files /dev/null and b/BPmono.ttf differ diff --git a/src/Box.cpp b/src/Box.cpp index 5e9ba12..1a7323b 100644 --- a/src/Box.cpp +++ b/src/Box.cpp @@ -107,6 +107,11 @@ glm::vec2 Box::get_north() const return glm::vec2(get_x() + get_w() / 2, get_y()); } +glm::vec2 Box::get_ne() const +{ + return glm::vec2(get_right(), get_y()); +} + glm::vec2 Box::get_east() const { return glm::vec2(get_right(), get_y() + get_h() / 2); @@ -138,6 +143,11 @@ void Box::set_north(const glm::vec2& n) move(n - get_north()); } +void Box::set_ne(const glm::vec2& ne) +{ + move(ne - get_ne()); +} + void Box::set_east(const glm::vec2& e) { move(e - get_east()); diff --git a/src/Box.hpp b/src/Box.hpp index 330ed56..86ed636 100644 --- a/src/Box.hpp +++ b/src/Box.hpp @@ -34,12 +34,14 @@ struct Box void set_left(float); glm::vec2 get_nw() const; glm::vec2 get_north() const; + glm::vec2 get_ne() const; glm::vec2 get_east() const; glm::vec2 get_south() const; glm::vec2 get_west() const; glm::vec2 get_center() const; void set_nw(const glm::vec2&); void set_north(const glm::vec2&); + void set_ne(const glm::vec2&); void set_east(const glm::vec2&); void set_south(const glm::vec2&); void set_west(const glm::vec2&); diff --git a/src/Configuration.cpp b/src/Configuration.cpp index 7eb58e4..bbab862 100644 --- a/src/Configuration.cpp +++ b/src/Configuration.cpp @@ -23,7 +23,7 @@ void Configuration::set_defaults() {"left", "left"}, {"pause", "enter"}, {"fullscreen", {"ALT", "enter"}}, - {"show-fps", {"CTRL", "f"}} + {"toggle-framerate", {"CTRL", "f"}} }; sys_config["path"] = { {"screenshots", "."}, @@ -31,7 +31,7 @@ void Configuration::set_defaults() }; sys_config["display"] = { {"dimensions", {640, 480}}, - {"fps", 60}, + {"framerate", 60}, {"title", "sfw"} }; sys_config["recording"] = { diff --git a/src/Display.cpp b/src/Display.cpp index 7e013a0..3d0aba6 100644 --- a/src/Display.cpp +++ b/src/Display.cpp @@ -1,7 +1,10 @@ #include "Display.hpp" #include "Game.hpp" -Display::Display(Node* parent) : Node(parent) {} +Display::Display(Node* parent) : Node(parent) +{ + get_delegate().subscribe(&Display::respond, this); +} glm::ivec2 Display::get_window_size() { @@ -88,3 +91,25 @@ SDL_Surface* Display::get_screen_surface_from_pixels( } return surface; } + +void Display::respond(SDL_Event& event) +{ + if (get_delegate().compare(event, "fullscreen")) + { + toggle_fullscreen(); + } +} + +void Display::toggle_fullscreen() +{ + if (SDL_GetWindowFlags(get_root()->get_window()) & SDL_WINDOW_FULLSCREEN) + { + SDL_Log("fullscreen requested"); + SDL_SetWindowFullscreen(get_root()->window, 0); + } + else + { + SDL_Log("exit fullscreen requested"); + SDL_SetWindowFullscreen(get_root()->get_window(), SDL_WINDOW_FULLSCREEN); + } +} diff --git a/src/Display.hpp b/src/Display.hpp index ade58f9..ed97251 100644 --- a/src/Display.hpp +++ b/src/Display.hpp @@ -28,6 +28,8 @@ struct Display : Node void get_screen_pixels(unsigned char*, int, int, int = 0, int = 0); SDL_Surface* get_screen_surface(); SDL_Surface* get_screen_surface_from_pixels(unsigned char*, bool); + void respond(SDL_Event&); + void toggle_fullscreen(); }; diff --git a/src/Game.cpp b/src/Game.cpp index deb4efd..b3a091d 100644 --- a/src/Game.cpp +++ b/src/Game.cpp @@ -1,16 +1,53 @@ #include "Game.hpp" -// FPSIndicator::FPSIndicator(Node* parent) : Node(parent) {} +FramerateIndicator::FramerateIndicator(Node* parent) : Sprite(parent) +{ + get_delegate().subscribe(&FramerateIndicator::respond, this); + hide(); +} -// FPSIndicator::update() -// { - -// } +void FramerateIndicator::respond(SDL_Event& event) +{ + if (get_delegate().compare(event, "toggle-framerate")) + { + toggle_hidden(); + } +} + +SDL_Surface* FramerateIndicator::get_surface() +{ + std::string padded = sfw::pad(get_root()->frame_count_this_second, 2); + SDL_Surface* shaded = TTF_RenderText_Shaded( + get_root()->bp_mono_font, padded.c_str(), {0, 0, 0}, {255, 255, 255}); + // SDL_Surface* converted = SDL_ConvertSurfaceFormat( + // shaded, SDL_PIXELFORMAT_ARGB8888, 0); + // SDL_Surface* flipped = zoomSurface(converted, 1, -1, SMOOTHING_OFF); + // SDL_FreeSurface(shaded); + // SDL_FreeSurface(converted); + if (!shaded) + { + get_root()->print_sdl_error("Could not create text"); + } + return shaded; +} + +void FramerateIndicator::refresh() +{ + if (!is_hidden() && get_root()->bp_mono_font != NULL) + { + unload(); + SDL_Surface* surface = get_surface(); + SDL_Texture* texture = SDL_CreateTextureFromSurface(get_root()->get_renderer(), surface); + add_frame(texture); + SDL_FreeSurface(surface); + box.set_ne(get_display().get_window_box().get_ne()); + } +} Game::Game() { frame_length_history.reserve(5000); - set_framerate(get_configuration()["display"]["fps"]); + set_framerate(get_configuration()["display"]["framerate"]); delegate.subscribe(&Game::handle_quit_event, this, SDL_QUIT); SDL_Log("GLEW %s", glewGetString(GLEW_VERSION)); putenv("SDL_VIDEO_X11_LEGACY_FULLSCREEN=0"); @@ -58,6 +95,10 @@ Game::Game() SDL_Log("initialized SDL ttf %d.%d.%d", SDL_TTF_MAJOR_VERSION, SDL_TTF_MINOR_VERSION, SDL_TTF_PATCHLEVEL); } + if ((bp_mono_font = TTF_OpenFont("BPmono.ttf", 14)) == NULL) + { + print_error("Could not load BPmono.ttf"); + } #if !defined(__EMSCRIPTEN__) if (Mix_Init(MIX_INIT_FLAC) == 0) { @@ -452,11 +493,23 @@ void Game::frame(float ticks) recorder.update(); delegate.dispatch(); update(); + framerate_indicator.update(); + if (!is_gl_context) + { + SDL_RenderPresent(renderer); + } if (frame_time_overflow > frame_length) { // SDL_Log("%i frame(s) dropped", ((int) (frame_time_overflow / frame_length))); frame_time_overflow = 0; } + frame_count_this_second++; + if (ticks - last_frame_count_timestamp >= 1000) + { + framerate_indicator.refresh(); + last_frame_count_timestamp = ticks; + frame_count_this_second = 0; + } } // std::cout << std::endl; } @@ -485,13 +538,13 @@ glm::vec2 Game::weight(glm::vec2 motion) return glm::vec2(weight(motion.x), weight(motion.y)); } -void Game::set_framerate(int fps) +void Game::set_framerate(int f) { - if (fps < 1) + if (f < 1) { - fps = 1; + f = 1; } - framerate = fps; + framerate = f; frame_length = 1000.0 / framerate; } @@ -519,6 +572,7 @@ void Game::quit() } if (TTF_WasInit()) { + TTF_CloseFont(bp_mono_font); TTF_Quit(); } Mix_CloseAudio(); diff --git a/src/Game.hpp b/src/Game.hpp index 537c2b4..7749be7 100644 --- a/src/Game.hpp +++ b/src/Game.hpp @@ -27,19 +27,19 @@ #endif #include "Node.hpp" -#include "Configuration.hpp" -#include "Delegate.hpp" -#include "Display.hpp" -#include "Recorder.hpp" #include "Input.hpp" -// #include "Sprite.hpp" +#include "Recorder.hpp" +#include "Sprite.hpp" -// struct FPSIndicator : Sprite -// { +struct FramerateIndicator : Sprite +{ -// FPSIndicator(Node*); + FramerateIndicator(Node*); + void respond(SDL_Event&); + SDL_Surface* get_surface(); + void refresh(); -// }; +}; struct Game : Node { @@ -54,9 +54,9 @@ struct Game : Node SDL_GLContext glcontext = NULL; // 768, 432 // 864, 486 - int framerate, ticks, last_frame_length; - float frame_length = 1000.0 / 60.0, frame_time_overflow = 0, - last_frame_timestamp, emscripten_previous_time; + int frame_count_this_second = 0, framerate, ticks, last_frame_length; + float frame_length = 1000.0 / 60.0, frame_time_overflow = 0, last_frame_timestamp, + last_frame_count_timestamp, emscripten_previous_time; bool done = false, show_framerate = true, is_gl_context = true; Configuration configuration = Configuration(this); Delegate delegate = Delegate(this); @@ -64,6 +64,8 @@ struct Game : Node Recorder recorder = Recorder(this); Input input = Input(this); std::vector frame_length_history; + TTF_Font* bp_mono_font = NULL; + FramerateIndicator framerate_indicator = FramerateIndicator(this); Game(); ~Game(); @@ -103,6 +105,4 @@ void loop(void*); #endif -#include "Sprite.hpp" - #endif diff --git a/src/Input.hpp b/src/Input.hpp index df3c5d0..04e75d2 100644 --- a/src/Input.hpp +++ b/src/Input.hpp @@ -9,7 +9,6 @@ #include "SDL.h" #include "Node.hpp" -#include "Delegate.hpp" struct KeyCombination { diff --git a/src/Node.cpp b/src/Node.cpp index 76f6011..a1fe1fe 100644 --- a/src/Node.cpp +++ b/src/Node.cpp @@ -1,7 +1,5 @@ #include "Node.hpp" #include "Game.hpp" -// #include "Display.hpp" -// #include "Delegate.hpp" Node::Node() : Node(NULL) {} diff --git a/src/Node.hpp b/src/Node.hpp index 51c526d..0edab5f 100644 --- a/src/Node.hpp +++ b/src/Node.hpp @@ -11,7 +11,6 @@ struct Game; struct Delegate; struct Display; -struct TimeFilter; struct Node { @@ -35,4 +34,8 @@ struct Node }; +#include "Configuration.hpp" +#include "Delegate.hpp" +#include "Display.hpp" + #endif diff --git a/src/Recorder.cpp b/src/Recorder.cpp index 6b42572..c1c0d2e 100644 --- a/src/Recorder.cpp +++ b/src/Recorder.cpp @@ -1,4 +1,3 @@ -#include "Game.hpp" #include "Recorder.hpp" #include "gif-h/gif.h" diff --git a/src/Recorder.hpp b/src/Recorder.hpp index 4b214e0..b288bfe 100644 --- a/src/Recorder.hpp +++ b/src/Recorder.hpp @@ -6,8 +6,10 @@ #include #include #include +#include #include "SDL.h" +#include "SDL_mixer.h" #define GLM_ENABLE_EXPERIMENTAL #include "glm/ext.hpp" @@ -15,9 +17,8 @@ #include "json/json.hpp" #include "filesystem.hpp" +#include "Node.hpp" #include "Animation.hpp" -#include "Delegate.hpp" -#include "Display.hpp" #include "extension.hpp" struct Stash @@ -67,4 +68,6 @@ struct Recorder : Node void process_audio(void*, Uint8*, int); +#include "Game.hpp" + #endif diff --git a/src/Sprite.cpp b/src/Sprite.cpp index b8b5ef1..8fd914a 100644 --- a/src/Sprite.cpp +++ b/src/Sprite.cpp @@ -136,17 +136,22 @@ void Sprite::advance_frame() void Sprite::hide() { - is_hidden = true; + hidden = true; } void Sprite::unhide() { - is_hidden = false; + hidden = false; } void Sprite::toggle_hidden() { - is_hidden = !is_hidden; + hidden = !hidden; +} + +bool Sprite::is_hidden() const +{ + return hidden; } void Sprite::set_step(glm::vec2 s) @@ -227,7 +232,7 @@ void Sprite::update() move(step); frame_animation.update(); blink_animation.update(); - if (is_loaded() && !is_hidden && get_current_frameset().get_frame_count()) + if (is_loaded() && !is_hidden() && get_current_frameset().get_frame_count()) { int index = get_current_frameset().get_current_frame_index(); SDL_Texture* texture = frames[index]; diff --git a/src/Sprite.hpp b/src/Sprite.hpp index 74797b2..2258f0a 100644 --- a/src/Sprite.hpp +++ b/src/Sprite.hpp @@ -11,11 +11,11 @@ #include #include "Node.hpp" -#include "Game.hpp" #include "Box.hpp" #include "Animation.hpp" #include "extension.hpp" +struct Game; struct Frameset; struct Sprite : Node @@ -26,7 +26,7 @@ struct Sprite : Node Box box; Animation frame_animation = Animation(&Sprite::advance_frame, this); Animation blink_animation = Animation(&Sprite::toggle_hidden, this, 500); - bool is_hidden = false; + bool hidden = false; glm::vec2 step = {0, 0}; Uint8 alpha_mod = 255; std::map framesets; @@ -51,6 +51,7 @@ struct Sprite : Node void hide(); void unhide(); void toggle_hidden(); + bool is_hidden() const; void set_step(glm::vec2); float get_w() const; float get_h() const; @@ -101,4 +102,6 @@ struct Frameset }; +#include "Game.hpp" + #endif diff --git a/src/extension.hpp b/src/extension.hpp index e9138a5..1e6ee57 100644 --- a/src/extension.hpp +++ b/src/extension.hpp @@ -7,6 +7,7 @@ #include #include #include +#include #define GLM_ENABLE_EXPERIMENTAL #include "glm/trigonometric.hpp" @@ -39,6 +40,38 @@ namespace sfw padded << end; return padded.str(); } + + // from https://stackoverflow.com/a/30312659/1256386 + template + std::vector range(IntType start, IntType stop, IntType step) + { + if (step == IntType(0)) + { + throw std::invalid_argument("step for range must be non-zero"); + } + std::vector result; + IntType i = start; + while ((step > 0) ? (i < stop) : (i > stop)) + { + result.push_back(i); + i += step; + } + return result; + } + + // from https://stackoverflow.com/a/30312659/1256386 + template + std::vector range(IntType start, IntType stop) + { + return range(start, stop, IntType(1)); + } + + // from https://stackoverflow.com/a/30312659/1256386 + template + std::vector range(IntType stop) + { + return range(IntType(0), stop, IntType(1)); + } } std::ostream& operator<<(std::ostream&, const glm::vec2&);