common.c (26287B)
1 /* See LICENSE for license details. */ 2 #include "opengl.h" 3 #include "GLFW/glfw3.h" 4 5 #include <stdio.h> 6 7 #include "options.h" 8 9 #define RENDER_TARGET_SIZE RENDER_TARGET_WIDTH, RENDER_TARGET_HEIGHT 10 #define TOTAL_OUTPUT_FRAMES (OUTPUT_FRAME_RATE * OUTPUT_TIME_SECONDS - 1) 11 12 #define MODEL_RENDER_MODEL_MATRIX_LOC 0 13 #define MODEL_RENDER_VIEW_MATRIX_LOC 1 14 #define MODEL_RENDER_PROJ_MATRIX_LOC 2 15 #define MODEL_RENDER_CLIP_FRACTION_LOC 3 16 #define MODEL_RENDER_SWIZZLE_LOC 4 17 #define MODEL_RENDER_LOG_SCALE_LOC 5 18 #define MODEL_RENDER_DYNAMIC_RANGE_LOC 6 19 #define MODEL_RENDER_THRESHOLD_LOC 7 20 #define MODEL_RENDER_GAMMA_LOC 8 21 #define MODEL_RENDER_BB_COLOUR_LOC 9 22 #define MODEL_RENDER_BB_FRACTION_LOC 10 23 #define MODEL_RENDER_GAIN_LOC 11 24 25 #define CYCLE_T_UPDATE_SPEED 0.25f 26 #define BG_CLEAR_COLOUR (v4){{0.12, 0.1, 0.1, 1}} 27 28 struct gl_debug_ctx { 29 Stream stream; 30 OS *os; 31 }; 32 33 function f32 34 get_frame_time_step(ViewerContext *ctx) 35 { 36 f32 result = 0; 37 /* NOTE(rnp): if we are outputting frames do a constant time step */ 38 if (ctx->output_frames_count > 0) { 39 result = 1.0f / (OUTPUT_FRAME_RATE * OUTPUT_TIME_SECONDS * CYCLE_T_UPDATE_SPEED); 40 } else { 41 f64 now = glfwGetTime(); 42 result = ctx->demo_mode * (now - ctx->last_time); 43 ctx->last_time = now; 44 } 45 ctx->do_update |= result != 0; 46 return result; 47 } 48 49 function void 50 gl_debug_logger(u32 src, u32 type, u32 id, u32 lvl, s32 len, const char *msg, const void *userctx) 51 { 52 (void)src; (void)type; (void)id; 53 54 struct gl_debug_ctx *ctx = (struct gl_debug_ctx *)userctx; 55 Stream *e = &ctx->stream; 56 stream_append_str8s(e, str8("[OpenGL] "), (str8){.len = len, .data = (u8 *)msg}, str8("\n")); 57 os_write_file(ctx->os->error_handle, stream_to_str8(e)); 58 stream_reset(e, 0); 59 } 60 61 function u32 62 compile_shader(OS *os, Arena a, u32 type, str8 shader, str8 name) 63 { 64 u32 sid = glCreateShader(type); 65 glShaderSource(sid, 1, (const char **)&shader.data, (int *)&shader.len); 66 glCompileShader(sid); 67 68 s32 res = 0; 69 glGetShaderiv(sid, GL_COMPILE_STATUS, &res); 70 71 if (res == GL_FALSE) { 72 Stream buf = arena_stream(a); 73 stream_append_str8s(&buf, name, str8(": failed to compile\n")); 74 75 s32 len = 0, out_len = 0; 76 glGetShaderiv(sid, GL_INFO_LOG_LENGTH, &len); 77 glGetShaderInfoLog(sid, len, &out_len, (char *)(buf.data + buf.widx)); 78 stream_commit(&buf, out_len); 79 glDeleteShader(sid); 80 os_write_file(os->error_handle, stream_to_str8(&buf)); 81 82 sid = 0; 83 } 84 85 return sid; 86 } 87 88 function u32 89 link_program(OS *os, Arena a, u32 *shader_ids, u32 shader_id_count) 90 { 91 s32 success = 0; 92 u32 result = glCreateProgram(); 93 for (u32 i = 0; i < shader_id_count; i++) 94 glAttachShader(result, shader_ids[i]); 95 glLinkProgram(result); 96 glGetProgramiv(result, GL_LINK_STATUS, &success); 97 if (success == GL_FALSE) { 98 s32 len = 0; 99 Stream buf = arena_stream(a); 100 stream_append_str8(&buf, str8("shader link error: ")); 101 glGetProgramInfoLog(result, buf.cap - buf.widx, &len, (c8 *)(buf.data + buf.widx)); 102 stream_reset(&buf, len); 103 stream_append_byte(&buf, '\n'); 104 os_write_file(os->error_handle, stream_to_str8(&buf)); 105 glDeleteProgram(result); 106 result = 0; 107 } 108 return result; 109 } 110 111 function u32 112 load_shader(OS *os, Arena arena, str8 vs_text, str8 fs_text, str8 info_name, str8 label) 113 { 114 u32 result = 0; 115 u32 fs_id = compile_shader(os, arena, GL_FRAGMENT_SHADER, fs_text, info_name); 116 u32 vs_id = compile_shader(os, arena, GL_VERTEX_SHADER, vs_text, info_name); 117 if (fs_id && vs_id) result = link_program(os, arena, (u32 []){vs_id, fs_id}, 2); 118 glDeleteShader(fs_id); 119 glDeleteShader(vs_id); 120 121 if (result) { 122 Stream buf = arena_stream(arena); 123 stream_append_str8s(&buf, str8("loaded: "), info_name, str8("\n")); 124 os_write_file(os->error_handle, stream_to_str8(&buf)); 125 LABEL_GL_OBJECT(GL_PROGRAM, result, label); 126 } 127 128 return result; 129 } 130 131 typedef struct { 132 RenderContext *render_context; 133 str8 vertex_text; 134 str8 fragment_header; 135 } ShaderReloadContext; 136 137 function FILE_WATCH_CALLBACK_FN(reload_shader) 138 { 139 ShaderReloadContext *ctx = (typeof(ctx))user_data; 140 str8 header = push_str8(&tmp, ctx->fragment_header); 141 str8 fragment = os_read_whole_file(&tmp, (c8 *)path.data); 142 fragment.data -= header.len; 143 fragment.len += header.len; 144 assert(fragment.data == header.data); 145 u32 new_program = load_shader(os, tmp, ctx->vertex_text, fragment, path, path); 146 if (new_program) { 147 glDeleteProgram(ctx->render_context->shader); 148 ctx->render_context->shader = new_program; 149 } 150 return 1; 151 } 152 153 function u32 154 load_complex_texture(Arena arena, c8 *file_path, u32 width, u32 height, u32 depth) 155 { 156 u32 result = 0; 157 glCreateTextures(GL_TEXTURE_3D, 1, &result); 158 glTextureStorage3D(result, 1, GL_RG32F, width, height, depth); 159 glTextureParameteri(result, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT); 160 glTextureParameteri(result, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT); 161 glTextureParameteri(result, GL_TEXTURE_WRAP_R, GL_MIRRORED_REPEAT); 162 glTextureParameteri(result, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 163 glTextureParameteri(result, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 164 165 str8 raw = os_read_whole_file(&arena, file_path); 166 if (raw.len) glTextureSubImage3D(result, 0, 0, 0, 0, width, height, depth, GL_RG, GL_FLOAT, raw.data); 167 168 return result; 169 } 170 171 function RenderModel 172 render_model_from_arrays(f32 *vertices, f32 *normals, u16 *indices, u32 index_count) 173 { 174 RenderModel result = {0}; 175 176 s32 buffer_size = index_count * (6 * sizeof(f32) + sizeof(u16)); 177 s32 indices_offset = index_count * (6 * sizeof(f32)); 178 s32 vert_size = index_count * 3 * sizeof(f32); 179 s32 ind_size = index_count * sizeof(u16); 180 181 result.elements = index_count; 182 result.elements_offset = indices_offset; 183 184 glCreateBuffers(1, &result.buffer); 185 glNamedBufferStorage(result.buffer, buffer_size, 0, GL_DYNAMIC_STORAGE_BIT); 186 glNamedBufferSubData(result.buffer, 0, vert_size, vertices); 187 glNamedBufferSubData(result.buffer, vert_size, vert_size, normals); 188 glNamedBufferSubData(result.buffer, indices_offset, ind_size, indices); 189 190 glCreateVertexArrays(1, &result.vao); 191 glVertexArrayVertexBuffer(result.vao, 0, result.buffer, 0, 3 * sizeof(f32)); 192 glVertexArrayVertexBuffer(result.vao, 1, result.buffer, vert_size, 3 * sizeof(f32)); 193 glVertexArrayElementBuffer(result.vao, result.buffer); 194 195 glEnableVertexArrayAttrib(result.vao, 0); 196 glEnableVertexArrayAttrib(result.vao, 1); 197 198 glVertexArrayAttribFormat(result.vao, 0, 3, GL_FLOAT, 0, 0); 199 glVertexArrayAttribFormat(result.vao, 1, 3, GL_FLOAT, 0, vert_size); 200 201 glVertexArrayAttribBinding(result.vao, 0, 0); 202 glVertexArrayAttribBinding(result.vao, 1, 0); 203 204 return result; 205 } 206 207 function RenderModel 208 load_render_model(Arena arena, c8 *positions_file_name, c8 *indices_file_name, c8 *normals_file_name) 209 { 210 str8 positions = os_read_whole_file(&arena, positions_file_name); 211 str8 normals = os_read_whole_file(&arena, normals_file_name); 212 str8 indices = os_read_whole_file(&arena, indices_file_name); 213 214 RenderModel result = render_model_from_arrays((f32 *)positions.data, (f32 *)normals.data, 215 (u16 *)indices.data, indices.len / sizeof(u16)); 216 return result; 217 } 218 219 function b32 220 export_bitmap(ViewerContext *ctx) 221 { 222 #pragma pack(push, 1) 223 struct bmp_header { 224 u8 file_type[2]; 225 u32 file_size; 226 u16 reserved_1; 227 u16 reserved_2; 228 u32 bitmap_offset; 229 230 u32 size; 231 s32 width; 232 s32 height; 233 u16 planes; 234 u16 bits_per_pixel; 235 236 u32 compression; 237 u32 size_of_bitmap; 238 239 s32 horizontal_resolution; 240 s32 vertical_resolution; 241 u32 colours_used; 242 u32 colours_important; 243 244 u32 red_mask; 245 u32 green_mask; 246 u32 blue_mask; 247 u32 alpha_mask; 248 249 u32 colour_space_type; 250 u32 cie_xyz_triples[9]; 251 u32 gamma_red; 252 u32 gamma_green; 253 u32 gamma_blue; 254 }; 255 #pragma pack(pop) 256 257 s32 texture_size = ctx->output_target.size.w * ctx->output_target.size.h * sizeof(u32); 258 s32 out_size = texture_size + round_up_power_of_2(sizeof(struct bmp_header)); 259 260 Arena arena = ctx->video_arena; 261 void *out_buf = arena_alloc(&arena, out_size, 64, 1); 262 263 struct bmp_header *header = (struct bmp_header *)out_buf; 264 header->bitmap_offset = out_size - texture_size; 265 header->file_type[0] = 'B'; 266 header->file_type[1] = 'M'; 267 header->file_size = out_size; 268 header->size = 108; 269 header->width = ctx->output_target.size.w; 270 header->height = -ctx->output_target.size.h; 271 header->planes = 1; 272 header->bits_per_pixel = 32; 273 header->compression = 3; 274 header->size_of_bitmap = texture_size; 275 header->red_mask = 0xFF000000; 276 header->green_mask = 0x00FF0000; 277 header->blue_mask = 0x0000FF00; 278 header->alpha_mask = 0x000000FF; 279 280 glGetTextureImage(ctx->output_target.textures[0], 0, GL_RGBA, 281 GL_UNSIGNED_INT_8_8_8_8, texture_size, 282 (u8 *)out_buf + header->bitmap_offset); 283 284 return os_write_new_file("view.bmp", (str8){.len = out_size, .data = out_buf}); 285 } 286 287 function void 288 scroll_callback(GLFWwindow *window, f64 x, f64 y) 289 { 290 ViewerContext *ctx = glfwGetWindowUserPointer(window); 291 ctx->camera_fov += y; 292 ctx->do_update = 1; 293 } 294 295 function void 296 key_callback(GLFWwindow *window, s32 key, s32 scancode, s32 action, s32 modifiers) 297 { 298 ViewerContext *ctx = glfwGetWindowUserPointer(window); 299 if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) 300 ctx->should_exit = 1; 301 302 if (key == GLFW_KEY_SPACE && action == GLFW_PRESS) 303 ctx->demo_mode = !ctx->demo_mode; 304 305 if (key == GLFW_KEY_F11 || key == GLFW_KEY_F12) { 306 sz frames = TOTAL_OUTPUT_FRAMES; 307 sz needed_bytes = sizeof(u32) * RENDER_TARGET_HEIGHT * RENDER_TARGET_WIDTH * frames; 308 if (!ctx->video_arena.beg) { 309 ctx->video_arena = os_alloc_arena(needed_bytes); 310 if (!ctx->video_arena.beg) 311 fputs("failed to allocate space for output data\n", stderr); 312 } 313 } 314 315 if (key == GLFW_KEY_F11) { 316 if (ctx->video_arena.beg) { 317 if (export_bitmap(ctx)) fputs("exported bitmap\n", stderr); 318 else fputs("failed to export bitmap\n", stderr); 319 } 320 } 321 322 if (key == GLFW_KEY_F12 && action == GLFW_PRESS && ctx->output_frames_count == 0) { 323 if (ctx->video_arena.beg) { 324 ctx->output_frames_count = TOTAL_OUTPUT_FRAMES; 325 ctx->cycle_t = 0; 326 } 327 } 328 329 if (key == GLFW_KEY_A && action != GLFW_RELEASE) 330 ctx->cycle_t += 4.0f / (OUTPUT_TIME_SECONDS * OUTPUT_FRAME_RATE); 331 if (key == GLFW_KEY_D && action != GLFW_RELEASE) 332 ctx->cycle_t -= 4.0f / (OUTPUT_TIME_SECONDS * OUTPUT_FRAME_RATE); 333 if (key == GLFW_KEY_W && action != GLFW_RELEASE) 334 ctx->camera_angle += 5 * PI / 180.0f; 335 if (key == GLFW_KEY_S && action != GLFW_RELEASE) 336 ctx->camera_angle -= 5 * PI / 180.0f; 337 338 ctx->do_update = 1; 339 } 340 341 function void 342 fb_callback(GLFWwindow *window, s32 w, s32 h) 343 { 344 ViewerContext *ctx = glfwGetWindowUserPointer(window); 345 ctx->window_size = (sv2){.w = w, .h = h}; 346 } 347 348 function void 349 init_viewer(ViewerContext *ctx) 350 { 351 ctx->demo_mode = 1; 352 ctx->window_size = (sv2){.w = 640, .h = 640}; 353 ctx->camera_radius = CAMERA_RADIUS; 354 ctx->camera_angle = -CAMERA_ELEVATION_ANGLE * PI / 180.0f; 355 ctx->camera_fov = 60.0f; 356 357 if (!glfwInit()) os_fatal(str8("failed to start glfw\n")); 358 359 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); 360 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6); 361 glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); 362 ctx->window = glfwCreateWindow(ctx->window_size.w, ctx->window_size.h, "3D Viewer", 0, 0); 363 if (!ctx->window) os_fatal(str8("failed to open window\n")); 364 glfwMakeContextCurrent(ctx->window); 365 glfwSetWindowUserPointer(ctx->window, ctx); 366 glfwSwapInterval(1); 367 368 glfwSetKeyCallback(ctx->window, key_callback); 369 glfwSetScrollCallback(ctx->window, scroll_callback); 370 glfwSetFramebufferSizeCallback(ctx->window, fb_callback); 371 372 #define X(name, ret, params) name = (name##_fn *)glfwGetProcAddress(#name); 373 OGLProcedureList 374 #undef X 375 376 /* NOTE: set up OpenGL debug logging */ 377 struct gl_debug_ctx *gl_debug_ctx = push_struct(&ctx->arena, typeof(*gl_debug_ctx)); 378 gl_debug_ctx->stream = stream_alloc(&ctx->arena, KB(4)); 379 gl_debug_ctx->os = &ctx->os; 380 glDebugMessageCallback(gl_debug_logger, gl_debug_ctx); 381 #ifdef _DEBUG 382 glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); 383 #endif 384 385 glEnable(GL_MULTISAMPLE); 386 glEnable(GL_DEPTH_TEST); 387 glEnable(GL_BLEND); 388 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 389 390 RenderContext *rc = &ctx->model_render_context; 391 392 RenderTarget *rt = &ctx->multisample_target; 393 rt->size = (sv2){{RENDER_TARGET_SIZE}}; 394 glCreateRenderbuffers(countof(rt->textures), rt->textures); 395 glNamedRenderbufferStorageMultisample(rt->textures[0], RENDER_MSAA_SAMPLES, 396 GL_RGBA8, RENDER_TARGET_SIZE); 397 glNamedRenderbufferStorageMultisample(rt->textures[1], RENDER_MSAA_SAMPLES, 398 GL_DEPTH_COMPONENT24, RENDER_TARGET_SIZE); 399 glCreateFramebuffers(1, &rt->fb); 400 glNamedFramebufferRenderbuffer(rt->fb, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rt->textures[0]); 401 glNamedFramebufferRenderbuffer(rt->fb, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rt->textures[1]); 402 403 rt = &ctx->output_target; 404 glCreateTextures(GL_TEXTURE_2D, countof(rt->textures), rt->textures); 405 rt->size = (sv2){{RENDER_TARGET_SIZE}}; 406 glTextureStorage2D(rt->textures[0], 8, GL_RGBA8, RENDER_TARGET_SIZE); 407 glTextureStorage2D(rt->textures[1], 1, GL_DEPTH_COMPONENT24, RENDER_TARGET_SIZE); 408 409 glCreateFramebuffers(1, &rt->fb); 410 glNamedFramebufferTexture(rt->fb, GL_COLOR_ATTACHMENT0, rt->textures[0], 0); 411 glNamedFramebufferTexture(rt->fb, GL_DEPTH_ATTACHMENT, rt->textures[1], 0); 412 413 glTextureParameteri(rt->textures[0], GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR); 414 glTextureParameteri(rt->textures[0], GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); 415 416 ShaderReloadContext *model_rc = push_struct(&ctx->arena, ShaderReloadContext); 417 model_rc->render_context = rc; 418 model_rc->vertex_text = str8("" 419 "#version 460 core\n" 420 "\n" 421 "layout(location = 0) in vec3 v_position;\n" 422 "layout(location = 1) in vec3 v_normal;\n" 423 "\n" 424 "layout(location = 0) out vec3 f_normal;\n" 425 "layout(location = 1) out vec3 f_texture_coordinate;\n" 426 "layout(location = 2) out vec3 f_orig_texture_coordinate;\n" 427 "\n" 428 "layout(location = " str(MODEL_RENDER_MODEL_MATRIX_LOC) ") uniform mat4 u_model;\n" 429 "layout(location = " str(MODEL_RENDER_VIEW_MATRIX_LOC) ") uniform mat4 u_view;\n" 430 "layout(location = " str(MODEL_RENDER_PROJ_MATRIX_LOC) ") uniform mat4 u_projection;\n" 431 "layout(location = " str(MODEL_RENDER_CLIP_FRACTION_LOC) ") uniform float u_clip_fraction = 1;\n" 432 "layout(location = " str(MODEL_RENDER_SWIZZLE_LOC) ") uniform bool u_swizzle;\n" 433 "\n" 434 "\n" 435 "void main()\n" 436 "{\n" 437 "\tvec3 pos = v_position;\n" 438 "\tf_orig_texture_coordinate = (v_position + 1) / 2;\n" 439 "\tif (v_position.y == -1) pos.x = clamp(v_position.x, -u_clip_fraction, u_clip_fraction);\n" 440 "\tvec3 tex_coord = (pos + 1) / 2;\n" 441 "\tf_texture_coordinate = u_swizzle? tex_coord.xzy : tex_coord;\n" 442 //"\tf_normal = normalize(mat3(u_model) * v_normal);\n" 443 "\tf_normal = v_normal;\n" 444 "\tgl_Position = u_projection * u_view * u_model * vec4(pos, 1);\n" 445 "}\n"); 446 447 model_rc->fragment_header = str8("" 448 "#version 460 core\n\n" 449 "layout(location = 0) in vec3 normal;\n" 450 "layout(location = 1) in vec3 texture_coordinate;\n\n" 451 "layout(location = 2) in vec3 test_texture_coordinate;\n\n" 452 "layout(location = 0) out vec4 out_colour;\n\n" 453 "layout(location = " str(MODEL_RENDER_DYNAMIC_RANGE_LOC) ") uniform float u_db_cutoff = 60;\n" 454 "layout(location = " str(MODEL_RENDER_THRESHOLD_LOC) ") uniform float u_threshold = 40;\n" 455 "layout(location = " str(MODEL_RENDER_GAMMA_LOC) ") uniform float u_gamma = 1;\n" 456 "layout(location = " str(MODEL_RENDER_LOG_SCALE_LOC) ") uniform bool u_log_scale;\n" 457 "layout(location = " str(MODEL_RENDER_BB_COLOUR_LOC) ") uniform vec4 u_bb_colour = vec4(" str(BOUNDING_BOX_COLOUR) ");\n" 458 "layout(location = " str(MODEL_RENDER_BB_FRACTION_LOC) ") uniform float u_bb_fraction = " str(BOUNDING_BOX_FRACTION) ";\n" 459 "layout(location = " str(MODEL_RENDER_GAIN_LOC) ") uniform float u_gain = 1.0f;\n" 460 "\n" 461 "layout(binding = 0) uniform sampler3D u_texture;\n" 462 "\n#line 1\n"); 463 464 str8 render_model = str8("render_model.frag.glsl"); 465 reload_shader(&ctx->os, render_model, (sptr)model_rc, ctx->arena); 466 os_add_file_watch(&ctx->os, &ctx->arena, render_model, reload_shader, (sptr)model_rc); 467 468 rc = &ctx->overlay_render_context; 469 ShaderReloadContext *overlay_rc = push_struct(&ctx->arena, ShaderReloadContext); 470 overlay_rc->render_context = rc; 471 overlay_rc->vertex_text = str8("" 472 "#version 460 core\n" 473 "\n" 474 "layout(location = 0) in vec2 v_position;\n" 475 "layout(location = 1) in vec2 v_texture_coordinate;\n" 476 "layout(location = 2) in vec3 v_colour;\n" 477 "layout(location = 3) in uint v_flags;\n" 478 "\n" 479 "layout(location = 0) out vec2 f_texture_coordinate;\n" 480 "layout(location = 1) out vec3 f_colour;\n" 481 "layout(location = 2) out uint f_flags;\n" 482 "\n" 483 "layout(location = 0) uniform ivec2 u_screen_size;\n" 484 "\n" 485 "void main()\n" 486 "{\n" 487 "\tf_texture_coordinate = v_texture_coordinate;\n" 488 "\tf_colour = v_colour;\n" 489 "\tf_flags = v_flags;\n" 490 "\tgl_Position = vec4(v_position, 0, 1);\n" 491 //"\tgl_Position = vec4(2 * (v_position / vec2(u_screen_size)) - 1, 0, 1);\n" 492 "}\n"); 493 494 overlay_rc->fragment_header = str8("" 495 "#version 460 core\n\n" 496 "layout(location = 0) in vec2 texture_coordinate;\n" 497 "layout(location = 1) in vec3 colour;\n" 498 "layout(location = 0) out vec4 out_colour;\n" 499 "\n#line 1\n"); 500 501 f32 overlay_vertices[] = { 502 -1, 1, 0, 0, 503 -1, -1, 0, 1, 504 1, -1, 1, 1, 505 -1, 1, 0, 0, 506 1, -1, 1, 1, 507 1, 1, 1, 0, 508 }; 509 glCreateVertexArrays(1, &rc->vao); 510 glCreateBuffers(1, &rc->vbo); 511 512 glNamedBufferData(rc->vbo, sizeof(overlay_vertices), overlay_vertices, GL_STATIC_DRAW); 513 514 glEnableVertexArrayAttrib(rc->vao, 0); 515 glEnableVertexArrayAttrib(rc->vao, 1); 516 glVertexArrayVertexBuffer(rc->vao, 0, rc->vbo, 0, 4 * sizeof(f32)); 517 glVertexArrayVertexBuffer(rc->vao, 1, rc->vbo, 2 * sizeof(f32), 4 * sizeof(f32)); 518 glVertexArrayAttribFormat(rc->vao, 0, 2, GL_FLOAT, 0, 0); 519 glVertexArrayAttribFormat(rc->vao, 1, 2, GL_FLOAT, 0, 2 * sizeof(f32)); 520 glVertexArrayAttribBinding(rc->vao, 0, 0); 521 glVertexArrayAttribBinding(rc->vao, 1, 0); 522 523 str8 render_overlay = str8("render_overlay.frag.glsl"); 524 reload_shader(&ctx->os, render_overlay, (sptr)overlay_rc, ctx->arena); 525 os_add_file_watch(&ctx->os, &ctx->arena, render_overlay, reload_shader, (sptr)overlay_rc); 526 527 f32 unit_cube_vertices[] = { 528 1.0f, 1.0f, -1.0f, 529 1.0f, 1.0f, -1.0f, 530 1.0f, 1.0f, -1.0f, 531 1.0f, -1.0f, -1.0f, 532 1.0f, -1.0f, -1.0f, 533 1.0f, -1.0f, -1.0f, 534 1.0f, 1.0f, 1.0f, 535 1.0f, 1.0f, 1.0f, 536 1.0f, 1.0f, 1.0f, 537 1.0f, -1.0f, 1.0f, 538 1.0f, -1.0f, 1.0f, 539 1.0f, -1.0f, 1.0f, 540 -1.0f, 1.0f, -1.0f, 541 -1.0f, 1.0f, -1.0f, 542 -1.0f, 1.0f, -1.0f, 543 -1.0f, -1.0f, -1.0f, 544 -1.0f, -1.0f, -1.0f, 545 -1.0f, -1.0f, -1.0f, 546 -1.0f, 1.0f, 1.0f, 547 -1.0f, 1.0f, 1.0f, 548 -1.0f, 1.0f, 1.0f, 549 -1.0f, -1.0f, 1.0f, 550 -1.0f, -1.0f, 1.0f, 551 -1.0f, -1.0f, 1.0f 552 }; 553 f32 unit_cube_normals[] = { 554 0.0f, 0.0f, -1.0f, 555 0.0f, 1.0f, 0.0f, 556 1.0f, 0.0f, 0.0f, 557 0.0f, 0.0f, -1.0f, 558 0.0f, -1.0f, 0.0f, 559 1.0f, 0.0f, 0.0f, 560 0.0f, 0.0f, 1.0f, 561 0.0f, 1.0f, 0.0f, 562 1.0f, 0.0f, 0.0f, 563 0.0f, 0.0f, 1.0f, 564 0.0f, -1.0f, 0.0f, 565 1.0f, 0.0f, 0.0f, 566 0.0f, 0.0f, -1.0f, 567 0.0f, 1.0f, 0.0f, 568 -1.0f, 0.0f, 0.0f, 569 0.0f, 0.0f, -1.0f, 570 0.0f, -1.0f, 0.0f, 571 -1.0f, 0.0f, 0.0f, 572 0.0f, 0.0f, 1.0f, 573 0.0f, 1.0f, 0.0f, 574 -1.0f, 0.0f, 0.0f, 575 0.0f, 0.0f, 1.0f, 576 0.0f, -1.0f, 0.0f, 577 -1.0f, 0.0f, 0.0f 578 }; 579 u16 unit_cube_indices[] = { 580 1, 13, 19, 581 1, 19, 7, 582 9, 6, 18, 583 9, 18, 21, 584 23, 20, 14, 585 23, 14, 17, 586 16, 4, 10, 587 16, 10, 22, 588 5, 2, 8, 589 5, 8, 11, 590 15, 12, 0, 591 15, 0, 3 592 }; 593 594 ctx->unit_cube = render_model_from_arrays(unit_cube_vertices, unit_cube_normals, 595 unit_cube_indices, countof(unit_cube_indices)); 596 } 597 598 function void 599 set_camera(u32 program, u32 location, v3 position, v3 normal, v3 orthogonal) 600 { 601 v3 right = cross(orthogonal, normal); 602 v3 up = cross(normal, right); 603 604 v3 translate; 605 position = v3_sub((v3){0}, position); 606 translate.x = v3_dot(position, right); 607 translate.y = v3_dot(position, up); 608 translate.z = v3_dot(position, normal); 609 610 m4 transform; 611 transform.c[0] = (v4){{right.x, up.x, normal.x, 0}}; 612 transform.c[1] = (v4){{right.y, up.y, normal.y, 0}}; 613 transform.c[2] = (v4){{right.z, up.z, normal.z, 0}}; 614 transform.c[3] = (v4){{translate.x, translate.y, translate.z, 1}}; 615 glProgramUniformMatrix4fv(program, location, 1, 0, transform.E); 616 } 617 618 function void 619 draw_volume_item(ViewerContext *ctx, VolumeDisplayItem *v, f32 rotation, f32 translate_x) 620 { 621 if (!v->texture) { 622 v->texture = load_complex_texture(ctx->arena, v->file_path, 623 v->width, v->height, v->depth); 624 } 625 626 u32 program = ctx->model_render_context.shader; 627 v3 scale = v3_sub(v->max_coord_mm, v->min_coord_mm); 628 m4 S; 629 S.c[0] = (v4){{scale.x, 0, 0, 0}}; 630 S.c[1] = (v4){{0, scale.z, 0, 0}}; 631 S.c[2] = (v4){{0, 0, scale.y, 0}}; 632 S.c[3] = (v4){{0, 0, 0, 1}}; 633 634 m4 T; 635 T.c[0] = (v4){{1, 0, 0, translate_x}}; 636 T.c[1] = (v4){{0, 1, 0, 0}}; 637 T.c[2] = (v4){{0, 0, 1, 0}}; 638 T.c[3] = (v4){{0, 0, 0, 1}}; 639 640 f32 sa = sin_f32(rotation); 641 f32 ca = cos_f32(rotation); 642 m4 R; 643 R.c[0] = (v4){{ ca, 0, sa, 0}}; 644 R.c[1] = (v4){{ 0, 1, 0, 0}}; 645 R.c[2] = (v4){{-sa, 0, ca, 0}}; 646 R.c[3] = (v4){{ 0, 0, 0, 1}}; 647 648 m4 model_transform = m4_mul(m4_mul(R, S), T); 649 glProgramUniformMatrix4fv(program, MODEL_RENDER_MODEL_MATRIX_LOC, 1, 0, model_transform.E); 650 651 glProgramUniform1f(program, MODEL_RENDER_CLIP_FRACTION_LOC, 1 - v->clip_fraction); 652 glProgramUniform1f(program, MODEL_RENDER_THRESHOLD_LOC, v->threshold); 653 glProgramUniform1f(program, MODEL_RENDER_GAIN_LOC, v->gain); 654 glProgramUniform1ui(program, MODEL_RENDER_SWIZZLE_LOC, v->swizzle); 655 656 glBindTextureUnit(0, v->texture); 657 glBindVertexArray(ctx->unit_cube.vao); 658 glDrawElements(GL_TRIANGLES, ctx->unit_cube.elements, GL_UNSIGNED_SHORT, 659 (void *)ctx->unit_cube.elements_offset); 660 } 661 662 function void 663 update_scene(ViewerContext *ctx, f32 dt) 664 { 665 ctx->cycle_t += CYCLE_T_UPDATE_SPEED * dt; 666 if (ctx->cycle_t > 1) ctx->cycle_t -= 1; 667 668 f32 angle = ctx->cycle_t * 2 * PI; 669 ctx->camera_position.x = 0; 670 ctx->camera_position.z = -ctx->camera_radius; 671 ctx->camera_position.y = ctx->camera_radius * tan_f32(ctx->camera_angle); 672 673 RenderTarget *rt = &ctx->multisample_target; 674 f32 one = 1; 675 glBindFramebuffer(GL_FRAMEBUFFER, rt->fb); 676 glClearNamedFramebufferfv(rt->fb, GL_COLOR, 0, OUTPUT_BG_CLEAR_COLOUR.E); 677 glClearNamedFramebufferfv(rt->fb, GL_DEPTH, 0, &one); 678 glViewport(0, 0, rt->size.w, rt->size.h); 679 680 u32 program = ctx->model_render_context.shader; 681 glUseProgram(program); 682 683 /* TODO(rnp): set this on hot reload instead of every frame */ 684 v2 points = {{RENDER_TARGET_SIZE}}; 685 f32 n = 0.1f; 686 f32 f = 400.0f; 687 f32 r = n * tan_f32(ctx->camera_fov / 2 * PI / 180.0f); 688 f32 t = r * points.h / points.w; 689 f32 a = -(f + n) / (f - n); 690 f32 b = -2 * f * n / (f - n); 691 692 m4 projection; 693 projection.c[0] = (v4){{n / r, 0, 0, 0}}; 694 projection.c[1] = (v4){{0, n / t, 0, 0}}; 695 projection.c[2] = (v4){{0, 0, a, -1}}; 696 projection.c[3] = (v4){{0, 0, b, 0}}; 697 glProgramUniformMatrix4fv(program, MODEL_RENDER_PROJ_MATRIX_LOC, 1, 0, projection.E); 698 699 v3 camera = ctx->camera_position; 700 set_camera(program, MODEL_RENDER_VIEW_MATRIX_LOC, camera, 701 v3_normalize(v3_sub(camera, (v3){0})), (v3){{0, 1, 0}}); 702 703 glProgramUniform1ui(program, MODEL_RENDER_LOG_SCALE_LOC, LOG_SCALE); 704 glProgramUniform1f(program, MODEL_RENDER_DYNAMIC_RANGE_LOC, DYNAMIC_RANGE); 705 706 #if DRAW_ALL_VOLUMES 707 for (u32 i = 0; i < countof(volumes); i++) 708 draw_volume_item(ctx, volumes + i, angle, volumes[i].translate_x); 709 #else 710 draw_volume_item(ctx, volumes + single_volume_index, angle, 0); 711 #endif 712 713 /* NOTE(rnp): resolve multisampled scene */ 714 glBlitNamedFramebuffer(rt->fb, ctx->output_target.fb, 0, 0, rt->size.w, rt->size.h, 715 0, 0, rt->size.w, rt->size.h, GL_COLOR_BUFFER_BIT, GL_NEAREST); 716 717 glGenerateTextureMipmap(ctx->output_target.textures[0]); 718 } 719 720 function void 721 viewer_frame_step(ViewerContext *ctx, f32 dt) 722 { 723 if (ctx->do_update) { 724 update_scene(ctx, dt); 725 if (ctx->output_frames_count) { 726 u32 frame_index = TOTAL_OUTPUT_FRAMES - ctx->output_frames_count--; 727 printf("Reading Frame: [%u/%u]\n", frame_index, (u32)TOTAL_OUTPUT_FRAMES - 1); 728 sz needed = ctx->output_target.size.w * ctx->output_target.size.h * sizeof(u32); 729 glGetTextureImage(ctx->output_target.textures[0], 0, GL_RGBA, 730 GL_UNSIGNED_INT_8_8_8_8, needed, 731 ctx->video_arena.beg + ctx->video_arena_offset); 732 ctx->video_arena_offset += needed; 733 if (!ctx->output_frames_count) { 734 str8 raw = {.len = TOTAL_OUTPUT_FRAMES * needed, 735 .data = ctx->video_arena.beg}; 736 ctx->video_arena_offset = 0; 737 os_write_new_file(RAW_OUTPUT_PATH, raw); 738 } 739 } 740 ctx->do_update = 0; 741 } 742 743 //////////////// 744 // UI Overlay 745 f32 one = 1; 746 glBindFramebuffer(GL_FRAMEBUFFER, 0); 747 glClearNamedFramebufferfv(0, GL_COLOR, 0, BG_CLEAR_COLOUR.E); 748 glClearNamedFramebufferfv(0, GL_DEPTH, 0, &one); 749 750 f32 aspect_ratio = (f32)ctx->output_target.size.w / (f32)ctx->output_target.size.h; 751 sv2 target_size = ctx->window_size; 752 if (aspect_ratio > 1) target_size.h = target_size.w / aspect_ratio; 753 else target_size.w = target_size.h * aspect_ratio; 754 755 if (target_size.w > ctx->window_size.w) { 756 target_size.w = ctx->window_size.w; 757 target_size.h = ctx->window_size.w / aspect_ratio; 758 } else if (target_size.h > ctx->window_size.h) { 759 target_size.h = ctx->window_size.h; 760 target_size.w = target_size.h * aspect_ratio; 761 } 762 763 sv2 size_delta = sv2_sub(ctx->window_size, target_size); 764 765 glViewport(size_delta.x / 2 + 0.5, size_delta.y / 2 + 0.5, target_size.w, target_size.h); 766 767 glUseProgram(ctx->overlay_render_context.shader); 768 glBindTextureUnit(0, ctx->output_target.textures[0]); 769 glBindVertexArray(ctx->overlay_render_context.vao); 770 glDrawArrays(GL_TRIANGLES, 0, 6); 771 772 ctx->should_exit |= glfwWindowShouldClose(ctx->window); 773 }