color class, sprite hue shift, generate halo and portal effect

animation frames, ignore repeat keys, hue shifted texture
This commit is contained in:
Frank DeMarco 2020-08-19 03:29:46 -04:00
parent ae644b7138
commit bab8c778ca
19 changed files with 544 additions and 96 deletions

View File

@ -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 :)

View File

@ -44,6 +44,11 @@ void Animation::reset()
timer.reset();
}
bool Animation::is_playing()
{
return playing && !paused;
}
void Animation::update()
{
timer.update();

View File

@ -5,11 +5,12 @@
#include <functional>
#include <algorithm>
#include "Node.hpp"
#include "Timer.hpp"
typedef std::function<void()> 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

View File

@ -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;

181
src/Color.cpp Normal file
View File

@ -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<int>(color.r) << ", g=" << static_cast<int>(color.g) << ", b=" <<
static_cast<int>(color.b) << ", a= " << static_cast<int>(color.a) << ", h=" << h <<
", s=" << s << ", v=" << v << "}";
return out;
}
/*
The following copyright applies to RGBtoHSV and HSVtoRGB:
*/
//
// Copyright (c) 2014, Jan Winkler <winkler@cs.uni-bremen.de>
// 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;
}

30
src/Color.hpp Normal file
View File

@ -0,0 +1,30 @@
#ifndef Color_h_
#define Color_h_
#include <cstdint>
#include <cstdlib>
#include <cmath>
#include <ostream>
#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

View File

@ -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", "."},

View File

@ -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<SDL_Window*>(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<SDL_Renderer*>(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<SDL_Window*>(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);
}
}

View File

@ -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;
};

View File

@ -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)};

View File

@ -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);

View File

@ -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<std::string>& system_any_key_ignore = get_configuration()["input"]["system-any-key-ignore-commands"];
const std::vector<std::string>& 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);
}
}
}
}

View File

@ -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<const Game*>(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()

View File

@ -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

View File

@ -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();

View File

@ -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<SDL_Texture*>& 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<int>(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<int>(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())
{

View File

@ -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<SDL_Texture*>&);
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*);

View File

@ -96,6 +96,69 @@ void sfw::populate_pixel_2d_array(
}
}
std::vector<SDL_Texture*> sfw::get_halo_frames(
Node& node, float radius, int segment_count, const std::vector<SDL_Color>& colors, float min_radius, bool fade)
{
std::vector<SDL_Texture*> 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<SDL_Texture*> sfw::get_portal_frames(
SDL_Renderer* renderer, glm::vec2 size, float hue_start, float hue_end, int dy, int count)
{
std::vector<SDL_Texture*> frames;
frames.reserve(count);
float y_margin = 10;
float max_y = size.y - y_margin;
std::vector<float> 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<const SDL_PixelFormat*>(pixel_format_struct),
&rgba.r, &rgba.g, &rgba.b, &rgba.a);
rgba.shift_hue(offset);
pixels[ii] = SDL_MapRGBA(const_cast<const SDL_PixelFormat*>(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<int>(color.b) << ", " << static_cast<int>(color.a) << "}";
return out;
}
std::ostream& operator<<(std::ostream& out, const std::vector<int> ints)
{
out << "{ ";
for (int ii : ints)
{
out << ii << " ";
}
out << "}";
return out;
}

View File

@ -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<std::vector<SDL_Color>>&);
void populate_pixel_2d_array(SDL_Renderer*, SDL_Texture*, std::vector<std::vector<SDL_Color>>&, const Box&);
std::vector<SDL_Texture*> get_halo_frames(
Node&, float, int, const std::vector<SDL_Color>& = {{0, 0, 0, 255}, {255, 255, 255, 255}}, float = 4.0f, bool = true);
std::vector<SDL_Texture*> 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<typename N>
float mod(N a, N b)
{
return (b + (a % b)) % b;
}
template<typename T>
std::string pad(T end, int width, char fill = '0')
{
@ -82,6 +94,19 @@ namespace sfw
return padded.str();
}
template <typename N>
std::vector<N> range_step(N start, N end, int count)
{
float step = (end - start) / (count - 1);
std::vector<N> 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 <typename IntType>
std::vector<IntType> range(IntType start, IntType stop, IntType step)
@ -113,12 +138,26 @@ namespace sfw
{
return range(IntType(0), stop, IntType(1));
}
}
template <typename T>
std::ostream& operator<<(std::ostream& out, const std::vector<T>& 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<int>);
#include "Node.hpp"
#endif