move model, carousel and connection classes into framework library code

This commit is contained in:
ohsqueezy 2023-05-02 19:15:54 -04:00
parent b4a8895e41
commit bdadda618f
9 changed files with 46 additions and 558 deletions

View File

@ -47,9 +47,9 @@ CREATE_FONT_SYMLINK := ln -nsf $(SB_DIR)"BPmono.ttf" .
SDL_CFLAGS = $(shell $(SDLCONFIG) --cflags)
SDL_LFLAGS := $(shell $(SDLCONFIG) --libs)
SB_H_FILES := $(wildcard $(addprefix $(SB_SRC_DIR),*.hpp))
SB_O_FILES := $(filter-out $(addprefix $(SB_SRC_DIR),filesystem.o),$(SB_H_FILES:.hpp=.o))
SB_O_FILES := $(filter-out $(addprefix $(SB_SRC_DIR),filesystem.o Connection.o Carousel.o),$(SB_H_FILES:.hpp=.o))
SRC_H_FILES := $(wildcard $(addprefix $(SRC_DIR),*.hpp))
SRC_O_FILES := $(filter-out $(addprefix $(SRC_DIR),Carousel.o),$(SRC_H_FILES:.hpp=.o))
SRC_O_FILES := $(SRC_H_FILES:.hpp=.o)
#####################################################################
# Targets for building [SPACE BOX], dependencies and project source #
@ -78,8 +78,8 @@ $(SB_SRC_DIR)GLObject.o : $(addprefix $(SB_SRC_DIR),Log.hpp)
$(SB_SRC_DIR)Texture.o : $(addprefix $(SB_SRC_DIR),GLObject.hpp filesystem.hpp Log.hpp)
$(SB_SRC_DIR)VBO.o : $(addprefix $(SB_SRC_DIR),Log.hpp GLObject.hpp Attributes.hpp extension.hpp)
$(SB_SRC_DIR)Attributes.o : $(addprefix $(SB_SRC_DIR),Log.hpp extension.hpp)
$(SRC_DIR)Model.o : $(addprefix $(SB_SRC_DIR),extension.hpp Attributes.hpp Texture.hpp utility.hpp) $(addprefix $(SRC_DIR),Carousel.hpp)
$(SRC_DIR)Item.o : $(addprefix $(SB_SRC_DIR),Texture.hpp Log.hpp utility.hpp) $(addprefix $(SRC_DIR),Model.hpp Carousel.hpp)
$(SB_SRC_DIR)Model.o : $(addprefix $(SB_SRC_DIR),extension.hpp Attributes.hpp Texture.hpp utility.hpp Carousel.hpp)
$(SRC_DIR)Item.o : $(addprefix $(SB_SRC_DIR),Texture.hpp Log.hpp utility.hpp Model.hpp Carousel.hpp)
$(SRC_DIR)Pudding.o : $(SRC_H_FILES) $(SB_H_FILES)
%.o : %.cpp %.hpp
$(CXX) $(CXXFLAGS) $< -c -o $@

2
lib/sb

@ -1 +1 @@
Subproject commit f79307334847947a7d259c9c6ac0e915ef4949c4
Subproject commit a8126605e87903c598ee2781c4ce8395a9294e96

View File

@ -1,85 +0,0 @@
/* _______________ ,----------------------------------------------------------------.
//`````````````\\ \ \
//~~~~~~~~~~~~~~~\\ \ by @ohsqueezy & @sleepin \
//=================\\ \ [ohsqueezy.itch.io] [sleepin.itch.io] \
// \\ \ \
// \\ \ code released under the zlib license [git.nugget.fun/pudding] \
// GUNKISS \\ \ \
//_________________________\\ `---------------------------------------------------------------*/
#ifndef CAROUSEL_H_
#define CAROUSEL_H_
#include <cstdint>
#include <iterator>
#include "utility.hpp"
class Carousel
{
private:
std::uint32_t offset = 0;
public:
template<typename Container>
auto current(Container& container) const
{
auto location = container.begin();
if (location != container.end())
{
std::advance(location, offset);
return location;
}
else
{
throw std::out_of_range("Container is empty");
}
}
template<typename Container>
void next(const Container& container)
{
offset = ++offset % container.size();
}
template<typename Container>
void previous(const Container& container)
{
offset = sb::mod(--offset, container.size());
}
template<typename Container>
void increment(const Container& container, int amount)
{
offset = sb::mod(offset + amount, container.size());
}
void beginning()
{
offset = 0;
}
template<typename Container>
void end(const Container& container)
{
offset = container.size() - 1;
}
/* Return true if the carousel currently points to the location at the beginning of the container. */
bool at_beginning() const
{
return offset == 0;
}
/* Return true if the carousel currently points to the location at the end of the container. */
template<typename Container>
bool at_end(const Container& container)
{
return offset >= container.size() - 1;
}
};
#endif

View File

@ -104,7 +104,7 @@ std::size_t Item::texture_count()
return item_view.textures().size();
}
Plane& Item::view()
sb::Plane& Item::view()
{
return item_view;
}

View File

@ -35,8 +35,8 @@ class Item
private:
nlohmann::json json = {};
Plane item_view;
Carousel carousel;
sb::Plane item_view;
sb::Carousel carousel;
std::string item_brand_name = "", item_product_name = "", item_upc = "";
void text_property(const std::string&, std::string&, const std::string&);
@ -60,7 +60,7 @@ public:
void next_texture();
void previous_texture();
std::size_t texture_count();
Plane& view();
sb::Plane& view();
bool at_first() const;
bool at_last();
void to_first();

View File

@ -1,209 +0,0 @@
/* _______________ ,----------------------------------------------------------------.
//`````````````\\ \ \
//~~~~~~~~~~~~~~~\\ \ by @ohsqueezy & @sleepin \
//=================\\ \ [ohsqueezy.itch.io] [sleepin.itch.io] \
// \\ \ \
// \\ \ code released under the zlib license [git.nugget.fun/pudding] \
// GUNKISS \\ \ \
//_________________________\\ `---------------------------------------------------------------*/
#include "Model.hpp"
/* Default constructor for Model */
Model::Model() {};
/* Construct a Model, adding Attributes each already wrapped in a shared pointer. The attributes should
* be passed as a map with each key being a name and each value being a shared pointer to attributes. */
Model::Model(const std::map<std::string, std::shared_ptr<sb::Attributes>>& attributes_pack)
{
for (auto attributes : attributes_pack)
{
this->attributes(attributes.second, attributes.first);
}
}
/* Construct a Model, adding Attributes, which will each be wrapped in a shared pointer and stored in the
* created object. The attributes should be passed as a map with each key being a name and each value being
* an attributes object. */
Model::Model(const std::map<std::string, sb::Attributes>& attributes_pack)
{
for (auto attributes : attributes_pack)
{
this->attributes(attributes.second, attributes.first);
}
}
/* Construct a new model object by passing a list of names which will be used to initialize
* empty attributes objects with the given names */
Model::Model(const std::initializer_list<std::string>& names)
{
for (const std::string& name : names)
{
this->attributes(sb::Attributes(), name);
}
}
/* Get the entire map of attributes, each wrapped in its shared pointer held by this object.
* Can be used to iterate through the attributes. */
std::map<std::string, std::shared_ptr<sb::Attributes>>& Model::attributes()
{
return model_attributes;
}
/* Get the attributes under name, wrapped in the shared pointer held by this object. This
* function uses the at method of std::map, so name must refer to attributes already
* stored in this model. Use this function to share ownership of the attributes or to gain
* access to the public interface of the attributes. */
std::shared_ptr<sb::Attributes>& Model::attributes(const std::string& name)
{
return attributes().at(name);
}
/* Get the attributes under name, wrapped in the shared pointer held by this object. This
* function uses operator[] or std::map, so this can be used to add new attributes to the
* object if they are wrapped in a shared pointer. */
std::shared_ptr<sb::Attributes>& Model::operator[](const std::string& name)
{
auto element = attributes().find(name);
/* add an empty Attributes at name if it doesn't exist yet */
if (element == attributes().end())
{
attributes(sb::Attributes{}, name);
}
return attributes()[name];
}
/* Assign name to attributes, copy and wrap in a shared pointer. The model can share
* ownership of the created attribute memory with callers that request it. */
void Model::attributes(const sb::Attributes& attributes, const std::string& name)
{
this->attributes(std::make_shared<sb::Attributes>(attributes), name);
}
/* Assign name to attributes and share ownership. */
void Model::attributes(const std::shared_ptr<sb::Attributes>& attributes, const std::string& name)
{
this->attributes()[name] = attributes;
}
/* Enable all attributes. */
void Model::enable()
{
for (const auto& attributes : this->attributes())
{
attributes.second->enable();
}
}
/* Disable all attributes. */
void Model::disable()
{
for (const auto& attributes : this->attributes())
{
attributes.second->disable();
}
}
/* Return a reference to the texture container. */
std::map<std::string, sb::Texture>& Model::textures()
{
return model_textures;
}
/* Get the texture at name. This can be used to read the texture memory, share ownership of it, or
* anything else a Texture object can be used for with direct calls to GL functions. */
sb::Texture& Model::texture(const std::string& name)
{
return textures().at(name);
}
/* Get the default texture. The default texture must have previously been set with the default key as
* the name, which can be done using Model::texture(sb::Texture). */
sb::Texture& Model::texture()
{
return texture(DEFAULT_TEXTURE_NAME);
}
/* Assign name to texture and share ownership. */
void Model::texture(const sb::Texture& texture, const std::string& name)
{
textures()[name] = texture;
}
/* If no name is specified, use the default texture. This can be used to conveniently setup a model
* with only one texture. */
void Model::texture(const sb::Texture& texture)
{
this->texture(texture, DEFAULT_TEXTURE_NAME);
}
/* Get the model's transformation matrix. */
const glm::mat4& Model::transformation() const
{
return model_transformation;
}
/* Set the model's transformation matrix. */
void Model::transformation(const glm::mat4& transformation)
{
model_transformation = transformation;
}
/* Return the size in bytes of the sum of the attributes. */
std::size_t Model::size()
{
std::size_t sum = 0;
for (const auto& attributes : this->attributes())
{
sum += attributes.second->size();
}
return sum;
}
/* Return the transformation matrix. */
Model::operator glm::mat4() const
{
return model_transformation;
}
/* Increment offset that indicates which tile texture is active. If offset reaches the end of the container,
* start over from the beginning. */
void Background::next()
{
carousel.next(textures());
}
/* Return the currently active texture. */
sb::Texture& Background::current()
{
return carousel.current(textures())->second;
}
PlaneDoubleBuffer::PlaneDoubleBuffer() : Plane()
{
texture(sb::Texture(), "front");
texture(sb::Texture(), "back");
}
void PlaneDoubleBuffer::generate(const glm::vec2& size)
{
for (sb::Texture* buffer : {&texture("front"), &texture("back")})
{
buffer->generate(size);
}
}
sb::Texture& PlaneDoubleBuffer::active()
{
return swapped ? texture("back") : texture("front");
}
sb::Texture& PlaneDoubleBuffer::inactive()
{
return swapped ? texture("front") : texture("back");
}
void PlaneDoubleBuffer::swap()
{
swapped = !swapped;
}

View File

@ -1,124 +0,0 @@
/* _______________ ,----------------------------------------------------------------.
//`````````````\\ \ \
//~~~~~~~~~~~~~~~\\ \ by @ohsqueezy & @sleepin \
//=================\\ \ [ohsqueezy.itch.io] [sleepin.itch.io] \
// \\ \ \
// \\ \ code released under the zlib license [git.nugget.fun/pudding] \
// GUNKISS \\ \ \
//_________________________\\ `---------------------------------------------------------------*/
#ifndef MODEL_H_
#define MODEL_H_
/* GL functions */
#if defined(__EMSCRIPTEN__)
#include <GL/glew.h>
#else
#include "glew/glew.h"
#endif
#include <iostream>
#include <string>
#include <map>
#include <memory>
#include <iterator>
#include "glm/glm.hpp"
#include "Attributes.hpp"
#include "Texture.hpp"
#include "Carousel.hpp"
class Model
{
private:
inline static const std::string DEFAULT_TEXTURE_NAME = "default";
std::map<std::string, sb::Texture> model_textures;
std::map<std::string, std::shared_ptr<sb::Attributes>> model_attributes;
glm::mat4 model_transformation {1.0f};
public:
Model();
Model(const std::map<std::string, std::shared_ptr<sb::Attributes>>&);
Model(const std::map<std::string, sb::Attributes>&);
Model(const std::initializer_list<std::string>&);
std::map<std::string, std::shared_ptr<sb::Attributes>>& attributes();
std::shared_ptr<sb::Attributes>& attributes(const std::string&);
void attributes(const sb::Attributes&, const std::string&);
void attributes(const std::shared_ptr<sb::Attributes>&, const std::string&);
std::shared_ptr<sb::Attributes>& operator[](const std::string&);
void enable();
void disable();
std::map<std::string, sb::Texture>& textures();
sb::Texture& texture(const std::string&);
sb::Texture& texture();
void texture(const sb::Texture&, const std::string&);
void texture(const sb::Texture&);
const glm::mat4& transformation() const;
void transformation(const glm::mat4&);
std::size_t size();
operator glm::mat4() const;
};
class Plane : public Model
{
public:
inline const static std::shared_ptr<sb::Attributes> position = std::make_shared<sb::Attributes>(sb::Attributes{
{-1.0f, 1.0f}, {1.0f, 1.0f}, {-1.0f, -1.0f},
{1.0f, 1.0f}, {1.0f, -1.0f}, {-1.0f, -1.0f}
});
inline const static std::shared_ptr<sb::Attributes> uv = std::make_shared<sb::Attributes>(sb::Attributes{
{0.0f, 1.0f}, {1.0f, 1.0f}, {0.0f, 0.0f},
{1.0f, 1.0f}, {1.0f, 0.0f}, {0.0f, 0.0f}
});
Plane() : Model(std::map<std::string, std::shared_ptr<sb::Attributes>>({{"position", position}, {"uv", uv}})) {}
};
/*!
* A version of `Plane` that contains a `Carousel` for cycling through active `sb::Texture` objects. Only one
* `sb::Texture` is * active at a time, returned by `Background::current`. Carousel only goes forward, using
* `Background::next`.
*/
class Background : public Plane
{
private:
Carousel carousel;
public:
void next();
sb::Texture& current();
};
/*!
* A version of `Plane` which contains two texture objects, one of which is active at a time. A reference
* to the active `sb::Texture` object is available from `PlaneDoubleBuffer.active`, and the inactive object is
* available from `PlaneDoubleBuffer.inactive`. The buffers can be swapped using `PlaneDoubleBuffer.swap`.
*/
class PlaneDoubleBuffer : public Plane
{
private:
bool swapped = false;
public:
PlaneDoubleBuffer();
void generate(const glm::vec2&);
sb::Texture& active();
sb::Texture& inactive();
void swap();
};
#endif

View File

@ -217,8 +217,8 @@ void Pudding::load_gl_context()
flat_program = glCreateProgram();
glAttachShader(flat_program, vertex_shader);
glAttachShader(flat_program, fragment_shader);
Plane::position->bind(0, flat_program, "in_position");
Plane::uv->bind(1, flat_program, "vertex_uv");
sb::Plane::position->bind(0, flat_program, "in_position");
sb::Plane::uv->bind(1, flat_program, "vertex_uv");
/* load, configure and link the 3D world program */
vertex_shader = load_shader("src/shaders/mvp.vert", GL_VERTEX_SHADER);
@ -233,8 +233,8 @@ void Pudding::load_gl_context()
/* Fill VBO with attribute data */
vbo.allocate(background.size() + pudding_model.size(), GL_STATIC_DRAW);
vbo.add(*Plane::position);
vbo.add(*Plane::uv);
vbo.add(*sb::Plane::position);
vbo.add(*sb::Plane::uv);
vbo.add(*pudding_model.attributes("uv"));
vbo.add(*pudding_model.attributes("position"));
vbo.add(*pudding_model.attributes("color"));
@ -1452,6 +1452,19 @@ const bool& Request::finished() const
return is_finished;
}
/* Increment offset that indicates which tile texture is active. If offset reaches the end of the container,
* start over from the beginning. */
void Background::next()
{
carousel.next(textures());
}
/* Return the currently active texture. */
sb::Texture& Background::current()
{
return carousel.current(textures())->second;
}
/* Construct a Pad using a texture, a translation, a scale, and a callback function. A Pad is a Plane which can be clicked
* to launch an arbitrary user function. It can be sized and placed by setting the translation and scale values. The translation
* is relative to (0.0, 0.0), and the scale is relative to the Plane, which has opposite corners at (-1.0, -1.0) and (1.0, 1.0).
@ -1518,8 +1531,8 @@ void Pad::transform()
}
collision_box.size(2.0f * glm::vec2{scale.x, scale.y}, true);
collision_box.center(translation_vector);
Model::transformation(glm::translate(glm::vec3{translation_vector.x, translation_vector.y, 0.0f}) *
glm::scale(scale) * glm::rotate(rotation_angle, ROTATION_AXIS));
sb::Model::transformation(glm::translate(glm::vec3{translation_vector.x, translation_vector.y, 0.0f}) *
glm::scale(scale) * glm::rotate(rotation_angle, ROTATION_AXIS));
}
/* Set the function that will run when a pad object is clicked. */

View File

@ -60,6 +60,7 @@ using namespace emscripten;
#include "Model.hpp"
#include "utility.hpp"
#include "Box.hpp"
#include "Connection.hpp"
/*!
* This class is used for printing the running time of a function or lambda. It can be
@ -134,130 +135,22 @@ public:
};
/* A connection is an object containing a binary state of either on (connected) or off (not connected)
* and user supplied functions that run automatically on each state change. The functions each have the
* same return type, number of arguments, and argument types determined by the template arguments.
*
* 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;
*/
template<typename return_type = void, typename... arguments>
class Connection
/*!
* A version of `Plane` that contains a `Carousel` for cycling through active `sb::Texture` objects. Only one
* `sb::Texture` is * active at a time, returned by `Background::current`. Carousel only goes forward, using
* `Background::next`.
*/
class Background : public sb::Plane
{
private:
enum State : bool
{
STATE_OFF,
STATE_ON
};
using callback = std::function<return_type(arguments...)>;
State connection_state = STATE_OFF;
callback on_connect_callback, on_disconnect_callback;
sb::Carousel carousel;
public:
/* Without any arguments, the connection object will be in the disconnected state with empty functions. Otherwise,
* the supplied functions will be added to the connection object. The first function argument will be run on a
* connection, and the second function argument will be run on a disconnection. */
Connection(callback on_connect_callback = callback(), callback on_disconnect_callback = callback())
{
if (on_connect_callback)
{
on_connect(on_connect_callback);
if (on_disconnect_callback)
{
on_disconnect(on_disconnect_callback);
}
}
}
/* Set the function that will run when a connection is made. */
void on_connect(callback on_connect)
{
on_connect_callback = on_connect;
}
/* Set the function that will run when a disconnection happens. */
void on_disconnect(callback on_disconnect)
{
on_disconnect_callback = on_disconnect;
}
/* Set state to Connection::STATE_ON and run response function. If return_type is non-void and the
* connection is already connected, the function will not run and the return value will be a default
* constructed value of the type return_type. Therefore, return_type must be default constructible. */
return_type connect(arguments... args)
{
if (!*this)
{
connection_state = STATE_ON;
if (on_connect_callback)
{
return on_connect_callback(args...);
}
}
return return_type();
}
/* Set state to Connection::STATE_OFF and run response function. If return_type is non-void and the
* connection is already disconnected, the function will not run and the return value will be a default
* constructed value of the type return_type. Therefore, return_type must be default constructible. */
return_type disconnect(arguments... args)
{
if (*this)
{
connection_state = STATE_OFF;
if (on_disconnect_callback)
{
return on_disconnect_callback(args...);
}
}
return return_type();
}
/* Set state to the opposite of current state, causing the appropriate response function to run. */
return_type toggle(arguments... args)
{
if (*this)
{
return disconnect(args...);
}
else
{
return connect(args...);
}
}
/* Return true if state is Connection::STATE_ON, false otherwise. */
bool connected()
{
return connection_state;
}
/* When called as a boolean, return the connection state. */
operator bool()
{
return connected();
}
void next();
sb::Texture& current();
};
@ -281,14 +174,14 @@ public:
* std::cout << p.transformation() << std::endl << final_position << std::endl;
* assert(final_position == glm::vec4({w.x, w.y, 0, 1}));
*/
class Pad : public Plane
class Pad : public sb::Plane
{
private:
inline static const glm::vec3 ROTATION_AXIS {0.0f, 0.0f, 1.0f};
using callback = std::function<void()>;
Connection<> connection;
sb::Connection<> connection;
Box collision_box;
float rotation_angle = 0.0f, scale_factor = 0.0f, scale_ratio = 1.0f;
glm::vec2 translation_vector {0.0f, 0.0f};
@ -427,7 +320,7 @@ private:
std::string current_barcode, previous_barcode, current_config_barcode;
std::vector<Item> items;
Item incoming_item;
Carousel item_carousel;
sb::Carousel item_carousel;
int effect_id = EFFECT_NONE, pudding_triangle_vertex_count = 0, pudding_fan_vertex_count = 0;
#ifndef __EMSCRIPTEN__
cv::VideoCapture capture;
@ -440,8 +333,8 @@ private:
std::map<std::string, std::map<std::string, GLuint>> uniform;
GLuint flat_program, mvp_program;
glm::mat4 projection, model {1.0f}, mvp;
Model pudding_model;
Plane plane, camera_view;
sb::Model pudding_model;
sb::Plane plane, camera_view;
Background background;
bool show_item = false;
sb::VAO vao;
@ -583,7 +476,7 @@ private:
#endif
/* Open camera on connection and close on disconnection. */
Connection<> camera_switch {
sb::Connection<> camera_switch {
std::bind(&Pudding::open_camera, this),
std::bind(&Pudding::close_camera, this)
};
@ -608,7 +501,7 @@ class Button
private:
Connection<return_type, arguments...> connection;
sb::Connection<return_type, arguments...> connection;
/* float threshold = 1.0f */
/* float force = 0.0f */