- add function for wrapping an arbitrary point or curve into an arbitrary clip space

- remove custom mod functions in favor of glm::mod
- deactivate recorder object during game construction after full config is loaded
This commit is contained in:
ohsqueezy 2023-06-05 18:22:43 -04:00
parent 824efcc71f
commit 1ca956b5ac
12 changed files with 187 additions and 90 deletions

View File

@ -145,17 +145,7 @@ namespace sb
* Construct a new Attributes object with vertices set to the vertices contained in this vector
*/
template<typename Type>
Attributes(const std::vector<Type>& 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<Type>& vertices) : vertices(vertices) {}
/*!
* Add a vertex. The vertex can be any of the variant types.

View File

@ -12,6 +12,11 @@
#include <cstdint>
#include <iterator>
#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<typename Container>
void previous(const Container& container)
{
offset = sb::mod(--offset, container.size());
offset = glm::mod(--offset, container.size());
}
template<typename Container>
void increment(const Container& container, int amount)
{
offset = sb::mod(offset + amount, container.size());
offset = glm::mod(offset + amount, container.size());
}
void beginning()

View File

@ -1,40 +1,40 @@
/* /\ +------------------------------------------------------+
* ____/ \____ /| - Open source game framework licensed to freely use, |
* \ / / | copy, modify and sell without restriction |
* +--\ ^__^ /--+ | |
* | ~/ \~ | | - created for <https://foam.shampoo.ooo> |
* | ~~~~~~~~~~~~ | +------------------------------------------------------+
* | 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<int, int, int> connection_f {
* std::function<int(int, int)>(&sb::mod), std::function<int(int, int)>(&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 <https://foam.shampoo.ooo> |
| ~~~~~~~~~~~~ | +------------------------------------------------------+
| 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<int, int, int> connection_f {
std::function<int(int, int)>(&glm::mod), std::function<int(int, int)>(&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

View File

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

View File

@ -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<std::uint8_t*>(source);
if (x < 0 || x >= rect.w)
{
x = sb::mod(x, static_cast<int>(rect.w));
x = glm::mod(x, static_cast<int>(rect.w));
}
if (y < 0 || y >= rect.y)
{
y = sb::mod(y, static_cast<int>(rect.h));
y = glm::mod(y, static_cast<int>(rect.h));
}
return reinterpret_cast<T>(access + y * get_bytes_per_row() + x * format->BytesPerPixel);
}

View File

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

View File

@ -12,6 +12,11 @@
#include <cstdint>
#include <iterator>
#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()

View File

@ -209,7 +209,7 @@ std::vector<SDL_Texture*> 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);

View File

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

View File

@ -49,6 +49,7 @@ Should print,
#pragma once
#include <vector>
#include <stdexcept>
/* 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<glm::vec2> bezier(const std::vector<glm::vec2>& 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::length_t dimensions, typename Type, glm::qualifier qualifier>
glm::vec<dimensions, Type, qualifier> wrap_point(const glm::vec<dimensions, Type, qualifier>& vertex,
const glm::vec<dimensions, Type, qualifier>& clip_lower,
const glm::vec<dimensions, Type, qualifier>& 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<glm::vec3> 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<glm::vec2> 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<glm::length_t dimensions, typename Type, glm::qualifier qualifier>
std::vector<std::vector<glm::vec<dimensions, Type, qualifier>>> wrap_curve(const std::vector<glm::vec<dimensions, Type, qualifier>>& vertices,
const glm::vec<dimensions, Type, qualifier>& clip_lower,
const glm::vec<dimensions, Type, qualifier>& clip_upper)
{
/* Create vector of vectors to store the segments. */
std::vector<std::vector<glm::vec<dimensions, Type, qualifier>>> 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<dimensions, Type, qualifier> sector, sector_prev;
/* Create vertices for per-vertex wrapping operations. */
glm::vec<dimensions, Type, qualifier> vertex, vertex_prev, wrapped;
/* Get the difference between upper and lower clips to define the range of a clip as a vector. */
glm::vec<dimensions, Type, qualifier> 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;
}
}

View File

@ -1,7 +0,0 @@
#include "utility.hpp"
/* Modulus that handles negative arguments */
int sb::mod(int a, int b)
{
return (b + (a % b)) % b;
}

View File

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