From f9c69779ea1a044ad808cdcaf7bf5ea0d237f8f4 Mon Sep 17 00:00:00 2001 From: jacob Date: Wed, 4 Feb 2026 13:55:38 -0600 Subject: [PATCH] ui sprite rendering --- src/pp/pp_res/tile/Empty.ase | 3 + src/pp/pp_res/tile/Wall.ase | 3 + src/pp/pp_shared.cgh | 2 +- src/pp/pp_vis/pp_vis_core.c | 103 +++++++++++++++++++++++++--- src/pp/pp_vis/pp_vis_core.h | 1 + src/sprite/sprite.c | 10 +++ src/sprite/sprite.h | 3 + src/ui/ui_core.c | 128 +++++++++++++++++++++++++++-------- src/ui/ui_core.h | 9 ++- 9 files changed, 223 insertions(+), 39 deletions(-) create mode 100644 src/pp/pp_res/tile/Empty.ase create mode 100644 src/pp/pp_res/tile/Wall.ase diff --git a/src/pp/pp_res/tile/Empty.ase b/src/pp/pp_res/tile/Empty.ase new file mode 100644 index 00000000..dd9d902b --- /dev/null +++ b/src/pp/pp_res/tile/Empty.ase @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1ee0cb2f0b8db4b96e0a04b9656ce7b744eb2a5f27a5a73314cf03b487bb6761 +size 674 diff --git a/src/pp/pp_res/tile/Wall.ase b/src/pp/pp_res/tile/Wall.ase new file mode 100644 index 00000000..0bc52c09 --- /dev/null +++ b/src/pp/pp_res/tile/Wall.ase @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cca4b3641533eaba2927c754410dfcaa2f09c41e7d692a82635f00f42f6d4334 +size 630 diff --git a/src/pp/pp_shared.cgh b/src/pp/pp_shared.cgh index 0cce5e79..aef37cb7 100644 --- a/src/pp/pp_shared.cgh +++ b/src/pp/pp_shared.cgh @@ -14,9 +14,9 @@ #define P_TilesXList(X) \ X(Empty) \ + X(Wall) \ X(Tile) \ X(Carpet) \ - X(Wall) \ /* -------------------- */ //- Tiles kinds enum diff --git a/src/pp/pp_vis/pp_vis_core.c b/src/pp/pp_vis/pp_vis_core.c index 0d7fc2f8..afedf275 100644 --- a/src/pp/pp_vis/pp_vis_core.c +++ b/src/pp/pp_vis/pp_vis_core.c @@ -2749,6 +2749,15 @@ void V_TickForever(WaveLaneCtx *lane) ++panel->windows_count; ++V.windows_count; } + { + V_Window *window = PushStruct(perm, V_Window); + window->panel = panel; + window->key = UI_RandKey(); // TODO: Don't use random keys + window->is_spawn_window = 1; + DllQueuePushNP(panel->first_window, panel->last_window, window, next_in_panel, prev_in_panel); + ++panel->windows_count; + ++V.windows_count; + } panel->active_window_idx = 1; } @@ -3068,6 +3077,10 @@ void V_TickForever(WaveLaneCtx *lane) { tab_name = Lit("Tiles"); } + else if (window->is_spawn_window) + { + tab_name = Lit("Spawn"); + } else { tab_name = Lit("Unknown"); @@ -3224,12 +3237,59 @@ void V_TickForever(WaveLaneCtx *lane) UI_PushCP(UI_BuildColumn()); { UI_Push(Tag, window->key.v); + + ////////////////////////////// + //- Build tile window + + // if (window->is_tile_window) + // { + // for (P_TileKind tile_kind = 0; tile_kind < P_TileKind_COUNT; ++tile_kind) + // { + // String name = P_NameFromTileKind(tile_kind); + // UI_Key key = UI_KeyF("Tile %F", FmtString(name)); + // UI_BoxReport rep = UI_ReportsFromKey(key).draw; + + // if (rep.m1.downs) + // { + // frame->equipped_tile = tile_kind; + // } + + // Vec4 bg_color = Zi; + // bg_color = LerpSrgb(bg_color, theme.col.button_hot, rep.hot); + // bg_color = LerpSrgb(bg_color, theme.col.button_active, rep.active); + + // b32 is_selected = tile_kind == frame->equipped_tile; + + // Vec4 border_color = Zi; + // border_color = LerpSrgb(border_color, theme.col.button_selected, rep.misc); + // border_color = LerpSrgb(border_color, theme.col.button_active, rep.hot); + + // UI_SetNext(BackgroundColor, bg_color); + // UI_SetNext(BorderColor, border_color); + // UI_SetNext(BorderSize, 1); + // UI_SetNext(Width, UI_GROW(1, 0)); + // UI_SetNext(Height, UI_SHRINK(0, 0)); + // UI_SetNext(Misc, is_selected); + // UI_SetNext(Flags, UI_BoxFlag_CaptureMouse); + // UI_PushCP(UI_BuildRowEx(key)); + // { + // UI_SetNext(ChildAlignment, UI_Region_Center); + // UI_SetNext(Text, name); + // UI_SetNext(Flags, UI_BoxFlag_DrawText); + // UI_SetNext(Width, UI_SHRINK(4, 0)); + // UI_SetNext(Height, UI_SHRINK(2, 0)); + // UI_BuildRow(); + // } + // UI_PopCP(UI_TopCP()); + // } + // } + if (window->is_tile_window) { for (P_TileKind tile_kind = 0; tile_kind < P_TileKind_COUNT; ++tile_kind) { - String name = P_NameFromTileKind(tile_kind); - UI_Key key = UI_KeyF("Tile %F", FmtString(name)); + String tile_name = P_NameFromTileKind(tile_kind); + UI_Key key = UI_KeyF("Tile %F", FmtString(tile_name)); UI_BoxReport rep = UI_ReportsFromKey(key).draw; if (rep.m1.downs) @@ -3254,18 +3314,41 @@ void V_TickForever(WaveLaneCtx *lane) UI_SetNext(Height, UI_SHRINK(0, 0)); UI_SetNext(Misc, is_selected); UI_SetNext(Flags, UI_BoxFlag_CaptureMouse); + UI_SetNext(ChildAlignment, UI_Region_Left); UI_PushCP(UI_BuildRowEx(key)); { - UI_SetNext(ChildAlignment, UI_Region_Center); - UI_SetNext(Text, name); - UI_SetNext(Flags, UI_BoxFlag_DrawText); - UI_SetNext(Width, UI_SHRINK(4, 0)); - UI_SetNext(Height, UI_SHRINK(2, 0)); - UI_BuildRow(); + // Tile sprite + { + String sheet_name = StringF(frame->arena, "tile/%F.ase", FmtString(tile_name)); + ResourceKey sheet_resource = ResourceKeyFromStore(&P_Resources, sheet_name); + SPR_SheetKey sheet = SPR_SheetKeyFromResource(sheet_resource); + + UI_SetNext(ChildAlignment, UI_Region_Center); + UI_SetNext(Width, UI_SHRINK(4, 0)); + UI_SetNext(Height, UI_SHRINK(4, 0)); + UI_SetNext(SpriteSheet, sheet); + UI_BuildRow(); + } + // Tile name + { + UI_SetNext(ChildAlignment, UI_Region_Center); + UI_SetNext(Text, tile_name); + UI_SetNext(Flags, UI_BoxFlag_DrawText); + UI_SetNext(Width, UI_SHRINK(4, 0)); + UI_SetNext(Height, UI_SHRINK(2, 0)); + UI_BuildRow(); + } } UI_PopCP(UI_TopCP()); } } + + ////////////////////////////// + //- Build spawn window + + if (window->is_spawn_window) + { + } } UI_PopCP(UI_TopCP()); } @@ -3395,7 +3478,7 @@ void V_TickForever(WaveLaneCtx *lane) UI_Push(ChildLayoutAxis, Axis_Y); UI_Push(FloatingPos, palette->pos); UI_SetNext(Anchor, UI_Region_Center); - UI_SetNext(Flags, UI_BoxFlag_Floating | UI_BoxFlag_CaptureMouse); + UI_SetNext(Flags, UI_BoxFlag_Floating | (UI_BoxFlag_CaptureMouse * !!palette->is_showing)); UI_PushCP(UI_BuildBoxEx(palette->key)); { // Title bar @@ -3407,7 +3490,7 @@ void V_TickForever(WaveLaneCtx *lane) UI_Push(ChildLayoutAxis, Axis_X); UI_Push(Width, UI_GROW(1, 0)); UI_Push(Height, UI_FNT(2, 1)); - UI_SetNext(Flags, UI_BoxFlag_DrawText | UI_BoxFlag_CaptureMouse); + UI_SetNext(Flags, UI_BoxFlag_DrawText | (UI_BoxFlag_CaptureMouse * !!palette->is_showing)); UI_PushCP(UI_BuildBoxEx(titlebar_key)); { UI_Push(Width, UI_GROW(1, 0)); diff --git a/src/pp/pp_vis/pp_vis_core.h b/src/pp/pp_vis/pp_vis_core.h index a9017600..b1de8cf9 100644 --- a/src/pp/pp_vis/pp_vis_core.h +++ b/src/pp/pp_vis/pp_vis_core.h @@ -202,6 +202,7 @@ Struct(V_Window) UI_Key key; b32 is_tile_window; + b32 is_spawn_window; }; //////////////////////////////////////////////////////////// diff --git a/src/sprite/sprite.c b/src/sprite/sprite.c index f3d2c352..f0d4875f 100644 --- a/src/sprite/sprite.c +++ b/src/sprite/sprite.c @@ -27,6 +27,16 @@ SPR_SpanKey SPR_SpanKeyFromName(String name) return result; } +b32 SPR_IsSheetKeyNil(SPR_SheetKey key) +{ + return key.r.v == 0; +} + +b32 SPR_IsSpanKeyNil(SPR_SpanKey key) +{ + return key.v == 0; +} + String SPR_NameFromRayKind(SPR_RayKind kind) { PERSIST Readonly String names[SPR_RayKind_COUNT] = { diff --git a/src/sprite/sprite.h b/src/sprite/sprite.h index 5bc087db..2c6bf81f 100644 --- a/src/sprite/sprite.h +++ b/src/sprite/sprite.h @@ -185,6 +185,9 @@ void SPR_Bootstrap(void); SPR_SheetKey SPR_SheetKeyFromResource(ResourceKey resource); SPR_SpanKey SPR_SpanKeyFromName(String name); +b32 SPR_IsSheetKeyNil(SPR_SheetKey key); +b32 SPR_IsSpanKeyNil(SPR_SpanKey key); + String SPR_NameFromRayKind(SPR_RayKind kind); SPR_LayerKind SPR_LayerKindFromName(String name); diff --git a/src/ui/ui_core.c b/src/ui/ui_core.c index 80a9dcc1..acd48709 100644 --- a/src/ui/ui_core.c +++ b/src/ui/ui_core.c @@ -503,6 +503,9 @@ UI_Key UI_BuildBoxEx(UI_Key semantic_key) n->cmd.box.icon = UI_Top(Icon); n->cmd.box.anchor = UI_Top(Anchor); n->cmd.box.floating_pos = UI_Top(FloatingPos); + n->cmd.box.sprite_sheet = UI_Top(SpriteSheet); + n->cmd.box.sprite_span = UI_Top(SpriteSpan); + n->cmd.box.sprite_seq = UI_Top(SpriteSeq); n->cmd.box.misc = UI_Top(Misc); } ++frame->cmds_count; @@ -865,7 +868,7 @@ Vec2 UI_CursorPos(void) } //////////////////////////////////////////////////////////// -//~ Text layout helpers +//~ Layout helpers GC_Run UI_ScaleRun(Arena *arena, GC_Run unscaled_run, Vec2 scale) { @@ -1005,7 +1008,16 @@ void UI_EndFrame(UI_Frame *frame, i32 vsync) { codepoints = String32FromString(scratch.arena, box->desc.text); } - box->glyph_run = GC_RunFromString32(frame->arena, codepoints, box->desc.font, box->desc.font_size); + + if (AnyBit(box->desc.flags, UI_BoxFlag_DrawText) && codepoints.len > 0) + { + box->glyph_run = GC_RunFromString32(frame->arena, codepoints, box->desc.font, box->desc.font_size); + } + + if (!SPR_IsSheetKeyNil(box->desc.sprite_sheet)) + { + box->sprite = SPR_SpriteFromSheet(box->desc.sprite_sheet, box->desc.sprite_span, box->desc.sprite_seq); + } } box->last_build_tick = frame->tick; } break; @@ -1134,23 +1146,30 @@ void UI_EndFrame(UI_Frame *frame, i32 vsync) UI_Box *box = boxes_pre[pre_index]; for (Axis axis = 0; axis < Axis_COUNTXY; ++axis) { - UI_Size sem_dims = box->desc.pref_semantic_dims[axis]; - if (sem_dims.kind == UI_SizeKind_Pixel) + UI_Size size = box->desc.pref_semantic_dims[axis]; + if (size.kind == UI_SizeKind_Pixel) { - box->solved_dims.v[axis] = sem_dims.v; + box->solved_dims.v[axis] = size.v; } - else if (sem_dims.kind == UI_SizeKind_Shrink && AnyBit(box->desc.flags, UI_BoxFlag_DrawText)) + else if (size.kind == UI_SizeKind_Shrink) { - f32 text_size = 0; - if (axis == Axis_X) + if (AnyBit(box->desc.flags, UI_BoxFlag_DrawText)) { - text_size = box->glyph_run.baseline_length; + f32 text_size = 0; + if (axis == Axis_X) + { + text_size = box->glyph_run.baseline_length; + } + else + { + text_size = box->glyph_run.font_ascent + box->glyph_run.font_descent; + } + box->solved_dims.v[axis] = text_size + (size.v * 2); } - else + else if (!SPR_IsSheetKeyNil(box->desc.sprite_sheet)) { - text_size = box->glyph_run.font_ascent + box->glyph_run.font_descent; + box->solved_dims.v[axis] = box->sprite.tex_rect.p1.v[axis] - box->sprite.tex_rect.p0.v[axis] + (size.v * 2); } - box->solved_dims.v[axis] = text_size + (sem_dims.v * 2); } } } @@ -1162,22 +1181,29 @@ void UI_EndFrame(UI_Frame *frame, i32 vsync) if (box->parent) { Axis axis = box->parent->desc.child_layout_axis; - UI_Size sem_dims = box->desc.pref_semantic_dims[axis]; - if (sem_dims.kind == UI_SizeKind_Grow) + UI_Size size = box->desc.pref_semantic_dims[axis]; + if (size.kind == UI_SizeKind_Grow) { f32 match_size = 0; b32 found_match = 0; for (UI_Box *ancestor = box->parent; ancestor != 0 && !found_match; ancestor = ancestor->parent) { UI_Size ancestor_size = ancestor->desc.pref_semantic_dims[axis]; - if (ancestor_size.kind == UI_SizeKind_Pixel || (ancestor_size.kind == UI_SizeKind_Shrink && AnyBit(box->desc.flags, UI_BoxFlag_DrawText))) + if ( + ancestor_size.kind == UI_SizeKind_Pixel || ( + ancestor_size.kind == UI_SizeKind_Shrink && ( + AnyBit(box->desc.flags, UI_BoxFlag_DrawText) || + !SPR_IsSheetKeyNil(box->desc.sprite_sheet) + ) + ) + ) { // Match independent ancestor match_size = ancestor->solved_dims.v[axis]; found_match = 1; } } - box->solved_dims.v[axis] = match_size * sem_dims.v; + box->solved_dims.v[axis] = match_size * size.v; } } } @@ -1188,8 +1214,8 @@ void UI_EndFrame(UI_Frame *frame, i32 vsync) UI_Box *box = boxes_post[post_index]; for (Axis axis = 0; axis < Axis_COUNTXY; ++axis) { - UI_Size sem_dims = box->desc.pref_semantic_dims[axis]; - if (sem_dims.kind == UI_SizeKind_Shrink && !AnyBit(box->desc.flags, UI_BoxFlag_DrawText)) + UI_Size size = box->desc.pref_semantic_dims[axis]; + if (size.kind == UI_SizeKind_Shrink && !(AnyBit(box->desc.flags, UI_BoxFlag_DrawText) || !SPR_IsSheetKeyNil(box->desc.sprite_sheet))) { f32 accum = 0; for (UI_Box *child = box->first; child; child = child->next) @@ -1207,7 +1233,7 @@ void UI_EndFrame(UI_Frame *frame, i32 vsync) } } } - box->solved_dims.v[axis] = CeilF32(accum + (sem_dims.v * 2)); + box->solved_dims.v[axis] = CeilF32(accum + (size.v * 2)); } } } @@ -1219,10 +1245,10 @@ void UI_EndFrame(UI_Frame *frame, i32 vsync) if (box->parent) { Axis axis = !box->parent->desc.child_layout_axis; - UI_Size sem_dims = box->desc.pref_semantic_dims[axis]; - if (sem_dims.kind == UI_SizeKind_Grow) + UI_Size size = box->desc.pref_semantic_dims[axis]; + if (size.kind == UI_SizeKind_Grow) { - box->solved_dims.v[axis] = box->parent->solved_dims.v[axis] * sem_dims.v; + box->solved_dims.v[axis] = box->parent->solved_dims.v[axis] * size.v; } } } @@ -1508,7 +1534,6 @@ void UI_EndFrame(UI_Frame *frame, i32 vsync) for (u64 pre_index = 0; pre_index < boxes_count; ++pre_index) { UI_Box *box = boxes_pre[pre_index]; - UI_RegionPair child_alignment = UI_PairFromRegion(box->desc.child_alignment); b32 is_visible = 1; is_visible = is_visible && (box->desc.tint.w >= 0.0025); @@ -1518,6 +1543,10 @@ void UI_EndFrame(UI_Frame *frame, i32 vsync) { Vec4 debug_lin = is_visible ? LinearFromSrgb(box->desc.debug_color) : LinearFromSrgb(box->desc.invisible_debug_color); Vec4 tint_lin = LinearFromSrgb(box->desc.tint); + Vec2 box_dims = DimsFromRng2(box->screen_rect); + UI_RegionPair child_alignment = UI_PairFromRegion(box->desc.child_alignment); + UI_AxisRegion x_alignment = child_alignment.v[Axis_X]; + UI_AxisRegion y_alignment = child_alignment.v[Axis_Y]; // Box rect { @@ -1536,6 +1565,55 @@ void UI_EndFrame(UI_Frame *frame, i32 vsync) rect->tex_slice_uv = box->raw_texture_slice_uv; } + // Sprite rect + if (!SPR_IsSheetKeyNil(box->desc.sprite_sheet)) + { + UI_GpuRect *rect = PushStruct(frame->rects_arena, UI_GpuRect); + rect->debug_lin = debug_lin; + rect->tint_lin = tint_lin; + rect->tex = box->sprite.tex; + rect->tex_slice_uv = DivRng2Vec2(box->sprite.tex_rect, box->sprite.tex_dims); + + Vec2 dims = DimsFromRng2(box->sprite.tex_rect); + Vec2 pos = Zi; + switch (x_alignment) + { + case UI_AxisRegion_Start: + { + pos.x = box->screen_rect.p0.x; + } break; + case UI_AxisRegion_End: + { + pos.x = box->screen_rect.p1.x; + pos.x -= dims.x; + } break; + case UI_AxisRegion_Center: + { + pos.x = box->screen_rect.p0.x; + pos.x += (box_dims.x - dims.x) / 2; + } break; + } + switch (y_alignment) + { + case UI_AxisRegion_Start: + { + pos.y = box->screen_rect.p0.y; + } break; + case UI_AxisRegion_End: + { + pos.y = box->screen_rect.p1.y; + pos.y -= dims.y; + } break; + case UI_AxisRegion_Center: + { + pos.y = box->screen_rect.p0.y; + pos.y += (box_dims.y - dims.y) / 2; + } break; + } + rect->bounds.p0 = pos; + rect->bounds.p1 = AddVec2(rect->bounds.p0, dims); + } + // Text rects GC_Run raw_run_unscaled = box->glyph_run; GC_Run raw_run = UI_ScaleRun(frame->arena, raw_run_unscaled, box->solved_scale); @@ -1590,15 +1668,11 @@ void UI_EndFrame(UI_Frame *frame, i32 vsync) final_baseline_length = raw_run.baseline_length; } - UI_AxisRegion x_alignment = child_alignment.v[Axis_X]; - UI_AxisRegion y_alignment = child_alignment.v[Axis_Y]; - // Compute baseline f32 ascent = raw_run.font_ascent; f32 font_descent = raw_run.font_descent; f32 cap = raw_run.font_cap; f32 baseline_height = ascent + font_descent; - Vec2 box_dims = DimsFromRng2(box->screen_rect); Vec2 baseline = Zi; switch (x_alignment) { diff --git a/src/ui/ui_core.h b/src/ui/ui_core.h index 536cbaca..9d360b99 100644 --- a/src/ui/ui_core.h +++ b/src/ui/ui_core.h @@ -121,6 +121,9 @@ Enum(UI_BoxFlag) X(TextColor, Vec4) \ X(Text, String) \ X(Icon, UI_Icon) \ + X(SpriteSheet, SPR_SheetKey) \ + X(SpriteSpan, SPR_SpanKey) \ + X(SpriteSeq, i64) \ X(BackgroundTexture, G_Texture2DRef) \ X(BackgroundTextureSliceUv, Rng2) \ X(Misc, f64) \ @@ -255,6 +258,9 @@ Struct(UI_BoxDesc) UI_Region child_alignment; UI_Region anchor; Vec2 floating_pos; + SPR_SheetKey sprite_sheet; + SPR_SpanKey sprite_span; + i64 sprite_seq; f64 misc; }; @@ -308,6 +314,7 @@ Struct(UI_Box) G_Texture2DRef raw_texture; Rng2 raw_texture_slice_uv; GC_Run glyph_run; + SPR_Sprite sprite; //- Pre-layout data u64 pre_index; @@ -527,7 +534,7 @@ Arena *UI_FrameArena(void); Vec2 UI_CursorPos(void); //////////////////////////////////////////////////////////// -//~ Text layout helpers +//~ Layout helpers GC_Run UI_ScaleRun(Arena *arena, GC_Run unscaled_run, Vec2 scale);