From 4813a8dde26422657c07ae03fe2b47a6b92f0935 Mon Sep 17 00:00:00 2001 From: 0scar Date: Mon, 5 Feb 2024 18:22:40 +0100 Subject: Move source files to their modules src folders --- src/core/cleanup.c | 0 src/core/dltools.c | 57 --- src/core/harness.c | 0 src/core/init.c | 0 src/core/logging.c | 82 ---- src/core/loop.c | 976 ------------------------------------------ src/core/memory.c | 62 --- src/core/src/cleanup.c | 0 src/core/src/dltools.c | 57 +++ src/core/src/harness.c | 0 src/core/src/init.c | 0 src/core/src/logging.c | 82 ++++ src/core/src/loop.c | 976 ++++++++++++++++++++++++++++++++++++++++++ src/core/src/memory.c | 62 +++ src/core/src/state.c | 178 ++++++++ src/core/state.c | 178 -------- src/ctrl/controller.c | 0 src/ctrl/ctx.c | 0 src/ctrl/input.c | 339 --------------- src/ctrl/keyboard.c | 0 src/ctrl/mouse.c | 0 src/ctrl/src/controller.c | 0 src/ctrl/src/ctx.c | 0 src/ctrl/src/input.c | 339 +++++++++++++++ src/ctrl/src/keyboard.c | 0 src/ctrl/src/mouse.c | 0 src/rendering/gl.c | 0 src/rendering/rendering.c | 137 ------ src/rendering/src/gl.c | 0 src/rendering/src/rendering.c | 137 ++++++ src/rendering/src/text.c | 0 src/rendering/src/window.c | 0 src/rendering/text.c | 0 src/rendering/window.c | 0 src/resources/fonts.c | 0 src/resources/scripts.c | 0 src/resources/src/fonts.c | 0 src/resources/src/scripts.c | 0 src/resources/src/textures.c | 0 src/resources/textures.c | 0 src/ui/positioning.c | 945 ---------------------------------------- src/ui/rendering.c | 63 --- src/ui/src/positioning.c | 945 ++++++++++++++++++++++++++++++++++++++++ src/ui/src/rendering.c | 63 +++ src/utils/btree.c | 800 ---------------------------------- src/utils/fov.c | 94 ---- src/utils/hashmap.c | 3 - src/utils/misc.c | 86 ---- src/utils/src/btree.c | 800 ++++++++++++++++++++++++++++++++++ src/utils/src/fov.c | 94 ++++ src/utils/src/hashmap.c | 3 + src/utils/src/misc.c | 86 ++++ src/utils/src/stack.c | 82 ++++ src/utils/src/vector.c | 32 ++ src/utils/stack.c | 82 ---- src/utils/vector.c | 32 -- 56 files changed, 3936 insertions(+), 3936 deletions(-) delete mode 100644 src/core/cleanup.c delete mode 100644 src/core/dltools.c delete mode 100644 src/core/harness.c delete mode 100644 src/core/init.c delete mode 100644 src/core/logging.c delete mode 100644 src/core/loop.c delete mode 100644 src/core/memory.c create mode 100644 src/core/src/cleanup.c create mode 100644 src/core/src/dltools.c create mode 100644 src/core/src/harness.c create mode 100644 src/core/src/init.c create mode 100644 src/core/src/logging.c create mode 100644 src/core/src/loop.c create mode 100644 src/core/src/memory.c create mode 100644 src/core/src/state.c delete mode 100644 src/core/state.c delete mode 100644 src/ctrl/controller.c delete mode 100644 src/ctrl/ctx.c delete mode 100644 src/ctrl/input.c delete mode 100644 src/ctrl/keyboard.c delete mode 100644 src/ctrl/mouse.c create mode 100644 src/ctrl/src/controller.c create mode 100644 src/ctrl/src/ctx.c create mode 100644 src/ctrl/src/input.c create mode 100644 src/ctrl/src/keyboard.c create mode 100644 src/ctrl/src/mouse.c delete mode 100644 src/rendering/gl.c delete mode 100644 src/rendering/rendering.c create mode 100644 src/rendering/src/gl.c create mode 100644 src/rendering/src/rendering.c create mode 100644 src/rendering/src/text.c create mode 100644 src/rendering/src/window.c delete mode 100644 src/rendering/text.c delete mode 100644 src/rendering/window.c delete mode 100644 src/resources/fonts.c delete mode 100644 src/resources/scripts.c create mode 100644 src/resources/src/fonts.c create mode 100644 src/resources/src/scripts.c create mode 100644 src/resources/src/textures.c delete mode 100644 src/resources/textures.c delete mode 100644 src/ui/positioning.c delete mode 100644 src/ui/rendering.c create mode 100644 src/ui/src/positioning.c create mode 100644 src/ui/src/rendering.c delete mode 100644 src/utils/btree.c delete mode 100644 src/utils/fov.c delete mode 100644 src/utils/hashmap.c delete mode 100644 src/utils/misc.c create mode 100644 src/utils/src/btree.c create mode 100644 src/utils/src/fov.c create mode 100644 src/utils/src/hashmap.c create mode 100644 src/utils/src/misc.c create mode 100644 src/utils/src/stack.c create mode 100644 src/utils/src/vector.c delete mode 100644 src/utils/stack.c delete mode 100644 src/utils/vector.c (limited to 'src') diff --git a/src/core/cleanup.c b/src/core/cleanup.c deleted file mode 100644 index e69de29..0000000 diff --git a/src/core/dltools.c b/src/core/dltools.c deleted file mode 100644 index 1e43b79..0000000 --- a/src/core/dltools.c +++ /dev/null @@ -1,57 +0,0 @@ -#include - -#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) -/* include winapi */ -#else -#include -#endif - -#include -#include - -bool dynamic_library_close(void* shared_library) { -#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) - return true; -#else - return dlclose(shared_library) == 0; -#endif -} - -void* dynamic_library_open(const char* library_path) { -#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) - return NULL; -#else - return dlopen(library_path, RTLD_NOW); -#endif -} - -char* dynamic_library_get_error(void) { -#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) - return "unsupported on windows"; -#else - return dlerror(); -#endif -} - -void* dynamic_library_reload(void* shared_library, const char* library_path) { - void* library_address = NULL; - if (!dynamic_library_close(shared_library)) { - ERROR("Failed to close shared library: %s", dynamic_library_get_error()); - ERROR("Reloading dynamic library failed."); - return library_address; - } - if ((library_address = dynamic_library_open(library_path)) == NULL) { - ERROR("Failed to open shared library: %s", dynamic_library_get_error()); - ERROR("Reloading dynamic library %s failed.", library_path); - } - return library_address; -} - -void* dynamic_library_get_symbol(void* restrict shared_library, - const char* symbol) { -#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) - return NULL; -#else - return dlsym(shared_library, symbol); -#endif -} diff --git a/src/core/harness.c b/src/core/harness.c deleted file mode 100644 index e69de29..0000000 diff --git a/src/core/init.c b/src/core/init.c deleted file mode 100644 index e69de29..0000000 diff --git a/src/core/logging.c b/src/core/logging.c deleted file mode 100644 index 7870258..0000000 --- a/src/core/logging.c +++ /dev/null @@ -1,82 +0,0 @@ -#include -#include -#include - -char* itoa(i32 x) { - const i32 size = (((i32)ceil(log10((f64)x))) + 1) * sizeof(char); - char* retval = (char*)malloc(size); - if (retval == NULL) { - perror("Failed to allocate memory for itoa"); - exit(EXIT_FAILURE); - } - sprintf(retval, "%d", x); - return retval; -} - -void _log(FILE* stream, const char* prefix, const char* fmt, va_list ap) { - if (stream == NULL) { - fprintf(stderr, "_log got NULL in stream argument\n"); - exit(EXIT_FAILURE); - } - fputs(prefix, stream); - vfprintf(stream, fmt, ap); -} - -void LOG(const char* fmt, ...) { - va_list ap; - va_start(ap, fmt); - _log(stdout, "[" TERM_COLOR_BLUE "LOG" TERM_COLOR_RESET "] ", fmt, ap); - va_end(ap); - puts(""); -} - -void INFO_(const char* fmt, ...) { - va_list ap; - va_start(ap, fmt); - _log(stdout, "[" TERM_COLOR_GREEN "INFO" TERM_COLOR_RESET "] ", fmt, ap); - va_end(ap); -} - -void INFO(const char* fmt, ...) { - va_list ap; - va_start(ap, fmt); - _log(stdout, "[" TERM_COLOR_GREEN "INFO" TERM_COLOR_RESET "] ", fmt, ap); - va_end(ap); - puts(""); -} - -void __DEBUG(const char* file, const i32 line, const char* func, - const char* fmt, ...) { - va_list ap; - - const usize prefix_len = 1024; - - char* prefix = malloc(sizeof(char) * 1024); - - snprintf(prefix, prefix_len, - "[" TERM_COLOR_YELLOW "DEBUG" TERM_COLOR_RESET "] " - "%s:%d <%s> ", - file, line, func); - - va_start(ap, fmt); - _log(stdout, prefix, fmt, ap); - va_end(ap); - - free(prefix); -} - -void WARN(const char* fmt, ...) { - va_list ap; - va_start(ap, fmt); - _log(stderr, "[" TERM_COLOR_PURPLE "WARN" TERM_COLOR_RESET "] ", fmt, ap); - va_end(ap); - puts(""); -} - -void ERROR(const char* fmt, ...) { - va_list ap; - va_start(ap, fmt); - _log(stderr, "[" TERM_COLOR_RED "ERROR" TERM_COLOR_RESET "] ", fmt, ap); - va_end(ap); - puts(""); -} diff --git a/src/core/loop.c b/src/core/loop.c deleted file mode 100644 index 79e0c0c..0000000 --- a/src/core/loop.c +++ /dev/null @@ -1,976 +0,0 @@ -#include -#include -#include -#include - -#define GLAD_GL_IMPLEMENTATION -#include -#undef GLAD_GL_IMPLEMENTATION - -#undef GLFW_INCLUDE_NONE -#include - -#include - -#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) -/* include winapi */ -#include -#elif defined(__APPLE__) -/* mac includes */ -#elif defined(__linux) || defined(__linux__) || defined(linux) - -#include -#include - -#endif - -#define ENGINE_INTERNALS -#include -#include -#include -#include - -#include -// #include -// #include - -#define DEFAULT_NUM_PROCS 8 - -#ifdef BENCHMARK -#define BENCHEXPR(timevar, expr) \ - { \ - f64 t = get_time(); \ - expr timevar += get_time() - t; \ - } - -extern i32 drawcall_len; - -#else -#define BENCHEXPR(timevar, expr) expr -#endif - - -// http://www.opengl-tutorial.org/beginners-tutorials/tutorial-2-the-first-triangle/ -GLuint LoadShaders(const GladGLContext* gl, const char * vertex_file_path, const char * fragment_file_path){ - - // Create the shaders - GLuint VertexShaderID = gl->CreateShader(GL_VERTEX_SHADER); - GLuint FragmentShaderID = gl->CreateShader(GL_FRAGMENT_SHADER); - - // Read the Vertex Shader code from the file - char* VertexShaderCode; - FILE* VertexShaderStream = fopen(vertex_file_path, "r"); - if(VertexShaderStream != NULL){ - fseek(VertexShaderStream, 0, SEEK_END); - const i64 size = ftell(VertexShaderStream); - rewind(VertexShaderStream); - VertexShaderCode = calloc(size + 1, sizeof(char)); - - // Assume the whole file is successfully read - fread(VertexShaderCode, sizeof(char), size, VertexShaderStream); - //LOG("vertex source is %d bytes\n%s\n", size, VertexShaderCode); - - fclose(VertexShaderStream); - } else { - ERROR("Impossible to open %s. Are you in the right directory?\n", vertex_file_path); - getchar(); - return 0; - } - - // Read the Fragment Shader code from the file - char* FragmentShaderCode; - FILE* FragmentShaderStream = fopen(fragment_file_path, "r"); - if(FragmentShaderStream != NULL){ - fseek(FragmentShaderStream, 0, SEEK_END); - const i64 size = ftell(FragmentShaderStream); - rewind(FragmentShaderStream); - FragmentShaderCode = calloc(size + 1, sizeof(char)); - - // Assume the whole file is successfully read - fread(FragmentShaderCode, sizeof(char), size, FragmentShaderStream); - LOG("fragment source is %d bytes", size); - - fclose(FragmentShaderStream); - } else { - ERROR("Impossible to open %s. Are you in the right directory?\n", fragment_file_path); - getchar(); - return 0; - } - - GLint Result = GL_FALSE; - int InfoLogLength; - - // Compile Vertex Shader - INFO("Compiling shader: %s\n", vertex_file_path); - char const * VertexSourcePointer = VertexShaderCode; - gl->ShaderSource(VertexShaderID, 1, &VertexSourcePointer , NULL); - gl->CompileShader(VertexShaderID); - - // Check Vertex Shader - gl->GetShaderiv(VertexShaderID, GL_COMPILE_STATUS, &Result); - gl->GetShaderiv(VertexShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength); - if ( InfoLogLength > 0 ) { - char* msg = calloc(InfoLogLength + 1, sizeof(char)); - gl->GetShaderInfoLog(VertexShaderID, InfoLogLength, NULL, msg); - ERROR("Compiling shader: %s\n", msg); - free(msg); - } - - // Compile Fragment Shader - INFO("Compiling shader: %s\n", fragment_file_path); - char const * FragmentSourcePointer = FragmentShaderCode; - gl->ShaderSource(FragmentShaderID, 1, &FragmentSourcePointer , NULL); - gl->CompileShader(FragmentShaderID); - - // Check Fragment Shader - gl->GetShaderiv(FragmentShaderID, GL_COMPILE_STATUS, &Result); - gl->GetShaderiv(FragmentShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength); - if ( InfoLogLength > 0 ){ - char* msg = calloc(InfoLogLength + 1, sizeof(char)); - gl->GetShaderInfoLog(FragmentShaderID, InfoLogLength, NULL, msg); - ERROR("Compiling shader: %s\n", msg); - free(msg); - } - - // Link the program - INFO("Linking program\n"); - GLuint ProgramID = gl->CreateProgram(); - gl->AttachShader(ProgramID, VertexShaderID); - gl->AttachShader(ProgramID, FragmentShaderID); - gl->LinkProgram(ProgramID); - - // Check the program - gl->GetProgramiv(ProgramID, GL_LINK_STATUS, &Result); - gl->GetProgramiv(ProgramID, GL_INFO_LOG_LENGTH, &InfoLogLength); - if ( InfoLogLength > 0 ){ - char* msg = calloc(InfoLogLength + 1, sizeof(char)); - gl->GetShaderInfoLog(ProgramID, InfoLogLength, NULL, msg); - ERROR("Compiling shader: %s\n", msg); - free(msg); - } - - gl->DetachShader(ProgramID, VertexShaderID); - gl->DetachShader(ProgramID, FragmentShaderID); - - gl->DeleteShader(VertexShaderID); - gl->DeleteShader(FragmentShaderID); - - - free(VertexShaderCode); - free(FragmentShaderCode); - return ProgramID; -} - - - - - - - - - - - -static u64 FPS_CAP = 50; -Platform* GLOBAL_PLATFORM = NULL; - -input_callback_t* callbacks[128]; -usize callbacks_len; - -i32 nproc(void) { - return get_nprocs(); -} - -void delay( uint32_t ms ) -{ -#ifdef _WIN32 - Sleep( ms ); -#else - usleep( ms * 1000 ); -#endif -} - -i32 cmp_int(const void* a, const void* b) { - const i32* x = a; - const i32* y = b; - - return *x - *y; -} - -v2_i32 get_canvas_size(void* window) { - v2_i32 realsize; - glfwGetWindowSize(window, &(realsize.x), &(realsize.y)); - - /* Set logical render size */ - return realsize; -} - -Texture* load_texture(void* render, const Asset_TextureSpec* ts) { - Texture* t = NULL; - - if (ts == NULL) { - ERROR("Invalid Asset_TextureSpec\n"); - return NULL; - } - - //t = (Texture*)malloc(sizeof(Texture)); - //t->texture = new_texture; - ///* Assigning const value */ - //*(i32*)&t->tilesize = tw; - //*(i32*)&t->width = ts->width; - //*(i32*)&t->height = ts->height; - - return t; -} - -void engine_update_window(Window* w, void* e) { - switch ((i32)e) { - default: - WARN("Unhandled window event 0x%04x", (i32)e); - break; - } - return; -} - -struct glfw_ctx { - GLFWwindow* w; - GladGLContext* c; -} glfw_ctx; - -/* GLFW And vulkan spaghetti boiler */ -void glfw_err_callback(int code, const char* description) { - ERROR("glfw [%d]: %s\n", code, description); - // Terminate? - exit(EXIT_FAILURE); -} - -struct QueueFamilyIndices { - int64_t graphicsFamily; - int64_t presentFamily; -}; - -GladGLContext* create_context(GLFWwindow *window) { - glfwMakeContextCurrent(window); - - GladGLContext* context = (GladGLContext*) calloc(1, sizeof(GladGLContext)); - if (!context) return NULL; - - int version = gladLoadGLContext(context, glfwGetProcAddress); - INFO("Loaded OpenGL %d.%d", GLAD_VERSION_MAJOR(version), GLAD_VERSION_MINOR(version)); - - return context; -} - -struct glfw_ctx initialize_GLFW( - const char* windowtitle, v2_i32 windowsize, - const u32 flags - ) { - GLFWwindow* window = NULL; - - glfwSetErrorCallback(&glfw_err_callback); - - INFO_("initializing glfw..."); - if (glfwInit() == GLFW_FALSE) { - const char *desc; - int code = glfwGetError(&desc); - ERROR("failed to initialize glfw [%d]: %s\n", code, *desc); - exit(EXIT_FAILURE); - } else - printf("ok\n"); - - - INFO_("initializing window..."); - //glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); - glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); - - glfwWindowHint(GLFW_SAMPLES, 0); // Disable anti aliasing - - // Use a modern opengl version - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6); - - glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); - -#ifdef __APPLE__ - // To make MacOS happy; should not be needed - glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); -#endif - - - /* "On Wayland specifically, you need to swap the buffers - * of a window for it to become visible." */ - window = glfwCreateWindow(windowsize.x, windowsize.y, windowtitle, NULL, NULL); - if (window == NULL) { - ERROR("Failed to create GLFW window!\n"); - const char *desc; - int code = glfwGetError(&desc); - ERROR("failed to initialize glfw window [%d]: %s\n", code, desc); - exit(EXIT_FAILURE); - } else - printf("ok\n"); - - //glfwMakeContextCurrent(window); - - // Remember to load GL :) (hours wasted because i forgot: approx 4) - GladGLContext *ctx = create_context(window); - //printf("GL %d.%d\n", GLAD_VERSION_MAJOR(version), GLAD_VERSION_MINOR(version)); - - //ctx->Viewport(0, 0, 200, 200); - - if (ctx == NULL) { - ERROR("Failed to create glad context"); - exit(EXIT_FAILURE); - } - - glfwSetFramebufferSizeCallback(window, window_size_callback); - glfwSwapInterval(0); - -#ifdef _DEBUG - ctx->ClearColor((float)0x10 / 255.f, (float)0x0a / 255.f, (float)0x33 / 255.f, 0.f); -#else - ctx->ClearColor(0x0, 0x0, 0x0, 0.f); -#endif - - ctx->Enable(GL_DEPTH_TEST); - ctx->DepthFunc(GL_LESS); - - return (struct glfw_ctx){.w = window, .c = ctx}; -} - - -/* Creates the window, initializes IO, Rendering, Fonts and engine-specific - * resources. */ -Platform* engine_init(const char* windowtitle, v2_i32 windowsize, - const f32 render_scale, const u32 flags, - const usize initial_memory, const Asset_FontSpec* fonts[], - const Asset_TextureSpec* textures[]) { - -#ifdef BENCHMARK - f64 init_start = get_time(); -#endif - -#if defined(__linux) || defined(__linux__) || defined(linux) - { - pid_t pid = getpid(); - INFO("Starting with pid %lu", pid); - } -#endif - - Platform* p = (Platform*)malloc(sizeof(Platform)); - Window* w = (Window*)malloc(sizeof(Window)); - - /* initialize resources */ - struct Resources* resources = - (struct Resources*)malloc(sizeof(struct Resources)); - resources->textures_len = 0; - resources->fonts_len = 0; - resources->texturepaths_len = 0; - resources->fontpaths_len = 0; - resources->texture_paths = NULL; - resources->font_paths = NULL; - resources->textures = NULL; - //resources->fonts = NULL; - - { - struct glfw_ctx ctx = initialize_GLFW(windowtitle, windowsize, flags); - w->window = ctx.w; - w->context = ctx.c; - } - - const GladGLContext *gl = w->context; - - struct RenderObject *testobject = malloc(sizeof(struct RenderObject)); - - gl->GenVertexArrays(1, &(testobject->vao)); - gl->BindVertexArray(testobject->vao); - - p->testobject = testobject; - - testobject->g_vertex_buffer_data[0] = -1.0f; - testobject->g_vertex_buffer_data[1] = -1.0f; - testobject->g_vertex_buffer_data[2] = 0.0f; - - testobject->g_vertex_buffer_data[3] = 1.0f; - testobject->g_vertex_buffer_data[4] = -1.0f; - testobject->g_vertex_buffer_data[5] = 0.0f; - - testobject->g_vertex_buffer_data[6] = 0.0f; - testobject->g_vertex_buffer_data[7] = 1.0f; - testobject->g_vertex_buffer_data[8] = 0.0f; - - static const float bufdata[] = { - -1.0f,-1.0f,-1.0f, // triangle 1 : begin - -1.0f,-1.0f, 1.0f, - -1.0f, 1.0f, 1.0f, // triangle 1 : end - 1.0f, 1.0f,-1.0f, // triangle 2 : begin - -1.0f,-1.0f,-1.0f, - -1.0f, 1.0f,-1.0f, // triangle 2 : end - 1.0f,-1.0f, 1.0f, - -1.0f,-1.0f,-1.0f, - 1.0f,-1.0f,-1.0f, - 1.0f, 1.0f,-1.0f, - 1.0f,-1.0f,-1.0f, - -1.0f,-1.0f,-1.0f, - -1.0f,-1.0f,-1.0f, - -1.0f, 1.0f, 1.0f, - -1.0f, 1.0f,-1.0f, - 1.0f,-1.0f, 1.0f, - -1.0f,-1.0f, 1.0f, - -1.0f,-1.0f,-1.0f, - -1.0f, 1.0f, 1.0f, - -1.0f,-1.0f, 1.0f, - 1.0f,-1.0f, 1.0f, - 1.0f, 1.0f, 1.0f, - 1.0f,-1.0f,-1.0f, - 1.0f, 1.0f,-1.0f, - 1.0f,-1.0f,-1.0f, - 1.0f, 1.0f, 1.0f, - 1.0f,-1.0f, 1.0f, - 1.0f, 1.0f, 1.0f, - 1.0f, 1.0f,-1.0f, - -1.0f, 1.0f,-1.0f, - 1.0f, 1.0f, 1.0f, - -1.0f, 1.0f,-1.0f, - -1.0f, 1.0f, 1.0f, - 1.0f, 1.0f, 1.0f, - -1.0f, 1.0f, 1.0f, - 1.0f,-1.0f, 1.0f - }; - - static const GLfloat g_color_buffer_data[] = { - 0.583f, 0.771f, 0.014f, - 0.609f, 0.115f, 0.436f, - 0.327f, 0.483f, 0.844f, - 0.822f, 0.569f, 0.201f, - 0.435f, 0.602f, 0.223f, - 0.310f, 0.747f, 0.185f, - 0.597f, 0.770f, 0.761f, - 0.559f, 0.436f, 0.730f, - 0.359f, 0.583f, 0.152f, - 0.483f, 0.596f, 0.789f, - 0.559f, 0.861f, 0.639f, - 0.195f, 0.548f, 0.859f, - 0.014f, 0.184f, 0.576f, - 0.771f, 0.328f, 0.970f, - 0.406f, 0.615f, 0.116f, - 0.676f, 0.977f, 0.133f, - 0.971f, 0.572f, 0.833f, - 0.140f, 0.616f, 0.489f, - 0.997f, 0.513f, 0.064f, - 0.945f, 0.719f, 0.592f, - 0.543f, 0.021f, 0.978f, - 0.279f, 0.317f, 0.505f, - 0.167f, 0.620f, 0.077f, - 0.347f, 0.857f, 0.137f, - 0.055f, 0.953f, 0.042f, - 0.714f, 0.505f, 0.345f, - 0.783f, 0.290f, 0.734f, - 0.722f, 0.645f, 0.174f, - 0.302f, 0.455f, 0.848f, - 0.225f, 0.587f, 0.040f, - 0.517f, 0.713f, 0.338f, - 0.053f, 0.959f, 0.120f, - 0.393f, 0.621f, 0.362f, - 0.673f, 0.211f, 0.457f, - 0.820f, 0.883f, 0.371f, - 0.982f, 0.099f, 0.879f -}; - - -// LOG("sizeof(bufdata) = %lu", sizeof(bufdata)); -// LOG("sizeof(g_vertex_buffer_data) = %lu", sizeof(testobject->g_vertex_buffer_data)); -// - - // Generate 1 buffer, put the resulting identifier in vertexbuffer - gl->GenBuffers(1, &(testobject->vbo)); - // The following commands will talk about our 'vertexbuffer' buffer - gl->BindBuffer(GL_ARRAY_BUFFER, testobject->vbo); - // Give our vertices to OpenGL. - gl->BufferData(GL_ARRAY_BUFFER, sizeof(bufdata), bufdata, GL_STATIC_DRAW); - - // Same for the color buffer - gl->GenBuffers(1, &(testobject->col)); - gl->BindBuffer(GL_ARRAY_BUFFER, testobject->col); - gl->BufferData(GL_ARRAY_BUFFER, sizeof(g_color_buffer_data), g_color_buffer_data, GL_STATIC_DRAW); - - testobject->shaderprogram = LoadShaders(gl, "shader.vertexshader", "shader.fragmentshader"); - INFO("Shaderprogram %d", testobject->shaderprogram); - - { /* Resource loading */ - - /* Count resources */ - usize n_textures = 0; - usize n_fonts = 0; - - if (textures != NULL) - while (textures[n_textures] != NULL) n_textures++; - if (fonts != NULL) - while (fonts[n_fonts] != NULL) n_fonts++; - - INFO("Number of textures: " TERM_COLOR_YELLOW "%d" TERM_COLOR_RESET, - n_textures); - INFO("Number of fonts: " TERM_COLOR_YELLOW "%d" TERM_COLOR_RESET, n_fonts); - - /* Save the textures and fonts, if we should need to reload them later */ - resources->texture_paths = (Asset_TextureSpec**)textures; - resources->font_paths = (Asset_FontSpec**)fonts; - - /* Allocate memory for textures and fonts */ - resources->textures = (Texture**)malloc(sizeof(Texture*) * n_textures); - //resources->fonts = (TTF_Font**)malloc(sizeof(TTF_Font*) * n_fonts); - resources->textures_size = n_textures; - - for (usize i = 0; i < n_textures; i++) resources->textures[i] = NULL; - //for (usize i = 0; i < n_fonts; i++) resources->fonts[i] = NULL; - - /* Load textures */ - for (usize i = 0; i < n_textures; i++) { - Texture* t = NULL; - INFO_("loading texture \"" TERM_COLOR_YELLOW "%s" TERM_COLOR_RESET - "\"...", - textures[i]->path); - -// t = load_texture(renderer, textures[i]); -// if (t == NULL) { -// puts(""); -// ERROR("failed to load texture\n"); -// exit(EXIT_FAILURE); -// } -// -// if (t->tilesize < 8) { -// puts(""); -// ERROR("texture too small!\n"); -// exit(EXIT_FAILURE); -// } -// - //if (t->texture == NULL) { - // puts(""); - // ERROR("failed to load texture\n"); - //} else { - // printf("ok\n"); - // resources->textures[i] = t; - // resources->textures_len++; - //} - } - - /* Load fonts */ - for (usize i = 0; i < n_fonts; i++) { - INFO_("loading font \"" TERM_COLOR_YELLOW "%s" TERM_COLOR_RESET "\"...", - fonts[i]->font_path); - - //TTF_Font* font = TTF_OpenFont(fonts[i]->font_path, fonts[i]->ptsize); - //if (!font) { - // ERROR("failed to load font: %s\n", TTF_GetError()); - //} else { - // printf("ok\n"); - // resources->fonts[i] = font; - // resources->fonts_len++; - //} - } - - if (resources->textures_len != n_textures) { - WARN("Done. %d/%d textures loaded.", resources->textures_len, n_textures); - } else { - INFO("Done. All %d textures loaded.", n_textures); - } - - if (resources->fonts_len != n_fonts) { - WARN("Done. %d/%d fonts loaded.", resources->fonts_len, n_fonts); - } else { - INFO("Done. All %d fonts loaded.", n_fonts); - } - - resources->texturepaths_len = resources->textures_len; - resources->fontpaths_len = resources->fonts_len; - } - - { /* Adjust window and such */ - /* Set actual windowsize, which might be forced by OS */ - INFO("Adjusting window size..."); - //windowsize = get_canvas_size(renderer); - - INFO("Windowsize: <%d,%d>", windowsize.x, windowsize.y); - } - - //w->renderer = renderer; - //w->window = window; - w->render_scale = render_scale; - w->windowsize = windowsize; - w->game_w = NULL; - w->game_h = NULL; - - p->data = (void*)resources; - p->data_len = sizeof(struct Resources); - p->window = w; - p->quit = false; - - p->frame = 0; - p->fps_target = 60; - - p->mem = memory_new(initial_memory); - - /* Getting the mouse coords now resolves the issue where a click "isn't - * registered" when the mouse isn't moved before the user clicks */ - - p->mousedown = (v2_i32){-1, -1}; - p->mouseup = (v2_i32){-1, -1}; - - p->mouse_lclick = false; - p->mouse_rclick = false; - - p->camera_x = 0; - p->camera_y = 0; - - p->edit_text = NULL; - p->edit_pos = 0; - - p->bindings = NULL; - p->bindings_sz = 0; - p->bindings_len = 0; - - // TODO: Add global bindings - -#ifdef BENCHMARK - f64 init_stop = get_time(); - INFO("Initialization took %dms", init_stop - init_start); -#endif - - INFO("Available cores: %d", nproc()); - - GLOBAL_PLATFORM = p; - -#ifdef DAW_BUILD_HOTRELOAD - -#define State(name) \ - if (!State_reload(STATE_##name, p->bindings, p->bindings_len)) { \ - ERROR("Failed to reload shared object file for state %s", #name); \ - }; - -#include -#undef State - -#endif - - return p; -} - -i32 engine_run(Platform* p, StateType initial_state) { - if (p == NULL) { - ERROR("Platform is uninitialized.\n"); - INFO("initialize with `engine_init`"); - return -1; - } - - memory* mem = p->mem; - - StateType state = initial_state; - - { - f64 state_init_time = get_time(); - State_init(state, mem); - INFO("Initializing state \"%s\" took %ldms", StateTypeStr[state], - get_time() - state_init_time); - } - - f64 time = get_time(); - - // Update ticks - u64 ticks = 0; - - /* Profiling values */ -#ifdef BENCHMARK - u64 profile_tick_counter = 0; - // u64 profile_slack = 0; - u64 profile_rendering = 0; - u64 profile_gameloop = 0; - u64 profile_input = 0; - u64 profile_input_handling = 0; - u64 profile_num_drawcalls = 0; - u32 profile_interval_timer = time; - const u32 profile_interval_ms = 5000; - const f32 profile_interval_scale = (f32)(profile_interval_ms) / 100.0f; -#endif - - const f64 frame_interval = 1000.0 / FPS_CAP; - - StateType (*update_func)(void*) = State_updateFunc(state); - - f64 last_fps_measurement = get_time(); - - /* Main loop */ - INFO("Program: %d", p->testobject->shaderprogram); - GladGLContext *gl = p->window->context; - do { - const f64 now = get_time(); - const f64 dt = now - time; - time = now; - /* Wait frame_interval */ - if (dt < frame_interval) { -#ifndef BENCHMARK - //delay(frame_interval - dt); - -#else - /* We want to know how much time is spend sleeping */ - // profile_slack += frame_interval - dt; -#endif - } - - if (now - last_fps_measurement > 1.000) { - last_fps_measurement = now; - printf("\n FPS: %.1f \t ticks: %lu", (double)ticks / now, ticks); - } - -#ifdef BENCHMARK - if (time - profile_interval_timer > profile_interval_ms) { - /* Ticks/frames since last measurement */ - u32 fps = (ticks - profile_tick_counter) / profile_interval_scale; - u64 drawcalls = profile_num_drawcalls / profile_interval_scale / fps; - - u32 sum = +profile_rendering - //+ profile_slack - + profile_input + profile_input_handling + profile_gameloop; - - /* Log fps and slack percentage */ - LOG("fps:%d\t" - "rendering:%.2f%%\t" - //"slack:%.2f%%\t" - "input:%.2f%% (%.2f%%)\t" - "gameloop:%.2f%%\t" - "unaccounted:%llu / %llu ms\t" - "avg drawcalls:%llu", - fps, 100.0f * (f32)profile_rendering / (f32)sum, - // 100.0f * (f32)profile_slack / (f32)sum, - 100.0f * (f32)profile_input / (f32)sum, - 100.0f * (f32)profile_input_handling / (f32)sum, - 100.0f * (f32)profile_gameloop / (f32)sum, - time - profile_interval_timer - sum, sum, drawcalls); - /* Reset values */ - profile_tick_counter = ticks; - profile_interval_timer = time; - // profile_slack = 0; - profile_rendering = 0; - profile_gameloop = 0; - profile_input = 0; - profile_input_handling = 0; - profile_num_drawcalls = 0; - } -#endif - - glfwPollEvents(); - /* Events */ -// if (p->mouse_lclick) { -// p->mouseup.x = -1; -// p->mouseup.y = -1; -// p->mousedown.x = -1; -// p->mousedown.y = -1; -// p->mouse_lclick = false; -// } -// if (p->mouse_rclick) { -// p->mouse_rclick = false; -// } -// -// /* Window events */ -// i32 num_events; -// -// /* Mouse events */ -// -// if (p->bindings != NULL) { -// const i_ctx* bindings = *p->bindings; -// const usize bindings_len = p->bindings_len; -// -// } -// -// i_flush_bindings(callbacks_len, mem->data, callbacks); -// callbacks_len = 0; -// -// /* update */ -// StateType next_state; -// next_state = update_func((void*)(mem->data)); -// -// if (next_state != STATE_null) { -// if (next_state == STATE_quit) break; -// -// drawcall_reset(); -// -// engine_window_resize_pointers_reset(); -// State_free(state, mem); -// memory_clear(mem); -// -// engine_input_ctx_reset(); -// -// state = next_state; -// update_func = State_updateFunc(state); -//#ifdef BENCHMARK -// { -// f64 t = get_time(); -// State_init(state, mem); -// LOG("Initializing %s took %dms", StateTypeStr[state], -// (int)((get_time() - t) * 1000.0)); -// } -//#else -// State_init(state, mem); -//#endif -// } else { -//#ifdef BENCHMARK -// profile_num_drawcalls += drawcall_len; -//#endif - render_begin(p->window); - - - gl->UseProgram(p->testobject->shaderprogram); - - { - vec3 cam = {4., 3., 3.}; // perspective - mat4 per; // perspective - mat4 v; // view - mat4 model = GLM_MAT4_IDENTITY_INIT; - mat4 modelviewprojection; - - f32 ratio = (float)p->window->windowsize.x / (float)p->window->windowsize.y; - //glm_perspective(45.f , 600.f / 400.f, 0.1, 100.0f, per); - glm_ortho(-10 * ratio, 10 * ratio, -10, 10, -10, 10, per); - - glm_lookat(cam, GLM_VEC3_ZERO, GLM_YUP, v); - - { mat4 t; - //modelviewprojection = p * v * model - glm_mat4_mul(v, model, t); - glm_rotate_at(t, (vec3){0,0,0}, get_time() / 2.f, (vec3){0,1,0}); //, (vec3)({0,1,0})); - glm_mat4_mul(per, t, modelviewprojection); - } - - // TODO: Do this only once during initialization - u32 matrix = gl->GetUniformLocation(p->testobject->shaderprogram, "MVP"); - - gl->UniformMatrix4fv(matrix, 1, GL_FALSE, &modelviewprojection[0][0]); - } - - - gl->EnableVertexAttribArray(0); - gl->BindBuffer(GL_ARRAY_BUFFER, p->testobject->vbo); - gl->VertexAttribPointer( - 0, // attribute 0. No particular reason for 0, but must match the layout in the shader. - 3, // size - GL_FLOAT, // type - GL_FALSE, // normalized? - 0, // stride - (void*)0 // array buffer offset - ); - - // Do the color buffer (?) - gl->EnableVertexAttribArray(1); - gl->BindBuffer(GL_ARRAY_BUFFER, p->testobject->col); - gl->VertexAttribPointer( - 1, // attribute. No particular reason for 1, but must match the layout in the shader. - 3, // size - GL_FLOAT, // type - GL_FALSE, // normalized? - 0, // stride - (void*)0 // array buffer offset - ); - - - - - gl->UseProgram(p->testobject->shaderprogram); - // Draw the triangle ! - gl->DrawArrays(GL_TRIANGLES, 0, 3*12); // Starting from vertex 0; 3 vertices total -> 1 triangle - - gl->DisableVertexAttribArray(0); - gl->DisableVertexAttribArray(1); - - render_present(p->window); -// } - - ticks++; - } while( - !glfwWindowShouldClose(p->window->window) - && state != STATE_quit); - - return 0; -} - -void stop(Platform* p) { - if (p == NULL) return; - - { /* Deallocate resources */ - struct Resources* r = (struct Resources*)p->data; - if (r != NULL) { - /* Destroy textures */ - for (usize i = 0; i < r->textures_len; i++) { - if (r->textures[i] != NULL) { - r->textures[i] = NULL; - } - } - free(r->textures); - - /* Destroy Fonts */ - } - } - - glfwDestroyWindow(p->window->window); - glfwTerminate(); - -} - -/* Set the maximum framerate */ -void engine_fps_max(u64 cap) { FPS_CAP = cap; } - -/* Pushes an input context onto the input handling stack */ -void engine_input_ctx_push(i_ctx* ctx) { - if (GLOBAL_PLATFORM->bindings == NULL) { - GLOBAL_PLATFORM->bindings = calloc(8, sizeof(i_ctx*)); - GLOBAL_PLATFORM->bindings_sz = 8; - } - - if (GLOBAL_PLATFORM->bindings_len + 1 >= GLOBAL_PLATFORM->bindings_sz) { - void* m = - realloc(GLOBAL_PLATFORM->bindings, GLOBAL_PLATFORM->bindings_sz + 8); - if (m == NULL) { - ERROR("Failed to allocate 8 bytes (%d): %s", errno, strerror(errno)); - exit(EXIT_FAILURE); - } - GLOBAL_PLATFORM->bindings_sz += 8; - } - - LOG("Bindings in ctx[%d]:", GLOBAL_PLATFORM->bindings_len); - for (isize i = 0; i < ctx->len; i++) { - switch (ctx->bindings[i].action.type) { - case InputType_error: - LOG("(error)"); - break; - - case InputType_action: - LOG("(action) %s", ctx->bindings[i].action.action.callback_str); - break; - - case InputType_state: - LOG("(+state) %s", ctx->bindings[i].action.state.activate_str); - LOG("(-state) %s", ctx->bindings[i].action.state.deactivate_str); - break; - case InputType_range: - LOG("(range) --unhandled--"); - break; - } - } - - GLOBAL_PLATFORM->bindings[GLOBAL_PLATFORM->bindings_len++] = ctx; -} - -/* Pops an input context from the input stack */ -void engine_input_ctx_pop(void) { - if (GLOBAL_PLATFORM->bindings == NULL || GLOBAL_PLATFORM->bindings_sz == 0) - return; - i_ctx_t_free(GLOBAL_PLATFORM->bindings[--GLOBAL_PLATFORM->bindings_len]); -} - -/* Removes all input contexts from the input stack */ -void engine_input_ctx_reset(void) { - while (GLOBAL_PLATFORM->bindings_len > 0) { - i_ctx_t_free(GLOBAL_PLATFORM->bindings[--GLOBAL_PLATFORM->bindings_len]); - } -} - -f64 get_time(void) { return glfwGetTime(); } -v2_i32 get_windowsize(void) { return GLOBAL_PLATFORM->window->windowsize; } -v2_i32* get_mousepos(void) { return &GLOBAL_PLATFORM->mouse_pos; } diff --git a/src/core/memory.c b/src/core/memory.c deleted file mode 100644 index f19803e..0000000 --- a/src/core/memory.c +++ /dev/null @@ -1,62 +0,0 @@ -#include -#include -#include - -#include - -#include - -memory* memory_new(usize max_size) { - memory* m = malloc(sizeof(memory)); - m->data = malloc(max_size); - m->size = max_size; - m->pos = 0; - m->free = max_size; - - memset(m->data, 0, max_size); - - return m; -} - -/* Returns a pointer to the allocated data */ -void* memory_allocate(memory* mem, usize size) { - void* data = NULL; - - if (mem->pos + size <= mem->size) { - data = (void*)((usize)mem->data + mem->pos); - mem->pos += size; - mem->free -= size; - } else { - ERROR("Trying to allocate %lu in a %lu sized memory block", size, - mem->size); - ERROR("No more room!"); - exit(EXIT_FAILURE); - } - - return data; -} - -memory memory_init(void* data, usize size) { - memory m = {0}; - m.data = data; - m.size = size; - m.free = 0; - return m; -} - -void memory_free(memory* mem, usize size) { - if (size > mem->pos) { - perror("Freeing too much memory!"); - exit(EXIT_FAILURE); - } else { - mem->pos -= size; - mem->free += size; - } -} - -void memory_clear(memory* mem) { - mem->pos = 0; - mem->free = mem->size; - /* Reset the memory? */ - memset(mem->data, 0, mem->size); -} diff --git a/src/core/src/cleanup.c b/src/core/src/cleanup.c new file mode 100644 index 0000000..e69de29 diff --git a/src/core/src/dltools.c b/src/core/src/dltools.c new file mode 100644 index 0000000..1e43b79 --- /dev/null +++ b/src/core/src/dltools.c @@ -0,0 +1,57 @@ +#include + +#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) +/* include winapi */ +#else +#include +#endif + +#include +#include + +bool dynamic_library_close(void* shared_library) { +#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) + return true; +#else + return dlclose(shared_library) == 0; +#endif +} + +void* dynamic_library_open(const char* library_path) { +#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) + return NULL; +#else + return dlopen(library_path, RTLD_NOW); +#endif +} + +char* dynamic_library_get_error(void) { +#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) + return "unsupported on windows"; +#else + return dlerror(); +#endif +} + +void* dynamic_library_reload(void* shared_library, const char* library_path) { + void* library_address = NULL; + if (!dynamic_library_close(shared_library)) { + ERROR("Failed to close shared library: %s", dynamic_library_get_error()); + ERROR("Reloading dynamic library failed."); + return library_address; + } + if ((library_address = dynamic_library_open(library_path)) == NULL) { + ERROR("Failed to open shared library: %s", dynamic_library_get_error()); + ERROR("Reloading dynamic library %s failed.", library_path); + } + return library_address; +} + +void* dynamic_library_get_symbol(void* restrict shared_library, + const char* symbol) { +#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) + return NULL; +#else + return dlsym(shared_library, symbol); +#endif +} diff --git a/src/core/src/harness.c b/src/core/src/harness.c new file mode 100644 index 0000000..e69de29 diff --git a/src/core/src/init.c b/src/core/src/init.c new file mode 100644 index 0000000..e69de29 diff --git a/src/core/src/logging.c b/src/core/src/logging.c new file mode 100644 index 0000000..7870258 --- /dev/null +++ b/src/core/src/logging.c @@ -0,0 +1,82 @@ +#include +#include +#include + +char* itoa(i32 x) { + const i32 size = (((i32)ceil(log10((f64)x))) + 1) * sizeof(char); + char* retval = (char*)malloc(size); + if (retval == NULL) { + perror("Failed to allocate memory for itoa"); + exit(EXIT_FAILURE); + } + sprintf(retval, "%d", x); + return retval; +} + +void _log(FILE* stream, const char* prefix, const char* fmt, va_list ap) { + if (stream == NULL) { + fprintf(stderr, "_log got NULL in stream argument\n"); + exit(EXIT_FAILURE); + } + fputs(prefix, stream); + vfprintf(stream, fmt, ap); +} + +void LOG(const char* fmt, ...) { + va_list ap; + va_start(ap, fmt); + _log(stdout, "[" TERM_COLOR_BLUE "LOG" TERM_COLOR_RESET "] ", fmt, ap); + va_end(ap); + puts(""); +} + +void INFO_(const char* fmt, ...) { + va_list ap; + va_start(ap, fmt); + _log(stdout, "[" TERM_COLOR_GREEN "INFO" TERM_COLOR_RESET "] ", fmt, ap); + va_end(ap); +} + +void INFO(const char* fmt, ...) { + va_list ap; + va_start(ap, fmt); + _log(stdout, "[" TERM_COLOR_GREEN "INFO" TERM_COLOR_RESET "] ", fmt, ap); + va_end(ap); + puts(""); +} + +void __DEBUG(const char* file, const i32 line, const char* func, + const char* fmt, ...) { + va_list ap; + + const usize prefix_len = 1024; + + char* prefix = malloc(sizeof(char) * 1024); + + snprintf(prefix, prefix_len, + "[" TERM_COLOR_YELLOW "DEBUG" TERM_COLOR_RESET "] " + "%s:%d <%s> ", + file, line, func); + + va_start(ap, fmt); + _log(stdout, prefix, fmt, ap); + va_end(ap); + + free(prefix); +} + +void WARN(const char* fmt, ...) { + va_list ap; + va_start(ap, fmt); + _log(stderr, "[" TERM_COLOR_PURPLE "WARN" TERM_COLOR_RESET "] ", fmt, ap); + va_end(ap); + puts(""); +} + +void ERROR(const char* fmt, ...) { + va_list ap; + va_start(ap, fmt); + _log(stderr, "[" TERM_COLOR_RED "ERROR" TERM_COLOR_RESET "] ", fmt, ap); + va_end(ap); + puts(""); +} diff --git a/src/core/src/loop.c b/src/core/src/loop.c new file mode 100644 index 0000000..79e0c0c --- /dev/null +++ b/src/core/src/loop.c @@ -0,0 +1,976 @@ +#include +#include +#include +#include + +#define GLAD_GL_IMPLEMENTATION +#include +#undef GLAD_GL_IMPLEMENTATION + +#undef GLFW_INCLUDE_NONE +#include + +#include + +#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) +/* include winapi */ +#include +#elif defined(__APPLE__) +/* mac includes */ +#elif defined(__linux) || defined(__linux__) || defined(linux) + +#include +#include + +#endif + +#define ENGINE_INTERNALS +#include +#include +#include +#include + +#include +// #include +// #include + +#define DEFAULT_NUM_PROCS 8 + +#ifdef BENCHMARK +#define BENCHEXPR(timevar, expr) \ + { \ + f64 t = get_time(); \ + expr timevar += get_time() - t; \ + } + +extern i32 drawcall_len; + +#else +#define BENCHEXPR(timevar, expr) expr +#endif + + +// http://www.opengl-tutorial.org/beginners-tutorials/tutorial-2-the-first-triangle/ +GLuint LoadShaders(const GladGLContext* gl, const char * vertex_file_path, const char * fragment_file_path){ + + // Create the shaders + GLuint VertexShaderID = gl->CreateShader(GL_VERTEX_SHADER); + GLuint FragmentShaderID = gl->CreateShader(GL_FRAGMENT_SHADER); + + // Read the Vertex Shader code from the file + char* VertexShaderCode; + FILE* VertexShaderStream = fopen(vertex_file_path, "r"); + if(VertexShaderStream != NULL){ + fseek(VertexShaderStream, 0, SEEK_END); + const i64 size = ftell(VertexShaderStream); + rewind(VertexShaderStream); + VertexShaderCode = calloc(size + 1, sizeof(char)); + + // Assume the whole file is successfully read + fread(VertexShaderCode, sizeof(char), size, VertexShaderStream); + //LOG("vertex source is %d bytes\n%s\n", size, VertexShaderCode); + + fclose(VertexShaderStream); + } else { + ERROR("Impossible to open %s. Are you in the right directory?\n", vertex_file_path); + getchar(); + return 0; + } + + // Read the Fragment Shader code from the file + char* FragmentShaderCode; + FILE* FragmentShaderStream = fopen(fragment_file_path, "r"); + if(FragmentShaderStream != NULL){ + fseek(FragmentShaderStream, 0, SEEK_END); + const i64 size = ftell(FragmentShaderStream); + rewind(FragmentShaderStream); + FragmentShaderCode = calloc(size + 1, sizeof(char)); + + // Assume the whole file is successfully read + fread(FragmentShaderCode, sizeof(char), size, FragmentShaderStream); + LOG("fragment source is %d bytes", size); + + fclose(FragmentShaderStream); + } else { + ERROR("Impossible to open %s. Are you in the right directory?\n", fragment_file_path); + getchar(); + return 0; + } + + GLint Result = GL_FALSE; + int InfoLogLength; + + // Compile Vertex Shader + INFO("Compiling shader: %s\n", vertex_file_path); + char const * VertexSourcePointer = VertexShaderCode; + gl->ShaderSource(VertexShaderID, 1, &VertexSourcePointer , NULL); + gl->CompileShader(VertexShaderID); + + // Check Vertex Shader + gl->GetShaderiv(VertexShaderID, GL_COMPILE_STATUS, &Result); + gl->GetShaderiv(VertexShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength); + if ( InfoLogLength > 0 ) { + char* msg = calloc(InfoLogLength + 1, sizeof(char)); + gl->GetShaderInfoLog(VertexShaderID, InfoLogLength, NULL, msg); + ERROR("Compiling shader: %s\n", msg); + free(msg); + } + + // Compile Fragment Shader + INFO("Compiling shader: %s\n", fragment_file_path); + char const * FragmentSourcePointer = FragmentShaderCode; + gl->ShaderSource(FragmentShaderID, 1, &FragmentSourcePointer , NULL); + gl->CompileShader(FragmentShaderID); + + // Check Fragment Shader + gl->GetShaderiv(FragmentShaderID, GL_COMPILE_STATUS, &Result); + gl->GetShaderiv(FragmentShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength); + if ( InfoLogLength > 0 ){ + char* msg = calloc(InfoLogLength + 1, sizeof(char)); + gl->GetShaderInfoLog(FragmentShaderID, InfoLogLength, NULL, msg); + ERROR("Compiling shader: %s\n", msg); + free(msg); + } + + // Link the program + INFO("Linking program\n"); + GLuint ProgramID = gl->CreateProgram(); + gl->AttachShader(ProgramID, VertexShaderID); + gl->AttachShader(ProgramID, FragmentShaderID); + gl->LinkProgram(ProgramID); + + // Check the program + gl->GetProgramiv(ProgramID, GL_LINK_STATUS, &Result); + gl->GetProgramiv(ProgramID, GL_INFO_LOG_LENGTH, &InfoLogLength); + if ( InfoLogLength > 0 ){ + char* msg = calloc(InfoLogLength + 1, sizeof(char)); + gl->GetShaderInfoLog(ProgramID, InfoLogLength, NULL, msg); + ERROR("Compiling shader: %s\n", msg); + free(msg); + } + + gl->DetachShader(ProgramID, VertexShaderID); + gl->DetachShader(ProgramID, FragmentShaderID); + + gl->DeleteShader(VertexShaderID); + gl->DeleteShader(FragmentShaderID); + + + free(VertexShaderCode); + free(FragmentShaderCode); + return ProgramID; +} + + + + + + + + + + + +static u64 FPS_CAP = 50; +Platform* GLOBAL_PLATFORM = NULL; + +input_callback_t* callbacks[128]; +usize callbacks_len; + +i32 nproc(void) { + return get_nprocs(); +} + +void delay( uint32_t ms ) +{ +#ifdef _WIN32 + Sleep( ms ); +#else + usleep( ms * 1000 ); +#endif +} + +i32 cmp_int(const void* a, const void* b) { + const i32* x = a; + const i32* y = b; + + return *x - *y; +} + +v2_i32 get_canvas_size(void* window) { + v2_i32 realsize; + glfwGetWindowSize(window, &(realsize.x), &(realsize.y)); + + /* Set logical render size */ + return realsize; +} + +Texture* load_texture(void* render, const Asset_TextureSpec* ts) { + Texture* t = NULL; + + if (ts == NULL) { + ERROR("Invalid Asset_TextureSpec\n"); + return NULL; + } + + //t = (Texture*)malloc(sizeof(Texture)); + //t->texture = new_texture; + ///* Assigning const value */ + //*(i32*)&t->tilesize = tw; + //*(i32*)&t->width = ts->width; + //*(i32*)&t->height = ts->height; + + return t; +} + +void engine_update_window(Window* w, void* e) { + switch ((i32)e) { + default: + WARN("Unhandled window event 0x%04x", (i32)e); + break; + } + return; +} + +struct glfw_ctx { + GLFWwindow* w; + GladGLContext* c; +} glfw_ctx; + +/* GLFW And vulkan spaghetti boiler */ +void glfw_err_callback(int code, const char* description) { + ERROR("glfw [%d]: %s\n", code, description); + // Terminate? + exit(EXIT_FAILURE); +} + +struct QueueFamilyIndices { + int64_t graphicsFamily; + int64_t presentFamily; +}; + +GladGLContext* create_context(GLFWwindow *window) { + glfwMakeContextCurrent(window); + + GladGLContext* context = (GladGLContext*) calloc(1, sizeof(GladGLContext)); + if (!context) return NULL; + + int version = gladLoadGLContext(context, glfwGetProcAddress); + INFO("Loaded OpenGL %d.%d", GLAD_VERSION_MAJOR(version), GLAD_VERSION_MINOR(version)); + + return context; +} + +struct glfw_ctx initialize_GLFW( + const char* windowtitle, v2_i32 windowsize, + const u32 flags + ) { + GLFWwindow* window = NULL; + + glfwSetErrorCallback(&glfw_err_callback); + + INFO_("initializing glfw..."); + if (glfwInit() == GLFW_FALSE) { + const char *desc; + int code = glfwGetError(&desc); + ERROR("failed to initialize glfw [%d]: %s\n", code, *desc); + exit(EXIT_FAILURE); + } else + printf("ok\n"); + + + INFO_("initializing window..."); + //glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); + + glfwWindowHint(GLFW_SAMPLES, 0); // Disable anti aliasing + + // Use a modern opengl version + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6); + + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + +#ifdef __APPLE__ + // To make MacOS happy; should not be needed + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); +#endif + + + /* "On Wayland specifically, you need to swap the buffers + * of a window for it to become visible." */ + window = glfwCreateWindow(windowsize.x, windowsize.y, windowtitle, NULL, NULL); + if (window == NULL) { + ERROR("Failed to create GLFW window!\n"); + const char *desc; + int code = glfwGetError(&desc); + ERROR("failed to initialize glfw window [%d]: %s\n", code, desc); + exit(EXIT_FAILURE); + } else + printf("ok\n"); + + //glfwMakeContextCurrent(window); + + // Remember to load GL :) (hours wasted because i forgot: approx 4) + GladGLContext *ctx = create_context(window); + //printf("GL %d.%d\n", GLAD_VERSION_MAJOR(version), GLAD_VERSION_MINOR(version)); + + //ctx->Viewport(0, 0, 200, 200); + + if (ctx == NULL) { + ERROR("Failed to create glad context"); + exit(EXIT_FAILURE); + } + + glfwSetFramebufferSizeCallback(window, window_size_callback); + glfwSwapInterval(0); + +#ifdef _DEBUG + ctx->ClearColor((float)0x10 / 255.f, (float)0x0a / 255.f, (float)0x33 / 255.f, 0.f); +#else + ctx->ClearColor(0x0, 0x0, 0x0, 0.f); +#endif + + ctx->Enable(GL_DEPTH_TEST); + ctx->DepthFunc(GL_LESS); + + return (struct glfw_ctx){.w = window, .c = ctx}; +} + + +/* Creates the window, initializes IO, Rendering, Fonts and engine-specific + * resources. */ +Platform* engine_init(const char* windowtitle, v2_i32 windowsize, + const f32 render_scale, const u32 flags, + const usize initial_memory, const Asset_FontSpec* fonts[], + const Asset_TextureSpec* textures[]) { + +#ifdef BENCHMARK + f64 init_start = get_time(); +#endif + +#if defined(__linux) || defined(__linux__) || defined(linux) + { + pid_t pid = getpid(); + INFO("Starting with pid %lu", pid); + } +#endif + + Platform* p = (Platform*)malloc(sizeof(Platform)); + Window* w = (Window*)malloc(sizeof(Window)); + + /* initialize resources */ + struct Resources* resources = + (struct Resources*)malloc(sizeof(struct Resources)); + resources->textures_len = 0; + resources->fonts_len = 0; + resources->texturepaths_len = 0; + resources->fontpaths_len = 0; + resources->texture_paths = NULL; + resources->font_paths = NULL; + resources->textures = NULL; + //resources->fonts = NULL; + + { + struct glfw_ctx ctx = initialize_GLFW(windowtitle, windowsize, flags); + w->window = ctx.w; + w->context = ctx.c; + } + + const GladGLContext *gl = w->context; + + struct RenderObject *testobject = malloc(sizeof(struct RenderObject)); + + gl->GenVertexArrays(1, &(testobject->vao)); + gl->BindVertexArray(testobject->vao); + + p->testobject = testobject; + + testobject->g_vertex_buffer_data[0] = -1.0f; + testobject->g_vertex_buffer_data[1] = -1.0f; + testobject->g_vertex_buffer_data[2] = 0.0f; + + testobject->g_vertex_buffer_data[3] = 1.0f; + testobject->g_vertex_buffer_data[4] = -1.0f; + testobject->g_vertex_buffer_data[5] = 0.0f; + + testobject->g_vertex_buffer_data[6] = 0.0f; + testobject->g_vertex_buffer_data[7] = 1.0f; + testobject->g_vertex_buffer_data[8] = 0.0f; + + static const float bufdata[] = { + -1.0f,-1.0f,-1.0f, // triangle 1 : begin + -1.0f,-1.0f, 1.0f, + -1.0f, 1.0f, 1.0f, // triangle 1 : end + 1.0f, 1.0f,-1.0f, // triangle 2 : begin + -1.0f,-1.0f,-1.0f, + -1.0f, 1.0f,-1.0f, // triangle 2 : end + 1.0f,-1.0f, 1.0f, + -1.0f,-1.0f,-1.0f, + 1.0f,-1.0f,-1.0f, + 1.0f, 1.0f,-1.0f, + 1.0f,-1.0f,-1.0f, + -1.0f,-1.0f,-1.0f, + -1.0f,-1.0f,-1.0f, + -1.0f, 1.0f, 1.0f, + -1.0f, 1.0f,-1.0f, + 1.0f,-1.0f, 1.0f, + -1.0f,-1.0f, 1.0f, + -1.0f,-1.0f,-1.0f, + -1.0f, 1.0f, 1.0f, + -1.0f,-1.0f, 1.0f, + 1.0f,-1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, + 1.0f,-1.0f,-1.0f, + 1.0f, 1.0f,-1.0f, + 1.0f,-1.0f,-1.0f, + 1.0f, 1.0f, 1.0f, + 1.0f,-1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, + 1.0f, 1.0f,-1.0f, + -1.0f, 1.0f,-1.0f, + 1.0f, 1.0f, 1.0f, + -1.0f, 1.0f,-1.0f, + -1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, + -1.0f, 1.0f, 1.0f, + 1.0f,-1.0f, 1.0f + }; + + static const GLfloat g_color_buffer_data[] = { + 0.583f, 0.771f, 0.014f, + 0.609f, 0.115f, 0.436f, + 0.327f, 0.483f, 0.844f, + 0.822f, 0.569f, 0.201f, + 0.435f, 0.602f, 0.223f, + 0.310f, 0.747f, 0.185f, + 0.597f, 0.770f, 0.761f, + 0.559f, 0.436f, 0.730f, + 0.359f, 0.583f, 0.152f, + 0.483f, 0.596f, 0.789f, + 0.559f, 0.861f, 0.639f, + 0.195f, 0.548f, 0.859f, + 0.014f, 0.184f, 0.576f, + 0.771f, 0.328f, 0.970f, + 0.406f, 0.615f, 0.116f, + 0.676f, 0.977f, 0.133f, + 0.971f, 0.572f, 0.833f, + 0.140f, 0.616f, 0.489f, + 0.997f, 0.513f, 0.064f, + 0.945f, 0.719f, 0.592f, + 0.543f, 0.021f, 0.978f, + 0.279f, 0.317f, 0.505f, + 0.167f, 0.620f, 0.077f, + 0.347f, 0.857f, 0.137f, + 0.055f, 0.953f, 0.042f, + 0.714f, 0.505f, 0.345f, + 0.783f, 0.290f, 0.734f, + 0.722f, 0.645f, 0.174f, + 0.302f, 0.455f, 0.848f, + 0.225f, 0.587f, 0.040f, + 0.517f, 0.713f, 0.338f, + 0.053f, 0.959f, 0.120f, + 0.393f, 0.621f, 0.362f, + 0.673f, 0.211f, 0.457f, + 0.820f, 0.883f, 0.371f, + 0.982f, 0.099f, 0.879f +}; + + +// LOG("sizeof(bufdata) = %lu", sizeof(bufdata)); +// LOG("sizeof(g_vertex_buffer_data) = %lu", sizeof(testobject->g_vertex_buffer_data)); +// + + // Generate 1 buffer, put the resulting identifier in vertexbuffer + gl->GenBuffers(1, &(testobject->vbo)); + // The following commands will talk about our 'vertexbuffer' buffer + gl->BindBuffer(GL_ARRAY_BUFFER, testobject->vbo); + // Give our vertices to OpenGL. + gl->BufferData(GL_ARRAY_BUFFER, sizeof(bufdata), bufdata, GL_STATIC_DRAW); + + // Same for the color buffer + gl->GenBuffers(1, &(testobject->col)); + gl->BindBuffer(GL_ARRAY_BUFFER, testobject->col); + gl->BufferData(GL_ARRAY_BUFFER, sizeof(g_color_buffer_data), g_color_buffer_data, GL_STATIC_DRAW); + + testobject->shaderprogram = LoadShaders(gl, "shader.vertexshader", "shader.fragmentshader"); + INFO("Shaderprogram %d", testobject->shaderprogram); + + { /* Resource loading */ + + /* Count resources */ + usize n_textures = 0; + usize n_fonts = 0; + + if (textures != NULL) + while (textures[n_textures] != NULL) n_textures++; + if (fonts != NULL) + while (fonts[n_fonts] != NULL) n_fonts++; + + INFO("Number of textures: " TERM_COLOR_YELLOW "%d" TERM_COLOR_RESET, + n_textures); + INFO("Number of fonts: " TERM_COLOR_YELLOW "%d" TERM_COLOR_RESET, n_fonts); + + /* Save the textures and fonts, if we should need to reload them later */ + resources->texture_paths = (Asset_TextureSpec**)textures; + resources->font_paths = (Asset_FontSpec**)fonts; + + /* Allocate memory for textures and fonts */ + resources->textures = (Texture**)malloc(sizeof(Texture*) * n_textures); + //resources->fonts = (TTF_Font**)malloc(sizeof(TTF_Font*) * n_fonts); + resources->textures_size = n_textures; + + for (usize i = 0; i < n_textures; i++) resources->textures[i] = NULL; + //for (usize i = 0; i < n_fonts; i++) resources->fonts[i] = NULL; + + /* Load textures */ + for (usize i = 0; i < n_textures; i++) { + Texture* t = NULL; + INFO_("loading texture \"" TERM_COLOR_YELLOW "%s" TERM_COLOR_RESET + "\"...", + textures[i]->path); + +// t = load_texture(renderer, textures[i]); +// if (t == NULL) { +// puts(""); +// ERROR("failed to load texture\n"); +// exit(EXIT_FAILURE); +// } +// +// if (t->tilesize < 8) { +// puts(""); +// ERROR("texture too small!\n"); +// exit(EXIT_FAILURE); +// } +// + //if (t->texture == NULL) { + // puts(""); + // ERROR("failed to load texture\n"); + //} else { + // printf("ok\n"); + // resources->textures[i] = t; + // resources->textures_len++; + //} + } + + /* Load fonts */ + for (usize i = 0; i < n_fonts; i++) { + INFO_("loading font \"" TERM_COLOR_YELLOW "%s" TERM_COLOR_RESET "\"...", + fonts[i]->font_path); + + //TTF_Font* font = TTF_OpenFont(fonts[i]->font_path, fonts[i]->ptsize); + //if (!font) { + // ERROR("failed to load font: %s\n", TTF_GetError()); + //} else { + // printf("ok\n"); + // resources->fonts[i] = font; + // resources->fonts_len++; + //} + } + + if (resources->textures_len != n_textures) { + WARN("Done. %d/%d textures loaded.", resources->textures_len, n_textures); + } else { + INFO("Done. All %d textures loaded.", n_textures); + } + + if (resources->fonts_len != n_fonts) { + WARN("Done. %d/%d fonts loaded.", resources->fonts_len, n_fonts); + } else { + INFO("Done. All %d fonts loaded.", n_fonts); + } + + resources->texturepaths_len = resources->textures_len; + resources->fontpaths_len = resources->fonts_len; + } + + { /* Adjust window and such */ + /* Set actual windowsize, which might be forced by OS */ + INFO("Adjusting window size..."); + //windowsize = get_canvas_size(renderer); + + INFO("Windowsize: <%d,%d>", windowsize.x, windowsize.y); + } + + //w->renderer = renderer; + //w->window = window; + w->render_scale = render_scale; + w->windowsize = windowsize; + w->game_w = NULL; + w->game_h = NULL; + + p->data = (void*)resources; + p->data_len = sizeof(struct Resources); + p->window = w; + p->quit = false; + + p->frame = 0; + p->fps_target = 60; + + p->mem = memory_new(initial_memory); + + /* Getting the mouse coords now resolves the issue where a click "isn't + * registered" when the mouse isn't moved before the user clicks */ + + p->mousedown = (v2_i32){-1, -1}; + p->mouseup = (v2_i32){-1, -1}; + + p->mouse_lclick = false; + p->mouse_rclick = false; + + p->camera_x = 0; + p->camera_y = 0; + + p->edit_text = NULL; + p->edit_pos = 0; + + p->bindings = NULL; + p->bindings_sz = 0; + p->bindings_len = 0; + + // TODO: Add global bindings + +#ifdef BENCHMARK + f64 init_stop = get_time(); + INFO("Initialization took %dms", init_stop - init_start); +#endif + + INFO("Available cores: %d", nproc()); + + GLOBAL_PLATFORM = p; + +#ifdef DAW_BUILD_HOTRELOAD + +#define State(name) \ + if (!State_reload(STATE_##name, p->bindings, p->bindings_len)) { \ + ERROR("Failed to reload shared object file for state %s", #name); \ + }; + +#include +#undef State + +#endif + + return p; +} + +i32 engine_run(Platform* p, StateType initial_state) { + if (p == NULL) { + ERROR("Platform is uninitialized.\n"); + INFO("initialize with `engine_init`"); + return -1; + } + + memory* mem = p->mem; + + StateType state = initial_state; + + { + f64 state_init_time = get_time(); + State_init(state, mem); + INFO("Initializing state \"%s\" took %ldms", StateTypeStr[state], + get_time() - state_init_time); + } + + f64 time = get_time(); + + // Update ticks + u64 ticks = 0; + + /* Profiling values */ +#ifdef BENCHMARK + u64 profile_tick_counter = 0; + // u64 profile_slack = 0; + u64 profile_rendering = 0; + u64 profile_gameloop = 0; + u64 profile_input = 0; + u64 profile_input_handling = 0; + u64 profile_num_drawcalls = 0; + u32 profile_interval_timer = time; + const u32 profile_interval_ms = 5000; + const f32 profile_interval_scale = (f32)(profile_interval_ms) / 100.0f; +#endif + + const f64 frame_interval = 1000.0 / FPS_CAP; + + StateType (*update_func)(void*) = State_updateFunc(state); + + f64 last_fps_measurement = get_time(); + + /* Main loop */ + INFO("Program: %d", p->testobject->shaderprogram); + GladGLContext *gl = p->window->context; + do { + const f64 now = get_time(); + const f64 dt = now - time; + time = now; + /* Wait frame_interval */ + if (dt < frame_interval) { +#ifndef BENCHMARK + //delay(frame_interval - dt); + +#else + /* We want to know how much time is spend sleeping */ + // profile_slack += frame_interval - dt; +#endif + } + + if (now - last_fps_measurement > 1.000) { + last_fps_measurement = now; + printf("\n FPS: %.1f \t ticks: %lu", (double)ticks / now, ticks); + } + +#ifdef BENCHMARK + if (time - profile_interval_timer > profile_interval_ms) { + /* Ticks/frames since last measurement */ + u32 fps = (ticks - profile_tick_counter) / profile_interval_scale; + u64 drawcalls = profile_num_drawcalls / profile_interval_scale / fps; + + u32 sum = +profile_rendering + //+ profile_slack + + profile_input + profile_input_handling + profile_gameloop; + + /* Log fps and slack percentage */ + LOG("fps:%d\t" + "rendering:%.2f%%\t" + //"slack:%.2f%%\t" + "input:%.2f%% (%.2f%%)\t" + "gameloop:%.2f%%\t" + "unaccounted:%llu / %llu ms\t" + "avg drawcalls:%llu", + fps, 100.0f * (f32)profile_rendering / (f32)sum, + // 100.0f * (f32)profile_slack / (f32)sum, + 100.0f * (f32)profile_input / (f32)sum, + 100.0f * (f32)profile_input_handling / (f32)sum, + 100.0f * (f32)profile_gameloop / (f32)sum, + time - profile_interval_timer - sum, sum, drawcalls); + /* Reset values */ + profile_tick_counter = ticks; + profile_interval_timer = time; + // profile_slack = 0; + profile_rendering = 0; + profile_gameloop = 0; + profile_input = 0; + profile_input_handling = 0; + profile_num_drawcalls = 0; + } +#endif + + glfwPollEvents(); + /* Events */ +// if (p->mouse_lclick) { +// p->mouseup.x = -1; +// p->mouseup.y = -1; +// p->mousedown.x = -1; +// p->mousedown.y = -1; +// p->mouse_lclick = false; +// } +// if (p->mouse_rclick) { +// p->mouse_rclick = false; +// } +// +// /* Window events */ +// i32 num_events; +// +// /* Mouse events */ +// +// if (p->bindings != NULL) { +// const i_ctx* bindings = *p->bindings; +// const usize bindings_len = p->bindings_len; +// +// } +// +// i_flush_bindings(callbacks_len, mem->data, callbacks); +// callbacks_len = 0; +// +// /* update */ +// StateType next_state; +// next_state = update_func((void*)(mem->data)); +// +// if (next_state != STATE_null) { +// if (next_state == STATE_quit) break; +// +// drawcall_reset(); +// +// engine_window_resize_pointers_reset(); +// State_free(state, mem); +// memory_clear(mem); +// +// engine_input_ctx_reset(); +// +// state = next_state; +// update_func = State_updateFunc(state); +//#ifdef BENCHMARK +// { +// f64 t = get_time(); +// State_init(state, mem); +// LOG("Initializing %s took %dms", StateTypeStr[state], +// (int)((get_time() - t) * 1000.0)); +// } +//#else +// State_init(state, mem); +//#endif +// } else { +//#ifdef BENCHMARK +// profile_num_drawcalls += drawcall_len; +//#endif + render_begin(p->window); + + + gl->UseProgram(p->testobject->shaderprogram); + + { + vec3 cam = {4., 3., 3.}; // perspective + mat4 per; // perspective + mat4 v; // view + mat4 model = GLM_MAT4_IDENTITY_INIT; + mat4 modelviewprojection; + + f32 ratio = (float)p->window->windowsize.x / (float)p->window->windowsize.y; + //glm_perspective(45.f , 600.f / 400.f, 0.1, 100.0f, per); + glm_ortho(-10 * ratio, 10 * ratio, -10, 10, -10, 10, per); + + glm_lookat(cam, GLM_VEC3_ZERO, GLM_YUP, v); + + { mat4 t; + //modelviewprojection = p * v * model + glm_mat4_mul(v, model, t); + glm_rotate_at(t, (vec3){0,0,0}, get_time() / 2.f, (vec3){0,1,0}); //, (vec3)({0,1,0})); + glm_mat4_mul(per, t, modelviewprojection); + } + + // TODO: Do this only once during initialization + u32 matrix = gl->GetUniformLocation(p->testobject->shaderprogram, "MVP"); + + gl->UniformMatrix4fv(matrix, 1, GL_FALSE, &modelviewprojection[0][0]); + } + + + gl->EnableVertexAttribArray(0); + gl->BindBuffer(GL_ARRAY_BUFFER, p->testobject->vbo); + gl->VertexAttribPointer( + 0, // attribute 0. No particular reason for 0, but must match the layout in the shader. + 3, // size + GL_FLOAT, // type + GL_FALSE, // normalized? + 0, // stride + (void*)0 // array buffer offset + ); + + // Do the color buffer (?) + gl->EnableVertexAttribArray(1); + gl->BindBuffer(GL_ARRAY_BUFFER, p->testobject->col); + gl->VertexAttribPointer( + 1, // attribute. No particular reason for 1, but must match the layout in the shader. + 3, // size + GL_FLOAT, // type + GL_FALSE, // normalized? + 0, // stride + (void*)0 // array buffer offset + ); + + + + + gl->UseProgram(p->testobject->shaderprogram); + // Draw the triangle ! + gl->DrawArrays(GL_TRIANGLES, 0, 3*12); // Starting from vertex 0; 3 vertices total -> 1 triangle + + gl->DisableVertexAttribArray(0); + gl->DisableVertexAttribArray(1); + + render_present(p->window); +// } + + ticks++; + } while( + !glfwWindowShouldClose(p->window->window) + && state != STATE_quit); + + return 0; +} + +void stop(Platform* p) { + if (p == NULL) return; + + { /* Deallocate resources */ + struct Resources* r = (struct Resources*)p->data; + if (r != NULL) { + /* Destroy textures */ + for (usize i = 0; i < r->textures_len; i++) { + if (r->textures[i] != NULL) { + r->textures[i] = NULL; + } + } + free(r->textures); + + /* Destroy Fonts */ + } + } + + glfwDestroyWindow(p->window->window); + glfwTerminate(); + +} + +/* Set the maximum framerate */ +void engine_fps_max(u64 cap) { FPS_CAP = cap; } + +/* Pushes an input context onto the input handling stack */ +void engine_input_ctx_push(i_ctx* ctx) { + if (GLOBAL_PLATFORM->bindings == NULL) { + GLOBAL_PLATFORM->bindings = calloc(8, sizeof(i_ctx*)); + GLOBAL_PLATFORM->bindings_sz = 8; + } + + if (GLOBAL_PLATFORM->bindings_len + 1 >= GLOBAL_PLATFORM->bindings_sz) { + void* m = + realloc(GLOBAL_PLATFORM->bindings, GLOBAL_PLATFORM->bindings_sz + 8); + if (m == NULL) { + ERROR("Failed to allocate 8 bytes (%d): %s", errno, strerror(errno)); + exit(EXIT_FAILURE); + } + GLOBAL_PLATFORM->bindings_sz += 8; + } + + LOG("Bindings in ctx[%d]:", GLOBAL_PLATFORM->bindings_len); + for (isize i = 0; i < ctx->len; i++) { + switch (ctx->bindings[i].action.type) { + case InputType_error: + LOG("(error)"); + break; + + case InputType_action: + LOG("(action) %s", ctx->bindings[i].action.action.callback_str); + break; + + case InputType_state: + LOG("(+state) %s", ctx->bindings[i].action.state.activate_str); + LOG("(-state) %s", ctx->bindings[i].action.state.deactivate_str); + break; + case InputType_range: + LOG("(range) --unhandled--"); + break; + } + } + + GLOBAL_PLATFORM->bindings[GLOBAL_PLATFORM->bindings_len++] = ctx; +} + +/* Pops an input context from the input stack */ +void engine_input_ctx_pop(void) { + if (GLOBAL_PLATFORM->bindings == NULL || GLOBAL_PLATFORM->bindings_sz == 0) + return; + i_ctx_t_free(GLOBAL_PLATFORM->bindings[--GLOBAL_PLATFORM->bindings_len]); +} + +/* Removes all input contexts from the input stack */ +void engine_input_ctx_reset(void) { + while (GLOBAL_PLATFORM->bindings_len > 0) { + i_ctx_t_free(GLOBAL_PLATFORM->bindings[--GLOBAL_PLATFORM->bindings_len]); + } +} + +f64 get_time(void) { return glfwGetTime(); } +v2_i32 get_windowsize(void) { return GLOBAL_PLATFORM->window->windowsize; } +v2_i32* get_mousepos(void) { return &GLOBAL_PLATFORM->mouse_pos; } diff --git a/src/core/src/memory.c b/src/core/src/memory.c new file mode 100644 index 0000000..f19803e --- /dev/null +++ b/src/core/src/memory.c @@ -0,0 +1,62 @@ +#include +#include +#include + +#include + +#include + +memory* memory_new(usize max_size) { + memory* m = malloc(sizeof(memory)); + m->data = malloc(max_size); + m->size = max_size; + m->pos = 0; + m->free = max_size; + + memset(m->data, 0, max_size); + + return m; +} + +/* Returns a pointer to the allocated data */ +void* memory_allocate(memory* mem, usize size) { + void* data = NULL; + + if (mem->pos + size <= mem->size) { + data = (void*)((usize)mem->data + mem->pos); + mem->pos += size; + mem->free -= size; + } else { + ERROR("Trying to allocate %lu in a %lu sized memory block", size, + mem->size); + ERROR("No more room!"); + exit(EXIT_FAILURE); + } + + return data; +} + +memory memory_init(void* data, usize size) { + memory m = {0}; + m.data = data; + m.size = size; + m.free = 0; + return m; +} + +void memory_free(memory* mem, usize size) { + if (size > mem->pos) { + perror("Freeing too much memory!"); + exit(EXIT_FAILURE); + } else { + mem->pos -= size; + mem->free += size; + } +} + +void memory_clear(memory* mem) { + mem->pos = 0; + mem->free = mem->size; + /* Reset the memory? */ + memset(mem->data, 0, mem->size); +} diff --git a/src/core/src/state.c b/src/core/src/state.c new file mode 100644 index 0000000..55f2a12 --- /dev/null +++ b/src/core/src/state.c @@ -0,0 +1,178 @@ +#include + +#include +#include +#include +#include + +typedef StateType state_update_t(void*); + +const char* StateTypeStr[] = { + "null", +#define State(name) #name, +#include +#undef State + "quit", +}; + +// Setup API for states +#define State(name) \ + typedef struct name##_state name##_state; \ + typedef void(state_##name##_init_t)(name##_state*); \ + typedef void(state_##name##_free_t)(name##_state*); \ + typedef StateType(state_##name##_update_t)(name##_state*); +#include +#undef State + +#ifdef DAW_BUILD_HOTRELOAD + +// When hotreloading is enabled, we want to assign state function pointers +// dynamically. +#define State(name) \ + state_##name##_init_t* name##_init = NULL; \ + state_##name##_free_t* name##_free = NULL; \ + state_##name##_update_t* name##_update = NULL; \ + \ + void* libstate_##name = NULL; \ + const char* libstate_##name##_str = "lib" #name ".so"; +#else + +// Otherwise we just declare them. +#define State(name) \ + extern state_##name##_init_t name##_init; \ + extern state_##name##_free_t name##_free; \ + extern state_##name##_update_t name##_update; +#endif + +#include +#undef State + +#include + +void binding_t_free(binding_t* b); + +void State_init(StateType type, memory* mem) { + switch (type) { +#define State(name) \ + case (STATE_##name): { \ + name##_init(memory_allocate(mem, sizeof(name##_state))); \ + break; \ + } +#include +#undef State + case STATE_null: + case STATE_quit: + DEBUG("Got %s state.\n", StateTypeStr[type]); + break; + default: + exit(EXIT_FAILURE); + } +} + +void State_free(StateType type, memory* mem) { + switch (type) { +#define State(name) \ + case (STATE_##name): { \ + name##_free(mem->data); \ + break; \ + } +#include +#undef State + case STATE_null: + case STATE_quit: + DEBUG("Got %s state.\n", StateTypeStr[type]); + break; + default: + exit(EXIT_FAILURE); + } + memory_clear(mem); +} + +StateType (*State_updateFunc(StateType type))(void*) { + switch (type) { +#ifdef DAW_BUILD_HOTRELOAD +#define State(name) \ + case (STATE_##name): { \ + return (state_update_t*)name##_update; \ + break; \ + } +#else +#define State(name) \ + case (STATE_##name): { \ + return (state_update_t*)&name##_update; \ + break; \ + } +#endif +#include +#undef State + case STATE_null: + case STATE_quit: + return NULL; // DEBUG("Got %s state.\n", StateTypeStr[type]); + break; + default: + exit(EXIT_FAILURE); + } + return NULL; +} + +StateType State_update(StateType type, memory* mem) { + StateType next_state = STATE_null; + switch (type) { +#define State(name) \ + case (STATE_##name): { \ + next_state = name##_update(mem->data); \ + break; \ + } +#include +#undef State + case STATE_null: + case STATE_quit: + DEBUG("Got %s state.\n", StateTypeStr[type]); + break; + default: + exit(EXIT_FAILURE); + } + return next_state; +} + +#ifdef DAW_BUILD_HOTRELOAD +bool State_reload(StateType type, i_ctx** ctx, usize ctx_len) { + void* libptr = NULL; + + switch (type) { +#define State(name) \ + case (STATE_##name): { \ + if (libstate_##name == NULL) { \ + libstate_##name = dynamic_library_open(libstate_##name##_str); \ + } else { \ + libstate_##name = \ + dynamic_library_reload(libstate_##name, libstate_##name##_str); \ + } \ + if (libstate_##name == NULL) { \ + ERROR("Failed loading shared object: %s (%s)", libstate_##name##_str, \ + dynamic_library_get_error()); \ + return false; \ + } \ + \ + name##_init = (state_##name##_init_t*)dynamic_library_get_symbol( \ + libstate_##name, STR(name##_init)); \ + name##_free = (state_##name##_free_t*)dynamic_library_get_symbol( \ + libstate_##name, STR(name##_free)); \ + name##_update = (state_##name##_update_t*)dynamic_library_get_symbol( \ + libstate_##name, STR(name##_update)); \ + libptr = libstate_##name; \ + break; \ + } +#include +#undef State + case STATE_null: + case STATE_quit: + ERROR("Invalid state"); + DEBUG("Got %s state.\n", StateTypeStr[type]); + return false; + default: + exit(EXIT_FAILURE); + } + return state_refresh_input_ctx(libptr, ctx, ctx_len); +} +#endif diff --git a/src/core/state.c b/src/core/state.c deleted file mode 100644 index 55f2a12..0000000 --- a/src/core/state.c +++ /dev/null @@ -1,178 +0,0 @@ -#include - -#include -#include -#include -#include - -typedef StateType state_update_t(void*); - -const char* StateTypeStr[] = { - "null", -#define State(name) #name, -#include -#undef State - "quit", -}; - -// Setup API for states -#define State(name) \ - typedef struct name##_state name##_state; \ - typedef void(state_##name##_init_t)(name##_state*); \ - typedef void(state_##name##_free_t)(name##_state*); \ - typedef StateType(state_##name##_update_t)(name##_state*); -#include -#undef State - -#ifdef DAW_BUILD_HOTRELOAD - -// When hotreloading is enabled, we want to assign state function pointers -// dynamically. -#define State(name) \ - state_##name##_init_t* name##_init = NULL; \ - state_##name##_free_t* name##_free = NULL; \ - state_##name##_update_t* name##_update = NULL; \ - \ - void* libstate_##name = NULL; \ - const char* libstate_##name##_str = "lib" #name ".so"; -#else - -// Otherwise we just declare them. -#define State(name) \ - extern state_##name##_init_t name##_init; \ - extern state_##name##_free_t name##_free; \ - extern state_##name##_update_t name##_update; -#endif - -#include -#undef State - -#include - -void binding_t_free(binding_t* b); - -void State_init(StateType type, memory* mem) { - switch (type) { -#define State(name) \ - case (STATE_##name): { \ - name##_init(memory_allocate(mem, sizeof(name##_state))); \ - break; \ - } -#include -#undef State - case STATE_null: - case STATE_quit: - DEBUG("Got %s state.\n", StateTypeStr[type]); - break; - default: - exit(EXIT_FAILURE); - } -} - -void State_free(StateType type, memory* mem) { - switch (type) { -#define State(name) \ - case (STATE_##name): { \ - name##_free(mem->data); \ - break; \ - } -#include -#undef State - case STATE_null: - case STATE_quit: - DEBUG("Got %s state.\n", StateTypeStr[type]); - break; - default: - exit(EXIT_FAILURE); - } - memory_clear(mem); -} - -StateType (*State_updateFunc(StateType type))(void*) { - switch (type) { -#ifdef DAW_BUILD_HOTRELOAD -#define State(name) \ - case (STATE_##name): { \ - return (state_update_t*)name##_update; \ - break; \ - } -#else -#define State(name) \ - case (STATE_##name): { \ - return (state_update_t*)&name##_update; \ - break; \ - } -#endif -#include -#undef State - case STATE_null: - case STATE_quit: - return NULL; // DEBUG("Got %s state.\n", StateTypeStr[type]); - break; - default: - exit(EXIT_FAILURE); - } - return NULL; -} - -StateType State_update(StateType type, memory* mem) { - StateType next_state = STATE_null; - switch (type) { -#define State(name) \ - case (STATE_##name): { \ - next_state = name##_update(mem->data); \ - break; \ - } -#include -#undef State - case STATE_null: - case STATE_quit: - DEBUG("Got %s state.\n", StateTypeStr[type]); - break; - default: - exit(EXIT_FAILURE); - } - return next_state; -} - -#ifdef DAW_BUILD_HOTRELOAD -bool State_reload(StateType type, i_ctx** ctx, usize ctx_len) { - void* libptr = NULL; - - switch (type) { -#define State(name) \ - case (STATE_##name): { \ - if (libstate_##name == NULL) { \ - libstate_##name = dynamic_library_open(libstate_##name##_str); \ - } else { \ - libstate_##name = \ - dynamic_library_reload(libstate_##name, libstate_##name##_str); \ - } \ - if (libstate_##name == NULL) { \ - ERROR("Failed loading shared object: %s (%s)", libstate_##name##_str, \ - dynamic_library_get_error()); \ - return false; \ - } \ - \ - name##_init = (state_##name##_init_t*)dynamic_library_get_symbol( \ - libstate_##name, STR(name##_init)); \ - name##_free = (state_##name##_free_t*)dynamic_library_get_symbol( \ - libstate_##name, STR(name##_free)); \ - name##_update = (state_##name##_update_t*)dynamic_library_get_symbol( \ - libstate_##name, STR(name##_update)); \ - libptr = libstate_##name; \ - break; \ - } -#include -#undef State - case STATE_null: - case STATE_quit: - ERROR("Invalid state"); - DEBUG("Got %s state.\n", StateTypeStr[type]); - return false; - default: - exit(EXIT_FAILURE); - } - return state_refresh_input_ctx(libptr, ctx, ctx_len); -} -#endif diff --git a/src/ctrl/controller.c b/src/ctrl/controller.c deleted file mode 100644 index e69de29..0000000 diff --git a/src/ctrl/ctx.c b/src/ctrl/ctx.c deleted file mode 100644 index e69de29..0000000 diff --git a/src/ctrl/input.c b/src/ctrl/input.c deleted file mode 100644 index 34b1718..0000000 --- a/src/ctrl/input.c +++ /dev/null @@ -1,339 +0,0 @@ -#include -#include -#include -#include - -/* Lazy binds, used internally. They are similar to BindAction and friends. - * The only difference is that we set callbacks and such to NULL, but populate - * the function name strings such that can be reloaded. */ -#define BindActionLazy(key, altkey, action_str) \ - (binding_t) { \ - .action = (action_t){.action = \ - { \ - .type = InputType_action, \ - .callback = NULL, \ - .callback_str = strdup(action_str), \ - }}, \ - .scancode = key, .scancode_alt = altkey, .since_last_activation = 0 \ - } - -#define BindStateLazy(key, altkey, _activate_str, _deactivate_str) \ - (binding_t) { \ - .action = (action_t){.state = \ - { \ - .type = InputType_state, \ - .activate = NULL, \ - .deactivate = NULL, \ - .activate_str = strdup(_activate_str), \ - .deactivate_str = strdup(_deactivate_str), \ - }}, \ - .scancode = key, .scancode_alt = altkey, .since_last_activation = 0 \ - } - -void binding_t_free(binding_t* b) { - switch (b->action.type) { - case InputType_error: - ERROR("Cannot free binding of type InputType_error"); - break; - case InputType_action: - free(b->action.action.callback_str); - return; - - case InputType_state: - free(b->action.state.activate_str); - free(b->action.state.deactivate_str); - break; - - case InputType_range: - ERROR("Cannot free binding of type InputType_rage"); - break; - - default: - ERROR("Unknown bindings type"); - break; - } - exit(EXIT_FAILURE); -} - -void i_ctx_t_free(i_ctx* c) { - for (isize i = 0; i < c->len; i++) { - binding_t_free(&c->bindings[i]); - } -} - -bool binding_action_cmp(binding_t* restrict a, binding_t* restrict b) { - InputType t = a->action.type; - if (t != b->action.type) return false; - switch (t) { - case InputType_action: - return !strcmp(a->action.action.callback_str, - b->action.action.callback_str); - - case InputType_state: - return (!strcmp(a->action.state.activate_str, - b->action.state.activate_str)) && - (!strcmp(a->action.state.deactivate_str, - b->action.state.deactivate_str)); - - case InputType_range: // fallthrough - default: - return false; // not implemented - } - return false; -} - -bool binding_scancode_cmp(binding_t* restrict a, binding_t* restrict b) { - return a->scancode == b->scancode || a->scancode_alt == b->scancode_alt; -} - -bool binding_scancode_cmp_i(binding_t* restrict a, scancode_t scancode) { - return a->scancode == scancode || a->scancode_alt == scancode; -} - -// If any binding in ctx contains action, replace that entry. -bool i_update_binding(i_ctx* ctx, binding_t* binding) { - isize idx = 0; - if (ctx == NULL || binding == NULL) { - ERROR("i_update_binding received nullptr!"); - return false; - } - - while (idx < ctx->len && !binding_action_cmp(&ctx->bindings[idx], binding)) - idx++; - - if (idx < ctx->len && binding_action_cmp(&ctx->bindings[idx], binding)) { - ctx->bindings[idx].scancode = binding->scancode; - ctx->bindings[idx].scancode_alt = binding->scancode_alt; - return true; - } - - return false; -} - -// If any binding in ctx contains scancode, replace that action. -bool i_update_unique_binding(i_ctx* ctx, binding_t* binding) { - isize idx = 0; - if (ctx == NULL || binding == NULL) { - ERROR("i_update_unique_binding received nullptr!"); - return false; - } - - while (idx < ctx->len && !binding_scancode_cmp(&ctx->bindings[idx], binding)) - idx++; - - if (idx < ctx->len && binding_scancode_cmp(&ctx->bindings[idx], binding)) { - ctx->bindings[idx].action = binding->action; - return true; - } - - return false; -} - -void i_flush_bindings(usize numcalls, void* state_mem, input_callback_t* c[]) { - for (usize i = 0; i < numcalls; i++) { - (c[i])(state_mem); - } -} - -action_t i_get_action(const i_ctx* restrict ctx, u32 time, - scancode_t scancode) { - isize idx = 0; - - if (ctx == NULL) { - ERROR("%s received nullptr!", __func__); - return (action_t){.type = InputType_error}; - } - - while (idx < ctx->len && - !binding_scancode_cmp_i(&ctx->bindings[idx], scancode)) - idx++; - - if (idx < ctx->len && binding_scancode_cmp_i(&ctx->bindings[idx], scancode)) { - ctx->bindings[idx].since_last_activation = time; - return ctx->bindings[idx].action; - } - - return (action_t){.type = InputType_error}; -} - -bool state_refresh_input_ctx(void* lib, i_ctx** ctx, usize ctx_len) { - if (ctx == NULL) return true; - if (ctx_len > 0 && ctx[0] == NULL) return false; - if (lib == NULL) return false; - - for (usize c = 0; c < ctx_len; c++) { - LOG("ctx[%d]->len = %d", c, ctx[c]->len); - for (isize b = 0; b < ctx[c]->len; b++) { - switch (ctx[c]->bindings[b].action.type) { - case InputType_error: - break; - case InputType_action: - if (strcmp("NULL", ctx[c]->bindings[b].action.action.callback_str) != - 0) { - - ctx[c]->bindings[b].action.action.callback = - (input_callback_t*)dynamic_library_get_symbol( - lib, ctx[c]->bindings[b].action.action.callback_str); - - if (ctx[c]->bindings[b].action.action.callback == NULL) { - ERROR("Failed to get binding for %s: %s", - ctx[c]->bindings[b].action.action.callback_str, - dynamic_library_get_error()); - return false; - } - } - break; - case InputType_state: - if (strcmp("NULL", ctx[c]->bindings[b].action.state.activate_str) != - 0) { - - ctx[c]->bindings[b].action.state.activate = - (input_callback_t*)dynamic_library_get_symbol( - lib, ctx[c]->bindings[b].action.state.activate_str); - - if (ctx[c]->bindings[b].action.state.activate == NULL) { - ERROR("Failed to get binding for %s: %s", - ctx[c]->bindings[b].action.state.activate_str, - dynamic_library_get_error()); - return false; - } - } - - if (strcmp("NULL", ctx[c]->bindings[b].action.state.deactivate_str) != - 0) { - - ctx[c]->bindings[b].action.state.deactivate = - (input_callback_t*)dynamic_library_get_symbol( - lib, ctx[c]->bindings[b].action.state.deactivate_str); - - if (ctx[c]->bindings[b].action.state.deactivate == NULL) { - ERROR("Failed to get binding for %s: %s", - ctx[c]->bindings[b].action.state.deactivate_str, - dynamic_library_get_error()); - return false; - } - } - break; - case InputType_range: - default: - break; - } - } - } - - return true; -} - -/* Make a lazy duplication of a binding. See comments on BindActionLazy and - * friends above. */ -i_ctx* i_ctx_dup(i_ctx** ctx, usize ctx_len) { - usize num_binds = 0; - for (usize c = 0; c < ctx_len; c++) { - num_binds += ctx[c]->len; - } - - binding_t* bb = calloc(num_binds, sizeof(binding_t)); - i_ctx* ret = calloc(ctx_len, sizeof(i_ctx)); - - usize cumsum = 0; - for (usize c = 0; c < ctx_len; c++) { - binding_t* b = ctx[c]->bindings; - ret[c].len = ctx[c]->len; - ret[c].bindings = &bb[cumsum]; - - for (isize i = 0; i < ctx[c]->len; i++) { - switch (b[i].action.type) { - case InputType_error: - break; - case InputType_action: - bb[cumsum] = BindActionLazy(b[i].scancode, b[i].scancode_alt, - strdup(b[i].action.action.callback_str)); - break; - case InputType_state: - bb[cumsum] = BindStateLazy(b[i].scancode, b[i].scancode_alt, - strdup(b[i].action.state.activate_str), - strdup(b[i].action.state.deactivate_str)); - break; - case InputType_range: - default: - break; - } - cumsum++; - } - } - return ret; -} - -/* Returns a pointer to a binding in c with .action == a. - * Returns NULL if not found. */ -binding_t* get_action(i_ctx* c, action_t* a) { - for (isize i = 0; i < c->len; i++) { - if (c->bindings[i].action.type != a->type) continue; - - switch (c->bindings[i].action.type) { - case InputType_error: - return NULL; - - case InputType_action: - if (!strcmp(c->bindings[i].action.action.callback_str, - a->action.callback_str)) { - return &c->bindings[i]; - } - break; - - case InputType_state: - if (!strcmp(c->bindings[i].action.state.activate_str, - a->state.activate_str) && - !strcmp(c->bindings[i].action.state.deactivate_str, - a->state.deactivate_str)) { - return &c->bindings[i]; - } - break; - - case InputType_range: - default: - return NULL; - } - } - return NULL; -} - -void i_bind_ctx(i_ctx* c, scancode_t s, action_t* a) { - binding_t* b = get_action(c, a); - - if (b == NULL) { - WARN("Failed to update keybinding"); - return; - } - - b->scancode = s; -} - -void i_bind_ctx_alt(i_ctx* c, scancode_t s, action_t* a) { - binding_t* b = get_action(c, a); - - if (b == NULL) { - WARN("Failed to update keybinding"); - return; - } - - b->scancode_alt = s; -} - -void i_bind(binding_t* b, scancode_t s) { - if (b == NULL) { - WARN("Failed to update keybinding"); - return; - } - - b->scancode = s; -} - -void i_bind_alt(binding_t* b, scancode_t s) { - if (b == NULL) { - WARN("Failed to update keybinding"); - return; - } - - b->scancode_alt = s; -} diff --git a/src/ctrl/keyboard.c b/src/ctrl/keyboard.c deleted file mode 100644 index e69de29..0000000 diff --git a/src/ctrl/mouse.c b/src/ctrl/mouse.c deleted file mode 100644 index e69de29..0000000 diff --git a/src/ctrl/src/controller.c b/src/ctrl/src/controller.c new file mode 100644 index 0000000..e69de29 diff --git a/src/ctrl/src/ctx.c b/src/ctrl/src/ctx.c new file mode 100644 index 0000000..e69de29 diff --git a/src/ctrl/src/input.c b/src/ctrl/src/input.c new file mode 100644 index 0000000..34b1718 --- /dev/null +++ b/src/ctrl/src/input.c @@ -0,0 +1,339 @@ +#include +#include +#include +#include + +/* Lazy binds, used internally. They are similar to BindAction and friends. + * The only difference is that we set callbacks and such to NULL, but populate + * the function name strings such that can be reloaded. */ +#define BindActionLazy(key, altkey, action_str) \ + (binding_t) { \ + .action = (action_t){.action = \ + { \ + .type = InputType_action, \ + .callback = NULL, \ + .callback_str = strdup(action_str), \ + }}, \ + .scancode = key, .scancode_alt = altkey, .since_last_activation = 0 \ + } + +#define BindStateLazy(key, altkey, _activate_str, _deactivate_str) \ + (binding_t) { \ + .action = (action_t){.state = \ + { \ + .type = InputType_state, \ + .activate = NULL, \ + .deactivate = NULL, \ + .activate_str = strdup(_activate_str), \ + .deactivate_str = strdup(_deactivate_str), \ + }}, \ + .scancode = key, .scancode_alt = altkey, .since_last_activation = 0 \ + } + +void binding_t_free(binding_t* b) { + switch (b->action.type) { + case InputType_error: + ERROR("Cannot free binding of type InputType_error"); + break; + case InputType_action: + free(b->action.action.callback_str); + return; + + case InputType_state: + free(b->action.state.activate_str); + free(b->action.state.deactivate_str); + break; + + case InputType_range: + ERROR("Cannot free binding of type InputType_rage"); + break; + + default: + ERROR("Unknown bindings type"); + break; + } + exit(EXIT_FAILURE); +} + +void i_ctx_t_free(i_ctx* c) { + for (isize i = 0; i < c->len; i++) { + binding_t_free(&c->bindings[i]); + } +} + +bool binding_action_cmp(binding_t* restrict a, binding_t* restrict b) { + InputType t = a->action.type; + if (t != b->action.type) return false; + switch (t) { + case InputType_action: + return !strcmp(a->action.action.callback_str, + b->action.action.callback_str); + + case InputType_state: + return (!strcmp(a->action.state.activate_str, + b->action.state.activate_str)) && + (!strcmp(a->action.state.deactivate_str, + b->action.state.deactivate_str)); + + case InputType_range: // fallthrough + default: + return false; // not implemented + } + return false; +} + +bool binding_scancode_cmp(binding_t* restrict a, binding_t* restrict b) { + return a->scancode == b->scancode || a->scancode_alt == b->scancode_alt; +} + +bool binding_scancode_cmp_i(binding_t* restrict a, scancode_t scancode) { + return a->scancode == scancode || a->scancode_alt == scancode; +} + +// If any binding in ctx contains action, replace that entry. +bool i_update_binding(i_ctx* ctx, binding_t* binding) { + isize idx = 0; + if (ctx == NULL || binding == NULL) { + ERROR("i_update_binding received nullptr!"); + return false; + } + + while (idx < ctx->len && !binding_action_cmp(&ctx->bindings[idx], binding)) + idx++; + + if (idx < ctx->len && binding_action_cmp(&ctx->bindings[idx], binding)) { + ctx->bindings[idx].scancode = binding->scancode; + ctx->bindings[idx].scancode_alt = binding->scancode_alt; + return true; + } + + return false; +} + +// If any binding in ctx contains scancode, replace that action. +bool i_update_unique_binding(i_ctx* ctx, binding_t* binding) { + isize idx = 0; + if (ctx == NULL || binding == NULL) { + ERROR("i_update_unique_binding received nullptr!"); + return false; + } + + while (idx < ctx->len && !binding_scancode_cmp(&ctx->bindings[idx], binding)) + idx++; + + if (idx < ctx->len && binding_scancode_cmp(&ctx->bindings[idx], binding)) { + ctx->bindings[idx].action = binding->action; + return true; + } + + return false; +} + +void i_flush_bindings(usize numcalls, void* state_mem, input_callback_t* c[]) { + for (usize i = 0; i < numcalls; i++) { + (c[i])(state_mem); + } +} + +action_t i_get_action(const i_ctx* restrict ctx, u32 time, + scancode_t scancode) { + isize idx = 0; + + if (ctx == NULL) { + ERROR("%s received nullptr!", __func__); + return (action_t){.type = InputType_error}; + } + + while (idx < ctx->len && + !binding_scancode_cmp_i(&ctx->bindings[idx], scancode)) + idx++; + + if (idx < ctx->len && binding_scancode_cmp_i(&ctx->bindings[idx], scancode)) { + ctx->bindings[idx].since_last_activation = time; + return ctx->bindings[idx].action; + } + + return (action_t){.type = InputType_error}; +} + +bool state_refresh_input_ctx(void* lib, i_ctx** ctx, usize ctx_len) { + if (ctx == NULL) return true; + if (ctx_len > 0 && ctx[0] == NULL) return false; + if (lib == NULL) return false; + + for (usize c = 0; c < ctx_len; c++) { + LOG("ctx[%d]->len = %d", c, ctx[c]->len); + for (isize b = 0; b < ctx[c]->len; b++) { + switch (ctx[c]->bindings[b].action.type) { + case InputType_error: + break; + case InputType_action: + if (strcmp("NULL", ctx[c]->bindings[b].action.action.callback_str) != + 0) { + + ctx[c]->bindings[b].action.action.callback = + (input_callback_t*)dynamic_library_get_symbol( + lib, ctx[c]->bindings[b].action.action.callback_str); + + if (ctx[c]->bindings[b].action.action.callback == NULL) { + ERROR("Failed to get binding for %s: %s", + ctx[c]->bindings[b].action.action.callback_str, + dynamic_library_get_error()); + return false; + } + } + break; + case InputType_state: + if (strcmp("NULL", ctx[c]->bindings[b].action.state.activate_str) != + 0) { + + ctx[c]->bindings[b].action.state.activate = + (input_callback_t*)dynamic_library_get_symbol( + lib, ctx[c]->bindings[b].action.state.activate_str); + + if (ctx[c]->bindings[b].action.state.activate == NULL) { + ERROR("Failed to get binding for %s: %s", + ctx[c]->bindings[b].action.state.activate_str, + dynamic_library_get_error()); + return false; + } + } + + if (strcmp("NULL", ctx[c]->bindings[b].action.state.deactivate_str) != + 0) { + + ctx[c]->bindings[b].action.state.deactivate = + (input_callback_t*)dynamic_library_get_symbol( + lib, ctx[c]->bindings[b].action.state.deactivate_str); + + if (ctx[c]->bindings[b].action.state.deactivate == NULL) { + ERROR("Failed to get binding for %s: %s", + ctx[c]->bindings[b].action.state.deactivate_str, + dynamic_library_get_error()); + return false; + } + } + break; + case InputType_range: + default: + break; + } + } + } + + return true; +} + +/* Make a lazy duplication of a binding. See comments on BindActionLazy and + * friends above. */ +i_ctx* i_ctx_dup(i_ctx** ctx, usize ctx_len) { + usize num_binds = 0; + for (usize c = 0; c < ctx_len; c++) { + num_binds += ctx[c]->len; + } + + binding_t* bb = calloc(num_binds, sizeof(binding_t)); + i_ctx* ret = calloc(ctx_len, sizeof(i_ctx)); + + usize cumsum = 0; + for (usize c = 0; c < ctx_len; c++) { + binding_t* b = ctx[c]->bindings; + ret[c].len = ctx[c]->len; + ret[c].bindings = &bb[cumsum]; + + for (isize i = 0; i < ctx[c]->len; i++) { + switch (b[i].action.type) { + case InputType_error: + break; + case InputType_action: + bb[cumsum] = BindActionLazy(b[i].scancode, b[i].scancode_alt, + strdup(b[i].action.action.callback_str)); + break; + case InputType_state: + bb[cumsum] = BindStateLazy(b[i].scancode, b[i].scancode_alt, + strdup(b[i].action.state.activate_str), + strdup(b[i].action.state.deactivate_str)); + break; + case InputType_range: + default: + break; + } + cumsum++; + } + } + return ret; +} + +/* Returns a pointer to a binding in c with .action == a. + * Returns NULL if not found. */ +binding_t* get_action(i_ctx* c, action_t* a) { + for (isize i = 0; i < c->len; i++) { + if (c->bindings[i].action.type != a->type) continue; + + switch (c->bindings[i].action.type) { + case InputType_error: + return NULL; + + case InputType_action: + if (!strcmp(c->bindings[i].action.action.callback_str, + a->action.callback_str)) { + return &c->bindings[i]; + } + break; + + case InputType_state: + if (!strcmp(c->bindings[i].action.state.activate_str, + a->state.activate_str) && + !strcmp(c->bindings[i].action.state.deactivate_str, + a->state.deactivate_str)) { + return &c->bindings[i]; + } + break; + + case InputType_range: + default: + return NULL; + } + } + return NULL; +} + +void i_bind_ctx(i_ctx* c, scancode_t s, action_t* a) { + binding_t* b = get_action(c, a); + + if (b == NULL) { + WARN("Failed to update keybinding"); + return; + } + + b->scancode = s; +} + +void i_bind_ctx_alt(i_ctx* c, scancode_t s, action_t* a) { + binding_t* b = get_action(c, a); + + if (b == NULL) { + WARN("Failed to update keybinding"); + return; + } + + b->scancode_alt = s; +} + +void i_bind(binding_t* b, scancode_t s) { + if (b == NULL) { + WARN("Failed to update keybinding"); + return; + } + + b->scancode = s; +} + +void i_bind_alt(binding_t* b, scancode_t s) { + if (b == NULL) { + WARN("Failed to update keybinding"); + return; + } + + b->scancode_alt = s; +} diff --git a/src/ctrl/src/keyboard.c b/src/ctrl/src/keyboard.c new file mode 100644 index 0000000..e69de29 diff --git a/src/ctrl/src/mouse.c b/src/ctrl/src/mouse.c new file mode 100644 index 0000000..e69de29 diff --git a/src/rendering/gl.c b/src/rendering/gl.c deleted file mode 100644 index e69de29..0000000 diff --git a/src/rendering/rendering.c b/src/rendering/rendering.c deleted file mode 100644 index dff3432..0000000 --- a/src/rendering/rendering.c +++ /dev/null @@ -1,137 +0,0 @@ -#include -#include - -//#define GLAD_GL_IMPLEMENTATION -#include -//#define GLFW_INCLUDE_NONE -#include - - -#define ENGINE_INTERNALS -#include -#include - -/* Extern globals */ -extern Platform* GLOBAL_PLATFORM; - -/* Globals */ -#define drawcall_limit (64 * 1024) -RenderDrawCall drawcalls[drawcall_limit]; -i32 drawcall_len = 0; - -//struct RenderObject; - -/* Implementations */ - -/* Clear the screen, - * To be used inbetween draw calls */ -void render_begin(Window* w) { - //glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - glfwMakeContextCurrent(w->window); - w->context->Clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); -} - -void render_present(Window* w) { -// for (i32 i = 0; i < drawcall_len; i++) { -// RenderDrawCall dc = drawcalls[i]; -// switch (dc.type) { -// case RenderDrawCallType_UITree: -// render_uitree(w, dc.data.data); -// break; -// case RenderDrawCallType_Text: -// LOG("RenderDrawCallType_Text rendering not implemented!"); -// break; -// case RenderDrawCallType_Sprite: { -//#ifdef _DEBUG -// if (dc.data.sprite.sprite == NULL) { -// __asm__("int3;"); -// WARN("Sprite %lx in drawcall %d/%d had NULL reference", -// dc.data.sprite.sprite, i, drawcall_len); -// -// drawcall_len = 0; -// exit(EXIT_FAILURE); -// } -//#endif -// Sprite s = *dc.data.sprite.sprite; -// Texture* t = -// ((struct Resources*)GLOBAL_PLATFORM->data)->textures[s.texture_id]; -// i32 ts = t->tilesize; -// } break; -// default: -// break; -// } -// } - - drawcall_len = 0; - - glfwSwapBuffers(w->window); -} - -void drawcall_reset(void) { drawcall_len = 0; } - -void window_size_callback(GLFWwindow* window, int width, int height) { - GLOBAL_PLATFORM->window->context->Viewport(0,0, width, height); - //*GLOBAL_PLATFORM->window->game_w = width; - //*GLOBAL_PLATFORM->window->game_h = height; - GLOBAL_PLATFORM->window->windowsize.x = width; - GLOBAL_PLATFORM->window->windowsize.y = height; -} - -void engine_window_resize_pointers(i32* w, i32* h) { - GLOBAL_PLATFORM->window->game_w = w; - GLOBAL_PLATFORM->window->game_h = h; -} - -void engine_window_resize_pointers_reset(void) { - GLOBAL_PLATFORM->window->game_w = NULL; - GLOBAL_PLATFORM->window->game_h = NULL; -} - -void engine_draw_uitree(UITree* t) { - if (drawcall_len + 1 >= drawcall_limit) return; - drawcalls[drawcall_len++] = (RenderDrawCall){ - .type = RenderDrawCallType_UITree, .data.data = (void*)t}; -} - -void engine_draw_sprite(Sprite* s, v2_i32* pos, f32 scale) { - if (drawcall_len + 1 >= drawcall_limit) return; -#ifdef _DEBUG - if (s == NULL) __asm__("int3;"); -#endif - drawcalls[drawcall_len++] = - (RenderDrawCall){.type = RenderDrawCallType_Sprite, - .data.sprite = { - .sprite = s, - .x = pos->x, - .y = pos->y, - .scale = scale, - //.mod = {0xFF, 0xFF, 0xFF, 0xFF}, - }}; -} - -void engine_draw_sprite_ex(Sprite* s, v2_i32* pos, f32 scale, - Engine_color colormod) { - if (drawcall_len + 1 >= drawcall_limit) return; -#ifdef _DEBUG - if (s == NULL) __asm__("int3;"); -#endif - drawcalls[drawcall_len++] = (RenderDrawCall){ - .type = RenderDrawCallType_Sprite, - .data.sprite = { - .sprite = s, - .x = pos->x, - .y = pos->y, - .scale = scale, - //.mod = {colormod.r, colormod.g, colormod.b, colormod.a}, - }}; -} - -Sprite sprite_new(u64 tid, u8 coord) { - const i32 ts = - ((struct Resources*)GLOBAL_PLATFORM->data)->textures[tid]->tilesize; - return (Sprite){.texture_id = tid, - (v2_i32){ - .x = ts * (coord & 0x0F), - .y = ts * ((coord & 0xF0) >> 4), - }}; -} diff --git a/src/rendering/src/gl.c b/src/rendering/src/gl.c new file mode 100644 index 0000000..e69de29 diff --git a/src/rendering/src/rendering.c b/src/rendering/src/rendering.c new file mode 100644 index 0000000..dff3432 --- /dev/null +++ b/src/rendering/src/rendering.c @@ -0,0 +1,137 @@ +#include +#include + +//#define GLAD_GL_IMPLEMENTATION +#include +//#define GLFW_INCLUDE_NONE +#include + + +#define ENGINE_INTERNALS +#include +#include + +/* Extern globals */ +extern Platform* GLOBAL_PLATFORM; + +/* Globals */ +#define drawcall_limit (64 * 1024) +RenderDrawCall drawcalls[drawcall_limit]; +i32 drawcall_len = 0; + +//struct RenderObject; + +/* Implementations */ + +/* Clear the screen, + * To be used inbetween draw calls */ +void render_begin(Window* w) { + //glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glfwMakeContextCurrent(w->window); + w->context->Clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +} + +void render_present(Window* w) { +// for (i32 i = 0; i < drawcall_len; i++) { +// RenderDrawCall dc = drawcalls[i]; +// switch (dc.type) { +// case RenderDrawCallType_UITree: +// render_uitree(w, dc.data.data); +// break; +// case RenderDrawCallType_Text: +// LOG("RenderDrawCallType_Text rendering not implemented!"); +// break; +// case RenderDrawCallType_Sprite: { +//#ifdef _DEBUG +// if (dc.data.sprite.sprite == NULL) { +// __asm__("int3;"); +// WARN("Sprite %lx in drawcall %d/%d had NULL reference", +// dc.data.sprite.sprite, i, drawcall_len); +// +// drawcall_len = 0; +// exit(EXIT_FAILURE); +// } +//#endif +// Sprite s = *dc.data.sprite.sprite; +// Texture* t = +// ((struct Resources*)GLOBAL_PLATFORM->data)->textures[s.texture_id]; +// i32 ts = t->tilesize; +// } break; +// default: +// break; +// } +// } + + drawcall_len = 0; + + glfwSwapBuffers(w->window); +} + +void drawcall_reset(void) { drawcall_len = 0; } + +void window_size_callback(GLFWwindow* window, int width, int height) { + GLOBAL_PLATFORM->window->context->Viewport(0,0, width, height); + //*GLOBAL_PLATFORM->window->game_w = width; + //*GLOBAL_PLATFORM->window->game_h = height; + GLOBAL_PLATFORM->window->windowsize.x = width; + GLOBAL_PLATFORM->window->windowsize.y = height; +} + +void engine_window_resize_pointers(i32* w, i32* h) { + GLOBAL_PLATFORM->window->game_w = w; + GLOBAL_PLATFORM->window->game_h = h; +} + +void engine_window_resize_pointers_reset(void) { + GLOBAL_PLATFORM->window->game_w = NULL; + GLOBAL_PLATFORM->window->game_h = NULL; +} + +void engine_draw_uitree(UITree* t) { + if (drawcall_len + 1 >= drawcall_limit) return; + drawcalls[drawcall_len++] = (RenderDrawCall){ + .type = RenderDrawCallType_UITree, .data.data = (void*)t}; +} + +void engine_draw_sprite(Sprite* s, v2_i32* pos, f32 scale) { + if (drawcall_len + 1 >= drawcall_limit) return; +#ifdef _DEBUG + if (s == NULL) __asm__("int3;"); +#endif + drawcalls[drawcall_len++] = + (RenderDrawCall){.type = RenderDrawCallType_Sprite, + .data.sprite = { + .sprite = s, + .x = pos->x, + .y = pos->y, + .scale = scale, + //.mod = {0xFF, 0xFF, 0xFF, 0xFF}, + }}; +} + +void engine_draw_sprite_ex(Sprite* s, v2_i32* pos, f32 scale, + Engine_color colormod) { + if (drawcall_len + 1 >= drawcall_limit) return; +#ifdef _DEBUG + if (s == NULL) __asm__("int3;"); +#endif + drawcalls[drawcall_len++] = (RenderDrawCall){ + .type = RenderDrawCallType_Sprite, + .data.sprite = { + .sprite = s, + .x = pos->x, + .y = pos->y, + .scale = scale, + //.mod = {colormod.r, colormod.g, colormod.b, colormod.a}, + }}; +} + +Sprite sprite_new(u64 tid, u8 coord) { + const i32 ts = + ((struct Resources*)GLOBAL_PLATFORM->data)->textures[tid]->tilesize; + return (Sprite){.texture_id = tid, + (v2_i32){ + .x = ts * (coord & 0x0F), + .y = ts * ((coord & 0xF0) >> 4), + }}; +} diff --git a/src/rendering/src/text.c b/src/rendering/src/text.c new file mode 100644 index 0000000..e69de29 diff --git a/src/rendering/src/window.c b/src/rendering/src/window.c new file mode 100644 index 0000000..e69de29 diff --git a/src/rendering/text.c b/src/rendering/text.c deleted file mode 100644 index e69de29..0000000 diff --git a/src/rendering/window.c b/src/rendering/window.c deleted file mode 100644 index e69de29..0000000 diff --git a/src/resources/fonts.c b/src/resources/fonts.c deleted file mode 100644 index e69de29..0000000 diff --git a/src/resources/scripts.c b/src/resources/scripts.c deleted file mode 100644 index e69de29..0000000 diff --git a/src/resources/src/fonts.c b/src/resources/src/fonts.c new file mode 100644 index 0000000..e69de29 diff --git a/src/resources/src/scripts.c b/src/resources/src/scripts.c new file mode 100644 index 0000000..e69de29 diff --git a/src/resources/src/textures.c b/src/resources/src/textures.c new file mode 100644 index 0000000..e69de29 diff --git a/src/resources/textures.c b/src/resources/textures.c deleted file mode 100644 index e69de29..0000000 diff --git a/src/ui/positioning.c b/src/ui/positioning.c deleted file mode 100644 index 3db73fe..0000000 --- a/src/ui/positioning.c +++ /dev/null @@ -1,945 +0,0 @@ -#include -#define ENGINE_INTERNALS - -#include -#include -#include - -static Engine_color DEFAULT_FG = {0xFF, 0xFF, 0xFF, 0xFF}; -static Engine_color DEFAULT_BG = {0x00, 0x00, 0x00, 0xFF}; -static i32 DEFAULT_PADDING = 8; -static i32 DEFAULT_MARGIN = 0; - -/* We want to keep track of the roots of the UI elements, s.t. we can resize and - * reposition the elements if needed be. */ -struct btree* GLOBAL_UIROOTS = NULL; - -extern Platform* GLOBAL_PLATFORM; - -const char* uitype_str[] = { - [uitype_container] = "uitype_container", - [uitype_button] = "uitype_button", - [uitype_title] = "uitype_title", - [uitype_text] = "uitype_text", - [uitype_MAX] = "uitype_MAX", - NULL, -}; - -const char* constraint_str[] = { - [ui_constraint_width] = "ui_constraint_width", - [ui_constraint_height] = "ui_constraint_height", - - [ui_constraint_horizontal_align] = "ui_constraint_horizontal_align", - [ui_constraint_vertical_align] = "ui_constraint_vertical_align", - - [ui_constraint_left] = "ui_constraint_left", - [ui_constraint_right] = "ui_constraint_right", - [ui_constraint_top] = "ui_constraint_top", - [ui_constraint_bottom] = "ui_constraint_bottom", -}; - -static i32 pointer_cmp(const void* a, const void* b) { - const u64* x = a; - const u64* y = b; - /* We just use the address as index */ - return *x - *y; -} - -static void pointer_print(const void* a) { - const u64* t = a; - printf("%lx\n", *t); -} - -void ui_rearrange(UITree* root, v2_i32 ppos, v2_i32 psize); -i32 get_padding(UITree* t); -i32 get_margin(UITree* t); -v2_i32 get_pos(UITree* t); -v2_i32 get_size(UITree* t); - -f32 ui_size_pixel_to_percent_width(i32 pixels) { - return (f32)pixels / (f32)GLOBAL_PLATFORM->window->windowsize.x; -} - -f32 ui_size_pixel_to_percent_height(i32 pixels) { - return (f32)pixels / (f32)GLOBAL_PLATFORM->window->windowsize.y; -} - -f32 ui_size_pixel_to_percent(i32 pixels) { - return ui_size_percent_height_to_pixel(pixels); -} - -i32 ui_size_percent_width_to_pixel(f32 percent) { - return (i32)(percent * GLOBAL_PLATFORM->window->windowsize.x); -} - -i32 ui_size_percent_height_to_pixel(f32 percent) { - return (i32)(percent * GLOBAL_PLATFORM->window->windowsize.y); -} - -i32 ui_size_percent_to_pixel(f32 percent) { - return ui_size_percent_height_to_pixel(percent); -} - -i32 ui_size_to_pixel_ex(ui_size s, bool vertical, v2_i32 psize) { - if (s.type == ui_size_pixel) { - return s.pixel.value; - } else { - psize.x = MAX(1, psize.x); - psize.y = MAX(1, psize.y); - if (vertical) { - return psize.y * s.percent.value; - } - return psize.x * s.percent.value; - } -} - -i32 ui_size_to_pixel(ui_size s) { - if (s.type == ui_size_pixel) { - return s.pixel.value; - } else { - return ui_size_percent_height_to_pixel(s.percent.value); - } -} - -void ui_add(UITree* t) { - - if (GLOBAL_UIROOTS == NULL) { - GLOBAL_UIROOTS = btree_new(sizeof(void*), 16, &pointer_cmp); - } - - u64 tmp = (u64)t; - btree_insert(GLOBAL_UIROOTS, &tmp); -} - -void clear_ui(void) { - UITree* t = NULL; - while ((t = btree_first(GLOBAL_UIROOTS)) != NULL) { - uitree_free(t); - if (!btree_delete(GLOBAL_UIROOTS, t)) { - WARN("Failed deletion"); - UITree* elem = (UITree*)t; - WARN("Failed to remove %s from global ui-table", uitype_str[elem->type]); - } - } -} - -/* Unwraps the size to a v2_i32 */ -v2_i32 uitree_get_size(UITree* t) { - if (t == NULL) return (v2_i32){.x = -1, .y = -1}; - switch (t->type) { - case uitype_container: - return (v2_i32){.x = t->container.w, .y = t->container.h}; - case uitype_button: - return (v2_i32){.x = t->button.w, .y = t->button.h}; - case uitype_title: - return (v2_i32){.x = t->title.w, .y = t->title.h}; - case uitype_text: - return (v2_i32){.x = t->text.w, .y = t->text.h}; - default: - return (v2_i32){.x = -1, .y = -1}; - } -} - -/* elem_size also takes into account the padding and other stuffs */ -v2_i32 elem_size(UITree* root) { - if (root == NULL) return (v2_i32){0, 0}; - - const i32 total_padding = 2 * get_padding(root); - return v2_i32_add_i(uitree_get_size(root), total_padding); -} - -v2_i32 elem_size_spacing(UITree* root) { - if (root == NULL) return (v2_i32){0, 0}; - - const i32 total_margin = 2 * get_padding(root) + 2 * get_margin(root); - return v2_i32_add_i(uitree_get_size(root), total_margin); -} - -UITree* ui_container_new_ex(Engine_color fg, Engine_color bg, - Engine_color border, bool direction, - struct List_ui_constraint* constraints) { - - UITree* t = malloc(sizeof(UITree)); - - t->container.type = uitype_container; - t->container.visible = true; - t->container.direction = direction; - - /* -1 == unsolved constraint */ - t->container.x = -1; - t->container.y = -1; - t->container.w = -1; - t->container.h = -1; - t->container.padding = 0; - t->container.margin = 0; - - t->container.fg = fg; - t->container.bg = bg; - t->container.bordercolor = border; - - t->container.children = NULL; - t->container.children_len = 0; - t->container.children_size = 0; - - t->container.constraints = constraints; - - ui_rearrange(t, (v2_i32){0, 0}, (v2_i32){0, 0}); - - ui_add(t); - - return t; -} - -UITree* ui_container(bool direction, struct List_ui_constraint* constraints) { - return ui_container_new_ex( - /* We set border color to fg by default */ - DEFAULT_FG, DEFAULT_BG, DEFAULT_FG, direction, constraints); -} - -UITree* ui_title(i32 font_id, const char* text, - struct List_ui_constraint* constraints) { - UITree_title* t = malloc(sizeof(UITree)); - t->type = uitype_title; - - t->x = -1; - t->y = -1; - - t->padding = DEFAULT_PADDING; - t->margin = DEFAULT_MARGIN; - - t->fg = DEFAULT_FG; - - t->font_id = font_id; - t->text_original = text; - t->text_texture_index = - engine_render_text(font_id, t->fg, (char*)text, &t->texture_size, false); - - t->w = t->texture_size.x; - t->h = t->texture_size.y; - - t->constraints = constraints; - - ui_rearrange((UITree*)t, (v2_i32){0, 0}, (v2_i32){0, 0}); - - ui_add((UITree*)t); - - return (UITree*)t; -} - -UITree* ui_text(i32 font_id, const char* text, - struct List_ui_constraint* constraints) { - UITree* t = malloc(sizeof(UITree)); - t->text.type = uitype_text; - - t->text.x = -1; - t->text.y = -1; - - t->text.padding = 0; - t->text.margin = DEFAULT_MARGIN / 2; - - t->text.fg = DEFAULT_FG; - - t->text.font_id = font_id; - t->text.text_original = text; - - /* Postpone rendering until we have a clear set of size-constraints */ - - // t->text.text_texture_index = - // engine_render_text(font_id, DEFAULT_FG, (char*)text, - //&t->text.texture_size, true); - - // t->text.w = t->text.texture_size.x; - // t->text.h = t->text.texture_size.y; - - t->text.constraints = constraints; - - ui_rearrange(t, (v2_i32){0, 0}, (v2_i32){0, 0}); - - ui_add(t); - - return t; -} - -UITree* ui_button(u64 id, i32 font_id, char* text, - struct List_ui_constraint* constraints) { - UITree_button* t = malloc(sizeof(UITree)); - t->type = uitype_button; - - t->enabled = true; - t->id = id; - t->x = -1; - t->y = -1; - t->padding = DEFAULT_PADDING; - t->margin = DEFAULT_MARGIN; - - t->fg = DEFAULT_FG; - t->bg = DEFAULT_BG; - - t->font_id = font_id; - t->text_original = text; - t->text_texture_index = - engine_render_text(font_id, t->fg, (char*)text, &t->texture_size, false); - - t->w = t->texture_size.x; - t->h = t->texture_size.y; - - t->constraints = constraints; - - ui_rearrange((UITree*)t, (v2_i32){0, 0}, (v2_i32){0, 0}); - - ui_add((UITree*)t); - - return (UITree*)t; -} - -void uitree_free(UITree* t) { - switch (t->type) { - case uitype_container: - if (t->container.children != NULL && t->container.children_len > 0) { - for (usize i = 0; i < t->container.children_len; i++) { - uitree_free(t->container.children[i]); - } - free(t->container.children); - } - break; - case uitype_button: - case uitype_title: - case uitype_text: - free(t); - break; - default: - break; - } -} - -struct List_ui_constraint* get_constraints(UITree* t) { - switch (t->type) { - case uitype_container: - return t->container.constraints; - case uitype_button: - return t->button.constraints; - case uitype_title: - return t->title.constraints; - case uitype_text: - return t->text.constraints; - default: - return NULL; - } -} - -i32 get_padding(UITree* t) { - switch (t->type) { - case uitype_container: - return t->container.padding; - case uitype_button: - return t->button.padding; - case uitype_title: - return t->title.padding; - case uitype_text: - return t->text.padding; - default: - return 0; - } -} - -i32 get_margin(UITree* t) { - switch (t->type) { - case uitype_container: - return t->container.margin; - case uitype_button: - return t->button.margin; - case uitype_title: - return t->title.margin; - case uitype_text: - return t->text.margin; - default: - return 0; - } -} - -v2_i32 get_pos(UITree* t) { - switch (t->type) { - case uitype_container: - return (v2_i32){t->container.x, t->container.y}; - case uitype_button: - return (v2_i32){t->button.x, t->button.y}; - case uitype_title: - return (v2_i32){t->title.x, t->title.y}; - case uitype_text: - return (v2_i32){t->text.x, t->text.y}; - default: - return (v2_i32){0, 0}; - } -} - -v2_i32 get_size(UITree* t) { - switch (t->type) { - case uitype_container: - return (v2_i32){t->container.w, t->container.h}; - case uitype_button: - return (v2_i32){t->button.w, t->button.h}; - case uitype_title: - return (v2_i32){t->title.w, t->title.h}; - case uitype_text: - return (v2_i32){t->text.w, t->text.h}; - default: - return (v2_i32){0, 0}; - } -} - -#define SETVAL(VAL, tt) \ - { \ - if ((VAL) > 0) t->tt = VAL; \ - } -void uitree_set_pos(UITree* t, v2_i32 newpos) { - switch (t->type) { - case uitype_container: - SETVAL(newpos.x, container.x); - SETVAL(newpos.y, container.y); - break; - case uitype_button: - SETVAL(newpos.x, button.x); - SETVAL(newpos.y, button.y); - break; - case uitype_title: - SETVAL(newpos.x, title.x); - SETVAL(newpos.y, title.y); - break; - case uitype_text: - SETVAL(newpos.x, text.x); - SETVAL(newpos.y, text.y); - break; - default: - return; - } -} - -void uitree_set_size(UITree* t, v2_i32 size) { - switch (t->type) { - case uitype_container: - SETVAL(size.x, container.w); - SETVAL(size.y, container.h); - break; - case uitype_button: - SETVAL(size.x, button.w); - SETVAL(size.y, button.h); - break; - case uitype_title: - SETVAL(size.x, title.w); - SETVAL(size.y, title.h); - break; - case uitype_text: - SETVAL(size.x, text.w); - SETVAL(size.y, text.h); - break; - default: - return; - } -} - -bool uitree_has_constraint(UITree* t, const ui_constraint_t constraint) { - struct List_ui_constraint* c = NULL; - struct List_ui_constraint* first_constraint; - if (t == NULL) { - ERROR("%s got nullptr argument!", __func__); - exit(EXIT_FAILURE); - } - - switch (t->type) { - case uitype_container: - c = t->container.constraints; - break; - case uitype_button: - c = t->button.constraints; - break; - case uitype_title: - c = t->title.constraints; - break; - case uitype_text: - c = t->text.constraints; - break; - default: - break; - } - - first_constraint = c; - - while (c != NULL) { - if ((void*)c->next == (void*)first_constraint) { - ERROR("Infinite constraint loop detected!"); - exit(EXIT_FAILURE); - } - if (constraint == c->value.type) return true; - - c = c->next; - } - return false; -} - -void ui_constraints_apply(UITree* t, v2_i32 ppos, v2_i32 psize) { - struct List_ui_constraint* constraints = get_constraints(t); - struct List_ui_constraint* first_constraint = constraints; - v2_i32 tsize = uitree_get_size(t); - - if (constraints == NULL) return; - - while (constraints != NULL) { - if ((void*)constraints->next == (void*)first_constraint) { - ERROR("Infinite constraint loop detected!"); - ERROR(" Type: %s", constraint_str[constraints->value.type]); - exit(EXIT_FAILURE); - } - - const constraintval_t con = constraints->value.constraint; - - switch (constraints->value.type) { - case ui_constraint_width: { - i32 ww = ui_size_to_pixel_ex(con.width.size, false, psize); - if (con.width.size.type == ui_size_percent) ww -= 2 * get_padding(t); - /* FIXME: Resizing the window makes some elements retain their - * size on window resize. - * This is fixed by replacing MAX(...) with just `ww`, - * This in turn makes elements that are supposed to have some - * relative width not take up the full width. */ - uitree_set_size(t, (v2_i32){.x = MAX(tsize.x, ww), .y = 0}); - tsize = uitree_get_size(t); - } break; - - case ui_constraint_height: { - i32 ww = ui_size_to_pixel_ex(con.height.size, true, psize); - if (con.height.size.type == ui_size_percent) ww -= 2 * get_padding(t); - /* FIXME: Same as above */ - uitree_set_size(t, (v2_i32){.y = MAX(tsize.y, ww), .x = 0}); - tsize = uitree_get_size(t); - } break; - - case ui_constraint_horizontal_align: { - const i32 size = - ui_size_to_pixel_ex(con.horizontal_align.spacing, false, psize); - i32 new_x = ppos.x; - switch (con.horizontal_align.align) { - case ui_constraint_horizontal_align_left: - new_x += size + get_margin(t); - break; - case ui_constraint_horizontal_align_center: - new_x += (psize.x / 2) - (tsize.x / 2); - break; - case ui_constraint_horizontal_align_right: - new_x += psize.x - size - tsize.x - get_margin(t); - break; - } - uitree_set_pos(t, (v2_i32){.x = new_x, .y = 0}); - } break; - - case ui_constraint_vertical_align: { - i32 size = ui_size_to_pixel_ex(con.horizontal_align.spacing, true, psize); - i32 new_y = ppos.y; - switch (con.vertical_align.align) { - case ui_constraint_vertical_align_top: - new_y += size + get_margin(t); - break; - case ui_constraint_vertical_align_center: - new_y += ((psize.y) / 2) - (tsize.y / 2); - break; - case ui_constraint_vertical_align_bottom: - new_y += psize.y - size - tsize.y - get_margin(t); - break; - } - uitree_set_pos(t, (v2_i32){.x = 0, .y = new_y}); - } break; - - case ui_constraint_left: - uitree_set_pos(t, (v2_i32){.x = ppos.x + ui_size_to_pixel_ex( - con.left.pos, false, psize), - .y = 0}); - break; - - case ui_constraint_top: - uitree_set_pos(t, (v2_i32){.y = ppos.y + ui_size_to_pixel_ex(con.top.pos, - true, psize), - .x = 0}); - break; - - case ui_constraint_right: - if (tsize.x > 0) - uitree_set_pos( - t, (v2_i32){.x = ppos.x + psize.x - - ui_size_to_pixel_ex(con.right.pos, false, psize) - - tsize.x, - .y = 0}); - break; - - case ui_constraint_bottom: - if (tsize.y > 0) - uitree_set_pos( - t, (v2_i32){.y = ppos.y + psize.y - - ui_size_to_pixel_ex(con.bottom.pos, true, psize) - - tsize.y, - .x = 0}); - break; - - default: - ERROR("Unknown constraint: %d", constraints->value.type); - exit(EXIT_FAILURE); - break; - } - - constraints = constraints->next; - } -} - -void ui_rearrange_container(UITree_container* c, v2_i32 ppos, v2_i32 psize) { - struct List_ui_constraint* constraints = c->constraints; - struct List_ui_constraint* first_constraint = constraints; - bool fitwidth = !uitree_has_constraint((UITree*)c, ui_constraint_width); - bool fitheight = !uitree_has_constraint((UITree*)c, ui_constraint_height); - - if (fitwidth) c->w = 0; - if (fitheight) c->h = 0; - - ui_constraints_apply((UITree*)c, ppos, psize); - /* Apply constraints to children here? */ - { - const v2_i32 pos = - v2_i32_sub_i((v2_i32){.x = c->x, .y = c->y}, 0); // c->margin); - const v2_i32 size = - v2_i32_sub_i((v2_i32){.x = c->w, .y = c->h}, - (2 + MAX(c->children_len - 1, 0)) * c->padding); - for (usize cc = 0; cc < c->children_len; cc++) { - ui_rearrange(c->children[cc], pos, size); - } - } - - /* TODO: We want to update children regardless of whether we are doing a fit - * on the size of children. Switch the conditions (fitwidth||fitheight) and - * (children_len > 0); the size-fit doesn't matter if there's no children, - * the childrens size only matters if we do a size-fit. */ - - /* If we do not have a size > -1 we need to adapt the size to all children - * and add a size constraint on the largest of one of them */ - if (fitwidth || fitheight) { - - i32 maxwidth = 2 * c->padding; - i32 maxheight = maxwidth; - - if (c->children_len > 0) { - if (c->children == NULL) { - ERROR( - "Container has children_len(%d) > 0 but children pointer is NULL!", - c->children_len); - exit(EXIT_FAILURE); - } - - /* Calculate new posisitions and save the largest one */ - v2_i32 startpos = (v2_i32){.x = c->x + c->margin, .y = c->y + c->margin}; - v2_i32 startsize = - (v2_i32){.x = c->w - (2 * c->padding), .y = c->h - (2 * c->padding)}; - - for (usize cc = 0; cc < c->children_len; cc++) { - const v2_i32 childsize = elem_size(c->children[cc]); - i32 margin = (cc + 1 == c->children_len) - ? 0 - : MAX(c->padding, get_margin(c->children[cc])); - - /* We just sum up in the direction we are going */ - if (c->direction == DIRECTION_HORIZONTAL) { - maxwidth += childsize.x + margin; - maxheight = MAX(childsize.y + 2 * c->padding, maxheight); - - startpos.x += childsize.x + margin; - startsize.x -= childsize.x + margin; - } else { - maxwidth = MAX(childsize.x + 2 * c->padding, maxwidth); - maxheight += childsize.y + margin; - - startpos.y += childsize.y + margin; - startsize.y -= childsize.y + margin; - } - } - - struct List_ui_constraint* constraint_head = c->constraints; - struct List_ui_constraint constraint_width; - struct List_ui_constraint constraint_height; - - if (fitwidth) { - /* Create new size constraint */ - constraint_width = (struct List_ui_constraint){ - .value = - { - .type = ui_constraint_width, - .constraint.width = - { - .size = ui_size_pixel_new(maxwidth), - }, - }, - .next = constraint_head, - }; - - /* Prepend it to the list of constraints */ - c->constraints = &constraint_width; - constraint_head = c->constraints; - } - if (fitheight) { - constraint_height = (struct List_ui_constraint){ - .value = - { - .type = ui_constraint_height, - .constraint.height = - { - .size = ui_size_pixel_new(maxheight), - }, - }, - .next = constraint_head, - }; - /* Prepend it to the list of constraints */ - c->constraints = &constraint_height; - // constraint_head = c->constraints; - /* TODO: Convert to scrollable container if the height is - * greater than some constant relative to the screen height */ - } - - /* Recurse with new constraints */ - ui_rearrange_container(c, ppos, psize); - - /* Replace the original set of constraints, as the new ones will be - * out of scope once we return from the current call frame */ - c->constraints = first_constraint; - - /* Update child elements */ - /* This is 1:1 (almost) to the previous iteration of children */ - startpos = (v2_i32){c->x + c->padding, c->y + c->padding}; - startsize = (v2_i32){c->w - 2 * c->padding, c->h - 2 * c->padding}; - - for (usize cc = 0; cc < c->children_len; cc++) { - v2_i32 childsize = elem_size(c->children[cc]); - i32 margin = MAX(c->padding, get_margin(c->children[cc])); - - v2_i32 startsize_2; - if (c->direction == DIRECTION_HORIZONTAL) { - startsize_2 = (v2_i32){childsize.x, startsize.y}; - } else { - startsize_2 = (v2_i32){startsize.x, childsize.y}; - } - ui_rearrange(c->children[cc], startpos, startsize_2); - - if (c->direction == DIRECTION_HORIZONTAL) { - maxwidth += childsize.x + margin; - maxheight = MAX(childsize.y, maxheight); - - startpos.x += childsize.x + margin; - startsize.x -= childsize.x + margin; - - } else { - maxwidth = MAX(childsize.x + 2 * c->padding, maxwidth); - maxheight += childsize.y + margin; - - startpos.y += childsize.y + margin; - startsize.y -= childsize.y + margin; - } - } - } - } -} - -void ui_rearrange_button(UITree_button* t, v2_i32 ppos, v2_i32 psize) { - t->x = ppos.x; - t->y = ppos.y; - - ui_constraints_apply((UITree*)t, ppos, psize); -} - -void ui_rearrange_title(UITree_title* t, v2_i32 ppos, v2_i32 psize) { - /*Defaults to just sit in the top-left corner of the parent*/ - t->x = ppos.x; - t->y = ppos.y; - - ui_constraints_apply((UITree*)t, ppos, psize); -} - -void ui_rearrange_text(UITree_text* t, v2_i32 ppos, v2_i32 psize) { - t->x = ppos.x; - t->y = ppos.y; - t->w = psize.x; - t->h = psize.y; - - t->texture_size.x = psize.x; - t->texture_size.y = psize.y; - - /* Free the old texture */ - /* Re-render the text */ - t->text_texture_index = engine_render_text( - t->font_id, t->fg, (char*)t->text_original, &t->texture_size, true); - - t->w = t->texture_size.x; - t->h = t->texture_size.y; - - ui_constraints_apply((UITree*)t, ppos, psize); -} - -void ui_rearrange(UITree* root, v2_i32 ppos, v2_i32 psize) { - if (root == NULL) { - WARN("%s received a null pointer, ignoring", __func__); - return; - } - - switch (root->type) { - case uitype_container: - ui_rearrange_container(&root->container, ppos, psize); - break; - case uitype_button: - ui_rearrange_button(&root->button, ppos, psize); - break; - case uitype_title: - ui_rearrange_title(&root->title, ppos, psize); - break; - case uitype_text: - ui_rearrange_text(&root->text, ppos, psize); - break; - default: - if (root->type >= uitype_MAX) { - ERROR("Unknown uitype: %d", root->type); - exit(EXIT_FAILURE); - } else { - WARN("Rendering not implemented for %s", uitype_str[root->type]); - } - break; - } -} - -void ui_resolve_constraints(void) { - u64* i = NULL; - struct btree_iter_t* it = NULL; - - /*absolutely bonkers way to do this*/ - if (GLOBAL_UIROOTS == NULL) { - WARN("UIROOTS not initialized"); - } - - it = btree_iter_t_new(GLOBAL_UIROOTS); - - while ((i = btree_iter(GLOBAL_UIROOTS, it)) != NULL) { - ui_rearrange((UITree*)*i, (v2_i32){0, 0}, - GLOBAL_PLATFORM->window->windowsize); - } - free(it); -} - -void ui_container_attach(UITree* root, UITree* child) { - if (root == NULL) { - WARN("%s received a null pointer", __func__); - return; - } - if (root->type != uitype_container) { - WARN("Trying to attach a child to a non-container UI element"); - return; - } - - UITree_container* c = &(root->container); - - static const usize size_increment = 32; - static const usize uitree_sz = sizeof(UITree*); - - if (c->children == NULL) { - /* Allocate space for children */ - c->children = malloc(uitree_sz * size_increment); - - } else if ((c->children_len + 1) * uitree_sz >= c->children_size) { - /* If there's not enough room for more children we will need to - * reallocate some more memory */ - c->children = - realloc(c->children, (c->children_len + size_increment) * uitree_sz); - c->children_size += size_increment * uitree_sz; - } - - /* Finally: attach the new element */ - c->children[c->children_len++] = child; - - /* Since the child is no longer a part of the root-ui we can delete it from - * the btree-mapping */ - { - u64 tmp = (u64)child; - if (!btree_delete(GLOBAL_UIROOTS, &tmp)) { - WARN("Could not find child %p in global lookup table", child); - btree_print(GLOBAL_UIROOTS, &pointer_print); - } - } -} - -void ui_button_enable(UITree_button* b) { - b->enabled = true; - // ->textures[b->text_texture_index] - // ->texture; -} - -void ui_button_disable(UITree_button* b) { - b->enabled = false; - - // ->textures[b->text_texture_index] - // ->texture; -} - -void ui_set_default_colors(Engine_color fg, Engine_color bg) { - DEFAULT_FG = fg; - DEFAULT_BG = bg; -} - -void ui_set_default_padding(i32 padding) { DEFAULT_PADDING = padding; } - -Engine_color ui_get_default_fg(void) { return DEFAULT_FG; } -Engine_color ui_get_default_bg(void) { return DEFAULT_BG; } - -bool check_point_in_rect(const v2_i32* p, const /* */ int r) { - //if (p->x > r->x && p->x < r->x + r->w && p->y > r->y && p->y < r->y + r->h) - // return true; - return false; -} - -u64 ui_check_click_internal(UITree* root, v2_i32 pos) { - v2_i32 ps = get_pos(root); - v2_i32 sz = get_size(root); - /* */ - //switch (root->type) { - //case uitype_container: - // if (root->container.children != NULL && root->container.children_len > 0) { - // const UITree_container* container = &root->container; - - // if (check_point_in_rect(&pos, &elem_rect)) { - - // for (usize i = 0; i < container->children_len; i++) { - // u64 res = ui_check_click_internal(container->children[i], pos); - // if (res != (u64)ELEM_NOT_FOUND) { - // return res; - // } - // } - // } - // } - // break; - //case uitype_button: - // if (root->button.enabled) { - // WARN("Checking button"); - // elem_rect.h += root->button.padding * 2; - // elem_rect.w += root->button.padding * 2; - // WARN("Button specs: <%d,%d>+%d,%d", elem_rect.w, elem_rect.h, - // elem_rect.x, elem_rect.y); - // if (check_point_in_rect(&pos, &elem_rect)) { - // return root->button.id; - // } - // } - // break; - //case uitype_title: - //case uitype_text: - //default: - // break; - //} - return ELEM_NOT_FOUND; -} - -u64 ui_check_click(UITree* root) { - if (!GLOBAL_PLATFORM->mouse_lclick) return NO_CLICK; - - v2_i32 pos = GLOBAL_PLATFORM->mouse_pos; - - INFO("Mousepos: <%d,%d>", pos.x, pos.y); - - return ui_check_click_internal(root, pos); -} diff --git a/src/ui/rendering.c b/src/ui/rendering.c deleted file mode 100644 index 1516736..0000000 --- a/src/ui/rendering.c +++ /dev/null @@ -1,63 +0,0 @@ -#include -#include -#include - -#define ENGINE_INTERNALS - -#include -#include - -extern Platform* GLOBAL_PLATFORM; - -extern const char* uitype_str[]; - -void render_uitree(Window* w, UITree* t) { - switch (t->type) { - case uitype_container: - render_container(w, &t->container); - break; - case uitype_button: - render_button(w, &t->button); - break; - case uitype_title: - render_title(w, &t->title); - break; - case uitype_text: - render_text(w, &t->text); - break; - default: - if (t->type >= uitype_MAX) { - ERROR("Unknown uitype: %d", t->type); - } else { - WARN("Rendering not implemented for %s", uitype_str[t->type]); - } - break; - } -} - -void render_container(Window* w, UITree_container* t) { - - if (t->children != NULL && t->children_len > 0) { - for (usize i = 0; i < t->children_len; i++) { - render_uitree(w, t->children[i]); - } - } -} - -void render_button(Window* w, UITree_button* t) { -} - -void render_title(Window* w, UITree_title* t) { -} - -void render_text(Window* w, UITree_text* t) { -} - -i64 add_texture(struct Resources* resptr, Texture* t) { - return 0; -} - -i64 engine_render_text(i32 font_id, Engine_color fg, char* text, - v2_i32* size_out, bool wrapped) { - return 0; -} diff --git a/src/ui/src/positioning.c b/src/ui/src/positioning.c new file mode 100644 index 0000000..3db73fe --- /dev/null +++ b/src/ui/src/positioning.c @@ -0,0 +1,945 @@ +#include +#define ENGINE_INTERNALS + +#include +#include +#include + +static Engine_color DEFAULT_FG = {0xFF, 0xFF, 0xFF, 0xFF}; +static Engine_color DEFAULT_BG = {0x00, 0x00, 0x00, 0xFF}; +static i32 DEFAULT_PADDING = 8; +static i32 DEFAULT_MARGIN = 0; + +/* We want to keep track of the roots of the UI elements, s.t. we can resize and + * reposition the elements if needed be. */ +struct btree* GLOBAL_UIROOTS = NULL; + +extern Platform* GLOBAL_PLATFORM; + +const char* uitype_str[] = { + [uitype_container] = "uitype_container", + [uitype_button] = "uitype_button", + [uitype_title] = "uitype_title", + [uitype_text] = "uitype_text", + [uitype_MAX] = "uitype_MAX", + NULL, +}; + +const char* constraint_str[] = { + [ui_constraint_width] = "ui_constraint_width", + [ui_constraint_height] = "ui_constraint_height", + + [ui_constraint_horizontal_align] = "ui_constraint_horizontal_align", + [ui_constraint_vertical_align] = "ui_constraint_vertical_align", + + [ui_constraint_left] = "ui_constraint_left", + [ui_constraint_right] = "ui_constraint_right", + [ui_constraint_top] = "ui_constraint_top", + [ui_constraint_bottom] = "ui_constraint_bottom", +}; + +static i32 pointer_cmp(const void* a, const void* b) { + const u64* x = a; + const u64* y = b; + /* We just use the address as index */ + return *x - *y; +} + +static void pointer_print(const void* a) { + const u64* t = a; + printf("%lx\n", *t); +} + +void ui_rearrange(UITree* root, v2_i32 ppos, v2_i32 psize); +i32 get_padding(UITree* t); +i32 get_margin(UITree* t); +v2_i32 get_pos(UITree* t); +v2_i32 get_size(UITree* t); + +f32 ui_size_pixel_to_percent_width(i32 pixels) { + return (f32)pixels / (f32)GLOBAL_PLATFORM->window->windowsize.x; +} + +f32 ui_size_pixel_to_percent_height(i32 pixels) { + return (f32)pixels / (f32)GLOBAL_PLATFORM->window->windowsize.y; +} + +f32 ui_size_pixel_to_percent(i32 pixels) { + return ui_size_percent_height_to_pixel(pixels); +} + +i32 ui_size_percent_width_to_pixel(f32 percent) { + return (i32)(percent * GLOBAL_PLATFORM->window->windowsize.x); +} + +i32 ui_size_percent_height_to_pixel(f32 percent) { + return (i32)(percent * GLOBAL_PLATFORM->window->windowsize.y); +} + +i32 ui_size_percent_to_pixel(f32 percent) { + return ui_size_percent_height_to_pixel(percent); +} + +i32 ui_size_to_pixel_ex(ui_size s, bool vertical, v2_i32 psize) { + if (s.type == ui_size_pixel) { + return s.pixel.value; + } else { + psize.x = MAX(1, psize.x); + psize.y = MAX(1, psize.y); + if (vertical) { + return psize.y * s.percent.value; + } + return psize.x * s.percent.value; + } +} + +i32 ui_size_to_pixel(ui_size s) { + if (s.type == ui_size_pixel) { + return s.pixel.value; + } else { + return ui_size_percent_height_to_pixel(s.percent.value); + } +} + +void ui_add(UITree* t) { + + if (GLOBAL_UIROOTS == NULL) { + GLOBAL_UIROOTS = btree_new(sizeof(void*), 16, &pointer_cmp); + } + + u64 tmp = (u64)t; + btree_insert(GLOBAL_UIROOTS, &tmp); +} + +void clear_ui(void) { + UITree* t = NULL; + while ((t = btree_first(GLOBAL_UIROOTS)) != NULL) { + uitree_free(t); + if (!btree_delete(GLOBAL_UIROOTS, t)) { + WARN("Failed deletion"); + UITree* elem = (UITree*)t; + WARN("Failed to remove %s from global ui-table", uitype_str[elem->type]); + } + } +} + +/* Unwraps the size to a v2_i32 */ +v2_i32 uitree_get_size(UITree* t) { + if (t == NULL) return (v2_i32){.x = -1, .y = -1}; + switch (t->type) { + case uitype_container: + return (v2_i32){.x = t->container.w, .y = t->container.h}; + case uitype_button: + return (v2_i32){.x = t->button.w, .y = t->button.h}; + case uitype_title: + return (v2_i32){.x = t->title.w, .y = t->title.h}; + case uitype_text: + return (v2_i32){.x = t->text.w, .y = t->text.h}; + default: + return (v2_i32){.x = -1, .y = -1}; + } +} + +/* elem_size also takes into account the padding and other stuffs */ +v2_i32 elem_size(UITree* root) { + if (root == NULL) return (v2_i32){0, 0}; + + const i32 total_padding = 2 * get_padding(root); + return v2_i32_add_i(uitree_get_size(root), total_padding); +} + +v2_i32 elem_size_spacing(UITree* root) { + if (root == NULL) return (v2_i32){0, 0}; + + const i32 total_margin = 2 * get_padding(root) + 2 * get_margin(root); + return v2_i32_add_i(uitree_get_size(root), total_margin); +} + +UITree* ui_container_new_ex(Engine_color fg, Engine_color bg, + Engine_color border, bool direction, + struct List_ui_constraint* constraints) { + + UITree* t = malloc(sizeof(UITree)); + + t->container.type = uitype_container; + t->container.visible = true; + t->container.direction = direction; + + /* -1 == unsolved constraint */ + t->container.x = -1; + t->container.y = -1; + t->container.w = -1; + t->container.h = -1; + t->container.padding = 0; + t->container.margin = 0; + + t->container.fg = fg; + t->container.bg = bg; + t->container.bordercolor = border; + + t->container.children = NULL; + t->container.children_len = 0; + t->container.children_size = 0; + + t->container.constraints = constraints; + + ui_rearrange(t, (v2_i32){0, 0}, (v2_i32){0, 0}); + + ui_add(t); + + return t; +} + +UITree* ui_container(bool direction, struct List_ui_constraint* constraints) { + return ui_container_new_ex( + /* We set border color to fg by default */ + DEFAULT_FG, DEFAULT_BG, DEFAULT_FG, direction, constraints); +} + +UITree* ui_title(i32 font_id, const char* text, + struct List_ui_constraint* constraints) { + UITree_title* t = malloc(sizeof(UITree)); + t->type = uitype_title; + + t->x = -1; + t->y = -1; + + t->padding = DEFAULT_PADDING; + t->margin = DEFAULT_MARGIN; + + t->fg = DEFAULT_FG; + + t->font_id = font_id; + t->text_original = text; + t->text_texture_index = + engine_render_text(font_id, t->fg, (char*)text, &t->texture_size, false); + + t->w = t->texture_size.x; + t->h = t->texture_size.y; + + t->constraints = constraints; + + ui_rearrange((UITree*)t, (v2_i32){0, 0}, (v2_i32){0, 0}); + + ui_add((UITree*)t); + + return (UITree*)t; +} + +UITree* ui_text(i32 font_id, const char* text, + struct List_ui_constraint* constraints) { + UITree* t = malloc(sizeof(UITree)); + t->text.type = uitype_text; + + t->text.x = -1; + t->text.y = -1; + + t->text.padding = 0; + t->text.margin = DEFAULT_MARGIN / 2; + + t->text.fg = DEFAULT_FG; + + t->text.font_id = font_id; + t->text.text_original = text; + + /* Postpone rendering until we have a clear set of size-constraints */ + + // t->text.text_texture_index = + // engine_render_text(font_id, DEFAULT_FG, (char*)text, + //&t->text.texture_size, true); + + // t->text.w = t->text.texture_size.x; + // t->text.h = t->text.texture_size.y; + + t->text.constraints = constraints; + + ui_rearrange(t, (v2_i32){0, 0}, (v2_i32){0, 0}); + + ui_add(t); + + return t; +} + +UITree* ui_button(u64 id, i32 font_id, char* text, + struct List_ui_constraint* constraints) { + UITree_button* t = malloc(sizeof(UITree)); + t->type = uitype_button; + + t->enabled = true; + t->id = id; + t->x = -1; + t->y = -1; + t->padding = DEFAULT_PADDING; + t->margin = DEFAULT_MARGIN; + + t->fg = DEFAULT_FG; + t->bg = DEFAULT_BG; + + t->font_id = font_id; + t->text_original = text; + t->text_texture_index = + engine_render_text(font_id, t->fg, (char*)text, &t->texture_size, false); + + t->w = t->texture_size.x; + t->h = t->texture_size.y; + + t->constraints = constraints; + + ui_rearrange((UITree*)t, (v2_i32){0, 0}, (v2_i32){0, 0}); + + ui_add((UITree*)t); + + return (UITree*)t; +} + +void uitree_free(UITree* t) { + switch (t->type) { + case uitype_container: + if (t->container.children != NULL && t->container.children_len > 0) { + for (usize i = 0; i < t->container.children_len; i++) { + uitree_free(t->container.children[i]); + } + free(t->container.children); + } + break; + case uitype_button: + case uitype_title: + case uitype_text: + free(t); + break; + default: + break; + } +} + +struct List_ui_constraint* get_constraints(UITree* t) { + switch (t->type) { + case uitype_container: + return t->container.constraints; + case uitype_button: + return t->button.constraints; + case uitype_title: + return t->title.constraints; + case uitype_text: + return t->text.constraints; + default: + return NULL; + } +} + +i32 get_padding(UITree* t) { + switch (t->type) { + case uitype_container: + return t->container.padding; + case uitype_button: + return t->button.padding; + case uitype_title: + return t->title.padding; + case uitype_text: + return t->text.padding; + default: + return 0; + } +} + +i32 get_margin(UITree* t) { + switch (t->type) { + case uitype_container: + return t->container.margin; + case uitype_button: + return t->button.margin; + case uitype_title: + return t->title.margin; + case uitype_text: + return t->text.margin; + default: + return 0; + } +} + +v2_i32 get_pos(UITree* t) { + switch (t->type) { + case uitype_container: + return (v2_i32){t->container.x, t->container.y}; + case uitype_button: + return (v2_i32){t->button.x, t->button.y}; + case uitype_title: + return (v2_i32){t->title.x, t->title.y}; + case uitype_text: + return (v2_i32){t->text.x, t->text.y}; + default: + return (v2_i32){0, 0}; + } +} + +v2_i32 get_size(UITree* t) { + switch (t->type) { + case uitype_container: + return (v2_i32){t->container.w, t->container.h}; + case uitype_button: + return (v2_i32){t->button.w, t->button.h}; + case uitype_title: + return (v2_i32){t->title.w, t->title.h}; + case uitype_text: + return (v2_i32){t->text.w, t->text.h}; + default: + return (v2_i32){0, 0}; + } +} + +#define SETVAL(VAL, tt) \ + { \ + if ((VAL) > 0) t->tt = VAL; \ + } +void uitree_set_pos(UITree* t, v2_i32 newpos) { + switch (t->type) { + case uitype_container: + SETVAL(newpos.x, container.x); + SETVAL(newpos.y, container.y); + break; + case uitype_button: + SETVAL(newpos.x, button.x); + SETVAL(newpos.y, button.y); + break; + case uitype_title: + SETVAL(newpos.x, title.x); + SETVAL(newpos.y, title.y); + break; + case uitype_text: + SETVAL(newpos.x, text.x); + SETVAL(newpos.y, text.y); + break; + default: + return; + } +} + +void uitree_set_size(UITree* t, v2_i32 size) { + switch (t->type) { + case uitype_container: + SETVAL(size.x, container.w); + SETVAL(size.y, container.h); + break; + case uitype_button: + SETVAL(size.x, button.w); + SETVAL(size.y, button.h); + break; + case uitype_title: + SETVAL(size.x, title.w); + SETVAL(size.y, title.h); + break; + case uitype_text: + SETVAL(size.x, text.w); + SETVAL(size.y, text.h); + break; + default: + return; + } +} + +bool uitree_has_constraint(UITree* t, const ui_constraint_t constraint) { + struct List_ui_constraint* c = NULL; + struct List_ui_constraint* first_constraint; + if (t == NULL) { + ERROR("%s got nullptr argument!", __func__); + exit(EXIT_FAILURE); + } + + switch (t->type) { + case uitype_container: + c = t->container.constraints; + break; + case uitype_button: + c = t->button.constraints; + break; + case uitype_title: + c = t->title.constraints; + break; + case uitype_text: + c = t->text.constraints; + break; + default: + break; + } + + first_constraint = c; + + while (c != NULL) { + if ((void*)c->next == (void*)first_constraint) { + ERROR("Infinite constraint loop detected!"); + exit(EXIT_FAILURE); + } + if (constraint == c->value.type) return true; + + c = c->next; + } + return false; +} + +void ui_constraints_apply(UITree* t, v2_i32 ppos, v2_i32 psize) { + struct List_ui_constraint* constraints = get_constraints(t); + struct List_ui_constraint* first_constraint = constraints; + v2_i32 tsize = uitree_get_size(t); + + if (constraints == NULL) return; + + while (constraints != NULL) { + if ((void*)constraints->next == (void*)first_constraint) { + ERROR("Infinite constraint loop detected!"); + ERROR(" Type: %s", constraint_str[constraints->value.type]); + exit(EXIT_FAILURE); + } + + const constraintval_t con = constraints->value.constraint; + + switch (constraints->value.type) { + case ui_constraint_width: { + i32 ww = ui_size_to_pixel_ex(con.width.size, false, psize); + if (con.width.size.type == ui_size_percent) ww -= 2 * get_padding(t); + /* FIXME: Resizing the window makes some elements retain their + * size on window resize. + * This is fixed by replacing MAX(...) with just `ww`, + * This in turn makes elements that are supposed to have some + * relative width not take up the full width. */ + uitree_set_size(t, (v2_i32){.x = MAX(tsize.x, ww), .y = 0}); + tsize = uitree_get_size(t); + } break; + + case ui_constraint_height: { + i32 ww = ui_size_to_pixel_ex(con.height.size, true, psize); + if (con.height.size.type == ui_size_percent) ww -= 2 * get_padding(t); + /* FIXME: Same as above */ + uitree_set_size(t, (v2_i32){.y = MAX(tsize.y, ww), .x = 0}); + tsize = uitree_get_size(t); + } break; + + case ui_constraint_horizontal_align: { + const i32 size = + ui_size_to_pixel_ex(con.horizontal_align.spacing, false, psize); + i32 new_x = ppos.x; + switch (con.horizontal_align.align) { + case ui_constraint_horizontal_align_left: + new_x += size + get_margin(t); + break; + case ui_constraint_horizontal_align_center: + new_x += (psize.x / 2) - (tsize.x / 2); + break; + case ui_constraint_horizontal_align_right: + new_x += psize.x - size - tsize.x - get_margin(t); + break; + } + uitree_set_pos(t, (v2_i32){.x = new_x, .y = 0}); + } break; + + case ui_constraint_vertical_align: { + i32 size = ui_size_to_pixel_ex(con.horizontal_align.spacing, true, psize); + i32 new_y = ppos.y; + switch (con.vertical_align.align) { + case ui_constraint_vertical_align_top: + new_y += size + get_margin(t); + break; + case ui_constraint_vertical_align_center: + new_y += ((psize.y) / 2) - (tsize.y / 2); + break; + case ui_constraint_vertical_align_bottom: + new_y += psize.y - size - tsize.y - get_margin(t); + break; + } + uitree_set_pos(t, (v2_i32){.x = 0, .y = new_y}); + } break; + + case ui_constraint_left: + uitree_set_pos(t, (v2_i32){.x = ppos.x + ui_size_to_pixel_ex( + con.left.pos, false, psize), + .y = 0}); + break; + + case ui_constraint_top: + uitree_set_pos(t, (v2_i32){.y = ppos.y + ui_size_to_pixel_ex(con.top.pos, + true, psize), + .x = 0}); + break; + + case ui_constraint_right: + if (tsize.x > 0) + uitree_set_pos( + t, (v2_i32){.x = ppos.x + psize.x - + ui_size_to_pixel_ex(con.right.pos, false, psize) - + tsize.x, + .y = 0}); + break; + + case ui_constraint_bottom: + if (tsize.y > 0) + uitree_set_pos( + t, (v2_i32){.y = ppos.y + psize.y - + ui_size_to_pixel_ex(con.bottom.pos, true, psize) - + tsize.y, + .x = 0}); + break; + + default: + ERROR("Unknown constraint: %d", constraints->value.type); + exit(EXIT_FAILURE); + break; + } + + constraints = constraints->next; + } +} + +void ui_rearrange_container(UITree_container* c, v2_i32 ppos, v2_i32 psize) { + struct List_ui_constraint* constraints = c->constraints; + struct List_ui_constraint* first_constraint = constraints; + bool fitwidth = !uitree_has_constraint((UITree*)c, ui_constraint_width); + bool fitheight = !uitree_has_constraint((UITree*)c, ui_constraint_height); + + if (fitwidth) c->w = 0; + if (fitheight) c->h = 0; + + ui_constraints_apply((UITree*)c, ppos, psize); + /* Apply constraints to children here? */ + { + const v2_i32 pos = + v2_i32_sub_i((v2_i32){.x = c->x, .y = c->y}, 0); // c->margin); + const v2_i32 size = + v2_i32_sub_i((v2_i32){.x = c->w, .y = c->h}, + (2 + MAX(c->children_len - 1, 0)) * c->padding); + for (usize cc = 0; cc < c->children_len; cc++) { + ui_rearrange(c->children[cc], pos, size); + } + } + + /* TODO: We want to update children regardless of whether we are doing a fit + * on the size of children. Switch the conditions (fitwidth||fitheight) and + * (children_len > 0); the size-fit doesn't matter if there's no children, + * the childrens size only matters if we do a size-fit. */ + + /* If we do not have a size > -1 we need to adapt the size to all children + * and add a size constraint on the largest of one of them */ + if (fitwidth || fitheight) { + + i32 maxwidth = 2 * c->padding; + i32 maxheight = maxwidth; + + if (c->children_len > 0) { + if (c->children == NULL) { + ERROR( + "Container has children_len(%d) > 0 but children pointer is NULL!", + c->children_len); + exit(EXIT_FAILURE); + } + + /* Calculate new posisitions and save the largest one */ + v2_i32 startpos = (v2_i32){.x = c->x + c->margin, .y = c->y + c->margin}; + v2_i32 startsize = + (v2_i32){.x = c->w - (2 * c->padding), .y = c->h - (2 * c->padding)}; + + for (usize cc = 0; cc < c->children_len; cc++) { + const v2_i32 childsize = elem_size(c->children[cc]); + i32 margin = (cc + 1 == c->children_len) + ? 0 + : MAX(c->padding, get_margin(c->children[cc])); + + /* We just sum up in the direction we are going */ + if (c->direction == DIRECTION_HORIZONTAL) { + maxwidth += childsize.x + margin; + maxheight = MAX(childsize.y + 2 * c->padding, maxheight); + + startpos.x += childsize.x + margin; + startsize.x -= childsize.x + margin; + } else { + maxwidth = MAX(childsize.x + 2 * c->padding, maxwidth); + maxheight += childsize.y + margin; + + startpos.y += childsize.y + margin; + startsize.y -= childsize.y + margin; + } + } + + struct List_ui_constraint* constraint_head = c->constraints; + struct List_ui_constraint constraint_width; + struct List_ui_constraint constraint_height; + + if (fitwidth) { + /* Create new size constraint */ + constraint_width = (struct List_ui_constraint){ + .value = + { + .type = ui_constraint_width, + .constraint.width = + { + .size = ui_size_pixel_new(maxwidth), + }, + }, + .next = constraint_head, + }; + + /* Prepend it to the list of constraints */ + c->constraints = &constraint_width; + constraint_head = c->constraints; + } + if (fitheight) { + constraint_height = (struct List_ui_constraint){ + .value = + { + .type = ui_constraint_height, + .constraint.height = + { + .size = ui_size_pixel_new(maxheight), + }, + }, + .next = constraint_head, + }; + /* Prepend it to the list of constraints */ + c->constraints = &constraint_height; + // constraint_head = c->constraints; + /* TODO: Convert to scrollable container if the height is + * greater than some constant relative to the screen height */ + } + + /* Recurse with new constraints */ + ui_rearrange_container(c, ppos, psize); + + /* Replace the original set of constraints, as the new ones will be + * out of scope once we return from the current call frame */ + c->constraints = first_constraint; + + /* Update child elements */ + /* This is 1:1 (almost) to the previous iteration of children */ + startpos = (v2_i32){c->x + c->padding, c->y + c->padding}; + startsize = (v2_i32){c->w - 2 * c->padding, c->h - 2 * c->padding}; + + for (usize cc = 0; cc < c->children_len; cc++) { + v2_i32 childsize = elem_size(c->children[cc]); + i32 margin = MAX(c->padding, get_margin(c->children[cc])); + + v2_i32 startsize_2; + if (c->direction == DIRECTION_HORIZONTAL) { + startsize_2 = (v2_i32){childsize.x, startsize.y}; + } else { + startsize_2 = (v2_i32){startsize.x, childsize.y}; + } + ui_rearrange(c->children[cc], startpos, startsize_2); + + if (c->direction == DIRECTION_HORIZONTAL) { + maxwidth += childsize.x + margin; + maxheight = MAX(childsize.y, maxheight); + + startpos.x += childsize.x + margin; + startsize.x -= childsize.x + margin; + + } else { + maxwidth = MAX(childsize.x + 2 * c->padding, maxwidth); + maxheight += childsize.y + margin; + + startpos.y += childsize.y + margin; + startsize.y -= childsize.y + margin; + } + } + } + } +} + +void ui_rearrange_button(UITree_button* t, v2_i32 ppos, v2_i32 psize) { + t->x = ppos.x; + t->y = ppos.y; + + ui_constraints_apply((UITree*)t, ppos, psize); +} + +void ui_rearrange_title(UITree_title* t, v2_i32 ppos, v2_i32 psize) { + /*Defaults to just sit in the top-left corner of the parent*/ + t->x = ppos.x; + t->y = ppos.y; + + ui_constraints_apply((UITree*)t, ppos, psize); +} + +void ui_rearrange_text(UITree_text* t, v2_i32 ppos, v2_i32 psize) { + t->x = ppos.x; + t->y = ppos.y; + t->w = psize.x; + t->h = psize.y; + + t->texture_size.x = psize.x; + t->texture_size.y = psize.y; + + /* Free the old texture */ + /* Re-render the text */ + t->text_texture_index = engine_render_text( + t->font_id, t->fg, (char*)t->text_original, &t->texture_size, true); + + t->w = t->texture_size.x; + t->h = t->texture_size.y; + + ui_constraints_apply((UITree*)t, ppos, psize); +} + +void ui_rearrange(UITree* root, v2_i32 ppos, v2_i32 psize) { + if (root == NULL) { + WARN("%s received a null pointer, ignoring", __func__); + return; + } + + switch (root->type) { + case uitype_container: + ui_rearrange_container(&root->container, ppos, psize); + break; + case uitype_button: + ui_rearrange_button(&root->button, ppos, psize); + break; + case uitype_title: + ui_rearrange_title(&root->title, ppos, psize); + break; + case uitype_text: + ui_rearrange_text(&root->text, ppos, psize); + break; + default: + if (root->type >= uitype_MAX) { + ERROR("Unknown uitype: %d", root->type); + exit(EXIT_FAILURE); + } else { + WARN("Rendering not implemented for %s", uitype_str[root->type]); + } + break; + } +} + +void ui_resolve_constraints(void) { + u64* i = NULL; + struct btree_iter_t* it = NULL; + + /*absolutely bonkers way to do this*/ + if (GLOBAL_UIROOTS == NULL) { + WARN("UIROOTS not initialized"); + } + + it = btree_iter_t_new(GLOBAL_UIROOTS); + + while ((i = btree_iter(GLOBAL_UIROOTS, it)) != NULL) { + ui_rearrange((UITree*)*i, (v2_i32){0, 0}, + GLOBAL_PLATFORM->window->windowsize); + } + free(it); +} + +void ui_container_attach(UITree* root, UITree* child) { + if (root == NULL) { + WARN("%s received a null pointer", __func__); + return; + } + if (root->type != uitype_container) { + WARN("Trying to attach a child to a non-container UI element"); + return; + } + + UITree_container* c = &(root->container); + + static const usize size_increment = 32; + static const usize uitree_sz = sizeof(UITree*); + + if (c->children == NULL) { + /* Allocate space for children */ + c->children = malloc(uitree_sz * size_increment); + + } else if ((c->children_len + 1) * uitree_sz >= c->children_size) { + /* If there's not enough room for more children we will need to + * reallocate some more memory */ + c->children = + realloc(c->children, (c->children_len + size_increment) * uitree_sz); + c->children_size += size_increment * uitree_sz; + } + + /* Finally: attach the new element */ + c->children[c->children_len++] = child; + + /* Since the child is no longer a part of the root-ui we can delete it from + * the btree-mapping */ + { + u64 tmp = (u64)child; + if (!btree_delete(GLOBAL_UIROOTS, &tmp)) { + WARN("Could not find child %p in global lookup table", child); + btree_print(GLOBAL_UIROOTS, &pointer_print); + } + } +} + +void ui_button_enable(UITree_button* b) { + b->enabled = true; + // ->textures[b->text_texture_index] + // ->texture; +} + +void ui_button_disable(UITree_button* b) { + b->enabled = false; + + // ->textures[b->text_texture_index] + // ->texture; +} + +void ui_set_default_colors(Engine_color fg, Engine_color bg) { + DEFAULT_FG = fg; + DEFAULT_BG = bg; +} + +void ui_set_default_padding(i32 padding) { DEFAULT_PADDING = padding; } + +Engine_color ui_get_default_fg(void) { return DEFAULT_FG; } +Engine_color ui_get_default_bg(void) { return DEFAULT_BG; } + +bool check_point_in_rect(const v2_i32* p, const /* */ int r) { + //if (p->x > r->x && p->x < r->x + r->w && p->y > r->y && p->y < r->y + r->h) + // return true; + return false; +} + +u64 ui_check_click_internal(UITree* root, v2_i32 pos) { + v2_i32 ps = get_pos(root); + v2_i32 sz = get_size(root); + /* */ + //switch (root->type) { + //case uitype_container: + // if (root->container.children != NULL && root->container.children_len > 0) { + // const UITree_container* container = &root->container; + + // if (check_point_in_rect(&pos, &elem_rect)) { + + // for (usize i = 0; i < container->children_len; i++) { + // u64 res = ui_check_click_internal(container->children[i], pos); + // if (res != (u64)ELEM_NOT_FOUND) { + // return res; + // } + // } + // } + // } + // break; + //case uitype_button: + // if (root->button.enabled) { + // WARN("Checking button"); + // elem_rect.h += root->button.padding * 2; + // elem_rect.w += root->button.padding * 2; + // WARN("Button specs: <%d,%d>+%d,%d", elem_rect.w, elem_rect.h, + // elem_rect.x, elem_rect.y); + // if (check_point_in_rect(&pos, &elem_rect)) { + // return root->button.id; + // } + // } + // break; + //case uitype_title: + //case uitype_text: + //default: + // break; + //} + return ELEM_NOT_FOUND; +} + +u64 ui_check_click(UITree* root) { + if (!GLOBAL_PLATFORM->mouse_lclick) return NO_CLICK; + + v2_i32 pos = GLOBAL_PLATFORM->mouse_pos; + + INFO("Mousepos: <%d,%d>", pos.x, pos.y); + + return ui_check_click_internal(root, pos); +} diff --git a/src/ui/src/rendering.c b/src/ui/src/rendering.c new file mode 100644 index 0000000..1516736 --- /dev/null +++ b/src/ui/src/rendering.c @@ -0,0 +1,63 @@ +#include +#include +#include + +#define ENGINE_INTERNALS + +#include +#include + +extern Platform* GLOBAL_PLATFORM; + +extern const char* uitype_str[]; + +void render_uitree(Window* w, UITree* t) { + switch (t->type) { + case uitype_container: + render_container(w, &t->container); + break; + case uitype_button: + render_button(w, &t->button); + break; + case uitype_title: + render_title(w, &t->title); + break; + case uitype_text: + render_text(w, &t->text); + break; + default: + if (t->type >= uitype_MAX) { + ERROR("Unknown uitype: %d", t->type); + } else { + WARN("Rendering not implemented for %s", uitype_str[t->type]); + } + break; + } +} + +void render_container(Window* w, UITree_container* t) { + + if (t->children != NULL && t->children_len > 0) { + for (usize i = 0; i < t->children_len; i++) { + render_uitree(w, t->children[i]); + } + } +} + +void render_button(Window* w, UITree_button* t) { +} + +void render_title(Window* w, UITree_title* t) { +} + +void render_text(Window* w, UITree_text* t) { +} + +i64 add_texture(struct Resources* resptr, Texture* t) { + return 0; +} + +i64 engine_render_text(i32 font_id, Engine_color fg, char* text, + v2_i32* size_out, bool wrapped) { + return 0; +} diff --git a/src/utils/btree.c b/src/utils/btree.c deleted file mode 100644 index c125564..0000000 --- a/src/utils/btree.c +++ /dev/null @@ -1,800 +0,0 @@ -#include - -#include -#include -#include -#include - -#include - -/* Definitions */ -typedef unsigned char byte; - -struct node { - ssize_t n; /* number of items/keys/elements */ - ssize_t c; /* number of children */ - byte* items; - struct node** children; -}; - -struct btree { - /* Memory stuffs */ - void* (*alloc)(size_t); - void (*dealloc)(void*); - - /* Size stuffs */ - size_t elem_size; - ssize_t degree; - - struct node* root; - - /* comparison */ - int (*cmp)(const void* a, const void* b); -}; - -struct btree_iter_t { - size_t head; - struct stack { - int pos; - struct node* node; - } stack[512]; - /* This heavily relies on the assumption that a tree never grows deeper than - * 512 nodes */ -}; - -/**********************/ -/* Node functionality */ -/**********************/ -#define node_leaf(node) (node->children == NULL) - -#define node_maxdegree(t) (2 * t - 1) - -#define node_mindegree(t) (t - 1) - -#define node_full(degree, t) (t->n >= 2 * degree - 1) - -/* Node memory */ - -/* `node_new` allocates a new leaf node, children should be added and allocated - * when the node is no longer a leaf node */ -struct node* node_new(const ssize_t degree, const size_t elem_size) { - const size_t max_items = 2 * degree; - struct node* retval = calloc(1, sizeof(struct node)); - - retval->n = 0; - retval->c = 0; - retval->items = calloc(max_items, elem_size); - retval->children = NULL; - - return retval; -} - -/* `node_transition` turns a leaf node into a non-leaf. Children are not - * allocated. - * returnvalue: `false` if we we're unable to allocate space for the new - * children. */ -bool node_transition(struct node* node, const ssize_t degree) { - const int max_children = 2 * degree + 1; - int c; - - if (!node_leaf(node)) { - perror("node_transition was called on an already non-leaf node?"); - return false; - } - - /* Allocate pointers for children */ - node->children = calloc(max_children, sizeof(struct node*)); - - if (node->children == NULL) { - perror("could not allocate space for children pointers"); - return false; - } - - /* Allocate children */ - for (c = 0; c < max_children; c++) { - node->children[c] = NULL; - } - - return true; -} - -void node_free(struct node** node, size_t elem_size, void (*dealloc)(void*)) { - if (*node == NULL) return; - - if (!node_leaf((*node))) { - ssize_t i; - for (i = 0; i < (*node)->c; i++) { - node_free(&((*node)->children[i]), elem_size, dealloc); - } - dealloc((*node)->children); - } - - dealloc((*node)->items); - (*node)->items = NULL; - - dealloc(*node); - *node = NULL; -} - -/* `node_tree_split_child` splits a _full_ node (c = 2t-1 items) into two nodes - * with t-1 items each. - * The median key/item/element moves up to the original nodes parent, to signify - * the split. If the parent is full too, we need to split it before inserting - * the median key. - * This can potentially split full nodes all the way up throughout the tree. */ -/* Instead of waiting to find out wether we should split the nodes, we split the - * full nodes we encounter on the way down, including the leafs themselves. - * By doing this, we are assured that whenever we split a node, its parent has - * room for the median key. */ -void node_tree_split_child(const ssize_t t, const size_t elem_size, - struct node* nonfull, ssize_t i) { - struct node* z = node_new(t, elem_size); - struct node* y = nonfull->children[i]; - ssize_t j; - - /* `z` should be a branching node if `y` is */ - if (!node_leaf(y)) { - node_transition(z, t); - } - - z->n = t - 1; - - /* Move last `t-1` items to new node `z` */ - /* TODO This can be done with one memcpy */ - for (j = 0; j < t - 1; j++) { - const size_t offset_dst = elem_size * j; - const size_t offset_src = elem_size * (t + j); - memcpy((z->items) + offset_dst, (y->items) + offset_src, elem_size); - } - /* Set unused item-memory to zero? */ - - /* Move children t..2t, if applicable*/ - if (!node_leaf(y)) { - for (j = 0; j < t + 1; j++) { - z->children[j] = y->children[j + t]; - } - y->c = t; - z->c = t; - } - - y->n = t - 1; - - /* Move children +1 */ - for (j = nonfull->n; j > i; j--) { - nonfull->children[j + 1] = nonfull->children[j]; - } - - /* new child */ - nonfull->children[i + 1] = z; - nonfull->c++; - - /* moving keys i..n + 1*/ - /* TODO This can be done with one memcpy */ - for (j = nonfull->n; j >= i; j--) { - const size_t offset = j * elem_size; - memcpy((nonfull->items) + offset + elem_size, (nonfull->items) + offset, - elem_size); - } - - /* Lastly, copy the median element to nonfull-parent*/ - memcpy((nonfull->items) + i * elem_size, (y->items) + (t - 1) * elem_size, - elem_size); - - nonfull->n++; -} - -/* `node_child_merge`: Merges two children around the key at index `i` (k) - * by appending k to the left child (y) followed by - * appending the right child (z) to y - * - * `x`: The parent node of y and z - * `i`: Index of the item that acts as the new median of the merged node - * - * WARNING: THIS FUNCTION ASSUMES THAT `i` IS A VALID INDEX - */ -void node_child_merge(struct node* x, ssize_t i, const size_t elem_size, - void (*dealloc)(void*)) { - struct node* y = x->children[i]; - struct node* z = x->children[i + 1]; - int j = 0; - - /* append k to y */ - memcpy(y->items + (elem_size * y->n++), x->items + (elem_size * i), - elem_size); - - /* append keys in z to y */ - memcpy(y->items + (elem_size * y->n), z->items, elem_size * z->n); - y->n += z->n; - - /* Move children from z to y */ - for (j = 0; j < z->c; j++) { - y->children[y->c + j] = z->children[j]; - } - y->c += z->c; - - /* Remove z from x */ - for (j = i + 1; j < x->c; j++) { - x->children[j] = x->children[j + 1]; - } - x->c--; - - /* remove k from x */ - /* TODO check if we need to use (x->n - 1 - i) instead */ - memmove(x->items + (elem_size * i), x->items + (elem_size * (i + 1)), - elem_size * (x->n - i)); - x->n--; - - dealloc(z); /* DO NOT USE THE RECURSIVE ONE AS CHILDREN WILL BE LOST!!! */ -} - -/* ASSUME i < x->c */ -void node_shift_left(struct node* x, ssize_t i, const size_t elem_size) { - struct node* y = x->children[i]; - struct node* z = x->children[i + 1]; - byte* x_k = x->items + (elem_size * i); - - /* Append x.k[i] to y */ - memcpy(y->items + (elem_size * y->n++), x_k, elem_size); - - /* Move first element of z to x.k[i] */ - memcpy(x_k, z->items, elem_size); - - /* Shift z's items left */ - memmove(z->items, z->items + elem_size, elem_size * (z->n - 1)); - - if (!node_leaf(z)) { - ssize_t j; - /* append first child of z to y */ - y->children[y->c++] = z->children[0]; - - /* Shift z's children left */ - for (j = 0; j < z->c; j++) { - z->children[j] = z->children[j + 1]; - } - z->c--; - } - - z->n--; -} - -void node_shift_right(struct node* x, ssize_t i, const size_t elem_size) { - struct node* y = x->children[i]; - struct node* z = x->children[i + 1]; - byte* x_k = x->items + (elem_size * i); - - /* Shift z's items right */ - memmove(z->items + elem_size, z->items, elem_size * z->n); - - /* Prepend x.k[i] to z */ - memcpy(z->items, x_k, elem_size); - - /* Move last element of y to x.k[i] */ - memcpy(x_k, y->items + (elem_size * --(y->n)), elem_size); - - if (!node_leaf(z)) { - size_t j; - /* Shift z's children right */ - for (j = z->c; j > 0; j--) { - z->children[j] = z->children[j - 1]; - } - z->c++; - - /* prepend last child of y to z */ - z->children[0] = y->children[--(y->c)]; - } - - z->n++; -} - -/* return: Returns the new root, if a split happens */ -void node_insert_nonfull(struct node* root, void* elem, const ssize_t degree, - const size_t elem_size, - int (*cmp)(const void* a, const void* b)) { - - /* TODO check correctness */ - ssize_t i = root->n - 1; - - if (node_leaf(root)) { - size_t offset = elem_size * i; - while (i >= 0 && cmp(elem, root->items + offset) < 0) { - /* TODO This can be done with one memcpy */ - memcpy(root->items + offset + elem_size, root->items + offset, elem_size); - - i--; - offset = elem_size * i; - } - offset = elem_size * (++i); - memcpy(root->items + offset, elem, elem_size); - root->n++; - - } else { - size_t offset = elem_size * i; - struct node* nextchild = NULL; - while (i >= 0 && cmp(elem, root->items + offset) < 0) { - i--; - offset = elem_size * i; - } - i++; - nextchild = root->children[i]; - if (node_full(degree, nextchild)) { - /* TODO Check if the root has changed */ - node_tree_split_child(degree, elem_size, root, i); - if (cmp(elem, root->items + elem_size * i) > 0) { - nextchild = root->children[++i]; - } - } - node_insert_nonfull(nextchild, elem, degree, elem_size, cmp); - } -} - -/* Returns the new root, if a split occurs */ -struct node* node_insert(struct node* root, void* elem, const ssize_t degree, - const size_t elem_size, - int (*cmp)(const void* a, const void* b)) { - - struct node* s = root; - - if (node_full(degree, root)) { - s = node_new(degree, elem_size); - if (s == NULL) { - fputs("BTree error: Failed to allocate new node for insertion!\n", - stderr); - return NULL; - } - node_transition(s, degree); - s->children[s->c++] = root; - /* TODO Check if the root has changed */ - node_tree_split_child(degree, elem_size, s, 0); - node_insert_nonfull(s, elem, degree, elem_size, cmp); - } else { - node_insert_nonfull(s, elem, degree, elem_size, cmp); - } - return s; -} - -void* node_search(struct node* x, void* key, - int (*cmp)(const void* a, const void* b), - const size_t elem_size) { - /* We set to one, since we pre-emptively do a comparison with the assumption - * that there's already one in the items */ - ssize_t i = 0; - int last_cmp_res = 0; - - while (i < x->n && - (last_cmp_res = cmp(key, (const void*)(x->items + (i * elem_size)))) > - 0) { - i++; - } - - if ((ssize_t)i < x->n && last_cmp_res == 0) { - return (void*)(x->items + (i * elem_size)); - } else if (node_leaf(x)) { - return NULL; - } - - /* Assumption: ¬node_leaf(x) → x.children is allocated */ - return node_search(x->children[i], key, cmp, elem_size); -} - -int node_delete(struct node* x, void* key, - int (*cmp)(const void* a, const void* b), const ssize_t degree, - const size_t elem_size, void* (*alloc)(size_t), - void (*dealloc)(void*)) { - /* Determine wether the key is in the node */ - int i = 0; /* Index of `k`, if found */ - int last_cmp_res = 0; - - while (i < x->n && - (last_cmp_res = cmp(key, (const void*)(x->items + (i * elem_size)))) > - 0) { - i++; - } - - if (last_cmp_res == 0) { - - if (node_leaf(x)) { - /* 1. k ϵ x && node_leaf(x) */ - /* Delete k from x */ - int j = i; - while (j + 1 < x->n) { - const size_t offset_dst = elem_size * j; - const size_t offset_src = elem_size * (j + 1); - memcpy((x->items) + offset_dst, (x->items) + offset_src, elem_size); - j++; - } - x->n--; - return 1; - } else { - /* 2. k ϵ x && !node_leaf(x) */ - /* let i be the index of k in x */ - /* 2a: if size(child[i]) >= t; find the largest k' in child[i] */ - /* replace k with k' */ - if (x->children[i]->n >= degree) { - struct node* y = x->children[i]; - byte* kk = alloc(elem_size); - - /* Find the predecessor, k' of k in y */ - { - struct node* tmp = y; - while (!node_leaf(tmp)) { - tmp = tmp->children[tmp->n - 1]; - } - - /* copy kk */ - memcpy(kk, tmp->items + elem_size * (tmp->n - 1), elem_size); - } - - /* Recursively delete kk from y */ - return node_delete(y, kk, cmp, degree, elem_size, alloc, dealloc); - - /* replace k with kk */ - memcpy(x->items + (elem_size * i), kk, elem_size); - - dealloc(kk); - - return 1; - - } else if (x->children[i + 1]->n >= degree) { - struct node* z = x->children[i + 1]; - byte* kk = alloc(elem_size); - - /* Find the successor, k' of k in z */ - { - struct node* tmp = z->children[0]; - while (!node_leaf(tmp)) { - tmp = tmp->children[0]; - } - - /* copy kk */ - memcpy(kk, tmp->items + elem_size * (tmp->n - 1), elem_size); - } - - /* Recursively delete kk from y */ - return node_delete(z, kk, cmp, degree, elem_size, alloc, dealloc); - - /* replace k with kk */ - memcpy(x->items + (elem_size * i), kk, elem_size); - - dealloc(kk); - - return 1; - } else { - /* Merge k and z into y */ - node_child_merge(x, i, elem_size, dealloc); - - /* recurse */ - return node_delete(x->children[i], key, cmp, degree, elem_size, alloc, - dealloc); - } - } - } else if (node_leaf(x)) { - return 0; - } else { - /* 3. !(k ϵ x) */ - - /* if x is a leaf, then it is not in the tree */ - - struct node* y; - int ii; /* index of x.c[i] */ - - /* Determine x.c[i] that must contain k */ - /* if last cmp < 0, then the child must be in the left child of x.items[i]*/ - if (last_cmp_res < 0) ii = i; - /* Otherwise, it must be the very last child */ - else if (i < x->n) - ii = i + 1; - else - ii = i; - - y = x->children[ii]; - - if (y->n < degree) { - /* we are left biased */ - if (ii > 0 && x->children[ii - 1]->n >= degree) { - node_shift_right(x, ii - 1, elem_size); - - } else if (ii < x->c - 1 && x->children[ii + 1]->n >= degree) { - node_shift_left(x, ii, elem_size); - - } else { - /* We need to determine wether we merge left or right, if possible */ - if (ii > 0) { - node_child_merge(x, ii - 1, elem_size, dealloc); - y = x->children[ii - 1]; - } else if (ii < x->c - 1) { - node_child_merge(x, ii, elem_size, dealloc); - } else { - perror("Cannot merge!"); - } - } - } - - return node_delete(y, key, cmp, degree, elem_size, alloc, dealloc); - } - return 0; -} - -/***********************/ -/* Btree functionality */ -/***********************/ -struct btree* btree_new(size_t elem_size, size_t t, - int (*cmp)(const void* a, const void* b)) { - return btree_new_with_allocator(elem_size, t, cmp, malloc, free); -} - -struct btree* btree_new_with_allocator(size_t elem_size, size_t t, - int (*cmp)(const void* a, const void* b), - void* (*alloc)(size_t), - void (*dealloc)(void*)) { - struct btree* new_tree = alloc(sizeof(struct btree)); - - new_tree->alloc = alloc; - new_tree->dealloc = dealloc; - - new_tree->elem_size = elem_size; - new_tree->degree = t; - - new_tree->root = NULL; - - new_tree->cmp = cmp; - - return new_tree; -} - -void btree_free(struct btree** btree) { - node_free(&((*btree)->root), (*btree)->elem_size, (*btree)->dealloc); - (*btree)->dealloc(*btree); - *btree = NULL; -} - -void btree_insert(struct btree* btree, void* elem) { - if (btree == NULL) { - fputs("BTree error: Inserting into a NULL ptr!\n", stderr); - return; - } - if (elem == NULL) { - fputs("BTree error: Inserting NULL into a tree!\n", stderr); - return; - } - if (btree->root == NULL) { - btree->root = node_new(btree->degree, btree->elem_size); - if (btree->root == NULL) { - fputs("BTree error: Failed to create new root node!\n", stderr); - return; - } - node_insert(btree->root, elem, btree->degree, btree->elem_size, btree->cmp); - } else { - btree->root = node_insert(btree->root, elem, btree->degree, - btree->elem_size, btree->cmp); - } -} - -void* btree_search(struct btree* btree, void* elem) { - return node_search(btree->root, elem, btree->cmp, btree->elem_size); -} - -int btree_delete(struct btree* btree, void* elem) { - struct node* newroot = btree->root; - int res = node_delete(btree->root, elem, btree->cmp, btree->degree, - btree->elem_size, btree->alloc, btree->dealloc); - if (newroot->n == 0) { - if (node_leaf(newroot)) return res; - /* shrink the tree */ - struct node* newroot_p = newroot->children[0]; - btree->dealloc(newroot); - btree->root = newroot_p; - } - return res; -} - -void node_print(struct node* root, const size_t elem_size, const int indent, - void (*print_elem)(const void*)) { - ssize_t i; - int t; - - for (t = 0; t < indent - 1; t++) { - fputs(" ┃ ", stdout); - } - if (indent > 0) { - fputs(" ┣┯", stdout); - } - printf("printing node \x1b[33m%0lx\x1b[0m," - " c:%ld n:%ld\t\t" - "\x1b[30m%p\x1b[0m\n", - (unsigned long)((size_t)root % 0x10000), root->c, root->n, - (void*)root); - - if (node_leaf(root)) { - for (i = 0; i < root->n - 1; i++) { - const size_t ofst = i * elem_size; - for (t = 0; t < indent; t++) { - fputs(" ┃├", stdout); - } - print_elem(root->items + ofst); - } - for (t = 0; t < indent; t++) { - fputs(" ┃└", stdout); - } - print_elem(root->items + i * elem_size); - } else { - size_t ofst = 0; - for (i = 0; i < root->c - 1; i++) { - node_print(root->children[i], elem_size, indent + 1, print_elem); - for (t = 0; t < indent; t++) { - fputs(" ┃ ", stdout); - } - print_elem(root->items + ofst); - ofst += elem_size; - } - node_print(root->children[i], elem_size, indent + 1, print_elem); - } -} - -void btree_print(struct btree* btree, void (*print_elem)(const void*)) { - printf("BTRee: degree:%ld\n", btree->degree); - if (btree->root == NULL) return; - node_print(btree->root, btree->elem_size, 0, print_elem); -} - -void* btree_first(struct btree* btree) { - struct node* root; - if (btree == NULL) return NULL; - root = btree->root; - - if (root == NULL) return NULL; - - while (!node_leaf(root)) root = root->children[0]; - - if (root->n == 0) return NULL; - return root->items; /* Return first element */ -} - -void* btree_last(struct btree* btree) { - struct node* root; - - if (btree == NULL) return NULL; - root = btree->root; - - if (root == NULL) return NULL; - - while (!node_leaf(root)) root = root->children[root->c]; - - if (root->n == 0) return NULL; - return root->items + - btree->elem_size * (root->n - 1); /* Return first element */ -} - -size_t btree_height(struct btree* btree) { - struct node* root; - size_t height = 0; - - if (btree == NULL) return 0; - root = btree->root; - - if (root == NULL) return 0; - - while (!node_leaf(root)) { - root = root->children[0]; - height++; - } - - return height; -} - -size_t u32_pow(size_t base, size_t exponent) { - size_t res = 1; - while (exponent > 0) { - res *= base; - exponent--; - } - return res; -} - -size_t btree_size(struct btree* btree) { - return u32_pow(2 * btree->degree, btree_height(btree)) - 1; -} - -struct btree_iter_t* btree_iter_t_new(struct btree* tree) { - struct btree_iter_t* iter = NULL; - - if (tree == NULL) return NULL; - - iter = (struct btree_iter_t*)tree->alloc(sizeof(struct btree_iter_t)); - - if (tree != NULL) { - iter->head = 0; - memset(iter->stack, 0, 512 * sizeof(struct node*)); - - iter->stack[iter->head].pos = 0; - iter->stack[iter->head].node = tree->root; - } else { - perror("Cannot instantiate iterator from null-pointer tree"); - } - return iter; -} - -void btree_iter_t_reset(struct btree* tree, struct btree_iter_t** it) { - (*it)->head = 0; - - (*it)->stack[0].pos = 0; - (*it)->stack[0].node = tree->root; -} - -void* btree_iter(struct btree* tree, struct btree_iter_t* iter) { - register int pos = 0; - register ssize_t head = 0; - register ssize_t n = 0; - - if (iter->stack[head].node == NULL) return NULL; - - head = iter->head; - pos = iter->stack[head].pos; - n = iter->stack[head].node->n; - -#define BTREE_ITER_POP(it) \ - { \ - iter->stack[head].pos = 0; \ - iter->stack[head].node = NULL; \ - iter->head--; \ - head--; \ - iter->stack[head].pos++; \ - \ - pos = iter->stack[head].pos; \ - n = iter->stack[head].node->n; \ - } - - /* Check if we have reached the end of a node. - * Take note of the difference of inequality in the if statement and - * following while */ - - /* Leafs are a special case, as, if the only node is the root node, we might - * want to exit */ - if (node_leaf(iter->stack[iter->head].node) && pos >= 2 * n) { - if (head == 0) return NULL; - - /* Pop, if so */ - else - BTREE_ITER_POP(iter); - } - - /* Otherwise, pop while we have reached the end of a node */ - while (pos > 2 * n) { - if (head == 0) return NULL; - - /* Pop, if so */ - else - BTREE_ITER_POP(iter); - } - -#undef BTREE_ITER_POP - - /* On evens, we decent into children */ - if (!node_leaf(iter->stack[head].node)) { - if (pos % 2 == 0) { - /* push child node onto iter->stack */ - iter->stack[head + 1].pos = 0; - iter->stack[head + 1].node = iter->stack[head].node->children[pos / 2]; - iter->head++; - head++; - - /* Decent all the way to the left, if pos == 0 */ - while (!node_leaf(iter->stack[iter->head].node)) { - iter->stack[head + 1].pos = 0; - iter->stack[head + 1].node = iter->stack[head].node->children[0]; - iter->head++; - head++; - } - } - } - - /* Finally, update index and return a value */ - if (node_leaf(iter->stack[head].node)) { - iter->stack[head].pos += 2; - pos = iter->stack[head].pos; - } else { - iter->stack[head].pos++; - pos = iter->stack[head].pos; - } - - return iter->stack[head].node->items + tree->elem_size * ((pos - 1) / 2); -} diff --git a/src/utils/fov.c b/src/utils/fov.c deleted file mode 100644 index 3d5ae16..0000000 --- a/src/utils/fov.c +++ /dev/null @@ -1,94 +0,0 @@ -#include -#include -#include -#include - -void fov_shadowcast_rec(const void* map, const v2_i32 mapsize, - bool (*visblocking)(const void*), i32* lightmap, - const i32 range, v2_i32 src, const i32 row, f32 start, - const f32 end, const i8 xx, const i8 xy, const i8 yx, - const i8 yy) { - - if (start < end) return; - - const i32 range_2 = range * range; - f32 new_start = start; - - for (i32 i = row; i <= range; i++) { - i32 dx = (-1 * i) - 1; - i32 dy = -1 * i; - - bool blocked = false; - - while (dx <= 0) { - dx += 1; - - const i32 mapx = src.x + dx * xx + dy * xy; - const i32 mapy = src.y + dx * yx + dy * yy; - - const f32 slope_l = (((f32)dx) - 0.5f) / (((f32)dy) + 0.5f); - const f32 slope_r = (((f32)dx) + 0.5f) / (((f32)dy) - 0.5f); - - if (start < slope_r) continue; - if (end > slope_l) break; - - if (dx * dx + dy * dy < range_2) { - /* set as visible */ - if (mapx >= 0 && mapx < (long)mapsize.x && mapy >= 0 && - mapy < (long)mapsize.y) { - // TODO: Calculate proper dist from source - f32 x_2 = (src.x - mapx) * (src.x - mapx); - f32 y_2 = (src.y - mapy) * (src.y - mapy); - lightmap[mapy * mapsize.x + mapx] = - MAX(lightmap[mapy * mapsize.x + mapx], - range - sqrt((f32)(x_2 + y_2))); - } - } - - /* sizeof(i32) is the size of enums */ - /* -- unless the compiler doesn't follow standard behaviour */ - const bool is_blocked = visblocking( - (void*)((u64)map + sizeof(i32) /* ~ enum size */ - * (mapsize.x * mapy + mapx) /* index */ - )); - - if (blocked) { - if (!is_blocked) { - new_start = slope_r; - } else { - blocked = false; - start = new_start; - } - } else if (!is_blocked && i < range) { - blocked = true; - fov_shadowcast_rec(map, mapsize, visblocking, lightmap, range, src, - i + 1, start, slope_l, xx, xy, yx, yy); - new_start = slope_r; - } - } - - if (blocked) break; - } -} - -/* http://www.roguebasin.com/index.php?title=FOV_using_recursive_shadowcasting - */ -void fov_shadowcast(const void* map, const v2_i32 mapsize, - bool (*visblocking)(const void*), i32* lightmap, - const i32 range, const v2_i32 src) { - - const i8 m[4][8] = { - {1, 0, 0, -1, -1, 0, 0, 1}, - {0, 1, -1, 0, 0, -1, 1, 0}, - {0, 1, 1, 0, 0, -1, -1, 0}, - {1, 0, 0, 1, -1, 0, 0, -1}, - }; - - for (i32 oct = 0; oct < 8; oct++) { - fov_shadowcast_rec(map, mapsize, visblocking, lightmap, range, src, 1, 1.0, - 0.0, m[0][oct], m[1][oct], m[2][oct], m[3][oct]); - } - - /* The center is the most lit square */ - lightmap[src.y * mapsize.x + src.x] = range; -} diff --git a/src/utils/hashmap.c b/src/utils/hashmap.c deleted file mode 100644 index 1652bb6..0000000 --- a/src/utils/hashmap.c +++ /dev/null @@ -1,3 +0,0 @@ -#include - -i32 lolhash(const usize s, i32 v) { return v % s; } diff --git a/src/utils/misc.c b/src/utils/misc.c deleted file mode 100644 index 0f3d218..0000000 --- a/src/utils/misc.c +++ /dev/null @@ -1,86 +0,0 @@ -#include -#include - -#include - -#include -#include - -/* These should all be in some external facing module "tools" */ - -f32 lerp(f32 dt, f32 a, f32 b) { return (a * (1.0f - dt)) + (b * dt); } - -i32 int_lerp(f32 dt, i32 a, i32 b) { - return ((f32)a * (1.0f - dt)) + ((f32)b * dt); -} - -u32 hash(char* str) { - u32 sum = 0; - while (*str != '\0') { - sum ^= (*str) * 0xdeece66d + 0xb; - str++; - } - return sum; -} - -/* Populates dstmap - * on success: return pointer to dstmap - * on failure: return NULL */ -i32* kernmap(const void* map, i32* dstmap, const v2_i32 mapsize, - predicate_t* predicate) { - const i32 w = mapsize.x; - const i32 h = mapsize.y; - i32 mask[w * h]; - - if (w * h < 1) return NULL; - - for (i32 i = 0; i < w * h; i++) { - mask[i] = predicate((void*)((u64)map + sizeof(i32) * i)) ? 1 : 0; - } - - for (i32 y = 1; y < h - 1; y++) { - for (i32 x = 1; x < w - 1; x++) { - const i32 global_idx = (y * w) + x; - const i32 offs = global_idx - w - 1; - i32 _sum = 0; - - i32 shift = 0; - - /* We go in the following order */ - /* ....|0|1|2|....*/ - /* ....|3|4|5|....*/ - /* ....|6|7|8|....*/ - /* Where `4` is in the center, MASK_C */ - for (i32 yy = offs; yy <= offs + w + w; yy += w) { - for (i32 xx = yy; xx < yy + 3; xx++) { - _sum = _sum | (mask[xx] << shift++); - } - } - - dstmap[global_idx] = _sum; - } - } - return dstmap; -} - -/* Returns an index from the given weights. */ -i32 pick_from_sample(const i32* weights, i32 len) { - if (len <= 0) return 0; - - /* Cumulative sum */ - i32 cumweights[len]; - i32 sum = 0; - for (i32 i = 0; i < len; i++) { - sum += weights[i]; - cumweights[i] = sum; - } - - if (sum == 0) return 0; - - i32 pick = rand() % sum; - - for (i32 i = 0; i < len; i++) { - if (pick < cumweights[i]) return i; - } - return -1; -} diff --git a/src/utils/src/btree.c b/src/utils/src/btree.c new file mode 100644 index 0000000..c125564 --- /dev/null +++ b/src/utils/src/btree.c @@ -0,0 +1,800 @@ +#include + +#include +#include +#include +#include + +#include + +/* Definitions */ +typedef unsigned char byte; + +struct node { + ssize_t n; /* number of items/keys/elements */ + ssize_t c; /* number of children */ + byte* items; + struct node** children; +}; + +struct btree { + /* Memory stuffs */ + void* (*alloc)(size_t); + void (*dealloc)(void*); + + /* Size stuffs */ + size_t elem_size; + ssize_t degree; + + struct node* root; + + /* comparison */ + int (*cmp)(const void* a, const void* b); +}; + +struct btree_iter_t { + size_t head; + struct stack { + int pos; + struct node* node; + } stack[512]; + /* This heavily relies on the assumption that a tree never grows deeper than + * 512 nodes */ +}; + +/**********************/ +/* Node functionality */ +/**********************/ +#define node_leaf(node) (node->children == NULL) + +#define node_maxdegree(t) (2 * t - 1) + +#define node_mindegree(t) (t - 1) + +#define node_full(degree, t) (t->n >= 2 * degree - 1) + +/* Node memory */ + +/* `node_new` allocates a new leaf node, children should be added and allocated + * when the node is no longer a leaf node */ +struct node* node_new(const ssize_t degree, const size_t elem_size) { + const size_t max_items = 2 * degree; + struct node* retval = calloc(1, sizeof(struct node)); + + retval->n = 0; + retval->c = 0; + retval->items = calloc(max_items, elem_size); + retval->children = NULL; + + return retval; +} + +/* `node_transition` turns a leaf node into a non-leaf. Children are not + * allocated. + * returnvalue: `false` if we we're unable to allocate space for the new + * children. */ +bool node_transition(struct node* node, const ssize_t degree) { + const int max_children = 2 * degree + 1; + int c; + + if (!node_leaf(node)) { + perror("node_transition was called on an already non-leaf node?"); + return false; + } + + /* Allocate pointers for children */ + node->children = calloc(max_children, sizeof(struct node*)); + + if (node->children == NULL) { + perror("could not allocate space for children pointers"); + return false; + } + + /* Allocate children */ + for (c = 0; c < max_children; c++) { + node->children[c] = NULL; + } + + return true; +} + +void node_free(struct node** node, size_t elem_size, void (*dealloc)(void*)) { + if (*node == NULL) return; + + if (!node_leaf((*node))) { + ssize_t i; + for (i = 0; i < (*node)->c; i++) { + node_free(&((*node)->children[i]), elem_size, dealloc); + } + dealloc((*node)->children); + } + + dealloc((*node)->items); + (*node)->items = NULL; + + dealloc(*node); + *node = NULL; +} + +/* `node_tree_split_child` splits a _full_ node (c = 2t-1 items) into two nodes + * with t-1 items each. + * The median key/item/element moves up to the original nodes parent, to signify + * the split. If the parent is full too, we need to split it before inserting + * the median key. + * This can potentially split full nodes all the way up throughout the tree. */ +/* Instead of waiting to find out wether we should split the nodes, we split the + * full nodes we encounter on the way down, including the leafs themselves. + * By doing this, we are assured that whenever we split a node, its parent has + * room for the median key. */ +void node_tree_split_child(const ssize_t t, const size_t elem_size, + struct node* nonfull, ssize_t i) { + struct node* z = node_new(t, elem_size); + struct node* y = nonfull->children[i]; + ssize_t j; + + /* `z` should be a branching node if `y` is */ + if (!node_leaf(y)) { + node_transition(z, t); + } + + z->n = t - 1; + + /* Move last `t-1` items to new node `z` */ + /* TODO This can be done with one memcpy */ + for (j = 0; j < t - 1; j++) { + const size_t offset_dst = elem_size * j; + const size_t offset_src = elem_size * (t + j); + memcpy((z->items) + offset_dst, (y->items) + offset_src, elem_size); + } + /* Set unused item-memory to zero? */ + + /* Move children t..2t, if applicable*/ + if (!node_leaf(y)) { + for (j = 0; j < t + 1; j++) { + z->children[j] = y->children[j + t]; + } + y->c = t; + z->c = t; + } + + y->n = t - 1; + + /* Move children +1 */ + for (j = nonfull->n; j > i; j--) { + nonfull->children[j + 1] = nonfull->children[j]; + } + + /* new child */ + nonfull->children[i + 1] = z; + nonfull->c++; + + /* moving keys i..n + 1*/ + /* TODO This can be done with one memcpy */ + for (j = nonfull->n; j >= i; j--) { + const size_t offset = j * elem_size; + memcpy((nonfull->items) + offset + elem_size, (nonfull->items) + offset, + elem_size); + } + + /* Lastly, copy the median element to nonfull-parent*/ + memcpy((nonfull->items) + i * elem_size, (y->items) + (t - 1) * elem_size, + elem_size); + + nonfull->n++; +} + +/* `node_child_merge`: Merges two children around the key at index `i` (k) + * by appending k to the left child (y) followed by + * appending the right child (z) to y + * + * `x`: The parent node of y and z + * `i`: Index of the item that acts as the new median of the merged node + * + * WARNING: THIS FUNCTION ASSUMES THAT `i` IS A VALID INDEX + */ +void node_child_merge(struct node* x, ssize_t i, const size_t elem_size, + void (*dealloc)(void*)) { + struct node* y = x->children[i]; + struct node* z = x->children[i + 1]; + int j = 0; + + /* append k to y */ + memcpy(y->items + (elem_size * y->n++), x->items + (elem_size * i), + elem_size); + + /* append keys in z to y */ + memcpy(y->items + (elem_size * y->n), z->items, elem_size * z->n); + y->n += z->n; + + /* Move children from z to y */ + for (j = 0; j < z->c; j++) { + y->children[y->c + j] = z->children[j]; + } + y->c += z->c; + + /* Remove z from x */ + for (j = i + 1; j < x->c; j++) { + x->children[j] = x->children[j + 1]; + } + x->c--; + + /* remove k from x */ + /* TODO check if we need to use (x->n - 1 - i) instead */ + memmove(x->items + (elem_size * i), x->items + (elem_size * (i + 1)), + elem_size * (x->n - i)); + x->n--; + + dealloc(z); /* DO NOT USE THE RECURSIVE ONE AS CHILDREN WILL BE LOST!!! */ +} + +/* ASSUME i < x->c */ +void node_shift_left(struct node* x, ssize_t i, const size_t elem_size) { + struct node* y = x->children[i]; + struct node* z = x->children[i + 1]; + byte* x_k = x->items + (elem_size * i); + + /* Append x.k[i] to y */ + memcpy(y->items + (elem_size * y->n++), x_k, elem_size); + + /* Move first element of z to x.k[i] */ + memcpy(x_k, z->items, elem_size); + + /* Shift z's items left */ + memmove(z->items, z->items + elem_size, elem_size * (z->n - 1)); + + if (!node_leaf(z)) { + ssize_t j; + /* append first child of z to y */ + y->children[y->c++] = z->children[0]; + + /* Shift z's children left */ + for (j = 0; j < z->c; j++) { + z->children[j] = z->children[j + 1]; + } + z->c--; + } + + z->n--; +} + +void node_shift_right(struct node* x, ssize_t i, const size_t elem_size) { + struct node* y = x->children[i]; + struct node* z = x->children[i + 1]; + byte* x_k = x->items + (elem_size * i); + + /* Shift z's items right */ + memmove(z->items + elem_size, z->items, elem_size * z->n); + + /* Prepend x.k[i] to z */ + memcpy(z->items, x_k, elem_size); + + /* Move last element of y to x.k[i] */ + memcpy(x_k, y->items + (elem_size * --(y->n)), elem_size); + + if (!node_leaf(z)) { + size_t j; + /* Shift z's children right */ + for (j = z->c; j > 0; j--) { + z->children[j] = z->children[j - 1]; + } + z->c++; + + /* prepend last child of y to z */ + z->children[0] = y->children[--(y->c)]; + } + + z->n++; +} + +/* return: Returns the new root, if a split happens */ +void node_insert_nonfull(struct node* root, void* elem, const ssize_t degree, + const size_t elem_size, + int (*cmp)(const void* a, const void* b)) { + + /* TODO check correctness */ + ssize_t i = root->n - 1; + + if (node_leaf(root)) { + size_t offset = elem_size * i; + while (i >= 0 && cmp(elem, root->items + offset) < 0) { + /* TODO This can be done with one memcpy */ + memcpy(root->items + offset + elem_size, root->items + offset, elem_size); + + i--; + offset = elem_size * i; + } + offset = elem_size * (++i); + memcpy(root->items + offset, elem, elem_size); + root->n++; + + } else { + size_t offset = elem_size * i; + struct node* nextchild = NULL; + while (i >= 0 && cmp(elem, root->items + offset) < 0) { + i--; + offset = elem_size * i; + } + i++; + nextchild = root->children[i]; + if (node_full(degree, nextchild)) { + /* TODO Check if the root has changed */ + node_tree_split_child(degree, elem_size, root, i); + if (cmp(elem, root->items + elem_size * i) > 0) { + nextchild = root->children[++i]; + } + } + node_insert_nonfull(nextchild, elem, degree, elem_size, cmp); + } +} + +/* Returns the new root, if a split occurs */ +struct node* node_insert(struct node* root, void* elem, const ssize_t degree, + const size_t elem_size, + int (*cmp)(const void* a, const void* b)) { + + struct node* s = root; + + if (node_full(degree, root)) { + s = node_new(degree, elem_size); + if (s == NULL) { + fputs("BTree error: Failed to allocate new node for insertion!\n", + stderr); + return NULL; + } + node_transition(s, degree); + s->children[s->c++] = root; + /* TODO Check if the root has changed */ + node_tree_split_child(degree, elem_size, s, 0); + node_insert_nonfull(s, elem, degree, elem_size, cmp); + } else { + node_insert_nonfull(s, elem, degree, elem_size, cmp); + } + return s; +} + +void* node_search(struct node* x, void* key, + int (*cmp)(const void* a, const void* b), + const size_t elem_size) { + /* We set to one, since we pre-emptively do a comparison with the assumption + * that there's already one in the items */ + ssize_t i = 0; + int last_cmp_res = 0; + + while (i < x->n && + (last_cmp_res = cmp(key, (const void*)(x->items + (i * elem_size)))) > + 0) { + i++; + } + + if ((ssize_t)i < x->n && last_cmp_res == 0) { + return (void*)(x->items + (i * elem_size)); + } else if (node_leaf(x)) { + return NULL; + } + + /* Assumption: ¬node_leaf(x) → x.children is allocated */ + return node_search(x->children[i], key, cmp, elem_size); +} + +int node_delete(struct node* x, void* key, + int (*cmp)(const void* a, const void* b), const ssize_t degree, + const size_t elem_size, void* (*alloc)(size_t), + void (*dealloc)(void*)) { + /* Determine wether the key is in the node */ + int i = 0; /* Index of `k`, if found */ + int last_cmp_res = 0; + + while (i < x->n && + (last_cmp_res = cmp(key, (const void*)(x->items + (i * elem_size)))) > + 0) { + i++; + } + + if (last_cmp_res == 0) { + + if (node_leaf(x)) { + /* 1. k ϵ x && node_leaf(x) */ + /* Delete k from x */ + int j = i; + while (j + 1 < x->n) { + const size_t offset_dst = elem_size * j; + const size_t offset_src = elem_size * (j + 1); + memcpy((x->items) + offset_dst, (x->items) + offset_src, elem_size); + j++; + } + x->n--; + return 1; + } else { + /* 2. k ϵ x && !node_leaf(x) */ + /* let i be the index of k in x */ + /* 2a: if size(child[i]) >= t; find the largest k' in child[i] */ + /* replace k with k' */ + if (x->children[i]->n >= degree) { + struct node* y = x->children[i]; + byte* kk = alloc(elem_size); + + /* Find the predecessor, k' of k in y */ + { + struct node* tmp = y; + while (!node_leaf(tmp)) { + tmp = tmp->children[tmp->n - 1]; + } + + /* copy kk */ + memcpy(kk, tmp->items + elem_size * (tmp->n - 1), elem_size); + } + + /* Recursively delete kk from y */ + return node_delete(y, kk, cmp, degree, elem_size, alloc, dealloc); + + /* replace k with kk */ + memcpy(x->items + (elem_size * i), kk, elem_size); + + dealloc(kk); + + return 1; + + } else if (x->children[i + 1]->n >= degree) { + struct node* z = x->children[i + 1]; + byte* kk = alloc(elem_size); + + /* Find the successor, k' of k in z */ + { + struct node* tmp = z->children[0]; + while (!node_leaf(tmp)) { + tmp = tmp->children[0]; + } + + /* copy kk */ + memcpy(kk, tmp->items + elem_size * (tmp->n - 1), elem_size); + } + + /* Recursively delete kk from y */ + return node_delete(z, kk, cmp, degree, elem_size, alloc, dealloc); + + /* replace k with kk */ + memcpy(x->items + (elem_size * i), kk, elem_size); + + dealloc(kk); + + return 1; + } else { + /* Merge k and z into y */ + node_child_merge(x, i, elem_size, dealloc); + + /* recurse */ + return node_delete(x->children[i], key, cmp, degree, elem_size, alloc, + dealloc); + } + } + } else if (node_leaf(x)) { + return 0; + } else { + /* 3. !(k ϵ x) */ + + /* if x is a leaf, then it is not in the tree */ + + struct node* y; + int ii; /* index of x.c[i] */ + + /* Determine x.c[i] that must contain k */ + /* if last cmp < 0, then the child must be in the left child of x.items[i]*/ + if (last_cmp_res < 0) ii = i; + /* Otherwise, it must be the very last child */ + else if (i < x->n) + ii = i + 1; + else + ii = i; + + y = x->children[ii]; + + if (y->n < degree) { + /* we are left biased */ + if (ii > 0 && x->children[ii - 1]->n >= degree) { + node_shift_right(x, ii - 1, elem_size); + + } else if (ii < x->c - 1 && x->children[ii + 1]->n >= degree) { + node_shift_left(x, ii, elem_size); + + } else { + /* We need to determine wether we merge left or right, if possible */ + if (ii > 0) { + node_child_merge(x, ii - 1, elem_size, dealloc); + y = x->children[ii - 1]; + } else if (ii < x->c - 1) { + node_child_merge(x, ii, elem_size, dealloc); + } else { + perror("Cannot merge!"); + } + } + } + + return node_delete(y, key, cmp, degree, elem_size, alloc, dealloc); + } + return 0; +} + +/***********************/ +/* Btree functionality */ +/***********************/ +struct btree* btree_new(size_t elem_size, size_t t, + int (*cmp)(const void* a, const void* b)) { + return btree_new_with_allocator(elem_size, t, cmp, malloc, free); +} + +struct btree* btree_new_with_allocator(size_t elem_size, size_t t, + int (*cmp)(const void* a, const void* b), + void* (*alloc)(size_t), + void (*dealloc)(void*)) { + struct btree* new_tree = alloc(sizeof(struct btree)); + + new_tree->alloc = alloc; + new_tree->dealloc = dealloc; + + new_tree->elem_size = elem_size; + new_tree->degree = t; + + new_tree->root = NULL; + + new_tree->cmp = cmp; + + return new_tree; +} + +void btree_free(struct btree** btree) { + node_free(&((*btree)->root), (*btree)->elem_size, (*btree)->dealloc); + (*btree)->dealloc(*btree); + *btree = NULL; +} + +void btree_insert(struct btree* btree, void* elem) { + if (btree == NULL) { + fputs("BTree error: Inserting into a NULL ptr!\n", stderr); + return; + } + if (elem == NULL) { + fputs("BTree error: Inserting NULL into a tree!\n", stderr); + return; + } + if (btree->root == NULL) { + btree->root = node_new(btree->degree, btree->elem_size); + if (btree->root == NULL) { + fputs("BTree error: Failed to create new root node!\n", stderr); + return; + } + node_insert(btree->root, elem, btree->degree, btree->elem_size, btree->cmp); + } else { + btree->root = node_insert(btree->root, elem, btree->degree, + btree->elem_size, btree->cmp); + } +} + +void* btree_search(struct btree* btree, void* elem) { + return node_search(btree->root, elem, btree->cmp, btree->elem_size); +} + +int btree_delete(struct btree* btree, void* elem) { + struct node* newroot = btree->root; + int res = node_delete(btree->root, elem, btree->cmp, btree->degree, + btree->elem_size, btree->alloc, btree->dealloc); + if (newroot->n == 0) { + if (node_leaf(newroot)) return res; + /* shrink the tree */ + struct node* newroot_p = newroot->children[0]; + btree->dealloc(newroot); + btree->root = newroot_p; + } + return res; +} + +void node_print(struct node* root, const size_t elem_size, const int indent, + void (*print_elem)(const void*)) { + ssize_t i; + int t; + + for (t = 0; t < indent - 1; t++) { + fputs(" ┃ ", stdout); + } + if (indent > 0) { + fputs(" ┣┯", stdout); + } + printf("printing node \x1b[33m%0lx\x1b[0m," + " c:%ld n:%ld\t\t" + "\x1b[30m%p\x1b[0m\n", + (unsigned long)((size_t)root % 0x10000), root->c, root->n, + (void*)root); + + if (node_leaf(root)) { + for (i = 0; i < root->n - 1; i++) { + const size_t ofst = i * elem_size; + for (t = 0; t < indent; t++) { + fputs(" ┃├", stdout); + } + print_elem(root->items + ofst); + } + for (t = 0; t < indent; t++) { + fputs(" ┃└", stdout); + } + print_elem(root->items + i * elem_size); + } else { + size_t ofst = 0; + for (i = 0; i < root->c - 1; i++) { + node_print(root->children[i], elem_size, indent + 1, print_elem); + for (t = 0; t < indent; t++) { + fputs(" ┃ ", stdout); + } + print_elem(root->items + ofst); + ofst += elem_size; + } + node_print(root->children[i], elem_size, indent + 1, print_elem); + } +} + +void btree_print(struct btree* btree, void (*print_elem)(const void*)) { + printf("BTRee: degree:%ld\n", btree->degree); + if (btree->root == NULL) return; + node_print(btree->root, btree->elem_size, 0, print_elem); +} + +void* btree_first(struct btree* btree) { + struct node* root; + if (btree == NULL) return NULL; + root = btree->root; + + if (root == NULL) return NULL; + + while (!node_leaf(root)) root = root->children[0]; + + if (root->n == 0) return NULL; + return root->items; /* Return first element */ +} + +void* btree_last(struct btree* btree) { + struct node* root; + + if (btree == NULL) return NULL; + root = btree->root; + + if (root == NULL) return NULL; + + while (!node_leaf(root)) root = root->children[root->c]; + + if (root->n == 0) return NULL; + return root->items + + btree->elem_size * (root->n - 1); /* Return first element */ +} + +size_t btree_height(struct btree* btree) { + struct node* root; + size_t height = 0; + + if (btree == NULL) return 0; + root = btree->root; + + if (root == NULL) return 0; + + while (!node_leaf(root)) { + root = root->children[0]; + height++; + } + + return height; +} + +size_t u32_pow(size_t base, size_t exponent) { + size_t res = 1; + while (exponent > 0) { + res *= base; + exponent--; + } + return res; +} + +size_t btree_size(struct btree* btree) { + return u32_pow(2 * btree->degree, btree_height(btree)) - 1; +} + +struct btree_iter_t* btree_iter_t_new(struct btree* tree) { + struct btree_iter_t* iter = NULL; + + if (tree == NULL) return NULL; + + iter = (struct btree_iter_t*)tree->alloc(sizeof(struct btree_iter_t)); + + if (tree != NULL) { + iter->head = 0; + memset(iter->stack, 0, 512 * sizeof(struct node*)); + + iter->stack[iter->head].pos = 0; + iter->stack[iter->head].node = tree->root; + } else { + perror("Cannot instantiate iterator from null-pointer tree"); + } + return iter; +} + +void btree_iter_t_reset(struct btree* tree, struct btree_iter_t** it) { + (*it)->head = 0; + + (*it)->stack[0].pos = 0; + (*it)->stack[0].node = tree->root; +} + +void* btree_iter(struct btree* tree, struct btree_iter_t* iter) { + register int pos = 0; + register ssize_t head = 0; + register ssize_t n = 0; + + if (iter->stack[head].node == NULL) return NULL; + + head = iter->head; + pos = iter->stack[head].pos; + n = iter->stack[head].node->n; + +#define BTREE_ITER_POP(it) \ + { \ + iter->stack[head].pos = 0; \ + iter->stack[head].node = NULL; \ + iter->head--; \ + head--; \ + iter->stack[head].pos++; \ + \ + pos = iter->stack[head].pos; \ + n = iter->stack[head].node->n; \ + } + + /* Check if we have reached the end of a node. + * Take note of the difference of inequality in the if statement and + * following while */ + + /* Leafs are a special case, as, if the only node is the root node, we might + * want to exit */ + if (node_leaf(iter->stack[iter->head].node) && pos >= 2 * n) { + if (head == 0) return NULL; + + /* Pop, if so */ + else + BTREE_ITER_POP(iter); + } + + /* Otherwise, pop while we have reached the end of a node */ + while (pos > 2 * n) { + if (head == 0) return NULL; + + /* Pop, if so */ + else + BTREE_ITER_POP(iter); + } + +#undef BTREE_ITER_POP + + /* On evens, we decent into children */ + if (!node_leaf(iter->stack[head].node)) { + if (pos % 2 == 0) { + /* push child node onto iter->stack */ + iter->stack[head + 1].pos = 0; + iter->stack[head + 1].node = iter->stack[head].node->children[pos / 2]; + iter->head++; + head++; + + /* Decent all the way to the left, if pos == 0 */ + while (!node_leaf(iter->stack[iter->head].node)) { + iter->stack[head + 1].pos = 0; + iter->stack[head + 1].node = iter->stack[head].node->children[0]; + iter->head++; + head++; + } + } + } + + /* Finally, update index and return a value */ + if (node_leaf(iter->stack[head].node)) { + iter->stack[head].pos += 2; + pos = iter->stack[head].pos; + } else { + iter->stack[head].pos++; + pos = iter->stack[head].pos; + } + + return iter->stack[head].node->items + tree->elem_size * ((pos - 1) / 2); +} diff --git a/src/utils/src/fov.c b/src/utils/src/fov.c new file mode 100644 index 0000000..3d5ae16 --- /dev/null +++ b/src/utils/src/fov.c @@ -0,0 +1,94 @@ +#include +#include +#include +#include + +void fov_shadowcast_rec(const void* map, const v2_i32 mapsize, + bool (*visblocking)(const void*), i32* lightmap, + const i32 range, v2_i32 src, const i32 row, f32 start, + const f32 end, const i8 xx, const i8 xy, const i8 yx, + const i8 yy) { + + if (start < end) return; + + const i32 range_2 = range * range; + f32 new_start = start; + + for (i32 i = row; i <= range; i++) { + i32 dx = (-1 * i) - 1; + i32 dy = -1 * i; + + bool blocked = false; + + while (dx <= 0) { + dx += 1; + + const i32 mapx = src.x + dx * xx + dy * xy; + const i32 mapy = src.y + dx * yx + dy * yy; + + const f32 slope_l = (((f32)dx) - 0.5f) / (((f32)dy) + 0.5f); + const f32 slope_r = (((f32)dx) + 0.5f) / (((f32)dy) - 0.5f); + + if (start < slope_r) continue; + if (end > slope_l) break; + + if (dx * dx + dy * dy < range_2) { + /* set as visible */ + if (mapx >= 0 && mapx < (long)mapsize.x && mapy >= 0 && + mapy < (long)mapsize.y) { + // TODO: Calculate proper dist from source + f32 x_2 = (src.x - mapx) * (src.x - mapx); + f32 y_2 = (src.y - mapy) * (src.y - mapy); + lightmap[mapy * mapsize.x + mapx] = + MAX(lightmap[mapy * mapsize.x + mapx], + range - sqrt((f32)(x_2 + y_2))); + } + } + + /* sizeof(i32) is the size of enums */ + /* -- unless the compiler doesn't follow standard behaviour */ + const bool is_blocked = visblocking( + (void*)((u64)map + sizeof(i32) /* ~ enum size */ + * (mapsize.x * mapy + mapx) /* index */ + )); + + if (blocked) { + if (!is_blocked) { + new_start = slope_r; + } else { + blocked = false; + start = new_start; + } + } else if (!is_blocked && i < range) { + blocked = true; + fov_shadowcast_rec(map, mapsize, visblocking, lightmap, range, src, + i + 1, start, slope_l, xx, xy, yx, yy); + new_start = slope_r; + } + } + + if (blocked) break; + } +} + +/* http://www.roguebasin.com/index.php?title=FOV_using_recursive_shadowcasting + */ +void fov_shadowcast(const void* map, const v2_i32 mapsize, + bool (*visblocking)(const void*), i32* lightmap, + const i32 range, const v2_i32 src) { + + const i8 m[4][8] = { + {1, 0, 0, -1, -1, 0, 0, 1}, + {0, 1, -1, 0, 0, -1, 1, 0}, + {0, 1, 1, 0, 0, -1, -1, 0}, + {1, 0, 0, 1, -1, 0, 0, -1}, + }; + + for (i32 oct = 0; oct < 8; oct++) { + fov_shadowcast_rec(map, mapsize, visblocking, lightmap, range, src, 1, 1.0, + 0.0, m[0][oct], m[1][oct], m[2][oct], m[3][oct]); + } + + /* The center is the most lit square */ + lightmap[src.y * mapsize.x + src.x] = range; +} diff --git a/src/utils/src/hashmap.c b/src/utils/src/hashmap.c new file mode 100644 index 0000000..1652bb6 --- /dev/null +++ b/src/utils/src/hashmap.c @@ -0,0 +1,3 @@ +#include + +i32 lolhash(const usize s, i32 v) { return v % s; } diff --git a/src/utils/src/misc.c b/src/utils/src/misc.c new file mode 100644 index 0000000..0f3d218 --- /dev/null +++ b/src/utils/src/misc.c @@ -0,0 +1,86 @@ +#include +#include + +#include + +#include +#include + +/* These should all be in some external facing module "tools" */ + +f32 lerp(f32 dt, f32 a, f32 b) { return (a * (1.0f - dt)) + (b * dt); } + +i32 int_lerp(f32 dt, i32 a, i32 b) { + return ((f32)a * (1.0f - dt)) + ((f32)b * dt); +} + +u32 hash(char* str) { + u32 sum = 0; + while (*str != '\0') { + sum ^= (*str) * 0xdeece66d + 0xb; + str++; + } + return sum; +} + +/* Populates dstmap + * on success: return pointer to dstmap + * on failure: return NULL */ +i32* kernmap(const void* map, i32* dstmap, const v2_i32 mapsize, + predicate_t* predicate) { + const i32 w = mapsize.x; + const i32 h = mapsize.y; + i32 mask[w * h]; + + if (w * h < 1) return NULL; + + for (i32 i = 0; i < w * h; i++) { + mask[i] = predicate((void*)((u64)map + sizeof(i32) * i)) ? 1 : 0; + } + + for (i32 y = 1; y < h - 1; y++) { + for (i32 x = 1; x < w - 1; x++) { + const i32 global_idx = (y * w) + x; + const i32 offs = global_idx - w - 1; + i32 _sum = 0; + + i32 shift = 0; + + /* We go in the following order */ + /* ....|0|1|2|....*/ + /* ....|3|4|5|....*/ + /* ....|6|7|8|....*/ + /* Where `4` is in the center, MASK_C */ + for (i32 yy = offs; yy <= offs + w + w; yy += w) { + for (i32 xx = yy; xx < yy + 3; xx++) { + _sum = _sum | (mask[xx] << shift++); + } + } + + dstmap[global_idx] = _sum; + } + } + return dstmap; +} + +/* Returns an index from the given weights. */ +i32 pick_from_sample(const i32* weights, i32 len) { + if (len <= 0) return 0; + + /* Cumulative sum */ + i32 cumweights[len]; + i32 sum = 0; + for (i32 i = 0; i < len; i++) { + sum += weights[i]; + cumweights[i] = sum; + } + + if (sum == 0) return 0; + + i32 pick = rand() % sum; + + for (i32 i = 0; i < len; i++) { + if (pick < cumweights[i]) return i; + } + return -1; +} diff --git a/src/utils/src/stack.c b/src/utils/src/stack.c new file mode 100644 index 0000000..ff195ba --- /dev/null +++ b/src/utils/src/stack.c @@ -0,0 +1,82 @@ +#include +#include +#include + +Stack stack_new_ex(const usize element_size, const usize size) { + Stack s = { + .head = 0, + .elem_size = element_size, + .size = element_size * size, + .chunk_size = element_size * size, + .data = NULL, + }; + + s.data = (void*)calloc(element_size, size); + return s; +} + +Stack stack_new(const usize element_size) { + return stack_new_ex(element_size, 512); +} + +void stack_free(Stack* s) { + if (s->data == NULL) return; + free(s->data); + s->data = NULL; +} + +void* stack_pop(Stack* s) { + if (s->head == 0) return NULL; /* Empty stack */ + return (u8*)s->data + (--(s->head) * s->elem_size); +} + +void stack_push(Stack* s, void* elem) { + if (elem == NULL) { + WARN("%s received a nullptr", __func__); + return; + } + if (s->head > 0 && s->head * s->elem_size >= s->size) { + WARN("Allocating more stack memory"); + /* Reallocate more memory and update size */ + void* ptr = realloc(s->data, s->size + s->chunk_size); + if (ptr == NULL) { + ERROR("Failed to resize memory for stack"); + exit(EXIT_FAILURE); + } + s->data = ptr; + // memset((void*)((u64)s->data + (s->size - s->elem_size)), 0, + // s->chunk_size); + s->size += s->chunk_size; + } + memcpy((u8*)s->data + s->head * s->elem_size, elem, s->elem_size); + s->head++; +} + +void* stack_peek(Stack* s) { + if (s->head <= 0) return NULL; /* Empty stack */ + return (u8*)s->data + ((s->head - 1) * s->elem_size); +} + +isize stack_size(const Stack* s) { return s->head; } + +void stack_swap(Stack* s, Stack* t) { + if (s->size > t->size) { + t->data = realloc(t->data, s->size); + } else if (t->size > s->size) { + s->data = realloc(s->data, t->size); + } + void* tmp = malloc(s->size); + if (tmp == NULL) { + ERROR("Failed to allocate memory for stack swapping!"); + exit(EXIT_FAILURE); + } + isize shead = s->head; + + memcpy(tmp, s->data, s->size); + memcpy(s->data, t->data, t->size); + memcpy(t->data, tmp, s->size); + + s->head = t->head; + t->head = shead; + free(tmp); +} diff --git a/src/utils/src/vector.c b/src/utils/src/vector.c new file mode 100644 index 0000000..3465df7 --- /dev/null +++ b/src/utils/src/vector.c @@ -0,0 +1,32 @@ +#include +#include + +bool v2_i32_eq(const v2_i32 a, const v2_i32 b) { + return (a.x == b.x) && (a.y == b.y); +} +v2_i32 v2_i32_add(v2_i32 a, v2_i32 b) { return (v2_i32){a.x + b.x, a.y + b.y}; } +v2_i32 v2_i32_add_i(v2_i32 a, i32 b) { return (v2_i32){a.x + b, a.y + b}; } +v2_i32 v2_i32_sub(v2_i32 a, v2_i32 b) { return (v2_i32){a.x - b.x, a.y - b.y}; } +v2_i32 v2_i32_sub_i(v2_i32 a, i32 b) { return (v2_i32){a.x - b, a.y - b}; } +v2_i32 v2_i32_div(v2_i32 a, v2_i32 b) { return (v2_i32){a.x / b.x, a.y / b.y}; } +v2_i32 v2_i32_div_i(v2_i32 a, i32 b) { return (v2_i32){a.x / b, a.y / b}; } +v2_i32 v2_i32_mul(v2_i32 a, v2_i32 b) { return (v2_i32){a.x * b.x, a.y * b.y}; } +v2_i32 v2_i32_mul_i(v2_i32 a, i32 b) { return (v2_i32){a.x * b, a.y * b}; } +v2_i32 v2_i32_mod(v2_i32 a, v2_i32 b) { return (v2_i32){a.x % b.x, a.y % b.y}; } +v2_i32 v2_i32_mod_i(v2_i32 a, i32 b) { return (v2_i32){a.x % b, a.y % b}; } +v2_i32 v2_i32_max(v2_i32 a, v2_i32 b) { + return (v2_i32){MAX(a.x, b.x), MAX(a.y, b.y)}; +} +v2_i32 v2_i32_min(v2_i32 a, v2_i32 b) { + return (v2_i32){MIN(a.x, b.x), MIN(a.y, b.y)}; +} +v2_i32 v2_i32_lerp(f32 dt, v2_i32 a, v2_i32 b) { + return (v2_i32){ + .x = lerp(dt, (f32)a.x, (f32)b.x), + .y = lerp(dt, (f32)a.y, (f32)b.y), + }; +} + +void v2_i32_fprintf(FILE* stream, v2_i32 a) { + fprintf(stream, "<%d,%d>", a.x, a.y); +} diff --git a/src/utils/stack.c b/src/utils/stack.c deleted file mode 100644 index ff195ba..0000000 --- a/src/utils/stack.c +++ /dev/null @@ -1,82 +0,0 @@ -#include -#include -#include - -Stack stack_new_ex(const usize element_size, const usize size) { - Stack s = { - .head = 0, - .elem_size = element_size, - .size = element_size * size, - .chunk_size = element_size * size, - .data = NULL, - }; - - s.data = (void*)calloc(element_size, size); - return s; -} - -Stack stack_new(const usize element_size) { - return stack_new_ex(element_size, 512); -} - -void stack_free(Stack* s) { - if (s->data == NULL) return; - free(s->data); - s->data = NULL; -} - -void* stack_pop(Stack* s) { - if (s->head == 0) return NULL; /* Empty stack */ - return (u8*)s->data + (--(s->head) * s->elem_size); -} - -void stack_push(Stack* s, void* elem) { - if (elem == NULL) { - WARN("%s received a nullptr", __func__); - return; - } - if (s->head > 0 && s->head * s->elem_size >= s->size) { - WARN("Allocating more stack memory"); - /* Reallocate more memory and update size */ - void* ptr = realloc(s->data, s->size + s->chunk_size); - if (ptr == NULL) { - ERROR("Failed to resize memory for stack"); - exit(EXIT_FAILURE); - } - s->data = ptr; - // memset((void*)((u64)s->data + (s->size - s->elem_size)), 0, - // s->chunk_size); - s->size += s->chunk_size; - } - memcpy((u8*)s->data + s->head * s->elem_size, elem, s->elem_size); - s->head++; -} - -void* stack_peek(Stack* s) { - if (s->head <= 0) return NULL; /* Empty stack */ - return (u8*)s->data + ((s->head - 1) * s->elem_size); -} - -isize stack_size(const Stack* s) { return s->head; } - -void stack_swap(Stack* s, Stack* t) { - if (s->size > t->size) { - t->data = realloc(t->data, s->size); - } else if (t->size > s->size) { - s->data = realloc(s->data, t->size); - } - void* tmp = malloc(s->size); - if (tmp == NULL) { - ERROR("Failed to allocate memory for stack swapping!"); - exit(EXIT_FAILURE); - } - isize shead = s->head; - - memcpy(tmp, s->data, s->size); - memcpy(s->data, t->data, t->size); - memcpy(t->data, tmp, s->size); - - s->head = t->head; - t->head = shead; - free(tmp); -} diff --git a/src/utils/vector.c b/src/utils/vector.c deleted file mode 100644 index 3465df7..0000000 --- a/src/utils/vector.c +++ /dev/null @@ -1,32 +0,0 @@ -#include -#include - -bool v2_i32_eq(const v2_i32 a, const v2_i32 b) { - return (a.x == b.x) && (a.y == b.y); -} -v2_i32 v2_i32_add(v2_i32 a, v2_i32 b) { return (v2_i32){a.x + b.x, a.y + b.y}; } -v2_i32 v2_i32_add_i(v2_i32 a, i32 b) { return (v2_i32){a.x + b, a.y + b}; } -v2_i32 v2_i32_sub(v2_i32 a, v2_i32 b) { return (v2_i32){a.x - b.x, a.y - b.y}; } -v2_i32 v2_i32_sub_i(v2_i32 a, i32 b) { return (v2_i32){a.x - b, a.y - b}; } -v2_i32 v2_i32_div(v2_i32 a, v2_i32 b) { return (v2_i32){a.x / b.x, a.y / b.y}; } -v2_i32 v2_i32_div_i(v2_i32 a, i32 b) { return (v2_i32){a.x / b, a.y / b}; } -v2_i32 v2_i32_mul(v2_i32 a, v2_i32 b) { return (v2_i32){a.x * b.x, a.y * b.y}; } -v2_i32 v2_i32_mul_i(v2_i32 a, i32 b) { return (v2_i32){a.x * b, a.y * b}; } -v2_i32 v2_i32_mod(v2_i32 a, v2_i32 b) { return (v2_i32){a.x % b.x, a.y % b.y}; } -v2_i32 v2_i32_mod_i(v2_i32 a, i32 b) { return (v2_i32){a.x % b, a.y % b}; } -v2_i32 v2_i32_max(v2_i32 a, v2_i32 b) { - return (v2_i32){MAX(a.x, b.x), MAX(a.y, b.y)}; -} -v2_i32 v2_i32_min(v2_i32 a, v2_i32 b) { - return (v2_i32){MIN(a.x, b.x), MIN(a.y, b.y)}; -} -v2_i32 v2_i32_lerp(f32 dt, v2_i32 a, v2_i32 b) { - return (v2_i32){ - .x = lerp(dt, (f32)a.x, (f32)b.x), - .y = lerp(dt, (f32)a.y, (f32)b.y), - }; -} - -void v2_i32_fprintf(FILE* stream, v2_i32 a) { - fprintf(stream, "<%d,%d>", a.x, a.y); -} -- cgit v1.3