Merge pull request #2 from Demexis/dev_text

Дооформление части с текстом
This commit is contained in:
Mikhail 2025-03-05 18:53:03 +08:00 committed by GitHub
commit 2fe73ab98d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 364 additions and 288 deletions

View File

@ -1,241 +0,0 @@
using DCFApixels.DebugXCore;
#if UNITY_EDITOR
using UnityEditor;
#endif
using UnityEngine;
using UnityEngine.Rendering;
namespace DCFApixels
{
using IN = System.Runtime.CompilerServices.MethodImplAttribute;
public static partial class DebugX
{
public readonly partial struct DrawHandler
{
#region Text
[IN(LINE)] public DrawHandler Text(Vector3 position, object text) => Gizmo(new TextGizmo(position, text, DebugXTextSettings.Default));
[IN(LINE)] public DrawHandler Text(Vector3 position, object text, DebugXTextSettings settings) => Gizmo(new TextGizmo(position, text, settings));
private readonly struct TextGizmo : IGizmo<TextGizmo>
{
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<TextGizmo> RegisterNewRenderer() { return new Renderer(); }
#region Renderer
private class Renderer : IGizmoRenderer_PostRender<TextGizmo>
{
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<TextGizmo> list) { }
public void Render(Camera camera, GizmosList<TextGizmo> list, CommandBuffer cb) { }
public void PostRender(Camera camera, GizmosList<TextGizmo> 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 костыльный вариант, нужно поискать более надежный способ определить рендер SceneView
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);
style.fontSize = Mathf.FloorToInt(Mathf.Lerp(item.Value.Settings.FontSize, item.Value.Settings.FontSize / zoom, item.Value.Settings.WorldSpaceScaleFactor));
style.alignment = item.Value.Settings.TextAnchor;
if (!(WorldToGUIPointWithDepth(camera, item.Value.Position).z < 0f))
{
Rect rect = WorldPointToSizedRect(camera, item.Value.Position, _labelDummy, _labelStyle);
Color backgroundColor = item.Value.Settings.BackgroundColor * GlobalColor;
Graphics.DrawTexture(rect, _whiteTexture, new Rect(0, 0, 1, 1), 0, 0, 0, 0, backgroundColor, backgroundMaterial, -1);
GUI.color = item.Color * GlobalColor;
style.Draw(rect, _labelDummy, false, false, false, false);
}
}
GUI.color = dfColor;
backgroundMaterial.SetColor(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;
}
GUISkin skin = (GUISkin)typeof(GUI).GetField("s_Skin", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static).GetValue(null); //GUI.s_Skin
//GUISkin skin = GUI.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 transform = camera.transform;
Vector3 position2 = transform.position;
float z = Vector3.Dot(position - position2, transform.TransformDirection(new Vector3(0f, 0f, 1f)));
Vector3 vector = camera.WorldToScreenPoint(position2 + transform.TransformDirection(new Vector3(0f, 0f, z)));
Vector3 vector2 = camera.WorldToScreenPoint(position2 + transform.TransformDirection(new Vector3(1f, 0f, z)));
float magnitude = (vector - vector2).magnitude;
return 80f / Mathf.Max(magnitude, 0.0001f) * EditorGUIUtility.pixelsPerPoint;
//const float DEFAULT_ZOOM = 1f;
//
//if (camera != null)
//{
// return camera.orthographicSize;
//}
//return DEFAULT_ZOOM;
//var currentDrawingSceneView = SceneView.currentDrawingSceneView;
//
//if (currentDrawingSceneView == null)
//{
// return DEFAULT_ZOOM;
//}
//
//var localCamera = currentDrawingSceneView.camera;
//
//if (localCamera != null)
//{
// return localCamera.orthographicSize;
//}
//
//return DEFAULT_ZOOM;
}
#endregion
}
#endregion
}
#endregion
}
}
}

View File

@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: c465dc7f5c78c9549a063ecfc5e8326d

8
Runtime/Gizmos/Text.meta Normal file
View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 9709faba0386b5244a8ab4c9c7ce49ec
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,98 @@
using UnityEngine;
namespace DCFApixels
{
/// <summary>
/// All additional settings for text rendering are stored here.
/// </summary>
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();
/// <summary>
/// Font size. Default is <see cref="DEFAULT_FONT_SIZE" />.
/// </summary>
public readonly int FontSize;
/// <summary>
/// Text alignment. Default is <see cref="DEFAULT_TEXT_ANCHOR" />.
/// </summary>
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;
}
/// <summary>
/// Set font size. Default is <see cref="DEFAULT_FONT_SIZE" />.
/// </summary>
public DebugXTextSettings SetSize(int fontSize)
{
return new DebugXTextSettings(fontSize, TextAnchor, BackgroundColor, WorldSpaceScaleFactor);
}
/// <summary>
/// Sets text alignment. Default is <see cref="DEFAULT_TEXT_ANCHOR" />.
/// </summary>
public DebugXTextSettings SetAnchor(TextAnchor textAnchor)
{
return new DebugXTextSettings(FontSize, textAnchor, BackgroundColor, WorldSpaceScaleFactor);
}
/// <summary>
/// Sets background image color behind text. Ignored if transparent.
/// </summary>
public DebugXTextSettings SetBackground(Color backgroundColor)
{
return new DebugXTextSettings(FontSize, TextAnchor, backgroundColor, WorldSpaceScaleFactor);
}
/// <summary>
/// Synchronizes the text scale in screen space. The text will remain the same size on the screen.
/// </summary>
// ReSharper disable once UnusedMember.Global
public DebugXTextSettings SetScreenSpaceScaleFactor()
{
return SetCustomSpaceScaleFactor(SCREEN_SPACE_SCALE_FACTOR);
}
/// <summary>
/// Synchronizes the text scale in world space. The text will remain the same size on the scene.
/// </summary>
public DebugXTextSettings SetWorldSpaceScaleFactor()
{
return SetCustomSpaceScaleFactor(WORLD_SPACE_SCALE_FACTOR);
}
/// <summary>
/// Allows you to control the text scale depending on the camera zoom.
/// </summary>
/// <param name="factor">
/// <br />
/// 0 - screen space<br />
/// 1 - world space<br />
/// Values in between [0.00 - 1.00] blend these spaces together.
/// </param>
public DebugXTextSettings SetCustomSpaceScaleFactor(float factor)
{
return new DebugXTextSettings(FontSize, TextAnchor, BackgroundColor, factor);
}
}
}

View File

@ -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));
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 16f0ca76a3494c91a2b0912101c88ddd
timeCreated: 1740751022

View File

@ -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<TextGizmo>
{
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<TextGizmo> RegisterNewRenderer() { return new Renderer(); }
#region Renderer
private class Renderer : IGizmoRenderer_PostRender<TextGizmo>
{
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<TextGizmo> list) { }
public void Render(Camera camera, GizmosList<TextGizmo> list, CommandBuffer cb) { }
public void PostRender(Camera camera, GizmosList<TextGizmo> 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
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: af700d933ae340608724469c70e8d0fc
timeCreated: 1740750866

View File

@ -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);
}
}
}