Add PNG loading support
This commit is contained in:
parent
4d4cbd334f
commit
ea9a94dc08
3 changed files with 158 additions and 22 deletions
4
Makefile
4
Makefile
|
|
@ -1,8 +1,8 @@
|
|||
LINK_FLAGS=-lm -lpthread -ldl -lm -lGLEW -lEGL -lGL -lGLU -lOpenGL -lglfw -lfreetype
|
||||
LINK_FLAGS=-lm -lpthread -ldl -lm -lGLEW -lEGL -lGL -lGLU -lOpenGL -lglfw -lfreetype -lpng16 -lz
|
||||
|
||||
CFLAGS=-I./include -I/usr/include/freetype2 -I/usr/include/libpng16 -I/usr/include/harfbuzz -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include -I/usr/include/sysprof-4 -Iimgui -pthread -fno-exceptions
|
||||
|
||||
.PHONY: hexnando
|
||||
|
||||
hexnando: main.cpp
|
||||
g++ -std=c++20 -Wall -g -Wextra -pedantic -Werror -Og main.cpp ${LINK_FLAGS} ${CFLAGS} -o hexnando
|
||||
g++ -std=c++20 -Wall -g -Wextra -pedantic -Werror -Wno-calloc-transposed-args -Og main.cpp ${LINK_FLAGS} ${CFLAGS} -o hexnando
|
||||
|
|
|
|||
172
main.cpp
172
main.cpp
|
|
@ -36,6 +36,9 @@
|
|||
#include "stb_sprintf.h"
|
||||
#undef STB_SPRINTF_IMPLEMENTATION
|
||||
|
||||
// libpng
|
||||
#include <png.h>
|
||||
|
||||
// POSIX
|
||||
#include <time.h>
|
||||
|
||||
|
|
@ -110,6 +113,12 @@ struct selection_t {
|
|||
int32_t nselected;
|
||||
};
|
||||
|
||||
struct texture_t {
|
||||
uint32_t gl_id; // OpenGL GLuint ID
|
||||
uint32_t w;
|
||||
uint32_t h;
|
||||
};
|
||||
|
||||
const int32_t ARRAY_LIMIT = 400;
|
||||
|
||||
charglyph_t glyphmap[128];
|
||||
|
|
@ -121,6 +130,7 @@ glm::mat4 transforms[ARRAY_LIMIT];
|
|||
int32_t letter_map[ARRAY_LIMIT];
|
||||
int32_t text_frame_idx;
|
||||
|
||||
texture_t pngtex = {};
|
||||
|
||||
// error processing
|
||||
enum err_type {
|
||||
|
|
@ -172,6 +182,65 @@ void scroll_callback([[maybe_unused]]GLFWwindow* window, [[maybe_unused]]double
|
|||
mouse_scroll = yoffset;
|
||||
}
|
||||
|
||||
int path_callback([[maybe_unused]]ImGuiInputTextCallbackData *data)
|
||||
{
|
||||
fprintf(stderr, "Path callback\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int read_png(const char *path)
|
||||
{
|
||||
|
||||
FILE *fd = fopen(path, "rb");
|
||||
if (!fd) return -1;
|
||||
|
||||
png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
|
||||
if (!png_ptr)
|
||||
return 4;
|
||||
|
||||
png_infop info_ptr = png_create_info_struct(png_ptr);
|
||||
if (!info_ptr)
|
||||
return 4;
|
||||
|
||||
if (setjmp(png_jmpbuf(png_ptr)))
|
||||
{
|
||||
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
||||
fclose(fd);
|
||||
fprintf(stderr, "Error routine\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
png_init_io(png_ptr, fd);
|
||||
png_set_crc_action(png_ptr, PNG_CRC_WARN_USE, PNG_CRC_WARN_USE);
|
||||
png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_NEVER, NULL, 0);
|
||||
// TODO: Use low-level interface to read textures in inverted order as an optimization
|
||||
png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_STRIP_16 | PNG_TRANSFORM_PACKING | PNG_TRANSFORM_EXPAND, NULL);
|
||||
|
||||
fprintf(stdout, "File %s read correctly\n", path);
|
||||
|
||||
uint32_t row_bytes = png_get_rowbytes(png_ptr, info_ptr);
|
||||
uint32_t width = png_get_image_width(png_ptr, info_ptr);
|
||||
uint32_t height = png_get_image_height(png_ptr, info_ptr);
|
||||
fprintf(stdout, "\tWidth: %d px; Height: %d px; Rowbytes: %d\n", width, height, row_bytes);
|
||||
|
||||
uint8_t **row_pointers = png_get_rows(png_ptr, info_ptr);
|
||||
uint8_t *data = (uint8_t*) calloc(1, row_bytes * height);
|
||||
for (uint32_t i = 0; i < height; ++i)
|
||||
memcpy(data + i * row_bytes, row_pointers[height - i - 1], row_bytes);
|
||||
|
||||
GLuint tex_id;
|
||||
glGenTextures(1, &tex_id);
|
||||
glBindTexture(GL_TEXTURE_2D, tex_id);
|
||||
GLenum tex_format = GL_RGBA;
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, tex_format, width, height, 0, tex_format, GL_UNSIGNED_BYTE, data);
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
pngtex.gl_id = tex_id;
|
||||
pngtex.w = width;
|
||||
pngtex.h = height;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
char* load_file(const char *pathname)
|
||||
{
|
||||
FILE *text_file = fopen(pathname, "r");
|
||||
|
|
@ -445,6 +514,9 @@ int main([[maybe_unused]]int argc, [[maybe_unused]]char **argv)
|
|||
glGetIntegerv(GL_MINOR_VERSION, &gl_version_minor);
|
||||
|
||||
fprintf(stdout, "Using OpenGL %d.%d\n", gl_version_major, gl_version_minor);
|
||||
fprintf(stdout, "Compiled using libPNG version %s\n", PNG_LIBPNG_VER_STRING);
|
||||
fprintf(stdout, "Runtime using libPNG version %s\n", png_libpng_ver);
|
||||
|
||||
|
||||
// font library initialization
|
||||
FT_Library ft;
|
||||
|
|
@ -543,8 +615,11 @@ int main([[maybe_unused]]int argc, [[maybe_unused]]char **argv)
|
|||
|
||||
double cursor_x;
|
||||
double cursor_y;
|
||||
double cursor_x_scaled;
|
||||
double cursor_y_scaled;
|
||||
camera_t the_camera;
|
||||
bool mouse_pressed;
|
||||
bool right_mouse_pressed;
|
||||
bool mouse_over_gui;
|
||||
static bool show_hex_numbers = true;
|
||||
static selection_t selection;
|
||||
|
|
@ -561,12 +636,19 @@ int main([[maybe_unused]]int argc, [[maybe_unused]]char **argv)
|
|||
double cursor_dy = 0.0;
|
||||
int hovered_hex = -1;
|
||||
int32_t window_width, window_height;
|
||||
int lf, tf, rf, bf;
|
||||
float content_scale_x, content_scale_y;
|
||||
int32_t scaled_window_width, scaled_window_height;
|
||||
{
|
||||
TIME_BLOCK(window_size_polling);
|
||||
glfwGetWindowSize(window, &window_width, &window_height);
|
||||
glViewport(0, 0, window_width, window_height);
|
||||
glfwGetWindowFrameSize(window, &lf, &tf, &rf, &bf);
|
||||
glfwGetWindowContentScale(window, &content_scale_x, &content_scale_y);
|
||||
scaled_window_width = (int)(window_width * content_scale_x);
|
||||
scaled_window_height = (int)(window_height * content_scale_y);
|
||||
glViewport(0, 0, scaled_window_width, scaled_window_height);
|
||||
}
|
||||
float aspect_ratio = (float)window_width/window_height;
|
||||
float aspect_ratio = (float)scaled_window_width/scaled_window_height;
|
||||
|
||||
{
|
||||
TIME_BLOCK(input_handling);
|
||||
|
|
@ -595,24 +677,27 @@ int main([[maybe_unused]]int argc, [[maybe_unused]]char **argv)
|
|||
}
|
||||
|
||||
mouse_pressed = glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS;
|
||||
right_mouse_pressed = glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_RIGHT) == GLFW_PRESS;
|
||||
mouse_over_gui = ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem);
|
||||
|
||||
cursor_dx = cursor_x;
|
||||
cursor_dy = cursor_y;
|
||||
cursor_dx = cursor_x * content_scale_x;
|
||||
cursor_dy = cursor_y * content_scale_y;
|
||||
|
||||
glfwGetCursorPos(window, &cursor_x, &cursor_y);
|
||||
cursor_x_scaled = cursor_x * content_scale_x;
|
||||
cursor_y_scaled = cursor_y * content_scale_y;
|
||||
|
||||
cursor_dx -= cursor_x;
|
||||
cursor_dy -= cursor_y;
|
||||
cursor_dx -= cursor_x_scaled;
|
||||
cursor_dy -= cursor_y_scaled;
|
||||
|
||||
// Mouse movement
|
||||
if (!mouse_over_gui && glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_MIDDLE) == GLFW_PRESS)
|
||||
{
|
||||
fprintf(stdout, "Moving camera by %f %f\n", cursor_dx, cursor_dx);
|
||||
// mouse delta!
|
||||
the_camera.position.x += cursor_dx * the_camera.size.x/window_width;
|
||||
the_camera.position.x += cursor_dx * the_camera.size.x/scaled_window_width;
|
||||
// The Y coord is inverted
|
||||
the_camera.position.y -= cursor_dy * the_camera.size.y/window_height;
|
||||
the_camera.position.y -= cursor_dy * the_camera.size.y/scaled_window_height;
|
||||
}
|
||||
|
||||
if (the_camera.size.x < 0.1)
|
||||
|
|
@ -636,13 +721,13 @@ int main([[maybe_unused]]int argc, [[maybe_unused]]char **argv)
|
|||
view_matrix = glm::mat4(1.0f);
|
||||
proj_matrix = glm::ortho((the_camera.position.x - the_camera.size.x/2) * aspect_ratio, (the_camera.position.x + the_camera.size.x/2) * aspect_ratio, the_camera.position.y - the_camera.size.y/2, the_camera.position.y + the_camera.size.y/2);
|
||||
|
||||
glm::vec4 aux((float)cursor_x, (float)cursor_y, 0.0f, 1.0f);
|
||||
glm::vec4 aux((float)cursor_x_scaled, (float)cursor_y_scaled, 0.0f, 1.0f);
|
||||
|
||||
// move screen coordinates to NDC
|
||||
aux.x -= window_width/2.0f;
|
||||
aux.x /= window_width/2.0f;
|
||||
aux.y -= window_height/2.0f;
|
||||
aux.y /= -window_height/2.0f;
|
||||
aux.x -= scaled_window_width/2.0f;
|
||||
aux.x /= scaled_window_width/2.0f;
|
||||
aux.y -= scaled_window_height/2.0f;
|
||||
aux.y /= -scaled_window_height/2.0f;
|
||||
aux = glm::inverse(proj_matrix) * aux;
|
||||
cursor_world = glm::vec2(aux.x, aux.y);
|
||||
|
||||
|
|
@ -662,15 +747,21 @@ int main([[maybe_unused]]int argc, [[maybe_unused]]char **argv)
|
|||
{
|
||||
select(idx, &selection);
|
||||
}
|
||||
else if (right_mouse_pressed)
|
||||
{
|
||||
deselect(idx, &selection);
|
||||
}
|
||||
if (!is_selected(idx, &selection))
|
||||
glUniform4f(glGetUniformLocation(hex_program, "hex_color"), 1.0f, 0.0f, 0.0f, 1.0f); // red
|
||||
else
|
||||
glUniform4f(glGetUniformLocation(hex_program, "hex_color"), 1.0f, 0.0f, 0.0f, 1.0f);
|
||||
glUniform4f(glGetUniformLocation(hex_program, "hex_color"), 1.0f, 1.0f, 0.0f, 1.0f); // yellow
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
if (is_selected(idx, &selection))
|
||||
{
|
||||
glUniform4f(glGetUniformLocation(hex_program, "hex_color"), 1.0f, 1.0f, 0.0f, 1.0f);
|
||||
glUniform4f(glGetUniformLocation(hex_program, "hex_color"), 1.0f, 1.0f, 0.0f, 1.0f); // yellow
|
||||
}
|
||||
else
|
||||
glUniform4f(glGetUniformLocation(hex_program, "hex_color"), grid.hexes[idx].color.x, grid.hexes[idx].color.y, grid.hexes[idx].color.z, grid.hexes[idx].color.w);
|
||||
|
|
@ -691,7 +782,7 @@ int main([[maybe_unused]]int argc, [[maybe_unused]]char **argv)
|
|||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
}
|
||||
|
||||
glm::mat4 text_proj_matrix = glm::ortho(0.0f, (float)window_width, 0.0f, (float)window_height);
|
||||
glm::mat4 text_proj_matrix = glm::ortho(0.0f, (float)scaled_window_width, 0.0f, (float)scaled_window_height);
|
||||
glUniformMatrix4fv(glGetUniformLocation(text_program, "projection"), 1, GL_FALSE, &text_proj_matrix[0][0]);
|
||||
|
||||
static char debug_text_buf[256];
|
||||
|
|
@ -704,7 +795,7 @@ int main([[maybe_unused]]int argc, [[maybe_unused]]char **argv)
|
|||
glBindVertexArray(textvao);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, textvbo);
|
||||
|
||||
stbsp_snprintf(debug_text_buf, 256, "Cursor position %.0f %.0f screen %.2f %.2f world", cursor_x, cursor_y, cursor_world.x, cursor_world.y);
|
||||
stbsp_snprintf(debug_text_buf, 256, "Cursor position %.0f %.0f screen %.2f %.2f world", cursor_x_scaled, cursor_y_scaled, cursor_world.x, cursor_world.y);
|
||||
render_text(debug_text_buf, 25, 25, .5f, glm::vec3(1.0f, 1.0f, 1.0f), true);
|
||||
|
||||
glm::mat4 camera_transform = proj_matrix * view_matrix * model_matrix;
|
||||
|
|
@ -714,11 +805,11 @@ int main([[maybe_unused]]int argc, [[maybe_unused]]char **argv)
|
|||
// Get position of hexes in screen
|
||||
glm::vec4 v = camera_transform * glm::vec4(grid.hexes[i].position.x, grid.hexes[i].position.y, grid.hexes[i].position.z, 1.0);
|
||||
v.x += 1;
|
||||
v.x *= window_width/2.0f;
|
||||
v.x *= scaled_window_width/2.0f;
|
||||
v.y += 1;
|
||||
v.y *= window_height/2.0f;
|
||||
v.y *= scaled_window_height/2.0f;
|
||||
// Do not draw text outside visible window
|
||||
if (v.x > window_width || v.x < 0 || v.y > window_height || v.y < 0)
|
||||
if (v.x > scaled_window_width || v.x < 0 || v.y > scaled_window_height || v.y < 0)
|
||||
continue;
|
||||
render_text(&hex_labels[i*label_size], v.x, v.y, .2f + (0.3f / the_camera.size.x), glm::vec3(0.0f, 0.0f, 0.0f), false);
|
||||
}
|
||||
|
|
@ -740,6 +831,7 @@ int main([[maybe_unused]]int argc, [[maybe_unused]]char **argv)
|
|||
|
||||
static int grid_rows = grid.rows;
|
||||
static int grid_columns = grid.columns;
|
||||
static char image_path[PATH_MAX];
|
||||
|
||||
if (grid_rows != grid.rows || grid_columns != grid.columns)
|
||||
{
|
||||
|
|
@ -756,6 +848,25 @@ int main([[maybe_unused]]int argc, [[maybe_unused]]char **argv)
|
|||
ImGui::SliderInt("Rows", &grid_rows, 1, 100);
|
||||
ImGui::SliderInt("Columns", &grid_columns, 1, 100);
|
||||
ImGui::Checkbox("Show hex numbers", &show_hex_numbers);
|
||||
if (ImGui::CollapsingHeader("Image"))
|
||||
{
|
||||
if (ImGui::InputText("Input image", image_path, PATH_MAX, ImGuiInputTextFlags_EnterReturnsTrue, path_callback))
|
||||
{
|
||||
// TODO: Remove testing code
|
||||
fprintf(stdout, "Trying to read file %s\n", image_path);
|
||||
if (read_png(image_path))
|
||||
{
|
||||
fprintf(stdout, "Could not read file %s\n", image_path);
|
||||
pngtex = {};
|
||||
}
|
||||
}
|
||||
if (pngtex.gl_id)
|
||||
{
|
||||
glBindTexture(GL_TEXTURE_2D, pngtex.gl_id);
|
||||
if (ImGui::CollapsingHeader("Preview"))
|
||||
ImGui::Image((void*)pngtex.gl_id, ImVec2(pngtex.w, pngtex.h), {0,1}, {1,0});
|
||||
}
|
||||
}
|
||||
if (ImGui::CollapsingHeader("Debug timers"))
|
||||
{
|
||||
for (int i = 0; i < ntimers; ++i)
|
||||
|
|
@ -763,6 +874,27 @@ int main([[maybe_unused]]int argc, [[maybe_unused]]char **argv)
|
|||
ImGui::Text("%s: %d nsecs", debug_timers[i].name, debug_timers[i].usec);
|
||||
}
|
||||
}
|
||||
if (ImGui::CollapsingHeader("Debug vars"))
|
||||
{
|
||||
ImGui::Text("Window width %d", window_width);
|
||||
ImGui::Text("Window height %d", window_height);
|
||||
ImGui::Text("Window borders: left %d top %d right %d bottom %d", lf, tf, rf, bf);
|
||||
ImGui::Text("Window content scale: %f X %f Y", content_scale_x, content_scale_y);
|
||||
ImGui::Text("True (scaled) width %d", scaled_window_width);
|
||||
ImGui::Text("True (scaled) height %d", scaled_window_height);
|
||||
}
|
||||
if (ImGui::CollapsingHeader("Debug selection"))
|
||||
{
|
||||
ImGui::Text("Selected %d indices", selection.nselected);
|
||||
std::string aux;
|
||||
for (int i = 0; i < selection.nselected - 1; ++i)
|
||||
aux += std::to_string(selection.indices[i]) + ",";
|
||||
|
||||
if (selection.nselected)
|
||||
aux += std::to_string(selection.indices[selection.nselected - 1]);
|
||||
|
||||
ImGui::Text(aux.c_str());
|
||||
}
|
||||
ImGui::End();
|
||||
ImGui::Render();
|
||||
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
||||
|
|
|
|||
4
notes.org
Normal file
4
notes.org
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
- Y axis going upwards, just like OGL
|
||||
- Text rendering optimized yet still taking too much time. Using atlas with GL_TEXTURE_ARRAY
|
||||
- All vertex data lives in the CPU for each hex too
|
||||
- Need UI to modify hex appeareance (show texture from file and then apply it to hex)
|
||||
Loading…
Add table
Add a link
Reference in a new issue