full removal of SDL_Renderer usage, working spatial triangle rendering, temporary(?) regression of HDPI
This commit is contained in:
parent
34cf504b2b
commit
55d85399e9
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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 });
|
||||
}
|
||||
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
#include "context.h"
|
||||
#include "rendering.h"
|
||||
#include "audio.h"
|
||||
#include "util.h"
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
|
22
src/input.c
22
src/input.c
@ -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;
|
||||
@ -59,9 +62,9 @@ static void update_action_pressed_state(struct input_state *input, struct action
|
||||
|
||||
|
||||
static void input_bind_code_to_action(struct input_state *input,
|
||||
char *action_name,
|
||||
enum button_source source,
|
||||
union button_code code)
|
||||
char *action_name,
|
||||
enum button_source source,
|
||||
union button_code code)
|
||||
{
|
||||
struct action_hash_item *action_item = shgetp_null(input->action_hash, action_name);
|
||||
if (action_item == NULL) {
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
19
src/main.c
19
src/main.c
@ -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) {
|
||||
@ -201,14 +203,13 @@ 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);
|
||||
|
||||
/* 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);
|
||||
glViewport(0, 0, RENDER_BASE_WIDTH, RENDER_BASE_HEIGHT);
|
||||
|
||||
/* 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 */
|
||||
/*
|
||||
|
@ -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
41
src/private/textures.h
Normal 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
|
278
src/rendering.c
278
src/rendering.c
@ -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);
|
||||
textures_bind_loner(&ctx.texture_cache,
|
||||
ctx.uncolored_mesh_batches_loners[i].key,
|
||||
GL_TEXTURE_2D);
|
||||
|
||||
draw_uncolored_space_traingle_batch(&ctx.uncolored_mesh_batches_loners[i].value);
|
||||
SDL_GL_UnbindTexture(atlas);
|
||||
}
|
||||
}
|
||||
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
glEnable(GL_BLEND);
|
||||
{
|
||||
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);
|
||||
|
||||
/* TODO: write with no depth test, just fill it in */
|
||||
render_sprites();
|
||||
render_rectangles();
|
||||
render_circles();
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
|
||||
// glEnable(GL_CULL_FACE);
|
||||
// glEnable(GL_DEPTH_TEST);
|
||||
glDisable(GL_BLEND);
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
|
||||
render_sprites();
|
||||
render_rectangles();
|
||||
render_circles();
|
||||
}
|
||||
|
||||
/* TODO: use depth test to optimize gui regions away */
|
||||
render_space();
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
120
src/textures.c
120
src/textures.c
@ -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 =
|
||||
@ -57,12 +76,51 @@ static void add_new_atlas(struct texture_cache *cache) {
|
||||
a_mask);
|
||||
SDL_FreeFormat(native_format);
|
||||
|
||||
SDL_SetSurfaceRLE(new_atlas, true);
|
||||
SDL_SetSurfaceRLE(new_atlas, true);
|
||||
arrput(cache->atlas_surfaces, new_atlas);
|
||||
|
||||
SDL_Texture *new_atlas_texture =
|
||||
SDL_CreateTextureFromSurface(cache->renderer, new_atlas);
|
||||
arrput(cache->atlas_textures, new_atlas_texture);
|
||||
arrput(cache->atlas_textures, new_gl_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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user