diff --git a/src/Attributes.hpp b/src/Attributes.hpp index ac9ddf5..a48d1a3 100644 --- a/src/Attributes.hpp +++ b/src/Attributes.hpp @@ -145,17 +145,7 @@ namespace sb * Construct a new Attributes object with vertices set to the vertices contained in this vector */ template - Attributes(const std::vector& vertices) : vertices(vertices) - { - /* debug message */ - std::ostringstream message; - message << "added vertex "; - for (const Type& vertex : vertices) - { - message << vertex << " "; - } - sb::Log::log(message, sb::Log::DEBUG); - } + Attributes(const std::vector& vertices) : vertices(vertices) {} /*! * Add a vertex. The vertex can be any of the variant types. diff --git a/src/Carousel.hpp b/src/Carousel.hpp index 522368c..579cd49 100644 --- a/src/Carousel.hpp +++ b/src/Carousel.hpp @@ -12,6 +12,11 @@ #include #include + +#define GLM_ENABLE_EXPERIMENTAL +#include "glm/common.hpp" +#include "glm/gtx/integer.hpp" + #include "utility.hpp" namespace sb @@ -50,13 +55,13 @@ namespace sb template void previous(const Container& container) { - offset = sb::mod(--offset, container.size()); + offset = glm::mod(--offset, container.size()); } template void increment(const Container& container, int amount) { - offset = sb::mod(offset + amount, container.size()); + offset = glm::mod(offset + amount, container.size()); } void beginning() diff --git a/src/Connection.hpp b/src/Connection.hpp index fe94418..5630237 100644 --- a/src/Connection.hpp +++ b/src/Connection.hpp @@ -1,40 +1,40 @@ -/* /\ +------------------------------------------------------+ - * ____/ \____ /| - Open source game framework licensed to freely use, | - * \ / / | copy, modify and sell without restriction | - * +--\ ^__^ /--+ | | - * | ~/ \~ | | - created for | - * | ~~~~~~~~~~~~ | +------------------------------------------------------+ - * | SPACE ~~~~~ | / - * | ~~~~~~~ BOX |/ - * +--------------+ - * - * Connection objects contain a binary state of either on (connected) or off (not connected). When their state - * is changed, an optional user supplied function corresponding to the state change is run automatically. - * - * The functions each have the same return type, number of arguments, and argument types determined by the - * template arguments. - * - * By default, the state must change in order for a callback to be triggered. To allow repeat calls to - * trigger a callback, the - * - * Original test code: - * - * Connection<> connection_d(std::bind(&Game::print_frame_length_history, this)); - * connection_d.toggle(); - * Connection connection_f { - * std::function(&sb::mod), std::function(&sb::mod) }; - * Connection<> connection_g = connection_d; - * connection_g.toggle(); - * connection_g.disconnect(); - * int result; - * result = connection_f.connect(3, 5); - * std::cout << result << " "; - * std::cout << connection_f.disconnect(20, 6) << " "; - * result = connection_f.toggle(800, 120); - * std::cout << result << std::endl; - * result = connection_f.connect(111, 44); - * std::cout << result << std::endl; - */ + /* +------------------------------------------------------+ + ____/ \____ /| - Open source game framework licensed to freely use, | + \ / / | copy, modify and sell without restriction | ++--\ ^__^ /--+ | | +| ~/ \~ | | - created for | +| ~~~~~~~~~~~~ | +------------------------------------------------------+ +| SPACE ~~~~~ | / +| ~~~~~~~ BOX |/ ++--------------+ + +Connection objects contain a binary state of either on (connected) or off (not connected). When their state +is changed, an optional user supplied function corresponding to the state change is run automatically. + +The functions each have the same return type, number of arguments, and argument types determined by the +template arguments. + +By default, the state must change in order for a callback to be triggered. To allow repeat calls to +trigger a callback, the + +Original test code: + + Connection<> connection_d(std::bind(&Game::print_frame_length_history, this)); + connection_d.toggle(); + Connection connection_f { + std::function(&glm::mod), std::function(&glm::mod) }; + Connection<> connection_g = connection_d; + connection_g.toggle(); + connection_g.disconnect(); + int result; + result = connection_f.connect(3, 5); + std::cout << result << " "; + std::cout << connection_f.disconnect(20, 6) << " "; + result = connection_f.toggle(800, 120); + std::cout << result << std::endl; + result = connection_f.connect(111, 44); + std::cout << result << std::endl; +*/ #pragma once diff --git a/src/Game.cpp b/src/Game.cpp index 783a207..7a1d19f 100644 --- a/src/Game.cpp +++ b/src/Game.cpp @@ -36,6 +36,12 @@ Game::Game() SDL_LogSetPriority(sb::Log::DEFAULT_CATEGORY, SDL_LOG_PRIORITY_INFO); } + /* If recording is disabled by configuration, deactive it. */ + if (!configuration()["recording"]["enabled"]) + { + deactivate(); + } + /* Log the current working directory as seen by std::filesystem */ std::ostringstream log_message; log_message << "Current path as seen by std::filesystem is " << std::filesystem::current_path(); diff --git a/src/Pixels.hpp b/src/Pixels.hpp index 16a43bd..bd463ea 100644 --- a/src/Pixels.hpp +++ b/src/Pixels.hpp @@ -11,11 +11,16 @@ #pragma once #include "SDL.h" + +#define GLM_ENABLE_EXPERIMENTAL +#include "glm/common.hpp" +#include "glm/gtx/integer.hpp" + #include "Box.hpp" #include "Color.hpp" #include "Log.hpp" #include "extension.hpp" -#include "utility.hpp" +#include "math.hpp" class Sprite; @@ -48,11 +53,11 @@ struct Pixels std::uint8_t* access = static_cast(source); if (x < 0 || x >= rect.w) { - x = sb::mod(x, static_cast(rect.w)); + x = glm::mod(x, static_cast(rect.w)); } if (y < 0 || y >= rect.y) { - y = sb::mod(y, static_cast(rect.h)); + y = glm::mod(y, static_cast(rect.h)); } return reinterpret_cast(access + y * get_bytes_per_row() + x * format->BytesPerPixel); } diff --git a/src/Recorder.cpp b/src/Recorder.cpp index 499d3db..3a9f248 100644 --- a/src/Recorder.cpp +++ b/src/Recorder.cpp @@ -11,10 +11,6 @@ Recorder::Recorder(Node* parent) : Node(parent) get_delegate().subscribe(&Recorder::respond, this); animation.play(); Mix_SetPostMix(Recorder::process_audio, this); - if (!configuration()["recording"]["enabled"]) - { - deactivate(); - } } /* Returns length of a recorded video frame in seconds. Defaults to the frame length of the game if this hasn't diff --git a/src/Selection.hpp b/src/Selection.hpp index 4f6cd51..ea56367 100644 --- a/src/Selection.hpp +++ b/src/Selection.hpp @@ -12,6 +12,11 @@ #include #include + +#define GLM_ENABLE_EXPERIMENTAL +#include "glm/common.hpp" +#include "glm/gtx/integer.hpp" + #include "utility.hpp" namespace sb @@ -51,12 +56,12 @@ namespace sb void previous() { - offset = sb::mod(--offset, container.size()); + offset = glm::mod(--offset, container.size()); } void increment(int amount) { - offset = sb::mod(offset + amount, container.size()); + offset = glm::mod(offset + amount, container.size()); } void beginning() diff --git a/src/extension.cpp b/src/extension.cpp index 7133c67..dc2f105 100644 --- a/src/extension.cpp +++ b/src/extension.cpp @@ -209,7 +209,7 @@ std::vector sb::get_portal_frames( for (int ellipse_ii = 0, y = max_y; y > y_margin - 3; ellipse_ii++, y -= dy) { color.a = y / max_y * 255.0f; - color.hsv(hues[mod(ellipse_ii - frame_ii, count)]); + color.hsv(hues[glm::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); diff --git a/src/extension.hpp b/src/extension.hpp index c1e89ef..7a6bc55 100644 --- a/src/extension.hpp +++ b/src/extension.hpp @@ -33,6 +33,8 @@ #include "glm/trigonometric.hpp" #include "glm/vec2.hpp" #include "glm/gtx/vector_angle.hpp" +#include "glm/common.hpp" +#include "glm/gtx/integer.hpp" #include "json/json.hpp" #include "sdl2-gfx/SDL2_gfxPrimitives.h" #include "Box.hpp" diff --git a/src/math.hpp b/src/math.hpp index e25e3d6..e7d9495 100644 --- a/src/math.hpp +++ b/src/math.hpp @@ -49,6 +49,7 @@ Should print, #pragma once #include +#include /* GLM */ #define GLM_ENABLE_EXPERIMENTAL @@ -113,4 +114,121 @@ namespace sb * @param resolution number of points that will be in the computed curve */ std::vector bezier(const std::vector& vertices, int resolution = 30); + + /*! + * Wrap a point so that it is translated into the clip space as if it entered the opposite side of the clip upon exiting it. + * + * Running the same algorithm with PyGLM, + * + * In [42]: V = glm.vec3(1.5, 8.88, 3.2) + * In [43]: C0 = glm.vec3(-1.0, -1.0, -1.0) + * In [44]: C1 = glm.vec3(1.0, 1.0, 1.0) + * In [45]: Vw = ((V - C1) % (C1 - C0)) + C0 + * In [46]: Vw + * Out[46]: vec3( -0.5, 0.88, -0.8 ) + * + * @param vertex point to wrap + * @param clip_lower the lower corner of the clip space to wrap into + * @param clip_upper the upper corner of the clip space to wrap into + * @return wrapped point + */ + template + glm::vec wrap_point(const glm::vec& vertex, + const glm::vec& clip_lower, + const glm::vec& clip_upper) + { + return glm::mod(vertex - clip_upper, clip_upper - clip_lower) + clip_lower; + } + + /*! + * Wrap a curve so that all the points on the curve are translated into the given clip space as if they entered the opposite + * side of the clip upon exiting it. The curve is traversed from start to end. At wrap points, the curve splits, creating two + * disjoint segments. All the segments generated are returned in a vector. + * + * When a curve splits, an unwrapped version of the point at the split is added to the first segment, and an unwrapped version + * of the point preceeding the split is added to the start of the second segment (technically, the points are still wrapped but + * offset by a sector to match with their respective segments). This is done to allow the curve to be drawn up to the edge of + * the clip (and past it). + * + * @warning Because this causes points to lie outside the clip at the edges, this function may change in the future to create + * new points that intersect exactly with the edge of the clip. + * + * Example, testing 2D and 3D vertices, + * + * std::vector test = { + * {0.0f, 0.5f, 3.0f}, {0.5f, 0.5f, 3.5f}, {1.0f, 0.5f, 3.2f}, {1.5f, 0.75f, 2.8f}, {2.0f, 1.25f, 3.5f}, {3.0f, 2.0f, 4.5f}}; + * std::cout << test << " -> " << sb::wrap_curve(test, {-2.0f, -1.0f, 3.0f}, {2.0f, 1.0f, 4.0f}) << std::endl; + * std::vector test2 = {{0.0f, 0.5f}, {0.5f, 0.5f}, {1.0f, 0.5f}, {1.5f, 0.75f}, {2.0f, 1.25f}, {3.0f, 2.0f}}; + * std::cout << test2 << " -> " << sb::wrap_curve(test2, {-(16.0f / 9.0f), -1.0f}, {16.0f / 9.0f, 1.0f}) << std::endl; + * + * Prints, + * + * { {0, 0.5, 3} {0.5, 0.5, 3.5} {1, 0.5, 3.2} {1.5, 0.75, 2.8} {2, 1.25, 3.5} {3, 2, 4.5} } -> { { \ + * {0, 0.5, 3} {0.5, 0.5, 3.5} {1, 0.5, 3.2} {1.5, 0.75, 2.8} } { {1, 0.5, 4.2} {1.5, 0.75, 3.8} {2, 1.25, 4.5} } \ + * { {-2.5, -1.25, 2.8} {-2, -0.75, 3.5} {-1, 0, 4.5} } { {-2, -0.75, 2.5} {-1, 0, 3.5} } } + * { {0, 0.5} {0.5, 0.5} {1, 0.5} {1.5, 0.75} {2, 1.25} {3, 2} } -> { { {0, 0.5} {0.5, 0.5} {1, 0.5} {1.5, 0.75} {2, 1.25} } \ + * { {-2.05556, -1.25} {-1.55556, -0.75} {-0.555556, 0} } } + * + * @param vertices a vector of vertices of any dimension defining a curve + * @param clip_lower the lower corner of the clip space to wrap into + * @param clip_upper the upper corner of the clip space to wrap into + * @return a vector of curves wrapped to fit + */ + template + std::vector>> wrap_curve(const std::vector>& vertices, + const glm::vec& clip_lower, + const glm::vec& clip_upper) + { + /* Create vector of vectors to store the segments. */ + std::vector>> segments = {{}}; + + /* Create vertices for tracking which sector the unwrapped point falls in. Which sector a vertex falls in represents how many clip-sized + * spaces away from the target clip space a vertex is. When a vertex is in a different sector than the previous vertex, the wrapped + * curve needs to split into a new disjoint segment. */ + glm::vec sector, sector_prev; + + /* Create vertices for per-vertex wrapping operations. */ + glm::vec vertex, vertex_prev, wrapped; + + /* Get the difference between upper and lower clips to define the range of a clip as a vector. */ + glm::vec clip_delta = clip_upper - clip_lower; + + /* If any clip dimension is zero, throw an error because it will cause NaN to appear in the output. */ + for (glm::length_t ii = 0; ii < clip_delta.length(); ii++) + { + if (clip_delta[ii] == 0) + { + throw std::invalid_argument("Submitted clip area contains a dimension of size zero."); + } + } + + /* Iterate over all input vertices, wrapping each one. */ + for (std::size_t ii = 0; ii < vertices.size(); ii++) + { + vertex = vertices[ii]; + sector = glm::floor((vertex - clip_lower) / clip_delta); + wrapped = wrap_point(vertex, clip_lower, clip_upper); + + /* A mismatch in sector means the most recent vertex wrapped, so add a new disjoint segment. */ + if (ii > 0 && sector != sector_prev) + { + /* Use the difference in sector to calculate an unwrapped version of the point to extend the end of the segment to the edge of the clip + * and past. */ + segments.back().push_back(wrapped + (sector - sector_prev) * clip_delta); + + /* Add a new disjoint segment. */ + segments.push_back({}); + + /* Use the difference in sector to calculate an unwrapped version of the previous vertex so that the new segment begins slightly + * outside of the clip. */ + segments.back().push_back(wrap_point(vertex_prev, clip_lower, clip_upper) + (sector_prev - sector) * clip_delta); + } + + segments.back().push_back(wrapped); + sector_prev = sector; + vertex_prev = vertex; + } + + return segments; + } } diff --git a/src/utility.cpp b/src/utility.cpp deleted file mode 100644 index ee19488..0000000 --- a/src/utility.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utility.hpp" - -/* Modulus that handles negative arguments */ -int sb::mod(int a, int b) -{ - return (b + (a % b)) % b; -} diff --git a/src/utility.hpp b/src/utility.hpp deleted file mode 100644 index 87bb0ab..0000000 --- a/src/utility.hpp +++ /dev/null @@ -1,23 +0,0 @@ -/* /\ +--------------------------------------------------------------+ - ____/ \____ /| - zlib/MIT/Unlicenced game framework licensed to freely use, | - \ / / | copy, modify and sell without restriction | - +--\ ^__^ /--+ | | - | ~/ \~ | | - originally created at [http://nugget.fun] | - | ~~~~~~~~~~~~ | +--------------------------------------------------------------+ - | SPACE ~~~~~ | / - | ~~~~~~~ BOX |/ - +--------------+ - - [utility.hpp] - - For lightweight utility functions that extend C++ functionality and don't depend on - any [SPACE BOX] specific functions. - -*/ - -#pragma once - -namespace sb -{ - int mod(int, int); -}