From d38deeef3af2316a666f8fc0173940bd769b748e Mon Sep 17 00:00:00 2001 From: onelin Date: Sat, 1 Nov 2025 00:55:42 +0100 Subject: Flatten project structure This will make it easier to break up the code into smaller chunks again later. One would think doing this seems fun to me at this point. --- CMakeLists.txt | 2 +- src/CMakeLists.txt | 73 +- src/api/daw.h | 39 + src/btree.c | 800 +++++++++++++++++++++ src/core/CMakeLists.txt | 18 - src/core/include/engine/core/dltools.h | 23 - src/core/include/engine/core/logging.h | 56 -- src/core/include/engine/core/memory.h | 31 - src/core/include/engine/core/platform.h | 44 -- src/core/include/engine/core/state.h | 41 -- src/core/include/engine/core/types.h | 41 -- src/core/include/engine/engine.h | 57 -- src/core/src/dltools.c | 58 -- src/core/src/logging.c | 82 --- src/core/src/loop.c | 344 --------- src/core/src/memory.c | 61 -- src/core/src/state.c | 251 ------- src/ctrl/CMakeLists.txt | 7 - src/ctrl/include/engine/ctrl/input.h | 108 --- src/ctrl/include/engine/ctrl/keycodes.h | 93 --- src/ctrl/include/engine/ctrl/scancodes.h | 89 --- src/ctrl/src/input.c | 424 ----------- src/daw.c | 344 +++++++++ src/dltools.c | 58 ++ src/fov.c | 95 +++ src/gl.c | 281 ++++++++ src/hashmap.c | 5 + src/include/daw/daw.h | 73 ++ src/include/daw/dltools.h | 23 + src/include/daw/input.h | 108 +++ src/include/daw/keycodes.h | 93 +++ src/include/daw/logging.h | 56 ++ src/include/daw/memory.h | 31 + src/include/daw/model.h | 30 + src/include/daw/platform.h | 56 ++ src/include/daw/platform_glfw.h | 32 + src/include/daw/rendering.h | 288 ++++++++ src/include/daw/resources.h | 138 ++++ src/include/daw/scancodes.h | 89 +++ src/include/daw/state.h | 41 ++ src/include/daw/texture.h | 17 + src/include/daw/types.h | 41 ++ src/include/daw/utils.h | 43 ++ src/include/daw/utils/btree.h | 61 ++ src/include/daw/utils/fov.h | 28 + src/include/daw/utils/hashmap.h | 61 ++ src/include/daw/utils/list.h | 18 + src/include/daw/utils/stack.h | 33 + src/include/daw/window.h | 55 ++ src/input.c | 425 +++++++++++ src/logging.c | 82 +++ src/memory.c | 61 ++ src/misc.c | 86 +++ src/model.c | 68 ++ src/platform_glfw.c | 232 ++++++ src/rendering.c | 696 ++++++++++++++++++ src/rendering/CMakeLists.txt | 33 - src/rendering/include/engine/rendering/platform.h | 57 -- .../include/engine/rendering/platform_glfw.h | 32 - src/rendering/include/engine/rendering/rendering.h | 288 -------- src/rendering/include/engine/rendering/window.h | 53 -- src/rendering/src/gl.c | 281 -------- src/rendering/src/platform_glfw.c | 232 ------ src/rendering/src/rendering.c | 696 ------------------ src/rendering/src/window.c | 83 --- src/resources.c | 155 ++++ src/resources/CMakeLists.txt | 19 - src/resources/include/engine/resources.h | 138 ---- src/resources/include/engine/resources/model.h | 30 - src/resources/include/engine/resources/texture.h | 17 - src/resources/src/model.c | 68 -- src/resources/src/resources.c | 154 ---- src/resources/src/textures.c | 41 -- src/stack.c | 82 +++ src/state.c | 251 +++++++ src/textures.c | 41 ++ src/utils/CMakeLists.txt | 11 - src/utils/include/engine/utils.h | 43 -- src/utils/include/engine/utils/btree.h | 61 -- src/utils/include/engine/utils/fov.h | 28 - src/utils/include/engine/utils/hashmap.h | 61 -- src/utils/include/engine/utils/list.h | 18 - src/utils/include/engine/utils/stack.h | 33 - src/utils/src/btree.c | 800 --------------------- src/utils/src/fov.c | 95 --- src/utils/src/hashmap.c | 5 - src/utils/src/misc.c | 86 --- src/utils/src/stack.c | 82 --- src/window.c | 82 +++ tools/cmake/DawAddState.cmake | 8 +- 90 files changed, 5358 insertions(+), 5396 deletions(-) create mode 100644 src/api/daw.h create mode 100644 src/btree.c delete mode 100644 src/core/CMakeLists.txt delete mode 100644 src/core/include/engine/core/dltools.h delete mode 100644 src/core/include/engine/core/logging.h delete mode 100644 src/core/include/engine/core/memory.h delete mode 100644 src/core/include/engine/core/platform.h delete mode 100644 src/core/include/engine/core/state.h delete mode 100644 src/core/include/engine/core/types.h delete mode 100644 src/core/include/engine/engine.h delete mode 100644 src/core/src/dltools.c delete mode 100644 src/core/src/logging.c delete mode 100644 src/core/src/loop.c delete mode 100644 src/core/src/memory.c delete mode 100644 src/core/src/state.c delete mode 100644 src/ctrl/CMakeLists.txt delete mode 100644 src/ctrl/include/engine/ctrl/input.h delete mode 100644 src/ctrl/include/engine/ctrl/keycodes.h delete mode 100644 src/ctrl/include/engine/ctrl/scancodes.h delete mode 100644 src/ctrl/src/input.c create mode 100644 src/daw.c create mode 100644 src/dltools.c create mode 100644 src/fov.c create mode 100644 src/gl.c create mode 100644 src/hashmap.c create mode 100644 src/include/daw/daw.h create mode 100644 src/include/daw/dltools.h create mode 100644 src/include/daw/input.h create mode 100644 src/include/daw/keycodes.h create mode 100644 src/include/daw/logging.h create mode 100644 src/include/daw/memory.h create mode 100644 src/include/daw/model.h create mode 100644 src/include/daw/platform.h create mode 100644 src/include/daw/platform_glfw.h create mode 100644 src/include/daw/rendering.h create mode 100644 src/include/daw/resources.h create mode 100644 src/include/daw/scancodes.h create mode 100644 src/include/daw/state.h create mode 100644 src/include/daw/texture.h create mode 100644 src/include/daw/types.h create mode 100644 src/include/daw/utils.h create mode 100644 src/include/daw/utils/btree.h create mode 100644 src/include/daw/utils/fov.h create mode 100644 src/include/daw/utils/hashmap.h create mode 100644 src/include/daw/utils/list.h create mode 100644 src/include/daw/utils/stack.h create mode 100644 src/include/daw/window.h create mode 100644 src/input.c create mode 100644 src/logging.c create mode 100644 src/memory.c create mode 100644 src/misc.c create mode 100644 src/model.c create mode 100644 src/platform_glfw.c create mode 100644 src/rendering.c delete mode 100644 src/rendering/CMakeLists.txt delete mode 100644 src/rendering/include/engine/rendering/platform.h delete mode 100644 src/rendering/include/engine/rendering/platform_glfw.h delete mode 100644 src/rendering/include/engine/rendering/rendering.h delete mode 100644 src/rendering/include/engine/rendering/window.h delete mode 100644 src/rendering/src/gl.c delete mode 100644 src/rendering/src/platform_glfw.c delete mode 100644 src/rendering/src/rendering.c delete mode 100644 src/rendering/src/window.c create mode 100644 src/resources.c delete mode 100644 src/resources/CMakeLists.txt delete mode 100644 src/resources/include/engine/resources.h delete mode 100644 src/resources/include/engine/resources/model.h delete mode 100644 src/resources/include/engine/resources/texture.h delete mode 100644 src/resources/src/model.c delete mode 100644 src/resources/src/resources.c delete mode 100644 src/resources/src/textures.c create mode 100644 src/stack.c create mode 100644 src/state.c create mode 100644 src/textures.c delete mode 100644 src/utils/CMakeLists.txt delete mode 100644 src/utils/include/engine/utils.h delete mode 100644 src/utils/include/engine/utils/btree.h delete mode 100644 src/utils/include/engine/utils/fov.h delete mode 100644 src/utils/include/engine/utils/hashmap.h delete mode 100644 src/utils/include/engine/utils/list.h delete mode 100644 src/utils/include/engine/utils/stack.h delete mode 100644 src/utils/src/btree.c delete mode 100644 src/utils/src/fov.c delete mode 100644 src/utils/src/hashmap.c delete mode 100644 src/utils/src/misc.c delete mode 100644 src/utils/src/stack.c create mode 100644 src/window.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 96d25e0..183a5a0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -162,7 +162,7 @@ add_subdirectory(src) # Configuration files configure_file(${CMAKE_CURRENT_LIST_DIR}/tools/cmake/configure.h.in - ${CMAKE_BINARY_DIR}/include/engine/configure.h) + ${CMAKE_BINARY_DIR}/include/daw/configure.h) configure_file(${CMAKE_CURRENT_LIST_DIR}/tools/cmake/list_of_states.h.in ${CMAKE_BINARY_DIR}/include/states/list_of_states.h) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8b9cc1d..2e915f8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,25 +1,71 @@ # Set global include dirs set(DAW_INCLUDE_DIRS - ${CMAKE_CURRENT_LIST_DIR}/core/include - ${CMAKE_CURRENT_LIST_DIR}/ctrl/include + ${CMAKE_CURRENT_LIST_DIR}/include ${CMAKE_CURRENT_LIST_DIR}/rendering/include - ${CMAKE_CURRENT_LIST_DIR}/resources/include - ${CMAKE_CURRENT_LIST_DIR}/utils/include ${CMAKE_BINARY_DIR}/include ) -# Add the engine modules -add_subdirectory(core) -add_subdirectory(ctrl) -add_subdirectory(rendering) -add_subdirectory(resources) -add_subdirectory(utils) +add_custom_command( + OUTPUT ${GLAD_HEADER} + COMMAND + glad + --api gl:core=4.6 + --out-path ${CMAKE_BINARY_DIR} + --reproducible + c + --header-only + --mx + ) + # DAW Engine compilation spec add_library(${PROJECT_NAME} + # User facing API + api/daw.h + + # Types and (internal) interfaces + include/daw/platform.h + include/daw/types.h + + # Utility algorithms & datastructures + include/daw/utils.h + include/daw/utils/list.h + btree.c include/daw/utils/btree.h + fov.c include/daw/utils/fov.h + hashmap.c include/daw/utils/hashmap.h + misc.c + stack.c include/daw/utils/stack.h + + # Core functionality + daw.c include/daw/daw.h + dltools.c include/daw/dltools.h + logging.c include/daw/logging.h + memory.c include/daw/memory.h + state.c include/daw/state.h + + # Peripheral IO + input.c include/daw/input.h + include/daw/keycodes.h + include/daw/scancodes.h + + # The rendering & presentation mess + gl.c + platform_glfw.c include/daw/platform_glfw.h + rendering.c include/daw/rendering.h + textures.c include/daw/texture.h + window.c include/daw/window.h + + # Resource handling + model.c include/daw/model.h + resources.c include/daw/resources.h + + # Autogenerated header ${GLAD_HEADER} ) +set_property(SOURCE src/window.c APPEND PROPERTY OBJECT_DEPENDS ${GLAD_HEADER}) +set_property(SOURCE src/rendering.c APPEND PROPERTY OBJECT_DEPENDS ${GLAD_HEADER}) + set_target_properties(${PROJECT_NAME} PROPERTIES LINKER_LANGUAGE C C_STANDARD 99 @@ -27,19 +73,14 @@ set_target_properties(${PROJECT_NAME} PROPERTIES target_include_directories(${PROJECT_NAME} PUBLIC include - # TODO include stb (??) ${CMAKE_BINARY_DIR}/include ) target_link_libraries(${PROJECT_NAME} glfw OpenGL::GL + assimp cglm - daw_core - daw_rendering - daw_ctrl - daw_resources - daw_utils $<$>:m> $<$:dl> $<$>,$>:ubsan> diff --git a/src/api/daw.h b/src/api/daw.h new file mode 100644 index 0000000..25ec177 --- /dev/null +++ b/src/api/daw.h @@ -0,0 +1,39 @@ +#ifndef API_DAW_H +#define API_DAW_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + + +/* Essential functions */ +Instance* engine_init(const char* windowtitle, i32 windowWidth, i32 windowHeight, + const u32 flags, + const usize initial_memory); + +i32 engine_run(Instance* p, StateType initial_state, void* state_arg); + +void engine_stop(Instance* p); + +/* Utility functions */ +void engine_fps_max(Instance* p, u16 cap); + +void render_set_zoom(f32 new_zoom); +void render_adjust_zoom(f32 diff); +void render_add_unit(RenderUnit* u); + +/* move this */ +void delay(uint64_t ms); + +// file operations +usize f_get_sz(FILE* f); + + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/btree.c b/src/btree.c new file mode 100644 index 0000000..e4cd0a6 --- /dev/null +++ b/src/btree.c @@ -0,0 +1,800 @@ +#include + +#include +#include +#include +#include + +#include + +/* Definitions */ +typedef unsigned char byte; + +struct node { + size_t n; /* number of items/keys/elements */ + size_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; + size_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 size_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 size_t degree) { + const size_t max_children = 2 * degree + 1; + size_t 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))) { + size_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 size_t t, const size_t elem_size, + struct node* nonfull, size_t i) { + struct node* z = node_new(t, elem_size); + struct node* y = nonfull->children[i]; + size_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, size_t i, const size_t elem_size, + void (*dealloc)(void*)) { + struct node* y = x->children[i]; + struct node* z = x->children[i + 1]; + size_t 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, size_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)) { + size_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, size_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 size_t degree, + const size_t elem_size, + int (*cmp)(const void* a, const void* b)) { + + /* TODO check correctness */ + size_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 size_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 */ + size_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 (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 size_t degree, + const size_t elem_size, void* (*alloc)(size_t), + void (*dealloc)(void*)) { + /* Determine wether the key is in the node */ + size_t 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 */ + size_t 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; + size_t 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*)) { + size_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 size_t head = 0; + register size_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) && (size_t)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 ((size_t)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 * (((size_t)pos - 1) / 2); +} diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt deleted file mode 100644 index a979cad..0000000 --- a/src/core/CMakeLists.txt +++ /dev/null @@ -1,18 +0,0 @@ -add_library(daw_core - src/dltools.c - src/logging.c - src/loop.c - src/memory.c - src/state.c - ${GLAD_HEADER} - ) - -#set_property(SOURCE src/loop.c APPEND PROPERTY OBJECT_DEPENDS ${GLAD_HEADER}) - -target_compile_options(daw_core PUBLIC ${BUILD_OPTS}) -target_include_directories(daw_core PRIVATE ${DAW_INCLUDE_DIRS}) -target_link_libraries(daw_core PRIVATE cglm) - -target_compile_definitions(daw_core PUBLIC - $<$:DAW_BUILD_HOTRELOAD> -) diff --git a/src/core/include/engine/core/dltools.h b/src/core/include/engine/core/dltools.h deleted file mode 100644 index d9c74ee..0000000 --- a/src/core/include/engine/core/dltools.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef DLTOOLS_H -#define DLTOOLS_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include - -/* Utility functions for handling runtime linked shared libraries */ -bool dynamic_library_close(void* shared_library); -void* dynamic_library_open(const char* library_path); -void* dynamic_library_reload(void* shared_library, const char* library_path); - -/* Returns the address of symbol in the provided shared_library handle. - * NULL on error*/ -void* dynamic_library_get_symbol(void* shared_library, const char* symbol); -char* dynamic_library_get_error(void); - -#ifdef __cplusplus -} -#endif -#endif diff --git a/src/core/include/engine/core/logging.h b/src/core/include/engine/core/logging.h deleted file mode 100644 index cd55442..0000000 --- a/src/core/include/engine/core/logging.h +++ /dev/null @@ -1,56 +0,0 @@ -#ifndef LOGGING_H -#define LOGGING_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include -#include -#include -#include - -#include - -#if defined __linux__ || defined __APPLE__ -#define TERM_COLOR_RESET "\033[0m" -#define TERM_COLOR_RED "\033[31m" -#define TERM_COLOR_GREEN "\033[32m" -#define TERM_COLOR_YELLOW "\033[33m" -#define TERM_COLOR_BLUE "\033[34m" -#define TERM_COLOR_PURPLE "\033[35m" -#else -#define TERM_COLOR_RESET -#define TERM_COLOR_RED -#define TERM_COLOR_GREEN -#define TERM_COLOR_YELLOW -#define TERM_COLOR_BLUE -#define TERM_COLOR_PURPLE -#endif - -#define STR(a) (#a) - -void _log(FILE* stream, const char* prefix, const char* fmt, va_list ap); - -void LOG(const char* fmt, ...); - -void INFO_(const char* fmt, ...); -void INFO(const char* fmt, ...); - -#ifdef _DEBUG -#define DEBUG(fmt, ...) __DEBUG(__FILE__, __LINE__, __func__, fmt, __VA_ARGS__) -void __DEBUG(const char* file, const i32 line, const char* func, - const char* fmt, ...); -#else -#define DEBUG(...) -#endif - -void WARN(const char* fmt, ...); - -void ERROR(const char* fmt, ...); - -#ifdef __cplusplus -} -#endif -#endif diff --git a/src/core/include/engine/core/memory.h b/src/core/include/engine/core/memory.h deleted file mode 100644 index d04d58e..0000000 --- a/src/core/include/engine/core/memory.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef MEMORY_H -#define MEMORY_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include - -typedef struct memory { - void* data; - usize size; - usize pos; - usize free; -} memory; - -memory* memory_new(usize max_size); - -/* Returns a pointer to the allocated data */ -void* memory_allocate(memory* mem, usize size); - -memory memory_init(void* data, usize size); - -void memory_free(memory* mem, usize size); - -void memory_clear(memory* mem); - -#ifdef __cplusplus -} -#endif -#endif diff --git a/src/core/include/engine/core/platform.h b/src/core/include/engine/core/platform.h deleted file mode 100644 index de39f71..0000000 --- a/src/core/include/engine/core/platform.h +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef ENGINE_CORE_PLATFORM_H -#define ENGINE_CORE_PLATFORM_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include - -#include -#include -#include -#include - -#define NUM_GLOBAL_BINDINGS 1 - -/* Defines the internally used state of the engine. - * A single instance is created during `engine_init`, and used as a global - * variable (yeah, im sorry). Due to this design flaw the engine as a whole is - * not quite thread safe. - */ -typedef struct Instance { - - Window* window; - bool quit; - - u64 frame; - u16 fps_target; - - /* TODO: Move input ctx/bindings to window */ - /* TODO: Move cam to window->renderer */ - Camera *cam; - - /* Global resources that live from engine_init to engine_free */ - Resources* resources; - - memory* mem; - -} Instance; - -#ifdef __cplusplus -} -#endif -#endif diff --git a/src/core/include/engine/core/state.h b/src/core/include/engine/core/state.h deleted file mode 100644 index a6a5e84..0000000 --- a/src/core/include/engine/core/state.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef STATE_H -#define STATE_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include - -typedef enum StateType { - STATE_null, -#define State(name) STATE_##name, -#include -#undef State - STATE_quit, -} StateType; - -extern const char* StateTypeStr[]; - -StateType (*State_updateFunc(StateType type))(f64, void*); - -void State_init(StateType type, memory* mem, void* arg); -void* State_free(StateType type, memory* mem); -StateType State_update(StateType type, f64 dt, memory* mem); - -/* Reloads shared object file associated with state */ -#ifdef DAW_BUILD_HOTRELOAD -#include -bool State_reload(StateType type, i_ctx** ctx, usize ctx_len); -bool state_refresh_input_ctx(void* lib, i_ctx** ctx, usize ctx_len); - -#else -#define State_reload(_, _0, _1) true -#define state_refresh_input_ctx(_0, _1, _2) true - -#endif - -#ifdef __cplusplus -} -#endif -#endif diff --git a/src/core/include/engine/core/types.h b/src/core/include/engine/core/types.h deleted file mode 100644 index a7d794d..0000000 --- a/src/core/include/engine/core/types.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef ENGINE_TYPES_H -#define ENGINE_TYPES_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include - -/* Signed */ -typedef int8_t i8; -typedef int16_t i16; -typedef int32_t i32; -typedef int64_t i64; - -/* Unsigned */ -typedef uint8_t u8; -typedef uint16_t u16; -typedef uint32_t u32; -typedef uint64_t u64; - -/* floating points */ -typedef float f32; -typedef double f64; - -/* sizes */ -#if __x86_64__ || __ppc64__ || _WIN64 -typedef u64 usize; -typedef i64 isize; -#else -typedef u32 usize; -typedef i32 isize; -#endif - -typedef bool(predicate_t)(const void*); - -#ifdef __cplusplus -} -#endif -#endif diff --git a/src/core/include/engine/engine.h b/src/core/include/engine/engine.h deleted file mode 100644 index aa4eff6..0000000 --- a/src/core/include/engine/engine.h +++ /dev/null @@ -1,57 +0,0 @@ -#ifndef ENGINE_ENGINE_H -#define ENGINE_ENGINE_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include - -/* TODO: Cleanup the includes, ideally this header file should only include all - * "public-facing" headers. - */ - -#include -#include -#include -#include -#include -#include - -typedef struct { - u32 texture_id; - i32 x, y, w, h; -} RenderUnit; - -#include -#include - -#include - -/* Essential functions */ -Instance* engine_init(const char* windowtitle, i32 windowWidth, i32 windowHeight, - const u32 flags, - const usize initial_memory); - -i32 engine_run(Instance* p, StateType initial_state, void* state_arg); - -void engine_stop(Instance* p); - -/* Utility functions */ -void engine_fps_max(Instance* p, u16 cap); - -void render_set_zoom(f32 new_zoom); -void render_adjust_zoom(f32 diff); -void render_add_unit(RenderUnit* u); - -/* move this */ -void delay(uint64_t ms); - -// file operations -usize f_get_sz(FILE* f); - - -#ifdef __cplusplus -} -#endif -#endif diff --git a/src/core/src/dltools.c b/src/core/src/dltools.c deleted file mode 100644 index d27c4ff..0000000 --- a/src/core/src/dltools.c +++ /dev/null @@ -1,58 +0,0 @@ -#include -#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/logging.c b/src/core/src/logging.c deleted file mode 100644 index 94782ac..0000000 --- a/src/core/src/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/src/loop.c b/src/core/src/loop.c deleted file mode 100644 index 4e9df36..0000000 --- a/src/core/src/loop.c +++ /dev/null @@ -1,344 +0,0 @@ -#include -#include -#include -#include -#include - -#include -#include - -#define STB_IMAGE_IMPLEMENTATION -#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 - -#include - -#include -#include -#include -#include -#include - -#include - -#include -#include -#include - - -#define DEFAULT_NUM_PROCS 8 - -Instance* GLOBAL_PLATFORM = NULL; - -static Camera default_camera = { - .pos = {3, 0, 0}, - .dir = {1, 1, 1}, -}; - -input_callback_t* callbacks[128]; -usize callbacks_len; - -i32 nproc(void) { - return get_nprocs(); -} - -void delay( uint64_t us ) -{ -#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) - Sleep( ms ); -#else - struct timespec ts = { - .tv_sec = (i64)us / 1000000, - .tv_nsec = ((i64)us * 1000) % 1000000000 - }; - struct timespec rem = {0,0}; - int err = 0; - while((err = clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, &rem))) { - switch (err) { - case EINTR: - WARN("clock_nanosleep didn't sleep full duration: interrupted."); - break; - case EINVAL: - WARN("clock_nanosleep didn't sleep full duration: invalid argument."); - break; - case ENOTSUP: - WARN("clock_nanosleep didn't sleep full duration: unsupported clock."); - break; - default: - WARN("clock_nanosleep didn't sleep full duration!"); - break; - } - ts = rem; - rem = (struct timespec){0,0}; - } -#endif -} - -i32 cmp_int(const void* a, const void* b) { - const i32* x = a; - const i32* y = b; - - return *x - *y; -} - -/* Creates the window, initializes IO, Rendering, Fonts and engine-specific - * resources. */ -Instance* engine_init(const char* windowtitle, i32 windowWidth, i32 windowHeight, - const u32 flags, - const usize initial_memory) { - - INFO("Engine version " ENGINE_VERSION); - - -#if defined(__linux) || defined(__linux__) || defined(linux) - { - pid_t pid = getpid(); - INFO("Starting with pid %lu", pid); - } -#endif - ivec2 windowsize = {windowWidth, windowHeight}; - - Instance* p = (Instance*)calloc(1, sizeof(Instance)); - Window* w = (Window*)calloc(1, sizeof(Window)); - - /* initialize resources */ - Resources* resources = calloc(1, sizeof(Resources)); - // TODO: Initialize them :) - - w = Window_new(windowtitle, - WINDOW_FRAMEWORK_GLFW, WINDOW_RENDERER_OPENGL, - (ivec2){windowsize[0], windowsize[1]}, flags); - - p->window = w; - p->quit = false; - - p->resources = resources; - - p->frame = 0; -#ifdef _DEBUG - engine_fps_max(p, 30); -#else - engine_fps_max(p, 300); -#endif - - p->mem = memory_new(initial_memory); - - p->cam = &default_camera; - - glm_ortho_default(45.f, p->cam->per); - - // TODO: Add global bindings - - INFO("Available cores: %d", nproc()); - - GLOBAL_PLATFORM = p; - -#ifdef DAW_BUILD_HOTRELOAD - -#define State(name) \ - if (!State_reload(STATE_##name, p->window->bindings, p->window->bindings_len)) { \ - ERROR("Failed to reload shared object file for state %s", #name); \ - }; - -#include -#undef State - -#endif - - return p; -} - -i32 engine_run(Instance* p, StateType initial_state, void* state_arg) { - - if (p == NULL) { - ERROR("Platform is uninitialized.\n"); - INFO("initialize with `engine_init`"); - return -1; - } - - memory* mem = p->mem; - - StateType state = initial_state; - - { - u64 state_init_time = get_time(); - State_init(state, mem, state_arg); - INFO("Initializing state \"%s\" took %.1fns", StateTypeStr[state], - (get_time() - state_init_time)); - } - - u64 frame_end = get_time(); - - // Update ticks - u64 ticks = 0; - - StateType (*update_func)(f64, void*) = State_updateFunc(state); - - u64 last_fps_measurement = frame_end; - u64 last_fps_ticks = 0; - - /* The target frametime measured in μs */ - const u32 fps_cap = p->fps_target > 0 ? 1000000 / p->fps_target : 0; - - /* Main loop */ - do { - /* frame_start is μs since engine was initialized */ - const u64 frame_start = get_time(); - - /* dt measured in μs */ - const u64 dt = frame_start - frame_end; - - /* Delta is relative to FPS cap */ - //const f64 delta = (f64)dt / (f64)fps_cap; - - /* Events */ - Platform_GLFW.window_poll(); - i_flush_bindings(dt, callbacks_len, callbacks, mem->data); - - /* Update */ - StateType next_state; - next_state = update_func(dt, (void*)(mem->data)); - - if (next_state != STATE_null) { - if (next_state == STATE_quit) break; - - drawcall_reset(); - - void* retval = State_free(state, mem); - memory_clear(mem); - - i_ctx_reset(); - - // Reset camera to default camera - p->cam = &default_camera; - - state = next_state; - update_func = State_updateFunc(state); - { - u64 state_init_time = get_time(); - State_init(state, mem, retval); - INFO("Initializing state \"%s\" took %.1fns", StateTypeStr[state], - (get_time() - state_init_time)); - } - } else { - - /* Render */ - const u64 rendertime_begin = get_time(); - render_begin(p->window); - render_present(p->window); - const u64 rendertime_dt = get_time() - rendertime_begin; - - /* Regulate FPS */ - frame_end = get_time(); - const i64 fps_diff = fps_cap - (i64)(frame_end - frame_start); - - if (fps_diff > 0) { - delay((u64)fps_diff); - } - - frame_end = frame_start; - - /* Print stats */ - const u64 dt_measurement = frame_end - last_fps_measurement; - - /* only make measurements once a second */ - if (dt_measurement > 1000000) { - - printf(" FPS: %.1f" - "\tticks: %lu" - "\tframetime: %lu.%03lums" - "\trendertime: %lu.%03lums" - "\n" - , ((f64)(ticks - last_fps_ticks)) / ((f64)dt_measurement / 1000000.0) - , ticks - , dt / 1000 , dt % 1000 - , rendertime_dt / 1000 , rendertime_dt % 1000 - ); - - last_fps_measurement = frame_end; - last_fps_ticks = ticks; - } - } - - ticks++; - } while( - !Platform_GLFW.window_should_close(p->window) - && state != STATE_quit - ); - - return 0; -} - -void engine_stop(Instance* 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 */ - // } - //} - - Platform_GLFW.window_destroy(p->window); - free(p->resources); -} - -/* Set the maximum framerate */ -void engine_fps_max(Instance* p, u16 cap) { - LOG("Setting max fps to %llu", cap); - p->fps_target = cap; -} - -usize f_get_sz(FILE* f) { - if (f == NULL) { - ERROR("File was null!"); - return 0; - } - - const isize pos = ftell(f); - if (pos == -1) { - ERROR("Failed to determine file size (%d): %s", errno, strerror(errno)); - return 0; - } - - const i32 err = fseek(f, 0, SEEK_END); - if (err != 0) { - if (err == ESPIPE) { - ERROR("File is a pipe"); - } else { - ERROR("Failed to determine file size!"); - } - return 0; - } - - const isize size = ftell(f); - - if (size == -1) { - ERROR("Failed to determine file size (%d): %s", errno, strerror(errno)); - return 0; - } - - // Reset the position to the position prior to calling f_get_sz - fseek(f, pos, SEEK_SET); - - return (usize)size; -} diff --git a/src/core/src/memory.c b/src/core/src/memory.c deleted file mode 100644 index 7a51a0d..0000000 --- a/src/core/src/memory.c +++ /dev/null @@ -1,61 +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/state.c b/src/core/src/state.c deleted file mode 100644 index a206cbb..0000000 --- a/src/core/src/state.c +++ /dev/null @@ -1,251 +0,0 @@ -#include - -#include -#include -#include -#include - -//typedef void state_init_t(void*,void*); -//typedef void* (state_free_t(void*)); -typedef StateType state_update_t(f64, 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*,void*); \ - 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 State_init(StateType type, memory* mem, void* arg) { - switch (type) { -#define State(name) \ - case (STATE_##name): { \ - name##_init(memory_allocate(mem, sizeof(name##_state)), arg); \ - 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) { - void* state_retval = NULL; - switch (type) { -#define State(name) \ - case (STATE_##name): { \ - state_retval = 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); - return state_retval; -} - -/* Returns the update function of a given state type */ -StateType (*State_updateFunc(StateType type))(f64, 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, f64 dt, 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); -} - - -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; -} - -#endif diff --git a/src/ctrl/CMakeLists.txt b/src/ctrl/CMakeLists.txt deleted file mode 100644 index f37fa6a..0000000 --- a/src/ctrl/CMakeLists.txt +++ /dev/null @@ -1,7 +0,0 @@ -add_library(daw_ctrl - src/input.c - ) - -target_compile_options(daw_ctrl PUBLIC ${BUILD_OPTS}) -target_include_directories(daw_ctrl PRIVATE ${DAW_INCLUDE_DIRS} ${GLFW_INCLUDE_DIR}) -target_link_libraries(daw_ctrl PRIVATE cglm glfw) diff --git a/src/ctrl/include/engine/ctrl/input.h b/src/ctrl/include/engine/ctrl/input.h deleted file mode 100644 index 18a6797..0000000 --- a/src/ctrl/include/engine/ctrl/input.h +++ /dev/null @@ -1,108 +0,0 @@ -#ifndef ENGINE_CTRL_INPUT_H -#define ENGINE_CTRL_INPUT_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include -#include - -typedef void input_callback_t(void*); -typedef i32 scancode_t; -typedef i32 keycode_t; - -typedef enum InputType { - InputType_error = 0, - InputType_action, - InputType_state, - InputType_range, /* TBD */ -} InputType; - -typedef union action_t { - InputType type; - - struct { - InputType type; - input_callback_t* callback; - char* callback_str; - } action; - - struct { - InputType type; - input_callback_t* activate; - input_callback_t* deactivate; - char* activate_str; - char* deactivate_str; - } state; -} action_t; - -typedef struct binding_t { - action_t action; - - // Change type depending on input handling back-end. like u16 for GLFW_KEY - keycode_t keycode; - keycode_t keycode_alt; - - u64 since_last_activation; -} binding_t; - -typedef struct i_ctx { - - // Current mouse position - ivec2 mouse_pos; - - usize len; - binding_t* bindings; -} i_ctx; - -void i_ctx_t_free(i_ctx* c); -/* Executes all callbacks that has been pushed onto the callstack and resets the - * callstack */ -void i_flush_bindings(u64 dt, usize numcalls, input_callback_t* c[], void* state_mem); -action_t i_get_action(const i_ctx* restrict ctx, u64 time, keycode_t keycode); - -void key_callback(void* window, int key, int scancode, int action, int mods); - -void i_ctx_push(i_ctx* ctx); -void i_ctx_pop(void); -void i_ctx_reset(void); - -/* Finds and updates the keycode of a binding with the given action in ctx */ -void i_bind_ctx(i_ctx* c, keycode_t s, action_t* a); -void i_bind_ctx_alt(i_ctx* c, keycode_t s, action_t* a); - -/* Update the keycode of a binding */ -void i_bind(binding_t* b, keycode_t s); -void i_bind_alt(binding_t* b, keycode_t s); - -#define BindAction(key, altkey, f_action) \ - (binding_t) { \ - .action = (action_t){.action = \ - { \ - .type = InputType_action, \ - .callback = (input_callback_t*)&f_action, \ - .callback_str = strdup(#f_action), \ - }}, \ - .keycode = key, .keycode_alt = altkey, .since_last_activation = 0 \ - } - -#define BindState(key, altkey, f_activate, f_deactivate) \ - (binding_t) { \ - .action = \ - (action_t){.state = \ - { \ - .type = InputType_state, \ - .activate = (input_callback_t*)&f_activate, \ - .deactivate = (input_callback_t*)&f_deactivate, \ - .activate_str = strdup(#f_activate), \ - .deactivate_str = strdup(#f_deactivate), \ - }}, \ - .keycode = key, .keycode_alt = altkey, .since_last_activation = 0 \ - } - -#ifdef __cplusplus -} -#endif -#endif diff --git a/src/ctrl/include/engine/ctrl/keycodes.h b/src/ctrl/include/engine/ctrl/keycodes.h deleted file mode 100644 index 4fff38e..0000000 --- a/src/ctrl/include/engine/ctrl/keycodes.h +++ /dev/null @@ -1,93 +0,0 @@ -#ifndef ENGINE_CTRL_SCANCODES_H - -#ifdef __cplusplus -extern "C" { -#endif - -// We want to reserve the following bytes marked with X for MODS, -// one bit per MODIFIER (shift, control, alt, super): -// XXXX 0000 0000 0000 0000 0000 0000 0000 -// -// We want to reserve the following bytes marked with X for ACTIONS -// enumerate by action type, pressed, released, repeat -// 0000 0000 XXXX 0000 0000 0000 0000 0000 -// -// We want to reserve the following bytes marked with X for the KEYCODE -// 0000 0000 0000 0000 XXXX XXXX XXXX XXXX -#define KEY_MOD(x) 1 << (32-4 + x) -#define ACTION(x) (1 << (32-8)) + x - -typedef enum { - KEY_SPACE = ' ', - KEY_APOSTROPHE = '\'', - KEY_COMMA = ',', - KEY_MINUS = '-', - KEY_PERIOD = '.', - KEY_SLASH = '/', - - KEY_0 = '0', - KEY_1 = '1', - KEY_2 = '2', - KEY_3 = '3', - KEY_4 = '4', - KEY_5 = '5', - KEY_6 = '6', - KEY_7 = '7', - KEY_8 = '8', - KEY_9 = '9', - - KEY_SEMICOLON = ';', - KEY_EQUAL = '=', - - KEY_A = 'A', - KEY_B = 'B', - KEY_C = 'C', - KEY_D = 'D', - KEY_E = 'E', - KEY_F = 'F', - KEY_G = 'G', - KEY_H = 'H', - KEY_I = 'I', - KEY_J = 'J', - KEY_K = 'K', - KEY_L = 'L', - KEY_M = 'M', - KEY_N = 'N', - KEY_O = 'O', - KEY_P = 'P', - KEY_Q = 'Q', - KEY_R = 'R', - KEY_S = 'S', - KEY_T = 'T', - KEY_U = 'U', - KEY_V = 'V', - KEY_W = 'W', - KEY_X = 'X', - KEY_Y = 'Y', - KEY_Z = 'Z', - - KEY_LEFT_BRACKET = '[', - KEY_BACKSLASH = '\\', - KEY_RIGHT_BRACKET = ']', - KEY_GRAVE_ACCENT = '`', - - ACTION_PRESS = ACTION(0), - ACTION_RELEASE = ACTION(1), - ACTION_REPEAT = ACTION(2), - - MOD_SHIFT = KEY_MOD(0), - MOD_CONTROL = KEY_MOD(1), - MOD_ALT = KEY_MOD(2), - MOD_SUPER = KEY_MOD(3), -} KeyCode; -#define MOD_SHFT MOD_SHIFT -#define MOD_CTRL MOD_CONTROL -#define MOD_SUPR MOD_SUPER - -#undef ACTION -#undef KEY_MOD - -#ifdef __cplusplus -} -#endif -#endif diff --git a/src/ctrl/include/engine/ctrl/scancodes.h b/src/ctrl/include/engine/ctrl/scancodes.h deleted file mode 100644 index 5b18833..0000000 --- a/src/ctrl/include/engine/ctrl/scancodes.h +++ /dev/null @@ -1,89 +0,0 @@ -#ifndef ENGINE_CTRL_SCANCODES_H - -#ifdef __cplusplus -extern "C" { -#endif - -// We want to reserve the following bytes marked with X for MODS, -// one bit per MODIFIER (shift, control, alt, super): -// XXXX 0000 0000 0000 0000 0000 0000 0000 -// -// We want to reserve the following bytes marked with X for ACTIONS -// enumerate by action type, pressed, released, repeat -// 0000 0000 XXXX 0000 0000 0000 0000 0000 -// -// We want to reserve the following bytes marked with X for the SCANCODE -// 0000 0000 0000 0000 XXXX XXXX XXXX XXXX -#define MOD(x) 1 << (32-4 + x) -#define ACTION(x) (1 << (32-8)) + x - -typedef enum { - SCAN_KEY_SPACE = ' ', - SCAN_KEY_APOSTROPHE = '\'', - SCAN_KEY_COMMA = ',', - SCAN_KEY_MINUS = '-', - SCAN_KEY_PERIOD = '.', - SCAN_KEY_SLASH = '/', - - SCAN_KEY_0 = '0', - SCAN_KEY_1 = '1', - SCAN_KEY_2 = '2', - SCAN_KEY_3 = '3', - SCAN_KEY_4 = '4', - SCAN_KEY_5 = '5', - SCAN_KEY_6 = '6', - SCAN_KEY_7 = '7', - SCAN_KEY_8 = '8', - SCAN_KEY_9 = '9', - - SCAN_KEY_SEMICOLON = ';', - SCAN_KEY_EQUAL = '=', - - SCAN_KEY_A = 'A', - SCAN_KEY_B = 'B', - SCAN_KEY_C = 'C', - SCAN_KEY_D = 'D', - SCAN_KEY_E = 'E', - SCAN_KEY_F = 'F', - SCAN_KEY_G = 'G', - SCAN_KEY_H = 'H', - SCAN_KEY_I = 'I', - SCAN_KEY_J = 'J', - SCAN_KEY_K = 'K', - SCAN_KEY_L = 'L', - SCAN_KEY_M = 'M', - SCAN_KEY_N = 'N', - SCAN_KEY_O = 'O', - SCAN_KEY_P = 'P', - SCAN_KEY_Q = 'Q', - SCAN_KEY_R = 'R', - SCAN_KEY_S = 'S', - SCAN_KEY_T = 'T', - SCAN_KEY_U = 'U', - SCAN_KEY_V = 'V', - SCAN_KEY_W = 'W', - SCAN_KEY_X = 'X', - SCAN_KEY_Y = 'Y', - SCAN_KEY_Z = 'Z', - - SCAN_KEY_LEFT_BRACKET = '[', - SCAN_KEY_BACKSLASH = '\\', - SCAN_KEY_RIGHT_BRACKET = ']', - SCAN_KEY_GRAVE_ACCENT = '`', - - SCAN_ACTION_PRESS = ACTION(0), - SCAN_ACTION_RELEASE = ACTION(1), - SCAN_ACTION_REPEAT = ACTION(2), - - SCAN_MOD_SHIFT = MOD(0), - SCAN_MOD_CONTROL = MOD(1), - SCAN_MOD_ALT = MOD(2), - SCAN_MOD_SUPER = MOD(3), -} ScanCode; -#undef ACTION -#undef MOD - -#ifdef __cplusplus -} -#endif -#endif diff --git a/src/ctrl/src/input.c b/src/ctrl/src/input.c deleted file mode 100644 index 52d5ce4..0000000 --- a/src/ctrl/src/input.c +++ /dev/null @@ -1,424 +0,0 @@ -#include -#include - -#undef GLFW_INCLUDE_NONE -#include - -#include -#include -#include -#include - -extern input_callback_t* callbacks[128]; -extern usize callbacks_len; - -extern Instance* GLOBAL_PLATFORM; -/* 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), \ - }}, \ - .keycode = key, .keycode_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), \ - }}, \ - .keycode = key, .keycode_alt = altkey, .since_last_activation = 0 \ - } - -void key_callback(void* window, int key, int scancode, int action, int mods) { - - const i_ctx* bindings = *GLOBAL_PLATFORM->window->bindings; - const usize bindings_len = GLOBAL_PLATFORM->window->bindings_len; - - const u64 now = get_time(); - - for (usize b = 0; b < bindings_len; b++) { - const action_t a = i_get_action(&bindings[b], now, key); - switch (action) { - case GLFW_PRESS: - - switch (a.type) { - case InputType_action: - if (a.action.callback != NULL) { - callbacks[callbacks_len++] = a.action.callback; - } - break; - - case InputType_state: - if (a.state.activate != NULL) { - callbacks[callbacks_len++] = a.state.activate; - } - break; - case InputType_range: -#ifdef _DEBUG - WARN("Range inputs not supported yet!"); -#endif - break; - case InputType_error: -#ifdef _DEBUG - WARN("Unhandled keycode: %lu", key); -#endif - - default: - break; - } - break; - - case GLFW_RELEASE: - switch (a.type) { - case InputType_state: - if (a.state.deactivate != NULL) { - callbacks[callbacks_len++] = a.state.deactivate; - } - break; - default:break; - } - break; - case GLFW_REPEAT: - /* unhandled so far */ - break; - default: - break; - } - } -} - -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 (usize 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_keycode_cmp(binding_t* restrict a, binding_t* restrict b) { - return a->keycode == b->keycode || a->keycode_alt == b->keycode_alt; -} - -bool binding_keycode_cmp_i(binding_t* restrict a, keycode_t keycode) { - return a->keycode == keycode || a->keycode_alt == keycode; -} - -// If any binding in ctx contains action, replace that entry. -bool i_update_binding(i_ctx* ctx, binding_t* binding) { - usize 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].keycode = binding->keycode; - ctx->bindings[idx].keycode_alt = binding->keycode_alt; - return true; - } - - return false; -} - -// If any binding in ctx contains keycode, replace that action. -bool i_update_unique_binding(i_ctx* ctx, binding_t* binding) { - usize idx = 0; - if (ctx == NULL || binding == NULL) { - ERROR("i_update_unique_binding received nullptr!"); - return false; - } - - while (idx < ctx->len && !binding_keycode_cmp(&ctx->bindings[idx], binding)) - idx++; - - if (idx < ctx->len && binding_keycode_cmp(&ctx->bindings[idx], binding)) { - ctx->bindings[idx].action = binding->action; - return true; - } - - return false; -} - -/* Call binding callbacks of respective bindings */ -void i_flush_bindings(u64 dt, usize numcalls, input_callback_t* c[], void* state_mem) { - for (usize i = 0; i < numcalls; i++) { - (c[i])(state_mem); - } - - // reset callback len and be ready for more - callbacks_len = 0; -} - -action_t i_get_action(const i_ctx* restrict ctx, u64 time, - keycode_t keycode) { - usize idx = 0; - - if (ctx == NULL) { - ERROR("%s received nullptr!", __func__); - return (action_t){.type = InputType_error}; - } - - // Needed for glfw - switch (keycode) { - case GLFW_KEY_LEFT_SHIFT: - case GLFW_KEY_RIGHT_SHIFT: - keycode = MOD_SHIFT; - break; - - case GLFW_KEY_LEFT_CONTROL: - case GLFW_KEY_RIGHT_CONTROL: - keycode = MOD_CONTROL; - break; - - case GLFW_KEY_LEFT_ALT: - case GLFW_KEY_RIGHT_ALT: - keycode = MOD_ALT; - break; - - case GLFW_KEY_LEFT_SUPER: - case GLFW_KEY_RIGHT_SUPER: - keycode = MOD_SUPER; - break; - - default: break; - } - - while (idx < ctx->len && - !binding_keycode_cmp_i(&ctx->bindings[idx], keycode)) - idx++; - - if (idx < ctx->len && binding_keycode_cmp_i(&ctx->bindings[idx], keycode)) { - ctx->bindings[idx].since_last_activation = time; - return ctx->bindings[idx].action; - } - - return (action_t){.type = InputType_error}; -} - -/* 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 (usize i = 0; i < ctx[c]->len; i++) { - switch (b[i].action.type) { - case InputType_error: - break; - case InputType_action: - bb[cumsum] = BindActionLazy(b[i].keycode, b[i].keycode_alt, - strdup(b[i].action.action.callback_str)); - break; - case InputType_state: - bb[cumsum] = BindStateLazy(b[i].keycode, b[i].keycode_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 (usize 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, keycode_t s, action_t* a) { - binding_t* b = get_action(c, a); - - if (b == NULL) { - WARN("Failed to update keybinding"); - return; - } - - b->keycode = s; -} - -void i_bind_ctx_alt(i_ctx* c, keycode_t s, action_t* a) { - binding_t* b = get_action(c, a); - - if (b == NULL) { - WARN("Failed to update keybinding"); - return; - } - - b->keycode_alt = s; -} - -void i_bind(binding_t* b, keycode_t s) { - if (b == NULL) { - WARN("Failed to update keybinding"); - return; - } - - b->keycode = s; -} - -void i_bind_alt(binding_t* b, keycode_t s) { - if (b == NULL) { - WARN("Failed to update keybinding"); - return; - } - - b->keycode_alt = s; -} - -/* Pushes an input context onto the input handling stack */ -void i_ctx_push(i_ctx* ctx) { - if (GLOBAL_PLATFORM->window->bindings == NULL) { - GLOBAL_PLATFORM->window->bindings = calloc(8, sizeof(i_ctx*)); - GLOBAL_PLATFORM->window->bindings_sz = 8; - } - - if (GLOBAL_PLATFORM->window->bindings_len + 1 >= GLOBAL_PLATFORM->window->bindings_sz) { - void* m = - realloc(GLOBAL_PLATFORM->window->bindings, GLOBAL_PLATFORM->window->bindings_sz + 8); - if (m == NULL) { - ERROR("Failed to allocate 8 bytes (%d): %s", errno, strerror(errno)); - exit(EXIT_FAILURE); - } - GLOBAL_PLATFORM->window->bindings_sz += 8; - } - - LOG("Bindings in ctx[%d]:", GLOBAL_PLATFORM->window->bindings_len); - for (usize 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->window->bindings[GLOBAL_PLATFORM->window->bindings_len++] = ctx; -} - -/* Pops an input context from the input stack */ -void i_ctx_pop(void) { - if (GLOBAL_PLATFORM->window->bindings == NULL || GLOBAL_PLATFORM->window->bindings_sz == 0) - return; - i_ctx_t_free(GLOBAL_PLATFORM->window->bindings[--GLOBAL_PLATFORM->window->bindings_len]); -} - -/* Removes all input contexts from the input stack */ -void i_ctx_reset(void) { - while (GLOBAL_PLATFORM->window->bindings_len > 0) { - i_ctx_t_free(GLOBAL_PLATFORM->window->bindings[--GLOBAL_PLATFORM->window->bindings_len]); - } -} diff --git a/src/daw.c b/src/daw.c new file mode 100644 index 0000000..b632466 --- /dev/null +++ b/src/daw.c @@ -0,0 +1,344 @@ +#include +#include +#include +#include +#include + +#include +#include + +#define STB_IMAGE_IMPLEMENTATION +#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 + +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + + +#define DEFAULT_NUM_PROCS 8 + +Instance* GLOBAL_PLATFORM = NULL; + +static Camera default_camera = { + .pos = {3, 0, 0}, + .dir = {1, 1, 1}, +}; + +input_callback_t* callbacks[128]; +usize callbacks_len; + +i32 nproc(void) { + return get_nprocs(); +} + +void delay( uint64_t us ) +{ +#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) + Sleep( ms ); +#else + struct timespec ts = { + .tv_sec = (i64)us / 1000000, + .tv_nsec = ((i64)us * 1000) % 1000000000 + }; + struct timespec rem = {0,0}; + int err = 0; + while((err = clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, &rem))) { + switch (err) { + case EINTR: + WARN("clock_nanosleep didn't sleep full duration: interrupted."); + break; + case EINVAL: + WARN("clock_nanosleep didn't sleep full duration: invalid argument."); + break; + case ENOTSUP: + WARN("clock_nanosleep didn't sleep full duration: unsupported clock."); + break; + default: + WARN("clock_nanosleep didn't sleep full duration!"); + break; + } + ts = rem; + rem = (struct timespec){0,0}; + } +#endif +} + +i32 cmp_int(const void* a, const void* b) { + const i32* x = a; + const i32* y = b; + + return *x - *y; +} + +/* Creates the window, initializes IO, Rendering, Fonts and engine-specific + * resources. */ +Instance* engine_init(const char* windowtitle, i32 windowWidth, i32 windowHeight, + const u32 flags, + const usize initial_memory) { + + INFO("Engine version " ENGINE_VERSION); + + +#if defined(__linux) || defined(__linux__) || defined(linux) + { + pid_t pid = getpid(); + INFO("Starting with pid %lu", pid); + } +#endif + ivec2 windowsize = {windowWidth, windowHeight}; + + Instance* p = (Instance*)calloc(1, sizeof(Instance)); + Window* w = (Window*)calloc(1, sizeof(Window)); + + /* initialize resources */ + Resources* resources = calloc(1, sizeof(Resources)); + // TODO: Initialize them :) + + w = Window_new(&Platform_GLFW, windowtitle, + WINDOW_FRAMEWORK_GLFW, WINDOW_RENDERER_OPENGL, + (ivec2){windowsize[0], windowsize[1]}, flags); + + p->window = w; + p->quit = false; + + p->resources = resources; + + p->frame = 0; +#ifdef _DEBUG + engine_fps_max(p, 30); +#else + engine_fps_max(p, 300); +#endif + + p->mem = memory_new(initial_memory); + + p->cam = &default_camera; + + glm_ortho_default(45.f, p->cam->per); + + // TODO: Add global bindings + + INFO("Available cores: %d", nproc()); + + GLOBAL_PLATFORM = p; + +#ifdef DAW_BUILD_HOTRELOAD + +#define State(name) \ + if (!State_reload(STATE_##name, p->window->bindings, p->window->bindings_len)) { \ + ERROR("Failed to reload shared object file for state %s", #name); \ + }; + +#include +#undef State + +#endif + + return p; +} + +i32 engine_run(Instance* p, StateType initial_state, void* state_arg) { + + if (p == NULL) { + ERROR("Platform is uninitialized.\n"); + INFO("initialize with `engine_init`"); + return -1; + } + + memory* mem = p->mem; + + StateType state = initial_state; + + { + u64 state_init_time = get_time(); + State_init(state, mem, state_arg); + INFO("Initializing state \"%s\" took %.1fns", StateTypeStr[state], + (get_time() - state_init_time)); + } + + u64 frame_end = get_time(); + + // Update ticks + u64 ticks = 0; + + StateType (*update_func)(f64, void*) = State_updateFunc(state); + + u64 last_fps_measurement = frame_end; + u64 last_fps_ticks = 0; + + /* The target frametime measured in μs */ + const u32 fps_cap = p->fps_target > 0 ? 1000000 / p->fps_target : 0; + + /* Main loop */ + do { + /* frame_start is μs since engine was initialized */ + const u64 frame_start = get_time(); + + /* dt measured in μs */ + const u64 dt = frame_start - frame_end; + + /* Delta is relative to FPS cap */ + //const f64 delta = (f64)dt / (f64)fps_cap; + + /* Events */ + Platform_GLFW.window_poll(); + i_flush_bindings(dt, callbacks_len, callbacks, mem->data); + + /* Update */ + StateType next_state; + next_state = update_func(dt, (void*)(mem->data)); + + if (next_state != STATE_null) { + if (next_state == STATE_quit) break; + + drawcall_reset(); + + void* retval = State_free(state, mem); + memory_clear(mem); + + i_ctx_reset(); + + // Reset camera to default camera + p->cam = &default_camera; + + state = next_state; + update_func = State_updateFunc(state); + { + u64 state_init_time = get_time(); + State_init(state, mem, retval); + INFO("Initializing state \"%s\" took %.1fns", StateTypeStr[state], + (get_time() - state_init_time)); + } + } else { + + /* Render */ + const u64 rendertime_begin = get_time(); + render_begin(p->window); + render_present(p->window); + const u64 rendertime_dt = get_time() - rendertime_begin; + + /* Regulate FPS */ + frame_end = get_time(); + const i64 fps_diff = fps_cap - (i64)(frame_end - frame_start); + + if (fps_diff > 0) { + delay((u64)fps_diff); + } + + frame_end = frame_start; + + /* Print stats */ + const u64 dt_measurement = frame_end - last_fps_measurement; + + /* only make measurements once a second */ + if (dt_measurement > 1000000) { + + printf(" FPS: %.1f" + "\tticks: %lu" + "\tframetime: %lu.%03lums" + "\trendertime: %lu.%03lums" + "\n" + , ((f64)(ticks - last_fps_ticks)) / ((f64)dt_measurement / 1000000.0) + , ticks + , dt / 1000 , dt % 1000 + , rendertime_dt / 1000 , rendertime_dt % 1000 + ); + + last_fps_measurement = frame_end; + last_fps_ticks = ticks; + } + } + + ticks++; + } while( + !Platform_GLFW.window_should_close(p->window) + && state != STATE_quit + ); + + return 0; +} + +void engine_stop(Instance* 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 */ + // } + //} + + Platform_GLFW.window_destroy(p->window); + free(p->resources); +} + +/* Set the maximum framerate */ +void engine_fps_max(Instance* p, u16 cap) { + LOG("Setting max fps to %llu", cap); + p->fps_target = cap; +} + +usize f_get_sz(FILE* f) { + if (f == NULL) { + ERROR("File was null!"); + return 0; + } + + const isize pos = ftell(f); + if (pos == -1) { + ERROR("Failed to determine file size (%d): %s", errno, strerror(errno)); + return 0; + } + + const i32 err = fseek(f, 0, SEEK_END); + if (err != 0) { + if (err == ESPIPE) { + ERROR("File is a pipe"); + } else { + ERROR("Failed to determine file size!"); + } + return 0; + } + + const isize size = ftell(f); + + if (size == -1) { + ERROR("Failed to determine file size (%d): %s", errno, strerror(errno)); + return 0; + } + + // Reset the position to the position prior to calling f_get_sz + fseek(f, pos, SEEK_SET); + + return (usize)size; +} diff --git a/src/dltools.c b/src/dltools.c new file mode 100644 index 0000000..6a0653a --- /dev/null +++ b/src/dltools.c @@ -0,0 +1,58 @@ +#include +#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/fov.c b/src/fov.c new file mode 100644 index 0000000..d7c6746 --- /dev/null +++ b/src/fov.c @@ -0,0 +1,95 @@ +#include +#include +#include +#include + +void fov_shadowcast_rec(const void* map, const ivec2 mapsize, + bool (*visblocking)(const void*), i32* lightmap, + const i32 range, ivec2 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[0] + dx * xx + dy * xy; + const i32 mapy = src[1] + 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[0] && mapy >= 0 && + mapy < (long)mapsize[1]) { + // TODO: Calculate proper dist from source + i32 x_2 = (src[0] - mapx) * (src[0] - mapx); + i32 y_2 = (src[1] - mapy) * (src[1] - mapy); + lightmap[mapy * mapsize[0] + mapx] = + MAX(lightmap[mapy * mapsize[0] + mapx], + (i32)(range - sqrt((f64)(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 */ + * (usize)(mapsize[0] * 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 ivec2 mapsize, + bool (*visblocking)(const void*), i32* lightmap, + const i32 range, const ivec2 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[1] * mapsize[0] + src[0]] = range; +} diff --git a/src/gl.c b/src/gl.c new file mode 100644 index 0000000..9dce5e5 --- /dev/null +++ b/src/gl.c @@ -0,0 +1,281 @@ +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include + +extern Instance* GLOBAL_PLATFORM; + +const char* ShaderType_str[] = { + [Shader_Error] = "Shader_Error", + [Shader_Program] = "Shader_Program", + [Shader_Vertex] = "Shader_Vertex", + [Shader_Tessellation] = "Shader_Tessellation", + [Shader_Geometry] = "Shader_Geometry", + [Shader_Fragment] = "Shader_Fragment", + [Shader_Compute] = "Shader_Compute", +}; + +Shader compile_shader(const char* file_path, const ShaderType shader_type) { + u32 shaderID = 0; + GLenum shadertype = GL_INVALID_ENUM; + + i32 Result = GL_FALSE; + i32 infolog_len; + + char* source = NULL; + FILE* file = NULL; + + const GladGLContext* gl = GLOBAL_PLATFORM->window->context; + + if (file_path == NULL) { + WARN("Empty path to shader"); + return (Shader){.program = 0, .type = Shader_Error}; + } + + switch (shader_type) { + case Shader_Vertex: + shadertype = GL_VERTEX_SHADER; + break; + case Shader_Fragment: + shadertype = GL_FRAGMENT_SHADER; + break; + default: break; + } + + file = fopen(file_path, "r"); + + shaderID = gl->CreateShader(shadertype); + LOG("CREATED SHADER ID %d", shaderID); + + if(file != NULL) { + const usize size = f_get_sz(file); + + source = calloc((usize)size + 1, sizeof(char)); + + // Assume the whole file is successfully read + fread(source, sizeof(char), (usize)size, file); + + fclose(file); + } else { + ERROR("Cannot open \"" TERM_COLOR_YELLOW "%s" TERM_COLOR_RESET"\".", file_path); + return (Shader){.program = 0, .type = Shader_Error}; + } + + // Compile shader + INFO("Compiling shader \"" TERM_COLOR_YELLOW "%s" TERM_COLOR_RESET"\".", file_path); + char const* src_ptr = source; + gl->ShaderSource(shaderID, 1, &src_ptr , NULL); + gl->CompileShader(shaderID); + + // Check shader + gl->GetShaderiv(shaderID, GL_COMPILE_STATUS, &Result); + gl->GetShaderiv(shaderID, GL_INFO_LOG_LENGTH, &infolog_len); + if ( infolog_len > 0 ) { + char* msg = calloc((usize)infolog_len + 1, sizeof(char)); + gl->GetShaderInfoLog(shaderID, infolog_len, NULL, msg); + ERROR("Failed to compile shader: " TERM_COLOR_YELLOW "%s" TERM_COLOR_RESET, msg); + free(msg); + } + + free(source); + + return (Shader){.program = shaderID, .type = shader_type}; +} + + +// http://www.opengl-tutorial.org/beginners-tutorials/tutorial-2-the-first-triangle/ +u32 load_shaders( + const GladGLContext* gl, + const char* vertex_file_path, + const char* fragment_file_path) { + + i32 Result = GL_FALSE; + i32 infolog_len; + + // Create the shaders + const Shader vertexShader = compile_shader(vertex_file_path, Shader_Vertex); + const Shader fragmentShader = compile_shader(fragment_file_path, Shader_Fragment); + + // Link the program + INFO("Linking program"); + u32 ProgramID = gl->CreateProgram(); + + if (vertex_file_path != NULL) gl->AttachShader(ProgramID, vertexShader.program); + if (fragment_file_path != NULL) gl->AttachShader(ProgramID, fragmentShader.program); + + gl->LinkProgram(ProgramID); + + // Check the program + gl->GetProgramiv(ProgramID, GL_LINK_STATUS, &Result); + gl->GetProgramiv(ProgramID, GL_INFO_LOG_LENGTH, &infolog_len); + if ( infolog_len > 0 ){ + char* msg = calloc((usize)infolog_len + 1, sizeof(char)); + gl->GetShaderInfoLog(ProgramID, infolog_len, NULL, msg); + ERROR("Compiling shader: " TERM_COLOR_YELLOW "%s" TERM_COLOR_RESET, msg); + free(msg); + } + + gl->DetachShader(ProgramID, vertexShader.program); + gl->DetachShader(ProgramID, fragmentShader.program); + + //gl->DeleteShader(vertexShader.program); + //gl->DeleteShader(fragmentShader.program); + + return ProgramID; +} + +/* Returns a shader program */ +Shader compose_shader(Shader *shaders, usize shaders_len) { + const GladGLContext* gl = GLOBAL_PLATFORM->window->context; + i32 Result = GL_FALSE; + + if (shaders_len == 0) { + ERROR("No shaders provided!"); + return (Shader){.program = 0, .type = Shader_Error}; + } + + u32 prog = gl->CreateProgram(); + + if (prog == 0) { + ERROR("Failed to create program!"); + return (Shader){.program = 0, .type = Shader_Error}; + } + + for (usize i = 0; i < shaders_len; i++) { + gl->AttachShader(prog, shaders[i].program); + INFO("Attaching shader %d to %d", shaders[i].program, prog); + } + + gl->LinkProgram(prog); + + // Check the program + gl->GetProgramiv(prog, GL_LINK_STATUS, &Result); + if (Result != GL_TRUE) { + // Get the size of the log + i32 log_len = 0; + i32 msg_len = 0; + gl->GetProgramiv(prog, GL_INFO_LOG_LENGTH, &log_len); + char* msg = calloc((usize)log_len + 1, sizeof(char)); + + // Copy the log message(s) + gl->GetProgramInfoLog(prog, log_len, &msg_len, msg); + + ERROR("(Compose) Compiling shader:\n" TERM_COLOR_YELLOW "%s" TERM_COLOR_RESET "\n", msg); + free(msg); + } + + for (usize i = 0; i < shaders_len; i++) { + gl->DetachShader(prog, shaders[i].program); + } + + return (Shader){.program = prog, .type = Shader_Program}; +} + +/* Free up resources associated with `shader` */ +void shaders_delete(Shader* shader, usize shader_len) { + const GladGLContext* gl = GLOBAL_PLATFORM->window->context; + + for (usize i = 0; i < shader_len; i++) { + gl->DeleteShader(shader[i].program); + } +} + +GLenum ShaderBuffer_get_gl_access(u64 flags) { + const ShaderBufferFlag access = ShaderBuffer_get_access_type(flags); + + switch(flags & 0b111) { + case ShaderBuffer_AccessFrequency_stream: + switch(access) { + case ShaderBuffer_AccessType_draw: return GL_STREAM_DRAW; + case ShaderBuffer_AccessType_read: return GL_STREAM_READ; + case ShaderBuffer_AccessType_copy: return GL_STREAM_COPY; + default: return GL_STREAM_DRAW; + } + case ShaderBuffer_AccessFrequency_static: + switch(access) { + case ShaderBuffer_AccessType_draw: return GL_STATIC_DRAW; + case ShaderBuffer_AccessType_read: return GL_STATIC_READ; + case ShaderBuffer_AccessType_copy: return GL_STATIC_COPY; + default: return GL_STATIC_DRAW; + } + case ShaderBuffer_AccessFrequency_dynamic: + switch(access) { + case ShaderBuffer_AccessType_draw: return GL_DYNAMIC_DRAW; + case ShaderBuffer_AccessType_read: return GL_DYNAMIC_READ; + case ShaderBuffer_AccessType_copy: return GL_DYNAMIC_COPY; + default: return GL_DYNAMIC_DRAW; + } + default: return GL_STATIC_DRAW; + } +} + +RenderObject RenderObject_new( + Shader* shader, + u32 texture, + ShaderBuffer *restrict buffers, usize num_buffers) { + + GladGLContext *gl = GLOBAL_PLATFORM->window->context; + RenderObject o; + + gl->GenVertexArrays(1, &(o.vao)); + gl->BindVertexArray(o.vao); + + /* For each buffer in the shader, */ + /* The shader should be generalied, */ + for (usize i = 0; i < num_buffers; i++) { + const usize sz = buffers[i].size_elem * buffers[i].count; + const u32 b_gl_type = ShaderBuffer_get_gl_type(buffers[i].buffertype); + + gl->GenBuffers(1, &(buffers[i].buffername)); + gl->BindBuffer(b_gl_type, buffers[i].buffername); + gl->BufferData(b_gl_type, (isize)sz, buffers[i].data, ShaderBuffer_get_gl_accesstype(buffers[i].buffertype)); + } + + o.shader = *shader; + o.texture = texture; + o.buffer = buffers; + o.buffer_len = num_buffers; + o.mvp = gl->GetUniformLocation(o.shader.program, "MVP"); + + // It is very much a non-issue if we don't find the model view projection in + // the shader. In fact, it is removed from a shader program if it is not used. + // TODO: Add common uniforms, should be a list of strings (uniform name) & + // their locations (i32), such as + // * mouse coords, + // * time, + // * delta time, + // * modelviewprojection, + // * window size. + // These should be added to the RenderObject, if found. + + // if (o.mvp == -1) { + // WARN("Unable to find \"MVP\" input in shader program"); + //} + + gl->BindVertexArray(0); + + + return o; +} + +ShaderType guess_shadertype_from_filename(const char *restrict fname) { + const usize path_len = strlen(fname); + + if (path_len <= 4) { + ERROR("Unable to determine shader type from suffix! (%s)", fname); + return Shader_Error; + } + + if (!strncmp(".vert", &fname[path_len - 5], 5)) { return Shader_Vertex; } + if (!strncmp(".frag", &fname[path_len - 5], 5)) { return Shader_Fragment; } + + return Shader_Error; +} diff --git a/src/hashmap.c b/src/hashmap.c new file mode 100644 index 0000000..653b615 --- /dev/null +++ b/src/hashmap.c @@ -0,0 +1,5 @@ +#include + +/* Currently, this is a "works, but very poorly" placeholder implementation. + * Should be avoided in practice */ +usize lolhash(const usize s, usize v) { return v % s; } diff --git a/src/include/daw/daw.h b/src/include/daw/daw.h new file mode 100644 index 0000000..acbbcd9 --- /dev/null +++ b/src/include/daw/daw.h @@ -0,0 +1,73 @@ +#ifndef ENGINE_ENGINE_H +#define ENGINE_ENGINE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct { + u32 texture_id; + i32 x, y, w, h; +} RenderUnit; + +#include + +typedef struct Instance { + + Window* window; + bool quit; + + u64 frame; + u16 fps_target; + + /* TODO: Move input ctx/bindings to window */ + /* TODO: Move cam to window->renderer */ + Camera *cam; + + /* Global resources that live from engine_init to engine_free */ + Resources* resources; + + memory* mem; + +} Instance; + +/* Essential functions */ +Instance* engine_init(const char* windowtitle, i32 windowWidth, i32 windowHeight, + const u32 flags, + const usize initial_memory); + +i32 engine_run(Instance* p, StateType initial_state, void* state_arg); + +void engine_stop(Instance* p); + +/* Utility functions */ +void engine_fps_max(Instance* p, u16 cap); + +void render_set_zoom(f32 new_zoom); +void render_adjust_zoom(f32 diff); +void render_add_unit(RenderUnit* u); + +/* move this */ +void delay(uint64_t ms); + +// file operations +usize f_get_sz(FILE* f); + + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/include/daw/dltools.h b/src/include/daw/dltools.h new file mode 100644 index 0000000..d9c74ee --- /dev/null +++ b/src/include/daw/dltools.h @@ -0,0 +1,23 @@ +#ifndef DLTOOLS_H +#define DLTOOLS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/* Utility functions for handling runtime linked shared libraries */ +bool dynamic_library_close(void* shared_library); +void* dynamic_library_open(const char* library_path); +void* dynamic_library_reload(void* shared_library, const char* library_path); + +/* Returns the address of symbol in the provided shared_library handle. + * NULL on error*/ +void* dynamic_library_get_symbol(void* shared_library, const char* symbol); +char* dynamic_library_get_error(void); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/include/daw/input.h b/src/include/daw/input.h new file mode 100644 index 0000000..9fbcd15 --- /dev/null +++ b/src/include/daw/input.h @@ -0,0 +1,108 @@ +#ifndef ENGINE_CTRL_INPUT_H +#define ENGINE_CTRL_INPUT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +typedef void input_callback_t(void*); +typedef i32 scancode_t; +typedef i32 keycode_t; + +typedef enum InputType { + InputType_error = 0, + InputType_action, + InputType_state, + InputType_range, /* TBD */ +} InputType; + +typedef union action_t { + InputType type; + + struct { + InputType type; + input_callback_t* callback; + char* callback_str; + } action; + + struct { + InputType type; + input_callback_t* activate; + input_callback_t* deactivate; + char* activate_str; + char* deactivate_str; + } state; +} action_t; + +typedef struct binding_t { + action_t action; + + // Change type depending on input handling back-end. like u16 for GLFW_KEY + keycode_t keycode; + keycode_t keycode_alt; + + u64 since_last_activation; +} binding_t; + +typedef struct i_ctx { + + // Current mouse position + ivec2 mouse_pos; + + usize len; + binding_t* bindings; +} i_ctx; + +void i_ctx_t_free(i_ctx* c); +/* Executes all callbacks that has been pushed onto the callstack and resets the + * callstack */ +void i_flush_bindings(u64 dt, usize numcalls, input_callback_t* c[], void* state_mem); +action_t i_get_action(const i_ctx* restrict ctx, u64 time, keycode_t keycode); + +void key_callback(void* window, int key, int scancode, int action, int mods); + +void i_ctx_push(i_ctx* ctx); +void i_ctx_pop(void); +void i_ctx_reset(void); + +/* Finds and updates the keycode of a binding with the given action in ctx */ +void i_bind_ctx(i_ctx* c, keycode_t s, action_t* a); +void i_bind_ctx_alt(i_ctx* c, keycode_t s, action_t* a); + +/* Update the keycode of a binding */ +void i_bind(binding_t* b, keycode_t s); +void i_bind_alt(binding_t* b, keycode_t s); + +#define BindAction(key, altkey, f_action) \ + (binding_t) { \ + .action = (action_t){.action = \ + { \ + .type = InputType_action, \ + .callback = (input_callback_t*)&f_action, \ + .callback_str = strdup(#f_action), \ + }}, \ + .keycode = key, .keycode_alt = altkey, .since_last_activation = 0 \ + } + +#define BindState(key, altkey, f_activate, f_deactivate) \ + (binding_t) { \ + .action = \ + (action_t){.state = \ + { \ + .type = InputType_state, \ + .activate = (input_callback_t*)&f_activate, \ + .deactivate = (input_callback_t*)&f_deactivate, \ + .activate_str = strdup(#f_activate), \ + .deactivate_str = strdup(#f_deactivate), \ + }}, \ + .keycode = key, .keycode_alt = altkey, .since_last_activation = 0 \ + } + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/include/daw/keycodes.h b/src/include/daw/keycodes.h new file mode 100644 index 0000000..869809f --- /dev/null +++ b/src/include/daw/keycodes.h @@ -0,0 +1,93 @@ +#ifndef ENGINE_CTRL_SCANCODES_H + +#ifdef __cplusplus +extern "C" { +#endif + +// We want to reserve the following bytes marked with X for MODS, +// one bit per MODIFIER (shift, control, alt, super): +// XXXX 0000 0000 0000 0000 0000 0000 0000 +// +// We want to reserve the following bytes marked with X for ACTIONS +// enumerate by action type, pressed, released, repeat +// 0000 0000 XXXX 0000 0000 0000 0000 0000 +// +// We want to reserve the following bytes marked with X for the KEYCODE +// 0000 0000 0000 0000 XXXX XXXX XXXX XXXX +#define KEY_MOD(x) (1 << (32-4 + x)) +#define ACTION(x) ((1 << (32-8)) + x) + +typedef enum { + KEY_SPACE = ' ', + KEY_APOSTROPHE = '\'', + KEY_COMMA = ',', + KEY_MINUS = '-', + KEY_PERIOD = '.', + KEY_SLASH = '/', + + KEY_0 = '0', + KEY_1 = '1', + KEY_2 = '2', + KEY_3 = '3', + KEY_4 = '4', + KEY_5 = '5', + KEY_6 = '6', + KEY_7 = '7', + KEY_8 = '8', + KEY_9 = '9', + + KEY_SEMICOLON = ';', + KEY_EQUAL = '=', + + KEY_A = 'A', + KEY_B = 'B', + KEY_C = 'C', + KEY_D = 'D', + KEY_E = 'E', + KEY_F = 'F', + KEY_G = 'G', + KEY_H = 'H', + KEY_I = 'I', + KEY_J = 'J', + KEY_K = 'K', + KEY_L = 'L', + KEY_M = 'M', + KEY_N = 'N', + KEY_O = 'O', + KEY_P = 'P', + KEY_Q = 'Q', + KEY_R = 'R', + KEY_S = 'S', + KEY_T = 'T', + KEY_U = 'U', + KEY_V = 'V', + KEY_W = 'W', + KEY_X = 'X', + KEY_Y = 'Y', + KEY_Z = 'Z', + + KEY_LEFT_BRACKET = '[', + KEY_BACKSLASH = '\\', + KEY_RIGHT_BRACKET = ']', + KEY_GRAVE_ACCENT = '`', + + ACTION_PRESS = ACTION(0), + ACTION_RELEASE = ACTION(1), + ACTION_REPEAT = ACTION(2), + + MOD_SHIFT = KEY_MOD(0), + MOD_CONTROL = KEY_MOD(1), + MOD_ALT = KEY_MOD(2), + MOD_SUPER = KEY_MOD(3), +} KeyCode; +#define MOD_SHFT MOD_SHIFT +#define MOD_CTRL MOD_CONTROL +#define MOD_SUPR MOD_SUPER + +#undef ACTION +#undef KEY_MOD + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/include/daw/logging.h b/src/include/daw/logging.h new file mode 100644 index 0000000..65ca827 --- /dev/null +++ b/src/include/daw/logging.h @@ -0,0 +1,56 @@ +#ifndef LOGGING_H +#define LOGGING_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include +#include + +#include + +#if defined __linux__ || defined __APPLE__ +#define TERM_COLOR_RESET "\033[0m" +#define TERM_COLOR_RED "\033[31m" +#define TERM_COLOR_GREEN "\033[32m" +#define TERM_COLOR_YELLOW "\033[33m" +#define TERM_COLOR_BLUE "\033[34m" +#define TERM_COLOR_PURPLE "\033[35m" +#else +#define TERM_COLOR_RESET +#define TERM_COLOR_RED +#define TERM_COLOR_GREEN +#define TERM_COLOR_YELLOW +#define TERM_COLOR_BLUE +#define TERM_COLOR_PURPLE +#endif + +#define STR(a) (#a) + +void _log(FILE* stream, const char* prefix, const char* fmt, va_list ap); + +void LOG(const char* fmt, ...); + +void INFO_(const char* fmt, ...); +void INFO(const char* fmt, ...); + +#ifdef _DEBUG +#define DEBUG(fmt, ...) __DEBUG(__FILE__, __LINE__, __func__, fmt, __VA_ARGS__) +void __DEBUG(const char* file, const i32 line, const char* func, + const char* fmt, ...); +#else +#define DEBUG(...) +#endif + +void WARN(const char* fmt, ...); + +void ERROR(const char* fmt, ...); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/include/daw/memory.h b/src/include/daw/memory.h new file mode 100644 index 0000000..bc10fe2 --- /dev/null +++ b/src/include/daw/memory.h @@ -0,0 +1,31 @@ +#ifndef MEMORY_H +#define MEMORY_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +typedef struct memory { + void* data; + usize size; + usize pos; + usize free; +} memory; + +memory* memory_new(usize max_size); + +/* Returns a pointer to the allocated data */ +void* memory_allocate(memory* mem, usize size); + +memory memory_init(void* data, usize size); + +void memory_free(memory* mem, usize size); + +void memory_clear(memory* mem); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/include/daw/model.h b/src/include/daw/model.h new file mode 100644 index 0000000..a78c4b8 --- /dev/null +++ b/src/include/daw/model.h @@ -0,0 +1,30 @@ +#ifndef ENGINE_RESOURCES_MODEL_H +#define ENGINE_RESOURCES_MODEL_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +typedef enum { + Model_error, + Model_obj, +} ModelType; + +typedef struct { + ModelType format; + + u32 m_uiVAO; + u32 m_uiVBO; + u32 m_uiIBO; + unsigned m_uiNumIndices; +} Model; + +#include +Model load_model(const Asset_ModelSpec *restrict ms); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/include/daw/platform.h b/src/include/daw/platform.h new file mode 100644 index 0000000..c837c63 --- /dev/null +++ b/src/include/daw/platform.h @@ -0,0 +1,56 @@ +#ifndef PLATFORM_H +#define PLATFORM_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include +// TODO: We only need the window once all the garbage in Instance is cleaned up. +#include + +#define DAW_WINDOW_VSYNC (1 << 0) +#define DAW_WINDOW_FULLSCREEN (1 << 1) +#define DAW_WINDOW_RESIZEABLE (1 << 2) + +// Whether or not it is clever to force API consistency using a struct like this +// can be debated, at the time of writing it seemed like a smart idea. + +// Platform libraries must implement a struct Platform: +struct Platform { + /* Initialize a window for the given platform. The rendering backend should + * also be initialized here. + * Parameters: + * const char* title: window title. + * ivec2 windowsize: the size in pixels for the new window. + * const u32 flags: window flags, such as static size and fullscreen. + * The flags are platform agnostic and needs to be + * converted to the specific library + * Returns: + * A pointer to a struct Window, NULL on error. + */ + Window* (*window_init)(const char *restrict title, ivec2 windowsize, const u32 flags); + + /* Destroy, close, and free up resources related to the window and the + * platform library specific resources. + */ + void (*window_destroy)(Window *restrict w); + + /* Resize the given window. Resize callbacks are handled by the wrapper + * implementation. + */ + void (*window_resize)(Window *restrict window, int width, int height); + + /* Return true if the platform has ordered the window to exit. */ + bool (*window_should_close)(Window *restrict w); + + /* Poll events on the window from the operating system. */ + void (*window_poll)(void); +}; + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/include/daw/platform_glfw.h b/src/include/daw/platform_glfw.h new file mode 100644 index 0000000..1136a9f --- /dev/null +++ b/src/include/daw/platform_glfw.h @@ -0,0 +1,32 @@ +#ifndef ENGINE_RENDERING_PLATFORM_GLFW_H +#define ENGINE_RENDERING_PLATFORM_GLFW_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include +#include +#include + +Window* window_init_glfw(const char *restrict windowtitle, ivec2 windowsize, const u32 flags); +void window_destroy_glfw(Window *restrict w); +void window_resize_glfw(Window *restrict window, int width, int height); +bool window_should_close_glfw(Window *restrict window); +void window_poll_glfw(void); + +const struct Platform Platform_GLFW = { + .window_init = window_init_glfw, + .window_destroy = window_destroy_glfw, + .window_resize = window_resize_glfw, + .window_should_close = window_should_close_glfw, + .window_poll = window_poll_glfw, +}; + +#ifdef __cplusplus +} +#endif +#endif + diff --git a/src/include/daw/rendering.h b/src/include/daw/rendering.h new file mode 100644 index 0000000..0da97f5 --- /dev/null +++ b/src/include/daw/rendering.h @@ -0,0 +1,288 @@ +#ifndef ENGINE_RENDERING_RENDERING_H +#define ENGINE_RENDERING_RENDERING_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#include + +/* Definitions */ +#define RGBA(_r, _g, _b, _a) ((Engine_color){.r = _r, .g = _g, .b = _b, .a = _a}) +#define RGB(_r, _g, _b) RGBA(_r, _g, _b, 0xFF) + +/* Types */ +/* TODO: Cleanup these types. */ +typedef struct { + u8 r; + u8 g; + u8 b; + u8 a; +} Engine_color; + +typedef struct { + /* Maybe implement types, such as `atlas` (default), `standalone`, or + * something idk. */ + u32 id; + i32 width; + i32 height; +} Texture; + +typedef struct { + u32 texture_id; + ivec2 coord; +} Sprite; + +typedef enum { + Shader_Error, + + Shader_Program, + Shader_Vertex, + Shader_Tessellation, + Shader_Geometry, + Shader_Fragment, + Shader_Compute, +} ShaderType; + +typedef struct { + /* Shader proram */ + ShaderType type; + u32 program; +} Shader; + +typedef struct { + vec3 position; + vec3 size; + mat4 rotation; +} Transform; + +typedef enum { + ShaderBufferFlag_none = 0x00, + + // First 3 bytes describe the access frequency. + ShaderBuffer_AccessFrequency_stream = 1, // 1 + ShaderBuffer_AccessFrequency_static = 2, // 2 + ShaderBuffer_AccessFrequency_dynamic = 3, // 3 + + // Next 3 bytes describe the access type. + ShaderBuffer_AccessType_draw = 1 << 3, // 8 + ShaderBuffer_AccessType_read = 2 << 3, // 16 + ShaderBuffer_AccessType_copy = 3 << 3, // 24 + + // Next 3 bytes describe the buffer type + ShaderBuffer_Type_vertexData = 1 << 6, // 64 + ShaderBuffer_Type_vertexPosition = 2 << 6, // 128 + ShaderBuffer_Type_vertexIndex = 3 << 6, // 192 + + // Next 4 bytes are designated for the data type + ShaderBuffer_DataType_nil = 1 << 9, // 512 + + ShaderBuffer_DataType_f32 = 2 << 9, // 1024 + ShaderBuffer_DataType_f64 = 3 << 9, // 1536 + + ShaderBuffer_DataType_i8 = 4 << 9, // 2048 + ShaderBuffer_DataType_i16 = 5 << 9, // 2560 + ShaderBuffer_DataType_i32 = 6 << 9, // 3072 + ShaderBuffer_DataType_i64 = 7 << 9, // 3584 + + ShaderBuffer_DataType_u8 = 8 << 9, // 4096 + ShaderBuffer_DataType_u16 = 9 << 9, // 4608 + ShaderBuffer_DataType_u32 = 10 << 9, // 5120 + ShaderBuffer_DataType_u64 = 11 << 9, // 5632 +} ShaderBufferFlag; + +typedef struct { + // The backend ID, ie. glGenBuffer(numBufferObjects, &this->buffername) + u32 buffername; + // Array, access, and data, type. + u32 buffertype; + // Buffer size of `data`. To get the size of the actual data, size_elem * count + usize size; + // Number of elements + usize count; + // components per generic vertex attribute (ie, 3 for RGB, 2 for UV) + usize components; + // size of each element + usize size_elem; + // Pointer to the data + void* data; +} ShaderBuffer; + +// SHADERBUFFER_NEW is a constructor that takes the +// * type T, as one of f32, f64, i8, i16, i32, i64, u8, u16, u32, or u64. +// * COUNT, number of elements in the buffer +// * COMPONENTS, number of elements to be grouped together, ie. 3 for a vec3. +// * DATA, the buffer (it is pointed to automatically) +// * FLAGS, are low-level GL flags that hints the access frequency, the access +// type, and what buffer type it is, such as "data" or an index buffer. +#define SHADERBUFFER_NEW(T, COUNT, COMPONENTS, DATA, FLAGS) \ + (ShaderBuffer){ \ + .buffername = 0, \ + .buffertype = ShaderBuffer_DataType_##T | FLAGS, \ + .size = COUNT * sizeof(T), \ + .count = COUNT, \ + .components = COMPONENTS, \ + .size_elem = sizeof(T), \ + .data = DATA, \ + } + +typedef struct { + /* Shader proram */ + Shader shader; + /* Vertex Array Object */ + u32 vao; + + /* MVP (a uniform from the shader). + * This could also probably be generalized */ + i32 mvp; + + // The texture ID, glBindTextures(target, &this->texture) + u32 texture; + + // Number of buffers + usize buffer_len; + + // The vertex buffer is also just a buffer + ShaderBuffer* buffer; +} RenderObject; + +typedef struct { + // Index of the model in the RenderBatch models buffer + usize model_idx; + // The transformation of the model + Transform transform; +} BatchModelInstance; + +typedef struct { + // Size of models buffer + usize msize; + // number of models in the `models` buffer + usize mcount; + // Pointers to original models in this batch + RenderObject **models; + + // Size of instance buffer + usize inst_size; + // number of instances in the `instances` buffer + usize inst_count; + // Pointers to original models in this batch + BatchModelInstance *instances; + + // The rendered destination object + RenderObject renderobj; +} RenderBatch; + +typedef enum { + Camera_Perspective, + Camera_Orthogonal, +} CameraType; + +typedef struct { + /* Position of the camera in world-space. */ + vec3 pos; + + /* The viewing direction of the camera, relative to the camera. */ + vec3 dir; + + /* Perspective matrix. Initialize with r_perspective_ortho or r_perspective. */ + /* Alternatively, use `glm_perspective` or `glm_ortho`. */ + mat4 per; + + /* Used to re-calculate the perspective matrix when resizing the window */ + CameraType type; + /* Yes, could use a singular "f32 arg", but this is more extendable in the + * future. */ + union { + struct {f32 fov;} perspective; + struct {f32 sz;} orthogonal; + } parameters; + +} Camera; + +usize ShaderBufferDataType_size(u16 flags); + +ShaderBufferFlag ShaderBuffer_get_access_frequency(u64 flags); +ShaderBufferFlag ShaderBuffer_get_access_type(u64 flags); +ShaderBufferFlag ShaderBuffer_get_type(u64 flags); +ShaderBufferFlag ShaderBuffer_get_data_type(u64 flags); + +/* Conversion to GL types */ +u32 ShaderBuffer_get_gl_type(u64 flags); +u32 ShaderBuffer_get_gl_accesstype(u64 flags); +u32 ShaderBuffer_get_gl_datatype(u64 flags); + +/* Rendering functions */ +void render_begin(Window* w); +void render_present(Window* w); +void drawcall_reset(void); +void render(Window* w); + +/* Misc */ +void r_perspective(f32 fov, Camera *c); +void r_perspective_ortho(f32 sz, Camera *c); + +void r_set_camera(Camera* c); +void r_reset_camera(Camera* c); + +//void window_size_callback(GLFWwindow* window, i32 width, i32 height); + +void engine_draw_sprite(Sprite* s, ivec2* pos, f32 scale); +void engine_draw_sprite_ex(Sprite* s, ivec2* pos, f32 scale, + Engine_color colormod); +void engine_draw_model(RenderObject* o, vec3 pos); + +Sprite sprite_new(u64 tid, u8 coord); + +typedef enum { + RenderDrawCallType_Text, + RenderDrawCallType_Sprite, + RenderDrawCallType_Model, +} RenderDrawCallType; + +typedef struct { + RenderDrawCallType type; + union { + void* data; + struct { + Sprite* sprite; + i32 x; + i32 y; + f32 scale; + } sprite; + struct { + RenderObject* model; + vec3 pos; + f32 scale; + } model; + } data; +} RenderDrawCall; + +// TODO make all the shader buffers a list + +RenderObject RenderObject_new( + Shader* shader, + u32 texture, + ShaderBuffer *restrict buffers, usize num_buffers); + +int renderbatch_new(RenderBatch* renderbatch, usize count); +i32 renderbatch_add(RenderBatch* renderbatch, RenderObject* obj, Transform* t); +void renderbatch_transform(RenderBatch* renderbatch, usize obj_idx, Transform* t); +int renderbatch_refresh(RenderBatch* renderbatch); + +Shader compile_shader(const char* file_path, const ShaderType shader_type); +Shader compose_shader(Shader *shaders, usize shaders_len); +void shaders_delete(Shader* shader, usize shader_len); + +u32 ComposeShader(u32 *shaders, usize shaders_len); + +ShaderType guess_shadertype_from_filename(const char *restrict fname); + +Texture createTextureFromImageData(unsigned char* image_data, i32 width, i32 height, u8 components); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/include/daw/resources.h b/src/include/daw/resources.h new file mode 100644 index 0000000..90f3b16 --- /dev/null +++ b/src/include/daw/resources.h @@ -0,0 +1,138 @@ +#ifndef ENGINE_RESOURCES_H +#define ENGINE_RESOURCES_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +// TODO +/* We need some "global resources", available to all states. + * These are resources used throughout the applications lifetime, such as common + * fonts, GUI frames, button background images. + * + * We need to define state-specific resources as well. + * - Can both be defined alike? + * If we lazy-load all resources we can get away with a lot. + * Maybe use fall-back resources? like the missing source texture, and an ugly + * font? + * - Then declare to the engine, in the main function for the game, that these + * resources are to be made available throughout? + * - Shaders, oh boy oh shaders. + * We need to make some meta-shader declaration, so we can declare a set of + * shaders, that are used to link with other shaders, s.t. we can free up the + * shaders after compilation. + * - Make all resource specifications a union. + * */ + +enum Asset { + Asset_error, + Asset_audio, + Asset_font, + Asset_shader, + Asset_shaderprog, + Asset_texture, + Asset_model, +}; + +typedef struct { + enum Asset type; + const char* path; +} Asset_AudioSpec; + +typedef struct { + enum Asset type; + const char* path; +} Asset_FontSpec; + +// if a shader is declared GLOBALLY, we should not destroy it when done with the +// "main" shader compilations. +typedef struct { + enum Asset type; + // Assume shader type from filename + const char* path; +} Asset_ShaderSpec; + +// Use list of gluint shader program ids to link against. This translates to a +// call to compose_shader. +typedef struct { + enum Asset type; + const u32* shader; + const usize shader_len; +} Asset_ShaderProgramSpec; + +typedef struct { + enum Asset type; + const char* path; + /* Bits per component, set to zero if you don't want to change the format. */ + i32 bpc; +} Asset_TextureSpec; + +typedef struct { + enum Asset type; + const char* path; +} Asset_ModelSpec; + +typedef union { + enum Asset type; + Asset_AudioSpec audio; + Asset_FontSpec font; + Asset_ShaderSpec shader; + Asset_ShaderProgramSpec shaderprog; + Asset_TextureSpec texture; + Asset_ModelSpec model; +} asset_t; + +#include + +// The resource spec +typedef struct { + /* Assorted asset specification, makes reloading them easier. */ + usize assets_len; + asset_t* assets; + + /* Translation from `assets`'s indices to type-specific loaded assets: */ + usize* get; // Let r=Resources, then use as: `r.shader[ r.get[ MyShader ] ]` + + /* Loaded assets */ + usize shader_len; + Shader* shader; + + usize texture_len; + Texture* texture; + + usize model_len; + Model* model; +} Resources; + +#define TextureDefinition(_path, ...) unimplemented +#define Resource_AudioDefinition(_path, ...) unimplemented +#define Declare_Shader(PATH) (const asset_t){.shader = {.type = Asset_shader, .path=PATH}} +#define Declare_ShaderProgram(SHADERS, NUMSHADERS) (const asset_t){.shaderprog = {.type = Asset_shaderprog, .shader=SHADERS, .shader_len=NUMSHADERS}} +#define Declare_Texture(PATH) (const asset_t){.texture = {.type = Asset_texture, .path=PATH, .bpc=0}} + +void* get_asset(Resources* r, u32 idx); + +/* Each of resource_load_font, resource_load_texture, and resource_load_audio + * loads a given resource into the engines memory and returns an identifier. + */ +//isize resource_load_font(Asset_FontSpec font_def); +//isize resource_load_texture(Asset_TextureSpec texture_def); +//isize resource_load_audio(Asset_AudioSpec audio_def); + +// Loads all resources specified in `assets` and populates corresponding +// resources members. +i32 resources_load(Resources *resources); + +/* Makes a resource globally available. This must be called **BEFORE** any call + * to `engine_run` */ +isize resource_make_global(isize resource_id); + +#include + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/include/daw/scancodes.h b/src/include/daw/scancodes.h new file mode 100644 index 0000000..5b18833 --- /dev/null +++ b/src/include/daw/scancodes.h @@ -0,0 +1,89 @@ +#ifndef ENGINE_CTRL_SCANCODES_H + +#ifdef __cplusplus +extern "C" { +#endif + +// We want to reserve the following bytes marked with X for MODS, +// one bit per MODIFIER (shift, control, alt, super): +// XXXX 0000 0000 0000 0000 0000 0000 0000 +// +// We want to reserve the following bytes marked with X for ACTIONS +// enumerate by action type, pressed, released, repeat +// 0000 0000 XXXX 0000 0000 0000 0000 0000 +// +// We want to reserve the following bytes marked with X for the SCANCODE +// 0000 0000 0000 0000 XXXX XXXX XXXX XXXX +#define MOD(x) 1 << (32-4 + x) +#define ACTION(x) (1 << (32-8)) + x + +typedef enum { + SCAN_KEY_SPACE = ' ', + SCAN_KEY_APOSTROPHE = '\'', + SCAN_KEY_COMMA = ',', + SCAN_KEY_MINUS = '-', + SCAN_KEY_PERIOD = '.', + SCAN_KEY_SLASH = '/', + + SCAN_KEY_0 = '0', + SCAN_KEY_1 = '1', + SCAN_KEY_2 = '2', + SCAN_KEY_3 = '3', + SCAN_KEY_4 = '4', + SCAN_KEY_5 = '5', + SCAN_KEY_6 = '6', + SCAN_KEY_7 = '7', + SCAN_KEY_8 = '8', + SCAN_KEY_9 = '9', + + SCAN_KEY_SEMICOLON = ';', + SCAN_KEY_EQUAL = '=', + + SCAN_KEY_A = 'A', + SCAN_KEY_B = 'B', + SCAN_KEY_C = 'C', + SCAN_KEY_D = 'D', + SCAN_KEY_E = 'E', + SCAN_KEY_F = 'F', + SCAN_KEY_G = 'G', + SCAN_KEY_H = 'H', + SCAN_KEY_I = 'I', + SCAN_KEY_J = 'J', + SCAN_KEY_K = 'K', + SCAN_KEY_L = 'L', + SCAN_KEY_M = 'M', + SCAN_KEY_N = 'N', + SCAN_KEY_O = 'O', + SCAN_KEY_P = 'P', + SCAN_KEY_Q = 'Q', + SCAN_KEY_R = 'R', + SCAN_KEY_S = 'S', + SCAN_KEY_T = 'T', + SCAN_KEY_U = 'U', + SCAN_KEY_V = 'V', + SCAN_KEY_W = 'W', + SCAN_KEY_X = 'X', + SCAN_KEY_Y = 'Y', + SCAN_KEY_Z = 'Z', + + SCAN_KEY_LEFT_BRACKET = '[', + SCAN_KEY_BACKSLASH = '\\', + SCAN_KEY_RIGHT_BRACKET = ']', + SCAN_KEY_GRAVE_ACCENT = '`', + + SCAN_ACTION_PRESS = ACTION(0), + SCAN_ACTION_RELEASE = ACTION(1), + SCAN_ACTION_REPEAT = ACTION(2), + + SCAN_MOD_SHIFT = MOD(0), + SCAN_MOD_CONTROL = MOD(1), + SCAN_MOD_ALT = MOD(2), + SCAN_MOD_SUPER = MOD(3), +} ScanCode; +#undef ACTION +#undef MOD + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/include/daw/state.h b/src/include/daw/state.h new file mode 100644 index 0000000..f98d5ff --- /dev/null +++ b/src/include/daw/state.h @@ -0,0 +1,41 @@ +#ifndef STATE_H +#define STATE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +typedef enum StateType { + STATE_null, +#define State(name) STATE_##name, +#include +#undef State + STATE_quit, +} StateType; + +extern const char* StateTypeStr[]; + +StateType (*State_updateFunc(StateType type))(f64, void*); + +void State_init(StateType type, memory* mem, void* arg); +void* State_free(StateType type, memory* mem); +StateType State_update(StateType type, f64 dt, memory* mem); + +/* Reloads shared object file associated with state */ +#ifdef DAW_BUILD_HOTRELOAD +#include +bool State_reload(StateType type, i_ctx** ctx, usize ctx_len); +bool state_refresh_input_ctx(void* lib, i_ctx** ctx, usize ctx_len); + +#else +#define State_reload(_, _0, _1) true +#define state_refresh_input_ctx(_0, _1, _2) true + +#endif + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/include/daw/texture.h b/src/include/daw/texture.h new file mode 100644 index 0000000..5904fc1 --- /dev/null +++ b/src/include/daw/texture.h @@ -0,0 +1,17 @@ +#ifndef TEXTURE_H +#define TEXTURE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +Texture load_texture(const Asset_TextureSpec *restrict ts); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/include/daw/types.h b/src/include/daw/types.h new file mode 100644 index 0000000..a7d794d --- /dev/null +++ b/src/include/daw/types.h @@ -0,0 +1,41 @@ +#ifndef ENGINE_TYPES_H +#define ENGINE_TYPES_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +/* Signed */ +typedef int8_t i8; +typedef int16_t i16; +typedef int32_t i32; +typedef int64_t i64; + +/* Unsigned */ +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; + +/* floating points */ +typedef float f32; +typedef double f64; + +/* sizes */ +#if __x86_64__ || __ppc64__ || _WIN64 +typedef u64 usize; +typedef i64 isize; +#else +typedef u32 usize; +typedef i32 isize; +#endif + +typedef bool(predicate_t)(const void*); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/include/daw/utils.h b/src/include/daw/utils.h new file mode 100644 index 0000000..5f735e9 --- /dev/null +++ b/src/include/daw/utils.h @@ -0,0 +1,43 @@ +#ifndef ENGINE_UTILS_H +#define ENGINE_UTILS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#define MIN(a, b) ((a < b) ? (a) : (b)) +#define MAX(a, b) ((a > b) ? (a) : (b)) + +#define MASK_TL (1 << 0) +#define MASK_T (1 << 1) +#define MASK_TR (1 << 2) +#define MASK_L (1 << 3) +#define MASK_C (1 << 4) +#define MASK_R (1 << 5) +#define MASK_BL (1 << 6) +#define MASK_B (1 << 7) +#define MASK_BR (1 << 8) + +/* Linear interpolate */ +f32 lerp(f32 dt, f32 a, f32 b); +i32 int_lerp(f32 dt, i32 a, i32 b); + +/* Hashes */ +u32 hash(char* str); + +/* Masks surrounding tiles of a kernel size of 3x3 */ +/* In reality we only need 9 bits for this, but I think I had a reason for using + * i32 */ +i32* kernmap(const void* map, i32* dstmap, const ivec2 mapsize, + predicate_t* predicate); + +/* Returns an index from the given weights. */ +i32 pick_from_sample(const i32* weights, i32 len); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/include/daw/utils/btree.h b/src/include/daw/utils/btree.h new file mode 100644 index 0000000..1dd2023 --- /dev/null +++ b/src/include/daw/utils/btree.h @@ -0,0 +1,61 @@ +#ifndef BTREE_H +#define BTREE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#define BTREE_DEGREE_DEFAULT 4 + +#define BTREE_SIZE_MIN 8 +#define BTREE_SIZE_MAX 4096 + +#define BTREE_CMP_LT (-1) +#define BTREE_CMP_EQ (0) +#define BTREE_CMP_GT (1) + +struct btree; +struct btree_iter_t; + +/* elem_size: the size of the elements, typically `sizeof(struct )` + * t: degree of the btree, if you're in doubt, use `BTREE_SIZE_DEFAULT` + * cmp: comparison function, in order to support any operations on the tree. + * + * This function just calls `btree_new_with_allocator` with `free` and `malloc` + * as initializers. + */ +struct btree* btree_new(size_t elem_size, size_t t, + int (*cmp)(const void* a, const void* b)); + +/* Same as `btree_new`, except that it actually initializes a btree, but with + * the given allocators. + */ +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*)); + +void btree_free(struct btree** btree); + +void* btree_search(struct btree* btree, void* elem); +void btree_insert(struct btree* btree, void* elem); +int btree_delete(struct btree* btree, void* elem); + +void btree_print(struct btree* btree, void (*print_elem)(const void*)); + +void* btree_first(struct btree* btree); +void* btree_last(struct btree* btree); + +size_t btree_size(struct btree* btree); + +struct btree_iter_t* btree_iter_t_new(struct btree* tree); +void btree_iter_t_reset(struct btree* tree, struct btree_iter_t** it); + +void* btree_iter(struct btree* tree, struct btree_iter_t* iter); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/include/daw/utils/fov.h b/src/include/daw/utils/fov.h new file mode 100644 index 0000000..74ccee8 --- /dev/null +++ b/src/include/daw/utils/fov.h @@ -0,0 +1,28 @@ +#ifndef ENGINE_FOV_H +#define ENGINE_FOV_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +/* `fov_shadowcast`: */ +/* map: your 2D enum tile grid + * mapsize: x: width, y: height of the map + * visblocking: pointer to a function that returns `true` when receiving a + * pointer to a LOS blocking tile + * lightmap: [out] 2D lightmap, this is simply overwritten with the + * distance to the source. range: visibility range/radius. src: 2D + * point to calculate FOV from + * */ +void fov_shadowcast(const void* map, const ivec2 mapsize, + bool (*visblocking)(const void*), i32* lightmap, + const i32 range, const ivec2 src); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/include/daw/utils/hashmap.h b/src/include/daw/utils/hashmap.h new file mode 100644 index 0000000..15ec5f0 --- /dev/null +++ b/src/include/daw/utils/hashmap.h @@ -0,0 +1,61 @@ +#ifndef ENGINE_HASHMAP_H +#define ENGINE_HASHMAP_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include +#include +#include + +usize lolhash(const usize s, usize v); + +/* Define a linked list before using this */ +/* Example: DEFINE_LLIST(i32) */ +#define DEFINE_HASHMAP(type, lsize, cmp, type_to_int) \ + typedef DEFINE_LLIST(type); \ + typedef struct hashmap_##type { \ + usize size; \ + List_##type elems[64]; \ + } hashmap_##type; \ + \ + type* hashmap_##type##_lookup(hashmap_##type* hmap, const type* val) { \ + const usize idx = lolhash(64, type_to_int(val)); \ + List_##type* head = &hmap->elems[idx]; \ + while (head != NULL) { \ + if (!cmp(&(head->value), val)) return &(head->value); \ + head = head->next; \ + } \ + return NULL; \ + } \ + \ + void hashmap_##type##_insert(memory* m, hashmap_##type* hmap, \ + const type* val) { \ + const usize idx = lolhash(64, type_to_int(val)); \ + List_##type* head = &(hmap->elems[idx]); \ + \ + /* This is highly dependant on whether the memory is zero-initialized */ \ + if (!type_to_int(&(head->value))) { \ + memcpy(&(head->value), val, sizeof(type)); \ + return; \ + } \ + \ + while (head->next != NULL && cmp(&head->value, val)) { \ + head = head->next; \ + } \ + \ + if (!cmp(&head->value, val)) \ + memcpy(&(head->value), val, sizeof(type)); \ + else { \ + head->next = memory_allocate(m, sizeof(List_##type)); \ + memcpy(&(head->next->value), val, sizeof(type)); \ + } \ + } + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/include/daw/utils/list.h b/src/include/daw/utils/list.h new file mode 100644 index 0000000..04dda04 --- /dev/null +++ b/src/include/daw/utils/list.h @@ -0,0 +1,18 @@ +#ifndef ENGINE_LIST_H +#define ENGINE_LIST_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define DEFINE_LLIST(type) \ + struct List_##type { \ + type value; \ + struct List_##type* next; \ + /* Force the user to add `;` for style consistency */ \ + } List_##type + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/include/daw/utils/stack.h b/src/include/daw/utils/stack.h new file mode 100644 index 0000000..004e686 --- /dev/null +++ b/src/include/daw/utils/stack.h @@ -0,0 +1,33 @@ +#ifndef STACK_H +#define STACK_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +typedef struct { + usize head; /* current number of elements */ + const usize elem_size; /* size in bytes of each element */ + usize size; /* current memory size used by the stack */ + const usize chunk_size; /* size of which the stack increases when running out + of mem */ + void* data; /* memory buffer */ +} Stack; + +Stack stack_new_ex(const usize element_size, const usize size); + +Stack stack_new(const usize element_size); + +void stack_free(Stack* s); +void* stack_pop(Stack* s); +void stack_push(Stack* s, void* elem); +void* stack_peek(Stack* s); +usize stack_size(const Stack* s); +void stack_swap(Stack* s, Stack* t); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/include/daw/window.h b/src/include/daw/window.h new file mode 100644 index 0000000..2924448 --- /dev/null +++ b/src/include/daw/window.h @@ -0,0 +1,55 @@ +#ifndef WINDOW_H +#define WINDOW_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include +#include + +#ifndef ENGINE_RENDERING_WINDOW_H_EXCLUDE_EXTERNS +extern u64 (*get_time)(void); +#endif + +typedef enum { + WINDOW_FRAMEWORK_NONE = 0, + WINDOW_FRAMEWORK_GLFW, +} Window_framework; + +typedef enum { + WINDOW_RENDERER_NONE = 0, + WINDOW_RENDERER_OPENGL, +} Window_renderer; + +typedef struct { + // Specifies the framwork & renderer combo used. + Window_framework framework; + Window_renderer renderer; + + // Window *buffer* size, in pixels. + ivec2 windowsize; + + // These are used differently depending on the framework / renderer combo. + // Subject to change to a union of backend-dependent structs + void* window; + void* context; + + /* The ctrl is probably the only sensible thing in this struct. */ + usize bindings_sz; + usize bindings_len; + i_ctx** bindings; +} Window; + +#include + +Window* Window_new(const struct Platform *p, const char *restrict title, Window_framework framework, Window_renderer renderer, ivec2 size, u32 flags); + +void get_mousepos(double *x, double *y); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/input.c b/src/input.c new file mode 100644 index 0000000..b33a1fa --- /dev/null +++ b/src/input.c @@ -0,0 +1,425 @@ +#include +#include + +#undef GLFW_INCLUDE_NONE +#include + +#include +#include +#include +#include +#include + +extern input_callback_t* callbacks[128]; +extern usize callbacks_len; + +extern Instance* GLOBAL_PLATFORM; +/* 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), \ + }}, \ + .keycode = key, .keycode_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), \ + }}, \ + .keycode = key, .keycode_alt = altkey, .since_last_activation = 0 \ + } + +void key_callback(void* window, int key, int scancode, int action, int mods) { + + const i_ctx* bindings = *GLOBAL_PLATFORM->window->bindings; + const usize bindings_len = GLOBAL_PLATFORM->window->bindings_len; + + const u64 now = get_time(); + + for (usize b = 0; b < bindings_len; b++) { + const action_t a = i_get_action(&bindings[b], now, key); + switch (action) { + case GLFW_PRESS: + + switch (a.type) { + case InputType_action: + if (a.action.callback != NULL) { + callbacks[callbacks_len++] = a.action.callback; + } + break; + + case InputType_state: + if (a.state.activate != NULL) { + callbacks[callbacks_len++] = a.state.activate; + } + break; + case InputType_range: +#ifdef _DEBUG + WARN("Range inputs not supported yet!"); +#endif + break; + case InputType_error: +#ifdef _DEBUG + WARN("Unhandled keycode: %lu", key); +#endif + + default: + break; + } + break; + + case GLFW_RELEASE: + switch (a.type) { + case InputType_state: + if (a.state.deactivate != NULL) { + callbacks[callbacks_len++] = a.state.deactivate; + } + break; + default:break; + } + break; + case GLFW_REPEAT: + /* unhandled so far */ + break; + default: + break; + } + } +} + +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 (usize 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_keycode_cmp(binding_t* restrict a, binding_t* restrict b) { + return a->keycode == b->keycode || a->keycode_alt == b->keycode_alt; +} + +bool binding_keycode_cmp_i(binding_t* restrict a, keycode_t keycode) { + return a->keycode == keycode || a->keycode_alt == keycode; +} + +// If any binding in ctx contains action, replace that entry. +bool i_update_binding(i_ctx* ctx, binding_t* binding) { + usize 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].keycode = binding->keycode; + ctx->bindings[idx].keycode_alt = binding->keycode_alt; + return true; + } + + return false; +} + +// If any binding in ctx contains keycode, replace that action. +bool i_update_unique_binding(i_ctx* ctx, binding_t* binding) { + usize idx = 0; + if (ctx == NULL || binding == NULL) { + ERROR("i_update_unique_binding received nullptr!"); + return false; + } + + while (idx < ctx->len && !binding_keycode_cmp(&ctx->bindings[idx], binding)) + idx++; + + if (idx < ctx->len && binding_keycode_cmp(&ctx->bindings[idx], binding)) { + ctx->bindings[idx].action = binding->action; + return true; + } + + return false; +} + +/* Call binding callbacks of respective bindings */ +void i_flush_bindings(u64 dt, usize numcalls, input_callback_t* c[], void* state_mem) { + for (usize i = 0; i < numcalls; i++) { + (c[i])(state_mem); + } + + // reset callback len and be ready for more + callbacks_len = 0; +} + +action_t i_get_action(const i_ctx* restrict ctx, u64 time, + keycode_t keycode) { + usize idx = 0; + + if (ctx == NULL) { + ERROR("%s received nullptr!", __func__); + return (action_t){.type = InputType_error}; + } + + // Needed for glfw + switch (keycode) { + case GLFW_KEY_LEFT_SHIFT: + case GLFW_KEY_RIGHT_SHIFT: + keycode = MOD_SHIFT; + break; + + case GLFW_KEY_LEFT_CONTROL: + case GLFW_KEY_RIGHT_CONTROL: + keycode = MOD_CONTROL; + break; + + case GLFW_KEY_LEFT_ALT: + case GLFW_KEY_RIGHT_ALT: + keycode = MOD_ALT; + break; + + case GLFW_KEY_LEFT_SUPER: + case GLFW_KEY_RIGHT_SUPER: + keycode = MOD_SUPER; + break; + + default: break; + } + + while (idx < ctx->len && + !binding_keycode_cmp_i(&ctx->bindings[idx], keycode)) + idx++; + + if (idx < ctx->len && binding_keycode_cmp_i(&ctx->bindings[idx], keycode)) { + ctx->bindings[idx].since_last_activation = time; + return ctx->bindings[idx].action; + } + + return (action_t){.type = InputType_error}; +} + +/* 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 (usize i = 0; i < ctx[c]->len; i++) { + switch (b[i].action.type) { + case InputType_error: + break; + case InputType_action: + bb[cumsum] = BindActionLazy(b[i].keycode, b[i].keycode_alt, + strdup(b[i].action.action.callback_str)); + break; + case InputType_state: + bb[cumsum] = BindStateLazy(b[i].keycode, b[i].keycode_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 (usize 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, keycode_t s, action_t* a) { + binding_t* b = get_action(c, a); + + if (b == NULL) { + WARN("Failed to update keybinding"); + return; + } + + b->keycode = s; +} + +void i_bind_ctx_alt(i_ctx* c, keycode_t s, action_t* a) { + binding_t* b = get_action(c, a); + + if (b == NULL) { + WARN("Failed to update keybinding"); + return; + } + + b->keycode_alt = s; +} + +void i_bind(binding_t* b, keycode_t s) { + if (b == NULL) { + WARN("Failed to update keybinding"); + return; + } + + b->keycode = s; +} + +void i_bind_alt(binding_t* b, keycode_t s) { + if (b == NULL) { + WARN("Failed to update keybinding"); + return; + } + + b->keycode_alt = s; +} + +/* Pushes an input context onto the input handling stack */ +void i_ctx_push(i_ctx* ctx) { + if (GLOBAL_PLATFORM->window->bindings == NULL) { + GLOBAL_PLATFORM->window->bindings = calloc(8, sizeof(i_ctx*)); + GLOBAL_PLATFORM->window->bindings_sz = 8; + } + + if (GLOBAL_PLATFORM->window->bindings_len + 1 >= GLOBAL_PLATFORM->window->bindings_sz) { + void* m = + realloc(GLOBAL_PLATFORM->window->bindings, GLOBAL_PLATFORM->window->bindings_sz + 8); + if (m == NULL) { + ERROR("Failed to allocate 8 bytes (%d): %s", errno, strerror(errno)); + exit(EXIT_FAILURE); + } + GLOBAL_PLATFORM->window->bindings_sz += 8; + } + + LOG("Bindings in ctx[%d]:", GLOBAL_PLATFORM->window->bindings_len); + for (usize 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->window->bindings[GLOBAL_PLATFORM->window->bindings_len++] = ctx; +} + +/* Pops an input context from the input stack */ +void i_ctx_pop(void) { + if (GLOBAL_PLATFORM->window->bindings == NULL || GLOBAL_PLATFORM->window->bindings_sz == 0) + return; + i_ctx_t_free(GLOBAL_PLATFORM->window->bindings[--GLOBAL_PLATFORM->window->bindings_len]); +} + +/* Removes all input contexts from the input stack */ +void i_ctx_reset(void) { + while (GLOBAL_PLATFORM->window->bindings_len > 0) { + i_ctx_t_free(GLOBAL_PLATFORM->window->bindings[--GLOBAL_PLATFORM->window->bindings_len]); + } +} diff --git a/src/logging.c b/src/logging.c new file mode 100644 index 0000000..c5eec18 --- /dev/null +++ b/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/memory.c b/src/memory.c new file mode 100644 index 0000000..8dc14eb --- /dev/null +++ b/src/memory.c @@ -0,0 +1,61 @@ +#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/misc.c b/src/misc.c new file mode 100644 index 0000000..16c0cfb --- /dev/null +++ b/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 (i32)((f32)a * (1.0f - dt)) + (i32)((f32)b * dt); +} + +u32 hash(char* str) { + u32 sum = 0; + while (*str != '\0') { + sum ^= (u32)(*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 ivec2 mapsize, + predicate_t* predicate) { + const usize w = (usize)mapsize[0]; + const usize h = (usize)mapsize[1]; + i32 mask[w * h]; + + if (w * h < 1) return NULL; + + for (usize i = 0; i < w * h; i++) { + mask[i] = predicate((void*)((u64)map + sizeof(i32) * i)) ? 1 : 0; + } + + for (usize y = 1; y < h - 1; y++) { + for (usize x = 1; x < w - 1; x++) { + const usize global_idx = (y * w) + x; + const usize 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 (usize yy = offs; yy <= offs + w + w; yy += w) { + for (usize 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/model.c b/src/model.c new file mode 100644 index 0000000..172c409 --- /dev/null +++ b/src/model.c @@ -0,0 +1,68 @@ +#include +#include +#include +#include +#include +#include + +Model load_model(const Asset_ModelSpec *restrict ms) { + ModelType format = Model_error; + + /* + isize vertices_len = 0; + vec3 *vertices = malloc(sizeof(vec3) * 512); + usize *vertices_idx = malloc(sizeof(usize) * 512); + + isize uvs_len = 0; + vec2 *uvs = malloc(sizeof(vec2) * 512); + usize *uvs_idx = malloc(sizeof(usize) * 512); + + isize normals_len = 0; + vec3 *normals = malloc(sizeof(vec3) * 512); + usize *normals_idx = malloc(sizeof(usize) * 512); + */ + + // For now, just default to obj + format = Model_obj; + + FILE* f = fopen(ms->path, "r"); + if (f == NULL) { + ERROR("Failed to load file " TERM_COLOR_YELLOW "%s" TERM_COLOR_RESET, ms->path); + return (Model){.format = format}; + } + const usize filesz = f_get_sz(f); + + char* filecontets = calloc(filesz, sizeof(char)); + fread(filecontets, sizeof(char), filesz, f); + + // Start the import on the given file with some example postprocessing + // Usually - if speed is not the most important aspect for you - you'll t + // probably to request more postprocessing than we do in this example. + const struct aiScene* scene = aiImportFile( filecontets, + aiProcess_CalcTangentSpace | + aiProcess_Triangulate | + aiProcess_JoinIdenticalVertices | + aiProcess_SortByPType); + + free(filecontets); + // If the import failed, report it + if( NULL == scene) { + ERROR("Failed to import %s: %s", ms->path, aiGetErrorString()); + return (Model){.format = format}; + } + + //// Now we can access the file's contents + //DoTheSceneProcessing(scene); + + //// We're done. Release all resources associated with this import + aiReleaseImport(scene); + + { + Model m = {0}; + m.format = format; + + // TODO add index array + + return m; + } +} diff --git a/src/platform_glfw.c b/src/platform_glfw.c new file mode 100644 index 0000000..211df47 --- /dev/null +++ b/src/platform_glfw.c @@ -0,0 +1,232 @@ +// Required for OpenGL rendering backend +#define GLAD_GL_IMPLEMENTATION +#include +#undef GLAD_GL_IMPLEMENTATION + +// TODO: import vulkan thingymajig once I get around to it. +// TODO: move OpenGL initialization code at some point + +#undef GLFW_INCLUDE_NONE +#include + +#include + +#include + +static void window_resize_callback(GLFWwindow *restrict window, int width, int height); +static void framebuffer_resize_callback(GLFWwindow *restrict window, int width, int height); +static void glfw_err_callback(int code, const char* description); +static void render_init_opengl(Window *restrict w, const u32 flags); +static void window_size(GLFWwindow *restrict w, ivec2 *restrict dst); + + +Window* window_init_glfw(const char *restrict windowtitle, ivec2 windowsize, const u32 flags) { + Window* ret = NULL; + 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); + if (!(flags & DAW_WINDOW_RESIZEABLE)) { + glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); + } + + glfwWindowHint(GLFW_DECORATED, GLFW_FALSE); + glfwWindowHint(GLFW_FLOATING, GLFW_TRUE); + + glfwWindowHint(GLFW_SAMPLES, 0); // Disable anti aliasing + + // Use a modern opengl version + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6); + + // Lean and mean + 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 + * once of a window for it to become visible." */ + { + GLFWmonitor* mon = NULL; + if (flags & DAW_WINDOW_FULLSCREEN) mon = glfwGetPrimaryMonitor(); + + window = glfwCreateWindow(windowsize[0], windowsize[1], windowtitle, mon, 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"); + + // Setup callbacks + glfwSetFramebufferSizeCallback(window, framebuffer_resize_callback); + glfwSetWindowSizeCallback(window, window_resize_callback); + glfwSetKeyCallback(window, (GLFWkeyfun)key_callback); + + // Create the window datastructure + ret = (Window*)calloc(1, sizeof(Window)); + ret->framework = WINDOW_FRAMEWORK_GLFW; + ret->renderer = WINDOW_RENDERER_NONE; + ret->window = window; + /* Last parameter is used for the renderer */ + ret->context = NULL; + + { + ivec2 wsize; + vec2 wscaling; + glfwGetWindowContentScale(window, &wscaling[0], &wscaling[1]); + glfwGetWindowSize(window, &wsize[0], &wsize[1]); + + ret->windowsize[0] = (i32)((f32)wsize[0] * wscaling[0]); + ret->windowsize[1] = (i32)((f32)wsize[1] * wscaling[1]); + + INFO("WINDOW CONTENT SCALING: %.2f x %.2f", wscaling[0], wscaling[1]); + INFO("WINDOW SIZE: %d x %d -> %d x %d", wsize[0], wsize[1], ret->windowsize[0], ret->windowsize[1]); + } + + // TODO: set this to `ret` once all the garbage is moved to `struct Window` + glfwSetWindowUserPointer(window, (void*)ret); + + render_init_opengl(ret, flags); + + return ret; +} + +void window_destroy_glfw(Window *restrict w) { + glfwDestroyWindow(w->window); + w->window = NULL; + + // If we ever do multi-window support, we need to make sure this is the last + // window before terminating + glfwTerminate(); + + switch(w->renderer) { + case WINDOW_RENDERER_OPENGL: + // Missing unloader function in glad MX library + free(w->context); + w->context = NULL; + break; + default: + ERROR("Destroying unknown renderer type."); + } +} + +void window_resize_glfw(Window *restrict window, int width, int height) { + window_resize_callback(window->window, width, height); + framebuffer_resize_callback(window->window, width, height); +} + +bool window_should_close_glfw(Window *restrict window) { + return glfwWindowShouldClose(window->window); +} + +void window_poll_glfw(void) { + glfwPollEvents(); +} + +// Helper function implementations +static void window_resize_callback(GLFWwindow* window, int width, int height) { + (void)width; (void)height; + Window* w = glfwGetWindowUserPointer(window); + glfwSwapBuffers(window); + if (w != NULL) { + const GladGLContext* gl = w->context; + gl->Finish(); + } +} + +static void framebuffer_resize_callback(GLFWwindow* window, int width, int height) { + (void)width; (void)height; + Window* w = glfwGetWindowUserPointer(window); + if (w != NULL) { + const GladGLContext* gl = w->context; + //TODO: Move the camera to window->renderer + //Camera* c = w->cam; + gl->Viewport(0,0, width, height); + w->windowsize[0] = width; + w->windowsize[1] = height; + + //r_reset_camera(c); + } +} + +static void glfw_err_callback(int code, const char* description) { + ERROR("glfw [%d]: %s\n", code, description); + // Terminate? + exit(EXIT_FAILURE); +} + +static void render_init_opengl(Window *restrict w, const u32 flags) { + if (w->renderer != WINDOW_RENDERER_NONE || w->context != NULL) { + ERROR("Window already initialized with a renderer!"); + return; + } + + // This is GLFW specific + glfwMakeContextCurrent(w->window); + + GladGLContext* ctx = (GladGLContext*)malloc(sizeof(GladGLContext)); + if (!ctx) { + ERROR("Failed to allocate memory for context"); + } + + int version = gladLoadGLContext(ctx, glfwGetProcAddress); + INFO("Loaded OpenGL %d.%d", GLAD_VERSION_MAJOR(version), GLAD_VERSION_MINOR(version)); + + if (ctx == NULL) { + ERROR("Failed to create glad context"); + exit(EXIT_FAILURE); + } + + if (flags & DAW_WINDOW_VSYNC) { + glfwSwapInterval(1); + } else { + glfwSwapInterval(0); + } + + + ctx->Viewport(0, 0, w->windowsize[0], w->windowsize[1]); + +#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 + + // Make sure faces closest to the camera are drawn on-top of faces that are + // further away + ctx->Enable(GL_DEPTH_TEST); + ctx->DepthFunc(GL_LESS); + + w->context = ctx; + w->renderer = WINDOW_RENDERER_OPENGL; +} + +static void window_size(GLFWwindow *restrict w, ivec2 *restrict dst) { + ivec2 wsize; + vec2 wscaling; + glfwGetWindowContentScale(w, &wscaling[0], &wscaling[1]); + glfwGetWindowSize(w, &wsize[0], &wsize[1]); + + *dst[0] = (i32)((f32)wsize[0] * wscaling[0]); + *dst[1] = (i32)((f32)wsize[1] * wscaling[1]); +} diff --git a/src/rendering.c b/src/rendering.c new file mode 100644 index 0000000..54dc7b4 --- /dev/null +++ b/src/rendering.c @@ -0,0 +1,696 @@ +#include +#include + +#include +#include +#include +#include +#include + + +#include +#include +#include + + +/* Extern globals */ +extern Instance* GLOBAL_PLATFORM; + +/* Globals */ +#define drawcall_limit (64 * 1024) +RenderDrawCall drawcalls[drawcall_limit]; +i32 drawcall_len = 0; + +usize +ShaderBufferDataType_size(u16 flags) { + const ShaderBufferFlag t = ShaderBuffer_get_data_type(flags); + switch (t) { + case ShaderBuffer_DataType_nil: return 0; + case ShaderBuffer_DataType_f32: return sizeof(f32); + case ShaderBuffer_DataType_f64: return sizeof(f64); + case ShaderBuffer_DataType_i8: return sizeof(i8); + case ShaderBuffer_DataType_i16: return sizeof(i16); + case ShaderBuffer_DataType_i32: return sizeof(i32); + case ShaderBuffer_DataType_i64: return sizeof(i64); + case ShaderBuffer_DataType_u8: return sizeof(u8); + case ShaderBuffer_DataType_u16: return sizeof(u16); + case ShaderBuffer_DataType_u32: return sizeof(u32); + case ShaderBuffer_DataType_u64: return sizeof(u64); + default: return 0; + } +} + +ShaderBufferFlag ShaderBuffer_get_access_frequency(u64 flags) { return flags & (0b111 << 0); } +ShaderBufferFlag ShaderBuffer_get_access_type(u64 flags) { return flags & (0b111 << 3); } +ShaderBufferFlag ShaderBuffer_get_type(u64 flags) { return flags & (0b111 << 6); } +ShaderBufferFlag ShaderBuffer_get_data_type(u64 flags) { return flags & (0b1111 << 9); } + +u32 ShaderBuffer_get_gl_type(u64 flags) { + switch(ShaderBuffer_get_type(flags)) { + case ShaderBuffer_Type_vertexData: + return GL_ARRAY_BUFFER; + case ShaderBuffer_Type_vertexPosition: + return GL_ARRAY_BUFFER; + case ShaderBuffer_Type_vertexIndex: + return GL_ELEMENT_ARRAY_BUFFER; + default: + return GL_ARRAY_BUFFER; + } +} + + +u32 ShaderBuffer_get_gl_accesstype(u64 flags) { + switch (ShaderBuffer_get_access_frequency(flags)) { + case ShaderBuffer_AccessFrequency_stream: + switch (ShaderBuffer_get_access_type(flags)) { + case ShaderBuffer_AccessType_draw: return GL_STREAM_DRAW; + case ShaderBuffer_AccessType_read: return GL_STREAM_READ; + case ShaderBuffer_AccessType_copy: return GL_STREAM_COPY; + default: return 0; + } + + case ShaderBuffer_AccessFrequency_static: + switch (ShaderBuffer_get_access_type(flags)) { + case ShaderBuffer_AccessType_draw: return GL_STATIC_DRAW; + case ShaderBuffer_AccessType_read: return GL_STATIC_READ; + case ShaderBuffer_AccessType_copy: return GL_STATIC_COPY; + default: return 0; + } + case ShaderBuffer_AccessFrequency_dynamic: + switch (ShaderBuffer_get_access_type(flags)) { + case ShaderBuffer_AccessType_draw: return GL_DYNAMIC_DRAW; + case ShaderBuffer_AccessType_read: return GL_DYNAMIC_READ; + case ShaderBuffer_AccessType_copy: return GL_DYNAMIC_COPY; + default: return 0; + } + default: return 0; + } +} + +ShaderBufferFlag ShaderBuffer_get_gl_datatype(u64 flags) { + switch (ShaderBuffer_get_data_type(flags)) { + case ShaderBuffer_DataType_nil: return GL_NONE; + + case ShaderBuffer_DataType_f32: return GL_FLOAT; + case ShaderBuffer_DataType_f64: return GL_DOUBLE; + + case ShaderBuffer_DataType_i8: return GL_BYTE; + case ShaderBuffer_DataType_i16: return GL_SHORT; + case ShaderBuffer_DataType_i32: return GL_INT; + case ShaderBuffer_DataType_i64: return GL_INT64_ARB; + + case ShaderBuffer_DataType_u8: return GL_UNSIGNED_BYTE; + case ShaderBuffer_DataType_u16: return GL_UNSIGNED_SHORT; + case ShaderBuffer_DataType_u32: return GL_UNSIGNED_INT; + case ShaderBuffer_DataType_u64: return GL_UNSIGNED_INT64_ARB; + + default: return GL_NONE; + } +} + +// `RenderBatch` is used for batch rendering. The struct is used as a +// "management" parent structure to keep track of multiple `RenderObject`s that +// are put into a final `RenderObject` to render. +// `RenderObject`s are copied to the internal `models` array, to which the +// pointer to the copied RenderObject is returned, or NULL if an error occurred. +// If changes are made to a render object the batch should be refreshed. +// Renderbatches assumes that all buffer layouts are the same. + +// renderbatch_new: Create a new render batch with space for `count` models. +int renderbatch_new(RenderBatch* renderbatch, usize count) { + /* TODO: Make it such that you can add identical models with different + * transforms, so you instead of relying on renderobject[n] to copy to the + * renderobject we have something like + * + * model { + * renderobj_idx // index in renderobj[n] that this model represents + * transform { + * size; + * pos; + * rotation; + * }; + * }; + * + * For this to work we will likely need to extend the shaderbuffer struct to + * also hold what type of data the buffer contains, s.t. we can apply the + * transformation to only geometry data. + * + * We'll therefore have both data type and buffer type stored somehow, + * maybe like we did the ShaderBufferDataType. + * TODO: Also use shaderbuffertype. + * */ + if (renderbatch == NULL) { + ERROR("renderbatch was null!"); + return -1; + } + + usize numisnstances = count; + + if (count == 0) { + // Just allocate enough for a couple hundred + count = 256; + numisnstances = count * 4; + } + + + renderbatch->msize = sizeof(RenderObject) * count; + renderbatch->mcount = 0; + renderbatch->inst_size = sizeof(BatchModelInstance) * numisnstances; + renderbatch->inst_count = 0; + renderbatch->models = (RenderObject**)calloc(count, sizeof(RenderObject*)); + + if (renderbatch->models == NULL) { + ERROR("Failed to allocate %lu size of bytes for models array!", sizeof(RenderObject*) * count); + return -1; + } + + renderbatch->instances = (BatchModelInstance*)calloc(numisnstances, sizeof(BatchModelInstance)); + + if (renderbatch->instances == NULL) { + ERROR("Failed to allocate %lu size of bytes for batch instances array!", sizeof(BatchModelInstance) * numisnstances); + return -1; + } + + memset(&(renderbatch->renderobj), 0, sizeof(RenderObject)); + + return 0; +} + +// Appends the data in src onto dst. More space for `data` is allocated if +// necessary, in which case a pointer to the new ShaderBuffer is returned. +ShaderBuffer* shaderbuffer_cat(ShaderBuffer* dst, ShaderBuffer *restrict src) { + if (dst == NULL) { + ERROR("dst is null"); + } + else if (src == NULL) { + ERROR("src is null"); + } + + if (ShaderBuffer_get_data_type(dst->buffertype) != ShaderBuffer_get_data_type(src->buffertype)) { + ERROR("Failed to concatenate shader buffers, incompatible datatypes: %d != %d", dst->buffertype, src->buffertype); + } + if (dst->components != src->components) { + ERROR("Failed to concatenate shader buffers, incompatible number of components: %d != %d", dst->components, src->components); + } + + // Assume that we single-handedly control the pointer to the data, copy and + // free the stuff. + + // Verify the size + const usize sz_src = src->size_elem * src->count; + const usize sz_dst = dst->size_elem * dst->count; + if (dst->data == NULL || sz_dst + sz_src >= dst->size) { + const usize sz_new = (1 + ((sz_src + sz_dst) / 4096)) * 4096; + // Resize dst size + dst->data = realloc(dst->data, sz_new); + dst->size = sz_new; + } + + memcpy(dst->data + sz_dst, src->data, sz_src); + + dst->count += src->count; + + return dst; +} + +// Add a render object to the render batch. +i32 renderbatch_add(RenderBatch* renderbatch, RenderObject* obj, Transform* t) { + // Check if its a valid renderbatch + if (renderbatch == NULL) { + ERROR("renderbatch was null!"); + return -1; + } + + // Check whether we have initialized models & instance memory + if (renderbatch->models == NULL) { + const usize sz = 8 * sizeof(RenderObject*); + renderbatch->models = calloc(8, sizeof(RenderObject*)); + renderbatch->msize = sz; + renderbatch->mcount = 0; + } + + if (renderbatch->instances == NULL) { + // Allocate enough for 4 times the models + const usize modelbufsz = renderbatch->msize / sizeof(RenderObject*); + const usize sz = 4 * modelbufsz * sizeof(BatchModelInstance); + renderbatch->instances = calloc(4 * modelbufsz, sizeof(BatchModelInstance)); + renderbatch->inst_size = sz; + renderbatch->inst_count = 0; + } + + // The index of the model + isize model_idx = -1; + + // Find the model, to check if it already exists + for (usize i = 0; i < renderbatch->mcount; i++) { + // Compare the model pointers + if (obj == renderbatch->models[i]) { + model_idx = (isize)i; + break; + } + } + + // Model doesn't exist, add it + if (-1 == model_idx) { + // Check if there's room enough + if ((1 + renderbatch->mcount) * sizeof(RenderObject*) > renderbatch->msize) { + // Realloc if necessary + const usize sz = renderbatch->msize * 2; + renderbatch->models = realloc(renderbatch->models, sz); + renderbatch->msize = sz; + } + + // If this is the first model, we want to copy the renderobj, and + // shaderbuffer parameters. + if (renderbatch->mcount == 0) { + // Shader, VAO, modelviewprojection, and texture, are set when the shaderobj + // is actually created with RenderObject_new later. + // The number of buffers should be the same. + //renderbatch->renderobj.shader = obj->shader; + //renderbatch->renderobj.texture = obj->texture; + + renderbatch->renderobj.buffer_len = obj->buffer_len; + if (renderbatch->renderobj.buffer == NULL) { + renderbatch->renderobj.buffer = calloc(obj->buffer_len, sizeof(ShaderBuffer)); + } else { + ERROR("RenderObj buffer is already initialized!"); + return -1; + } + + // Copy each buffers parameters + for (usize i = 0; i < renderbatch->renderobj.buffer_len; i++) { + renderbatch->renderobj.buffer[i].buffername = 0; + renderbatch->renderobj.buffer[i].buffertype = obj->buffer[i].buffertype; + // Size and count should be zero + + renderbatch->renderobj.buffer[i].components = obj->buffer[i].components; + renderbatch->renderobj.buffer[i].size_elem = obj->buffer[i].size_elem; + // Data should also be null + } + } + + //// Only concatenate the buffers once we refresh + //for (usize i = 0; i < renderbatch->renderobj.buffer_len; i++) { + // shaderbuffer_cat(&renderbatch->renderobj.buffer[i], &obj->buffer[i]); + //} + + model_idx = (isize)renderbatch->mcount; + renderbatch->models[renderbatch->mcount++] = obj; + } + + // Create batch instance + // Check if there's room enough + if ((1 + renderbatch->inst_count) * sizeof(BatchModelInstance) > renderbatch->inst_size) { + // Realloc if necessary + const usize sz = renderbatch->inst_size * 2; + renderbatch->instances = realloc(renderbatch->instances, sz); + renderbatch->inst_size = sz; + } + + BatchModelInstance inst = { + .model_idx = (usize)model_idx, + .transform = *t, + }; + + // Add it to the batch + renderbatch->instances[renderbatch->inst_count++] = inst; + + // Return instance index + return (i32)renderbatch->inst_count - 1; +} + +void renderbatch_transform(RenderBatch* renderbatch, usize obj_idx, Transform* t) { + // TODO: Combine transformation, ie. pos' += pos, etc. + const usize m = renderbatch->instances[obj_idx].model_idx; + const RenderObject* model = renderbatch->models[m]; + renderbatch->instances[obj_idx].transform = *t; + + if(renderbatch->inst_count < obj_idx) { + ERROR("renderbatch_transform: object index is outside range!"); + return; + } + /* TODO: Update the model data, we might need to + * 0. Iteratively go through each renderobj buffer, to find a vertexPosition + * buffer, + * 1. Calculate the models start index in the renderobj, + * 2. Apply transformation to the model in the renderobj buffer. + * */ + usize b; + for (b = 0; b < renderbatch->renderobj.buffer_len; b++) { + if (ShaderBuffer_Type_vertexPosition != ShaderBuffer_get_type(renderbatch->renderobj.buffer[b].buffertype) + || ShaderBuffer_DataType_f32 != ShaderBuffer_get_data_type(renderbatch->renderobj.buffer[b].buffertype)) { + continue; + } + } + + usize offset = 0; + for (usize i = 0; i < obj_idx; i++) { + const usize idx = renderbatch->instances[i].model_idx; + offset += renderbatch->models[idx]->buffer->size_elem + * renderbatch->models[idx]->buffer->count; + } + + float *data = renderbatch->renderobj.buffer[b].data; + data = &data[offset]; + const usize len = model->buffer[b].count; + + Transform tt = renderbatch->instances[obj_idx].transform; + if (model->buffer[b].components == 2) { + for (usize v = 0; v < len; v += 2) { + // scale + // rotate + // offset + glm_vec2_add(&data[v], tt.position, &data[v]); + } + } + else if (model->buffer[b].components == 3) { + for (usize v = 0; v < len; v += 3) { + // scale + // rotate + // offset + glm_vec3_add(&data[v], tt.position, &data[v]); + } + } +} + +// renderbatch_refresh: Copy all instances/models in the renderbatch to the +// batchs' model. +int renderbatch_refresh(RenderBatch* renderbatch) { + const usize bufs = renderbatch->renderobj.buffer_len; + usize *offsets = calloc(bufs, sizeof(usize)); + + // Reset renderobj buffers + for (usize b = 0; b < renderbatch->renderobj.buffer_len; b++) { + // Zero the old data + renderbatch->renderobj.buffer[b].count = 0; + memset(renderbatch->renderobj.buffer[b].data, 0, renderbatch->renderobj.buffer[b].size); + } + + // Copy the instances models buffers, and vertex position buffers with translations applied + for (usize i = 0; i < renderbatch->inst_count; i++) { + const usize m = renderbatch->instances[i].model_idx; + const RenderObject* model = renderbatch->models[m]; + Transform t = renderbatch->instances[i].transform; + + for (usize b = 0; b < renderbatch->renderobj.buffer_len; b++) { + shaderbuffer_cat(&renderbatch->renderobj.buffer[b], &model->buffer[b]); + + if (ShaderBuffer_Type_vertexPosition == ShaderBuffer_get_type(renderbatch->renderobj.buffer[b].buffertype)) { + if (ShaderBuffer_DataType_f32 != ShaderBuffer_get_data_type(renderbatch->renderobj.buffer[b].buffertype)) { + WARN("Buffer data type is not f32, skipping transformation..."); + continue; + } + // Apply transformation in renderbatch buffer-memory + + float *data = renderbatch->renderobj.buffer[b].data; + const usize len = model->buffer[b].count; + // Data points to the start of the model in renderobj + data = &data[renderbatch->renderobj.buffer[b].count - len]; + + if (model->buffer[b].components == 2) { + for (usize v = 0; v < len; v += 2) { + // scale + // rotate + // offset + glm_vec2_add(&data[v], t.position, &data[v]); + } + } + else if (model->buffer[b].components == 3) { + for (usize v = 0; v < len; v += 3) { + // scale + // rotate + // offset + glm_vec3_add(&data[v], t.position, &data[v]); + } + } + } + } + } + + free(offsets); + return 0; +} + + +/* Implementations */ + +/* Clear the screen, + * To be used inbetween draw calls */ +void render_begin(Window* w) { + glfwMakeContextCurrent(w->window); + ((GladGLContext*)(w->context))->Clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +} + +void render_present(Window* w) { + /* This is GL specific, TODO: move the GL-specific code elsewhere. Maybe make + * this whole present GL specific? assign it as a fn ptr in the Window struct? */ + GladGLContext *restrict gl = w->context; + Camera c = *GLOBAL_PLATFORM->cam; + + mat4 view; // view + vec3 angle; // viewing angle / direction of the camera + mat4 camera_matrix; + + glm_vec3_sub(c.pos, c.dir, angle); + glm_lookat(c.pos, angle, GLM_YUP, view); + glm_mat4_mul(c.per, view, camera_matrix); + + for (i32 drawcall_idx = 0; drawcall_idx < drawcall_len; drawcall_idx++) { + RenderDrawCall dc = drawcalls[drawcall_idx]; + switch (dc.type) { + case RenderDrawCallType_Sprite: { + // TODO render a quad + } break; + + case RenderDrawCallType_Model: { + + // bind shader program + // - set uniforms + // bind vertex array + // bind index buffer + + + RenderObject* o = dc.data.model.model; + vec3 pos; + glm_vec3_copy(dc.data.model.pos, pos); + + gl->UseProgram(o->shader.program); + // TODO: Use texture atlas + gl->ActiveTexture(GL_TEXTURE0); + gl->BindTexture(GL_TEXTURE_2D, o->texture); + + { + mat4 model = GLM_MAT4_IDENTITY_INIT; + mat4 modelviewprojection; + + model[3][0] = pos[0]; + model[3][1] = pos[1]; + model[3][2] = pos[2]; + + // modelviewprojection = p * view * model + glm_mat4_mul(model, camera_matrix, modelviewprojection); + + // TODO: Do this only once during initialization + gl->UniformMatrix4fv(o->mvp, 1, GL_FALSE, &modelviewprojection[0][0]); + } + + // TODO the buffers need to be abstracted a bit more + gl->BindVertexArray(o->vao); + + ShaderBuffer* ibo = NULL; + for (usize i = 0; i < o->buffer_len; i++) { + const u32 b_gl_type = ShaderBuffer_get_gl_type(o->buffer[i].buffertype); + if (b_gl_type == GL_ELEMENT_ARRAY_BUFFER) { + ibo = &o->buffer[i]; + } + + gl->EnableVertexAttribArray((u32)i); + gl->BindBuffer(b_gl_type, o->buffer[i].buffername); + gl->VertexAttribPointer( + // index of the attribute + (u32)i, + // number of component + (i32)o->buffer[i].components, + // type + ShaderBuffer_get_gl_datatype(o->buffer[i].buffertype), + // normalized? + GL_FALSE, + // stride + 0, + // array buffer offset + (void*)0 + ); + } + + // Draw the model ! + const i32 sz = (i32)(o->buffer->count * o->buffer->size_elem); + if (ibo) { + gl->DrawElements( + GL_TRIANGLES, + (i32)ibo->count, + ShaderBuffer_get_gl_datatype(ibo->buffertype), + (void*)0 + ); + } else { + // Starting from vertex 0; 3 vertices total -> 1 triangle + gl->DrawArrays(GL_TRIANGLES, 0, sz); + } + + for (u32 i = 0; i < o->buffer_len; i++) { + gl->DisableVertexAttribArray(i); + } + + gl->BindVertexArray(0); + + //if (i == 8) { + // printf("\r obj: %.3f", (double)(get_time() - t) * 1000.); + //} + } break; + default: + break; + } + } + + drawcall_len = 0; + + glfwSwapBuffers(w->window); +} + +void drawcall_reset(void) { + drawcall_len = 0; + memset(drawcalls, 0, sizeof(RenderDrawCall) * drawcall_limit); +} + +void r_perspective(f32 fov, Camera *c) { + const f32 ratio = (f32)GLOBAL_PLATFORM->window->windowsize[0] + / (f32)GLOBAL_PLATFORM->window->windowsize[1]; + + c->type = Camera_Perspective; + c->parameters.perspective.fov = fov; + + glm_perspective(glm_rad(fov), ratio, 0.1f, 100.0f, c->per); +} + +void r_perspective_ortho(f32 sz, Camera *c) { + const f32 ratio = (f32)GLOBAL_PLATFORM->window->windowsize[0] + / (f32)GLOBAL_PLATFORM->window->windowsize[1]; + + c->type = Camera_Orthogonal; + c->parameters.orthogonal.sz = sz; + + glm_ortho(-sz * ratio, sz * ratio, -sz, sz, -sz * 10.f, sz * 10.f, c->per); +} + +void r_set_camera(Camera* c) { + GLOBAL_PLATFORM->cam = c; +} + + +void r_reset_camera(Camera* c) { + if (c->type == Camera_Perspective) { + r_perspective(c->parameters.perspective.fov, c); + } + else if (c->type == Camera_Orthogonal) { + r_perspective_ortho(c->parameters.orthogonal.sz, c); + } +} + +void engine_draw_sprite(Sprite* s, ivec2* 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[0], + .y = *pos[1], + .scale = scale, + //.mod = {0xFF, 0xFF, 0xFF, 0xFF}, + }}; +} + +void engine_draw_sprite_ex(Sprite* s, ivec2* 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[0], + .y = *pos[1], + .scale = scale, + //.mod = {colormod.r, colormod.g, colormod.b, colormod.a}, + }}; +} + +void engine_draw_model(RenderObject* o, vec3 pos) { + if (drawcall_len + 1 >= drawcall_limit) return; +#ifdef _DEBUG + if (o == NULL) __asm__("int3;"); +#endif + RenderDrawCall dc = { + .type = RenderDrawCallType_Model, + .data.model = { + .model = o, + .scale = 1.f, + }}; + + + glm_vec3_copy(pos, dc.data.model.pos); + + drawcalls[drawcall_len++] = dc; +} + +Sprite sprite_new(u64 tid, u8 coord) { + const i32 ts = 16; + // FIXME; used to be + //((struct Resources*)GLOBAL_PLATFORM->data)->textures[tid]->tilesize; + return (Sprite){ + .texture_id = (u32)tid, + { + ts * (coord & 0x0F), + ts * ((coord & 0xF0) >> 4), + }}; +} + +Texture createTextureFromImageData(unsigned char* image_data, i32 width, i32 height, u8 components) { + Window* restrict w = GLOBAL_PLATFORM->window; + Texture t; + t.width = width; + t.height = height; + + if (w->renderer != WINDOW_RENDERER_OPENGL) { + ERROR("createTextureFromImageData not implemented for chosen renderer!"); + return (Texture){.id = 0, .width = 0, .height = 0}; + } + + const GladGLContext* gl = w->context; + + gl->GenTextures(1, &t.id); + gl->BindTexture(GL_TEXTURE_2D, t.id); + u32 err = gl->GetError(); + if (err) { + ERROR("Failed to bind texture from image data!"); + } + + /* TODO: Support more formats than rgb and rgba, such as gray, gray/alpha, etc.*/ + u32 format = GL_RGB; + if (components == 4) format = GL_RGBA; + + /* TODO: Don't force internal format to RGB */ + + gl->TexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, format, + GL_UNSIGNED_BYTE, image_data); + + gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + + gl->BindTexture(GL_TEXTURE_2D, 0); + + return t; +} diff --git a/src/rendering/CMakeLists.txt b/src/rendering/CMakeLists.txt deleted file mode 100644 index f3f4710..0000000 --- a/src/rendering/CMakeLists.txt +++ /dev/null @@ -1,33 +0,0 @@ -add_library(daw_rendering - src/gl.c - src/rendering.c - src/window.c - src/platform_glfw.c - ${GLAD_HEADER} - ) - -add_custom_command( - OUTPUT ${GLAD_HEADER} - COMMAND - glad - --api gl:core=4.6 - --out-path ${CMAKE_BINARY_DIR} - --reproducible - c - --header-only - --mx - ) - -set_property(SOURCE src/window.c APPEND PROPERTY OBJECT_DEPENDS ${GLAD_HEADER}) -set_property(SOURCE src/rendering.c APPEND PROPERTY OBJECT_DEPENDS ${GLAD_HEADER}) - -target_compile_options(daw_rendering PUBLIC ${BUILD_OPTS}) -target_include_directories(daw_rendering PRIVATE - ${DAW_INCLUDE_DIRS} - ${GLFW_INCLUDE_DIR} -) -target_link_libraries(daw_rendering PRIVATE - OpenGL::GL - cglm - glfw -) diff --git a/src/rendering/include/engine/rendering/platform.h b/src/rendering/include/engine/rendering/platform.h deleted file mode 100644 index ea51c47..0000000 --- a/src/rendering/include/engine/rendering/platform.h +++ /dev/null @@ -1,57 +0,0 @@ -#ifndef PLATFORM_H -#define PLATFORM_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include - -#include -// TODO: We only need the window once all the garbage in Instance is cleaned up. -#include -#include - -#define DAW_WINDOW_VSYNC (1 << 0) -#define DAW_WINDOW_FULLSCREEN (1 << 1) -#define DAW_WINDOW_RESIZEABLE (1 << 2) - -// Whether or not it is clever to force API consistency using a struct like this -// can be debated, at the time of writing it seemed like a smart idea. - -// Platform libraries must implement a struct Platform: -struct Platform { - /* Initialize a window for the given platform. The rendering backend should - * also be initialized here. - * Parameters: - * const char* title: window title. - * ivec2 windowsize: the size in pixels for the new window. - * const u32 flags: window flags, such as static size and fullscreen. - * The flags are platform agnostic and needs to be - * converted to the specific library - * Returns: - * A pointer to a struct Window, NULL on error. - */ - Window* (*window_init)(const char *restrict title, ivec2 windowsize, const u32 flags); - - /* Destroy, close, and free up resources related to the window and the - * platform library specific resources. - */ - void (*window_destroy)(Window *restrict w); - - /* Resize the given window. Resize callbacks are handled by the wrapper - * implementation. - */ - void (*window_resize)(Window *restrict window, int width, int height); - - /* Return true if the platform has ordered the window to exit. */ - bool (*window_should_close)(Window *restrict w); - - /* Poll events on the window from the operating system. */ - void (*window_poll)(void); -}; - -#ifdef __cplusplus -} -#endif -#endif diff --git a/src/rendering/include/engine/rendering/platform_glfw.h b/src/rendering/include/engine/rendering/platform_glfw.h deleted file mode 100644 index 949968d..0000000 --- a/src/rendering/include/engine/rendering/platform_glfw.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef ENGINE_RENDERING_PLATFORM_GLFW_H -#define ENGINE_RENDERING_PLATFORM_GLFW_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include - -#include -#include -#include - -Window* window_init_glfw(const char *restrict windowtitle, ivec2 windowsize, const u32 flags); -void window_destroy_glfw(Window *restrict w); -void window_resize_glfw(Window *restrict window, int width, int height); -bool window_should_close_glfw(Window *restrict window); -void window_poll_glfw(void); - -const struct Platform Platform_GLFW = { - .window_init = window_init_glfw, - .window_destroy = window_destroy_glfw, - .window_resize = window_resize_glfw, - .window_should_close = window_should_close_glfw, - .window_poll = window_poll_glfw, -}; - -#ifdef __cplusplus -} -#endif -#endif - diff --git a/src/rendering/include/engine/rendering/rendering.h b/src/rendering/include/engine/rendering/rendering.h deleted file mode 100644 index a996257..0000000 --- a/src/rendering/include/engine/rendering/rendering.h +++ /dev/null @@ -1,288 +0,0 @@ -#ifndef ENGINE_RENDERING_RENDERING_H -#define ENGINE_RENDERING_RENDERING_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include - -#include - -/* Definitions */ -#define RGBA(_r, _g, _b, _a) ((Engine_color){.r = _r, .g = _g, .b = _b, .a = _a}) -#define RGB(_r, _g, _b) RGBA(_r, _g, _b, 0xFF) - -/* Types */ -/* TODO: Cleanup these types. */ -typedef struct { - u8 r; - u8 g; - u8 b; - u8 a; -} Engine_color; - -typedef struct { - /* Maybe implement types, such as `atlas` (default), `standalone`, or - * something idk. */ - u32 id; - i32 width; - i32 height; -} Texture; - -typedef struct { - u32 texture_id; - ivec2 coord; -} Sprite; - -typedef enum { - Shader_Error, - - Shader_Program, - Shader_Vertex, - Shader_Tessellation, - Shader_Geometry, - Shader_Fragment, - Shader_Compute, -} ShaderType; - -typedef struct { - /* Shader proram */ - ShaderType type; - u32 program; -} Shader; - -typedef struct { - vec3 position; - vec3 size; - mat4 rotation; -} Transform; - -typedef enum { - ShaderBufferFlag_none = 0x00, - - // First 3 bytes describe the access frequency. - ShaderBuffer_AccessFrequency_stream = 0b0000000000001, // 1 - ShaderBuffer_AccessFrequency_static = 0b0000000000010, // 2 - ShaderBuffer_AccessFrequency_dynamic = 0b0000000000011, // 3 - - // Next 3 bytes describe the access type. - ShaderBuffer_AccessType_draw = 0b0000000001000, // 8 - ShaderBuffer_AccessType_read = 0b0000000010000, // 16 - ShaderBuffer_AccessType_copy = 0b0000000011000, // 24 - - // Next 3 bytes describe the buffer type - ShaderBuffer_Type_vertexData = 0b0000001000000, // 64 - ShaderBuffer_Type_vertexPosition = 0b0000010000000, // 128 - ShaderBuffer_Type_vertexIndex = 0b0000011000000, // 192 - - // Next 4 bytes are designated for the data type - ShaderBuffer_DataType_nil = 0b0001000000000, // 512 - - ShaderBuffer_DataType_f32 = 0b0010000000000, // 1024 - ShaderBuffer_DataType_f64 = 0b0011000000000, // 1536 - - ShaderBuffer_DataType_i8 = 0b0100000000000, // 2048 - ShaderBuffer_DataType_i16 = 0b0101000000000, // 2560 - ShaderBuffer_DataType_i32 = 0b0110000000000, // 3072 - ShaderBuffer_DataType_i64 = 0b0111000000000, // 3584 - - ShaderBuffer_DataType_u8 = 0b1000000000000, // 4096 - ShaderBuffer_DataType_u16 = 0b1001000000000, // 4608 - ShaderBuffer_DataType_u32 = 0b1010000000000, // 5120 - ShaderBuffer_DataType_u64 = 0b1011000000000, // 5632 -} ShaderBufferFlag; - -typedef struct { - // The backend ID, ie. glGenBuffer(numBufferObjects, &this->buffername) - u32 buffername; - // Array, access, and data, type. - u32 buffertype; - // Buffer size of `data`. To get the size of the actual data, size_elem * count - usize size; - // Number of elements - usize count; - // components per generic vertex attribute (ie, 3 for RGB, 2 for UV) - usize components; - // size of each element - usize size_elem; - // Pointer to the data - void* data; -} ShaderBuffer; - -// SHADERBUFFER_NEW is a constructor that takes the -// * type T, as one of f32, f64, i8, i16, i32, i64, u8, u16, u32, or u64. -// * COUNT, number of elements in the buffer -// * COMPONENTS, number of elements to be grouped together, ie. 3 for a vec3. -// * DATA, the buffer (it is pointed to automatically) -// * FLAGS, are low-level GL flags that hints the access frequency, the access -// type, and what buffer type it is, such as "data" or an index buffer. -#define SHADERBUFFER_NEW(T, COUNT, COMPONENTS, DATA, FLAGS) \ - (ShaderBuffer){ \ - .buffername = 0, \ - .buffertype = ShaderBuffer_DataType_##T | FLAGS, \ - .size = COUNT * sizeof(T), \ - .count = COUNT, \ - .components = COMPONENTS, \ - .size_elem = sizeof(T), \ - .data = DATA, \ - } - -typedef struct { - /* Shader proram */ - Shader shader; - /* Vertex Array Object */ - u32 vao; - - /* MVP (a uniform from the shader). - * This could also probably be generalized */ - i32 mvp; - - // The texture ID, glBindTextures(target, &this->texture) - u32 texture; - - // Number of buffers - usize buffer_len; - - // The vertex buffer is also just a buffer - ShaderBuffer* buffer; -} RenderObject; - -typedef struct { - // Index of the model in the RenderBatch models buffer - usize model_idx; - // The transformation of the model - Transform transform; -} BatchModelInstance; - -typedef struct { - // Size of models buffer - usize msize; - // number of models in the `models` buffer - usize mcount; - // Pointers to original models in this batch - RenderObject **models; - - // Size of instance buffer - usize inst_size; - // number of instances in the `instances` buffer - usize inst_count; - // Pointers to original models in this batch - BatchModelInstance *instances; - - // The rendered destination object - RenderObject renderobj; -} RenderBatch; - -typedef enum { - Camera_Perspective, - Camera_Orthogonal, -} CameraType; - -typedef struct { - /* Position of the camera in world-space. */ - vec3 pos; - - /* The viewing direction of the camera, relative to the camera. */ - vec3 dir; - - /* Perspective matrix. Initialize with r_perspective_ortho or r_perspective. */ - /* Alternatively, use `glm_perspective` or `glm_ortho`. */ - mat4 per; - - /* Used to re-calculate the perspective matrix when resizing the window */ - CameraType type; - /* Yes, could use a singular "f32 arg", but this is more extendable in the - * future. */ - union { - struct {f32 fov;} perspective; - struct {f32 sz;} orthogonal; - } parameters; - -} Camera; - -usize ShaderBufferDataType_size(u16 flags); - -ShaderBufferFlag ShaderBuffer_get_access_frequency(u64 flags); -ShaderBufferFlag ShaderBuffer_get_access_type(u64 flags); -ShaderBufferFlag ShaderBuffer_get_type(u64 flags); -ShaderBufferFlag ShaderBuffer_get_data_type(u64 flags); - -/* Conversion to GL types */ -u32 ShaderBuffer_get_gl_type(u64 flags); -u32 ShaderBuffer_get_gl_accesstype(u64 flags); -u32 ShaderBuffer_get_gl_datatype(u64 flags); - -/* Rendering functions */ -void render_begin(Window* w); -void render_present(Window* w); -void drawcall_reset(void); -void render(Window* w); - -/* Misc */ -void r_perspective(f32 fov, Camera *c); -void r_perspective_ortho(f32 sz, Camera *c); - -void r_set_camera(Camera* c); -void r_reset_camera(Camera* c); - -//void window_size_callback(GLFWwindow* window, i32 width, i32 height); - -void engine_draw_sprite(Sprite* s, ivec2* pos, f32 scale); -void engine_draw_sprite_ex(Sprite* s, ivec2* pos, f32 scale, - Engine_color colormod); -void engine_draw_model(RenderObject* o, vec3 pos); - -Sprite sprite_new(u64 tid, u8 coord); - -typedef enum { - RenderDrawCallType_Text, - RenderDrawCallType_Sprite, - RenderDrawCallType_Model, -} RenderDrawCallType; - -typedef struct { - RenderDrawCallType type; - union { - void* data; - struct { - Sprite* sprite; - i32 x; - i32 y; - f32 scale; - } sprite; - struct { - RenderObject* model; - vec3 pos; - f32 scale; - } model; - } data; -} RenderDrawCall; - -// TODO make all the shader buffers a list - -RenderObject RenderObject_new( - Shader* shader, - u32 texture, - ShaderBuffer *restrict buffers, usize num_buffers); - -int renderbatch_new(RenderBatch* renderbatch, usize count); -i32 renderbatch_add(RenderBatch* renderbatch, RenderObject* obj, Transform* t); -void renderbatch_transform(RenderBatch* renderbatch, usize obj_idx, Transform* t); -int renderbatch_refresh(RenderBatch* renderbatch); - -Shader compile_shader(const char* file_path, const ShaderType shader_type); -Shader compose_shader(Shader *shaders, usize shaders_len); -void shaders_delete(Shader* shader, usize shader_len); - -u32 ComposeShader(u32 *shaders, usize shaders_len); - -ShaderType guess_shadertype_from_filename(const char *restrict fname); - -Texture createTextureFromImageData(unsigned char* image_data, i32 width, i32 height, u8 components); - -#ifdef __cplusplus -} -#endif -#endif diff --git a/src/rendering/include/engine/rendering/window.h b/src/rendering/include/engine/rendering/window.h deleted file mode 100644 index a964b38..0000000 --- a/src/rendering/include/engine/rendering/window.h +++ /dev/null @@ -1,53 +0,0 @@ -#ifndef WINDOW_H -#define WINDOW_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include - -#include -#include - -#ifndef ENGINE_RENDERING_WINDOW_H_EXCLUDE_EXTERNS -extern u64 (*get_time)(void); -#endif - -typedef enum { - WINDOW_FRAMEWORK_NONE = 0, - WINDOW_FRAMEWORK_GLFW, -} Window_framework; - -typedef enum { - WINDOW_RENDERER_NONE = 0, - WINDOW_RENDERER_OPENGL, -} Window_renderer; - -typedef struct { - // Specifies the framwork & renderer combo used. - Window_framework framework; - Window_renderer renderer; - - // Window *buffer* size, in pixels. - ivec2 windowsize; - - // These are used differently depending on the framework / renderer combo. - // Subject to change to a union of backend-dependent structs - void* window; - void* context; - - /* The ctrl is probably the only sensible thing in this struct. */ - usize bindings_sz; - usize bindings_len; - i_ctx** bindings; -} Window; - -Window* Window_new(const char *restrict title, Window_framework framework, Window_renderer renderer, ivec2 size, u32 flags); - -void get_mousepos(double *x, double *y); - -#ifdef __cplusplus -} -#endif -#endif diff --git a/src/rendering/src/gl.c b/src/rendering/src/gl.c deleted file mode 100644 index 920518f..0000000 --- a/src/rendering/src/gl.c +++ /dev/null @@ -1,281 +0,0 @@ -#include -#include -#include - -#include - -#include -#include - -#include -#include -#include - -extern Instance* GLOBAL_PLATFORM; - -const char* ShaderType_str[] = { - [Shader_Error] = "Shader_Error", - [Shader_Program] = "Shader_Program", - [Shader_Vertex] = "Shader_Vertex", - [Shader_Tessellation] = "Shader_Tessellation", - [Shader_Geometry] = "Shader_Geometry", - [Shader_Fragment] = "Shader_Fragment", - [Shader_Compute] = "Shader_Compute", -}; - -Shader compile_shader(const char* file_path, const ShaderType shader_type) { - u32 shaderID = 0; - GLenum shadertype = GL_INVALID_ENUM; - - i32 Result = GL_FALSE; - i32 infolog_len; - - char* source = NULL; - FILE* file = NULL; - - const GladGLContext* gl = GLOBAL_PLATFORM->window->context; - - if (file_path == NULL) { - WARN("Empty path to shader"); - return (Shader){.program = 0, .type = Shader_Error}; - } - - switch (shader_type) { - case Shader_Vertex: - shadertype = GL_VERTEX_SHADER; - break; - case Shader_Fragment: - shadertype = GL_FRAGMENT_SHADER; - break; - default: break; - } - - file = fopen(file_path, "r"); - - shaderID = gl->CreateShader(shadertype); - LOG("CREATED SHADER ID %d", shaderID); - - if(file != NULL) { - const usize size = f_get_sz(file); - - source = calloc((usize)size + 1, sizeof(char)); - - // Assume the whole file is successfully read - fread(source, sizeof(char), (usize)size, file); - - fclose(file); - } else { - ERROR("Cannot open \"" TERM_COLOR_YELLOW "%s" TERM_COLOR_RESET"\".", file_path); - return (Shader){.program = 0, .type = Shader_Error}; - } - - // Compile shader - INFO("Compiling shader \"" TERM_COLOR_YELLOW "%s" TERM_COLOR_RESET"\".", file_path); - char const* src_ptr = source; - gl->ShaderSource(shaderID, 1, &src_ptr , NULL); - gl->CompileShader(shaderID); - - // Check shader - gl->GetShaderiv(shaderID, GL_COMPILE_STATUS, &Result); - gl->GetShaderiv(shaderID, GL_INFO_LOG_LENGTH, &infolog_len); - if ( infolog_len > 0 ) { - char* msg = calloc((usize)infolog_len + 1, sizeof(char)); - gl->GetShaderInfoLog(shaderID, infolog_len, NULL, msg); - ERROR("Failed to compile shader: " TERM_COLOR_YELLOW "%s" TERM_COLOR_RESET, msg); - free(msg); - } - - free(source); - - return (Shader){.program = shaderID, .type = shader_type}; -} - - -// http://www.opengl-tutorial.org/beginners-tutorials/tutorial-2-the-first-triangle/ -u32 load_shaders( - const GladGLContext* gl, - const char* vertex_file_path, - const char* fragment_file_path) { - - i32 Result = GL_FALSE; - i32 infolog_len; - - // Create the shaders - const Shader vertexShader = compile_shader(vertex_file_path, Shader_Vertex); - const Shader fragmentShader = compile_shader(fragment_file_path, Shader_Fragment); - - // Link the program - INFO("Linking program"); - u32 ProgramID = gl->CreateProgram(); - - if (vertex_file_path != NULL) gl->AttachShader(ProgramID, vertexShader.program); - if (fragment_file_path != NULL) gl->AttachShader(ProgramID, fragmentShader.program); - - gl->LinkProgram(ProgramID); - - // Check the program - gl->GetProgramiv(ProgramID, GL_LINK_STATUS, &Result); - gl->GetProgramiv(ProgramID, GL_INFO_LOG_LENGTH, &infolog_len); - if ( infolog_len > 0 ){ - char* msg = calloc((usize)infolog_len + 1, sizeof(char)); - gl->GetShaderInfoLog(ProgramID, infolog_len, NULL, msg); - ERROR("Compiling shader: " TERM_COLOR_YELLOW "%s" TERM_COLOR_RESET, msg); - free(msg); - } - - gl->DetachShader(ProgramID, vertexShader.program); - gl->DetachShader(ProgramID, fragmentShader.program); - - //gl->DeleteShader(vertexShader.program); - //gl->DeleteShader(fragmentShader.program); - - return ProgramID; -} - -/* Returns a shader program */ -Shader compose_shader(Shader *shaders, usize shaders_len) { - const GladGLContext* gl = GLOBAL_PLATFORM->window->context; - i32 Result = GL_FALSE; - - if (shaders_len == 0) { - ERROR("No shaders provided!"); - return (Shader){.program = 0, .type = Shader_Error}; - } - - u32 prog = gl->CreateProgram(); - - if (prog == 0) { - ERROR("Failed to create program!"); - return (Shader){.program = 0, .type = Shader_Error}; - } - - for (usize i = 0; i < shaders_len; i++) { - gl->AttachShader(prog, shaders[i].program); - INFO("Attaching shader %d to %d", shaders[i].program, prog); - } - - gl->LinkProgram(prog); - - // Check the program - gl->GetProgramiv(prog, GL_LINK_STATUS, &Result); - if (Result != GL_TRUE) { - // Get the size of the log - i32 log_len = 0; - i32 msg_len = 0; - gl->GetProgramiv(prog, GL_INFO_LOG_LENGTH, &log_len); - char* msg = calloc((usize)log_len + 1, sizeof(char)); - - // Copy the log message(s) - gl->GetProgramInfoLog(prog, log_len, &msg_len, msg); - - ERROR("(Compose) Compiling shader:\n" TERM_COLOR_YELLOW "%s" TERM_COLOR_RESET "\n", msg); - free(msg); - } - - for (usize i = 0; i < shaders_len; i++) { - gl->DetachShader(prog, shaders[i].program); - } - - return (Shader){.program = prog, .type = Shader_Program}; -} - -/* Free up resources associated with `shader` */ -void shaders_delete(Shader* shader, usize shader_len) { - const GladGLContext* gl = GLOBAL_PLATFORM->window->context; - - for (usize i = 0; i < shader_len; i++) { - gl->DeleteShader(shader[i].program); - } -} - -GLenum ShaderBuffer_get_gl_access(u64 flags) { - const ShaderBufferFlag access = ShaderBuffer_get_access_type(flags); - - switch(flags & 0b111) { - case ShaderBuffer_AccessFrequency_stream: - switch(access) { - case ShaderBuffer_AccessType_draw: return GL_STREAM_DRAW; - case ShaderBuffer_AccessType_read: return GL_STREAM_READ; - case ShaderBuffer_AccessType_copy: return GL_STREAM_COPY; - default: return GL_STREAM_DRAW; - } - case ShaderBuffer_AccessFrequency_static: - switch(access) { - case ShaderBuffer_AccessType_draw: return GL_STATIC_DRAW; - case ShaderBuffer_AccessType_read: return GL_STATIC_READ; - case ShaderBuffer_AccessType_copy: return GL_STATIC_COPY; - default: return GL_STATIC_DRAW; - } - case ShaderBuffer_AccessFrequency_dynamic: - switch(access) { - case ShaderBuffer_AccessType_draw: return GL_DYNAMIC_DRAW; - case ShaderBuffer_AccessType_read: return GL_DYNAMIC_READ; - case ShaderBuffer_AccessType_copy: return GL_DYNAMIC_COPY; - default: return GL_DYNAMIC_DRAW; - } - default: return GL_STATIC_DRAW; - } -} - -RenderObject RenderObject_new( - Shader* shader, - u32 texture, - ShaderBuffer *restrict buffers, usize num_buffers) { - - GladGLContext *gl = GLOBAL_PLATFORM->window->context; - RenderObject o; - - gl->GenVertexArrays(1, &(o.vao)); - gl->BindVertexArray(o.vao); - - /* For each buffer in the shader, */ - /* The shader should be generalied, */ - for (usize i = 0; i < num_buffers; i++) { - const usize sz = buffers[i].size_elem * buffers[i].count; - const u32 b_gl_type = ShaderBuffer_get_gl_type(buffers[i].buffertype); - - gl->GenBuffers(1, &(buffers[i].buffername)); - gl->BindBuffer(b_gl_type, buffers[i].buffername); - gl->BufferData(b_gl_type, (isize)sz, buffers[i].data, ShaderBuffer_get_gl_accesstype(buffers[i].buffertype)); - } - - o.shader = *shader; - o.texture = texture; - o.buffer = buffers; - o.buffer_len = num_buffers; - o.mvp = gl->GetUniformLocation(o.shader.program, "MVP"); - - // It is very much a non-issue if we don't find the model view projection in - // the shader. In fact, it is removed from a shader program if it is not used. - // TODO: Add common uniforms, should be a list of strings (uniform name) & - // their locations (i32), such as - // * mouse coords, - // * time, - // * delta time, - // * modelviewprojection, - // * window size. - // These should be added to the RenderObject, if found. - - // if (o.mvp == -1) { - // WARN("Unable to find \"MVP\" input in shader program"); - //} - - gl->BindVertexArray(0); - - - return o; -} - -ShaderType guess_shadertype_from_filename(const char *restrict fname) { - const usize path_len = strlen(fname); - - if (path_len <= 4) { - ERROR("Unable to determine shader type from suffix! (%s)", fname); - return Shader_Error; - } - - if (!strncmp(".vert", &fname[path_len - 5], 5)) { return Shader_Vertex; } - if (!strncmp(".frag", &fname[path_len - 5], 5)) { return Shader_Fragment; } - - return Shader_Error; -} diff --git a/src/rendering/src/platform_glfw.c b/src/rendering/src/platform_glfw.c deleted file mode 100644 index 47638e2..0000000 --- a/src/rendering/src/platform_glfw.c +++ /dev/null @@ -1,232 +0,0 @@ -// Required for OpenGL rendering backend -#define GLAD_GL_IMPLEMENTATION -#include -#undef GLAD_GL_IMPLEMENTATION - -// TODO: import vulkan thingymajig once I get around to it. -// TODO: move OpenGL initialization code at some point - -#undef GLFW_INCLUDE_NONE -#include - -#include - -#include - -static void window_resize_callback(GLFWwindow *restrict window, int width, int height); -static void framebuffer_resize_callback(GLFWwindow *restrict window, int width, int height); -static void glfw_err_callback(int code, const char* description); -static void render_init_opengl(Window *restrict w, const u32 flags); -static void window_size(GLFWwindow *restrict w, ivec2 *restrict dst); - - -Window* window_init_glfw(const char *restrict windowtitle, ivec2 windowsize, const u32 flags) { - Window* ret = NULL; - 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); - if (!(flags & DAW_WINDOW_RESIZEABLE)) { - glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); - } - - glfwWindowHint(GLFW_DECORATED, GLFW_FALSE); - glfwWindowHint(GLFW_FLOATING, GLFW_TRUE); - - glfwWindowHint(GLFW_SAMPLES, 0); // Disable anti aliasing - - // Use a modern opengl version - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6); - - // Lean and mean - 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 - * once of a window for it to become visible." */ - { - GLFWmonitor* mon = NULL; - if (flags & DAW_WINDOW_FULLSCREEN) mon = glfwGetPrimaryMonitor(); - - window = glfwCreateWindow(windowsize[0], windowsize[1], windowtitle, mon, 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"); - - // Setup callbacks - glfwSetFramebufferSizeCallback(window, framebuffer_resize_callback); - glfwSetWindowSizeCallback(window, window_resize_callback); - glfwSetKeyCallback(window, (GLFWkeyfun)key_callback); - - // Create the window datastructure - ret = (Window*)calloc(1, sizeof(Window)); - ret->framework = WINDOW_FRAMEWORK_GLFW; - ret->renderer = WINDOW_RENDERER_NONE; - ret->window = window; - /* Last parameter is used for the renderer */ - ret->context = NULL; - - { - ivec2 wsize; - vec2 wscaling; - glfwGetWindowContentScale(window, &wscaling[0], &wscaling[1]); - glfwGetWindowSize(window, &wsize[0], &wsize[1]); - - ret->windowsize[0] = (i32)((f32)wsize[0] * wscaling[0]); - ret->windowsize[1] = (i32)((f32)wsize[1] * wscaling[1]); - - INFO("WINDOW CONTENT SCALING: %.2f x %.2f", wscaling[0], wscaling[1]); - INFO("WINDOW SIZE: %d x %d -> %d x %d", wsize[0], wsize[1], ret->windowsize[0], ret->windowsize[1]); - } - - // TODO: set this to `ret` once all the garbage is moved to `struct Window` - glfwSetWindowUserPointer(window, (void*)ret); - - render_init_opengl(ret, flags); - - return ret; -} - -void window_destroy_glfw(Window *restrict w) { - glfwDestroyWindow(w->window); - w->window = NULL; - - // If we ever do multi-window support, we need to make sure this is the last - // window before terminating - glfwTerminate(); - - switch(w->renderer) { - case WINDOW_RENDERER_OPENGL: - // Missing unloader function in glad MX library - free(w->context); - w->context = NULL; - break; - default: - ERROR("Destroying unknown renderer type."); - } -} - -void window_resize_glfw(Window *restrict window, int width, int height) { - window_resize_callback(window->window, width, height); - framebuffer_resize_callback(window->window, width, height); -} - -bool window_should_close_glfw(Window *restrict window) { - return glfwWindowShouldClose(window->window); -} - -void window_poll_glfw(void) { - glfwPollEvents(); -} - -// Helper function implementations -static void window_resize_callback(GLFWwindow* window, int width, int height) { - (void)width; (void)height; - Window* w = glfwGetWindowUserPointer(window); - glfwSwapBuffers(window); - if (w != NULL) { - const GladGLContext* gl = w->context; - gl->Finish(); - } -} - -static void framebuffer_resize_callback(GLFWwindow* window, int width, int height) { - (void)width; (void)height; - Window* w = glfwGetWindowUserPointer(window); - if (w != NULL) { - const GladGLContext* gl = w->context; - //TODO: Move the camera to window->renderer - //Camera* c = w->cam; - gl->Viewport(0,0, width, height); - w->windowsize[0] = width; - w->windowsize[1] = height; - - //r_reset_camera(c); - } -} - -static void glfw_err_callback(int code, const char* description) { - ERROR("glfw [%d]: %s\n", code, description); - // Terminate? - exit(EXIT_FAILURE); -} - -static void render_init_opengl(Window *restrict w, const u32 flags) { - if (w->renderer != WINDOW_RENDERER_NONE || w->context != NULL) { - ERROR("Window already initialized with a renderer!"); - return; - } - - // This is GLFW specific - glfwMakeContextCurrent(w->window); - - GladGLContext* ctx = (GladGLContext*)malloc(sizeof(GladGLContext)); - if (!ctx) { - ERROR("Failed to allocate memory for context"); - } - - int version = gladLoadGLContext(ctx, glfwGetProcAddress); - INFO("Loaded OpenGL %d.%d", GLAD_VERSION_MAJOR(version), GLAD_VERSION_MINOR(version)); - - if (ctx == NULL) { - ERROR("Failed to create glad context"); - exit(EXIT_FAILURE); - } - - if (flags & DAW_WINDOW_VSYNC) { - glfwSwapInterval(1); - } else { - glfwSwapInterval(0); - } - - - ctx->Viewport(0, 0, w->windowsize[0], w->windowsize[1]); - -#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 - - // Make sure faces closest to the camera are drawn on-top of faces that are - // further away - ctx->Enable(GL_DEPTH_TEST); - ctx->DepthFunc(GL_LESS); - - w->context = ctx; - w->renderer = WINDOW_RENDERER_OPENGL; -} - -static void window_size(GLFWwindow *restrict w, ivec2 *restrict dst) { - ivec2 wsize; - vec2 wscaling; - glfwGetWindowContentScale(w, &wscaling[0], &wscaling[1]); - glfwGetWindowSize(w, &wsize[0], &wsize[1]); - - *dst[0] = (i32)((f32)wsize[0] * wscaling[0]); - *dst[1] = (i32)((f32)wsize[1] * wscaling[1]); -} diff --git a/src/rendering/src/rendering.c b/src/rendering/src/rendering.c deleted file mode 100644 index 8932cfc..0000000 --- a/src/rendering/src/rendering.c +++ /dev/null @@ -1,696 +0,0 @@ -#include -#include - -#include -#include -#include -#include -#include - - -#include -#include -#include - - -/* Extern globals */ -extern Instance* GLOBAL_PLATFORM; - -/* Globals */ -#define drawcall_limit (64 * 1024) -RenderDrawCall drawcalls[drawcall_limit]; -i32 drawcall_len = 0; - -usize -ShaderBufferDataType_size(u16 flags) { - const ShaderBufferFlag t = ShaderBuffer_get_data_type(flags); - switch (t) { - case ShaderBuffer_DataType_nil: return 0; - case ShaderBuffer_DataType_f32: return sizeof(f32); - case ShaderBuffer_DataType_f64: return sizeof(f64); - case ShaderBuffer_DataType_i8: return sizeof(i8); - case ShaderBuffer_DataType_i16: return sizeof(i16); - case ShaderBuffer_DataType_i32: return sizeof(i32); - case ShaderBuffer_DataType_i64: return sizeof(i64); - case ShaderBuffer_DataType_u8: return sizeof(u8); - case ShaderBuffer_DataType_u16: return sizeof(u16); - case ShaderBuffer_DataType_u32: return sizeof(u32); - case ShaderBuffer_DataType_u64: return sizeof(u64); - default: return 0; - } -} - -ShaderBufferFlag ShaderBuffer_get_access_frequency(u64 flags) { return flags & (0b111 << 0); } -ShaderBufferFlag ShaderBuffer_get_access_type(u64 flags) { return flags & (0b111 << 3); } -ShaderBufferFlag ShaderBuffer_get_type(u64 flags) { return flags & (0b111 << 6); } -ShaderBufferFlag ShaderBuffer_get_data_type(u64 flags) { return flags & (0b1111 << 9); } - -u32 ShaderBuffer_get_gl_type(u64 flags) { - switch(ShaderBuffer_get_type(flags)) { - case ShaderBuffer_Type_vertexData: - return GL_ARRAY_BUFFER; - case ShaderBuffer_Type_vertexPosition: - return GL_ARRAY_BUFFER; - case ShaderBuffer_Type_vertexIndex: - return GL_ELEMENT_ARRAY_BUFFER; - default: - return GL_ARRAY_BUFFER; - } -} - - -u32 ShaderBuffer_get_gl_accesstype(u64 flags) { - switch (ShaderBuffer_get_access_frequency(flags)) { - case ShaderBuffer_AccessFrequency_stream: - switch (ShaderBuffer_get_access_type(flags)) { - case ShaderBuffer_AccessType_draw: return GL_STREAM_DRAW; - case ShaderBuffer_AccessType_read: return GL_STREAM_READ; - case ShaderBuffer_AccessType_copy: return GL_STREAM_COPY; - default: return 0; - } - - case ShaderBuffer_AccessFrequency_static: - switch (ShaderBuffer_get_access_type(flags)) { - case ShaderBuffer_AccessType_draw: return GL_STATIC_DRAW; - case ShaderBuffer_AccessType_read: return GL_STATIC_READ; - case ShaderBuffer_AccessType_copy: return GL_STATIC_COPY; - default: return 0; - } - case ShaderBuffer_AccessFrequency_dynamic: - switch (ShaderBuffer_get_access_type(flags)) { - case ShaderBuffer_AccessType_draw: return GL_DYNAMIC_DRAW; - case ShaderBuffer_AccessType_read: return GL_DYNAMIC_READ; - case ShaderBuffer_AccessType_copy: return GL_DYNAMIC_COPY; - default: return 0; - } - default: return 0; - } -} - -ShaderBufferFlag ShaderBuffer_get_gl_datatype(u64 flags) { - switch (ShaderBuffer_get_data_type(flags)) { - case ShaderBuffer_DataType_nil: return GL_NONE; - - case ShaderBuffer_DataType_f32: return GL_FLOAT; - case ShaderBuffer_DataType_f64: return GL_DOUBLE; - - case ShaderBuffer_DataType_i8: return GL_BYTE; - case ShaderBuffer_DataType_i16: return GL_SHORT; - case ShaderBuffer_DataType_i32: return GL_INT; - case ShaderBuffer_DataType_i64: return GL_INT64_ARB; - - case ShaderBuffer_DataType_u8: return GL_UNSIGNED_BYTE; - case ShaderBuffer_DataType_u16: return GL_UNSIGNED_SHORT; - case ShaderBuffer_DataType_u32: return GL_UNSIGNED_INT; - case ShaderBuffer_DataType_u64: return GL_UNSIGNED_INT64_ARB; - - default: return GL_NONE; - } -} - -// `RenderBatch` is used for batch rendering. The struct is used as a -// "management" parent structure to keep track of multiple `RenderObject`s that -// are put into a final `RenderObject` to render. -// `RenderObject`s are copied to the internal `models` array, to which the -// pointer to the copied RenderObject is returned, or NULL if an error occurred. -// If changes are made to a render object the batch should be refreshed. -// Renderbatches assumes that all buffer layouts are the same. - -// renderbatch_new: Create a new render batch with space for `count` models. -int renderbatch_new(RenderBatch* renderbatch, usize count) { - /* TODO: Make it such that you can add identical models with different - * transforms, so you instead of relying on renderobject[n] to copy to the - * renderobject we have something like - * - * model { - * renderobj_idx // index in renderobj[n] that this model represents - * transform { - * size; - * pos; - * rotation; - * }; - * }; - * - * For this to work we will likely need to extend the shaderbuffer struct to - * also hold what type of data the buffer contains, s.t. we can apply the - * transformation to only geometry data. - * - * We'll therefore have both data type and buffer type stored somehow, - * maybe like we did the ShaderBufferDataType. - * TODO: Also use shaderbuffertype. - * */ - if (renderbatch == NULL) { - ERROR("renderbatch was null!"); - return -1; - } - - usize numisnstances = count; - - if (count == 0) { - // Just allocate enough for a couple hundred - count = 256; - numisnstances = count * 4; - } - - - renderbatch->msize = sizeof(RenderObject) * count; - renderbatch->mcount = 0; - renderbatch->inst_size = sizeof(BatchModelInstance) * numisnstances; - renderbatch->inst_count = 0; - renderbatch->models = (RenderObject**)calloc(count, sizeof(RenderObject*)); - - if (renderbatch->models == NULL) { - ERROR("Failed to allocate %lu size of bytes for models array!", sizeof(RenderObject*) * count); - return -1; - } - - renderbatch->instances = (BatchModelInstance*)calloc(numisnstances, sizeof(BatchModelInstance)); - - if (renderbatch->instances == NULL) { - ERROR("Failed to allocate %lu size of bytes for batch instances array!", sizeof(BatchModelInstance) * numisnstances); - return -1; - } - - memset(&(renderbatch->renderobj), 0, sizeof(RenderObject)); - - return 0; -} - -// Appends the data in src onto dst. More space for `data` is allocated if -// necessary, in which case a pointer to the new ShaderBuffer is returned. -ShaderBuffer* shaderbuffer_cat(ShaderBuffer* dst, ShaderBuffer *restrict src) { - if (dst == NULL) { - ERROR("dst is null"); - } - else if (src == NULL) { - ERROR("src is null"); - } - - if (ShaderBuffer_get_data_type(dst->buffertype) != ShaderBuffer_get_data_type(src->buffertype)) { - ERROR("Failed to concatenate shader buffers, incompatible datatypes: %d != %d", dst->buffertype, src->buffertype); - } - if (dst->components != src->components) { - ERROR("Failed to concatenate shader buffers, incompatible number of components: %d != %d", dst->components, src->components); - } - - // Assume that we single-handedly control the pointer to the data, copy and - // free the stuff. - - // Verify the size - const usize sz_src = src->size_elem * src->count; - const usize sz_dst = dst->size_elem * dst->count; - if (dst->data == NULL || sz_dst + sz_src >= dst->size) { - const usize sz_new = (1 + ((sz_src + sz_dst) / 4096)) * 4096; - // Resize dst size - dst->data = realloc(dst->data, sz_new); - dst->size = sz_new; - } - - memcpy(dst->data + sz_dst, src->data, sz_src); - - dst->count += src->count; - - return dst; -} - -// Add a render object to the render batch. -i32 renderbatch_add(RenderBatch* renderbatch, RenderObject* obj, Transform* t) { - // Check if its a valid renderbatch - if (renderbatch == NULL) { - ERROR("renderbatch was null!"); - return -1; - } - - // Check whether we have initialized models & instance memory - if (renderbatch->models == NULL) { - const usize sz = 8 * sizeof(RenderObject*); - renderbatch->models = calloc(8, sizeof(RenderObject*)); - renderbatch->msize = sz; - renderbatch->mcount = 0; - } - - if (renderbatch->instances == NULL) { - // Allocate enough for 4 times the models - const usize modelbufsz = renderbatch->msize / sizeof(RenderObject*); - const usize sz = 4 * modelbufsz * sizeof(BatchModelInstance); - renderbatch->instances = calloc(4 * modelbufsz, sizeof(BatchModelInstance)); - renderbatch->inst_size = sz; - renderbatch->inst_count = 0; - } - - // The index of the model - isize model_idx = -1; - - // Find the model, to check if it already exists - for (usize i = 0; i < renderbatch->mcount; i++) { - // Compare the model pointers - if (obj == renderbatch->models[i]) { - model_idx = (isize)i; - break; - } - } - - // Model doesn't exist, add it - if (-1 == model_idx) { - // Check if there's room enough - if ((1 + renderbatch->mcount) * sizeof(RenderObject*) > renderbatch->msize) { - // Realloc if necessary - const usize sz = renderbatch->msize * 2; - renderbatch->models = realloc(renderbatch->models, sz); - renderbatch->msize = sz; - } - - // If this is the first model, we want to copy the renderobj, and - // shaderbuffer parameters. - if (renderbatch->mcount == 0) { - // Shader, VAO, modelviewprojection, and texture, are set when the shaderobj - // is actually created with RenderObject_new later. - // The number of buffers should be the same. - //renderbatch->renderobj.shader = obj->shader; - //renderbatch->renderobj.texture = obj->texture; - - renderbatch->renderobj.buffer_len = obj->buffer_len; - if (renderbatch->renderobj.buffer == NULL) { - renderbatch->renderobj.buffer = calloc(obj->buffer_len, sizeof(ShaderBuffer)); - } else { - ERROR("RenderObj buffer is already initialized!"); - return -1; - } - - // Copy each buffers parameters - for (usize i = 0; i < renderbatch->renderobj.buffer_len; i++) { - renderbatch->renderobj.buffer[i].buffername = 0; - renderbatch->renderobj.buffer[i].buffertype = obj->buffer[i].buffertype; - // Size and count should be zero - - renderbatch->renderobj.buffer[i].components = obj->buffer[i].components; - renderbatch->renderobj.buffer[i].size_elem = obj->buffer[i].size_elem; - // Data should also be null - } - } - - //// Only concatenate the buffers once we refresh - //for (usize i = 0; i < renderbatch->renderobj.buffer_len; i++) { - // shaderbuffer_cat(&renderbatch->renderobj.buffer[i], &obj->buffer[i]); - //} - - model_idx = (isize)renderbatch->mcount; - renderbatch->models[renderbatch->mcount++] = obj; - } - - // Create batch instance - // Check if there's room enough - if ((1 + renderbatch->inst_count) * sizeof(BatchModelInstance) > renderbatch->inst_size) { - // Realloc if necessary - const usize sz = renderbatch->inst_size * 2; - renderbatch->instances = realloc(renderbatch->instances, sz); - renderbatch->inst_size = sz; - } - - BatchModelInstance inst = { - .model_idx = (usize)model_idx, - .transform = *t, - }; - - // Add it to the batch - renderbatch->instances[renderbatch->inst_count++] = inst; - - // Return instance index - return (i32)renderbatch->inst_count - 1; -} - -void renderbatch_transform(RenderBatch* renderbatch, usize obj_idx, Transform* t) { - // TODO: Combine transformation, ie. pos' += pos, etc. - const usize m = renderbatch->instances[obj_idx].model_idx; - const RenderObject* model = renderbatch->models[m]; - renderbatch->instances[obj_idx].transform = *t; - - if(renderbatch->inst_count < obj_idx) { - ERROR("renderbatch_transform: object index is outside range!"); - return; - } - /* TODO: Update the model data, we might need to - * 0. Iteratively go through each renderobj buffer, to find a vertexPosition - * buffer, - * 1. Calculate the models start index in the renderobj, - * 2. Apply transformation to the model in the renderobj buffer. - * */ - usize b; - for (b = 0; b < renderbatch->renderobj.buffer_len; b++) { - if (ShaderBuffer_Type_vertexPosition != ShaderBuffer_get_type(renderbatch->renderobj.buffer[b].buffertype) - || ShaderBuffer_DataType_f32 != ShaderBuffer_get_data_type(renderbatch->renderobj.buffer[b].buffertype)) { - continue; - } - } - - usize offset = 0; - for (usize i = 0; i < obj_idx; i++) { - const usize idx = renderbatch->instances[i].model_idx; - offset += renderbatch->models[idx]->buffer->size_elem - * renderbatch->models[idx]->buffer->count; - } - - float *data = renderbatch->renderobj.buffer[b].data; - data = &data[offset]; - const usize len = model->buffer[b].count; - - Transform tt = renderbatch->instances[obj_idx].transform; - if (model->buffer[b].components == 2) { - for (usize v = 0; v < len; v += 2) { - // scale - // rotate - // offset - glm_vec2_add(&data[v], tt.position, &data[v]); - } - } - else if (model->buffer[b].components == 3) { - for (usize v = 0; v < len; v += 3) { - // scale - // rotate - // offset - glm_vec3_add(&data[v], tt.position, &data[v]); - } - } -} - -// renderbatch_refresh: Copy all instances/models in the renderbatch to the -// batchs' model. -int renderbatch_refresh(RenderBatch* renderbatch) { - const usize bufs = renderbatch->renderobj.buffer_len; - usize *offsets = calloc(bufs, sizeof(usize)); - - // Reset renderobj buffers - for (usize b = 0; b < renderbatch->renderobj.buffer_len; b++) { - // Zero the old data - renderbatch->renderobj.buffer[b].count = 0; - memset(renderbatch->renderobj.buffer[b].data, 0, renderbatch->renderobj.buffer[b].size); - } - - // Copy the instances models buffers, and vertex position buffers with translations applied - for (usize i = 0; i < renderbatch->inst_count; i++) { - const usize m = renderbatch->instances[i].model_idx; - const RenderObject* model = renderbatch->models[m]; - Transform t = renderbatch->instances[i].transform; - - for (usize b = 0; b < renderbatch->renderobj.buffer_len; b++) { - shaderbuffer_cat(&renderbatch->renderobj.buffer[b], &model->buffer[b]); - - if (ShaderBuffer_Type_vertexPosition == ShaderBuffer_get_type(renderbatch->renderobj.buffer[b].buffertype)) { - if (ShaderBuffer_DataType_f32 != ShaderBuffer_get_data_type(renderbatch->renderobj.buffer[b].buffertype)) { - WARN("Buffer data type is not f32, skipping transformation..."); - continue; - } - // Apply transformation in renderbatch buffer-memory - - float *data = renderbatch->renderobj.buffer[b].data; - const usize len = model->buffer[b].count; - // Data points to the start of the model in renderobj - data = &data[renderbatch->renderobj.buffer[b].count - len]; - - if (model->buffer[b].components == 2) { - for (usize v = 0; v < len; v += 2) { - // scale - // rotate - // offset - glm_vec2_add(&data[v], t.position, &data[v]); - } - } - else if (model->buffer[b].components == 3) { - for (usize v = 0; v < len; v += 3) { - // scale - // rotate - // offset - glm_vec3_add(&data[v], t.position, &data[v]); - } - } - } - } - } - - free(offsets); - return 0; -} - - -/* Implementations */ - -/* Clear the screen, - * To be used inbetween draw calls */ -void render_begin(Window* w) { - glfwMakeContextCurrent(w->window); - ((GladGLContext*)(w->context))->Clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); -} - -void render_present(Window* w) { - /* This is GL specific, TODO: move the GL-specific code elsewhere. Maybe make - * this whole present GL specific? assign it as a fn ptr in the Window struct? */ - GladGLContext *restrict gl = w->context; - Camera c = *GLOBAL_PLATFORM->cam; - - mat4 view; // view - vec3 angle; // viewing angle / direction of the camera - mat4 camera_matrix; - - glm_vec3_sub(c.pos, c.dir, angle); - glm_lookat(c.pos, angle, GLM_YUP, view); - glm_mat4_mul(c.per, view, camera_matrix); - - for (i32 drawcall_idx = 0; drawcall_idx < drawcall_len; drawcall_idx++) { - RenderDrawCall dc = drawcalls[drawcall_idx]; - switch (dc.type) { - case RenderDrawCallType_Sprite: { - // TODO render a quad - } break; - - case RenderDrawCallType_Model: { - - // bind shader program - // - set uniforms - // bind vertex array - // bind index buffer - - - RenderObject* o = dc.data.model.model; - vec3 pos; - glm_vec3_copy(dc.data.model.pos, pos); - - gl->UseProgram(o->shader.program); - // TODO: Use texture atlas - gl->ActiveTexture(GL_TEXTURE0); - gl->BindTexture(GL_TEXTURE_2D, o->texture); - - { - mat4 model = GLM_MAT4_IDENTITY_INIT; - mat4 modelviewprojection; - - model[3][0] = pos[0]; - model[3][1] = pos[1]; - model[3][2] = pos[2]; - - // modelviewprojection = p * view * model - glm_mat4_mul(model, camera_matrix, modelviewprojection); - - // TODO: Do this only once during initialization - gl->UniformMatrix4fv(o->mvp, 1, GL_FALSE, &modelviewprojection[0][0]); - } - - // TODO the buffers need to be abstracted a bit more - gl->BindVertexArray(o->vao); - - ShaderBuffer* ibo = NULL; - for (usize i = 0; i < o->buffer_len; i++) { - const u32 b_gl_type = ShaderBuffer_get_gl_type(o->buffer[i].buffertype); - if (b_gl_type == GL_ELEMENT_ARRAY_BUFFER) { - ibo = &o->buffer[i]; - } - - gl->EnableVertexAttribArray((u32)i); - gl->BindBuffer(b_gl_type, o->buffer[i].buffername); - gl->VertexAttribPointer( - // index of the attribute - (u32)i, - // number of component - (i32)o->buffer[i].components, - // type - ShaderBuffer_get_gl_datatype(o->buffer[i].buffertype), - // normalized? - GL_FALSE, - // stride - 0, - // array buffer offset - (void*)0 - ); - } - - // Draw the model ! - const i32 sz = (i32)(o->buffer->count * o->buffer->size_elem); - if (ibo) { - gl->DrawElements( - GL_TRIANGLES, - (i32)ibo->count, - ShaderBuffer_get_gl_datatype(ibo->buffertype), - (void*)0 - ); - } else { - // Starting from vertex 0; 3 vertices total -> 1 triangle - gl->DrawArrays(GL_TRIANGLES, 0, sz); - } - - for (u32 i = 0; i < o->buffer_len; i++) { - gl->DisableVertexAttribArray(i); - } - - gl->BindVertexArray(0); - - //if (i == 8) { - // printf("\r obj: %.3f", (double)(get_time() - t) * 1000.); - //} - } break; - default: - break; - } - } - - drawcall_len = 0; - - glfwSwapBuffers(w->window); -} - -void drawcall_reset(void) { - drawcall_len = 0; - memset(drawcalls, 0, sizeof(RenderDrawCall) * drawcall_limit); -} - -void r_perspective(f32 fov, Camera *c) { - const f32 ratio = (f32)GLOBAL_PLATFORM->window->windowsize[0] - / (f32)GLOBAL_PLATFORM->window->windowsize[1]; - - c->type = Camera_Perspective; - c->parameters.perspective.fov = fov; - - glm_perspective(glm_rad(fov), ratio, 0.1f, 100.0f, c->per); -} - -void r_perspective_ortho(f32 sz, Camera *c) { - const f32 ratio = (f32)GLOBAL_PLATFORM->window->windowsize[0] - / (f32)GLOBAL_PLATFORM->window->windowsize[1]; - - c->type = Camera_Orthogonal; - c->parameters.orthogonal.sz = sz; - - glm_ortho(-sz * ratio, sz * ratio, -sz, sz, -sz * 10.f, sz * 10.f, c->per); -} - -void r_set_camera(Camera* c) { - GLOBAL_PLATFORM->cam = c; -} - - -void r_reset_camera(Camera* c) { - if (c->type == Camera_Perspective) { - r_perspective(c->parameters.perspective.fov, c); - } - else if (c->type == Camera_Orthogonal) { - r_perspective_ortho(c->parameters.orthogonal.sz, c); - } -} - -void engine_draw_sprite(Sprite* s, ivec2* 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[0], - .y = *pos[1], - .scale = scale, - //.mod = {0xFF, 0xFF, 0xFF, 0xFF}, - }}; -} - -void engine_draw_sprite_ex(Sprite* s, ivec2* 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[0], - .y = *pos[1], - .scale = scale, - //.mod = {colormod.r, colormod.g, colormod.b, colormod.a}, - }}; -} - -void engine_draw_model(RenderObject* o, vec3 pos) { - if (drawcall_len + 1 >= drawcall_limit) return; -#ifdef _DEBUG - if (o == NULL) __asm__("int3;"); -#endif - RenderDrawCall dc = { - .type = RenderDrawCallType_Model, - .data.model = { - .model = o, - .scale = 1.f, - }}; - - - glm_vec3_copy(pos, dc.data.model.pos); - - drawcalls[drawcall_len++] = dc; -} - -Sprite sprite_new(u64 tid, u8 coord) { - const i32 ts = 16; - // FIXME; used to be - //((struct Resources*)GLOBAL_PLATFORM->data)->textures[tid]->tilesize; - return (Sprite){ - .texture_id = (u32)tid, - { - ts * (coord & 0x0F), - ts * ((coord & 0xF0) >> 4), - }}; -} - -Texture createTextureFromImageData(unsigned char* image_data, i32 width, i32 height, u8 components) { - Window* restrict w = GLOBAL_PLATFORM->window; - Texture t; - t.width = width; - t.height = height; - - if (w->renderer != WINDOW_RENDERER_OPENGL) { - ERROR("createTextureFromImageData not implemented for chosen renderer!"); - return (Texture){.id = 0, .width = 0, .height = 0}; - } - - const GladGLContext* gl = w->context; - - gl->GenTextures(1, &t.id); - gl->BindTexture(GL_TEXTURE_2D, t.id); - u32 err = gl->GetError(); - if (err) { - ERROR("Failed to bind texture from image data!"); - } - - /* TODO: Support more formats than rgb and rgba, such as gray, gray/alpha, etc.*/ - u32 format = GL_RGB; - if (components == 4) format = GL_RGBA; - - /* TODO: Don't force internal format to RGB */ - - gl->TexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, format, - GL_UNSIGNED_BYTE, image_data); - - gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - - gl->BindTexture(GL_TEXTURE_2D, 0); - - return t; -} diff --git a/src/rendering/src/window.c b/src/rendering/src/window.c deleted file mode 100644 index d761543..0000000 --- a/src/rendering/src/window.c +++ /dev/null @@ -1,83 +0,0 @@ -#include - -/* TODO: REMOVE THIS INCLUSION */ -#include - -#include -#include - -#define ENGINE_RENDERING_WINDOW_H_EXCLUDE_EXTERNS -#include -#undef ENGINE_RENDERING_WINDOW_H_EXCLUDE_EXTERNS - -#include - -#include - -#undef GLFW_INCLUDE_NONE -#include - -#include - -extern Instance* GLOBAL_PLATFORM; - -static inline u64 platform_get_time_usec(void) { - struct timespec t; - int res = clock_gettime(CLOCK_MONOTONIC, &t); - if (res != 0) { - // TODO: Check errno - WARN("Failed to get system time"); - } - return (u64)(t.tv_sec * 1000000 + t.tv_nsec / 1000); -} - -/* wrapper to get time in ms */ -u64 (*get_time)(void) = platform_get_time_usec; - - -#define DAW_WINDOW_VSYNC (1 << 0) -#define DAW_WINDOW_FULLSCREEN (1 << 1) -#define DAW_WINDOW_RESIZEABLE (1 << 2) - -// Wrapper to get a specific window and set up related structures. -Window* Window_new(const char *restrict title, Window_framework framework, Window_renderer renderer, ivec2 size, u32 flags) { - Window* w = NULL; - - switch (framework) { - case WINDOW_FRAMEWORK_GLFW: - switch (renderer) { - case WINDOW_RENDERER_OPENGL: - /* For now, pass instance as NULL, fix it once all the necessary bs is - * out of struct Instance */ - w = Platform_GLFW.window_init(title, size, flags); - - w->bindings = NULL; - w->bindings_sz = 0; - w->bindings_len = 0; - - /// TODO - //w->cam = &default_camera; - return w; - break; - default: - ERROR("Unsupported renderer."); - } - break; - default: - ERROR("Unsupported framework."); - } - return NULL; -} - -void get_mousepos(double *x, double *y) { - Window* w = GLOBAL_PLATFORM->window; - - switch(w->framework) { - case WINDOW_FRAMEWORK_GLFW: - glfwGetCursorPos(GLOBAL_PLATFORM->window->window, x, y); - break; - default: - ERROR("get_mouse_pos not implemented for chosen framework."); - } - -} diff --git a/src/resources.c b/src/resources.c new file mode 100644 index 0000000..49b20e0 --- /dev/null +++ b/src/resources.c @@ -0,0 +1,155 @@ +#include + +#include +#include +#include +#include +#include + +extern Instance* GLOBAL_PLATFORM; + +extern const char* ShaderType_str[]; + +void* get_asset(Resources* r, u32 idx) { + switch (r->assets[idx].type) { + case Asset_shader: + // The shaders used to compile shader programs are not preserved. + WARN("We don't store pure shaders"); + break; + + case Asset_shaderprog: + LOG("Idx: r->get[%d] = %d", idx, r->get[idx]); + //LOG("Ptr: &r->shader[r->get[idx]] = %z", &r->shader[r->get[idx]]); + return &r->shader[r->get[idx]]; + + case Asset_texture: + return &r->texture[r->get[idx]]; + + case Asset_error: + case Asset_audio: + case Asset_font: + default: + ERROR("Asset type Not implemented"); + break; + } + return NULL; +} + +i32 resources_load(Resources *resources) { + resources->get = calloc(resources->assets_len, sizeof(usize)); + usize audio_len = 0; + usize font_len = 0; + usize shader_len = 0; + usize shaderprog_len = 0; + usize texture_len = 0; + + usize i = 0; + + // Count each type of resource so we can allocate appropriate sizes. + for (i = 0; i < resources->assets_len; i++) { + usize idx = 0; + + switch (resources->assets[i].type) { + case Asset_audio: idx = audio_len++; WARN("Audio resource type not implemented!"); break; + case Asset_font: idx = font_len++; WARN("Font resource type not implemented!"); break; + case Asset_shader: idx = shader_len++; break; + case Asset_shaderprog: idx = shaderprog_len++; break; + case Asset_texture: idx = texture_len++; break; + + case Asset_error: break; + default: + ERROR("Unknown resource type!"); + exit(EXIT_FAILURE); + break; + } + + // Update index translation table. + resources->get[i] = idx; + } + + //resources->audio = calloc(audio_len, sizeof()); + //resources->font = calloc(font_len, sizeof()); + resources->shader = calloc(shaderprog_len, sizeof(Shader)); + resources->texture = calloc(texture_len, sizeof(Texture)); + + Shader* imm_shader = calloc(shader_len, sizeof(Shader)); + + audio_len = 0; + font_len = 0; + shader_len = 0; + shaderprog_len = 0; + texture_len = 0; + + for (i = 0; i < resources->assets_len; i++) { + switch (resources->assets[i].type) { + case Asset_audio: + //resources->audio_len++; + WARN("Audio resource type not implemented!"); + break; + case Asset_font: + //resources->font_len++; + WARN("Font resource type not implemented!"); + break; + case Asset_shader: { + ShaderType t = + guess_shadertype_from_filename(resources->assets[i].shader.path); + const Shader s = compile_shader(resources->assets[i].shader.path, t); + LOG("Compiled %s! (%s)", resources->assets[i].shader.path, ShaderType_str[t]); + + imm_shader[shader_len] = s; + shader_len++; + } break; + case Asset_shaderprog: { + const usize sz = resources->assets[i].shaderprog.shader_len; + const u32* shader_ids = resources->assets[i].shaderprog.shader; + Shader* shaders = calloc(sz, sizeof(Shader)); + + for (usize j = 0; j < sz; j++) { + //DEBUG("shader[%d] = %d\n", j, imm_shader[resources->assets[i].shaderprog.shader[j]].program); + //shaders[j] = imm_shader[resources->assets[i].shaderprog.shader[j]]; + u32 shader_idx = shader_ids[j]; + //DEBUG("Idx: get[%d] = %d\n", shader_idx, imm_shader[resources->get[shader_idx]].program); + shaders[j] = imm_shader[resources->get[shader_idx]]; + } + + const Shader s = compose_shader(shaders, sz); + + resources->shader[resources->shader_len] = s; + resources->shader_len++; + } break; + case Asset_texture: + resources->texture[resources->texture_len] = load_texture(&resources->assets[i].texture); + resources->texture_len++; + LOG("Loaded texture \"%s\"!", resources->assets[i].texture.path); + break; + + case Asset_error: + break; + default: + ERROR("Unknown resource type!"); + exit(EXIT_FAILURE); + break; + } + + //resources->get[i] = idx; + } + + LOG("Total assets: %lu", resources->assets_len); + LOG(" Shaders: %lu", resources->shader_len); + LOG(" Textures: %lu", resources->texture_len); + + // Delete the immediate shaders + for (i = 0; i < shader_len; i++) { + // glDeleteShader() + } + + shaders_delete(imm_shader, shader_len); + free(imm_shader); + + return 0; +} + +isize resource_make_global(isize resource_id) { + ERROR("`resource_make_global` Not implemented"); + return -1; +} diff --git a/src/resources/CMakeLists.txt b/src/resources/CMakeLists.txt deleted file mode 100644 index 4d3ee22..0000000 --- a/src/resources/CMakeLists.txt +++ /dev/null @@ -1,19 +0,0 @@ -## Uncomment the following to fetch and build assimp. -#FetchContent_Declare(assimp -# GIT_REPOSITORY https://github.com/assimp/assimp.git -# GIT_TAG v5.3.1 -#) -#FetchContent_MakeAvailable(assimp) - -add_library(daw_resources - src/resources.c - src/textures.c - src/model.c - ) - -target_compile_options(daw_resources PUBLIC ${BUILD_OPTS}) -target_include_directories(daw_resources PRIVATE ${DAW_INCLUDE_DIRS} stb) -target_link_libraries(daw_resources PRIVATE - assimp - cglm - ) diff --git a/src/resources/include/engine/resources.h b/src/resources/include/engine/resources.h deleted file mode 100644 index d967403..0000000 --- a/src/resources/include/engine/resources.h +++ /dev/null @@ -1,138 +0,0 @@ -#ifndef ENGINE_RESOURCES_H -#define ENGINE_RESOURCES_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include - -// TODO -/* We need some "global resources", available to all states. - * These are resources used throughout the applications lifetime, such as common - * fonts, GUI frames, button background images. - * - * We need to define state-specific resources as well. - * - Can both be defined alike? - * If we lazy-load all resources we can get away with a lot. - * Maybe use fall-back resources? like the missing source texture, and an ugly - * font? - * - Then declare to the engine, in the main function for the game, that these - * resources are to be made available throughout? - * - Shaders, oh boy oh shaders. - * We need to make some meta-shader declaration, so we can declare a set of - * shaders, that are used to link with other shaders, s.t. we can free up the - * shaders after compilation. - * - Make all resource specifications a union. - * */ - -enum Asset { - Asset_error, - Asset_audio, - Asset_font, - Asset_shader, - Asset_shaderprog, - Asset_texture, - Asset_model, -}; - -typedef struct { - enum Asset type; - const char* path; -} Asset_AudioSpec; - -typedef struct { - enum Asset type; - const char* path; -} Asset_FontSpec; - -// if a shader is declared GLOBALLY, we should not destroy it when done with the -// "main" shader compilations. -typedef struct { - enum Asset type; - // Assume shader type from filename - const char* path; -} Asset_ShaderSpec; - -// Use list of gluint shader program ids to link against. This translates to a -// call to compose_shader. -typedef struct { - enum Asset type; - const u32* shader; - const usize shader_len; -} Asset_ShaderProgramSpec; - -typedef struct { - enum Asset type; - const char* path; - /* Bits per component, set to zero if you don't want to change the format. */ - i32 bpc; -} Asset_TextureSpec; - -typedef struct { - enum Asset type; - const char* path; -} Asset_ModelSpec; - -typedef union { - enum Asset type; - Asset_AudioSpec audio; - Asset_FontSpec font; - Asset_ShaderSpec shader; - Asset_ShaderProgramSpec shaderprog; - Asset_TextureSpec texture; - Asset_ModelSpec model; -} asset_t; - -#include - -// The resource spec -typedef struct { - /* Assorted asset specification, makes reloading them easier. */ - usize assets_len; - asset_t* assets; - - /* Translation from `assets`'s indices to type-specific loaded assets: */ - usize* get; // Let r=Resources, then use as: `r.shader[ r.get[ MyShader ] ]` - - /* Loaded assets */ - usize shader_len; - Shader* shader; - - usize texture_len; - Texture* texture; - - usize model_len; - Model* model; -} Resources; - -#define TextureDefinition(_path, ...) unimplemented -#define Resource_AudioDefinition(_path, ...) unimplemented -#define Declare_Shader(PATH) (const asset_t){.shader = {.type = Asset_shader, .path=PATH}} -#define Declare_ShaderProgram(SHADERS, NUMSHADERS) (const asset_t){.shaderprog = {.type = Asset_shaderprog, .shader=SHADERS, .shader_len=NUMSHADERS}} -#define Declare_Texture(PATH) (const asset_t){.texture = {.type = Asset_texture, .path=PATH, .bpc=0}} - -void* get_asset(Resources* r, u32 idx); - -/* Each of resource_load_font, resource_load_texture, and resource_load_audio - * loads a given resource into the engines memory and returns an identifier. - */ -//isize resource_load_font(Asset_FontSpec font_def); -//isize resource_load_texture(Asset_TextureSpec texture_def); -//isize resource_load_audio(Asset_AudioSpec audio_def); - -// Loads all resources specified in `assets` and populates corresponding -// resources members. -i32 resources_load(Resources *resources); - -/* Makes a resource globally available. This must be called **BEFORE** any call - * to `engine_run` */ -isize resource_make_global(isize resource_id); - -#include - -#ifdef __cplusplus -} -#endif -#endif diff --git a/src/resources/include/engine/resources/model.h b/src/resources/include/engine/resources/model.h deleted file mode 100644 index a2fb29c..0000000 --- a/src/resources/include/engine/resources/model.h +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef ENGINE_RESOURCES_MODEL_H -#define ENGINE_RESOURCES_MODEL_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include - -typedef enum { - Model_error, - Model_obj, -} ModelType; - -typedef struct { - ModelType format; - - u32 m_uiVAO; - u32 m_uiVBO; - u32 m_uiIBO; - unsigned m_uiNumIndices; -} Model; - -#include -Model load_model(const Asset_ModelSpec *restrict ms); - -#ifdef __cplusplus -} -#endif -#endif diff --git a/src/resources/include/engine/resources/texture.h b/src/resources/include/engine/resources/texture.h deleted file mode 100644 index 9f533d6..0000000 --- a/src/resources/include/engine/resources/texture.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef TEXTURE_H -#define TEXTURE_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include -#include - -Texture load_texture(const Asset_TextureSpec *restrict ts); - -#ifdef __cplusplus -} -#endif -#endif diff --git a/src/resources/src/model.c b/src/resources/src/model.c deleted file mode 100644 index 7244e63..0000000 --- a/src/resources/src/model.c +++ /dev/null @@ -1,68 +0,0 @@ -#include -#include -#include -#include -#include -#include - -Model load_model(const Asset_ModelSpec *restrict ms) { - ModelType format = Model_error; - - /* - isize vertices_len = 0; - vec3 *vertices = malloc(sizeof(vec3) * 512); - usize *vertices_idx = malloc(sizeof(usize) * 512); - - isize uvs_len = 0; - vec2 *uvs = malloc(sizeof(vec2) * 512); - usize *uvs_idx = malloc(sizeof(usize) * 512); - - isize normals_len = 0; - vec3 *normals = malloc(sizeof(vec3) * 512); - usize *normals_idx = malloc(sizeof(usize) * 512); - */ - - // For now, just default to obj - format = Model_obj; - - FILE* f = fopen(ms->path, "r"); - if (f == NULL) { - ERROR("Failed to load file " TERM_COLOR_YELLOW "%s" TERM_COLOR_RESET, ms->path); - return (Model){.format = format}; - } - const usize filesz = f_get_sz(f); - - char* filecontets = calloc(filesz, sizeof(char)); - fread(filecontets, sizeof(char), filesz, f); - - // Start the import on the given file with some example postprocessing - // Usually - if speed is not the most important aspect for you - you'll t - // probably to request more postprocessing than we do in this example. - const struct aiScene* scene = aiImportFile( filecontets, - aiProcess_CalcTangentSpace | - aiProcess_Triangulate | - aiProcess_JoinIdenticalVertices | - aiProcess_SortByPType); - - free(filecontets); - // If the import failed, report it - if( NULL == scene) { - ERROR("Failed to import %s: %s", ms->path, aiGetErrorString()); - return (Model){.format = format}; - } - - //// Now we can access the file's contents - //DoTheSceneProcessing(scene); - - //// We're done. Release all resources associated with this import - aiReleaseImport(scene); - - { - Model m = {}; - m.format = format; - - // TODO add index array - - return m; - } -} diff --git a/src/resources/src/resources.c b/src/resources/src/resources.c deleted file mode 100644 index 6e35a4f..0000000 --- a/src/resources/src/resources.c +++ /dev/null @@ -1,154 +0,0 @@ -#include - -#include -#include -#include -#include - -extern Instance* GLOBAL_PLATFORM; - -extern const char* ShaderType_str[]; - -void* get_asset(Resources* r, u32 idx) { - switch (r->assets[idx].type) { - case Asset_shader: - // The shaders used to compile shader programs are not preserved. - WARN("We don't store pure shaders"); - break; - - case Asset_shaderprog: - LOG("Idx: r->get[%d] = %d", idx, r->get[idx]); - //LOG("Ptr: &r->shader[r->get[idx]] = %z", &r->shader[r->get[idx]]); - return &r->shader[r->get[idx]]; - - case Asset_texture: - return &r->texture[r->get[idx]]; - - case Asset_error: - case Asset_audio: - case Asset_font: - default: - ERROR("Asset type Not implemented"); - break; - } - return NULL; -} - -i32 resources_load(Resources *resources) { - resources->get = calloc(resources->assets_len, sizeof(usize)); - usize audio_len = 0; - usize font_len = 0; - usize shader_len = 0; - usize shaderprog_len = 0; - usize texture_len = 0; - - usize i = 0; - - // Count each type of resource so we can allocate appropriate sizes. - for (i = 0; i < resources->assets_len; i++) { - usize idx = 0; - - switch (resources->assets[i].type) { - case Asset_audio: idx = audio_len++; WARN("Audio resource type not implemented!"); break; - case Asset_font: idx = font_len++; WARN("Font resource type not implemented!"); break; - case Asset_shader: idx = shader_len++; break; - case Asset_shaderprog: idx = shaderprog_len++; break; - case Asset_texture: idx = texture_len++; break; - - case Asset_error: break; - default: - ERROR("Unknown resource type!"); - exit(EXIT_FAILURE); - break; - } - - // Update index translation table. - resources->get[i] = idx; - } - - //resources->audio = calloc(audio_len, sizeof()); - //resources->font = calloc(font_len, sizeof()); - resources->shader = calloc(shaderprog_len, sizeof(Shader)); - resources->texture = calloc(texture_len, sizeof(Texture)); - - Shader* imm_shader = calloc(shader_len, sizeof(Shader)); - - audio_len = 0; - font_len = 0; - shader_len = 0; - shaderprog_len = 0; - texture_len = 0; - - for (i = 0; i < resources->assets_len; i++) { - switch (resources->assets[i].type) { - case Asset_audio: - //resources->audio_len++; - WARN("Audio resource type not implemented!"); - break; - case Asset_font: - //resources->font_len++; - WARN("Font resource type not implemented!"); - break; - case Asset_shader: { - ShaderType t = - guess_shadertype_from_filename(resources->assets[i].shader.path); - const Shader s = compile_shader(resources->assets[i].shader.path, t); - LOG("Compiled %s! (%s)", resources->assets[i].shader.path, ShaderType_str[t]); - - imm_shader[shader_len] = s; - shader_len++; - } break; - case Asset_shaderprog: { - const usize sz = resources->assets[i].shaderprog.shader_len; - const u32* shader_ids = resources->assets[i].shaderprog.shader; - Shader* shaders = calloc(sz, sizeof(Shader)); - - for (usize j = 0; j < sz; j++) { - //DEBUG("shader[%d] = %d\n", j, imm_shader[resources->assets[i].shaderprog.shader[j]].program); - //shaders[j] = imm_shader[resources->assets[i].shaderprog.shader[j]]; - u32 shader_idx = shader_ids[j]; - //DEBUG("Idx: get[%d] = %d\n", shader_idx, imm_shader[resources->get[shader_idx]].program); - shaders[j] = imm_shader[resources->get[shader_idx]]; - } - - const Shader s = compose_shader(shaders, sz); - - resources->shader[resources->shader_len] = s; - resources->shader_len++; - } break; - case Asset_texture: - resources->texture[resources->texture_len] = load_texture(&resources->assets[i].texture); - resources->texture_len++; - LOG("Loaded texture \"%s\"!", resources->assets[i].texture.path); - break; - - case Asset_error: - break; - default: - ERROR("Unknown resource type!"); - exit(EXIT_FAILURE); - break; - } - - //resources->get[i] = idx; - } - - LOG("Total assets: %lu", resources->assets_len); - LOG(" Shaders: %lu", resources->shader_len); - LOG(" Textures: %lu", resources->texture_len); - - // Delete the immediate shaders - for (i = 0; i < shader_len; i++) { - // glDeleteShader() - } - - shaders_delete(imm_shader, shader_len); - free(imm_shader); - - return 0; -} - -isize resource_make_global(isize resource_id) { - ERROR("`resource_make_global` Not implemented"); - return -1; -} diff --git a/src/resources/src/textures.c b/src/resources/src/textures.c deleted file mode 100644 index 913c931..0000000 --- a/src/resources/src/textures.c +++ /dev/null @@ -1,41 +0,0 @@ -#include -#include -#include - -/* Uses stb_image to load an image, and passes it to the renderer for - * backend-specific texture creation. */ -Texture load_texture(const Asset_TextureSpec *restrict ts) { - int width; - int height; - int components_per_pixel; - unsigned char* img; - Texture t; - const Texture err = (Texture){.id = 0, .width = 0, .height = 0}; - - if (ts == NULL) { - ERROR("Invalid Asset_TextureSpec\n"); - return err; - } - - if (ts->path == NULL) { - ERROR("Missing path in Asset_TextureSpec\n"); - return err; - } - - img = stbi_load(ts->path, &width, &height, &components_per_pixel, 0); - - if (img == NULL) { - ERROR("Failed to load image %s", ts->path); - return err; - } else { - t = createTextureFromImageData(img, width, height, (u8)components_per_pixel); - stbi_image_free(img); - } - - if (t.id == 0) { - ERROR("Failed to create texture %s!", ts->path); - return err; - } - - return t; -} diff --git a/src/stack.c b/src/stack.c new file mode 100644 index 0000000..f0023c4 --- /dev/null +++ b/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); +} + +usize 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); + } + usize 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/state.c b/src/state.c new file mode 100644 index 0000000..08d9f37 --- /dev/null +++ b/src/state.c @@ -0,0 +1,251 @@ +#include + +#include +#include +#include +#include + +//typedef void state_init_t(void*,void*); +//typedef void* (state_free_t(void*)); +typedef StateType state_update_t(f64, 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*,void*); \ + 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 State_init(StateType type, memory* mem, void* arg) { + switch (type) { +#define State(name) \ + case (STATE_##name): { \ + name##_init(memory_allocate(mem, sizeof(name##_state)), arg); \ + 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) { + void* state_retval = NULL; + switch (type) { +#define State(name) \ + case (STATE_##name): { \ + state_retval = 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); + return state_retval; +} + +/* Returns the update function of a given state type */ +StateType (*State_updateFunc(StateType type))(f64, 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, f64 dt, 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); +} + + +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; +} + +#endif diff --git a/src/textures.c b/src/textures.c new file mode 100644 index 0000000..5b23e7a --- /dev/null +++ b/src/textures.c @@ -0,0 +1,41 @@ +#include +#include +#include + +/* Uses stb_image to load an image, and passes it to the renderer for + * backend-specific texture creation. */ +Texture load_texture(const Asset_TextureSpec *restrict ts) { + int width; + int height; + int components_per_pixel; + unsigned char* img; + Texture t; + const Texture err = (Texture){.id = 0, .width = 0, .height = 0}; + + if (ts == NULL) { + ERROR("Invalid Asset_TextureSpec\n"); + return err; + } + + if (ts->path == NULL) { + ERROR("Missing path in Asset_TextureSpec\n"); + return err; + } + + img = stbi_load(ts->path, &width, &height, &components_per_pixel, 0); + + if (img == NULL) { + ERROR("Failed to load image %s", ts->path); + return err; + } else { + t = createTextureFromImageData(img, width, height, (u8)components_per_pixel); + stbi_image_free(img); + } + + if (t.id == 0) { + ERROR("Failed to create texture %s!", ts->path); + return err; + } + + return t; +} diff --git a/src/utils/CMakeLists.txt b/src/utils/CMakeLists.txt deleted file mode 100644 index 6acdba1..0000000 --- a/src/utils/CMakeLists.txt +++ /dev/null @@ -1,11 +0,0 @@ -add_library(daw_utils - src/btree.c - src/fov.c - src/hashmap.c - src/misc.c - src/stack.c - ) - -target_compile_options(daw_utils PUBLIC ${BUILD_OPTS}) -target_include_directories(daw_utils PRIVATE ${DAW_INCLUDE_DIRS}) -target_link_libraries(daw_utils PRIVATE cglm) diff --git a/src/utils/include/engine/utils.h b/src/utils/include/engine/utils.h deleted file mode 100644 index 5096aa4..0000000 --- a/src/utils/include/engine/utils.h +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef ENGINE_UTILS_H -#define ENGINE_UTILS_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include - -#define MIN(a, b) ((a < b) ? (a) : (b)) -#define MAX(a, b) ((a > b) ? (a) : (b)) - -#define MASK_TL (1 << 0) -#define MASK_T (1 << 1) -#define MASK_TR (1 << 2) -#define MASK_L (1 << 3) -#define MASK_C (1 << 4) -#define MASK_R (1 << 5) -#define MASK_BL (1 << 6) -#define MASK_B (1 << 7) -#define MASK_BR (1 << 8) - -/* Linear interpolate */ -f32 lerp(f32 dt, f32 a, f32 b); -i32 int_lerp(f32 dt, i32 a, i32 b); - -/* Hashes */ -u32 hash(char* str); - -/* Masks surrounding tiles of a kernel size of 3x3 */ -/* In reality we only need 9 bits for this, but I think I had a reason for using - * i32 */ -i32* kernmap(const void* map, i32* dstmap, const ivec2 mapsize, - predicate_t* predicate); - -/* Returns an index from the given weights. */ -i32 pick_from_sample(const i32* weights, i32 len); - -#ifdef __cplusplus -} -#endif -#endif diff --git a/src/utils/include/engine/utils/btree.h b/src/utils/include/engine/utils/btree.h deleted file mode 100644 index 1dd2023..0000000 --- a/src/utils/include/engine/utils/btree.h +++ /dev/null @@ -1,61 +0,0 @@ -#ifndef BTREE_H -#define BTREE_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include - -#define BTREE_DEGREE_DEFAULT 4 - -#define BTREE_SIZE_MIN 8 -#define BTREE_SIZE_MAX 4096 - -#define BTREE_CMP_LT (-1) -#define BTREE_CMP_EQ (0) -#define BTREE_CMP_GT (1) - -struct btree; -struct btree_iter_t; - -/* elem_size: the size of the elements, typically `sizeof(struct )` - * t: degree of the btree, if you're in doubt, use `BTREE_SIZE_DEFAULT` - * cmp: comparison function, in order to support any operations on the tree. - * - * This function just calls `btree_new_with_allocator` with `free` and `malloc` - * as initializers. - */ -struct btree* btree_new(size_t elem_size, size_t t, - int (*cmp)(const void* a, const void* b)); - -/* Same as `btree_new`, except that it actually initializes a btree, but with - * the given allocators. - */ -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*)); - -void btree_free(struct btree** btree); - -void* btree_search(struct btree* btree, void* elem); -void btree_insert(struct btree* btree, void* elem); -int btree_delete(struct btree* btree, void* elem); - -void btree_print(struct btree* btree, void (*print_elem)(const void*)); - -void* btree_first(struct btree* btree); -void* btree_last(struct btree* btree); - -size_t btree_size(struct btree* btree); - -struct btree_iter_t* btree_iter_t_new(struct btree* tree); -void btree_iter_t_reset(struct btree* tree, struct btree_iter_t** it); - -void* btree_iter(struct btree* tree, struct btree_iter_t* iter); - -#ifdef __cplusplus -} -#endif -#endif diff --git a/src/utils/include/engine/utils/fov.h b/src/utils/include/engine/utils/fov.h deleted file mode 100644 index be64a9e..0000000 --- a/src/utils/include/engine/utils/fov.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef ENGINE_FOV_H -#define ENGINE_FOV_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include -#include - -/* `fov_shadowcast`: */ -/* map: your 2D enum tile grid - * mapsize: x: width, y: height of the map - * visblocking: pointer to a function that returns `true` when receiving a - * pointer to a LOS blocking tile - * lightmap: [out] 2D lightmap, this is simply overwritten with the - * distance to the source. range: visibility range/radius. src: 2D - * point to calculate FOV from - * */ -void fov_shadowcast(const void* map, const ivec2 mapsize, - bool (*visblocking)(const void*), i32* lightmap, - const i32 range, const ivec2 src); - -#ifdef __cplusplus -} -#endif -#endif diff --git a/src/utils/include/engine/utils/hashmap.h b/src/utils/include/engine/utils/hashmap.h deleted file mode 100644 index 0113ce5..0000000 --- a/src/utils/include/engine/utils/hashmap.h +++ /dev/null @@ -1,61 +0,0 @@ -#ifndef ENGINE_HASHMAP_H -#define ENGINE_HASHMAP_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include - -#include -#include -#include - -usize lolhash(const usize s, usize v); - -/* Define a linked list before using this */ -/* Example: DEFINE_LLIST(i32) */ -#define DEFINE_HASHMAP(type, lsize, cmp, type_to_int) \ - typedef DEFINE_LLIST(type); \ - typedef struct hashmap_##type { \ - usize size; \ - List_##type elems[64]; \ - } hashmap_##type; \ - \ - type* hashmap_##type##_lookup(hashmap_##type* hmap, const type* val) { \ - const usize idx = lolhash(64, type_to_int(val)); \ - List_##type* head = &hmap->elems[idx]; \ - while (head != NULL) { \ - if (!cmp(&(head->value), val)) return &(head->value); \ - head = head->next; \ - } \ - return NULL; \ - } \ - \ - void hashmap_##type##_insert(memory* m, hashmap_##type* hmap, \ - const type* val) { \ - const usize idx = lolhash(64, type_to_int(val)); \ - List_##type* head = &(hmap->elems[idx]); \ - \ - /* This is highly dependant on whether the memory is zero-initialized */ \ - if (!type_to_int(&(head->value))) { \ - memcpy(&(head->value), val, sizeof(type)); \ - return; \ - } \ - \ - while (head->next != NULL && cmp(&head->value, val)) { \ - head = head->next; \ - } \ - \ - if (!cmp(&head->value, val)) \ - memcpy(&(head->value), val, sizeof(type)); \ - else { \ - head->next = memory_allocate(m, sizeof(List_##type)); \ - memcpy(&(head->next->value), val, sizeof(type)); \ - } \ - } - -#ifdef __cplusplus -} -#endif -#endif diff --git a/src/utils/include/engine/utils/list.h b/src/utils/include/engine/utils/list.h deleted file mode 100644 index 04dda04..0000000 --- a/src/utils/include/engine/utils/list.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef ENGINE_LIST_H -#define ENGINE_LIST_H - -#ifdef __cplusplus -extern "C" { -#endif - -#define DEFINE_LLIST(type) \ - struct List_##type { \ - type value; \ - struct List_##type* next; \ - /* Force the user to add `;` for style consistency */ \ - } List_##type - -#ifdef __cplusplus -} -#endif -#endif diff --git a/src/utils/include/engine/utils/stack.h b/src/utils/include/engine/utils/stack.h deleted file mode 100644 index b4caf5f..0000000 --- a/src/utils/include/engine/utils/stack.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef STACK_H -#define STACK_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include - -typedef struct { - usize head; /* current number of elements */ - const usize elem_size; /* size in bytes of each element */ - usize size; /* current memory size used by the stack */ - const usize chunk_size; /* size of which the stack increases when running out - of mem */ - void* data; /* memory buffer */ -} Stack; - -Stack stack_new_ex(const usize element_size, const usize size); - -Stack stack_new(const usize element_size); - -void stack_free(Stack* s); -void* stack_pop(Stack* s); -void stack_push(Stack* s, void* elem); -void* stack_peek(Stack* s); -usize stack_size(const Stack* s); -void stack_swap(Stack* s, Stack* t); - -#ifdef __cplusplus -} -#endif -#endif diff --git a/src/utils/src/btree.c b/src/utils/src/btree.c deleted file mode 100644 index 9411072..0000000 --- a/src/utils/src/btree.c +++ /dev/null @@ -1,800 +0,0 @@ -#include - -#include -#include -#include -#include - -#include - -/* Definitions */ -typedef unsigned char byte; - -struct node { - size_t n; /* number of items/keys/elements */ - size_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; - size_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 size_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 size_t degree) { - const size_t max_children = 2 * degree + 1; - size_t 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))) { - size_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 size_t t, const size_t elem_size, - struct node* nonfull, size_t i) { - struct node* z = node_new(t, elem_size); - struct node* y = nonfull->children[i]; - size_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, size_t i, const size_t elem_size, - void (*dealloc)(void*)) { - struct node* y = x->children[i]; - struct node* z = x->children[i + 1]; - size_t 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, size_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)) { - size_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, size_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 size_t degree, - const size_t elem_size, - int (*cmp)(const void* a, const void* b)) { - - /* TODO check correctness */ - size_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 size_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 */ - size_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 (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 size_t degree, - const size_t elem_size, void* (*alloc)(size_t), - void (*dealloc)(void*)) { - /* Determine wether the key is in the node */ - size_t 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 */ - size_t 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; - size_t 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*)) { - size_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 size_t head = 0; - register size_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) && (size_t)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 ((size_t)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 * (((size_t)pos - 1) / 2); -} diff --git a/src/utils/src/fov.c b/src/utils/src/fov.c deleted file mode 100644 index 90df5af..0000000 --- a/src/utils/src/fov.c +++ /dev/null @@ -1,95 +0,0 @@ -#include -#include -#include -#include - -void fov_shadowcast_rec(const void* map, const ivec2 mapsize, - bool (*visblocking)(const void*), i32* lightmap, - const i32 range, ivec2 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[0] + dx * xx + dy * xy; - const i32 mapy = src[1] + 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[0] && mapy >= 0 && - mapy < (long)mapsize[1]) { - // TODO: Calculate proper dist from source - i32 x_2 = (src[0] - mapx) * (src[0] - mapx); - i32 y_2 = (src[1] - mapy) * (src[1] - mapy); - lightmap[mapy * mapsize[0] + mapx] = - MAX(lightmap[mapy * mapsize[0] + mapx], - (i32)(range - sqrt((f64)(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 */ - * (usize)(mapsize[0] * 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 ivec2 mapsize, - bool (*visblocking)(const void*), i32* lightmap, - const i32 range, const ivec2 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[1] * mapsize[0] + src[0]] = range; -} diff --git a/src/utils/src/hashmap.c b/src/utils/src/hashmap.c deleted file mode 100644 index f7784d5..0000000 --- a/src/utils/src/hashmap.c +++ /dev/null @@ -1,5 +0,0 @@ -#include - -/* Currently, this is a "works, but very poorly" placeholder implementation. - * Should be avoided in practice */ -usize lolhash(const usize s, usize v) { return v % s; } diff --git a/src/utils/src/misc.c b/src/utils/src/misc.c deleted file mode 100644 index 063efbf..0000000 --- a/src/utils/src/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 (i32)((f32)a * (1.0f - dt)) + (i32)((f32)b * dt); -} - -u32 hash(char* str) { - u32 sum = 0; - while (*str != '\0') { - sum ^= (u32)(*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 ivec2 mapsize, - predicate_t* predicate) { - const usize w = (usize)mapsize[0]; - const usize h = (usize)mapsize[1]; - i32 mask[w * h]; - - if (w * h < 1) return NULL; - - for (usize i = 0; i < w * h; i++) { - mask[i] = predicate((void*)((u64)map + sizeof(i32) * i)) ? 1 : 0; - } - - for (usize y = 1; y < h - 1; y++) { - for (usize x = 1; x < w - 1; x++) { - const usize global_idx = (y * w) + x; - const usize 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 (usize yy = offs; yy <= offs + w + w; yy += w) { - for (usize 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 deleted file mode 100644 index a7cb16d..0000000 --- a/src/utils/src/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); -} - -usize 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); - } - usize 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/window.c b/src/window.c new file mode 100644 index 0000000..ea42678 --- /dev/null +++ b/src/window.c @@ -0,0 +1,82 @@ +#include + +/* TODO: REMOVE THIS INCLUSION */ +#include + +#include +#include + +#define ENGINE_RENDERING_WINDOW_H_EXCLUDE_EXTERNS +#include +#undef ENGINE_RENDERING_WINDOW_H_EXCLUDE_EXTERNS + +#include + +#undef GLFW_INCLUDE_NONE +#include + +#include + +extern Instance* GLOBAL_PLATFORM; + +static inline u64 platform_get_time_usec(void) { + struct timespec t; + int res = clock_gettime(CLOCK_MONOTONIC, &t); + if (res != 0) { + // TODO: Check errno + WARN("Failed to get system time"); + } + return (u64)(t.tv_sec * 1000000 + t.tv_nsec / 1000); +} + +/* wrapper to get time in ms */ +u64 (*get_time)(void) = platform_get_time_usec; + + +#define DAW_WINDOW_VSYNC (1 << 0) +#define DAW_WINDOW_FULLSCREEN (1 << 1) +#define DAW_WINDOW_RESIZEABLE (1 << 2) + +// Wrapper to get a specific window and set up related structures. +// What the fuck is this doing here. +Window* Window_new(const struct Platform* p, const char *restrict title, Window_framework framework, Window_renderer renderer, ivec2 size, u32 flags) { + Window* w = NULL; + + switch (framework) { + case WINDOW_FRAMEWORK_GLFW: + switch (renderer) { + case WINDOW_RENDERER_OPENGL: + /* For now, pass instance as NULL, fix it once all the necessary bs is + * out of struct Instance */ + w = p->window_init(title, size, flags); + + w->bindings = NULL; + w->bindings_sz = 0; + w->bindings_len = 0; + + /// TODO + //w->cam = &default_camera; + return w; + break; + default: + ERROR("Unsupported renderer."); + } + break; + default: + ERROR("Unsupported framework."); + } + return NULL; +} + +void get_mousepos(double *x, double *y) { + Window* w = GLOBAL_PLATFORM->window; + + switch(w->framework) { + case WINDOW_FRAMEWORK_GLFW: + glfwGetCursorPos(GLOBAL_PLATFORM->window->window, x, y); + break; + default: + ERROR("get_mouse_pos not implemented for chosen framework."); + } + +} diff --git a/tools/cmake/DawAddState.cmake b/tools/cmake/DawAddState.cmake index 213b77b..902e814 100644 --- a/tools/cmake/DawAddState.cmake +++ b/tools/cmake/DawAddState.cmake @@ -15,7 +15,7 @@ # Then call `daw_add_state(${STATENAME}) for each of your states. macro(daw_add_state STATENAME) # Add state include directory to the engines target - set_property(TARGET daw_core + set_property(TARGET daw APPEND PROPERTY INCLUDE_DIRECTORIES ${CMAKE_SOURCE_DIR}/state_${STATENAME}/include) @@ -54,7 +54,7 @@ macro(daw_add_state STATENAME) add_library(${STATENAME} OBJECT ${STATE_SOURCES}) # The game-state source is withing the core module - set_property(TARGET daw_core + set_property(TARGET daw APPEND PROPERTY INCLUDE_DIRECTORIES ${CMAKE_SOURCE_DIR}/state_${STATENAME}/include ) @@ -64,10 +64,6 @@ macro(daw_add_state STATENAME) cglm ) - set_property(TARGET daw_core - APPEND PROPERTY LINK_LIBRARIES - ${STATENAME}) - set_property(TARGET daw APPEND PROPERTY LINK_LIBRARIES ${STATENAME}) -- cgit v1.3