added performance timing to camera thread and temporarily fixed crashing by setting thread to wait, added README

This commit is contained in:
ohsqueezy 2022-08-06 19:03:54 -04:00
parent cecf008d5e
commit c500cd332e
10 changed files with 230 additions and 62 deletions

View File

@ -1,14 +1,14 @@
# _______________ ,----------------------------------------------------------------.
# //`````````````\\ \ \
# //~~~~~~~~~~~~~~~\\ \ by @ohsqueezy & @sleepin \
# //=================\\ \ [ohsqueezy.itch.io] [sleepin.itch.io] \
# // \\ \ \
# // \\ \ code released under the zlib license [git.nugget.fun/pudding] \
# // ☆ GUNKISS ☆ \\ \ \
# //_________________________\\ `----------------------------------------------------------------'
# _______________ ,-------------------------------------------------------------------.
# //`````````````\\ \ \
# //~~~~~~~~~~~~~~~\\ \ by @ohsqueezy & @sleepin \
# //=================\\ \ [ohsqueezy.itch.io] [sleepin.itch.io] \
# // \\ \ \
# // \\ \ code released under zlib license [git.nugget.fun/nugget/gunkiss] \
# // ☆ GUNKISS ☆ \\ \ \
# //_________________________\\ `-------------------------------------------------------------------'
#
# This Makefile should build a Linux binary from `make linux`. Other platform targets have not been tested yet.
# It requires the [SPACE BOX] framework to be located in the paths that can be configured in the code block
# It requires the [SPACEBOX] framework to be located in the paths that can be configured in the code block
# below.
#######################
@ -18,8 +18,8 @@
# Location of project specific source files
SRC_DIR := src/
# Locations of [SPACE BOX] source and dependencies required to be compiled from source. These
# locations are configured to match the structure of the [SPACE BOX] repository but can be
# Locations of [SPACEBOX] source and dependencies required to be compiled from source. These
# locations are configured to match the structure of the [SPACEBOX] repository but can be
# modified as necessary.
SB_DIR := lib/sb/
SB_SRC_DIR := $(SB_DIR)src/
@ -90,13 +90,34 @@ linux : CPPC = clang++
linux : CFLAGS = -g -Wall -Wextra -O0 -c -I$(SB_LIB_DIR) -I$(SB_SRC_DIR) $(SDL_CFLAGS) -I$(HOME)/local/zbar/include \
-I$(HOME)/local/opencv/include/opencv4
linux : CPP_FLAGS = $(CFLAGS) --std=c++17
linux : LFLAGS = $(SDL_LFLAGS) -lpthread -lGL -lGLESv2 -lSDL2_image -lSDL2_ttf -lSDL2_mixer -lcurl -lstdc++fs \
-L/home/frank/local/opencv/lib -Wl,-rpath,/home/frank/local/opencv/lib -Wl,--enable-new-dtags -lopencv_videoio -lopencv_core \
-lopencv_highgui -lopencv_imgproc -L/home/frank/local/zbar/lib -Wl,-rpath,/home/frank/local/zbar/lib -Wl,--enable-new-dtags -lzbar
linux : LFLAGS = $(SDL_LFLAGS) -Wl,--enable-new-dtags -lpthread -lGL -lGLESv2 -lSDL2_image -lSDL2_ttf -lSDL2_mixer -lstdc++fs -lcurl \
-L$(HOME)/local/opencv/lib -Wl,-rpath,$(HOME)/local/opencv/lib -lopencv_videoio -lopencv_core -lopencv_highgui -lopencv_imgproc \
-L$(HOME)/local/zbar/lib -Wl,-rpath,$(HOME)/local/zbar/lib -lzbar
linux : $(GLEW_DIR)glew.o $(addprefix $(SDLGFX2_DIR),SDL2_rotozoom.o SDL2_gfxPrimitives.o) \
$(SB_O_FILES) $(SRC_O_FILES)
$(CREATE_FONT_SYMLINK)
$(CPPC) $(LFLAGS) -D__LINUX__ $^ -o pudding
$(CPPC) $^ $(LFLAGS) -D__LINUX__ -o pudding
#############
# Web build #
#############
# Use Emscripten to output JavaScript and an HTML index page for running in the browser
EMSCRIPTENHOME = $(HOME)/ext/software/emsdk/upstream/emscripten
EMSCRIPTEN_CFLAGS = -O3 -Wall -s USE_SDL=2 -s USE_SDL_IMAGE=2 -s SDL2_IMAGE_FORMATS="['png', 'jpg']" \
-s USE_SDL_TTF=2 -s USE_SDL_MIXER=2 -s MAX_WEBGL_VERSION=1 -s EXPORTED_FUNCTIONS="['_main']" \
-s ALLOW_MEMORY_GROWTH=1 -s GL_PREINITIALIZED_CONTEXT=1 -s ENVIRONMENT=web --shell-file shell_minimal.html \
--no-heap-copy -I$(SFW_LIB_DIR) -I$(SFW_SRC_DIR)
EMSCRIPTEN_PRELOADS = --preload-file "BPmono.ttf"@/ --preload-file "config.json" --preload-file "resource"
emscripten : CC = $(EMSCRIPTENHOME)/emcc
emscripten : CPPC = $(EMSCRIPTENHOME)/em++
emscripten : CFLAGS = $(EMSCRIPTEN_CFLAGS)
emscripten : CPP_FLAGS = $(CFLAGS) --std=c++17
emscripten : $(addprefix $(SDLGFX2_DIR),SDL2_rotozoom.o SDL2_gfxPrimitives.o) $(SB_O_FILES) $(SRC_O_FILES)
$(CREATE_FONT_SYMLINK)
$(CPPC) $(CPP_FLAGS) $(EMSCRIPTEN_PRELOADS) $^ -o "index.html"
#########################
# Clean up object files #
@ -130,32 +151,17 @@ compiledb :
# and OS/X, but cross-compilation targets have not been tested in a while and won't compile without significant changes.
BUILDDIR := build
EXT_SRC_ROOT := /home/frank/ext/software
SDLHOME := $(EXT_SRC_ROOT)/SDL2-2.0.14
SDL_IMG_HOME := $(EXT_SRC_ROOT)/SDL2_image-2.0.4
SDL_TTF_HOME := $(EXT_SRC_ROOT)/SDL2_ttf-2.0.14
GLEW_WIN32_HOME = $(EXT_SRC_ROOT)/glew-2.1.0-win32
PROJECTHOME = $(shell pwd)
EMSCRIPTENHOME = $(EXT_SRC_ROOT)/emsdk/upstream/emscripten
WINBUILDDIR = win
SDLMINGWHOME = $(SDLHOME)/i686-w64-mingw32
SDL_IMG_MINGW_HOME = $(SDL_IMG_HOME)/i686-w64-mingw32
SDL_TTF_MINGW_HOME = $(SDL_TTF_HOME)/i686-w64-mingw32
APPDIR = Main.app/Contents
SYSFWPATH = /Library/Frameworks
EMSCRIPTEN_CFLAGS = -O3 -Wall -s USE_SDL=2 -s USE_SDL_IMAGE=2 -s SDL2_IMAGE_FORMATS="['png']" \
-s USE_SDL_TTF=2 -s USE_SDL_MIXER=2 -s MAX_WEBGL_VERSION=1 -s EXTRA_EXPORTED_RUNTIME_METHODS="['ccall']" \
-s EXPORTED_FUNCTIONS="['_main', '_reset_game']" -s ALLOW_MEMORY_GROWTH=1 -s GL_PREINITIALIZED_CONTEXT=1 \
-s ENVIRONMENT=web --shell-file shell_minimal.html --no-heap-copy -I$(SFW_LIB_DIR) -I$(SFW_SRC_DIR)
EMSCRIPTEN_PRELOADS = --preload-file "BPmono.ttf"@/ --preload-file "config.json" --preload-file "resource"
emscripten : CC = $(EMSCRIPTENHOME)/emcc
emscripten : CPPC = $(EMSCRIPTENHOME)/em++
emscripten : CFLAGS = $(EMSCRIPTEN_CFLAGS)
emscripten : CPP_FLAGS = $(CFLAGS) --std=c++17
emscripten : $(addprefix $(SDLGFX2_DIR),SDL2_rotozoom.o SDL2_gfxPrimitives.o) $(SFW_O_FILES) $(GAME_O_FILES)
$(CREATE_FONT_SYMLINK)
$(CPPC) $(CPP_FLAGS) $(EMSCRIPTEN_PRELOADS) $^ -o "index.html"
android :
if [ ! -d $(BUILDDIR) ]; then mkdir $(BUILDDIR); fi;

53
README.md Normal file
View File

@ -0,0 +1,53 @@
```
_______________ ,--------------------------------------------------------.
//`````````````\\ \ \
//~~~~~~~~~~~~~~~\\ \ by @ohsqueezy & @sleepin \
//=================\\ \ [ohsqueezy.itch.io] [sleepin.itch.io] \
// \\ \ \
// \\ \ zlib licensed code at [git.nugget.fun/nugget/gunkiss] \
// ☆ GUNKISS ☆ \\ \ \
//_________________________\\ `--------------------------------------------------------'
```
Gunkiss
=======
Generate a custom pudding from food product UPC codes and help a pair of rats take over the video game industry, using their extraterrestrial ability to turn trash into performance enhancing drug puddings that enable business professionals to predict the stock market with supernatural accuracy.
Where to play
-------------
Gunkiss is in-development. Public builds for the web and Android are expected to be released soon.
Libraries
---------
The following external libraries are required to compile the game.
### cURL
The game uses [cURL][] to communicate with the [Open Food Facts][] API. The cURL library and development headers must be installed to the system paths in order to compile the program.
#### Linux
On Linux, you can install libcurl and development headers through the package manager. For example, on Debian:
sudo apt install libcurl4 libcurl4-openssl-dev
#### Other systems
See [the downloads page][] for ways to get the library and headers. It must be installed in such a way that linking can be done with `-lcurl` and headers are in the default search path.
### OpenCV
The game uses [OpenCV][] to enable barcode reading through the camera. The library and headers by default are expected to be installed to `[HOME]/local/opencv/lib` and `[HOME]/local/opencv/headers` respectively, but those locations can be edited in the [Makefile][]
### ZBar
The game uses [ZBar][] to scan images for barcodes and report what the barcode is. The library and headers by default are expected to be installed to `[HOME]/local/zbar/lib` and `[HOME]/local/zbar/headers` respectively, but those locations can be edited in the [Makefile][].
[cURL]: https://curl.se/
[Open Food Facts]: https://world.openfoodfacts.org/
[OpenCV]: https://opencv.org/
[ZBar]: http://zbar.sourceforge.net/
[Makefile]: Makefile

View File

@ -39,16 +39,18 @@
"log":
{
"enabled": true,
"output-directory": "local",
"output-directory": "/var/log/sb/",
"debug-to-stdout": false,
"debug-to-file": true
"debug-to-file": true,
"info-file-name": "gunkiss_info.log",
"debug-file-name": "gunkiss_debug.log"
},
"scan":
{
"enabled": true,
"json-save": true,
"json-save-directory": "local/scans",
"barcode": "014100085980",
"barcode": "0140231056",
"capture-device": "/dev/video0"
},
"api":
@ -62,7 +64,7 @@
"giantbomb-api-key": "91a395231f4e1fd9f9ba8840c52a61cda343cd70",
"nutronix-enabled": false,
"edamam-enabled": true,
"open-food-enabled": true,
"open-food-enabled": false,
"open-products-enabled": true,
"best-buy-enabled": true,
"google-books-enabled": true
@ -87,11 +89,11 @@
"main-button-y": -0.75,
"main-button-single-x": 0.0,
"main-button-double-x": 0.6,
"main-button-scale": 0.25,
"main-button-scale": 0.3,
"camera-button-label": "scan",
"inventory-button-label": "inventory",
"arrow-button-location": [0.75, 0.0],
"arrow-button-scale": 0.2,
"arrow-button-scale": 0.3,
"arrow-button-label": "arrow",
"pop-up-viewport-height": 0.6
}

2
lib/sb

@ -1 +1 @@
Subproject commit 86ca3eabec1d46a5a56d004fb5f3abd90304b9a8
Subproject commit 21e1e7e707004fee91425cb5aef099a19b3fb4ff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.9 KiB

After

Width:  |  Height:  |  Size: 212 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 241 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 227 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

After

Width:  |  Height:  |  Size: 234 KiB

View File

@ -1,18 +1,18 @@
/* _______________ ,----------------------------------------------------------------.
//`````````````\\ \ \
//~~~~~~~~~~~~~~~\\ \ by @ohsqueezy & @sleepin \
//=================\\ \ [ohsqueezy.itch.io] [sleepin.itch.io] \
// \\ \ \
// \\ \ code released under the zlib license [git.nugget.fun/pudding] \
// GUNKISS \\ \ \
//_________________________\\ `----------------------------------------------------------------'
/* _______________ ,-------------------------------------------------------------------.
//`````````````\\ \ \
//~~~~~~~~~~~~~~~\\ \ by @ohsqueezy & @sleepin \
//=================\\ \ [ohsqueezy.itch.io] [sleepin.itch.io] \
// \\ \ \
// \\ \ code released under zlib license [git.nugget.fun/nugget/gunkiss] \
// GUNKISS \\ \ \
//_________________________\\ `-------------------------------------------------------------------'
Generate a custom pudding from food product UPC codes and help a pair of rats take over the video game industry, using
their extraterrestrial ability to turn trash into performance enhancing drug puddings that enable business professionals
to predict the stock market with supernatural accuracy.
*/
#include "Pudding.hpp"
/* Launch the Pudding instance's mainloop */
@ -36,7 +36,7 @@ Pudding::Pudding()
/* set up pudding model */
nlohmann::json pudding = get_configuration()["pudding"];
load_pudding_model(pudding["top-radius"], pudding["base-radius"], pudding["ring-vertex-count"], pudding["layer-count"],
pudding["y-range"][0], pudding["y-range"][1], pudding["gradient-position"]);
pudding["y-range"][0], pudding["y-range"][1], pudding["gradient-position"]);
/* loading GL context instead of SDL context for 3D */
load_gl_context();
load_tiles();
@ -46,8 +46,8 @@ Pudding::Pudding()
}
/* Assign vertices, colors and texture UV coordinates to the pudding model */
void Pudding::load_pudding_model(
float top_radius, float base_radius, int ring_vertex_count, int layer_count, float min_y, float max_y, float gradient_position)
void Pudding::load_pudding_model(float top_radius, float base_radius, int ring_vertex_count, int layer_count, float min_y,
float max_y, float gradient_position)
{
size_t ii;
const glm::vec3 *layer_top_color, *layer_bottom_color;
@ -159,20 +159,23 @@ void Pudding::load_pudding_model(
void Pudding::load_gl_context()
{
super::load_gl_context();
/* create another GL context for loading camera frame textures */
SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1);
/* Create another GL context for loading camera frame textures */
if ((capture_frame_thread_context = SDL_GL_CreateContext(window)) == nullptr)
{
sb::Log::log("could not create capture frame thread context");
}
/* Generate a vertex array object ID, bind it as current (requirement of OpenGL) */
vao.generate();
vao.bind();
/* Generate ID for the vertex buffer object that will hold all vertex data. Since we're using one buffer, data
* will be copied in one after the other, offset to after the previous data location. The same buffer offset will
* be passed to the vertex attributes for each data. */
vbo.generate();
vbo.bind();
/* Load two shader programs, one for rendering the flat objects, and one for rendering the 3D model. Load and configure
* the flat shader program first. */
GLuint vertex_shader = load_shader("src/flat.vert", GL_VERTEX_SHADER);
@ -182,6 +185,7 @@ void Pudding::load_gl_context()
glAttachShader(flat_program, fragment_shader);
Plane::position->bind(0, flat_program, "in_position");
Plane::uv->bind(1, flat_program, "vertex_uv");
/* load, configure and link the 3D world program */
vertex_shader = load_shader("src/mvp.vert", GL_VERTEX_SHADER);
fragment_shader = load_shader("src/mvp.frag", GL_FRAGMENT_SHADER);
@ -192,6 +196,7 @@ void Pudding::load_gl_context()
pudding_model.attributes("uv")->bind(3, mvp_program, "vertex_uv");
pudding_model.attributes("color")->bind(4, mvp_program, "vertex_color");
sb::Log::gl_errors("after loading shaders");
/* Fill VBO with attribute data */
vbo.allocate(background.size() + pudding_model.size(), GL_STATIC_DRAW);
vbo.add(*Plane::position);
@ -200,10 +205,12 @@ void Pudding::load_gl_context()
vbo.add(*pudding_model.attributes("position"));
vbo.add(*pudding_model.attributes("color"));
sb::Log::gl_errors("after filling VBO");
/* link shaders */
link_shader(flat_program);
link_shader(mvp_program);
sb::Log::gl_errors("after linking");
/* store uniform locations after linking */
uniform["flat"]["texture"] = glGetUniformLocation(flat_program, "base_texture");
uniform["flat"]["time"] = glGetUniformLocation(flat_program, "time");
@ -216,9 +223,10 @@ void Pudding::load_gl_context()
uniform["mvp"]["uv transformation"] = glGetUniformLocation(mvp_program, "uv_transformation");
uniform["mvp"]["coordinate bound"] = glGetUniformLocation(mvp_program, "coordinate_bound");
uniform["mvp"]["pudding texture"] = glGetUniformLocation(mvp_program, "pudding_texture");
/* enable alpha rendering */
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable( GL_BLEND );
glEnable(GL_BLEND);
sb::Log::gl_errors("after uniform locations");
}
@ -815,10 +823,11 @@ bool Pudding::item_display_active() const
* on which is not currently in use */
int Pudding::capture_frame(void* game)
{
time_it("total thread")([&]{
Pudding* pudding = reinterpret_cast<Pudding*>(game);
/* Make the thread context the current context (not sure what that means, but it doesn't seem to conflict
* with the main rendering context) */
if (SDL_GL_MakeCurrent(pudding->window, pudding->capture_frame_thread_context) < 0)
if (time_it<int>("make current")([&] { return SDL_GL_MakeCurrent(pudding->window, pudding->capture_frame_thread_context); }) < 0)
{
sb::Log::log("error making thread context current");
}
@ -827,26 +836,35 @@ int Pudding::capture_frame(void* game)
if (pudding->capture.isOpened())
{
cv::Mat frame;
time_it("read frame")([&]{
pudding->capture.read(frame);
});
if (!frame.empty())
{
time_it("flip")([&]{
/* rotate the opencv matrix 180 to work with opengl coords */
cv::flip(frame, frame, -1);
});
time_it("load texture")([&]{
/* use whichever texture ID is not being used by the main rendering thread */
sb::Texture& texture = pudding->camera_view.free();
/* bind texture for accepting pixel data */
texture.bind();
/* fill texture memory with last frame's pixels */
texture.load(frame.ptr(), {frame.cols, frame.rows}, GL_BGR, GL_UNSIGNED_BYTE);
});
pudding->camera_view.swap();
if (pudding->get_configuration()["scan"]["enabled"])
{
time_it("gray")([&]{
/* convert to gray and scan with zbar */
cv::cvtColor(frame, frame, cv::COLOR_BGR2GRAY);
});
zbar::Image query_image(frame.cols, frame.rows, "Y800", static_cast<void*>(frame.data), frame.cols * frame.rows);
int result = pudding->image_scanner.scan(query_image);
if (result > 0)
{
time_it("barcode lookup")([&] {
for (zbar::Image::SymbolIterator symbol = query_image.symbol_begin(); symbol != query_image.symbol_end(); ++symbol)
{
std::ostringstream message;
@ -855,6 +873,7 @@ int Pudding::capture_frame(void* game)
pudding->current_camera_barcode = symbol->get_data();
pudding->current_barcode = pudding->current_camera_barcode;
}
});
}
query_image.set_data(nullptr, 0);
}
@ -865,6 +884,9 @@ int Pudding::capture_frame(void* game)
sb::Log::gl_errors("in capture thread, after capturing frame");
}
pudding->reading_capture_frame = false;
// using namespace std::chrono_literals;
// std::this_thread::sleep_for(2s);
});
return 0;
}
@ -873,17 +895,20 @@ void Pudding::update()
{
/* number of seconds we've been running for */
float time_seconds = SDL_GetTicks() / 1000.0f;
/* launch the camera capture thread if it is not currently running */
if (capture.isOpened() && !reading_capture_frame)
{
SDL_Thread* capture_thread = SDL_CreateThread(Pudding::capture_frame, "capture frame", reinterpret_cast<void*>(this));
if (capture_thread == nullptr)
/* launch the camera capture thread if it is not currently running */
if (capture.isOpened() && !reading_capture_frame)
{
sb::Log::log("could not create capture thread");
}
else
{
reading_capture_frame = true;
SDL_Thread* capture_thread = SDL_CreateThread(Pudding::capture_frame, "capture frame", reinterpret_cast<void*>(this));
if (capture_thread == nullptr)
{
sb::Log::log("could not create capture thread");
}
else
{
reading_capture_frame = true;
SDL_WaitThread(capture_thread, nullptr);
}
}
}
sb::Log::gl_errors("in main thread, after capturing frame");

View File

@ -18,9 +18,11 @@
#include <iostream>
#include <algorithm>
#include <thread>
#include <mutex>
#include <memory>
#include <stdexcept>
#include <functional>
#include <chrono>
#include <curl/curl.h>
#include "SDL.h"
#include "SDL_image.h"
@ -48,6 +50,79 @@
#include "utility.hpp"
#include "Box.hpp"
/*!
* This class is used for printing the running time of a function or lambda. It can be
* inserted inline into existing code by wrapping the block to time in a lambda. It can also be
* given a function and optionally the function's arguments and will return a value of the type specified.
*
* The following code
*
* ~~~{.cpp}
* std::string fake(std::string x, std::string y)
* {
* return x + y;
* }
*
* // Needs SPACEBOX initialization for log call
* int ii = 0;
* time_it()([&] { while (ii++ < 1000); });
* ii = 0;
* int revolving = time_it<int, int>("for revolving")([&](int x) -> int { while (ii++ < 10000); return x; }, 1);
* ii = 0;
* std::cout << "Revolving is " << revolving << std::endl;
* std::string addition = time_it<std::string, std::string, std::string>("for combination")(fake, "Hello, ", "World!");
* std::cout << "Combination is " << addition << std::endl;
* time_it("for many combinations")([&] { while (ii++ < 100000) { fake("a", "b"); } });
* ~~~
*
* gives the expected output
*
* Elapsed time: 2.588e-06s
* Elapsed time for revolving: 2.1547e-05s
* Revolving is 1
* Elapsed time for combination: 7.27e-07s
* Combination is Hello, World!
* Elapsed time for many combinations: 0.0107351s
*/
template<typename return_type = void, typename... arguments>
class time_it
{
private:
std::string info = "";
public:
time_it(std::string info = "") : info(info) {};
return_type operator()(const std::function<return_type(arguments...)>&& block, arguments... args)
{
std::cout << "{Elapsed time";
if (!info.empty())
{
std::cout << " " << info;
}
std::cout << ": ";
auto start = std::chrono::steady_clock::now();
std::chrono::duration<float> elapsed;
if constexpr (std::is_same_v<return_type, void>)
{
block(args...);
elapsed = std::chrono::steady_clock::now() - start;
std::cout << elapsed.count() << "s}" << std::endl;
}
else
{
return_type result = block(args...);
elapsed = std::chrono::steady_clock::now() - start;
std::cout << elapsed.count() << "s}" << std::endl;
return result;
}
}
};
/* 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.
@ -244,7 +319,10 @@ private:
UV_SQUIRCLE
};
/* Convention for calling parent class in a consistent way across classes */
typedef Game super;
/* Constants */
inline static const std::string OPEN_FOOD_API_URL = "https://world.openfoodfacts.org/api/v0/product/";
inline static const std::string OPEN_PRODUCTS_API_URL = "https://world.openproductsfacts.org/api/v0/product/";
inline static const std::string NUTRONIX_API_URL = "https://trackapi.nutritionix.com/v2/search/item?upc=";
@ -259,6 +337,8 @@ private:
inline static const glm::mat4 VIEW_MATRIX = glm::lookAt({4.0f, 2.0f, 1.0f}, {0.0f, -0.325f, 0.0f}, Y_UNIT_NORMAL_3D);
inline static const glm::vec3 PUDDING_BROWN {0.713f, 0.359f, 0.224f};
inline static const glm::vec3 PUDDING_YELLOW {0.878f, 0.859f, 0.122f};
/* Member variables */
std::shared_ptr<SDL_Cursor> poke;
std::string current_barcode, previous_barcode, current_config_barcode, current_camera_barcode;
std::vector<Item> items;
@ -280,6 +360,8 @@ private:
std::map<std::string, sb::Texture> labels;
Pad camera_button, previous_button, next_button, inventory_button;
Box viewport, main_viewport, pop_up_viewport;
std::mutex camera_mutex;
SDL_Thread* capture_thread = nullptr;
void load_pudding_model(float, float, int, int = 1, float = -1.0f, float = 1.0f, float = 0.3f);
void load_gl_context();