2024-07-08 00:44:20 +00:00
|
|
|
#include "private/rendering.h"
|
|
|
|
#include "context.h"
|
|
|
|
#include "textures.h"
|
|
|
|
|
|
|
|
#include <SDL2/SDL.h>
|
|
|
|
#include <stb_ds.h>
|
2024-07-09 12:35:54 +00:00
|
|
|
#include <glad/glad.h>
|
2024-07-08 00:44:20 +00:00
|
|
|
|
2024-07-10 16:15:28 +00:00
|
|
|
#include <stddef.h>
|
2024-07-08 00:44:20 +00:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <tgmath.h>
|
|
|
|
|
2024-07-12 18:10:25 +00:00
|
|
|
/* http://www.swiftless.com/opengltuts.html */
|
2024-07-08 00:44:20 +00:00
|
|
|
|
|
|
|
void render_queue_clear(void) {
|
|
|
|
/* since i don't intend to free the queues, */
|
|
|
|
/* it's faster and simpler to just "start over" */
|
|
|
|
/* and start overwriting the existing data */
|
|
|
|
arrsetlen(ctx.render_queue_sprites, 0);
|
|
|
|
arrsetlen(ctx.render_queue_rectangles, 0);
|
|
|
|
arrsetlen(ctx.render_queue_circles, 0);
|
2024-07-10 16:15:28 +00:00
|
|
|
|
|
|
|
for (size_t i = 0; i < arrlenu(ctx.uncolored_mesh_batches); ++i)
|
|
|
|
arrsetlen(ctx.uncolored_mesh_batches[i].data, 0);
|
|
|
|
|
|
|
|
for (size_t i = 0; i < shlenu(ctx.uncolored_mesh_batches_loners); ++i)
|
|
|
|
arrsetlen(ctx.uncolored_mesh_batches_loners[i].value.data, 0);
|
2024-07-08 00:44:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* an implementation note:
|
|
|
|
* try to avoid doing expensive work in the push functions,
|
|
|
|
* because they will be called multiple times in the main loop
|
|
|
|
* before anything is really rendered
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* sprite */
|
|
|
|
void push_sprite(char *path, t_frect rect) {
|
|
|
|
textures_load(&ctx.texture_cache, path);
|
|
|
|
|
|
|
|
struct sprite_primitive sprite = {
|
|
|
|
.rect = rect,
|
|
|
|
.color = (t_color) { 255, 255, 255, 255 },
|
|
|
|
.path = path,
|
|
|
|
.rotation = 0.0,
|
|
|
|
.blend_mode = SDL_BLENDMODE_BLEND,
|
|
|
|
.atlas_index =
|
|
|
|
textures_get_atlas_index(&ctx.texture_cache, path),
|
|
|
|
.layer = 0,
|
|
|
|
.flip_x = false,
|
|
|
|
.flip_y = false,
|
|
|
|
};
|
|
|
|
|
|
|
|
arrput(ctx.render_queue_sprites, sprite);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void push_sprite_ex(t_frect rect, t_push_sprite_args args) {
|
|
|
|
textures_load(&ctx.texture_cache, args.path);
|
|
|
|
|
|
|
|
struct sprite_primitive sprite = {
|
|
|
|
.rect = rect,
|
|
|
|
.color = args.color,
|
|
|
|
.path = args.path,
|
|
|
|
.rotation = args.rotation,
|
|
|
|
.blend_mode = args.blend_mode,
|
|
|
|
.atlas_index =
|
|
|
|
textures_get_atlas_index(&ctx.texture_cache, args.path),
|
|
|
|
.layer = args.layer,
|
|
|
|
.flip_x = args.flip_x,
|
|
|
|
.flip_y = args.flip_y,
|
|
|
|
};
|
|
|
|
|
|
|
|
arrput(ctx.render_queue_sprites, sprite);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* rectangle */
|
|
|
|
void push_rectangle(t_frect rect, t_color color) {
|
|
|
|
struct rect_primitive rectangle = {
|
|
|
|
.rect = rect,
|
|
|
|
.color = color,
|
|
|
|
};
|
|
|
|
|
|
|
|
arrput(ctx.render_queue_rectangles, rectangle);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* circle */
|
|
|
|
void push_circle(t_fvec2 position, float radius, t_color color) {
|
|
|
|
struct circle_primitive circle = {
|
|
|
|
.radius = radius,
|
|
|
|
.color = color,
|
|
|
|
.position = position,
|
|
|
|
};
|
|
|
|
|
|
|
|
arrput(ctx.render_queue_circles, circle);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2024-07-10 16:15:28 +00:00
|
|
|
/* TODO: automatic handling of repeating textures */
|
|
|
|
void unfurl_triangle(const char *path,
|
|
|
|
t_fvec3 v0,
|
|
|
|
t_fvec3 v1,
|
|
|
|
t_fvec3 v2,
|
|
|
|
t_shvec2 uv0,
|
|
|
|
t_shvec2 uv1,
|
|
|
|
t_shvec2 uv2)
|
|
|
|
{
|
|
|
|
/* corrected atlas texture coordinates */
|
2024-07-12 18:10:25 +00:00
|
|
|
t_fvec2 uv0c, uv1c, uv2c;
|
2024-07-10 16:15:28 +00:00
|
|
|
struct mesh_batch *batch_p;
|
|
|
|
|
|
|
|
textures_load(&ctx.texture_cache, path);
|
2024-07-12 18:10:25 +00:00
|
|
|
const SDL_Rect srcrect = textures_get_srcrect(&ctx.texture_cache, path);
|
2024-07-10 16:15:28 +00:00
|
|
|
|
|
|
|
const int atlas_index = textures_get_atlas_index(&ctx.texture_cache, path);
|
|
|
|
if (atlas_index == -1) {
|
2024-07-12 18:10:25 +00:00
|
|
|
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;
|
2024-07-10 16:15:28 +00:00
|
|
|
|
|
|
|
batch_p = &shgetp(ctx.uncolored_mesh_batches_loners, path)->value;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
const size_t old_len = arrlenu(ctx.uncolored_mesh_batches);
|
|
|
|
if ((size_t)atlas_index + 1 >= old_len) {
|
|
|
|
/* grow to accommodate texture cache atlases */
|
|
|
|
arrsetlen(ctx.uncolored_mesh_batches, atlas_index + 1);
|
|
|
|
|
|
|
|
/* zero initialize it all, it's a valid state */
|
|
|
|
SDL_memset(&ctx.uncolored_mesh_batches[atlas_index],
|
|
|
|
0,
|
|
|
|
sizeof (struct mesh_batch) * ((atlas_index + 1) - old_len));
|
|
|
|
}
|
|
|
|
|
2024-07-12 18:10:25 +00:00
|
|
|
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;
|
2024-07-10 16:15:28 +00:00
|
|
|
|
2024-07-12 18:10:25 +00:00
|
|
|
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;
|
2024-07-10 16:15:28 +00:00
|
|
|
|
|
|
|
batch_p = &ctx.uncolored_mesh_batches[atlas_index];
|
|
|
|
}
|
|
|
|
|
|
|
|
struct uncolored_space_triangle *data = (struct uncolored_space_triangle *)batch_p->data;
|
|
|
|
struct uncolored_space_triangle pack = {
|
|
|
|
.v0 = v0,
|
2024-07-12 18:10:25 +00:00
|
|
|
.uv0 = uv0c,
|
2024-07-10 16:15:28 +00:00
|
|
|
.v1 = v1,
|
2024-07-12 18:10:25 +00:00
|
|
|
.uv1 = uv1c,
|
2024-07-10 16:15:28 +00:00
|
|
|
.v2 = v2,
|
2024-07-12 18:10:25 +00:00
|
|
|
.uv2 = uv2c,
|
2024-07-10 16:15:28 +00:00
|
|
|
};
|
|
|
|
arrpush(data, pack);
|
|
|
|
|
|
|
|
batch_p->data = (uint8_t *)data;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2024-07-08 00:44:20 +00:00
|
|
|
/* compare functions for the sort in render_sprites */
|
|
|
|
static int cmp_atlases(const void *a, const void *b) {
|
|
|
|
int index_a = ((const struct sprite_primitive *)a)->atlas_index;
|
|
|
|
int index_b = ((const struct sprite_primitive *)b)->atlas_index;
|
|
|
|
|
|
|
|
return (index_a > index_b) - (index_a < index_b);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int cmp_blend_modes(const void *a, const void *b) {
|
|
|
|
SDL_BlendMode mode_a = ((const struct sprite_primitive *)a)->blend_mode;
|
|
|
|
SDL_BlendMode mode_b = ((const struct sprite_primitive *)b)->blend_mode;
|
|
|
|
|
|
|
|
return (mode_a > mode_b) - (mode_a < mode_b);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int cmp_colors(const void *a, const void *b) {
|
|
|
|
t_color color_a = ((const struct sprite_primitive *)a)->color;
|
|
|
|
t_color color_b = ((const struct sprite_primitive *)b)->color;
|
|
|
|
|
|
|
|
/* check reds */
|
|
|
|
if (color_a.r < color_b.r)
|
|
|
|
return -1;
|
|
|
|
else if (color_a.r > color_b.r)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
/* reds were equal, check greens */
|
|
|
|
else if (color_a.g < color_b.g)
|
|
|
|
return -1;
|
|
|
|
else if (color_a.g > color_b.g)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
/* greens were equal, check blues */
|
|
|
|
else if (color_a.b < color_b.b)
|
|
|
|
return -1;
|
|
|
|
else if (color_a.b > color_b.b)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
/* blues were equal, check alphas */
|
|
|
|
else if (color_a.a < color_b.a)
|
|
|
|
return -1;
|
|
|
|
else if (color_a.a > color_b.a)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
/* entirely equal */
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int cmp_layers(const void *a, const void *b) {
|
|
|
|
int layer_a = ((const struct sprite_primitive *)a)->layer;
|
|
|
|
int layer_b = ((const struct sprite_primitive *)b)->layer;
|
|
|
|
|
|
|
|
return (layer_a > layer_b) - (layer_a < layer_b);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2024-07-12 18:10:25 +00:00
|
|
|
/* TODO: not that we're SDL free we need to implement batching ourselves */
|
2024-07-08 00:44:20 +00:00
|
|
|
/* 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);
|
|
|
|
qsort(sprites, sprites_len, sizeof *sprites, cmp_atlases);
|
|
|
|
qsort(sprites, sprites_len, sizeof *sprites, cmp_blend_modes);
|
|
|
|
qsort(sprites, sprites_len, sizeof *sprites, cmp_colors);
|
|
|
|
qsort(sprites, sprites_len, sizeof *sprites, cmp_layers);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2024-07-12 18:10:25 +00:00
|
|
|
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 */
|
2024-07-08 00:44:20 +00:00
|
|
|
static void render_sprite(struct sprite_primitive *sprite) {
|
2024-07-12 18:10:25 +00:00
|
|
|
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
|
|
glClientActiveTexture(GL_TEXTURE0);
|
2024-07-08 00:44:20 +00:00
|
|
|
|
|
|
|
/* loner */
|
|
|
|
if (sprite->atlas_index == -1) {
|
2024-07-12 18:10:25 +00:00
|
|
|
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 });
|
2024-07-08 00:44:20 +00:00
|
|
|
} else {
|
2024-07-12 18:10:25 +00:00
|
|
|
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 });
|
2024-07-08 00:44:20 +00:00
|
|
|
}
|
|
|
|
|
2024-07-12 18:10:25 +00:00
|
|
|
glColor4ub(sprite->color.r, sprite->color.g, sprite->color.b, sprite->color.a);
|
|
|
|
|
|
|
|
// SDL_SetTextureBlendMode(texture, sprite->blend_mode);
|
2024-07-08 00:44:20 +00:00
|
|
|
|
2024-07-12 18:10:25 +00:00
|
|
|
glEnableClientState(GL_VERTEX_ARRAY);
|
|
|
|
upload_quad_vertices(sprite->rect);
|
|
|
|
|
|
|
|
glDrawArrays(GL_TRIANGLES, 0, 6);
|
|
|
|
|
|
|
|
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
|
|
glDisableClientState(GL_VERTEX_ARRAY);
|
|
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
2024-07-08 00:44:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void render_rectangle(struct rect_primitive *rectangle) {
|
2024-07-12 18:10:25 +00:00
|
|
|
glColor4ub(rectangle->color.r, rectangle->color.g,
|
|
|
|
rectangle->color.b, rectangle->color.a);
|
|
|
|
|
|
|
|
glEnableClientState(GL_VERTEX_ARRAY);
|
|
|
|
upload_quad_vertices(rectangle->rect);
|
2024-07-08 00:44:20 +00:00
|
|
|
|
2024-07-12 18:10:25 +00:00
|
|
|
glDrawArrays(GL_TRIANGLES, 0, 6);
|
|
|
|
glDisableClientState(GL_VERTEX_ARRAY);
|
2024-07-08 00:44:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* vertices_out and indices_out MUST BE FREED */
|
|
|
|
static void create_circle_geometry(t_fvec2 position,
|
|
|
|
t_color color,
|
|
|
|
float radius,
|
|
|
|
size_t num_vertices,
|
|
|
|
SDL_Vertex **vertices_out,
|
|
|
|
int **indices_out)
|
|
|
|
{
|
|
|
|
SDL_Vertex *vertices = cmalloc(sizeof *vertices * (num_vertices + 1));
|
|
|
|
int *indices = cmalloc(sizeof *indices * (num_vertices * 3));
|
|
|
|
|
|
|
|
/* the angle (in radians) to rotate by on each iteration */
|
|
|
|
float seg_rotation_angle = (360.0f / (float)num_vertices) * ((float)M_PI / 180);
|
|
|
|
|
|
|
|
vertices[0].position.x = (float)position.x;
|
|
|
|
vertices[0].position.y = (float)position.y;
|
|
|
|
vertices[0].color.r = color.r;
|
|
|
|
vertices[0].color.g = color.g;
|
|
|
|
vertices[0].color.b = color.b;
|
|
|
|
vertices[0].color.a = color.a;
|
|
|
|
vertices[0].tex_coord = (SDL_FPoint){ 0, 0 };
|
|
|
|
|
|
|
|
/* this point will rotate around the center */
|
|
|
|
float start_x = 0.0f - radius;
|
|
|
|
float start_y = 0.0f;
|
|
|
|
|
|
|
|
for (size_t i = 1; i < num_vertices + 1; ++i) {
|
|
|
|
float final_seg_rotation_angle = (float)i * seg_rotation_angle;
|
|
|
|
|
|
|
|
vertices[i].position.x =
|
|
|
|
cos(final_seg_rotation_angle) * start_x -
|
|
|
|
sin(final_seg_rotation_angle) * start_y;
|
|
|
|
vertices[i].position.y =
|
|
|
|
cos(final_seg_rotation_angle) * start_y +
|
|
|
|
sin(final_seg_rotation_angle) * start_x;
|
|
|
|
|
|
|
|
vertices[i].position.x += position.x;
|
|
|
|
vertices[i].position.y += position.y;
|
|
|
|
|
|
|
|
vertices[i].color.r = color.r;
|
|
|
|
vertices[i].color.g = color.g;
|
|
|
|
vertices[i].color.b = color.b;
|
|
|
|
vertices[i].color.a = color.a;
|
|
|
|
|
|
|
|
vertices[i].tex_coord = (SDL_FPoint){ 0, 0 };
|
|
|
|
|
|
|
|
|
|
|
|
size_t triangle_offset = 3 * (i - 1);
|
|
|
|
|
|
|
|
/* center point index */
|
|
|
|
indices[triangle_offset] = 0;
|
|
|
|
/* generated point index */
|
|
|
|
indices[triangle_offset + 1] = (int)i;
|
|
|
|
|
|
|
|
size_t index = (i + 1) % num_vertices;
|
|
|
|
if (index == 0)
|
|
|
|
index = num_vertices;
|
|
|
|
indices[triangle_offset + 2] = (int)index;
|
|
|
|
}
|
|
|
|
|
|
|
|
*vertices_out = vertices;
|
|
|
|
*indices_out = indices;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void render_circle(struct circle_primitive *circle) {
|
|
|
|
SDL_Vertex *vertices = NULL;
|
|
|
|
int *indices = NULL;
|
|
|
|
int num_vertices = (int)circle->radius;
|
|
|
|
|
|
|
|
create_circle_geometry(circle->position,
|
|
|
|
circle->color,
|
|
|
|
circle->radius,
|
|
|
|
num_vertices,
|
|
|
|
&vertices,
|
|
|
|
&indices);
|
|
|
|
|
2024-07-12 18:10:25 +00:00
|
|
|
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);
|
2024-07-08 00:44:20 +00:00
|
|
|
|
|
|
|
free(vertices);
|
|
|
|
free(indices);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void render_sprites(void) {
|
|
|
|
sort_sprites(ctx.render_queue_sprites);
|
|
|
|
|
|
|
|
for (size_t i = 0; i < arrlenu(ctx.render_queue_sprites); ++i) {
|
|
|
|
render_sprite(&ctx.render_queue_sprites[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void render_rectangles(void) {
|
|
|
|
for (size_t i = 0; i < arrlenu(ctx.render_queue_rectangles); ++i) {
|
|
|
|
render_rectangle(&ctx.render_queue_rectangles[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void render_circles(void) {
|
|
|
|
for (size_t i = 0; i < arrlenu(ctx.render_queue_circles); ++i) {
|
|
|
|
render_circle(&ctx.render_queue_circles[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2024-07-10 16:15:28 +00:00
|
|
|
static void draw_uncolored_space_traingle_batch(struct mesh_batch *batch) {
|
|
|
|
size_t data_len = arrlenu(batch->data);
|
|
|
|
|
|
|
|
/* create vertex array object */
|
|
|
|
if (batch->buffer == 0)
|
|
|
|
glGenBuffers(1, &batch->buffer);
|
|
|
|
|
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, batch->buffer);
|
|
|
|
|
|
|
|
/* TODO: try using mapped buffers while building batches instead? */
|
|
|
|
/* this way we could skip client side copy that is kept until commitment */
|
|
|
|
/* alternatively we could commit glBufferSubData based on a threshold */
|
|
|
|
|
|
|
|
/* upload batched data */
|
|
|
|
glBufferData(GL_ARRAY_BUFFER,
|
|
|
|
data_len * sizeof (struct uncolored_space_triangle),
|
|
|
|
batch->data,
|
|
|
|
GL_STREAM_DRAW);
|
|
|
|
|
|
|
|
/* vertex specification*/
|
|
|
|
glEnableClientState(GL_VERTEX_ARRAY);
|
|
|
|
glVertexPointer(3,
|
|
|
|
GL_FLOAT,
|
|
|
|
offsetof(struct uncolored_space_triangle, v1),
|
|
|
|
(void *)offsetof(struct uncolored_space_triangle, v0));
|
|
|
|
|
|
|
|
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
|
|
glClientActiveTexture(GL_TEXTURE0);
|
|
|
|
glTexCoordPointer(2,
|
2024-07-12 18:10:25 +00:00
|
|
|
GL_FLOAT,
|
2024-07-10 16:15:28 +00:00
|
|
|
offsetof(struct uncolored_space_triangle, v1),
|
|
|
|
(void *)offsetof(struct uncolored_space_triangle, uv0));
|
|
|
|
|
|
|
|
/* commit for drawing */
|
|
|
|
glDrawArrays(GL_TRIANGLES, 0, 3 * (int)data_len);
|
|
|
|
|
|
|
|
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
|
|
glDisableClientState(GL_VERTEX_ARRAY);
|
|
|
|
|
|
|
|
/* invalidate the buffer immediately */
|
|
|
|
glBufferData(GL_ARRAY_BUFFER, 0, NULL, GL_STREAM_DRAW);
|
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2024-07-09 12:35:54 +00:00
|
|
|
static void render_space(void) {
|
2024-07-12 18:10:25 +00:00
|
|
|
glEnable(GL_TEXTURE_2D);
|
2024-07-10 16:15:28 +00:00
|
|
|
glActiveTexture(GL_TEXTURE0);
|
|
|
|
|
|
|
|
/* solid white, no modulation */
|
|
|
|
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
|
|
|
|
|
|
|
|
for (size_t i = 0; i < arrlenu(ctx.uncolored_mesh_batches); ++i) {
|
|
|
|
if (arrlenu(&ctx.uncolored_mesh_batches[i].data) > 0) {
|
2024-07-12 18:10:25 +00:00
|
|
|
textures_bind_atlas(&ctx.texture_cache, (int)i, GL_TEXTURE_2D);
|
2024-07-10 16:15:28 +00:00
|
|
|
draw_uncolored_space_traingle_batch(&ctx.uncolored_mesh_batches[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (size_t i = 0; i < shlenu(ctx.uncolored_mesh_batches_loners); ++i) {
|
|
|
|
if (arrlenu(&ctx.uncolored_mesh_batches_loners[i].value.data) > 0) {
|
2024-07-12 18:10:25 +00:00
|
|
|
textures_bind_loner(&ctx.texture_cache,
|
|
|
|
ctx.uncolored_mesh_batches_loners[i].key,
|
|
|
|
GL_TEXTURE_2D);
|
|
|
|
|
2024-07-10 16:15:28 +00:00
|
|
|
draw_uncolored_space_traingle_batch(&ctx.uncolored_mesh_batches_loners[i].value);
|
|
|
|
}
|
|
|
|
}
|
2024-07-09 12:35:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2024-07-08 00:44:20 +00:00
|
|
|
void render(void) {
|
2024-07-10 16:15:28 +00:00
|
|
|
if (ctx.texture_cache.is_dirty)
|
|
|
|
textures_update_current_atlas(&ctx.texture_cache);
|
|
|
|
|
2024-07-12 18:10:25 +00:00
|
|
|
/* 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);
|
|
|
|
|
2024-07-09 12:35:54 +00:00
|
|
|
glClearColor((1.0f / 255) * 230,
|
|
|
|
(1.0f / 255) * 230,
|
|
|
|
(1.0f / 255) * 230, 1);
|
|
|
|
|
2024-07-10 16:15:28 +00:00
|
|
|
glClear(GL_COLOR_BUFFER_BIT |
|
|
|
|
GL_DEPTH_BUFFER_BIT |
|
|
|
|
GL_STENCIL_BUFFER_BIT);
|
2024-07-08 00:44:20 +00:00
|
|
|
|
2024-07-12 18:10:25 +00:00
|
|
|
{
|
|
|
|
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();
|
2024-07-10 16:15:28 +00:00
|
|
|
|
2024-07-12 18:10:25 +00:00
|
|
|
glEnable(GL_TEXTURE_2D);
|
|
|
|
|
|
|
|
render_sprites();
|
|
|
|
render_rectangles();
|
|
|
|
render_circles();
|
|
|
|
}
|
2024-07-08 00:44:20 +00:00
|
|
|
|
2024-07-12 18:10:25 +00:00
|
|
|
{
|
|
|
|
glMatrixMode(GL_PROJECTION);
|
|
|
|
glLoadIdentity();
|
2024-07-10 16:15:28 +00:00
|
|
|
|
2024-07-12 18:10:25 +00:00
|
|
|
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();
|
|
|
|
}
|
2024-07-10 16:15:28 +00:00
|
|
|
|
2024-07-09 12:35:54 +00:00
|
|
|
SDL_GL_SwapWindow(ctx.window);
|
2024-07-08 00:44:20 +00:00
|
|
|
}
|