From d28426478b2f987e6fe9386b59d3d4b1b753a48e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20L=C3=BCbe=C3=9F?= Date: Sun, 17 Mar 2024 20:29:52 +0100 Subject: [PATCH] Font renderer improvements + component for scripting --- .../src/EditWindows/ComponentEditWindow.bf | 2 +- GlitchyEngine/src/Renderer/Text/Font.bf | 3 - .../src/Renderer/Text/FontRenderer.bf | 19 ++++++- GlitchyEngine/src/Scripting/ScriptGlue.bf | 56 +++++++++++++++++++ GlitchyEngine/src/World/Scene.bf | 12 +++- GlitchyEngine/src/World/SceneRenderer.bf | 18 +++++- ScriptCore/Graphics/TextRenderer.cs | 49 ++++++++++++++++ ScriptCore/ScriptGlue.cs | 22 ++++++++ 8 files changed, 170 insertions(+), 11 deletions(-) create mode 100644 ScriptCore/Graphics/TextRenderer.cs diff --git a/GlitchyEditor/src/EditWindows/ComponentEditWindow.bf b/GlitchyEditor/src/EditWindows/ComponentEditWindow.bf index 583a7da..ff717da 100644 --- a/GlitchyEditor/src/EditWindows/ComponentEditWindow.bf +++ b/GlitchyEditor/src/EditWindows/ComponentEditWindow.bf @@ -481,7 +481,7 @@ namespace GlitchyEditor.EditWindows { StartNewProperty("Rich text"); - ImGui.AttachTooltip("If checked, the text will be interpreted as rich text. This means, that you can use tags to change the style of the text."); + ImGui.AttachTooltip("If checked, the text will be interpreted as rich text."); bool isRichText = textRendererComponent.IsRichText; diff --git a/GlitchyEngine/src/Renderer/Text/Font.bf b/GlitchyEngine/src/Renderer/Text/Font.bf index 04df7ba..4959e85 100644 --- a/GlitchyEngine/src/Renderer/Text/Font.bf +++ b/GlitchyEngine/src/Renderer/Text/Font.bf @@ -117,9 +117,6 @@ namespace GlitchyEngine.Renderer.Text { Debug.Profiler.ProfileResourceFunction!(); - // Make sure the fontrenderer is initialized (Font only cares about freetype) - FontRenderer.Init(); - // Set default sampler Sampler = null; diff --git a/GlitchyEngine/src/Renderer/Text/FontRenderer.bf b/GlitchyEngine/src/Renderer/Text/FontRenderer.bf index 7801759..4d98ba8 100644 --- a/GlitchyEngine/src/Renderer/Text/FontRenderer.bf +++ b/GlitchyEngine/src/Renderer/Text/FontRenderer.bf @@ -84,6 +84,18 @@ namespace GlitchyEngine.Renderer.Text } public static readonly Self Empty = new Self(null) ~ _.ReleaseRef(); + + /// Optimizes rendering of the text by sorting the glyphs + public void Optimize() + { + // Sort by texture -> render all glyphs with same font at once + Glyphs.Sort((lhs, rhs) => (int)Internal.UnsafeCastToPtr(lhs.Font._atlas) - (int)Internal.UnsafeCastToPtr(rhs.Font._atlas)); + } + + public void Clear() + { + Glyphs.Clear(); + } } /* public class PreparedLine @@ -178,11 +190,11 @@ namespace GlitchyEngine.Renderer.Text } } - public static PreparedText PrepareText(Font font, StringView text, float fontSize, Color fontColor = .White, Color bitmapColor = .White, float lineSpaceScale = 1.0f, TextDirection direction = .LeftToRight) + public static void PrepareText(PreparedText preparedText, Font font, StringView text, float fontSize, Color fontColor = .White, Color bitmapColor = .White, float lineSpaceScale = 1.0f, TextDirection direction = .LeftToRight) { Debug.Profiler.ProfileRendererFunction!(); - PreparedText preparedText = new PreparedText(font); + preparedText.Clear(); float scale = fontSize / (float)font._fontSize; @@ -476,7 +488,7 @@ namespace GlitchyEngine.Renderer.Text preparedText.AdvanceY = baseline; - return preparedText; + preparedText.Optimize(); } public static void DrawText(PreparedText text, float x, float y, Color fontColor = .White) @@ -497,6 +509,7 @@ namespace GlitchyEngine.Renderer.Text Renderer2D.Flush(); // TODO: this is very not good! + // At some point we will support using materials with the 2D renderer. At that point we can simply bind different materials with fonts (or glyphs?) and don't need this hack anymore. One day... var lastEffect = Renderer2D.[Friend]s_currentQuadEffect; Renderer2D.[Friend]s_currentQuadEffect = _msdfEffect; // TODO: oh no.... diff --git a/GlitchyEngine/src/Scripting/ScriptGlue.bf b/GlitchyEngine/src/Scripting/ScriptGlue.bf index cc94cf0..fb71c68 100644 --- a/GlitchyEngine/src/Scripting/ScriptGlue.bf +++ b/GlitchyEngine/src/Scripting/ScriptGlue.bf @@ -91,6 +91,7 @@ static class ScriptGlue RegisterComponent("GlitchyEngine.Physics.Rigidbody2D"); RegisterComponent("GlitchyEngine.Core.Camera"); RegisterComponent("GlitchyEngine.Graphics.CircleRenderer"); + RegisterComponent("GlitchyEngine.Graphics.TextRenderer"); } [RegisterMethod] @@ -996,6 +997,61 @@ static class ScriptGlue #endregion +#region TextRenderer + + [RegisterCall("ScriptGlue::TextRenderer_SetIsRichText")] + static bool TextRenderer_SetIsRichText(UUID entityId) + { + return GetComponentSafe(entityId).IsRichText; + } + + [RegisterCall("ScriptGlue::TextRenderer_SetIsRichText")] + static void TextRenderer_SetIsRichText(UUID entityId, bool isRichText) + { + TextRendererComponent* textComponent = GetComponentSafe(entityId); + + textComponent.IsRichText = isRichText; + textComponent.NeedsRebuild = true; + } + + [RegisterCall("ScriptGlue::TextRenderer_GetText")] + static void TextRenderer_GetText(UUID entityId, out MonoString* text) + { + TextRendererComponent* textComponent = GetComponentSafe(entityId); + + text = Mono.mono_string_new_len(ScriptEngine.[Friend]s_AppDomain, textComponent.Text.Ptr, (uint32)textComponent.Text.Length); + } + + [RegisterCall("ScriptGlue::TextRenderer_SetText")] + static void TextRenderer_SetText(UUID entityId, MonoString* text) + { + TextRendererComponent* textComponent = GetComponentSafe(entityId); + + char8* rawText = Mono.mono_string_to_utf8(text); + + textComponent.Text = StringView(rawText); + + textComponent.NeedsRebuild = true; + + Mono.mono_free(rawText); + } + + [RegisterCall("ScriptGlue::TextRenderer_GetColor")] + static void TextRenderer_GetColor(UUID entityId, out ColorRGBA color) + { + color = default; + + return; + } + + [RegisterCall("ScriptGlue::TextRenderer_SetColor")] + static void TextRenderer_SetColor(UUID entityId, ColorRGBA color) + { + return; + } + +#endregion + #region Math private static void RegisterMathFunctions() diff --git a/GlitchyEngine/src/World/Scene.bf b/GlitchyEngine/src/World/Scene.bf index f878110..20770df 100644 --- a/GlitchyEngine/src/World/Scene.bf +++ b/GlitchyEngine/src/World/Scene.bf @@ -799,10 +799,18 @@ namespace GlitchyEngine.World { if (textRenderer.NeedsRebuild || textRenderer.PreparedText == null) { - using (PreparedText preparedText = FontRenderer.PrepareText(_font, textRenderer.Text, 24, .Black)) + if (textRenderer.PreparedText == null) { - textRenderer.PreparedText = preparedText; + textRenderer.PreparedText = new FontRenderer.PreparedText(_font); } + else + { + textRenderer.PreparedText.AddRef(); + } + + FontRenderer.PrepareText(textRenderer.PreparedText, _font, textRenderer.Text, 24, .Black); + + textRenderer.PreparedText.ReleaseRef(); textRenderer.NeedsRebuild = false; } diff --git a/GlitchyEngine/src/World/SceneRenderer.bf b/GlitchyEngine/src/World/SceneRenderer.bf index 2553b16..918e812 100644 --- a/GlitchyEngine/src/World/SceneRenderer.bf +++ b/GlitchyEngine/src/World/SceneRenderer.bf @@ -118,15 +118,29 @@ class SceneRenderer // Sprite renderer Renderer2D.BeginScene(*primaryCamera, primaryCameraTransform, .BackToFront); - for (var (entity, transform, sprite) in Scene._ecsWorld.Enumerate()) + for (var (entity, transform, sprite, editorFlags) in Scene._ecsWorld.Enumerate()) { + if (editorFlags.Flags.HasFlag(.HideInScene)) + continue; + Renderer2D.DrawSprite(transform.WorldTransform, sprite, entity.Index); } - for (var (entity, transform, circle) in Scene._ecsWorld.Enumerate()) + for (var (entity, transform, circle, editorFlags) in Scene._ecsWorld.Enumerate()) { + if (editorFlags.Flags.HasFlag(.HideInScene)) + continue; + Renderer2D.DrawCircle(transform.WorldTransform, circle, entity.Index); } + + for (var (entity, transform, text, editorFlags) in Scene._ecsWorld.Enumerate()) + { + if (editorFlags.Flags.HasFlag(.HideInScene) || text.PreparedText == null) + continue; + + FontRenderer.DrawText(text.PreparedText, transform.WorldTransform * Matrix.Scaling(1.0f / 24.0f)); + } Renderer2D.EndScene(); diff --git a/ScriptCore/Graphics/TextRenderer.cs b/ScriptCore/Graphics/TextRenderer.cs new file mode 100644 index 0000000..851c786 --- /dev/null +++ b/ScriptCore/Graphics/TextRenderer.cs @@ -0,0 +1,49 @@ +using GlitchyEngine.Core; +using GlitchyEngine.Math; + +namespace GlitchyEngine.Graphics; + +/// +/// Renders text. +/// +public class TextRenderer : Component +{ + /// + /// Gets or sets whether the text is rich text.
+ /// If , the text will be parsed for rich text tags; if , the text will be rendered as plain text. + ///
+ public bool IsRichText + { + get => ScriptGlue.TextRenderer_GetIsRichText(_uuid); + set => ScriptGlue.TextRenderer_SetIsRichText(_uuid, value); + } + + /// + /// Gets or sets the text. + /// + public string Text + { + get + { + ScriptGlue.TextRenderer_GetText(_uuid, out string text); + return text; + } + set => ScriptGlue.TextRenderer_SetText(_uuid, value); + } + + /// + /// The color that will be used to render the text. This color can be overridden by rich text tags. + /// + public ColorRGBA Color + { + get + { + ScriptGlue.TextRenderer_GetColor(_uuid, out ColorRGBA color); + + return color; + } + set => ScriptGlue.TextRenderer_SetColor(_uuid, value); + } + + // TODO: PreparedText component +} diff --git a/ScriptCore/ScriptGlue.cs b/ScriptCore/ScriptGlue.cs index 7a26d4c..638a9ad 100644 --- a/ScriptCore/ScriptGlue.cs +++ b/ScriptCore/ScriptGlue.cs @@ -255,6 +255,28 @@ internal static class ScriptGlue #endregion +#region TextRenderer + + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern bool TextRenderer_GetIsRichText(UUID entityId); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern void TextRenderer_SetIsRichText(UUID entityId, bool isRichText); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern void TextRenderer_GetText(UUID entityId, out string text); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern void TextRenderer_SetText(UUID entityId, string text); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern void TextRenderer_GetColor(UUID entityId, out ColorRGBA color); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern void TextRenderer_SetColor(UUID entityId, ColorRGBA color); + +#endregion + #region Math [MethodImpl(MethodImplOptions.InternalCall)]