196 lines
5.3 KiB
C
196 lines
5.3 KiB
C
#include "rendering/internal_api.h"
|
|
#include "rendering/sprites.h"
|
|
#include "rendering/triangles.h"
|
|
#include "rendering/circles.h"
|
|
#include "context.h"
|
|
#include "textures.h"
|
|
|
|
#include <SDL2/SDL.h>
|
|
#include <glad/glad.h>
|
|
#include <stb_ds.h>
|
|
|
|
#include <stddef.h>
|
|
#include <tgmath.h>
|
|
|
|
|
|
/* TODO: have a default initialized one */
|
|
static t_matrix4 camera_projection;
|
|
|
|
|
|
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_2d, 0);
|
|
|
|
for (size_t i = 0; i < hmlenu(ctx.uncolored_mesh_batches); ++i)
|
|
arrsetlen(ctx.uncolored_mesh_batches[i].value.primitives, 0);
|
|
}
|
|
|
|
|
|
/* rectangle */
|
|
void push_rectangle(t_frect rect, t_color color) {
|
|
struct rect_primitive rectangle = {
|
|
.rect = rect,
|
|
.color = color,
|
|
};
|
|
|
|
struct primitive_2d primitive = {
|
|
.type = PRIMITIVE_2D_RECT,
|
|
.rect = rectangle,
|
|
};
|
|
|
|
arrput(ctx.render_queue_2d, primitive);
|
|
}
|
|
|
|
|
|
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);
|
|
}
|
|
|
|
|
|
static void render_rectangle(const struct rect_primitive *rectangle) {
|
|
glColor4ub(rectangle->color.r, rectangle->color.g,
|
|
rectangle->color.b, rectangle->color.a);
|
|
|
|
glEnableClientState(GL_VERTEX_ARRAY);
|
|
upload_quad_vertices(rectangle->rect);
|
|
|
|
glDrawArrays(GL_TRIANGLES, 0, 6);
|
|
glDisableClientState(GL_VERTEX_ARRAY);
|
|
}
|
|
|
|
|
|
static void render_2d(void) {
|
|
glEnable(GL_TEXTURE_2D);
|
|
glActiveTexture(GL_TEXTURE0);
|
|
glDisable(GL_CULL_FACE);
|
|
glEnable(GL_DEPTH_TEST);
|
|
|
|
const size_t render_queue_len = arrlenu(ctx.render_queue_2d);
|
|
|
|
size_t batch_count = 0;
|
|
|
|
for (size_t i = 0; i < render_queue_len; ++i) {
|
|
const struct primitive_2d *current = &ctx.render_queue_2d[i];
|
|
|
|
switch (current->type) {
|
|
case PRIMITIVE_2D_SPRITE: {
|
|
const struct sprite_batch batch =
|
|
collect_sprite_batch(current, render_queue_len - i);
|
|
|
|
glDepthRange((double)batch_count / UINT16_MAX, 1.0);
|
|
|
|
render_sprites(current, batch);
|
|
|
|
i += batch.size - 1; ++batch_count;
|
|
break;
|
|
}
|
|
case PRIMITIVE_2D_RECT:
|
|
render_rectangle(¤t->rect);
|
|
break;
|
|
case PRIMITIVE_2D_CIRCLE:
|
|
render_circle(¤t->circle);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static void render_space(void) {
|
|
glEnable(GL_CULL_FACE);
|
|
glEnable(GL_DEPTH_TEST);
|
|
glDepthMask(GL_TRUE);
|
|
glDepthFunc(GL_LESS);
|
|
glEnable(GL_BLEND);
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
glEnable(GL_TEXTURE_2D);
|
|
glDisable(GL_ALPHA_TEST);
|
|
glActiveTexture(GL_TEXTURE0);
|
|
|
|
/* solid white, no modulation */
|
|
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
|
|
|
|
for (size_t i = 0; i < hmlenu(ctx.uncolored_mesh_batches); ++i) {
|
|
draw_uncolored_space_traingle_batch(&ctx.uncolored_mesh_batches[i].value,
|
|
ctx.uncolored_mesh_batches[i].key);
|
|
}
|
|
}
|
|
|
|
|
|
void render(void) {
|
|
textures_update_atlas(&ctx.texture_cache);
|
|
|
|
/* fit rendering context onto the resizable screen */
|
|
if (ctx.window_size_has_changed) {
|
|
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
|
|
);
|
|
}
|
|
}
|
|
|
|
glClearColor((1.0f / 255) * 230,
|
|
(1.0f / 255) * 230,
|
|
(1.0f / 255) * 230, 1);
|
|
|
|
glClear(GL_COLOR_BUFFER_BIT |
|
|
GL_DEPTH_BUFFER_BIT |
|
|
GL_STENCIL_BUFFER_BIT);
|
|
|
|
{
|
|
glMatrixMode(GL_PROJECTION);
|
|
glLoadMatrixf(&camera_projection.row[0].x);
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glLoadIdentity();
|
|
|
|
render_space();
|
|
}
|
|
|
|
/* TODO: only do it when transition between spaces is needed */
|
|
glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
|
|
|
|
{
|
|
glMatrixMode(GL_PROJECTION);
|
|
glLoadIdentity();
|
|
glOrtho(0, RENDER_BASE_WIDTH, RENDER_BASE_HEIGHT, 0, -1, 1);
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glLoadIdentity();
|
|
|
|
render_2d();
|
|
}
|
|
|
|
SDL_GL_SwapWindow(ctx.window);
|
|
}
|
|
|
|
void push_camera(const t_camera *const camera) {
|
|
/* TODO: skip recaulculating if it's the same? */
|
|
camera_projection = camera_look_at(camera);
|
|
}
|