opengl moment #1

Merged
veclavtalica merged 27 commits from opengl into main 2024-07-28 14:50:35 +00:00
12 changed files with 348 additions and 224 deletions
Showing only changes of commit 55d85399e9 - Show all commits

View File

@ -17,6 +17,7 @@
#define RENDER_BASE_WIDTH 640
#define RENDER_BASE_HEIGHT 360
#define RENDER_BASE_RATIO ((float)RENDER_BASE_WIDTH / (float)RENDER_BASE_HEIGHT)
#define TEXTURE_ATLAS_SIZE 2048
#define TEXTURE_ATLAS_BIT_DEPTH 32

View File

@ -54,7 +54,6 @@ typedef struct context {
unsigned int update_multiplicity;
SDL_GLContext *gl_context;
SDL_Renderer *renderer;
SDL_Window *window;
uint32_t window_id;
int window_w;

View File

@ -11,28 +11,20 @@ static void ingame_tick(struct state *state) {
world_drawdef(scn->world);
player_calc(scn->player);
// unfurl_triangle("/assets/title.png",
// (t_fvec3){ 1250, 700, 0 },
// (t_fvec3){ 0, 800, 0 },
// (t_fvec3){ 0, 0, 0 },
// (t_shvec2){ 0, 360 },
// (t_shvec2){ 0, 0 },
// (t_shvec2){ 360, 0 });
unfurl_triangle("/assets/red.png",
(t_fvec3){ 0, 0, 0 },
(t_fvec3){ RENDER_BASE_WIDTH, 0, 0 },
(t_fvec3){ RENDER_BASE_WIDTH, RENDER_BASE_HEIGHT, 0 },
unfurl_triangle("/assets/player/baron-walk.png",
(t_fvec3){ -1, -1, 0 },
(t_fvec3){ 1, -1, 0 },
(t_fvec3){ 1, 1, 0 },
(t_shvec2){ 0, 0 },
(t_shvec2){ 80, 0 },
(t_shvec2){ 80, 80 });
(t_shvec2){ 48, 0 },
(t_shvec2){ 48, 48 });
unfurl_triangle("/assets/red.png",
(t_fvec3){ RENDER_BASE_WIDTH, RENDER_BASE_HEIGHT, 0 },
(t_fvec3){ 0, RENDER_BASE_HEIGHT, 0 },
(t_fvec3){ 0, 0, 0 },
(t_shvec2){ 80, 80 },
(t_shvec2){ 0, 80 },
unfurl_triangle("/assets/player/baron-walk.png",
(t_fvec3){ 1, 1, 0 },
(t_fvec3){ -1, 1, 0 },
(t_fvec3){ -1, -1, 0 },
(t_shvec2){ 48, 48 },
(t_shvec2){ 0, 48 },
(t_shvec2){ 0, 0 });
}

View File

@ -5,6 +5,7 @@
#include "context.h"
#include "rendering.h"
#include "audio.h"
#include "util.h"
#include <SDL2/SDL.h>

View File

@ -36,18 +36,21 @@ static void update_action_pressed_state(struct input_state *input, struct action
else {
action->just_changed = !action->is_pressed;
action->is_pressed = true;
action->position.x = (float)input->mouse_window_position.x;
action->position.x = (float)input->mouse_window_position.x;
/* TODO: */
/*
* SDL_RenderWindowToLogical will turn window mouse
* coords into a position inside the logical render
* area. this has to be done to get an accurate point
* that can actually be used in game logic
*/
SDL_RenderWindowToLogical(input->renderer,
input->mouse_window_position.x,
input->mouse_window_position.y,
&action->position.x,
&action->position.y);
// SDL_RenderWindowToLogical(input->renderer,
// input->mouse_window_position.x,
// input->mouse_window_position.y,
// &action->position.x,
// &action->position.y);
return;
}
break;
@ -172,9 +175,8 @@ static void input_unbind_code_from_action(struct input_state *input,
}
void input_state_init(struct input_state *input, SDL_Renderer *renderer) {
void input_state_init(struct input_state *input) {
sh_new_strdup(input->action_hash);
input->renderer = renderer;
}

View File

@ -63,7 +63,7 @@ struct input_state {
};
void input_state_init(struct input_state *input, SDL_Renderer *renderer);
void input_state_init(struct input_state *input);
void input_state_deinit(struct input_state *input);
void input_state_update(struct input_state *input);

View File

@ -35,6 +35,8 @@ static void poll_events(void) {
switch (e.window.event) {
case SDL_WINDOWEVENT_RESIZED:
ctx.window_w = e.window.data1;
ctx.window_h = e.window.data2;
break;
}
@ -175,7 +177,7 @@ static bool initialize(void) {
SDL_WINDOWPOS_CENTERED,
RENDER_BASE_WIDTH,
RENDER_BASE_HEIGHT,
SDL_WINDOW_ALLOW_HIGHDPI |
// SDL_WINDOW_ALLOW_HIGHDPI |
SDL_WINDOW_RESIZABLE |
SDL_WINDOW_OPENGL);
if (ctx.window == NULL) {
@ -202,13 +204,12 @@ static bool initialize(void) {
/* might need this to have multiple windows */
ctx.window_id = SDL_GetWindowID(ctx.window);
/* now that we have a window, we know a renderer can be created */
ctx.renderer = SDL_CreateRenderer(ctx.window, -1, SDL_RENDERER_PRESENTVSYNC);
glViewport(0, 0, RENDER_BASE_WIDTH, RENDER_BASE_HEIGHT);
/* SDL_SetHint(SDL_HINT_RENDER_LOGICAL_SIZE_MODE, "overscan"); */
/* SDL_RenderSetIntegerScale(ctx.renderer, SDL_TRUE); */
SDL_RenderSetLogicalSize(ctx.renderer, RENDER_BASE_WIDTH, RENDER_BASE_HEIGHT);
SDL_GetRendererOutputSize(ctx.renderer, &ctx.window_w, &ctx.window_h);
/* TODO: */
// SDL_GetRendererOutputSize(ctx.renderer, &ctx.window_w, &ctx.window_h);
ctx.window_w = RENDER_BASE_WIDTH;
ctx.window_h = RENDER_BASE_HEIGHT;
/* audio initialization */
{
@ -298,7 +299,7 @@ static bool initialize(void) {
}
/* input */
input_state_init(&ctx.input, ctx.renderer);
input_state_init(&ctx.input);
/* scripting */
/*

View File

@ -47,11 +47,11 @@ struct mesh_batch_item {
/* is structure that is in opengl vertex array */
struct uncolored_space_triangle {
t_fvec3 v0;
t_shvec2 uv0;
t_fvec2 uv0;
t_fvec3 v1;
t_shvec2 uv1;
t_fvec2 uv1;
t_fvec3 v2;
t_shvec2 uv2;
t_fvec2 uv2;
};
#endif

41
src/private/textures.h Normal file
View File

@ -0,0 +1,41 @@
#ifndef PRIVATE_TEXTURES_H
#define PRIVATE_TEXTURES_H
#include <SDL2/SDL.h>
#include <stb_rect_pack.h>
#include <glad/glad.h>
#include <stdbool.h>
struct texture {
SDL_Rect srcrect; /* position in atlas */
SDL_Surface *data; /* original image data */
GLuint loner_data; /* loner textures store their data directly */
int atlas_index; /* which atlas the texture is in */
int8_t layer;
};
struct texture_cache_item {
char *key;
struct texture value;
};
struct texture_cache {
/* from context */
SDL_Window *window;
struct texture_cache_item *hash;
struct texture_cache_item *loner_hash;
stbrp_node *node_buffer; /* used internally by stb_rect_pack */
SDL_Surface **atlas_surfaces;
GLuint *atlas_textures;
int atlas_index;
bool is_dirty; /* current atlas needs to be recreated */
};
#endif

View File

@ -10,6 +10,7 @@
#include <stdlib.h>
#include <tgmath.h>
/* http://www.swiftless.com/opengltuts.html */
void render_queue_clear(void) {
/* since i don't intend to free the queues, */
@ -108,17 +109,20 @@ void unfurl_triangle(const char *path,
t_shvec2 uv2)
{
/* corrected atlas texture coordinates */
t_shvec2 uv0c, uv1c, uv2c;
t_fvec2 uv0c, uv1c, uv2c;
struct mesh_batch *batch_p;
textures_load(&ctx.texture_cache, path);
const SDL_Rect srcrect = textures_get_srcrect(&ctx.texture_cache, path);
const int atlas_index = textures_get_atlas_index(&ctx.texture_cache, path);
if (atlas_index == -1) {
/* loners span whole texture i assume */
uv0c = uv0;
uv1c = uv1;
uv2c = uv2;
uv0c.x = (float)uv0.x / (float)srcrect.w;
uv0c.y = (float)uv0.y / (float)srcrect.h;
uv1c.x = (float)uv1.x / (float)srcrect.w;
uv1c.y = (float)uv1.y / (float)srcrect.h;
uv2c.x = (float)uv2.x / (float)srcrect.w;
uv2c.y = (float)uv2.y / (float)srcrect.h;
batch_p = &shgetp(ctx.uncolored_mesh_batches_loners, path)->value;
@ -134,20 +138,17 @@ void unfurl_triangle(const char *path,
sizeof (struct mesh_batch) * ((atlas_index + 1) - old_len));
}
const SDL_Rect srcrect = ctx.texture_cache.hash[atlas_index].value.srcrect; /* TODO: does it work? */
const float wr = (float)srcrect.w / TEXTURE_ATLAS_SIZE;
const float hr = (float)srcrect.h / TEXTURE_ATLAS_SIZE;
const float xr = (float)srcrect.x / TEXTURE_ATLAS_SIZE;
const float yr = (float)srcrect.y / TEXTURE_ATLAS_SIZE;
/* fixed point galore */
const int16_t srcratx = (int16_t)srcrect.x * (INT16_MAX / TEXTURE_ATLAS_SIZE);
const int16_t srcratw = (int16_t)srcrect.w * (INT16_MAX / TEXTURE_ATLAS_SIZE);
const int16_t srcrath = (int16_t)srcrect.h * (INT16_MAX / TEXTURE_ATLAS_SIZE);
const int16_t srcraty = (int16_t)srcrect.y * (INT16_MAX / TEXTURE_ATLAS_SIZE); /* flip? */
uv0c.x = (int16_t)(srcratx + ((uint16_t)((uv0.x * (INT16_MAX / srcrect.w)) * srcratw) >> 7));
uv0c.y = (int16_t)(srcraty + ((uint16_t)((uv0.y * (INT16_MAX / srcrect.h)) * srcrath) >> 7));
uv1c.x = (int16_t)(srcratx + ((uint16_t)((uv1.x * (INT16_MAX / srcrect.w)) * srcratw) >> 7));
uv1c.y = (int16_t)(srcraty + ((uint16_t)((uv1.y * (INT16_MAX / srcrect.h)) * srcrath) >> 7));
uv2c.x = (int16_t)(srcratx + ((uint16_t)((uv2.x * (INT16_MAX / srcrect.w)) * srcratw) >> 7));
uv2c.y = (int16_t)(srcraty + ((uint16_t)((uv2.y * (INT16_MAX / srcrect.h)) * srcrath) >> 7));
uv0c.x = xr + ((float)uv0.x / (float)srcrect.w) * wr;
uv0c.y = yr + ((float)uv0.y / (float)srcrect.h) * hr;
uv1c.x = xr + ((float)uv1.x / (float)srcrect.w) * wr;
uv1c.y = yr + ((float)uv1.y / (float)srcrect.h) * hr;
uv2c.x = xr + ((float)uv2.x / (float)srcrect.w) * wr;
uv2c.y = yr + ((float)uv2.y / (float)srcrect.h) * hr;
batch_p = &ctx.uncolored_mesh_batches[atlas_index];
}
@ -155,14 +156,11 @@ void unfurl_triangle(const char *path,
struct uncolored_space_triangle *data = (struct uncolored_space_triangle *)batch_p->data;
struct uncolored_space_triangle pack = {
.v0 = v0,
// .uv0 = uv0c,
.uv0 = { 0, 0 },
.uv0 = uv0c,
.v1 = v1,
// .uv1 = uv1c,
.uv1 = { INT16_MAX, 0 },
.uv1 = uv1c,
.v2 = v2,
// .uv2 = uv2c,
.uv2 = { INT16_MAX, INT16_MAX },
.uv2 = uv2c,
};
arrpush(data, pack);
@ -229,6 +227,7 @@ static int cmp_layers(const void *a, const void *b) {
}
/* TODO: not that we're SDL free we need to implement batching ourselves */
/* necessary to allow the renderer to draw in batches in the best case */
static void sort_sprites(struct sprite_primitive *sprites) {
size_t sprites_len = arrlenu(sprites);
@ -239,66 +238,86 @@ static void sort_sprites(struct sprite_primitive *sprites) {
}
static void upload_quad_vertices(t_frect rect) {
/* client memory needs to be reachable on glDraw*, so */
static float vertices[6 * 2];
vertices[0] = rect.x; vertices[1] = rect.y;
vertices[2] = rect.x; vertices[3] = rect.y + rect.h;
vertices[4] = rect.x + rect.w; vertices[5] = rect.y + rect.h;
vertices[6] = rect.x + rect.w; vertices[7] = rect.y + rect.h;
vertices[8] = rect.x + rect.w; vertices[9] = rect.y;
vertices[10] = rect.x; vertices[11] = rect.y;
glVertexPointer(2, GL_FLOAT, 0, (void *)&vertices);
}
/* TODO: texture flipping */
/* assumes that orthogonal matrix setup is done already */
static void render_sprite(struct sprite_primitive *sprite) {
SDL_Rect srcrect_value = { 0 };
SDL_Rect *srcrect = &srcrect_value;
SDL_Texture *texture = NULL;
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glClientActiveTexture(GL_TEXTURE0);
/* loner */
if (sprite->atlas_index == -1) {
srcrect = NULL;
texture = textures_get_loner(&ctx.texture_cache, sprite->path);
textures_bind_loner(&ctx.texture_cache, sprite->path, GL_TEXTURE_2D);
glTexCoordPointer(2,
GL_SHORT,
0,
(void *)(int16_t[6 * 2]) {
0, INT16_MAX,
0, 0,
INT16_MAX, 0,
INT16_MAX, 0,
INT16_MAX, INT16_MAX,
0, INT16_MAX });
} else {
*srcrect = textures_get_srcrect(&ctx.texture_cache, sprite->path);
texture = textures_get_atlas(&ctx.texture_cache, sprite->atlas_index);
SDL_Rect srcrect = textures_get_srcrect(&ctx.texture_cache, sprite->path);
textures_bind_atlas(&ctx.texture_cache, sprite->atlas_index, GL_TEXTURE_2D);
glTexCoordPointer(2,
GL_FLOAT,
0,
/* TODO: try using shorts */
(void *)(float[6 * 2]) {
(float)srcrect.x / TEXTURE_ATLAS_SIZE,
(float)srcrect.y / TEXTURE_ATLAS_SIZE,
(float)srcrect.x / TEXTURE_ATLAS_SIZE,
(float)(srcrect.y + srcrect.h) / TEXTURE_ATLAS_SIZE,
(float)(srcrect.x + srcrect.w) / TEXTURE_ATLAS_SIZE,
(float)(srcrect.y + srcrect.h) / TEXTURE_ATLAS_SIZE,
(float)(srcrect.x + srcrect.w) / TEXTURE_ATLAS_SIZE,
(float)(srcrect.y + srcrect.h) / TEXTURE_ATLAS_SIZE,
(float)(srcrect.x + srcrect.w) / TEXTURE_ATLAS_SIZE,
(float)srcrect.y / TEXTURE_ATLAS_SIZE,
(float)srcrect.x / TEXTURE_ATLAS_SIZE,
(float)srcrect.y / TEXTURE_ATLAS_SIZE });
}
SDL_RendererFlip flip = SDL_FLIP_NONE;
if (sprite->flip_x)
flip |= SDL_FLIP_HORIZONTAL;
if (sprite->flip_y)
flip |= SDL_FLIP_VERTICAL;
glColor4ub(sprite->color.r, sprite->color.g, sprite->color.b, sprite->color.a);
SDL_SetTextureColorMod(texture,
sprite->color.r,
sprite->color.g,
sprite->color.b);
SDL_SetTextureAlphaMod(texture, sprite->color.a);
SDL_SetTextureBlendMode(texture, sprite->blend_mode);
// SDL_SetTextureBlendMode(texture, sprite->blend_mode);
SDL_Rect rect = {
(int)sprite->rect.x,
(int)sprite->rect.y,
(int)sprite->rect.w,
(int)sprite->rect.h
};
glEnableClientState(GL_VERTEX_ARRAY);
upload_quad_vertices(sprite->rect);
SDL_RenderCopyEx(ctx.renderer,
texture,
srcrect,
&rect,
sprite->rotation,
NULL,
flip);
glDrawArrays(GL_TRIANGLES, 0, 6);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
glBindTexture(GL_TEXTURE_2D, 0);
}
static void render_rectangle(struct rect_primitive *rectangle) {
SDL_SetRenderDrawColor(ctx.renderer,
rectangle->color.r,
rectangle->color.g,
rectangle->color.b,
rectangle->color.a);
glColor4ub(rectangle->color.r, rectangle->color.g,
rectangle->color.b, rectangle->color.a);
SDL_Rect rect = {
(int)rectangle->rect.x,
(int)rectangle->rect.y,
(int)rectangle->rect.w,
(int)rectangle->rect.h
};
glEnableClientState(GL_VERTEX_ARRAY);
upload_quad_vertices(rectangle->rect);
SDL_RenderFillRect(ctx.renderer, &rect);
SDL_SetRenderDrawColor(ctx.renderer, 255, 255, 255, 255);
glDrawArrays(GL_TRIANGLES, 0, 6);
glDisableClientState(GL_VERTEX_ARRAY);
}
@ -379,11 +398,25 @@ static void render_circle(struct circle_primitive *circle) {
&vertices,
&indices);
SDL_RenderGeometry(ctx.renderer, NULL,
vertices,
num_vertices + 1, /* vertices + center vertex */
indices,
num_vertices * 3);
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(2,
GL_FLOAT,
sizeof (SDL_Vertex),
&vertices->position);
glEnableClientState(GL_COLOR_ARRAY);
glColorPointer(4,
GL_UNSIGNED_BYTE,
sizeof (SDL_Vertex),
&vertices->color);
glDrawElements(GL_TRIANGLES,
num_vertices * 3,
GL_UNSIGNED_INT,
indices);
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
free(vertices);
free(indices);
@ -439,19 +472,13 @@ static void draw_uncolored_space_traingle_batch(struct mesh_batch *batch) {
offsetof(struct uncolored_space_triangle, v1),
(void *)offsetof(struct uncolored_space_triangle, v0));
/* note: propagates further to where texture binding is done */
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glClientActiveTexture(GL_TEXTURE0);
glTexCoordPointer(2,
GL_SHORT,
GL_FLOAT,
offsetof(struct uncolored_space_triangle, v1),
(void *)offsetof(struct uncolored_space_triangle, uv0));
// for (size_t i = 0; i < data_len; ++i) {
// struct uncolored_space_triangle t = ((struct uncolored_space_triangle *)batch->data)[i];
// log_info("{%i, %i, %i, %i, %i, %i}\n", t.uv0.x, t.uv0.y, t.uv1.x, t.uv1.y, t.uv2.x, t.uv2.y);
// }
/* commit for drawing */
glDrawArrays(GL_TRIANGLES, 0, 3 * (int)data_len);
@ -465,12 +492,7 @@ static void draw_uncolored_space_traingle_batch(struct mesh_batch *batch) {
static void render_space(void) {
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glOrtho(0, RENDER_BASE_WIDTH, RENDER_BASE_HEIGHT, 0, -1, 1);
glUseProgramObjectARB(0);
glEnable(GL_TEXTURE_2D);
glActiveTexture(GL_TEXTURE0);
/* solid white, no modulation */
@ -478,24 +500,20 @@ static void render_space(void) {
for (size_t i = 0; i < arrlenu(ctx.uncolored_mesh_batches); ++i) {
if (arrlenu(&ctx.uncolored_mesh_batches[i].data) > 0) {
SDL_Texture *const atlas = textures_get_atlas(&ctx.texture_cache, (int)i);
SDL_GL_BindTexture(atlas, NULL, NULL);
textures_bind_atlas(&ctx.texture_cache, (int)i, GL_TEXTURE_2D);
draw_uncolored_space_traingle_batch(&ctx.uncolored_mesh_batches[i]);
SDL_GL_UnbindTexture(atlas);
}
}
for (size_t i = 0; i < shlenu(ctx.uncolored_mesh_batches_loners); ++i) {
if (arrlenu(&ctx.uncolored_mesh_batches_loners[i].value.data) > 0) {
SDL_Texture *const atlas = textures_get_loner(&ctx.texture_cache,
ctx.uncolored_mesh_batches_loners[i].key);
SDL_GL_BindTexture(atlas, NULL, NULL);
draw_uncolored_space_traingle_batch(&ctx.uncolored_mesh_batches_loners[i].value);
SDL_GL_UnbindTexture(atlas);
}
}
textures_bind_loner(&ctx.texture_cache,
ctx.uncolored_mesh_batches_loners[i].key,
GL_TEXTURE_2D);
glPopMatrix();
draw_uncolored_space_traingle_batch(&ctx.uncolored_mesh_batches_loners[i].value);
}
}
}
@ -503,6 +521,30 @@ void render(void) {
if (ctx.texture_cache.is_dirty)
textures_update_current_atlas(&ctx.texture_cache);
/* fit rendering context onto the resizable screen */
if ((float)ctx.window_w / (float)ctx.window_h > RENDER_BASE_RATIO) {
float ratio = (float)ctx.window_h / (float)RENDER_BASE_HEIGHT;
int w = (int)((float)RENDER_BASE_WIDTH * ratio);
glViewport(
ctx.window_w / 2 - w / 2,
0,
w,
ctx.window_h
);
} else {
float ratio = (float)ctx.window_w / (float)RENDER_BASE_WIDTH;
int h = (int)((float)RENDER_BASE_HEIGHT * ratio);
glViewport(
0,
ctx.window_h / 2 - h / 2,
ctx.window_w,
h
);
}
glEnable(GL_DEPTH_TEST);
glEnable(GL_ALPHA_TEST);
glClearColor((1.0f / 255) * 230,
(1.0f / 255) * 230,
(1.0f / 255) * 230, 1);
@ -511,22 +553,42 @@ void render(void) {
GL_DEPTH_BUFFER_BIT |
GL_STENCIL_BUFFER_BIT);
// glDisable(GL_CULL_FACE);
// glDisable(GL_DEPTH_TEST);
{
glDisable(GL_CULL_FACE);
glDepthFunc(GL_ALWAYS); /* fill depth buffer with ones, 2d view is always in front */
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, RENDER_BASE_WIDTH, RENDER_BASE_HEIGHT, 0, -1, 1);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glEnable(GL_TEXTURE_2D);
/* TODO: write with no depth test, just fill it in */
render_sprites();
render_rectangles();
render_circles();
}
// glEnable(GL_CULL_FACE);
// glEnable(GL_DEPTH_TEST);
glDisable(GL_BLEND);
{
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glRotatef(60, 1, 0, 0);
glEnable(GL_CULL_FACE);
glDepthFunc(GL_LESS);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
/* TODO: use depth test to optimize gui regions away */
render_space();
}
SDL_RenderFlush(ctx.renderer);
SDL_GL_SwapWindow(ctx.window);
}

View File

@ -1,6 +1,7 @@
#include "textures.h"
#include "private/textures.h"
#include "config.h"
#include "util.h"
#include "textures.h"
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
@ -35,6 +36,24 @@ fail:
}
static GLuint new_gl_texture(void) {
GLuint texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glBindTexture(GL_TEXTURE_2D, 0);
return texture;
}
/* adds a new, blank atlas surface to the cache */
static void add_new_atlas(struct texture_cache *cache) {
SDL_PixelFormat *native_format =
@ -59,10 +78,49 @@ static void add_new_atlas(struct texture_cache *cache) {
SDL_SetSurfaceRLE(new_atlas, true);
arrput(cache->atlas_surfaces, new_atlas);
arrput(cache->atlas_textures, new_gl_texture());
}
SDL_Texture *new_atlas_texture =
SDL_CreateTextureFromSurface(cache->renderer, new_atlas);
arrput(cache->atlas_textures, new_atlas_texture);
static void upload_texture_from_surface(GLuint texture, SDL_Surface *surface) {
Uint32 rmask, gmask, bmask, amask;
glBindTexture(GL_TEXTURE_2D, texture);
// glPixelStorei(GL_PACK_ALIGNMENT, 1);
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
rmask = 0xff000000;
gmask = 0x00ff0000;
bmask = 0x0000ff00;
amask = 0x000000ff;
#else
rmask = 0x000000ff;
gmask = 0x0000ff00;
bmask = 0x00ff0000;
amask = 0xff000000;
#endif
/* TODO: don't do it if format is compatible */
SDL_Surface* intermediate = SDL_CreateRGBSurface(0,
surface->w, surface->h, 32, rmask, gmask, bmask, amask);
SDL_BlitSurface(surface, NULL, intermediate, NULL);
SDL_LockSurface(intermediate);
glTexImage2D(GL_TEXTURE_2D,
0,
GL_RGBA,
surface->w,
surface->h,
0,
GL_RGBA,
GL_UNSIGNED_BYTE,
intermediate->pixels);
SDL_UnlockSurface(intermediate);
SDL_FreeSurface(intermediate);
glBindTexture(GL_TEXTURE_2D, 0);
}
@ -85,12 +143,7 @@ static void recreate_current_atlas_texture(struct texture_cache *cache) {
}
/* texturize it! */
SDL_LockSurface(atlas_surface);
SDL_UpdateTexture(cache->atlas_textures[cache->atlas_index],
NULL,
atlas_surface->pixels,
atlas_surface->pitch);
SDL_UnlockSurface(atlas_surface);
upload_texture_from_surface(cache->atlas_textures[cache->atlas_index], atlas_surface);
}
@ -171,7 +224,6 @@ static void update_texture_rects_in_atlas(struct texture_cache *cache, stbrp_rec
void textures_cache_init(struct texture_cache *cache, SDL_Window *window) {
cache->window = window;
cache->renderer = SDL_GetRenderer(window);
sh_new_arena(cache->hash);
sh_new_arena(cache->loner_hash);
@ -185,7 +237,7 @@ void textures_cache_init(struct texture_cache *cache, SDL_Window *window) {
void textures_cache_deinit(struct texture_cache *cache) {
/* free atlas textures */
for (size_t i = 0; i < arrlenu(cache->atlas_textures); ++i) {
SDL_DestroyTexture(cache->atlas_textures[i]);
glDeleteTextures(1, &cache->atlas_textures[i]);
}
arrfree(cache->atlas_textures);
@ -247,8 +299,11 @@ void textures_load(struct texture_cache *cache, const char *path) {
/* it's a "loner texture," it doesn't fit in an atlas so it's not in one */
if (surface->w > TEXTURE_ATLAS_SIZE || surface->h > TEXTURE_ATLAS_SIZE) {
new_texture.loner_data = SDL_CreateTextureFromSurface(cache->renderer, surface);
new_texture.loner_data = new_gl_texture();
upload_texture_from_surface(new_texture.loner_data, surface);
new_texture.atlas_index = -1;
new_texture.srcrect = (SDL_Rect) {
.w = surface->w, .h = surface->h };
shput(cache->loner_hash, path, new_texture);
} else {
new_texture.atlas_index = cache->atlas_index;
@ -301,17 +356,23 @@ void textures_update_current_atlas(struct texture_cache *cache) {
SDL_Rect textures_get_srcrect(struct texture_cache *cache, const char *path) {
struct texture_cache_item *texture = shgetp_null(cache->hash, path);
if (texture == NULL) {
int index = textures_get_atlas_index(cache, path);
if (index == -1) {
return shget(cache->loner_hash, path).srcrect;
} else if (index == INT_MIN) {
CRY("Texture lookup failed.",
"Tried to get texture that isn't loaded.");
return (SDL_Rect){ 0, 0, 0, 0 };
} else {
return shget(cache->hash, path).srcrect;
}
return texture->value.srcrect;
}
/* TODO: instead of index, return a 'key' with following encoded meaning: */
/* value of 0 - no atlas (#define NO_ATLAS (0) ?) */
/* negative value - index in loners (-key - 1) */
/* positive value - index in atlases (key - 1) */
int textures_get_atlas_index(struct texture_cache *cache, const char *path) {
struct texture_cache_item *texture = shgetp_null(cache->hash, path);
@ -331,25 +392,28 @@ int textures_get_atlas_index(struct texture_cache *cache, const char *path) {
}
SDL_Texture *textures_get_atlas(struct texture_cache *cache, int index) {
void textures_bind_atlas(struct texture_cache *cache, int index, GLenum target) {
/* out of bounds */
if (arrlen(cache->atlas_textures) < index + 1 || index < 0)
return NULL;
if (arrlen(cache->atlas_textures) < index + 1 || index < 0) {
CRY("Atlas texture binding failed.",
"Tried to bind texture by invalid index");
return;
}
return cache->atlas_textures[index];
glBindTexture(target, cache->atlas_textures[index]);
}
SDL_Texture *textures_get_loner(struct texture_cache *cache, const char *path) {
void textures_bind_loner(struct texture_cache *cache, const char *path, GLenum target) {
struct texture_cache_item *texture = shgetp_null(cache->loner_hash, path);
if (texture == NULL) {
CRY("Loner texture lookup failed.",
"Tried to get texture that isn't loaded.");
return NULL;
CRY("Loner texture binding failed.",
"Tried to bind texture that isn't loaded.");
return;
}
return texture->value.loner_data;
glBindTexture(target, texture->value.loner_data);
}

View File

@ -1,46 +1,10 @@
#ifndef TEXTURES_H
#define TEXTURES_H
#include "private/textures.h"
#include <SDL2/SDL.h>
#include <stb_rect_pack.h>
#include <stdbool.h>
struct texture {
SDL_Rect srcrect; /* position in atlas */
SDL_Surface *data; /* original image data */
SDL_Texture *loner_data; /* loner textures store their data directly */
int atlas_index; /* which atlas the texture is in */
int8_t layer;
};
struct texture_cache_item {
char *key;
struct texture value;
};
/* use the public API to create and manipulate instances of this structure */
struct texture_cache {
/* from context */
SDL_Window *window;
SDL_Renderer *renderer;
struct texture_cache_item *hash;
struct texture_cache_item *loner_hash;
stbrp_node *node_buffer; /* used internally by stb_rect_pack */
SDL_Surface **atlas_surfaces;
SDL_Texture **atlas_textures;
int atlas_index;
bool is_dirty; /* current atlas needs to be recreated */
};
#include <glad/glad.h>
void textures_cache_init(struct texture_cache *cache, SDL_Window *window);
void textures_cache_deinit(struct texture_cache *cache);
@ -64,15 +28,12 @@ SDL_Rect textures_get_srcrect(struct texture_cache *cache, const char *path);
/* if the texture is not found, returns INT_MIN */
int textures_get_atlas_index(struct texture_cache *cache, const char *path);
/* returns a pointer to the atlas at `index` */
/* if the index is out of bounds, returns NULL. */
/* you can get the index via texture_get_atlas_index */
SDL_Texture *textures_get_atlas(struct texture_cache *cache, int index);
/* binds atlas texture in opengl state */
void textures_bind_atlas(struct texture_cache *cache, int index, GLenum target);
SDL_Texture *textures_get_loner(struct texture_cache *cache, const char *path);
void textures_bind_loner(struct texture_cache *cache, const char *path, GLenum target);
/* returns the number of atlases in the cache */
size_t textures_get_num_atlases(struct texture_cache *cache);
#endif