Commit: 84a0929c638a45db0d27910d291a68bae52d2492
Parent: 6f70fb0962e6c9cd37882b12821159b434f61c2e
Author: Randy Palamar
Date: Sun, 25 May 2025 15:50:24 -0600
finish constant update rate video output
Diffstat:
10 files changed, 168 insertions(+), 32 deletions(-)
diff --git a/build.c b/build.c
@@ -134,12 +134,6 @@ os_remove_file(char *name)
return result;
}
-function void
-os_make_directory(char *name)
-{
- mkdir(name, 0770);
-}
-
function u64
os_get_filetime(char *file)
{
@@ -200,19 +194,12 @@ enum {
MOVEFILE_REPLACE_EXISTING = 0x01,
};
-W32(b32) CreateDirectoryA(c8 *, void *);
W32(b32) CreateProcessA(u8 *, u8 *, sptr, sptr, b32, u32, sptr, u8 *, sptr, sptr);
W32(b32) GetExitCodeProcess(iptr handle, u32 *);
W32(b32) GetFileTime(sptr, sptr, sptr, sptr);
W32(b32) MoveFileExA(c8 *, c8 *, u32);
W32(u32) WaitForSingleObject(sptr, u32);
-function void
-os_make_directory(char *name)
-{
- CreateDirectoryA(name, 0);
-}
-
function b32
os_rename_file(char *name, char *new)
{
diff --git a/common.c b/common.c
@@ -9,9 +9,11 @@
/* NOTE(rnp): for video output we will render a full rotation in this much time at the
* the specified frame rate */
#define OUTPUT_TIME_SECONDS 8.0f
-#define OUTPUT_FRAME_RATE 30.0f
+#define OUTPUT_FRAME_RATE 60.0f
#define OUTPUT_BG_CLEAR_COLOUR (v4){{0.05, 0.05, 0.05, 1}}
+#define OUTPUT_PATH "/tmp/out"
+
#define RENDER_MSAA_SAMPLES 8
#define RENDER_TARGET_SIZE 1920, 1080
#define CAMERA_ELEVATION_ANGLE 25.0f
@@ -39,8 +41,10 @@ typedef struct {
global u32 single_volume_index = 0;
global VolumeDisplayItem volumes[] = {
/* WALKING FORCES */
- {"./data/test/frame_%02u.bin", 512, 1024, 64, {{-18.5, -9.6, 5}}, {{18.5, 9.6, 42}}, 0.58, 62, 3 * -18.5, 0, 1},
- {"./data/test/frame_%02u.bin", 512, 1024, 64, {{-18.5, -9.6, 5}}, {{18.5, 9.6, 42}}, 0.58, 62, 3 * 18.5, 0, 1},
+ {"./data/test/frame_%02u.bin", 512, 1024, 64, {{-18.5, -9.6, 5}}, {{18.5, 9.6, 42}}, 0.58, 62, 0, 0, 1},
+ /* RCA */
+ {"./data/tpw.bin", 512, 64, 1024, {{-9.6, -9.6, 5}}, {{9.6, 9.6, 42}}, 0, 85, -5 * 18.5, 1, 0},
+ {"./data/vls.bin", 512, 64, 1024, {{-9.6, -9.6, 5}}, {{9.6, 9.6, 42}}, 0, 82, 5 * 18.5, 1, 0},
};
#define MODEL_RENDER_MODEL_MATRIX_LOC (0)
@@ -53,14 +57,32 @@ global VolumeDisplayItem volumes[] = {
#define MODEL_RENDER_GAMMA_LOC (7)
#define MODEL_RENDER_BB_COLOUR_LOC (8)
#define MODEL_RENDER_BB_FRACTION_LOC (9)
+#define MODEL_RENDER_SWIZZLE_LOC (10)
-#define BG_CLEAR_COLOUR (v4){{0.12, 0.1, 0.1, 1}}
+#define CYCLE_T_UPDATE_SPEED 0.25f
+#define BG_CLEAR_COLOUR (v4){{0.12, 0.1, 0.1, 1}}
struct gl_debug_ctx {
Stream stream;
OS *os;
};
+function f32
+get_frame_time_step(ViewerContext *ctx)
+{
+ f32 result = 0;
+ /* NOTE(rnp): if we are outputting frames do a constant time step */
+ if (ctx->output_frames_count > 0) {
+ result = 1.0f / (OUTPUT_FRAME_RATE * OUTPUT_TIME_SECONDS * CYCLE_T_UPDATE_SPEED);
+ } else {
+ f64 now = glfwGetTime();
+ result = ctx->demo_mode * (now - ctx->last_time) + ctx->input_dt;
+ ctx->last_time = now;
+ ctx->input_dt = 0;
+ }
+ return result;
+}
+
function void
gl_debug_logger(u32 src, u32 type, u32 id, u32 lvl, s32 len, const char *msg, const void *userctx)
{
@@ -275,6 +297,11 @@ key_callback(GLFWwindow *window, s32 key, s32 scancode, s32 action, s32 modifier
if (key == GLFW_KEY_LEFT && (action == GLFW_PRESS || action == GLFW_REPEAT))
ctx->input_dt -= 4.0f / (OUTPUT_TIME_SECONDS * OUTPUT_FRAME_RATE);
+ if (key == GLFW_KEY_F12 && action == GLFW_PRESS && ctx->output_frames_count == 0) {
+ ctx->output_frames_count = OUTPUT_TIME_SECONDS * OUTPUT_FRAME_RATE;
+ ctx->cycle_t = 0;
+ }
+
ctx->camera_angle += (key == GLFW_KEY_W && action != GLFW_RELEASE) * 5 * PI / 180.0f;
ctx->camera_angle -= (key == GLFW_KEY_S && action != GLFW_RELEASE) * 5 * PI / 180.0f;
}
@@ -295,8 +322,9 @@ init_viewer(ViewerContext *ctx)
ctx->camera_angle = -CAMERA_ELEVATION_ANGLE * PI / 180.0f;
ctx->camera_fov = 60.0f;
- if (!glfwInit()) os_fatal(str8("failed to start glfw\n"));
+ os_make_directory(OUTPUT_PATH);
+ if (!glfwInit()) os_fatal(str8("failed to start glfw\n"));
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6);
@@ -393,7 +421,8 @@ init_viewer(ViewerContext *ctx)
"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(location = " str(MODEL_RENDER_BB_FRACTION_LOC) ") uniform float u_bb_fraction = " str(BOUNDING_BOX_FRACTION) ";\n"
+ "layout(location = " str(MODEL_RENDER_SWIZZLE_LOC) ") uniform bool u_swizzle;\n\n"
"layout(binding = 0) uniform sampler3D u_texture;\n"
"\n#line 1\n");
@@ -516,6 +545,7 @@ draw_volume_item(ViewerContext *ctx, VolumeDisplayItem *v, f32 rotation)
glProgramUniform1f(program, MODEL_RENDER_CLIP_FRACTION_LOC, 1 - v->clip_fraction);
glProgramUniform1f(program, MODEL_RENDER_THRESHOLD_LOC, v->threshold);
+ glProgramUniform1ui(program, MODEL_RENDER_SWIZZLE_LOC, v->swizzle);
glBindTextureUnit(0, v->texture);
glBindVertexArray(ctx->unit_cube.vao);
@@ -526,7 +556,7 @@ draw_volume_item(ViewerContext *ctx, VolumeDisplayItem *v, f32 rotation)
function void
update_scene(ViewerContext *ctx, f32 dt)
{
- ctx->cycle_t += 0.25 * dt;
+ ctx->cycle_t += CYCLE_T_UPDATE_SPEED * dt;
if (ctx->cycle_t > 1) ctx->cycle_t -= 1;
f32 angle = ctx->cycle_t * 2 * PI;
@@ -579,9 +609,40 @@ update_scene(ViewerContext *ctx, f32 dt)
}
function void
+export_frame(Arena arena, u32 texture, str8 out_directory, u32 frame_index, u32 width, u32 height)
+{
+ Stream spath = arena_stream(arena);
+ stream_append_str8(&spath, out_directory);
+ if (spath.widx > 0 && spath.data[spath.widx - 1] != OS_PATH_SEPARATOR_CHAR)
+ stream_append_byte(&spath, OS_PATH_SEPARATOR_CHAR);
+ stream_append_str8(&spath, str8("frame_"));
+ stream_append_u64_width(&spath, frame_index, 4);
+ stream_append_str8(&spath, str8(".bin"));
+ str8 path = arena_stream_commit_zero(&arena, &spath);
+
+ sz padding = -(uintptr_t)arena.beg & (64 - 1);
+ sz available = arena.end - arena.beg - padding;
+ sz needed = width * height * sizeof(u32);
+ if (available > needed) {
+ glGetTextureImage(texture, 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, needed, arena.beg);
+ str8 raw = {.len = needed, .data = arena.beg};
+ os_write_new_file((c8 *)path.data, raw);
+ }
+}
+
+function void
viewer_frame_step(ViewerContext *ctx, f32 dt)
{
- if (dt != 0) update_scene(ctx, dt);
+ if (dt != 0) {
+ update_scene(ctx, dt);
+ if (ctx->output_frames_count) {
+ u32 total_frames = OUTPUT_FRAME_RATE * OUTPUT_TIME_SECONDS;
+ u32 frame_index = total_frames - ctx->output_frames_count--;
+ printf("Saving Frame: [%u/%u]\n", frame_index, total_frames);
+ export_frame(ctx->arena, ctx->output_target.textures[0], str8(OUTPUT_PATH),
+ frame_index, RENDER_TARGET_SIZE);
+ }
+ }
////////////////
// UI Overlay
diff --git a/main_linux.c b/main_linux.c
@@ -60,15 +60,11 @@ main(void)
fds[0].fd = ctx->os.file_watch_context.handle;
fds[0].events = POLLIN;
- f64 last_time = 0;
while (!ctx->should_exit) {
poll(fds, countof(fds), 0);
if (fds[0].revents & POLLIN)
dispatch_file_watch_events(&ctx->os, ctx->arena);
- f64 now = glfwGetTime();
- viewer_frame_step(ctx, ctx->demo_mode * (now - last_time) + ctx->input_dt);
- last_time = now;
- ctx->input_dt = 0;
+ viewer_frame_step(ctx, get_frame_time_step(ctx));
glfwSwapBuffers(ctx->window);
glfwPollEvents();
}
diff --git a/main_w32.c b/main_w32.c
@@ -89,13 +89,9 @@ main(void)
init_viewer(ctx);
- f64 last_time = 0;
while (!ctx->should_exit) {
clear_io_queue(&ctx->os, ctx->arena);
- f64 now = glfwGetTime();
- viewer_frame_step(ctx, ctx->demo_mode * (now - last_time) + ctx->input_dt);
- last_time = now;
- ctx->input_dt = 0;
+ viewer_frame_step(ctx, get_frame_time_step(ctx));
glfwSwapBuffers(ctx->window);
glfwPollEvents();
}
diff --git a/make_video.py b/make_video.py
@@ -0,0 +1,64 @@
+import cv2
+import subprocess
+import tempfile as tf
+import pandas as pd
+import numpy as np
+import os
+
+def read_raw(file_name, points):
+ raw = np.fromfile(file_name, dtype=np.uint8)
+ real_points = [points[0], points[1], 4]
+ raw = np.squeeze(raw.reshape(real_points))
+ img = raw[:,:,(1, 2, 3, 0)]
+ return img
+
+def get_files(directory):
+ files = []
+ for filename in os.listdir(directory):
+ base, ext = os.path.splitext(filename)
+ if ext == '.bin':
+ files.append(filename)
+ return files
+
+def make_frames(directory, points):
+ imgs = []
+ for f in get_files(directory):
+ raw_file_name = os.path.join(directory, f)
+ raw = read_raw(raw_file_name, points)
+ imgs.append(raw)
+ return imgs
+
+
+def make_video(name, frames, framerate):
+ with tf.TemporaryDirectory() as tmp:
+ count = 0
+ for frame in frames:
+ cv2.imwrite(os.path.join(tmp, "frame_{:02d}.png".format(count)), frame)
+ count = count + 1
+
+ ffmpeg_command = [
+ "ffmpeg",
+ "-y",
+ "-framerate", "{:d}".format(framerate),
+ "-i", os.path.join(tmp, 'frame_%02d.png'),
+ "-c:v", "libx265",
+ "-crf", "18",
+ name
+ ]
+ subprocess.run(ffmpeg_command)
+
+
+##################################
+save = True
+out_dir = "/tmp/downloads"
+points = [1080, 1920]
+framerate = 60
+if save:
+ os.makedirs(out_dir, mode=0o644, exist_ok=True)
+
+frames = make_frames("/tmp/downloads/out", points)
+if save:
+ make_video(os.path.join(out_dir, "volumes.mp4"), frames, framerate)
+
+#cv2.imshow("", frames[0])
+#cv2.waitKey(0)
diff --git a/os_linux.c b/os_linux.c
@@ -105,3 +105,9 @@ function OS_ADD_FILE_WATCH_FN(os_add_file_watch)
fw->callback = callback;
fw->hash = str8_hash(path);
}
+
+function void
+os_make_directory(char *name)
+{
+ mkdir(name, 0770);
+}
diff --git a/os_win32.c b/os_win32.c
@@ -81,6 +81,7 @@ typedef struct {
#define W32(r) __declspec(dllimport) r __stdcall
W32(b32) CloseHandle(sptr);
+W32(b32) CreateDirectoryA(c8 *, void *);
W32(sptr) CreateFileA(c8 *, u32, u32, void *, u32, u32, void *);
W32(sptr) CreateFileMappingA(sptr, void *, u32, u32, u32, c8 *);
W32(sptr) CreateIoCompletionPort(sptr, sptr, uptr, u32);
@@ -236,3 +237,9 @@ function OS_ADD_FILE_WATCH_FN(os_add_file_watch)
fw->callback = callback;
fw->hash = str8_hash(path);
}
+
+function void
+os_make_directory(char *name)
+{
+ CreateDirectoryA(name, 0);
+}
diff --git a/render_model.frag.glsl b/render_model.frag.glsl
@@ -20,7 +20,8 @@ bool bounding_box_test(vec3 coord, float p)
void main()
{
- float smp = length(texture(u_texture, texture_coordinate).xy);
+ vec3 tex_coord = u_swizzle? texture_coordinate.xzy : texture_coordinate;
+ float smp = length(texture(u_texture, tex_coord).xy);
float threshold_val = pow(10.0f, u_threshold / 20.0f);
smp = clamp(smp, 0.0f, threshold_val);
smp = smp / threshold_val;
diff --git a/util.c b/util.c
@@ -305,6 +305,21 @@ stream_append_u64(Stream *s, u64 n)
}
function void
+stream_append_u64_width(Stream *s, u64 n, u64 min_width)
+{
+ u8 tmp[64];
+ u8 *end = tmp + sizeof(tmp);
+ u8 *beg = end;
+ min_width = MIN(sizeof(tmp), min_width);
+
+ do { *--beg = '0' + (n % 10); } while (n /= 10);
+ while (end - beg > 0 && end - beg < min_width)
+ *--beg = '0';
+
+ stream_append(s, beg, end - beg);
+}
+
+function void
stream_append_s64(Stream *s, s64 n)
{
if (n < 0) {
diff --git a/util.h b/util.h
@@ -307,6 +307,9 @@ typedef struct {
f32 camera_radius;
v3 camera_position;
+ u32 output_frames_count;
+
+ f32 last_time;
f32 input_dt;
b32 should_exit;