volviewer

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

Commit: ff39ae97d3787079e0a78305cfb2b90fc4e0b72d
Parent: 719cb1e490abf88c1acd38bbc08ca540a9dd60ca
Author: Randy Palamar
Date:   Fri, 23 May 2025 23:05:21 -0600

basic model loading and drawing

Diffstat:
M.gitignore | 2++
Mcommon.c | 86++++++++++++++++++++++++++++++++++++++++----------------------------------------
Mmain_linux.c | 4----
Mmain_w32.c | 17++++++-----------
Mrender_model.frag.glsl | 2+-
Arepack_gltf.py | 34++++++++++++++++++++++++++++++++++
Mutil.h | 18++++++++----------
7 files changed, 94 insertions(+), 69 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -1,3 +1,5 @@ +*.bin *.exe +*.gltf build volviewer diff --git a/common.c b/common.c @@ -47,7 +47,7 @@ gl_debug_logger(u32 src, u32 type, u32 id, u32 lvl, s32 len, const char *msg, co } stream_append(e, (char *)msg, len); stream_append_byte(e, '\n'); - ctx->os->write_file(ctx->os->error_handle, stream_to_str8(e)); + os_write_file(ctx->os->error_handle, stream_to_str8(e)); stream_reset(e, 0); } @@ -70,7 +70,7 @@ compile_shader(OS *os, Arena a, u32 type, str8 shader, str8 name) glGetShaderInfoLog(sid, len, &out_len, (char *)(buf.data + buf.widx)); stream_commit(&buf, out_len); glDeleteShader(sid); - os->write_file(os->error_handle, stream_to_str8(&buf)); + os_write_file(os->error_handle, stream_to_str8(&buf)); sid = 0; } @@ -94,7 +94,7 @@ link_program(OS *os, Arena a, u32 *shader_ids, u32 shader_id_count) glGetProgramInfoLog(result, buf.cap - buf.widx, &len, (c8 *)(buf.data + buf.widx)); stream_reset(&buf, len); stream_append_byte(&buf, '\n'); - os->write_file(os->error_handle, stream_to_str8(&buf)); + os_write_file(os->error_handle, stream_to_str8(&buf)); glDeleteProgram(result); result = 0; } @@ -114,7 +114,7 @@ load_shader(OS *os, Arena arena, str8 vs_text, str8 fs_text, str8 info_name, str if (result) { Stream buf = arena_stream(arena); stream_append_str8s(&buf, str8("loaded: "), info_name, str8("\n")); - os->write_file(os->error_handle, stream_to_str8(&buf)); + os_write_file(os->error_handle, stream_to_str8(&buf)); LABEL_GL_OBJECT(GL_PROGRAM, result, label); } @@ -131,7 +131,7 @@ function FILE_WATCH_CALLBACK_FN(reload_shader) { ShaderReloadContext *ctx = (typeof(ctx))user_data; str8 header = push_str8(&tmp, ctx->fragment_header); - str8 fragment = os->read_whole_file(&tmp, (c8 *)path.data); + str8 fragment = os_read_whole_file(&tmp, (c8 *)path.data); fragment.data -= header.len; fragment.len += header.len; assert(fragment.data == header.data); @@ -143,6 +143,36 @@ function FILE_WATCH_CALLBACK_FN(reload_shader) return 1; } +function RenderModel +load_render_model(Arena arena, c8 *positions_file_name, c8 *indices_file_name) +{ + RenderModel result = {0}; + + str8 positions = os_read_whole_file(&arena, positions_file_name); + str8 indices = os_read_whole_file(&arena, indices_file_name); + + result.elements = indices.len / sizeof(u16); + + s32 buffer_size = positions.len + indices.len; + + result.elements_offset = positions.len; + + glCreateBuffers(1, &result.buffer); + glNamedBufferStorage(result.buffer, buffer_size, 0, GL_DYNAMIC_STORAGE_BIT); + glNamedBufferSubData(result.buffer, 0, positions.len, positions.data); + glNamedBufferSubData(result.buffer, positions.len, indices.len, indices.data); + + glCreateVertexArrays(1, &result.vao); + glVertexArrayVertexBuffer(result.vao, 0, result.buffer, 0, 3 * sizeof(f32)); + glVertexArrayElementBuffer(result.vao, result.buffer); + + glEnableVertexArrayAttrib(result.vao, 0); + glVertexArrayAttribFormat(result.vao, 0, 3, GL_FLOAT, 0, 0); + glVertexArrayAttribBinding(result.vao, 0, 0); + + return result; +} + function void key_callback(GLFWwindow *window, s32 key, s32 scancode, s32 action, s32 modifiers) { @@ -190,29 +220,6 @@ init_viewer(ViewerContext *ctx) RenderContext *rc = &ctx->model_render_context; - Vertex vertices[] = { - {.position = {{-0.5, -0.5, 0.0}}, .colour = {{1.0, 0.0, 0.0}}}, - {.position = {{ 0, 0.5, 0.0}}, .colour = {{0.0, 1.0, 0.0}}}, - {.position = {{ 0.5, -0.5, 0.0}}, .colour = {{0.0, 0.0, 1.0}}}, - }; - - glGenVertexArrays(1, &rc->vao); - glBindVertexArray(rc->vao); - glGenBuffers(1, &rc->vbo); - glBindBuffer(GL_ARRAY_BUFFER, rc->vbo); - glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); - - glVertexAttribPointer(0, 3, GL_FLOAT, 0, sizeof(Vertex), (void *)offsetof(Vertex, normal)); - glVertexAttribPointer(1, 3, GL_FLOAT, 0, sizeof(Vertex), (void *)offsetof(Vertex, position)); - glVertexAttribPointer(2, 3, GL_FLOAT, 0, sizeof(Vertex), (void *)offsetof(Vertex, texture_coordinate)); - glVertexAttribPointer(3, 3, GL_FLOAT, 0, sizeof(Vertex), (void *)offsetof(Vertex, colour)); - glVertexAttribPointer(4, 1, GL_INT, 0, sizeof(Vertex), (void *)offsetof(Vertex, flags)); - glEnableVertexAttribArray(0); - glEnableVertexAttribArray(1); - glEnableVertexAttribArray(2); - glEnableVertexAttribArray(3); - glEnableVertexAttribArray(4); - RenderTarget *rt = &ctx->output_target; glCreateTextures(GL_TEXTURE_2D, countof(rt->textures), rt->textures); rt->size = (sv2){{RENDER_TARGET_SIZE}}; @@ -228,26 +235,15 @@ init_viewer(ViewerContext *ctx) model_rc->vertex_text = str8("" "#version 460 core\n" "\n" - "layout(location = 0) in vec3 v_normal;\n" - "layout(location = 1) in vec3 v_position;\n" - "layout(location = 2) in vec3 v_texture_coordinate;\n" - "layout(location = 3) in vec3 v_colour;\n" - "layout(location = 4) in uint v_flags;\n" - "\n" - "layout(location = 0) out vec3 f_texture_coordinate;\n" - "layout(location = 1) out vec3 f_colour;\n" + "layout(location = 0) in vec3 v_position;\n" "\n" "void main()\n" "{\n" - "\tf_texture_coordinate = v_texture_coordinate;\n" - "\tf_colour = v_colour;\n" - "\tgl_Position = vec4(v_position, 1);\n" + "\tgl_Position = vec4(0.5 * v_position, 1);\n" "}\n"); model_rc->fragment_header = str8("" "#version 460 core\n\n" - "layout(location = 0) in vec3 texture_coordinate;\n" - "layout(location = 1) in vec3 colour;\n" "layout(location = 0) out vec4 out_colour;\n\n" "layout(location = " str(VIEWER_RENDER_DYNAMIC_RANGE_LOC) ") uniform float u_db_cutoff = 60;\n" "layout(location = " str(VIEWER_RENDER_THRESHOLD_LOC) ") uniform float u_threshold = 40;\n" @@ -314,6 +310,9 @@ init_viewer(ViewerContext *ctx) str8 render_overlay = str8("render_overlay.frag.glsl"); reload_shader(&ctx->os, render_overlay, (sptr)overlay_rc, ctx->arena); os_add_file_watch(&ctx->os, &ctx->arena, render_overlay, reload_shader, (sptr)overlay_rc); + + ctx->unit_cube = load_render_model(ctx->arena, "unit_cube_positions.bin", + "unit_cube_indices.bin"); } function void @@ -326,8 +325,9 @@ viewer_frame_step(ViewerContext *ctx) glViewport(0, 0, ctx->output_target.size.w, ctx->output_target.size.h); glUseProgram(ctx->model_render_context.shader); - glBindVertexArray(ctx->model_render_context.vao); - glDrawArrays(GL_TRIANGLES, 0, 3); + glBindVertexArray(ctx->unit_cube.vao); + glDrawElements(GL_TRIANGLES, ctx->unit_cube.elements, GL_UNSIGNED_SHORT, + (void *)ctx->unit_cube.elements_offset); //////////////// // UI Overlay diff --git a/main_linux.c b/main_linux.c @@ -51,10 +51,6 @@ main(void) ViewerContext *ctx = push_struct(&memory, ViewerContext); ctx->arena = memory; - #define X(name) ctx->os.name = os_ ## name; - OS_FNS - #undef X - ctx->os.file_watch_context.handle = inotify_init1(IN_NONBLOCK|IN_CLOEXEC); ctx->os.error_handle = STDERR_FILENO; diff --git a/main_w32.c b/main_w32.c @@ -12,11 +12,10 @@ function void dispatch_file_watch(OS *os, FileWatchDirectory *fw_dir, u8 *buf, Arena arena) { i64 offset = 0; - TempArena save_point = {0}; + Arena start_arena = arena; w32_file_notify_info *fni = (w32_file_notify_info *)buf; do { - end_temp_arena(save_point); - save_point = begin_temp_arena(&arena); + arena = start_arena; Stream path = {.data = arena_commit(&arena, KB(1)), .cap = KB(1)}; @@ -52,7 +51,7 @@ dispatch_file_watch(OS *os, FileWatchDirectory *fw_dir, u8 *buf, Arena arena) } function void -clear_io_queue(OS *os, BeamformerInput *input, Arena arena) +clear_io_queue(OS *os, Arena arena) { w32_context *ctx = (w32_context *)os->context; @@ -78,13 +77,9 @@ clear_io_queue(OS *os, BeamformerInput *input, Arena arena) extern i32 main(void) { - BeamformerCtx ctx = {0}; - BeamformerInput input = {.executable_reloaded = 1}; - Arena temp_memory = os_alloc_arena((Arena){0}, MB(16)); - - #define X(name) ctx.os.name = os_ ## name; - OS_FNS - #undef X + Arena memory = os_alloc_arena(MB(16)); + ViewerContext *ctx = push_struct(&memory, ViewerContext); + ctx->arena = memory; w32_context w32_ctx = {0}; w32_ctx.io_completion_handle = CreateIoCompletionPort(INVALID_FILE, 0, 0, 0); diff --git a/render_model.frag.glsl b/render_model.frag.glsl @@ -36,5 +36,5 @@ void main() //v_out_colour = vec4(hsv2rgb(vec3(360 * smp, 0.8, 0.95)), 1); #endif - out_colour = vec4(colour, 1); + out_colour = vec4(1); } diff --git a/repack_gltf.py b/repack_gltf.py @@ -0,0 +1,34 @@ +import json + +def dump_sub_buffer_to_file(gltf, accessor, output): + position_attribute = gltf["accessors"][accessor] + buffer_view = gltf["bufferViews"][position_attribute["bufferView"]] + offset = buffer_view["byteOffset"] + length = buffer_view["byteLength"] + buffer = buffer_view["buffer"] + + with open(gltf["buffers"][buffer]["uri"], "rb") as input: + input.seek(offset, 0) + output.write(input.read(length)) + +def dump_sub_buffer(gltf, accessor, output_name): + with open(output_name, "wb") as output: + dump_sub_buffer_to_file(gltf, accessor, output) + +def main(): + with open('unit_cube.gltf', 'r') as f: + unit_cube = json.load(f) + + assert(len(unit_cube["meshes"]) == 1) + mesh = unit_cube["meshes"][0] + + assert(len(mesh["primitives"]) == 1) + attributes = mesh["primitives"][0]["attributes"] + indices = mesh["primitives"][0]["indices"] + + dump_sub_buffer(unit_cube, attributes["POSITION"], "unit_cube_positions.bin") + dump_sub_buffer(unit_cube, attributes["NORMAL"], "unit_cube_normals.bin") + dump_sub_buffer(unit_cube, indices, "unit_cube_indices.bin") + +if __name__ == '__main__': + main() diff --git a/util.h b/util.h @@ -257,17 +257,7 @@ typedef OS_WRITE_NEW_FILE_FN(os_write_new_file_fn); #define OS_WRITE_FILE_FN(name) b32 name(sptr file, str8 raw) typedef OS_WRITE_FILE_FN(os_write_file_fn); -#define OS_FNS \ - X(add_file_watch) \ - X(alloc_arena) \ - X(read_whole_file) \ - X(write_new_file) \ - X(write_file) - struct OS { -#define X(name) os_ ## name ## _fn *name; - OS_FNS -#undef X FileWatchContext file_watch_context; sptr context; sptr error_handle; @@ -286,6 +276,13 @@ typedef struct { } RenderTarget; typedef struct { + sptr elements_offset; + s32 elements; + u32 buffer; + u32 vao; +} RenderModel; + +typedef struct { Arena arena; OS os; @@ -293,6 +290,7 @@ typedef struct { RenderContext overlay_render_context; RenderTarget output_target; + RenderModel unit_cube; sv2 window_size;