timeline visualization

This commit is contained in:
jacob 2026-03-13 13:10:09 -05:00
parent a19c2395ae
commit df5346f514
3 changed files with 190 additions and 85 deletions

View File

@ -5,6 +5,9 @@ V_Ctx V = Zi;
void V_Bootstrap(void)
{
V.timeline.transient_markers_arena = AcquireArena(Gibi(64));
V.timeline.persistent_markers_arena = AcquireArena(Gibi(64));
V.timeline.show = 1;
DispatchWave(Lit("Vis"), 1, V_TickForever, 0);
OnExit(V_Shutdown);
}
@ -252,6 +255,27 @@ void V_DrawPoint(Vec2 p, Vec4 srgb)
V_DrawShape(ui_shape, AffineIdentity, srgb, 24, V_DrawFlag_None);
}
////////////////////////////////////////////////////////////
//~ Timeline helpers
void V_PushTimelineMarker(i64 time_ns, Vec4 color, b32 transient)
{
if (!V.timeline.paused)
{
V_TimelineMarker *marker = 0;
if (transient)
{
marker = PushStruct(V.timeline.transient_markers_arena, V_TimelineMarker);
}
else
{
marker = PushStruct(V.timeline.persistent_markers_arena, V_TimelineMarker);
}
marker->time_ns = time_ns;
marker->color = color;
}
}
////////////////////////////////////////////////////////////
//~ Theme
@ -571,7 +595,6 @@ void V_TickForever(WaveLaneCtx *lane)
frame->is_editing = prev_frame->is_editing;
frame->ui_debug = prev_frame->ui_debug;
frame->show_console = prev_frame->show_console;
frame->show_timeline = prev_frame->show_timeline;
frame->look = prev_frame->look;
frame->fire_presses = prev_frame->fire_presses;
frame->roll_presses = prev_frame->roll_presses;
@ -1671,6 +1694,12 @@ void V_TickForever(WaveLaneCtx *lane)
// P_DebugDrawFrame(predict_frame);
// TODO: Extract information that occurred between first & last prediction, like bullet hits etc?
V_PushTimelineMarker(predict_frame->time_ns, Color_Purple, 1);
if (predict_to != prev_frame_predict_to)
{
V_PushTimelineMarker(predict_frame->time_ns, Color_Purple, 0);
}
}
@ -1876,6 +1905,8 @@ void V_TickForever(WaveLaneCtx *lane)
{
i64 delay_ns = SIM_TICK_INTERVAL_NS * interp_ratio;
V.target_blend_time_ns = predict_frame->time_ns - delay_ns;
V_PushTimelineMarker(ack_frame->time_ns, Color_Cyan, 0);
}
@ -2029,6 +2060,10 @@ void V_TickForever(WaveLaneCtx *lane)
f64 blend_t = (f64)(V.blend_time_ns - left_predict_frame->base_time_ns) / (f64)(right_predict_frame->base_time_ns - left_predict_frame->base_time_ns);
V_PushTimelineMarker(V.blend_time_ns, Color_Yellow, 1);
V_PushTimelineMarker(V.target_blend_time_ns, Color_Red, 1);
if (IsInf(blend_t))
@ -2334,7 +2369,7 @@ void V_TickForever(WaveLaneCtx *lane)
frame->shade_cursor = MulAffineVec2(frame->af.screen_to_shade, frame->screen_cursor);
frame->world_cursor = MulAffineVec2(frame->af.screen_to_world, frame->screen_cursor);
b32 show_editor_ui = TweakBool("Show editor UI", 1);
b32 show_editor_ui = TweakBool("Show editor UI", 0);
frame->world_selection_start = frame->world_cursor;
if (frame->is_editing)
@ -4572,101 +4607,149 @@ void V_TickForever(WaveLaneCtx *lane)
//////////////////////////////
//- Build timeline debug UI
if (frame->show_timeline)
{
UI_Key timeline_key = UI_KeyF("Timeline box");
// FIXME: Remove this
i64 timeline_span_ns = NsFromSeconds(10);
PERSIST i64 last_timeline_switch_ns = 0;
f64 timeline_span_seconds = TweakFloat("Timeline span", 1, 0.01, 5);
i64 timeline_span_ns = NsFromSeconds(timeline_span_seconds);
PERSIST i64 last_timeline_reset_ns = 0;
PERSIST i64 timeline_start_ns = 0;
PERSIST i64 current_marker_time_ns = 0;
if (frame->time_ns - last_timeline_switch_ns > timeline_span_ns)
// Current frame time marker
if (!V.timeline.paused)
{
last_timeline_switch_ns = frame->time_ns;
current_marker_time_ns = frame->time_ns - last_timeline_reset_ns + timeline_start_ns;
}
V_PushTimelineMarker(current_marker_time_ns, Color_White, 1);
b32 locked = frame->held_buttons[Button_R];
b32 paused = frame->held_buttons[Button_E];
V.timeline.locked = locked;
V.timeline.paused = paused;
i64 reset_span_ns = timeline_span_ns;
if (locked)
{
reset_span_ns *= 0.5;
}
if (!paused && frame->time_ns - last_timeline_reset_ns > reset_span_ns)
{
last_timeline_reset_ns = frame->time_ns;
timeline_start_ns = ack_frame->time_ns;
ResetArena(V.timeline.persistent_markers_arena);
}
UI_Size timeline_width = UI_FNT(80, 1);
UI_Size timeline_height = UI_FNT(2.5, 1);
Vec4 tmld_bg = Color_Black;
f32 timeline_opacity = 0.75;
Vec2 timeline_pos = VEC2(frame->screen_dims.x * 0.5, frame->screen_dims.y * 0.25);
//- Push markers
Struct(TimelineMarker)
if (V.timeline.show)
{
TimelineMarker *next;
i64 time_ns;
Vec4 color;
};
TimelineMarker *first_marker = 0;
TimelineMarker *last_marker = 0;
{
// Now frame time
// {
// TimelineMarker *marker = PushStruct(frame->arena, TimelineMarker);
// SllQueuePush(first_marker, last_marker, marker);
// marker->color = Color_White;
// marker->time_ns = frame->time_ns;
// }
// Ack frame time
UI_Key timeline_key = UI_KeyF("Timeline box");
UI_Size timeline_width = UI_FNT(120, 1);
UI_Size timeline_height = UI_FNT(2, 1);
Vec4 tmld_bg = Color_Black;
Vec2 timeline_pos = VEC2(frame->screen_dims.x * 0.5, frame->screen_dims.y * 0.25);
f32 timeline_opacity = 0.75;
f32 marker_opacity = 0.75;
// i64 timeline_span_ns = NsFromSeconds(10);
i64 timeline_end_ns = timeline_start_ns + timeline_span_ns;
UI_Size transient_marker_width = UI_FNT(0.1, 1);
UI_Size transient_marker_height = UI_FNT(2, 1);
UI_Size persistent_marker_width = UI_FNT(0.1, 1);
UI_Size persistent_marker_height = timeline_height;
UI_PushCP(UI_NilKey);
{
TimelineMarker *marker = PushStruct(frame->arena, TimelineMarker);
SllQueuePush(first_marker, last_marker, marker);
marker->color = Color_Yellow;
marker->time_ns = ack_frame->time_ns;
}
}
UI_Push(Tint, VEC4(1, 1, 1, timeline_opacity));
// i64 timeline_span_ns = NsFromSeconds(10);
i64 timeline_span_ns = NsFromSeconds(1);
i64 timeline_start_ns = (frame->time_ns / timeline_span_ns) * timeline_span_ns;
i64 timeline_end_ns = timeline_start_ns + timeline_span_ns;
UI_Size marker_width = UI_FNT(0.5, 1);
UI_Size marker_height = timeline_height;
UI_PushCP(UI_NilKey);
{
UI_Push(Tint, VEC4(1, 1, 1, timeline_opacity));
//- Build timeline
{
UI_SetNext(Anchor, UI_Region_Top);
UI_SetNext(Width, timeline_width);
UI_SetNext(Height, timeline_height);
UI_SetNext(Anchor, UI_Region_Center);
UI_SetNext(FloatingPos, timeline_pos);
UI_SetNext(BackgroundColor, tmld_bg);
UI_SetNext(Flags, UI_BoxFlag_Floating);
UI_PushCP(UI_BuildRowEx(timeline_key));
//- Build timeline
{
UI_SetNext(Anchor, UI_Region_Top);
UI_SetNext(Width, timeline_width);
UI_SetNext(Height, timeline_height);
UI_SetNext(Anchor, UI_Region_Center);
UI_SetNext(FloatingPos, timeline_pos);
UI_SetNext(BackgroundColor, tmld_bg);
UI_SetNext(Flags, UI_BoxFlag_Floating | UI_BoxFlag_NoFloatingClamp);
UI_PushCP(UI_BuildRowEx(timeline_key));
{
}
UI_PopCP(UI_TopCP());
}
UI_PopCP(UI_TopCP());
}
//- Build markers
for (TimelineMarker *marker = first_marker; marker; marker = marker->next)
{
f64 marker_ratio = (f64)(marker->time_ns - timeline_start_ns) / (f64)(timeline_end_ns - timeline_start_ns);
Vec2 marker_pos = timeline_pos;
marker_pos.x += marker_ratio * timeline_width.v;
Vec2 marker_offset = Zi;
if (locked)
{
i64 offset_ns = current_marker_time_ns;
marker_offset.x = -((f64)(offset_ns - timeline_start_ns) / (f64)timeline_span_ns) * timeline_width.v + timeline_width.v * 0.5;
}
//- Build markers
Arena *marker_arenas[] = {
V.timeline.transient_markers_arena,
V.timeline.persistent_markers_arena,
};
for (u64 marker_arena_idx = 0; marker_arena_idx < countof(marker_arenas); ++marker_arena_idx)
{
Arena *marker_arena = marker_arenas[marker_arena_idx];
u64 markers_count = ArenaCount(marker_arena, V_TimelineMarker);
b32 is_transient = marker_arena == V.timeline.transient_markers_arena;
V_TimelineMarker *markers = ArenaFirst(marker_arena, V_TimelineMarker);
for (u64 marker_idx = 0; marker_idx < markers_count; ++marker_idx)
{
V_TimelineMarker *marker = &markers[marker_idx];
UI_Size width = persistent_marker_width;
UI_Size height = persistent_marker_height;
if (is_transient)
{
width = transient_marker_width;
height = transient_marker_height;
}
Vec4 marker_color = marker->color;
f64 marker_ratio = (f64)(marker->time_ns - timeline_start_ns) / (f64)timeline_span_ns;
Vec2 marker_pos = timeline_pos;
if (is_transient)
{
marker_pos.x += -timeline_width.v * 0.5 - width.v * 0.5;
marker_pos.y -= timeline_height.v * 0.5;
}
else
{
marker_pos.x += -timeline_width.v * 0.5 - width.v * 0.5;
marker_pos.y += timeline_height.v * 0.5;
}
marker_pos.x += marker_ratio * timeline_width.v;
marker_pos = AddVec2(marker_pos, marker_offset);
Vec4 tint = UI_Top(Tint);
tint.a *= marker_opacity;
UI_SetNext(Tint, tint);
UI_SetNext(Flags, UI_BoxFlag_Floating | UI_BoxFlag_NoFloatingClamp);
UI_SetNext(BackgroundColor, marker_color);
UI_SetNext(Anchor, UI_Region_Center);
UI_SetNext(Width, width);
UI_SetNext(Height, height);
UI_SetNext(FloatingPos, marker_pos);
UI_BuildBox();
}
}
UI_SetNext(Flags, UI_BoxFlag_Floating);
UI_SetNext(BackgroundColor, marker->color);
UI_SetNext(Anchor, UI_Region_Center);
UI_SetNext(Width, marker_width);
UI_SetNext(Height, marker_height);
UI_SetNext(FloatingPos, marker_pos);
UI_BuildBox();
}
UI_PopCP(UI_TopCP());
}
if (!paused)
{
ResetArena(V.timeline.transient_markers_arena);
}
UI_PopCP(UI_TopCP());
}
@ -5078,8 +5161,8 @@ void V_TickForever(WaveLaneCtx *lane)
case V_CmdKind_toggle_timeline:
{
b32 new = !frame->show_timeline;
frame->show_timeline = new;
b32 new = !V.timeline.show;
V.timeline.show = new;
} break;
case V_CmdKind_toggle_fullscreen:

View File

@ -216,6 +216,15 @@ Enum(V_DrawFlag)
V_DrawFlag_Line = (1 << 0),
};
////////////////////////////////////////////////////////////
//~ Timeline types
Struct(V_TimelineMarker)
{
Vec4 color;
i64 time_ns;
};
////////////////////////////////////////////////////////////
//~ State types
@ -261,6 +270,15 @@ Struct(V_Ctx)
P_EntKey player_key;
P_EntKey follow_key;
struct
{
b32 show;
b32 paused;
b32 locked;
Arena *transient_markers_arena;
Arena *persistent_markers_arena;
} timeline;
i64 panels_count;
i64 windows_count;
V_Panel *root_panel;
@ -311,6 +329,11 @@ void V_DrawLine(Vec2 p0, Vec2 p1, Vec4 srgb);
void V_DrawRect(Rng2 rect, Vec4 srgb, V_DrawFlag flags);
void V_DrawPoint(Vec2 p, Vec4 srgb);
////////////////////////////////////////////////////////////
//~ Timeline helpers
void V_PushTimelineMarker(i64 time_ns, Vec4 color, b32 transient);
////////////////////////////////////////////////////////////
//~ Theme

View File

@ -229,8 +229,8 @@ Enum(V_SelectionMode)
Enum(V_EditMode)
{
V_EditMode_Tile,
V_EditMode_Prefab,
V_EditMode_Tile,
};
Struct(V_Affines)
@ -283,7 +283,6 @@ Struct(V_SharedFrame)
b32 is_editing;
b32 ui_debug;
b32 show_console;
b32 show_timeline;
b32 is_selecting;
b32 is_panning;
b32 has_mouse_focus;