volviewer

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

Commit: cb637e808de46b397ef4d6b62872fecb0e068f0c
Parent: 64a87ca0208c054cda530c8fb020531c92fd4388
Author: Randy Palamar
Date:   Wed, 29 Oct 2025 12:11:11 -0600

add bitmap export

Diffstat:
Mcommon.c | 86++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
Mutil.c | 15+++++++++++++++
2 files changed, 96 insertions(+), 5 deletions(-)

diff --git a/common.c b/common.c @@ -216,6 +216,74 @@ load_render_model(Arena arena, c8 *positions_file_name, c8 *indices_file_name, c return result; } +function b32 +export_bitmap(ViewerContext *ctx) +{ + #pragma pack(push, 1) + struct bmp_header { + u8 file_type[2]; + u32 file_size; + u16 reserved_1; + u16 reserved_2; + u32 bitmap_offset; + + u32 size; + s32 width; + s32 height; + u16 planes; + u16 bits_per_pixel; + + u32 compression; + u32 size_of_bitmap; + + s32 horizontal_resolution; + s32 vertical_resolution; + u32 colours_used; + u32 colours_important; + + u32 red_mask; + u32 green_mask; + u32 blue_mask; + u32 alpha_mask; + + u32 colour_space_type; + u32 cie_xyz_triples[9]; + u32 gamma_red; + u32 gamma_green; + u32 gamma_blue; + }; + #pragma pack(pop) + + s32 texture_size = ctx->output_target.size.w * ctx->output_target.size.h * sizeof(u32); + s32 out_size = texture_size + round_up_power_of_2(sizeof(struct bmp_header)); + + Arena arena = ctx->video_arena; + void *out_buf = arena_alloc(&arena, out_size, 64, 1); + + struct bmp_header *header = (struct bmp_header *)out_buf; + header->bitmap_offset = out_size - texture_size; + header->file_type[0] = 'B'; + header->file_type[1] = 'M'; + header->file_size = out_size; + header->size = 108; + header->width = ctx->output_target.size.w; + header->height = -ctx->output_target.size.h; + header->planes = 1; + header->bits_per_pixel = 32; + header->compression = 3; + header->size_of_bitmap = texture_size; + header->red_mask = 0xFF000000; + header->green_mask = 0x00FF0000; + header->blue_mask = 0x0000FF00; + header->alpha_mask = 0x000000FF; + + glGetTextureImage(ctx->output_target.textures[0], 0, GL_RGBA, + GL_UNSIGNED_INT_8_8_8_8, texture_size, + (u8 *)out_buf + header->bitmap_offset); + + return os_write_new_file("view.bmp", (str8){.len = out_size, .data = out_buf}); +} + function void scroll_callback(GLFWwindow *window, f64 x, f64 y) { @@ -234,16 +302,24 @@ key_callback(GLFWwindow *window, s32 key, s32 scancode, s32 action, s32 modifier if (key == GLFW_KEY_SPACE && action == GLFW_PRESS) ctx->demo_mode = !ctx->demo_mode; - if (key == GLFW_KEY_F12 && action == GLFW_PRESS && ctx->output_frames_count == 0) { + if (key == GLFW_KEY_F11 || key == GLFW_KEY_F12) { sz frames = TOTAL_OUTPUT_FRAMES; sz needed_bytes = sizeof(u32) * RENDER_TARGET_HEIGHT * RENDER_TARGET_WIDTH * frames; if (!ctx->video_arena.beg) { ctx->video_arena = os_alloc_arena(needed_bytes); - if (!ctx->video_arena.beg) { - fputs("failed to allocate space for output video, video " - "won't be saved\n", stderr); - } + if (!ctx->video_arena.beg) + fputs("failed to allocate space for output data\n", stderr); } + } + + if (key == GLFW_KEY_F11) { + if (ctx->video_arena.beg) { + if (export_bitmap(ctx)) fputs("exported bitmap\n", stderr); + else fputs("failed to export bitmap\n", stderr); + } + } + + if (key == GLFW_KEY_F12 && action == GLFW_PRESS && ctx->output_frames_count == 0) { if (ctx->video_arena.beg) { ctx->output_frames_count = TOTAL_OUTPUT_FRAMES; ctx->cycle_t = 0; diff --git a/util.c b/util.c @@ -351,6 +351,21 @@ stream_append_f64(Stream *s, f64 f, s64 prec) } } +function u32 +clz_u32(u32 a) +{ + u32 result = 32; + if (a) result = (u32)__builtin_clz(a); + return result; +} + +function u32 +round_up_power_of_2(u32 a) +{ + u32 result = 0x80000000UL >> (clz_u32(a - 1) - 1); + return result; +} + function Stream arena_stream(Arena a) {