LMQT/Assets/TouchScript/Scripts/Debugging/GL/GLDebug.cs
2024-12-10 09:03:45 +08:00

671 lines
26 KiB
C#

/*
* @author Valentin Simonov / http://va.lent.in/
* Based on http://pastebin.com/69QP1s45
*/
#if TOUCHSCRIPT_DEBUG
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
#if UNITY_EDITOR
using UnityEditor;
using UnityEditor.Build;
#endif
namespace TouchScript.Debugging.GL
{
public class GLDebug : MonoBehaviour
{
public static readonly Color MULTIPLY = new Color(0, 0, 0, 0);
public static readonly Vector2 DEFAULT_SCREEN_SPACE_SCALE = new Vector2(10, 10);
private static GLDebug instance
{
get
{
if (!_instance && Application.isPlaying)
{
if (Camera.main)
{
_instance = Camera.main.gameObject.AddComponent<GLDebug>();
}
else
{
var go = new GameObject("GLDebug");
var camera = go.AddComponent<Camera>();
camera.clearFlags = CameraClearFlags.Nothing;
camera.depth = 9000;
_instance = go.AddComponent<GLDebug>();
}
}
return _instance;
}
}
public KeyCode ToggleKey;
public bool DisplayLines = true;
private static GLDebug _instance;
private static int nextFigureId = 1;
private Material materialDepthTest;
private Material materialNoDepthTest;
private Material materialMultiplyDepthTest;
private Material materialMultiplyNoDepthTest;
private Dictionary<int, Figure> figuresDepthTest;
private Dictionary<int, Figure> figuresMultiplyDepthTest;
private Dictionary<int, Figure> figuresNoDepthTest;
private Dictionary<int, Figure> figuresMultiplyNoDepthTest;
private Dictionary<int, Figure> figuresScreenSpace;
private Dictionary<int, Figure> figuresMultiplyScreenSpace;
private Dictionary<int, Figure> figuresTmp;
private WaitForEndOfFrame wait;
#region Public methods
public static void RemoveFigure(int id)
{
instance.figuresDepthTest.Remove(id);
instance.figuresNoDepthTest.Remove(id);
instance.figuresScreenSpace.Remove(id);
instance.figuresMultiplyDepthTest.Remove(id);
instance.figuresMultiplyNoDepthTest.Remove(id);
instance.figuresMultiplyScreenSpace.Remove(id);
}
#region Line
public static int DrawLine(Vector3 start, Vector3 end, Color? color = null, float duration = 0, bool depthTest = false)
{
return DrawLine(null, start, end, color, duration, depthTest);
}
public static int DrawLine(int? id, Vector3 start, Vector3 end, Color? color = null, float duration = 0, bool depthTest = false)
{
return drawFigure(id, new List<Line>() {new Line(start, end)}, color ?? Color.white, duration, depthTest);
}
public static int DrawLineScreenSpace(Vector2 start, Vector2 end, Color? color = null, float duration = 0)
{
return DrawLineScreenSpace(null, start, end, color, duration);
}
public static int DrawLineScreenSpace(int? id, Vector2 start, Vector2 end, Color? color = null, float duration = 0)
{
return drawFigureScreenSpace(id, new List<Line>() {new Line(start, end)}, color ?? Color.white, duration);
}
#endregion
#region Ray
public static int DrawRay(Vector3 start, Vector3 dir, Color? color = null, float duration = 0, bool depthTest = false)
{
return DrawRay(null, start, dir, color, duration, depthTest);
}
public static int DrawRay(int? id, Vector3 start, Vector3 dir, Color? color = null, float duration = 0, bool depthTest = false)
{
if (dir == Vector3.zero)
return 0;
return DrawLine(start, start + dir, color, duration, depthTest);
}
#endregion
#region Cross
public static int DrawCross(Vector3 pos, Quaternion? rot = null, Vector3? scale = null, Color? color = null, float duration = 0, bool depthTest = false)
{
return DrawCross(null, Matrix4x4.TRS(pos, rot ?? Quaternion.identity, scale ?? Vector3.one), color, duration, depthTest);
}
public static int DrawCross(int? id, Vector3 pos, Quaternion? rot = null, Vector3? scale = null, Color? color = null, float duration = 0, bool depthTest = false)
{
return DrawCross(id, Matrix4x4.TRS(pos, rot ?? Quaternion.identity, scale ?? Vector3.one), color, duration, depthTest);
}
public static int DrawCross(Matrix4x4 matrix, Color? color = null, float duration = 0, bool depthTest = false)
{
return DrawCross(null, matrix, color, duration, depthTest);
}
public static int DrawCross(int? id, Matrix4x4 matrix, Color? color = null, float duration = 0, bool depthTest = false)
{
return drawFigure(id, createCrossLines(matrix), color ?? Color.white, duration, depthTest);
}
public static int DrawCrossScreenSpace(Vector2 pos, float rot = 0, Vector2? scale = null, Color? color = null, float duration = 0)
{
return DrawCrossScreenSpace(null, pos, rot, scale, color, duration);
}
public static int DrawCrossScreenSpace(int? id, Vector2 pos, float rot = 0, Vector2? scale = null, Color? color = null, float duration = 0)
{
return drawFigureScreenSpace(id, createScreenSpaceCrossLines(pos, rot, scale ?? DEFAULT_SCREEN_SPACE_SCALE), color ?? Color.white, duration);
}
#endregion
#region Arrow
public static int DrawArrow(Vector3 start, Vector3 end, float arrowHeadLength = 0.25f, float arrowHeadAngle = 20, Color? color = null, float duration = 0, bool depthTest = false)
{
return DrawArrow(null, start, end, arrowHeadLength, arrowHeadAngle, color, duration, depthTest);
}
public static int DrawArrow(int? id, Vector3 start, Vector3 end, float arrowHeadLength = 0.25f, float arrowHeadAngle = 20, Color? color = null, float duration = 0, bool depthTest = false)
{
if (start == end)
return 0;
return drawFigure(id, createArrowLines(start, end, arrowHeadLength, arrowHeadAngle), color ?? Color.white, duration, depthTest);
}
#endregion
#region Plane with normal
public static int DrawPlaneWithNormal(Vector3 pos, Vector3 normal, float scale = 1f, Color? color = null, float duration = 0, bool depthTest = false)
{
return DrawPlaneWithNormal(null, pos, normal, scale, color, duration, depthTest);
}
public static int DrawPlaneWithNormal(int? id, Vector3 pos, Vector3 normal, float scale = 1f, Color? color = null, float duration = 0, bool depthTest = false)
{
var lines = createArrowLines(pos, pos + normal);
lines.AddRange(createCrossLines(Matrix4x4.TRS(pos, Quaternion.LookRotation(normal) * Quaternion.Euler(0, 0, 45f), Vector3.one)));
lines.AddRange(createSquareLines(Matrix4x4.TRS(pos, Quaternion.FromToRotation(Vector3.up, normal), Vector3.one * scale)));
return drawFigure(id, lines, color ?? Color.white, duration, depthTest);
}
#endregion
#region Line with cross
public static int DrawLineWithCross(Vector3 start, Vector3 end, float crossRelativePosition = 0.5f, Vector3? scale = null, Color? color = null, float duration = 0, bool depthTest = false)
{
return DrawLineWithCross(null, start, end, crossRelativePosition, scale, color, duration, depthTest);
}
public static int DrawLineWithCross(int? id, Vector3 start, Vector3 end, float crossRelativePosition = 0.5f, Vector3? scale = null, Color? color = null, float duration = 0, bool depthTest = false)
{
var lines = new List<Line>() {new Line(start, end)};
// TODO: Calculate cross rotation
lines.AddRange(createCrossLines(Matrix4x4.TRS(Vector3.Lerp(start, end, crossRelativePosition), Quaternion.identity, scale ?? Vector3.one)));
return drawFigure(id, lines, color ?? Color.white, duration, depthTest);
}
public static int DrawLineWithCrossScreenSpace(Vector2 start, Vector2 end, float crossRelativePosition, Vector2? scale = null, Color? color = null, float duration = 0)
{
return DrawLineWithCrossScreenSpace(null, start, end, crossRelativePosition, scale, color, duration);
}
public static int DrawLineWithCrossScreenSpace(int? id, Vector2 start, Vector2 end, float crossRelativePosition, Vector2? scale = null, Color? color = null, float duration = 0)
{
var lines = new List<Line>() {new Line(start, end)};
lines.AddRange(createScreenSpaceCrossLines(Vector2.Lerp(start, end, crossRelativePosition), Mathf.Atan2(end.y - start.y, end.x - start.x) * Mathf.Rad2Deg + 45f, scale ?? Vector2.one * 10));
return drawFigureScreenSpace(id, lines, color ?? Color.white, duration);
}
#endregion
#region Square
public static int DrawSquare(Vector3 pos, Quaternion? rot = null, Vector3? scale = null, Color? color = null, float duration = 0, bool depthTest = false)
{
return DrawSquare(null, pos, rot, scale, color, duration, depthTest);
}
public static int DrawSquare(int? id, Vector3 pos, Quaternion? rot = null, Vector3? scale = null, Color? color = null, float duration = 0, bool depthTest = false)
{
return DrawSquare(Matrix4x4.TRS(pos, rot ?? Quaternion.identity, scale ?? Vector3.one), color, duration, depthTest);
}
public static int DrawSquare(Matrix4x4 matrix, Color? color = null, float duration = 0, bool depthTest = false)
{
return DrawSquare(null, matrix, color, duration, depthTest);
}
public static int DrawSquare(int? id, Matrix4x4 matrix, Color? color = null, float duration = 0, bool depthTest = false)
{
return drawFigure(id, createSquareLines(matrix), color ?? Color.white, duration, depthTest);
}
public static int DrawSquareScreenSpace(Vector2 pos, float rot = 0, Vector2? scale = null, Color? color = null, float duration = 0)
{
return DrawSquareScreenSpace(null, pos, rot, scale, color, duration);
}
public static int DrawSquareScreenSpace(int? id, Vector2 pos, float rot = 0, Vector2? scale = null, Color? color = null, float duration = 0)
{
return drawFigureScreenSpace(id, createScreenSpaceSquareLines(pos, rot, scale ?? DEFAULT_SCREEN_SPACE_SCALE), color ?? Color.white, duration);
}
#endregion
#region Cube
public static int DrawCube(Vector3 pos, Quaternion? rot = null, Vector3? scale = null, Color? color = null, float duration = 0, bool depthTest = false)
{
return DrawCube(null, pos, rot, scale, color, duration, depthTest);
}
public static int DrawCube(int? id, Vector3 pos, Quaternion? rot = null, Vector3? scale = null, Color? color = null, float duration = 0, bool depthTest = false)
{
return DrawCube(Matrix4x4.TRS(pos, rot ?? Quaternion.identity, scale ?? Vector3.one), color, duration, depthTest);
}
public static int DrawCube(Matrix4x4 matrix, Color? color = null, float duration = 0, bool depthTest = false)
{
return DrawCube(null, matrix, color, duration, depthTest);
}
public static int DrawCube(int? id, Matrix4x4 matrix, Color? color = null, float duration = 0, bool depthTest = false)
{
return drawFigure(id, createCubeLines(matrix), color ?? Color.white, duration, depthTest);
}
#endregion
#endregion
#region Unity methods
private void Awake()
{
if (_instance)
{
Destroy(this);
return;
}
_instance = this;
figuresDepthTest = new Dictionary<int, Figure>();
figuresNoDepthTest = new Dictionary<int, Figure>();
figuresScreenSpace = new Dictionary<int, Figure>();
figuresMultiplyDepthTest = new Dictionary<int, Figure>();
figuresMultiplyNoDepthTest = new Dictionary<int, Figure>();
figuresMultiplyScreenSpace = new Dictionary<int, Figure>();
figuresTmp = new Dictionary<int, Figure>();
wait = new WaitForEndOfFrame();
setMaterials();
}
private void Update()
{
if (Input.GetKeyDown(ToggleKey))
DisplayLines = !DisplayLines;
}
private IEnumerator OnPostRender()
{
if (!DisplayLines) yield break;
yield return wait;
materialDepthTest.SetPass(0);
UnityEngine.GL.Begin(UnityEngine.GL.LINES);
figuresDepthTest = draw(figuresDepthTest);
UnityEngine.GL.End();
materialMultiplyDepthTest.SetPass(0);
UnityEngine.GL.Begin(UnityEngine.GL.LINES);
figuresMultiplyDepthTest = draw(figuresMultiplyDepthTest);
UnityEngine.GL.End();
materialNoDepthTest.SetPass(0);
UnityEngine.GL.Begin(UnityEngine.GL.LINES);
figuresNoDepthTest = draw(figuresNoDepthTest);
UnityEngine.GL.End();
materialMultiplyNoDepthTest.SetPass(0);
UnityEngine.GL.Begin(UnityEngine.GL.LINES);
figuresMultiplyNoDepthTest = draw(figuresMultiplyNoDepthTest);
UnityEngine.GL.End();
UnityEngine.GL.PushMatrix();
UnityEngine.GL.LoadPixelMatrix();
materialNoDepthTest.SetPass(0);
UnityEngine.GL.Begin(UnityEngine.GL.LINES);
figuresScreenSpace = draw(figuresScreenSpace);
UnityEngine.GL.End();
materialMultiplyNoDepthTest.SetPass(0);
UnityEngine.GL.Begin(UnityEngine.GL.LINES);
figuresMultiplyScreenSpace = draw(figuresMultiplyScreenSpace);
UnityEngine.GL.End();
UnityEngine.GL.PopMatrix();
}
#endregion
#region Private functions
#region Misc
private Dictionary<int, Figure> draw(Dictionary<int, Figure> figures)
{
figuresTmp.Clear();
var newFigures = figuresTmp;
foreach (var key in figures.Keys)
{
var value = figures[key];
value.Duration = value.Draw();
if (value.Duration > 0)
{
newFigures[key] = value;
}
}
figuresTmp = figures;
return newFigures;
}
private void setMaterials()
{
materialDepthTest = new Material(Shader.Find("Hidden/DebugDepthTest"));
materialNoDepthTest = new Material(Shader.Find("Hidden/DebugNoDepthTest"));
materialMultiplyDepthTest = new Material(Shader.Find("Hidden/DebugMultiplyDepthTest"));
materialMultiplyNoDepthTest = new Material(Shader.Find("Hidden/DebugMultiplyNoDepthTest"));
materialDepthTest.hideFlags = HideFlags.HideAndDontSave;
materialNoDepthTest.hideFlags = HideFlags.HideAndDontSave;
materialMultiplyDepthTest.hideFlags = HideFlags.HideAndDontSave;
materialMultiplyNoDepthTest.hideFlags = HideFlags.HideAndDontSave;
}
#endregion
#region Figure creation
private static int drawFigure(int? id, List<Line> lines, Color color, float duration = 0, bool depthTest = false)
{
if (duration == 0 && !instance.DisplayLines)
return 0;
int figureId;
if (id.HasValue)
{
figureId = id.Value;
RemoveFigure(figureId);
}
else
{
figureId = nextFigureId++;
}
if (depthTest)
{
if (color == MULTIPLY) instance.figuresMultiplyDepthTest.Add(figureId, new Figure(figureId, lines, Color.white, duration));
else instance.figuresDepthTest.Add(figureId, new Figure(figureId, lines, color, duration));
}
else
{
if (color == MULTIPLY) instance.figuresMultiplyNoDepthTest.Add(figureId, new Figure(figureId, lines, Color.white, duration));
else instance.figuresNoDepthTest.Add(figureId, new Figure(figureId, lines, color, duration));
}
return figureId;
}
private static int drawFigureScreenSpace(int? id, List<Line> lines, Color color, float duration = 0)
{
if (duration == 0 && !instance.DisplayLines)
return 0;
int figureId;
if (id.HasValue)
{
figureId = id.Value;
RemoveFigure(figureId);
}
else
{
figureId = nextFigureId++;
}
if (color == MULTIPLY) instance.figuresMultiplyScreenSpace.Add(figureId, new Figure(figureId, lines, Color.white, duration));
else instance.figuresScreenSpace.Add(figureId, new Figure(figureId, lines, color, duration));
return figureId;
}
#endregion
#region Line helpers
private static List<Line> createCrossLines(Matrix4x4 matrix)
{
Vector3
p_1 = matrix.MultiplyPoint3x4(new Vector3(-.5f, 0, 0)),
p_2 = matrix.MultiplyPoint3x4(new Vector3(.5f, 0, 0)),
p_3 = matrix.MultiplyPoint3x4(new Vector3(0, -.5f, 0)),
p_4 = matrix.MultiplyPoint3x4(new Vector3(0, .5f, 0));
return new List<Line>()
{
new Line(p_1, p_2),
new Line(p_3, p_4),
};
}
private static List<Line> createScreenSpaceCrossLines(Vector2 pos, float rot, Vector2 scale)
{
Vector2 p_1, p_2, p_3, p_4;
float x = .5f * scale.x;
float y = .5f * scale.y;
if (rot == 0)
{
p_1 = new Vector2(-x, 0) + pos;
p_2 = new Vector2(x, 0) + pos;
p_3 = new Vector2(0, -y) + pos;
p_4 = new Vector2(0, y) + pos;
}
else
{
var cos = Mathf.Cos(rot * Mathf.Deg2Rad);
var sin = Mathf.Sin(rot * Mathf.Deg2Rad);
p_1 = new Vector2(-x * cos, -x * sin) + pos;
p_2 = new Vector2(x * cos, x * sin) + pos;
p_3 = new Vector2(y * sin, -y * cos) + pos;
p_4 = new Vector2(-y * sin, y * cos) + pos;
}
return new List<Line>()
{
new Line(p_1, p_2),
new Line(p_3, p_4),
};
}
private static List<Line> createArrowLines(Vector3 start, Vector3 end, float arrowHeadLength = 0.25f, float arrowHeadAngle = 20)
{
var dir = end - start;
Vector3 right = Quaternion.LookRotation(dir) * Quaternion.Euler(0, 180 + arrowHeadAngle, 0) * Vector3.forward;
Vector3 left = Quaternion.LookRotation(dir) * Quaternion.Euler(0, 180 - arrowHeadAngle, 0) * Vector3.forward;
return new List<Line>()
{
new Line(start, end),
new Line(end, end + right * arrowHeadLength),
new Line(end, end + left * arrowHeadLength)
};
}
private static List<Line> createSquareLines(Matrix4x4 matrix)
{
Vector3
p_1 = matrix.MultiplyPoint3x4(new Vector3(.5f, 0, .5f)),
p_2 = matrix.MultiplyPoint3x4(new Vector3(.5f, 0, -.5f)),
p_3 = matrix.MultiplyPoint3x4(new Vector3(-.5f, 0, -.5f)),
p_4 = matrix.MultiplyPoint3x4(new Vector3(-.5f, 0, .5f));
return new List<Line>()
{
new Line(p_1, p_2),
new Line(p_2, p_3),
new Line(p_3, p_4),
new Line(p_4, p_1)
};
}
private static List<Line> createScreenSpaceSquareLines(Vector2 pos, float rot, Vector2 scale)
{
Vector2 p_1, p_2, p_3, p_4;
float x = .5f * scale.x;
float y = .5f * scale.y;
if (rot == 0)
{
p_1 = new Vector2(x, y) + pos;
p_2 = new Vector2(x, -y) + pos;
p_3 = new Vector2(-x, -y) + pos;
p_4 = new Vector2(-x, y) + pos;
}
else
{
var cos = Mathf.Cos(rot * Mathf.Deg2Rad);
var sin = Mathf.Sin(rot * Mathf.Deg2Rad);
p_1 = new Vector2(x * cos - y * sin, x * sin + y * cos) + pos;
p_2 = new Vector2(x * cos + y * sin, x * sin - y * cos) + pos;
p_3 = new Vector2(-x * cos + y * sin, -x * sin - y * cos) + pos;
p_4 = new Vector2(-x * cos - y * sin, -x * sin + y * cos) + pos;
}
return new List<Line>()
{
new Line(p_1, p_2),
new Line(p_2, p_3),
new Line(p_3, p_4),
new Line(p_4, p_1)
};
}
private static List<Line> createCubeLines(Matrix4x4 matrix)
{
Vector3
down_1 = matrix.MultiplyPoint3x4(new Vector3(.5f, -.5f, .5f)),
down_2 = matrix.MultiplyPoint3x4(new Vector3(.5f, -.5f, -.5f)),
down_3 = matrix.MultiplyPoint3x4(new Vector3(-.5f, -.5f, -.5f)),
down_4 = matrix.MultiplyPoint3x4(new Vector3(-.5f, -.5f, .5f)),
up_1 = matrix.MultiplyPoint3x4(new Vector3(.5f, .5f, .5f)),
up_2 = matrix.MultiplyPoint3x4(new Vector3(.5f, .5f, -.5f)),
up_3 = matrix.MultiplyPoint3x4(new Vector3(-.5f, .5f, -.5f)),
up_4 = matrix.MultiplyPoint3x4(new Vector3(-.5f, .5f, .5f));
return new List<Line>()
{
new Line(down_1, down_2),
new Line(down_2, down_3),
new Line(down_3, down_4),
new Line(down_4, down_1),
new Line(down_1, up_1),
new Line(down_2, up_2),
new Line(down_3, up_3),
new Line(down_4, up_4),
new Line(up_1, up_2),
new Line(up_2, up_3),
new Line(up_3, up_4),
new Line(up_4, up_1)
};
}
#endregion
#endregion
#region Structs
private struct Figure
{
public int Id;
public Color Color;
public float Duration;
public List<Line> Lines;
public Figure(int id, List<Line> lines, Color color, float duration)
{
Id = id;
Color = color;
Duration = duration;
Lines = lines;
}
public float Draw()
{
UnityEngine.GL.Color(Color);
for (var i = 0; i < Lines.Count; i++)
{
Lines[i].Draw();
}
return Duration - Time.unscaledDeltaTime;
}
}
private struct Line
{
public Vector3 start;
public Vector3 end;
public Line(Vector3 start, Vector3 end)
{
this.start = start;
this.end = end;
}
public void Draw()
{
UnityEngine.GL.Vertex(start);
UnityEngine.GL.Vertex(end);
}
}
#endregion
}
#if UNITY_EDITOR
internal class BuildProcessor : IPreprocessBuild, IPostprocessBuild
{
public int callbackOrder
{
get { return 0; }
}
public void OnPreprocessBuild(BuildTarget target, string path)
{
// Add hidden shaders to the build.
var objs = Resources.FindObjectsOfTypeAll<GraphicsSettings>();
var graphicsSettings = new SerializedObject(objs[0]);
var alwaysIncludedShaders = graphicsSettings.FindProperty("m_AlwaysIncludedShaders");
insertShaderInProperty(alwaysIncludedShaders, "Hidden/DebugDepthTest");
insertShaderInProperty(alwaysIncludedShaders, "Hidden/DebugNoDepthTest");
insertShaderInProperty(alwaysIncludedShaders, "Hidden/DebugMultiplyDepthTest");
insertShaderInProperty(alwaysIncludedShaders, "Hidden/DebugMultiplyNoDepthTest");
graphicsSettings.ApplyModifiedProperties();
}
public void OnPostprocessBuild(BuildTarget target, string path)
{
// Reverd GraphicsSettings.
var objs = Resources.FindObjectsOfTypeAll<GraphicsSettings>();
var graphicsSettings = new SerializedObject(objs[0]);
var alwaysIncludedShaders = graphicsSettings.FindProperty("m_AlwaysIncludedShaders");
alwaysIncludedShaders.arraySize = alwaysIncludedShaders.arraySize - 4;
graphicsSettings.ApplyModifiedProperties();
}
private void insertShaderInProperty(SerializedProperty prop, string shaderName)
{
var index = prop.arraySize;
prop.InsertArrayElementAtIndex(index);
prop.GetArrayElementAtIndex(index).objectReferenceValue = Shader.Find(shaderName);
}
}
#endif
}
#endif