#include "private/rendering.h" #include "rendering/sprites.h" #include "rendering/triangles.h" #include "rendering/circles.h" #include "context.h" #include "textures.h" #include #include #include #include #include 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); glDisable(GL_ALPHA_TEST); 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); glDisable(GL_ALPHA_TEST); glDepthFunc(GL_LESS); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_TEXTURE_2D); 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); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); render_space(); } { 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); }