ui sprite rendering

This commit is contained in:
jacob 2026-02-04 13:55:38 -06:00
parent 5f8e701ac1
commit f9c69779ea
9 changed files with 223 additions and 39 deletions

BIN
src/pp/pp_res/tile/Empty.ase (Stored with Git LFS) Normal file

Binary file not shown.

BIN
src/pp/pp_res/tile/Wall.ase (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -14,9 +14,9 @@
#define P_TilesXList(X) \ #define P_TilesXList(X) \
X(Empty) \ X(Empty) \
X(Wall) \
X(Tile) \ X(Tile) \
X(Carpet) \ X(Carpet) \
X(Wall) \
/* -------------------- */ /* -------------------- */
//- Tiles kinds enum //- Tiles kinds enum

View File

@ -2749,6 +2749,15 @@ void V_TickForever(WaveLaneCtx *lane)
++panel->windows_count; ++panel->windows_count;
++V.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; panel->active_window_idx = 1;
} }
@ -3068,6 +3077,10 @@ void V_TickForever(WaveLaneCtx *lane)
{ {
tab_name = Lit("Tiles"); tab_name = Lit("Tiles");
} }
else if (window->is_spawn_window)
{
tab_name = Lit("Spawn");
}
else else
{ {
tab_name = Lit("Unknown"); tab_name = Lit("Unknown");
@ -3224,12 +3237,59 @@ void V_TickForever(WaveLaneCtx *lane)
UI_PushCP(UI_BuildColumn()); UI_PushCP(UI_BuildColumn());
{ {
UI_Push(Tag, window->key.v); 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) if (window->is_tile_window)
{ {
for (P_TileKind tile_kind = 0; tile_kind < P_TileKind_COUNT; ++tile_kind) for (P_TileKind tile_kind = 0; tile_kind < P_TileKind_COUNT; ++tile_kind)
{ {
String name = P_NameFromTileKind(tile_kind); String tile_name = P_NameFromTileKind(tile_kind);
UI_Key key = UI_KeyF("Tile %F", FmtString(name)); UI_Key key = UI_KeyF("Tile %F", FmtString(tile_name));
UI_BoxReport rep = UI_ReportsFromKey(key).draw; UI_BoxReport rep = UI_ReportsFromKey(key).draw;
if (rep.m1.downs) if (rep.m1.downs)
@ -3254,18 +3314,41 @@ void V_TickForever(WaveLaneCtx *lane)
UI_SetNext(Height, UI_SHRINK(0, 0)); UI_SetNext(Height, UI_SHRINK(0, 0));
UI_SetNext(Misc, is_selected); UI_SetNext(Misc, is_selected);
UI_SetNext(Flags, UI_BoxFlag_CaptureMouse); UI_SetNext(Flags, UI_BoxFlag_CaptureMouse);
UI_SetNext(ChildAlignment, UI_Region_Left);
UI_PushCP(UI_BuildRowEx(key)); UI_PushCP(UI_BuildRowEx(key));
{ {
// 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(ChildAlignment, UI_Region_Center);
UI_SetNext(Text, name); 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(Flags, UI_BoxFlag_DrawText);
UI_SetNext(Width, UI_SHRINK(4, 0)); UI_SetNext(Width, UI_SHRINK(4, 0));
UI_SetNext(Height, UI_SHRINK(2, 0)); UI_SetNext(Height, UI_SHRINK(2, 0));
UI_BuildRow(); UI_BuildRow();
} }
}
UI_PopCP(UI_TopCP()); UI_PopCP(UI_TopCP());
} }
} }
//////////////////////////////
//- Build spawn window
if (window->is_spawn_window)
{
}
} }
UI_PopCP(UI_TopCP()); UI_PopCP(UI_TopCP());
} }
@ -3395,7 +3478,7 @@ void V_TickForever(WaveLaneCtx *lane)
UI_Push(ChildLayoutAxis, Axis_Y); UI_Push(ChildLayoutAxis, Axis_Y);
UI_Push(FloatingPos, palette->pos); UI_Push(FloatingPos, palette->pos);
UI_SetNext(Anchor, UI_Region_Center); 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)); UI_PushCP(UI_BuildBoxEx(palette->key));
{ {
// Title bar // Title bar
@ -3407,7 +3490,7 @@ void V_TickForever(WaveLaneCtx *lane)
UI_Push(ChildLayoutAxis, Axis_X); UI_Push(ChildLayoutAxis, Axis_X);
UI_Push(Width, UI_GROW(1, 0)); UI_Push(Width, UI_GROW(1, 0));
UI_Push(Height, UI_FNT(2, 1)); 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_PushCP(UI_BuildBoxEx(titlebar_key));
{ {
UI_Push(Width, UI_GROW(1, 0)); UI_Push(Width, UI_GROW(1, 0));

View File

@ -202,6 +202,7 @@ Struct(V_Window)
UI_Key key; UI_Key key;
b32 is_tile_window; b32 is_tile_window;
b32 is_spawn_window;
}; };
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////

View File

@ -27,6 +27,16 @@ SPR_SpanKey SPR_SpanKeyFromName(String name)
return result; 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) String SPR_NameFromRayKind(SPR_RayKind kind)
{ {
PERSIST Readonly String names[SPR_RayKind_COUNT] = { PERSIST Readonly String names[SPR_RayKind_COUNT] = {

View File

@ -185,6 +185,9 @@ void SPR_Bootstrap(void);
SPR_SheetKey SPR_SheetKeyFromResource(ResourceKey resource); SPR_SheetKey SPR_SheetKeyFromResource(ResourceKey resource);
SPR_SpanKey SPR_SpanKeyFromName(String name); SPR_SpanKey SPR_SpanKeyFromName(String name);
b32 SPR_IsSheetKeyNil(SPR_SheetKey key);
b32 SPR_IsSpanKeyNil(SPR_SpanKey key);
String SPR_NameFromRayKind(SPR_RayKind kind); String SPR_NameFromRayKind(SPR_RayKind kind);
SPR_LayerKind SPR_LayerKindFromName(String name); SPR_LayerKind SPR_LayerKindFromName(String name);

View File

@ -503,6 +503,9 @@ UI_Key UI_BuildBoxEx(UI_Key semantic_key)
n->cmd.box.icon = UI_Top(Icon); n->cmd.box.icon = UI_Top(Icon);
n->cmd.box.anchor = UI_Top(Anchor); n->cmd.box.anchor = UI_Top(Anchor);
n->cmd.box.floating_pos = UI_Top(FloatingPos); 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); n->cmd.box.misc = UI_Top(Misc);
} }
++frame->cmds_count; ++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) GC_Run UI_ScaleRun(Arena *arena, GC_Run unscaled_run, Vec2 scale)
{ {
@ -1005,8 +1008,17 @@ void UI_EndFrame(UI_Frame *frame, i32 vsync)
{ {
codepoints = String32FromString(scratch.arena, box->desc.text); codepoints = String32FromString(scratch.arena, box->desc.text);
} }
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); 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; box->last_build_tick = frame->tick;
} break; } break;
@ -1134,12 +1146,14 @@ void UI_EndFrame(UI_Frame *frame, i32 vsync)
UI_Box *box = boxes_pre[pre_index]; UI_Box *box = boxes_pre[pre_index];
for (Axis axis = 0; axis < Axis_COUNTXY; ++axis) for (Axis axis = 0; axis < Axis_COUNTXY; ++axis)
{ {
UI_Size sem_dims = box->desc.pref_semantic_dims[axis]; UI_Size size = box->desc.pref_semantic_dims[axis];
if (sem_dims.kind == UI_SizeKind_Pixel) 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)
{
if (AnyBit(box->desc.flags, UI_BoxFlag_DrawText))
{ {
f32 text_size = 0; f32 text_size = 0;
if (axis == Axis_X) if (axis == Axis_X)
@ -1150,7 +1164,12 @@ void UI_EndFrame(UI_Frame *frame, i32 vsync)
{ {
text_size = box->glyph_run.font_ascent + box->glyph_run.font_descent; text_size = box->glyph_run.font_ascent + box->glyph_run.font_descent;
} }
box->solved_dims.v[axis] = text_size + (sem_dims.v * 2); box->solved_dims.v[axis] = text_size + (size.v * 2);
}
else if (!SPR_IsSheetKeyNil(box->desc.sprite_sheet))
{
box->solved_dims.v[axis] = box->sprite.tex_rect.p1.v[axis] - box->sprite.tex_rect.p0.v[axis] + (size.v * 2);
}
} }
} }
} }
@ -1162,22 +1181,29 @@ void UI_EndFrame(UI_Frame *frame, i32 vsync)
if (box->parent) if (box->parent)
{ {
Axis axis = box->parent->desc.child_layout_axis; Axis axis = box->parent->desc.child_layout_axis;
UI_Size sem_dims = box->desc.pref_semantic_dims[axis]; UI_Size size = box->desc.pref_semantic_dims[axis];
if (sem_dims.kind == UI_SizeKind_Grow) if (size.kind == UI_SizeKind_Grow)
{ {
f32 match_size = 0; f32 match_size = 0;
b32 found_match = 0; b32 found_match = 0;
for (UI_Box *ancestor = box->parent; ancestor != 0 && !found_match; ancestor = ancestor->parent) for (UI_Box *ancestor = box->parent; ancestor != 0 && !found_match; ancestor = ancestor->parent)
{ {
UI_Size ancestor_size = ancestor->desc.pref_semantic_dims[axis]; 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 independent ancestor
match_size = ancestor->solved_dims.v[axis]; match_size = ancestor->solved_dims.v[axis];
found_match = 1; 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]; UI_Box *box = boxes_post[post_index];
for (Axis axis = 0; axis < Axis_COUNTXY; ++axis) for (Axis axis = 0; axis < Axis_COUNTXY; ++axis)
{ {
UI_Size sem_dims = box->desc.pref_semantic_dims[axis]; UI_Size size = box->desc.pref_semantic_dims[axis];
if (sem_dims.kind == UI_SizeKind_Shrink && !AnyBit(box->desc.flags, UI_BoxFlag_DrawText)) if (size.kind == UI_SizeKind_Shrink && !(AnyBit(box->desc.flags, UI_BoxFlag_DrawText) || !SPR_IsSheetKeyNil(box->desc.sprite_sheet)))
{ {
f32 accum = 0; f32 accum = 0;
for (UI_Box *child = box->first; child; child = child->next) 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) if (box->parent)
{ {
Axis axis = !box->parent->desc.child_layout_axis; Axis axis = !box->parent->desc.child_layout_axis;
UI_Size sem_dims = box->desc.pref_semantic_dims[axis]; UI_Size size = box->desc.pref_semantic_dims[axis];
if (sem_dims.kind == UI_SizeKind_Grow) 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) for (u64 pre_index = 0; pre_index < boxes_count; ++pre_index)
{ {
UI_Box *box = boxes_pre[pre_index]; UI_Box *box = boxes_pre[pre_index];
UI_RegionPair child_alignment = UI_PairFromRegion(box->desc.child_alignment);
b32 is_visible = 1; b32 is_visible = 1;
is_visible = is_visible && (box->desc.tint.w >= 0.0025); 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 debug_lin = is_visible ? LinearFromSrgb(box->desc.debug_color) : LinearFromSrgb(box->desc.invisible_debug_color);
Vec4 tint_lin = LinearFromSrgb(box->desc.tint); 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 // Box rect
{ {
@ -1536,6 +1565,55 @@ void UI_EndFrame(UI_Frame *frame, i32 vsync)
rect->tex_slice_uv = box->raw_texture_slice_uv; 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 // Text rects
GC_Run raw_run_unscaled = box->glyph_run; GC_Run raw_run_unscaled = box->glyph_run;
GC_Run raw_run = UI_ScaleRun(frame->arena, raw_run_unscaled, box->solved_scale); 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; 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 // Compute baseline
f32 ascent = raw_run.font_ascent; f32 ascent = raw_run.font_ascent;
f32 font_descent = raw_run.font_descent; f32 font_descent = raw_run.font_descent;
f32 cap = raw_run.font_cap; f32 cap = raw_run.font_cap;
f32 baseline_height = ascent + font_descent; f32 baseline_height = ascent + font_descent;
Vec2 box_dims = DimsFromRng2(box->screen_rect);
Vec2 baseline = Zi; Vec2 baseline = Zi;
switch (x_alignment) switch (x_alignment)
{ {

View File

@ -121,6 +121,9 @@ Enum(UI_BoxFlag)
X(TextColor, Vec4) \ X(TextColor, Vec4) \
X(Text, String) \ X(Text, String) \
X(Icon, UI_Icon) \ X(Icon, UI_Icon) \
X(SpriteSheet, SPR_SheetKey) \
X(SpriteSpan, SPR_SpanKey) \
X(SpriteSeq, i64) \
X(BackgroundTexture, G_Texture2DRef) \ X(BackgroundTexture, G_Texture2DRef) \
X(BackgroundTextureSliceUv, Rng2) \ X(BackgroundTextureSliceUv, Rng2) \
X(Misc, f64) \ X(Misc, f64) \
@ -255,6 +258,9 @@ Struct(UI_BoxDesc)
UI_Region child_alignment; UI_Region child_alignment;
UI_Region anchor; UI_Region anchor;
Vec2 floating_pos; Vec2 floating_pos;
SPR_SheetKey sprite_sheet;
SPR_SpanKey sprite_span;
i64 sprite_seq;
f64 misc; f64 misc;
}; };
@ -308,6 +314,7 @@ Struct(UI_Box)
G_Texture2DRef raw_texture; G_Texture2DRef raw_texture;
Rng2 raw_texture_slice_uv; Rng2 raw_texture_slice_uv;
GC_Run glyph_run; GC_Run glyph_run;
SPR_Sprite sprite;
//- Pre-layout data //- Pre-layout data
u64 pre_index; u64 pre_index;
@ -527,7 +534,7 @@ Arena *UI_FrameArena(void);
Vec2 UI_CursorPos(void); Vec2 UI_CursorPos(void);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Text layout helpers //~ Layout helpers
GC_Run UI_ScaleRun(Arena *arena, GC_Run unscaled_run, Vec2 scale); GC_Run UI_ScaleRun(Arena *arena, GC_Run unscaled_run, Vec2 scale);