added rotation to pad transformation, added pointer cursor

This commit is contained in:
frank 2021-11-13 14:58:30 -05:00
parent 80aebaf8de
commit e0b1d1fd53
8 changed files with 132 additions and 76 deletions

View File

@ -79,6 +79,19 @@
}, },
"resource": "resource":
{ {
"tile-path": "resource/tile" "tile-path": "resource/tile",
"button-path": "resource/button"
},
"interface":
{
"main-button-y": -0.75,
"main-button-single-x": 0.0,
"main-button-double-x": 0.65,
"main-button-scale": 0.25,
"camera-button-label": "scan",
"inventory-button-label": "inventory",
"arrow-button-location": [0.75, 0.0],
"arrow-button-scale": 0.1,
"arrow-button-label": "arrow"
} }
} }

2
lib/sb

@ -1 +1 @@
Subproject commit 03d179eed4c8323576157f806806b214e42d07c7 Subproject commit 54cf01246b0e5ec81ba5b9158248bca7492823db

BIN
resource/button/arrow.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

BIN
resource/button/home.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

BIN
resource/button/scan.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

View File

@ -29,33 +29,24 @@ Pudding::Pudding()
{ {
/* subscribe to command events */ /* subscribe to command events */
get_delegate().subscribe(&Pudding::respond, this); get_delegate().subscribe(&Pudding::respond, this);
get_delegate().subscribe(&Pudding::respond, this, SDL_MOUSEMOTION);
get_delegate().subscribe(&Pudding::respond, this, SDL_MOUSEBUTTONDOWN); get_delegate().subscribe(&Pudding::respond, this, SDL_MOUSEBUTTONDOWN);
/* initialize a zbar image scanner for reading barcodes of any format */ /* initialize a zbar image scanner for reading barcodes of any format */
image_scanner.set_config(zbar::ZBAR_NONE, zbar::ZBAR_CFG_ENABLE, 1); image_scanner.set_config(zbar::ZBAR_NONE, zbar::ZBAR_CFG_ENABLE, 1);
/* set up pudding model */ /* set up pudding model */
nlohmann::json pudding = get_configuration()["pudding"]; nlohmann::json pudding = get_configuration()["pudding"];
set_pudding_model(pudding["top-radius"], pudding["base-radius"], pudding["ring-vertex-count"], pudding["layer-count"], 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 */ /* loading GL context instead of SDL context for 3D */
load_gl_context(); load_gl_context();
load_tiles(); load_tiles();
glm::vec3 w = glm::mat3({{1, 0, 0}, {0, 1, 0}, {-0.6739, -0.74, 1}}) * glm::mat3({{.1, 0, 0}, {0, .1 * (460.0 / 768.0), 0}, {0, 0, 1}}) * load_pads();
glm::vec3({-1, -1, 1}); /* Load a pointer cursor from the system library that will be freed automatically */
std::cout << w << std::endl << glm::translate(glm::vec3{-0.6739, -0.74, 0}) * poke = std::shared_ptr<SDL_Cursor>(SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_HAND), SDL_FreeCursor);
glm::scale(glm::vec3{.1, .1 * (460.0 / 768.0), 1}) * glm::vec4{-1, -1, 0, 1} << std::endl;
Pad p {background.current(), {-0.6739f, -0.74f}, 0.1f, get_display().window_box().aspect(), std::function<void()>()};
const std::vector<glm::vec2>& p_position = *p.attributes("position");
glm::vec4 final_position = p.transformation() * glm::vec4{p_position[2].x, p_position[2].y, 0, 1};
std::cout << p.transformation() << std::endl << final_position << std::endl;
assert(final_position == glm::vec4({w.x, w.y, 0, 1}));
sb::Texture label {"local/button/scan.png"};
label.load();
pad.texture(label);
pad.transform({-0.6739f, -0.74f}, 0.25f, get_display().window_box().aspect());
} }
/* Assign vertices, colors and texture UV coordinates to the pudding model */ /* Assign vertices, colors and texture UV coordinates to the pudding model */
void Pudding::set_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) 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; size_t ii;
@ -231,17 +222,31 @@ void Pudding::load_gl_context()
sb::Log::gl_errors("after uniform locations"); sb::Log::gl_errors("after uniform locations");
} }
/* Read every resource/tile/.*.jpg into a GL texture, storing the texture pointer and file name in a std::map */ /* Read every jpg in the folder at tile path into a GL texture and associate with the background object. */
void Pudding::load_tiles() void Pudding::load_tiles()
{ {
for (fs::path path : sb::glob(get_configuration()["resource"]["tile-path"].get<fs::path>() / ".*.jpg")) for (fs::path path : sb::glob(get_configuration()["resource"]["tile-path"].get<fs::path>() / ".*.jpg"))
{ {
sb::Texture texture = sb::Texture(path); sb::Texture texture {path};
texture.load(); texture.load();
background.texture(texture, path); background.texture(texture, path);
} }
} }
/* Load every png in the button path as a Texture and add to a map. */
void Pudding::load_pads()
{
for (fs::path path : sb::glob(get_configuration()["resource"]["button-path"].get<fs::path>() / ".*.png"))
{
labels[path.stem()] = sb::Texture(path);
labels[path.stem()].load();
}
nlohmann::json interface = get_configuration()["interface"];
camera_button.texture(labels["scan"]);
camera_button.transform({interface["main-button-single-x"], interface["main-button-y"]},
interface["main-button-scale"], window_box().aspect());
}
/* Try to create cv::VideoCapture object using device ID #0. If successful, this will create GL texture IDs and storage /* Try to create cv::VideoCapture object using device ID #0. If successful, this will create GL texture IDs and storage
* for the camera frames, so it must be called after GL context has been created. Two textures will be created, so they * for the camera frames, so it must be called after GL context has been created. Two textures will be created, so they
* can be used as a double buffer. * can be used as a double buffer.
@ -312,15 +317,31 @@ void Pudding::respond(SDL_Event& event)
{ {
background.next(); background.next();
} }
else if (event.type == SDL_MOUSEBUTTONDOWN) /* Mouse interface */
else if (event.type == SDL_MOUSEMOTION || event.type == SDL_MOUSEBUTTONDOWN)
{ {
glm::vec2 gl_coordinates { glm::vec2 mouse = event.type == SDL_MOUSEBUTTONDOWN ? glm::vec2{event.button.x, event.button.y} :
float(event.button.x) / window_box().width() * 2.0f - 1.0f, glm::vec2{event.motion.x, event.motion.y};
(1.0f - float(event.button.y) / window_box().height()) * 2.0f - 1.0f glm::vec2 ndc {
float(mouse.x) / window_box().width() * 2.0f - 1.0f, (1.0f - float(mouse.y) / window_box().height()) * 2.0f - 1.0f
}; };
if (pad.collide(gl_coordinates)) /* Check for collision with the camera button if camera view is open, otherwise check for collision with main viewport. */
if ((!capture.isOpened() && camera_button.collide(ndc)) ||
(capture.isOpened() && get_display().ndc_subsection(main_viewport).collide(ndc)))
{ {
camera_switch.toggle(); if (SDL_GetCursor() != poke.get())
{
SDL_SetCursor(poke.get());
}
if (event.type == SDL_MOUSEBUTTONDOWN)
{
SDL_SetCursor(SDL_GetDefaultCursor());
camera_switch.toggle();
}
}
else if (SDL_GetCursor() == poke.get())
{
SDL_SetCursor(SDL_GetDefaultCursor());
} }
} }
} }
@ -793,13 +814,15 @@ void Pudding::update()
sb::Log::log(message); sb::Log::log(message);
} }
/* viewport box will be used to tell GL where to draw */ /* viewport box will be used to tell GL where to draw */
Box viewport_box = window_box(true); viewport = window_box(true);
/* shrink viewport if item texture or camera will be displayed */ /* shrink viewport if item texture or camera will be displayed */
if (item_display_active() || capture.isOpened()) if (item_display_active() || capture.isOpened())
{ {
viewport_box.drag_bottom(0.3f * viewport_box.height()); viewport.drag_bottom(0.3f * viewport.height());
} }
glViewport(viewport_box); /* Save the main viewport dimensions */
main_viewport = viewport;
glViewport(viewport);
glDisable(GL_DEPTH_TEST); glDisable(GL_DEPTH_TEST);
glClearColor(0, 0, 0, 1); glClearColor(0, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
@ -826,7 +849,7 @@ void Pudding::update()
/* calculate the transformation matrix for displaying pudding in viewport */ /* calculate the transformation matrix for displaying pudding in viewport */
model = glm::rotate(model, weight(get_configuration()["pudding"]["rotation-speed"].get<float>()), Y_UNIT_NORMAL_3D); model = glm::rotate(model, weight(get_configuration()["pudding"]["rotation-speed"].get<float>()), Y_UNIT_NORMAL_3D);
projection = glm::perspective( projection = glm::perspective(
glm::radians(40.0f * 1 / viewport_box.aspect()), viewport_box.aspect(), 0.1f, 100.0f); glm::radians(40.0f * 1 / viewport.aspect()), viewport.aspect(), 0.1f, 100.0f);
mvp = projection * VIEW_MATRIX * model; mvp = projection * VIEW_MATRIX * model;
/* uniforms */ /* uniforms */
glUniform1f(uniform["mvp"]["time"], time_seconds); glUniform1f(uniform["mvp"]["time"], time_seconds);
@ -875,8 +898,8 @@ void Pudding::update()
glUniform1i(uniform["flat"]["texture"], 0); glUniform1i(uniform["flat"]["texture"], 0);
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
/* move viewport to the bottom of screen */ /* move viewport to the bottom of screen */
viewport_box.top(viewport_box.bottom(), true); viewport.top(viewport.bottom(), true);
viewport_box.bottom(window_box(true).bottom(), true); viewport.bottom(window_box(true).bottom(), true);
/* reset blend to display the original texture colors */ /* reset blend to display the original texture colors */
glUniform3f(uniform["flat"]["blend"], 0.0f, 0.0f, 1.0f); glUniform3f(uniform["flat"]["blend"], 0.0f, 0.0f, 1.0f);
/* draw the current item image if we're supposed to */ /* draw the current item image if we're supposed to */
@ -885,9 +908,9 @@ void Pudding::update()
/* shrink viewport to half size if camera will also be displayed */ /* shrink viewport to half size if camera will also be displayed */
if (capture.isOpened()) if (capture.isOpened())
{ {
viewport_box.left(viewport_box.cx(), true); viewport.left(viewport.cx(), true);
} }
glViewport(viewport_box); glViewport(viewport);
current_item().current_texture().bind(); current_item().current_texture().bind();
plane.enable(); plane.enable();
/* draws rectangle vertices and rectangle texture using UV coords */ /* draws rectangle vertices and rectangle texture using UV coords */
@ -896,8 +919,8 @@ void Pudding::update()
/* draw the camera if the camera has been opened */ /* draw the camera if the camera has been opened */
if (capture.isOpened()) if (capture.isOpened())
{ {
viewport_box.left(window_box(true).left()); viewport.left(window_box(true).left());
glViewport(viewport_box); glViewport(viewport);
/* bind texture for drawing */ /* bind texture for drawing */
camera_view.current().bind(); camera_view.current().bind();
camera_view.enable(); camera_view.enable();
@ -905,16 +928,17 @@ void Pudding::update()
glDrawArrays(GL_TRIANGLES, 0, camera_view.attributes("position")->count()); glDrawArrays(GL_TRIANGLES, 0, camera_view.attributes("position")->count());
} }
} }
sb::Log::gl_errors("after capture, before test pad"); else
/* Test Pad */ {
glUseProgram(flat_program); /* Draw the camera button if neither the camera or inventory is displayed */
glUniformMatrix4fv(uniform["flat"]["transformation"], 1, GL_FALSE, &pad.transformation()[0][0]); glUseProgram(flat_program);
pad.texture().bind(); glUniformMatrix4fv(uniform["flat"]["transformation"], 1, GL_FALSE, &camera_button.transformation()[0][0]);
plane.enable(); camera_button.texture().bind();
glViewport(window_box(true)); plane.enable();
glDrawArrays(GL_TRIANGLES, 0, pad.attributes("position")->count()); glDrawArrays(GL_TRIANGLES, 0, camera_button.attributes("position")->count());
}
SDL_GL_SwapWindow(get_window()); SDL_GL_SwapWindow(get_window());
sb::Log::gl_errors("after test pad"); sb::Log::gl_errors("at end of update");
/* add a new item if a new barcode was scanned or entered */ /* add a new item if a new barcode was scanned or entered */
if (current_barcode != previous_barcode) if (current_barcode != previous_barcode)
{ {
@ -928,21 +952,22 @@ void Pudding::update()
* 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 * 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). * The texture is the graphic that displays in the Pad location. The callback must be a function that * (1.0, 1.0). * The texture is the graphic that displays in the Pad location. The callback must be a function that
* doesn't return a value or accept any arguments. */ * doesn't return a value or accept any arguments. */
Pad::Pad(sb::Texture texture, glm::vec2 offset, float scale, float ratio, std::function<void()> on_connect) Pad::Pad(sb::Texture texture, glm::vec2 offset, float scale, float ratio, std::function<void()> on_connect, float rotation)
{ {
this->texture(texture); this->texture(texture);
transform(offset, scale, ratio); transform(offset, scale, ratio, rotation);
this->on_connect(on_connect); this->on_connect(on_connect);
box.gl(true); box.invert_y(true);
} }
/* Set the Pad's transformation matrix based on an offset, a scale, and an aspect ratio. The offset is amount it will /* Set the Pad's transformation matrix based on an offset, a scale, an aspect ratio, and a rotation. The offset is the
* be shifted in the (x, y) plane. The scale is a value relative to the (x, y) plane, but it is a single value because * amount it will be shifted in the (x, y) plane. The scale is a value relative to the (x, y) plane, and it is a single
* the aspect ratio will determine how much each axis is scaled. If the aspect ratio is above one, the x-axis's scale * value because the aspect ratio will determine how much each axis is scaled. If the aspect ratio is above one, the
* will be divided by the ratio. If the aspect ratio is below one, the y-axis's scale will be multiplied by the aspect * x-axis's scale will be divided by the ratio. If the aspect ratio is below one, the y-axis's scale will be multiplied
* ratio. If the aspect ratio of the window is given, this will force the Pad to display as a square, and the ratio * by the aspect ratio. If the aspect ratio of the window is given, this will force the Pad to display as a square, and
* will be relative to the shorter axis. */ * the ratio will be relative to the shorter axis. The rotation is an angle in radians to rotate the pad object around
void Pad::transform(glm::vec2 offset, float scale, float ratio) * its center after it has been offset and scaled. */
void Pad::transform(glm::vec2 offset, float scale, float ratio, float rotation)
{ {
glm::vec3 scale_components { scale, scale, 1 }; glm::vec3 scale_components { scale, scale, 1 };
if (ratio > 1.0f) if (ratio > 1.0f)
@ -956,7 +981,8 @@ void Pad::transform(glm::vec2 offset, float scale, float ratio)
box.size({scale_components.x * 2, scale_components.y * 2}); box.size({scale_components.x * 2, scale_components.y * 2});
box.center(offset); box.center(offset);
std::cout << "pad box is " << box << std::endl; std::cout << "pad box is " << box << std::endl;
Model::transform(glm::translate(glm::vec3{offset.x, offset.y, 0}) * glm::scale(scale_components)); Model::transform(glm::translate(glm::vec3{offset.x, offset.y, 0}) * glm::scale(scale_components) *
glm::rotate(rotation, glm::vec3{0.0f, 0.0f, 1.0f}));
} }
/* Set the function that will run when a pad object is clicked. */ /* Set the function that will run when a pad object is clicked. */

View File

@ -44,8 +44,6 @@
#include "utility.hpp" #include "utility.hpp"
#include "Box.hpp" #include "Box.hpp"
void glViewport(Box);
/* A connection is an object containing a binary state of either on (connected) or off (not connected) /* 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 * 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. * same return type, number of arguments, and argument types determined by the template arguments.
@ -180,6 +178,18 @@ public:
* - Has its own response to click * - Has its own response to click
* - Shares mouse collision code * - Shares mouse collision code
* - Has its own translate + scale transformation * - Has its own translate + scale transformation
*
* Example:
*
* glm::vec3 w = glm::mat3({{1, 0, 0}, {0, 1, 0}, {-0.6739, -0.74, 1}}) * glm::mat3({{.1, 0, 0}, {0, .1 * (460.0 / 768.0), 0}, {0, 0, 1}}) *
* glm::vec3({-1, -1, 1});
* std::cout << w << std::endl << glm::translate(glm::vec3{-0.6739, -0.74, 0}) *
* glm::scale(glm::vec3{.1, .1 * (460.0 / 768.0), 1}) * glm::vec4{-1, -1, 0, 1} << std::endl;
* Pad p {background.current(), {-0.6739f, -0.74f}, 0.1f, get_display().window_box().aspect(), std::function<void()>()};
* const std::vector<glm::vec2>& p_position = *p.attributes("position");
* glm::vec4 final_position = p.transformation() * glm::vec4{p_position[2].x, p_position[2].y, 0, 1};
* 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 Plane
{ {
@ -193,8 +203,8 @@ private:
public: public:
Pad() {}; Pad() {};
Pad(sb::Texture, glm::vec2, float, float, callback); Pad(sb::Texture, glm::vec2, float, float, callback, float = 0.0f);
void transform(glm::vec2, float, float); void transform(glm::vec2, float, float = 1.0f, float = 0.0f);
void on_connect(callback); void on_connect(callback);
bool collide(const glm::vec2&) const; bool collide(const glm::vec2&) const;
@ -206,7 +216,7 @@ class Pudding : public Game
private: private:
/* Defines for effect IDs that will be passed to the shader program. Since COUNT is last and every value /* Defines for effect IDs that will be passed to the shader program. Since COUNT is last and every value
* is the default integer, it will define the number of effects available */ * is the default integer, it will be set to the number of effects available. */
enum Effect enum Effect
{ {
EFFECT_NONE, EFFECT_NONE,
@ -223,20 +233,21 @@ private:
}; };
typedef Game super; typedef Game super;
const std::string OPEN_FOOD_API_URL = "https://world.openfoodfacts.org/api/v0/product/"; inline static const std::string OPEN_FOOD_API_URL = "https://world.openfoodfacts.org/api/v0/product/";
const std::string OPEN_PRODUCTS_API_URL = "https://world.openproductsfacts.org/api/v0/product/"; inline static const std::string OPEN_PRODUCTS_API_URL = "https://world.openproductsfacts.org/api/v0/product/";
const std::string NUTRONIX_API_URL = "https://trackapi.nutritionix.com/v2/search/item?upc="; inline static const std::string NUTRONIX_API_URL = "https://trackapi.nutritionix.com/v2/search/item?upc=";
const std::string BARCODE_MONSTER_API_URL = "https://barcode.monster/api/"; inline static const std::string BARCODE_MONSTER_API_URL = "https://barcode.monster/api/";
const std::string BEST_BUY_API_URL_1 = "https://api.bestbuy.com/v1/products(upc="; inline static const std::string BEST_BUY_API_URL_1 = "https://api.bestbuy.com/v1/products(upc=";
const std::string BEST_BUY_API_URL_2 = ")?format=json&apiKey="; inline static const std::string BEST_BUY_API_URL_2 = ")?format=json&apiKey=";
const std::string NUTRONIX_NOT_FOUND = "resource not found"; inline static const std::string NUTRONIX_NOT_FOUND = "resource not found";
const std::string GOOGLE_BOOKS_API_URL = "https://www.googleapis.com/books/v1/volumes?q=isbn:"; inline static const std::string GOOGLE_BOOKS_API_URL = "https://www.googleapis.com/books/v1/volumes?q=isbn:";
const std::string GIANTBOMB_API_URL = "https://www.giantbomb.com/api/release/?api_key="; inline static const std::string GIANTBOMB_API_URL = "https://www.giantbomb.com/api/release/?api_key=";
const glm::vec3 ZERO_VECTOR_3D {0, 0, 0}; inline static const glm::vec3 ZERO_VECTOR_3D {0, 0, 0};
const glm::vec3 Y_UNIT_NORMAL_3D {0, 1, 0}; inline static const glm::vec3 Y_UNIT_NORMAL_3D {0, 1, 0};
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::mat4 VIEW_MATRIX = glm::lookAt({4.0f, 2.0f, 1.0f}, {0.0f, -0.325f, 0.0f}, Y_UNIT_NORMAL_3D);
const glm::vec3 PUDDING_BROWN {0.713f, 0.359f, 0.224f}; inline static const glm::vec3 PUDDING_BROWN {0.713f, 0.359f, 0.224f};
const glm::vec3 PUDDING_YELLOW {0.878f, 0.859f, 0.122f}; inline static const glm::vec3 PUDDING_YELLOW {0.878f, 0.859f, 0.122f};
std::shared_ptr<SDL_Cursor> poke;
std::string current_barcode, previous_barcode, current_config_barcode, current_camera_barcode; std::string current_barcode, previous_barcode, current_config_barcode, current_camera_barcode;
std::vector<Item> items; std::vector<Item> items;
Carousel item_carousel; Carousel item_carousel;
@ -254,11 +265,14 @@ private:
SDL_GLContext capture_frame_thread_context = nullptr; SDL_GLContext capture_frame_thread_context = nullptr;
sb::VAO vao; sb::VAO vao;
sb::VBO vbo; sb::VBO vbo;
Pad pad; std::map<std::string, sb::Texture> labels;
Pad camera_button, previous_button, next_button, inventory_button;
Box viewport, main_viewport;
void set_pudding_model(float, float, int, int = 1, float = -1.0f, float = 1.0f, float = 0.3f); void load_pudding_model(float, float, int, int = 1, float = -1.0f, float = 1.0f, float = 0.3f);
void load_gl_context(); void load_gl_context();
void load_tiles(); void load_tiles();
void load_pads();
void initialize_camera(); void initialize_camera();
void incorporate_open_api(Item&, const std::string&); void incorporate_open_api(Item&, const std::string&);
void incorporate_nutronix_api(Item&); void incorporate_nutronix_api(Item&);
@ -308,4 +322,7 @@ private:
/* float weighted depression rate */ /* float weighted depression rate */
}; };
/* Allow a box object to be passed to glViewport instead of four vertices. */
void glViewport(Box);
#endif #endif