From be419f6063143b5c5ca109f64ecf36de658c2c93 Mon Sep 17 00:00:00 2001 From: TheCherno Date: Mon, 27 Feb 2023 21:02:51 +1100 Subject: [PATCH] Added TextComponent with some more text params - Also fixed BoxColliders not rendering correctly with physics visualization turned on when offsets were used (thanks VagueLobster for the fix) --- Hazel/src/Hazel/ImGui/ImGuiBuild.cpp | 4 +- Hazel/src/Hazel/Renderer/Renderer2D.cpp | 57 +++++++--- Hazel/src/Hazel/Renderer/Renderer2D.h | 9 +- Hazel/src/Hazel/Scene/Components.h | 12 ++- Hazel/src/Hazel/Scene/Scene.cpp | 110 +++++--------------- Hazel/src/Hazel/Scene/SceneSerializer.cpp | 26 +++++ Hazelnut/src/EditorLayer.cpp | 5 +- Hazelnut/src/Panels/SceneHierarchyPanel.cpp | 10 ++ 8 files changed, 128 insertions(+), 105 deletions(-) diff --git a/Hazel/src/Hazel/ImGui/ImGuiBuild.cpp b/Hazel/src/Hazel/ImGui/ImGuiBuild.cpp index 43aab4c53..7a19749a3 100644 --- a/Hazel/src/Hazel/ImGui/ImGuiBuild.cpp +++ b/Hazel/src/Hazel/ImGui/ImGuiBuild.cpp @@ -1,5 +1,7 @@ #include "hzpch.h" +#include + #define IMGUI_IMPL_OPENGL_LOADER_GLAD #include -#include \ No newline at end of file +#include diff --git a/Hazel/src/Hazel/Renderer/Renderer2D.cpp b/Hazel/src/Hazel/Renderer/Renderer2D.cpp index 9ec63769e..a4907f5d7 100644 --- a/Hazel/src/Hazel/Renderer/Renderer2D.cpp +++ b/Hazel/src/Hazel/Renderer/Renderer2D.cpp @@ -550,7 +550,7 @@ namespace Hazel { DrawQuad(transform, src.Color, entityID); } - void Renderer2D::DrawString(const std::string& string, Ref font, const glm::mat4& transform, const glm::vec4& color) + void Renderer2D::DrawString(const std::string& string, Ref font, const glm::mat4& transform, const TextParams& textParams, int entityID) { const auto& fontGeometry = font->GetMSDFData()->FontGeometry; const auto& metrics = fontGeometry.getMetrics(); @@ -561,7 +561,8 @@ namespace Hazel { double x = 0.0; double fsScale = 1.0 / (metrics.ascenderY - metrics.descenderY); double y = 0.0; - float lineHeightOffset = 0.0f; + + const float spaceGlyphAdvance = fontGeometry.getGlyph(' ')->getAdvance(); for (size_t i = 0; i < string.size(); i++) { @@ -572,18 +573,38 @@ namespace Hazel { if (character == '\n') { x = 0; - y -= fsScale * metrics.lineHeight + lineHeightOffset; + y -= fsScale * metrics.lineHeight + textParams.LineSpacing; + continue; + } + + if (character == ' ') + { + float advance = spaceGlyphAdvance; + if (i < string.size() - 1) + { + char nextCharacter = string[i + 1]; + double dAdvance; + fontGeometry.getAdvance(dAdvance, character, nextCharacter); + advance = (float)dAdvance; + } + + x += fsScale * advance + textParams.Kerning; + continue; + } + + if (character == '\t') + { + // NOTE(Yan): is this right? + x += 4.0f * (fsScale * spaceGlyphAdvance + textParams.Kerning); continue; } + auto glyph = fontGeometry.getGlyph(character); if (!glyph) glyph = fontGeometry.getGlyph('?'); if (!glyph) return; - if (character == '\t') - glyph = fontGeometry.getGlyph(' '); - double al, ab, ar, at; glyph->getQuadAtlasBounds(al, ab, ar, at); glm::vec2 texCoordMin((float)al, (float)ab); @@ -605,27 +626,27 @@ namespace Hazel { // render here s_Data.TextVertexBufferPtr->Position = transform * glm::vec4(quadMin, 0.0f, 1.0f); - s_Data.TextVertexBufferPtr->Color = color; + s_Data.TextVertexBufferPtr->Color = textParams.Color; s_Data.TextVertexBufferPtr->TexCoord = texCoordMin; - s_Data.TextVertexBufferPtr->EntityID = 0; // TODO + s_Data.TextVertexBufferPtr->EntityID = entityID; s_Data.TextVertexBufferPtr++; s_Data.TextVertexBufferPtr->Position = transform * glm::vec4(quadMin.x, quadMax.y, 0.0f, 1.0f); - s_Data.TextVertexBufferPtr->Color = color; + s_Data.TextVertexBufferPtr->Color = textParams.Color; s_Data.TextVertexBufferPtr->TexCoord = { texCoordMin.x, texCoordMax.y }; - s_Data.TextVertexBufferPtr->EntityID = 0; // TODO + s_Data.TextVertexBufferPtr->EntityID = entityID; s_Data.TextVertexBufferPtr++; s_Data.TextVertexBufferPtr->Position = transform * glm::vec4(quadMax, 0.0f, 1.0f); - s_Data.TextVertexBufferPtr->Color = color; + s_Data.TextVertexBufferPtr->Color = textParams.Color; s_Data.TextVertexBufferPtr->TexCoord = texCoordMax; - s_Data.TextVertexBufferPtr->EntityID = 0; // TODO + s_Data.TextVertexBufferPtr->EntityID = entityID; s_Data.TextVertexBufferPtr++; s_Data.TextVertexBufferPtr->Position = transform * glm::vec4(quadMax.x, quadMin.y, 0.0f, 1.0f); - s_Data.TextVertexBufferPtr->Color = color; + s_Data.TextVertexBufferPtr->Color = textParams.Color; s_Data.TextVertexBufferPtr->TexCoord = { texCoordMax.x, texCoordMin.y }; - s_Data.TextVertexBufferPtr->EntityID = 0; // TODO + s_Data.TextVertexBufferPtr->EntityID = entityID; s_Data.TextVertexBufferPtr++; s_Data.TextIndexCount += 6; @@ -637,12 +658,16 @@ namespace Hazel { char nextCharacter = string[i + 1]; fontGeometry.getAdvance(advance, character, nextCharacter); - float kerningOffset = 0.0f; - x += fsScale * advance + kerningOffset; + x += fsScale * advance + textParams.Kerning; } } } + void Renderer2D::DrawString(const std::string& string, const glm::mat4& transform, const TextComponent& component, int entityID) + { + DrawString(string, component.FontAsset, transform, { component.Color, component.Kerning, component.LineSpacing }, entityID); + } + float Renderer2D::GetLineWidth() { return s_Data.LineWidth; diff --git a/Hazel/src/Hazel/Renderer/Renderer2D.h b/Hazel/src/Hazel/Renderer/Renderer2D.h index 4504247ad..244bd035e 100644 --- a/Hazel/src/Hazel/Renderer/Renderer2D.h +++ b/Hazel/src/Hazel/Renderer/Renderer2D.h @@ -47,7 +47,14 @@ namespace Hazel { static void DrawSprite(const glm::mat4& transform, SpriteRendererComponent& src, int entityID); - static void DrawString(const std::string& string, Ref font, const glm::mat4& transform, const glm::vec4& color); + struct TextParams + { + glm::vec4 Color{ 1.0f }; + float Kerning = 0.0f; + float LineSpacing = 0.0f; + }; + static void DrawString(const std::string& string, Ref font, const glm::mat4& transform, const TextParams& textParams, int entityID = -1); + static void DrawString(const std::string& string, const glm::mat4& transform, const TextComponent& component, int entityID = -1); static float GetLineWidth(); static void SetLineWidth(float width); diff --git a/Hazel/src/Hazel/Scene/Components.h b/Hazel/src/Hazel/Scene/Components.h index 26a152bab..a22f9122b 100644 --- a/Hazel/src/Hazel/Scene/Components.h +++ b/Hazel/src/Hazel/Scene/Components.h @@ -3,6 +3,7 @@ #include "SceneCamera.h" #include "Hazel/Core/UUID.h" #include "Hazel/Renderer/Texture.h" +#include "Hazel/Renderer/Font.h" #include #include @@ -160,6 +161,15 @@ namespace Hazel { CircleCollider2DComponent(const CircleCollider2DComponent&) = default; }; + struct TextComponent + { + std::string TextString; + Ref FontAsset = Font::GetDefault(); + glm::vec4 Color{ 1.0f }; + float Kerning = 0.0f; + float LineSpacing = 0.0f; + }; + template struct ComponentGroup { @@ -169,6 +179,6 @@ namespace Hazel { ComponentGroup; + CircleCollider2DComponent, TextComponent>; } diff --git a/Hazel/src/Hazel/Scene/Scene.cpp b/Hazel/src/Hazel/Scene/Scene.cpp index 81a6fe6cf..22781c6f1 100644 --- a/Hazel/src/Hazel/Scene/Scene.cpp +++ b/Hazel/src/Hazel/Scene/Scene.cpp @@ -254,6 +254,17 @@ namespace Hazel { } } + // Draw text + { + auto view = m_Registry.view(); + for (auto entity : view) + { + auto [transform, text] = view.get(entity); + + Renderer2D::DrawString(text.TextString, transform.GetTransform(), text, (int)entity); + } + } + Renderer2D::EndScene(); } @@ -386,7 +397,7 @@ namespace Hazel { auto& bc2d = entity.GetComponent(); b2PolygonShape boxShape; - boxShape.SetAsBox(bc2d.Size.x * transform.Scale.x, bc2d.Size.y * transform.Scale.y); + boxShape.SetAsBox(bc2d.Size.x * transform.Scale.x, bc2d.Size.y * transform.Scale.y, b2Vec2(bc2d.Offset.x, bc2d.Offset.y), 0.0f); b2FixtureDef fixtureDef; fixtureDef.shape = &boxShape; @@ -448,90 +459,16 @@ namespace Hazel { } } - Renderer2D::DrawString("Cherno", Font::GetDefault(), glm::mat4(1.0f), glm::vec4(1.0f)); - Renderer2D::DrawString( - R"( - // MSDF text shader - -#type vertex -#version 450 core - -layout(location = 0) in vec3 a_Position; -layout(location = 1) in vec4 a_Color; -layout(location = 2) in vec2 a_TexCoord; -layout(location = 3) in int a_EntityID; - -layout(std140, binding = 0) uniform Camera -{ - mat4 u_ViewProjection; -}; - -struct VertexOutput -{ - vec4 Color; - vec2 TexCoord; -}; - -layout (location = 0) out VertexOutput Output; -layout (location = 2) out flat int v_EntityID; - -void main() -{ - Output.Color = a_Color; - Output.TexCoord = a_TexCoord; - v_EntityID = a_EntityID; - - gl_Position = u_ViewProjection * vec4(a_Position, 1.0); -} - -#type fragment -#version 450 core - -layout(location = 0) out vec4 o_Color; -layout(location = 1) out int o_EntityID; - -struct VertexOutput -{ - vec4 Color; - vec2 TexCoord; -}; - -layout (location = 0) in VertexOutput Input; -layout (location = 2) in flat int v_EntityID; - -layout (binding = 0) uniform sampler2D u_FontAtlas; - -float screenPxRange() { - const float pxRange = 2.0; // set to distance field's pixel range - vec2 unitRange = vec2(pxRange)/vec2(textureSize(u_FontAtlas, 0)); - vec2 screenTexSize = vec2(1.0)/fwidth(Input.TexCoord); - return max(0.5*dot(unitRange, screenTexSize), 1.0); -} - -float median(float r, float g, float b) { - return max(min(r, g), min(max(r, g), b)); -} + // Draw text + { + auto view = m_Registry.view(); + for (auto entity : view) + { + auto [transform, text] = view.get(entity); -void main() -{ - vec4 texColor = Input.Color * texture(u_FontAtlas, Input.TexCoord); - - vec3 msd = texture(u_FontAtlas, Input.TexCoord).rgb; - float sd = median(msd.r, msd.g, msd.b); - float screenPxDistance = screenPxRange()*(sd - 0.5); - float opacity = clamp(screenPxDistance + 0.5, 0.0, 1.0); - if (opacity == 0.0) - discard; - - vec4 bgColor = vec4(0.0); - o_Color = mix(bgColor, Input.Color, opacity); - if (o_Color.a == 0.0) - discard; - - o_EntityID = v_EntityID; -} -)" - , Font::GetDefault(), glm::mat4(1.0f), glm::vec4(1.0f)); + Renderer2D::DrawString(text.TextString, transform.GetTransform(), text, (int)entity); + } + } Renderer2D::EndScene(); } @@ -599,4 +536,9 @@ void main() { } + template<> + void Scene::OnComponentAdded(Entity entity, TextComponent& component) + { + } + } diff --git a/Hazel/src/Hazel/Scene/SceneSerializer.cpp b/Hazel/src/Hazel/Scene/SceneSerializer.cpp index c490de4e6..37ff5838c 100644 --- a/Hazel/src/Hazel/Scene/SceneSerializer.cpp +++ b/Hazel/src/Hazel/Scene/SceneSerializer.cpp @@ -355,6 +355,21 @@ namespace Hazel { out << YAML::EndMap; // CircleCollider2DComponent } + if (entity.HasComponent()) + { + out << YAML::Key << "TextComponent"; + out << YAML::BeginMap; // TextComponent + + auto& textComponent = entity.GetComponent(); + out << YAML::Key << "TextString" << YAML::Value << textComponent.TextString; + // TODO: textComponent.FontAsset + out << YAML::Key << "Color" << YAML::Value << textComponent.Color; + out << YAML::Key << "Kerning" << YAML::Value << textComponent.Kerning; + out << YAML::Key << "LineSpacing" << YAML::Value << textComponent.LineSpacing; + + out << YAML::EndMap; // TextComponent + } + out << YAML::EndMap; // Entity } @@ -562,6 +577,17 @@ namespace Hazel { cc2d.Restitution = circleCollider2DComponent["Restitution"].as(); cc2d.RestitutionThreshold = circleCollider2DComponent["RestitutionThreshold"].as(); } + + auto textComponent = entity["TextComponent"]; + if (textComponent) + { + auto& tc = deserializedEntity.AddComponent(); + tc.TextString = textComponent["TextString"].as(); + // tc.FontAsset // TODO + tc.Color = textComponent["Color"].as(); + tc.Kerning = textComponent["Kerning"].as(); + tc.LineSpacing = textComponent["LineSpacing"].as(); + } } } diff --git a/Hazelnut/src/EditorLayer.cpp b/Hazelnut/src/EditorLayer.cpp index 0a8f1f5a7..c945ef33e 100644 --- a/Hazelnut/src/EditorLayer.cpp +++ b/Hazelnut/src/EditorLayer.cpp @@ -577,10 +577,11 @@ namespace Hazel { glm::vec3 translation = tc.Translation + glm::vec3(bc2d.Offset, 0.001f); glm::vec3 scale = tc.Scale * glm::vec3(bc2d.Size * 2.0f, 1.0f); - glm::mat4 transform = glm::translate(glm::mat4(1.0f), translation) + glm::mat4 transform = glm::translate(glm::mat4(1.0f), tc.Translation) * glm::rotate(glm::mat4(1.0f), tc.Rotation.z, glm::vec3(0.0f, 0.0f, 1.0f)) + * glm::translate(glm::mat4(1.0f), glm::vec3(bc2d.Offset, 0.001f)) * glm::scale(glm::mat4(1.0f), scale); - + Renderer2D::DrawRect(transform, glm::vec4(0, 1, 0, 1)); } } diff --git a/Hazelnut/src/Panels/SceneHierarchyPanel.cpp b/Hazelnut/src/Panels/SceneHierarchyPanel.cpp index e174ce1aa..f602c376c 100644 --- a/Hazelnut/src/Panels/SceneHierarchyPanel.cpp +++ b/Hazelnut/src/Panels/SceneHierarchyPanel.cpp @@ -6,6 +6,7 @@ #include #include +#include #include @@ -247,6 +248,7 @@ namespace Hazel { DisplayAddComponentEntry("Rigidbody 2D"); DisplayAddComponentEntry("Box Collider 2D"); DisplayAddComponentEntry("Circle Collider 2D"); + DisplayAddComponentEntry("Text Component"); ImGui::EndPopup(); } @@ -474,6 +476,14 @@ namespace Hazel { ImGui::DragFloat("Restitution Threshold", &component.RestitutionThreshold, 0.01f, 0.0f); }); + DrawComponent("Text Renderer", entity, [](auto& component) + { + ImGui::InputTextMultiline("Text String", &component.TextString); + ImGui::ColorEdit4("Color", glm::value_ptr(component.Color)); + ImGui::DragFloat("Kerning", &component.Kerning, 0.025f); + ImGui::DragFloat("Line Spacing", &component.LineSpacing, 0.025f); + }); + } template