148 lines
		
	
	
		
			4.1 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			148 lines
		
	
	
		
			4.1 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| #include "twn_rendering_c.h"
 | |
| #include "twn_rendering.h"
 | |
| #include "twn_engine_context_c.h"
 | |
| #include "twn_camera.h"
 | |
| 
 | |
| #include <SDL2/SDL.h>
 | |
| #include <stb_ds.h>
 | |
| 
 | |
| #ifdef EMSCRIPTEN
 | |
| #include <GLES2/gl2.h>
 | |
| #else
 | |
| #include <glad/glad.h>
 | |
| #endif
 | |
| 
 | |
| #include <stddef.h>
 | |
| #include <tgmath.h>
 | |
| 
 | |
| 
 | |
| /* TODO: have a default initialized one */
 | |
| t_matrix4 camera_projection_matrix;
 | |
| t_matrix4 camera_look_at_matrix;
 | |
| 
 | |
| 
 | |
| void render_queue_clear(void) {
 | |
|     /* this doesn't even _deserve_ a TODO */
 | |
|     /* if you're gonna remove it, this is also being done in main.c */
 | |
|     for (size_t i = 0; i < arrlenu(ctx.render_queue_2d); ++i) {
 | |
|         if (ctx.render_queue_2d[i].type == PRIMITIVE_2D_TEXT) {
 | |
|             free(ctx.render_queue_2d[i].text.text);
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     /* 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 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 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);
 | |
| 
 | |
|                 /* 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 > RENDER_BASE_RATIO) {
 | |
|             float ratio = (float)ctx.game.window_h / (float)RENDER_BASE_HEIGHT;
 | |
|             int w = (int)((float)RENDER_BASE_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)RENDER_BASE_WIDTH;
 | |
|             int h = (int)((float)RENDER_BASE_HEIGHT * ratio);
 | |
|             setup_viewport(
 | |
|                 0,
 | |
|                 ctx.game.window_h / 2 - h / 2,
 | |
|                 ctx.game.window_w,
 | |
|                 h
 | |
|             );
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     clear_draw_buffer();
 | |
|     render_space();
 | |
|     render_2d();
 | |
|     swap_buffers();
 | |
| }
 | |
| 
 | |
| 
 | |
| void set_camera(const t_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);
 | |
| }
 |