#include "twn_rendering_c.h" #include "twn_rendering.h" #include "twn_engine_context_c.h" #include "twn_camera.h" #include #include #ifdef EMSCRIPTEN #include #else #include #endif #include #include /* TODO: have a default initialized one */ Matrix4 camera_projection_matrix; Matrix4 camera_look_at_matrix; void render_queue_clear(void) { text_cache_reset_arena(&ctx.text_cache); /* 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(Rect rect, Color color) { RectPrimitive rectangle = { .rect = rect, .color = color, }; Primitive2D primitive = { .type = PRIMITIVE_2D_RECT, .rect = rectangle, }; arrput(ctx.render_queue_2d, primitive); } static void render_2d(void) { use_2d_pipeline(); 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 Primitive2D *current = &ctx.render_queue_2d[i]; switch (current->type) { case PRIMITIVE_2D_SPRITE: { const struct SpriteBatch batch = collect_sprite_batch(current, render_queue_len - i); /* TODO: what's even the point? just use OR_EQUAL comparison */ set_depth_range((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; case PRIMITIVE_2D_TEXT: render_text(¤t->text); break; } } } static void render_space(void) { /* nothing to do, abort */ /* as space pipeline isn't used we can have fewer changes and initialization costs */ if (hmlenu(ctx.uncolored_mesh_batches) == 0) return; use_space_pipeline(); 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.game.window_size_has_changed) { if ((float)ctx.game.window_w / (float)ctx.game.window_h > (float)(ctx.base_render_width / ctx.base_render_height)) { float ratio = (float)ctx.game.window_h / (float)ctx.base_render_height; int w = (int)((float)ctx.base_render_width * ratio); setup_viewport( ctx.game.window_w / 2 - w / 2, 0, w, ctx.game.window_h ); } else { float ratio = (float)ctx.game.window_w / (float)ctx.base_render_width; int h = (int)((float)ctx.base_render_height * ratio); setup_viewport( 0, ctx.game.window_h / 2 - h / 2, ctx.game.window_w, h ); } } clear_draw_buffer(); render_space(); render_skybox(); /* after space, as to use depth buffer for early rejection */ render_2d(); swap_buffers(); } void set_camera(const Camera *const camera) { /* TODO: skip recaulculating if it's the same? */ camera_projection_matrix = camera_perspective(camera); camera_look_at_matrix = camera_look_at(camera); }