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 /// /// Draw text at the world position. /// Can pass any object where ToString() will be called. /// /// World position. /// String or any other object. [IN(LINE)] public DrawHandler Text(Vector3 position, object text) => Gizmo(new TextGizmo(new TextBuilder(position, text))); /// /// Draw text at the world position. /// Can pass any object where ToString() will be called. /// /// World position. /// String or any other object. /// Text font size. [IN(LINE)] public DrawHandler Text(Vector3 position, object text, int fontSize) => Gizmo(new TextGizmo(new TextBuilder(position, text, fontSize))); /// /// Draw text at the world position. /// Can pass any object where ToString() will be called. /// /// World position. /// String or any other object. /// Text font size. /// Text alignment. [IN(LINE)] public DrawHandler Text(Vector3 position, object text, int fontSize, TextAnchor textAnchor) => Gizmo(new TextGizmo(new TextBuilder(position, text, fontSize, textAnchor))); /// /// Use text builder to pass more details about how text-gizmo should be drawn. /// /// Settings with a builder pattern. [IN(LINE)] public DrawHandler Text(TextBuilder textBuilder) => Gizmo(new TextGizmo(textBuilder)); private readonly struct TextGizmo : IGizmo { private const int BACKGROUND_TEXTURE_WIDTH = 2; private const int BACKGROUND_TEXTURE_HEIGHT = 2; private const int BACKGROUND_TEXTURE_PIXELS_COUNT = BACKGROUND_TEXTURE_WIDTH * BACKGROUND_TEXTURE_HEIGHT; // TODO: Normally Texture2D should be destroyed when not needed anymore. Though it will live through entire app lifetime. What about editor? private static Texture2D _backgroundTexture; private static Color32[] _backgroundTexturePixels; public readonly TextBuilder TextBuilderInstance; [IN(LINE)] public TextGizmo(TextBuilder textBuilder) { TextBuilderInstance = textBuilder; CheckTextureInstance(); } /// /// Texture should be set once after the app domain is cleared. /// private void CheckTextureInstance() { if (_backgroundTexture != null) { return; } _backgroundTexture = new Texture2D(BACKGROUND_TEXTURE_WIDTH, BACKGROUND_TEXTURE_HEIGHT); _backgroundTexturePixels = new Color32[BACKGROUND_TEXTURE_PIXELS_COUNT]; _backgroundTexture.SetPixels32(_backgroundTexturePixels); _backgroundTexture.Apply(); } public IGizmoRenderer RegisterNewRenderer() { return new Renderer(); } #region Renderer private class Renderer : IGizmoRenderer_UnityGizmos { private static GUIStyle _labelStyle; private static GUIContent _labelDummy; 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 Render_UnityGizmos(Camera camera, GizmosList list) { #if UNITY_EDITOR Color defaultColor = GUI.color; if (_labelStyle == null || _labelDummy == null) { _labelStyle = new GUIStyle(GUI.skin.label) { richText = false }; _labelDummy = new GUIContent(); } if (list.Count == 0) { return; } var zoom = GetCameraZoom(); Handles.BeginGUI(); foreach (ref readonly var item in list) { GUI.color = item.Color * GlobalColor; _labelDummy.text = item.Value.TextBuilderInstance.Text.ToString(); _labelStyle.fontSize = item.Value.TextBuilderInstance.UseWorldScale ? Mathf.FloorToInt(item.Value.TextBuilderInstance.FontSize / zoom) : item.Value.TextBuilderInstance.FontSize; _labelStyle.alignment = item.Value.TextBuilderInstance.TextAnchor; _labelStyle.normal = new GUIStyleState { textColor = item.Color * GlobalColor, }; if (item.Value.TextBuilderInstance.UseBackground) { for (int i = 0; i < BACKGROUND_TEXTURE_PIXELS_COUNT; i++) { _backgroundTexturePixels[i] = item.Value.TextBuilderInstance.BackgroundColor; } _backgroundTexture.SetPixels32(_backgroundTexturePixels); _backgroundTexture.Apply(); _labelStyle.normal.background = _backgroundTexture; } if (!(HandleUtility.WorldToGUIPointWithDepth(item.Value.TextBuilderInstance.Position).z < 0f)) { GUI.Label(HandleUtility.WorldPointToSizedRect(item.Value.TextBuilderInstance.Position, _labelDummy, _labelStyle), _labelDummy, _labelStyle); } } Handles.EndGUI(); GUI.color = defaultColor; float GetCameraZoom() { const float DEFAULT_ZOOM = 1f; if (camera != null) { return camera.orthographicSize; } var currentDrawingSceneView = SceneView.currentDrawingSceneView; if (currentDrawingSceneView == null) { return DEFAULT_ZOOM; } var localCamera = currentDrawingSceneView.camera; if (localCamera != null) { return localCamera.orthographicSize; } return DEFAULT_ZOOM; } #endif } } #endregion } #endregion #region TextBuilder /// /// Set text gizmos instance settings using a builder pattern. /// public struct TextBuilder { private const TextAnchor DEFAULT_TEXT_ANCHOR = TextAnchor.MiddleLeft; private const int DEFAULT_FONT_SIZE = 16; /// /// Text world position. /// public Vector3 Position { get; set; } /// /// Text. Uses ToString() of the passed object. /// public object Text { get; set; } /// /// Font size. Default is . /// public int FontSize { get; set; } /// /// Text alignment. Default is . /// public TextAnchor TextAnchor { get; set; } /// /// Background texture color. /// public Color BackgroundColor { get; set; } /// /// Flag to use background texture and background color when rendering text gizmo instance. /// public bool UseBackground { get; set; } /// /// If set true - camera zooming will affect text scale to keep same size in the world. /// public bool UseWorldScale { get; set; } public TextBuilder(Vector3 position, object text, int fontSize = DEFAULT_FONT_SIZE, TextAnchor textAnchor = DEFAULT_TEXT_ANCHOR) : this() { Position = position; Text = text; FontSize = fontSize; TextAnchor = textAnchor; } public TextBuilder SetPosition(Vector3 position) { Position = position; return this; } public TextBuilder SetText(object text) { Text = text; return this; } public TextBuilder SetFontSize(int fontSize) { FontSize = fontSize; return this; } public TextBuilder SetTextAnchor(TextAnchor textAnchor) { TextAnchor = textAnchor; return this; } public TextBuilder SetBackground(Color backgroundColor) { UseBackground = true; BackgroundColor = backgroundColor; return this; } public TextBuilder RemoveBackground() { UseBackground = false; return this; } public TextBuilder SetWorldScaling() { UseWorldScale = true; return this; } public TextBuilder RemoveWorldScaling() { UseWorldScale = false; return this; } } #endregion } } }