From 21ddf9ebaafdf2aca86fed71a666bfcd0ac1077d Mon Sep 17 00:00:00 2001 From: Pier-Olivier Boulianne Date: Sun, 4 May 2025 13:14:18 -0400 Subject: [PATCH 1/5] Added msdf-atlas-gen submodule + basic example in Font.cpp --- .gitmodules | 3 ++ Dependencies.lua | 2 ++ StarEditor/src/EditorLayer.cpp | 4 ++- StarEngine/premake5.lua | 3 ++ StarEngine/src/StarEngine/Renderer/Font.cpp | 36 +++++++++++++++++++++ StarEngine/src/StarEngine/Renderer/Font.h | 13 ++++++++ StarEngine/vendor/msdf-atlas-gen | 1 + premake5.lua | 1 + 8 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 StarEngine/src/StarEngine/Renderer/Font.cpp create mode 100644 StarEngine/src/StarEngine/Renderer/Font.h create mode 160000 StarEngine/vendor/msdf-atlas-gen diff --git a/.gitmodules b/.gitmodules index ff7fca0d..c6c8e2c7 100644 --- a/.gitmodules +++ b/.gitmodules @@ -23,3 +23,6 @@ [submodule "StarEngine/vendor/Box2D"] path = StarEngine/vendor/Box2D url = https://github.com/sheazywi/box2d +[submodule "StarEngine/vendor/msdf-atlas-gen"] + path = StarEngine/vendor/msdf-atlas-gen + url = https://github.com/sheazywi/msdf-atlas-gen diff --git a/Dependencies.lua b/Dependencies.lua index 658caf78..9ebb6a4c 100644 --- a/Dependencies.lua +++ b/Dependencies.lua @@ -17,6 +17,8 @@ IncludeDir["mono"] = "%{wks.location}/StarEngine/vendor/mono/include" IncludeDir["shaderc"] = "%{wks.location}/StarEngine/vendor/shaderc/include" IncludeDir["SPIRV_Cross"] = "%{wks.location}/StarEngine/vendor/SPIRV-Cross" IncludeDir["VulkanSDK"] = "%{VULKAN_SDK}/Include" +IncludeDir["msdfgen"] = "%{wks.location}/StarEngine/vendor/msdf-atlas-gen/msdfgen" +IncludeDir["msdf_atlas_gen"] = "%{wks.location}/StarEngine/vendor/msdf-atlas-gen/msdf-atlas-gen" LibraryDir = {} diff --git a/StarEditor/src/EditorLayer.cpp b/StarEditor/src/EditorLayer.cpp index 5686c23e..7eefb209 100644 --- a/StarEditor/src/EditorLayer.cpp +++ b/StarEditor/src/EditorLayer.cpp @@ -4,6 +4,7 @@ #include "StarEngine/Utils/PlatformUtils.h" #include "StarEngine/Math/Math.h" #include "StarEngine/Scripting/ScriptEngine.h" +#include "StarEngine/Renderer/Font.h" #include #include @@ -12,13 +13,14 @@ #include "imgui/imgui_internal.h" #include "ImGuizmo.h" - namespace StarEngine { EditorLayer::EditorLayer() : Layer("EditorLayer"), m_CameraController(1280.0f / 720.0f, true), m_SquareColor({ 0.2f, 0.3f, 0.8f, 1.0f }) { + Font font("assets/fonts/opensans/OpenSans-Regular.ttf"); + } void EditorLayer::OnAttach() diff --git a/StarEngine/premake5.lua b/StarEngine/premake5.lua index add3ce29..ffdaac25 100644 --- a/StarEngine/premake5.lua +++ b/StarEngine/premake5.lua @@ -42,6 +42,8 @@ project "StarEngine" "%{IncludeDir.filewatch}", "%{IncludeDir.stb_image}", "%{IncludeDir.entt}", + "%{IncludeDir.msdfgen}", + "%{IncludeDir.msdf_atlas_gen}", "%{IncludeDir.yaml_cpp}", "%{IncludeDir.ImGuizmo}", "%{IncludeDir.VulkanSDK}", @@ -57,6 +59,7 @@ project "StarEngine" "opengl32", "%{Library.mono}", "yaml-cpp", + "msdf-atlas-gen", "Box2D", "dwmapi.lib" } diff --git a/StarEngine/src/StarEngine/Renderer/Font.cpp b/StarEngine/src/StarEngine/Renderer/Font.cpp new file mode 100644 index 00000000..91f003d4 --- /dev/null +++ b/StarEngine/src/StarEngine/Renderer/Font.cpp @@ -0,0 +1,36 @@ +#include "sepch.h" +#include "Font.h" + +#undef INFINITE +#include "msdf-atlas-gen.h" + +namespace StarEngine { + + Font::Font(const std::filesystem::path& filepath) + { + msdfgen::FreetypeHandle* ft = msdfgen::initializeFreetype(); + if (ft) + { + std::string fileString = filepath.string(); + msdfgen::FontHandle* font = msdfgen::loadFont(ft, fileString.c_str()); + if (font) + { + msdfgen::Shape shape; + if (msdfgen::loadGlyph(shape, font, 'C')) + { + shape.normalize(); + // max. angle + msdfgen::edgeColoringSimple(shape, 3.0); + // image width, height + msdfgen::Bitmap msdf(32, 32); + // range, scale, translation + msdfgen::generateMSDF(msdf, shape, 4.0, 1.0, msdfgen::Vector2(4.0, 4.0)); + msdfgen::savePng(msdf, "output.png"); + } + msdfgen::destroyFont(font); + } + msdfgen::deinitializeFreetype(ft); + } + } + +} diff --git a/StarEngine/src/StarEngine/Renderer/Font.h b/StarEngine/src/StarEngine/Renderer/Font.h new file mode 100644 index 00000000..45785d82 --- /dev/null +++ b/StarEngine/src/StarEngine/Renderer/Font.h @@ -0,0 +1,13 @@ +#pragma once + +namespace StarEngine { + + class Font + { + public: + Font(const std::filesystem::path& font); + + + }; + +} diff --git a/StarEngine/vendor/msdf-atlas-gen b/StarEngine/vendor/msdf-atlas-gen new file mode 160000 index 00000000..b50e101d --- /dev/null +++ b/StarEngine/vendor/msdf-atlas-gen @@ -0,0 +1 @@ +Subproject commit b50e101d24b1f6009841ce3a386e1bc9365dc66a diff --git a/premake5.lua b/premake5.lua index 8dc00bc3..f3647a2b 100644 --- a/premake5.lua +++ b/premake5.lua @@ -30,6 +30,7 @@ group "Dependencies" include "StarEngine/vendor/GLFW" include "StarEngine/vendor/GLAD" include "StarEngine/vendor/imgui" + include "StarEngine/vendor/msdf-atlas-gen" include "StarEngine/vendor/yaml-cpp" group "" From 1bd7f4df24e0585402ba5435fc4bff2fb874d0b3 Mon Sep 17 00:00:00 2001 From: Pier-Olivier Boulianne Date: Sun, 4 May 2025 14:18:38 -0400 Subject: [PATCH 2/5] Generating texture atlas + displaying it in UI - Also added TextureSpecification with some params to Texture API --- StarEditor/src/EditorLayer.cpp | 8 +- .../src/Platform/OpenGL/OpenGLTexture.cpp | 36 ++++- .../src/Platform/OpenGL/OpenGLTexture.h | 6 +- StarEngine/src/StarEngine/Renderer/Font.cpp | 131 +++++++++++++++--- StarEngine/src/StarEngine/Renderer/Font.h | 14 +- .../src/StarEngine/Renderer/Renderer2D.cpp | 2 +- .../src/StarEngine/Renderer/Texture.cpp | 4 +- StarEngine/src/StarEngine/Renderer/Texture.h | 21 ++- 8 files changed, 191 insertions(+), 31 deletions(-) diff --git a/StarEditor/src/EditorLayer.cpp b/StarEditor/src/EditorLayer.cpp index 7eefb209..81eeabc5 100644 --- a/StarEditor/src/EditorLayer.cpp +++ b/StarEditor/src/EditorLayer.cpp @@ -15,11 +15,13 @@ namespace StarEngine { + static Font* s_Font; + EditorLayer::EditorLayer() : Layer("EditorLayer"), m_CameraController(1280.0f / 720.0f, true), m_SquareColor({ 0.2f, 0.3f, 0.8f, 1.0f }) { - Font font("assets/fonts/opensans/OpenSans-Regular.ttf"); + s_Font = new Font("assets/fonts/opensans/OpenSans-Regular.ttf"); } @@ -266,6 +268,10 @@ namespace StarEngine { ImGui::Checkbox("Show Physics Colliders", &m_ShowPhysicsColliders); + ImGui::Separator(); + + ImGui::Image((ImTextureID)s_Font->GetAtlasTexture()->GetRendererID(), { 512,512 }, { 0, 1 }, { 1, 0 }); + ImGui::End(); ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2{ 0, 0 }); diff --git a/StarEngine/src/Platform/OpenGL/OpenGLTexture.cpp b/StarEngine/src/Platform/OpenGL/OpenGLTexture.cpp index 01d01e65..bfa8d6ff 100644 --- a/StarEngine/src/Platform/OpenGL/OpenGLTexture.cpp +++ b/StarEngine/src/Platform/OpenGL/OpenGLTexture.cpp @@ -5,13 +5,41 @@ namespace StarEngine { - OpenGLTexture2D::OpenGLTexture2D(uint32_t width, uint32_t height) - : m_Width(width), m_Height(height) + namespace Utils { + + static GLenum HazelImageFormatToGLDataFormat(ImageFormat format) + { + switch (format) + { + case ImageFormat::RGB8: return GL_RGB; + case ImageFormat::RGBA8: return GL_RGBA; + } + + SE_CORE_ASSERT(false); + return 0; + } + + static GLenum HazelImageFormatToGLInternalFormat(ImageFormat format) + { + switch (format) + { + case ImageFormat::RGB8: return GL_RGB8; + case ImageFormat::RGBA8: return GL_RGBA8; + } + + SE_CORE_ASSERT(false); + return 0; + } + + } + + OpenGLTexture2D::OpenGLTexture2D(const TextureSpecification& specification) + : m_Specification(specification), m_Width(m_Specification.Width), m_Height(m_Specification.Height) { SE_PROFILE_FUNCTION(); - m_InternalFormat = GL_RGBA8; - m_DataFormat = GL_RGBA; + m_InternalFormat = Utils::HazelImageFormatToGLInternalFormat(m_Specification.Format); + m_DataFormat = Utils::HazelImageFormatToGLDataFormat(m_Specification.Format); glCreateTextures(GL_TEXTURE_2D, 1, &m_RendererID); glTextureStorage2D(m_RendererID, 1, m_InternalFormat, m_Width, m_Height); diff --git a/StarEngine/src/Platform/OpenGL/OpenGLTexture.h b/StarEngine/src/Platform/OpenGL/OpenGLTexture.h index 44f1e42f..7c744167 100644 --- a/StarEngine/src/Platform/OpenGL/OpenGLTexture.h +++ b/StarEngine/src/Platform/OpenGL/OpenGLTexture.h @@ -9,10 +9,12 @@ namespace StarEngine { class OpenGLTexture2D : public Texture2D { public: - OpenGLTexture2D(uint32_t width, uint32_t height); + OpenGLTexture2D(const TextureSpecification& specification); OpenGLTexture2D(const std::string& path); virtual ~OpenGLTexture2D(); + virtual const TextureSpecification& GetSpecification() const override { return m_Specification; } + virtual uint32_t GetWidth() const override { return m_Width; } virtual uint32_t GetHeight() const override { return m_Height; } virtual uint32_t GetRendererID() const override { return m_RendererID; } @@ -30,6 +32,8 @@ namespace StarEngine { return m_RendererID == other.GetRendererID(); } private: + TextureSpecification m_Specification; + std::string m_Path; bool m_IsLoaded = false; uint32_t m_Width, m_Height; diff --git a/StarEngine/src/StarEngine/Renderer/Font.cpp b/StarEngine/src/StarEngine/Renderer/Font.cpp index 91f003d4..c9c6075a 100644 --- a/StarEngine/src/StarEngine/Renderer/Font.cpp +++ b/StarEngine/src/StarEngine/Renderer/Font.cpp @@ -3,34 +3,125 @@ #undef INFINITE #include "msdf-atlas-gen.h" +#include "FontGeometry.h" +#include "GlyphGeometry.h" namespace StarEngine { + struct MSDFData + { + std::vector Glyphs; + msdf_atlas::FontGeometry FontGeometry; + + }; + + template GenFunc> + static Ref CreateAndCacheAtlas(const std::string& fontName, float fontSize, const std::vector& glyphs, + const msdf_atlas::FontGeometry& fontGeometry, uint32_t width, uint32_t height) + { + msdf_atlas::GeneratorAttributes attributes; + attributes.config.overlapSupport = true; + attributes.scanlinePass = true; + + msdf_atlas::ImmediateAtlasGenerator> generator(width, height); + generator.setAttributes(attributes); + generator.setThreadCount(8); + generator.generate(glyphs.data(), (int)glyphs.size()); + + msdfgen::BitmapConstRef bitmap = (msdfgen::BitmapConstRef)generator.atlasStorage(); + + TextureSpecification spec; + spec.Width = bitmap.width; + spec.Height = bitmap.height; + spec.Format = ImageFormat::RGB8; + spec.GenerateMips = false; + + Ref texture = Texture2D::Create(spec); + texture->SetData((void*)bitmap.pixels, bitmap.width * bitmap.height * 3); + return texture; + } + Font::Font(const std::filesystem::path& filepath) + : m_Data(new MSDFData()) { msdfgen::FreetypeHandle* ft = msdfgen::initializeFreetype(); - if (ft) + SE_CORE_ASSERT(ft); + + std::string fileString = filepath.string(); + + // TODO: msdfgen::loadFontData loads from memory buffer which we'll need + msdfgen::FontHandle* font = msdfgen::loadFont(ft, fileString.c_str()); + if (!font) + { + SE_CORE_ERROR("Failed to load font: {}", fileString); + return; + } + + struct CharsetRange + { + uint32_t Begin, End; + }; + + // From imgui_draw.cpp + static const CharsetRange charsetRanges[] = { - std::string fileString = filepath.string(); - msdfgen::FontHandle* font = msdfgen::loadFont(ft, fileString.c_str()); - if (font) - { - msdfgen::Shape shape; - if (msdfgen::loadGlyph(shape, font, 'C')) - { - shape.normalize(); - // max. angle - msdfgen::edgeColoringSimple(shape, 3.0); - // image width, height - msdfgen::Bitmap msdf(32, 32); - // range, scale, translation - msdfgen::generateMSDF(msdf, shape, 4.0, 1.0, msdfgen::Vector2(4.0, 4.0)); - msdfgen::savePng(msdf, "output.png"); - } - msdfgen::destroyFont(font); - } - msdfgen::deinitializeFreetype(ft); + { 0x0020, 0x00FF } + }; + + msdf_atlas::Charset charset; + for (CharsetRange range : charsetRanges) + { + for (uint32_t c = range.Begin; c <= range.End; c++) + charset.add(c); } + + double fontScale = 1.0; + m_Data->FontGeometry = msdf_atlas::FontGeometry(&m_Data->Glyphs); + int glyphsLoaded = m_Data->FontGeometry.loadCharset(font, fontScale, charset); + SE_CORE_INFO("Loaded {} glyphs from font (out of {})", glyphsLoaded, charset.size()); + + + double emSize = 40.0; + + msdf_atlas::TightAtlasPacker atlasPacker; + // atlasPacker.setDimensionsConstraint() + atlasPacker.setPixelRange(2.0); + atlasPacker.setMiterLimit(1.0); + atlasPacker.setPadding(0); + atlasPacker.setScale(emSize); + int remaining = atlasPacker.pack(m_Data->Glyphs.data(), (int)m_Data->Glyphs.size()); + SE_CORE_ASSERT(remaining == 0); + + int width, height; + atlasPacker.getDimensions(width, height); + emSize = atlasPacker.getScale(); + + m_AtlasTexture = CreateAndCacheAtlas("Test", (float)emSize, m_Data->Glyphs, m_Data->FontGeometry, width, height); + + +#if 0 + msdfgen::Shape shape; + if (msdfgen::loadGlyph(shape, font, 'C')) + { + shape.normalize(); + // max. angle + msdfgen::edgeColoringSimple(shape, 3.0); + // image width, height + msdfgen::Bitmap msdf(32, 32); + // range, scale, translation + msdfgen::generateMSDF(msdf, shape, 4.0, 1.0, msdfgen::Vector2(4.0, 4.0)); + msdfgen::savePng(msdf, "output.png"); + } +#endif + + msdfgen::destroyFont(font); + msdfgen::deinitializeFreetype(ft); } + Font::~Font() + { + delete m_Data; + } + + } diff --git a/StarEngine/src/StarEngine/Renderer/Font.h b/StarEngine/src/StarEngine/Renderer/Font.h index 45785d82..a12acefa 100644 --- a/StarEngine/src/StarEngine/Renderer/Font.h +++ b/StarEngine/src/StarEngine/Renderer/Font.h @@ -1,13 +1,25 @@ #pragma once +#include + +#include "StarEngine/Core/Base.h" +#include "StarEngine/Renderer/Texture.h" + namespace StarEngine { + struct MSDFData; + class Font { public: Font(const std::filesystem::path& font); + ~Font(); - + Ref GetAtlasTexture() const { return m_AtlasTexture; } + private: + MSDFData* m_Data; + Ref m_AtlasTexture; }; + } diff --git a/StarEngine/src/StarEngine/Renderer/Renderer2D.cpp b/StarEngine/src/StarEngine/Renderer/Renderer2D.cpp index 92418417..ab8dabc3 100644 --- a/StarEngine/src/StarEngine/Renderer/Renderer2D.cpp +++ b/StarEngine/src/StarEngine/Renderer/Renderer2D.cpp @@ -161,7 +161,7 @@ namespace StarEngine { s_Data.LineVertexArray->AddVertexBuffer(s_Data.LineVertexBuffer); s_Data.LineVertexBufferBase = new LineVertex[s_Data.MaxVertices]; - s_Data.WhiteTexture = Texture2D::Create(1, 1); + s_Data.WhiteTexture = Texture2D::Create(TextureSpecification()); uint32_t whiteTextureData = 0xffffffff; s_Data.WhiteTexture->SetData(&whiteTextureData, sizeof(uint32_t)); diff --git a/StarEngine/src/StarEngine/Renderer/Texture.cpp b/StarEngine/src/StarEngine/Renderer/Texture.cpp index eb6b8f80..1efc3a2f 100644 --- a/StarEngine/src/StarEngine/Renderer/Texture.cpp +++ b/StarEngine/src/StarEngine/Renderer/Texture.cpp @@ -6,12 +6,12 @@ namespace StarEngine { - Ref Texture2D::Create(uint32_t width, uint32_t height) + Ref Texture2D::Create(const TextureSpecification& specification) { switch (Renderer::GetAPI()) { case RendererAPI::API::None: SE_CORE_ASSERT(false, "RendererAPI::None is currently not supported!"); return nullptr; - case RendererAPI::API::OpenGL: return CreateRef(width, height); + case RendererAPI::API::OpenGL: return CreateRef(specification); } SE_CORE_ASSERT(false, "Unknown RendererAPI!"); diff --git a/StarEngine/src/StarEngine/Renderer/Texture.h b/StarEngine/src/StarEngine/Renderer/Texture.h index 8f02663a..08676ff4 100644 --- a/StarEngine/src/StarEngine/Renderer/Texture.h +++ b/StarEngine/src/StarEngine/Renderer/Texture.h @@ -6,11 +6,30 @@ namespace StarEngine { + enum class ImageFormat + { + None = 0, + R8, + RGB8, + RGBA8, + RGBA32F + }; + + struct TextureSpecification + { + uint32_t Width = 1; + uint32_t Height = 1; + ImageFormat Format = ImageFormat::RGBA8; + bool GenerateMips = true; + }; + class Texture { public: virtual ~Texture() = default; + virtual const TextureSpecification& GetSpecification() const = 0; + virtual uint32_t GetWidth() const = 0; virtual uint32_t GetHeight() const = 0; virtual uint32_t GetRendererID() const = 0; @@ -29,7 +48,7 @@ namespace StarEngine { class Texture2D : public Texture { public: - static Ref Create(uint32_t width, uint32_t height); + static Ref Create(const TextureSpecification& specification); static Ref Create(const std::string& path); }; From 73a1cec081558da88d7f5629e56910bcfc674dfe Mon Sep 17 00:00:00 2001 From: Pier-Olivier Boulianne Date: Sun, 4 May 2025 16:13:43 -0400 Subject: [PATCH 3/5] Rendering text successfully! Seems to have some issues with nvidia drivers... --- .../assets/shaders/Renderer2D_Text.glsl | 79 ++++++++++ StarEditor/src/EditorLayer.cpp | 4 +- .../src/Platform/OpenGL/OpenGLShader.cpp | 2 +- StarEngine/src/StarEngine/Renderer/Font.cpp | 42 ++++- StarEngine/src/StarEngine/Renderer/Font.h | 3 + StarEngine/src/StarEngine/Renderer/MSDFData.h | 17 ++ .../src/StarEngine/Renderer/Renderer2D.cpp | 148 ++++++++++++++++++ .../src/StarEngine/Renderer/Renderer2D.h | 6 +- StarEngine/src/StarEngine/Scene/Scene.cpp | 1 + 9 files changed, 291 insertions(+), 11 deletions(-) create mode 100644 StarEditor/assets/shaders/Renderer2D_Text.glsl create mode 100644 StarEngine/src/StarEngine/Renderer/MSDFData.h diff --git a/StarEditor/assets/shaders/Renderer2D_Text.glsl b/StarEditor/assets/shaders/Renderer2D_Text.glsl new file mode 100644 index 00000000..ecbd1b9f --- /dev/null +++ b/StarEditor/assets/shaders/Renderer2D_Text.glsl @@ -0,0 +1,79 @@ +// 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)); +} + +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; +} diff --git a/StarEditor/src/EditorLayer.cpp b/StarEditor/src/EditorLayer.cpp index 81eeabc5..b746da92 100644 --- a/StarEditor/src/EditorLayer.cpp +++ b/StarEditor/src/EditorLayer.cpp @@ -15,13 +15,13 @@ namespace StarEngine { - static Font* s_Font; + static Ref s_Font; EditorLayer::EditorLayer() : Layer("EditorLayer"), m_CameraController(1280.0f / 720.0f, true), m_SquareColor({ 0.2f, 0.3f, 0.8f, 1.0f }) { - s_Font = new Font("assets/fonts/opensans/OpenSans-Regular.ttf"); + s_Font = Font::GetDefault(); } diff --git a/StarEngine/src/Platform/OpenGL/OpenGLShader.cpp b/StarEngine/src/Platform/OpenGL/OpenGLShader.cpp index 8bb85175..99a56f63 100644 --- a/StarEngine/src/Platform/OpenGL/OpenGLShader.cpp +++ b/StarEngine/src/Platform/OpenGL/OpenGLShader.cpp @@ -195,7 +195,7 @@ namespace StarEngine { shaderc::Compiler compiler; shaderc::CompileOptions options; options.SetTargetEnvironment(shaderc_target_env_vulkan, shaderc_env_version_vulkan_1_2); - const bool optimize = true; + const bool optimize = false; if (optimize) options.SetOptimizationLevel(shaderc_optimization_level_performance); diff --git a/StarEngine/src/StarEngine/Renderer/Font.cpp b/StarEngine/src/StarEngine/Renderer/Font.cpp index c9c6075a..707e0223 100644 --- a/StarEngine/src/StarEngine/Renderer/Font.cpp +++ b/StarEngine/src/StarEngine/Renderer/Font.cpp @@ -6,14 +6,9 @@ #include "FontGeometry.h" #include "GlyphGeometry.h" -namespace StarEngine { - - struct MSDFData - { - std::vector Glyphs; - msdf_atlas::FontGeometry FontGeometry; +#include "MSDFData.h" - }; +namespace StarEngine { template GenFunc> static Ref CreateAndCacheAtlas(const std::string& fontName, float fontSize, const std::vector& glyphs, @@ -96,6 +91,31 @@ namespace StarEngine { atlasPacker.getDimensions(width, height); emSize = atlasPacker.getScale(); +#define DEFAULT_ANGLE_THRESHOLD 3.0 +#define LCG_MULTIPLIER 6364136223846793005ull +#define LCG_INCREMENT 1442695040888963407ull +#define THREAD_COUNT 8 + // if MSDF || MTSDF + + uint64_t coloringSeed = 0; + bool expensiveColoring = false; + if (expensiveColoring) + { + msdf_atlas::Workload([&glyphs = m_Data->Glyphs, &coloringSeed](int i, int threadNo) -> bool { + unsigned long long glyphSeed = (LCG_MULTIPLIER * (coloringSeed ^ i) + LCG_INCREMENT) * !!coloringSeed; + glyphs[i].edgeColoring(msdfgen::edgeColoringInkTrap, DEFAULT_ANGLE_THRESHOLD, glyphSeed); + return true; + }, m_Data->Glyphs.size()).finish(THREAD_COUNT); + } + else { + unsigned long long glyphSeed = coloringSeed; + for (msdf_atlas::GlyphGeometry& glyph : m_Data->Glyphs) + { + glyphSeed *= LCG_MULTIPLIER; + glyph.edgeColoring(msdfgen::edgeColoringInkTrap, DEFAULT_ANGLE_THRESHOLD, glyphSeed); + } + } + m_AtlasTexture = CreateAndCacheAtlas("Test", (float)emSize, m_Data->Glyphs, m_Data->FontGeometry, width, height); @@ -123,5 +143,13 @@ namespace StarEngine { delete m_Data; } + Ref Font::GetDefault() + { + static Ref DefaultFont; + if (!DefaultFont) + DefaultFont = CreateRef("assets/fonts/opensans/OpenSans-Regular.ttf"); + + return DefaultFont; + } } diff --git a/StarEngine/src/StarEngine/Renderer/Font.h b/StarEngine/src/StarEngine/Renderer/Font.h index a12acefa..01b673f1 100644 --- a/StarEngine/src/StarEngine/Renderer/Font.h +++ b/StarEngine/src/StarEngine/Renderer/Font.h @@ -15,7 +15,10 @@ namespace StarEngine { Font(const std::filesystem::path& font); ~Font(); + const MSDFData* GetMSDFData() const { return m_Data; } Ref GetAtlasTexture() const { return m_AtlasTexture; } + + static Ref GetDefault(); private: MSDFData* m_Data; Ref m_AtlasTexture; diff --git a/StarEngine/src/StarEngine/Renderer/MSDFData.h b/StarEngine/src/StarEngine/Renderer/MSDFData.h new file mode 100644 index 00000000..ba0b139e --- /dev/null +++ b/StarEngine/src/StarEngine/Renderer/MSDFData.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +#undef INFINITE +#include "msdf-atlas-gen.h" + +namespace StarEngine { + + struct MSDFData + { + std::vector Glyphs; + msdf_atlas::FontGeometry FontGeometry; + }; + + +} diff --git a/StarEngine/src/StarEngine/Renderer/Renderer2D.cpp b/StarEngine/src/StarEngine/Renderer/Renderer2D.cpp index ab8dabc3..0677748f 100644 --- a/StarEngine/src/StarEngine/Renderer/Renderer2D.cpp +++ b/StarEngine/src/StarEngine/Renderer/Renderer2D.cpp @@ -8,6 +8,8 @@ #include #include +#include "MSDFData.h" + namespace StarEngine { struct QuadVertex @@ -22,6 +24,18 @@ namespace StarEngine { int EntityID; }; + struct TextVertex + { + glm::vec3 Position; + glm::vec4 Color; + glm::vec2 TexCoord; + + // TODO: bg color for outline/bg + + // Editor-only + int EntityID; + }; + struct CircleVertex { glm::vec3 WorldPosition; @@ -63,6 +77,10 @@ namespace StarEngine { Ref LineVertexBuffer; Ref LineShader; + Ref TextVertexArray; + Ref TextVertexBuffer; + Ref TextShader; + uint32_t QuadIndexCount = 0; QuadVertex* QuadVertexBufferBase = nullptr; QuadVertex* QuadVertexBufferPtr = nullptr; @@ -75,11 +93,17 @@ namespace StarEngine { LineVertex* LineVertexBufferBase = nullptr; LineVertex* LineVertexBufferPtr = nullptr; + uint32_t TextIndexCount = 0; + TextVertex* TextVertexBufferBase = nullptr; + TextVertex* TextVertexBufferPtr = nullptr; + float LineWidth = 2.0f; std::array, MaxTextureSlots> TextureSlots; uint32_t TextureSlotIndex = 1; // 0 = white texture + Ref FontAtlasTexture; + glm::vec4 QuadVertexPositions[4]; Renderer2D::Statistics Stats; @@ -161,6 +185,20 @@ namespace StarEngine { s_Data.LineVertexArray->AddVertexBuffer(s_Data.LineVertexBuffer); s_Data.LineVertexBufferBase = new LineVertex[s_Data.MaxVertices]; + // Text + s_Data.TextVertexArray = VertexArray::Create(); + + s_Data.TextVertexBuffer = VertexBuffer::Create(s_Data.MaxVertices * sizeof(TextVertex)); + s_Data.TextVertexBuffer->SetLayout({ + { ShaderDataType::Float3, "a_Position" }, + { ShaderDataType::Float4, "a_Color" }, + { ShaderDataType::Float2, "a_TexCoord" }, + { ShaderDataType::Int, "a_EntityID" } + }); + s_Data.TextVertexArray->AddVertexBuffer(s_Data.TextVertexBuffer); + s_Data.TextVertexArray->SetIndexBuffer(quadIB); + s_Data.TextVertexBufferBase = new TextVertex[s_Data.MaxVertices]; + s_Data.WhiteTexture = Texture2D::Create(TextureSpecification()); uint32_t whiteTextureData = 0xffffffff; s_Data.WhiteTexture->SetData(&whiteTextureData, sizeof(uint32_t)); @@ -172,6 +210,7 @@ namespace StarEngine { s_Data.QuadShader = Shader::Create("assets/shaders/Renderer2D_Quad.glsl"); s_Data.CircleShader = Shader::Create("assets/shaders/Renderer2D_Circle.glsl"); s_Data.LineShader = Shader::Create("assets/shaders/Renderer2D_Line.glsl"); + s_Data.TextShader = Shader::Create("assets/shaders/Renderer2D_Text.glsl"); // Set first texture slot to 0 s_Data.TextureSlots[0] = s_Data.WhiteTexture; @@ -239,6 +278,9 @@ namespace StarEngine { s_Data.LineVertexCount = 0; s_Data.LineVertexBufferPtr = s_Data.LineVertexBufferBase; + s_Data.TextIndexCount = 0; + s_Data.TextVertexBufferPtr = s_Data.TextVertexBufferBase; + s_Data.TextureSlotIndex = 1; } @@ -280,6 +322,19 @@ namespace StarEngine { RenderCommand::DrawLines(s_Data.LineVertexArray, s_Data.LineVertexCount); s_Data.Stats.DrawCalls++; } + + if (s_Data.TextIndexCount) + { + uint32_t dataSize = (uint32_t)((uint8_t*)s_Data.TextVertexBufferPtr - (uint8_t*)s_Data.TextVertexBufferBase); + s_Data.TextVertexBuffer->SetData(s_Data.TextVertexBufferBase, dataSize); + + auto buf = s_Data.TextVertexBufferBase; + s_Data.FontAtlasTexture->Bind(0); + + s_Data.TextShader->Bind(); + RenderCommand::DrawIndexed(s_Data.TextVertexArray, s_Data.TextIndexCount); + s_Data.Stats.DrawCalls++; + } } void Renderer2D::NextBatch() @@ -502,6 +557,99 @@ namespace StarEngine { } } + void Renderer2D::DrawString(const std::string& string, Ref font, const glm::mat4& transform, const glm::vec4& color) + { + const auto& fontGeometry = font->GetMSDFData()->FontGeometry; + const auto& metrics = fontGeometry.getMetrics(); + Ref fontAtlas = font->GetAtlasTexture(); + + s_Data.FontAtlasTexture = fontAtlas; + + double x = 0.0; + double fsScale = 1.0 / (metrics.ascenderY - metrics.descenderY); + double y = 0.0; + float lineHeightOffset = 0.0f; + + for (size_t i = 0; i < string.size(); i++) + { + char character = string[i]; + if (character == '\r') + continue; + + if (character == '\n') + { + x = 0; + y -= fsScale * metrics.lineHeight + lineHeightOffset; + 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); + glm::vec2 texCoordMax((float)ar, (float)at); + + double pl, pb, pr, pt; + glyph->getQuadPlaneBounds(pl, pb, pr, pt); + glm::vec2 quadMin((float)pl, (float)pb); + glm::vec2 quadMax((float)pr, (float)pt); + + quadMin *= fsScale, quadMax *= fsScale; + quadMin += glm::vec2(x, y); + quadMax += glm::vec2(x, y); + + float texelWidth = 1.0f / fontAtlas->GetWidth(); + float texelHeight = 1.0f / fontAtlas->GetHeight(); + texCoordMin *= glm::vec2(texelWidth, texelHeight); + texCoordMax *= glm::vec2(texelWidth, texelHeight); + + // render here + s_Data.TextVertexBufferPtr->Position = transform * glm::vec4(quadMin, 0.0f, 1.0f); + s_Data.TextVertexBufferPtr->Color = color; + s_Data.TextVertexBufferPtr->TexCoord = texCoordMin; + s_Data.TextVertexBufferPtr->EntityID = 0; // TODO + 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->TexCoord = { texCoordMin.x, texCoordMax.y }; + s_Data.TextVertexBufferPtr->EntityID = 0; // TODO + s_Data.TextVertexBufferPtr++; + + s_Data.TextVertexBufferPtr->Position = transform * glm::vec4(quadMax, 0.0f, 1.0f); + s_Data.TextVertexBufferPtr->Color = color; + s_Data.TextVertexBufferPtr->TexCoord = texCoordMax; + s_Data.TextVertexBufferPtr->EntityID = 0; // TODO + 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->TexCoord = { texCoordMax.x, texCoordMin.y }; + s_Data.TextVertexBufferPtr->EntityID = 0; // TODO + s_Data.TextVertexBufferPtr++; + + s_Data.TextIndexCount += 6; + s_Data.Stats.QuadCount++; + + if (i < string.size() - 1) + { + double advance = glyph->getAdvance(); + char nextCharacter = string[i + 1]; + fontGeometry.getAdvance(advance, character, nextCharacter); + + float kerningOffset = 0.0f; + x += fsScale * advance + kerningOffset; + } + } + } + float Renderer2D::GetLineWidth() { return s_Data.LineWidth; diff --git a/StarEngine/src/StarEngine/Renderer/Renderer2D.h b/StarEngine/src/StarEngine/Renderer/Renderer2D.h index 42e2c344..e7d9b288 100644 --- a/StarEngine/src/StarEngine/Renderer/Renderer2D.h +++ b/StarEngine/src/StarEngine/Renderer/Renderer2D.h @@ -7,6 +7,7 @@ #include "StarEngine/Renderer/Camera.h" #include "StarEngine/Renderer/EditorCamera.h" +#include "StarEngine/Renderer/Font.h" #include "StarEngine/Scene/Components.h" @@ -62,9 +63,12 @@ namespace StarEngine { static void DrawRect(const glm::vec3& position, const glm::vec2& size, const glm::vec4& color, int entityID = -1); static void DrawRect(const glm::mat4& transform, const glm::vec4& color, int entityID = -1); - //Sprite + // Sprite static void DrawSprite(const glm::mat4& transform, SpriteRendererComponent& src, int entityID); + // Text + static void DrawString(const std::string& string, Ref font, const glm::mat4& transform, const glm::vec4& color); + static float GetLineWidth(); static void SetLineWidth(float width); diff --git a/StarEngine/src/StarEngine/Scene/Scene.cpp b/StarEngine/src/StarEngine/Scene/Scene.cpp index a956f385..633218e6 100644 --- a/StarEngine/src/StarEngine/Scene/Scene.cpp +++ b/StarEngine/src/StarEngine/Scene/Scene.cpp @@ -443,6 +443,7 @@ namespace StarEngine { Renderer2D::DrawCircle(transform.GetTransform(), circle.Color, circle.Thickness, circle.Fade, (int)entity); } } + Renderer2D::DrawString("StarEngine", Font::GetDefault(), glm::mat4(1.0f), glm::vec4(1.0f)); Renderer2D::EndScene(); } From 564177a9ba037e8c9c29721b05cdd16576bfc150 Mon Sep 17 00:00:00 2001 From: Pier-Olivier Boulianne Date: Sun, 4 May 2025 22:53:44 -0400 Subject: [PATCH 4/5] 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) --- .../Assets/Scenes/Physics2D.starscene | 108 ++++++++++-------- StarEditor/src/EditorLayer.cpp | 3 +- StarEditor/src/Panels/SceneHierarchyPanel.cpp | 9 ++ .../src/StarEngine/ImGui/ImGuiBuild.cpp | 4 +- .../src/StarEngine/Renderer/Renderer2D.cpp | 56 ++++++--- .../src/StarEngine/Renderer/Renderer2D.h | 9 +- StarEngine/src/StarEngine/Scene/Components.h | 12 +- StarEngine/src/StarEngine/Scene/Scene.cpp | 29 ++++- .../src/StarEngine/Scene/SceneSerializer.cpp | 26 +++++ 9 files changed, 186 insertions(+), 70 deletions(-) diff --git a/StarEditor/SandboxProject/Assets/Scenes/Physics2D.starscene b/StarEditor/SandboxProject/Assets/Scenes/Physics2D.starscene index 17b97746..16f8b624 100644 --- a/StarEditor/SandboxProject/Assets/Scenes/Physics2D.starscene +++ b/StarEditor/SandboxProject/Assets/Scenes/Physics2D.starscene @@ -1,10 +1,63 @@ Scene: Untitled Entities: - - Entity: 14040563719264005740 + - Entity: 12327645238897851592 TagComponent: - Tag: Player + Tag: Welcome TransformComponent: - Translation: [0, 5, 0] + Translation: [-3.79999995, 1.79999995, 0] + Rotation: [0, 0, 0] + Scale: [1, 1, 1] + TextComponent: + TextString: Welcome to StarEditor + Color: [1, 1, 1, 1] + Kerning: 0 + LineSpacing: 0 + - Entity: 6793217508611625246 + TagComponent: + Tag: Circle + TransformComponent: + Translation: [0, 0, 0] + Rotation: [0, 0, 0] + Scale: [1, 1, 1] + CircleRendererComponent: + Color: [0.934362948, 0.497845858, 0, 1] + Thickness: 1 + Fade: 0.00499999989 + RigidBody2DComponent: + BodyType: Dynamic + FixedRotation: false + CircleCollider2DComponent: + Offset: [0, 0] + Radius: 0.5 + Density: 1 + Friction: 0.5 + Restitution: 1 + RestitutionThreshold: 0.5 + - Entity: 7665505456185074185 + TagComponent: + Tag: Platform + TransformComponent: + Translation: [0, -10, 0] + Rotation: [0, 0, 0] + Scale: [10, 1, 1] + SpriteRendererComponent: + Color: [1, 1, 1, 1] + TilingFactor: 1 + RigidBody2DComponent: + BodyType: Static + FixedRotation: true + BoxCollider2DComponent: + Offset: [0, 0] + Size: [0.5, 0.5] + Density: 1 + Friction: 0.5 + Restitution: 0 + RestitutionThreshold: 0.5 + - Entity: 16059016071823036758 + TagComponent: + Tag: Cube + TransformComponent: + Translation: [0, 10, 0] Rotation: [0, 0, 0] Scale: [1, 1, 1] SpriteRendererComponent: @@ -18,7 +71,7 @@ Entities: Size: [0.5, 0.5] Density: 1 Friction: 0.5 - Restitution: 0 + Restitution: 0.100000001 RestitutionThreshold: 0.5 - Entity: 16241606122818465121 TagComponent: @@ -38,11 +91,11 @@ Entities: OrthographicFar: 1 Primary: true FixedAspectRatio: false - - Entity: 16059016071823036758 + - Entity: 14040563719264005740 TagComponent: - Tag: Cube + Tag: Player TransformComponent: - Translation: [0, 10, 0] + Translation: [0, 5, 0] Rotation: [0, 0, 0] Scale: [1, 1, 1] SpriteRendererComponent: @@ -51,51 +104,10 @@ Entities: RigidBody2DComponent: BodyType: Dynamic FixedRotation: false - BoxCollider2DComponent: - Offset: [0, 0] - Size: [0.5, 0.5] - Density: 1 - Friction: 0.5 - Restitution: 0.100000001 - RestitutionThreshold: 0.5 - - Entity: 7665505456185074185 - TagComponent: - Tag: Platform - TransformComponent: - Translation: [0, -10, 0] - Rotation: [0, 0, 0] - Scale: [10, 1, 1] - SpriteRendererComponent: - Color: [1, 1, 1, 1] - TilingFactor: 1 - RigidBody2DComponent: - BodyType: Static - FixedRotation: true BoxCollider2DComponent: Offset: [0, 0] Size: [0.5, 0.5] Density: 1 Friction: 0.5 Restitution: 0 - RestitutionThreshold: 0.5 - - Entity: 6793217508611625246 - TagComponent: - Tag: Circle - TransformComponent: - Translation: [0, 0, 0] - Rotation: [0, 0, 0] - Scale: [1, 1, 1] - CircleRendererComponent: - Color: [0.934362948, 0.497845858, 0, 1] - Thickness: 1 - Fade: 0.00499999989 - RigidBody2DComponent: - BodyType: Dynamic - FixedRotation: false - CircleCollider2DComponent: - Offset: [0, 0] - Radius: 0.5 - Density: 1 - Friction: 0.5 - Restitution: 1 RestitutionThreshold: 0.5 \ No newline at end of file diff --git a/StarEditor/src/EditorLayer.cpp b/StarEditor/src/EditorLayer.cpp index b746da92..beb2abb1 100644 --- a/StarEditor/src/EditorLayer.cpp +++ b/StarEditor/src/EditorLayer.cpp @@ -604,8 +604,9 @@ namespace StarEngine { 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/StarEditor/src/Panels/SceneHierarchyPanel.cpp b/StarEditor/src/Panels/SceneHierarchyPanel.cpp index 4f1ed00f..f9cec346 100644 --- a/StarEditor/src/Panels/SceneHierarchyPanel.cpp +++ b/StarEditor/src/Panels/SceneHierarchyPanel.cpp @@ -6,6 +6,7 @@ #include #include +#include #include @@ -247,6 +248,7 @@ namespace StarEngine DisplayAddComponentEntry("RigidBody 2D"); DisplayAddComponentEntry("Box Collider 2D"); DisplayAddComponentEntry("Circle Collider 2D"); + DisplayAddComponentEntry("Text Component"); ImGui::EndPopup(); } @@ -474,6 +476,13 @@ namespace StarEngine 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 diff --git a/StarEngine/src/StarEngine/ImGui/ImGuiBuild.cpp b/StarEngine/src/StarEngine/ImGui/ImGuiBuild.cpp index cdea2890..9eadc3a8 100644 --- a/StarEngine/src/StarEngine/ImGui/ImGuiBuild.cpp +++ b/StarEngine/src/StarEngine/ImGui/ImGuiBuild.cpp @@ -1,5 +1,7 @@ #include "sepch.h" +#include + #define IMGUI_IMPL_OPENGL_LOADER_GLAD #include -#include \ No newline at end of file +#include diff --git a/StarEngine/src/StarEngine/Renderer/Renderer2D.cpp b/StarEngine/src/StarEngine/Renderer/Renderer2D.cpp index 0677748f..332f5736 100644 --- a/StarEngine/src/StarEngine/Renderer/Renderer2D.cpp +++ b/StarEngine/src/StarEngine/Renderer/Renderer2D.cpp @@ -557,7 +557,7 @@ namespace StarEngine { } } - 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(); @@ -568,7 +568,8 @@ namespace StarEngine { 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++) { @@ -579,7 +580,29 @@ namespace StarEngine { 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); @@ -588,9 +611,6 @@ namespace StarEngine { 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); @@ -612,27 +632,27 @@ namespace StarEngine { // 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; @@ -644,12 +664,16 @@ namespace StarEngine { 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/StarEngine/src/StarEngine/Renderer/Renderer2D.h b/StarEngine/src/StarEngine/Renderer/Renderer2D.h index e7d9b288..caa5b92a 100644 --- a/StarEngine/src/StarEngine/Renderer/Renderer2D.h +++ b/StarEngine/src/StarEngine/Renderer/Renderer2D.h @@ -67,7 +67,14 @@ namespace StarEngine { static void DrawSprite(const glm::mat4& transform, SpriteRendererComponent& src, int entityID); // Text - 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/StarEngine/src/StarEngine/Scene/Components.h b/StarEngine/src/StarEngine/Scene/Components.h index d148eef0..738382b6 100644 --- a/StarEngine/src/StarEngine/Scene/Components.h +++ b/StarEngine/src/StarEngine/Scene/Components.h @@ -3,6 +3,7 @@ #include "SceneCamera.h" #include "StarEngine/Core/UUID.h" #include "StarEngine/Renderer/Texture.h" +#include "StarEngine/Renderer/Font.h" #include #include @@ -168,6 +169,15 @@ namespace StarEngine { 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 { @@ -177,6 +187,6 @@ namespace StarEngine { ComponentGroup; + CircleCollider2DComponent, TextComponent>; } diff --git a/StarEngine/src/StarEngine/Scene/Scene.cpp b/StarEngine/src/StarEngine/Scene/Scene.cpp index 633218e6..375481a8 100644 --- a/StarEngine/src/StarEngine/Scene/Scene.cpp +++ b/StarEngine/src/StarEngine/Scene/Scene.cpp @@ -251,6 +251,17 @@ namespace StarEngine { } } + // 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(); } @@ -384,7 +395,7 @@ namespace StarEngine { 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; @@ -443,7 +454,15 @@ namespace StarEngine { Renderer2D::DrawCircle(transform.GetTransform(), circle.Color, circle.Thickness, circle.Fade, (int)entity); } } - Renderer2D::DrawString("StarEngine", Font::GetDefault(), glm::mat4(1.0f), glm::vec4(1.0f)); + // 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(); } @@ -519,4 +538,10 @@ namespace StarEngine { } + template<> + void Scene::OnComponentAdded(Entity entity, TextComponent& component) + { + + } + } diff --git a/StarEngine/src/StarEngine/Scene/SceneSerializer.cpp b/StarEngine/src/StarEngine/Scene/SceneSerializer.cpp index ef338e3e..90b03318 100644 --- a/StarEngine/src/StarEngine/Scene/SceneSerializer.cpp +++ b/StarEngine/src/StarEngine/Scene/SceneSerializer.cpp @@ -358,6 +358,21 @@ namespace StarEngine { 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 } @@ -569,6 +584,17 @@ namespace StarEngine { 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(); + } } } From 24d40bafee0bd4d67350f7d892f1d8c7d3e48c57 Mon Sep 17 00:00:00 2001 From: Pier-Olivier Boulianne Date: Mon, 5 May 2025 00:37:44 -0400 Subject: [PATCH 5/5] Exposed TextComponent to C# - Added Vector4 and some script utils --- .../Source/StarEngine/InternalCalls.cs | 38 +++++- .../Source/StarEngine/Scene/Components.cs | 43 ++++++- .../Source/StarEngine/Vector4.cs | 65 ++++++++++ .../src/StarEngine/Scripting/ScriptEngine.cpp | 5 + .../src/StarEngine/Scripting/ScriptEngine.h | 3 + .../src/StarEngine/Scripting/ScriptGlue.cpp | 121 +++++++++++++++++- 6 files changed, 262 insertions(+), 13 deletions(-) create mode 100644 StarEngine-ScriptCore/Source/StarEngine/Vector4.cs diff --git a/StarEngine-ScriptCore/Source/StarEngine/InternalCalls.cs b/StarEngine-ScriptCore/Source/StarEngine/InternalCalls.cs index 93e509e8..a9b8cb1b 100644 --- a/StarEngine-ScriptCore/Source/StarEngine/InternalCalls.cs +++ b/StarEngine-ScriptCore/Source/StarEngine/InternalCalls.cs @@ -5,33 +5,57 @@ namespace StarEngine { public static class InternalCalls { + #region Entity [MethodImplAttribute(MethodImplOptions.InternalCall)] internal extern static bool Entity_HasComponent(ulong entityID, Type componentType); - [MethodImplAttribute(MethodImplOptions.InternalCall)] internal extern static ulong Entity_FindEntityByName(string name); [MethodImplAttribute(MethodImplOptions.InternalCall)] internal extern static object GetScriptInstance(ulong entityID); + #endregion + #region TransformComponent [MethodImplAttribute(MethodImplOptions.InternalCall)] internal extern static void TransformComponent_GetTranslation(ulong entityID, out Vector3 translation); - [MethodImplAttribute(MethodImplOptions.InternalCall)] internal extern static void TransformComponent_SetTranslation(ulong entityID, ref Vector3 translation); + #endregion + #region Rigidbody2DComponent [MethodImplAttribute(MethodImplOptions.InternalCall)] internal extern static void RigidBody2DComponent_ApplyLinearImpulse(ulong entityID, ref Vector2 impulse, ref Vector2 point, bool wake); - [MethodImplAttribute(MethodImplOptions.InternalCall)] - internal extern static void Rigidbody2DComponent_GetLinearVelocity(ulong entityID, out Vector2 linearVelocity); + internal extern static void RigidBody2DComponent_GetLinearVelocity(ulong entityID, out Vector2 linearVelocity); [MethodImplAttribute(MethodImplOptions.InternalCall)] - internal extern static RigidBody2DComponent.BodyType Rigidbody2DComponent_GetType(ulong entityID); + internal extern static RigidBody2DComponent.BodyType RigidBody2DComponent_GetType(ulong entityID); [MethodImplAttribute(MethodImplOptions.InternalCall)] - internal extern static void Rigidbody2DComponent_SetType(ulong entityID, RigidBody2DComponent.BodyType type); - + internal extern static void RigidBody2DComponent_SetType(ulong entityID, RigidBody2DComponent.BodyType type); [MethodImplAttribute(MethodImplOptions.InternalCall)] internal extern static void RigidBody2DComponent_ApplyLinearImpulseToCenter(ulong entityID, ref Vector2 impulse, bool wake); + #endregion + + #region TextComponent + [MethodImplAttribute(MethodImplOptions.InternalCall)] + internal extern static string TextComponent_GetText(ulong entityID); + [MethodImplAttribute(MethodImplOptions.InternalCall)] + internal extern static void TextComponent_SetText(ulong entityID, string text); + [MethodImplAttribute(MethodImplOptions.InternalCall)] + internal extern static void TextComponent_GetColor(ulong entityID, out Vector4 color); + [MethodImplAttribute(MethodImplOptions.InternalCall)] + internal extern static void TextComponent_SetColor(ulong entityID, ref Vector4 color); + [MethodImplAttribute(MethodImplOptions.InternalCall)] + internal extern static float TextComponent_GetKerning(ulong entityID); + [MethodImplAttribute(MethodImplOptions.InternalCall)] + internal extern static void TextComponent_SetKerning(ulong entityID, float kerning); + [MethodImplAttribute(MethodImplOptions.InternalCall)] + internal extern static float TextComponent_GetLineSpacing(ulong entityID); + [MethodImplAttribute(MethodImplOptions.InternalCall)] + internal extern static void TextComponent_SetLineSpacing(ulong entityID, float lineSpacing); + #endregion + + #region Rigidbody2DComponent [MethodImplAttribute(MethodImplOptions.InternalCall)] internal extern static bool Input_IsKeyDown(KeyCode keycode); + #endregion } } diff --git a/StarEngine-ScriptCore/Source/StarEngine/Scene/Components.cs b/StarEngine-ScriptCore/Source/StarEngine/Scene/Components.cs index 49b5916d..ac3bd3be 100644 --- a/StarEngine-ScriptCore/Source/StarEngine/Scene/Components.cs +++ b/StarEngine-ScriptCore/Source/StarEngine/Scene/Components.cs @@ -36,7 +36,7 @@ public Vector2 LinearVelocity { get { - InternalCalls.Rigidbody2DComponent_GetLinearVelocity(Entity.ID, out Vector2 velocity); + InternalCalls.RigidBody2DComponent_GetLinearVelocity(Entity.ID, out Vector2 velocity); return velocity; } } @@ -44,8 +44,8 @@ public Vector2 LinearVelocity public BodyType Type { - get => InternalCalls.Rigidbody2DComponent_GetType(Entity.ID); - set => InternalCalls.Rigidbody2DComponent_SetType(Entity.ID, value); + get => InternalCalls.RigidBody2DComponent_GetType(Entity.ID); + set => InternalCalls.RigidBody2DComponent_SetType(Entity.ID, value); } public void ApplyLinearImpulse(Vector2 impulse, Vector2 worldPosition, bool wake) { @@ -58,4 +58,41 @@ public void ApplyLinearImpulse(Vector2 impulse, bool wake) } } + + public class TextComponent : Component + { + + public string Text + { + get => InternalCalls.TextComponent_GetText(Entity.ID); + set => InternalCalls.TextComponent_SetText(Entity.ID, value); + } + + public Vector4 Color + { + get + { + InternalCalls.TextComponent_GetColor(Entity.ID, out Vector4 color); + return color; + } + + set + { + InternalCalls.TextComponent_SetColor(Entity.ID, ref value); + } + } + + public float Kerning + { + get => InternalCalls.TextComponent_GetKerning(Entity.ID); + set => InternalCalls.TextComponent_SetKerning(Entity.ID, value); + } + + public float LineSpacing + { + get => InternalCalls.TextComponent_GetLineSpacing(Entity.ID); + set => InternalCalls.TextComponent_SetLineSpacing(Entity.ID, value); + } + + } } diff --git a/StarEngine-ScriptCore/Source/StarEngine/Vector4.cs b/StarEngine-ScriptCore/Source/StarEngine/Vector4.cs new file mode 100644 index 00000000..977c623a --- /dev/null +++ b/StarEngine-ScriptCore/Source/StarEngine/Vector4.cs @@ -0,0 +1,65 @@ +namespace StarEngine +{ + public struct Vector4 + { + public float X, Y, Z, W; + + public static Vector4 Zero => new Vector4(0.0f); + + public Vector4(float scalar) + { + X = scalar; + Y = scalar; + Z = scalar; + W = scalar; + } + + public Vector4(float x, float y, float z, float w) + { + X = x; + Y = y; + Z = z; + W = w; + } + + public Vector4(Vector3 xyz, float w) + { + X = xyz.X; + Y = xyz.Y; + Z = xyz.Z; + W = w; + } + + public Vector2 XY + { + get => new Vector2(X, Y); + set + { + X = value.X; + Y = value.Y; + } + } + + public Vector3 XYZ + { + get => new Vector3(X, Y, Z); + set + { + X = value.X; + Y = value.Y; + Z = value.Z; + } + } + + public static Vector4 operator +(Vector4 a, Vector4 b) + { + return new Vector4(a.X + b.X, a.Y + b.Y, a.Z + b.Z, a.W + b.W); + } + + public static Vector4 operator *(Vector4 vector, float scalar) + { + return new Vector4(vector.X * scalar, vector.Y * scalar, vector.Z * scalar, vector.W * scalar); + } + + } +} diff --git a/StarEngine/src/StarEngine/Scripting/ScriptEngine.cpp b/StarEngine/src/StarEngine/Scripting/ScriptEngine.cpp index 7d089337..1613a8e9 100644 --- a/StarEngine/src/StarEngine/Scripting/ScriptEngine.cpp +++ b/StarEngine/src/StarEngine/Scripting/ScriptEngine.cpp @@ -463,6 +463,11 @@ namespace StarEngine { return s_Data->EntityInstances.at(uuid)->GetManagedObject(); } + MonoString* ScriptEngine::CreateString(const char* string) + { + return mono_string_new(s_Data->AppDomain, string); + } + MonoObject* ScriptEngine::InstantiateClass(MonoClass* monoClass) { MonoObject* instance = mono_object_new(s_Data->AppDomain, monoClass); diff --git a/StarEngine/src/StarEngine/Scripting/ScriptEngine.h b/StarEngine/src/StarEngine/Scripting/ScriptEngine.h index 00423493..150f1529 100644 --- a/StarEngine/src/StarEngine/Scripting/ScriptEngine.h +++ b/StarEngine/src/StarEngine/Scripting/ScriptEngine.h @@ -14,6 +14,7 @@ extern "C" { typedef struct _MonoAssembly MonoAssembly; typedef struct _MonoImage MonoImage; typedef struct _MonoClassField MonoClassField; + typedef struct _MonoString MonoString; } namespace StarEngine { @@ -166,6 +167,8 @@ namespace StarEngine { static MonoImage* GetCoreAssemblyImage(); static MonoObject* GetManagedInstance(UUID uuid); + + static MonoString* CreateString(const char* string); private: static void InitMono(); static void ShutdownMono(); diff --git a/StarEngine/src/StarEngine/Scripting/ScriptGlue.cpp b/StarEngine/src/StarEngine/Scripting/ScriptGlue.cpp index cac91901..ebb294f6 100644 --- a/StarEngine/src/StarEngine/Scripting/ScriptGlue.cpp +++ b/StarEngine/src/StarEngine/Scripting/ScriptGlue.cpp @@ -28,15 +28,25 @@ struct fmt::formatter { namespace StarEngine { + namespace Utils { + + std::string MonoStringToString(MonoString* string) + { + char* cStr = mono_string_to_utf8(string); + std::string str(cStr); + mono_free(cStr); + return str; + } + + } + static std::unordered_map> s_EntityHasComponentFuncs; #define SE_ADD_INTERNAL_CALL(Name) mono_add_internal_call("StarEngine.InternalCalls::" #Name, Name) static void NativeLog(MonoString* string, int parameter) { - char* cStr = mono_string_to_utf8(string); - std::string str(cStr); - mono_free(cStr); + std::string str = Utils::MonoStringToString(string); std::cout << str << ", " << parameter << std::endl; } @@ -165,6 +175,102 @@ namespace StarEngine { body->SetType(Utils::RigidBody2DTypeToBox2DBody(bodyType)); } + static MonoString* TextComponent_GetText(UUID entityID) + { + Scene* scene = ScriptEngine::GetSceneContext(); + SE_CORE_ASSERT(scene); + Entity entity = scene->GetEntityByUUID(entityID); + SE_CORE_ASSERT(entity); + SE_CORE_ASSERT(entity.HasComponent()); + + auto& tc = entity.GetComponent(); + return ScriptEngine::CreateString(tc.TextString.c_str()); + } + + static void TextComponent_SetText(UUID entityID, MonoString* textString) + { + Scene* scene = ScriptEngine::GetSceneContext(); + SE_CORE_ASSERT(scene); + Entity entity = scene->GetEntityByUUID(entityID); + SE_CORE_ASSERT(entity); + SE_CORE_ASSERT(entity.HasComponent()); + + auto& tc = entity.GetComponent(); + tc.TextString = Utils::MonoStringToString(textString); + } + + static void TextComponent_GetColor(UUID entityID, glm::vec4* color) + { + Scene* scene = ScriptEngine::GetSceneContext(); + SE_CORE_ASSERT(scene); + Entity entity = scene->GetEntityByUUID(entityID); + SE_CORE_ASSERT(entity); + SE_CORE_ASSERT(entity.HasComponent()); + + auto& tc = entity.GetComponent(); + *color = tc.Color; + } + + static void TextComponent_SetColor(UUID entityID, glm::vec4* color) + { + Scene* scene = ScriptEngine::GetSceneContext(); + SE_CORE_ASSERT(scene); + Entity entity = scene->GetEntityByUUID(entityID); + SE_CORE_ASSERT(entity); + SE_CORE_ASSERT(entity.HasComponent()); + + auto& tc = entity.GetComponent(); + tc.Color = *color; + } + + static float TextComponent_GetKerning(UUID entityID) + { + Scene* scene = ScriptEngine::GetSceneContext(); + SE_CORE_ASSERT(scene); + Entity entity = scene->GetEntityByUUID(entityID); + SE_CORE_ASSERT(entity); + SE_CORE_ASSERT(entity.HasComponent()); + + auto& tc = entity.GetComponent(); + return tc.Kerning; + } + + static void TextComponent_SetKerning(UUID entityID, float kerning) + { + Scene* scene = ScriptEngine::GetSceneContext(); + SE_CORE_ASSERT(scene); + Entity entity = scene->GetEntityByUUID(entityID); + SE_CORE_ASSERT(entity); + SE_CORE_ASSERT(entity.HasComponent()); + + auto& tc = entity.GetComponent(); + tc.Kerning = kerning; + } + + static float TextComponent_GetLineSpacing(UUID entityID) + { + Scene* scene = ScriptEngine::GetSceneContext(); + SE_CORE_ASSERT(scene); + Entity entity = scene->GetEntityByUUID(entityID); + SE_CORE_ASSERT(entity); + SE_CORE_ASSERT(entity.HasComponent()); + + auto& tc = entity.GetComponent(); + return tc.LineSpacing; + } + + static void TextComponent_SetLineSpacing(UUID entityID, float lineSpacing) + { + Scene* scene = ScriptEngine::GetSceneContext(); + SE_CORE_ASSERT(scene); + Entity entity = scene->GetEntityByUUID(entityID); + SE_CORE_ASSERT(entity); + SE_CORE_ASSERT(entity.HasComponent()); + + auto& tc = entity.GetComponent(); + tc.LineSpacing = lineSpacing; + } + static bool Input_IsKeyDown(KeyCode keycode) { return Input::IsKeyPressed(keycode); @@ -222,6 +328,15 @@ namespace StarEngine { SE_ADD_INTERNAL_CALL(RigidBody2DComponent_GetType); SE_ADD_INTERNAL_CALL(RigidBody2DComponent_SetType); + SE_ADD_INTERNAL_CALL(TextComponent_GetText); + SE_ADD_INTERNAL_CALL(TextComponent_SetText); + SE_ADD_INTERNAL_CALL(TextComponent_GetColor); + SE_ADD_INTERNAL_CALL(TextComponent_SetColor); + SE_ADD_INTERNAL_CALL(TextComponent_GetKerning); + SE_ADD_INTERNAL_CALL(TextComponent_SetKerning); + SE_ADD_INTERNAL_CALL(TextComponent_GetLineSpacing); + SE_ADD_INTERNAL_CALL(TextComponent_SetLineSpacing); + SE_ADD_INTERNAL_CALL(Input_IsKeyDown); }