diff --git a/include/MLX42/MLX42.h b/include/MLX42/MLX42.h index 867189f..d7dc3c6 100644 --- a/include/MLX42/MLX42.h +++ b/include/MLX42/MLX42.h @@ -73,15 +73,15 @@ typedef struct xpm * * @param x The x location. * @param y The y location. - * @param z The z depth, controls if the image is on the fore or background. + * @param custom_depth The custom z depth, controls if the image is on the fore or background. * @param enabled If true, the instance is drawn else its not. */ typedef struct mlx_instance { - int32_t x; - int32_t y; - int32_t z; - bool enabled; + int32_t x; + int32_t y; + int32_t custom_depth; + bool enabled; } mlx_instance_t; /** @@ -678,18 +678,6 @@ void mlx_delete_image(mlx_t* mlx, mlx_image_t* image); */ bool mlx_resize_image(mlx_image_t* img, uint32_t nwidth, uint32_t nheight); -/** - * Sets the depth / Z axis value of an instance. - * - * NOTE: Keep in mind that images that are on the same Z layer, cut each other off. - * so if you don't see your image anymore make sure its not conflicting by being on - * the same layer as another image. - * - * @param[in] instance The instane on which to change the depth. - * @param[in] zdepth The new depth value. - */ -void mlx_set_instance_depth(mlx_instance_t* instance, int32_t zdepth); - //= String Functions =// /** diff --git a/include/MLX42/MLX42_Int.h b/include/MLX42/MLX42_Int.h index 11b0a9a..d0f0462 100644 --- a/include/MLX42/MLX42_Int.h +++ b/include/MLX42/MLX42_Int.h @@ -94,6 +94,16 @@ typedef struct mlx_list struct mlx_list* prev; } mlx_list_t; +typedef struct mlx_vec +{ + void* data; + int32_t count; + + size_t capacity; + size_t elementSize; + int32_t position; +} mlx_vec_t; + //= Hook structs =// /** * There are 2 types of hooks, special and generics. @@ -185,7 +195,8 @@ typedef struct mlx_ctx mlx_list_t* hooks; mlx_list_t* images; - mlx_list_t* render_queue; + mlx_vec_t render_queue; + int32_t instance_count; mlx_scroll_t scroll_hook; mlx_mouse_t mouse_hook; @@ -205,6 +216,7 @@ typedef struct draw_queue { mlx_image_t* image; int32_t instanceid; + int32_t caluclatedDepth; } draw_queue_t; // Image context. @@ -215,6 +227,7 @@ typedef struct mlx_image_ctx } mlx_image_ctx_t; //= Functions =// + /** * All sorts of internal functions shared in the library that * should not be accessible to the user! No touch! @@ -229,7 +242,6 @@ void mlx_lstclear(mlx_list_t** lst, void (*del)(void*)); void mlx_lstadd_back(mlx_list_t** lst, mlx_list_t* new); void mlx_lstadd_front(mlx_list_t** lst, mlx_list_t* new); mlx_list_t* mlx_lstremove(mlx_list_t** lst, void* value, bool (*comp)(void*, void*)); -void mlx_sort_renderqueue(mlx_list_t** lst); //= Misc functions =// @@ -245,13 +257,28 @@ bool mlx_freen(int32_t count, ...); //= OpenGL Functions =// void mlx_update_matrix(const mlx_t* mlx, int32_t width, int32_t height); -void mlx_draw_instance(mlx_ctx_t* mlx, mlx_image_t* img, mlx_instance_t* instance); +void mlx_draw_instance(mlx_ctx_t* mlx, mlx_image_t* img, mlx_instance_t* instance, int32_t instanceDepth); void mlx_flush_batch(mlx_ctx_t* mlx); -// Utils Functions =// +//= Utils Functions =// bool mlx_getline(char** out, size_t* out_size, FILE* file); uint32_t mlx_rgba_to_mono(uint32_t color); int32_t mlx_atoi_base(const char* str, int32_t base); uint64_t mlx_fnv_hash(char* str, size_t len); + +void mlx_render_queue_sort(mlx_vec_t *render_queue); + +//= Image Helper Functions =// +int32_t mlx_image_calculate_max_depth(mlx_image_t *image); + +//= Vector Functions =// + +bool mlx_vector_init(mlx_vec_t* v, size_t elementSize); +bool mlx_vector_push_back(mlx_vec_t* v, void* item); +void mlx_vector_set(mlx_vec_t* v, int32_t index, void* item); +void* mlx_vector_get(mlx_vec_t* v, int32_t index); +bool mlx_vector_delete(mlx_vec_t* v, int32_t index); +void mlx_vector_free(mlx_vec_t* v); +void mlx_vector_swap(mlx_vec_t* v, int32_t srcIndex, int32_t dstIndex); #endif diff --git a/src/font/mlx_font.c b/src/font/mlx_font.c index 8c92b60..72475cc 100644 --- a/src/font/mlx_font.c +++ b/src/font/mlx_font.c @@ -52,7 +52,7 @@ int32_t mlx_get_texoffset(char c) { const bool _isprint = isprint(c); - // NOTE: Cheesy branchless operation :D + // NOTE(W2): Cheesy branchless operation :D // +2 To skip line separator in texture return (-1 * !_isprint + ((FONT_WIDTH + 2) * (c - 32)) * _isprint); } diff --git a/src/mlx_exit.c b/src/mlx_exit.c index 138c787..488d71d 100644 --- a/src/mlx_exit.c +++ b/src/mlx_exit.c @@ -41,7 +41,7 @@ void mlx_terminate(mlx_t* mlx) glfwTerminate(); mlx_lstclear((mlx_list_t**)(&mlxctx->hooks), &free); - mlx_lstclear((mlx_list_t**)(&mlxctx->render_queue), &free); mlx_lstclear((mlx_list_t**)(&mlxctx->images), &mlx_free_image); + mlx_vector_free(&mlxctx->render_queue); mlx_freen(2, mlxctx, mlx); } diff --git a/src/mlx_images.c b/src/mlx_images.c index 79cc726..0a9e154 100644 --- a/src/mlx_images.c +++ b/src/mlx_images.c @@ -60,22 +60,22 @@ static int8_t mlx_bind_texture(mlx_ctx_t* mlx, mlx_image_t* img) * Internal function to draw a single instance of an image * to the screen. */ -void mlx_draw_instance(mlx_ctx_t* mlx, mlx_image_t* img, mlx_instance_t* instance) +void mlx_draw_instance(mlx_ctx_t* mlx, mlx_image_t* img, mlx_instance_t* instance, int32_t instanceDepth) { - float w = (float) img->width; - float h = (float) img->height; - float x = (float) instance->x; - float y = (float) instance->y; - float z = (float) instance->z; + float w = (float)img->width; + float h = (float)img->height; + float x = (float)instance->x; + float y = (float)instance->y; + float z = (float)instanceDepth; int8_t tex = mlx_bind_texture(mlx, img); vertex_t vertices[6] = { - (vertex_t){x, y, z, 0.f, 0.f, tex}, - (vertex_t){x + w, y + h, z, 1.f, 1.f, tex}, - (vertex_t){x + w, y, z, 1.f, 0.f, tex}, - (vertex_t){x, y, z, 0.f, 0.f, tex}, - (vertex_t){x, y + h, z, 0.f, 1.f, tex}, - (vertex_t){x + w, y + h, z, 1.f, 1.f, tex}, + (vertex_t){x, y, z, 0.f, 0.f, tex}, + (vertex_t){x + w, y + h, z, 1.f, 1.f, tex}, + (vertex_t){x + w, y, z, 1.f, 0.f, tex}, + (vertex_t){x, y, z, 0.f, 0.f, tex}, + (vertex_t){x, y + h, z, 0.f, 1.f, tex}, + (vertex_t){x + w, y + h, z, 1.f, 1.f, tex}, }; memmove(mlx->batch_vertices + mlx->batch_size, vertices, sizeof(vertices)); mlx->batch_size += 6; @@ -84,81 +84,66 @@ void mlx_draw_instance(mlx_ctx_t* mlx, mlx_image_t* img, mlx_instance_t* instanc mlx_flush_batch(mlx); } -mlx_instance_t* mlx_grow_instances(mlx_image_t* img, bool* did_realloc) +static mlx_instance_t* mlx_grow_instances(mlx_image_t* img) { + size_t new_size = 0; mlx_image_ctx_t* const ctx = img->context; - if (img->count >= ctx->instances_capacity) + mlx_instance_t* temp = NULL; + + // Do we need to grow ? + if (img->count + 1 >= ctx->instances_capacity) { if (ctx->instances_capacity == 0) - ctx->instances_capacity = img->count; + new_size++; else - ctx->instances_capacity *= 2; - *did_realloc = true; - return realloc(img->instances, ctx->instances_capacity * sizeof(mlx_instance_t)); + { + new_size = ctx->instances_capacity * 2; + } + + if (!(temp = realloc(img->instances, new_size * sizeof(mlx_instance_t)))) + return (NULL); + ctx->instances_capacity = new_size; + img->instances = temp; } - *did_realloc = false; - return img->instances; + img->count++; + return (img->instances); } //= Public =// -void mlx_set_instance_depth(mlx_instance_t* instance, int32_t zdepth) -{ - MLX_NONNULL(instance); - - if (instance->z == zdepth) - return; - instance->z = zdepth; - - /** - * NOTE: The reason why we don't sort directly is that - * the user might call this function multiple times in a row and we don't - * want to sort for every change. Pre-loop wise that is. - */ - sort_queue = true; -} - int32_t mlx_image_to_window(mlx_t* mlx, mlx_image_t* img, int32_t x, int32_t y) { MLX_NONNULL(mlx); MLX_NONNULL(img); - // Allocate buffers... - img->count++; - bool did_realloc; - mlx_instance_t* instances = mlx_grow_instances(img, &did_realloc); - draw_queue_t* queue = calloc(1, sizeof(draw_queue_t)); - if (!instances || !queue) + mlx_ctx_t* mlxctx = mlx->context; + + // Grow instances to fit new instance. + mlx_instance_t* instances; + + if (!(instances = mlx_grow_instances(img))) { - if (did_realloc) - free(instances); - return (free(queue), mlx_error(MLX_MEMFAIL), -1); + mlx_error(MLX_MEMFAIL); + return (-1); } - // Set data... - queue->image = img; - int32_t index = queue->instanceid = img->count - 1; + const int32_t index = img->count - 1; img->instances = instances; img->instances[index].x = x; img->instances[index].y = y; - - // NOTE: We keep updating the Z for the convenience of the user. - // Always update Z depth to prevent overlapping images by default. - img->instances[index].z = ((mlx_ctx_t*)mlx->context)->zdepth++; + img->instances[index].custom_depth = -1; img->instances[index].enabled = true; - // Add draw call... - sort_queue = true; - mlx_list_t* templst; - if ((templst = mlx_lstnew(queue))) - { - mlx_lstadd_front(&((mlx_ctx_t*)mlx->context)->render_queue, templst); - return (index); - } - return (mlx_freen(2, instances, queue), mlx_error(MLX_MEMFAIL), -1); + // Add drawcall + draw_queue_t queue = (draw_queue_t){ img, img->count - 1, 0}; + if (!mlx_vector_push_back(&(mlxctx->render_queue), &queue)) + return (-1); + + mlxctx->instance_count++; + return (index); } -mlx_image_t* mlx_new_image(mlx_t* mlx, uint32_t width, uint32_t height) +mlx_image_t *mlx_new_image(mlx_t* mlx, uint32_t width, uint32_t height) { MLX_NONNULL(mlx); @@ -209,9 +194,15 @@ void mlx_delete_image(mlx_t* mlx, mlx_image_t* image) mlx_ctx_t* mlxctx = mlx->context; // Delete all instances in the render queue - mlx_list_t* quelst; - while ((quelst = mlx_lstremove(&mlxctx->render_queue, image, &mlx_equal_inst))) - mlx_freen(2, quelst->content, quelst); + for(int32_t i = mlxctx->render_queue.count - 1; i >= 0 ; i--) + { + draw_queue_t* element = mlx_vector_get(&mlxctx->render_queue, i); + + if (element->image == image) + mlx_vector_delete(&mlxctx->render_queue, i); + } + + mlxctx->instance_count -= image->count; mlx_list_t* imglst; if ((imglst = mlx_lstremove(&mlxctx->images, image, &mlx_equal_image))) @@ -221,6 +212,18 @@ void mlx_delete_image(mlx_t* mlx, mlx_image_t* image) } } +int32_t mlx_image_calculate_max_depth(mlx_image_t* image) +{ + int32_t depth = 0; + for (int32_t i = 0; i < image->count; i++) + { + const mlx_instance_t* instance = &image->instances[i]; + depth += instance->custom_depth > 0 ? instance->custom_depth : 1; + } + + return depth; +} + bool mlx_resize_image(mlx_image_t* img, uint32_t nwidth, uint32_t nheight) { MLX_NONNULL(img); diff --git a/src/mlx_init.c b/src/mlx_init.c index de73356..a164188 100644 --- a/src/mlx_init.c +++ b/src/mlx_init.c @@ -170,6 +170,7 @@ mlx_t* mlx_init(int32_t width, int32_t height, const char* title, bool resize) mlx->height = height; mlxctx->initialWidth = width; mlxctx->initialHeight = height; + mlx_vector_init(&mlxctx->render_queue, sizeof(draw_queue_t)); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); diff --git a/src/mlx_loop.c b/src/mlx_loop.c index cf0d531..75d2549 100644 --- a/src/mlx_loop.c +++ b/src/mlx_loop.c @@ -31,12 +31,9 @@ static void mlx_render_images(mlx_t* mlx) { mlx_ctx_t* mlxctx = mlx->context; mlx_list_t* imglst = mlxctx->images; - - if (sort_queue) - { - sort_queue = false; - mlx_sort_renderqueue(&mlxctx->render_queue); - } + mlx_vec_t *render_queue = &mlxctx->render_queue; + + mlx_render_queue_sort(render_queue); // Upload image textures to GPU while (imglst) @@ -50,15 +47,13 @@ static void mlx_render_images(mlx_t* mlx) } // Execute draw calls - mlx_list_t* render_queue = mlxctx->render_queue; - while (render_queue) + for (int32_t i = 0; i < render_queue->count; i++) { - draw_queue_t* drawcall = render_queue->content; + draw_queue_t* drawcall = mlx_vector_get(render_queue, i); mlx_instance_t* instance = &drawcall->image->instances[drawcall->instanceid]; - if (drawcall && drawcall->image->enabled && instance->enabled) - mlx_draw_instance(mlx->context, drawcall->image, instance); - render_queue = render_queue->next; + if (drawcall->image->enabled && instance->enabled) + mlx_draw_instance(mlx->context, drawcall->image, instance, drawcall->caluclatedDepth); } } diff --git a/src/mlx_window.c b/src/mlx_window.c index 646edce..cc85b6e 100644 --- a/src/mlx_window.c +++ b/src/mlx_window.c @@ -21,7 +21,22 @@ void mlx_update_matrix(const mlx_t* mlx, int32_t width, int32_t height) { const mlx_ctx_t* mlxctx = mlx->context; - const float depth = mlxctx->zdepth; + const mlx_list_t* imglst = mlxctx->images; + + int32_t maxDepth = 1; + while (imglst) + { + mlx_image_t *image; + if (!(image = imglst->content)) + { + imglst = imglst->next; + continue; + } + + maxDepth += mlx_image_calculate_max_depth(image); + + imglst = imglst->next; + } /** * Incase the setting to stretch the image is set, we maintain the width and height but not @@ -33,9 +48,9 @@ void mlx_update_matrix(const mlx_t* mlx, int32_t width, int32_t height) const float matrix[16] = { 2.f / width, 0, 0, 0, 0, 2.f / -(height), 0, 0, - 0, 0, -2.f / (depth - -depth), 0, + 0, 0, -2.f / (maxDepth - -maxDepth), 0, -1, -(height / -height), - -((depth + -depth) / (depth - -depth)), 1 + -((maxDepth + -maxDepth) / (maxDepth - -maxDepth)), 1 }; glUniformMatrix4fv(glGetUniformLocation(mlxctx->shaderprogram, "ProjMatrix"), 1, GL_FALSE, matrix); diff --git a/src/utils/mlx_list.c b/src/utils/mlx_list.c index 99873e2..dc7e2fc 100644 --- a/src/utils/mlx_list.c +++ b/src/utils/mlx_list.c @@ -137,64 +137,3 @@ mlx_list_t* mlx_lstremove(mlx_list_t** lst, void* value, bool (*comp)(void*, voi lstcpy->prev->next = lstcpy->next; return (lstcpy); } - -// Retrieve Z value from queue. -static int32_t mlx_getdata(mlx_list_t* entry) -{ - const draw_queue_t* queue = entry->content; - - return (queue->image->instances[queue->instanceid].z); -} - -// Insert the entry back into head sorted. -static void mlx_insertsort(mlx_list_t** head, mlx_list_t* new) -{ - mlx_list_t* current; - - if (*head == NULL) - *head = new; - else if (mlx_getdata(*head) >= mlx_getdata(new)) - { - new->next = *head; - new->next->prev = new; - *head = new; - } - else - { - current = *head; - - // Find insertion location. - while (current->next != NULL && mlx_getdata(current->next) < mlx_getdata(new)) - current = current->next; - new->next = current->next; - - // Insert at the end - if (current->next != NULL) - new->next->prev = new; - current->next = new; - new->prev = current; - } -} - -/** - * Okay-ish sorting algorithm to sort the render queue / doubly linked list. - * We need to do this to fix transparency. - * - * @param lst The render queue. - */ -void mlx_sort_renderqueue(mlx_list_t** lst) -{ - mlx_list_t* sorted = NULL; - mlx_list_t* lstcpy = *lst; - - while (lstcpy != NULL) - { - mlx_list_t* next = lstcpy->next; - - // Separate entry out of list and insert it back but sorted. - lstcpy->prev = lstcpy->next = NULL; - mlx_insertsort(&sorted, lstcpy); - lstcpy = next; - } - *lst = sorted; -} diff --git a/src/utils/mlx_render_queue_sort.c b/src/utils/mlx_render_queue_sort.c new file mode 100644 index 0000000..38163c1 --- /dev/null +++ b/src/utils/mlx_render_queue_sort.c @@ -0,0 +1,51 @@ +/* ************************************************************************** */ +/* */ +/* :::::::: */ +/* mlx_render_queue_sort.c :+: :+: */ +/* +:+ */ +/* By: W2Wizard +#+ */ +/* +#+ */ +/* Created: 2022/08/20 21:33:52 by W2Wizard #+# #+# */ +/* Updated: 2022/08/20 21:33:52 by W2Wizard ######## odam.nl */ +/* */ +/* ************************************************************************** */ + +#include "MLX42/MLX42_Int.h" + +void mlx_render_queue_sort(mlx_vec_t* render_queue) +{ + if(render_queue->count < 1) + return; + + int32_t currentDepth = 0; + for (int32_t i = 0; i < render_queue->count; i++) + { + draw_queue_t* element = mlx_vector_get(render_queue, i); + mlx_instance_t* instance = &element->image->instances[element->instanceid]; + + element->caluclatedDepth = instance->custom_depth > 0 ? instance->custom_depth : currentDepth++; + // printf("%i -> %i\n", element->instanceid, element->caluclatedDepth); + } + + for (int32_t i = 0; i < render_queue->count; i++) + { + bool hasSwap = false; + for (int32_t j = 0; j < render_queue->count - i - 1; j++) + { + draw_queue_t* aElement = mlx_vector_get(render_queue, j); + draw_queue_t* bElement = mlx_vector_get(render_queue, j + 1); + + if (aElement->caluclatedDepth > bElement->caluclatedDepth) + { + mlx_vector_swap(render_queue, j, j + 1); + hasSwap = true; + } + } + + if (!hasSwap) + break; + } + + // printf("firstInstanceToRender -> %i\n", ((draw_queue_t*)mlx_vector_get(render_queue, 0))->instanceid); + // printf("lastInstanceToRender -> %i\n", ((draw_queue_t*)mlx_vector_get(render_queue, render_queue->count - 1))->instanceid); +} \ No newline at end of file diff --git a/src/utils/mlx_vec.c b/src/utils/mlx_vec.c new file mode 100644 index 0000000..628623c --- /dev/null +++ b/src/utils/mlx_vec.c @@ -0,0 +1,130 @@ +/* ************************************************************************** */ +/* */ +/* :::::::: */ +/* mlx_vec.c :+: :+: */ +/* +:+ */ +/* By: W2Wizard +#+ */ +/* +#+ */ +/* Created: 2022/08/20 21:33:52 by W2Wizard #+# #+# */ +/* Updated: 2022/08/20 21:33:52 by W2Wizard ######## odam.nl */ +/* */ +/* ************************************************************************** */ + +#include "MLX42/MLX42_Int.h" + +//= Private =// + +static bool mlx_vector_resize(mlx_vec_t* v, int32_t capacity) +{ + MLX_NONNULL(v); + + void* data = realloc(v->data, v->elementSize * capacity); + if (data) + { + v->data = data; + v->capacity = capacity; + return (true); + } + return (mlx_error(MLX_MEMFAIL)); +} + +bool mlx_vector_init(mlx_vec_t* v, size_t elementSize) +{ + MLX_NONNULL(v); + + v->count = 0; + v->capacity = 1; + v->position = 0; + v->elementSize = elementSize; + if (!(v->data = malloc(elementSize * v->capacity))) + return (mlx_error(MLX_MEMFAIL)); + return (true); +} + +bool mlx_vector_push_back(mlx_vec_t* v, void* item) +{ + MLX_NONNULL(v); + + if (v->capacity == v->count && !mlx_vector_resize(v, v->capacity * 2)) + return (false); + + memcpy(v->data + v->position, item, v->elementSize); + v->position += v->elementSize; + v->count++; + return (true); +} + +void mlx_vector_set(mlx_vec_t* v, int32_t index, void* item) +{ + MLX_NONNULL(v); + MLX_ASSERT(index >= 0 && index < v->count, "Out of bounds"); + + memcpy(v->data + (index * v->elementSize), item, v->elementSize); +} + +void* mlx_vector_get(mlx_vec_t* v, int32_t index) +{ + MLX_NONNULL(v); + MLX_ASSERT(index >= 0 && index < v->count, "Out of bounds"); + + return (v->data + (index * v->elementSize)); +} + +void mlx_vector_swap(mlx_vec_t* v, int32_t srcIndex, int32_t dstIndex) +{ + MLX_NONNULL(v); + MLX_ASSERT(srcIndex >= 0 && srcIndex < v->count, "Src Out of bounds"); + MLX_ASSERT(dstIndex >= 0 && dstIndex < v->count, "Dst Out of bounds"); + + int32_t srcPosition = v->elementSize * srcIndex; + int32_t dstPosition = v->elementSize * dstIndex; + + void* temp = alloca(v->elementSize); + + memcpy(temp, v->data + srcPosition, v->elementSize); + memcpy(v->data + srcPosition, v->data + dstPosition, v->elementSize); + memcpy(v->data + dstPosition, temp, v->elementSize); +} + +bool mlx_vector_delete(mlx_vec_t* v, int32_t index) +{ + MLX_NONNULL(v); + MLX_ASSERT(index >= 0 && index < v->count, "Out of bounds"); + + if (index == 0) + { + if (v->count == 1) + v->position = 0; + else + { + memcpy(v->data, v->data + v->elementSize, v->elementSize * v->count - 1); + v->position -= v->elementSize; + } + } + else if(index == v->count - 1) + v->position -= v->elementSize; + else + { + memcpy(v->data + (index * v->elementSize), v->data + ((index + 1) * v->elementSize), v->elementSize * (v->count - index)); + v->position = v->elementSize * (v->count - 1); + } + + v->count--; + + if (v->count > 0 && v->count == v->capacity / 4) + return (mlx_vector_resize(v, v->capacity / 2)); + return (true); +} + +void mlx_vector_clear(mlx_vec_t* v) +{ + v->position = 0; + v->count = 0; +} + +void mlx_vector_free(mlx_vec_t* v) +{ + mlx_vector_clear(v); + free(v->data); + v->capacity = 0; +}