Commit: 744d93141298ce58994b740def9e7801d5735d10
Parent: b4c42403b82967692920ab8ed6614e259b166bb8
Author: Randy Palamar
Date: Wed, 2 Jul 2025 10:09:07 -0600
ui: 3D X-Plane view
Diffstat:
13 files changed, 1130 insertions(+), 519 deletions(-)
diff --git a/beamformer.c b/beamformer.c
@@ -35,7 +35,7 @@ global renderdoc_end_frame_capture_fn *end_frame_capture;
#endif
typedef struct {
- BeamformComputeFrame *frames;
+ BeamformerComputeFrame *frames;
u32 capacity;
u32 offset;
u32 cursor;
@@ -66,10 +66,10 @@ compute_frame_iterator(BeamformerCtx *ctx, u32 start_index, u32 needed_frames)
return result;
}
-function BeamformComputeFrame *
+function BeamformerComputeFrame *
frame_next(ComputeFrameIterator *bfi)
{
- BeamformComputeFrame *result = 0;
+ BeamformerComputeFrame *result = 0;
if (bfi->cursor != bfi->needed_frames) {
u32 index = (bfi->offset + bfi->cursor++) % bfi->capacity;
result = bfi->frames + index;
@@ -78,7 +78,7 @@ frame_next(ComputeFrameIterator *bfi)
}
function void
-alloc_beamform_frame(GLParams *gp, BeamformFrame *out, uv3 out_dim, s8 name, Arena arena)
+alloc_beamform_frame(GLParams *gp, BeamformerFrame *out, uv3 out_dim, s8 name, Arena arena)
{
out->dim.x = MAX(1, out_dim.x);
out->dim.y = MAX(1, out_dim.y);
@@ -104,6 +104,10 @@ alloc_beamform_frame(GLParams *gp, BeamformFrame *out, uv3 out_dim, s8 name, Are
glDeleteTextures(1, &out->texture);
glCreateTextures(GL_TEXTURE_3D, 1, &out->texture);
glTextureStorage3D(out->texture, out->mips, GL_RG32F, out->dim.x, out->dim.y, out->dim.z);
+
+ glTextureParameteri(out->texture, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTextureParameteri(out->texture, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
LABEL_GL_OBJECT(GL_TEXTURE, out->texture, stream_to_s8(&label));
}
@@ -263,7 +267,7 @@ compute_cursor_finished(struct compute_cursor *cursor)
}
function void
-do_compute_shader(BeamformerCtx *ctx, Arena arena, BeamformComputeFrame *frame, BeamformerShaderKind shader)
+do_compute_shader(BeamformerCtx *ctx, Arena arena, BeamformerComputeFrame *frame, BeamformerShaderKind shader)
{
ComputeShaderCtx *csctx = &ctx->csctx;
BeamformerSharedMemory *sm = ctx->shared_memory.region;
@@ -373,9 +377,9 @@ do_compute_shader(BeamformerCtx *ctx, Arena arena, BeamformComputeFrame *frame,
}break;
case BeamformerShaderKind_Sum:{
u32 aframe_index = ctx->averaged_frame_index % ARRAY_COUNT(ctx->averaged_frames);
- BeamformComputeFrame *aframe = ctx->averaged_frames + aframe_index;
- aframe->ready_to_present = 0;
- aframe->frame.id = ctx->averaged_frame_index;
+ BeamformerComputeFrame *aframe = ctx->averaged_frames + aframe_index;
+ aframe->ready_to_present = 0;
+ aframe->frame.id = ctx->averaged_frame_index;
/* TODO(rnp): hack we need a better way of specifying which frames to sum;
* this is fine for rolling averaging but what if we want to do something else */
assert(frame >= ctx->beamform_frames);
@@ -386,10 +390,10 @@ do_compute_shader(BeamformerCtx *ctx, Arena arena, BeamformComputeFrame *frame,
u32 *in_textures = push_array(&arena, u32, MAX_BEAMFORMED_SAVED_FRAMES);
ComputeFrameIterator cfi = compute_frame_iterator(ctx, 1 + base_index - to_average,
to_average);
- for (BeamformComputeFrame *it = frame_next(&cfi); it; it = frame_next(&cfi))
+ for (BeamformerComputeFrame *it = frame_next(&cfi); it; it = frame_next(&cfi))
in_textures[frame_count++] = it->frame.texture;
- ASSERT(to_average == frame_count);
+ assert(to_average == frame_count);
do_sum_shader(csctx, in_textures, frame_count, 1 / (f32)frame_count,
aframe->frame.texture, aframe->frame.dim);
@@ -541,7 +545,7 @@ complete_queue(BeamformerCtx *ctx, BeamformWorkQueue *q, Arena arena, iptr gl_co
BeamformerExportContext *ec = &work->export_context;
switch (ec->kind) {
case BeamformerExportKind_BeamformedData:{
- BeamformComputeFrame *frame = ctx->latest_frame;
+ BeamformerComputeFrame *frame = ctx->latest_frame;
assert(frame->ready_to_present);
u32 texture = frame->frame.texture;
uv3 dim = frame->frame.dim;
@@ -644,7 +648,7 @@ complete_queue(BeamformerCtx *ctx, BeamformWorkQueue *q, Arena arena, iptr gl_co
atomic_store_u32(&cs->processing_compute, 1);
start_renderdoc_capture(gl_context);
- BeamformComputeFrame *frame = work->frame;
+ BeamformerComputeFrame *frame = work->frame;
uv3 try_dim = make_valid_test_dim(bp->output_points);
if (!uv3_equal(try_dim, frame->frame.dim))
alloc_beamform_frame(&ctx->gl, &frame->frame, try_dim, s8("Beamformed_Data"), arena);
diff --git a/beamformer.h b/beamformer.h
@@ -57,13 +57,23 @@ typedef struct {
#undef X
} CudaLib;
-#define FRAME_VIEW_RENDER_DYNAMIC_RANGE_LOC 1
-#define FRAME_VIEW_RENDER_THRESHOLD_LOC 2
-#define FRAME_VIEW_RENDER_GAMMA_LOC 3
-#define FRAME_VIEW_RENDER_LOG_SCALE_LOC 4
+/* TODO(rnp): this should be a UBO */
+#define FRAME_VIEW_MODEL_MATRIX_LOC 0
+#define FRAME_VIEW_VIEW_MATRIX_LOC 1
+#define FRAME_VIEW_PROJ_MATRIX_LOC 2
+#define FRAME_VIEW_DYNAMIC_RANGE_LOC 3
+#define FRAME_VIEW_THRESHOLD_LOC 4
+#define FRAME_VIEW_GAMMA_LOC 5
+#define FRAME_VIEW_LOG_SCALE_LOC 6
+#define FRAME_VIEW_BB_COLOUR_LOC 7
+#define FRAME_VIEW_BB_FRACTION_LOC 8
+
+#define FRAME_VIEW_BB_COLOUR 0.92, 0.88, 0.78, 1.0
+#define FRAME_VIEW_BB_FRACTION 0.007f
typedef struct {
- u32 shader;
+ /* NOTE(rnp): shaders[0] -> 2D render, shader[1] -> 3D render */
+ u32 shaders[2];
u32 framebuffer;
u32 vao;
u32 vbo;
@@ -73,6 +83,12 @@ typedef struct {
#include "beamformer_parameters.h"
#include "beamformer_work_queue.h"
+typedef struct {
+ iptr elements_offset;
+ i32 elements;
+ u32 buffer;
+ u32 vao;
+} BeamformerRenderModel;
typedef struct {
u32 programs[BeamformerShaderKind_ComputeCount];
@@ -99,6 +115,8 @@ typedef struct {
uv4 dec_data_dim;
u32 rf_raw_size;
+
+ BeamformerRenderModel unit_cube_model;
} ComputeShaderCtx;
typedef enum {
@@ -142,7 +160,8 @@ typedef struct {
ComputeTimingInfo buffer[4096];
} ComputeTimingTable;
-typedef struct BeamformFrame {
+typedef struct BeamformerFrame BeamformerFrame;
+struct BeamformerFrame {
uv3 dim;
u32 texture;
@@ -156,12 +175,12 @@ typedef struct BeamformFrame {
u32 compound_count;
u32 id;
- struct BeamformFrame *next;
-} BeamformFrame;
+ BeamformerFrame *next;
+};
-struct BeamformComputeFrame {
- BeamformFrame frame;
- b32 ready_to_present;
+struct BeamformerComputeFrame {
+ BeamformerFrame frame;
+ b32 ready_to_present;
BeamformerViewPlaneTag view_plane_tag;
};
@@ -194,14 +213,14 @@ typedef struct {
/* TODO(rnp): this is nasty and should be removed */
b32 ui_read_params;
- BeamformComputeFrame beamform_frames[MAX_BEAMFORMED_SAVED_FRAMES];
- BeamformComputeFrame *latest_frame;
+ BeamformerComputeFrame beamform_frames[MAX_BEAMFORMED_SAVED_FRAMES];
+ BeamformerComputeFrame *latest_frame;
u32 next_render_frame_index;
u32 display_frame_index;
/* NOTE: this will only be used when we are averaging */
- u32 averaged_frame_index;
- BeamformComputeFrame averaged_frames[2];
+ u32 averaged_frame_index;
+ BeamformerComputeFrame averaged_frames[2];
ComputeShaderCtx csctx;
diff --git a/beamformer_parameters.h b/beamformer_parameters.h
@@ -24,6 +24,7 @@ typedef enum {
COMPUTE_SHADERS
#undef X
BeamformerShaderKind_Render2D,
+ BeamformerShaderKind_Render3D,
BeamformerShaderKind_Count,
BeamformerShaderKind_ComputeCount = BeamformerShaderKind_Render2D,
diff --git a/beamformer_work_queue.h b/beamformer_work_queue.h
@@ -4,8 +4,8 @@
#define BEAMFORMER_SHARED_MEMORY_VERSION (7UL)
-typedef struct BeamformComputeFrame BeamformComputeFrame;
-typedef struct ShaderReloadContext ShaderReloadContext;
+typedef struct BeamformerComputeFrame BeamformerComputeFrame;
+typedef struct ShaderReloadContext ShaderReloadContext;
typedef enum {
BeamformerWorkKind_Compute,
@@ -58,7 +58,7 @@ typedef enum {BEAMFORMER_SHARED_MEMORY_LOCKS BeamformerSharedMemoryLockKind_Coun
/* NOTE: discriminated union based on type */
typedef struct {
union {
- BeamformComputeFrame *frame;
+ BeamformerComputeFrame *frame;
BeamformerUploadContext upload_context;
BeamformerExportContext export_context;
ShaderReloadContext *shader_reload_context;
diff --git a/intrinsics.c b/intrinsics.c
@@ -39,6 +39,9 @@
#define atomic_or_u32(ptr, n) _InterlockedOr((volatile u32 *)(ptr), (n))
#define atan2_f32(y, x) atan2f(y, x)
+ #define cos_f32(a) cosf(a)
+ #define sin_f32(a) sinf(a)
+ #define tan_f32(a) tanf(a)
#define ceil_f32(a) ceilf(a)
#define sqrt_f32(a) sqrtf(a)
@@ -68,6 +71,9 @@
#define atomic_store_u32 atomic_store_u64
#define atan2_f32(y, x) __builtin_atan2f(y, x)
+ #define cos_f32(a) __builtin_cosf(a)
+ #define sin_f32(a) __builtin_sinf(a)
+ #define tan_f32(a) __builtin_tanf(a)
#define ceil_f32(a) __builtin_ceilf(a)
#define sqrt_f32(a) __builtin_sqrtf(a)
diff --git a/math.c b/math.c
@@ -0,0 +1,444 @@
+function void
+fill_kronecker_sub_matrix(i32 *out, i32 out_stride, i32 scale, i32 *b, uv2 b_dim)
+{
+ f32x4 vscale = dup_f32x4(scale);
+ for (u32 i = 0; i < b_dim.y; i++) {
+ for (u32 j = 0; j < b_dim.x; j += 4, b += 4) {
+ f32x4 vb = cvt_i32x4_f32x4(load_i32x4(b));
+ store_i32x4(cvt_f32x4_i32x4(mul_f32x4(vscale, vb)), out + j);
+ }
+ out += out_stride;
+ }
+}
+
+/* NOTE: this won't check for valid space/etc and assumes row major order */
+function void
+kronecker_product(i32 *out, i32 *a, uv2 a_dim, i32 *b, uv2 b_dim)
+{
+ uv2 out_dim = {.x = a_dim.x * b_dim.x, .y = a_dim.y * b_dim.y};
+ ASSERT(out_dim.y % 4 == 0);
+ for (u32 i = 0; i < a_dim.y; i++) {
+ i32 *vout = out;
+ for (u32 j = 0; j < a_dim.x; j++, a++) {
+ fill_kronecker_sub_matrix(vout, out_dim.y, *a, b, b_dim);
+ vout += b_dim.y;
+ }
+ out += out_dim.y * b_dim.x;
+ }
+}
+
+/* NOTE/TODO: to support even more hadamard sizes use the Paley construction */
+function i32 *
+make_hadamard_transpose(Arena *a, u32 dim)
+{
+ read_only local_persist i32 hadamard_12_12_transpose[] = {
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, -1, -1, 1, -1, -1, -1, 1, 1, 1, -1, 1,
+ 1, 1, -1, -1, 1, -1, -1, -1, 1, 1, 1, -1,
+ 1, -1, 1, -1, -1, 1, -1, -1, -1, 1, 1, 1,
+ 1, 1, -1, 1, -1, -1, 1, -1, -1, -1, 1, 1,
+ 1, 1, 1, -1, 1, -1, -1, 1, -1, -1, -1, 1,
+ 1, 1, 1, 1, -1, 1, -1, -1, 1, -1, -1, -1,
+ 1, -1, 1, 1, 1, -1, 1, -1, -1, 1, -1, -1,
+ 1, -1, -1, 1, 1, 1, -1, 1, -1, -1, 1, -1,
+ 1, -1, -1, -1, 1, 1, 1, -1, 1, -1, -1, 1,
+ 1, 1, -1, -1, -1, 1, 1, 1, -1, 1, -1, -1,
+ 1, -1, 1, -1, -1, -1, 1, 1, 1, -1, 1, -1,
+ };
+
+ read_only local_persist i32 hadamard_20_20_transpose[] = {
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, -1, 1, -1, 1, 1, 1, 1, -1, -1, 1,
+ 1, -1, 1, 1, -1, -1, -1, -1, 1, -1, 1, -1, 1, 1, 1, 1, -1, -1, 1, -1,
+ 1, 1, 1, -1, -1, -1, -1, 1, -1, 1, -1, 1, 1, 1, 1, -1, -1, 1, -1, -1,
+ 1, 1, -1, -1, -1, -1, 1, -1, 1, -1, 1, 1, 1, 1, -1, -1, 1, -1, -1, 1,
+ 1, -1, -1, -1, -1, 1, -1, 1, -1, 1, 1, 1, 1, -1, -1, 1, -1, -1, 1, 1,
+ 1, -1, -1, -1, 1, -1, 1, -1, 1, 1, 1, 1, -1, -1, 1, -1, -1, 1, 1, -1,
+ 1, -1, -1, 1, -1, 1, -1, 1, 1, 1, 1, -1, -1, 1, -1, -1, 1, 1, -1, -1,
+ 1, -1, 1, -1, 1, -1, 1, 1, 1, 1, -1, -1, 1, -1, -1, 1, 1, -1, -1, -1,
+ 1, 1, -1, 1, -1, 1, 1, 1, 1, -1, -1, 1, -1, -1, 1, 1, -1, -1, -1, -1,
+ 1, -1, 1, -1, 1, 1, 1, 1, -1, -1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1,
+ 1, 1, -1, 1, 1, 1, 1, -1, -1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, -1,
+ 1, -1, 1, 1, 1, 1, -1, -1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, -1, 1,
+ 1, 1, 1, 1, 1, -1, -1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, -1, 1, -1,
+ 1, 1, 1, 1, -1, -1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, -1, 1, -1, 1,
+ 1, 1, 1, -1, -1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, -1, 1, -1, 1, 1,
+ 1, 1, -1, -1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, -1, 1, -1, 1, 1, 1,
+ 1, -1, -1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, -1, 1, -1, 1, 1, 1, 1,
+ 1, -1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, -1, 1, -1, 1, 1, 1, 1, -1,
+ 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, -1, 1, -1, 1, 1, 1, 1, -1, -1,
+ };
+
+ i32 *result = 0;
+
+ b32 power_of_2 = ISPOWEROF2(dim);
+ b32 multiple_of_12 = dim % 12 == 0;
+ b32 multiple_of_20 = dim % 20 == 0;
+ iz elements = dim * dim;
+
+ u32 base_dim = 0;
+ if (power_of_2) {
+ base_dim = dim;
+ } else if (multiple_of_20 && ISPOWEROF2(dim / 20)) {
+ base_dim = 20;
+ dim /= 20;
+ } else if (multiple_of_12 && ISPOWEROF2(dim / 12)) {
+ base_dim = 12;
+ dim /= 12;
+ }
+
+ if (ISPOWEROF2(dim) && base_dim && arena_capacity(a, i32) >= elements * (1 + (dim != base_dim))) {
+ result = push_array(a, i32, elements);
+
+ Arena tmp = *a;
+ i32 *m = dim == base_dim ? result : push_array(&tmp, i32, elements);
+
+ #define IND(i, j) ((i) * dim + (j))
+ m[0] = 1;
+ for (u32 k = 1; k < dim; k *= 2) {
+ for (u32 i = 0; i < k; i++) {
+ for (u32 j = 0; j < k; j++) {
+ i32 val = m[IND(i, j)];
+ m[IND(i + k, j)] = val;
+ m[IND(i, j + k)] = val;
+ m[IND(i + k, j + k)] = -val;
+ }
+ }
+ }
+ #undef IND
+
+ i32 *m2 = 0;
+ uv2 m2_dim;
+ switch (base_dim) {
+ case 12:{ m2 = hadamard_12_12_transpose; m2_dim = (uv2){{12, 12}}; }break;
+ case 20:{ m2 = hadamard_20_20_transpose; m2_dim = (uv2){{20, 20}}; }break;
+ }
+ if (m2) kronecker_product(result, m, (uv2){{dim, dim}}, m2, m2_dim);
+ }
+
+ return result;
+}
+
+function b32
+uv2_equal(uv2 a, uv2 b)
+{
+ return a.x == b.x && a.y == b.y;
+}
+
+function b32
+uv3_equal(uv3 a, uv3 b)
+{
+ return a.x == b.x && a.y == b.y && a.z == b.z;
+}
+
+function v2
+clamp_v2_rect(v2 v, Rect r)
+{
+ v2 result = v;
+ result.x = CLAMP(v.x, r.pos.x, r.pos.x + r.size.x);
+ result.y = CLAMP(v.y, r.pos.y, r.pos.y + r.size.y);
+ return result;
+}
+
+function v2
+v2_scale(v2 a, f32 scale)
+{
+ v2 result;
+ result.x = a.x * scale;
+ result.y = a.y * scale;
+ return result;
+}
+
+function v2
+v2_add(v2 a, v2 b)
+{
+ v2 result;
+ result.x = a.x + b.x;
+ result.y = a.y + b.y;
+ return result;
+}
+
+function v2
+v2_sub(v2 a, v2 b)
+{
+ v2 result = v2_add(a, v2_scale(b, -1.0f));
+ return result;
+}
+
+function v2
+v2_mul(v2 a, v2 b)
+{
+ v2 result;
+ result.x = a.x * b.x;
+ result.y = a.y * b.y;
+ return result;
+}
+
+function v2
+v2_div(v2 a, v2 b)
+{
+ v2 result;
+ result.x = a.x / b.x;
+ result.y = a.y / b.y;
+ return result;
+}
+
+function v2
+v2_floor(v2 a)
+{
+ v2 result;
+ result.x = (i32)a.x;
+ result.y = (i32)a.y;
+ return result;
+}
+
+function f32
+v2_magnitude(v2 a)
+{
+ f32 result = sqrt_f32(a.x * a.x + a.y * a.y);
+ return result;
+}
+
+
+function v3
+cross(v3 a, v3 b)
+{
+ v3 result;
+ result.x = a.y * b.z - a.z * b.y;
+ result.y = a.z * b.x - a.x * b.z;
+ result.z = a.x * b.y - a.y * b.x;
+ return result;
+}
+
+function v3
+v3_scale(v3 a, f32 scale)
+{
+ v3 result;
+ result.x = scale * a.x;
+ result.y = scale * a.y;
+ result.z = scale * a.z;
+ return result;
+}
+
+function v3
+v3_add(v3 a, v3 b)
+{
+ v3 result;
+ result.x = a.x + b.x;
+ result.y = a.y + b.y;
+ result.z = a.z + b.z;
+ return result;
+}
+
+function v3
+v3_sub(v3 a, v3 b)
+{
+ v3 result = v3_add(a, v3_scale(b, -1.0f));
+ return result;
+}
+
+function f32
+v3_dot(v3 a, v3 b)
+{
+ f32 result = a.x * b.x + a.y * b.y + a.z * b.z;
+ return result;
+}
+
+function f32
+v3_length_squared(v3 a)
+{
+ f32 result = v3_dot(a, a);
+ return result;
+}
+
+function v3
+v3_normalize(v3 a)
+{
+ v3 result = v3_scale(a, 1.0f / sqrt_f32(v3_length_squared(a)));
+ return result;
+}
+
+function uv4
+uv4_from_u32_array(u32 v[4])
+{
+ uv4 result;
+ result.E[0] = v[0];
+ result.E[1] = v[1];
+ result.E[2] = v[2];
+ result.E[3] = v[3];
+ return result;
+}
+
+function b32
+uv4_equal(uv4 a, uv4 b)
+{
+ return a.x == b.x && a.y == b.y && a.z == b.z && a.w == b.w;
+}
+
+function v4
+v4_from_f32_array(f32 v[4])
+{
+ v4 result;
+ result.E[0] = v[0];
+ result.E[1] = v[1];
+ result.E[2] = v[2];
+ result.E[3] = v[3];
+ return result;
+}
+
+function v4
+v4_scale(v4 a, f32 scale)
+{
+ v4 result;
+ result.x = scale * a.x;
+ result.y = scale * a.y;
+ result.z = scale * a.z;
+ result.w = scale * a.w;
+ return result;
+}
+
+function v4
+v4_add(v4 a, v4 b)
+{
+ v4 result;
+ result.x = a.x + b.x;
+ result.y = a.y + b.y;
+ result.z = a.z + b.z;
+ result.w = a.w + b.w;
+ return result;
+}
+
+function v4
+v4_sub(v4 a, v4 b)
+{
+ v4 result = v4_add(a, v4_scale(b, -1));
+ return result;
+}
+
+function f32
+v4_dot(v4 a, v4 b)
+{
+ f32 result = a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w;
+ return result;
+}
+
+function v4
+v4_lerp(v4 a, v4 b, f32 t)
+{
+ v4 result = v4_add(a, v4_scale(v4_sub(b, a), t));
+ return result;
+}
+
+function v4
+m4_row(m4 a, u32 row)
+{
+ v4 result;
+ result.E[0] = a.c[0].E[row];
+ result.E[1] = a.c[1].E[row];
+ result.E[2] = a.c[2].E[row];
+ result.E[3] = a.c[3].E[row];
+ return result;
+}
+
+function v4
+m4_column(m4 a, u32 column)
+{
+ v4 result = a.c[column];
+ return result;
+}
+
+function m4
+m4_mul(m4 a, m4 b)
+{
+ m4 result;
+ for (u32 i = 0; i < countof(result.E); i++) {
+ u32 base = i / 4;
+ u32 sub = i % 4;
+ v4 v1 = m4_row(a, base);
+ v4 v2 = m4_column(b, sub);
+ result.E[i] = v4_dot(v1, v2);
+ }
+ return result;
+}
+
+function m4
+m4_rotation_about_y(f32 turns)
+{
+ f32 sa = sin_f32(turns * 2 * PI);
+ f32 ca = cos_f32(turns * 2 * PI);
+ m4 result;
+ result.c[0] = (v4){{ca, 0, -sa, 0}};
+ result.c[1] = (v4){{0, 1, 0, 0}};
+ result.c[2] = (v4){{sa, 0, ca, 0}};
+ result.c[3] = (v4){{0, 0, 0, 1}};
+ return result;
+}
+
+function m4
+y_aligned_volume_transform(v3 extent, v3 translation, f32 rotation_turns)
+{
+ m4 S;
+ S.c[0] = (v4){{extent.x, 0, 0, 0}};
+ S.c[1] = (v4){{0, extent.y, 0, 0}};
+ S.c[2] = (v4){{0, 0, extent.z, 0}};
+ S.c[3] = (v4){{0, 0, 0, 1}};
+
+ m4 R = m4_rotation_about_y(rotation_turns);
+
+ m4 T;
+ T.c[0] = (v4){{1, 0, 0, translation.x}};
+ T.c[1] = (v4){{0, 1, 0, translation.y}};
+ T.c[2] = (v4){{0, 0, 1, translation.z}};
+ T.c[3] = (v4){{0, 0, 0, 1}};
+
+ m4 result = m4_mul(m4_mul(R, S), T);
+ return result;
+}
+
+function v4
+m4_mul_v4(m4 a, v4 v)
+{
+ v4 result;
+ result.x = v4_dot(m4_row(a, 0), v);
+ result.y = v4_dot(m4_row(a, 1), v);
+ result.z = v4_dot(m4_row(a, 2), v);
+ result.w = v4_dot(m4_row(a, 3), v);
+ return result;
+}
+
+function m4
+perspective_projection(f32 n, f32 f, f32 fov, f32 aspect)
+{
+ m4 result;
+ f32 t = tan_f32(fov / 2.0f);
+ f32 r = t * aspect;
+ f32 a = -(f + n) / (f - n);
+ f32 b = -2 * f * n / (f - n);
+ result.c[0] = (v4){{1 / r, 0, 0, 0}};
+ result.c[1] = (v4){{0, 1 / t, 0, 0}};
+ result.c[2] = (v4){{0, 0, a, -1}};
+ result.c[3] = (v4){{0, 0, b, 0}};
+ return result;
+}
+
+function m4
+camera_look_at(v3 camera, v3 point)
+{
+ v3 orthogonal = {{0, 1.0f, 0}};
+ v3 normal = v3_normalize(v3_sub(camera, point));
+ v3 right = cross(orthogonal, normal);
+ v3 up = cross(normal, right);
+
+ v3 translate;
+ camera = v3_sub((v3){0}, camera);
+ translate.x = v3_dot(camera, right);
+ translate.y = v3_dot(camera, up);
+ translate.z = v3_dot(camera, normal);
+
+ m4 result;
+ result.c[0] = (v4){{right.x, up.x, normal.x, 0}};
+ result.c[1] = (v4){{right.y, up.y, normal.y, 0}};
+ result.c[2] = (v4){{right.z, up.z, normal.z, 0}};
+ result.c[3] = (v4){{translate.x, translate.y, translate.z, 1}};
+ return result;
+}
diff --git a/opengl.h b/opengl.h
@@ -21,6 +21,7 @@
#define GL_MAX_3D_TEXTURE_SIZE 0x8073
#define GL_MULTISAMPLE 0x809D
#define GL_CLAMP_TO_BORDER 0x812D
+#define GL_CLAMP_TO_EDGE 0x812F
#define GL_DEPTH_COMPONENT24 0x81A6
#define GL_MAJOR_VERSION 0x821B
#define GL_MINOR_VERSION 0x821C
diff --git a/shaders/render_2d.frag.glsl b/shaders/render_2d.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] */
@@ -12,15 +11,13 @@ vec3 hsv2rgb(vec3 hsv)
void main()
{
- ivec3 out_data_dim = textureSize(u_out_data_tex, 0);
+ ivec3 out_data_dim = textureSize(u_texture, 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(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);
-
+ vec3 tex_coord = vec3(texture_coordinate.x, 0.5, texture_coordinate.y);
+ 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/shaders/render_3d.frag.glsl b/shaders/render_3d.frag.glsl
@@ -0,0 +1,48 @@
+/* See LICENSE for license details. */
+
+/* input: h [0,360] | s,v [0, 1] *
+ * output: rgb [0,1] */
+vec3 hsv2rgb(vec3 hsv)
+{
+ vec3 k = mod(vec3(5, 3, 1) + hsv.x / 60, 6);
+ k = max(min(min(k, 4 - k), 1), 0);
+ return hsv.z - hsv.z * hsv.y * k;
+}
+
+bool bounding_box_test(vec3 coord, float p)
+{
+ 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;
+}
+
+void main()
+{
+ float smp = length(texture(u_texture, texture_coordinate).xy);
+ float threshold_val = pow(10.0f, u_threshold / 20.0f);
+ smp = clamp(smp, 0.0f, threshold_val);
+ smp = smp / threshold_val;
+ smp = pow(smp, u_gamma);
+
+ //float t = test_texture_coordinate.y;
+ //smp = smp * smoothstep(-0.4, 1.1, t) * u_gain;
+
+ if (u_log_scale) {
+ smp = 20 * log(smp) / log(10);
+ smp = clamp(smp, -u_db_cutoff, 0) / -u_db_cutoff;
+ smp = 1 - smp;
+ }
+
+ if (bounding_box_test(test_texture_coordinate, u_bb_fraction)) {
+ out_colour = u_bb_colour;
+ } else {
+ out_colour = vec4(smp, smp, smp, 1);
+ }
+
+ //out_colour = vec4(textureQueryLod(u_texture, texture_coordinate).y, 0, 0, 1);
+ //out_colour = vec4(abs(normal), 1);
+ //out_colour = vec4(1, 1, 1, smp);
+ //out_colour = vec4(smp * abs(normal), 1);
+}
diff --git a/static.c b/static.c
@@ -212,6 +212,42 @@ function FILE_WATCH_CALLBACK_FN(load_cuda_lib)
return result;
}
+function BeamformerRenderModel
+render_model_from_arrays(f32 *vertices, f32 *normals, u16 *indices, u32 index_count)
+{
+ BeamformerRenderModel result = {0};
+
+ i32 buffer_size = index_count * (6 * sizeof(f32) + sizeof(u16));
+ i32 indices_offset = index_count * (6 * sizeof(f32));
+ i32 vert_size = index_count * 3 * sizeof(f32);
+ i32 ind_size = index_count * sizeof(u16);
+
+ result.elements = index_count;
+ result.elements_offset = indices_offset;
+
+ glCreateBuffers(1, &result.buffer);
+ glNamedBufferStorage(result.buffer, buffer_size, 0, GL_DYNAMIC_STORAGE_BIT);
+ glNamedBufferSubData(result.buffer, 0, vert_size, vertices);
+ glNamedBufferSubData(result.buffer, vert_size, vert_size, normals);
+ glNamedBufferSubData(result.buffer, indices_offset, ind_size, indices);
+
+ glCreateVertexArrays(1, &result.vao);
+ glVertexArrayVertexBuffer(result.vao, 0, result.buffer, 0, 3 * sizeof(f32));
+ glVertexArrayVertexBuffer(result.vao, 1, result.buffer, vert_size, 3 * sizeof(f32));
+ glVertexArrayElementBuffer(result.vao, result.buffer);
+
+ glEnableVertexArrayAttrib(result.vao, 0);
+ glEnableVertexArrayAttrib(result.vao, 1);
+
+ glVertexArrayAttribFormat(result.vao, 0, 3, GL_FLOAT, 0, 0);
+ glVertexArrayAttribFormat(result.vao, 1, 3, GL_FLOAT, 0, vert_size);
+
+ glVertexArrayAttribBinding(result.vao, 0, 0);
+ glVertexArrayAttribBinding(result.vao, 1, 0);
+
+ return result;
+}
+
#define GLFW_VISIBLE 0x00020004
void glfwWindowHint(i32, i32);
iptr glfwCreateWindow(i32, i32, char *, iptr, iptr);
@@ -375,15 +411,16 @@ setup_beamformer(BeamformerCtx *ctx, BeamformerInput *input, Arena *memory)
render_2d->name = s8("shaders/render_2d.glsl");
render_2d->gl_type = GL_FRAGMENT_SHADER;
render_2d->kind = BeamformerShaderKind_Render2D;
- render_2d->shader = &fvr->shader;
+ render_2d->shader = fvr->shaders + 0;
render_2d->header = s8(""
"layout(location = 0) in vec2 texture_coordinate;\n"
"layout(location = 0) out vec4 v_out_colour;\n\n"
- "layout(location = " str(FRAME_VIEW_RENDER_DYNAMIC_RANGE_LOC) ") uniform float u_db_cutoff = 60;\n"
- "layout(location = " str(FRAME_VIEW_RENDER_THRESHOLD_LOC) ") uniform float u_threshold = 40;\n"
- "layout(location = " str(FRAME_VIEW_RENDER_GAMMA_LOC) ") uniform float u_gamma = 1;\n"
- "layout(location = " str(FRAME_VIEW_RENDER_LOG_SCALE_LOC) ") uniform bool u_log_scale;\n"
- "\n#line 1\n");
+ "layout(location = " str(FRAME_VIEW_DYNAMIC_RANGE_LOC) ") uniform float u_db_cutoff = 60;\n"
+ "layout(location = " str(FRAME_VIEW_THRESHOLD_LOC) ") uniform float u_threshold = 40;\n"
+ "layout(location = " str(FRAME_VIEW_GAMMA_LOC) ") uniform float u_gamma = 1;\n"
+ "layout(location = " str(FRAME_VIEW_LOG_SCALE_LOC) ") uniform bool u_log_scale;\n"
+ "\n"
+ "layout(binding = 0) uniform sampler3D u_texture;\n");
render_2d->link = push_struct(memory, typeof(*render_2d));
render_2d->link->gl_type = GL_VERTEX_SHADER;
render_2d->link->link = render_2d;
@@ -400,6 +437,128 @@ setup_beamformer(BeamformerCtx *ctx, BeamformerInput *input, Arena *memory)
"}\n");
reload_shader(&ctx->os, render_2d->path, (iptr)render_2d, *memory);
os_add_file_watch(&ctx->os, memory, render_2d->path, reload_shader, (iptr)render_2d);
+
+ ShaderReloadContext *render_3d = push_struct(memory, typeof(*render_3d));
+ render_3d->beamformer_context = ctx;
+ render_3d->path = s8(static_path_join("shaders", "render_3d.frag.glsl"));
+ render_3d->name = s8("shaders/render_3d.glsl");
+ render_3d->gl_type = GL_FRAGMENT_SHADER;
+ render_3d->kind = BeamformerShaderKind_Render3D;
+ render_3d->shader = fvr->shaders + 1;
+ render_3d->header = s8(""
+ "layout(location = 0) in vec3 normal;\n"
+ "layout(location = 1) in vec3 texture_coordinate;\n\n"
+ "layout(location = 2) in vec3 test_texture_coordinate;\n\n"
+ "layout(location = 0) out vec4 out_colour;\n\n"
+ "layout(location = " str(FRAME_VIEW_DYNAMIC_RANGE_LOC) ") uniform float u_db_cutoff = 60;\n"
+ "layout(location = " str(FRAME_VIEW_THRESHOLD_LOC) ") uniform float u_threshold = 40;\n"
+ "layout(location = " str(FRAME_VIEW_GAMMA_LOC) ") uniform float u_gamma = 1;\n"
+ "layout(location = " str(FRAME_VIEW_LOG_SCALE_LOC) ") uniform bool u_log_scale;\n"
+ "layout(location = " str(FRAME_VIEW_BB_COLOUR_LOC) ") uniform vec4 u_bb_colour = vec4(" str(FRAME_VIEW_BB_COLOUR) ");\n"
+ "layout(location = " str(FRAME_VIEW_BB_FRACTION_LOC) ") uniform float u_bb_fraction = " str(FRAME_VIEW_BB_FRACTION) ";\n"
+ "\n"
+ "layout(binding = 0) uniform sampler3D u_texture;\n");
+
+ render_3d->link = push_struct(memory, typeof(*render_3d));
+ render_3d->link->gl_type = GL_VERTEX_SHADER;
+ render_3d->link->link = render_3d;
+ render_3d->link->header = s8(""
+ "layout(location = 0) in vec3 v_position;\n"
+ "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"
+ "layout(location = 2) out vec3 f_orig_texture_coordinate;\n"
+ "\n"
+ "layout(location = " str(FRAME_VIEW_MODEL_MATRIX_LOC) ") uniform mat4 u_model;\n"
+ "layout(location = " str(FRAME_VIEW_VIEW_MATRIX_LOC) ") uniform mat4 u_view;\n"
+ "layout(location = " str(FRAME_VIEW_PROJ_MATRIX_LOC) ") uniform mat4 u_projection;\n"
+ "\n"
+ "\n"
+ "void main()\n"
+ "{\n"
+ "\tvec3 pos = v_position;\n"
+ "\tf_orig_texture_coordinate = (2 * v_position + 1) / 2;\n"
+ //"\tif (v_position.y == -1) pos.x = clamp(v_position.x, -u_clip_fraction, u_clip_fraction);\n"
+ "\tvec3 tex_coord = (2 * pos + 1) / 2;\n"
+ "\tf_texture_coordinate = tex_coord.xzy;\n"
+ //"\tf_texture_coordinate = u_swizzle? tex_coord.xzy : tex_coord;\n"
+ //"\tf_normal = normalize(mat3(u_model) * v_normal);\n"
+ "\tf_normal = v_normal;\n"
+ "\tgl_Position = u_projection * u_view * u_model * vec4(pos, 1);\n"
+ "}\n");
+ reload_shader(&ctx->os, render_3d->path, (iptr)render_3d, *memory);
+ os_add_file_watch(&ctx->os, memory, render_3d->path, reload_shader, (iptr)render_3d);
+
+ f32 unit_cube_vertices[] = {
+ 0.5f, 0.5f, -0.5f,
+ 0.5f, 0.5f, -0.5f,
+ 0.5f, 0.5f, -0.5f,
+ 0.5f, -0.5f, -0.5f,
+ 0.5f, -0.5f, -0.5f,
+ 0.5f, -0.5f, -0.5f,
+ 0.5f, 0.5f, 0.5f,
+ 0.5f, 0.5f, 0.5f,
+ 0.5f, 0.5f, 0.5f,
+ 0.5f, -0.5f, 0.5f,
+ 0.5f, -0.5f, 0.5f,
+ 0.5f, -0.5f, 0.5f,
+ -0.5f, 0.5f, -0.5f,
+ -0.5f, 0.5f, -0.5f,
+ -0.5f, 0.5f, -0.5f,
+ -0.5f, -0.5f, -0.5f,
+ -0.5f, -0.5f, -0.5f,
+ -0.5f, -0.5f, -0.5f,
+ -0.5f, 0.5f, 0.5f,
+ -0.5f, 0.5f, 0.5f,
+ -0.5f, 0.5f, 0.5f,
+ -0.5f, -0.5f, 0.5f,
+ -0.5f, -0.5f, 0.5f,
+ -0.5f, -0.5f, 0.5f
+ };
+ f32 unit_cube_normals[] = {
+ 0.0f, 0.0f, -1.0f,
+ 0.0f, 1.0f, 0.0f,
+ 1.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, -1.0f,
+ 0.0f, -1.0f, 0.0f,
+ 1.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 1.0f,
+ 0.0f, 1.0f, 0.0f,
+ 1.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 1.0f,
+ 0.0f, -1.0f, 0.0f,
+ 1.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, -1.0f,
+ 0.0f, 1.0f, 0.0f,
+ -1.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, -1.0f,
+ 0.0f, -1.0f, 0.0f,
+ -1.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 1.0f,
+ 0.0f, 1.0f, 0.0f,
+ -1.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 1.0f,
+ 0.0f, -1.0f, 0.0f,
+ -1.0f, 0.0f, 0.0f
+ };
+ u16 unit_cube_indices[] = {
+ 1, 13, 19,
+ 1, 19, 7,
+ 9, 6, 18,
+ 9, 18, 21,
+ 23, 20, 14,
+ 23, 14, 17,
+ 16, 4, 10,
+ 16, 10, 22,
+ 5, 2, 8,
+ 5, 8, 11,
+ 15, 12, 0,
+ 15, 0, 3
+ };
+
+ cs->unit_cube_model = render_model_from_arrays(unit_cube_vertices, unit_cube_normals,
+ unit_cube_indices, countof(unit_cube_indices));
}
function void
diff --git a/ui.c b/ui.c
@@ -1,5 +1,7 @@
/* See LICENSE for license details. */
/* TODO(rnp):
+ * [ ]: MSAA for 3D views
+ * [ ]: refactor: render_2d.frag should be merged into render_3d.frag
* [ ]: refactor: ui should be in its own thread and that thread should only be concerned with the ui
* [ ]: refactor: ui shouldn't fully destroy itself on hot reload
* [ ]: refactor: remove all the excessive measure_texts (cell drawing, hover_interaction in params table)
@@ -198,8 +200,10 @@ typedef struct {
X(FV_COPY_VERTICAL, "Copy Vertical")
#define GLOBAL_MENU_BUTTONS \
- X(GM_OPEN_LIVE_VIEW_RIGHT, "Open Live View Right") \
- X(GM_OPEN_LIVE_VIEW_BELOW, "Open Live View Below")
+ X(GM_OPEN_LIVE_VIEW_RIGHT, "Open Live View Right") \
+ X(GM_OPEN_LIVE_VIEW_BELOW, "Open Live View Below") \
+ X(GM_OPEN_XPLANE_VIEW_RIGHT, "Open Live X-Plane Right") \
+ X(GM_OPEN_XPLANE_VIEW_BELOW, "Open Live X-Plane Below")
#define X(id, text) UI_BID_ ##id,
typedef enum {
@@ -228,6 +232,7 @@ typedef enum {
V_INPUT = 1 << 0,
V_TEXT = 1 << 1,
V_RADIO_BUTTON = 1 << 2,
+ V_IMAGING_PARAM = 1 << 28,
V_CAUSES_COMPUTE = 1 << 29,
V_UPDATE_VIEW = 1 << 30,
} VariableFlags;
@@ -268,21 +273,28 @@ typedef enum {
} BeamformerFrameViewKind;
typedef struct BeamformerFrameView {
- Variable lateral_scale_bar;
- Variable axial_scale_bar;
+ union {
+ Variable plane_offsets[2];
+ struct {
+ Variable lateral_scale_bar;
+ Variable axial_scale_bar;
+ };
+ };
/* NOTE(rnp): these are pointers because they are added to the menu and will
- * be put onto the freelist if the view is closed */
+ * be put onto the freelist if the view is closed. Some are optional based on kind. */
Variable *lateral_scale_bar_active;
Variable *axial_scale_bar_active;
Variable *log_scale;
+ Variable *demo;
/* NOTE(rnp): if type is LATEST selects which type of latest to use
* if type is INDEXED selects the index */
Variable *cycler;
u32 cycler_state;
- v4 min_coordinate;
- v4 max_coordinate;
+ v3 min_coordinate;
+ v3 max_coordinate;
+ f32 rotation;
Ruler ruler;
@@ -290,13 +302,12 @@ typedef struct BeamformerFrameView {
Variable dynamic_range;
Variable gamma;
- FrameViewRenderContext *ctx;
- BeamformFrame *frame;
+ BeamformerFrame *frame;
struct BeamformerFrameView *prev, *next;
uv2 texture_dim;
+ u32 textures[2];
u32 texture_mipmaps;
- u32 texture;
BeamformerFrameViewKind kind;
b32 needs_update;
@@ -339,7 +350,7 @@ struct BeamformerUI {
BeamformerFrameView *views;
BeamformerFrameView *view_freelist;
- BeamformFrame *frame_freelist;
+ BeamformerFrame *frame_freelist;
Interaction interaction;
Interaction hot_interaction;
@@ -347,9 +358,12 @@ struct BeamformerUI {
InputState text_input_state;
+ /* TODO(rnp): ideally this isn't copied all over the place */
+ BeamformerRenderModel unit_cube_model;
+
v2_sll *scale_bar_savepoint_freelist;
- BeamformFrame *latest_plane[BeamformerViewPlaneTag_Count + 1];
+ BeamformerFrame *latest_plane[BeamformerViewPlaneTag_Count + 1];
BeamformerUIParameters params;
b32 flush_params;
@@ -503,7 +517,7 @@ function Texture
make_raylib_texture(BeamformerFrameView *v)
{
Texture result;
- result.id = v->texture;
+ result.id = v->textures[0];
result.width = v->texture_dim.w;
result.height = v->texture_dim.h;
result.mipmaps = v->texture_mipmaps;
@@ -590,7 +604,7 @@ table_iterator_new(Table *table, TableIteratorKind kind, Arena *a, i32 starting_
result->frame.row_index = starting_row;
result->start_x = at.x;
result->cell_rect.size.h = font->baseSize;
- result->cell_rect.pos = add_v2(at, scale_v2(table->cell_pad, 0.5));
+ result->cell_rect.pos = v2_add(at, v2_scale(table->cell_pad, 0.5));
result->cell_rect.pos.y += (starting_row - 1) * (result->cell_rect.size.h + table->cell_pad.h + table->row_border_thick);
da_reserve(a, &result->stack, 4);
return result;
@@ -754,24 +768,26 @@ table_end_subtable(Table *table)
function void
resize_frame_view(BeamformerFrameView *view, uv2 dim)
{
- glDeleteTextures(1, &view->texture);
- glCreateTextures(GL_TEXTURE_2D, 1, &view->texture);
+ glDeleteTextures(countof(view->textures), view->textures);
+ glCreateTextures(GL_TEXTURE_2D, countof(view->textures), view->textures);
view->texture_dim = dim;
view->texture_mipmaps = ctz_u32(MAX(dim.x, dim.y)) + 1;
- /* TODO(rnp): HDR? */
- glTextureStorage2D(view->texture, view->texture_mipmaps, GL_RGBA8, dim.x, dim.y);
- glGenerateTextureMipmap(view->texture);
+ glTextureStorage2D(view->textures[0], view->texture_mipmaps, GL_RGBA8, dim.x, dim.y);
+ glTextureStorage2D(view->textures[1], 1, GL_DEPTH_COMPONENT24, dim.x, dim.y);
+
+ glGenerateTextureMipmap(view->textures[0]);
/* NOTE(rnp): work around raylib's janky texture sampling */
- glTextureParameteri(view->texture, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
- glTextureParameteri(view->texture, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
- glTextureParameterfv(view->texture, GL_TEXTURE_BORDER_COLOR, (f32 []){0, 0, 0, 1});
- glTextureParameteri(view->texture, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- glTextureParameteri(view->texture, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTextureParameteri(view->textures[0], GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
+ glTextureParameteri(view->textures[0], GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
+ glTextureParameterfv(view->textures[0], GL_TEXTURE_BORDER_COLOR, (f32 []){0, 0, 0, 1});
+ /* TODO(rnp): better choice when depth component is included */
+ glTextureParameteri(view->textures[0], GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTextureParameteri(view->textures[0], GL_TEXTURE_MIN_FILTER, GL_NEAREST);
/* TODO(rnp): add some ID for the specific view here */
- LABEL_GL_OBJECT(GL_TEXTURE, view->texture, s8("Frame View Texture"));
+ LABEL_GL_OBJECT(GL_TEXTURE, view->textures[0], s8("Frame View Texture"));
}
function void
@@ -1063,23 +1079,40 @@ add_beamformer_frame_view(BeamformerUI *ui, Variable *parent, Arena *arena,
bv->gamma.scaled_real32.val = 1.0f;
bv->gamma.scaled_real32.scale = 0.05f;
- fill_variable(&bv->lateral_scale_bar, var, s8(""), V_INPUT, VT_SCALE_BAR, ui->small_font);
- fill_variable(&bv->axial_scale_bar, var, s8(""), V_INPUT, VT_SCALE_BAR, ui->small_font);
- ScaleBar *lateral = &bv->lateral_scale_bar.scale_bar;
- ScaleBar *axial = &bv->axial_scale_bar.scale_bar;
- lateral->direction = SB_LATERAL;
- axial->direction = SB_AXIAL;
- lateral->scroll_scale = (v2){.x = -0.5e-3, .y = 0.5e-3};
- axial->scroll_scale = (v2){.x = 0, .y = 1e-3};
- lateral->zoom_starting_coord = F32_INFINITY;
- axial->zoom_starting_coord = F32_INFINITY;
-
Variable *menu = result->view.menu = add_variable_group(ui, 0, arena, s8(""),
VariableGroupKind_List, ui->small_font);
menu->parent = result;
- #define X(id, text) add_button(ui, menu, arena, s8(text), UI_BID_ ##id, 0, ui->small_font);
- FRAME_VIEW_BUTTONS
- #undef X
+
+ switch (kind) {
+ case BeamformerFrameViewKind_3DXPlane:{
+ resize_frame_view(bv, (uv2){{1024, 1024}});
+ fill_variable(&bv->plane_offsets[0], var, s8("XZ Offset"), V_INPUT|V_IMAGING_PARAM, VT_F32, ui->small_font);
+ fill_variable(&bv->plane_offsets[1], var, s8("YZ Offset"), V_INPUT|V_IMAGING_PARAM, VT_F32, ui->small_font);
+ bv->demo = add_variable(ui, menu, arena, s8("Demo Mode"), V_INPUT|V_UPDATE_VIEW|V_RADIO_BUTTON,
+ VT_B32, ui->small_font);
+ }break;
+ default:{
+ fill_variable(&bv->lateral_scale_bar, var, s8(""), V_INPUT, VT_SCALE_BAR, ui->small_font);
+ fill_variable(&bv->axial_scale_bar, var, s8(""), V_INPUT, VT_SCALE_BAR, ui->small_font);
+ ScaleBar *lateral = &bv->lateral_scale_bar.scale_bar;
+ ScaleBar *axial = &bv->axial_scale_bar.scale_bar;
+ lateral->direction = SB_LATERAL;
+ axial->direction = SB_AXIAL;
+ lateral->scroll_scale = (v2){{-0.5e-3, 0.5e-3}};
+ axial->scroll_scale = (v2){{ 0, 1.0e-3}};
+ lateral->zoom_starting_coord = F32_INFINITY;
+ axial->zoom_starting_coord = F32_INFINITY;
+
+ #define X(id, text) add_button(ui, menu, arena, s8(text), UI_BID_ ##id, 0, ui->small_font);
+ FRAME_VIEW_BUTTONS
+ #undef X
+
+ bv->axial_scale_bar_active = add_variable(ui, menu, arena, s8("Axial Scale Bar"),
+ V_INPUT|V_RADIO_BUTTON, VT_B32, ui->small_font);
+ bv->lateral_scale_bar_active = add_variable(ui, menu, arena, s8("Lateral Scale Bar"),
+ V_INPUT|V_RADIO_BUTTON, VT_B32, ui->small_font);
+ }break;
+ }
switch (kind) {
case BeamformerFrameViewKind_Latest:{
@@ -1097,13 +1130,9 @@ add_beamformer_frame_view(BeamformerUI *ui, Variable *parent, Arena *arena,
default:{}break;
}
- bv->log_scale = add_variable(ui, menu, arena, s8("Log Scale"),
- V_INPUT|V_UPDATE_VIEW|V_RADIO_BUTTON, VT_B32,
- ui->small_font);
- bv->axial_scale_bar_active = add_variable(ui, menu, arena, s8("Axial Scale Bar"),
- V_INPUT|V_RADIO_BUTTON, VT_B32, ui->small_font);
- bv->lateral_scale_bar_active = add_variable(ui, menu, arena, s8("Lateral Scale Bar"),
- V_INPUT|V_RADIO_BUTTON, VT_B32, ui->small_font);
+ bv->log_scale = add_variable(ui, menu, arena, s8("Log Scale"),
+ V_INPUT|V_UPDATE_VIEW|V_RADIO_BUTTON, VT_B32, ui->small_font);
+
add_global_menu_to_group(ui, arena, menu);
return result;
}
@@ -1165,30 +1194,30 @@ ui_split_region(BeamformerUI *ui, Variable *region, Variable *split_side, Region
function void
ui_fill_live_frame_view(BeamformerUI *ui, BeamformerFrameView *bv)
{
- ScaleBar *lateral = &bv->lateral_scale_bar.scale_bar;
- ScaleBar *axial = &bv->axial_scale_bar.scale_bar;
- lateral->min_value = ui->params.output_min_coordinate + 0;
- lateral->max_value = ui->params.output_max_coordinate + 0;
- axial->min_value = ui->params.output_min_coordinate + 2;
- axial->max_value = ui->params.output_max_coordinate + 2;
- bv->axial_scale_bar_active->bool32 = 1;
- bv->lateral_scale_bar_active->bool32 = 1;
- bv->ctx = ui->frame_view_render_context;
- bv->axial_scale_bar.flags |= V_CAUSES_COMPUTE;
- bv->lateral_scale_bar.flags |= V_CAUSES_COMPUTE;
+ if (bv->kind != BeamformerFrameViewKind_3DXPlane) {
+ ScaleBar *lateral = &bv->lateral_scale_bar.scale_bar;
+ ScaleBar *axial = &bv->axial_scale_bar.scale_bar;
+ lateral->min_value = ui->params.output_min_coordinate + 0;
+ lateral->max_value = ui->params.output_max_coordinate + 0;
+ axial->min_value = ui->params.output_min_coordinate + 2;
+ axial->max_value = ui->params.output_max_coordinate + 2;
+ bv->axial_scale_bar_active->bool32 = 1;
+ bv->lateral_scale_bar_active->bool32 = 1;
+ bv->axial_scale_bar.flags |= V_CAUSES_COMPUTE;
+ bv->lateral_scale_bar.flags |= V_CAUSES_COMPUTE;
+ }
}
function void
-ui_add_live_frame_view(BeamformerUI *ui, Variable *view, RegionSplitDirection direction)
+ui_add_live_frame_view(BeamformerUI *ui, Variable *view, RegionSplitDirection direction,
+ BeamformerFrameViewKind kind)
{
Variable *region = view->parent;
assert(region->type == VT_UI_REGION_SPLIT);
assert(view->type == VT_UI_VIEW);
Variable *new_region = ui_split_region(ui, region, view, direction);
- new_region->region_split.right = add_beamformer_frame_view(ui, new_region, &ui->arena,
- BeamformerFrameViewKind_Latest, 1);
-
+ new_region->region_split.right = add_beamformer_frame_view(ui, new_region, &ui->arena, kind, 1);
ui_fill_live_frame_view(ui, new_region->region_split.right->group.first->generic);
}
@@ -1216,14 +1245,13 @@ ui_copy_frame(BeamformerUI *ui, Variable *view, RegionSplitDirection direction)
axial->min_value = &bv->min_coordinate.z;
axial->max_value = &bv->max_coordinate.z;
- bv->ctx = old->ctx;
bv->needs_update = 1;
bv->threshold.real32 = old->threshold.real32;
bv->dynamic_range.real32 = old->dynamic_range.real32;
bv->gamma.real32 = old->gamma.real32;
bv->log_scale->bool32 = old->log_scale->bool32;
- bv->min_coordinate = old->frame->min_coordinate;
- bv->max_coordinate = old->frame->max_coordinate;
+ bv->min_coordinate = old->frame->min_coordinate.xyz;
+ bv->max_coordinate = old->frame->max_coordinate.xyz;
bv->frame = SLLPop(ui->frame_freelist);
if (!bv->frame) bv->frame = push_struct(&ui->arena, typeof(*bv->frame));
@@ -1241,6 +1269,103 @@ ui_copy_frame(BeamformerUI *ui, Variable *view, RegionSplitDirection direction)
resize_frame_view(bv, (uv2){.x = bv->frame->dim.x, .y = bv->frame->dim.z});
}
+function v3
+x_plane_size(BeamformerUI *ui)
+{
+ v3 min = v4_from_f32_array(ui->params.output_min_coordinate).xyz;
+ v3 max = v4_from_f32_array(ui->params.output_max_coordinate).xyz;
+ v3 result = v3_sub(max, min);
+ swap(result.y, result.z);
+ result.x = MAX(1e-3, result.x);
+ result.y = MAX(1e-3, result.y);
+ result.z = MAX(1e-3, result.z);
+ return result;
+}
+
+function v3
+x_plane_position(BeamformerUI *ui)
+{
+ f32 y_min = ui->params.output_min_coordinate[2];
+ f32 y_max = ui->params.output_max_coordinate[2];
+ v3 result = {.y = y_min + (y_max - y_min) / 2};
+ return result;
+}
+
+function v3
+camera_for_x_plane_view(BeamformerUI *ui, BeamformerFrameView *view)
+{
+ v3 size = x_plane_size(ui);
+ v3 target = x_plane_position(ui);
+ f32 dist = v2_magnitude(XY(size));
+ v3 result = v3_add(target, (v3){{dist, -0.5f * size.y * tan_f32(50.0f * PI / 180.0f), dist}});
+ return result;
+}
+
+function m4
+view_matrix_for_x_plane_view(BeamformerUI *ui, BeamformerFrameView *view, v3 camera)
+{
+ assert(view->kind == BeamformerFrameViewKind_3DXPlane);
+ m4 result = camera_look_at(camera, x_plane_position(ui));
+ return result;
+}
+
+function m4
+projection_matrix_for_x_plane_view(BeamformerFrameView *view)
+{
+ assert(view->kind == BeamformerFrameViewKind_3DXPlane);
+ f32 aspect = (f32)view->texture_dim.w / view->texture_dim.h;
+ m4 result = perspective_projection(10e-3, 500e-3, 45.0f * PI / 180.0f, aspect);
+ return result;
+}
+
+function void
+render_3D_xplane(BeamformerUI *ui, BeamformerFrameView *view, FrameViewRenderContext *ctx)
+{
+ u32 program = ctx->shaders[1];
+ glBindVertexArray(ui->unit_cube_model.vao);
+
+ if (view->demo->bool32) {
+ view->rotation += dt_for_frame * 0.125f;
+ if (view->rotation > 1.0f) view->rotation -= 1.0f;
+ }
+
+ v3 camera = camera_for_x_plane_view(ui, view);
+ m4 view_m = view_matrix_for_x_plane_view(ui, view, camera);
+ m4 projection = projection_matrix_for_x_plane_view(view);
+
+ v3 scale = x_plane_size(ui);
+ v3 model_translate = x_plane_position(ui);
+ m4 model_transform = y_aligned_volume_transform(scale, model_translate, view->rotation);
+
+ v4 colour = v4_lerp(FG_COLOUR, HOVERED_COLOUR, view->plane_offsets[0].hover_t);
+
+ u32 texture = 0;
+ if (ui->latest_plane[BeamformerViewPlaneTag_XZ])
+ texture = ui->latest_plane[BeamformerViewPlaneTag_XZ]->texture;
+
+ glProgramUniformMatrix4fv(program, FRAME_VIEW_VIEW_MATRIX_LOC, 1, 0, view_m.E);
+ glProgramUniformMatrix4fv(program, FRAME_VIEW_PROJ_MATRIX_LOC, 1, 0, projection.E);
+ glProgramUniformMatrix4fv(program, FRAME_VIEW_MODEL_MATRIX_LOC, 1, 0, model_transform.E);
+ glProgramUniform4fv(program, FRAME_VIEW_BB_COLOUR_LOC, 1, colour.E);
+ glBindTextureUnit(0, texture);
+ glDrawElements(GL_TRIANGLES, ui->unit_cube_model.elements, GL_UNSIGNED_SHORT,
+ (void *)ui->unit_cube_model.elements_offset);
+
+ model_translate.y -= 0.0001;
+ model_transform = y_aligned_volume_transform(scale, model_translate, view->rotation + 0.25f);
+ colour = v4_lerp(FG_COLOUR, HOVERED_COLOUR, view->plane_offsets[1].hover_t);
+
+ texture = 0;
+ if (ui->latest_plane[BeamformerViewPlaneTag_YZ])
+ texture = ui->latest_plane[BeamformerViewPlaneTag_YZ]->texture;
+
+ glProgramUniformMatrix4fv(program, FRAME_VIEW_MODEL_MATRIX_LOC, 1, 0, model_transform.E);
+ glProgramUniform4fv(program, FRAME_VIEW_BB_COLOUR_LOC, 1, colour.E);
+ glBindTextureUnit(0, texture);
+ glDrawElements(GL_TRIANGLES, ui->unit_cube_model.elements, GL_UNSIGNED_SHORT,
+ (void *)ui->unit_cube_model.elements_offset);
+}
+
function b32
view_update(BeamformerUI *ui, BeamformerFrameView *view)
{
@@ -1249,8 +1374,8 @@ view_update(BeamformerUI *ui, BeamformerFrameView *view)
view->needs_update |= view->frame != ui->latest_plane[index];
view->frame = ui->latest_plane[index];
if (view->needs_update) {
- view->min_coordinate = v4_from_f32_array(ui->params.output_min_coordinate);
- view->max_coordinate = v4_from_f32_array(ui->params.output_max_coordinate);
+ view->min_coordinate = v4_from_f32_array(ui->params.output_min_coordinate).xyz;
+ view->max_coordinate = v4_from_f32_array(ui->params.output_max_coordinate).xyz;
}
}
@@ -1258,39 +1383,59 @@ view_update(BeamformerUI *ui, BeamformerFrameView *view)
/* TODO(rnp): add method of setting a target size in frame view */
uv2 current = view->texture_dim;
uv2 target = {.w = ui->params.output_points[0], .h = ui->params.output_points[2]};
- if (view->kind != BeamformerFrameViewKind_Copy && !uv2_equal(current, target) && !uv2_equal(target, (uv2){0})) {
+ if (view->kind != BeamformerFrameViewKind_Copy &&
+ view->kind != BeamformerFrameViewKind_3DXPlane &&
+ !uv2_equal(current, target) && !uv2_equal(target, (uv2){0}))
+ {
resize_frame_view(view, target);
view->needs_update = 1;
}
- return (view->ctx->updated || view->needs_update) && view->frame;
+ b32 result = view->kind == BeamformerFrameViewKind_3DXPlane;
+ result |= (ui->frame_view_render_context->updated || view->needs_update) && view->frame;
+ return result;
}
function void
update_frame_views(BeamformerUI *ui, Rect window)
{
- b32 fbo_bound = 0;
+ FrameViewRenderContext *ctx = ui->frame_view_render_context;
+ b32 fbo_bound = 0, shader_3d = 0;
for (BeamformerFrameView *view = ui->views; view; view = view->next) {
if (view_update(ui, view)) {
if (!fbo_bound) {
fbo_bound = 1;
- glBindFramebuffer(GL_FRAMEBUFFER, view->ctx->framebuffer);
- glUseProgram(view->ctx->shader);
- glBindVertexArray(view->ctx->vao);
+ glBindFramebuffer(GL_FRAMEBUFFER, ctx->framebuffer);
+ glUseProgram(ctx->shaders[0]);
+ glEnable(GL_DEPTH_TEST);
}
+
+ if (view->kind == BeamformerFrameViewKind_3DXPlane) {
+ if (!shader_3d) { glUseProgram(ctx->shaders[1]); shader_3d = 1; }
+ } else {
+ if ( shader_3d) { glUseProgram(ctx->shaders[0]); shader_3d = 0; }
+ }
+
+ glNamedFramebufferTexture(ctx->framebuffer, GL_COLOR_ATTACHMENT0, view->textures[0], 0);
+ glNamedFramebufferTexture(ctx->framebuffer, GL_DEPTH_ATTACHMENT, view->textures[1], 0);
+ glClearNamedFramebufferfv(ctx->framebuffer, GL_COLOR, 0, (f32 []){0, 0, 0, 0});
+ glClearNamedFramebufferfv(ctx->framebuffer, GL_DEPTH, 0, (f32 []){1});
+
+ u32 program = ctx->shaders[shader_3d];
glViewport(0, 0, view->texture_dim.w, view->texture_dim.h);
- glNamedFramebufferTexture(view->ctx->framebuffer, GL_COLOR_ATTACHMENT0,
- view->texture, 0);
- glClearNamedFramebufferfv(view->ctx->framebuffer, GL_COLOR, 0,
- (f32 []){0.79, 0.46, 0.77, 1});
- glBindTextureUnit(0, view->frame->texture);
- glProgramUniform1f(view->ctx->shader, FRAME_VIEW_RENDER_DYNAMIC_RANGE_LOC, view->dynamic_range.real32);
- glProgramUniform1f(view->ctx->shader, FRAME_VIEW_RENDER_THRESHOLD_LOC, view->threshold.real32);
- glProgramUniform1f(view->ctx->shader, FRAME_VIEW_RENDER_GAMMA_LOC, view->gamma.scaled_real32.val);
- glProgramUniform1ui(view->ctx->shader, FRAME_VIEW_RENDER_LOG_SCALE_LOC, view->log_scale->bool32);
-
- glDrawArrays(GL_TRIANGLES, 0, 6);
- glGenerateTextureMipmap(view->texture);
+ glProgramUniform1f(program, FRAME_VIEW_THRESHOLD_LOC, view->threshold.real32);
+ glProgramUniform1f(program, FRAME_VIEW_DYNAMIC_RANGE_LOC, view->dynamic_range.real32);
+ glProgramUniform1f(program, FRAME_VIEW_GAMMA_LOC, view->gamma.scaled_real32.val);
+ glProgramUniform1ui(program, FRAME_VIEW_LOG_SCALE_LOC, view->log_scale->bool32);
+
+ if (view->kind == BeamformerFrameViewKind_3DXPlane) {
+ render_3D_xplane(ui, view, ctx);
+ } else {
+ glBindVertexArray(ctx->vao);
+ glBindTextureUnit(0, view->frame->texture);
+ glDrawArrays(GL_TRIANGLES, 0, 6);
+ }
+ glGenerateTextureMipmap(view->textures[0]);
view->needs_update = 0;
}
}
@@ -1299,6 +1444,7 @@ update_frame_views(BeamformerUI *ui, Rect window)
glViewport(window.pos.x, window.pos.y, window.size.w, window.size.h);
/* NOTE(rnp): I don't trust raylib to not mess with us */
glBindVertexArray(0);
+ glDisable(GL_DEPTH_TEST);
}
}
@@ -1322,17 +1468,6 @@ fade(Color a, f32 visibility)
return a;
}
-function v4
-lerp_v4(v4 a, v4 b, f32 t)
-{
- return (v4){
- .x = a.x + t * (b.x - a.x),
- .y = a.y + t * (b.y - a.y),
- .z = a.z + t * (b.z - a.z),
- .w = a.w + t * (b.w - a.w),
- };
-}
-
function s8
push_das_shader_kind(Stream *s, DASShaderKind shader, u32 transmit_count)
{
@@ -1399,7 +1534,7 @@ push_custom_view_title(Stream *s, Variable *var)
function v2
draw_text_base(Font font, s8 text, v2 pos, Color colour)
{
- v2 off = floor_v2(pos);
+ v2 off = v2_floor(pos);
for (iz i = 0; i < text.len; i++) {
/* NOTE: assumes font glyphs are ordered ASCII */
i32 idx = text.data[i] - 0x20;
@@ -1432,10 +1567,10 @@ draw_outlined_text(s8 text, v2 pos, TextSpec *ts)
f32 ow = ts->outline_thick;
Color outline = colour_from_normalized(ts->outline_colour);
Color colour = colour_from_normalized(ts->colour);
- draw_text_base(*ts->font, text, sub_v2(pos, (v2){.x = ow, .y = ow}), outline);
- draw_text_base(*ts->font, text, sub_v2(pos, (v2){.x = ow, .y = -ow}), outline);
- draw_text_base(*ts->font, text, sub_v2(pos, (v2){.x = -ow, .y = ow}), outline);
- draw_text_base(*ts->font, text, sub_v2(pos, (v2){.x = -ow, .y = -ow}), outline);
+ draw_text_base(*ts->font, text, v2_sub(pos, (v2){{ ow, ow}}), outline);
+ draw_text_base(*ts->font, text, v2_sub(pos, (v2){{ ow, -ow}}), outline);
+ draw_text_base(*ts->font, text, v2_sub(pos, (v2){{-ow, ow}}), outline);
+ draw_text_base(*ts->font, text, v2_sub(pos, (v2){{-ow, -ow}}), outline);
v2 result = draw_text_base(*ts->font, text, pos, colour);
@@ -1539,7 +1674,7 @@ interaction_is_hot(BeamformerUI *ui, Interaction a)
function b32
point_in_rect(v2 p, Rect r)
{
- v2 end = add_v2(r.pos, r.size);
+ v2 end = v2_add(r.pos, r.size);
b32 result = BETWEEN(p.x, r.pos.x, end.x) & BETWEEN(p.y, r.pos.y, end.y);
return result;
}
@@ -1547,16 +1682,16 @@ point_in_rect(v2 p, Rect r)
function v2
screen_point_to_world_2d(v2 p, v2 screen_min, v2 screen_max, v2 world_min, v2 world_max)
{
- v2 pixels_to_m = div_v2(sub_v2(world_max, world_min), sub_v2(screen_max, screen_min));
- v2 result = add_v2(mul_v2(sub_v2(p, screen_min), pixels_to_m), world_min);
+ v2 pixels_to_m = v2_div(v2_sub(world_max, world_min), v2_sub(screen_max, screen_min));
+ v2 result = v2_add(v2_mul(v2_sub(p, screen_min), pixels_to_m), world_min);
return result;
}
function v2
world_point_to_screen_2d(v2 p, v2 world_min, v2 world_max, v2 screen_min, v2 screen_max)
{
- v2 m_to_pixels = div_v2(sub_v2(screen_max, screen_min), sub_v2(world_max, world_min));
- v2 result = add_v2(mul_v2(sub_v2(p, world_min), m_to_pixels), screen_min);
+ v2 m_to_pixels = v2_div(v2_sub(screen_max, screen_min), v2_sub(world_max, world_min));
+ v2 result = v2_add(v2_mul(v2_sub(p, world_min), m_to_pixels), screen_min);
return result;
}
@@ -1578,11 +1713,11 @@ draw_close_button(BeamformerUI *ui, Variable *close, v2 mouse, Rect r, v2 x_scal
assert(close->type == VT_UI_BUTTON);
hover_interaction(ui, mouse, auto_interaction(r, close));
- Color colour = colour_from_normalized(lerp_v4(MENU_CLOSE_COLOUR, FG_COLOUR, close->hover_t));
+ Color colour = colour_from_normalized(v4_lerp(MENU_CLOSE_COLOUR, FG_COLOUR, close->hover_t));
r = scale_rect_centered(r, x_scale);
- DrawLineEx(r.pos.rl, add_v2(r.pos, r.size).rl, 4, colour);
- DrawLineEx(add_v2(r.pos, (v2){.x = r.size.w}).rl,
- add_v2(r.pos, (v2){.y = r.size.h}).rl, 4, colour);
+ DrawLineEx(r.pos.rl, v2_add(r.pos, r.size).rl, 4, colour);
+ DrawLineEx(v2_add(r.pos, (v2){.x = r.size.w}).rl,
+ v2_add(r.pos, (v2){.y = r.size.h}).rl, 4, colour);
}
function Rect
@@ -1620,12 +1755,12 @@ draw_title_bar(BeamformerUI *ui, Arena arena, Variable *ui_view, Rect r, v2 mous
Interaction interaction = {.kind = InteractionKind_Menu, .var = view->menu, .rect = menu};
hover_interaction(ui, mouse, interaction);
- Color colour = colour_from_normalized(lerp_v4(MENU_PLUS_COLOUR, FG_COLOUR, view->menu->hover_t));
+ Color colour = colour_from_normalized(v4_lerp(MENU_PLUS_COLOUR, FG_COLOUR, view->menu->hover_t));
menu = shrink_rect_centered(menu, (v2){.x = 14, .y = 14});
- DrawLineEx(add_v2(menu.pos, (v2){.x = menu.size.w / 2}).rl,
- add_v2(menu.pos, (v2){.x = menu.size.w / 2, .y = menu.size.h}).rl, 4, colour);
- DrawLineEx(add_v2(menu.pos, (v2){.y = menu.size.h / 2}).rl,
- add_v2(menu.pos, (v2){.x = menu.size.w, .y = menu.size.h / 2}).rl, 4, colour);
+ DrawLineEx(v2_add(menu.pos, (v2){.x = menu.size.w / 2}).rl,
+ v2_add(menu.pos, (v2){.x = menu.size.w / 2, .y = menu.size.h}).rl, 4, colour);
+ DrawLineEx(v2_add(menu.pos, (v2){.y = menu.size.h / 2}).rl,
+ v2_add(menu.pos, (v2){.x = menu.size.w, .y = menu.size.h / 2}).rl, 4, colour);
}
v2 title_pos = title_rect.pos;
@@ -1646,14 +1781,14 @@ draw_ruler(BeamformerUI *ui, Arena arena, v2 start_point, v2 end_point,
{
b32 draw_plus = SIGN(start_value) != SIGN(end_value);
- end_point = sub_v2(end_point, start_point);
+ end_point = v2_sub(end_point, start_point);
f32 rotation = atan2_f32(end_point.y, end_point.x) * 180 / PI;
rlPushMatrix();
rlTranslatef(start_point.x, start_point.y, 0);
rlRotatef(rotation, 0, 0, 1);
- f32 inc = magnitude_v2(end_point) / segments;
+ f32 inc = v2_magnitude(end_point) / segments;
f32 value_inc = (end_value - start_value) / segments;
f32 value = start_value;
@@ -1702,7 +1837,7 @@ do_scale_bar(BeamformerUI *ui, Arena arena, Variable *scale_bar, v2 mouse, Rect
Rect tick_rect = draw_rect;
v2 start_pos = tick_rect.pos;
v2 end_pos = tick_rect.pos;
- v2 relative_mouse = sub_v2(mouse, tick_rect.pos);
+ v2 relative_mouse = v2_sub(mouse, tick_rect.pos);
f32 markers[2];
u32 marker_count = 1;
@@ -1731,7 +1866,7 @@ do_scale_bar(BeamformerUI *ui, Arena arena, Variable *scale_bar, v2 mouse, Rect
marker_count = 2;
draw_ruler(ui, arena, start_pos, end_pos, start_value, end_value, markers, marker_count,
- tick_count, suffix, RULER_COLOUR, lerp_v4(FG_COLOUR, HOVERED_COLOUR, scale_bar->hover_t));
+ tick_count, suffix, RULER_COLOUR, v4_lerp(FG_COLOUR, HOVERED_COLOUR, scale_bar->hover_t));
}
function v2
@@ -1753,7 +1888,7 @@ draw_radio_button(BeamformerUI *ui, Variable *var, v2 at, v2 mouse, v4 base_colo
hover_rect = shrink_rect_centered(hover_rect, (v2){.x = 8, .y = 8});
Rect inner = shrink_rect_centered(hover_rect, (v2){.x = 4, .y = 4});
- v4 fill = lerp_v4(value? base_colour : (v4){0}, HOVERED_COLOUR, var->hover_t);
+ v4 fill = v4_lerp(value? base_colour : (v4){0}, HOVERED_COLOUR, var->hover_t);
DrawRectangleRoundedLinesEx(hover_rect.rl, 0.2, 0, 2, colour_from_normalized(base_colour));
DrawRectangleRec(inner.rl, colour_from_normalized(fill));
@@ -1777,7 +1912,7 @@ draw_variable(BeamformerUI *ui, Arena arena, Variable *var, v2 at, v2 mouse, v4
text_rect = extend_rect_centered(text_rect, (v2){.x = 8});
if (hover_interaction(ui, mouse, auto_interaction(text_rect, var)) && (var->flags & V_TEXT))
ui->text_input_state.hot_font = text_spec.font;
- text_spec.colour = lerp_v4(base_colour, HOVERED_COLOUR, var->hover_t);
+ text_spec.colour = v4_lerp(base_colour, HOVERED_COLOUR, var->hover_t);
}
draw_text(text, at, &text_spec);
@@ -1878,11 +2013,11 @@ draw_table(BeamformerUI *ui, Arena arena, Table *table, Rect draw_rect, TextSpec
function void
draw_view_ruler(BeamformerFrameView *view, Arena a, Rect view_rect, TextSpec ts)
{
- v2 vr_max_p = add_v2(view_rect.pos, view_rect.size);
+ v2 vr_max_p = v2_add(view_rect.pos, view_rect.size);
v2 start_p = world_point_to_screen_2d(view->ruler.start, XZ(view->min_coordinate),
XZ(view->max_coordinate), view_rect.pos, vr_max_p);
v2 end_p = world_point_to_screen_2d(view->ruler.end, XZ(view->min_coordinate),
- XZ(view->max_coordinate), view_rect.pos, vr_max_p);
+ XZ(view->max_coordinate), view_rect.pos, vr_max_p);
Color rl_colour = colour_from_normalized(ts.colour);
DrawCircleV(start_p.rl, 3, rl_colour);
@@ -1890,12 +2025,12 @@ draw_view_ruler(BeamformerFrameView *view, Arena a, Rect view_rect, TextSpec ts)
DrawCircleV(end_p.rl, 3, rl_colour);
Stream buf = arena_stream(a);
- stream_append_f64(&buf, 1e3 * magnitude_v2(sub_v2(view->ruler.end, view->ruler.start)), 100);
+ stream_append_f64(&buf, 1e3 * v2_magnitude(v2_sub(view->ruler.end, view->ruler.start)), 100);
stream_append_s8(&buf, s8(" mm"));
v2 txt_p = start_p;
v2 txt_s = measure_text(*ts.font, stream_to_s8(&buf));
- v2 pixel_delta = sub_v2(start_p, end_p);
+ v2 pixel_delta = v2_sub(start_p, end_p);
if (pixel_delta.y < 0) txt_p.y -= txt_s.y;
if (pixel_delta.x < 0) txt_p.x -= txt_s.x;
if (txt_p.x < view_rect.pos.x) txt_p.x = view_rect.pos.x;
@@ -1904,19 +2039,72 @@ draw_view_ruler(BeamformerFrameView *view, Arena a, Rect view_rect, TextSpec ts)
draw_text(stream_to_s8(&buf), txt_p, &ts);
}
+function v2
+draw_frame_view_controls(BeamformerUI *ui, Arena arena, BeamformerFrameView *view, Rect vr, v2 mouse)
+{
+ TextSpec text_spec = {.font = &ui->small_font, .flags = TF_LIMITED|TF_OUTLINED,
+ .colour = RULER_COLOUR, .outline_thick = 1, .outline_colour.a = 1,
+ .limits.size.x = vr.size.w};
+
+ Table *table = table_new(&arena, 3, TextAlignment_Left, TextAlignment_Left, TextAlignment_Left);
+ table_push_parameter_row(table, &arena, view->gamma.name, &view->gamma, s8(""));
+ table_push_parameter_row(table, &arena, view->threshold.name, &view->threshold, s8(""));
+ if (view->log_scale->bool32)
+ table_push_parameter_row(table, &arena, view->dynamic_range.name, &view->dynamic_range, s8("[dB]"));
+
+ Rect table_rect = vr;
+ f32 height = table_extent(table, arena, text_spec.font).y;
+ height = MIN(height, vr.size.h);
+ table_rect.pos.w += 8;
+ table_rect.pos.y += vr.size.h - height - 8;
+ table_rect.size.h = height;
+ table_rect.size.w = vr.size.w - 16;
+
+ return draw_table(ui, arena, table, table_rect, text_spec, mouse, 0);
+}
+
+function void
+draw_3D_xplane_frame_view(BeamformerUI *ui, Arena arena, Variable *var, Rect display_rect, v2 mouse)
+{
+ assert(var->type == VT_BEAMFORMER_FRAME_VIEW);
+ BeamformerFrameView *view = var->generic;
+
+ f32 aspect = (f32)view->texture_dim.w / (f32)view->texture_dim.h;
+ Rect vr = display_rect;
+ if (aspect > 1.0f) vr.size.w = vr.size.h;
+ else vr.size.h = vr.size.w;
+
+ if (vr.size.w > display_rect.size.w) {
+ vr.size.w -= (vr.size.w - display_rect.size.w);
+ vr.size.h = vr.size.w / aspect;
+ } else if (vr.size.h > display_rect.size.h) {
+ vr.size.h -= (vr.size.h - display_rect.size.h);
+ vr.size.w = vr.size.h * aspect;
+ }
+ vr.pos = v2_add(vr.pos, v2_scale(v2_sub(display_rect.size, vr.size), 0.5));
+
+ hover_interaction(ui, mouse, auto_interaction(vr, var));
+
+ Rectangle tex_r = {0, 0, view->texture_dim.w, view->texture_dim.h};
+ NPatchInfo tex_np = {tex_r, 0, 0, 0, 0, NPATCH_NINE_PATCH};
+ DrawTextureNPatch(make_raylib_texture(view), tex_np, vr.rl, (Vector2){0}, 0, WHITE);
+
+ draw_frame_view_controls(ui, arena, view, vr, mouse);
+}
+
function void
draw_beamformer_frame_view(BeamformerUI *ui, Arena a, Variable *var, Rect display_rect, v2 mouse)
{
assert(var->type == VT_BEAMFORMER_FRAME_VIEW);
- BeamformerFrameView *view = var->generic;
- BeamformFrame *frame = view->frame;
+ BeamformerFrameView *view = var->generic;
+ BeamformerFrame *frame = view->frame;
v2 txt_s = measure_text(ui->small_font, s8("-288.8 mm"));
f32 scale_bar_size = 1.2 * txt_s.x + RULER_TICK_LENGTH;
- v4 min = view->min_coordinate;
- v4 max = view->max_coordinate;
- v2 requested_dim = sub_v2(XZ(max), XZ(min));
+ v3 min = view->min_coordinate;
+ v3 max = view->max_coordinate;
+ v2 requested_dim = v2_sub(XZ(max), XZ(min));
f32 aspect = requested_dim.w / requested_dim.h;
Rect vr = display_rect;
@@ -1933,11 +2121,11 @@ draw_beamformer_frame_view(BeamformerUI *ui, Arena a, Variable *var, Rect displa
scale_bar_area.y += scale_bar_size;
}
- vr.size = sub_v2(vr.size, scale_bar_area);
+ vr.size = v2_sub(vr.size, scale_bar_area);
if (aspect > 1) vr.size.h = vr.size.w / aspect;
else vr.size.w = vr.size.h * aspect;
- v2 occupied = add_v2(vr.size, scale_bar_area);
+ v2 occupied = v2_add(vr.size, scale_bar_area);
if (occupied.w > display_rect.size.w) {
vr.size.w -= (occupied.w - display_rect.size.w);
vr.size.h = vr.size.w / aspect;
@@ -1945,21 +2133,19 @@ draw_beamformer_frame_view(BeamformerUI *ui, Arena a, Variable *var, Rect displa
vr.size.h -= (occupied.h - display_rect.size.h);
vr.size.w = vr.size.h * aspect;
}
- occupied = add_v2(vr.size, scale_bar_area);
- vr.pos = add_v2(vr.pos, scale_v2(sub_v2(display_rect.size, occupied), 0.5));
+ occupied = v2_add(vr.size, scale_bar_area);
+ vr.pos = v2_add(vr.pos, v2_scale(v2_sub(display_rect.size, occupied), 0.5));
/* TODO(rnp): make this depend on the requested draw orientation (x-z or y-z or x-y) */
- v2 output_dim = {
- .x = frame->max_coordinate.x - frame->min_coordinate.x,
- .y = frame->max_coordinate.z - frame->min_coordinate.z,
- };
-
+ v2 output_dim = v2_sub(XZ(frame->max_coordinate), XZ(frame->min_coordinate));
v2 pixels_per_meter = {
.w = (f32)view->texture_dim.w / output_dim.w,
.h = (f32)view->texture_dim.h / output_dim.h,
};
- v2 texture_points = mul_v2(pixels_per_meter, requested_dim);
+ /* NOTE(rnp): math to resize the texture without stretching when the view changes
+ * but the texture hasn't been (or cannot be) rebeamformed */
+ v2 texture_points = v2_mul(pixels_per_meter, requested_dim);
/* TODO(rnp): this also depends on x-y, y-z, x-z */
v2 texture_start = {
.x = pixels_per_meter.x * 0.5 * (output_dim.x - requested_dim.x),
@@ -2000,11 +2186,11 @@ draw_beamformer_frame_view(BeamformerUI *ui, Arena a, Variable *var, Rect displa
if (point_in_rect(mouse, viewer.rect)) {
ui->next_interaction = viewer;
- v2 world = screen_point_to_world_2d(mouse, vr.pos, add_v2(vr.pos, vr.size),
+ v2 world = screen_point_to_world_2d(mouse, vr.pos, v2_add(vr.pos, vr.size),
XZ(view->min_coordinate),
XZ(view->max_coordinate));
Stream buf = arena_stream(a);
- stream_append_v2(&buf, scale_v2(world, 1e3));
+ stream_append_v2(&buf, v2_scale(world, 1e3));
text_spec.limits.size.w -= 4;
v2 txt_s = measure_text(*text_spec.font, stream_to_s8(&buf));
@@ -2035,21 +2221,8 @@ draw_beamformer_frame_view(BeamformerUI *ui, Arena a, Variable *var, Rect displa
if (view->ruler.state != RulerState_None) draw_view_ruler(view, a, vr, text_spec);
- Table *table = table_new(&a, 3, TextAlignment_Left, TextAlignment_Left, TextAlignment_Left);
- table_push_parameter_row(table, &a, view->gamma.name, &view->gamma, s8(""));
- table_push_parameter_row(table, &a, view->threshold.name, &view->threshold, s8(""));
- if (view->log_scale->bool32)
- table_push_parameter_row(table, &a, view->dynamic_range.name, &view->dynamic_range, s8("[dB]"));
-
- Rect table_rect = vr;
- f32 height = table_extent(table, a, text_spec.font).y;
- height = MIN(height, vr.size.h);
- table_rect.pos.w += 8;
- table_rect.pos.y += vr.size.h - height - 8;
- table_rect.size.h = height;
- table_rect.size.w = draw_table_width - 16;
-
- draw_table(ui, a, table, table_rect, text_spec, mouse, 0);
+ vr.size.w = draw_table_width;
+ draw_frame_view_controls(ui, a, view, vr, mouse);
}
function v2
@@ -2130,14 +2303,14 @@ draw_compute_stats_bar_view(BeamformerUI *ui, Arena arena, ComputeShaderStats *s
u32 frame_index = (stats->latest_frame_index - row_index) % countof(stats->table.times);
f32 total_width = average_width * total_times[row_index] / compute_time_sum;
Rect rect;
- rect.pos = add_v2(cr.pos, (v2){.x = cr.size.w + table->cell_pad.w , .y = cr.size.h * 0.15});
+ rect.pos = v2_add(cr.pos, (v2){.x = cr.size.w + table->cell_pad.w , .y = cr.size.h * 0.15});
rect.size = (v2){.y = 0.7 * cr.size.h};
for (u32 i = 0; i < stages_count; i++) {
rect.size.w = total_width * stats->table.times[frame_index][stages[i]] / total_times[row_index];
Color color = colour_from_normalized(g_colour_palette[stages[i] % countof(g_colour_palette)]);
DrawRectangleRec(rect.rl, color);
if (point_in_rect(mouse, rect)) {
- text_pos = add_v2(rect.pos, (v2){.x = table->cell_pad.w});
+ text_pos = v2_add(rect.pos, (v2){.x = table->cell_pad.w});
mouse_text = push_compute_time(&arena, labels[stages[i]],
stats->table.times[frame_index][stages[i]]);
}
@@ -2146,8 +2319,8 @@ draw_compute_stats_bar_view(BeamformerUI *ui, Arena arena, ComputeShaderStats *s
row_index++;
}
- v2 start = add_v2(r.pos, (v2){.x = table->widths[0] + average_width + table->cell_pad.w});
- v2 end = add_v2(start, (v2){.y = result.y});
+ v2 start = v2_add(r.pos, (v2){.x = table->widths[0] + average_width + table->cell_pad.w});
+ v2 end = v2_add(start, (v2){.y = result.y});
DrawLineEx(start.rl, end.rl, 4, colour_from_normalized(FG_COLOUR));
if (mouse_text.len) {
@@ -2213,7 +2386,7 @@ draw_compute_stats_view(BeamformerUI *ui, Arena arena, Variable *view, Rect r, v
case ComputeStatsViewKind_Bar:{
result = draw_compute_stats_bar_view(ui, arena, stats, compute_stages, stages, compute_time_sum,
text_spec, r, mouse);
- r.pos = add_v2(r.pos, (v2){.y = result.y});
+ r.pos = v2_add(r.pos, (v2){.y = result.y});
}break;
InvalidDefaultCase;
}
@@ -2221,7 +2394,7 @@ draw_compute_stats_view(BeamformerUI *ui, Arena arena, Variable *view, Rect r, v
push_table_time_row(table, &arena, s8("Compute Total:"), compute_time_sum);
push_table_time_row(table, &arena, s8("RF Upload Delta:"), stats->rf_time_delta_average);
- result = add_v2(result, table_extent(table, arena, text_spec.font));
+ result = v2_add(result, table_extent(table, arena, text_spec.font));
draw_table(ui, arena, table, r, text_spec, (v2){0}, 0);
return result;
}
@@ -2352,7 +2525,7 @@ draw_ui_view_container(BeamformerUI *ui, Variable *var, v2 mouse, Rect bounds)
f32 line_height = ui->small_font.baseSize;
if (fw->rect.pos.y - line_height < 0) fw->rect.pos.y += line_height - fw->rect.pos.y;
- f32 delta_x = add_v2(fw->rect.pos, fw->rect.size).x - add_v2(bounds.size, bounds.pos).x;
+ f32 delta_x = (fw->rect.pos.x + fw->rect.size.x) - (bounds.size.x + bounds.pos.x);
if (delta_x > 0) {
fw->rect.pos.x -= delta_x;
fw->rect.pos.x = MAX(0, fw->rect.pos.x);
@@ -2413,8 +2586,12 @@ draw_ui_view(BeamformerUI *ui, Variable *ui_view, Rect r, v2 mouse, TextSpec tex
}break;
case VT_BEAMFORMER_FRAME_VIEW: {
BeamformerFrameView *bv = var->generic;
- if (frame_view_ready_to_present(bv))
- draw_beamformer_frame_view(ui, ui->arena, var, r, mouse);
+ if (bv->kind == BeamformerFrameViewKind_3DXPlane) {
+ draw_3D_xplane_frame_view(ui, ui->arena, var, r, mouse);
+ } else {
+ if (frame_view_ready_to_present(bv))
+ draw_beamformer_frame_view(ui, ui->arena, var, r, mouse);
+ }
} break;
case VT_COMPUTE_PROGRESS_BAR: {
size = draw_compute_progress_bar(ui, ui->arena, &var->compute_progress_bar, r);
@@ -2432,7 +2609,7 @@ draw_layout_variable(BeamformerUI *ui, Variable *var, Rect draw_rect, v2 mouse)
if (var->type != VT_UI_REGION_SPLIT) {
v2 shrink = {.x = UI_REGION_PAD, .y = UI_REGION_PAD};
draw_rect = shrink_rect_centered(draw_rect, shrink);
- draw_rect.size = floor_v2(draw_rect.size);
+ draw_rect.size = v2_floor(draw_rect.size);
BeginScissorMode(draw_rect.pos.x, draw_rect.pos.y, draw_rect.size.w, draw_rect.size.h);
draw_rect = draw_title_bar(ui, ui->arena, var, draw_rect, mouse);
EndScissorMode();
@@ -2442,7 +2619,7 @@ draw_layout_variable(BeamformerUI *ui, Variable *var, Rect draw_rect, v2 mouse)
if (!CheckCollisionPointRec(mouse.rl, draw_rect.rl))
mouse = (v2){.x = F32_INFINITY, .y = F32_INFINITY};
- draw_rect.size = floor_v2(draw_rect.size);
+ draw_rect.size = v2_floor(draw_rect.size);
BeginScissorMode(draw_rect.pos.x, draw_rect.pos.y, draw_rect.size.w, draw_rect.size.h);
switch (var->type) {
case VT_UI_VIEW: {
@@ -2559,7 +2736,7 @@ draw_floating_widgets(BeamformerUI *ui, Rect window_rect, v2 mouse)
v4 cursor_colour = FOCUSED_COLOUR;
cursor_colour.a = CLAMP01(is->cursor_blink_t);
- v4 text_colour = lerp_v4(FG_COLOUR, HOVERED_COLOUR, fw->child->hover_t);
+ v4 text_colour = v4_lerp(FG_COLOUR, HOVERED_COLOUR, fw->child->hover_t);
TextSpec text_spec = {.font = is->font, .colour = text_colour};
draw_text(text, text_position, &text_spec);
@@ -2716,7 +2893,7 @@ scale_bar_interaction(BeamformerUI *ui, ScaleBar *sb, v2 mouse)
if (mouse_left_pressed) {
v2 world_mouse = screen_point_to_world_2d(mouse, it->rect.pos,
- add_v2(it->rect.pos, it->rect.size),
+ v2_add(it->rect.pos, it->rect.size),
(v2){{*sb->min_value, *sb->min_value}},
(v2){{*sb->max_value, *sb->max_value}});
f32 new_coord = F32_INFINITY;
@@ -2832,10 +3009,16 @@ ui_button_interaction(BeamformerUI *ui, Variable *button)
ui_copy_frame(ui, button->parent->parent, RSD_VERTICAL);
}break;
case UI_BID_GM_OPEN_LIVE_VIEW_RIGHT:{
- ui_add_live_frame_view(ui, button->parent->parent, RSD_HORIZONTAL);
+ ui_add_live_frame_view(ui, button->parent->parent, RSD_HORIZONTAL, BeamformerFrameViewKind_Latest);
}break;
case UI_BID_GM_OPEN_LIVE_VIEW_BELOW:{
- ui_add_live_frame_view(ui, button->parent->parent, RSD_VERTICAL);
+ ui_add_live_frame_view(ui, button->parent->parent, RSD_VERTICAL, BeamformerFrameViewKind_Latest);
+ }break;
+ case UI_BID_GM_OPEN_XPLANE_VIEW_RIGHT:{
+ ui_add_live_frame_view(ui, button->parent->parent, RSD_HORIZONTAL, BeamformerFrameViewKind_3DXPlane);
+ }break;
+ case UI_BID_GM_OPEN_XPLANE_VIEW_BELOW:{
+ ui_add_live_frame_view(ui, button->parent->parent, RSD_VERTICAL, BeamformerFrameViewKind_3DXPlane);
}break;
}
}
@@ -2871,19 +3054,27 @@ ui_begin_interact(BeamformerUI *ui, BeamformerInput *input, b32 scroll)
if (scroll) {
hot->kind = InteractionKind_Scroll;
} else {
- hot->kind = InteractionKind_Nop;
BeamformerFrameView *bv = hot->var->generic;
- switch (++bv->ruler.state) {
- case RulerState_Start:{
- hot->kind = InteractionKind_Ruler;
- v2 r_max = add_v2(hot->rect.pos, hot->rect.size);
- v2 p = screen_point_to_world_2d(input->mouse, hot->rect.pos, r_max,
- XZ(bv->min_coordinate),
- XZ(bv->max_coordinate));
- bv->ruler.start = p;
+ switch (bv->kind) {
+ case BeamformerFrameViewKind_3DXPlane:{
+ if (IsMouseButtonDown(MOUSE_BUTTON_LEFT)) hot->kind = InteractionKind_Drag;
+ else hot->kind = InteractionKind_Nop;
+ }break;
+ default:{
+ hot->kind = InteractionKind_Nop;
+ switch (++bv->ruler.state) {
+ case RulerState_Start:{
+ hot->kind = InteractionKind_Ruler;
+ v2 r_max = v2_add(hot->rect.pos, hot->rect.size);
+ v2 p = screen_point_to_world_2d(input->mouse, hot->rect.pos, r_max,
+ XZ(bv->min_coordinate),
+ XZ(bv->max_coordinate));
+ bv->ruler.start = p;
+ }break;
+ case RulerState_Hold:{}break;
+ default:{ bv->ruler.state = RulerState_None; }break;
+ }
}break;
- case RulerState_Hold:{}break;
- default:{ bv->ruler.state = RulerState_None; }break;
}
}
}break;
@@ -2918,6 +3109,13 @@ ui_begin_interact(BeamformerUI *ui, BeamformerInput *input, b32 scroll)
} else {
ui->interaction.kind = InteractionKind_Nop;
}
+
+ if (ui->interaction.kind == InteractionKind_Drag) {
+ HideCursor();
+ DisableCursor();
+ /* wtf raylib */
+ SetMousePosition(input->mouse.x, input->mouse.y);
+ }
}
function void
@@ -2927,7 +3125,7 @@ ui_end_interact(BeamformerUI *ui, v2 mouse)
b32 start_compute = (it->var->flags & V_CAUSES_COMPUTE) != 0;
switch (it->kind) {
case InteractionKind_Nop:{}break;
- case InteractionKind_Drag:{}break;
+ case InteractionKind_Drag:{ EnableCursor(); }break;
case InteractionKind_Set:{
switch (it->var->type) {
case VT_B32:{ it->var->bool32 = !it->var->bool32; }break;
@@ -3021,7 +3219,7 @@ ui_interact(BeamformerUI *ui, BeamformerInput *input, Rect window_rect)
case InteractionKind_Ruler:{
assert(it->var->type == VT_BEAMFORMER_FRAME_VIEW);
BeamformerFrameView *bv = it->var->generic;
- v2 r_max = add_v2(it->rect.pos, it->rect.size);
+ v2 r_max = v2_add(it->rect.pos, it->rect.size);
v2 mouse = clamp_v2_rect(input->mouse, it->rect);
bv->ruler.end = screen_point_to_world_2d(mouse, it->rect.pos, r_max,
XZ(bv->min_coordinate),
@@ -3032,16 +3230,27 @@ ui_interact(BeamformerUI *ui, BeamformerInput *input, Rect window_rect)
ui_end_interact(ui, input->mouse);
} else {
v2 ws = window_rect.size;
- v2 dMouse = sub_v2(input->mouse, input->last_mouse);
+ v2 dMouse = v2_sub(input->mouse, input->last_mouse);
switch (ui->interaction.var->type) {
+ case VT_BEAMFORMER_FRAME_VIEW:{
+ BeamformerFrameView *bv = it->var->generic;
+ switch (bv->kind) {
+ case BeamformerFrameViewKind_3DXPlane:{
+ bv->rotation += dMouse.x / ws.w;
+ if (bv->rotation > 1.0f) bv->rotation -= 1.0f;
+ if (bv->rotation < 0.0f) bv->rotation += 1.0f;
+ }break;
+ InvalidDefaultCase;
+ }
+ }break;
case VT_UI_MENU:{
v2 *pos = &ui->interaction.var->view.rect.pos;
- *pos = clamp_v2_rect(add_v2(*pos, dMouse), window_rect);
+ *pos = clamp_v2_rect(v2_add(*pos, dMouse), window_rect);
}break;
case VT_UI_REGION_SPLIT:{
f32 min_fraction = 0;
- dMouse = mul_v2(dMouse, (v2){.x = 1.0f / ws.w, .y = 1.0f / ws.h});
+ dMouse = v2_mul(dMouse, (v2){{1.0f / ws.w, 1.0f / ws.h}});
RegionSplit *rs = &ui->interaction.var->region_split;
switch (rs->direction) {
case RSD_VERTICAL: {
@@ -3084,14 +3293,15 @@ ui_init(BeamformerCtx *ctx, Arena store)
UnloadFont(ui->small_font);
for (BeamformerFrameView *view = ui->views; view; view = view->next)
- if (view->texture)
- glDeleteTextures(1, &view->texture);
+ if (view->textures[0])
+ glDeleteTextures(countof(view->textures), view->textures);
}
ui = ctx->ui = push_struct(&store, typeof(*ui));
ui->os = &ctx->os;
ui->arena = store;
ui->frame_view_render_context = &ctx->frame_view_render_context;
+ ui->unit_cube_model = ctx->csctx.unit_cube_model;
/* TODO: build these into the binary */
/* TODO(rnp): better font, this one is jank at small sizes */
@@ -3107,7 +3317,6 @@ ui_init(BeamformerCtx *ctx, Arena store)
RSD_VERTICAL, ui->font);
split->region_split.right = add_beamformer_frame_view(ui, split, &ui->arena,
BeamformerFrameViewKind_Latest, 0);
-
ui_fill_live_frame_view(ui, split->region_split.right->view.child->generic);
split = split->region_split.left;
@@ -3135,7 +3344,7 @@ validate_ui_parameters(BeamformerUIParameters *p)
}
function void
-draw_ui(BeamformerCtx *ctx, BeamformerInput *input, BeamformFrame *frame_to_draw, BeamformerViewPlaneTag frame_plane)
+draw_ui(BeamformerCtx *ctx, BeamformerInput *input, BeamformerFrame *frame_to_draw, BeamformerViewPlaneTag frame_plane)
{
BeamformerUI *ui = ctx->ui;
BeamformerSharedMemory *sm = ctx->shared_memory.region;
diff --git a/util.c b/util.c
@@ -22,7 +22,6 @@ mem_move(u8 *dest, u8 *src, iz n)
else while (n) { n--; dest[n] = src[n]; }
}
-
function u8 *
arena_commit(Arena *a, iz size)
{
@@ -524,169 +523,6 @@ round_up_to(iz value, iz multiple)
return result;
}
-function b32
-uv2_equal(uv2 a, uv2 b)
-{
- return a.x == b.x && a.y == b.y;
-}
-
-function b32
-uv3_equal(uv3 a, uv3 b)
-{
- return a.x == b.x && a.y == b.y && a.z == b.z;
-}
-
-function v3
-cross(v3 a, v3 b)
-{
- v3 result = {
- .x = a.y * b.z - a.z * b.y,
- .y = a.z * b.x - a.x * b.z,
- .z = a.x * b.y - a.y * b.x,
- };
- return result;
-}
-
-function v3
-sub_v3(v3 a, v3 b)
-{
- v3 result = {
- .x = a.x - b.x,
- .y = a.y - b.y,
- .z = a.z - b.z,
- };
- return result;
-}
-
-function f32
-length_v3(v3 a)
-{
- f32 result = a.x * a.x + a.y * a.y + a.z * a.z;
- return result;
-}
-
-function v3
-normalize_v3(v3 a)
-{
- f32 length = length_v3(a);
- v3 result = {.x = a.x / length, .y = a.y / length, .z = a.z / length};
- return result;
-}
-
-function v2
-clamp_v2_rect(v2 v, Rect r)
-{
- v2 result = v;
- result.x = CLAMP(v.x, r.pos.x, r.pos.x + r.size.x);
- result.y = CLAMP(v.y, r.pos.y, r.pos.y + r.size.y);
- return result;
-}
-
-function v2
-add_v2(v2 a, v2 b)
-{
- v2 result = {
- .x = a.x + b.x,
- .y = a.y + b.y,
- };
- return result;
-}
-
-function v2
-sub_v2(v2 a, v2 b)
-{
- v2 result = {
- .x = a.x - b.x,
- .y = a.y - b.y,
- };
- return result;
-}
-
-function v2
-scale_v2(v2 a, f32 scale)
-{
- v2 result = {
- .x = a.x * scale,
- .y = a.y * scale,
- };
- return result;
-}
-
-function v2
-mul_v2(v2 a, v2 b)
-{
- v2 result = {
- .x = a.x * b.x,
- .y = a.y * b.y,
- };
- return result;
-}
-
-function v2
-div_v2(v2 a, v2 b)
-{
- v2 result;
- result.x = a.x / b.x;
- result.y = a.y / b.y;
- return result;
-}
-
-
-function v2
-floor_v2(v2 a)
-{
- v2 result;
- result.x = (i32)a.x;
- result.y = (i32)a.y;
- return result;
-}
-
-function f32
-magnitude_v2(v2 a)
-{
- f32 result = sqrt_f32(a.x * a.x + a.y * a.y);
- return result;
-}
-
-function uv4
-uv4_from_u32_array(u32 v[4])
-{
- uv4 result;
- result.E[0] = v[0];
- result.E[1] = v[1];
- result.E[2] = v[2];
- result.E[3] = v[3];
- return result;
-}
-
-function b32
-uv4_equal(uv4 a, uv4 b)
-{
- return a.x == b.x && a.y == b.y && a.z == b.z && a.w == b.w;
-}
-
-function v4
-v4_from_f32_array(f32 v[4])
-{
- v4 result;
- result.E[0] = v[0];
- result.E[1] = v[1];
- result.E[2] = v[2];
- result.E[3] = v[3];
- return result;
-}
-
-function v4
-sub_v4(v4 a, v4 b)
-{
- v4 result;
- result.x = a.x - b.x;
- result.y = a.y - b.y;
- result.z = a.z - b.z;
- result.w = a.w - b.w;
- return result;
-}
-
function void
split_rect_horizontal(Rect rect, f32 fraction, Rect *left, Rect *right)
{
@@ -792,123 +628,4 @@ lookup_file_watch_directory(FileWatchContext *ctx, u64 hash)
return result;
}
-function void
-fill_kronecker_sub_matrix(i32 *out, i32 out_stride, i32 scale, i32 *b, uv2 b_dim)
-{
- f32x4 vscale = dup_f32x4(scale);
- for (u32 i = 0; i < b_dim.y; i++) {
- for (u32 j = 0; j < b_dim.x; j += 4, b += 4) {
- f32x4 vb = cvt_i32x4_f32x4(load_i32x4(b));
- store_i32x4(cvt_f32x4_i32x4(mul_f32x4(vscale, vb)), out + j);
- }
- out += out_stride;
- }
-}
-
-/* NOTE: this won't check for valid space/etc and assumes row major order */
-function void
-kronecker_product(i32 *out, i32 *a, uv2 a_dim, i32 *b, uv2 b_dim)
-{
- uv2 out_dim = {.x = a_dim.x * b_dim.x, .y = a_dim.y * b_dim.y};
- ASSERT(out_dim.y % 4 == 0);
- for (u32 i = 0; i < a_dim.y; i++) {
- i32 *vout = out;
- for (u32 j = 0; j < a_dim.x; j++, a++) {
- fill_kronecker_sub_matrix(vout, out_dim.y, *a, b, b_dim);
- vout += b_dim.y;
- }
- out += out_dim.y * b_dim.x;
- }
-}
-
-/* NOTE/TODO: to support even more hadamard sizes use the Paley construction */
-function i32 *
-make_hadamard_transpose(Arena *a, u32 dim)
-{
- read_only local_persist i32 hadamard_12_12_transpose[] = {
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, -1, -1, 1, -1, -1, -1, 1, 1, 1, -1, 1,
- 1, 1, -1, -1, 1, -1, -1, -1, 1, 1, 1, -1,
- 1, -1, 1, -1, -1, 1, -1, -1, -1, 1, 1, 1,
- 1, 1, -1, 1, -1, -1, 1, -1, -1, -1, 1, 1,
- 1, 1, 1, -1, 1, -1, -1, 1, -1, -1, -1, 1,
- 1, 1, 1, 1, -1, 1, -1, -1, 1, -1, -1, -1,
- 1, -1, 1, 1, 1, -1, 1, -1, -1, 1, -1, -1,
- 1, -1, -1, 1, 1, 1, -1, 1, -1, -1, 1, -1,
- 1, -1, -1, -1, 1, 1, 1, -1, 1, -1, -1, 1,
- 1, 1, -1, -1, -1, 1, 1, 1, -1, 1, -1, -1,
- 1, -1, 1, -1, -1, -1, 1, 1, 1, -1, 1, -1,
- };
-
- read_only local_persist i32 hadamard_20_20_transpose[] = {
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, -1, 1, -1, 1, 1, 1, 1, -1, -1, 1,
- 1, -1, 1, 1, -1, -1, -1, -1, 1, -1, 1, -1, 1, 1, 1, 1, -1, -1, 1, -1,
- 1, 1, 1, -1, -1, -1, -1, 1, -1, 1, -1, 1, 1, 1, 1, -1, -1, 1, -1, -1,
- 1, 1, -1, -1, -1, -1, 1, -1, 1, -1, 1, 1, 1, 1, -1, -1, 1, -1, -1, 1,
- 1, -1, -1, -1, -1, 1, -1, 1, -1, 1, 1, 1, 1, -1, -1, 1, -1, -1, 1, 1,
- 1, -1, -1, -1, 1, -1, 1, -1, 1, 1, 1, 1, -1, -1, 1, -1, -1, 1, 1, -1,
- 1, -1, -1, 1, -1, 1, -1, 1, 1, 1, 1, -1, -1, 1, -1, -1, 1, 1, -1, -1,
- 1, -1, 1, -1, 1, -1, 1, 1, 1, 1, -1, -1, 1, -1, -1, 1, 1, -1, -1, -1,
- 1, 1, -1, 1, -1, 1, 1, 1, 1, -1, -1, 1, -1, -1, 1, 1, -1, -1, -1, -1,
- 1, -1, 1, -1, 1, 1, 1, 1, -1, -1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1,
- 1, 1, -1, 1, 1, 1, 1, -1, -1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, -1,
- 1, -1, 1, 1, 1, 1, -1, -1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, -1, 1,
- 1, 1, 1, 1, 1, -1, -1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, -1, 1, -1,
- 1, 1, 1, 1, -1, -1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, -1, 1, -1, 1,
- 1, 1, 1, -1, -1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, -1, 1, -1, 1, 1,
- 1, 1, -1, -1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, -1, 1, -1, 1, 1, 1,
- 1, -1, -1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, -1, 1, -1, 1, 1, 1, 1,
- 1, -1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, -1, 1, -1, 1, 1, 1, 1, -1,
- 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, -1, 1, -1, 1, 1, 1, 1, -1, -1,
- };
-
- i32 *result = 0;
-
- b32 power_of_2 = ISPOWEROF2(dim);
- b32 multiple_of_12 = dim % 12 == 0;
- b32 multiple_of_20 = dim % 20 == 0;
- iz elements = dim * dim;
-
- u32 base_dim = 0;
- if (power_of_2) {
- base_dim = dim;
- } else if (multiple_of_20 && ISPOWEROF2(dim / 20)) {
- base_dim = 20;
- dim /= 20;
- } else if (multiple_of_12 && ISPOWEROF2(dim / 12)) {
- base_dim = 12;
- dim /= 12;
- }
-
- if (ISPOWEROF2(dim) && base_dim && arena_capacity(a, i32) >= elements * (1 + (dim != base_dim))) {
- result = push_array(a, i32, elements);
-
- Arena tmp = *a;
- i32 *m = dim == base_dim ? result : push_array(&tmp, i32, elements);
-
- #define IND(i, j) ((i) * dim + (j))
- m[0] = 1;
- for (u32 k = 1; k < dim; k *= 2) {
- for (u32 i = 0; i < k; i++) {
- for (u32 j = 0; j < k; j++) {
- i32 val = m[IND(i, j)];
- m[IND(i + k, j)] = val;
- m[IND(i, j + k)] = val;
- m[IND(i + k, j + k)] = -val;
- }
- }
- }
- #undef IND
-
- i32 *m2 = 0;
- uv2 m2_dim;
- switch (base_dim) {
- case 12:{ m2 = hadamard_12_12_transpose; m2_dim = (uv2){{12, 12}}; }break;
- case 20:{ m2 = hadamard_20_20_transpose; m2_dim = (uv2){{20, 20}}; }break;
- }
- if (m2) kronecker_product(result, m, (uv2){{dim, dim}}, m2, m2_dim);
- }
-
- return result;
-}
+#include "math.c"
diff --git a/util.h b/util.h
@@ -55,8 +55,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))
@@ -71,6 +71,8 @@
#define SIGN(x) ((x) < 0? -1 : 1)
#define swap(a, b) do {typeof(a) __tmp = (a); (a) = (b); (b) = __tmp;} while(0)
+#define f32_cmp(x, y) (ABS((x) - (y)) <= F32_EPSILON * MAX(1.0f, MAX(ABS(x), ABS(y))))
+
#define EachElement(array, it) (u64 it = 0; it < countof(array); it += 1)
#define EachEnumValue(type, it) (type it = (type)0; it < type##_Count; it = (type)(it + 1))
#define EachNonZeroEnumValue(type, it) (type it = (type)1; it < type##_Count; it = (type)(it + 1))
@@ -100,6 +102,10 @@
#define I32_MAX (0x7FFFFFFFL)
#define U32_MAX (0xFFFFFFFFUL)
#define F32_INFINITY (1e+300*1e+300)
+#define F32_EPSILON (1e-6)
+#ifndef PI
+ #define PI (3.14159265358979323846f)
+#endif
typedef char c8;
typedef uint8_t u8;