diff --git a/CMakeLists.txt b/CMakeLists.txt index 227e3dd..174acd7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.25) include(FetchContent) -set(BLT_GRAPHICS_VERSION 1.0.11) +set(BLT_GRAPHICS_VERSION 1.0.12) set(BLT_GRAPHICS_TEST_VERSION 0.0.1) project(BLT_WITH_GRAPHICS VERSION ${BLT_GRAPHICS_VERSION}) diff --git a/include/blt/gfx/imgui/ImGuiUtils.h b/include/blt/gfx/imgui/ImGuiUtils.h index 565d502..e368246 100644 --- a/include/blt/gfx/imgui/ImGuiUtils.h +++ b/include/blt/gfx/imgui/ImGuiUtils.h @@ -8,83 +8,87 @@ namespace ImGui { + namespace Spectrum + { + extern const unsigned int SourceSansProRegular_compressed_size; + extern const unsigned int SourceSansProRegular_compressed_data[]; // defined later in the file + } - inline void SetupImGuiStyle( bool bStyleDark_, float alpha_ ) + inline void SetupImGuiStyle(bool bStyleDark_, float alpha_) { ImGuiStyle& style = ImGui::GetStyle(); // light style from PacĂ´me Danhiez (user itamago) https://github.com/ocornut/imgui/pull/511#issuecomment-175719267 style.Alpha = 1.0f; style.FrameRounding = 3.0f; - style.Colors[ImGuiCol_Text] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); - style.Colors[ImGuiCol_TextDisabled] = ImVec4(0.60f, 0.60f, 0.60f, 1.00f); - style.Colors[ImGuiCol_WindowBg] = ImVec4(0.94f, 0.94f, 0.94f, 0.94f); + style.Colors[ImGuiCol_Text] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); + style.Colors[ImGuiCol_TextDisabled] = ImVec4(0.60f, 0.60f, 0.60f, 1.00f); + style.Colors[ImGuiCol_WindowBg] = ImVec4(0.94f, 0.94f, 0.94f, 0.94f); //style.Colors[ImGuiCol_ChildWindowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); - style.Colors[ImGuiCol_PopupBg] = ImVec4(1.00f, 1.00f, 1.00f, 0.94f); - style.Colors[ImGuiCol_Border] = ImVec4(0.00f, 0.00f, 0.00f, 0.39f); - style.Colors[ImGuiCol_BorderShadow] = ImVec4(1.00f, 1.00f, 1.00f, 0.10f); - style.Colors[ImGuiCol_FrameBg] = ImVec4(1.00f, 1.00f, 1.00f, 0.94f); - style.Colors[ImGuiCol_FrameBgHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.40f); - style.Colors[ImGuiCol_FrameBgActive] = ImVec4(0.26f, 0.59f, 0.98f, 0.67f); - style.Colors[ImGuiCol_TitleBg] = ImVec4(0.96f, 0.96f, 0.96f, 1.00f); - style.Colors[ImGuiCol_TitleBgCollapsed] = ImVec4(1.00f, 1.00f, 1.00f, 0.51f); - style.Colors[ImGuiCol_TitleBgActive] = ImVec4(0.82f, 0.82f, 0.82f, 1.00f); - style.Colors[ImGuiCol_MenuBarBg] = ImVec4(0.86f, 0.86f, 0.86f, 1.00f); - style.Colors[ImGuiCol_ScrollbarBg] = ImVec4(0.98f, 0.98f, 0.98f, 0.53f); - style.Colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.69f, 0.69f, 0.69f, 1.00f); - style.Colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.59f, 0.59f, 0.59f, 1.00f); - style.Colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.49f, 0.49f, 0.49f, 1.00f); + style.Colors[ImGuiCol_PopupBg] = ImVec4(1.00f, 1.00f, 1.00f, 0.94f); + style.Colors[ImGuiCol_Border] = ImVec4(0.00f, 0.00f, 0.00f, 0.39f); + style.Colors[ImGuiCol_BorderShadow] = ImVec4(1.00f, 1.00f, 1.00f, 0.10f); + style.Colors[ImGuiCol_FrameBg] = ImVec4(1.00f, 1.00f, 1.00f, 0.94f); + style.Colors[ImGuiCol_FrameBgHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.40f); + style.Colors[ImGuiCol_FrameBgActive] = ImVec4(0.26f, 0.59f, 0.98f, 0.67f); + style.Colors[ImGuiCol_TitleBg] = ImVec4(0.96f, 0.96f, 0.96f, 1.00f); + style.Colors[ImGuiCol_TitleBgCollapsed] = ImVec4(1.00f, 1.00f, 1.00f, 0.51f); + style.Colors[ImGuiCol_TitleBgActive] = ImVec4(0.82f, 0.82f, 0.82f, 1.00f); + style.Colors[ImGuiCol_MenuBarBg] = ImVec4(0.86f, 0.86f, 0.86f, 1.00f); + style.Colors[ImGuiCol_ScrollbarBg] = ImVec4(0.98f, 0.98f, 0.98f, 0.53f); + style.Colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.69f, 0.69f, 0.69f, 1.00f); + style.Colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.59f, 0.59f, 0.59f, 1.00f); + style.Colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.49f, 0.49f, 0.49f, 1.00f); //style.Colors[ImGuiCol_ComboBg] = ImVec4(0.86f, 0.86f, 0.86f, 0.99f); - style.Colors[ImGuiCol_CheckMark] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f); - style.Colors[ImGuiCol_SliderGrab] = ImVec4(0.24f, 0.52f, 0.88f, 1.00f); - style.Colors[ImGuiCol_SliderGrabActive] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f); - style.Colors[ImGuiCol_Button] = ImVec4(0.26f, 0.59f, 0.98f, 0.40f); - style.Colors[ImGuiCol_ButtonHovered] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f); - style.Colors[ImGuiCol_ButtonActive] = ImVec4(0.06f, 0.53f, 0.98f, 1.00f); - style.Colors[ImGuiCol_Header] = ImVec4(0.26f, 0.59f, 0.98f, 0.31f); - style.Colors[ImGuiCol_HeaderHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.80f); - style.Colors[ImGuiCol_HeaderActive] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f); + style.Colors[ImGuiCol_CheckMark] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f); + style.Colors[ImGuiCol_SliderGrab] = ImVec4(0.24f, 0.52f, 0.88f, 1.00f); + style.Colors[ImGuiCol_SliderGrabActive] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f); + style.Colors[ImGuiCol_Button] = ImVec4(0.26f, 0.59f, 0.98f, 0.40f); + style.Colors[ImGuiCol_ButtonHovered] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f); + style.Colors[ImGuiCol_ButtonActive] = ImVec4(0.06f, 0.53f, 0.98f, 1.00f); + style.Colors[ImGuiCol_Header] = ImVec4(0.26f, 0.59f, 0.98f, 0.31f); + style.Colors[ImGuiCol_HeaderHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.80f); + style.Colors[ImGuiCol_HeaderActive] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f); //style.Colors[ImGuiCol_Column] = ImVec4(0.39f, 0.39f, 0.39f, 1.00f); //style.Colors[ImGuiCol_ColumnHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.78f); //style.Colors[ImGuiCol_ColumnActive] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f); - style.Colors[ImGuiCol_ResizeGrip] = ImVec4(1.00f, 1.00f, 1.00f, 0.50f); - style.Colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.67f); - style.Colors[ImGuiCol_ResizeGripActive] = ImVec4(0.26f, 0.59f, 0.98f, 0.95f); + style.Colors[ImGuiCol_ResizeGrip] = ImVec4(1.00f, 1.00f, 1.00f, 0.50f); + style.Colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.67f); + style.Colors[ImGuiCol_ResizeGripActive] = ImVec4(0.26f, 0.59f, 0.98f, 0.95f); //style.Colors[ImGuiCol_CloseButton] = ImVec4(0.59f, 0.59f, 0.59f, 0.50f); //style.Colors[ImGuiCol_CloseButtonHovered] = ImVec4(0.98f, 0.39f, 0.36f, 1.00f); //style.Colors[ImGuiCol_CloseButtonActive] = ImVec4(0.98f, 0.39f, 0.36f, 1.00f); - style.Colors[ImGuiCol_PlotLines] = ImVec4(0.39f, 0.39f, 0.39f, 1.00f); - style.Colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f); - style.Colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f); - style.Colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.60f, 0.00f, 1.00f); - style.Colors[ImGuiCol_TextSelectedBg] = ImVec4(0.26f, 0.59f, 0.98f, 0.35f); + style.Colors[ImGuiCol_PlotLines] = ImVec4(0.39f, 0.39f, 0.39f, 1.00f); + style.Colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f); + style.Colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f); + style.Colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.60f, 0.00f, 1.00f); + style.Colors[ImGuiCol_TextSelectedBg] = ImVec4(0.26f, 0.59f, 0.98f, 0.35f); //style.Colors[ImGuiCol_ModalWindowDarkening] = ImVec4(0.20f, 0.20f, 0.20f, 0.35f); - - if( bStyleDark_ ) + + if (bStyleDark_) { for (int i = 0; i < ImGuiCol_COUNT; i++) { ImVec4& col = style.Colors[i]; float H, S, V; - ImGui::ColorConvertRGBtoHSV( col.x, col.y, col.z, H, S, V ); - - if( S < 0.1f ) + ImGui::ColorConvertRGBtoHSV(col.x, col.y, col.z, H, S, V); + + if (S < 0.1f) { - V = 1.0f - V; + V = 1.0f - V; } - ImGui::ColorConvertHSVtoRGB( H, S, V, col.x, col.y, col.z ); - if( col.w < 1.00f ) + ImGui::ColorConvertHSVtoRGB(H, S, V, col.x, col.y, col.z); + if (col.w < 1.00f) { col.w *= alpha_; } } - } - else + } else { for (int i = 0; i < ImGuiCol_COUNT; i++) { ImVec4& col = style.Colors[i]; - if( col.w < 1.00f ) + if (col.w < 1.00f) { col.x *= alpha_; col.y *= alpha_; @@ -94,208 +98,207 @@ namespace ImGui } } } - - inline bool CheckBoxFont( const char* name_, bool* pB_, const char* pOn_ = "[X]", const char* pOff_="[ ]" ) + + inline bool CheckBoxFont(const char* name_, bool* pB_, const char* pOn_ = "[X]", const char* pOff_ = "[ ]") { - if( *pB_ ) + if (*pB_) { ImGui::Text("%s", pOn_); - } - else + } else { ImGui::Text("%s", pOff_); } bool bHover = false; bHover = bHover || ImGui::IsItemHovered(); ImGui::SameLine(); - ImGui::Text( "%s", name_ ); + ImGui::Text("%s", name_); bHover = bHover || ImGui::IsItemHovered(); - if( bHover && ImGui::IsMouseClicked(0) ) + if (bHover && ImGui::IsMouseClicked(0)) { - *pB_ = ! *pB_; + *pB_ = !*pB_; return true; } return false; } - - inline bool CheckBoxTick( const char* name_, bool* pB_ ) + + inline bool CheckBoxTick(const char* name_, bool* pB_) { - return CheckBoxFont( name_, pB_, ICON_FA_CHECK_SQUARE, ICON_FA_SQUARE ); + return CheckBoxFont(name_, pB_, ICON_FA_CHECK_SQUARE, ICON_FA_SQUARE); } - - inline bool MenuItemCheckbox( const char* name_, bool* pB_ ) + + inline bool MenuItemCheckbox(const char* name_, bool* pB_) { - bool retval = ImGui::MenuItem( name_ ); + bool retval = ImGui::MenuItem(name_); ImGui::SameLine(); - if( *pB_ ) + if (*pB_) { ImGui::Text(ICON_FA_CHECK_SQUARE); - } - else + } else { ImGui::Text(ICON_FA_SQUARE); } - if( retval ) + if (retval) { - *pB_ = ! *pB_; + *pB_ = !*pB_; } return retval; } - + struct FrameTimeHistogram { // configuration params - modify these at will static const int NUM = 101; //last value is from T-1 to inf. - - float dT = 0.001f; // in seconds, default 1ms - float refresh = 1.0f/60.0f;// set this to your target refresh rate - + + float dT = 0.001f; // in seconds, default 1ms + float refresh = 1.0f / 60.0f;// set this to your target refresh rate + static const int NUM_MARKERS = 2; - float markers[NUM_MARKERS] = { 0.99f, 0.999f }; - + float markers[NUM_MARKERS] = {0.99f, 0.999f}; + // data - ImVec2 size = ImVec2( 3.0f * NUM, 40.0f ); - float lastdT = 0.0f; - float timesTotal; - float countsTotal; - float times[ NUM]; - float counts[NUM]; - float hitchTimes[ NUM]; - float hitchCounts[NUM]; - + ImVec2 size = ImVec2(3.0f * NUM, 40.0f); + float lastdT = 0.0f; + float timesTotal; + float countsTotal; + float times[NUM]; + float counts[NUM]; + float hitchTimes[NUM]; + float hitchCounts[NUM]; + FrameTimeHistogram() { Clear(); } - + void Clear() { - timesTotal = 0.0f; + timesTotal = 0.0f; countsTotal = 0.0f; - memset(times, 0, sizeof(times) ); - memset(counts, 0, sizeof(counts) ); - memset(hitchTimes, 0, sizeof(hitchTimes) ); - memset(hitchCounts, 0, sizeof(hitchCounts) ); + memset(times, 0, sizeof(times)); + memset(counts, 0, sizeof(counts)); + memset(hitchTimes, 0, sizeof(hitchTimes)); + memset(hitchCounts, 0, sizeof(hitchCounts)); } - - int GetBin( float time_ ) + + int GetBin(float time_) { - int bin = (int)std::floor( time_ / dT ); - if( bin >= NUM ) + int bin = (int) std::floor(time_ / dT); + if (bin >= NUM) { bin = NUM - 1; } return bin; } - - void Update( float deltaT_ ) + + void Update(float deltaT_) { - if( deltaT_ < 0.0f ) + if (deltaT_ < 0.0f) { assert(false); return; } - int bin = GetBin( deltaT_ ); - times[ bin] += deltaT_; - timesTotal += deltaT_; + int bin = GetBin(deltaT_); + times[bin] += deltaT_; + timesTotal += deltaT_; counts[bin] += 1.0f; countsTotal += 1.0f; - - float hitch = abs( lastdT - deltaT_ ); - int deltaBin = GetBin( hitch ); - hitchTimes[ deltaBin] += hitch; + + float hitch = abs(lastdT - deltaT_); + int deltaBin = GetBin(hitch); + hitchTimes[deltaBin] += hitch; hitchCounts[deltaBin] += 1.0f; lastdT = deltaT_; } - void PlotRefreshLines( float total_ = 0.0f, float* pValues_ = NULL) + void PlotRefreshLines(float total_ = 0.0f, float* pValues_ = NULL) { ImDrawList* draw = ImGui::GetWindowDrawList(); const ImGuiStyle& style = ImGui::GetStyle(); - ImVec2 pad = style.FramePadding; - ImVec2 min = ImGui::GetItemRectMin(); + ImVec2 pad = style.FramePadding; + ImVec2 min = ImGui::GetItemRectMin(); min.x += pad.x; - ImVec2 max = ImGui::GetItemRectMax(); + ImVec2 max = ImGui::GetItemRectMax(); max.x -= pad.x; - - float xRefresh = (max.x - min.x) * refresh / ( dT * NUM ); - - float xCurr = xRefresh + min.x; - while( xCurr < max.x ) + + float xRefresh = (max.x - min.x) * refresh / (dT * NUM); + + float xCurr = xRefresh + min.x; + while (xCurr < max.x) { - float xP = ceil( xCurr ); // use ceil to get integer coords or else lines look odd - draw->AddLine( ImVec2( xP, min.y ), ImVec2( xP, max.y ), 0x50FFFFFF ); + float xP = ceil(xCurr); // use ceil to get integer coords or else lines look odd + draw->AddLine(ImVec2(xP, min.y), ImVec2(xP, max.y), 0x50FFFFFF); xCurr += xRefresh; } - - if( pValues_ ) + + if (pValues_) { // calc markers float currTotal = 0.0f; - int mark = 0; - for( int i = 0; i < NUM && mark < NUM_MARKERS; ++i ) + int mark = 0; + for (int i = 0; i < NUM && mark < NUM_MARKERS; ++i) { currTotal += pValues_[i]; - if( total_ * markers[mark] < currTotal ) + if (total_ * markers[mark] < currTotal) { - float xP = ceil( (float)(i+1)/(float)NUM * ( max.x - min.x ) + min.x ); // use ceil to get integer coords or else lines look odd - draw->AddLine( ImVec2( xP, min.y ), ImVec2( xP, max.y ), 0xFFFF0000 ); + float xP = ceil( + (float) (i + 1) / (float) NUM * (max.x - min.x) + min.x); // use ceil to get integer coords or else lines look odd + draw->AddLine(ImVec2(xP, min.y), ImVec2(xP, max.y), 0xFFFF0000); ++mark; } } } } - - void CalcHistogramSize( int numShown_ ) + + void CalcHistogramSize(int numShown_) { ImVec2 wRegion = ImGui::GetContentRegionMax(); float heightGone = 7.0f * ImGui::GetTextLineHeightWithSpacing(); wRegion.y -= heightGone; wRegion.y /= (float) numShown_; const ImGuiStyle& style = ImGui::GetStyle(); - ImVec2 pad = style.FramePadding; + ImVec2 pad = style.FramePadding; wRegion.x -= 2.0f * pad.x; size = wRegion; } - - void Draw(const char* name_, bool* pOpen_ = NULL ) + + void Draw(const char* name_, bool* pOpen_ = NULL) { - if (ImGui::Begin( name_, pOpen_ )) + if (ImGui::Begin(name_, pOpen_)) { int numShown = 0; - if(ImGui::CollapsingHeader("Time Histogram")) + if (ImGui::CollapsingHeader("Time Histogram")) { ++numShown; - ImGui::PlotHistogram("", times, NUM, 0, NULL, FLT_MAX, FLT_MAX, size ); - PlotRefreshLines( timesTotal, times ); + ImGui::PlotHistogram("", times, NUM, 0, NULL, FLT_MAX, FLT_MAX, size); + PlotRefreshLines(timesTotal, times); } - if(ImGui::CollapsingHeader("Count Histogram")) + if (ImGui::CollapsingHeader("Count Histogram")) { ++numShown; - ImGui::PlotHistogram("", counts, NUM, 0, NULL, FLT_MAX, FLT_MAX, size ); - PlotRefreshLines( countsTotal, counts ); + ImGui::PlotHistogram("", counts, NUM, 0, NULL, FLT_MAX, FLT_MAX, size); + PlotRefreshLines(countsTotal, counts); } - if(ImGui::CollapsingHeader("Hitch Time Histogram")) + if (ImGui::CollapsingHeader("Hitch Time Histogram")) { ++numShown; - ImGui::PlotHistogram("", hitchTimes, NUM, 0, NULL, FLT_MAX, FLT_MAX, size ); + ImGui::PlotHistogram("", hitchTimes, NUM, 0, NULL, FLT_MAX, FLT_MAX, size); PlotRefreshLines(); } - if(ImGui::CollapsingHeader("Hitch Count Histogram")) + if (ImGui::CollapsingHeader("Hitch Count Histogram")) { ++numShown; - ImGui::PlotHistogram("", hitchCounts, NUM, 0, NULL, FLT_MAX, FLT_MAX, size ); + ImGui::PlotHistogram("", hitchCounts, NUM, 0, NULL, FLT_MAX, FLT_MAX, size); PlotRefreshLines(); } - if( ImGui::Button("Clear") ) + if (ImGui::Button("Clear")) { Clear(); } - CalcHistogramSize( numShown ); + CalcHistogramSize(numShown); } ImGui::End(); } }; - + }; diff --git a/include/blt/gfx/renderer/batch_2d_renderer.h b/include/blt/gfx/renderer/batch_2d_renderer.h index 3775911..887f619 100644 --- a/include/blt/gfx/renderer/batch_2d_renderer.h +++ b/include/blt/gfx/renderer/batch_2d_renderer.h @@ -87,7 +87,8 @@ namespace blt::gfx explicit point2d_t(const vec2f pos): pos(pos) {} - point2d_t apply_scale(float s) const { + point2d_t apply_scale(float s) const + { return {pos, scale * s}; } }; diff --git a/include/blt/gfx/renderer/font_renderer.h b/include/blt/gfx/renderer/font_renderer.h index 4643323..0816c57 100644 --- a/include/blt/gfx/renderer/font_renderer.h +++ b/include/blt/gfx/renderer/font_renderer.h @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -118,6 +119,24 @@ namespace blt::gfx class font_renderer_t { public: + struct render_context + { + blt::vec2f position, bounds; + blt::vec2f scale; + blt::vec4 color; + }; + + struct text_index_t + { + std::string text; + float size; + std::string font; + + bool operator==(const text_index_t& rhs) const; + + bool operator!=(const text_index_t& rhs) const; + }; + struct compiled_text_t { friend font_renderer_t; @@ -135,22 +154,21 @@ namespace blt::gfx compiled_text_t& setBounds(const blt::vec2f& limit) { bounds = limit; + recompile(); return *this; } + compiled_text_t& setBounds(float width, float height) + { + return setBounds({width, height}); + } + compiled_text_t& setUnrestrictedBounds() { bounds = {}; return *this; } - compiled_text_t& setSize(float size) - { - current_size = size; - scale = {size / generator.get_generated_size(), size / generator.get_generated_size()}; - return *this; - } - compiled_text_t& setColor(const blt::vec4f& c) { color = c; @@ -169,6 +187,14 @@ namespace blt::gfx return *this; } + compiled_text_t& setSize(float size) + { + current_size = size; + scale = {size / generator.get_generated_size(), size / generator.get_generated_size()}; + recompile(); + return *this; + } + compiled_text_t& setZIndex(float s) { z_index = s; @@ -212,7 +238,7 @@ namespace blt::gfx std::vector renders; blt::vec2f position, bounds; blt::vec2f scale = {1, 1}; - blt::vec4 color = blt::make_color(1, 1, 1); + blt::vec4 color = blt::make_color(0.8, 0.8, 0.8); float z_index = 0; float current_size = 0; }; @@ -237,19 +263,52 @@ namespace blt::gfx compiled_text_t* create_text(std::string_view str, float size, std::string_view font = "__default"); + compiled_text_t* render_text(std::string_view str, float size, std::string_view font = "__default"); + void destroy_text(compiled_text_t* text); void render(); private: + std::unique_ptr allocate_text(); + std::unique_ptr generator; std::unique_ptr font_shader; + blt::hashmap_t rendered_strings; + blt::hashmap_t text_ptr_to_text_index; + + blt::hashset_t last_rendered_strings; + blt::hashset_t currently_rendered_strings; + blt::hashmap_t text_ptr_to_index; std::vector> added_texts; - std::vector> cached_text_objects; + std::vector> cached_deallocated_text_objects; }; } +namespace blt::gfx::detail +{ + inline void hash_combine(std::size_t& seed, const std::size_t& value) + { + seed ^= value + 0x9e3779b9 + (seed << 6) + (seed >> 2); + } +} + +// Specialization of std::hash for text_index_t +template<> +struct std::hash +{ + std::size_t operator()(const blt::gfx::font_renderer_t::text_index_t& key) const + { + blt::size_t hash = 0; + blt::gfx::detail::hash_combine(hash, std::hash{}(key.text)); + blt::gfx::detail::hash_combine(hash, std::hash{}(key.size)); + blt::gfx::detail::hash_combine(hash, std::hash{}(key.font)); + + return hash; + } +}; + #endif //BLT_WITH_GRAPHICS_FONT_RENDERER_H diff --git a/include/blt/gfx/renderer/shaders/2d_font.frag b/include/blt/gfx/renderer/shaders/2d_font.frag index 27aa0e7..bf3d133 100644 --- a/include/blt/gfx/renderer/shaders/2d_font.frag +++ b/include/blt/gfx/renderer/shaders/2d_font.frag @@ -11,9 +11,14 @@ const std::string shader_2d_font_frag = R"(" uniform vec4 color; void main() { - FragColor = texture(tex, uv).r * color; - if (FragColor.a < 0.2) + if (texture(tex, uv).r > 0.2) + FragColor = color; + else discard; +// if (FragColor.a < 0.2) +// discard; +// else +// FragColor.a = 1.0; } ")"; diff --git a/src/blt/gfx/renderer/font_renderer.cpp b/src/blt/gfx/renderer/font_renderer.cpp index b038c98..b5e4270 100644 --- a/src/blt/gfx/renderer/font_renderer.cpp +++ b/src/blt/gfx/renderer/font_renderer.cpp @@ -18,7 +18,10 @@ #include #include #include +#include "blt/gfx/window.h" + #include +#include #include // TODO: signed distance fonts @@ -147,18 +150,20 @@ namespace blt::gfx void font_renderer_t::create_default(float gen_size, blt::i32 dimensions) { create(gen_size, dimensions); - - add_default_font(reinterpret_cast(font::default_font_compressed_data), font::default_font_compressed_size, true); + +// add_default_font(reinterpret_cast(font::default_font_compressed_data), font::default_font_compressed_size, true); + add_default_font(reinterpret_cast(ImGui::Spectrum::SourceSansProRegular_compressed_data), + ImGui::Spectrum::SourceSansProRegular_compressed_size, true); } void font_renderer_t::cleanup() { for (auto& n : added_texts) n = nullptr; - for (auto& c : cached_text_objects) + for (auto& c : cached_deallocated_text_objects) c = nullptr; added_texts.clear(); - cached_text_objects.clear(); + cached_deallocated_text_objects.clear(); text_ptr_to_index.clear(); font_shader = nullptr; generator = nullptr; @@ -167,15 +172,7 @@ namespace blt::gfx font_renderer_t::compiled_text_t* font_renderer_t::create_text(std::string_view str, float size, std::string_view font) { - std::unique_ptr ptr; - if (!cached_text_objects.empty()) - { - ptr = std::move(*cached_text_objects.begin()); - cached_text_objects.erase(cached_text_objects.begin()); - } else - { - ptr = std::unique_ptr(new compiled_text_t{*generator}); - } + std::unique_ptr ptr = allocate_text(); ptr->setText(str, size, font); auto index = added_texts.size(); added_texts.push_back(std::move(ptr)); @@ -187,18 +184,22 @@ namespace blt::gfx { auto index = text_ptr_to_index[text]; text_ptr_to_index.erase(text); - cached_text_objects.push_back(std::move(added_texts[index])); + cached_deallocated_text_objects.push_back(std::move(added_texts[index])); added_texts[index] = nullptr; added_texts.erase(added_texts.begin() + static_cast(index)); } void font_renderer_t::render() { +#ifndef __EMSCRIPTEN__ + glDisable(GL_MULTISAMPLE); +#endif + glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); font_shader->bind(); glActiveTexture(GL_TEXTURE0); - for (auto& text : added_texts) + for (auto [text, index] : text_ptr_to_index) { blt::mat4x4 transform; transform.translate(text->position); @@ -208,22 +209,87 @@ namespace blt::gfx font_shader->setVec4("color", text->color); text->draw(); } + + for (auto& text : currently_rendered_strings) + { + blt::mat4x4 transform; + transform.translate(text->position); + transform.scale(text->scale); + font_shader->setMatrix("transform", transform); + font_shader->setFloat("z_index", text->z_index); + font_shader->setVec4("color", text->color); + text->draw(); + + if (last_rendered_strings.find(text) != last_rendered_strings.end()) + last_rendered_strings.erase(text); + } + + for (auto& text : last_rendered_strings) + { + rendered_strings.erase(text_ptr_to_text_index[text]); + text_ptr_to_text_index.erase(text); + cached_deallocated_text_objects.emplace_back(std::move(added_texts[text_ptr_to_index[text]])); + text_ptr_to_index.erase(text); + } + + for (auto& text : currently_rendered_strings) + last_rendered_strings.insert(text); + currently_rendered_strings.clear(); + +#ifndef __EMSCRIPTEN__ + glEnable(GL_MULTISAMPLE); +#endif } void font_renderer_t::add_default_font(std::string_view path, std::string_view name) { font::font_face_t default_font_face{path, name}; - font::font_file_t default_font{default_font_face, 0, 128}; + font::font_file_t default_font{default_font_face, 0, 256}; add_font(default_font); } void font_renderer_t::add_default_font(const blt::u8* data, blt::size_t size, bool compressed, std::string_view name) { font::font_face_t default_font_face{name, data, size, compressed}; - font::font_file_t default_font{default_font_face, 0, 128}; + font::font_file_t default_font{default_font_face, 0, 256}; add_font(default_font); } + std::unique_ptr font_renderer_t::allocate_text() + { + std::unique_ptr ptr; + if (!cached_deallocated_text_objects.empty()) + { + ptr = std::move(*cached_deallocated_text_objects.begin()); + cached_deallocated_text_objects.erase(cached_deallocated_text_objects.begin()); + } else + { + ptr = std::unique_ptr(new compiled_text_t{*generator}); + } + return ptr; + } + + font_renderer_t::compiled_text_t* font_renderer_t::render_text(std::string_view str, float size, std::string_view font) + { + auto str_iter = rendered_strings.find(text_index_t{std::string(str), size, std::string(font)}); + if (str_iter != rendered_strings.end()) + { + currently_rendered_strings.insert(str_iter->second); + return str_iter->second; + } + auto index = added_texts.size(); + added_texts.emplace_back(allocate_text()); + auto ptr = added_texts.back().get(); + ptr->setText(str, size); + + text_ptr_to_text_index[ptr] = text_index_t{std::string(str), size, std::string(font)}; + rendered_strings[text_index_t{std::string(str), size, std::string(font)}] = ptr; + text_ptr_to_index[ptr] = index; + + currently_rendered_strings.insert(ptr); + return ptr; + } + font_renderer_t::compiled_text_t& font_renderer_t::compiled_text_t::setText(std::string_view str, float size, std::string_view f) { if (str == contents) @@ -262,40 +328,9 @@ namespace blt::gfx } } - struct word_t - { - std::vector vertices; - float x = 0; - - void apply(std::vector& out) - { - for (float v : vertices) - out.push_back(v + x); - vertices.clear(); - x = 0; - } - }; - - struct position_tracker_t - { - float global_x = 0, global_y = 0; - - void newline(float line_size) - { - global_y -= line_size; - global_x = 0; - } - - void advance(blt::u64 size) - { - global_x += static_cast(size >> 6); - } - }; - font_renderer_t::compiled_text_t& font_renderer_t::compiled_text_t::recompile() { static std::vector vertices; - static word_t current_word; vertices.clear(); renders.clear(); @@ -303,27 +338,33 @@ namespace blt::gfx blt::size_t draw_start = 0; blt::size_t draw_count = 0; - position_tracker_t current_pos; + const float sc = generator.get_generated_size() / current_size; + + float global_x = 0; + float global_y = 0; // TODO: parker UTF8 for (const auto& c : contents) { -// std::cout << "C: " << c << " || " << static_cast(c) << std::endl; + if (bounds.y() > 0 && std::abs(global_y) >= bounds.y() * sc) + continue; if (c == '\n') { - current_pos.newline(generator.get_generated_size()); + global_x = 0; + global_y -= generator.get_generated_size(); continue; } auto& texture = generator.get_texture(std::string(font), c); auto& ch = texture.get_glyph(c, font); -// if (std::isblank(c)) -// { -// if (bounds.x() > 0 && current_pos.global_x + current_word.x > bounds.x()) -// current_pos.newline(generator.get_generated_size()); -// -// } - if (std::isspace(c)) - current_word.apply(vertices); + if (std::isblank(c)) + { + if (bounds.x() > 0 && global_x > bounds.x() * sc) + { + global_x = 0; + global_y -= generator.get_generated_size(); + continue; + } + } if (last_texture == nullptr) last_texture = &texture; @@ -336,8 +377,8 @@ namespace blt::gfx auto dims = static_cast(generator.get_dimensions()); - float x_pos = current_pos.global_x + static_cast(ch.glyph.bearing.x()); - float y_pos = current_pos.global_y - static_cast(ch.glyph.size.y() - ch.glyph.bearing.y()); + float x_pos = global_x + static_cast(ch.glyph.bearing.x()); + float y_pos = global_y - static_cast(ch.glyph.size.y() - ch.glyph.bearing.y()); auto w = static_cast(ch.glyph.size.x()); auto h = static_cast(ch.glyph.size.y()); @@ -358,12 +399,11 @@ namespace blt::gfx x_pos + w, y_pos, texture_max_x, texture_max_y, x_pos + w, y_pos + h, texture_max_x, texture_min_y }; - current_word.vertices.insert(current_word.vertices.end(), local_vert.begin(), local_vert.end()); + vertices.insert(vertices.end(), local_vert.begin(), local_vert.end()); draw_count += 6; - current_pos.advance(ch.glyph.advance); + global_x += static_cast(ch.glyph.advance >> 6); } - current_word.apply(vertices); // BLT_TRACE("size: %ld %ld %ld", draw_count, vertices.size(), vertices.size() / 4); @@ -373,4 +413,13 @@ namespace blt::gfx } + bool font_renderer_t::text_index_t::operator==(const font_renderer_t::text_index_t& rhs) const + { + return text == rhs.text && blt::f_equal(size, rhs.size) && font == rhs.font; + } + + bool font_renderer_t::text_index_t::operator!=(const font_renderer_t::text_index_t& rhs) const + { + return !(rhs == *this); + } } \ No newline at end of file diff --git a/src/blt/gfx/renderer/resource_manager.cpp b/src/blt/gfx/renderer/resource_manager.cpp index 9dcccde..57cccac 100644 --- a/src/blt/gfx/renderer/resource_manager.cpp +++ b/src/blt/gfx/renderer/resource_manager.cpp @@ -76,10 +76,13 @@ namespace blt::gfx std::this_thread::sleep_for(std::chrono::milliseconds(100)); } while (true); #else - for (const auto& texture : textures_to_load) + for (const auto& [destination, texture] : textures_to_load) { - BLT_DEBUG("Loading texture file %s", texture.path.c_str()); - loaded_textures.push_back(new texture_file(texture.path, texture.name)); + auto path = resource_prefix + texture.get_path(); + auto* file = new texture_file(path, texture.get_name(), texture.get_desired_channels()); + file->resize(texture.get_desired_width(), texture.get_desired_height()); + BLT_DEBUG("Loading texture file %s", path.c_str()); + destination.add(file); } textures_to_load.clear(); #endif diff --git a/src/blt/gfx/window.cpp b/src/blt/gfx/window.cpp index 86a4c61..f5b5eec 100644 --- a/src/blt/gfx/window.cpp +++ b/src/blt/gfx/window.cpp @@ -136,7 +136,7 @@ namespace blt::gfx // Setup Dear ImGui style ImGui::StyleColorsDark(); - //ImGui::Spectrum::StyleColorsSpectrum(); +// ImGui::Spectrum::StyleColorsSpectrum(); ImGui::Spectrum::LoadFont(); ImGui::SetupImGuiStyle(true, 1.0); @@ -157,7 +157,7 @@ namespace blt::gfx // Setup Platform/Renderer backends ImGui_ImplGlfw_InitForOpenGL(window_state.window, true); #ifdef __EMSCRIPTEN__ - ImGui_ImplGlfw_InstallEmscriptenCanvasResizeCallback("#canvas"); +// ImGui_ImplGlfw_InstallEmscriptenCanvasResizeCallback("#canvas"); #endif ImGui_ImplOpenGL3_Init(glsl_version); diff --git a/src/blt/imgui/imgui_utils.cpp b/src/blt/imgui/imgui_utils.cpp index 1a2a3bb..2c29678 100644 --- a/src/blt/imgui/imgui_utils.cpp +++ b/src/blt/imgui/imgui_utils.cpp @@ -19,8 +19,8 @@ namespace ImGui::Spectrum { - extern const unsigned int SourceSansProRegular_compressed_size = 149392; - extern const unsigned int SourceSansProRegular_compressed_data[]; // defined later in the file +// extern const unsigned int SourceSansProRegular_compressed_size; +// extern const unsigned int SourceSansProRegular_compressed_data[]; // defined later in the file void StyleColorsSpectrum() { @@ -81,6 +81,7 @@ namespace ImGui::Spectrum io.FontDefault = font; } + const unsigned int SourceSansProRegular_compressed_size = 149392; const unsigned int SourceSansProRegular_compressed_data[149392 / 4] = { 0x0000bc57, 0x00000000, 0x7ce00300, 0x00000400, 0x00010037, 0x000f0000, 0x00030080, 0x53414270, 0x5d1e6545, 0x2d0200bd,