252 lines
7.6 KiB
C
252 lines
7.6 KiB
C
|
/* ************************************************************************** */
|
||
|
/* */
|
||
|
/* :::::::: */
|
||
|
/* mlx_images.c :+: :+: */
|
||
|
/* +:+ */
|
||
|
/* By: W2Wizard <main@w2wizard.dev> +#+ */
|
||
|
/* +#+ */
|
||
|
/* Created: 2021/12/28 02:29:06 by W2Wizard #+# #+# */
|
||
|
/* Updated: 2023/03/30 16:36:39 by ntamayo- ######## odam.nl */
|
||
|
/* */
|
||
|
/* ************************************************************************** */
|
||
|
|
||
|
#include "MLX42/MLX42_Int.h"
|
||
|
|
||
|
//= Private =//
|
||
|
|
||
|
void mlx_flush_batch(mlx_ctx_t* mlx)
|
||
|
{
|
||
|
if (mlx->batch_size <= 0)
|
||
|
return;
|
||
|
|
||
|
glBindBuffer(GL_ARRAY_BUFFER, mlx->vbo);
|
||
|
glBufferData(GL_ARRAY_BUFFER, mlx->batch_size * sizeof(vertex_t), mlx->batch_vertices, GL_STATIC_DRAW);
|
||
|
glDrawArrays(GL_TRIANGLES, 0, mlx->batch_size);
|
||
|
|
||
|
mlx->batch_size = 0;
|
||
|
memset(mlx->bound_textures, 0, sizeof(mlx->bound_textures));
|
||
|
}
|
||
|
|
||
|
static int8_t mlx_bind_texture(mlx_ctx_t* mlx, mlx_image_t* img)
|
||
|
{
|
||
|
const GLint handle = (GLint)((mlx_image_ctx_t*)img->context)->texture;
|
||
|
|
||
|
// Attempt to bind the texture, or obtain the index if it is already bound.
|
||
|
for (int8_t i = 0; i < 16; i++)
|
||
|
{
|
||
|
if (mlx->bound_textures[i] == handle)
|
||
|
return (i);
|
||
|
|
||
|
if (mlx->bound_textures[i] == 0)
|
||
|
{
|
||
|
mlx->bound_textures[i] = handle;
|
||
|
|
||
|
glActiveTexture(GL_TEXTURE0 + i);
|
||
|
glBindTexture(GL_TEXTURE_2D, handle);
|
||
|
return (i);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// If no free slot was found, flush the batch and assign the texture to the first available slot
|
||
|
mlx_flush_batch(mlx);
|
||
|
|
||
|
mlx->bound_textures[0] = handle;
|
||
|
glActiveTexture(GL_TEXTURE0);
|
||
|
glBindTexture(GL_TEXTURE_2D, handle);
|
||
|
return (0);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 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)
|
||
|
{
|
||
|
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;
|
||
|
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},
|
||
|
};
|
||
|
memmove(mlx->batch_vertices + mlx->batch_size, vertices, sizeof(vertices));
|
||
|
mlx->batch_size += 6;
|
||
|
|
||
|
if (mlx->batch_size >= MLX_BATCH_SIZE)
|
||
|
mlx_flush_batch(mlx);
|
||
|
}
|
||
|
|
||
|
mlx_instance_t* mlx_grow_instances(mlx_image_t* img, bool* did_realloc)
|
||
|
{
|
||
|
mlx_image_ctx_t* const ctx = img->context;
|
||
|
if (img->count >= ctx->instances_capacity)
|
||
|
{
|
||
|
if (ctx->instances_capacity == 0)
|
||
|
ctx->instances_capacity = img->count;
|
||
|
else
|
||
|
ctx->instances_capacity *= 2;
|
||
|
*did_realloc = true;
|
||
|
return realloc(img->instances, ctx->instances_capacity * sizeof(mlx_instance_t));
|
||
|
}
|
||
|
*did_realloc = false;
|
||
|
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)
|
||
|
{
|
||
|
if (did_realloc)
|
||
|
free(instances);
|
||
|
return (free(queue), mlx_error(MLX_MEMFAIL), -1);
|
||
|
}
|
||
|
|
||
|
// Set data...
|
||
|
queue->image = img;
|
||
|
int32_t index = queue->instanceid = 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].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);
|
||
|
}
|
||
|
|
||
|
mlx_image_t* mlx_new_image(mlx_t* mlx, uint32_t width, uint32_t height)
|
||
|
{
|
||
|
MLX_NONNULL(mlx);
|
||
|
|
||
|
if (!width || !height || width > INT16_MAX || height > INT16_MAX)
|
||
|
return ((void*)mlx_error(MLX_INVDIM));
|
||
|
|
||
|
const mlx_ctx_t* mlxctx = mlx->context;
|
||
|
mlx_image_t* newimg = calloc(1, sizeof(mlx_image_t));
|
||
|
mlx_image_ctx_t* newctx = calloc(1, sizeof(mlx_image_ctx_t));
|
||
|
if (!newimg || !newctx)
|
||
|
{
|
||
|
mlx_freen(2, newimg, newctx);
|
||
|
return ((void *)mlx_error(MLX_MEMFAIL));
|
||
|
}
|
||
|
newimg->enabled = true;
|
||
|
newimg->context = newctx;
|
||
|
(*(uint32_t*)&newimg->width) = width;
|
||
|
(*(uint32_t*)&newimg->height) = height;
|
||
|
if (!(newimg->pixels = calloc(width * height, sizeof(int32_t))))
|
||
|
{
|
||
|
mlx_freen(2, newimg, newctx);
|
||
|
return ((void *)mlx_error(MLX_MEMFAIL));
|
||
|
}
|
||
|
|
||
|
mlx_list_t* newentry;
|
||
|
if (!(newentry = mlx_lstnew(newimg)))
|
||
|
{
|
||
|
mlx_freen(3, newimg->pixels, newimg->context, newimg);
|
||
|
return ((void *)mlx_error(MLX_MEMFAIL));
|
||
|
}
|
||
|
|
||
|
// Generate OpenGL texture
|
||
|
glGenTextures(1, &newctx->texture);
|
||
|
glBindTexture(GL_TEXTURE_2D, newctx->texture);
|
||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||
|
mlx_lstadd_front((mlx_list_t**)(&mlxctx->images), newentry);
|
||
|
return (newimg);
|
||
|
}
|
||
|
|
||
|
void mlx_delete_image(mlx_t* mlx, mlx_image_t* image)
|
||
|
{
|
||
|
MLX_NONNULL(mlx);
|
||
|
MLX_NONNULL(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);
|
||
|
|
||
|
mlx_list_t* imglst;
|
||
|
if ((imglst = mlx_lstremove(&mlxctx->images, image, &mlx_equal_image)))
|
||
|
{
|
||
|
glDeleteTextures(1, &((mlx_image_ctx_t*)image->context)->texture);
|
||
|
mlx_freen(5, image->pixels, image->instances, image->context, imglst, image);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool mlx_resize_image(mlx_image_t* img, uint32_t nwidth, uint32_t nheight)
|
||
|
{
|
||
|
MLX_NONNULL(img);
|
||
|
|
||
|
if (!nwidth || !nheight || nwidth > INT16_MAX || nheight > INT16_MAX)
|
||
|
return (mlx_error(MLX_INVDIM));
|
||
|
if (nwidth != img->width || nheight != img->height)
|
||
|
{
|
||
|
uint32_t* origin = (uint32_t*)img->pixels;
|
||
|
float wstep = (float)img->width / nwidth;
|
||
|
float hstep = (float)img->height / nheight;
|
||
|
|
||
|
uint8_t* tempbuff = calloc(nwidth * nheight, BPP);
|
||
|
if (!tempbuff)
|
||
|
return (mlx_error(MLX_MEMFAIL));
|
||
|
img->pixels = tempbuff;
|
||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, nwidth, nheight, 0, GL_RGBA, GL_UNSIGNED_BYTE, img->pixels);
|
||
|
|
||
|
uint32_t* destin = (uint32_t*)img->pixels;
|
||
|
for (uint32_t j = 0; j < nheight; j++)
|
||
|
for (uint32_t i = 0; i < nwidth; i++)
|
||
|
destin[j * nwidth + i] = origin[(uint32_t)(j * hstep) * img->width + (uint32_t)(i * wstep)];
|
||
|
(*(uint32_t*)&img->width) = nwidth;
|
||
|
(*(uint32_t*)&img->height) = nheight;
|
||
|
free(origin);
|
||
|
}
|
||
|
return (true);
|
||
|
}
|