using System.Collections.Generic; using Unity.Burst; using Unity.Collections; using Unity.Mathematics; using UnityEngine; using static Pathfinding.Drawing.CommandBuilder; namespace Pathfinding.Drawing { /// /// 2D wrapper for a . /// /// /// var p1 = new Vector2(0, 1); /// var p2 = new Vector2(5, 7); /// /// // Draw it in the XY plane /// Draw.xy.Line(p1, p2); /// /// // Draw it in the XZ plane /// Draw.xz.Line(p1, p2); /// /// /// See: 2d-drawing (view in online documentation for working links) /// See: /// See: /// public partial struct CommandBuilder2D { /// The wrapped command builder private CommandBuilder draw; /// True if drawing in the XY plane, false if drawing in the XZ plane bool xy; static readonly float3 XY_UP = new float3(0, 0, 1); static readonly float3 XZ_UP = new float3(0, 1, 0); static readonly quaternion XY_TO_XZ_ROTATION = quaternion.RotateX(-math.PI*0.5f); static readonly quaternion XZ_TO_XZ_ROTATION = quaternion.identity; static readonly float4x4 XZ_TO_XY_MATRIX = new float4x4(new float4(1, 0, 0, 0), new float4(0, 0, 1, 0), new float4(0, 1, 0, 0), new float4(0, 0, 0, 1)); public CommandBuilder2D(CommandBuilder draw, bool xy) { this.draw = draw; this.xy = xy; } /// /// Draws a line between two points. /// /// [Open online documentation to see images] /// /// /// void Update () { /// Draw.Line(Vector3.zero, Vector3.up); /// } /// /// public void Line (float2 a, float2 b) { draw.Reserve(); // Add(Command.Line); // Add(new LineData { a = a, b = b }); // The code below is equivalent to the commented out code above. // But drawing lines is the most common operation so it needs to be really fast. // Having this hardcoded improves line rendering performance by about 8%. unsafe { var buffer = draw.buffer; var bufferSize = buffer->Length; var newLen = bufferSize + 4 + 24; #if ENABLE_UNITY_COLLECTIONS_CHECKS UnityEngine.Assertions.Assert.IsTrue(newLen <= buffer->Capacity); #endif var ptr = (byte*)buffer->Ptr + bufferSize; *(Command*)ptr = Command.Line; var lineData = (LineData*)(ptr + 4); if (xy) { lineData->a = new float3(a, 0); lineData->b = new float3(b, 0); } else { lineData->a = new float3(a.x, 0, a.y); lineData->b = new float3(b.x, 0, b.y); } buffer->Length = newLen; } } /// /// Draws a line between two points. /// /// [Open online documentation to see images] /// /// /// void Update () { /// Draw.Line(Vector3.zero, Vector3.up); /// } /// /// public void Line (float2 a, float2 b, Color color) { draw.Reserve(); // Add(Command.Line); // Add(new LineData { a = a, b = b }); // The code below is equivalent to the commented out code above. // But drawing lines is the most common operation so it needs to be really fast. // Having this hardcoded improves line rendering performance by about 8%. unsafe { var buffer = draw.buffer; var bufferSize = buffer->Length; var newLen = bufferSize + 4 + 24 + 4; #if ENABLE_UNITY_COLLECTIONS_CHECKS UnityEngine.Assertions.Assert.IsTrue(newLen <= buffer->Capacity); #endif var ptr = (byte*)buffer->Ptr + bufferSize; *(Command*)ptr = Command.Line | Command.PushColorInline; *(uint*)(ptr + 4) = CommandBuilder.ConvertColor(color); var lineData = (LineData*)(ptr + 8); if (xy) { lineData->a = new float3(a, 0); lineData->b = new float3(b, 0); } else { lineData->a = new float3(a.x, 0, a.y); lineData->b = new float3(b.x, 0, b.y); } buffer->Length = newLen; } } /// /// Draws a line between two points. /// /// [Open online documentation to see images] /// /// /// void Update () { /// Draw.Line(Vector3.zero, Vector3.up); /// } /// /// public void Line (float3 a, float3 b) { draw.Line(a, b); } /// /// Draws a circle. /// /// You can draw an arc by supplying the startAngle and endAngle parameters. /// /// [Open online documentation to see images] /// /// See: /// See: /// /// Center of the circle or arc. /// Radius of the circle or arc. /// Starting angle in radians. 0 corrsponds to the positive X axis. /// End angle in radians. public void Circle (float2 center, float radius, float startAngle = 0f, float endAngle = 2 * math.PI) { Circle(xy ? new float3(center, 0) : new float3(center.x, 0, center.y), radius, startAngle, endAngle); } /// /// Draws a circle. /// /// You can draw an arc by supplying the startAngle and endAngle parameters. /// /// [Open online documentation to see images] /// /// See: /// See: /// /// Center of the circle or arc. /// Radius of the circle or arc. /// Starting angle in radians. 0 corrsponds to the positive X axis. /// End angle in radians. public void Circle (float3 center, float radius, float startAngle = 0f, float endAngle = 2 * math.PI) { if (xy) { draw.PushMatrix(XZ_TO_XY_MATRIX); draw.CircleXZInternal(new float3(center.x, center.z, center.y), radius, startAngle, endAngle); draw.PopMatrix(); } else { draw.CircleXZInternal(center, radius, startAngle, endAngle); } } /// \copydocref{SolidCircle(float3,float,float,float)} public void SolidCircle (float2 center, float radius, float startAngle = 0f, float endAngle = 2 * math.PI) { SolidCircle(xy ? new float3(center, 0) : new float3(center.x, 0, center.y), radius, startAngle, endAngle); } /// /// Draws a disc. /// /// You can draw an arc by supplying the startAngle and endAngle parameters. /// /// [Open online documentation to see images] /// /// See: /// See: /// /// Center of the disc or solid arc. /// Radius of the disc or solid arc. /// Starting angle in radians. 0 corrsponds to the positive X axis. /// End angle in radians. public void SolidCircle (float3 center, float radius, float startAngle = 0f, float endAngle = 2 * math.PI) { if (xy) draw.PushMatrix(XZ_TO_XY_MATRIX); draw.SolidCircleXZInternal(xy ? new float3(center.x, center.z, center.y) : center, radius, startAngle, endAngle); if (xy) draw.PopMatrix(); } /// /// Draws a wire pill in 2D. /// /// /// Draw.xy.WirePill(new float2(-0.5f, -0.5f), new float2(0.5f, 0.5f), 0.5f, color); /// /// /// [Open online documentation to see images] /// /// See: /// /// Center of the first circle of the capsule. /// Center of the second circle of the capsule. /// Radius of the capsule. public void WirePill (float2 a, float2 b, float radius) { WirePill(a, b - a, math.length(b - a), radius); } /// /// Draws a wire pill in 2D. /// /// /// Draw.xy.WirePill(new float2(-0.5f, -0.5f), new float2(1, 1), 1, 0.5f, color); /// /// /// [Open online documentation to see images] /// /// See: /// /// Center of the first circle of the capsule. /// The main axis of the capsule. Does not have to be normalized. If zero, a circle will be drawn. /// Length of the main axis of the capsule, from circle center to circle center. If zero, a circle will be drawn. /// Radius of the capsule. public void WirePill (float2 position, float2 direction, float length, float radius) { direction = math.normalizesafe(direction); if (radius <= 0) { Line(position, position + direction * length); } else if (length <= 0 || math.all(direction == 0)) { Circle(position, radius); } else { float4x4 m; if (xy) { m = new float4x4( new float4(direction, 0, 0), new float4(math.cross(new float3(direction, 0), XY_UP), 0), new float4(0, 0, 1, 0), new float4(position, 0, 1) ); } else { m = new float4x4( new float4(direction.x, 0, direction.y, 0), new float4(0, 1, 0, 0), new float4(math.cross(new float3(direction.x, 0, direction.y), XZ_UP), 0), new float4(position.x, 0, position.y, 1) ); } draw.PushMatrix(m); Circle(new float2(0, 0), radius, 0.5f * math.PI, 1.5f * math.PI); Line(new float2(0, -radius), new float2(length, -radius)); Circle(new float2(length, 0), radius, -0.5f * math.PI, 0.5f * math.PI); Line(new float2(0, radius), new float2(length, radius)); draw.PopMatrix(); } } /// \copydocref{CommandBuilder.Polyline(List,bool)} [BurstDiscard] public void Polyline (List points, bool cycle = false) { for (int i = 0; i < points.Count - 1; i++) { Line(points[i], points[i+1]); } if (cycle && points.Count > 1) Line(points[points.Count - 1], points[0]); } /// \copydocref{CommandBuilder.Polyline(Vector3[],bool)} [BurstDiscard] public void Polyline (Vector2[] points, bool cycle = false) { for (int i = 0; i < points.Length - 1; i++) { Line(points[i], points[i+1]); } if (cycle && points.Length > 1) Line(points[points.Length - 1], points[0]); } /// \copydocref{CommandBuilder.Polyline(float3[],bool)} [BurstDiscard] public void Polyline (float2[] points, bool cycle = false) { for (int i = 0; i < points.Length - 1; i++) { Line(points[i], points[i+1]); } if (cycle && points.Length > 1) Line(points[points.Length - 1], points[0]); } /// \copydocref{CommandBuilder.Polyline(NativeArray,bool)} public void Polyline (NativeArray points, bool cycle = false) { for (int i = 0; i < points.Length - 1; i++) { Line(points[i], points[i+1]); } if (cycle && points.Length > 1) Line(points[points.Length - 1], points[0]); } /// /// Draws a 2D cross. /// /// /// Draw.xz.Cross(float3.zero, color); /// /// [Open online documentation to see images] /// /// See: /// public void Cross (float2 position, float size = 1) { size *= 0.5f; Line(position - new float2(size, 0), position + new float2(size, 0)); Line(position - new float2(0, size), position + new float2(0, size)); } /// /// Draws a rectangle outline. /// The rectangle will be oriented along the rotation's X and Z axes. /// /// /// Draw.xz.WireRectangle(new Vector3(0f, 0, 0), new Vector2(1, 1), Color.black); /// /// [Open online documentation to see images] /// /// This is identical to , but this name is added for consistency. /// /// See: /// public void WireRectangle (float3 center, float2 size) { draw.WirePlane(center, xy ? XY_TO_XZ_ROTATION : XZ_TO_XZ_ROTATION, size); } /// /// Draws a rectangle outline. /// This is particularly useful when combined with . /// /// /// using (Draw.InScreenSpace(Camera.main)) { /// Draw.xy.WireRectangle(new Rect(10, 10, 100, 100), Color.black); /// } /// /// [Open online documentation to see images] /// /// See: /// See: /// public void WireRectangle (Rect rect) { float2 min = rect.min; float2 max = rect.max; Line(new float2(min.x, min.y), new float2(max.x, min.y)); Line(new float2(max.x, min.y), new float2(max.x, max.y)); Line(new float2(max.x, max.y), new float2(min.x, max.y)); Line(new float2(min.x, max.y), new float2(min.x, min.y)); } /// /// Draws a solid rectangle. /// This is particularly useful when combined with . /// /// Behind the scenes this is implemented using . /// /// /// using (Draw.InScreenSpace(Camera.main)) { /// Draw.xy.SolidRectangle(new Rect(10, 10, 100, 100), Color.black); /// } /// /// [Open online documentation to see images] /// /// See: /// See: /// See: /// public void SolidRectangle (Rect rect) { draw.SolidPlane(new float3(rect.center.x, rect.center.y, 0.0f), xy ? XY_TO_XZ_ROTATION : XZ_TO_XZ_ROTATION, new float2(rect.width, rect.height)); } /// /// Draws a grid of lines. /// /// /// Draw.xz.WireGrid(Vector3.zero, new int2(3, 3), new float2(1, 1), color); /// /// [Open online documentation to see images] /// /// See: /// /// Center of the grid /// Number of cells of the grid. Should be greater than 0. /// Total size of the grid along the X and Z axes. public void WireGrid (float2 center, int2 cells, float2 totalSize) { draw.WireGrid(xy ? new float3(center, 0) : new float3(center.x, 0, center.y), xy ? XY_TO_XZ_ROTATION : XZ_TO_XZ_ROTATION, cells, totalSize); } /// /// Draws a grid of lines. /// /// /// Draw.xz.WireGrid(Vector3.zero, new int2(3, 3), new float2(1, 1), color); /// /// [Open online documentation to see images] /// /// See: /// /// Center of the grid /// Number of cells of the grid. Should be greater than 0. /// Total size of the grid along the X and Z axes. public void WireGrid (float3 center, int2 cells, float2 totalSize) { draw.WireGrid(center, xy ? XY_TO_XZ_ROTATION : XZ_TO_XZ_ROTATION, cells, totalSize); } } }