diff --git a/demo/Demo.cpp b/demo/Demo.cpp index 92f2aa9..1284ec5 100644 --- a/demo/Demo.cpp +++ b/demo/Demo.cpp @@ -11,7 +11,8 @@ dict with metadata, move added sprite locations by offset when location is changed, gradients, level select code input, logging, variable screen resolution, debug display, loading wheel animation, shadowed sprite, separate - update and draw, sprite movement cage, multiple windows, multiple renderers + update and draw, sprite movement cage, multiple windows, multiple renderers, + node children list, node animations list :) SWEATY HANDS :) OILY SNACKS :) AND BAD HYGIENE :) diff --git a/src/Animation.cpp b/src/Animation.cpp index 88857c5..3495667 100644 --- a/src/Animation.cpp +++ b/src/Animation.cpp @@ -44,6 +44,11 @@ void Animation::reset() timer.reset(); } +bool Animation::is_playing() +{ + return playing && !paused; +} + void Animation::update() { timer.update(); diff --git a/src/Animation.hpp b/src/Animation.hpp index 00a7662..1c852e8 100644 --- a/src/Animation.hpp +++ b/src/Animation.hpp @@ -5,11 +5,12 @@ #include #include -#include "Node.hpp" #include "Timer.hpp" typedef std::function callback; +struct Node; + struct Animation { @@ -34,8 +35,11 @@ struct Animation void pause(); void unpause(); void reset(); + bool is_playing(); void update(); }; +#include "Node.hpp" + #endif diff --git a/src/Box.hpp b/src/Box.hpp index c39636b..a29bb03 100644 --- a/src/Box.hpp +++ b/src/Box.hpp @@ -14,7 +14,7 @@ struct Segment; struct Box { - SDL_FRect rect; + SDL_FRect rect = {0, 0, 0, 0}; Box(const glm::vec2& = {0, 0}, const glm::vec2& = {0, 0}); float get_x() const; diff --git a/src/Color.cpp b/src/Color.cpp new file mode 100644 index 0000000..59aa541 --- /dev/null +++ b/src/Color.cpp @@ -0,0 +1,181 @@ +#include "Color.hpp" + +Color::Color() : Color(0, 0, 0, 255) {} + +Color::Color(std::uint8_t r, std::uint8_t g, std::uint8_t b, std::uint8_t a) : r(r), g(g), b(b), a(a) {}; + +Color::Color(const SDL_Color& color) : Color(color.r, color.g, color.b, color.a) {}; + +void Color::set_rgb_float(const float& rf, const float& gf, const float& bf) +{ + r = std::round(255.0f * rf); + g = std::round(255.0f * gf); + b = std::round(255.0f * bf); +} + +void Color::set_hsv(const float& h, const float& s, const float& v) +{ + float rf, gf, bf; + HSVtoRGB(rf, gf, bf, h, s, v); + set_rgb_float(rf, gf, bf); +} + +void Color::shift_hue(float offset) +{ + float h, s, v; + float rf = r / 255.0f, gf = g / 255.0f, bf = b / 255.0f; + RGBtoHSV(rf, gf, bf, h, s, v); + h = std::fmod(h + offset, 360.0); + HSVtoRGB(rf, gf, bf, h, s, v); + set_rgb_float(rf, gf, bf); +} + +SDL_Color Color::get_sdl_color() +{ + return {r, g, b, a}; +} + +std::ostream& operator<<(std::ostream& out, const Color& color) +{ + float h, s, v; + RGBtoHSV(color.r / 255.0f, color.g / 255.0f, color.b / 255.0f, h, s, v); + out << "{r=" << static_cast(color.r) << ", g=" << static_cast(color.g) << ", b=" << + static_cast(color.b) << ", a= " << static_cast(color.a) << ", h=" << h << + ", s=" << s << ", v=" << v << "}"; + return out; +} + +/* + The following copyright applies to RGBtoHSV and HSVtoRGB: +*/ + +// +// Copyright (c) 2014, Jan Winkler +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of Universität Bremen nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +/*! \brief Convert RGB to HSV color space + + Converts a given set of RGB values `r', `g', `b' into HSV + coordinates. The input RGB values are in the range [0, 1], and the + output HSV values are in the ranges h = [0, 360], and s, v = [0, + 1], respectively. + + \param fR Red component, used as input, range: [0, 1] + \param fG Green component, used as input, range: [0, 1] + \param fB Blue component, used as input, range: [0, 1] + \param fH Hue component, used as output, range: [0, 360] + \param fS Hue component, used as output, range: [0, 1] + \param fV Hue component, used as output, range: [0, 1] + +*/ +void RGBtoHSV(const float& fR, const float& fG, const float& fB, float& fH, float& fS, float& fV) { + float fCMax = std::max(std::max(fR, fG), fB); + float fCMin = std::min(std::min(fR, fG), fB); + float fDelta = fCMax - fCMin; + + if(fDelta > 0) { + if(fCMax == fR) { + fH = 60 * (std::fmod(((fG - fB) / fDelta), 6)); + } else if(fCMax == fG) { + fH = 60 * (((fB - fR) / fDelta) + 2); + } else if(fCMax == fB) { + fH = 60 * (((fR - fG) / fDelta) + 4); + } + + if(fCMax > 0) { + fS = fDelta / fCMax; + } else { + fS = 0; + } + + fV = fCMax; + } else { + fH = 0; + fS = 0; + fV = fCMax; + } + + if(fH < 0) { + fH = 360 + fH; + } +} + +/*! \brief Convert HSV to RGB color space + + Converts a given set of HSV values `h', `s', `v' into RGB + coordinates. The output RGB values are in the range [0, 1], and + the input HSV values are in the ranges h = [0, 360], and s, v = + [0, 1], respectively. + + \param fR Red component, used as output, range: [0, 1] + \param fG Green component, used as output, range: [0, 1] + \param fB Blue component, used as output, range: [0, 1] + \param fH Hue component, used as input, range: [0, 360] + \param fS Hue component, used as input, range: [0, 1] + \param fV Hue component, used as input, range: [0, 1] + +*/ +void HSVtoRGB(float& fR, float& fG, float& fB, const float& fH, const float& fS, const float& fV) { + float fC = fV * fS; // Chroma + float fHPrime = std::fmod(fH / 60.0, 6); + float fX = fC * (1 - std::fabs(std::fmod(fHPrime, 2) - 1)); + float fM = fV - fC; + + if(0 <= fHPrime && fHPrime < 1) { + fR = fC; + fG = fX; + fB = 0; + } else if(1 <= fHPrime && fHPrime < 2) { + fR = fX; + fG = fC; + fB = 0; + } else if(2 <= fHPrime && fHPrime < 3) { + fR = 0; + fG = fC; + fB = fX; + } else if(3 <= fHPrime && fHPrime < 4) { + fR = 0; + fG = fX; + fB = fC; + } else if(4 <= fHPrime && fHPrime < 5) { + fR = fX; + fG = 0; + fB = fC; + } else if(5 <= fHPrime && fHPrime < 6) { + fR = fC; + fG = 0; + fB = fX; + } else { + fR = 0; + fG = 0; + fB = 0; + } + + fR += fM; + fG += fM; + fB += fM; +} diff --git a/src/Color.hpp b/src/Color.hpp new file mode 100644 index 0000000..7051a46 --- /dev/null +++ b/src/Color.hpp @@ -0,0 +1,30 @@ +#ifndef Color_h_ +#define Color_h_ + +#include +#include +#include +#include + +#include "SDL_pixels.h" + +struct Color +{ + + std::uint8_t r, g, b, a; + + Color(); + Color(std::uint8_t, std::uint8_t, std::uint8_t, std::uint8_t); + Color(const SDL_Color&); + void set_rgb_float(const float&, const float&, const float&); + void set_hsv(const float&, const float& = 1.0f, const float& = 1.0f); + void shift_hue(float); + SDL_Color get_sdl_color(); + +}; + +std::ostream& operator<<(std::ostream&, const Color&); +void RGBtoHSV(const float&, const float&, const float&, float&, float&, float&); +void HSVtoRGB(float&, float&, float&, const float&, const float&, const float&); + +#endif diff --git a/src/Configuration.cpp b/src/Configuration.cpp index 4d026b1..9bbfe36 100644 --- a/src/Configuration.cpp +++ b/src/Configuration.cpp @@ -31,7 +31,8 @@ void Configuration::set_defaults() {"system-any-key-ignore-commands", {"fullscreen", "screenshot", "toggle-framerate", "record", "quit"}}, {"any-key-ignore-commands", {}}, - {"default-unsuppress-delay", 700} + {"default-unsuppress-delay", 700}, + {"ignore-repeat-keypress", true} }; sys_config["path"] = { {"screenshots", "."}, diff --git a/src/Display.cpp b/src/Display.cpp index 96fca21..b7bc8d4 100644 --- a/src/Display.cpp +++ b/src/Display.cpp @@ -6,19 +6,33 @@ Display::Display(Node* parent) : Node(parent) get_delegate().subscribe(&Display::respond, this); } -glm::ivec2 Display::get_window_size() +glm::ivec2 Display::get_window_size() const { glm::ivec2 size; - SDL_GetWindowSize(get_root()->get_window(), &size.x, &size.y); + SDL_GetWindowSize(const_cast(get_window()), &size.x, &size.y); return size; } -Box Display::get_window_box() +Box Display::get_window_box() const { return Box(glm::vec2(0, 0), get_window_size()); } -void Display::get_screen_pixels(unsigned char* pixels, int w, int h, int x, int y) +Uint32 Display::get_pixel_format(int display_index) const +{ + SDL_DisplayMode display_mode; + if (SDL_GetCurrentDisplayMode(display_index, &display_mode) != 0) + { + SDL_Log("could not get display mode for index %i: %s", display_index, SDL_GetError()); + return SDL_PIXELFORMAT_UNKNOWN; + } + else + { + return display_mode.format; + } +} + +void Display::get_screen_pixels(unsigned char* pixels, int w, int h, int x, int y) const { if (get_root()->is_gl_context) { @@ -38,20 +52,14 @@ void Display::get_screen_pixels(unsigned char* pixels, int w, int h, int x, int } else { - // Uint32 format; -// #if SDL_BYTEORDER == SDL_BIG_ENDIAN -// format = SDL_PIXELFORMAT_ARGB8888; -// #else -// format = SDL_PIXELFORMAT_ABGR8888; -// #endif - SDL_SetRenderTarget(get_root()->renderer, NULL); - SDL_RenderPresent(get_root()->renderer); - SDL_RenderReadPixels( - get_root()->renderer, NULL, SDL_PIXELFORMAT_RGBA32, pixels, bpp / 8 * w); + SDL_Renderer* renderer = const_cast(get_renderer()); + SDL_SetRenderTarget(renderer, NULL); + SDL_RenderPresent(renderer); + SDL_RenderReadPixels(renderer, NULL, SDL_PIXELFORMAT_RGBA32, pixels, bpp / 8 * w); } } -SDL_Surface* Display::get_screen_surface() +SDL_Surface* Display::get_screen_surface() const { glm::ivec2 size = get_window_size(); unsigned char* pixels = new unsigned char[bpp / 8 * size.x * size.y]; @@ -62,8 +70,7 @@ SDL_Surface* Display::get_screen_surface() return surface; } -SDL_Surface* Display::get_screen_surface_from_pixels( - unsigned char* pixels, bool flip) +SDL_Surface* Display::get_screen_surface_from_pixels(unsigned char* pixels, bool flip) const { glm::ivec2 size = get_window_size(); SDL_Surface* surface; @@ -102,16 +109,17 @@ void Display::respond(SDL_Event& event) } } -void Display::toggle_fullscreen() +void Display::toggle_fullscreen() const { - if (SDL_GetWindowFlags(get_root()->get_window()) & SDL_WINDOW_FULLSCREEN) + SDL_Window* window = const_cast(get_window()); + if (SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN) { SDL_Log("fullscreen requested"); - SDL_SetWindowFullscreen(get_root()->window, 0); + SDL_SetWindowFullscreen(window, 0); } else { SDL_Log("exit fullscreen requested"); - SDL_SetWindowFullscreen(get_root()->get_window(), SDL_WINDOW_FULLSCREEN); + SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN); } } diff --git a/src/Display.hpp b/src/Display.hpp index ed97251..db30543 100644 --- a/src/Display.hpp +++ b/src/Display.hpp @@ -23,13 +23,14 @@ struct Display : Node const static int bpp = 32; Display(Node*); - glm::ivec2 get_window_size(); - Box get_window_box(); - 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); + glm::ivec2 get_window_size() const; + Uint32 get_pixel_format(int = 0) const; + Box get_window_box() const; + void get_screen_pixels(unsigned char*, int, int, int = 0, int = 0) const; + SDL_Surface* get_screen_surface() const; + SDL_Surface* get_screen_surface_from_pixels(unsigned char*, bool) const; void respond(SDL_Event&); - void toggle_fullscreen(); + void toggle_fullscreen() const; }; diff --git a/src/Game.cpp b/src/Game.cpp index c67a72f..1d934ed 100644 --- a/src/Game.cpp +++ b/src/Game.cpp @@ -38,7 +38,7 @@ void FramerateIndicator::refresh() unload(); SDL_Surface* surface = get_surface(); SDL_Texture* texture = SDL_CreateTextureFromSurface(get_root()->get_renderer(), surface); - add_frame(texture); + add_frames(texture); SDL_FreeSurface(surface); set_ne(get_display().get_window_box().get_ne()); } @@ -445,6 +445,11 @@ std::string Game::get_pixel_format_string(Uint32 format) return pixel_format; } +const SDL_Window* Game::get_window() const +{ + return window; +} + SDL_Window* Game::get_window() { return window; @@ -460,6 +465,16 @@ SDL_Renderer* Game::get_renderer() return renderer; } +const Input& Game::get_input() const +{ + return input; +} + +Input& Game::get_input() +{ + return input; +} + glm::vec2 Game::weight(glm::vec2 motion) { return {weight(motion.x), weight(motion.y)}; diff --git a/src/Game.hpp b/src/Game.hpp index c313c1d..c35c81d 100644 --- a/src/Game.hpp +++ b/src/Game.hpp @@ -67,6 +67,7 @@ struct Game : Node Game(); ~Game(); + virtual void reset() { activate(); } void print_error(const std::string&); void print_sdl_error(const std::string&); void print_gl_attributes(); @@ -77,9 +78,12 @@ struct Game : Node void log_display_mode(); void log_surface_format(SDL_Surface*, std::string = "surface"); std::string get_pixel_format_string(Uint32); + const SDL_Window* get_window() const; SDL_Window* get_window(); const SDL_Renderer* get_renderer() const; SDL_Renderer* get_renderer(); + const Input& get_input() const; + Input& get_input(); glm::vec2 weight(glm::vec2); void run(); void frame(float); diff --git a/src/Input.cpp b/src/Input.cpp index 86c650a..11183d4 100644 --- a/src/Input.cpp +++ b/src/Input.cpp @@ -86,23 +86,27 @@ void Input::respond(SDL_Event &event) suppress_any_key = get_configuration()["input"]["suppress-any-key-on-mods"] && (ctrl || alt); const std::vector& system_any_key_ignore = get_configuration()["input"]["system-any-key-ignore-commands"]; const std::vector& any_key_ignore = get_configuration()["input"]["any-key-ignore-commands"]; - for (KeyCombination& combination : key_map) + bool ignore_repeat = get_configuration()["input"]["ignore-repeat-keypress"]; + if (event.key.repeat == 0 || !ignore_repeat) { - if (sym == combination.key && (!combination.ctrl || ctrl) && (!combination.shift || shift) && - (!combination.alt || alt)) + for (KeyCombination& combination : key_map) { - post_command(combination.command, cancel); - if (!sfw::is_in_container(system_any_key_ignore, combination.command) && !found_command && - !sfw::is_in_container(any_key_ignore, combination.command) && !suppress_any_key) + if (sym == combination.key && (!combination.ctrl || ctrl) && (!combination.shift || shift) && + (!combination.alt || alt)) { - post_command(any, cancel); + post_command(combination.command, cancel); + if (!sfw::is_in_container(system_any_key_ignore, combination.command) && !found_command && + !sfw::is_in_container(any_key_ignore, combination.command) && !suppress_any_key) + { + post_command(any, cancel); + } + found_command = true; } - found_command = true; } - } - if (!found_command && !suppress_any_key) - { - post_command(any, cancel); + if (!found_command && !suppress_any_key) + { + post_command(any, cancel); + } } } } diff --git a/src/Node.cpp b/src/Node.cpp index 8eacf08..3b681c0 100644 --- a/src/Node.cpp +++ b/src/Node.cpp @@ -14,16 +14,6 @@ void Node::set_parent(Node* other) parent = other; } -void Node::activate() -{ - active = true; -} - -void Node::deactivate() -{ - active = false; -} - bool Node::is_active() const { return active; @@ -39,7 +29,7 @@ Delegate& Node::get_delegate() return get_root()->delegate; } -Display& Node::get_display() +const Display& Node::get_display() const { return get_root()->display; } @@ -54,11 +44,21 @@ SDL_Renderer* Node::get_renderer() return get_root()->get_renderer(); } +const SDL_Window* Node::get_window() const +{ + return get_root()->get_window(); +} + SDL_Window* Node::get_window() { return get_root()->get_window(); } +const Input& Node::get_input() const +{ + return get_root()->get_input(); +} + const Game* Node::get_root() const { const Node* root = this; @@ -69,14 +69,29 @@ const Game* Node::get_root() const return dynamic_cast(root); } +Box Node::get_window_box() +{ + return get_display().get_window_box(); +} + +void Node::suppress_input() +{ + get_root()->get_input().suppress(); +} + void Node::suppress_input_temporarily(int length) { - get_root()->input.suppress(); + suppress_input(); if (length == 0) { length = get_configuration()["input"]["default-unsuppress-delay"]; } - get_root()->input.unsuppress_animation.play_once(length); + get_root()->get_input().unsuppress_animation.play_once(length); +} + +void Node::unsuppress_input() +{ + get_root()->get_input().unsuppress(); } void Node::print_branch() diff --git a/src/Node.hpp b/src/Node.hpp index 236e095..e7b3d79 100644 --- a/src/Node.hpp +++ b/src/Node.hpp @@ -7,11 +7,13 @@ #include "json/json.hpp" #include "SDL.h" +#include "Box.hpp" #include "filesystem.hpp" struct Game; struct Delegate; struct Display; +struct Input; struct Node { @@ -22,18 +24,24 @@ struct Node Node(); Node(Node*); void set_parent(Node*); - void activate(); - void deactivate(); - virtual void reset() { deactivate(); }; + virtual void reset() { deactivate(); } + virtual void activate() { active = true; } + virtual void deactivate() { active = false; } bool is_active() const; nlohmann::json& get_configuration(); Delegate& get_delegate(); - Display& get_display(); + const Display& get_display() const; const SDL_Renderer* get_renderer() const; SDL_Renderer* get_renderer(); + const SDL_Window* get_window() const; SDL_Window* get_window(); + const Input& get_input() const; + Input& get_input(); const Game* get_root() const; + Box get_window_box(); + void suppress_input(); void suppress_input_temporarily(int = 0); + void unsuppress_input(); void print_branch(); virtual std::string get_class_name() { return "Node"; }; virtual ~Node(); @@ -56,5 +64,6 @@ struct Node #include "Configuration.hpp" #include "Delegate.hpp" #include "Display.hpp" +#include "Input.hpp" #endif diff --git a/src/Recorder.cpp b/src/Recorder.cpp index c1c0d2e..3708053 100644 --- a/src/Recorder.cpp +++ b/src/Recorder.cpp @@ -275,7 +275,7 @@ void Recorder::keep_stash() { in_game_stashes.push_back(current_stash); current_stash = Stash(); - int max_stashes = get_configuration()["recording"]["max-in-game-stashes"]; + auto max_stashes = get_configuration()["recording"]["max-in-game-stashes"]; if (in_game_stashes.size() > max_stashes) { Stash& stash = in_game_stashes.front(); diff --git a/src/Sprite.cpp b/src/Sprite.cpp index 20b217c..2a680ea 100644 --- a/src/Sprite.cpp +++ b/src/Sprite.cpp @@ -15,6 +15,12 @@ Sprite::Sprite(Node* parent, std::string path) : Sprite(parent) associate(path); } +void Sprite::reset() +{ + Node::reset(); + activate(); +} + void Sprite::associate(std::string path) { if (fs::is_regular_file(path)) @@ -70,17 +76,17 @@ void Sprite::load_file(fs::path path) } else { - add_frame(texture); + add_frames(texture); } } -void Sprite::add_frame(SDL_Texture* texture) +void Sprite::add_frames(SDL_Texture* frame) { bool preserve_center = frames.size() > 0; - frames.push_back(texture); + frames.push_back(frame); Frameset& all_frames_frameset = get_all_frames_frameset(); all_frames_frameset.clear(); - for (int ii = 0; ii < frames.size(); ii++) + for (std::size_t ii = 0; ii < frames.size(); ii++) { all_frames_frameset.add_frame_index(ii); } @@ -91,6 +97,14 @@ void Sprite::add_frame(SDL_Texture* texture) update_size(preserve_center); } +void Sprite::add_frames(const std::vector& frames) +{ + for (SDL_Texture* frame : frames) + { + add_frames(frame); + } +} + Frameset& Sprite::get_all_frames_frameset() { return framesets[get_configuration()["animation"]["all-frames-frameset-name"]]; @@ -126,6 +140,11 @@ const Frameset& Sprite::get_current_frameset() const return framesets.at(current_frameset_name); } +void Sprite::set_frame_length(float length) +{ + get_current_frameset().set_frame_length(length); +} + SDL_Texture* Sprite::get_current_frame() const { return frames[get_current_frameset().get_current_frame_index()]; @@ -152,7 +171,7 @@ void Sprite::add_box(glm::vec2 position, bool absolute) void Sprite::update_size(bool preserve_center) { - for (int ii = 0; ii < boxes.size(); ii++) + for (std::size_t ii = 0; ii < boxes.size(); ii++) { boxes[ii].set_size(get_current_frameset().get_size(), preserve_center); if (scale != 1) @@ -367,6 +386,16 @@ void Sprite::set_center_y(float y) move({0, y - get_center_y()}, false); } +void Sprite::set_nw(const glm::vec2& nw) +{ + move(nw - get_nw(), false); +} + +void Sprite::set_ne(const glm::vec2& ne) +{ + move(ne - get_ne(), false); +} + void Sprite::set_se(const glm::vec2& se) { move(se - get_se(), false); @@ -382,14 +411,9 @@ void Sprite::set_sw(const glm::vec2& sw) move(sw - get_sw(), false); } -void Sprite::set_nw(const glm::vec2& nw) +void Sprite::set_west(const glm::vec2& west) { - move(nw - get_nw(), false); -} - -void Sprite::set_ne(const glm::vec2& ne) -{ - move(ne - get_ne(), false); + move(west - get_west(), false); } void Sprite::set_center(const glm::vec2& center) @@ -424,6 +448,16 @@ void Sprite::add_wrap(bool x, bool y, Box frame) wrap_frame = frame; } +void Sprite::add_hue_shift_frames(int count) +{ + float step = 360 / (count + 1); + SDL_Texture* base = get_current_frame(); + for (float offset = step; offset <= 359.9; offset += step) + { + add_frames(sfw::get_hue_shifted_texture(get_renderer(), base, offset)); + } +} + glm::vec2 Sprite::move(glm::vec2 delta, bool weighted) { if (weighted) @@ -530,7 +564,7 @@ bool Sprite::collide(const Box& box, bool precise, Box* overlap, bool all, SDL_T overlap = &o; } } - for (int ii = 0; ii < get_boxes().size(); ii++) + for (auto ii = 0; ii < static_cast(get_boxes().size()); ii++) { if (get_box(ii).collide(box, overlap)) { @@ -643,7 +677,7 @@ void Sprite::update() SDL_Rect wrap_frame_rect = wrap_frame.get_int_rect(); SDL_RenderSetClipRect(renderer, &wrap_frame_rect); } - for (int ii = 0; ii < boxes.size(); ii++) + for (auto ii = 0; ii < static_cast(boxes.size()); ii++) { SDL_RenderCopyF(renderer, texture, NULL, boxes[ii].get_rect()); } @@ -709,7 +743,7 @@ glm::vec2 Frameset::measure() const { glm::vec2 s(0, 0); int w, h; - for (int index : order) + for (std::size_t index : order) { if (index < sprite->frames.size()) { diff --git a/src/Sprite.hpp b/src/Sprite.hpp index aabca2b..df610a3 100644 --- a/src/Sprite.hpp +++ b/src/Sprite.hpp @@ -14,6 +14,7 @@ #include "Node.hpp" #include "Box.hpp" #include "Animation.hpp" +#include "Color.hpp" #include "extension.hpp" struct Game; @@ -42,14 +43,17 @@ struct Sprite : Node Sprite(); Sprite(Node*); Sprite(Node*, std::string); + void reset(); void associate(std::string); void load(); void load_file(fs::path); - void add_frame(SDL_Texture*); + void add_frames(SDL_Texture*); + void add_frames(const std::vector&); Frameset& get_all_frames_frameset(); Frameset& add_frameset(std::string); Frameset& set_frameset(std::string); Frameset& get_current_frameset(); + void set_frame_length(float); const Frameset& get_current_frameset() const; SDL_Texture* get_current_frame() const; const Box& get_box(int = 0) const; @@ -100,9 +104,11 @@ struct Sprite : Node void set_se(const glm::vec2&); void set_south(const glm::vec2&); void set_sw(const glm::vec2&); + void set_west(const glm::vec2&); void set_center(const glm::vec2&); void add_wrap(bool, bool); void add_wrap(bool, bool, Box); + void add_hue_shift_frames(int); glm::vec2 move(glm::vec2, bool = true); bool collide(const glm::vec2&, bool = false) const; bool collide(const Segment&, glm::vec2* = NULL, bool = false) const; @@ -125,7 +131,7 @@ struct Frameset int order_index = 0; float frame_length = 0; bool reversed = false; - glm::vec2 size; + glm::vec2 size = {0, 0}; Frameset(); Frameset(Sprite*); diff --git a/src/extension.cpp b/src/extension.cpp index 2b666bd..4641dca 100644 --- a/src/extension.cpp +++ b/src/extension.cpp @@ -96,6 +96,69 @@ void sfw::populate_pixel_2d_array( } } +std::vector sfw::get_halo_frames( + Node& node, float radius, int segment_count, const std::vector& colors, float min_radius, bool fade) +{ + std::vector frames; + frames.reserve(segment_count); + SDL_Renderer* renderer = node.get_renderer(); + SDL_Texture* frame; + float alpha = 255, alpha_step = 255.0f / segment_count, segment_radius; + int color_count = colors.size(); + SDL_Color color; + for (int color_offset = 0; color_offset < color_count; color_offset++) + { + if (fade) + { + alpha = alpha_step; + } + frame = sfw::get_filled_texture(renderer, {2 * radius, 2 * radius}, {255, 255, 255, 0}); + SDL_SetTextureBlendMode(frame, SDL_BLENDMODE_BLEND); + SDL_SetRenderTarget(renderer, frame); + for (int segment_ii = 0; segment_ii < segment_count; segment_ii++) + { + color = colors[(color_offset + segment_ii) % color_count]; + color.a = std::round(alpha); + segment_radius = min_radius + (segment_count - 1.0f - segment_ii) / (segment_count - 1.0f) * (radius - min_radius); + aaFilledEllipseRGBA( + renderer, radius, radius, segment_radius, segment_radius, color.r, color.g, color.b, color.a); + if (fade) + { + alpha += alpha_step; + } + } + frames.push_back(frame); + } + return frames; +} + +std::vector sfw::get_portal_frames( + SDL_Renderer* renderer, glm::vec2 size, float hue_start, float hue_end, int dy, int count) +{ + std::vector frames; + frames.reserve(count); + float y_margin = 10; + float max_y = size.y - y_margin; + std::vector hues = range_step(hue_start, hue_end, count); + std::cout << hues << std::endl; + SDL_Texture* frame; + Color color; + for (int frame_ii = 0; frame_ii < count; frame_ii++) + { + frame = sfw::get_filled_texture(renderer, size, {255, 255, 255, 0}); + SDL_SetRenderTarget(renderer, frame); + SDL_SetTextureBlendMode(frame, SDL_BLENDMODE_BLEND); + for (int ellipse_ii = 0, y = max_y; y > y_margin - 3; ellipse_ii++, y -= dy) + { + color.a = y / max_y * 255.0f; + color.set_hsv(hues[mod(ellipse_ii - frame_ii, count)]); + aaFilledEllipseRGBA(renderer, size.x / 2, y, size.x / 2, y_margin - 3, color.r, color.g, color.b, color.a); + } + frames.push_back(frame); + } + return frames; +} + void sfw::fill_texture(SDL_Renderer* renderer, SDL_Texture* texture, Uint8 r, Uint8 g, Uint8 b, Uint8 a) { SDL_SetRenderTarget(renderer, texture); @@ -133,6 +196,43 @@ SDL_Texture* sfw::get_filled_texture(SDL_Renderer* renderer, glm::vec2 size, SDL return texture; } +SDL_Texture* sfw::get_hue_shifted_texture(SDL_Renderer* renderer, SDL_Texture* base, float offset) +{ + SDL_Texture* hue_shifted_texture = sfw::duplicate_texture(renderer, base); + Uint32 pixel_format; + int w, h; + SDL_QueryTexture(hue_shifted_texture, &pixel_format, NULL, &w, &h); + SDL_PixelFormat* pixel_format_struct = SDL_AllocFormat(pixel_format); + SDL_SetRenderTarget(renderer, hue_shifted_texture); + int bytes_per_pixel = SDL_BYTESPERPIXEL(pixel_format); + int bytes_per_row = bytes_per_pixel * w; + int bytes_total = bytes_per_row * h; + int length = bytes_total / 4 + (bytes_total % 4 ? 1 : 0); + Uint32* pixels = new Uint32[length]; + if (SDL_RenderReadPixels(renderer, NULL, pixel_format, pixels, bytes_per_row) < 0) + { + print_sdl_error("Could not read pixels"); + } + else + { + Color rgba; + for (int ii = 0; ii < length; ii++) + { + SDL_GetRGBA(pixels[ii], const_cast(pixel_format_struct), + &rgba.r, &rgba.g, &rgba.b, &rgba.a); + rgba.shift_hue(offset); + pixels[ii] = SDL_MapRGBA(const_cast(pixel_format_struct), rgba.r, rgba.g, rgba.b, rgba.a); + } + if (SDL_UpdateTexture(hue_shifted_texture, NULL, pixels, bytes_per_row) < 0) + { + print_sdl_error("Could not apply hue shifted pixels update to texture"); + } + } + delete[] pixels; + SDL_FreeFormat(pixel_format_struct); + return hue_shifted_texture; +} + SDL_Texture* sfw::duplicate_texture(SDL_Renderer* renderer, SDL_Texture* base, Uint32 format) { Box box = get_texture_box(base); @@ -141,7 +241,8 @@ SDL_Texture* sfw::duplicate_texture(SDL_Renderer* renderer, SDL_Texture* base, U SDL_Texture* sfw::duplicate_texture(SDL_Renderer* renderer, SDL_Texture* base, const glm::vec2& size, Uint32 format) { - Box box = get_texture_box(base); + SDL_BlendMode original_blend_mode; + SDL_GetTextureBlendMode(base, &original_blend_mode); SDL_Texture* duplicate = SDL_CreateTexture(renderer, format, SDL_TEXTUREACCESS_TARGET, size.x, size.y); if (duplicate == NULL) { @@ -160,6 +261,8 @@ SDL_Texture* sfw::duplicate_texture(SDL_Renderer* renderer, SDL_Texture* base, c print_sdl_error("could not render base onto duplicate"); return NULL; } + SDL_SetTextureBlendMode(base, original_blend_mode); + SDL_SetTextureBlendMode(duplicate, original_blend_mode); return duplicate; } @@ -239,9 +342,8 @@ SDL_Texture* sfw::get_remapped_texture( #include "superxbr.cpp" /* - Base texture must be set to SDL_TEXTUREACCESS_TARGET - - Scale2x implementation based on the explanation at http://www.scale2x.it/algorithm.html + - Base texture must be set to SDL_TEXTUREACCESS_TARGET + - Scale2x implementation based on http://www.scale2x.it/algorithm.html */ SDL_Texture* sfw::get_pixel_scaled_texture(SDL_Renderer* renderer, SDL_Texture* base, int count, int version) { @@ -434,14 +536,3 @@ std::ostream& operator<<(std::ostream& out, const SDL_Color& color) static_cast(color.b) << ", " << static_cast(color.a) << "}"; return out; } - -std::ostream& operator<<(std::ostream& out, const std::vector ints) -{ - out << "{ "; - for (int ii : ints) - { - out << ii << " "; - } - out << "}"; - return out; -} diff --git a/src/extension.hpp b/src/extension.hpp index 609b3c9..35ee3e7 100644 --- a/src/extension.hpp +++ b/src/extension.hpp @@ -14,7 +14,6 @@ #include "SDL.h" #include "SDL_image.h" #include "SDL_pixels.h" - #define GLM_ENABLE_EXPERIMENTAL #include "glm/trigonometric.hpp" #include "glm/vec2.hpp" @@ -22,8 +21,11 @@ #include "Box.hpp" #include "Segment.hpp" +#include "Color.hpp" #include "filesystem.hpp" +struct Node; + namespace sfw { @@ -36,10 +38,14 @@ namespace sfw Box get_texture_box(SDL_Texture*); void populate_pixel_2d_array(SDL_Renderer*, SDL_Texture*, std::vector>&); void populate_pixel_2d_array(SDL_Renderer*, SDL_Texture*, std::vector>&, const Box&); + std::vector get_halo_frames( + Node&, float, int, const std::vector& = {{0, 0, 0, 255}, {255, 255, 255, 255}}, float = 4.0f, bool = true); + std::vector get_portal_frames(SDL_Renderer*, glm::vec2, float = 60, float = 30, int = 4, int = 6); void fill_texture(SDL_Renderer*, SDL_Texture*, Uint8, Uint8, Uint8, Uint8 = 0xff); void fill_texture(SDL_Renderer*, SDL_Texture*, SDL_Texture*); SDL_Texture* get_filled_texture(SDL_Renderer*, glm::vec2, const SDL_Color&, Uint32 = SDL_PIXELFORMAT_RGBA32); SDL_Texture* get_filled_texture(SDL_Renderer*, glm::vec2, SDL_Texture*, Uint32 = SDL_PIXELFORMAT_RGBA32); + SDL_Texture* get_hue_shifted_texture(SDL_Renderer*, SDL_Texture*, float); SDL_Texture* duplicate_texture(SDL_Renderer*, SDL_Texture*, Uint32 = SDL_PIXELFORMAT_RGBA32); SDL_Texture* duplicate_texture(SDL_Renderer*, SDL_Texture*, const glm::vec2&, Uint32 = SDL_PIXELFORMAT_RGBA32); SDL_Texture* get_remapped_texture( @@ -72,6 +78,12 @@ namespace sfw return std::find(container.begin(), container.end(), member) != container.end(); } + template + float mod(N a, N b) + { + return (b + (a % b)) % b; + } + template std::string pad(T end, int width, char fill = '0') { @@ -82,6 +94,19 @@ namespace sfw return padded.str(); } + template + std::vector range_step(N start, N end, int count) + { + float step = (end - start) / (count - 1); + std::vector nums; + nums.reserve(count); + for (int ii = 0; ii < count; ii++) + { + nums.push_back(start + ii * step); + } + return nums; + } + // from https://stackoverflow.com/a/30312659/1256386 template std::vector range(IntType start, IntType stop, IntType step) @@ -113,12 +138,26 @@ namespace sfw { return range(IntType(0), stop, IntType(1)); } + +} + +template +std::ostream& operator<<(std::ostream& out, const std::vector& members) +{ + out << "{ "; + for (const T& member : members) + { + out << member << " "; + } + out << "}"; + return out; } std::ostream& operator<<(std::ostream&, const glm::vec2&); bool operator<(const SDL_Color& color_1, const SDL_Color& color_2); bool operator==(const SDL_Color& color_1, const SDL_Color& color_2); std::ostream& operator<<(std::ostream&, const SDL_Color&); -std::ostream& operator<<(std::ostream&, const std::vector); + +#include "Node.hpp" #endif