diff --git a/src/draw.c b/src/draw.c index 212b1b6f..d23663c9 100644 --- a/src/draw.c +++ b/src/draw.c @@ -334,49 +334,186 @@ void draw_grid(struct gpu_cmd_buffer *cmdbuff, struct rect rect, u32 bg0_srgb, u * Text * ========================== */ -void draw_text_ex(struct gpu_cmd_buffer *cmdbuff, struct font *font, struct v2 pos, f32 scale, struct string str) + /* Returns the rect that the drawn text spans */ +struct rect draw_text_ex(struct gpu_cmd_buffer *cmdbuff, struct font *font, struct v2 pos, f32 scale, enum draw_text_alignment alignment, enum draw_text_offset_x offset_x, enum draw_text_offset_x offset_y, struct string str) { - struct v2 draw_pos = pos; - draw_pos.y += font->point_size * scale; + struct temp_arena scratch = scratch_begin_no_conflict(); - struct string_codepoint_iter iter = string_codepoint_iter_begin(str); - while (string_codepoint_iter_next(&iter)) { - u32 codepoint = iter.codepoint; + f32 inv_font_image_width = 1.0 / (f32)font->image_width; + f32 inv_font_image_height = 1.0 / (f32)font->image_height; + f32 line_spacing = font->point_size * 1.5f * scale; - /* TODO: Remove this (placeholder \n) */ - if (codepoint == '\n') { - draw_pos.x = pos.x; - draw_pos.y += (font->point_size * 1.5f) * scale; - continue; + struct drawable_glyph { + f32 off_x; + f32 off_y; + f32 width; + f32 height; + f32 advance; + struct clip_rect clip; + }; + + struct drawable_line { + f32 line_width; + u32 num_glyphs; + struct drawable_glyph *glyphs; + struct drawable_line *next; + }; + + u64 num_lines = 0; + f32 widest_line = 0; + + struct drawable_line *first_line = NULL; + struct drawable_line *last_line = NULL; + f32 first_line_top_offset = 0; + f32 last_line_bottom_offset = 0; + + if (str.len > 0) { + b32 string_done = false; + struct string_codepoint_iter iter = string_codepoint_iter_begin(str); + while (!string_done) { + f32 line_width = 0; + f32 top_offset = 0; + f32 bottom_offset = 0; + u64 num_line_glyphs = 0; + struct drawable_glyph *line_glyphs = arena_dry_push(scratch.arena, struct drawable_glyph); + + b32 submit_line = false; + b32 line_done = false; + while (!line_done) { + string_done = !string_codepoint_iter_next(&iter); + if (string_done) { + line_done = true; + } else { + submit_line = true; + u32 codepoint = iter.codepoint; + if (codepoint == '\n') { + line_done = true; + } else { + struct drawable_glyph *tg = arena_push(scratch.arena, struct drawable_glyph); + ++num_line_glyphs; + struct font_glyph *glyph = font_get_glyph(font, codepoint); + tg->off_x = glyph->off_x * scale; + tg->off_y = glyph->off_y * scale; + tg->width = glyph->width * scale; + tg->height = glyph->height * scale; + tg->advance = glyph->advance * scale; + struct rect glyph_atlas_rect = glyph->atlas_rect; + tg->clip = (struct clip_rect) { + { + glyph_atlas_rect.x * inv_font_image_width, + glyph_atlas_rect.y * inv_font_image_height + }, + { + (glyph_atlas_rect.x + glyph_atlas_rect.width) * inv_font_image_width, + (glyph_atlas_rect.y + glyph_atlas_rect.height) * inv_font_image_height + } + }; + line_width += tg->advance; + top_offset = min_f32(top_offset, tg->off_y); + bottom_offset = max_f32(bottom_offset, tg->off_y + tg->height); + } + } + } + + /* Line ended */ + if (submit_line) { + /* TODO: Only create nodes for non-empty lines. Embed line number in the node. */ + struct drawable_line *node = arena_push(scratch.arena, struct drawable_line); + node->line_width = line_width; + node->num_glyphs = num_line_glyphs; + node->glyphs = line_glyphs; + if (last_line) { + last_line->next = node; + } else { + first_line = node; + first_line_top_offset = top_offset; + } + last_line = node; + last_line_bottom_offset = bottom_offset; + widest_line = max_f32(widest_line, line_width); + ++num_lines; + } + } + string_codepoint_iter_end(&iter); + } + + struct rect bounds = ZI; + bounds.x = pos.x; + bounds.y = pos.y; + bounds.width = widest_line; + bounds.height = num_lines * line_spacing + first_line_top_offset + last_line_bottom_offset; + + /* Offset bounds X */ + switch (offset_x) { + case DRAW_TEXT_OFFSET_X_LEFT: break; + case DRAW_TEXT_OFFSET_X_CENTER: + { + bounds.x -= bounds.width / 2.f; + } break; + case DRAW_TEXT_OFFSET_X_RIGHT: + { + bounds.x -= bounds.width; + } break; + } + + /* Offset bounds Y */ + switch (offset_y) { + case DRAW_TEXT_OFFSET_Y_TOP: + { + if (first_line) { + bounds.y -= first_line_top_offset; + } + } break; + case DRAW_TEXT_OFFSET_Y_CENTER: + { + bounds.y -= bounds.height / 2.f; + } break; + case DRAW_TEXT_OFFSET_Y_BOTTOM: + { + if (last_line) { + bounds.y -= bounds.height - first_line_top_offset - last_line_bottom_offset; + } + } break; + } + + + u64 line_number = 0; + for (struct drawable_line *line = first_line; line; line = line->next) { + struct v2 draw_pos = bounds.pos; + draw_pos.y += line_number * line_spacing; + + /* Alignment */ + switch (alignment) { + case DRAW_TEXT_ALIGNMENT_LEFT: break; + case DRAW_TEXT_ALIGNMENT_CENTER: + { + draw_pos.x += (bounds.width - line->line_width) / 2.f; + } break; + case DRAW_TEXT_ALIGNMENT_RIGHT: + { + draw_pos.x += bounds.width - line->line_width; + } break; } - struct font_glyph *glyph = font_get_glyph(font, codepoint); - f32 x = draw_pos.x + glyph->off_x * scale; - f32 y = draw_pos.y + glyph->off_y * scale; - f32 width = glyph->width * scale; - f32 height = glyph->height * scale; + /* Draw glyphs */ + for (u64 i = 0; i < line->num_glyphs; ++i) { + struct drawable_glyph *tg = &line->glyphs[i]; + f32 x = draw_pos.x + tg->off_x; + f32 y = draw_pos.y + tg->off_y; + struct quad quad = quad_from_rect(RECT(x, y, tg->width, tg->height)); + draw_quad_texture_ex(cmdbuff, font->texture, sprite_tag_nil(), tg->clip, COLOR_WHITE, COLOR_WHITE, quad); + draw_pos.x += tg->advance; - struct clip_rect clip = { - { - glyph->atlas_rect.x / (f32)font->image_width, - glyph->atlas_rect.y / (f32)font->image_height - }, - - { - (glyph->atlas_rect.x + glyph->atlas_rect.width) / (f32)font->image_width, - (glyph->atlas_rect.y + glyph->atlas_rect.height) / (f32)font->image_height - } - }; - - struct quad quad = quad_from_rect(RECT(x, y, width, height)); - draw_quad_texture_ex(cmdbuff, font->texture, sprite_tag_nil(), clip, COLOR_WHITE, COLOR_WHITE, quad); - - draw_pos.x += glyph->advance * scale; + } + ++line_number; } - string_codepoint_iter_end(&iter); + + scratch_end(scratch); + return bounds; } -void draw_text(struct gpu_cmd_buffer *cmdbuff, struct font *font, struct v2 pos, struct string str) +/* Returns the rect that the drawn text spans */ +struct rect draw_text(struct gpu_cmd_buffer *cmdbuff, struct font *font, struct v2 pos, struct string str) { - draw_text_ex(cmdbuff, font, pos, 1.0, str); + return draw_text_ex(cmdbuff, font, pos, 1.0, DRAW_TEXT_ALIGNMENT_LEFT, DRAW_TEXT_OFFSET_X_LEFT, DRAW_TEXT_OFFSET_Y_TOP, str); } diff --git a/src/draw.h b/src/draw.h index 9b234219..021e0844 100644 --- a/src/draw.h +++ b/src/draw.h @@ -21,6 +21,27 @@ struct draw_texture_params { u32 tint; }; +/* How is text aligned within its area */ +enum draw_text_alignment { + DRAW_TEXT_ALIGNMENT_LEFT, /* Default */ + DRAW_TEXT_ALIGNMENT_CENTER, + DRAW_TEXT_ALIGNMENT_RIGHT +}; + +/* How does the specified text position relate to the text area. + * E.g. LEFT & TOP means the top left of the text area will snap to + * the specified position. */ +enum draw_text_offset_x { + DRAW_TEXT_OFFSET_X_LEFT, /* Default */ + DRAW_TEXT_OFFSET_X_CENTER, + DRAW_TEXT_OFFSET_X_RIGHT +}; +enum draw_text_offset_y { + DRAW_TEXT_OFFSET_Y_TOP, /* Default */ + DRAW_TEXT_OFFSET_Y_CENTER, + DRAW_TEXT_OFFSET_Y_BOTTOM +}; + struct draw_startup_receipt { i32 _; }; struct draw_startup_receipt draw_startup(struct gpu_startup_receipt *gpu_sr, struct font_startup_receipt *font_sr); @@ -44,7 +65,7 @@ void draw_collider_line(struct gpu_cmd_buffer *cmdbuff, struct xform draw_xf, st void draw_grid(struct gpu_cmd_buffer *cmdbuff, struct rect rect, u32 bg0_srgb, u32 bg1_srgb, u32 line_srgb, u32 x_srgb, u32 y_srgb, f32 thickness, f32 spacing, struct v2 offset); -void draw_text(struct gpu_cmd_buffer *cmdbuff, struct font *font, struct v2 pos, struct string str); -void draw_text_ex(struct gpu_cmd_buffer *cmdbuff, struct font *font, struct v2 pos, f32 scale, struct string str); +struct rect draw_text(struct gpu_cmd_buffer *cmdbuff, struct font *font, struct v2 pos, struct string str); +struct rect draw_text_ex(struct gpu_cmd_buffer *cmdbuff, struct font *font, struct v2 pos, f32 scale, enum draw_text_alignment alignment, enum draw_text_offset_x offset_x, enum draw_text_offset_x offset_y, struct string str); #endif diff --git a/src/string.c b/src/string.c index 136f5819..1554085b 100644 --- a/src/string.c +++ b/src/string.c @@ -559,6 +559,7 @@ struct string_codepoint_iter string_codepoint_iter_begin(struct string str) }; } +/* Returns false if done iterating */ b32 string_codepoint_iter_next(struct string_codepoint_iter *iter) { if (iter->pos < iter->src.len) { diff --git a/src/user.c b/src/user.c index 826e4977..9fc170f0 100644 --- a/src/user.c +++ b/src/user.c @@ -698,7 +698,7 @@ INTERNAL void user_update(void) * Update user state from binds * ========================== */ - /* Test fullscreen */ + /* Test fullscreen */ { struct bind_state state = G.bind_states[USER_BIND_KIND_FULLSCREEN]; if (state.num_presses) { @@ -906,7 +906,7 @@ INTERNAL void user_update(void) * Alloc / release tile cache entries * ========================== */ - /* Alloc entries from new sim chunks */ + /* Alloc entries from new sim chunks */ for (u64 ent_index = 0; ent_index < G.ss_blended->num_ents_reserved; ++ent_index) { struct sim_ent *chunk_ent = &G.ss_blended->ents[ent_index]; @@ -971,22 +971,22 @@ INTERNAL void user_update(void) * [L ] X [R ] * [BL] [B] [BR] */ - struct v2i32 chunk_pos_tl = V2I32(chunk_pos.x - 1, chunk_pos.y - 1); - struct v2i32 chunk_pos_t = V2I32(chunk_pos.x, chunk_pos.y - 1); - struct v2i32 chunk_pos_tr = V2I32(chunk_pos.x + 1, chunk_pos.y - 1); - struct v2i32 chunk_pos_l = V2I32(chunk_pos.x - 1, chunk_pos.y); - struct v2i32 chunk_pos_r = V2I32(chunk_pos.x + 1, chunk_pos.y); - struct v2i32 chunk_pos_bl = V2I32(chunk_pos.x - 1, chunk_pos.y + 1); - struct v2i32 chunk_pos_b = V2I32(chunk_pos.x, chunk_pos.y + 1); - struct v2i32 chunk_pos_br = V2I32(chunk_pos.x + 1, chunk_pos.y + 1); - struct sim_ent *chunk_ent_tl = sim_ent_from_chunk_pos(chunk_pos_tl); - struct sim_ent *chunk_ent_t = sim_ent_from_chunk_pos(chunk_pos_t); - struct sim_ent *chunk_ent_tr = sim_ent_from_chunk_pos(chunk_pos_tr); - struct sim_ent *chunk_ent_l = sim_ent_from_chunk_pos(chunk_pos_l); - struct sim_ent *chunk_ent_r = sim_ent_from_chunk_pos(chunk_pos_r); - struct sim_ent *chunk_ent_bl = sim_ent_from_chunk_pos(chunk_pos_bl); - struct sim_ent *chunk_ent_b = sim_ent_from_chunk_pos(chunk_pos_b); - struct sim_ent *chunk_ent_br = sim_ent_from_chunk_pos(chunk_pos_br); + struct v2i32 chunk_pos_tl = V2I32(chunk_pos.x - 1, chunk_pos.y - 1); + struct v2i32 chunk_pos_t = V2I32(chunk_pos.x, chunk_pos.y - 1); + struct v2i32 chunk_pos_tr = V2I32(chunk_pos.x + 1, chunk_pos.y - 1); + struct v2i32 chunk_pos_l = V2I32(chunk_pos.x - 1, chunk_pos.y); + struct v2i32 chunk_pos_r = V2I32(chunk_pos.x + 1, chunk_pos.y); + struct v2i32 chunk_pos_bl = V2I32(chunk_pos.x - 1, chunk_pos.y + 1); + struct v2i32 chunk_pos_b = V2I32(chunk_pos.x, chunk_pos.y + 1); + struct v2i32 chunk_pos_br = V2I32(chunk_pos.x + 1, chunk_pos.y + 1); + struct sim_ent *chunk_ent_tl = sim_ent_from_chunk_pos(chunk_pos_tl); + struct sim_ent *chunk_ent_t = sim_ent_from_chunk_pos(chunk_pos_t); + struct sim_ent *chunk_ent_tr = sim_ent_from_chunk_pos(chunk_pos_tr); + struct sim_ent *chunk_ent_l = sim_ent_from_chunk_pos(chunk_pos_l); + struct sim_ent *chunk_ent_r = sim_ent_from_chunk_pos(chunk_pos_r); + struct sim_ent *chunk_ent_bl = sim_ent_from_chunk_pos(chunk_pos_bl); + struct sim_ent *chunk_ent_b = sim_ent_from_chunk_pos(chunk_pos_b); + struct sim_ent *chunk_ent_br = sim_ent_from_chunk_pos(chunk_pos_br); struct string data = sim_ent_get_chunk_tile_data(chunk_ent); @@ -1767,7 +1767,14 @@ INTERNAL void user_update(void) struct string dbg_text = ZI; dbg_text.text = arena_dry_push(temp.arena, u8); dbg_text.len += get_ent_debug_text(temp.arena, ent).len; +#if 0 draw_text(G.ui_cmd_buffer, font, pos, dbg_text); +#else + enum draw_text_alignment alignment = DRAW_TEXT_ALIGNMENT_LEFT; + enum draw_text_offset_y offset_y = DRAW_TEXT_OFFSET_Y_TOP; + enum draw_text_offset_x offset_x = DRAW_TEXT_OFFSET_X_LEFT; + draw_text_ex(G.ui_cmd_buffer, font, pos, 1, alignment, offset_x, offset_y, dbg_text); +#endif arena_temp_end(temp); } @@ -1778,146 +1785,109 @@ INTERNAL void user_update(void) * ========================== */ if (G.debug_draw) { - f32 spacing = -20; - struct v2 pos = V2(10, G.ui_size.y - 20); + //struct v2 pos = V2(0, 500); + //struct v2 pos = V2(10, G.ui_size.y - 20); + struct v2 pos = V2(0, G.ui_size.y); struct font *font = font_load_async(LIT("fonts/fixedsys.ttf"), 12.0f); if (font) { struct temp_arena temp = arena_temp_begin(scratch.arena); + struct string text = ZI; + text.text = arena_dry_push(temp.arena, u8); #if BITBUFF_DEBUG - draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("(bitbuff debug enabled)"))); - pos.y += spacing; + text.len += string_format(temp.arena, LIT("(bitbuff debug enabled)")).len; + text.len += string_copy(temp.arena, LIT("\n")).len; #endif - draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("blended world entities: %F/%F"), FMT_UINT(G.ss_blended->num_ents_allocated), FMT_UINT(G.ss_blended->num_ents_reserved))); - pos.y += spacing; + text.len += string_format(temp.arena, LIT("blended world entities: %F/%F"), FMT_UINT(G.ss_blended->num_ents_allocated), FMT_UINT(G.ss_blended->num_ents_reserved)).len; + text.len += string_copy(temp.arena, LIT("\n")).len; - draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("blended world tick: %F"), FMT_UINT(G.ss_blended->tick))); - pos.y += spacing; + text.len += string_format(temp.arena, LIT("blended world tick: %F"), FMT_UINT(G.ss_blended->tick)).len; + text.len += string_copy(temp.arena, LIT("\n")).len; - draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("blended world time: %F"), FMT_FLOAT(SECONDS_FROM_NS(G.ss_blended->sim_time_ns)))); - pos.y += spacing; - pos.y += spacing; + text.len += string_format(temp.arena, LIT("blended world time: %F"), FMT_FLOAT(SECONDS_FROM_NS(G.ss_blended->sim_time_ns))).len; + text.len += string_copy(temp.arena, LIT("\n")).len; + text.len += string_copy(temp.arena, LIT("\n")).len; - draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("average local sim publish dt: %F"), FMT_FLOAT(SECONDS_FROM_NS(G.average_local_to_user_snapshot_publish_dt_ns)))); - pos.y += spacing; + text.len += string_format(temp.arena, LIT("average local sim publish dt: %F"), FMT_FLOAT(SECONDS_FROM_NS(G.average_local_to_user_snapshot_publish_dt_ns))).len; + text.len += string_copy(temp.arena, LIT("\n")).len; - draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("local sim last known tick: %F"), FMT_UINT(G.local_sim_last_known_tick))); - pos.y += spacing; + text.len += string_format(temp.arena, LIT("local sim last known tick: %F"), FMT_UINT(G.local_sim_last_known_tick)).len; + text.len += string_copy(temp.arena, LIT("\n")).len; - draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("local sim last known time: %F"), FMT_FLOAT(SECONDS_FROM_NS(G.local_sim_last_known_time_ns)))); - pos.y += spacing; + text.len += string_format(temp.arena, LIT("local sim last known time: %F"), FMT_FLOAT(SECONDS_FROM_NS(G.local_sim_last_known_time_ns))).len; + text.len += string_copy(temp.arena, LIT("\n")).len; - draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("local sim predicted time: %F"), FMT_FLOAT(SECONDS_FROM_NS(G.local_sim_predicted_time_ns)))); - pos.y += spacing; + text.len += string_format(temp.arena, LIT("local sim predicted time: %F"), FMT_FLOAT(SECONDS_FROM_NS(G.local_sim_predicted_time_ns))).len; + text.len += string_copy(temp.arena, LIT("\n")).len; - draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("render time target: %F"), FMT_FLOAT(SECONDS_FROM_NS(G.render_time_target_ns)))); - pos.y += spacing; + text.len += string_format(temp.arena, LIT("render time target: %F"), FMT_FLOAT(SECONDS_FROM_NS(G.render_time_target_ns))).len; + text.len += string_copy(temp.arena, LIT("\n")).len; - draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("render time: %F"), FMT_FLOAT(SECONDS_FROM_NS(G.render_time_ns)))); - pos.y += spacing; - pos.y += spacing; + text.len += string_format(temp.arena, LIT("render time: %F"), FMT_FLOAT(SECONDS_FROM_NS(G.render_time_ns))).len; + text.len += string_copy(temp.arena, LIT("\n")).len; + text.len += string_copy(temp.arena, LIT("\n")).len; - draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("local player: [%F]"), FMT_UID(local_player->id.uid))); - pos.y += spacing; - pos.y += spacing; + text.len += string_format(temp.arena, LIT("local player: [%F]"), FMT_UID(local_player->id.uid)).len; + text.len += string_copy(temp.arena, LIT("\n")).len; + text.len += string_copy(temp.arena, LIT("\n")).len; struct v2 world_cursor = G.world_cursor; - draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("cursor world: %F, %F"), FMT_FLOAT(world_cursor.x), FMT_FLOAT(world_cursor.y))); - pos.y += spacing; + text.len += string_format(temp.arena, LIT("cursor world: %F, %F"), FMT_FLOAT(world_cursor.x), FMT_FLOAT(world_cursor.y)).len; + text.len += string_copy(temp.arena, LIT("\n")).len; struct v2i32 world_tile_cursor = sim_world_tile_index_from_pos(world_cursor); - draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("cursor world tile: %F, %F"), FMT_SINT(world_tile_cursor.x), FMT_SINT(world_tile_cursor.y))); - pos.y += spacing; + text.len += string_format(temp.arena, LIT("cursor world tile: %F, %F"), FMT_SINT(world_tile_cursor.x), FMT_SINT(world_tile_cursor.y)).len; + text.len += string_copy(temp.arena, LIT("\n")).len; struct v2i32 local_tile_cursor = sim_local_tile_index_from_world_tile_index(world_tile_cursor); - draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("cursor local tile: %F, %F"), FMT_SINT(local_tile_cursor.x), FMT_SINT(local_tile_cursor.y))); - pos.y += spacing; + text.len += string_format(temp.arena, LIT("cursor local tile: %F, %F"), FMT_SINT(local_tile_cursor.x), FMT_SINT(local_tile_cursor.y)).len; + text.len += string_copy(temp.arena, LIT("\n")).len; struct v2i32 tile_chunk_cursor = sim_tile_chunk_index_from_world_tile_index(world_tile_cursor); - draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("cursor tile chunk: %F, %F"), FMT_SINT(tile_chunk_cursor.x), FMT_SINT(tile_chunk_cursor.y))); - pos.y += spacing; - pos.y += spacing; + text.len += string_format(temp.arena, LIT("cursor tile chunk: %F, %F"), FMT_SINT(tile_chunk_cursor.x), FMT_SINT(tile_chunk_cursor.y)).len; + text.len += string_copy(temp.arena, LIT("\n")).len; + text.len += string_copy(temp.arena, LIT("\n")).len; - draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("Network read: %F mbit/s"), FMT_FLOAT((f64)G.net_bytes_read.last_second * 8 / 1000 / 1000))); - pos.y += spacing; + text.len += string_format(temp.arena, LIT("Network read: %F mbit/s"), FMT_FLOAT((f64)G.net_bytes_read.last_second * 8 / 1000 / 1000)).len; + text.len += string_copy(temp.arena, LIT("\n")).len; - draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("Network write: %F mbit/s"), FMT_FLOAT((f64)G.net_bytes_sent.last_second * 8 / 1000 / 1000))); - pos.y += spacing; + text.len += string_format(temp.arena, LIT("Network write: %F mbit/s"), FMT_FLOAT((f64)G.net_bytes_sent.last_second * 8 / 1000 / 1000)).len; + text.len += string_copy(temp.arena, LIT("\n")).len; - draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("Ping (real): %F ms"), FMT_FLOAT(SECONDS_FROM_NS(local_player->player_last_rtt_ns) * 1000))); - pos.y += spacing; + text.len += string_format(temp.arena, LIT("Ping (real): %F ms"), FMT_FLOAT(SECONDS_FROM_NS(local_player->player_last_rtt_ns) * 1000)).len; + text.len += string_copy(temp.arena, LIT("\n")).len; - draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("Ping (average): %F ms"), FMT_FLOAT(local_player->player_average_rtt_seconds * 1000))); - pos.y += spacing; - pos.y += spacing; + text.len += string_format(temp.arena, LIT("Ping (average): %F ms"), FMT_FLOAT(local_player->player_average_rtt_seconds * 1000)).len; + text.len += string_copy(temp.arena, LIT("\n")).len; + text.len += string_copy(temp.arena, LIT("\n")).len; - draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("Memory committed: %F MiB"), FMT_FLOAT((f64)gstat_get(GSTAT_MEMORY_COMMITTED) / 1024 / 1024))); - pos.y += spacing; + text.len += string_format(temp.arena, LIT("Memory committed: %F MiB"), FMT_FLOAT((f64)gstat_get(GSTAT_MEMORY_COMMITTED) / 1024 / 1024)).len; + text.len += string_copy(temp.arena, LIT("\n")).len; - draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("Virtual memory reserved: %F TiB"), FMT_FLOAT((f64)gstat_get(GSTAT_MEMORY_RESERVED) / 1024 / 1024 / 1024 / 1024))); - pos.y += spacing; + text.len += string_format(temp.arena, LIT("Virtual memory reserved: %F TiB"), FMT_FLOAT((f64)gstat_get(GSTAT_MEMORY_RESERVED) / 1024 / 1024 / 1024 / 1024)).len; + text.len += string_copy(temp.arena, LIT("\n")).len; - draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("Arenas allocated: %F"), FMT_UINT(gstat_get(GSTAT_NUM_ARENAS)))); - pos.y += spacing; - pos.y += spacing; + text.len += string_format(temp.arena, LIT("Arenas allocated: %F"), FMT_UINT(gstat_get(GSTAT_NUM_ARENAS))).len; + text.len += string_copy(temp.arena, LIT("\n")).len; + text.len += string_copy(temp.arena, LIT("\n")).len; - draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("Video memory usage: %F MiB"), FMT_FLOAT((f64)gstat_get(GSTAT_VRAM_USAGE) / 1024 / 1024))); - pos.y += spacing; - pos.y += spacing; - -#if 0 - draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("screen_size: (%F, %F)"), FMT_FLOAT((f64)G.screen_size.x), FMT_FLOAT((f64)G.screen_size.y))); - pos.y += spacing; - - draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("screen_cursor: (%F, %F)"), FMT_FLOAT((f64)G.screen_cursor.x), FMT_FLOAT((f64)G.screen_cursor.y))); - pos.y += spacing; - - draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("ui_screen_offset: (%F, %F)"), FMT_FLOAT((f64)G.ui_screen_offset.x), FMT_FLOAT((f64)G.ui_screen_offset.y))); - pos.y += spacing; - - draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("ui_size: (%F, %F)"), FMT_FLOAT((f64)G.ui_size.x), FMT_FLOAT((f64)G.ui_size.y))); - pos.y += spacing; - - draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("ui_center: (%F, %F)"), FMT_FLOAT((f64)G.ui_center.x), FMT_FLOAT((f64)G.ui_center.y))); - pos.y += spacing; - - draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("ui_cursor: (%F, %F)"), FMT_FLOAT((f64)G.ui_cursor.x), FMT_FLOAT((f64)G.ui_cursor.y))); - pos.y += spacing; - - draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("world_to_ui_xf.og: (%F, %F)"), FMT_FLOAT((f64)G.world_to_ui_xf.og.x), FMT_FLOAT((f64)G.world_to_ui_xf.og.y))); - pos.y += spacing; - - draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("world_to_ui_xf rotation: %F"), FMT_FLOAT((f64)xform_get_rotation(G.world_to_ui_xf)))); - pos.y += spacing; - - draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("world_to_ui_xf scale: (%F, %F)"), FMT_FLOAT((f64)xform_get_scale(G.world_to_ui_xf).x), FMT_FLOAT((f64)xform_get_scale(G.world_to_ui_xf).x))); - pos.y += spacing; - - draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("world_cursor: (%F, %F)"), FMT_FLOAT((f64)G.world_cursor.x), FMT_FLOAT((f64)G.world_cursor.y))); - pos.y += spacing; - - draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("debug_camera: %F"), FMT_STR(G.debug_camera ? LIT("true") : LIT("false")))); - pos.y += spacing; - - struct v2 player_linear_vel = sim_ent_find_first_match_one(store, SEPROP_PLAYER_CONTROLLED)->linear_velocity; - draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("player linear velocity: (%F, %F)"), FMT_FLOAT_P((f64)player_linear_vel.x, 12), FMT_FLOAT_P((f64)player_linear_vel.y, 12))); - pos.y += spacing; - - f32 player_angular_vel = sim_ent_find_first_match_one(store, SEPROP_PLAYER_CONTROLLED)->angular_velocity; - draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("player angular velocity: %F"), FMT_FLOAT_P((f64)player_angular_vel, 12))); - pos.y += spacing; - - struct v2 player_pos = sim_ent_get_xform(sim_ent_find_first_match_one(store, SEPROP_PLAYER_CONTROLLED)).og; - draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("player pos: (%F, %F)"), FMT_FLOAT_P((f64)player_pos.x, 12), FMT_FLOAT_P((f64)player_pos.y, 12))); - pos.y += spacing; -#endif + text.len += string_format(temp.arena, LIT("Video memory usage: %F MiB"), FMT_FLOAT((f64)gstat_get(GSTAT_VRAM_USAGE) / 1024 / 1024)).len; + //text.len += string_copy(temp.arena, LIT("\n")).len; + //text.len += string_copy(temp.arena, LIT("\n")).len; #if COLLIDER_DEBUG draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("collider gjk steps: %F"), FMT_UINT(collider_debug_steps))); pos.y += spacing; #endif + //draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("blended world entities: %F/%F"), FMT_UINT(G.ss_blended->num_ents_allocated), FMT_UINT(G.ss_blended->num_ents_reserved))); + //draw_text(G.ui_cmd_buffer, font, pos, text); + + enum draw_text_alignment alignment = DRAW_TEXT_ALIGNMENT_LEFT; + enum draw_text_offset_x offset_x = DRAW_TEXT_OFFSET_X_LEFT; + enum draw_text_offset_y offset_y = DRAW_TEXT_OFFSET_Y_BOTTOM; + draw_text_ex(G.ui_cmd_buffer, font, pos, 1, alignment, offset_x, offset_y, text); arena_temp_end(temp); }