mirror of
https://github.com/aharabada/glitchy-engine-beef.git
synced 2026-09-05 13:01:52 +00:00
Start of text renderer component
- UC, LC, IC, RC tags
This commit is contained in:
@@ -74,7 +74,11 @@ float4 PS(PS_Input input) : SV_Target0
|
||||
float screenPxDistance = ScreenPxRange(input.TexCoord) * (sd - 0.5);
|
||||
float opacity = clamp(screenPxDistance + 0.5, 0.0, 1.0);
|
||||
|
||||
return float4(input.Color.rgb, opacity * input.Color.a);
|
||||
clip(opacity - 0.001f);
|
||||
|
||||
//return float4(input.Color.rgb, opacity * input.Color.a) * 0.0001f + float4(opacity.xxxx);
|
||||
return input.Color * opacity; // TODO: This is not quite right // float4(input.Color.rgb, input.Color.a * opacity);
|
||||
//return float4(input.Color.rgb, opacity * input.Color.a) * 0.0001f + float4(msd, 1.0f);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -9,6 +9,7 @@ using GlitchyEngine.Content;
|
||||
using GlitchyEngine.Scripting;
|
||||
using GlitchyEngine.Core;
|
||||
using Mono;
|
||||
using GlitchyEngine.World.Components;
|
||||
|
||||
namespace GlitchyEditor.EditWindows
|
||||
{
|
||||
@@ -81,9 +82,12 @@ namespace GlitchyEditor.EditWindows
|
||||
|
||||
ShowComponentEditor<SpriteRendererComponent>("Sprite Renderer", entity, => ShowSpriteRendererComponentEditor, => ShowComponentContextMenu<SpriteRendererComponent>);
|
||||
ShowComponentEditor<CircleRendererComponent>("Circle Renderer", entity, => ShowCircleRendererComponentEditor, => ShowComponentContextMenu<CircleRendererComponent>);
|
||||
ShowComponentEditor<TextRendererComponent>("Text Renderer", entity, => ShowTextRendererComponentEditor, => ShowComponentContextMenu<TextRendererComponent>);
|
||||
|
||||
ShowComponentEditor<MeshRendererComponent>("Mesh Renderer", entity, => ShowMeshRendererComponentEditor, => ShowComponentContextMenu<MeshRendererComponent>);
|
||||
ShowComponentEditor<LightComponent>("Light", entity, => ShowLightComponentEditor, => ShowComponentContextMenu<LightComponent>);
|
||||
ShowComponentEditor<MeshComponent>("Mesh", entity, => ShowMeshComponentEditor, => ShowComponentContextMenu<MeshComponent>);
|
||||
|
||||
ShowComponentEditor<Rigidbody2DComponent>("Rigidbody 2D", entity, => ShowRigidBody2DComponentEditor, => ShowComponentContextMenu<Rigidbody2DComponent>);
|
||||
ShowComponentEditor<BoxCollider2DComponent>("Box collider 2D", entity, => ShowBoxCollider2DComponentEditor, => ShowComponentContextMenu<BoxCollider2DComponent>);
|
||||
ShowComponentEditor<CircleCollider2DComponent>("Circle collider 2D", entity, => ShowCircleCollider2DComponentEditor, => ShowComponentContextMenu<CircleCollider2DComponent>);
|
||||
@@ -454,6 +458,49 @@ namespace GlitchyEditor.EditWindows
|
||||
ImGui.DragFloat("##Inner Radius", &circleRendererComponent.InnerRadius, 0.1f, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
static int InputTextCallback(ImGui.InputTextCallbackData* data)
|
||||
{
|
||||
if (data.EventFlag == .CallbackResize)
|
||||
{
|
||||
String str = (String)Internal.UnsafeCastToObject(data.UserData);
|
||||
|
||||
Log.EngineLogger.AssertDebug(str.Ptr == data.Buf);
|
||||
|
||||
str.PadRight(data.BufTextLen * 2, '\0');
|
||||
data.Buf = str.Ptr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static void ShowTextRendererComponentEditor(Entity entity, TextRendererComponent* textRendererComponent)
|
||||
{
|
||||
StartNewProperty("Text");
|
||||
|
||||
String text = textRendererComponent.[Friend]_text;
|
||||
|
||||
if (text == null)
|
||||
{
|
||||
text = textRendererComponent.[Friend]_text = new String();
|
||||
}
|
||||
|
||||
text?.EnsureNullTerminator();
|
||||
|
||||
if (ImGui.InputTextMultiline("##text", text.Ptr, (uint64)text.Length, .Zero, .CallbackResize, => InputTextCallback, Internal.UnsafeCastToPtr(text)))
|
||||
{
|
||||
// To deleting text, ImGui simply puts a null char after the remaining text.
|
||||
// Search for null char and set the length of the string accordingly.
|
||||
int length = text.IndexOf('\0');
|
||||
|
||||
if (length >= 0)
|
||||
{
|
||||
text.Length = length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void ShowMeshRendererComponentEditor(Entity entity, MeshRendererComponent* meshRendererComponent)
|
||||
{
|
||||
StartNewProperty("Material");
|
||||
@@ -844,6 +891,7 @@ namespace GlitchyEditor.EditWindows
|
||||
ShowComponentButton<CameraComponent>("Camera");
|
||||
ShowComponentButton<SpriteRendererComponent>("Sprite Renderer");
|
||||
ShowComponentButton<CircleRendererComponent>("Circle Renderer");
|
||||
ShowComponentButton<TextRendererComponent>("Text Renderer");
|
||||
ShowComponentButton<LightComponent>("Light");
|
||||
ShowComponentButton<Rigidbody2DComponent>("Rigidbody 2D");
|
||||
ShowComponentButton<BoxCollider2DComponent>("Box collider 2D");
|
||||
|
||||
@@ -5,6 +5,7 @@ using GlitchyEngine.Math;
|
||||
using System.Collections;
|
||||
using GlitchyEngine.Core;
|
||||
using GlitchyEngine.Content;
|
||||
using System.Text;
|
||||
using static FreeType.HarfBuzz;
|
||||
|
||||
using internal GlitchyEngine.Renderer.Text;
|
||||
@@ -126,7 +127,15 @@ namespace GlitchyEngine.Renderer.Text
|
||||
case BottomToTop;
|
||||
}
|
||||
|
||||
public static PreparedText PrepareText(Font font, String text, float fontSize, Color fontColor = .White, Color bitmapColor = .White, float lineSpaceScale = 1.0f, TextDirection direction = .LeftToRight)
|
||||
public enum TextCase
|
||||
{
|
||||
case RetainCase;
|
||||
case UpperCase;
|
||||
case LowerCase;
|
||||
case InvertCase;
|
||||
}
|
||||
|
||||
public static PreparedText PrepareText(Font font, StringView text, float fontSize, Color fontColor = .White, Color bitmapColor = .White, float lineSpaceScale = 1.0f, TextDirection direction = .LeftToRight)
|
||||
{
|
||||
Debug.Profiler.ProfileRendererFunction!();
|
||||
|
||||
@@ -143,17 +152,18 @@ namespace GlitchyEngine.Renderer.Text
|
||||
// Position of the next character on the line
|
||||
float penPosition = 0;
|
||||
|
||||
// TODO: use stringview?
|
||||
StringView textBuffer = .(text, 0, 0);//new:ScopedAlloc! String();
|
||||
List<char32> chars = new:ScopedAlloc! .(text.Length);
|
||||
int runStart = 0;
|
||||
|
||||
hb_buffer_t* buf = hb_buffer_create();
|
||||
|
||||
//PreparedLine currentLine = new PreparedLine();
|
||||
|
||||
// Stack to keep track of the text direction.
|
||||
List<TextDirection> textDirectionStack = scope .();
|
||||
textDirectionStack.Add(direction);
|
||||
|
||||
List<TextCase> textCaseStack = scope .();
|
||||
textCaseStack.Add(.RetainCase);
|
||||
|
||||
int movedLines = 0;
|
||||
|
||||
Font currentFont = font;
|
||||
@@ -168,10 +178,9 @@ namespace GlitchyEngine.Renderer.Text
|
||||
|
||||
hb_buffer_clear_contents(buf);
|
||||
|
||||
hb_buffer_add_utf8(buf, text.Ptr, (.)text.Length, (.)(textBuffer.Ptr - text.Ptr), (.)textBuffer.Length);
|
||||
hb_buffer_add_utf32(buf, chars.Ptr, (.)chars.Count, (.)runStart, (.)(chars.Count - runStart));
|
||||
|
||||
textBuffer.Ptr += textBuffer.Length;
|
||||
textBuffer.Length = 0;
|
||||
runStart = chars.Count;
|
||||
|
||||
// Set the script, language and direction of the buffer.
|
||||
// TODO: change direction, script, etc.
|
||||
@@ -226,6 +235,8 @@ namespace GlitchyEngine.Renderer.Text
|
||||
penPosition = 0;
|
||||
}
|
||||
|
||||
bool escapeNextChar = false;
|
||||
|
||||
for (char32 char in text.DecodedChars)
|
||||
{
|
||||
defer
|
||||
@@ -233,16 +244,21 @@ namespace GlitchyEngine.Renderer.Text
|
||||
currentIndex = @char.NextIndex;
|
||||
}
|
||||
|
||||
switch(char)
|
||||
void DoLineBreak()
|
||||
{
|
||||
case '\n':
|
||||
chars.Add('\n');
|
||||
// Only flush after the first new line
|
||||
if (movedLines == 0)
|
||||
FlushShapeBuffer();
|
||||
|
||||
// carriage return
|
||||
movedLines++;
|
||||
}
|
||||
|
||||
switch(char)
|
||||
{
|
||||
case '\n':
|
||||
DoLineBreak();
|
||||
continue;
|
||||
case '\u{240}':
|
||||
FlushShapeBuffer();
|
||||
@@ -274,6 +290,107 @@ namespace GlitchyEngine.Renderer.Text
|
||||
textDirectionStack.PopBack();
|
||||
|
||||
continue;
|
||||
case '\\':
|
||||
if (escapeNextChar)
|
||||
break;
|
||||
else
|
||||
{
|
||||
escapeNextChar = true;
|
||||
continue;
|
||||
}
|
||||
case '<':
|
||||
if (escapeNextChar)
|
||||
break;
|
||||
|
||||
// Copy the char enumerator to make a lookahead
|
||||
String.UTF8Enumerator tagEnumerator = @char;
|
||||
|
||||
String tagText = scope .();
|
||||
|
||||
int endIndex = -1;
|
||||
|
||||
for (char32 tagChar in tagEnumerator)
|
||||
{
|
||||
if (tagChar == '>')
|
||||
{
|
||||
endIndex = tagEnumerator.NextIndex;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
UTF32.Decode(Span<char32>(&tagChar, 1), tagText);
|
||||
}
|
||||
}
|
||||
|
||||
// It seems that it's just an unescaped <
|
||||
if (endIndex == -1)
|
||||
break; // TODO: Probably warn or something
|
||||
|
||||
bool isEndTag = tagText.StartsWith('/');
|
||||
|
||||
if (isEndTag)
|
||||
tagText.Remove(0);
|
||||
|
||||
switch(tagText)
|
||||
{
|
||||
case "b", "bold":
|
||||
// bold
|
||||
case "br", "linebreak":
|
||||
DoLineBreak();
|
||||
case "u", "underline":
|
||||
// underlined
|
||||
case "i", "italic":
|
||||
// italic
|
||||
case "small":
|
||||
case "sub", "subscript":
|
||||
case "sup", "superscript":
|
||||
case "s", "strikethrough":
|
||||
|
||||
// Text case control
|
||||
case "lc", "lowercase":
|
||||
if (isEndTag && textCaseStack.Count > 0)
|
||||
textCaseStack.PopBack();
|
||||
else
|
||||
textCaseStack.Add(.LowerCase);
|
||||
case "uc", "uppercase":
|
||||
if (isEndTag && textCaseStack.Count > 0)
|
||||
textCaseStack.PopBack();
|
||||
else
|
||||
textCaseStack.Add(.UpperCase);
|
||||
case "rc", "retaincase":
|
||||
if (isEndTag && textCaseStack.Count > 0)
|
||||
textCaseStack.PopBack();
|
||||
else
|
||||
textCaseStack.Add(.RetainCase);
|
||||
case "ic", "invertcase":
|
||||
if (isEndTag && textCaseStack.Count > 0)
|
||||
textCaseStack.PopBack();
|
||||
else
|
||||
textCaseStack.Add(.InvertCase);
|
||||
|
||||
}
|
||||
|
||||
// Skip the tag in the main enumerator
|
||||
@char.NextIndex = endIndex;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
escapeNextChar = false;
|
||||
|
||||
switch (textCaseStack.Back)
|
||||
{
|
||||
case .RetainCase:
|
||||
// Nothing to do
|
||||
case .LowerCase:
|
||||
char = char.ToLower;
|
||||
case .UpperCase:
|
||||
char = char.ToUpper;
|
||||
case .InvertCase:
|
||||
if (char.IsUpper)
|
||||
char = char.ToLower;
|
||||
else if (char.IsLower)
|
||||
char = char.ToUpper;
|
||||
}
|
||||
|
||||
// Get the font that can draw the char. Use the given font if no fallback is found (will draw the "missing glyph").
|
||||
@@ -299,12 +416,7 @@ namespace GlitchyEngine.Renderer.Text
|
||||
movedLines = 0;
|
||||
}
|
||||
|
||||
//textBuffer.Append(char);
|
||||
if(textBuffer.Length == 0)
|
||||
{
|
||||
textBuffer.Ptr = text.Ptr + currentIndex;
|
||||
}
|
||||
textBuffer.Length += @char.NextIndex - currentIndex;
|
||||
chars.Add(char);
|
||||
}
|
||||
|
||||
FlushShapeBuffer();
|
||||
@@ -336,7 +448,7 @@ namespace GlitchyEngine.Renderer.Text
|
||||
Renderer2D.[Friend]s_currentQuadEffect = _msdfEffect;
|
||||
// TODO: oh no....
|
||||
// Copy viewProjection from current effect
|
||||
Matrix viewProjection = lastEffect.Variables["ViewProjection"].[Friend]GetData<Matrix>();
|
||||
Matrix viewProjection = Renderer2D.[Friend]s_quadBatchEffect.Variables["ViewProjection"].[Friend]GetData<Matrix>();
|
||||
_msdfEffect.Variables["ViewProjection"].SetData(viewProjection);
|
||||
|
||||
// TODO: this doesn't really work with fallback fonts
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
using System;
|
||||
namespace GlitchyEngine.World.Components;
|
||||
|
||||
struct TextRendererComponent : IDisposableComponent
|
||||
{
|
||||
private String _text;
|
||||
|
||||
private bool _isRichText;
|
||||
|
||||
public StringView Text
|
||||
{
|
||||
get => _text;
|
||||
set mut
|
||||
{
|
||||
if (_text == null)
|
||||
_text = new String();
|
||||
|
||||
_text.Set(value);
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsRichText
|
||||
{
|
||||
get => _isRichText;
|
||||
set mut => _isRichText = value;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
delete _text;
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ using GlitchyEngine.Content;
|
||||
using GlitchyEngine.Math;
|
||||
using System;
|
||||
using GlitchyEngine.World.Components;
|
||||
using GlitchyEngine.Renderer.Text;
|
||||
|
||||
namespace GlitchyEngine.World;
|
||||
|
||||
@@ -24,6 +25,10 @@ class SceneRenderer
|
||||
|
||||
public RenderTargetGroup CompositeTarget => _compositeTarget;
|
||||
|
||||
Font _font ~ _.ReleaseRef();
|
||||
|
||||
FontRenderer.PreparedText _smallLinesInfo;
|
||||
|
||||
public this()
|
||||
{
|
||||
RenderTargetGroupDescription desc = .(100, 100,
|
||||
@@ -47,6 +52,8 @@ class SceneRenderer
|
||||
_cameraTarget.[Friend]Identifier = "Camera Target";
|
||||
|
||||
_gammaCorrectEffect = Content.LoadAsset("Resources/Shaders/GammaCorrect.hlsl");
|
||||
|
||||
_font = new Font(@"C:\Windows\Fonts\arial.ttf", 24);
|
||||
}
|
||||
|
||||
/// Sets the size of the viewport into which the scene will be rendered.
|
||||
@@ -205,6 +212,20 @@ class SceneRenderer
|
||||
Renderer2D.DrawCircle(transform.WorldTransform, circle, entity.Index);
|
||||
}
|
||||
|
||||
for (var (entity, transform, text, editorFlags) in Scene._ecsWorld.Enumerate<TransformComponent, TextRendererComponent, EditorFlagsComponent>())
|
||||
{
|
||||
if (editorFlags.Flags.HasFlag(.HideInScene))
|
||||
continue;
|
||||
|
||||
_smallLinesInfo = FontRenderer.PrepareText(_font, text.Text, 24, .Black);
|
||||
|
||||
FontRenderer.DrawText(_smallLinesInfo, transform.WorldTransform * Matrix.Scaling(1.0f / 24.0f));
|
||||
|
||||
_smallLinesInfo.ReleaseRef();
|
||||
}
|
||||
|
||||
//FontRenderer.DrawText(_smallLinesInfo, 0, 0);
|
||||
|
||||
Renderer2D.EndScene();
|
||||
|
||||
Renderer2D.BeginScene(camera, .BackToFront);
|
||||
|
||||
@@ -249,6 +249,12 @@ class SceneSerializer
|
||||
{
|
||||
Serialize.Value(writer, "Flags", component.Flags);
|
||||
});
|
||||
|
||||
SerializeComponent<TextRendererComponent>(writer, entity, "TextRenderer", scope (component) =>
|
||||
{
|
||||
Serialize.Value(writer, "IsRichText", component.IsRichText);
|
||||
Serialize.Value(writer, "Text", component.Text);
|
||||
});
|
||||
}
|
||||
|
||||
writer.EntryEnd();
|
||||
@@ -671,6 +677,27 @@ class SceneSerializer
|
||||
{
|
||||
Try!(Deserialize.Value(reader, "Flags", out component.Flags));
|
||||
|
||||
return .Ok;
|
||||
}));
|
||||
case "TextRenderer":
|
||||
Try!(DeserializeComponent<TextRendererComponent>(reader, entity, scope (component) =>
|
||||
{
|
||||
Try!(Deserialize.Value<bool>(reader, "IsRichText", let isRichText));
|
||||
component.IsRichText = isRichText;
|
||||
|
||||
Try!(reader.EntryEnd());
|
||||
|
||||
String text = null;
|
||||
|
||||
Try!(Deserialize.Value<String>(reader, "Text", out text));
|
||||
|
||||
if (text != null)
|
||||
{
|
||||
component.Text = text;
|
||||
|
||||
delete text;
|
||||
}
|
||||
|
||||
return .Ok;
|
||||
}));
|
||||
default:
|
||||
|
||||
Reference in New Issue
Block a user