diff --git a/Runtime/Gizmos/DebugX.text.cs.meta b/Runtime/Gizmos/DebugX.text.cs.meta
deleted file mode 100644
index baa2bda..0000000
--- a/Runtime/Gizmos/DebugX.text.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: c465dc7f5c78c9549a063ecfc5e8326d
\ No newline at end of file
diff --git a/Runtime/Gizmos/Text.meta b/Runtime/Gizmos/Text.meta
new file mode 100644
index 0000000..22f0f23
--- /dev/null
+++ b/Runtime/Gizmos/Text.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 9709faba0386b5244a8ab4c9c7ce49ec
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Runtime/Gizmos/Text/DebugXTextSettings.cs b/Runtime/Gizmos/Text/DebugXTextSettings.cs
new file mode 100644
index 0000000..ba9a3d1
--- /dev/null
+++ b/Runtime/Gizmos/Text/DebugXTextSettings.cs
@@ -0,0 +1,98 @@
+using UnityEngine;
+
+namespace DCFApixels
+{
+ ///
+ /// All additional settings for text rendering are stored here.
+ ///
+ public readonly struct DebugXTextSettings
+ {
+ public const TextAnchor DEFAULT_TEXT_ANCHOR = TextAnchor.MiddleLeft;
+ public const int DEFAULT_FONT_SIZE = 16;
+
+ public const float SCREEN_SPACE_SCALE_FACTOR = 0f;
+ public const float WORLD_SPACE_SCALE_FACTOR = 1f;
+
+ public static readonly DebugXTextSettings Default = new DebugXTextSettings(DEFAULT_FONT_SIZE, DEFAULT_TEXT_ANCHOR, default, 0);
+ public static readonly DebugXTextSettings WorldSpaceScale = Default.SetWorldSpaceScaleFactor();
+
+ ///
+ /// Font size. Default is .
+ ///
+ public readonly int FontSize;
+
+ ///
+ /// Text alignment. Default is .
+ ///
+ public readonly TextAnchor TextAnchor;
+
+ public readonly Color BackgroundColor;
+ public readonly float WorldSpaceScaleFactor;
+
+ // ReSharper disable once UnusedMember.Global
+ public bool IsHasBackground => BackgroundColor.a > 0;
+
+ public DebugXTextSettings(int fontSize, TextAnchor textAnchor, Color backgroundColor, float worldSpaceScaleFactor)
+ {
+ FontSize = fontSize;
+ TextAnchor = textAnchor;
+ BackgroundColor = backgroundColor;
+ WorldSpaceScaleFactor = worldSpaceScaleFactor;
+ }
+
+ ///
+ /// Set font size. Default is .
+ ///
+ public DebugXTextSettings SetSize(int fontSize)
+ {
+ return new DebugXTextSettings(fontSize, TextAnchor, BackgroundColor, WorldSpaceScaleFactor);
+ }
+
+ ///
+ /// Sets text alignment. Default is .
+ ///
+ public DebugXTextSettings SetAnchor(TextAnchor textAnchor)
+ {
+ return new DebugXTextSettings(FontSize, textAnchor, BackgroundColor, WorldSpaceScaleFactor);
+ }
+
+ ///
+ /// Sets background image color behind text. Ignored if transparent.
+ ///
+ public DebugXTextSettings SetBackground(Color backgroundColor)
+ {
+ return new DebugXTextSettings(FontSize, TextAnchor, backgroundColor, WorldSpaceScaleFactor);
+ }
+
+ ///
+ /// Synchronizes the text scale in screen space. The text will remain the same size on the screen.
+ ///
+ // ReSharper disable once UnusedMember.Global
+ public DebugXTextSettings SetScreenSpaceScaleFactor()
+ {
+ return SetCustomSpaceScaleFactor(SCREEN_SPACE_SCALE_FACTOR);
+ }
+
+ ///
+ /// Synchronizes the text scale in world space. The text will remain the same size on the scene.
+ ///
+ public DebugXTextSettings SetWorldSpaceScaleFactor()
+ {
+ return SetCustomSpaceScaleFactor(WORLD_SPACE_SCALE_FACTOR);
+ }
+
+ ///
+ /// Allows you to control the text scale depending on the camera zoom.
+ ///
+ ///
+ ///
+ /// 0 - screen space
+ /// 1 - world space
+ /// Values in between [0.00 - 1.00] blend these spaces together.
+ ///
+ public DebugXTextSettings SetCustomSpaceScaleFactor(float factor)
+ {
+ return new DebugXTextSettings(FontSize, TextAnchor, BackgroundColor, factor);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Runtime/Utils/DebugXTextSettings.cs.meta b/Runtime/Gizmos/Text/DebugXTextSettings.cs.meta
similarity index 100%
rename from Runtime/Utils/DebugXTextSettings.cs.meta
rename to Runtime/Gizmos/Text/DebugXTextSettings.cs.meta
diff --git a/Runtime/Gizmos/Text/TextDrawHandlerExtensions.cs b/Runtime/Gizmos/Text/TextDrawHandlerExtensions.cs
new file mode 100644
index 0000000..272a8d4
--- /dev/null
+++ b/Runtime/Gizmos/Text/TextDrawHandlerExtensions.cs
@@ -0,0 +1,34 @@
+using System.Runtime.CompilerServices;
+using UnityEngine;
+
+namespace DCFApixels
+{
+ using DrawHandler = DebugX.DrawHandler;
+ using IN = MethodImplAttribute;
+
+ public static class TextDrawHandlerExtensions
+ {
+ private const MethodImplOptions LINE = DebugX.LINE;
+#if DEBUG
+ private static bool _singleWarningToggle = true;
+#endif
+ [IN(LINE)]
+ public static DrawHandler Text(this DrawHandler h, Vector3 position, object text) => h.Text(position, text, DebugXTextSettings.Default);
+ [IN(LINE)]
+ public static DrawHandler Text(this DrawHandler h, Vector3 position, object text, DebugXTextSettings settings)
+ {
+ if(settings.FontSize <= float.Epsilon)
+ {
+#if DEBUG
+ if (_singleWarningToggle)
+ {
+ Debug.LogWarning("Text rendering requires FontSize > 0, otherwise the text will be invisible. To avoid invalid parameters, use DebugXTextSettings.Default instead of manual instantiation.");
+ _singleWarningToggle = false;
+ }
+#endif
+ settings = settings.SetSize(DebugXTextSettings.DEFAULT_FONT_SIZE);
+ }
+ return h.Gizmo(new TextGizmo(position, text, settings));
+ }
+ }
+}
\ No newline at end of file
diff --git a/Runtime/Gizmos/Text/TextDrawHandlerExtensions.cs.meta b/Runtime/Gizmos/Text/TextDrawHandlerExtensions.cs.meta
new file mode 100644
index 0000000..73073c1
--- /dev/null
+++ b/Runtime/Gizmos/Text/TextDrawHandlerExtensions.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 16f0ca76a3494c91a2b0912101c88ddd
+timeCreated: 1740751022
\ No newline at end of file
diff --git a/Runtime/Gizmos/Text/TextGizmo.cs b/Runtime/Gizmos/Text/TextGizmo.cs
new file mode 100644
index 0000000..6c70b72
--- /dev/null
+++ b/Runtime/Gizmos/Text/TextGizmo.cs
@@ -0,0 +1,218 @@
+using DCFApixels.DebugXCore;
+using System.Runtime.CompilerServices;
+using UnityEditor;
+using UnityEngine;
+using UnityEngine.Rendering;
+
+namespace DCFApixels
+{
+ using IN = MethodImplAttribute;
+ internal readonly struct TextGizmo : IGizmo
+ {
+ private const MethodImplOptions LINE = MethodImplOptions.AggressiveInlining;
+ public readonly Vector3 Position;
+ public readonly string Text;
+ public readonly DebugXTextSettings Settings;
+ [IN(LINE)]
+ public TextGizmo(Vector3 position, object text, DebugXTextSettings settings)
+ {
+ Position = position;
+ Text = text.ToString();
+ Settings = settings;
+ }
+
+ public IGizmoRenderer RegisterNewRenderer() { return new Renderer(); }
+
+ #region Renderer
+ private class Renderer : IGizmoRenderer_PostRender
+ {
+ private static GUIStyle _labelStyle;
+ private static GUIContent _labelDummy;
+ private static Texture2D _whiteTexture;
+ public int ExecuteOrder => default(UnlitMat).GetExecuteOrder();
+ public bool IsStaticRender => false;
+ public void Prepare(Camera camera, GizmosList list) { }
+ public void Render(Camera camera, GizmosList list, CommandBuffer cb) { }
+ public void PostRender(Camera camera, GizmosList list)
+ {
+ if (camera == null) { return; }
+ if (Event.current.type != EventType.Repaint) { return; }
+ Color dfColor = GUI.color;
+ InitStatic();
+ bool isSceneView = false;
+ var backgroundMaterial = DebugXAssets.Materials.TextBackground;
+
+#if UNITY_EDITOR
+ // TODO: Might replace this with `camera != null && SceneView.GetAllSceneCameras().Any(x => x == camera)`;
+ // TODO: Not sure if older Unity-versions have GetAllSceneCameras or not.
+ isSceneView = camera.name == "SceneCamera";
+#endif
+
+ if (isSceneView)
+ {
+#if UNITY_EDITOR
+ Handles.BeginGUI();
+#endif
+ }
+ else
+ {
+ GL.PushMatrix();
+ GL.LoadPixelMatrix(0, Screen.width, Screen.height, 0);
+ }
+ foreach (ref readonly var item in list)
+ {
+ _labelDummy.text = item.Value.Text;
+ GUIStyle style = _labelStyle;
+
+ var zoom = GetCameraZoom(camera, item.Value.Position);
+ float fontSize = Mathf.Lerp(item.Value.Settings.FontSize, item.Value.Settings.FontSize / zoom, item.Value.Settings.WorldSpaceScaleFactor);
+ style.fontSize = Mathf.Max(1, Mathf.FloorToInt(fontSize));
+
+ style.alignment = item.Value.Settings.TextAnchor;
+ if (!(WorldToGUIPointWithDepth(camera, item.Value.Position).z < 0f))
+ {
+ Rect rect = WorldPointToSizedRect(camera, item.Value.Position, _labelDummy, _labelStyle);
+ if (item.Value.Settings.IsHasBackground)
+ {
+ Color backgroundColor = item.Value.Settings.BackgroundColor * DebugX.GlobalColor;
+ if(fontSize < 1)
+ {
+ backgroundColor.a *= fontSize;
+ }
+ Graphics.DrawTexture(rect, _whiteTexture, new Rect(0, 0, 1, 1), 0, 0, 0, 0, backgroundColor, backgroundMaterial, -1);
+ }
+ Color color= item.Color * DebugX.GlobalColor;
+ if (fontSize < 1)
+ {
+ color.a *= fontSize;
+ }
+ GUI.color = color;
+ style.Draw(rect, _labelDummy, false, false, false, false);
+ }
+ }
+ GUI.color = dfColor;
+ backgroundMaterial.SetColor(DebugX.ColorPropertyID, Color.white);
+
+ if (isSceneView)
+ {
+#if UNITY_EDITOR
+ Handles.EndGUI();
+#endif
+ }
+ else
+ {
+ GL.PopMatrix();
+ }
+ }
+
+ #region Init
+ private void InitStatic()
+ {
+ if (_labelStyle == null || _labelDummy == null || _whiteTexture == null)
+ {
+ GUIStyleState GenerateGUIStyleState()
+ {
+ var result = new GUIStyleState();
+ result.textColor = Color.white;
+ result.background = null;
+ return result;
+ }
+
+ // If calling GUI.skin directly - Unity will throw exceptions saying that GUI.skin can be called only from OnSceneGUI context.
+ GUISkin skin = (GUISkin)typeof(GUI).GetField("s_Skin", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static).GetValue(null); //GUI.s_Skin
+
+ _labelStyle = new GUIStyle(skin.label)
+ {
+ richText = false,
+ padding = new RectOffset(1, 1, 0, 0),
+ margin = new RectOffset(0, 0, 0, 0),
+ normal = GenerateGUIStyleState(),
+ active = GenerateGUIStyleState(),
+ hover = GenerateGUIStyleState(),
+ focused = GenerateGUIStyleState(),
+ };
+
+ _labelDummy = new GUIContent();
+
+ _whiteTexture = new Texture2D(2, 2);
+ Color32[] color = new Color32[]
+ {
+ Color.white,
+ Color.white,
+ Color.white,
+ Color.white,
+ };
+ _whiteTexture.SetPixels32(color);
+ _whiteTexture.Apply();
+ }
+ }
+ #endregion
+
+ #region Utils
+ public static Vector3 WorldToGUIPointWithDepth(Camera camera, Vector3 world)
+ {
+#if UNITY_EDITOR
+ world = Handles.matrix.MultiplyPoint(world);
+#endif
+ Vector3 vector = camera.WorldToScreenPoint(world);
+ vector.y = camera.pixelHeight - vector.y;
+ Vector2 vector2 = (Vector2)(vector);
+#if UNITY_EDITOR
+ vector2 = EditorGUIUtility.PixelsToPoints(vector);
+#endif
+ return new Vector3(vector2.x, vector2.y, vector.z);
+ }
+ public static Rect WorldPointToSizedRect(Camera camera, Vector3 position, GUIContent content, GUIStyle style)
+ {
+ Vector2 vector = (Vector2)WorldToGUIPointWithDepth(camera, position);
+ Vector2 vector2 = style.CalcSize(content);
+ Rect rect = new Rect(vector.x, vector.y, vector2.x, vector2.y);
+ switch (style.alignment)
+ {
+ case TextAnchor.UpperCenter:
+ rect.x -= rect.width * 0.5f;
+ break;
+ case TextAnchor.UpperRight:
+ rect.x -= rect.width;
+ break;
+ case TextAnchor.MiddleLeft:
+ rect.y -= rect.height * 0.5f;
+ break;
+ case TextAnchor.MiddleCenter:
+ rect.x -= rect.width * 0.5f;
+ rect.y -= rect.height * 0.5f;
+ break;
+ case TextAnchor.MiddleRight:
+ rect.x -= rect.width;
+ rect.y -= rect.height * 0.5f;
+ break;
+ case TextAnchor.LowerLeft:
+ rect.y -= rect.height;
+ break;
+ case TextAnchor.LowerCenter:
+ rect.x -= rect.width * 0.5f;
+ rect.y -= rect.height;
+ break;
+ case TextAnchor.LowerRight:
+ rect.x -= rect.width;
+ rect.y -= rect.height;
+ break;
+ }
+ return style.padding.Add(rect);
+ }
+ private static float GetCameraZoom(Camera camera, Vector3 position)
+ {
+ position = Handles.matrix.MultiplyPoint(position);
+ Transform cameraTransform = camera.transform;
+ Vector3 cameraPos = cameraTransform.position;
+ float z = Vector3.Dot(position - cameraPos, cameraTransform.TransformDirection(new Vector3(0f, 0f, 1f)));
+ Vector3 pos1 = camera.WorldToScreenPoint(cameraPos + cameraTransform.TransformDirection(new Vector3(0f, 0f, z)));
+ Vector3 pos2 = camera.WorldToScreenPoint(cameraPos + cameraTransform.TransformDirection(new Vector3(1f, 0f, z)));
+ float magnitude = (pos1 - pos2).magnitude;
+ return 80f / Mathf.Max(magnitude, 0.0001f) * EditorGUIUtility.pixelsPerPoint;
+ }
+ #endregion
+ }
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/Runtime/Gizmos/Text/TextGizmo.cs.meta b/Runtime/Gizmos/Text/TextGizmo.cs.meta
new file mode 100644
index 0000000..c28c895
--- /dev/null
+++ b/Runtime/Gizmos/Text/TextGizmo.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: af700d933ae340608724469c70e8d0fc
+timeCreated: 1740750866
\ No newline at end of file
diff --git a/Runtime/Utils/DebugXTextSettings.cs b/Runtime/Utils/DebugXTextSettings.cs
deleted file mode 100644
index fb5d9f8..0000000
--- a/Runtime/Utils/DebugXTextSettings.cs
+++ /dev/null
@@ -1,45 +0,0 @@
-using UnityEngine;
-
-namespace DCFApixels
-{
- public readonly struct DebugXTextSettings
- {
- public const TextAnchor DEFAULT_TEXT_ANCHOR = TextAnchor.MiddleLeft;
- public const int DEFAULT_FONT_SIZE = 16;
- public static readonly DebugXTextSettings Default = new DebugXTextSettings(DEFAULT_FONT_SIZE, DEFAULT_TEXT_ANCHOR, default, 0);
- public static readonly DebugXTextSettings WorldSpaceScale = Default.SetWorldSpaceScaleFactor(1f);
-
- public readonly int FontSize;
- public readonly TextAnchor TextAnchor;
- public readonly Color BackgroundColor;
- public readonly float WorldSpaceScaleFactor;
- public bool IsHasBackground
- {
- get { return BackgroundColor.a > 0; }
- }
- public DebugXTextSettings(int fontSize, TextAnchor textAnchor, Color backgroundColor, float worldSpaceScaleFactor)
- {
- FontSize = fontSize;
- TextAnchor = textAnchor;
- BackgroundColor = backgroundColor;
- WorldSpaceScaleFactor = worldSpaceScaleFactor;
- }
-
- public DebugXTextSettings SetSize(int fontSize)
- {
- return new DebugXTextSettings(fontSize, TextAnchor, BackgroundColor, WorldSpaceScaleFactor);
- }
- public DebugXTextSettings SetAnchor(TextAnchor textAnchor)
- {
- return new DebugXTextSettings(FontSize, textAnchor, BackgroundColor, WorldSpaceScaleFactor);
- }
- public DebugXTextSettings SetBackground(Color backgroundColor)
- {
- return new DebugXTextSettings(FontSize, TextAnchor, backgroundColor, WorldSpaceScaleFactor);
- }
- public DebugXTextSettings SetWorldSpaceScaleFactor(float factor)
- {
- return new DebugXTextSettings(FontSize, TextAnchor, BackgroundColor, factor);
- }
- }
-}
\ No newline at end of file