render.c (11572B)
1 internal void renderer_init(struct RendererState *renderer, struct Image *images, size_t num_images, struct StackAllocator *allocator) 2 { 3 GLfloat quad_vertices[] = { 4 0.5f, 0.5f, 1.0f, 0.0f, 5 0.5f, -0.5f, 1.0f, 1.0f, 6 -0.5f, -0.5f, 0.0f, 1.0f, 7 -0.5f, 0.5f, 0.0f, 0.0f, 8 }; 9 10 GLuint quad_elements[] = {0, 1, 3, 1, 2, 3}; 11 GLuint rect_elements[] = {0, 1, 1, 2, 2, 3, 3, 0}; 12 13 GLuint vao; 14 GLuint quad_vbo; 15 GLuint quad_ebo; 16 GLuint rect_ebo; 17 glGenVertexArrays(1, &vao); 18 glGenBuffers(1, &quad_vbo); 19 glGenBuffers(1, &quad_ebo); 20 glGenBuffers(1, &rect_ebo); 21 22 // NOTE(amin): We will leave this bound for the duration of the game. 23 // There should not be any calls to glBindVertexArray(0). 24 glBindVertexArray(vao); 25 26 glBindBuffer(GL_ARRAY_BUFFER, quad_vbo); 27 glBufferData(GL_ARRAY_BUFFER, sizeof(quad_vertices), quad_vertices, GL_STATIC_DRAW); 28 glVertexAttribPointer( 29 0, // Data location 30 2, // Number of values 31 GL_FLOAT, // Data type 32 GL_FALSE, // Normalize data 33 4 * sizeof(GLfloat), // Stride 34 (GLvoid*)0 // Position data offset (0) 35 ); 36 glEnableVertexAttribArray(0); 37 38 // texture coordinates 39 glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (GLvoid*)(2 * sizeof(GLfloat))); 40 glEnableVertexAttribArray(1); 41 42 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, quad_ebo); 43 glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(quad_elements), quad_elements, GL_STATIC_DRAW); 44 45 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, rect_ebo); 46 glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(rect_elements), rect_elements, GL_STATIC_DRAW); 47 48 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, quad_ebo); 49 50 for (u32 i = 0; i < num_images; i++) 51 { 52 struct Image img = images[i]; 53 u8 bytes_per_texel = 4; 54 v2u tile_dim = {64, 64}; 55 v2u tileset_dim = {img.dim.x / tile_dim.x, img.dim.y / tile_dim.y}; 56 57 u32 texture_id; 58 glGenTextures(1, &texture_id); 59 glBindTexture(GL_TEXTURE_2D_ARRAY, texture_id); 60 u32 mip_map_levels = 4; 61 // NOTE(amin): This allocates space for the 2D array texture. If we 62 // required OpenGL 4.2, we could instead use glTexStorage3D and no 63 // loop: 64 // glTexStorage3D(GL_TEXTURE_2D_ARRAY, mip_map_levels, GL_RGBA8, tile_dim.x, tile_dim.y, tileset_dim.x * tileset_dim.y); 65 for (u32 level = 0; level < mip_map_levels; level++) 66 { 67 v2u mip_dim = {tile_dim.x / math_pow_of_2(level), tile_dim.y / math_pow_of_2(level)}; 68 assert(!math_v2u_eq(mip_dim, (v2u) {0, 0})); 69 glTexImage3D(GL_TEXTURE_2D_ARRAY, level, GL_RGBA8, mip_dim.x, mip_dim.y, tileset_dim.x * tileset_dim.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); 70 } 71 72 size_t marker = mem_st_get_marker(allocator); 73 { 74 u8 *tile_data = mem_st_alloc_buffer(allocator, u8, tile_dim.x * tile_dim.y * bytes_per_texel); 75 for (u32 tile_y = 0; tile_y < tileset_dim.y; tile_y++) 76 { 77 for (u32 tile_x = 0; tile_x < tileset_dim.x; tile_x++) 78 { 79 // NOTE(amin): We must copy the disparate interleaved 80 // texels associated with a given tile in the image to a 81 // contiguous memory region. 82 for (u32 texel_y = 0; texel_y < tile_dim.y; texel_y++) 83 { 84 for (u32 texel_x = 0; texel_x < tile_dim.x; texel_x++) 85 { 86 v2u texel_index_in_image = { 87 (tile_x * tile_dim.x) + texel_x, 88 (tile_y * tile_dim.y) + texel_y, 89 }; 90 assert(texel_index_in_image.x < tileset_dim.x * tile_dim.x); 91 assert(texel_index_in_image.y < tileset_dim.y * tile_dim.y); 92 u32 linearized_texel_i = texel_index_in_image.x + (texel_index_in_image.y * tileset_dim.x * tile_dim.x); 93 u32 *texel = (u32 *)(img.data + (linearized_texel_i * bytes_per_texel)); 94 ((u32 *)tile_data)[texel_x + (texel_y * tile_dim.x)] = *texel; 95 } 96 } 97 u32 tile_id = tile_x + (tile_y * tileset_dim.x); 98 glTexSubImage3D( 99 GL_TEXTURE_2D_ARRAY, 100 0, 0, 0, 101 tile_id, 102 tile_dim.x, 103 tile_dim.y, 104 1, 105 GL_RGBA, 106 GL_UNSIGNED_BYTE, 107 tile_data); 108 } 109 } 110 } 111 mem_st_free_to_marker(allocator, marker); 112 113 glGenerateMipmap(GL_TEXTURE_2D_ARRAY); 114 glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 115 glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 116 glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); 117 glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 118 119 glActiveTexture(GL_TEXTURE0); 120 } 121 122 renderer->vao = vao; 123 renderer->quad_vbo = quad_vbo; 124 renderer->quad_ebo = quad_ebo; 125 renderer->rect_ebo = rect_ebo; 126 renderer->queue = mem_st_alloc_buffer(allocator, struct RenderJob, RENDER_QUEUE_SIZE); 127 renderer->queue_count = 0; 128 129 glEnable(GL_BLEND); 130 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 131 } 132 133 internal void renderer_jobs_draw(struct RendererState *renderer, v2u framebuffer) 134 { 135 v2 viewport_min = {0}; 136 v2 viewport_dim = {0}; 137 // renderer_update_viewport 138 { 139 f32 ppm = 0.0f; 140 f32 screen_aspect_ratio = (f32)framebuffer.width / (f32)framebuffer.height; 141 if (screen_aspect_ratio < ROOM_ASPECT_RATIO) 142 { 143 ppm = (f32)framebuffer.width / (f32)ROOM_DIM_X; 144 } 145 else 146 { 147 ppm = (f32)framebuffer.height / (f32)ROOM_DIM_Y; 148 } 149 150 viewport_dim = (v2) { 151 ppm * (f32)ROOM_DIM_X, 152 ppm * (f32)ROOM_DIM_Y, 153 }; 154 155 f32 h_pad_size = (framebuffer.width - viewport_dim.width) / 2.0f; 156 f32 v_pad_size = (framebuffer.height - viewport_dim.height) / 2.0f; 157 158 viewport_min = (v2) {h_pad_size, v_pad_size}; 159 160 m4 view = math_m4_init_id(); 161 view = math_translate(view, (v3) {viewport_min.x, viewport_min.y, 0.0f}); 162 view = math_scale(view, (v3) {ppm, ppm, 1.0f}); 163 renderer->view = view; 164 165 renderer->projection = math_projection_ortho(0.0f, framebuffer.width, 0.0f, framebuffer.height, -1.0f, 0.0f); 166 } 167 168 glViewport(0, 0, framebuffer.width, framebuffer.height); 169 glClearColor(0.0f, 0.0f, 0.0f, 1.0f); 170 171 glDisable(GL_SCISSOR_TEST); 172 glClear(GL_COLOR_BUFFER_BIT); 173 glEnable(GL_SCISSOR_TEST); 174 175 glScissor( 176 viewport_min.x, 177 viewport_min.y, 178 viewport_dim.x, 179 viewport_dim.y); 180 181 #if 0 182 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); 183 #else 184 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); 185 #endif 186 187 shader_use(&renderer->shader); 188 shader_setm4(&renderer->shader, SHADER_UNIFORM_PROJECTION, &renderer->projection); 189 shader_setm4(&renderer->shader, SHADER_UNIFORM_VIEW, &renderer->view); 190 191 GLuint current_ebo = renderer->quad_ebo; 192 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, current_ebo); 193 for (size_t i = 0; i < renderer->queue_count; i++) 194 { 195 struct RenderJob j = renderer->queue[i]; 196 197 if (j.layer == RENDER_LAYER_DEBUG) 198 { 199 if (!renderer->scissor_test_is_disabled) 200 { 201 glDisable(GL_SCISSOR_TEST); 202 renderer->scissor_test_is_disabled = true; 203 } 204 } 205 else if (renderer->scissor_test_is_disabled) 206 { 207 glEnable(GL_SCISSOR_TEST); 208 renderer->scissor_test_is_disabled = false; 209 } 210 211 if (current_ebo != j.ebo) 212 { 213 current_ebo = j.ebo; 214 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, current_ebo); 215 } 216 shader_setv3(&renderer->shader, SHADER_UNIFORM_COLOR, &j.color); 217 shader_setm4(&renderer->shader, SHADER_UNIFORM_MODEL, &j.model); 218 shader_setf(&renderer->shader, SHADER_UNIFORM_TEX_INTERP, j.tex_interp); 219 shader_setf(&renderer->shader, SHADER_UNIFORM_TILE_ID, j.tile_id); 220 if (j.ebo == renderer->quad_ebo) 221 { 222 glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); 223 } 224 else if (j.ebo == renderer->rect_ebo) 225 { 226 glDrawElements(GL_LINES, 8, GL_UNSIGNED_INT, 0); 227 } 228 else 229 { 230 assert(false); 231 } 232 233 assert(j.layer < NUM_RENDER_LAYERS); 234 } 235 236 renderer->queue_count = 0; 237 } 238 239 internal void renderer_jobs_sort(struct RendererState *renderer, struct StackAllocator *allocator) 240 { 241 size_t free_marker = mem_st_get_marker(allocator); 242 243 struct RenderJob *job_queue = renderer->queue; 244 245 size_t layer_pop_count[NUM_RENDER_LAYERS] = {0}; 246 for (size_t i = 0; i < renderer->queue_count; i++) 247 { 248 struct RenderJob j = job_queue[i]; 249 assert(j.layer < NUM_RENDER_LAYERS); 250 layer_pop_count[j.layer] += 1; 251 } 252 253 { 254 size_t sum = 0; 255 for (size_t i = 0; i < NUM_RENDER_LAYERS; i++) 256 { 257 sum += layer_pop_count[i]; 258 } 259 assert(sum == renderer->queue_count); 260 } 261 262 struct RenderJob *sorted = mem_st_alloc_buffer(allocator, struct RenderJob, renderer->queue_count); 263 size_t layer_pop_count_sorted[NUM_RENDER_LAYERS] = {0}; 264 for (size_t i = 0; i < renderer->queue_count; i++) 265 { 266 struct RenderJob j = job_queue[i]; 267 assert(j.layer < NUM_RENDER_LAYERS); 268 269 size_t insertion_index = 0; 270 for (enum RenderLayer l = 0; l < j.layer; l++) 271 { 272 insertion_index += layer_pop_count[l]; 273 } 274 insertion_index += layer_pop_count_sorted[j.layer]; 275 276 assert(layer_pop_count_sorted[j.layer] < layer_pop_count[j.layer]); 277 sorted[insertion_index] = j; 278 layer_pop_count_sorted[j.layer] += 1; 279 } 280 281 mem_move_buffer(job_queue, sorted, struct RenderJob, renderer->queue_count); 282 mem_st_free_to_marker(allocator, free_marker); 283 } 284 285 internal void renderer_job_enqueue(struct RendererState *renderer, struct RenderJob job) 286 { 287 assert(renderer); 288 assert(renderer->queue); 289 assert(renderer->queue_count + 1 < RENDER_QUEUE_SIZE); 290 renderer->queue[renderer->queue_count] = job; 291 renderer->queue_count++; 292 } 293 294 internal void renderer_debug_quad_draw(struct RendererState *renderer, rect r, v3 color) 295 { 296 v2 dim = {r.max.x - r.min.x, r.max.y - r.min.y}; 297 298 m4 model = math_m4_init_id(); 299 model = math_translate(model, (v3) {dim.x * 0.5f, dim.y * 0.5f, 0.0f}); 300 model = math_translate(model, (v3) {r.min.x, r.min.y, 0.0f}); 301 model = math_scale(model, (v3) {dim.width, dim.height, 1.0f}); 302 303 renderer_job_enqueue( 304 renderer, 305 (struct RenderJob) { 306 .ebo = renderer->rect_ebo, 307 .color = color, 308 .model = model, 309 .layer = RENDER_LAYER_DEBUG, 310 }); 311 } 312 313 internal void renderer_cleanup(struct RendererState *renderer) 314 { 315 glDeleteVertexArrays(1, &renderer->vao); 316 glDeleteBuffers(1, &renderer->quad_vbo); 317 glDeleteBuffers(1, &renderer->quad_ebo); 318 glDeleteBuffers(1, &renderer->rect_ebo); 319 }