diff --git a/NewHorizons/Builder/Props/QuantumBuilder.cs b/NewHorizons/Builder/Props/QuantumBuilder.cs index 5bdf571a..1333ed9f 100644 --- a/NewHorizons/Builder/Props/QuantumBuilder.cs +++ b/NewHorizons/Builder/Props/QuantumBuilder.cs @@ -1,6 +1,7 @@ using HarmonyLib; using NewHorizons.External.Configs; using NewHorizons.External.Modules; +using NewHorizons.Utility; using OWML.Common; using System; using System.Collections.Generic; @@ -90,6 +91,7 @@ namespace NewHorizons.Builder.Props var boxShape = prop.AddComponent(); boxShape.center = boxBounds.center; boxShape.extents = boxBounds.size; + prop.AddComponent(); prop.AddComponent(); } @@ -107,6 +109,7 @@ namespace NewHorizons.Builder.Props var boxShape = empty.AddComponent(); boxShape.center = boxBounds.center; boxShape.extents = boxBounds.size; + empty.AddComponent(); empty.AddComponent(); } @@ -139,6 +142,8 @@ namespace NewHorizons.Builder.Props var boxShape = shuffleParent.AddComponent(); boxShape.center = boxBounds.center; boxShape.extents = boxBounds.size; + + shuffleParent.AddComponent(); shuffleParent.AddComponent(); shuffleParent.SetActive(true); diff --git a/NewHorizons/Utility/BoxShapeVisualizer.cs b/NewHorizons/Utility/BoxShapeVisualizer.cs new file mode 100644 index 00000000..96d8005f --- /dev/null +++ b/NewHorizons/Utility/BoxShapeVisualizer.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; +using Popcron; + +namespace NewHorizons.Utility +{ + public class BoxShapeVisualizer : MonoBehaviour + { + BoxShape box; + + void Awake() + { + box = GetComponent(); + } + + void OnRenderImage() + { + Popcron.Gizmos.Cube(transform.TransformPoint(box.center), transform.rotation, box.size); + } + } +} diff --git a/NewHorizons/Utility/Popcron.Gizmos/Constants.cs b/NewHorizons/Utility/Popcron.Gizmos/Constants.cs new file mode 100644 index 00000000..bec9bc33 --- /dev/null +++ b/NewHorizons/Utility/Popcron.Gizmos/Constants.cs @@ -0,0 +1,7 @@ +namespace Popcron +{ + public class Constants + { + public const string UniqueIdentifier = "Popcron.Gizmos"; + } +} \ No newline at end of file diff --git a/NewHorizons/Utility/Popcron.Gizmos/Drawer.cs b/NewHorizons/Utility/Popcron.Gizmos/Drawer.cs new file mode 100644 index 00000000..024634d4 --- /dev/null +++ b/NewHorizons/Utility/Popcron.Gizmos/Drawer.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace Popcron +{ + public abstract class Drawer + { + private static Dictionary typeToDrawer = null; + + public abstract int Draw(ref Vector3[] buffer, params object[] args); + + public Drawer() + { + + } + + public static Drawer Get() where T : class + { + //find all drawers + if (typeToDrawer == null) + { + typeToDrawer = new Dictionary + { + + //add defaults + { typeof(CubeDrawer), new CubeDrawer() }, + { typeof(LineDrawer), new LineDrawer() }, + { typeof(PolygonDrawer), new PolygonDrawer() }, + { typeof(SquareDrawer), new SquareDrawer() }, + { typeof(FrustumDrawer), new FrustumDrawer() } + }; + } + + return typeToDrawer.TryGetValue(typeof(T), out var drawer) + ? drawer + : null; + } + } +} \ No newline at end of file diff --git a/NewHorizons/Utility/Popcron.Gizmos/Drawers/CubeDrawer.cs b/NewHorizons/Utility/Popcron.Gizmos/Drawers/CubeDrawer.cs new file mode 100644 index 00000000..86ece8ec --- /dev/null +++ b/NewHorizons/Utility/Popcron.Gizmos/Drawers/CubeDrawer.cs @@ -0,0 +1,96 @@ +using UnityEngine; + +namespace Popcron +{ + public class CubeDrawer : Drawer + { + public CubeDrawer() + { + + } + + public override int Draw(ref Vector3[] buffer, params object[] values) + { + var position = (Vector3)values[0]; + var rotation = (Quaternion)values[1]; + var size = (Vector3)values[2]; + + size *= 0.5f; + + var point1 = new Vector3(position.x - size.x, position.y - size.y, position.z - size.z); + var point2 = new Vector3(position.x + size.x, position.y - size.y, position.z - size.z); + var point3 = new Vector3(position.x + size.x, position.y + size.y, position.z - size.z); + var point4 = new Vector3(position.x - size.x, position.y + size.y, position.z - size.z); + + var point5 = new Vector3(position.x - size.x, position.y - size.y, position.z + size.z); + var point6 = new Vector3(position.x + size.x, position.y - size.y, position.z + size.z); + var point7 = new Vector3(position.x + size.x, position.y + size.y, position.z + size.z); + var point8 = new Vector3(position.x - size.x, position.y + size.y, position.z + size.z); + + point1 = rotation * (point1 - position); + point1 += position; + + point2 = rotation * (point2 - position); + point2 += position; + + point3 = rotation * (point3 - position); + point3 += position; + + point4 = rotation * (point4 - position); + point4 += position; + + point5 = rotation * (point5 - position); + point5 += position; + + point6 = rotation * (point6 - position); + point6 += position; + + point7 = rotation * (point7 - position); + point7 += position; + + point8 = rotation * (point8 - position); + point8 += position; + + //square + buffer[0] = point1; + buffer[1] = point2; + + buffer[2] = point2; + buffer[3] = point3; + + buffer[4] = point3; + buffer[5] = point4; + + buffer[6] = point4; + buffer[7] = point1; + + //other square + buffer[8] = point5; + buffer[9] = point6; + + buffer[10] = point6; + buffer[11] = point7; + + buffer[12] = point7; + buffer[13] = point8; + + buffer[14] = point8; + buffer[15] = point5; + + //connectors + buffer[16] = point1; + buffer[17] = point5; + + buffer[18] = point2; + buffer[19] = point6; + + buffer[20] = point3; + buffer[21] = point7; + + buffer[22] = point4; + buffer[23] = point8; + + return 24; + } + } +} \ No newline at end of file diff --git a/NewHorizons/Utility/Popcron.Gizmos/Drawers/FrustumDrawer.cs b/NewHorizons/Utility/Popcron.Gizmos/Drawers/FrustumDrawer.cs new file mode 100644 index 00000000..70aef597 --- /dev/null +++ b/NewHorizons/Utility/Popcron.Gizmos/Drawers/FrustumDrawer.cs @@ -0,0 +1,43 @@ +using UnityEngine; + +namespace Popcron +{ + internal class FrustumDrawer : Drawer + { + public FrustumDrawer() + { + + } + + public override int Draw(ref Vector3[] buffer, params object[] values) + { + var camera = (OWCamera)values[0]; + + //bottom left + buffer[0] = camera.ScreenToWorldPoint(new Vector3(0, 0, camera.nearClipPlane)); + buffer[1] = camera.ScreenToWorldPoint(new Vector3(0, 0, camera.farClipPlane / 2)); + //bottom right + buffer[2] = camera.ScreenToWorldPoint(new Vector3(camera.pixelWidth, 0, camera.nearClipPlane)); + buffer[3] = camera.ScreenToWorldPoint(new Vector3(camera.pixelWidth, 0, camera.farClipPlane / 2)); + //top left + buffer[4] = camera.ScreenToWorldPoint(new Vector3(0, camera.pixelHeight, camera.nearClipPlane)); + buffer[5] = camera.ScreenToWorldPoint(new Vector3(0, camera.pixelHeight, camera.farClipPlane / 2)); + //top right + buffer[6] = camera.ScreenToWorldPoint(new Vector3(camera.pixelWidth, camera.pixelHeight, camera.nearClipPlane)); + buffer[7] = camera.ScreenToWorldPoint(new Vector3(camera.pixelWidth, camera.pixelHeight, camera.farClipPlane / 2)); + //bottom left to bottom right + buffer[8] = camera.ScreenToWorldPoint(new Vector3(0, 0, camera.farClipPlane / 2)); + buffer[9] = camera.ScreenToWorldPoint(new Vector3(camera.pixelWidth, 0, camera.farClipPlane / 2)); + //bottom right to top right + buffer[10] = camera.ScreenToWorldPoint(new Vector3(camera.pixelWidth, 0, camera.farClipPlane / 2)); + buffer[11] = camera.ScreenToWorldPoint(new Vector3(camera.pixelWidth, camera.pixelHeight, camera.farClipPlane / 2)); + //top right to top left + buffer[12] = camera.ScreenToWorldPoint(new Vector3(camera.pixelWidth, camera.pixelHeight, camera.farClipPlane / 2)); + buffer[13] = camera.ScreenToWorldPoint(new Vector3(0, camera.pixelHeight, camera.farClipPlane / 2)); + //top left to bottom left + buffer[14] = camera.ScreenToWorldPoint(new Vector3(0, camera.pixelHeight, camera.farClipPlane / 2)); + buffer[15] = camera.ScreenToWorldPoint(new Vector3(0, 0, camera.farClipPlane / 2)); + return 16; + } + } +} \ No newline at end of file diff --git a/NewHorizons/Utility/Popcron.Gizmos/Drawers/LineDrawer.cs b/NewHorizons/Utility/Popcron.Gizmos/Drawers/LineDrawer.cs new file mode 100644 index 00000000..29287814 --- /dev/null +++ b/NewHorizons/Utility/Popcron.Gizmos/Drawers/LineDrawer.cs @@ -0,0 +1,19 @@ +using UnityEngine; + +namespace Popcron +{ + public class LineDrawer : Drawer + { + public LineDrawer() + { + + } + + public override int Draw(ref Vector3[] buffer, params object[] args) + { + buffer[0] = (Vector3)args[0]; + buffer[1] = (Vector3)args[1]; + return 2; + } + } +} \ No newline at end of file diff --git a/NewHorizons/Utility/Popcron.Gizmos/Drawers/PolygonDrawer.cs b/NewHorizons/Utility/Popcron.Gizmos/Drawers/PolygonDrawer.cs new file mode 100644 index 00000000..cfd07f89 --- /dev/null +++ b/NewHorizons/Utility/Popcron.Gizmos/Drawers/PolygonDrawer.cs @@ -0,0 +1,40 @@ +using UnityEngine; + +namespace Popcron +{ + public class PolygonDrawer : Drawer + { + public PolygonDrawer() + { + + } + + public override int Draw(ref Vector3[] buffer, params object[] values) + { + var position = (Vector3)values[0]; + var points = (int)values[1]; + var radius = (float)values[2]; + var offset = (float)values[3]; + var rotation = (Quaternion)values[4]; + + var step = 360f / points; + offset *= Mathf.Deg2Rad; + + for (var i = 0; i < points; i++) + { + var cx = Mathf.Cos((Mathf.Deg2Rad * step * i) + offset) * radius; + var cy = Mathf.Sin((Mathf.Deg2Rad * step * i) + offset) * radius; + var current = new Vector3(cx, cy); + + var nx = Mathf.Cos((Mathf.Deg2Rad * step * (i + 1)) + offset) * radius; + var ny = Mathf.Sin((Mathf.Deg2Rad * step * (i + 1)) + offset) * radius; + var next = new Vector3(nx, ny); + + buffer[i * 2] = position + (rotation * current); + buffer[(i * 2) + 1] = position + (rotation * next); + } + + return points * 2; + } + } +} \ No newline at end of file diff --git a/NewHorizons/Utility/Popcron.Gizmos/Drawers/SquareDrawer.cs b/NewHorizons/Utility/Popcron.Gizmos/Drawers/SquareDrawer.cs new file mode 100644 index 00000000..ca067e43 --- /dev/null +++ b/NewHorizons/Utility/Popcron.Gizmos/Drawers/SquareDrawer.cs @@ -0,0 +1,72 @@ +using UnityEngine; + +namespace Popcron +{ + public class SquareDrawer : Drawer + { + public SquareDrawer() + { + + } + + public override int Draw(ref Vector3[] buffer, params object[] values) + { + Vector2 position = default; + if (values[0] is Vector2 p2) + { + position = p2; + } + else if (values[0] is Vector3 p3) + { + position = p3; + } + + var rotation = (Quaternion)values[1]; + + Vector2 size = default; + if (values[2] is Vector2 s2) + { + size = s2; + } + else if (values[2] is Vector3 s3) + { + size = s3; + } + + size *= 0.5f; + + Vector2 point1 = new Vector3(position.x - size.x, position.y - size.y); + Vector2 point2 = new Vector3(position.x + size.x, position.y - size.y); + Vector2 point3 = new Vector3(position.x + size.x, position.y + size.y); + Vector2 point4 = new Vector3(position.x - size.x, position.y + size.y); + + point1 = rotation * (point1 - position); + point1 += position; + + point2 = rotation * (point2 - position); + point2 += position; + + point3 = rotation * (point3 - position); + point3 += position; + + point4 = rotation * (point4 - position); + point4 += position; + + //square + buffer[0] = point1; + buffer[1] = point2; + + buffer[2] = point2; + buffer[3] = point3; + + buffer[4] = point3; + buffer[5] = point4; + + //loop back to start + buffer[6] = point4; + buffer[7] = point1; + + return 8; + } + } +} \ No newline at end of file diff --git a/NewHorizons/Utility/Popcron.Gizmos/Element.cs b/NewHorizons/Utility/Popcron.Gizmos/Element.cs new file mode 100644 index 00000000..3883ecdc --- /dev/null +++ b/NewHorizons/Utility/Popcron.Gizmos/Element.cs @@ -0,0 +1,10 @@ +using UnityEngine; + +namespace Popcron +{ + internal class Element + { + public Vector3[] points = { }; + public Color color = Color.white; + } +} \ No newline at end of file diff --git a/NewHorizons/Utility/Popcron.Gizmos/Gizmos.cs b/NewHorizons/Utility/Popcron.Gizmos/Gizmos.cs new file mode 100644 index 00000000..f1da5d5b --- /dev/null +++ b/NewHorizons/Utility/Popcron.Gizmos/Gizmos.cs @@ -0,0 +1,198 @@ +using System; +using UnityEngine; + +namespace Popcron +{ + public class Gizmos + { + private static string _prefsKey = null; + private static int? _bufferSize = null; + private static float? _dashGap = null; + private static Vector3? _offset = null; + + private static Vector3[] buffer = new Vector3[BufferSize]; + + private static string PrefsKey + { + get + { + if (string.IsNullOrEmpty(_prefsKey)) + { + _prefsKey = $"{SystemInfo.deviceUniqueIdentifier}.{Application.companyName}.{Application.productName}.{Constants.UniqueIdentifier}"; + } + + return _prefsKey; + } + } + + public static int BufferSize + { + get + { + if (_bufferSize == null) + { + _bufferSize = PlayerPrefs.GetInt($"{PrefsKey}.BufferSize", 4096); + } + + return _bufferSize.Value; + } + set + { + value = Mathf.Clamp(value, 0, int.MaxValue); + if (_bufferSize != value) + { + _bufferSize = value; + PlayerPrefs.SetInt($"{PrefsKey}.BufferSize", value); + + //buffer size changed, so recreate the buffer array too + buffer = new Vector3[value]; + } + } + } + + public static float DashGap + { + get + { + if (_dashGap == null) + { + _dashGap = PlayerPrefs.GetFloat($"{PrefsKey}.DashGap", 0.1f); + } + + return _dashGap.Value; + } + set + { + if (_dashGap != value) + { + _dashGap = value; + PlayerPrefs.SetFloat($"{PrefsKey}.DashGap", value); + } + } + } + + public static Material Material + { + get => GizmosInstance.Material; + set => GizmosInstance.Material = value; + } + + public static Vector3 Offset + { + get + { + const string Delim = ","; + if (_offset == null) + { + var data = PlayerPrefs.GetString($"{PrefsKey}.Offset", 0 + Delim + 0 + Delim + 0); + var indexOf = data.IndexOf(Delim); + var lastIndexOf = data.LastIndexOf(Delim); + if (indexOf + lastIndexOf > 0) + { + var arr = data.Split(Delim[0]); + _offset = new Vector3(float.Parse(arr[0]), float.Parse(arr[1]), float.Parse(arr[2])); + } + else + { + return Vector3.zero; + } + } + + return _offset.Value; + } + set + { + const string Delim = ","; + if (_offset != value) + { + _offset = value; + PlayerPrefs.SetString($"{PrefsKey}.Offset", value.x + Delim + value.y + Delim + value.y); + } + } + } + + public static void Draw(Color? color, params object[] args) where T : Drawer + { + var drawer = Drawer.Get(); + if (drawer != null) + { + var points = drawer.Draw(ref buffer, args); + + //copy from buffer and add to the queue + var array = new Vector3[points]; + Array.Copy(buffer, array, points); + GizmosInstance.Submit(array, color); + } + } + + public static void Lines(Vector3[] lines, Color? color = null) + => GizmosInstance.Submit(lines, color); + + public static void Line(Vector3 a, Vector3 b, Color? color = null) + => Draw(color, a, b); + + public static void Square(Vector2 position, Vector2 size, Color? color = null) + => Square(position, Quaternion.identity, size, color); + + public static void Square(Vector2 position, float diameter, Color? color = null) + => Square(position, Quaternion.identity, Vector2.one * diameter, color); + + public static void Square(Vector2 position, Quaternion rotation, Vector2 size, Color? color = null) + => Draw(color, position, rotation, size); + + public static void Cube(Vector3 position, Quaternion rotation, Vector3 size, Color? color = null) + => Draw(color, position, rotation, size); + + public static void Rect(Rect rect, Camera camera, Color? color = null) + { + rect.y = Screen.height - rect.y; + Vector2 corner = camera.ScreenToWorldPoint(new Vector2(rect.x, rect.y - rect.height)); + Draw(color, corner + (rect.size * 0.5f), Quaternion.identity, rect.size); + } + + public static void Bounds(Bounds bounds, Color? color = null) + => Draw(color, bounds.center, Quaternion.identity, bounds.size); + + public static void Cone(Vector3 position, Quaternion rotation, float length, float angle, Color? color = null, int pointsCount = 16) + { + //draw the end of the cone + var endAngle = Mathf.Tan(angle * 0.5f * Mathf.Deg2Rad) * length; + var forward = rotation * Vector3.forward; + var endPosition = position + (forward * length); + var offset = 0f; + Draw(color, endPosition, pointsCount, endAngle, offset, rotation); + + //draw the 4 lines + for (var i = 0; i < 4; i++) + { + var a = i * 90f * Mathf.Deg2Rad; + var point = rotation * new Vector3(Mathf.Cos(a), Mathf.Sin(a)) * endAngle; + Line(position, position + point + (forward * length), color); + } + } + + public static void Sphere(Vector3 position, float radius, Color? color = null, int pointsCount = 16) + { + var offset = 0f; + Draw(color, position, pointsCount, radius, offset, Quaternion.Euler(0f, 0f, 0f)); + Draw(color, position, pointsCount, radius, offset, Quaternion.Euler(90f, 0f, 0f)); + Draw(color, position, pointsCount, radius, offset, Quaternion.Euler(0f, 90f, 90f)); + } + + public static void Circle(Vector3 position, float radius, Camera camera, Color? color = null, int pointsCount = 16) + { + var offset = 0f; + var rotation = Quaternion.LookRotation(position - camera.transform.position); + Draw(color, position, pointsCount, radius, offset, rotation); + } + + public static void Circle(Vector3 position, float radius, Quaternion rotation, Color? color = null, int pointsCount = 16) + { + var offset = 0f; + Draw(color, position, pointsCount, radius, offset, rotation); + } + + public static void Frustum(OWCamera camera, Color? color = null) + => Draw(color, camera); + } +} \ No newline at end of file diff --git a/NewHorizons/Utility/Popcron.Gizmos/GizmosInstance.cs b/NewHorizons/Utility/Popcron.Gizmos/GizmosInstance.cs new file mode 100644 index 00000000..5e8b2cb5 --- /dev/null +++ b/NewHorizons/Utility/Popcron.Gizmos/GizmosInstance.cs @@ -0,0 +1,181 @@ +using System; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.Rendering; + +namespace Popcron +{ + public class GizmosInstance : MonoBehaviour + { + private const int DefaultQueueSize = 4096; + + private static GizmosInstance instance; + private static Material defaultMaterial; + + private Material overrideMaterial; + private int queueIndex = 0; + private int lastFrame; + private Element[] queue = new Element[DefaultQueueSize]; + + /// + /// The material being used to render + /// + public static Material Material + { + get + { + var inst = GetOrCreate(); + if (inst.overrideMaterial) + { + return inst.overrideMaterial; + } + + return DefaultMaterial; + } + set + { + var inst = GetOrCreate(); + inst.overrideMaterial = value; + } + } + + /// + /// The default line renderer material + /// + public static Material DefaultMaterial + { + get + { + if (!defaultMaterial) + { + // Unity has a built-in shader that is useful for drawing + // simple colored things. + var shader = Shader.Find("UI/Default"); + defaultMaterial = new Material(shader) + { + hideFlags = HideFlags.HideAndDontSave + }; + + // Turn on alpha blending + defaultMaterial.SetInt("unity_GUIZTestMode", (int)CompareFunction.Always); + } + + return defaultMaterial; + } + } + + internal static GizmosInstance GetOrCreate() + { + if (!instance) + { + var gizmosInstances = FindObjectsOfType(); + for (var i = 0; i < gizmosInstances.Length; i++) + { + instance = gizmosInstances[i]; + + //destroy any extra gizmo instances + if (i > 0) + { + Destroy(gizmosInstances[i]); + } + } + + //none were found, create a new one + if (!instance) + { + //instance = new GameObject(typeof(GizmosInstance).FullName).AddComponent(); + //instance.gameObject.hideFlags = HideFlags.HideInHierarchy | HideFlags.HideInInspector; + instance = Locator.GetPlayerCamera().gameObject.AddComponent(); + } + } + + return instance; + } + + private float CurrentTime => Time.time; + + /// + /// Submits an array of points to draw into the queue. + /// + internal static void Submit(Vector3[] points, Color? color) + { + var inst = GetOrCreate(); + + //if new frame, reset index + if (inst.lastFrame != Time.frameCount) + { + inst.lastFrame = Time.frameCount; + inst.queueIndex = 0; + } + + //excedeed the length, so make it even bigger + if (inst.queueIndex >= inst.queue.Length) + { + var bigger = new Element[inst.queue.Length + DefaultQueueSize]; + for (var i = inst.queue.Length; i < bigger.Length; i++) + { + bigger[i] = new Element(); + } + + Array.Copy(inst.queue, 0, bigger, 0, inst.queue.Length); + inst.queue = bigger; + } + + inst.queue[inst.queueIndex].color = color ?? Color.white; + inst.queue[inst.queueIndex].points = points; + + inst.queueIndex++; + } + + private void OnEnable() + { + //populate queue with empty elements + queue = new Element[DefaultQueueSize]; + for (var i = 0; i < DefaultQueueSize; i++) + { + queue[i] = new Element(); + } + } + + private void Update() => + //always render something + Gizmos.Line(default, default); + + private void OnPostRender() + { + Material.SetPass(0); + + var offset = Gizmos.Offset; + + GL.PushMatrix(); + GL.MultMatrix(Matrix4x4.identity); + GL.Begin(GL.LINES); + + var points = new List(); + + //draw le elements + for (var e = 0; e < queueIndex; e++) + { + //just in case + if (queue.Length <= e) + { + break; + } + + var element = queue[e]; + + points.Clear(); + points.AddRange(element.points); + + GL.Color(element.color); + for (var i = 0; i < points.Count; i++) + { + GL.Vertex(points[i] + offset); + } + } + + GL.End(); + GL.PopMatrix(); + } + } +} \ No newline at end of file