text layouting

This commit is contained in:
jacob 2025-10-23 03:03:12 -05:00
parent ee1f720fa1
commit 56fd5e19ff
14 changed files with 354 additions and 167 deletions

View File

@ -257,7 +257,7 @@ i64 LerpI64(i64 val0, i64 val1, f64 t);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Vec2 operations //~ Vec2 operations
#define Vec2FromVec2I32(v) VEC2((v).x, (v).y) #define Vec2FromVec(v) VEC2((v).x, (v).y)
b32 IsVec2Zero(Vec2 a); b32 IsVec2Zero(Vec2 a);
b32 EqVec2(Vec2 a, Vec2 b); b32 EqVec2(Vec2 a, Vec2 b);

View File

@ -52,6 +52,20 @@ Vec4 LinearFromSrgbU32(u32 srgb32)
return LinearFromSrgbVec4(Vec4NormFromU32(srgb32)); return LinearFromSrgbVec4(Vec4NormFromU32(srgb32));
} }
////////////////////////////////////////////////////////////
//~ Vertex ID helpers
Vec2 RectUvFromVertexId(u32 id)
{
static const Vec2 uvs[4] = {
Vec2(0, 0),
Vec2(1, 0),
Vec2(1, 1),
Vec2(0, 1)
};
return uvs[id];
}
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Ndc helpers //~ Ndc helpers

View File

@ -130,6 +130,8 @@ JobDef(F_Load, sig, _)
font->image_height = decoded.image_height; font->image_height = decoded.image_height;
font->glyphs_count = decoded.glyphs_count; font->glyphs_count = decoded.glyphs_count;
font->point_size = point_size; font->point_size = point_size;
font->ascent = decoded.ascent;
font->descent = decoded.descent;
/* FIXME: Load baked font instead of panicking */ /* FIXME: Load baked font instead of panicking */
if (font->glyphs_count <= 0) if (font->glyphs_count <= 0)
@ -140,7 +142,8 @@ JobDef(F_Load, sig, _)
} }
/* Copy glyphs from decode decoded */ /* Copy glyphs from decode decoded */
StaticAssert(sizeof(*font->glyphs) == sizeof(*decoded.glyphs)); /* Font glyph size must match TTF glyph size for memcpy */ /* NOTE: Font glyph size must match TTF glyph size for memcpy */
StaticAssert(sizeof(*font->glyphs) == sizeof(*decoded.glyphs));
CopyBytes(font->glyphs, decoded.glyphs, sizeof(*font->glyphs) * decoded.glyphs_count); CopyBytes(font->glyphs, decoded.glyphs, sizeof(*font->glyphs) * decoded.glyphs_count);
/* Build lookup table */ /* Build lookup table */
@ -237,19 +240,86 @@ F_Glyph F_GlyphFromCodepoint(F_Font *font, u32 codepoint)
} }
} }
F_GlyphRun F_GlyphRunFromString(Arena *arena, F_Font *font, String str) F_Run F_RunFromString(Arena *arena, F_Font *font, String str)
{ {
F_GlyphRun result = ZI; F_Run result = ZI;
result.glyphs = PushDry(arena, F_Glyph); result.rects = PushDry(arena, F_RunRect);
for (CodepointIter it = InitCodepointIter(str); NextCodepoint(&it);) for (CodepointIter it = InitCodepointIter(str); NextCodepoint(&it);)
{ {
u32 codepoint = it.codepoint; u32 codepoint = it.codepoint;
u16 index = font->lookup[codepoint]; u16 index = font->lookup[codepoint];
F_Glyph glyph = font->glyphs[index]; F_Glyph glyph = font->glyphs[index];
*PushStructNoZero(arena, F_Glyph) = glyph; F_RunRect *rect = PushStruct(arena, F_RunRect);
++result.count; ++result.count;
result.width += glyph.advance;
result.height = MaxF32(result.height, glyph.height); rect->atlas_p0 = glyph.atlas_p0;
rect->atlas_p1 = glyph.atlas_p1;
Vec2I32 size = SubVec2I32(glyph.atlas_p1, glyph.atlas_p0);
rect->baseline_start_offset = glyph.baseline_offset;
rect->baseline_start_offset.x += result.baseline_length;
result.p0.x = MinF32(result.p0.x, rect->baseline_start_offset.x); /* Left run bounds */
result.p1.x = MaxF32(result.p1.x, rect->baseline_start_offset.x + size.x); /* Right run bounds */
result.p0.y = MinF32(result.p0.y, rect->baseline_start_offset.y); /* Top run bounds */
result.p1.y = MaxF32(result.p1.y, rect->baseline_start_offset.y + size.y); /* Bottom run bounds */
result.baseline_length += glyph.advance;
} }
return result; return result;
#if 0
F_Run result = ZI;
result.rects = PushDry(arena, F_RunRect);
f32 left = 0;
f32 right = 0;
f32 width = 0;
f32 top = 0;
f32 bottom = 0;
f32 baseline_cursor = 0;
f32 baseline_depth = 0;
for (CodepointIter it = InitCodepointIter(str); NextCodepoint(&it);)
{
u32 codepoint = it.codepoint;
u16 index = font->lookup[codepoint];
F_Glyph glyph = font->glyphs[index];
F_RunRect *rect = PushStruct(arena, F_RunRect);
++result.count;
rect->atlas_p0 = glyph.atlas_p0;
rect->atlas_p1 = glyph.atlas_p1;
Vec2I32 size = SubVec2I32(glyph.atlas_p1, glyph.atlas_p0);
f32 offset_x = glyph.baseline_offset.x;
f32 offset_y = glyph.baseline_offset.y;
rect->pos.x = baseline_cursor + offset_x;
/* NOTE: Actual Y position gets calculated in second pass after baseline depth is determined */
rect->pos.y = offset_y;
baseline_depth = MaxF32(baseline_depth, -offset_y);
left = MinF32(left, rect->pos.x);
top = MinF32(top, offset_y);
right = MaxF32(right, rect->pos.x + size.x);
bottom = MaxF32(bottom, rect->pos.y + size.y);
baseline_cursor += glyph.advance;
}
/* Adjust rect Y positions so they're relative to top of run */
for (u32 i = 0; i < result.count; ++i)
{
F_RunRect *rect = &result.rects[i];
rect->pos.y += baseline_depth;
}
result.size = VEC2(right - left, bottom - top);
result.baseline_depth = baseline_depth;
return result;
#endif
} }

View File

@ -5,20 +5,10 @@
Struct(F_Glyph) Struct(F_Glyph)
{ {
f32 off_x;
f32 off_y;
f32 width;
f32 height;
i32 advance; i32 advance;
Rect atlas_rect; Vec2 baseline_offset;
}; Vec2I32 atlas_p0;
Vec2I32 atlas_p1;
Struct(F_GlyphRun)
{
f32 width;
f32 height;
u64 count;
F_Glyph *glyphs;
}; };
Struct(F_Font) Struct(F_Font)
@ -26,10 +16,33 @@ Struct(F_Font)
GPU_Resource *texture; GPU_Resource *texture;
u32 image_width; u32 image_width;
u32 image_height; u32 image_height;
f32 point_size;
u16 glyphs_count; u16 glyphs_count;
F_Glyph *glyphs; F_Glyph *glyphs;
u16 *lookup; u16 *lookup;
/* Metrics */
f32 point_size;
f32 ascent;
f32 descent;
};
////////////////////////////////////////////////////////////
//~ Run types
Struct(F_RunRect)
{
Vec2 baseline_start_offset; /* Vector from start of baseline to top left of rect */
Vec2I32 atlas_p0;
Vec2I32 atlas_p1;
};
Struct(F_Run)
{
Vec2 p0; /* Start of baseline to top-left-most rect */
Vec2 p1; /* Start of baseline to bottom-right-most rect corner */
f32 baseline_length;
u32 count;
F_RunRect *rects;
}; };
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
@ -45,7 +58,7 @@ F_Font *F_LoadFontAsync(Resource resource, f32 point_size);
F_Font *F_LoadFontWait(Resource resource, f32 point_size); F_Font *F_LoadFontWait(Resource resource, f32 point_size);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Font data operations //~ Run operations
F_Glyph F_GlyphFromCodepoint(F_Font *font, u32 codepoint); F_Glyph F_GlyphFromCodepoint(F_Font *font, u32 codepoint);
F_GlyphRun F_GlyphRunFromString(Arena *arena, F_Font *font, String str); F_Run F_RunFromString(Arena *arena, F_Font *font, String str);

View File

@ -59,7 +59,19 @@ ExitFuncDef(ShutdownUser)
} }
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Debug draw //~ Ui helpers
//- Ui style
void PushGameUiStyle(void)
{
// UI_Push(FontSize, 48);
UI_Push(FontSize, 24);
// UI_Push(FontSize, 12);
UI_Push(TextPadding, 12);
// UI_Push(Border, 1);
// UI_Push(BorderColor, Rgba32F(1, 0, 1, 0.4));
}
//- Draw xform //- Draw xform
void DrawDebugXform(Xform xf, u32 color_x, u32 color_y) void DrawDebugXform(Xform xf, u32 color_x, u32 color_y)
@ -451,6 +463,7 @@ void UpdateUser(P_Window *window)
//- Begin UI //- Begin UI
UI_BeginBuild(); UI_BeginBuild();
PushGameUiStyle();
UI_Box *pp_root_box = UI_BuildBox(UI_BoxFlag_DrawImage, UI_NilKey); UI_Box *pp_root_box = UI_BuildBox(UI_BoxFlag_DrawImage, UI_NilKey);
UI_Push(Parent, pp_root_box); UI_Push(Parent, pp_root_box);
@ -1224,8 +1237,8 @@ void UpdateUser(P_Window *window)
mat->xf = sprite_xform; mat->xf = sprite_xform;
mat->tex = GPU_Texture2DRidFromResource(texture->gpu_texture); mat->tex = GPU_Texture2DRidFromResource(texture->gpu_texture);
mat->tint_srgb = tint; mat->tint_srgb = tint;
mat->uv0 = frame.clip.p0; mat->tex_uv0 = frame.clip.p0;
mat->uv1 = frame.clip.p1; mat->tex_uv1 = frame.clip.p1;
mat->is_light = is_light; mat->is_light = is_light;
mat->light_emittance_srgb = emittance; mat->light_emittance_srgb = emittance;
} }
@ -1999,8 +2012,6 @@ void UpdateUser(P_Window *window)
////////////////////////////// //////////////////////////////
//- Debug draw //- Debug draw
//~ Draw global debug info
/* FIXME: Enable this */ /* FIXME: Enable this */
#if 1 #if 1
#if 1 #if 1
@ -2015,90 +2026,93 @@ void UpdateUser(P_Window *window)
UI_Push(Parent, dbg_box); UI_Push(Parent, dbg_box);
UI_Push(Width, UI_TextSize(0)); UI_Push(Width, UI_TextSize(0));
UI_Push(Height, UI_TextSize(0)); UI_Push(Height, UI_TextSize(0));
UI_Push(BackgroundColor, Rgba32F(0.3, 0.3, 0.3, 1)); UI_Push(BackgroundColor, Rgba32F(0.3, 0.6, 0.3, 0.5));
UI_BuildLabelF("ytest string");
// UI_BuildSeparator();
UI_BuildLabelF("blended world entities: %F/%F", FmtUint(g->ss_blended->num_ents_allocated), FmtUint(g->ss_blended->num_ents_reserved)); UI_BuildLabelF("blended world entities: %F/%F", FmtUint(g->ss_blended->num_ents_allocated), FmtUint(g->ss_blended->num_ents_reserved));
UI_BuildSeparator(); // UI_BuildSeparator();
UI_BuildLabelF("blended world tick: %F", FmtUint(g->ss_blended->tick)); UI_BuildLabelF("blended world tick: %F", FmtUint(g->ss_blended->tick));
UI_BuildSeparator(); // UI_BuildSeparator();
UI_BuildLabelF("blended world time: %F", FmtFloat(SecondsFromNs(g->ss_blended->sim_time_ns))); UI_BuildLabelF("blended world time: %F", FmtFloat(SecondsFromNs(g->ss_blended->sim_time_ns)));
UI_BuildSeparator(); // UI_BuildSeparator();
UI_BuildSeparator(); // UI_BuildSeparator();
UI_BuildLabelF("average local sim publish dt: %F", FmtFloat(SecondsFromNs(g->average_local_to_user_snapshot_publish_dt_ns))); UI_BuildLabelF("average local sim publish dt: %F", FmtFloat(SecondsFromNs(g->average_local_to_user_snapshot_publish_dt_ns)));
UI_BuildSeparator(); // UI_BuildSeparator();
UI_BuildLabelF("local sim last known tick: %F", FmtUint(g->local_sim_last_known_tick)); UI_BuildLabelF("local sim last known tick: %F", FmtUint(g->local_sim_last_known_tick));
UI_BuildSeparator(); // UI_BuildSeparator();
UI_BuildLabelF("local sim last known time: %F", FmtFloat(SecondsFromNs(g->local_sim_last_known_time_ns))); UI_BuildLabelF("local sim last known time: %F", FmtFloat(SecondsFromNs(g->local_sim_last_known_time_ns)));
UI_BuildSeparator(); // UI_BuildSeparator();
UI_BuildLabelF("local sim predicted time: %F", FmtFloat(SecondsFromNs(g->local_sim_predicted_time_ns))); UI_BuildLabelF("local sim predicted time: %F", FmtFloat(SecondsFromNs(g->local_sim_predicted_time_ns)));
UI_BuildSeparator(); // UI_BuildSeparator();
UI_BuildLabelF("render time target: %F", FmtFloat(SecondsFromNs(g->render_time_target_ns))); UI_BuildLabelF("render time target: %F", FmtFloat(SecondsFromNs(g->render_time_target_ns)));
UI_BuildSeparator(); // UI_BuildSeparator();
UI_BuildLabelF("render time: %F", FmtFloat(SecondsFromNs(g->render_time_ns))); UI_BuildLabelF("render time: %F", FmtFloat(SecondsFromNs(g->render_time_ns)));
UI_BuildSeparator(); // UI_BuildSeparator();
UI_BuildSeparator(); // UI_BuildSeparator();
UI_BuildLabelF("local player: [%F]", FmtUid(local_player->id.uid)); UI_BuildLabelF("local player: [%F]", FmtUid(local_player->id.uid));
UI_BuildSeparator(); // UI_BuildSeparator();
UI_BuildSeparator(); // UI_BuildSeparator();
Vec2 world_cursor = g->world_cursor; Vec2 world_cursor = g->world_cursor;
UI_BuildLabelF("cursor world: %F, %F", FmtFloat(world_cursor.x), FmtFloat(world_cursor.y)); UI_BuildLabelF("cursor world: %F, %F", FmtFloat(world_cursor.x), FmtFloat(world_cursor.y));
UI_BuildSeparator(); // UI_BuildSeparator();
Vec2I32 world_tile_cursor = WorldTileIndexFromPos(world_cursor); Vec2I32 world_tile_cursor = WorldTileIndexFromPos(world_cursor);
UI_BuildLabelF("cursor world tile: %F, %F", FmtSint(world_tile_cursor.x), FmtSint(world_tile_cursor.y)); UI_BuildLabelF("cursor world tile: %F, %F", FmtSint(world_tile_cursor.x), FmtSint(world_tile_cursor.y));
UI_BuildSeparator(); // UI_BuildSeparator();
Vec2I32 local_tile_cursor = LocalTileIndexFromWorldTileIndex(world_tile_cursor); Vec2I32 local_tile_cursor = LocalTileIndexFromWorldTileIndex(world_tile_cursor);
UI_BuildLabelF("cursor local tile: %F, %F", FmtSint(local_tile_cursor.x), FmtSint(local_tile_cursor.y)); UI_BuildLabelF("cursor local tile: %F, %F", FmtSint(local_tile_cursor.x), FmtSint(local_tile_cursor.y));
UI_BuildSeparator(); // UI_BuildSeparator();
Vec2I32 tile_chunk_cursor = TileChunkIndexFromWorldTileIndex(world_tile_cursor); Vec2I32 tile_chunk_cursor = TileChunkIndexFromWorldTileIndex(world_tile_cursor);
UI_BuildLabelF("cursor tile chunk: %F, %F", FmtSint(tile_chunk_cursor.x), FmtSint(tile_chunk_cursor.y)); UI_BuildLabelF("cursor tile chunk: %F, %F", FmtSint(tile_chunk_cursor.x), FmtSint(tile_chunk_cursor.y));
UI_BuildSeparator(); // UI_BuildSeparator();
UI_BuildSeparator(); // UI_BuildSeparator();
UI_BuildLabelF("Network read: %F mbit/s", FmtFloat((f64)g->net_bytes_read.last_second * 8 / 1000 / 1000)); UI_BuildLabelF("Network read: %F mbit/s", FmtFloat((f64)g->net_bytes_read.last_second * 8 / 1000 / 1000));
UI_BuildSeparator(); // UI_BuildSeparator();
UI_BuildLabelF("Network write: %F mbit/s", FmtFloat((f64)g->net_bytes_sent.last_second * 8 / 1000 / 1000)); UI_BuildLabelF("Network write: %F mbit/s", FmtFloat((f64)g->net_bytes_sent.last_second * 8 / 1000 / 1000));
UI_BuildSeparator(); // UI_BuildSeparator();
UI_BuildLabelF("Ping (real): %F ms", FmtFloat(SecondsFromNs(local_player->player_last_rtt_ns) * 1000)); UI_BuildLabelF("Ping (real): %F ms", FmtFloat(SecondsFromNs(local_player->player_last_rtt_ns) * 1000));
UI_BuildSeparator(); // UI_BuildSeparator();
UI_BuildLabelF("Ping (average): %F ms", FmtFloat(local_player->player_average_rtt_seconds * 1000)); UI_BuildLabelF("Ping (average): %F ms", FmtFloat(local_player->player_average_rtt_seconds * 1000));
UI_BuildSeparator(); // UI_BuildSeparator();
UI_BuildSeparator(); // UI_BuildSeparator();
UI_BuildLabelF("Memory committed: %F MiB", FmtFloat((f64)GetGstat(GSTAT_MEMORY_COMMITTED) / 1024 / 1024)); UI_BuildLabelF("Memory committed: %F MiB", FmtFloat((f64)GetGstat(GSTAT_MEMORY_COMMITTED) / 1024 / 1024));
UI_BuildSeparator(); // UI_BuildSeparator();
UI_BuildLabelF("Virtual memory reserved: %F TiB", FmtFloat((f64)GetGstat(GSTAT_MEMORY_RESERVED) / 1024 / 1024 / 1024 / 1024)); UI_BuildLabelF("Virtual memory reserved: %F TiB", FmtFloat((f64)GetGstat(GSTAT_MEMORY_RESERVED) / 1024 / 1024 / 1024 / 1024));
UI_BuildSeparator(); // UI_BuildSeparator();
UI_BuildLabelF("Arenas allocated: %F", FmtUint(GetGstat(GSTAT_NUM_ARENAS))); UI_BuildLabelF("Arenas allocated: %F", FmtUint(GetGstat(GSTAT_NUM_ARENAS)));
UI_BuildSeparator(); // UI_BuildSeparator();
UI_BuildSeparator(); // UI_BuildSeparator();
UI_BuildLabelF("Video memory (GPU): %F MiB", FmtFloat((f64)vram.local_used / 1024 / 1024)); UI_BuildLabelF("Video memory (GPU): %F MiB", FmtFloat((f64)vram.local_used / 1024 / 1024));
UI_BuildSeparator(); // UI_BuildSeparator();
UI_BuildLabelF("Video memory (shared): %F MiB", FmtFloat((f64)vram.non_local_used / 1024 / 1024)); UI_BuildLabelF("Video memory (shared): %F MiB", FmtFloat((f64)vram.non_local_used / 1024 / 1024));
//UI_BuildLabelF(\n")); //UI_BuildLabelF(\n"));
//UI_BuildLabelF(\n")); //UI_BuildLabelF(\n"));
#if RtcIsEnabled #if RtcIsEnabled
UI_BuildSeparator(); // UI_BuildSeparator();
UI_BuildSeparator(); // UI_BuildSeparator();
UI_BuildLabelF("Debug steps: %F", FmtUint(GetGstat(GSTAT_DEBUG_STEPS))); UI_BuildLabelF("Debug steps: %F", FmtUint(GetGstat(GSTAT_DEBUG_STEPS)));
//UI_BuildLabelF(\n")); //UI_BuildLabelF(\n"));
@ -2552,6 +2566,7 @@ void UpdateUser(P_Window *window)
/* Render UI */ /* Render UI */
UI_SetDisplayImage(pp_root_box, g->ui_target); UI_SetDisplayImage(pp_root_box, g->ui_target);
// UI_SetDisplayImage(pp_root_box, F_LoadFontWait(UI_GetDefaultFontResource(), 12.0)->texture);
GPU_Resource *ui_render = UI_EndBuild(ui_viewport); GPU_Resource *ui_render = UI_EndBuild(ui_viewport);
////////////////////////////// //////////////////////////////

View File

@ -282,8 +282,9 @@ void StartupUser(void);
ExitFuncDef(ShutdownUser); ExitFuncDef(ShutdownUser);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Debug draw operations //~ Ui helpers
void PushGameUiStyle(void);
void DrawDebugXform(Xform xf, u32 color_x, u32 color_y); void DrawDebugXform(Xform xf, u32 color_x, u32 color_y);
void DrawDebugMovement(Entity *ent); void DrawDebugMovement(Entity *ent);
String DebugStringFromEntity(Arena *arena, Entity *ent); String DebugStringFromEntity(Arena *arena, Entity *ent);

View File

@ -13,7 +13,7 @@ Struct(MaterialPS_Input)
Semantic(Vec4, sv_position); Semantic(Vec4, sv_position);
Semantic(nointerpolation Texture2DRid, tex); Semantic(nointerpolation Texture2DRid, tex);
Semantic(nointerpolation u32, grid_id); Semantic(nointerpolation u32, grid_id);
Semantic(Vec2, uv); Semantic(Vec2, tex_uv);
Semantic(Vec4, tint_lin); Semantic(Vec4, tint_lin);
Semantic(Vec4, emittance_lin); Semantic(Vec4, emittance_lin);
}; };
@ -29,24 +29,18 @@ Struct(MaterialPS_Output)
MaterialPS_Input VSDef(MaterialVS, Semantic(u32, sv_instanceid), Semantic(u32, sv_vertexid)) MaterialPS_Input VSDef(MaterialVS, Semantic(u32, sv_instanceid), Semantic(u32, sv_vertexid))
{ {
ConstantBuffer<MaterialSig> sig = mat_sig; ConstantBuffer<MaterialSig> sig = mat_sig;
static const Vec2 unit_quad_verts[4] = {
Vec2(-0.5f, -0.5f),
Vec2(0.5f, -0.5f),
Vec2(0.5f, 0.5f),
Vec2(-0.5f, 0.5f)
};
StructuredBuffer<MaterialInstance> instances = UniformResourceFromRid(sig.instances); StructuredBuffer<MaterialInstance> instances = UniformResourceFromRid(sig.instances);
Vec2 vert = unit_quad_verts[sv_vertexid];
MaterialInstance instance = instances[sv_instanceid]; MaterialInstance instance = instances[sv_instanceid];
Vec2 world_pos = mul(instance.xf, Vec3(vert, 1)).xy; Vec2 mat_uv = RectUvFromVertexId(sv_vertexid);
Vec2 mat_uv_centered = mat_uv - 0.5f;
Vec2 world_pos = mul(instance.xf, Vec3(mat_uv_centered, 1)).xy;
MaterialPS_Input result; MaterialPS_Input result;
result.sv_position = mul(sig.projection, Vec4(world_pos, 0, 1)); result.sv_position = mul(sig.projection, Vec4(world_pos, 0, 1));
result.tex = instance.tex; result.tex = instance.tex;
result.grid_id = instance.grid_id; result.grid_id = instance.grid_id;
result.uv = instance.uv0 + ((vert + 0.5) * (instance.uv1 - instance.uv0)); result.tex_uv = lerp(instance.tex_uv0, instance.tex_uv1, mat_uv);
result.tint_lin = LinearFromSrgbU32(instance.tint_srgb); result.tint_lin = LinearFromSrgbU32(instance.tint_srgb);
result.emittance_lin = LinearFromSrgbVec4(Vec4(instance.light_emittance_srgb, instance.is_light)); result.emittance_lin = LinearFromSrgbVec4(Vec4(instance.light_emittance_srgb, instance.is_light));
return result; return result;
@ -66,7 +60,7 @@ MaterialPS_Output PSDef(MaterialPS, MaterialPS_Input input)
{ {
SamplerState sampler = UniformSamplerFromRid(sig.sampler); SamplerState sampler = UniformSamplerFromRid(sig.sampler);
Texture2D<Vec4> tex = NonUniformResourceFromRid(input.tex); Texture2D<Vec4> tex = NonUniformResourceFromRid(input.tex);
albedo *= tex.Sample(sampler, input.uv); albedo *= tex.Sample(sampler, input.tex_uv);
} }
/* Grid */ /* Grid */
@ -163,7 +157,7 @@ void CSDef(FloodCS, Semantic(Vec3U32, sv_dispatchthreadid))
(Vec2I32)id + Vec2I32(+step_len, 0), /* center right */ (Vec2I32)id + Vec2I32(+step_len, 0), /* center right */
(Vec2I32)id + Vec2I32(-step_len, +step_len), /* bottom left */ (Vec2I32)id + Vec2I32(-step_len, +step_len), /* bottom left */
(Vec2I32)id + Vec2I32(0, +step_len), /* bottom center */ (Vec2I32)id + Vec2I32(0, +step_len), /* bottom center */
(Vec2I32)id + Vec2I32(+step_len, +step_len) /* bottom right */ (Vec2I32)id + Vec2I32(+step_len, +step_len), /* bottom right */
}; };
Vec2U32 closest_seed = Vec2U32(0xFFFF, 0xFFFF); Vec2U32 closest_seed = Vec2U32(0xFFFF, 0xFFFF);
u32 closest_seed_len_sq = 0xFFFFFFFF; u32 closest_seed_len_sq = 0xFFFFFFFF;
@ -300,7 +294,7 @@ void CSDef(ShadeCS, Semantic(Vec3U32, sv_dispatchthreadid))
Struct(UiBlitPS_Input) Struct(UiBlitPS_Input)
{ {
Semantic(Vec4, sv_position); Semantic(Vec4, sv_position);
Semantic(Vec2, uv); Semantic(Vec2, tex_uv);
}; };
Struct(UiBlitPS_Output) Struct(UiBlitPS_Output)
@ -322,17 +316,12 @@ Vec3 ToneMap(Vec3 v)
UiBlitPS_Input VSDef(UiBlitVS, Semantic(u32, sv_vertexid)) UiBlitPS_Input VSDef(UiBlitVS, Semantic(u32, sv_vertexid))
{ {
ConstantBuffer<UiBlitSig> sig = ui_blit_sig; ConstantBuffer<UiBlitSig> sig = ui_blit_sig;
static const Vec2 unit_quad_verts[4] = { Vec2 tex_uv = RectUvFromVertexId(sv_vertexid);
Vec2(-0.5f, -0.5f), Vec2 tex_uv_centered = tex_uv - 0.5;
Vec2(0.5f, -0.5f),
Vec2(0.5f, 0.5f),
Vec2(-0.5f, 0.5f)
};
Vec2 vert = unit_quad_verts[sv_vertexid];
UiBlitPS_Input result; UiBlitPS_Input result;
result.sv_position = mul(sig.projection, Vec4(vert, 0, 1)); result.sv_position = mul(sig.projection, Vec4(tex_uv_centered, 0, 1));
result.uv = vert + 0.5; result.tex_uv = tex_uv;
return result; return result;
} }
@ -345,7 +334,7 @@ UiBlitPS_Output PSDef(UiBlitPS, UiBlitPS_Input input)
UiBlitPS_Output result; UiBlitPS_Output result;
Texture2D<Vec4> tex = UniformResourceFromRid(sig.src); Texture2D<Vec4> tex = UniformResourceFromRid(sig.src);
Vec4 color = tex.Sample(sampler, input.uv); Vec4 color = tex.Sample(sampler, input.tex_uv);
/* Apply tone map */ /* Apply tone map */
if (sig.flags & UiBlitFlag_ToneMap) if (sig.flags & UiBlitFlag_ToneMap)
@ -372,7 +361,7 @@ Struct(UiRectPS_Input)
{ {
Semantic(Vec4, sv_position); Semantic(Vec4, sv_position);
Semantic(nointerpolation Texture2DRid, tex); Semantic(nointerpolation Texture2DRid, tex);
Semantic(Vec2, uv); Semantic(Vec2, tex_uv);
Semantic(Vec4, tint_srgb); Semantic(Vec4, tint_srgb);
}; };
@ -386,22 +375,17 @@ Struct(UiRectPS_Output)
UiRectPS_Input VSDef(UiRectVS, Semantic(u32, sv_instanceid), Semantic(u32, sv_vertexid)) UiRectPS_Input VSDef(UiRectVS, Semantic(u32, sv_instanceid), Semantic(u32, sv_vertexid))
{ {
ConstantBuffer<UiRectSig> sig = ui_rect_sig; ConstantBuffer<UiRectSig> sig = ui_rect_sig;
static const Vec2 unit_quad_verts[4] = {
Vec2(-0.5f, -0.5f),
Vec2(0.5f, -0.5f),
Vec2(0.5f, 0.5f),
Vec2(-0.5f, 0.5f)
};
StructuredBuffer<UiRectInstance> instances = UniformResourceFromRid(sig.instances); StructuredBuffer<UiRectInstance> instances = UniformResourceFromRid(sig.instances);
UiRectInstance instance = instances[sv_instanceid]; UiRectInstance instance = instances[sv_instanceid];
Vec2 vert = unit_quad_verts[sv_vertexid];
Vec2 world_pos = mul(instance.xf, Vec3(vert, 1)).xy; Vec2 rect_uv = RectUvFromVertexId(sv_vertexid);
Vec2 rect_uv_centered = rect_uv - 0.5;
Vec2 world_pos = mul(instance.xf, Vec3(rect_uv_centered, 1)).xy;
UiRectPS_Input result; UiRectPS_Input result;
result.sv_position = mul(sig.projection, Vec4(world_pos, 0, 1)); result.sv_position = mul(sig.projection, Vec4(world_pos, 0, 1));
result.tex = instance.tex; result.tex = instance.tex;
result.uv = instance.uv0 + ((vert + 0.5) * (instance.uv1 - instance.uv0)); result.tex_uv = lerp(instance.tex_uv0, instance.tex_uv1, rect_uv);
result.tint_srgb = Vec4NormFromU32(instance.tint_srgb); result.tint_srgb = Vec4NormFromU32(instance.tint_srgb);
return result; return result;
} }
@ -419,7 +403,7 @@ UiRectPS_Output PSDef(UiRectPS, UiRectPS_Input input)
{ {
Texture2D<Vec4> tex = NonUniformResourceFromRid(input.tex); Texture2D<Vec4> tex = NonUniformResourceFromRid(input.tex);
SamplerState sampler = UniformSamplerFromRid(sig.sampler); SamplerState sampler = UniformSamplerFromRid(sig.sampler);
color *= tex.Sample(sampler, input.uv); color *= tex.Sample(sampler, input.tex_uv);
} }
result.sv_target0 = color; result.sv_target0 = color;

View File

@ -19,8 +19,8 @@ Struct(MaterialInstance)
Texture2DRid tex; Texture2DRid tex;
u32 grid_id; u32 grid_id;
Xform xf; Xform xf;
Vec2 uv0; Vec2 tex_uv0;
Vec2 uv1; Vec2 tex_uv1;
u32 tint_srgb; u32 tint_srgb;
u32 is_light; u32 is_light;
Vec3 light_emittance_srgb; Vec3 light_emittance_srgb;
@ -29,7 +29,7 @@ Struct(MaterialInstance)
.tex = { U32Max }, \ .tex = { U32Max }, \
.grid_id = U32Max, \ .grid_id = U32Max, \
.xf = XformIdentity, \ .xf = XformIdentity, \
.uv1 = VEC2(1, 1), \ .tex_uv1 = VEC2(1, 1), \
.tint_srgb = Color_White, \ .tint_srgb = Color_White, \
} }
@ -149,8 +149,8 @@ Struct(UiRectInstance)
{ {
Texture2DRid tex; Texture2DRid tex;
Xform xf; Xform xf;
Vec2 uv0; Vec2 tex_uv0;
Vec2 uv1; Vec2 tex_uv1;
u32 tint_srgb; u32 tint_srgb;
}; };

View File

@ -1,11 +1,9 @@
Struct(TTF_Glyph) Struct(TTF_Glyph)
{ {
f32 off_x;
f32 off_y;
f32 width;
f32 height;
i32 advance; i32 advance;
Rect atlas_rect; Vec2 baseline_offset;
Vec2I32 atlas_p0;
Vec2I32 atlas_p1;
}; };
Struct(TTF_Decoded) Struct(TTF_Decoded)
@ -16,6 +14,11 @@ Struct(TTF_Decoded)
u32 image_width; u32 image_width;
u32 image_height; u32 image_height;
u32 *image_pixels; /* Array of [width * height] pixels */ u32 *image_pixels; /* Array of [width * height] pixels */
/* Metrics */
f32 ascent;
f32 descent;
}; };
void TTF_Startup(void); void TTF_Startup(void);

View File

@ -128,13 +128,16 @@ TTF_Decoded TTF_Decode(Arena *arena, String encoded, f32 point_size, u32 *cache_
TTF_Glyph *glyphs = (TTF_Glyph *)PushStructs(arena, TTF_Glyph, glyph_count); TTF_Glyph *glyphs = (TTF_Glyph *)PushStructs(arena, TTF_Glyph, glyph_count);
/* Acquire (starting) atlas memory /* Acquire (starting) atlas memory
* NOTE: This is unecessary since atlas memory will grow anyway. Could * NOTE: This is unnecessary since atlas memory will grow anyway. Could
* just start w/ atlas height 0. * just start w/ atlas height 0.
*/ */
u64 atlas_w = 1024; u64 atlas_w = 1024;
u64 atlas_h = 1; u64 atlas_h = 1;
u32 *atlas_memory = PushStructs(arena, u32, atlas_w * atlas_h); u32 *atlas_memory = PushStructs(arena, u32, atlas_w * atlas_h);
i32 ascent = 0;
i32 descent = 0;
//- Fill atlas & metric data //- Fill atlas & metric data
u32 out_offset_x = 0; u32 out_offset_x = 0;
u32 out_offset_y = 0; u32 out_offset_y = 0;
@ -173,6 +176,8 @@ TTF_Decoded TTF_Decode(Arena *arena, String encoded, f32 point_size, u32 *cache_
//- Compute glyph metrics //- Compute glyph metrics
DWRITE_GLYPH_METRICS glyph_metrics = ZI; DWRITE_GLYPH_METRICS glyph_metrics = ZI;
error = IDWriteFontFace_GetDesignGlyphMetrics(font_face, &i, 1, &glyph_metrics, 0); error = IDWriteFontFace_GetDesignGlyphMetrics(font_face, &i, 1, &glyph_metrics, 0);
ascent = metrics.ascent * pixel_per_design_unit;
descent = metrics.descent * pixel_per_design_unit;
f32 off_x = (f32)bounding_box.left - raster_target_x; f32 off_x = (f32)bounding_box.left - raster_target_x;
f32 off_y = (f32)bounding_box.top - raster_target_y; f32 off_y = (f32)bounding_box.top - raster_target_y;
@ -181,10 +186,7 @@ TTF_Decoded TTF_Decode(Arena *arena, String encoded, f32 point_size, u32 *cache_
f32 advance = CeilF32ToI32((f32)glyph_metrics.advanceWidth * pixel_per_design_unit); f32 advance = CeilF32ToI32((f32)glyph_metrics.advanceWidth * pixel_per_design_unit);
TTF_Glyph *glyph = &glyphs[i]; TTF_Glyph *glyph = &glyphs[i];
glyph->off_x = off_x; glyph->baseline_offset = VEC2(off_x, off_y);
glyph->off_y = off_y;
glyph->width = (f32)tex_w;
glyph->height = (f32)tex_h;
glyph->advance = advance; glyph->advance = advance;
/* Get the bitmap */ /* Get the bitmap */
@ -211,11 +213,8 @@ TTF_Decoded TTF_Decode(Arena *arena, String encoded, f32 point_size, u32 *cache_
} }
/* Set bounding box metrics (now that we know atlas x & y) */ /* Set bounding box metrics (now that we know atlas x & y) */
ZeroStruct(&glyph->atlas_rect); glyph->atlas_p0 = VEC2I32(out_offset_x, out_offset_y);
glyph->atlas_rect.x = (f32)out_offset_x; glyph->atlas_p1 = VEC2I32(out_offset_x + tex_w, out_offset_y + tex_h);
glyph->atlas_rect.y = (f32)out_offset_y;
glyph->atlas_rect.width = (f32)tex_w;
glyph->atlas_rect.height = (f32)tex_h;
//- Fill atlas //- Fill atlas
u64 in_pitch = (u64)dib.dsBm.bmWidthBytes / 4; u64 in_pitch = (u64)dib.dsBm.bmWidthBytes / 4;
@ -281,5 +280,7 @@ TTF_Decoded TTF_Decode(Arena *arena, String encoded, f32 point_size, u32 *cache_
result.image_width = (u32)atlas_w; result.image_width = (u32)atlas_w;
result.image_height = (u32)atlas_h; result.image_height = (u32)atlas_h;
result.image_pixels = (u32 *)atlas_memory; result.image_pixels = (u32 *)atlas_memory;
result.ascent = ascent;
result.descent = descent;
return result; return result;
} }

View File

@ -6,10 +6,18 @@ UI_SharedState UI_shared_state = ZI;
void UI_Startup(void) void UI_Startup(void)
{ {
/* Prefetch default font */ /* Prefetch default font */
Resource default_font = ResourceFromStore(&UI_Resources, Lit("font/default.ttf")); Resource default_font = UI_GetDefaultFontResource();
F_LoadFontAsync(default_font, 12); F_LoadFontAsync(default_font, 12);
} }
////////////////////////////////////////////////////////////
//~ Font helpers
Resource UI_GetDefaultFontResource(void)
{
return ResourceFromStore(&UI_Resources, Lit("font/default.ttf"));
}
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Key helpers //~ Key helpers
@ -188,8 +196,11 @@ UI_Box *UI_BuildBox(UI_BoxFlag flags, UI_Key key)
box->pref_size[Axis_Y] = UI_UseTop(Height); box->pref_size[Axis_Y] = UI_UseTop(Height);
box->layout_axis = UI_UseTop(LayoutAxis); box->layout_axis = UI_UseTop(LayoutAxis);
box->background_color = UI_UseTop(BackgroundColor); box->background_color = UI_UseTop(BackgroundColor);
box->border_color = UI_UseTop(BorderColor);
box->border = UI_UseTop(Border);
box->font_resource = UI_UseTop(Font); box->font_resource = UI_UseTop(Font);
box->font_size = UI_UseTop(FontSize); box->font_size = UI_UseTop(FontSize);
box->text_padding = UI_UseTop(TextPadding);
/* Prefetch font */ /* Prefetch font */
if (box->flags & UI_BoxFlag_DrawText) if (box->flags & UI_BoxFlag_DrawText)
@ -268,7 +279,7 @@ void UI_BeginBuild(void)
g->style_tops[UI_StyleKind_Parent]->style.Parent = g->root_box; g->style_tops[UI_StyleKind_Parent]->style.Parent = g->root_box;
g->style_tops[UI_StyleKind_Width]->style.Width = UI_RatioSize(1, 0); g->style_tops[UI_StyleKind_Width]->style.Width = UI_RatioSize(1, 0);
g->style_tops[UI_StyleKind_Height]->style.Height = UI_RatioSize(1, 0); g->style_tops[UI_StyleKind_Height]->style.Height = UI_RatioSize(1, 0);
g->style_tops[UI_StyleKind_Font]->style.Font = ResourceFromStore(&UI_Resources, Lit("font/default.ttf")); g->style_tops[UI_StyleKind_Font]->style.Font = UI_GetDefaultFontResource();
g->style_tops[UI_StyleKind_FontSize]->style.FontSize = 12.0f; g->style_tops[UI_StyleKind_FontSize]->style.FontSize = 12.0f;
} }
@ -351,17 +362,25 @@ GPU_Resource *UI_EndBuild(Rect render_viewport)
} }
else if (pref_size.kind == UI_SizeKind_Text) else if (pref_size.kind == UI_SizeKind_Text)
{ {
/* TODO: Distinguish between baseline alignment & visual alignment */
f32 text_size = 0; f32 text_size = 0;
if (box->display_text.len > 0 && box->font) if (box->display_text.len > 0 && box->font)
{ {
if (box->glyph_run.count == 0) if (box->glyph_run.count == 0)
{ {
box->glyph_run = F_GlyphRunFromString(g->build_arena, box->font, box->display_text); box->glyph_run = F_RunFromString(g->build_arena, box->font, box->display_text);
} }
text_size += box->glyph_run.width * (axis == Axis_X); if (axis == Axis_X)
text_size += box->glyph_run.height * (axis == Axis_Y); {
f32 baseline_length = box->glyph_run.baseline_length;
text_size = baseline_length;
} }
box->solved_dims[axis] = text_size; else
{
text_size = box->font->ascent + box->font->descent;
}
}
box->solved_dims[axis] = text_size + box->text_padding;
} }
} }
} }
@ -477,13 +496,17 @@ GPU_Resource *UI_EndBuild(Rect render_viewport)
rect->tex_uv0 = VEC2(0, 0); rect->tex_uv0 = VEC2(0, 0);
rect->tex_uv1 = VEC2(1, 1); rect->tex_uv1 = VEC2(1, 1);
rect->background_srgb = box->background_color; rect->background_srgb = box->background_color;
rect->border_srgb = box->border_color;
rect->border = box->border;
/* FIXME: Remove this */ /* FIXME: Remove this */
u64 test_idx = rect - ((UI_RectInstance *)ArenaBase(g->draw_rects_arena)); #if 1
if (test_idx == 931) if (pre_index > 2)
{ {
DEBUGBREAKABLE; rect->border_srgb = Rgba32F(0, 1, 1, 0.4);
rect->border = 1;
} }
#endif
if ((box->flags & UI_BoxFlag_DrawImage) && box->display_image) if ((box->flags & UI_BoxFlag_DrawImage) && box->display_image)
@ -495,24 +518,54 @@ GPU_Resource *UI_EndBuild(Rect render_viewport)
if ((box->flags & UI_BoxFlag_DrawText) && box->glyph_run.count > 0 && box->font) if ((box->flags & UI_BoxFlag_DrawText) && box->glyph_run.count > 0 && box->font)
{ {
Texture2DRid tex_rid = GPU_Texture2DRidFromResource(box->font->texture); Texture2DRid tex_rid = GPU_Texture2DRidFromResource(box->font->texture);
f32 inv_font_image_width = 1.0f / (f32)box->font->image_width; Vec2 inv_font_image_size = VEC2(1.0f / (f32)box->font->image_width, 1.0f / (f32)box->font->image_height);
f32 inv_font_image_height = 1.0f / (f32)box->font->image_height;
String text = box->display_text; String text = box->display_text;
F_GlyphRun run = box->glyph_run; F_Run run = box->glyph_run;
f32 offset = 0;
f32 ascent = box->font->ascent;
f32 descent = box->font->descent;
f32 padding = box->text_padding;
f32 half_padding = padding * 0.5f;
Vec2 baseline = box->p0;
baseline = AddVec2(baseline, VEC2(half_padding, half_padding + ascent));
// offset.x += run.width / 2;
// offset.y += run.baseline_depth;
// offset.x += half_padding;
// offset.y += half_padding;
for (u64 i = 0; i < run.count; ++i) for (u64 i = 0; i < run.count; ++i)
{ {
F_Glyph glyph = run.glyphs[i]; F_RunRect rr = run.rects[i];
Rect atlas_rect = glyph.atlas_rect; Vec2 atlas_p0 = Vec2FromVec(rr.atlas_p0);
Vec2 atlas_p1 = Vec2FromVec(rr.atlas_p1);
Vec2 glyph_size = SubVec2(atlas_p1, atlas_p0);
if (glyph_size.x != 0 || glyph_size.y != 0)
{
UI_RectInstance *rect = PushStruct(g->draw_rects_arena, UI_RectInstance); UI_RectInstance *rect = PushStruct(g->draw_rects_arena, UI_RectInstance);
rect->flags |= UI_RectFlag_DrawTexture; rect->flags |= UI_RectFlag_DrawTexture;
rect->p0 = AddVec2(box->p0, VEC2(offset, 0));
rect->p1 = AddVec2(rect->p0, VEC2(glyph.width, glyph.height)); /* FIXME: Shouldn't have to do this */
rect->tex_uv0 = VEC2(atlas_rect.x * inv_font_image_width, atlas_rect.y * inv_font_image_height); atlas_p0.x += 1;
rect->tex_uv1 = VEC2((atlas_rect.x + atlas_rect.width) * inv_font_image_width, (atlas_rect.y + atlas_rect.height) * inv_font_image_height); atlas_p0.y += 1;
atlas_p1.x += 1;
atlas_p1.y += 1;
rect->p0 = AddVec2(baseline, rr.baseline_start_offset);
rect->p1 = AddVec2(rect->p0, glyph_size);
rect->tex_uv0 = MulVec2Vec2(atlas_p0, inv_font_image_size);
rect->tex_uv1 = MulVec2Vec2(atlas_p1, inv_font_image_size);
/* FIXME: Remove this (debugging) */
#if 0
rect->border_srgb = Rgba32F(1, 0, 1, 0.4);
rect->border = 1;
#endif
rect->tex = tex_rid; rect->tex = tex_rid;
offset += glyph.advance; }
} }
} }
} }

View File

@ -66,12 +66,15 @@ Struct(UI_Box)
UI_Size pref_size[Axis_CountXY]; UI_Size pref_size[Axis_CountXY];
u32 background_color; u32 background_color;
u32 border_color;
f32 border;
f32 text_padding;
Resource font_resource; Resource font_resource;
f32 font_size; f32 font_size;
//- Layout data //- Layout data
F_GlyphRun glyph_run; F_Run glyph_run;
F_Font *font; F_Font *font;
f32 solved_dims[Axis_CountXY]; f32 solved_dims[Axis_CountXY];
f32 layout_cursor; f32 layout_cursor;
@ -96,8 +99,11 @@ Struct(UI_BoxBin)
x(Width, UI_Size) \ x(Width, UI_Size) \
x(Height, UI_Size) \ x(Height, UI_Size) \
x(BackgroundColor, u32) \ x(BackgroundColor, u32) \
x(BorderColor, u32) \
x(Border, f32) \
x(Font, Resource) \ x(Font, Resource) \
x(FontSize, u32) \ x(FontSize, u32) \
x(TextPadding, f32) \
/* ----------------------------------- */ /* ----------------------------------- */
Enum(UI_StyleKind) Enum(UI_StyleKind)
@ -178,6 +184,11 @@ Struct(UI_SharedState)
void UI_Startup(void); void UI_Startup(void);
////////////////////////////////////////////////////////////
//~ Font helpers
Resource UI_GetDefaultFontResource(void);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Key helpers //~ Key helpers

View File

@ -8,6 +8,8 @@ Struct(UI_RectPS_Input)
Semantic(Vec4, sv_position); Semantic(Vec4, sv_position);
Semantic(Vec4, background_lin); Semantic(Vec4, background_lin);
Semantic(Vec4, border_lin);
Semantic(Vec2, rect_uv);
Semantic(Vec2, tex_uv); Semantic(Vec2, tex_uv);
Semantic(nointerpolation u32, instance_idx); Semantic(nointerpolation u32, instance_idx);
}; };
@ -22,25 +24,25 @@ Struct(UI_RectPS_Output)
UI_RectPS_Input VSDef(UI_RectVS, Semantic(u32, sv_instanceid), Semantic(u32, sv_vertexid)) UI_RectPS_Input VSDef(UI_RectVS, Semantic(u32, sv_instanceid), Semantic(u32, sv_vertexid))
{ {
ConstantBuffer<UI_RectSig> sig = UI_rect_sig; ConstantBuffer<UI_RectSig> sig = UI_rect_sig;
static const Vec2 uvs[4] = {
Vec2(0, 0),
Vec2(1, 0),
Vec2(1, 1),
Vec2(0, 1),
};
StructuredBuffer<UI_RectInstance> instances = UniformResourceFromRid(sig.instances); StructuredBuffer<UI_RectInstance> instances = UniformResourceFromRid(sig.instances);
UI_RectInstance instance = instances[sv_instanceid]; UI_RectInstance instance = instances[sv_instanceid];
Vec2 uv = uvs[sv_vertexid]; #if 0
Vec2 tex_uv = lerp(instance.tex_uv0, instance.tex_uv1, uv); Vec2 rect_uv = RectUvFromVertexId(sv_vertexid);
Vec2 screen_vert = lerp(instance.p0, instance.p1, uv); Vec2 tex_uv = lerp(instance.tex_uv0, instance.tex_uv1, rect_uv);
Vec2 ndc = NdcFromViewport(sig.viewport_size, screen_vert); Vec2 screen_vert = lerp(instance.p0, instance.p1, rect_uv);
#else
Vec2 rect_uv = RectUvFromVertexId(sv_vertexid);
Vec2 tex_uv = lerp(instance.tex_uv0, instance.tex_uv1, rect_uv);
Vec2 screen_vert = lerp(instance.p0, instance.p1, rect_uv);
#endif
UI_RectPS_Input result; UI_RectPS_Input result;
result.sv_position = Vec4(ndc.xy, 0, 1); result.sv_position = Vec4(NdcFromViewport(sig.viewport_size, screen_vert).xy, 0, 1);
result.background_lin = LinearFromSrgbU32(instance.background_srgb); result.background_lin = LinearFromSrgbU32(instance.background_srgb);
result.border_lin = LinearFromSrgbU32(instance.border_srgb);
result.instance_idx = sv_instanceid; result.instance_idx = sv_instanceid;
result.rect_uv = rect_uv;
result.tex_uv = tex_uv; result.tex_uv = tex_uv;
return result; return result;
@ -53,21 +55,39 @@ UI_RectPS_Output PSDef(UI_RectPS, UI_RectPS_Input input)
ConstantBuffer<UI_RectSig> sig = UI_rect_sig; ConstantBuffer<UI_RectSig> sig = UI_rect_sig;
StructuredBuffer<UI_RectInstance> instances = UniformResourceFromRid(sig.instances); StructuredBuffer<UI_RectInstance> instances = UniformResourceFromRid(sig.instances);
UI_RectInstance instance = instances[input.instance_idx]; UI_RectInstance instance = instances[input.instance_idx];
Vec4 color;
Vec4 result = 0;
Vec2 p = input.sv_position.xy;
Vec2 p0 = instance.p0;
Vec2 p1 = instance.p1;
Vec2 rect_uv = input.rect_uv;
/* Background */ /* Background */
if (instance.flags & UI_RectFlag_DrawTexture) if (instance.flags & UI_RectFlag_DrawTexture)
{ {
SamplerState sampler = UniformSamplerFromRid(sig.sampler); SamplerState sampler = UniformSamplerFromRid(sig.sampler);
Texture2D<Vec4> tex = NonUniformResourceFromRid(instance.tex); Texture2D<Vec4> tex = NonUniformResourceFromRid(instance.tex);
color = tex.Sample(sampler, input.tex_uv); result = tex.Sample(sampler, input.tex_uv);
} }
else else
{ {
color = input.background_lin; result = input.background_lin;
} }
UI_RectPS_Output result; /* Border */
result.sv_target0 = color; Vec2 to_border = Vec2(0, 0);
return result; to_border.x = min(p.x - p0.x, p1.x - p.x);
to_border.y = min(p.y - p0.y, p1.y - p.y);
f32 dist_to_border = min(to_border.x, to_border.y);
if (dist_to_border < instance.border)
{
result = input.border_lin;
}
f32 border_thickness = instance.border;
UI_RectPS_Output output;
output.sv_target0 = result;
return output;
} }

View File

@ -23,6 +23,8 @@ Struct(UI_RectInstance)
Vec2 p0; Vec2 p0;
Vec2 p1; Vec2 p1;
u32 background_srgb; u32 background_srgb;
u32 border_srgb;
f32 border;
Vec2 tex_uv0; Vec2 tex_uv0;
Vec2 tex_uv1; Vec2 tex_uv1;
Texture2DRid tex; Texture2DRid tex;