volviewer

Volumetric Data Toy Viewer
git clone anongit@rnpnr.xyz:volviewer.git
Log | Files | Refs | Feed | LICENSE

Commit: b2a678ea2cb8f0ded3db53cf9c34a8843a1506d9
Parent: af4c8ec39796779c9fd0b93b87f8b31d216a0037
Author: Randy Palamar
Date:   Sat, 24 May 2025 22:39:20 -0600

textured cube with drawn bounding box

Diffstat:
Mcommon.c | 104+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------
Mmain_linux.c | 2+-
Mmain_w32.c | 2+-
Mrender_model.frag.glsl | 34+++++++++++++++++++---------------
Mutil.h | 14++++++++++++--
5 files changed, 113 insertions(+), 43 deletions(-)

diff --git a/common.c b/common.c @@ -3,6 +3,9 @@ #include "glad/gl.h" #include "GLFW/glfw3.h" +#include <stdio.h> +#include <stdarg.h> + #define BG_CLEAR_COLOUR (v4){{0.12, 0.1, 0.1, 1}} #define RENDER_TARGET_SIZE 1024, 1024 @@ -12,7 +15,10 @@ * the specified frame rate */ #define OUTPUT_TIME_SECONDS 8.0f #define OUTPUT_FRAME_RATE 30.0f -#define OUTPUT_BG_CLEAR_COLOUR (v4){{0.0, 0.2, 0.0, 1}} +#define OUTPUT_BG_CLEAR_COLOUR (v4){{0.0, 0.0, 0.0, 1}} + +#define BOUNDING_BOX_COLOUR 0.78, 0.07, 0.20, 1 +#define BOUNDING_BOX_FRACTION 0.005f #define MODEL_RENDER_MODEL_MATRIX_LOC (0) #define MODEL_RENDER_VIEW_MATRIX_LOC (1) @@ -21,14 +27,8 @@ #define MODEL_RENDER_DYNAMIC_RANGE_LOC (4) #define MODEL_RENDER_THRESHOLD_LOC (5) #define MODEL_RENDER_GAMMA_LOC (6) - -typedef struct { - v3 normal; - v3 position; - v3 colour; - v3 texture_coordinate; - u32 flags; -} Vertex; +#define MODEL_RENDER_BB_COLOUR_LOC (7) +#define MODEL_RENDER_BB_FRACTION_LOC (8) struct gl_debug_ctx { Stream stream; @@ -56,6 +56,21 @@ gl_debug_logger(u32 src, u32 type, u32 id, u32 lvl, s32 len, const char *msg, co stream_reset(e, 0); } +function void +stream_printf(Stream *s, const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + s32 length = vsnprintf(0, 0, format, ap); + s->errors |= (s->cap - s->widx) < (length + 1); + if (!s->errors) { + vsnprintf((char *)(s->data + s->widx), s->cap - s->widx, format, ap); + s->widx += length; + } + va_end(ap); +} + function u32 compile_shader(OS *os, Arena a, u32 type, str8 shader, str8 name) { @@ -148,6 +163,33 @@ function FILE_WATCH_CALLBACK_FN(reload_shader) return 1; } +function Texture +load_complex_texture(Arena arena, c8 *file_path, b32 multi_file, u32 width, u32 height, u32 depth) +{ + Texture result = {0}; + glCreateTextures(GL_TEXTURE_3D, 1, &result.texture); + glTextureStorage3D(result.texture, 1, GL_RG32F, width, height, depth); + glTextureParameteri(result.texture, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT); + glTextureParameteri(result.texture, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT); + + if (multi_file) { + /* NOTE(rnp): assumes single plane */ + for (u32 i = 0; i < depth; i++) { + Stream spath = arena_stream(arena); + stream_printf(&spath, file_path, i); + str8 path = arena_stream_commit_zero(&arena, &spath); + str8 raw = os_read_whole_file(&arena, (char *)path.data); + glTextureSubImage3D(result.texture, 0, 0, 0, i, width, height, 1, GL_RG, + GL_FLOAT, raw.data); + } + } else { + str8 raw = os_read_whole_file(&arena, file_path); + glTextureSubImage3D(result.texture, 0, 0, 0, 0, width, height, depth, GL_RG, + GL_FLOAT, raw.data); + } + return result; +} + function RenderModel load_render_model(Arena arena, c8 *positions_file_name, c8 *indices_file_name, c8 *normals_file_name) { @@ -217,7 +259,7 @@ init_viewer(ViewerContext *ctx) { ctx->window_size = (sv2){.w = 640, .h = 640}; ctx->camera_radius = 5; - ctx->camera_angle = CAMERA_ELEVATION_ANGLE * PI / 180.0f; + ctx->camera_angle = -CAMERA_ELEVATION_ANGLE * PI / 180.0f; if (!glfwInit()) os_fatal(str8("failed to start glfw\n")); @@ -246,6 +288,8 @@ init_viewer(ViewerContext *ctx) #endif glEnable(GL_DEPTH_TEST); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); RenderContext *rc = &ctx->model_render_context; @@ -268,6 +312,7 @@ init_viewer(ViewerContext *ctx) "layout(location = 1) in vec3 v_normal;\n" "\n" "layout(location = 0) out vec3 f_normal;\n" + "layout(location = 1) out vec3 f_texture_coordinate;\n" "\n" "layout(location = " str(MODEL_RENDER_MODEL_MATRIX_LOC) ") uniform mat4 u_model;\n" "layout(location = " str(MODEL_RENDER_VIEW_MATRIX_LOC) ") uniform mat4 u_view;\n" @@ -276,6 +321,7 @@ init_viewer(ViewerContext *ctx) "\n" "void main()\n" "{\n" + "\tf_texture_coordinate = (v_position + 1) / 2;\n" //"\tf_normal = normalize(mat3(u_model) * v_normal);\n" "\tf_normal = v_normal;\n" "\tgl_Position = u_projection * u_view * u_model * vec4(v_position, 1);\n" @@ -283,30 +329,22 @@ init_viewer(ViewerContext *ctx) model_rc->fragment_header = str8("" "#version 460 core\n\n" - "layout(location = 0) in vec3 normal;\n\n" + "layout(location = 0) in vec3 normal;\n" + "layout(location = 1) in vec3 texture_coordinate;\n\n" "layout(location = 0) out vec4 out_colour;\n\n" "layout(location = " str(MODEL_RENDER_DYNAMIC_RANGE_LOC) ") uniform float u_db_cutoff = 60;\n" "layout(location = " str(MODEL_RENDER_THRESHOLD_LOC) ") uniform float u_threshold = 40;\n" "layout(location = " str(MODEL_RENDER_GAMMA_LOC) ") uniform float u_gamma = 1;\n" "layout(location = " str(MODEL_RENDER_LOG_SCALE_LOC) ") uniform bool u_log_scale;\n" + "layout(location = " str(MODEL_RENDER_BB_COLOUR_LOC) ") uniform vec4 u_bb_colour = vec4(" str(BOUNDING_BOX_COLOUR) ");\n" + "layout(location = " str(MODEL_RENDER_BB_FRACTION_LOC) ") uniform float u_bb_fraction = " str(BOUNDING_BOX_FRACTION) ";\n\n" + "layout(binding = 0) uniform sampler3D u_texture;\n" "\n#line 1\n"); str8 render_model = str8("render_model.frag.glsl"); reload_shader(&ctx->os, render_model, (sptr)model_rc, ctx->arena); os_add_file_watch(&ctx->os, &ctx->arena, render_model, reload_shader, (sptr)model_rc); - /* TODO(rnp): think about a reasonable region (probably min_coord -> max_coord + 10%) */ - f32 n = 1; - f32 f = 20; - f32 a = -f / (f - n); - f32 b = -f * n / (f - n); - m4 projection; - projection.c[0] = (v4){{1, 0, 0, 0}}; - projection.c[1] = (v4){{0, 1, 0, 0}}; - projection.c[2] = (v4){{0, 0, a, -1}}; - projection.c[3] = (v4){{0, 0, b, 0}}; - glProgramUniformMatrix4fv(rc->shader, MODEL_RENDER_PROJ_MATRIX_LOC, 1, 0, projection.E); - rc = &ctx->overlay_render_context; ShaderReloadContext *overlay_rc = push_struct(&ctx->arena, ShaderReloadContext); overlay_rc->render_context = rc; @@ -326,7 +364,7 @@ init_viewer(ViewerContext *ctx) "\n" "void main()\n" "{\n" - "\tf_texture_coordinate = 1 - v_texture_coordinate;\n" + "\tf_texture_coordinate = v_texture_coordinate;\n" "\tf_colour = v_colour;\n" "\tf_flags = v_flags;\n" "\tgl_Position = vec4(v_position, 0, 1);\n" @@ -365,6 +403,9 @@ init_viewer(ViewerContext *ctx) ctx->unit_cube = load_render_model(ctx->arena, "unit_cube_positions.bin", "unit_cube_indices.bin", "unit_cube_normals.bin"); + + c8 *walking_cysts = "./data/test/frame_%02u.bin"; + ctx->view_texture = load_complex_texture(ctx->arena, walking_cysts, 1, 512, 1024, 64); } function void @@ -408,6 +449,20 @@ viewer_frame_step(ViewerContext *ctx, f32 dt) glUseProgram(ctx->model_render_context.shader); + /* TODO(rnp): this needs to be set on hot reload */ + /* TODO(rnp): think about a reasonable region (probably min_coord -> max_coord + 10%) */ + f32 n = 1; + f32 f = 20; + f32 a = -f / (f - n); + f32 b = -f * n / (f - n); + m4 projection; + projection.c[0] = (v4){{1, 0, 0, 0}}; + projection.c[1] = (v4){{0, 1, 0, 0}}; + projection.c[2] = (v4){{0, 0, a, -1}}; + projection.c[3] = (v4){{0, 0, b, 0}}; + glProgramUniformMatrix4fv(ctx->model_render_context.shader, MODEL_RENDER_PROJ_MATRIX_LOC, + 1, 0, projection.E); + v3 camera = ctx->camera_position; set_camera(ctx->model_render_context.shader, MODEL_RENDER_VIEW_MATRIX_LOC, camera, v3_normalize(v3_sub(camera, (v3){0})), (v3){{0, 1, 0}}); @@ -421,6 +476,7 @@ viewer_frame_step(ViewerContext *ctx, f32 dt) glProgramUniformMatrix4fv(ctx->model_render_context.shader, MODEL_RENDER_MODEL_MATRIX_LOC, 1, 0, model_transform.E); + glBindTextureUnit(0, ctx->view_texture.texture); glBindVertexArray(ctx->unit_cube.vao); glDrawElements(GL_TRIANGLES, ctx->unit_cube.elements, GL_UNSIGNED_SHORT, (void *)ctx->unit_cube.elements_offset); diff --git a/main_linux.c b/main_linux.c @@ -47,7 +47,7 @@ dispatch_file_watch_events(OS *os, Arena arena) extern s32 main(void) { - Arena memory = os_alloc_arena(MB(16)); + Arena memory = os_alloc_arena(GB(1)); ViewerContext *ctx = push_struct(&memory, ViewerContext); ctx->arena = memory; diff --git a/main_w32.c b/main_w32.c @@ -77,7 +77,7 @@ clear_io_queue(OS *os, Arena arena) extern i32 main(void) { - Arena memory = os_alloc_arena(MB(16)); + Arena memory = os_alloc_arena(GB(1)); ViewerContext *ctx = push_struct(&memory, ViewerContext); ctx->arena = memory; diff --git a/render_model.frag.glsl b/render_model.frag.glsl @@ -1,5 +1,4 @@ /* See LICENSE for license details. */ -//layout(binding = 0) uniform sampler3D u_out_data_tex; /* input: h [0,360] | s,v [0, 1] * * output: rgb [0,1] */ @@ -10,19 +9,19 @@ vec3 hsv2rgb(vec3 hsv) return hsv.z - hsv.z * hsv.y * k; } -void main() +bool bounding_box_test(vec3 coord, float p) { - #if 0 - ivec3 out_data_dim = textureSize(u_out_data_tex, 0); - - //vec2 min_max = texelFetch(u_out_data_tex, ivec3(0), textureQueryLevels(u_out_data_tex) - 1).xy; - - /* TODO(rnp): select between x and y and specify slice */ - ivec2 coord = ivec2(fragment_texture_coordinate * vec2(out_data_dim.xz)); - ivec3 smp_coord = ivec3(coord.x, out_data_dim.y / 2, coord.y); - float smp = length(texelFetch(u_out_data_tex, smp_coord, 0).xy); + bool result = false; + bvec3 tests = bvec3(1 - step(vec3(p), coord) * step(coord, vec3(1 - p))); + if ((tests.x && tests.y) || (tests.x && tests.z) || (tests.y && tests.z)) + result = true; + return result; +} - float threshold_val = pow(10.0f, u_threshold / 20.0f); +void main() +{ + float smp = length(texture(u_texture, texture_coordinate).xy); + float threshold_val = pow(10.0f, (u_threshold + 13) / 20.0f); smp = clamp(smp, 0.0f, threshold_val); smp = smp / threshold_val; smp = pow(smp, u_gamma); @@ -33,8 +32,13 @@ void main() smp = 1 - smp; } - //v_out_colour = vec4(hsv2rgb(vec3(360 * smp, 0.8, 0.95)), 1); - #endif + //out_colour = vec4(abs(normal), 1); + //out_colour = vec4(1, 1, 1, smp); + //out_colour = vec4(smp * abs(normal), 1); - out_colour = vec4(abs(normal), 1); + if (bounding_box_test(texture_coordinate, u_bb_fraction)) { + out_colour = u_bb_colour; + } else { + out_colour = vec4(smp, smp, smp, 1); + } } diff --git a/util.h b/util.h @@ -72,8 +72,8 @@ #define static_assert _Static_assert /* NOTE: garbage to get the prepocessor to properly stringize the value of a macro */ -#define str_(x) #x -#define str(x) str_(x) +#define str_(...) #__VA_ARGS__ +#define str(...) str_(__VA_ARGS__) #define countof(a) (sizeof(a) / sizeof(*a)) #define ARRAY_COUNT(a) (sizeof(a) / sizeof(*a)) @@ -269,6 +269,13 @@ struct OS { }; typedef struct { + u32 texture; + u32 width; + u32 height; + u32 depth; +} Texture; + +typedef struct { u32 shader; u32 vao; u32 vbo; @@ -303,6 +310,9 @@ typedef struct { f32 camera_angle; f32 camera_radius; v3 camera_position; + + Texture view_texture; + b32 should_exit; void *window;