common.c (24310B)
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 void 220 scroll_callback(GLFWwindow *window, f64 x, f64 y) 221 { 222 ViewerContext *ctx = glfwGetWindowUserPointer(window); 223 ctx->camera_fov += y; 224 ctx->do_update = 1; 225 } 226 227 function void 228 key_callback(GLFWwindow *window, s32 key, s32 scancode, s32 action, s32 modifiers) 229 { 230 ViewerContext *ctx = glfwGetWindowUserPointer(window); 231 if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) 232 ctx->should_exit = 1; 233 234 if (key == GLFW_KEY_SPACE && action == GLFW_PRESS) 235 ctx->demo_mode = !ctx->demo_mode; 236 237 if (key == GLFW_KEY_F12 && action == GLFW_PRESS && ctx->output_frames_count == 0) { 238 sz frames = TOTAL_OUTPUT_FRAMES; 239 sz needed_bytes = sizeof(u32) * RENDER_TARGET_HEIGHT * RENDER_TARGET_WIDTH * frames; 240 if (!ctx->video_arena.beg) { 241 ctx->video_arena = os_alloc_arena(needed_bytes); 242 if (!ctx->video_arena.beg) { 243 fputs("failed to allocate space for output video, video " 244 "won't be saved\n", stderr); 245 } 246 } 247 if (ctx->video_arena.beg) { 248 ctx->output_frames_count = TOTAL_OUTPUT_FRAMES; 249 ctx->cycle_t = 0; 250 } 251 } 252 253 if (key == GLFW_KEY_A && action != GLFW_RELEASE) 254 ctx->cycle_t += 4.0f / (OUTPUT_TIME_SECONDS * OUTPUT_FRAME_RATE); 255 if (key == GLFW_KEY_D && action != GLFW_RELEASE) 256 ctx->cycle_t -= 4.0f / (OUTPUT_TIME_SECONDS * OUTPUT_FRAME_RATE); 257 if (key == GLFW_KEY_W && action != GLFW_RELEASE) 258 ctx->camera_angle += 5 * PI / 180.0f; 259 if (key == GLFW_KEY_S && action != GLFW_RELEASE) 260 ctx->camera_angle -= 5 * PI / 180.0f; 261 262 ctx->do_update = 1; 263 } 264 265 function void 266 fb_callback(GLFWwindow *window, s32 w, s32 h) 267 { 268 ViewerContext *ctx = glfwGetWindowUserPointer(window); 269 ctx->window_size = (sv2){.w = w, .h = h}; 270 } 271 272 function void 273 init_viewer(ViewerContext *ctx) 274 { 275 ctx->demo_mode = 1; 276 ctx->window_size = (sv2){.w = 640, .h = 640}; 277 ctx->camera_radius = CAMERA_RADIUS; 278 ctx->camera_angle = -CAMERA_ELEVATION_ANGLE * PI / 180.0f; 279 ctx->camera_fov = 60.0f; 280 281 if (!glfwInit()) os_fatal(str8("failed to start glfw\n")); 282 283 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); 284 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6); 285 glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); 286 ctx->window = glfwCreateWindow(ctx->window_size.w, ctx->window_size.h, "3D Viewer", 0, 0); 287 if (!ctx->window) os_fatal(str8("failed to open window\n")); 288 glfwMakeContextCurrent(ctx->window); 289 glfwSetWindowUserPointer(ctx->window, ctx); 290 glfwSwapInterval(1); 291 292 glfwSetKeyCallback(ctx->window, key_callback); 293 glfwSetScrollCallback(ctx->window, scroll_callback); 294 glfwSetFramebufferSizeCallback(ctx->window, fb_callback); 295 296 #define X(name, ret, params) name = (name##_fn *)glfwGetProcAddress(#name); 297 OGLProcedureList 298 #undef X 299 300 /* NOTE: set up OpenGL debug logging */ 301 struct gl_debug_ctx *gl_debug_ctx = push_struct(&ctx->arena, typeof(*gl_debug_ctx)); 302 gl_debug_ctx->stream = stream_alloc(&ctx->arena, KB(4)); 303 gl_debug_ctx->os = &ctx->os; 304 glDebugMessageCallback(gl_debug_logger, gl_debug_ctx); 305 #ifdef _DEBUG 306 glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); 307 #endif 308 309 glEnable(GL_MULTISAMPLE); 310 glEnable(GL_DEPTH_TEST); 311 glEnable(GL_BLEND); 312 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 313 314 RenderContext *rc = &ctx->model_render_context; 315 316 RenderTarget *rt = &ctx->multisample_target; 317 rt->size = (sv2){{RENDER_TARGET_SIZE}}; 318 glCreateRenderbuffers(countof(rt->textures), rt->textures); 319 glNamedRenderbufferStorageMultisample(rt->textures[0], RENDER_MSAA_SAMPLES, 320 GL_RGBA8, RENDER_TARGET_SIZE); 321 glNamedRenderbufferStorageMultisample(rt->textures[1], RENDER_MSAA_SAMPLES, 322 GL_DEPTH_COMPONENT24, RENDER_TARGET_SIZE); 323 glCreateFramebuffers(1, &rt->fb); 324 glNamedFramebufferRenderbuffer(rt->fb, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rt->textures[0]); 325 glNamedFramebufferRenderbuffer(rt->fb, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rt->textures[1]); 326 327 rt = &ctx->output_target; 328 glCreateTextures(GL_TEXTURE_2D, countof(rt->textures), rt->textures); 329 rt->size = (sv2){{RENDER_TARGET_SIZE}}; 330 glTextureStorage2D(rt->textures[0], 8, GL_RGBA8, RENDER_TARGET_SIZE); 331 glTextureStorage2D(rt->textures[1], 1, GL_DEPTH_COMPONENT24, RENDER_TARGET_SIZE); 332 333 glCreateFramebuffers(1, &rt->fb); 334 glNamedFramebufferTexture(rt->fb, GL_COLOR_ATTACHMENT0, rt->textures[0], 0); 335 glNamedFramebufferTexture(rt->fb, GL_DEPTH_ATTACHMENT, rt->textures[1], 0); 336 337 glTextureParameteri(rt->textures[0], GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR); 338 glTextureParameteri(rt->textures[0], GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); 339 340 ShaderReloadContext *model_rc = push_struct(&ctx->arena, ShaderReloadContext); 341 model_rc->render_context = rc; 342 model_rc->vertex_text = str8("" 343 "#version 460 core\n" 344 "\n" 345 "layout(location = 0) in vec3 v_position;\n" 346 "layout(location = 1) in vec3 v_normal;\n" 347 "\n" 348 "layout(location = 0) out vec3 f_normal;\n" 349 "layout(location = 1) out vec3 f_texture_coordinate;\n" 350 "layout(location = 2) out vec3 f_orig_texture_coordinate;\n" 351 "\n" 352 "layout(location = " str(MODEL_RENDER_MODEL_MATRIX_LOC) ") uniform mat4 u_model;\n" 353 "layout(location = " str(MODEL_RENDER_VIEW_MATRIX_LOC) ") uniform mat4 u_view;\n" 354 "layout(location = " str(MODEL_RENDER_PROJ_MATRIX_LOC) ") uniform mat4 u_projection;\n" 355 "layout(location = " str(MODEL_RENDER_CLIP_FRACTION_LOC) ") uniform float u_clip_fraction = 1;\n" 356 "layout(location = " str(MODEL_RENDER_SWIZZLE_LOC) ") uniform bool u_swizzle;\n" 357 "\n" 358 "\n" 359 "void main()\n" 360 "{\n" 361 "\tvec3 pos = v_position;\n" 362 "\tf_orig_texture_coordinate = (v_position + 1) / 2;\n" 363 "\tif (v_position.y == -1) pos.x = clamp(v_position.x, -u_clip_fraction, u_clip_fraction);\n" 364 "\tvec3 tex_coord = (pos + 1) / 2;\n" 365 "\tf_texture_coordinate = u_swizzle? tex_coord.xzy : tex_coord;\n" 366 //"\tf_normal = normalize(mat3(u_model) * v_normal);\n" 367 "\tf_normal = v_normal;\n" 368 "\tgl_Position = u_projection * u_view * u_model * vec4(pos, 1);\n" 369 "}\n"); 370 371 model_rc->fragment_header = str8("" 372 "#version 460 core\n\n" 373 "layout(location = 0) in vec3 normal;\n" 374 "layout(location = 1) in vec3 texture_coordinate;\n\n" 375 "layout(location = 2) in vec3 test_texture_coordinate;\n\n" 376 "layout(location = 0) out vec4 out_colour;\n\n" 377 "layout(location = " str(MODEL_RENDER_DYNAMIC_RANGE_LOC) ") uniform float u_db_cutoff = 60;\n" 378 "layout(location = " str(MODEL_RENDER_THRESHOLD_LOC) ") uniform float u_threshold = 40;\n" 379 "layout(location = " str(MODEL_RENDER_GAMMA_LOC) ") uniform float u_gamma = 1;\n" 380 "layout(location = " str(MODEL_RENDER_LOG_SCALE_LOC) ") uniform bool u_log_scale;\n" 381 "layout(location = " str(MODEL_RENDER_BB_COLOUR_LOC) ") uniform vec4 u_bb_colour = vec4(" str(BOUNDING_BOX_COLOUR) ");\n" 382 "layout(location = " str(MODEL_RENDER_BB_FRACTION_LOC) ") uniform float u_bb_fraction = " str(BOUNDING_BOX_FRACTION) ";\n" 383 "layout(location = " str(MODEL_RENDER_GAIN_LOC) ") uniform float u_gain = 1.0f;\n" 384 "\n" 385 "layout(binding = 0) uniform sampler3D u_texture;\n" 386 "\n#line 1\n"); 387 388 str8 render_model = str8("render_model.frag.glsl"); 389 reload_shader(&ctx->os, render_model, (sptr)model_rc, ctx->arena); 390 os_add_file_watch(&ctx->os, &ctx->arena, render_model, reload_shader, (sptr)model_rc); 391 392 rc = &ctx->overlay_render_context; 393 ShaderReloadContext *overlay_rc = push_struct(&ctx->arena, ShaderReloadContext); 394 overlay_rc->render_context = rc; 395 overlay_rc->vertex_text = str8("" 396 "#version 460 core\n" 397 "\n" 398 "layout(location = 0) in vec2 v_position;\n" 399 "layout(location = 1) in vec2 v_texture_coordinate;\n" 400 "layout(location = 2) in vec3 v_colour;\n" 401 "layout(location = 3) in uint v_flags;\n" 402 "\n" 403 "layout(location = 0) out vec2 f_texture_coordinate;\n" 404 "layout(location = 1) out vec3 f_colour;\n" 405 "layout(location = 2) out uint f_flags;\n" 406 "\n" 407 "layout(location = 0) uniform ivec2 u_screen_size;\n" 408 "\n" 409 "void main()\n" 410 "{\n" 411 "\tf_texture_coordinate = v_texture_coordinate;\n" 412 "\tf_colour = v_colour;\n" 413 "\tf_flags = v_flags;\n" 414 "\tgl_Position = vec4(v_position, 0, 1);\n" 415 //"\tgl_Position = vec4(2 * (v_position / vec2(u_screen_size)) - 1, 0, 1);\n" 416 "}\n"); 417 418 overlay_rc->fragment_header = str8("" 419 "#version 460 core\n\n" 420 "layout(location = 0) in vec2 texture_coordinate;\n" 421 "layout(location = 1) in vec3 colour;\n" 422 "layout(location = 0) out vec4 out_colour;\n" 423 "\n#line 1\n"); 424 425 f32 overlay_vertices[] = { 426 -1, 1, 0, 0, 427 -1, -1, 0, 1, 428 1, -1, 1, 1, 429 -1, 1, 0, 0, 430 1, -1, 1, 1, 431 1, 1, 1, 0, 432 }; 433 glCreateVertexArrays(1, &rc->vao); 434 glCreateBuffers(1, &rc->vbo); 435 436 glNamedBufferData(rc->vbo, sizeof(overlay_vertices), overlay_vertices, GL_STATIC_DRAW); 437 438 glEnableVertexArrayAttrib(rc->vao, 0); 439 glEnableVertexArrayAttrib(rc->vao, 1); 440 glVertexArrayVertexBuffer(rc->vao, 0, rc->vbo, 0, 4 * sizeof(f32)); 441 glVertexArrayVertexBuffer(rc->vao, 1, rc->vbo, 2 * sizeof(f32), 4 * sizeof(f32)); 442 glVertexArrayAttribFormat(rc->vao, 0, 2, GL_FLOAT, 0, 0); 443 glVertexArrayAttribFormat(rc->vao, 1, 2, GL_FLOAT, 0, 2 * sizeof(f32)); 444 glVertexArrayAttribBinding(rc->vao, 0, 0); 445 glVertexArrayAttribBinding(rc->vao, 1, 0); 446 447 str8 render_overlay = str8("render_overlay.frag.glsl"); 448 reload_shader(&ctx->os, render_overlay, (sptr)overlay_rc, ctx->arena); 449 os_add_file_watch(&ctx->os, &ctx->arena, render_overlay, reload_shader, (sptr)overlay_rc); 450 451 f32 unit_cube_vertices[] = { 452 1.0f, 1.0f, -1.0f, 453 1.0f, 1.0f, -1.0f, 454 1.0f, 1.0f, -1.0f, 455 1.0f, -1.0f, -1.0f, 456 1.0f, -1.0f, -1.0f, 457 1.0f, -1.0f, -1.0f, 458 1.0f, 1.0f, 1.0f, 459 1.0f, 1.0f, 1.0f, 460 1.0f, 1.0f, 1.0f, 461 1.0f, -1.0f, 1.0f, 462 1.0f, -1.0f, 1.0f, 463 1.0f, -1.0f, 1.0f, 464 -1.0f, 1.0f, -1.0f, 465 -1.0f, 1.0f, -1.0f, 466 -1.0f, 1.0f, -1.0f, 467 -1.0f, -1.0f, -1.0f, 468 -1.0f, -1.0f, -1.0f, 469 -1.0f, -1.0f, -1.0f, 470 -1.0f, 1.0f, 1.0f, 471 -1.0f, 1.0f, 1.0f, 472 -1.0f, 1.0f, 1.0f, 473 -1.0f, -1.0f, 1.0f, 474 -1.0f, -1.0f, 1.0f, 475 -1.0f, -1.0f, 1.0f 476 }; 477 f32 unit_cube_normals[] = { 478 0.0f, 0.0f, -1.0f, 479 0.0f, 1.0f, 0.0f, 480 1.0f, 0.0f, 0.0f, 481 0.0f, 0.0f, -1.0f, 482 0.0f, -1.0f, 0.0f, 483 1.0f, 0.0f, 0.0f, 484 0.0f, 0.0f, 1.0f, 485 0.0f, 1.0f, 0.0f, 486 1.0f, 0.0f, 0.0f, 487 0.0f, 0.0f, 1.0f, 488 0.0f, -1.0f, 0.0f, 489 1.0f, 0.0f, 0.0f, 490 0.0f, 0.0f, -1.0f, 491 0.0f, 1.0f, 0.0f, 492 -1.0f, 0.0f, 0.0f, 493 0.0f, 0.0f, -1.0f, 494 0.0f, -1.0f, 0.0f, 495 -1.0f, 0.0f, 0.0f, 496 0.0f, 0.0f, 1.0f, 497 0.0f, 1.0f, 0.0f, 498 -1.0f, 0.0f, 0.0f, 499 0.0f, 0.0f, 1.0f, 500 0.0f, -1.0f, 0.0f, 501 -1.0f, 0.0f, 0.0f 502 }; 503 u16 unit_cube_indices[] = { 504 1, 13, 19, 505 1, 19, 7, 506 9, 6, 18, 507 9, 18, 21, 508 23, 20, 14, 509 23, 14, 17, 510 16, 4, 10, 511 16, 10, 22, 512 5, 2, 8, 513 5, 8, 11, 514 15, 12, 0, 515 15, 0, 3 516 }; 517 518 ctx->unit_cube = render_model_from_arrays(unit_cube_vertices, unit_cube_normals, 519 unit_cube_indices, countof(unit_cube_indices)); 520 } 521 522 function void 523 set_camera(u32 program, u32 location, v3 position, v3 normal, v3 orthogonal) 524 { 525 v3 right = cross(orthogonal, normal); 526 v3 up = cross(normal, right); 527 528 v3 translate; 529 position = v3_sub((v3){0}, position); 530 translate.x = v3_dot(position, right); 531 translate.y = v3_dot(position, up); 532 translate.z = v3_dot(position, normal); 533 534 m4 transform; 535 transform.c[0] = (v4){{right.x, up.x, normal.x, 0}}; 536 transform.c[1] = (v4){{right.y, up.y, normal.y, 0}}; 537 transform.c[2] = (v4){{right.z, up.z, normal.z, 0}}; 538 transform.c[3] = (v4){{translate.x, translate.y, translate.z, 1}}; 539 glProgramUniformMatrix4fv(program, location, 1, 0, transform.E); 540 } 541 542 function void 543 draw_volume_item(ViewerContext *ctx, VolumeDisplayItem *v, f32 rotation, f32 translate_x) 544 { 545 if (!v->texture) { 546 v->texture = load_complex_texture(ctx->arena, v->file_path, 547 v->width, v->height, v->depth); 548 } 549 550 u32 program = ctx->model_render_context.shader; 551 v3 scale = v3_sub(v->max_coord_mm, v->min_coord_mm); 552 m4 S; 553 S.c[0] = (v4){{scale.x, 0, 0, 0}}; 554 S.c[1] = (v4){{0, scale.z, 0, 0}}; 555 S.c[2] = (v4){{0, 0, scale.y, 0}}; 556 S.c[3] = (v4){{0, 0, 0, 1}}; 557 558 m4 T; 559 T.c[0] = (v4){{1, 0, 0, translate_x}}; 560 T.c[1] = (v4){{0, 1, 0, 0}}; 561 T.c[2] = (v4){{0, 0, 1, 0}}; 562 T.c[3] = (v4){{0, 0, 0, 1}}; 563 564 f32 sa = sin_f32(rotation); 565 f32 ca = cos_f32(rotation); 566 m4 R; 567 R.c[0] = (v4){{ ca, 0, sa, 0}}; 568 R.c[1] = (v4){{ 0, 1, 0, 0}}; 569 R.c[2] = (v4){{-sa, 0, ca, 0}}; 570 R.c[3] = (v4){{ 0, 0, 0, 1}}; 571 572 m4 model_transform = m4_mul(m4_mul(R, S), T); 573 glProgramUniformMatrix4fv(program, MODEL_RENDER_MODEL_MATRIX_LOC, 1, 0, model_transform.E); 574 575 glProgramUniform1f(program, MODEL_RENDER_CLIP_FRACTION_LOC, 1 - v->clip_fraction); 576 glProgramUniform1f(program, MODEL_RENDER_THRESHOLD_LOC, v->threshold); 577 glProgramUniform1f(program, MODEL_RENDER_GAIN_LOC, v->gain); 578 glProgramUniform1ui(program, MODEL_RENDER_SWIZZLE_LOC, v->swizzle); 579 580 glBindTextureUnit(0, v->texture); 581 glBindVertexArray(ctx->unit_cube.vao); 582 glDrawElements(GL_TRIANGLES, ctx->unit_cube.elements, GL_UNSIGNED_SHORT, 583 (void *)ctx->unit_cube.elements_offset); 584 } 585 586 function void 587 update_scene(ViewerContext *ctx, f32 dt) 588 { 589 ctx->cycle_t += CYCLE_T_UPDATE_SPEED * dt; 590 if (ctx->cycle_t > 1) ctx->cycle_t -= 1; 591 592 f32 angle = ctx->cycle_t * 2 * PI; 593 ctx->camera_position.x = 0; 594 ctx->camera_position.z = -ctx->camera_radius; 595 ctx->camera_position.y = ctx->camera_radius * tan_f32(ctx->camera_angle); 596 597 RenderTarget *rt = &ctx->multisample_target; 598 f32 one = 1; 599 glBindFramebuffer(GL_FRAMEBUFFER, rt->fb); 600 glClearNamedFramebufferfv(rt->fb, GL_COLOR, 0, OUTPUT_BG_CLEAR_COLOUR.E); 601 glClearNamedFramebufferfv(rt->fb, GL_DEPTH, 0, &one); 602 glViewport(0, 0, rt->size.w, rt->size.h); 603 604 u32 program = ctx->model_render_context.shader; 605 glUseProgram(program); 606 607 /* TODO(rnp): set this on hot reload instead of every frame */ 608 v2 points = {{RENDER_TARGET_SIZE}}; 609 f32 n = 0.1f; 610 f32 f = 400.0f; 611 f32 r = n * tan_f32(ctx->camera_fov / 2 * PI / 180.0f); 612 f32 t = r * points.h / points.w; 613 f32 a = -(f + n) / (f - n); 614 f32 b = -2 * f * n / (f - n); 615 616 m4 projection; 617 projection.c[0] = (v4){{n / r, 0, 0, 0}}; 618 projection.c[1] = (v4){{0, n / t, 0, 0}}; 619 projection.c[2] = (v4){{0, 0, a, -1}}; 620 projection.c[3] = (v4){{0, 0, b, 0}}; 621 glProgramUniformMatrix4fv(program, MODEL_RENDER_PROJ_MATRIX_LOC, 1, 0, projection.E); 622 623 v3 camera = ctx->camera_position; 624 set_camera(program, MODEL_RENDER_VIEW_MATRIX_LOC, camera, 625 v3_normalize(v3_sub(camera, (v3){0})), (v3){{0, 1, 0}}); 626 627 glProgramUniform1ui(program, MODEL_RENDER_LOG_SCALE_LOC, LOG_SCALE); 628 glProgramUniform1f(program, MODEL_RENDER_DYNAMIC_RANGE_LOC, DYNAMIC_RANGE); 629 630 #if DRAW_ALL_VOLUMES 631 for (u32 i = 0; i < countof(volumes); i++) 632 draw_volume_item(ctx, volumes + i, angle, volumes[i].translate_x); 633 #else 634 draw_volume_item(ctx, volumes + single_volume_index, angle, 0); 635 #endif 636 637 /* NOTE(rnp): resolve multisampled scene */ 638 glBlitNamedFramebuffer(rt->fb, ctx->output_target.fb, 0, 0, rt->size.w, rt->size.h, 639 0, 0, rt->size.w, rt->size.h, GL_COLOR_BUFFER_BIT, GL_NEAREST); 640 641 glGenerateTextureMipmap(ctx->output_target.textures[0]); 642 } 643 644 function void 645 viewer_frame_step(ViewerContext *ctx, f32 dt) 646 { 647 if (ctx->do_update) { 648 update_scene(ctx, dt); 649 if (ctx->output_frames_count) { 650 u32 frame_index = TOTAL_OUTPUT_FRAMES - ctx->output_frames_count--; 651 printf("Reading Frame: [%u/%u]\n", frame_index, (u32)TOTAL_OUTPUT_FRAMES - 1); 652 sz needed = ctx->output_target.size.w * ctx->output_target.size.h * sizeof(u32); 653 glGetTextureImage(ctx->output_target.textures[0], 0, GL_RGBA, 654 GL_UNSIGNED_INT_8_8_8_8, needed, 655 ctx->video_arena.beg + ctx->video_arena_offset); 656 ctx->video_arena_offset += needed; 657 if (!ctx->output_frames_count) { 658 str8 raw = {.len = TOTAL_OUTPUT_FRAMES * needed, 659 .data = ctx->video_arena.beg}; 660 ctx->video_arena_offset = 0; 661 os_write_new_file(RAW_OUTPUT_PATH, raw); 662 } 663 } 664 ctx->do_update = 0; 665 } 666 667 //////////////// 668 // UI Overlay 669 f32 one = 1; 670 glBindFramebuffer(GL_FRAMEBUFFER, 0); 671 glClearNamedFramebufferfv(0, GL_COLOR, 0, BG_CLEAR_COLOUR.E); 672 glClearNamedFramebufferfv(0, GL_DEPTH, 0, &one); 673 674 f32 aspect_ratio = (f32)ctx->output_target.size.w / (f32)ctx->output_target.size.h; 675 sv2 target_size = ctx->window_size; 676 if (aspect_ratio > 1) target_size.h = target_size.w / aspect_ratio; 677 else target_size.w = target_size.h * aspect_ratio; 678 679 if (target_size.w > ctx->window_size.w) { 680 target_size.w = ctx->window_size.w; 681 target_size.h = ctx->window_size.w / aspect_ratio; 682 } else if (target_size.h > ctx->window_size.h) { 683 target_size.h = ctx->window_size.h; 684 target_size.w = target_size.h * aspect_ratio; 685 } 686 687 sv2 size_delta = sv2_sub(ctx->window_size, target_size); 688 689 glViewport(size_delta.x / 2 + 0.5, size_delta.y / 2 + 0.5, target_size.w, target_size.h); 690 691 glUseProgram(ctx->overlay_render_context.shader); 692 glBindTextureUnit(0, ctx->output_target.textures[0]); 693 glBindVertexArray(ctx->overlay_render_context.vao); 694 glDrawArrays(GL_TRIANGLES, 0, 6); 695 696 ctx->should_exit |= glfwWindowShouldClose(ctx->window); 697 }