add TextComponent

This commit is contained in:
2025-10-31 00:53:18 +08:00
parent 3272797873
commit 66a3171ccd
7 changed files with 131 additions and 42 deletions

View File

@ -9,9 +9,10 @@
#include <imgui_internal.h>
#include <glm/gtc/type_ptr.hpp>
#include <Hazel/Scene/Components.h>
#include "Hazel/UI/UI.h"
#include <misc/cpp/imgui_stdlib.h>
#include "Hazel/Scripting/ScriptEngine.h"
#include "Hazel/UI/UI.h"
namespace Hazel
{
@ -285,6 +286,7 @@ namespace Hazel
DisplayAddComponentEntry<RigidBody2DComponent>("RigidBody 2D");
DisplayAddComponentEntry<BoxCollider2DComponent>("Box Collider 2D");
DisplayAddComponentEntry<CircleCollider2DComponent>("Circle Collider 2D");
DisplayAddComponentEntry<TextComponent>("Text Component");
ImGui::EndPopup();
}
@ -525,6 +527,14 @@ namespace Hazel
ImGui::DragFloat("Restitution", &component.Restitution, 0.01f, 0.0f, 1.0f);
ImGui::DragFloat("Restitution Threshold", &component.RestitutionThreshold, 0.01f, 0.0f);
});
DrawComponent<TextComponent>("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.01f);
ImGui::DragFloat("LineSpacing", &component.LineSpacing, 0.01f);
});
}

View File

@ -9,6 +9,7 @@
#include <imgui_impl_sdl3.h>
#include <Hazel/Core/Application.h>
#include <Hazel/Debug/Instrumentor.h>
#include <misc/cpp/imgui_stdlib.cpp>
#include "ImGuizmo.h"
#include "imgui_internal.h"

View File

@ -760,7 +760,7 @@ namespace Hazel
}
void Renderer2D::DrawString(const std::string& string, const Ref<Font>& font, const glm::mat4& transform,
const glm::vec4& color, int entityID)
const TextParams& textParams, int entityID)
{
const auto& fontGeomatry = font->GetMSDFData()->FontGeometry;
const auto& metrics = fontGeomatry.getMetrics();
@ -772,7 +772,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 = fontGeomatry.getGlyph(' ')->getAdvance();
for (size_t i = 0; i < string.size(); i++)
{
@ -783,17 +784,35 @@ 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;
fontGeomatry.getAdvance(dAdvance, character, nextCharacter);
advance = (float)dAdvance;
}
x += fsScale * advance + textParams.Kerning;
continue;
}
if (character == '\t')
{
x += 4.0f * (fsScale * spaceGlyphAdvance + textParams.Kerning) ;
continue;
}
auto glphy = fontGeomatry.getGlyph(character);
if (!glphy)
glphy = fontGeomatry.getGlyph('?');
if (!glphy)
return;
if (character == '\t')
glphy = fontGeomatry.getGlyph(' ');
// Atlas bound
double aLeft, aButtom, aRight, aTop;
@ -822,25 +841,25 @@ namespace Hazel
// Render
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 = 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 = 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 = 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 = entityID;
s_Data.TextVertexBufferPtr++;
@ -854,12 +873,17 @@ namespace Hazel
const char nextCharacter = string[i + 1];
fontGeomatry.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;

View File

@ -62,7 +62,14 @@ namespace Hazel
static void DrawRect(const glm::mat4& transform, const glm::vec4& color, int entityID = -1);
static void DrawString(const std::string& string, const Ref<Font>& font, const glm::mat4& transform, const glm::vec4& color, int entityID = -1);
struct TextParams
{
glm::vec4 Color {0.0f};
float Kerning = 0.0f;
float LineSpacing = 0.0f;
};
static void DrawString(const std::string& string, const Ref<Font>& 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);

View File

@ -17,6 +17,8 @@
#include <box2d/types.h>
#include <Hazel/Core/UUID.h>
#include "Hazel/Renderer/Font.h"
namespace Hazel
{
struct IDComponent
@ -168,6 +170,16 @@ namespace Hazel
CircleCollider2DComponent(const CircleCollider2DComponent&) = default;
};
struct TextComponent
{
std::string TextString;
Ref<Font> FontAsset = Font::GetDefault();
glm::vec4 Color{1.0f, 1.0f, 1.0f, 1.0f};
float Kerning = 0.0f;
float LineSpacing = -.0f;
};
template<typename... Components>
struct ComponentGroup
{
@ -177,7 +189,7 @@ namespace Hazel
CircleRendererComponent, BoxCollider2DComponent,
CircleCollider2DComponent, CameraComponent,
ScriptComponent, NativeScriptComponent,
RigidBody2DComponent>;
RigidBody2DComponent, TextComponent>;
}

View File

@ -223,11 +223,15 @@ namespace Hazel
Renderer2D::DrawCircle(transform.GetTransform(), circle.Color, circle.Thickness, circle.Fade, (int)entity);
}
// Renderer2D::DrawString("hello", Font::GetDefault(), glm::mat4(1.0f), glm::vec4(1.0f), 0);
Renderer2D::DrawString(R"(
Hello World!
)", Font::GetDefault(), glm::mat4(1.0f), glm::vec4(0.2f, 0.5f, 0.3f, 1.0f));
// Draw Text
{
const auto& view = m_Registry.view<TransformComponent, TextComponent>();
for (const auto& entity : view)
{
auto [transform, text] = view.get<TransformComponent, TextComponent>(entity);
Renderer2D::DrawString(text.TextString, transform.GetTransform(), text, (int)entity);
}
}
Renderer2D::EndScene();
}
@ -532,6 +536,11 @@ namespace Hazel
{
}
template <>
void Scene::OnComponentAdded<TextComponent>(Entity entity, TextComponent& component)
{
}
template HAZEL_API void Scene::OnComponentAdded<IDComponent>(Entity, IDComponent&);
template HAZEL_API void Scene::OnComponentAdded<TransformComponent>(Entity, TransformComponent&);
@ -544,4 +553,5 @@ namespace Hazel
template HAZEL_API void Scene::OnComponentAdded<RigidBody2DComponent>(Entity, RigidBody2DComponent&);
template HAZEL_API void Scene::OnComponentAdded<BoxCollider2DComponent>(Entity, BoxCollider2DComponent&);
template HAZEL_API void Scene::OnComponentAdded<CircleCollider2DComponent>(Entity, CircleCollider2DComponent&);
template HAZEL_API void Scene::OnComponentAdded<TextComponent>(Entity, TextComponent&);
}

View File

@ -197,7 +197,7 @@ namespace Hazel
out << YAML::Key << "TransformComponent";
out << YAML::BeginMap; // TransformComponent
auto& transform = entity.GetComponent<TransformComponent>();
const auto& transform = entity.GetComponent<TransformComponent>();
out << YAML::Key << "Translation" << YAML::Value << transform.Translation;
out << YAML::Key << "Rotation" << YAML::Value << transform.Rotation;
out << YAML::Key << "Scale" << YAML::Value << transform.Scale;
@ -211,8 +211,8 @@ namespace Hazel
out << YAML::BeginMap; // CameraComponent
auto& cameraComponent = entity.GetComponent<CameraComponent>();
auto& camera = cameraComponent.Camera;
const auto& cameraComponent = entity.GetComponent<CameraComponent>();
const auto& camera = cameraComponent.Camera;
out << YAML::Key << "Camera" << YAML::Value; // CameraComponent
out << YAML::BeginMap; // Camera
@ -234,7 +234,7 @@ namespace Hazel
if (entity.HasComponent<ScriptComponent>())
{
auto& scriptComponent = entity.GetComponent<ScriptComponent>();
const auto& scriptComponent = entity.GetComponent<ScriptComponent>();
out << YAML::Key << "ScriptComponent";
@ -242,7 +242,7 @@ namespace Hazel
out << YAML::Key << "ClassName" << YAML::Value << scriptComponent.ClassName;
// fields
Ref<ScriptClass> entityClass = ScriptEngine::GetEntityClass(scriptComponent.ClassName);
const Ref<ScriptClass> entityClass = ScriptEngine::GetEntityClass(scriptComponent.ClassName);
const auto& fields = entityClass->GetFields();
if (fields.size() > 0)
{
@ -303,7 +303,7 @@ namespace Hazel
out << YAML::Key << "CircleRendererComponent";
out << YAML::BeginMap; // CircleRendererComponent
auto& circleRendererComponent = entity.GetComponent<CircleRendererComponent>();
const auto& circleRendererComponent = entity.GetComponent<CircleRendererComponent>();
out << YAML::Key << "Color" << YAML::Value << circleRendererComponent.Color;
out << YAML::Key << "Radius" << YAML::Value << circleRendererComponent.Radius;
out << YAML::Key << "Thickness" << YAML::Value << circleRendererComponent.Thickness;
@ -316,7 +316,7 @@ namespace Hazel
out << YAML::Key << "SpriteRendererComponent";
out << YAML::BeginMap; // SpriteRendererComponent
auto& spriteRendererComponent = entity.GetComponent<SpriteRendererComponent>();
const auto& spriteRendererComponent = entity.GetComponent<SpriteRendererComponent>();
out << YAML::Key << "Color" << YAML::Value << spriteRendererComponent.Color;
if (spriteRendererComponent.Texture)
out << YAML::Key << "TexturePath" << YAML::Value << spriteRendererComponent.Texture->GetPath();
@ -329,7 +329,7 @@ namespace Hazel
out << YAML::Key << "RigidBody2DComponent";
out << YAML::BeginMap; // RigidBody2DComponent
auto& rb2dComponent = entity.GetComponent<RigidBody2DComponent>();
const auto& rb2dComponent = entity.GetComponent<RigidBody2DComponent>();
out << YAML::Key << "BodyType" << YAML::Value << YAML::RigidBody2DTypeToString(rb2dComponent.Type);
out << YAML::Key << "FixedRotation" << YAML::Value << rb2dComponent.FixedRotation;
out << YAML::EndMap; // RigidBody2DComponent
@ -340,7 +340,7 @@ namespace Hazel
out << YAML::Key << "BoxCollider2DComponent";
out << YAML::BeginMap; // BoxCollider2DComponent
auto& bc2dComponent = entity.GetComponent<BoxCollider2DComponent>();
const auto& bc2dComponent = entity.GetComponent<BoxCollider2DComponent>();
out << YAML::Key << "Offset" << YAML::Value << bc2dComponent.Offset;
out << YAML::Key << "Size" << YAML::Value << bc2dComponent.Size;
out << YAML::Key << "Density" << YAML::Value << bc2dComponent.Density;
@ -356,7 +356,7 @@ namespace Hazel
out << YAML::Key << "CircleCollider2DComponent";
out << YAML::BeginMap; // CircleCollider2DComponent
auto& cc2dComponent = entity.GetComponent<CircleCollider2DComponent>();
const auto& cc2dComponent = entity.GetComponent<CircleCollider2DComponent>();
out << YAML::Key << "Offset" << YAML::Value << cc2dComponent.Offset;
out << YAML::Key << "Radius" << YAML::Value << cc2dComponent.Radius;
out << YAML::Key << "Density" << YAML::Value << cc2dComponent.Density;
@ -367,6 +367,20 @@ namespace Hazel
out << YAML::EndMap; // CircleCollider2DComponent
}
if (entity.HasComponent<TextComponent>())
{
out << YAML::Key << "TextComponent";
out << YAML::BeginMap; // TextComponent
const auto& textComponent = entity.GetComponent<TextComponent>();
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
}
@ -438,15 +452,15 @@ namespace Hazel
HZ_CORE_DEBUG("Deserializing Scene {}", strStream.str());
auto entities = data["Entities"];
const auto& entities = data["Entities"];
if (entities)
{
for (auto entity : entities)
for (const auto& entity : entities)
{
uint64_t uuid = entity["Entity"].as<uint64_t>();
std::string name;
auto TagComponent = entity["TagComponent"];
const auto& TagComponent = entity["TagComponent"];
if (TagComponent)
name = TagComponent["Tag"].as<std::string>();
@ -454,7 +468,7 @@ namespace Hazel
Entity deserializedEntity = m_Scene->CreateEntityWithUUID(uuid, name);
auto transformComponent = entity["TransformComponent"];
const auto& transformComponent = entity["TransformComponent"];
if (transformComponent)
{
auto& transform = deserializedEntity.GetComponent<TransformComponent>();
@ -463,7 +477,7 @@ namespace Hazel
transform.Scale = transformComponent["Scale"].as<glm::vec3>();
}
auto cameraComponent = entity["CameraComponent"];
const auto& cameraComponent = entity["CameraComponent"];
if (cameraComponent)
{
auto& camera = deserializedEntity.AddComponent<CameraComponent>();
@ -483,13 +497,13 @@ namespace Hazel
camera.FixedAspectRatio = cameraProps["FixedAspectRatio"].as<bool>();
}
auto scriptComponent = entity["ScriptComponent"];
const auto& scriptComponent = entity["ScriptComponent"];
if (scriptComponent)
{
auto& sc = deserializedEntity.AddComponent<ScriptComponent>();
sc.ClassName = scriptComponent["ClassName"].as<std::string>();
auto scriptFields = scriptComponent["ScriptFields"];
const auto& scriptFields = scriptComponent["ScriptFields"];
if (scriptFields)
{
Ref<ScriptClass> entityClass = ScriptEngine::GetEntityClass(sc.ClassName);
@ -535,7 +549,7 @@ namespace Hazel
}
}
auto spriteRendererComponent = entity["SpriteRendererComponent"];
const auto& spriteRendererComponent = entity["SpriteRendererComponent"];
if (spriteRendererComponent)
{
auto& sprite = deserializedEntity.AddComponent<SpriteRendererComponent>();
@ -550,7 +564,7 @@ namespace Hazel
}
}
auto circleRendererComponent = entity["CircleRendererComponent"];
const auto& circleRendererComponent = entity["CircleRendererComponent"];
if (circleRendererComponent)
{
auto& circle = deserializedEntity.AddComponent<CircleRendererComponent>();
@ -560,7 +574,7 @@ namespace Hazel
circle.Fade = circleRendererComponent["Fade"].as<float>();
}
auto rigidBody2DComponent = entity["RigidBody2DComponent"];
const auto& rigidBody2DComponent = entity["RigidBody2DComponent"];
if (rigidBody2DComponent)
{
auto& rb2d = deserializedEntity.AddComponent<RigidBody2DComponent>();
@ -568,7 +582,7 @@ namespace Hazel
rb2d.FixedRotation = rigidBody2DComponent["FixedRotation"].as<bool>();
}
auto boxCollider2DComponent = entity["BoxCollider2DComponent"];
const auto& boxCollider2DComponent = entity["BoxCollider2DComponent"];
if (boxCollider2DComponent)
{
auto& bc2d = deserializedEntity.AddComponent<BoxCollider2DComponent>();
@ -580,7 +594,7 @@ namespace Hazel
bc2d.RestitutionThreshold = boxCollider2DComponent["RestitutionThreshold"].as<float>();
}
auto circleCollider2DComponent = entity["CircleCollider2DComponent"];
const auto& circleCollider2DComponent = entity["CircleCollider2DComponent"];
if (circleCollider2DComponent)
{
auto& cc2d = deserializedEntity.AddComponent<CircleCollider2DComponent>();
@ -591,6 +605,17 @@ namespace Hazel
cc2d.Restitution = circleCollider2DComponent["Restitution"].as<float>();
cc2d.RestitutionThreshold = circleCollider2DComponent["RestitutionThreshold"].as<float>();
}
const auto& textComponent = entity["TextComponent"];
if (textComponent)
{
auto& tc = deserializedEntity.AddComponent<TextComponent>();
tc.TextString = textComponent["TextString"].as<std::string>();
// TODO: tc.FontAsset
tc.Color = textComponent["Color"].as<glm::vec4>();
tc.Kerning = textComponent["Kerning"].as<float>();
tc.LineSpacing = textComponent["LineSpacing"].as<float>();
}
}
}