diff options
Diffstat (limited to 'Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing')
85 files changed, 12795 insertions, 0 deletions
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/ALINE.asmdef b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/ALINE.asmdef new file mode 100644 index 0000000..814e041 --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/ALINE.asmdef @@ -0,0 +1,82 @@ +{ + "name": "Drawing", + "rootNamespace": "", + "references": [ + "GUID:2bafac87e7f4b9b418d9448d219b01ab", + "GUID:2665a8d13d1b3f18800f46e256720795", + "GUID:d8b63aba1907145bea998dd612889d6b", + "GUID:e0cd26848372d4e5c891c569017e11f1", + "GUID:15fc0a57446b3144c949da3e2b9737a9", + "GUID:457756d89b35d2941b3e7b37b4ece6f1", + "GUID:df380645f10b7bc4b97d4f5eb6303d95" + ], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": true, + "overrideReferences": true, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [ + "MODULE_BURST", + "MODULE_MATHEMATICS", + "MODULE_COLLECTIONS" + ], + "versionDefines": [ + { + "name": "com.unity.burst", + "expression": "1.2.1-preview", + "define": "MODULE_BURST" + }, + { + "name": "com.unity.mathematics", + "expression": "1.1.0", + "define": "MODULE_MATHEMATICS" + }, + { + "name": "com.unity.collections", + "expression": "0.4.0-preview", + "define": "MODULE_COLLECTIONS" + }, + { + "name": "com.unity.collections", + "expression": "0.6.0-preview", + "define": "MODULE_COLLECTIONS_0_6_0_OR_NEWER" + }, + { + "name": "com.unity.collections", + "expression": "0.8.0-preview", + "define": "MODULE_COLLECTIONS_0_8_0_OR_NEWER" + }, + { + "name": "com.unity.render-pipelines.universal", + "expression": "0.0.0", + "define": "MODULE_RENDER_PIPELINES_UNIVERSAL" + }, + { + "name": "com.unity.render-pipelines.high-definition", + "expression": "0.0.0", + "define": "MODULE_RENDER_PIPELINES_HIGH_DEFINITION" + }, + { + "name": "com.unity.render-pipelines.high-definition", + "expression": "9.0", + "define": "MODULE_RENDER_PIPELINES_HIGH_DEFINITION_9_0_OR_NEWER" + }, + { + "name": "com.unity.collections", + "expression": "0.12.0-preview", + "define": "MODULE_COLLECTIONS_0_12_0_OR_NEWER" + }, + { + "name": "com.unity.render-pipelines.universal", + "expression": "15.0.7", + "define": "MODULE_RENDER_PIPELINES_UNIVERSAL_15_0_7_OR_NEWER" + }, + { + "name": "com.unity.render-pipelines.universal", + "expression": "17.0.0", + "define": "MODULE_RENDER_PIPELINES_UNIVERSAL_17_0_0_OR_NEWER" + } + ], + "noEngineReferences": false +}
\ No newline at end of file diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/ALINE.asmdef.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/ALINE.asmdef.meta new file mode 100644 index 0000000..8288d60 --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/ALINE.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: de4e6084e6d474788bb8c799d6b461eb +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/AlineHDRPCustomPass.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/AlineHDRPCustomPass.cs new file mode 100644 index 0000000..9f62206 --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/AlineHDRPCustomPass.cs @@ -0,0 +1,31 @@ +#if MODULE_RENDER_PIPELINES_HIGH_DEFINITION +using UnityEngine; +using UnityEngine.Rendering.HighDefinition; +using UnityEngine.Rendering; +using UnityEngine.Experimental.Rendering; + +namespace Pathfinding.Drawing { + /// <summary>Custom High Definition Render Pipeline Render Pass for ALINE</summary> + class AlineHDRPCustomPass : CustomPass { + protected override void Setup (ScriptableRenderContext renderContext, CommandBuffer cmd) { + } + +#if MODULE_RENDER_PIPELINES_HIGH_DEFINITION_9_0_OR_NEWER + protected override void Execute (CustomPassContext context) { + UnityEngine.Profiling.Profiler.BeginSample("ALINE"); + DrawingManager.instance.SubmitFrame(context.hdCamera.camera, new DrawingData.CommandBufferWrapper { cmd = context.cmd }, true); + UnityEngine.Profiling.Profiler.EndSample(); + } +#else + protected override void Execute (ScriptableRenderContext context, CommandBuffer cmd, HDCamera camera, CullingResults cullingResult) { + UnityEngine.Profiling.Profiler.BeginSample("ALINE"); + DrawingManager.instance.SubmitFrame(camera.camera, new DrawingData.CommandBufferWrapper { cmd = cmd }, true); + UnityEngine.Profiling.Profiler.EndSample(); + } +#endif + + protected override void Cleanup () { + } + } +} +#endif diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/AlineHDRPCustomPass.cs.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/AlineHDRPCustomPass.cs.meta new file mode 100644 index 0000000..e07cd43 --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/AlineHDRPCustomPass.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 87f13550b86e77241bf777114486cbd7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/AlineURPRenderPassFeature.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/AlineURPRenderPassFeature.cs new file mode 100644 index 0000000..829aa3f --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/AlineURPRenderPassFeature.cs @@ -0,0 +1,88 @@ +#if MODULE_RENDER_PIPELINES_UNIVERSAL +using UnityEngine; +using UnityEngine.Rendering; +#if MODULE_RENDER_PIPELINES_UNIVERSAL_17_0_0_OR_NEWER +using UnityEngine.Rendering.RenderGraphModule; +#endif +using UnityEngine.Rendering.Universal; + +namespace Pathfinding.Drawing { + /// <summary>Custom Universal Render Pipeline Render Pass for ALINE</summary> + public class AlineURPRenderPassFeature : ScriptableRendererFeature { + /// <summary>Custom Universal Render Pipeline Render Pass for ALINE</summary> + public class AlineURPRenderPass : ScriptableRenderPass { + /// <summary>This method is called before executing the render pass</summary> +#if MODULE_RENDER_PIPELINES_UNIVERSAL_17_0_0_OR_NEWER + [System.Obsolete] +#endif + public override void Configure (CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor) { + } + +#if MODULE_RENDER_PIPELINES_UNIVERSAL_17_0_0_OR_NEWER + [System.Obsolete] +#endif + public override void Execute (ScriptableRenderContext context, ref RenderingData renderingData) { + DrawingManager.instance.ExecuteCustomRenderPass(context, renderingData.cameraData.camera); + } + + public AlineURPRenderPass() : base() { + profilingSampler = new ProfilingSampler("ALINE"); + } + +#if MODULE_RENDER_PIPELINES_UNIVERSAL_17_0_0_OR_NEWER + private class PassData { + public Camera camera; + } + + public override void RecordRenderGraph (RenderGraph renderGraph, ContextContainer frameData) { + var cameraData = frameData.Get<UniversalCameraData>(); + var resourceData = frameData.Get<UniversalResourceData>(); + + using (IRasterRenderGraphBuilder builder = renderGraph.AddRasterRenderPass<PassData>("ALINE", out PassData passData, profilingSampler)) { + bool allowDisablingWireframe = false; + + if (Application.isEditor && (cameraData.cameraType & (CameraType.SceneView | CameraType.Preview)) != 0) { + // We need this to be able to disable wireframe rendering in the scene view + builder.AllowGlobalStateModification(true); + allowDisablingWireframe = true; + } + + builder.SetRenderAttachment(resourceData.activeColorTexture, 0); + builder.SetRenderAttachmentDepth(resourceData.activeDepthTexture); + passData.camera = cameraData.camera; + + builder.SetRenderFunc<PassData>( + (PassData data, RasterGraphContext context) => { + DrawingManager.instance.ExecuteCustomRenderGraphPass(new DrawingData.CommandBufferWrapper { cmd2 = context.cmd, allowDisablingWireframe = allowDisablingWireframe }, data.camera); + } + ); + } + } +#endif + + public override void FrameCleanup (CommandBuffer cmd) { + } + } + + AlineURPRenderPass m_ScriptablePass; + + public override void Create () { + m_ScriptablePass = new AlineURPRenderPass(); + + // Configures where the render pass should be injected. + // URP's post processing actually happens in BeforeRenderingPostProcessing, not after BeforeRenderingPostProcessing as one would expect. + // Use BeforeRenderingPostProcessing-1 to ensure this pass gets executed before post processing effects. + m_ScriptablePass.renderPassEvent = RenderPassEvent.BeforeRenderingPostProcessing-1; + } + + /// <summary>This method is called when setting up the renderer once per-camera</summary> + public override void AddRenderPasses (ScriptableRenderer renderer, ref RenderingData renderingData) { + AddRenderPasses(renderer); + } + + public void AddRenderPasses (ScriptableRenderer renderer) { + renderer.EnqueuePass(m_ScriptablePass); + } + } +} +#endif diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/AlineURPRenderPassFeature.cs.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/AlineURPRenderPassFeature.cs.meta new file mode 100644 index 0000000..7d03425 --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/AlineURPRenderPassFeature.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 241f64a005256f7e091422226bb805c5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/CommandBuilder.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/CommandBuilder.cs new file mode 100644 index 0000000..7932907 --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/CommandBuilder.cs @@ -0,0 +1,3062 @@ +using System; +using Unity.Collections.LowLevel.Unsafe; +using Unity.Mathematics; +using Unity.Jobs.LowLevel.Unsafe; +using UnityEngine; +using System.Collections.Generic; +using Unity.Burst; +using UnityEngine.Profiling; +using Unity.Collections; +using Unity.Jobs; +using UnityEngine.Rendering; +using System.Runtime.InteropServices; + +namespace Pathfinding.Drawing { + using static DrawingData; + using BitPackedMeta = DrawingData.BuilderData.BitPackedMeta; + using Pathfinding.Drawing.Text; + using Unity.Profiling; + + /// <summary> + /// Specifies text alignment relative to an anchor point. + /// + /// <code> + /// Draw.Label2D(transform.position, "Hello World", 14, LabelAlignment.TopCenter); + /// </code> + /// <code> + /// // Draw the label 20 pixels below the object + /// Draw.Label2D(transform.position, "Hello World", 14, LabelAlignment.TopCenter.withPixelOffset(0, -20)); + /// </code> + /// + /// See: <see cref="Draw.Label2D"/> + /// See: <see cref="Draw.Label3D"/> + /// </summary> + public struct LabelAlignment { + /// <summary> + /// Where on the text's bounding box to anchor the text. + /// + /// The pivot is specified in relative coordinates, where (0,0) is the bottom left corner and (1,1) is the top right corner. + /// </summary> + public float2 relativePivot; + /// <summary>How much to move the text in screen-space</summary> + public float2 pixelOffset; + + public static readonly LabelAlignment TopLeft = new LabelAlignment { relativePivot = new float2(0.0f, 1.0f), pixelOffset = new float2(0, 0) }; + public static readonly LabelAlignment MiddleLeft = new LabelAlignment { relativePivot = new float2(0.0f, 0.5f), pixelOffset = new float2(0, 0) }; + public static readonly LabelAlignment BottomLeft = new LabelAlignment { relativePivot = new float2(0.0f, 0.0f), pixelOffset = new float2(0, 0) }; + public static readonly LabelAlignment BottomCenter = new LabelAlignment { relativePivot = new float2(0.5f, 0.0f), pixelOffset = new float2(0, 0) }; + public static readonly LabelAlignment BottomRight = new LabelAlignment { relativePivot = new float2(1.0f, 0.0f), pixelOffset = new float2(0, 0) }; + public static readonly LabelAlignment MiddleRight = new LabelAlignment { relativePivot = new float2(1.0f, 0.5f), pixelOffset = new float2(0, 0) }; + public static readonly LabelAlignment TopRight = new LabelAlignment { relativePivot = new float2(1.0f, 1.0f), pixelOffset = new float2(0, 0) }; + public static readonly LabelAlignment TopCenter = new LabelAlignment { relativePivot = new float2(0.5f, 1.0f), pixelOffset = new float2(0, 0) }; + public static readonly LabelAlignment Center = new LabelAlignment { relativePivot = new float2(0.5f, 0.5f), pixelOffset = new float2(0, 0) }; + + /// <summary> + /// Moves the text by the specified amount of pixels in screen-space. + /// + /// <code> + /// // Draw the label 20 pixels below the object + /// Draw.Label2D(transform.position, "Hello World", 14, LabelAlignment.TopCenter.withPixelOffset(0, -20)); + /// </code> + /// </summary> + public LabelAlignment withPixelOffset (float x, float y) { + return new LabelAlignment { + relativePivot = this.relativePivot, + pixelOffset = new float2(x, y), + }; + } + } + + /// <summary>Maximum allowed delay for a job that is drawing to a command buffer</summary> + public enum AllowedDelay { + /// <summary> + /// If the job is not complete at the end of the frame, drawing will block until it is completed. + /// This is recommended for most jobs that are expected to complete within a single frame. + /// </summary> + EndOfFrame, + /// <summary> + /// Wait indefinitely for the job to complete, and only submit the results for rendering once it is done. + /// This is recommended for long running jobs that may take many frames to complete. + /// </summary> + Infinite, + } + + /// <summary>Some static fields that need to be in a separate class because Burst doesn't support them</summary> + static class CommandBuilderSamplers { + internal static readonly ProfilerMarker MarkerConvert = new ProfilerMarker("Convert"); + internal static readonly ProfilerMarker MarkerSetLayout = new ProfilerMarker("SetLayout"); + internal static readonly ProfilerMarker MarkerUpdateVertices = new ProfilerMarker("UpdateVertices"); + internal static readonly ProfilerMarker MarkerUpdateIndices = new ProfilerMarker("UpdateIndices"); + internal static readonly ProfilerMarker MarkerSubmesh = new ProfilerMarker("Submesh"); + internal static readonly ProfilerMarker MarkerUpdateBuffer = new ProfilerMarker("UpdateComputeBuffer"); + + internal static readonly ProfilerMarker MarkerProcessCommands = new ProfilerMarker("Commands"); + internal static readonly ProfilerMarker MarkerCreateTriangles = new ProfilerMarker("CreateTriangles"); + } + + /// <summary> + /// Builder for drawing commands. + /// You can use this to queue many drawing commands. The commands will be queued for rendering when you call the Dispose method. + /// It is recommended that you use the <a href="https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/using-statement">using statement</a> which automatically calls the Dispose method. + /// + /// <code> + /// // Create a new CommandBuilder + /// using (var draw = DrawingManager.GetBuilder()) { + /// // Use the exact same API as the global Draw class + /// draw.WireBox(Vector3.zero, Vector3.one); + /// } + /// </code> + /// + /// Warning: You must call either <see cref="Dispose"/> or <see cref="DiscardAndDispose"/> when you are done with this object to avoid memory leaks. + /// </summary> + [StructLayout(LayoutKind.Sequential)] + [BurstCompile] + public partial struct CommandBuilder : IDisposable { + // Note: Many fields/methods are explicitly marked as private. This is because doxygen otherwise thinks they are public by default (like struct members are in c++) + + [NativeDisableUnsafePtrRestriction] + internal unsafe UnsafeAppendBuffer* buffer; + + private GCHandle gizmos; + + [NativeSetThreadIndex] + private int threadIndex; + + private DrawingData.BuilderData.BitPackedMeta uniqueID; + + internal unsafe CommandBuilder(UnsafeAppendBuffer* buffer, GCHandle gizmos, int threadIndex, DrawingData.BuilderData.BitPackedMeta uniqueID) { + this.buffer = buffer; + this.gizmos = gizmos; + this.threadIndex = threadIndex; + this.uniqueID = uniqueID; + } + + + internal CommandBuilder(DrawingData gizmos, Hasher hasher, RedrawScope frameRedrawScope, RedrawScope customRedrawScope, bool isGizmos, bool isBuiltInCommandBuilder, int sceneModeVersion) { + // We need to use a GCHandle instead of a normal reference to be able to pass this object to burst compiled function pointers. + // The NativeSetClassTypeToNullOnSchedule unfortunately only works together with the job system, not with raw functions. + this.gizmos = GCHandle.Alloc(gizmos, GCHandleType.Normal); + + threadIndex = 0; + uniqueID = gizmos.data.Reserve(isBuiltInCommandBuilder); + gizmos.data.Get(uniqueID).Init(hasher, frameRedrawScope, customRedrawScope, isGizmos, gizmos.GetNextDrawOrderIndex(), sceneModeVersion); + unsafe { + buffer = gizmos.data.Get(uniqueID).bufferPtr; + } + } + + internal unsafe int BufferSize { + get { + return buffer->Length; + } + set { + buffer->Length = value; + } + } + + /// <summary> + /// Wrapper for drawing in the XY plane. + /// + /// <code> + /// 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); + /// </code> + /// + /// See: 2d-drawing (view in online documentation for working links) + /// See: <see cref="Draw.xz"/> + /// </summary> + public CommandBuilder2D xy => new CommandBuilder2D(this, true); + + /// <summary> + /// Wrapper for drawing in the XZ plane. + /// + /// <code> + /// 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); + /// </code> + /// + /// See: 2d-drawing (view in online documentation for working links) + /// See: <see cref="Draw.xy"/> + /// </summary> + public CommandBuilder2D xz => new CommandBuilder2D(this, false); + + static readonly float3 DEFAULT_UP = new float3(0, 1, 0); + + /// <summary> + /// Can be set to render specifically to these cameras. + /// If you set this property to an array of cameras then this command builder will only be rendered + /// to the specified cameras. Setting this property bypasses <see cref="Drawing.DrawingManager.allowRenderToRenderTextures"/>. + /// The camera will be rendered to even if it renders to a render texture. + /// + /// A null value indicates that all valid cameras should be rendered to. This is the default value. + /// + /// <code> + /// var draw = DrawingManager.GetBuilder(true); + /// + /// draw.cameraTargets = new Camera[] { myCamera }; + /// // This sphere will only be rendered to myCamera + /// draw.WireSphere(Vector3.zero, 0.5f, Color.black); + /// draw.Dispose(); + /// </code> + /// + /// See: advanced (view in online documentation for working links) + /// </summary> + public Camera[] cameraTargets { + get { + if (gizmos.IsAllocated && gizmos.Target != null) { + var target = gizmos.Target as DrawingData; + if (target.data.StillExists(uniqueID)) { + return target.data.Get(uniqueID).meta.cameraTargets; + } + } + throw new System.Exception("Cannot get cameraTargets because the command builder has already been disposed or does not exist."); + } + set { + if (uniqueID.isBuiltInCommandBuilder) throw new System.Exception("You cannot set the camera targets for a built-in command builder. Create a custom command builder instead."); + if (gizmos.IsAllocated && gizmos.Target != null) { + var target = gizmos.Target as DrawingData; + if (!target.data.StillExists(uniqueID)) { + throw new System.Exception("Cannot set cameraTargets because the command builder has already been disposed or does not exist."); + } + target.data.Get(uniqueID).meta.cameraTargets = value; + } + } + } + + /// <summary>Submits this command builder for rendering</summary> + public void Dispose () { + if (uniqueID.isBuiltInCommandBuilder) throw new System.Exception("You cannot dispose a built-in command builder"); + DisposeInternal(); + } + + /// <summary> + /// Disposes this command builder after the given job has completed. + /// + /// This is convenient if you are using the entity-component-system/burst in Unity and don't know exactly when the job will complete. + /// + /// You will not be able to use this command builder on the main thread anymore. + /// + /// See: job-system (view in online documentation for working links) + /// </summary> + /// <param name="dependency">The job that must complete before this command builder is disposed.</param> + /// <param name="allowedDelay">Whether to block on this dependency before rendering the current frame or not. + /// If the job is expected to complete during a single frame, leave at the default of \reflink{AllowedDelay.EndOfFrame}. + /// But if the job is expected to take multiple frames to complete, you can set this to \reflink{AllowedDelay.Infinite}.</param> + public void DisposeAfter (JobHandle dependency, AllowedDelay allowedDelay = AllowedDelay.EndOfFrame) { + if (!gizmos.IsAllocated) throw new System.Exception("You cannot dispose an invalid command builder. Are you trying to dispose it twice?"); + try { + if (gizmos.IsAllocated && gizmos.Target != null) { + var target = gizmos.Target as DrawingData; + if (!target.data.StillExists(uniqueID)) { + throw new System.Exception("Cannot dispose the command builder because the drawing manager has been destroyed"); + } + target.data.Get(uniqueID).SubmitWithDependency(gizmos, dependency, allowedDelay); + } + } finally { + this = default; + } + } + + internal void DisposeInternal () { + if (!gizmos.IsAllocated) throw new System.Exception("You cannot dispose an invalid command builder. Are you trying to dispose it twice?"); + try { + if (gizmos.IsAllocated && gizmos.Target != null) { + var target = gizmos.Target as DrawingData; + if (!target.data.StillExists(uniqueID)) { + throw new System.Exception("Cannot dispose the command builder because the drawing manager has been destroyed"); + } + target.data.Get(uniqueID).Submit(gizmos.Target as DrawingData); + } + } finally { + gizmos.Free(); + this = default; + } + } + + /// <summary> + /// Discards the contents of this command builder without rendering anything. + /// If you are not going to draw anything (i.e. you do not call the <see cref="Dispose"/> method) then you must call this method to avoid + /// memory leaks. + /// </summary> + public void DiscardAndDispose () { + if (uniqueID.isBuiltInCommandBuilder) throw new System.Exception("You cannot dispose a built-in command builder"); + DiscardAndDisposeInternal(); + } + + internal void DiscardAndDisposeInternal () { + try { + if (gizmos.IsAllocated && gizmos.Target != null) { + var target = gizmos.Target as DrawingData; + if (!target.data.StillExists(uniqueID)) { + throw new System.Exception("Cannot dispose the command builder because the drawing manager has been destroyed"); + } + target.data.Release(uniqueID); + } + } finally { + if (gizmos.IsAllocated) gizmos.Free(); + this = default; + } + } + + /// <summary> + /// Pre-allocates the internal buffer to an additional size bytes. + /// This can give you a minor performance boost if you are drawing a lot of things. + /// + /// Note: Only resizes the buffer for the current thread. + /// </summary> + public void Preallocate (int size) { + Reserve(size); + } + + /// <summary>Internal rendering command</summary> + [System.Flags] + internal enum Command { + PushColorInline = 1 << 8, + PushColor = 0, + PopColor, + PushMatrix, + PushSetMatrix, + PopMatrix, + Line, + Circle, + CircleXZ, + Disc, + DiscXZ, + SphereOutline, + Box, + WirePlane, + WireBox, + SolidTriangle, + PushPersist, + PopPersist, + Text, + Text3D, + PushLineWidth, + PopLineWidth, + CaptureState, + } + + internal struct TriangleData { + public float3 a, b, c; + } + + /// <summary>Holds rendering data for a line</summary> + internal struct LineData { + public float3 a, b; + } + + internal struct LineDataV3 { + public Vector3 a, b; + } + + /// <summary>Holds rendering data for a circle</summary> + internal struct CircleXZData { + public float3 center; + public float radius, startAngle, endAngle; + } + + /// <summary>Holds rendering data for a circle</summary> + internal struct CircleData { + public float3 center; + public float3 normal; + public float radius; + } + + /// <summary>Holds rendering data for a sphere</summary> + internal struct SphereData { + public float3 center; + public float radius; + } + + /// <summary>Holds rendering data for a box</summary> + internal struct BoxData { + public float3 center; + public float3 size; + } + + internal struct PlaneData { + public float3 center; + public quaternion rotation; + public float2 size; + } + + internal struct PersistData { + public float endTime; + } + + internal struct LineWidthData { + public float pixels; + public bool automaticJoins; + } + + + + internal struct TextData { + public float3 center; + public LabelAlignment alignment; + public float sizeInPixels; + public int numCharacters; + } + + internal struct TextData3D { + public float3 center; + public quaternion rotation; + public LabelAlignment alignment; + public float size; + public int numCharacters; + } + + /// <summary>Ensures the buffer has room for at least N more bytes</summary> + [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + private void Reserve (int additionalSpace) { + unsafe { + if (Unity.Burst.CompilerServices.Hint.Unlikely(threadIndex >= 0)) { +#if ENABLE_UNITY_COLLECTIONS_CHECKS + if (threadIndex < 0 || threadIndex >= JobsUtility.MaxJobThreadCount) throw new System.Exception("Thread index outside the expected range"); + if (threadIndex > 0 && uniqueID.isBuiltInCommandBuilder) throw new System.Exception("You should use a custom command builder when using the Unity Job System. Take a look at the documentation for more info."); + if (buffer == null) throw new System.Exception("CommandBuilder does not have a valid buffer. Is it properly initialized?"); + + // Exploit the fact that right after this package has drawn gizmos the buffers will be empty + // and the next task is that Unity will render its own internal gizmos. + // We can therefore easily (and without a high performance cost) + // trap accidental Draw.* calls from OnDrawGizmos functions + // by doing this check when the first Reserve call is made. + AssertNotRendering(); +#endif + + buffer += threadIndex; + threadIndex = -1; + } + + var newLength = buffer->Length + additionalSpace; + if (newLength > buffer->Capacity) { +#if ENABLE_UNITY_COLLECTIONS_CHECKS + // This really should run every time we access the buffer... but that would be a bit slow + // This code will catch the error eventually. + AssertBufferExists(); + const int MAX_BUFFER_SIZE = 1024 * 1024 * 256; // 256 MB + if (buffer->Length * 2 > MAX_BUFFER_SIZE) { + throw new System.Exception("CommandBuilder buffer is very large. Are you trying to draw things in an infinite loop?"); + } +#endif + buffer->SetCapacity(math.max(newLength, buffer->Length * 2)); + } + } + } + + [BurstDiscard] + private void AssertBufferExists () { + if (!gizmos.IsAllocated || gizmos.Target == null || !(gizmos.Target as DrawingData).data.StillExists(uniqueID)) { + // This command builder is invalid, clear all data on it to prevent it being used again + this = default; + throw new System.Exception("This command builder no longer exists. Are you trying to draw to a command builder which has already been disposed?"); + } + } + + [BurstDiscard] + static void AssertNotRendering () { + // Some checking to see if drawing is being done from inside OnDrawGizmos + // This check is relatively fast (about 0.05 ms), but we still do it only every 128th frame for performance reasons + if (!GizmoContext.drawingGizmos && !JobsUtility.IsExecutingJob && (Time.renderedFrameCount & 127) == 0) { + // Inspect the stack-trace to be able to provide more helpful error messages + var st = StackTraceUtility.ExtractStackTrace(); + if (st.Contains("OnDrawGizmos")) { + throw new System.Exception("You are trying to use Draw.* functions from within Unity's OnDrawGizmos function. Use this package's gizmo callbacks instead (see the documentation)."); + } + } + } + + [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + internal void Reserve<A>() where A : struct { + Reserve(UnsafeUtility.SizeOf<Command>() + UnsafeUtility.SizeOf<A>()); + } + + [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + internal void Reserve<A, B>() where A : struct where B : struct { + Reserve(UnsafeUtility.SizeOf<Command>() * 2 + UnsafeUtility.SizeOf<A>() + UnsafeUtility.SizeOf<B>()); + } + + [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + internal void Reserve<A, B, C>() where A : struct where B : struct where C : struct { + Reserve(UnsafeUtility.SizeOf<Command>() * 3 + UnsafeUtility.SizeOf<A>() + UnsafeUtility.SizeOf<B>() + UnsafeUtility.SizeOf<C>()); + } + + /// <summary> + /// Converts a Color to a Color32. + /// This method is faster than Unity's native color conversion, especially when using Burst. + /// </summary> + [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + internal static unsafe uint ConvertColor (Color color) { + // If SSE2 is supported (which it is on essentially all X86 CPUs) + // then we can use a much faster conversion from Color to Color32. + // This will only be possible inside Burst. + if (Unity.Burst.Intrinsics.X86.Sse2.IsSse2Supported) { + // Convert from 0-1 float range to 0-255 integer range + var ci = (int4)(255 * new float4(color.r, color.g, color.b, color.a) + 0.5f); + var v32 = new Unity.Burst.Intrinsics.v128(ci.x, ci.y, ci.z, ci.w); + // Convert four 32-bit numbers to four 16-bit numbers + var v16 = Unity.Burst.Intrinsics.X86.Sse2.packs_epi32(v32, v32); + // Convert four 16-bit numbers to four 8-bit numbers + var v8 = Unity.Burst.Intrinsics.X86.Sse2.packus_epi16(v16, v16); + return v8.UInt0; + } else { + // If we don't have SSE2 (most likely we are not running inside Burst), + // then we will do a manual conversion from Color to Color32. + // This is significantly faster than just casting to a Color32. + var r = (uint)Mathf.Clamp((int)(color.r*255f + 0.5f), 0, 255); + var g = (uint)Mathf.Clamp((int)(color.g*255f + 0.5f), 0, 255); + var b = (uint)Mathf.Clamp((int)(color.b*255f + 0.5f), 0, 255); + var a = (uint)Mathf.Clamp((int)(color.a*255f + 0.5f), 0, 255); + return (a << 24) | (b << 16) | (g << 8) | r; + } + } + + internal unsafe void Add<T>(T value) where T : struct { + int num = UnsafeUtility.SizeOf<T>(); + var buffer = this.buffer; + var bufferSize = buffer->Length; + // We assume this because the Reserve function has already taken care of that. + // This removes a few branches from the assembly when running in burst. + Unity.Burst.CompilerServices.Hint.Assume(buffer->Ptr != null); + Unity.Burst.CompilerServices.Hint.Assume(buffer->Ptr + bufferSize != null); + + unsafe { + UnsafeUtility.CopyStructureToPtr(ref value, (void*)((byte*)buffer->Ptr + bufferSize)); + buffer->Length = bufferSize + num; + } + } + + public struct ScopeMatrix : IDisposable { + internal CommandBuilder builder; + public void Dispose () { +#if ENABLE_UNITY_COLLECTIONS_CHECKS + if (!builder.gizmos.IsAllocated || !(builder.gizmos.Target is DrawingData data) || !data.data.StillExists(builder.uniqueID)) throw new System.InvalidOperationException("The drawing instance this matrix scope belongs to no longer exists. Matrix scopes cannot survive for longer than a frame unless you have a custom drawing instance. Are you using a matrix scope inside a coroutine?"); +#endif + unsafe { + builder.PopMatrix(); + builder.buffer = null; + } + } + } + + public struct ScopeColor : IDisposable { + internal CommandBuilder builder; + public void Dispose () { +#if ENABLE_UNITY_COLLECTIONS_CHECKS + if (!builder.gizmos.IsAllocated || !(builder.gizmos.Target is DrawingData data) || !data.data.StillExists(builder.uniqueID)) throw new System.InvalidOperationException("The drawing instance this color scope belongs to no longer exists. Color scopes cannot survive for longer than a frame unless you have a custom drawing instance. Are you using a color scope inside a coroutine?"); +#endif + unsafe { + builder.PopColor(); + builder.buffer = null; + } + } + } + + public struct ScopePersist : IDisposable { + internal CommandBuilder builder; + public void Dispose () { +#if ENABLE_UNITY_COLLECTIONS_CHECKS + if (!builder.gizmos.IsAllocated || !(builder.gizmos.Target is DrawingData data) || !data.data.StillExists(builder.uniqueID)) throw new System.InvalidOperationException("The drawing instance this persist scope belongs to no longer exists. Persist scopes cannot survive for longer than a frame unless you have a custom drawing instance. Are you using a persist scope inside a coroutine?"); +#endif + unsafe { + builder.PopDuration(); + builder.buffer = null; + } + } + } + + /// <summary> + /// Scope that does nothing. + /// Used for optimization in standalone builds. + /// </summary> + public struct ScopeEmpty : IDisposable { + public void Dispose () { + } + } + + public struct ScopeLineWidth : IDisposable { + internal CommandBuilder builder; + public void Dispose () { +#if ENABLE_UNITY_COLLECTIONS_CHECKS + if (!builder.gizmos.IsAllocated || !(builder.gizmos.Target is DrawingData data) || !data.data.StillExists(builder.uniqueID)) throw new System.InvalidOperationException("The drawing instance this line width scope belongs to no longer exists. Line width scopes cannot survive for longer than a frame unless you have a custom drawing instance. Are you using a line width scope inside a coroutine?"); +#endif + unsafe { + builder.PopLineWidth(); + builder.buffer = null; + } + } + } + + /// <summary> + /// Scope to draw multiple things with an implicit matrix transformation. + /// All coordinates for items drawn inside the scope will be multiplied by the matrix. + /// If WithMatrix scopes are nested then coordinates are multiplied by all nested matrices in order. + /// + /// <code> + /// using (Draw.InLocalSpace(transform)) { + /// // Draw a box at (0,0,0) relative to the current object + /// // This means it will show up at the object's position + /// Draw.WireBox(Vector3.zero, Vector3.one); + /// } + /// + /// // Equivalent code using the lower level WithMatrix scope + /// using (Draw.WithMatrix(transform.localToWorldMatrix)) { + /// Draw.WireBox(Vector3.zero, Vector3.one); + /// } + /// </code> + /// + /// See: <see cref="InLocalSpace"/> + /// </summary> + [BurstDiscard] + public ScopeMatrix WithMatrix (Matrix4x4 matrix) { + PushMatrix(matrix); + // TODO: Keep track of alive scopes and prevent dispose unless all scopes have been disposed + unsafe { + return new ScopeMatrix { builder = this }; + } + } + + /// <summary> + /// Scope to draw multiple things with an implicit matrix transformation. + /// All coordinates for items drawn inside the scope will be multiplied by the matrix. + /// If WithMatrix scopes are nested then coordinates are multiplied by all nested matrices in order. + /// + /// <code> + /// using (Draw.InLocalSpace(transform)) { + /// // Draw a box at (0,0,0) relative to the current object + /// // This means it will show up at the object's position + /// Draw.WireBox(Vector3.zero, Vector3.one); + /// } + /// + /// // Equivalent code using the lower level WithMatrix scope + /// using (Draw.WithMatrix(transform.localToWorldMatrix)) { + /// Draw.WireBox(Vector3.zero, Vector3.one); + /// } + /// </code> + /// + /// See: <see cref="InLocalSpace"/> + /// </summary> + [BurstDiscard] + public ScopeMatrix WithMatrix (float3x3 matrix) { + PushMatrix(new float4x4(matrix, float3.zero)); + // TODO: Keep track of alive scopes and prevent dispose unless all scopes have been disposed + unsafe { + return new ScopeMatrix { builder = this }; + } + } + + /// <summary> + /// Scope to draw multiple things with the same color. + /// + /// <code> + /// void Update () { + /// using (Draw.WithColor(Color.red)) { + /// Draw.Line(new Vector3(0, 0, 0), new Vector3(1, 1, 1)); + /// Draw.Line(new Vector3(0, 0, 0), new Vector3(0, 1, 2)); + /// } + /// } + /// </code> + /// + /// Any command that is passed an explicit color parameter will override this color. + /// If another color scope is nested inside this one then that scope will override this color. + /// </summary> + [BurstDiscard] + public ScopeColor WithColor (Color color) { + PushColor(color); + unsafe { + return new ScopeColor { builder = this }; + } + } + + /// <summary> + /// Scope to draw multiple things for a longer period of time. + /// + /// Normally drawn items will only be rendered for a single frame. + /// Using a persist scope you can make the items be drawn for any amount of time. + /// + /// <code> + /// void Update () { + /// using (Draw.WithDuration(1.0f)) { + /// var offset = Time.time; + /// Draw.Line(new Vector3(offset, 0, 0), new Vector3(offset, 0, 1)); + /// } + /// } + /// </code> + /// + /// Note: Outside of play mode the duration is measured against Unity's Time.realtimeSinceStartup. + /// + /// Warning: It is recommended not to use this inside a DrawGizmos callback since DrawGizmos is called every frame anyway. + /// </summary> + /// <param name="duration">How long the drawn items should persist in seconds.</param> + + [BurstDiscard] + public ScopePersist WithDuration (float duration) { + PushDuration(duration); + unsafe { + return new ScopePersist { builder = this }; + } + } + + /// <summary> + /// Scope to draw multiple things with a given line width. + /// + /// Note that the line join algorithm is a quite simple one optimized for speed. It normally looks good on a 2D plane, but if the polylines curve a lot in 3D space then + /// it can look odd from some angles. + /// + /// [Open online documentation to see images] + /// + /// In the picture the top row has automaticJoins enabled and in the bottom row it is disabled. + /// </summary> + /// <param name="pixels">Line width in pixels</param> + /// <param name="automaticJoins">If true then sequences of lines that are adjacent will be automatically joined at their vertices. This typically produces nicer polylines without weird gaps.</param> + [BurstDiscard] + public ScopeLineWidth WithLineWidth (float pixels, bool automaticJoins = true) { + PushLineWidth(pixels, automaticJoins); + unsafe { + return new ScopeLineWidth { builder = this }; + } + } + + /// <summary> + /// Scope to draw multiple things relative to a transform object. + /// All coordinates for items drawn inside the scope will be multiplied by the transform's localToWorldMatrix. + /// + /// <code> + /// void Update () { + /// using (Draw.InLocalSpace(transform)) { + /// // Draw a box at (0,0,0) relative to the current object + /// // This means it will show up at the object's position + /// // The box is also rotated and scaled with the transform + /// Draw.WireBox(Vector3.zero, Vector3.one); + /// } + /// } + /// </code> + /// + /// [Open online documentation to see videos] + /// </summary> + [BurstDiscard] + public ScopeMatrix InLocalSpace (Transform transform) { + return WithMatrix(transform.localToWorldMatrix); + } + + /// <summary> + /// Scope to draw multiple things in screen space of a camera. + /// If you draw 2D coordinates (i.e. (x,y,0)) they will be projected onto a plane approximately [2*near clip plane of the camera] world units in front of the camera (but guaranteed to be between the near and far planes). + /// + /// The lower left corner of the camera is (0,0,0) and the upper right is (camera.pixelWidth, camera.pixelHeight, 0) + /// + /// Note: As a corollary, the centers of pixels are offset by 0.5. So for example the center of the top left pixel is at (0.5, 0.5, 0). + /// Therefore, if you want to draw 1 pixel wide lines in screen space, you may want to offset the coordinates by 0.5 pixels. + /// + /// See: <see cref="InLocalSpace"/> + /// See: <see cref="WithMatrix"/> + /// </summary> + [BurstDiscard] + public ScopeMatrix InScreenSpace (Camera camera) { + return WithMatrix(camera.cameraToWorldMatrix * camera.nonJitteredProjectionMatrix.inverse * Matrix4x4.TRS(new Vector3(-1.0f, -1.0f, 0), Quaternion.identity, new Vector3(2.0f/camera.pixelWidth, 2.0f/camera.pixelHeight, 1))); + } + + /// <summary> + /// Multiply all coordinates until the next PopMatrix with the given matrix. + /// This differs from <see cref="PushSetMatrix"/> in that this stacks with all previously pushed matrices while <see cref="PushSetMatrix"/> does not. + /// </summary> + public void PushMatrix (Matrix4x4 matrix) { + Reserve<float4x4>(); + Add(Command.PushMatrix); + Add(matrix); + } + + /// <summary> + /// Multiply all coordinates until the next PopMatrix with the given matrix. + /// This differs from <see cref="PushSetMatrix"/> in that this stacks with all previously pushed matrices while <see cref="PushSetMatrix"/> does not. + /// </summary> + public void PushMatrix (float4x4 matrix) { + Reserve<float4x4>(); + Add(Command.PushMatrix); + Add(matrix); + } + + /// <summary> + /// Multiply all coordinates until the next PopMatrix with the given matrix. + /// This differs from <see cref="PushMatrix"/> in that this sets the current matrix directly while <see cref="PushMatrix"/> stacks with all previously pushed matrices. + /// </summary> + public void PushSetMatrix (Matrix4x4 matrix) { + Reserve<float4x4>(); + Add(Command.PushSetMatrix); + Add((float4x4)matrix); + } + + /// <summary> + /// Multiply all coordinates until the next PopMatrix with the given matrix. + /// This differs from <see cref="PushMatrix"/> in that this sets the current matrix directly while <see cref="PushMatrix"/> stacks with all previously pushed matrices. + /// </summary> + public void PushSetMatrix (float4x4 matrix) { + Reserve<float4x4>(); + Add(Command.PushSetMatrix); + Add(matrix); + } + + /// <summary>Pops a matrix from the stack</summary> + public void PopMatrix () { + Reserve(4); + Add(Command.PopMatrix); + } + + /// <summary> + /// Draws everything until the next PopColor with the given color. + /// Any command that is passed an explicit color parameter will override this color. + /// If another color scope is nested inside this one then that scope will override this color. + /// </summary> + public void PushColor (Color color) { + Reserve<Color32>(); + Add(Command.PushColor); + Add(ConvertColor(color)); + } + + /// <summary>Pops a color from the stack</summary> + public void PopColor () { + Reserve(4); + Add(Command.PopColor); + } + + /// <summary> + /// Draws everything until the next PopDuration for a number of seconds. + /// Warning: This is not recommended inside a DrawGizmos callback since DrawGizmos is called every frame anyway. + /// </summary> + public void PushDuration (float duration) { + Reserve<PersistData>(); + Add(Command.PushPersist); + // We must use the BurstTime variable which is updated more rarely than Time.time. + // This is necessary because this code may be called from a burst job or from a different thread. + // Time.time can only be accessed in the main thread. + Add(new PersistData { endTime = SharedDrawingData.BurstTime.Data + duration }); + } + + /// <summary>Pops a duration scope from the stack</summary> + public void PopDuration () { + Reserve(4); + Add(Command.PopPersist); + } + + /// <summary> + /// Draws everything until the next PopPersist for a number of seconds. + /// Warning: This is not recommended inside a DrawGizmos callback since DrawGizmos is called every frame anyway. + /// + /// Deprecated: Renamed to <see cref="PushDuration"/> + /// </summary> + [System.Obsolete("Renamed to PushDuration for consistency")] + public void PushPersist (float duration) { + PushDuration(duration); + } + + /// <summary> + /// Pops a persist scope from the stack. + /// Deprecated: Renamed to <see cref="PopDuration"/> + /// </summary> + [System.Obsolete("Renamed to PopDuration for consistency")] + public void PopPersist () { + PopDuration(); + } + + /// <summary> + /// Draws all lines until the next PopLineWidth with a given line width in pixels. + /// + /// Note that the line join algorithm is a quite simple one optimized for speed. It normally looks good on a 2D plane, but if the polylines curve a lot in 3D space then + /// it can look odd from some angles. + /// + /// [Open online documentation to see images] + /// + /// In the picture the top row has automaticJoins enabled and in the bottom row it is disabled. + /// </summary> + /// <param name="pixels">Line width in pixels</param> + /// <param name="automaticJoins">If true then sequences of lines that are adjacent will be automatically joined at their vertices. This typically produces nicer polylines without weird gaps.</param> + public void PushLineWidth (float pixels, bool automaticJoins = true) { + if (pixels < 0) throw new System.ArgumentOutOfRangeException("pixels", "Line width must be positive"); + + Reserve<LineWidthData>(); + Add(Command.PushLineWidth); + Add(new LineWidthData { pixels = pixels, automaticJoins = automaticJoins }); + } + + /// <summary>Pops a line width scope from the stack</summary> + public void PopLineWidth () { + Reserve(4); + Add(Command.PopLineWidth); + } + + /// <summary> + /// Draws a line between two points. + /// + /// [Open online documentation to see images] + /// + /// <code> + /// void Update () { + /// Draw.Line(Vector3.zero, Vector3.up); + /// } + /// </code> + /// </summary> + public void Line (float3 a, float3 b) { + Reserve<LineData>(); + Add(Command.Line); + Add(new LineData { a = a, b = b }); + } + + /// <summary> + /// Draws a line between two points. + /// + /// [Open online documentation to see images] + /// + /// <code> + /// void Update () { + /// Draw.Line(Vector3.zero, Vector3.up); + /// } + /// </code> + /// </summary> + public void Line (Vector3 a, Vector3 b) { + Reserve<LineData>(); + // Add(Command.Line); + // Add(new LineDataV3 { 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%. + var bufferSize = BufferSize; + + unsafe { + 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 = (LineDataV3*)(ptr + 4); + lineData->a = a; + lineData->b = b; + buffer->Length = newLen; + } + } + + /// <summary> + /// Draws a line between two points. + /// + /// [Open online documentation to see images] + /// + /// <code> + /// void Update () { + /// Draw.Line(Vector3.zero, Vector3.up); + /// } + /// </code> + /// </summary> + public void Line (Vector3 a, Vector3 b, Color color) { + Reserve<Color32, LineData>(); + // Add(Command.Line | Command.PushColorInline); + // Add(ConvertColor(color)); + // Add(new LineDataV3 { a = a, b = b }); + + // The code below is equivalent to the code which is commented out 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%. + var bufferSize = BufferSize; + + unsafe { + 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) = ConvertColor(color); + var lineData = (LineDataV3*)(ptr + 8); + lineData->a = a; + lineData->b = b; + buffer->Length = newLen; + } + } + + /// <summary> + /// Draws a ray starting at a point and going in the given direction. + /// The ray will end at origin + direction. + /// + /// [Open online documentation to see images] + /// + /// <code> + /// Draw.Ray(Vector3.zero, Vector3.up); + /// </code> + /// </summary> + public void Ray (float3 origin, float3 direction) { + Line(origin, origin + direction); + } + + /// <summary> + /// Draws a ray with a given length. + /// + /// [Open online documentation to see images] + /// + /// <code> + /// Draw.Ray(Camera.main.ScreenPointToRay(Vector3.zero), 10); + /// </code> + /// </summary> + public void Ray (Ray ray, float length) { + Line(ray.origin, ray.origin + ray.direction * length); + } + + /// <summary> + /// Draws an arc between two points. + /// + /// The rendered arc is the shortest arc between the two points. + /// The radius of the arc will be equal to the distance between center and start. + /// + /// [Open online documentation to see images] + /// <code> + /// float a1 = Mathf.PI*0.9f; + /// float a2 = Mathf.PI*0.1f; + /// var arcStart = new float3(Mathf.Cos(a1), 0, Mathf.Sin(a1)); + /// var arcEnd = new float3(Mathf.Cos(a2), 0, Mathf.Sin(a2)); + /// Draw.Arc(new float3(0, 0, 0), arcStart, arcEnd, color); + /// </code> + /// + /// See: <see cref="CommandBuilder2D.Circle(float3,float,float,float)"/> + /// </summary> + /// <param name="center">Center of the imaginary circle that the arc is part of.</param> + /// <param name="start">Starting point of the arc.</param> + /// <param name="end">End point of the arc.</param> + public void Arc (float3 center, float3 start, float3 end) { + var d1 = start - center; + var d2 = end - center; + var normal = math.cross(d2, d1); + + if (math.any(normal != 0) && math.all(math.isfinite(normal))) { + var m = Matrix4x4.TRS(center, Quaternion.LookRotation(d1, normal), Vector3.one); + var angle = Vector3.SignedAngle(d1, d2, normal) * Mathf.Deg2Rad; + PushMatrix(m); + CircleXZInternal(float3.zero, math.length(d1), 90 * Mathf.Deg2Rad, 90 * Mathf.Deg2Rad - angle); + PopMatrix(); + } + } + + /// <summary> + /// Draws a circle in the XZ plane. + /// + /// You can draw an arc by supplying the startAngle and endAngle parameters. + /// + /// [Open online documentation to see images] + /// + /// See: <see cref="Circle(float3,float3,float)"/> + /// See: <see cref="CircleXY(float3,float,float,float)"/> + /// See: <see cref="Arc(float3,float3,float3)"/> + /// </summary> + /// <param name="center">Center of the circle or arc.</param> + /// <param name="radius">Radius of the circle or arc.</param> + /// <param name="startAngle">Starting angle in radians. 0 corrsponds to the positive X axis.</param> + /// <param name="endAngle">End angle in radians.</param> + [System.Obsolete("Use Draw.xz.Circle instead")] + public void CircleXZ (float3 center, float radius, float startAngle = 0f, float endAngle = 2 * Mathf.PI) { + CircleXZInternal(center, radius, startAngle, endAngle); + } + + internal void CircleXZInternal (float3 center, float radius, float startAngle = 0f, float endAngle = 2 * Mathf.PI) { + Reserve<CircleXZData>(); + Add(Command.CircleXZ); + Add(new CircleXZData { center = center, radius = radius, startAngle = startAngle, endAngle = endAngle }); + } + + internal void CircleXZInternal (float3 center, float radius, float startAngle, float endAngle, Color color) { + Reserve<Color32, CircleXZData>(); + Add(Command.CircleXZ | Command.PushColorInline); + Add(ConvertColor(color)); + Add(new CircleXZData { center = center, radius = radius, startAngle = startAngle, endAngle = endAngle }); + } + + internal static readonly float4x4 XZtoXYPlaneMatrix = float4x4.RotateX(-math.PI*0.5f); + internal static readonly float4x4 XZtoYZPlaneMatrix = float4x4.RotateZ(math.PI*0.5f); + + /// <summary> + /// Draws a circle in the XY plane. + /// + /// You can draw an arc by supplying the startAngle and endAngle parameters. + /// + /// [Open online documentation to see images] + /// + /// See: <see cref="Circle(float3,float3,float)"/> + /// See: <see cref="Arc(float3,float3,float3)"/> + /// </summary> + /// <param name="center">Center of the circle or arc.</param> + /// <param name="radius">Radius of the circle or arc.</param> + /// <param name="startAngle">Starting angle in radians. 0 corrsponds to the positive X axis.</param> + /// <param name="endAngle">End angle in radians.</param> + [System.Obsolete("Use Draw.xy.Circle instead")] + public void CircleXY (float3 center, float radius, float startAngle = 0f, float endAngle = 2 * Mathf.PI) { + PushMatrix(XZtoXYPlaneMatrix); + CircleXZ(new float3(center.x, -center.z, center.y), radius, startAngle, endAngle); + PopMatrix(); + } + + /// <summary> + /// Draws a circle. + /// + /// [Open online documentation to see images] + /// + /// Note: This overload does not allow you to draw an arc. For that purpose use <see cref="Arc"/>, <see cref="CircleXY"/> or <see cref="CircleXZ"/> instead. + /// </summary> + public void Circle (float3 center, float3 normal, float radius) { + Reserve<CircleData>(); + Add(Command.Circle); + Add(new CircleData { center = center, normal = normal, radius = radius }); + } + + /// <summary> + /// Draws a solid arc between two points. + /// + /// The rendered arc is the shortest arc between the two points. + /// The radius of the arc will be equal to the distance between center and start. + /// + /// [Open online documentation to see images] + /// <code> + /// float a1 = Mathf.PI*0.9f; + /// float a2 = Mathf.PI*0.1f; + /// var arcStart = new float3(Mathf.Cos(a1), 0, Mathf.Sin(a1)); + /// var arcEnd = new float3(Mathf.Cos(a2), 0, Mathf.Sin(a2)); + /// Draw.SolidArc(new float3(0, 0, 0), arcStart, arcEnd, color); + /// </code> + /// + /// See: <see cref="CommandBuilder2D.SolidCircle(float3,float,float,float)"/> + /// </summary> + /// <param name="center">Center of the imaginary circle that the arc is part of.</param> + /// <param name="start">Starting point of the arc.</param> + /// <param name="end">End point of the arc.</param> + public void SolidArc (float3 center, float3 start, float3 end) { + var d1 = start - center; + var d2 = end - center; + var normal = math.cross(d2, d1); + + if (math.any(normal)) { + var m = Matrix4x4.TRS(center, Quaternion.LookRotation(d1, normal), Vector3.one); + var angle = Vector3.SignedAngle(d1, d2, normal) * Mathf.Deg2Rad; + PushMatrix(m); + SolidCircleXZInternal(float3.zero, math.length(d1), 90 * Mathf.Deg2Rad, 90 * Mathf.Deg2Rad - angle); + PopMatrix(); + } + } + + /// <summary> + /// Draws a disc in the XZ plane. + /// + /// You can draw an arc by supplying the startAngle and endAngle parameters. + /// + /// [Open online documentation to see images] + /// + /// See: <see cref="SolidCircle(float3,float3,float)"/> + /// See: <see cref="CommandBuilder2D.SolidCircle(float3,float,float,float)"/> + /// See: <see cref="SolidArc(float3,float3,float3)"/> + /// </summary> + /// <param name="center">Center of the disc or solid arc.</param> + /// <param name="radius">Radius of the disc or solid arc.</param> + /// <param name="startAngle">Starting angle in radians. 0 corrsponds to the positive X axis.</param> + /// <param name="endAngle">End angle in radians.</param> + [System.Obsolete("Use Draw.xz.SolidCircle instead")] + public void SolidCircleXZ (float3 center, float radius, float startAngle = 0f, float endAngle = 2 * Mathf.PI) { + SolidCircleXZInternal(center, radius, startAngle, endAngle); + } + + internal void SolidCircleXZInternal (float3 center, float radius, float startAngle = 0f, float endAngle = 2 * Mathf.PI) { + Reserve<CircleXZData>(); + Add(Command.DiscXZ); + Add(new CircleXZData { center = center, radius = radius, startAngle = startAngle, endAngle = endAngle }); + } + + internal void SolidCircleXZInternal (float3 center, float radius, float startAngle, float endAngle, Color color) { + Reserve<Color32, CircleXZData>(); + Add(Command.DiscXZ | Command.PushColorInline); + Add(ConvertColor(color)); + Add(new CircleXZData { center = center, radius = radius, startAngle = startAngle, endAngle = endAngle }); + } + + /// <summary> + /// Draws a disc in the XY plane. + /// + /// You can draw an arc by supplying the startAngle and endAngle parameters. + /// + /// [Open online documentation to see images] + /// + /// See: <see cref="SolidCircle(float3,float3,float)"/> + /// See: <see cref="CommandBuilder2D.SolidCircle(float3,float,float,float)"/> + /// See: <see cref="SolidArc(float3,float3,float3)"/> + /// </summary> + /// <param name="center">Center of the disc or solid arc.</param> + /// <param name="radius">Radius of the disc or solid arc.</param> + /// <param name="startAngle">Starting angle in radians. 0 corrsponds to the positive X axis.</param> + /// <param name="endAngle">End angle in radians.</param> + [System.Obsolete("Use Draw.xy.SolidCircle instead")] + public void SolidCircleXY (float3 center, float radius, float startAngle = 0f, float endAngle = 2 * Mathf.PI) { + PushMatrix(XZtoXYPlaneMatrix); + SolidCircleXZInternal(new float3(center.x, -center.z, center.y), radius, startAngle, endAngle); + PopMatrix(); + } + + /// <summary> + /// Draws a disc. + /// + /// [Open online documentation to see images] + /// + /// Note: This overload does not allow you to draw an arc. For that purpose use <see cref="SolidArc"/> or <see cref="CommandBuilder2D.SolidCircle(float3,float,float,float)"/> instead. + /// </summary> + public void SolidCircle (float3 center, float3 normal, float radius) { + Reserve<CircleData>(); + Add(Command.Disc); + Add(new CircleData { center = center, normal = normal, radius = radius }); + } + + /// <summary> + /// Draws a circle outline around a sphere. + /// + /// Visually, this is a circle that always faces the camera, and is resized automatically to fit the sphere. + /// + /// [Open online documentation to see images] + /// </summary> + public void SphereOutline (float3 center, float radius) { + Reserve<SphereData>(); + Add(Command.SphereOutline); + Add(new SphereData { center = center, radius = radius }); + } + + /// <summary> + /// Draws a cylinder. + /// The cylinder's bottom circle will be centered at the bottom parameter and similarly for the top circle. + /// + /// <code> + /// // Draw a tilted cylinder between the points (0,0,0) and (1,1,1) with a radius of 0.5 + /// Draw.WireCylinder(Vector3.zero, Vector3.one, 0.5f, Color.black); + /// </code> + /// + /// [Open online documentation to see images] + /// </summary> + public void WireCylinder (float3 bottom, float3 top, float radius) { + WireCylinder(bottom, top - bottom, math.length(top - bottom), radius); + } + + /// <summary> + /// Draws a cylinder. + /// + /// <code> + /// // Draw a two meter tall cylinder at the world origin with a radius of 0.5 + /// Draw.WireCylinder(Vector3.zero, Vector3.up, 2, 0.5f, Color.black); + /// </code> + /// + /// [Open online documentation to see images] + /// </summary> + /// <param name="position">The center of the cylinder's "bottom" circle.</param> + /// <param name="up">The cylinder's main axis. Does not have to be normalized. If zero, nothing will be drawn.</param> + /// <param name="height">The length of the cylinder, as measured along it's main axis.</param> + /// <param name="radius">The radius of the cylinder.</param> + public void WireCylinder (float3 position, float3 up, float height, float radius) { + up = math.normalizesafe(up); + if (math.all(up == 0) || math.any(math.isnan(up)) || math.isnan(height) || math.isnan(radius)) return; + + OrthonormalBasis(up, out var basis1, out var basis2); + + PushMatrix(new float4x4( + new float4(basis1 * radius, 0), + new float4(up * height, 0), + new float4(basis2 * radius, 0), + new float4(position, 1) + )); + + CircleXZInternal(float3.zero, 1); + if (height > 0) { + CircleXZInternal(new float3(0, 1, 0), 1); + Line(new float3(1, 0, 0), new float3(1, 1, 0)); + Line(new float3(-1, 0, 0), new float3(-1, 1, 0)); + Line(new float3(0, 0, 1), new float3(0, 1, 1)); + Line(new float3(0, 0, -1), new float3(0, 1, -1)); + } + PopMatrix(); + } + + /// <summary> + /// Constructs an orthonormal basis from a single normal vector. + /// + /// This is similar to math.orthonormal_basis, but it tries harder to be continuous in its input. + /// In contrast, math.orthonormal_basis has a tendency to jump around even with small changes to the normal. + /// + /// It's not as fast as math.orthonormal_basis, though. + /// </summary> + static void OrthonormalBasis (float3 normal, out float3 basis1, out float3 basis2) { + basis1 = math.cross(normal, new float3(1, 1, 1)); + if (math.all(basis1 == 0)) basis1 = math.cross(normal, new float3(-1, 1, 1)); + basis1 = math.normalizesafe(basis1); + basis2 = math.cross(normal, basis1); + } + + /// <summary> + /// Draws a capsule with a (start,end) parameterization. + /// + /// The behavior of this method matches common Unity APIs such as Physics.CheckCapsule. + /// + /// <code> + /// // Draw a tilted capsule between the points (0,0,0) and (1,1,1) with a radius of 0.5 + /// Draw.WireCapsule(Vector3.zero, Vector3.one, 0.5f, Color.black); + /// </code> + /// + /// [Open online documentation to see images] + /// </summary> + /// <param name="start">Center of the start hemisphere of the capsule.</param> + /// <param name="end">Center of the end hemisphere of the capsule.</param> + /// <param name="radius">Radius of the capsule.</param> + public void WireCapsule (float3 start, float3 end, float radius) { + var dir = end - start; + var length = math.length(dir); + + if (length < 0.0001) { + // The endpoints are the same, we can't draw a capsule from this because we don't know its orientation. + // Draw a sphere as a fallback + WireSphere(start, radius); + } else { + var normalized_dir = dir / length; + + WireCapsule(start - normalized_dir*radius, normalized_dir, length + 2*radius, radius); + } + } + + // TODO: Change to center, up, height parameterization + /// <summary> + /// Draws a capsule with a (position,direction/length) parameterization. + /// + /// <code> + /// // Draw a capsule that touches the y=0 plane, is 2 meters tall and has a radius of 0.5 + /// Draw.WireCapsule(Vector3.zero, Vector3.up, 2.0f, 0.5f, Color.black); + /// </code> + /// + /// [Open online documentation to see images] + /// </summary> + /// <param name="position">One endpoint of the capsule. This is at the edge of the capsule, not at the center of one of the hemispheres.</param> + /// <param name="direction">The main axis of the capsule. Does not have to be normalized. If zero, nothing will be drawn.</param> + /// <param name="length">Distance between the two endpoints of the capsule. The length will be clamped to be at least 2*radius.</param> + /// <param name="radius">The radius of the capsule.</param> + public void WireCapsule (float3 position, float3 direction, float length, float radius) { + direction = math.normalizesafe(direction); + if (math.all(direction == 0) || math.any(math.isnan(direction)) || math.isnan(length) || math.isnan(radius)) return; + + if (radius <= 0) { + Line(position, position + direction * length); + } else { + length = math.max(length, radius*2); + OrthonormalBasis(direction, out var basis1, out var basis2); + + PushMatrix(new float4x4( + new float4(basis1, 0), + new float4(direction, 0), + new float4(basis2, 0), + new float4(position, 1) + )); + CircleXZInternal(new float3(0, radius, 0), radius); + PushMatrix(XZtoXYPlaneMatrix); + CircleXZInternal(new float3(0, 0, radius), radius, Mathf.PI, 2 * Mathf.PI); + PopMatrix(); + PushMatrix(XZtoYZPlaneMatrix); + CircleXZInternal(new float3(radius, 0, 0), radius, Mathf.PI*0.5f, Mathf.PI*1.5f); + PopMatrix(); + if (length > 0) { + var upperY = length - radius; + var lowerY = radius; + CircleXZInternal(new float3(0, upperY, 0), radius); + PushMatrix(XZtoXYPlaneMatrix); + CircleXZInternal(new float3(0, 0, upperY), radius, 0, Mathf.PI); + PopMatrix(); + PushMatrix(XZtoYZPlaneMatrix); + CircleXZInternal(new float3(upperY, 0, 0), radius, -Mathf.PI*0.5f, Mathf.PI*0.5f); + PopMatrix(); + Line(new float3(radius, lowerY, 0), new float3(radius, upperY, 0)); + Line(new float3(-radius, lowerY, 0), new float3(-radius, upperY, 0)); + Line(new float3(0, lowerY, radius), new float3(0, upperY, radius)); + Line(new float3(0, lowerY, -radius), new float3(0, upperY, -radius)); + } + PopMatrix(); + } + } + + /// <summary> + /// Draws a wire sphere. + /// + /// [Open online documentation to see images] + /// + /// <code> + /// // Draw a wire sphere at the origin with a radius of 0.5 + /// Draw.WireSphere(Vector3.zero, 0.5f, Color.black); + /// </code> + /// + /// See: <see cref="Circle"/> + /// </summary> + public void WireSphere (float3 position, float radius) { + SphereOutline(position, radius); + Circle(position, new float3(1, 0, 0), radius); + Circle(position, new float3(0, 1, 0), radius); + Circle(position, new float3(0, 0, 1), radius); + } + + /// <summary> + /// Draws lines through a sequence of points. + /// + /// [Open online documentation to see images] + /// <code> + /// // Draw a square + /// Draw.Polyline(new [] { new Vector3(0, 0, 0), new Vector3(1, 0, 0), new Vector3(1, 1, 0), new Vector3(0, 1, 0) }, true); + /// </code> + /// </summary> + /// <param name="points">Sequence of points to draw lines through</param> + /// <param name="cycle">If true a line will be drawn from the last point in the sequence back to the first point.</param> + [BurstDiscard] + public void Polyline (List<Vector3> 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]); + } + + /// <summary> + /// Draws lines through a sequence of points. + /// + /// [Open online documentation to see images] + /// <code> + /// // Draw a square + /// Draw.Polyline(new [] { new Vector3(0, 0, 0), new Vector3(1, 0, 0), new Vector3(1, 1, 0), new Vector3(0, 1, 0) }, true); + /// </code> + /// </summary> + /// <param name="points">Sequence of points to draw lines through</param> + /// <param name="cycle">If true a line will be drawn from the last point in the sequence back to the first point.</param> + public void Polyline<T>(T points, bool cycle = false) where T : IReadOnlyList<float3> { + 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]); + } + + /// <summary> + /// Draws lines through a sequence of points. + /// + /// [Open online documentation to see images] + /// <code> + /// // Draw a square + /// Draw.Polyline(new [] { new Vector3(0, 0, 0), new Vector3(1, 0, 0), new Vector3(1, 1, 0), new Vector3(0, 1, 0) }, true); + /// </code> + /// </summary> + /// <param name="points">Sequence of points to draw lines through</param> + /// <param name="cycle">If true a line will be drawn from the last point in the sequence back to the first point.</param> + [BurstDiscard] + public void Polyline (Vector3[] 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]); + } + + /// <summary> + /// Draws lines through a sequence of points. + /// + /// [Open online documentation to see images] + /// <code> + /// // Draw a square + /// Draw.Polyline(new [] { new Vector3(0, 0, 0), new Vector3(1, 0, 0), new Vector3(1, 1, 0), new Vector3(0, 1, 0) }, true); + /// </code> + /// </summary> + /// <param name="points">Sequence of points to draw lines through</param> + /// <param name="cycle">If true a line will be drawn from the last point in the sequence back to the first point.</param> + [BurstDiscard] + public void Polyline (float3[] 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]); + } + + /// <summary> + /// Draws lines through a sequence of points. + /// + /// [Open online documentation to see images] + /// <code> + /// // Draw a square + /// Draw.Polyline(new [] { new Vector3(0, 0, 0), new Vector3(1, 0, 0), new Vector3(1, 1, 0), new Vector3(0, 1, 0) }, true); + /// </code> + /// </summary> + /// <param name="points">Sequence of points to draw lines through</param> + /// <param name="cycle">If true a line will be drawn from the last point in the sequence back to the first point.</param> + public void Polyline (NativeArray<float3> 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]); + } + + /// <summary>Determines the symbol to use for <see cref="PolylineWithSymbol"/></summary> + public enum SymbolDecoration { + /// <summary> + /// No symbol. + /// + /// Space will still be reserved, but no symbol will be drawn. + /// Can be used to draw dashed lines. + /// + /// [Open online documentation to see images] + /// </summary> + None, + /// <summary> + /// An arrowhead symbol. + /// + /// [Open online documentation to see images] + /// </summary> + ArrowHead, + /// <summary> + /// A circle symbol. + /// + /// [Open online documentation to see images] + /// </summary> + Circle, + } + + /// <summary> + /// Draws a dashed line between two points. + /// + /// <code> + /// Draw.DashedPolyline(points, 0.1f, 0.1f, color); + /// </code> + /// + /// [Open online documentation to see images] + /// + /// Warning: An individual line segment is drawn for each dash. This means that performance may suffer if you make the dash + gap distance too small. + /// But for most use cases the performance is nothing to worry about. + /// + /// See: <see cref="DashedPolyline"/> + /// See: <see cref="PolylineWithSymbol"/> + /// </summary> + public void DashedLine (float3 a, float3 b, float dash, float gap) { + var p = new PolylineWithSymbol(SymbolDecoration.None, gap, 0, dash + gap); + p.MoveTo(ref this, a); + p.MoveTo(ref this, b); + } + + /// <summary> + /// Draws a dashed line through a sequence of points. + /// + /// <code> + /// Draw.DashedPolyline(points, 0.1f, 0.1f, color); + /// </code> + /// + /// [Open online documentation to see images] + /// + /// Warning: An individual line segment is drawn for each dash. This means that performance may suffer if you make the dash + gap distance too small. + /// But for most use cases the performance is nothing to worry about. + /// + /// If you have a different collection type, or you do not have the points in a collection at all, then you can use the <see cref="PolylineWithSymbol"/> struct directly. + /// + /// <code> + /// using (Draw.WithColor(color)) { + /// var dash = 0.1f; + /// var gap = 0.1f; + /// var p = new CommandBuilder.PolylineWithSymbol(CommandBuilder.SymbolDecoration.None, gap, 0, dash + gap); + /// for (int i = 0; i < points.Count; i++) { + /// p.MoveTo(ref Draw.editor, points[i]); + /// } + /// } + /// </code> + /// + /// See: <see cref="DashedLine"/> + /// See: <see cref="PolylineWithSymbol"/> + /// </summary> + public void DashedPolyline (List<Vector3> points, float dash, float gap) { + var p = new PolylineWithSymbol(SymbolDecoration.None, gap, 0, dash + gap); + for (int i = 0; i < points.Count; i++) { + p.MoveTo(ref this, points[i]); + } + } + + /// <summary> + /// Helper for drawing a polyline with symbols at regular intervals. + /// + /// <code> + /// var generator = new CommandBuilder.PolylineWithSymbol(CommandBuilder.SymbolDecoration.Circle, 0.2f, 0.0f, 0.47f); + /// generator.MoveTo(ref Draw.editor, new float3(-0.5f, 0, -0.5f)); + /// generator.MoveTo(ref Draw.editor, new float3(0.5f, 0, 0.5f)); + /// </code> + /// + /// [Open online documentation to see images] + /// + /// [Open online documentation to see images] + /// + /// You can also draw a dashed line using this struct, but for common cases you can use the <see cref="DashedPolyline"/> helper function instead. + /// + /// <code> + /// using (Draw.WithColor(color)) { + /// var dash = 0.1f; + /// var gap = 0.1f; + /// var p = new CommandBuilder.PolylineWithSymbol(CommandBuilder.SymbolDecoration.None, gap, 0, dash + gap); + /// for (int i = 0; i < points.Count; i++) { + /// p.MoveTo(ref Draw.editor, points[i]); + /// } + /// } + /// </code> + /// + /// [Open online documentation to see images] + /// </summary> + public struct PolylineWithSymbol { + float3 prev; + float offset; + readonly float symbolSize; + readonly float symbolSpacing; + readonly float symbolPadding; + readonly float symbolOffset; + readonly SymbolDecoration symbol; + readonly bool reverseSymbols; + bool odd; + + /// <summary>Create a new polyline with symbol generator.</summary> + /// <param name="symbol">The symbol to use</param> + /// <param name="symbolSize">The size of the symbol. In case of a circle, this is the diameter.</param> + /// <param name="symbolPadding">The padding on both sides of the symbol between the symbol and the line.</param> + /// <param name="symbolSpacing">The spacing between symbols. This is the distance between the centers of the symbols.</param> + /// <param name="reverseSymbols">If true, the symbols will be reversed. For cicles this has no effect, but arrowhead symbols will be reversed.</param> + public PolylineWithSymbol(SymbolDecoration symbol, float symbolSize, float symbolPadding, float symbolSpacing, bool reverseSymbols = false) { + if (symbolSpacing <= math.FLT_MIN_NORMAL) throw new System.ArgumentOutOfRangeException(nameof(symbolSpacing), "Symbol spacing must be greater than zero"); + if (symbolSize <= math.FLT_MIN_NORMAL) throw new System.ArgumentOutOfRangeException(nameof(symbolSize), "Symbol size must be greater than zero"); + if (symbolPadding < 0) throw new System.ArgumentOutOfRangeException(nameof(symbolPadding), "Symbol padding must non-negative"); + + this.prev = float3.zero; + this.symbol = symbol; + this.symbolSize = symbolSize; + this.symbolPadding = symbolPadding; + this.symbolSpacing = math.max(0, symbolSpacing - symbolPadding * 2f - symbolSize); + this.reverseSymbols = reverseSymbols; + symbolOffset = symbol == SymbolDecoration.ArrowHead ? -0.25f * symbolSize : 0; + if (reverseSymbols) { + symbolOffset = -symbolOffset; + } + symbolOffset += 0.5f * symbolSize; + offset = -1; + odd = false; + } + + /// <summary> + /// Move to a new point. + /// + /// This will draw the symbols and line segments between the previous point and the new point. + /// </summary> + /// <param name="draw">The command builder to draw to. You can use a built-in builder like \reflink{Draw.editor} or \reflink{Draw.ingame}, or use a custom one.</param> + /// <param name="next">The next point in the polyline to move to.</param> + public void MoveTo (ref CommandBuilder draw, float3 next) { + if (offset == -1) { + offset = this.symbolSpacing * 0.5f; + prev = next; + return; + } + var len = math.length(next - prev); + var invLen = math.rcp(len); + var dir = next - prev; + float3 up = default; + if (symbol != SymbolDecoration.None) { + up = math.normalizesafe(math.cross(dir, math.cross(dir, new float3(0, 1, 0)))); + if (math.all(up == 0f)) { + up = new float3(0, 0, 1); + } + } + if (reverseSymbols) dir = -dir; + if (offset > 0 && !odd) { + draw.Line(prev, math.lerp(prev, next, math.min(offset * invLen, 1))); + } + while (offset < len) { + if (odd) { + var pLast = math.lerp(prev, next, offset * invLen); + offset += symbolSpacing; + var p = math.lerp(prev, next, math.min(offset * invLen, 1)); + draw.Line(pLast, p); + offset += symbolPadding; + } else { + var p = math.lerp(prev, next, (offset + symbolOffset) * invLen); + switch (symbol) { + case SymbolDecoration.None: + break; + case SymbolDecoration.ArrowHead: + draw.Arrowhead(p, dir, up, symbolSize); + break; + case SymbolDecoration.Circle: + default: + draw.Circle(p, up, symbolSize * 0.5f); + break; + } + offset += symbolSize + symbolPadding; + } + odd = !odd; + } + offset -= len; + prev = next; + } + } + + /// <summary> + /// Draws the outline of a box which is axis-aligned. + /// + /// [Open online documentation to see images] + /// </summary> + /// <param name="center">Center of the box</param> + /// <param name="size">Width of the box along all dimensions</param> + public void WireBox (float3 center, float3 size) { + Reserve<BoxData>(); + Add(Command.WireBox); + Add(new BoxData { center = center, size = size }); + } + + /// <summary> + /// Draws the outline of a box. + /// + /// [Open online documentation to see images] + /// </summary> + /// <param name="center">Center of the box</param> + /// <param name="rotation">Rotation of the box</param> + /// <param name="size">Width of the box along all dimensions</param> + public void WireBox (float3 center, quaternion rotation, float3 size) { + PushMatrix(float4x4.TRS(center, rotation, size)); + WireBox(float3.zero, new float3(1, 1, 1)); + PopMatrix(); + } + + /// <summary> + /// Draws the outline of a box. + /// + /// [Open online documentation to see images] + /// </summary> + public void WireBox (Bounds bounds) { + WireBox(bounds.center, bounds.size); + } + + /// <summary> + /// Draws a wire mesh. + /// Every single edge of the mesh will be drawn using a <see cref="Line"/> command. + /// + /// <code> + /// var go = GameObject.CreatePrimitive(PrimitiveType.Sphere); + /// go.transform.position = new Vector3(0, 0, 0); + /// using (Draw.InLocalSpace(go.transform)) { + /// Draw.WireMesh(go.GetComponent<MeshFilter>().sharedMesh, color); + /// } + /// </code> + /// [Open online documentation to see images] + /// + /// See: <see cref="SolidMesh(Mesh)"/> + /// + /// Version: Supported in Unity 2020.1 or later. + /// </summary> + public void WireMesh (Mesh mesh) { +#if UNITY_2020_1_OR_NEWER + if (mesh == null) throw new System.ArgumentNullException(); + + // Use a burst compiled function to draw the lines + // This is significantly faster than pure C# (about 5x). + var meshDataArray = Mesh.AcquireReadOnlyMeshData(mesh); + var meshData = meshDataArray[0]; + + JobWireMesh.JobWireMeshFunctionPointer(ref meshData, ref this); + meshDataArray.Dispose(); +#else + Debug.LogError("The WireMesh method is only suppored in Unity 2020.1 or later"); +#endif + } + + /// <summary> + /// Draws a wire mesh. + /// Every single edge of the mesh will be drawn using a <see cref="Line"/> command. + /// + /// <code> + /// var go = GameObject.CreatePrimitive(PrimitiveType.Sphere); + /// go.transform.position = new Vector3(0, 0, 0); + /// using (Draw.InLocalSpace(go.transform)) { + /// Draw.WireMesh(go.GetComponent<MeshFilter>().sharedMesh, color); + /// } + /// </code> + /// [Open online documentation to see images] + /// + /// See: <see cref="SolidMesh(Mesh)"/> + /// + /// Version: Supported in Unity 2020.1 or later. + /// </summary> + public void WireMesh (NativeArray<float3> vertices, NativeArray<int> triangles) { +#if UNITY_2020_1_OR_NEWER + unsafe { + JobWireMesh.WireMesh((float3*)vertices.GetUnsafeReadOnlyPtr(), (int*)triangles.GetUnsafeReadOnlyPtr(), vertices.Length, triangles.Length, ref this); + } +#else + Debug.LogError("The WireMesh method is only suppored in Unity 2020.1 or later"); +#endif + } + +#if UNITY_2020_1_OR_NEWER + /// <summary>Helper job for <see cref="WireMesh"/></summary> + [BurstCompile] + class JobWireMesh { + public delegate void JobWireMeshDelegate(ref Mesh.MeshData rawMeshData, ref CommandBuilder draw); + + public static readonly JobWireMeshDelegate JobWireMeshFunctionPointer = BurstCompiler.CompileFunctionPointer<JobWireMeshDelegate>(Execute).Invoke; + + [BurstCompile] + public static unsafe void WireMesh (float3* verts, int* indices, int vertexCount, int indexCount, ref CommandBuilder draw) { + // Ignore warning about NativeHashMap being obsolete in early versions of the collections package. + // It works just fine, and in later versions the NativeHashMap is not obsolete. + #pragma warning disable 618 + var seenEdges = new NativeHashMap<int2, bool>(indexCount, Allocator.Temp); + #pragma warning restore 618 + for (int i = 0; i < indexCount; i += 3) { + var a = indices[i]; + var b = indices[i+1]; + var c = indices[i+2]; + if (a < 0 || b < 0 || c < 0 || a >= vertexCount || b >= vertexCount || c >= vertexCount) { + throw new Exception("Invalid vertex index. Index out of bounds"); + } + int v1, v2; + + // Draw each edge of the triangle. + // Check so that we do not draw an edge twice. + v1 = math.min(a, b); + v2 = math.max(a, b); + if (!seenEdges.ContainsKey(new int2(v1, v2))) { + seenEdges.Add(new int2(v1, v2), true); + draw.Line(verts[v1], verts[v2]); + } + + v1 = math.min(b, c); + v2 = math.max(b, c); + if (!seenEdges.ContainsKey(new int2(v1, v2))) { + seenEdges.Add(new int2(v1, v2), true); + draw.Line(verts[v1], verts[v2]); + } + + v1 = math.min(c, a); + v2 = math.max(c, a); + if (!seenEdges.ContainsKey(new int2(v1, v2))) { + seenEdges.Add(new int2(v1, v2), true); + draw.Line(verts[v1], verts[v2]); + } + } + } + + [BurstCompile] + [AOT.MonoPInvokeCallback(typeof(JobWireMeshDelegate))] + static void Execute (ref Mesh.MeshData rawMeshData, ref CommandBuilder draw) { + int maxIndices = 0; + for (int subMeshIndex = 0; subMeshIndex < rawMeshData.subMeshCount; subMeshIndex++) { + maxIndices = math.max(maxIndices, rawMeshData.GetSubMesh(subMeshIndex).indexCount); + } + var tris = new NativeArray<int>(maxIndices, Allocator.Temp, NativeArrayOptions.UninitializedMemory); + var verts = new NativeArray<Vector3>(rawMeshData.vertexCount, Allocator.Temp, NativeArrayOptions.UninitializedMemory); + rawMeshData.GetVertices(verts); + + for (int subMeshIndex = 0; subMeshIndex < rawMeshData.subMeshCount; subMeshIndex++) { + var submesh = rawMeshData.GetSubMesh(subMeshIndex); + rawMeshData.GetIndices(tris, subMeshIndex); + unsafe { + WireMesh((float3*)verts.GetUnsafeReadOnlyPtr(), (int*)tris.GetUnsafeReadOnlyPtr(), verts.Length, submesh.indexCount, ref draw); + } + } + } + } +#endif + + /// <summary> + /// Draws a solid mesh. + /// The mesh will be drawn with a solid color. + /// + /// <code> + /// var go = GameObject.CreatePrimitive(PrimitiveType.Sphere); + /// go.transform.position = new Vector3(0, 0, 0); + /// using (Draw.InLocalSpace(go.transform)) { + /// Draw.SolidMesh(go.GetComponent<MeshFilter>().sharedMesh, color); + /// } + /// </code> + /// [Open online documentation to see images] + /// + /// Note: This method is not thread safe and must not be used from the Unity Job System. + /// TODO: Are matrices handled? + /// + /// See: <see cref="WireMesh(Mesh)"/> + /// </summary> + public void SolidMesh (Mesh mesh) { + SolidMeshInternal(mesh, false); + } + + void SolidMeshInternal (Mesh mesh, bool temporary, Color color) { + PushColor(color); + SolidMeshInternal(mesh, temporary); + PopColor(); + } + + + void SolidMeshInternal (Mesh mesh, bool temporary) { + var g = gizmos.Target as DrawingData; + + g.data.Get(uniqueID).meshes.Add(new SubmittedMesh { + mesh = mesh, + temporary = temporary, + }); + // Internally we need to make sure to capture the current state + // (which includes the current matrix and color) so that it + // can be applied to the mesh. + Reserve(4); + Add(Command.CaptureState); + } + + /// <summary> + /// Draws a solid mesh with the given vertices. + /// + /// [Open online documentation to see images] + /// + /// Note: This method is not thread safe and must not be used from the Unity Job System. + /// TODO: Are matrices handled? + /// </summary> + [BurstDiscard] + public void SolidMesh (List<Vector3> vertices, List<int> triangles, List<Color> colors) { + if (vertices.Count != colors.Count) throw new System.ArgumentException("Number of colors must be the same as the number of vertices"); + + // TODO: Is this mesh getting recycled at all? + var g = gizmos.Target as DrawingData; + var mesh = g.GetMesh(vertices.Count); + + // Set all data on the mesh + mesh.Clear(); + mesh.SetVertices(vertices); + mesh.SetTriangles(triangles, 0); + mesh.SetColors(colors); + // Upload all data + mesh.UploadMeshData(false); + SolidMeshInternal(mesh, true); + } + + /// <summary> + /// Draws a solid mesh with the given vertices. + /// + /// [Open online documentation to see images] + /// + /// Note: This method is not thread safe and must not be used from the Unity Job System. + /// TODO: Are matrices handled? + /// </summary> + [BurstDiscard] + public void SolidMesh (Vector3[] vertices, int[] triangles, Color[] colors, int vertexCount, int indexCount) { + if (vertices.Length != colors.Length) throw new System.ArgumentException("Number of colors must be the same as the number of vertices"); + + // TODO: Is this mesh getting recycled at all? + var g = gizmos.Target as DrawingData; + var mesh = g.GetMesh(vertices.Length); + + // Set all data on the mesh + mesh.Clear(); + mesh.SetVertices(vertices, 0, vertexCount); + mesh.SetTriangles(triangles, 0, indexCount, 0); + mesh.SetColors(colors, 0, vertexCount); + // Upload all data + mesh.UploadMeshData(false); + SolidMeshInternal(mesh, true); + } + + /// <summary> + /// Draws a 3D cross. + /// + /// [Open online documentation to see images] + /// </summary> + public void Cross (float3 position, float size = 1) { + size *= 0.5f; + Line(position - new float3(size, 0, 0), position + new float3(size, 0, 0)); + Line(position - new float3(0, size, 0), position + new float3(0, size, 0)); + Line(position - new float3(0, 0, size), position + new float3(0, 0, size)); + } + + /// <summary> + /// Draws a cross in the XZ plane. + /// + /// [Open online documentation to see images] + /// </summary> + [System.Obsolete("Use Draw.xz.Cross instead")] + public void CrossXZ (float3 position, float size = 1) { + size *= 0.5f; + Line(position - new float3(size, 0, 0), position + new float3(size, 0, 0)); + Line(position - new float3(0, 0, size), position + new float3(0, 0, size)); + } + + /// <summary> + /// Draws a cross in the XY plane. + /// + /// [Open online documentation to see images] + /// </summary> + [System.Obsolete("Use Draw.xy.Cross instead")] + public void CrossXY (float3 position, float size = 1) { + size *= 0.5f; + Line(position - new float3(size, 0, 0), position + new float3(size, 0, 0)); + Line(position - new float3(0, size, 0), position + new float3(0, size, 0)); + } + + /// <summary>Returns a point on a cubic bezier curve. t is clamped between 0 and 1</summary> + public static float3 EvaluateCubicBezier (float3 p0, float3 p1, float3 p2, float3 p3, float t) { + t = math.clamp(t, 0, 1); + float tr = 1-t; + return tr*tr*tr * p0 + 3 * tr*tr * t * p1 + 3 * tr * t*t * p2 + t*t*t * p3; + } + + /// <summary> + /// Draws a cubic bezier curve. + /// + /// [Open online documentation to see images] + /// + /// [Open online documentation to see images] + /// + /// TODO: Currently uses a fixed resolution of 20 segments. Resolution should depend on the distance to the camera. + /// + /// See: https://en.wikipedia.org/wiki/Bezier_curve + /// </summary> + /// <param name="p0">Start point</param> + /// <param name="p1">First control point</param> + /// <param name="p2">Second control point</param> + /// <param name="p3">End point</param> + public void Bezier (float3 p0, float3 p1, float3 p2, float3 p3) { + float3 prev = p0; + + for (int i = 1; i <= 20; i++) { + float t = i/20.0f; + float3 p = EvaluateCubicBezier(p0, p1, p2, p3, t); + Line(prev, p); + prev = p; + } + } + + /// <summary> + /// Draws a smooth curve through a list of points. + /// + /// A catmull-rom spline is equivalent to a bezier curve with control points determined by an algorithm. + /// In fact, this package displays catmull-rom splines by first converting them to bezier curves. + /// + /// [Open online documentation to see images] + /// + /// See: https://en.wikipedia.org/wiki/Centripetal_Catmull%E2%80%93Rom_spline + /// See: <see cref="CatmullRom(float3,float3,float3,float3)"/> + /// </summary> + /// <param name="points">The curve will smoothly pass through each point in the list in order.</param> + public void CatmullRom (List<Vector3> points) { + if (points.Count < 2) return; + + if (points.Count == 2) { + Line(points[0], points[1]); + } else { + // count >= 3 + var count = points.Count; + // Draw first curve, this is special because the first two control points are the same + CatmullRom(points[0], points[0], points[1], points[2]); + for (int i = 0; i + 3 < count; i++) { + CatmullRom(points[i], points[i+1], points[i+2], points[i+3]); + } + // Draw last curve + CatmullRom(points[count-3], points[count-2], points[count-1], points[count-1]); + } + } + + /// <summary> + /// Draws a centripetal catmull rom spline. + /// + /// The curve starts at p1 and ends at p2. + /// + /// [Open online documentation to see images] + /// [Open online documentation to see images] + /// + /// See: <see cref="CatmullRom(List<Vector3>)"/> + /// </summary> + /// <param name="p0">First control point</param> + /// <param name="p1">Second control point. Start of the curve.</param> + /// <param name="p2">Third control point. End of the curve.</param> + /// <param name="p3">Fourth control point.</param> + public void CatmullRom (float3 p0, float3 p1, float3 p2, float3 p3) { + // References used: + // p.266 GemsV1 + // + // tension is often set to 0.5 but you can use any reasonable value: + // http://www.cs.cmu.edu/~462/projects/assn2/assn2/catmullRom.pdf + // + // bias and tension controls: + // http://local.wasp.uwa.edu.au/~pbourke/miscellaneous/interpolation/ + + // We will convert the catmull rom spline to a bezier curve for simplicity. + // The end result of this will be a conversion matrix where we transform catmull rom control points + // into the equivalent bezier curve control points. + + // Conversion matrix + // ================= + + // A centripetal catmull rom spline can be separated into the following terms: + // 1 * p1 + + // t * (-0.5 * p0 + 0.5*p2) + + // t*t * (p0 - 2.5*p1 + 2.0*p2 + 0.5*t2) + + // t*t*t * (-0.5*p0 + 1.5*p1 - 1.5*p2 + 0.5*p3) + // + // Matrix form: + // 1 t t^2 t^3 + // {0, -1/2, 1, -1/2} + // {1, 0, -5/2, 3/2} + // {0, 1/2, 2, -3/2} + // {0, 0, -1/2, 1/2} + + // Transposed matrix: + // M_1 = {{0, 1, 0, 0}, {-1/2, 0, 1/2, 0}, {1, -5/2, 2, -1/2}, {-1/2, 3/2, -3/2, 1/2}} + + // A bezier spline can be separated into the following terms: + // (-t^3 + 3 t^2 - 3 t + 1) * c0 + + // (3t^3 - 6*t^2 + 3t) * c1 + + // (3t^2 - 3t^3) * c2 + + // t^3 * c3 + // + // Matrix form: + // 1 t t^2 t^3 + // {1, -3, 3, -1} + // {0, 3, -6, 3} + // {0, 0, 3, -3} + // {0, 0, 0, 1} + + // Transposed matrix: + // M_2 = {{1, 0, 0, 0}, {-3, 3, 0, 0}, {3, -6, 3, 0}, {-1, 3, -3, 1}} + + // Thus a bezier curve can be evaluated using the expression + // output1 = T * M_1 * c + // where T = [1, t, t^2, t^3] and c being the control points c = [c0, c1, c2, c3]^T + // + // and a catmull rom spline can be evaluated using + // + // output2 = T * M_2 * p + // where T = same as before and p = [p0, p1, p2, p3]^T + // + // We can solve for c in output1 = output2 + // T * M_1 * c = T * M_2 * p + // M_1 * c = M_2 * p + // c = M_1^(-1) * M_2 * p + // Thus a conversion matrix from p to c is M_1^(-1) * M_2 + // This can be calculated and the result is the following matrix: + // + // {0, 1, 0, 0} + // {-1/6, 1, 1/6, 0} + // {0, 1/6, 1, -1/6} + // {0, 0, 1, 0} + // ------------------------------------------------------------------ + // + // Using this we can calculate c = M_1^(-1) * M_2 * p + var c0 = p1; + var c1 = (-p0 + 6*p1 + 1*p2)*(1/6.0f); + var c2 = (p1 + 6*p2 - p3)*(1/6.0f); + var c3 = p2; + + // And finally draw the bezier curve which is equivalent to the desired catmull-rom spline + Bezier(c0, c1, c2, c3); + } + + /// <summary> + /// Draws an arrow between two points. + /// + /// The size of the head defaults to 20% of the length of the arrow. + /// + /// [Open online documentation to see images] + /// + /// See: <see cref="ArrowheadArc"/> + /// See: <see cref="Arrow(float3,float3,float3,float)"/> + /// See: <see cref="ArrowRelativeSizeHead"/> + /// </summary> + /// <param name="from">Base of the arrow.</param> + /// <param name="to">Head of the arrow.</param> + public void Arrow (float3 from, float3 to) { + ArrowRelativeSizeHead(from, to, DEFAULT_UP, 0.2f); + } + + /// <summary> + /// Draws an arrow between two points. + /// + /// [Open online documentation to see images] + /// + /// See: <see cref="ArrowRelativeSizeHead"/> + /// See: <see cref="ArrowheadArc"/> + /// </summary> + /// <param name="from">Base of the arrow.</param> + /// <param name="to">Head of the arrow.</param> + /// <param name="up">Up direction of the world, the arrowhead plane will be as perpendicular as possible to this direction. Defaults to Vector3.up.</param> + /// <param name="headSize">The size of the arrowhead in world units.</param> + public void Arrow (float3 from, float3 to, float3 up, float headSize) { + var length_sq = math.lengthsq(to - from); + + if (length_sq > 0.000001f) { + ArrowRelativeSizeHead(from, to, up, headSize * math.rsqrt(length_sq)); + } + } + + /// <summary> + /// Draws an arrow between two points with a head that varies with the length of the arrow. + /// + /// [Open online documentation to see images] + /// + /// See: <see cref="ArrowheadArc"/> + /// See: <see cref="Arrow"/> + /// </summary> + /// <param name="from">Base of the arrow.</param> + /// <param name="to">Head of the arrow.</param> + /// <param name="up">Up direction of the world, the arrowhead plane will be as perpendicular as possible to this direction.</param> + /// <param name="headFraction">The length of the arrowhead is the distance between from and to multiplied by this fraction. Should be between 0 and 1.</param> + public void ArrowRelativeSizeHead (float3 from, float3 to, float3 up, float headFraction) { + Line(from, to); + var dir = to - from; + + var normal = math.cross(dir, up); + // Pick a different up direction if the direction happened to be colinear with that one. + if (math.all(normal == 0)) normal = math.cross(new float3(1, 0, 0), dir); + // Pick a different up direction if up=(1,0,0) and thus the above check would have generated a zero vector again + if (math.all(normal == 0)) normal = math.cross(new float3(0, 1, 0), dir); + normal = math.normalizesafe(normal) * math.length(dir); + + Line(to, to - (dir + normal) * headFraction); + Line(to, to - (dir - normal) * headFraction); + } + + /// <summary> + /// Draws an arrowhead at a point. + /// + /// <code> + /// Draw.Arrowhead(Vector3.zero, Vector3.forward, 0.75f, color); + /// </code> + /// [Open online documentation to see images] + /// + /// See: <see cref="Arrow"/> + /// See: <see cref="ArrowRelativeSizeHead"/> + /// </summary> + /// <param name="center">Center of the arrowhead.</param> + /// <param name="direction">Direction the arrow is pointing.</param> + /// <param name="radius">Distance from the center to each corner of the arrowhead.</param> + public void Arrowhead (float3 center, float3 direction, float radius) { + Arrowhead(center, direction, DEFAULT_UP, radius); + } + + /// <summary> + /// Draws an arrowhead at a point. + /// + /// <code> + /// Draw.Arrowhead(Vector3.zero, Vector3.forward, 0.75f, color); + /// </code> + /// [Open online documentation to see images] + /// + /// See: <see cref="Arrow"/> + /// See: <see cref="ArrowRelativeSizeHead"/> + /// </summary> + /// <param name="center">Center of the arrowhead.</param> + /// <param name="direction">Direction the arrow is pointing.</param> + /// <param name="up">Up direction of the world, the arrowhead plane will be as perpendicular as possible to this direction. Defaults to Vector3.up. Must be normalized.</param> + /// <param name="radius">Distance from the center to each corner of the arrowhead.</param> + public void Arrowhead (float3 center, float3 direction, float3 up, float radius) { + if (math.all(direction == 0)) return; + direction = math.normalizesafe(direction); + var normal = math.cross(direction, up); + const float SinPiOver3 = 0.866025f; + const float CosPiOver3 = 0.5f; + var circleCenter = center - radius * (1 - CosPiOver3)*0.5f * direction; + var p1 = circleCenter + radius * direction; + var p2 = circleCenter - radius * CosPiOver3 * direction + radius * SinPiOver3 * normal; + var p3 = circleCenter - radius * CosPiOver3 * direction - radius * SinPiOver3 * normal; + Line(p1, p2); + Line(p2, circleCenter); + Line(circleCenter, p3); + Line(p3, p1); + } + + /// <summary> + /// Draws an arrowhead centered around a circle. + /// + /// This can be used to for example show the direction a character is moving in. + /// + /// [Open online documentation to see images] + /// + /// Note: In the image above the arrowhead is the only part that is drawn by this method. The cylinder is only included for context. + /// + /// See: <see cref="Arrow"/> + /// </summary> + /// <param name="origin">Point around which the arc is centered</param> + /// <param name="direction">Direction the arrow is pointing</param> + /// <param name="offset">Distance from origin that the arrow starts.</param> + /// <param name="width">Width of the arrowhead in degrees (defaults to 60). Should be between 0 and 90.</param> + public void ArrowheadArc (float3 origin, float3 direction, float offset, float width = 60) { + if (!math.any(direction)) return; + if (offset < 0) throw new System.ArgumentOutOfRangeException(nameof(offset)); + if (offset == 0) return; + + var rot = Quaternion.LookRotation(direction, DEFAULT_UP); + PushMatrix(Matrix4x4.TRS(origin, rot, Vector3.one)); + var a1 = math.PI * 0.5f - width * (0.5f * Mathf.Deg2Rad); + var a2 = math.PI * 0.5f + width * (0.5f * Mathf.Deg2Rad); + CircleXZInternal(float3.zero, offset, a1, a2); + var p1 = new float3(math.cos(a1), 0, math.sin(a1)) * offset; + var p2 = new float3(math.cos(a2), 0, math.sin(a2)) * offset; + const float sqrt2 = 1.4142f; + var p3 = new float3(0, 0, sqrt2 * offset); + Line(p1, p3); + Line(p3, p2); + PopMatrix(); + } + + /// <summary> + /// Draws a grid of lines. + /// + /// <code> + /// Draw.xz.WireGrid(Vector3.zero, new int2(3, 3), new float2(1, 1), color); + /// </code> + /// [Open online documentation to see images] + /// </summary> + /// <param name="center">Center of the grid</param> + /// <param name="rotation">Rotation of the grid. The grid will be aligned to the X and Z axes of the rotation.</param> + /// <param name="cells">Number of cells of the grid. Should be greater than 0.</param> + /// <param name="totalSize">Total size of the grid along the X and Z axes.</param> + public void WireGrid (float3 center, quaternion rotation, int2 cells, float2 totalSize) { + cells = math.max(cells, new int2(1, 1)); + PushMatrix(float4x4.TRS(center, rotation, new Vector3(totalSize.x, 0, totalSize.y))); + int w = cells.x; + int h = cells.y; + for (int i = 0; i <= w; i++) Line(new float3(i/(float)w - 0.5f, 0, -0.5f), new float3(i/(float)w - 0.5f, 0, 0.5f)); + for (int i = 0; i <= h; i++) Line(new float3(-0.5f, 0, i/(float)h - 0.5f), new float3(0.5f, 0, i/(float)h - 0.5f)); + PopMatrix(); + } + + /// <summary> + /// Draws a triangle outline. + /// + /// <code> + /// Draw.WireTriangle(new Vector3(-0.5f, 0, 0), new Vector3(0, 1, 0), new Vector3(0.5f, 0, 0), Color.black); + /// </code> + /// [Open online documentation to see images] + /// + /// See: <see cref="Draw.WirePlane(float3,quaternion,float2)"/> + /// See: <see cref="WirePolygon"/> + /// See: <see cref="SolidTriangle"/> + /// </summary> + /// <param name="a">First corner of the triangle</param> + /// <param name="b">Second corner of the triangle</param> + /// <param name="c">Third corner of the triangle</param> + public void WireTriangle (float3 a, float3 b, float3 c) { + Line(a, b); + Line(b, c); + Line(c, a); + } + + /// <summary> + /// Draws a rectangle outline. + /// The rectangle will be aligned to the X and Z axes. + /// + /// <code> + /// Draw.xz.WireRectangle(new Vector3(0f, 0, 0), new Vector2(1, 1), Color.black); + /// </code> + /// [Open online documentation to see images] + /// + /// See: <see cref="WirePolygon"/> + /// </summary> + [System.Obsolete("Use Draw.xz.WireRectangle instead")] + public void WireRectangleXZ (float3 center, float2 size) { + WireRectangle(center, quaternion.identity, size); + } + + /// <summary> + /// Draws a rectangle outline. + /// The rectangle will be oriented along the rotation's X and Z axes. + /// + /// <code> + /// Draw.WireRectangle(new Vector3(0f, 0, 0), Quaternion.identity, new Vector2(1, 1), Color.black); + /// </code> + /// [Open online documentation to see images] + /// + /// This is identical to <see cref="Draw.WirePlane(float3,quaternion,float2)"/>, but this name is added for consistency. + /// + /// See: <see cref="WirePolygon"/> + /// </summary> + public void WireRectangle (float3 center, quaternion rotation, float2 size) { + WirePlane(center, rotation, size); + } + + /// <summary> + /// Draws a rectangle outline. + /// The rectangle corners are assumed to be in XY space. + /// This is particularly useful when combined with <see cref="InScreenSpace"/>. + /// + /// <code> + /// using (Draw.InScreenSpace(Camera.main)) { + /// Draw.xy.WireRectangle(new Rect(10, 10, 100, 100), Color.black); + /// } + /// </code> + /// [Open online documentation to see images] + /// + /// See: <see cref="WireRectangleXZ"/> + /// See: <see cref="WireRectangle(float3,quaternion,float2)"/> + /// See: <see cref="WirePolygon"/> + /// </summary> + [System.Obsolete("Use Draw.xy.WireRectangle instead")] + public void WireRectangle (Rect rect) { + xy.WireRectangle(rect); + } + + + /// <summary> + /// Draws a triangle outline. + /// + /// <code> + /// Draw.WireTriangle(Vector3.zero, Quaternion.identity, 0.5f, color); + /// </code> + /// [Open online documentation to see images] + /// + /// Note: This is a convenience wrapper for <see cref="WirePolygon(float3,int,quaternion,float)"/> + /// + /// See: <see cref="WireTriangle(float3,float3,float3)"/> + /// </summary> + /// <param name="center">Center of the triangle.</param> + /// <param name="rotation">Rotation of the triangle. The first vertex will be radius units in front of center as seen from the rotation's point of view.</param> + /// <param name="radius">Distance from the center to each vertex.</param> + public void WireTriangle (float3 center, quaternion rotation, float radius) { + WirePolygon(center, 3, rotation, radius); + } + + /// <summary> + /// Draws a pentagon outline. + /// + /// <code> + /// Draw.WirePentagon(Vector3.zero, Quaternion.identity, 0.5f, color); + /// </code> + /// [Open online documentation to see images] + /// + /// Note: This is a convenience wrapper for <see cref="WirePolygon(float3,int,quaternion,float)"/> + /// </summary> + /// <param name="center">Center of the polygon.</param> + /// <param name="rotation">Rotation of the polygon. The first vertex will be radius units in front of center as seen from the rotation's point of view.</param> + /// <param name="radius">Distance from the center to each vertex.</param> + public void WirePentagon (float3 center, quaternion rotation, float radius) { + WirePolygon(center, 5, rotation, radius); + } + + /// <summary> + /// Draws a hexagon outline. + /// + /// <code> + /// Draw.WireHexagon(Vector3.zero, Quaternion.identity, 0.5f, color); + /// </code> + /// [Open online documentation to see images] + /// + /// Note: This is a convenience wrapper for <see cref="WirePolygon(float3,int,quaternion,float)"/> + /// </summary> + /// <param name="center">Center of the polygon.</param> + /// <param name="rotation">Rotation of the polygon. The first vertex will be radius units in front of center as seen from the rotation's point of view.</param> + /// <param name="radius">Distance from the center to each vertex.</param> + public void WireHexagon (float3 center, quaternion rotation, float radius) { + WirePolygon(center, 6, rotation, radius); + } + + /// <summary> + /// Draws a regular polygon outline. + /// + /// <code> + /// Draw.WirePolygon(new Vector3(-0.5f, 0, +0.5f), 3, Quaternion.identity, 0.4f, color); + /// Draw.WirePolygon(new Vector3(+0.5f, 0, +0.5f), 4, Quaternion.identity, 0.4f, color); + /// Draw.WirePolygon(new Vector3(-0.5f, 0, -0.5f), 5, Quaternion.identity, 0.4f, color); + /// Draw.WirePolygon(new Vector3(+0.5f, 0, -0.5f), 6, Quaternion.identity, 0.4f, color); + /// </code> + /// [Open online documentation to see images] + /// + /// See: <see cref="WireTriangle"/> + /// See: <see cref="WirePentagon"/> + /// See: <see cref="WireHexagon"/> + /// </summary> + /// <param name="center">Center of the polygon.</param> + /// <param name="vertices">Number of corners (and sides) of the polygon.</param> + /// <param name="rotation">Rotation of the polygon. The first vertex will be radius units in front of center as seen from the rotation's point of view.</param> + /// <param name="radius">Distance from the center to each vertex.</param> + public void WirePolygon (float3 center, int vertices, quaternion rotation, float radius) { + PushMatrix(float4x4.TRS(center, rotation, new float3(radius, radius, radius))); + float3 prev = new float3(0, 0, 1); + for (int i = 1; i <= vertices; i++) { + float a = 2 * math.PI * (i / (float)vertices); + var p = new float3(math.sin(a), 0, math.cos(a)); + Line(prev, p); + prev = p; + } + PopMatrix(); + } + + /// <summary> + /// Draws a solid rectangle. + /// The rectangle corners are assumed to be in XY space. + /// This is particularly useful when combined with <see cref="InScreenSpace"/>. + /// + /// Behind the scenes this is implemented using <see cref="SolidPlane"/>. + /// + /// <code> + /// using (Draw.InScreenSpace(Camera.main)) { + /// Draw.xy.SolidRectangle(new Rect(10, 10, 100, 100), Color.black); + /// } + /// </code> + /// [Open online documentation to see images] + /// + /// See: <see cref="WireRectangleXZ"/> + /// See: <see cref="WireRectangle(float3,quaternion,float2)"/> + /// See: <see cref="SolidBox"/> + /// </summary> + [System.Obsolete("Use Draw.xy.SolidRectangle instead")] + public void SolidRectangle (Rect rect) { + xy.SolidRectangle(rect); + } + + /// <summary> + /// Draws a solid plane. + /// + /// <code> + /// Draw.SolidPlane(new float3(0, 0, 0), new float3(0, 1, 0), 1.0f, color); + /// </code> + /// [Open online documentation to see images] + /// </summary> + /// <param name="center">Center of the visualized plane.</param> + /// <param name="normal">Direction perpendicular to the plane. If this is (0,0,0) then nothing will be rendered.</param> + /// <param name="size">Width and height of the visualized plane.</param> + public void SolidPlane (float3 center, float3 normal, float2 size) { + if (math.any(normal)) { + SolidPlane(center, Quaternion.LookRotation(calculateTangent(normal), normal), size); + } + } + + /// <summary> + /// Draws a solid plane. + /// + /// The plane will lie in the XZ plane with respect to the rotation. + /// + /// <code> + /// Draw.SolidPlane(new float3(0, 0, 0), new float3(0, 1, 0), 1.0f, color); + /// </code> + /// [Open online documentation to see images] + /// </summary> + /// <param name="center">Center of the visualized plane.</param> + /// <param name="size">Width and height of the visualized plane.</param> + public void SolidPlane (float3 center, quaternion rotation, float2 size) { + PushMatrix(float4x4.TRS(center, rotation, new float3(size.x, 0, size.y))); + Reserve<BoxData>(); + Add(Command.Box); + Add(new BoxData { center = 0, size = 1 }); + PopMatrix(); + } + + /// <summary>Returns an arbitrary vector which is orthogonal to the given one</summary> + private static float3 calculateTangent (float3 normal) { + var tangent = math.cross(new float3(0, 1, 0), normal); + + if (math.all(tangent == 0)) tangent = math.cross(new float3(1, 0, 0), normal); + return tangent; + } + + /// <summary> + /// Draws a wire plane. + /// + /// <code> + /// Draw.WirePlane(new float3(0, 0, 0), new float3(0, 1, 0), 1.0f, color); + /// </code> + /// [Open online documentation to see images] + /// </summary> + /// <param name="center">Center of the visualized plane.</param> + /// <param name="normal">Direction perpendicular to the plane. If this is (0,0,0) then nothing will be rendered.</param> + /// <param name="size">Width and height of the visualized plane.</param> + public void WirePlane (float3 center, float3 normal, float2 size) { + if (math.any(normal)) { + WirePlane(center, Quaternion.LookRotation(calculateTangent(normal), normal), size); + } + } + + /// <summary> + /// Draws a wire plane. + /// + /// This is identical to <see cref="WireRectangle(float3,quaternion,float2)"/>, but it is included for consistency. + /// + /// <code> + /// Draw.WirePlane(new float3(0, 0, 0), new float3(0, 1, 0), 1.0f, color); + /// </code> + /// [Open online documentation to see images] + /// </summary> + /// <param name="center">Center of the visualized plane.</param> + /// <param name="rotation">Rotation of the plane. The plane will lie in the XZ plane with respect to the rotation.</param> + /// <param name="size">Width and height of the visualized plane.</param> + public void WirePlane (float3 center, quaternion rotation, float2 size) { + Reserve<PlaneData>(); + Add(Command.WirePlane); + Add(new PlaneData { center = center, rotation = rotation, size = size }); + } + + /// <summary> + /// Draws a plane and a visualization of its normal. + /// + /// <code> + /// Draw.PlaneWithNormal(new float3(0, 0, 0), new float3(0, 1, 0), 1.0f, color); + /// </code> + /// [Open online documentation to see images] + /// </summary> + /// <param name="center">Center of the visualized plane.</param> + /// <param name="normal">Direction perpendicular to the plane. If this is (0,0,0) then nothing will be rendered.</param> + /// <param name="size">Width and height of the visualized plane.</param> + public void PlaneWithNormal (float3 center, float3 normal, float2 size) { + if (math.any(normal)) { + PlaneWithNormal(center, Quaternion.LookRotation(calculateTangent(normal), normal), size); + } + } + + /// <summary> + /// Draws a plane and a visualization of its normal. + /// + /// <code> + /// Draw.PlaneWithNormal(new float3(0, 0, 0), new float3(0, 1, 0), 1.0f, color); + /// </code> + /// [Open online documentation to see images] + /// </summary> + /// <param name="center">Center of the visualized plane.</param> + /// <param name="rotation">Rotation of the plane. The plane will lie in the XZ plane with respect to the rotation.</param> + /// <param name="size">Width and height of the visualized plane.</param> + public void PlaneWithNormal (float3 center, quaternion rotation, float2 size) { + SolidPlane(center, rotation, size); + WirePlane(center, rotation, size); + ArrowRelativeSizeHead(center, center + math.mul(rotation, new float3(0, 1, 0)) * 0.5f, math.mul(rotation, new float3(0, 0, 1)), 0.2f); + } + + /// <summary> + /// Draws a solid triangle. + /// + /// <code> + /// Draw.xy.SolidTriangle(new float2(-0.43f, -0.25f), new float2(0, 0.5f), new float2(0.43f, -0.25f), color); + /// </code> + /// [Open online documentation to see images] + /// + /// Note: If you are going to be drawing lots of triangles it's better to use <see cref="Draw.SolidMesh"/> instead as it will be more efficient. + /// + /// See: <see cref="Draw.SolidMesh"/> + /// See: <see cref="Draw.WireTriangle"/> + /// </summary> + /// <param name="a">First corner of the triangle.</param> + /// <param name="b">Second corner of the triangle.</param> + /// <param name="c">Third corner of the triangle.</param> + public void SolidTriangle (float3 a, float3 b, float3 c) { + Reserve<TriangleData>(); + Add(Command.SolidTriangle); + Add(new TriangleData { a = a, b = b, c = c }); + } + + /// <summary> + /// Draws a solid box. + /// + /// <code> + /// Draw.SolidBox(new float3(0, 0, 0), new float3(1, 1, 1), color); + /// </code> + /// [Open online documentation to see images] + /// </summary> + /// <param name="center">Center of the box</param> + /// <param name="size">Width of the box along all dimensions</param> + public void SolidBox (float3 center, float3 size) { + Reserve<BoxData>(); + Add(Command.Box); + Add(new BoxData { center = center, size = size }); + } + + /// <summary> + /// Draws a solid box. + /// + /// <code> + /// Draw.SolidBox(new float3(0, 0, 0), new float3(1, 1, 1), color); + /// </code> + /// [Open online documentation to see images] + /// </summary> + /// <param name="bounds">Bounding box of the box</param> + public void SolidBox (Bounds bounds) { + SolidBox(bounds.center, bounds.size); + } + + /// <summary> + /// Draws a solid box. + /// + /// <code> + /// Draw.SolidBox(new float3(0, 0, 0), new float3(1, 1, 1), color); + /// </code> + /// [Open online documentation to see images] + /// </summary> + /// <param name="center">Center of the box</param> + /// <param name="rotation">Rotation of the box</param> + /// <param name="size">Width of the box along all dimensions</param> + public void SolidBox (float3 center, quaternion rotation, float3 size) { + PushMatrix(float4x4.TRS(center, rotation, size)); + SolidBox(float3.zero, Vector3.one); + PopMatrix(); + } + + /// <summary> + /// Draws a label in 3D space. + /// + /// The default alignment is <see cref="Drawing.LabelAlignment.MiddleLeft"/>. + /// + /// <code> + /// Draw.Label3D(new float3(0.2f, -1f, 0.2f), Quaternion.Euler(45, -110, -90), "Label", 1, LabelAlignment.Center, color); + /// </code> + /// [Open online documentation to see images] + /// + /// See: Label3D(float3,quaternion,string,float,LabelAlignment) + /// + /// Note: Only ASCII is supported since the built-in font texture only includes ASCII. Other characters will be rendered as question marks (?). + /// </summary> + /// <param name="position">Position in 3D space.</param> + /// <param name="rotation">Rotation in 3D space.</param> + /// <param name="text">Text to display.</param> + /// <param name="size">World size of the text. For large sizes an SDF (signed distance field) font is used and for small sizes a normal font texture is used.</param> + public void Label3D (float3 position, quaternion rotation, string text, float size) { + Label3D(position, rotation, text, size, LabelAlignment.MiddleLeft); + } + + /// <summary> + /// Draws a label in 3D space. + /// + /// <code> + /// Draw.Label3D(new float3(0.2f, -1f, 0.2f), Quaternion.Euler(45, -110, -90), "Label", 1, LabelAlignment.Center, color); + /// </code> + /// [Open online documentation to see images] + /// + /// See: Label3D(float3,quaternion,string,float) + /// + /// Note: Only ASCII is supported since the built-in font texture only includes ASCII. Other characters will be rendered as question marks (?). + /// + /// Note: This method cannot be used in burst since managed strings are not suppported in burst. However, you can use the separate Label3D overload which takes a FixedString. + /// </summary> + /// <param name="position">Position in 3D space.</param> + /// <param name="rotation">Rotation in 3D space.</param> + /// <param name="text">Text to display.</param> + /// <param name="size">World size of the text. For large sizes an SDF (signed distance field) font is used and for small sizes a normal font texture is used.</param> + /// <param name="alignment">How to align the text relative to the given position.</param> + public void Label3D (float3 position, quaternion rotation, string text, float size, LabelAlignment alignment) { + AssertBufferExists(); + var g = gizmos.Target as DrawingData; + Reserve<TextData3D>(); + Add(Command.Text3D); + Add(new TextData3D { center = position, rotation = rotation, numCharacters = text.Length, size = size, alignment = alignment }); + + Reserve(UnsafeUtility.SizeOf<System.UInt16>() * text.Length); + for (int i = 0; i < text.Length; i++) { + char c = text[i]; + System.UInt16 index = (System.UInt16)g.fontData.GetIndex(c); + Add(index); + } + } + + /// <summary> + /// Draws a label in 3D space aligned with the camera. + /// + /// The default alignment is <see cref="Drawing.LabelAlignment.MiddleLeft"/>. + /// + /// <code> + /// Draw.Label2D(Vector3.zero, "Label", 48, LabelAlignment.Center, color); + /// </code> + /// [Open online documentation to see images] + /// + /// See: Label2D(float3,string,float,LabelAlignment) + /// + /// Note: Only ASCII is supported since the built-in font texture only includes ASCII. Other characters will be rendered as question marks (?). + /// </summary> + /// <param name="position">Position in 3D space.</param> + /// <param name="text">Text to display.</param> + /// <param name="sizeInPixels">Size of the text in screen pixels. For large sizes an SDF (signed distance field) font is used and for small sizes a normal font texture is used.</param> + public void Label2D (float3 position, string text, float sizeInPixels = 14) { + Label2D(position, text, sizeInPixels, LabelAlignment.MiddleLeft); + } + + /// <summary> + /// Draws a label in 3D space aligned with the camera. + /// + /// <code> + /// Draw.Label2D(Vector3.zero, "Label", 48, LabelAlignment.Center, color); + /// </code> + /// [Open online documentation to see images] + /// + /// See: Label2D(float3,string,float) + /// + /// Note: Only ASCII is supported since the built-in font texture only includes ASCII. Other characters will be rendered as question marks (?). + /// + /// Note: This method cannot be used in burst since managed strings are not suppported in burst. However, you can use the separate Label2D overload which takes a FixedString. + /// </summary> + /// <param name="position">Position in 3D space.</param> + /// <param name="text">Text to display.</param> + /// <param name="sizeInPixels">Size of the text in screen pixels. For large sizes an SDF (signed distance field) font is used and for small sizes a normal font texture is used.</param> + /// <param name="alignment">How to align the text relative to the given position.</param> + public void Label2D (float3 position, string text, float sizeInPixels, LabelAlignment alignment) { + AssertBufferExists(); + var g = gizmos.Target as DrawingData; + Reserve<TextData>(); + Add(Command.Text); + Add(new TextData { center = position, numCharacters = text.Length, sizeInPixels = sizeInPixels, alignment = alignment }); + + Reserve(UnsafeUtility.SizeOf<System.UInt16>() * text.Length); + for (int i = 0; i < text.Length; i++) { + char c = text[i]; + System.UInt16 index = (System.UInt16)g.fontData.GetIndex(c); + Add(index); + } + } + + #region Label2DFixedString + /// <summary> + /// Draws a label in 3D space aligned with the camera. + /// + /// <code> + /// // This part can be inside a burst job + /// for (int i = 0; i < 10; i++) { + /// Unity.Collections.FixedString32Bytes text = $"X = {i}"; + /// builder.Label2D(new float3(i, 0, 0), ref text, 12, LabelAlignment.Center); + /// } + /// </code> + /// [Open online documentation to see images] + /// + /// See: Label2D(float3,string,float) + /// + /// Note: Only ASCII is supported since the built-in font texture only includes ASCII. Other characters will be rendered as question marks (?). + /// + /// Note: This method requires the Unity.Collections package version 0.8 or later. + /// </summary> + /// <param name="position">Position in 3D space.</param> + /// <param name="text">Text to display.</param> + /// <param name="sizeInPixels">Size of the text in screen pixels. For large sizes an SDF (signed distance field) font is used and for small sizes a normal font texture is used.</param> + public void Label2D (float3 position, ref FixedString32Bytes text, float sizeInPixels = 14) { + Label2D(position, ref text, sizeInPixels, LabelAlignment.MiddleLeft); + } + + /// <summary>\copydocref{Label2D(float3,FixedString32Bytes,float)}</summary> + public void Label2D (float3 position, ref FixedString64Bytes text, float sizeInPixels = 14) { + Label2D(position, ref text, sizeInPixels, LabelAlignment.MiddleLeft); + } + + /// <summary>\copydocref{Label2D(float3,FixedString32Bytes,float)}</summary> + public void Label2D (float3 position, ref FixedString128Bytes text, float sizeInPixels = 14) { + Label2D(position, ref text, sizeInPixels, LabelAlignment.MiddleLeft); + } + + /// <summary>\copydocref{Label2D(float3,FixedString32Bytes,float)}</summary> + public void Label2D (float3 position, ref FixedString512Bytes text, float sizeInPixels = 14) { + Label2D(position, ref text, sizeInPixels, LabelAlignment.MiddleLeft); + } + + /// <summary> + /// Draws a label in 3D space aligned with the camera. + /// + /// <code> + /// // This part can be inside a burst job + /// for (int i = 0; i < 10; i++) { + /// Unity.Collections.FixedString32Bytes text = $"X = {i}"; + /// builder.Label2D(new float3(i, 0, 0), ref text, 12, LabelAlignment.Center); + /// } + /// </code> + /// [Open online documentation to see images] + /// + /// See: Label2D(float3,string,float) + /// + /// Note: Only ASCII is supported since the built-in font texture only includes ASCII. Other characters will be rendered as question marks (?). + /// + /// Note: This method requires the Unity.Collections package version 0.8 or later. + /// </summary> + /// <param name="position">Position in 3D space.</param> + /// <param name="text">Text to display.</param> + /// <param name="sizeInPixels">Size of the text in screen pixels. For large sizes an SDF (signed distance field) font is used and for small sizes a normal font texture is used.</param> + /// <param name="alignment">How to align the text relative to the given position.</param> + public void Label2D (float3 position, ref FixedString32Bytes text, float sizeInPixels, LabelAlignment alignment) { +#if MODULE_COLLECTIONS_0_12_0_OR_NEWER + unsafe { + Label2D(position, text.GetUnsafePtr(), text.Length, sizeInPixels, alignment); + } +#else + Debug.LogError("The Label2D method which takes FixedStrings requires the Unity.Collections package version 0.12 or newer"); +#endif + } + + /// <summary>\copydocref{Label2D(float3,FixedString32Bytes,float,LabelAlignment)}</summary> + public void Label2D (float3 position, ref FixedString64Bytes text, float sizeInPixels, LabelAlignment alignment) { +#if MODULE_COLLECTIONS_0_12_0_OR_NEWER + unsafe { + Label2D(position, text.GetUnsafePtr(), text.Length, sizeInPixels, alignment); + } +#else + Debug.LogError("The Label2D method which takes FixedStrings requires the Unity.Collections package version 0.12 or newer"); +#endif + } + + /// <summary>\copydocref{Label2D(float3,FixedString32Bytes,float,LabelAlignment)}</summary> + public void Label2D (float3 position, ref FixedString128Bytes text, float sizeInPixels, LabelAlignment alignment) { +#if MODULE_COLLECTIONS_0_12_0_OR_NEWER + unsafe { + Label2D(position, text.GetUnsafePtr(), text.Length, sizeInPixels, alignment); + } +#else + Debug.LogError("The Label2D method which takes FixedStrings requires the Unity.Collections package version 0.12 or newer"); +#endif + } + + /// <summary>\copydocref{Label2D(float3,FixedString32Bytes,float,LabelAlignment)}</summary> + public void Label2D (float3 position, ref FixedString512Bytes text, float sizeInPixels, LabelAlignment alignment) { +#if MODULE_COLLECTIONS_0_12_0_OR_NEWER + unsafe { + Label2D(position, text.GetUnsafePtr(), text.Length, sizeInPixels, alignment); + } +#else + Debug.LogError("The Label2D method which takes FixedStrings requires the Unity.Collections package version 0.12 or newer"); +#endif + } + + /// <summary>\copydocref{Label2D(float3,FixedString32Bytes,float,LabelAlignment)}</summary> + internal unsafe void Label2D (float3 position, byte* text, int byteCount, float sizeInPixels, LabelAlignment alignment) { +#if MODULE_COLLECTIONS_0_12_0_OR_NEWER + AssertBufferExists(); + Reserve<TextData>(); + Add(Command.Text); + Add(new TextData { center = position, numCharacters = byteCount, sizeInPixels = sizeInPixels, alignment = alignment }); + + Reserve(UnsafeUtility.SizeOf<System.UInt16>() * byteCount); + for (int i = 0; i < byteCount; i++) { + // The first 128 elements in the font data are guaranteed to be laid out as ascii. + // We use this since we cannot use the dynamic font lookup. + System.UInt16 c = *(text + i); + if (c >= 128) c = (System.UInt16) '?'; + if (c == (byte)'\n') c = SDFLookupData.Newline; + // Ignore carriage return instead of printing them as '?'. Windows encodes newlines as \r\n. + if (c == (byte)'\r') continue; + Add(c); + } +#endif + } + #endregion + + #region Label3DFixedString + /// <summary> + /// Draws a label in 3D space. + /// + /// <code> + /// // This part can be inside a burst job + /// for (int i = 0; i < 10; i++) { + /// Unity.Collections.FixedString32Bytes text = $"X = {i}"; + /// builder.Label3D(new float3(i, 0, 0), quaternion.identity, ref text, 1, LabelAlignment.Center); + /// } + /// </code> + /// [Open online documentation to see images] + /// + /// See: Label3D(float3,quaternion,string,float) + /// + /// Note: Only ASCII is supported since the built-in font texture only includes ASCII. Other characters will be rendered as question marks (?). + /// + /// Note: This method requires the Unity.Collections package version 0.8 or later. + /// </summary> + /// <param name="position">Position in 3D space.</param> + /// <param name="rotation">Rotation in 3D space.</param> + /// <param name="text">Text to display.</param> + /// <param name="size">World size of the text. For large sizes an SDF (signed distance field) font is used and for small sizes a normal font texture is used.</param> + public void Label3D (float3 position, quaternion rotation, ref FixedString32Bytes text, float size) { + Label3D(position, rotation, ref text, size, LabelAlignment.MiddleLeft); + } + + /// <summary>\copydocref{Label3D(float3,quaternion,FixedString32Bytes,float)}</summary> + public void Label3D (float3 position, quaternion rotation, ref FixedString64Bytes text, float size) { + Label3D(position, rotation, ref text, size, LabelAlignment.MiddleLeft); + } + + /// <summary>\copydocref{Label3D(float3,quaternion,FixedString32Bytes,float)}</summary> + public void Label3D (float3 position, quaternion rotation, ref FixedString128Bytes text, float size) { + Label3D(position, rotation, ref text, size, LabelAlignment.MiddleLeft); + } + + /// <summary>\copydocref{Label3D(float3,quaternion,FixedString32Bytes,float)}</summary> + public void Label3D (float3 position, quaternion rotation, ref FixedString512Bytes text, float size) { + Label3D(position, rotation, ref text, size, LabelAlignment.MiddleLeft); + } + + /// <summary> + /// Draws a label in 3D space. + /// + /// <code> + /// // This part can be inside a burst job + /// for (int i = 0; i < 10; i++) { + /// Unity.Collections.FixedString32Bytes text = $"X = {i}"; + /// builder.Label3D(new float3(i, 0, 0), quaternion.identity, ref text, 1, LabelAlignment.Center); + /// } + /// </code> + /// [Open online documentation to see images] + /// + /// See: Label3D(float3,quaternion,string,float) + /// + /// Note: Only ASCII is supported since the built-in font texture only includes ASCII. Other characters will be rendered as question marks (?). + /// + /// Note: This method requires the Unity.Collections package version 0.8 or later. + /// </summary> + /// <param name="position">Position in 3D space.</param> + /// <param name="rotation">Rotation in 3D space.</param> + /// <param name="text">Text to display.</param> + /// <param name="size">World size of the text. For large sizes an SDF (signed distance field) font is used and for small sizes a normal font texture is used.</param> + /// <param name="alignment">How to align the text relative to the given position.</param> + public void Label3D (float3 position, quaternion rotation, ref FixedString32Bytes text, float size, LabelAlignment alignment) { +#if MODULE_COLLECTIONS_0_12_0_OR_NEWER + unsafe { + Label3D(position, rotation, text.GetUnsafePtr(), text.Length, size, alignment); + } +#else + Debug.LogError("The Label3D method which takes FixedStrings requires the Unity.Collections package version 0.12 or newer"); +#endif + } + + /// <summary>\copydocref{Label3D(float3,quaternion,FixedString32Bytes,float,LabelAlignment)}</summary> + public void Label3D (float3 position, quaternion rotation, ref FixedString64Bytes text, float size, LabelAlignment alignment) { +#if MODULE_COLLECTIONS_0_12_0_OR_NEWER + unsafe { + Label3D(position, rotation, text.GetUnsafePtr(), text.Length, size, alignment); + } +#else + Debug.LogError("The Label3D method which takes FixedStrings requires the Unity.Collections package version 0.12 or newer"); +#endif + } + + /// <summary>\copydocref{Label3D(float3,quaternion,FixedString32Bytes,float,LabelAlignment)}</summary> + public void Label3D (float3 position, quaternion rotation, ref FixedString128Bytes text, float size, LabelAlignment alignment) { +#if MODULE_COLLECTIONS_0_12_0_OR_NEWER + unsafe { + Label3D(position, rotation, text.GetUnsafePtr(), text.Length, size, alignment); + } +#else + Debug.LogError("The Label3D method which takes FixedStrings requires the Unity.Collections package version 0.12 or newer"); +#endif + } + + /// <summary>\copydocref{Label3D(float3,quaternion,FixedString32Bytes,float,LabelAlignment)}</summary> + public void Label3D (float3 position, quaternion rotation, ref FixedString512Bytes text, float size, LabelAlignment alignment) { +#if MODULE_COLLECTIONS_0_12_0_OR_NEWER + unsafe { + Label3D(position, rotation, text.GetUnsafePtr(), text.Length, size, alignment); + } +#else + Debug.LogError("The Label3D method which takes FixedStrings requires the Unity.Collections package version 0.12 or newer"); +#endif + } + + /// <summary>\copydocref{Label3D(float3,quaternion,FixedString32Bytes,float,LabelAlignment)}</summary> + internal unsafe void Label3D (float3 position, quaternion rotation, byte* text, int byteCount, float size, LabelAlignment alignment) { +#if MODULE_COLLECTIONS_0_12_0_OR_NEWER + AssertBufferExists(); + Reserve<TextData3D>(); + Add(Command.Text3D); + Add(new TextData3D { center = position, rotation = rotation, numCharacters = byteCount, size = size, alignment = alignment }); + + Reserve(UnsafeUtility.SizeOf<System.UInt16>() * byteCount); + for (int i = 0; i < byteCount; i++) { + // The first 128 elements in the font data are guaranteed to be laid out as ascii. + // We use this since we cannot use the dynamic font lookup. + System.UInt16 c = *(text + i); + if (c >= 128) c = (System.UInt16) '?'; + if (c == (byte)'\n') c = SDFLookupData.Newline; + Add(c); + } +#endif + } + #endregion + } +} diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/CommandBuilder.cs.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/CommandBuilder.cs.meta new file mode 100644 index 0000000..4fe5c08 --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/CommandBuilder.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0c687e553ea664510a066332ee0f6582 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/CommandBuilder2D.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/CommandBuilder2D.cs new file mode 100644 index 0000000..10fb225 --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/CommandBuilder2D.cs @@ -0,0 +1,420 @@ +using System.Collections.Generic; +using Unity.Burst; +using Unity.Collections; +using Unity.Mathematics; +using UnityEngine; +using static Pathfinding.Drawing.CommandBuilder; + +namespace Pathfinding.Drawing { + /// <summary> + /// 2D wrapper for a <see cref="CommandBuilder"/>. + /// + /// <code> + /// 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); + /// </code> + /// + /// See: 2d-drawing (view in online documentation for working links) + /// See: <see cref="Draw.xy"/> + /// See: <see cref="Draw.xz"/> + /// </summary> + public partial struct CommandBuilder2D { + /// <summary>The wrapped command builder</summary> + private CommandBuilder draw; + /// <summary>True if drawing in the XY plane, false if drawing in the XZ plane</summary> + 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; + } + + /// <summary> + /// Draws a line between two points. + /// + /// [Open online documentation to see images] + /// + /// <code> + /// void Update () { + /// Draw.Line(Vector3.zero, Vector3.up); + /// } + /// </code> + /// </summary> + public void Line (float2 a, float2 b) { + draw.Reserve<LineData>(); + // 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; + } + } + + /// <summary> + /// Draws a line between two points. + /// + /// [Open online documentation to see images] + /// + /// <code> + /// void Update () { + /// Draw.Line(Vector3.zero, Vector3.up); + /// } + /// </code> + /// </summary> + public void Line (float2 a, float2 b, Color color) { + draw.Reserve<Color32, LineData>(); + // 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; + } + } + + /// <summary> + /// Draws a line between two points. + /// + /// [Open online documentation to see images] + /// + /// <code> + /// void Update () { + /// Draw.Line(Vector3.zero, Vector3.up); + /// } + /// </code> + /// </summary> + public void Line (float3 a, float3 b) { + draw.Line(a, b); + } + + /// <summary> + /// Draws a circle. + /// + /// You can draw an arc by supplying the startAngle and endAngle parameters. + /// + /// [Open online documentation to see images] + /// + /// See: <see cref="Circle(float3,float3,float)"/> + /// See: <see cref="Arc(float3,float3,float3)"/> + /// </summary> + /// <param name="center">Center of the circle or arc.</param> + /// <param name="radius">Radius of the circle or arc.</param> + /// <param name="startAngle">Starting angle in radians. 0 corrsponds to the positive X axis.</param> + /// <param name="endAngle">End angle in radians.</param> + 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); + } + + /// <summary> + /// Draws a circle. + /// + /// You can draw an arc by supplying the startAngle and endAngle parameters. + /// + /// [Open online documentation to see images] + /// + /// See: <see cref="Circle(float3,float3,float)"/> + /// See: <see cref="Arc(float3,float3,float3)"/> + /// </summary> + /// <param name="center">Center of the circle or arc.</param> + /// <param name="radius">Radius of the circle or arc.</param> + /// <param name="startAngle">Starting angle in radians. 0 corrsponds to the positive X axis.</param> + /// <param name="endAngle">End angle in radians.</param> + 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); + } + } + + /// <summary>\copydocref{SolidCircle(float3,float,float,float)}</summary> + 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); + } + + /// <summary> + /// Draws a disc. + /// + /// You can draw an arc by supplying the startAngle and endAngle parameters. + /// + /// [Open online documentation to see images] + /// + /// See: <see cref="Draw.SolidCircle(float3,float3,float)"/> + /// See: <see cref="SolidArc(float3,float3,float3)"/> + /// </summary> + /// <param name="center">Center of the disc or solid arc.</param> + /// <param name="radius">Radius of the disc or solid arc.</param> + /// <param name="startAngle">Starting angle in radians. 0 corrsponds to the positive X axis.</param> + /// <param name="endAngle">End angle in radians.</param> + 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(new float3(center.x, -center.z, center.y), radius, startAngle, endAngle); + if (xy) draw.PopMatrix(); + } + + /// <summary> + /// Draws a wire pill in 2D. + /// + /// <code> + /// Draw.xy.WirePill(new float2(-0.5f, -0.5f), new float2(0.5f, 0.5f), 0.5f, color); + /// </code> + /// + /// [Open online documentation to see images] + /// + /// See: <see cref="WirePill(float2,float2,float,float)"/> + /// </summary> + /// <param name="a">Center of the first circle of the capsule.</param> + /// <param name="b">Center of the second circle of the capsule.</param> + /// <param name="radius">Radius of the capsule.</param> + public void WirePill (float2 a, float2 b, float radius) { + WirePill(a, b - a, math.length(b - a), radius); + } + + /// <summary> + /// Draws a wire pill in 2D. + /// + /// <code> + /// Draw.xy.WirePill(new float2(-0.5f, -0.5f), new float2(1, 1), 1, 0.5f, color); + /// </code> + /// + /// [Open online documentation to see images] + /// + /// See: <see cref="WirePill(float2,float2,float)"/> + /// </summary> + /// <param name="position">Center of the first circle of the capsule.</param> + /// <param name="direction">The main axis of the capsule. Does not have to be normalized. If zero, a circle will be drawn.</param> + /// <param name="length">Length of the main axis of the capsule, from circle center to circle center. If zero, a circle will be drawn.</param> + /// <param name="radius">Radius of the capsule.</param> + 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(); + } + } + + /// <summary>\copydocref{CommandBuilder.Polyline(List<Vector3>,bool)}</summary> + [BurstDiscard] + public void Polyline (List<Vector2> 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]); + } + + /// <summary>\copydocref{CommandBuilder.Polyline(Vector3[],bool)}</summary> + [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]); + } + + /// <summary>\copydocref{CommandBuilder.Polyline(float3[],bool)}</summary> + [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]); + } + + /// <summary>\copydocref{CommandBuilder.Polyline(NativeArray<float3>,bool)}</summary> + public void Polyline (NativeArray<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]); + } + + /// <summary> + /// Draws a 2D cross. + /// + /// <code> + /// Draw.xz.Cross(float3.zero, color); + /// </code> + /// [Open online documentation to see images] + /// + /// See: <see cref="Draw.Cross"/> + /// </summary> + 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)); + } + + /// <summary> + /// Draws a rectangle outline. + /// The rectangle will be oriented along the rotation's X and Z axes. + /// + /// <code> + /// Draw.xz.WireRectangle(new Vector3(0f, 0, 0), new Vector2(1, 1), Color.black); + /// </code> + /// [Open online documentation to see images] + /// + /// This is identical to <see cref="Draw.WirePlane(float3,quaternion,float2)"/>, but this name is added for consistency. + /// + /// See: <see cref="Draw.WirePolygon"/> + /// </summary> + public void WireRectangle (float3 center, float2 size) { + draw.WirePlane(center, xy ? XY_TO_XZ_ROTATION : XZ_TO_XZ_ROTATION, size); + } + + /// <summary> + /// Draws a rectangle outline. + /// This is particularly useful when combined with <see cref="InScreenSpace"/>. + /// + /// <code> + /// using (Draw.InScreenSpace(Camera.main)) { + /// Draw.xy.WireRectangle(new Rect(10, 10, 100, 100), Color.black); + /// } + /// </code> + /// [Open online documentation to see images] + /// + /// See: <see cref="Draw.WireRectangle(float3,quaternion,float2)"/> + /// See: <see cref="Draw.WirePolygon"/> + /// </summary> + 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)); + } + + /// <summary> + /// Draws a solid rectangle. + /// This is particularly useful when combined with <see cref="InScreenSpace"/>. + /// + /// Behind the scenes this is implemented using <see cref="Draw.SolidPlane"/>. + /// + /// <code> + /// using (Draw.InScreenSpace(Camera.main)) { + /// Draw.xy.SolidRectangle(new Rect(10, 10, 100, 100), Color.black); + /// } + /// </code> + /// [Open online documentation to see images] + /// + /// See: <see cref="WireRectangle"/> + /// See: <see cref="Draw.WireRectangle(float3,quaternion,float2)"/> + /// See: <see cref="Draw.SolidBox"/> + /// </summary> + 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)); + } + + /// <summary> + /// Draws a grid of lines. + /// + /// <code> + /// Draw.xz.WireGrid(Vector3.zero, new int2(3, 3), new float2(1, 1), color); + /// </code> + /// [Open online documentation to see images] + /// + /// See: <see cref="Draw.WireGrid"/> + /// </summary> + /// <param name="center">Center of the grid</param> + /// <param name="cells">Number of cells of the grid. Should be greater than 0.</param> + /// <param name="totalSize">Total size of the grid along the X and Z axes.</param> + 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); + } + + /// <summary> + /// Draws a grid of lines. + /// + /// <code> + /// Draw.xz.WireGrid(Vector3.zero, new int2(3, 3), new float2(1, 1), color); + /// </code> + /// [Open online documentation to see images] + /// + /// See: <see cref="Draw.WireGrid"/> + /// </summary> + /// <param name="center">Center of the grid</param> + /// <param name="cells">Number of cells of the grid. Should be greater than 0.</param> + /// <param name="totalSize">Total size of the grid along the X and Z axes.</param> + public void WireGrid (float3 center, int2 cells, float2 totalSize) { + draw.WireGrid(center, xy ? XY_TO_XZ_ROTATION : XZ_TO_XZ_ROTATION, cells, totalSize); + } + } +} diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/CommandBuilder2D.cs.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/CommandBuilder2D.cs.meta new file mode 100644 index 0000000..dbc8cc1 --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/CommandBuilder2D.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 87689c56d7a309340b0da559853df2c7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/CommandBuilder2DExtensions.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/CommandBuilder2DExtensions.cs new file mode 100644 index 0000000..3bb4ae0 --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/CommandBuilder2DExtensions.cs @@ -0,0 +1,587 @@ +// This file is automatically generated by a script based on the CommandBuilder API. +// This file adds additional overloads to the CommandBuilder API. +using Unity.Burst; +using UnityEngine; +using System.Collections.Generic; +using Unity.Collections; +using Unity.Mathematics; +using Unity.Collections.LowLevel.Unsafe; +using Unity.Jobs; +using static Pathfinding.Drawing.CommandBuilder; + +namespace Pathfinding.Drawing { + public partial struct CommandBuilder2D { + /// <summary>\copydocref{CommandBuilder.WithMatrix(Matrix4x4)}</summary> + [BurstDiscard] + public ScopeMatrix WithMatrix (Matrix4x4 matrix) { + return draw.WithMatrix(matrix); + } + /// <summary>\copydocref{CommandBuilder.WithMatrix(float3x3)}</summary> + [BurstDiscard] + public ScopeMatrix WithMatrix (float3x3 matrix) { + return draw.WithMatrix(matrix); + } + /// <summary>\copydocref{CommandBuilder.WithColor(Color)}</summary> + [BurstDiscard] + public ScopeColor WithColor (Color color) { + return draw.WithColor(color); + } + + /// <summary>\copydocref{CommandBuilder.WithLineWidth(float,bool)}</summary> + [BurstDiscard] + public ScopeLineWidth WithLineWidth (float pixels, bool automaticJoins = true) { + return draw.WithLineWidth(pixels, automaticJoins); + } + + + /// <summary>\copydocref{CommandBuilder.PushMatrix(Matrix4x4)}</summary> + public void PushMatrix (Matrix4x4 matrix) { + draw.PushMatrix(matrix); + } + /// <summary>\copydocref{CommandBuilder.PushMatrix(float4x4)}</summary> + public void PushMatrix (float4x4 matrix) { + draw.PushMatrix(matrix); + } + + + /// <summary>\copydocref{CommandBuilder.PopMatrix()}</summary> + public void PopMatrix () { + draw.PopMatrix(); + } + + + + + + + + + /// <summary>\copydocref{CommandBuilder.Line(Vector3,Vector3)}</summary> + public void Line (Vector3 a, Vector3 b) { + draw.Line(a, b); + } + /// <summary>\copydocref{CommandBuilder.Line(Vector3,Vector3)}</summary> + public void Line (Vector2 a, Vector2 b) { + Line(xy ? new Vector3(a.x, a.y, 0) : new Vector3(a.x, 0, a.y), xy ? new Vector3(b.x, b.y, 0) : new Vector3(b.x, 0, b.y)); + } + /// <summary>\copydocref{CommandBuilder.Line(Vector3,Vector3,Color)}</summary> + public void Line (Vector3 a, Vector3 b, Color color) { + draw.Line(a, b, color); + } + /// <summary>\copydocref{CommandBuilder.Line(Vector3,Vector3,Color)}</summary> + public void Line (Vector2 a, Vector2 b, Color color) { + Line(xy ? new Vector3(a.x, a.y, 0) : new Vector3(a.x, 0, a.y), xy ? new Vector3(b.x, b.y, 0) : new Vector3(b.x, 0, b.y), color); + } + /// <summary>\copydocref{CommandBuilder.Ray(float3,float3)}</summary> + public void Ray (float3 origin, float3 direction) { + draw.Ray(origin, direction); + } + /// <summary>\copydocref{CommandBuilder.Ray(float3,float3)}</summary> + public void Ray (float2 origin, float2 direction) { + Ray(xy ? new float3(origin, 0) : new float3(origin.x, 0, origin.y), xy ? new float3(direction, 0) : new float3(direction.x, 0, direction.y)); + } + /// <summary>\copydocref{CommandBuilder.Ray(Ray,float)}</summary> + public void Ray (Ray ray, float length) { + draw.Ray(ray, length); + } + /// <summary>\copydocref{CommandBuilder.Arc(float3,float3,float3)}</summary> + public void Arc (float3 center, float3 start, float3 end) { + draw.Arc(center, start, end); + } + /// <summary>\copydocref{CommandBuilder.Arc(float3,float3,float3)}</summary> + public void Arc (float2 center, float2 start, float2 end) { + Arc(xy ? new float3(center, 0) : new float3(center.x, 0, center.y), xy ? new float3(start, 0) : new float3(start.x, 0, start.y), xy ? new float3(end, 0) : new float3(end.x, 0, end.y)); + } + + + + + /// <summary>\copydocref{CommandBuilder.Polyline(List<Vector3>,bool)}</summary> + [BurstDiscard] + public void Polyline (List<Vector3> points, bool cycle = false) { + draw.Polyline(points, cycle); + } + /// <summary>\copydocref{CommandBuilder.Polyline(Vector3[],bool)}</summary> + [BurstDiscard] + public void Polyline (Vector3[] points, bool cycle = false) { + draw.Polyline(points, cycle); + } + /// <summary>\copydocref{CommandBuilder.Polyline(float3[],bool)}</summary> + [BurstDiscard] + public void Polyline (float3[] points, bool cycle = false) { + draw.Polyline(points, cycle); + } + /// <summary>\copydocref{CommandBuilder.Polyline(NativeArray<float3>,bool)}</summary> + public void Polyline (NativeArray<float3> points, bool cycle = false) { + draw.Polyline(points, cycle); + } + + + + /// <summary>\copydocref{CommandBuilder.Cross(float3,float)}</summary> + public void Cross (float3 position, float size = 1) { + draw.Cross(position, size); + } + /// <summary>\copydocref{CommandBuilder.Bezier(float3,float3,float3,float3)}</summary> + public void Bezier (float3 p0, float3 p1, float3 p2, float3 p3) { + draw.Bezier(p0, p1, p2, p3); + } + /// <summary>\copydocref{CommandBuilder.Bezier(float3,float3,float3,float3)}</summary> + public void Bezier (float2 p0, float2 p1, float2 p2, float2 p3) { + Bezier(xy ? new float3(p0, 0) : new float3(p0.x, 0, p0.y), xy ? new float3(p1, 0) : new float3(p1.x, 0, p1.y), xy ? new float3(p2, 0) : new float3(p2.x, 0, p2.y), xy ? new float3(p3, 0) : new float3(p3.x, 0, p3.y)); + } + + + + /// <summary>\copydocref{CommandBuilder.Arrow(float3,float3)}</summary> + public void Arrow (float3 from, float3 to) { + ArrowRelativeSizeHead(from, to, xy ? XY_UP : XZ_UP, 0.2f); + } + /// <summary>\copydocref{CommandBuilder.Arrow(float3,float3)}</summary> + public void Arrow (float2 from, float2 to) { + Arrow(xy ? new float3(from, 0) : new float3(from.x, 0, from.y), xy ? new float3(to, 0) : new float3(to.x, 0, to.y)); + } + /// <summary>\copydocref{CommandBuilder.Arrow(float3,float3,float3,float)}</summary> + public void Arrow (float3 from, float3 to, float3 up, float headSize) { + draw.Arrow(from, to, up, headSize); + } + /// <summary>\copydocref{CommandBuilder.Arrow(float3,float3,float3,float)}</summary> + public void Arrow (float2 from, float2 to, float2 up, float headSize) { + Arrow(xy ? new float3(from, 0) : new float3(from.x, 0, from.y), xy ? new float3(to, 0) : new float3(to.x, 0, to.y), xy ? new float3(up, 0) : new float3(up.x, 0, up.y), headSize); + } + /// <summary>\copydocref{CommandBuilder.ArrowRelativeSizeHead(float3,float3,float3,float)}</summary> + public void ArrowRelativeSizeHead (float3 from, float3 to, float3 up, float headFraction) { + draw.ArrowRelativeSizeHead(from, to, up, headFraction); + } + /// <summary>\copydocref{CommandBuilder.ArrowRelativeSizeHead(float3,float3,float3,float)}</summary> + public void ArrowRelativeSizeHead (float2 from, float2 to, float2 up, float headFraction) { + ArrowRelativeSizeHead(xy ? new float3(from, 0) : new float3(from.x, 0, from.y), xy ? new float3(to, 0) : new float3(to.x, 0, to.y), xy ? new float3(up, 0) : new float3(up.x, 0, up.y), headFraction); + } + + + + + /// <summary>\copydocref{CommandBuilder.ArrowheadArc(float3,float3,float,float)}</summary> + public void ArrowheadArc (float3 origin, float3 direction, float offset, float width = 60) { + if (!math.any(direction)) return; + if (offset < 0) throw new System.ArgumentOutOfRangeException(nameof(offset)); + if (offset == 0) return; + + var rot = Quaternion.LookRotation(direction, xy ? XY_UP : XZ_UP); + PushMatrix(Matrix4x4.TRS(origin, rot, Vector3.one)); + var a1 = math.PI * 0.5f - width * (0.5f * Mathf.Deg2Rad); + var a2 = math.PI * 0.5f + width * (0.5f * Mathf.Deg2Rad); + draw.CircleXZInternal(float3.zero, offset, a1, a2); + var p1 = new float3(math.cos(a1), 0, math.sin(a1)) * offset; + var p2 = new float3(math.cos(a2), 0, math.sin(a2)) * offset; + const float sqrt2 = 1.4142f; + var p3 = new float3(0, 0, sqrt2 * offset); + Line(p1, p3); + Line(p3, p2); + PopMatrix(); + } + /// <summary>\copydocref{CommandBuilder.ArrowheadArc(float3,float3,float,float)}</summary> + public void ArrowheadArc (float2 origin, float2 direction, float offset, float width = 60) { + ArrowheadArc(xy ? new float3(origin, 0) : new float3(origin.x, 0, origin.y), xy ? new float3(direction, 0) : new float3(direction.x, 0, direction.y), offset, width); + } + + + /// <summary>\copydocref{CommandBuilder.WireRectangle(float3,quaternion,float2)}</summary> + public void WireRectangle (float3 center, quaternion rotation, float2 size) { + draw.WireRectangle(center, rotation, size); + } + /// <summary>\copydocref{CommandBuilder.WireRectangle(float3,quaternion,float2)}</summary> + public void WireRectangle (float2 center, quaternion rotation, float2 size) { + WireRectangle(xy ? new float3(center, 0) : new float3(center.x, 0, center.y), rotation, size); + } + + + + + + + + + + + + + + + + + + + + + + + + + /// <summary>\copydocref{Ray(float3,float3)}</summary> + public void Ray (float3 origin, float3 direction, Color color) { + draw.Ray(origin, direction, color); + } + /// <summary>\copydocref{Ray(float2,float2)}</summary> + public void Ray (float2 origin, float2 direction, Color color) { + Ray(xy ? new float3(origin, 0) : new float3(origin.x, 0, origin.y), xy ? new float3(direction, 0) : new float3(direction.x, 0, direction.y), color); + } + /// <summary>\copydocref{Ray(Ray,float)}</summary> + public void Ray (Ray ray, float length, Color color) { + draw.Ray(ray, length, color); + } + /// <summary>\copydocref{Arc(float3,float3,float3)}</summary> + public void Arc (float3 center, float3 start, float3 end, Color color) { + draw.Arc(center, start, end, color); + } + /// <summary>\copydocref{Arc(float2,float2,float2)}</summary> + public void Arc (float2 center, float2 start, float2 end, Color color) { + Arc(xy ? new float3(center, 0) : new float3(center.x, 0, center.y), xy ? new float3(start, 0) : new float3(start.x, 0, start.y), xy ? new float3(end, 0) : new float3(end.x, 0, end.y), color); + } + + + + + + + /// <summary>\copydocref{Polyline(List<Vector3>,bool)}</summary> + [BurstDiscard] + public void Polyline (List<Vector3> points, bool cycle, Color color) { + draw.Polyline(points, cycle, color); + } + /// <summary>\copydocref{Polyline(List<Vector3>,bool)}</summary> + [BurstDiscard] + public void Polyline (List<Vector3> points, Color color) { + Polyline(points, false, color); + } + /// <summary>\copydocref{Polyline(Vector3[],bool)}</summary> + [BurstDiscard] + public void Polyline (Vector3[] points, bool cycle, Color color) { + draw.Polyline(points, cycle, color); + } + /// <summary>\copydocref{Polyline(Vector3[],bool)}</summary> + [BurstDiscard] + public void Polyline (Vector3[] points, Color color) { + Polyline(points, false, color); + } + /// <summary>\copydocref{Polyline(float3[],bool)}</summary> + [BurstDiscard] + public void Polyline (float3[] points, bool cycle, Color color) { + draw.Polyline(points, cycle, color); + } + /// <summary>\copydocref{Polyline(float3[],bool)}</summary> + [BurstDiscard] + public void Polyline (float3[] points, Color color) { + Polyline(points, false, color); + } + /// <summary>\copydocref{Polyline(NativeArray<float3>,bool)}</summary> + public void Polyline (NativeArray<float3> points, bool cycle, Color color) { + draw.Polyline(points, cycle, color); + } + /// <summary>\copydocref{Polyline(NativeArray<float3>,bool)}</summary> + public void Polyline (NativeArray<float3> points, Color color) { + Polyline(points, false, color); + } + + + + /// <summary>\copydocref{Cross(float3,float)}</summary> + public void Cross (float3 position, float size, Color color) { + draw.Cross(position, size, color); + } + /// <summary>\copydocref{Cross(float3,float)}</summary> + public void Cross (float3 position, Color color) { + Cross(position, 1, color); + } + /// <summary>\copydocref{Bezier(float3,float3,float3,float3)}</summary> + public void Bezier (float3 p0, float3 p1, float3 p2, float3 p3, Color color) { + draw.Bezier(p0, p1, p2, p3, color); + } + /// <summary>\copydocref{Bezier(float2,float2,float2,float2)}</summary> + public void Bezier (float2 p0, float2 p1, float2 p2, float2 p3, Color color) { + Bezier(xy ? new float3(p0, 0) : new float3(p0.x, 0, p0.y), xy ? new float3(p1, 0) : new float3(p1.x, 0, p1.y), xy ? new float3(p2, 0) : new float3(p2.x, 0, p2.y), xy ? new float3(p3, 0) : new float3(p3.x, 0, p3.y), color); + } + + + + /// <summary>\copydocref{Arrow(float3,float3)}</summary> + public void Arrow (float3 from, float3 to, Color color) { + ArrowRelativeSizeHead(from, to, xy ? XY_UP : XZ_UP, 0.2f, color); + } + /// <summary>\copydocref{Arrow(float2,float2)}</summary> + public void Arrow (float2 from, float2 to, Color color) { + Arrow(xy ? new float3(from, 0) : new float3(from.x, 0, from.y), xy ? new float3(to, 0) : new float3(to.x, 0, to.y), color); + } + /// <summary>\copydocref{Arrow(float3,float3,float3,float)}</summary> + public void Arrow (float3 from, float3 to, float3 up, float headSize, Color color) { + draw.Arrow(from, to, up, headSize, color); + } + /// <summary>\copydocref{Arrow(float2,float2,float2,float)}</summary> + public void Arrow (float2 from, float2 to, float2 up, float headSize, Color color) { + Arrow(xy ? new float3(from, 0) : new float3(from.x, 0, from.y), xy ? new float3(to, 0) : new float3(to.x, 0, to.y), xy ? new float3(up, 0) : new float3(up.x, 0, up.y), headSize, color); + } + /// <summary>\copydocref{ArrowRelativeSizeHead(float3,float3,float3,float)}</summary> + public void ArrowRelativeSizeHead (float3 from, float3 to, float3 up, float headFraction, Color color) { + draw.ArrowRelativeSizeHead(from, to, up, headFraction, color); + } + /// <summary>\copydocref{ArrowRelativeSizeHead(float2,float2,float2,float)}</summary> + public void ArrowRelativeSizeHead (float2 from, float2 to, float2 up, float headFraction, Color color) { + ArrowRelativeSizeHead(xy ? new float3(from, 0) : new float3(from.x, 0, from.y), xy ? new float3(to, 0) : new float3(to.x, 0, to.y), xy ? new float3(up, 0) : new float3(up.x, 0, up.y), headFraction, color); + } + + + + + /// <summary>\copydocref{ArrowheadArc(float3,float3,float,float)}</summary> + public void ArrowheadArc (float3 origin, float3 direction, float offset, float width, Color color) { + if (!math.any(direction)) return; + if (offset < 0) throw new System.ArgumentOutOfRangeException(nameof(offset)); + if (offset == 0) return; + draw.PushColor(color); + + var rot = Quaternion.LookRotation(direction, xy ? XY_UP : XZ_UP); + PushMatrix(Matrix4x4.TRS(origin, rot, Vector3.one)); + var a1 = math.PI * 0.5f - width * (0.5f * Mathf.Deg2Rad); + var a2 = math.PI * 0.5f + width * (0.5f * Mathf.Deg2Rad); + draw.CircleXZInternal(float3.zero, offset, a1, a2); + var p1 = new float3(math.cos(a1), 0, math.sin(a1)) * offset; + var p2 = new float3(math.cos(a2), 0, math.sin(a2)) * offset; + const float sqrt2 = 1.4142f; + var p3 = new float3(0, 0, sqrt2 * offset); + Line(p1, p3); + Line(p3, p2); + PopMatrix(); + draw.PopColor(); + } + /// <summary>\copydocref{ArrowheadArc(float3,float3,float,float)}</summary> + public void ArrowheadArc (float3 origin, float3 direction, float offset, Color color) { + ArrowheadArc(origin, direction, offset, 60, color); + } + /// <summary>\copydocref{ArrowheadArc(float2,float2,float,float)}</summary> + public void ArrowheadArc (float2 origin, float2 direction, float offset, float width, Color color) { + ArrowheadArc(xy ? new float3(origin, 0) : new float3(origin.x, 0, origin.y), xy ? new float3(direction, 0) : new float3(direction.x, 0, direction.y), offset, width, color); + } + /// <summary>\copydocref{ArrowheadArc(float2,float2,float,float)}</summary> + public void ArrowheadArc (float2 origin, float2 direction, float offset, Color color) { + ArrowheadArc(origin, direction, offset, 60, color); + } + + + /// <summary>\copydocref{WireRectangle(float3,quaternion,float2)}</summary> + public void WireRectangle (float3 center, quaternion rotation, float2 size, Color color) { + draw.WireRectangle(center, rotation, size, color); + } + /// <summary>\copydocref{WireRectangle(float2,quaternion,float2)}</summary> + public void WireRectangle (float2 center, quaternion rotation, float2 size, Color color) { + WireRectangle(xy ? new float3(center, 0) : new float3(center.x, 0, center.y), rotation, size, color); + } + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + /// <summary>\copydocref{Line(float3,float3)}</summary> + public void Line (float3 a, float3 b, Color color) { + draw.Line(a, b, color); + } + /// <summary>\copydocref{Circle(float2,float,float,float)}</summary> + /// <param name="color">Color of the object</param> + public void Circle (float2 center, float radius, float startAngle, float endAngle, Color color) { + Circle(xy ? new float3(center, 0) : new float3(center.x, 0, center.y), radius, startAngle, endAngle, color); + } + /// <summary>\copydocref{Circle(float2,float,float,float)}</summary> + /// <param name="color">Color of the object</param> + public void Circle (float2 center, float radius, Color color) { + Circle(center, radius, 0f, 2 * math.PI, color); + } + /// <summary>\copydocref{Circle(float3,float,float,float)}</summary> + /// <param name="color">Color of the object</param> + public void Circle (float3 center, float radius, float startAngle, float endAngle, Color color) { + draw.PushColor(color); + 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); + } + draw.PopColor(); + } + /// <summary>\copydocref{Circle(float3,float,float,float)}</summary> + /// <param name="color">Color of the object</param> + public void Circle (float3 center, float radius, Color color) { + Circle(center, radius, 0f, 2 * math.PI, color); + } + + + + + /// <summary>\copydocref{WirePill(float2,float2,float)}</summary> + /// <param name="color">Color of the object</param> + public void WirePill (float2 a, float2 b, float radius, Color color) { + WirePill(a, b - a, math.length(b - a), radius, color); + } + /// <summary>\copydocref{WirePill(float2,float2,float,float)}</summary> + /// <param name="color">Color of the object</param> + public void WirePill (float2 position, float2 direction, float length, float radius, Color color) { + draw.PushColor(color); + 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(); + } + draw.PopColor(); + } + /// <summary>\copydocref{Polyline(List<Vector2>,bool)}</summary> + [BurstDiscard] + public void Polyline (List<Vector2> points, bool cycle, Color color) { + draw.PushColor(color); + 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]); + draw.PopColor(); + } + /// <summary>\copydocref{Polyline(List<Vector2>,bool)}</summary> + [BurstDiscard] + public void Polyline (List<Vector2> points, Color color) { + Polyline(points, false, color); + } + /// <summary>\copydocref{Polyline(Vector2[],bool)}</summary> + [BurstDiscard] + public void Polyline (Vector2[] points, bool cycle, Color color) { + draw.PushColor(color); + 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]); + draw.PopColor(); + } + /// <summary>\copydocref{Polyline(Vector2[],bool)}</summary> + [BurstDiscard] + public void Polyline (Vector2[] points, Color color) { + Polyline(points, false, color); + } + /// <summary>\copydocref{Polyline(float2[],bool)}</summary> + [BurstDiscard] + public void Polyline (float2[] points, bool cycle, Color color) { + draw.PushColor(color); + 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]); + draw.PopColor(); + } + /// <summary>\copydocref{Polyline(float2[],bool)}</summary> + [BurstDiscard] + public void Polyline (float2[] points, Color color) { + Polyline(points, false, color); + } + /// <summary>\copydocref{Polyline(NativeArray<float2>,bool)}</summary> + public void Polyline (NativeArray<float2> points, bool cycle, Color color) { + draw.PushColor(color); + 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]); + draw.PopColor(); + } + /// <summary>\copydocref{Polyline(NativeArray<float2>,bool)}</summary> + public void Polyline (NativeArray<float2> points, Color color) { + Polyline(points, false, color); + } + /// <summary>\copydocref{Cross(float2,float)}</summary> + public void Cross (float2 position, float size, Color color) { + draw.PushColor(color); + 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)); + draw.PopColor(); + } + /// <summary>\copydocref{Cross(float2,float)}</summary> + public void Cross (float2 position, Color color) { + Cross(position, 1, color); + } + /// <summary>\copydocref{WireRectangle(float3,float2)}</summary> + public void WireRectangle (float3 center, float2 size, Color color) { + draw.WirePlane(center, xy ? XY_TO_XZ_ROTATION : XZ_TO_XZ_ROTATION, size, color); + } + /// <summary>\copydocref{WireRectangle(Rect)}</summary> + public void WireRectangle (Rect rect, Color color) { + draw.PushColor(color); + 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)); + draw.PopColor(); + } + + /// <summary>\copydocref{WireGrid(float2,int2,float2)}</summary> + /// <param name="color">Color of the object</param> + public void WireGrid (float2 center, int2 cells, float2 totalSize, Color color) { + 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, color); + } + /// <summary>\copydocref{WireGrid(float3,int2,float2)}</summary> + /// <param name="color">Color of the object</param> + public void WireGrid (float3 center, int2 cells, float2 totalSize, Color color) { + draw.WireGrid(center, xy ? XY_TO_XZ_ROTATION : XZ_TO_XZ_ROTATION, cells, totalSize, color); + } + } +} diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/CommandBuilder2DExtensions.cs.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/CommandBuilder2DExtensions.cs.meta new file mode 100644 index 0000000..21fadd9 --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/CommandBuilder2DExtensions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 90565319c93ed2f49b157dfd0532a74b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/CommandBuilderExtensions.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/CommandBuilderExtensions.cs new file mode 100644 index 0000000..af1583e --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/CommandBuilderExtensions.cs @@ -0,0 +1,490 @@ +// This file is automatically generated by a script based on the CommandBuilder API. +// This file adds additional overloads to the CommandBuilder API with convenience parameters like colors. +using Unity.Burst; +using UnityEngine; +using System.Collections.Generic; +using Unity.Collections; +using Unity.Mathematics; +using Unity.Collections.LowLevel.Unsafe; +using Unity.Jobs; + +namespace Pathfinding.Drawing { + public partial struct CommandBuilder { + /// <summary>\copydocref{Line(float3,float3)}</summary> + public void Line (float3 a, float3 b, Color color) { + Reserve<Color32, LineData>(); + Add(Command.Line | Command.PushColorInline); + Add(ConvertColor(color)); + Add(new LineData { a = a, b = b }); + } + /// <summary>\copydocref{Ray(float3,float3)}</summary> + public void Ray (float3 origin, float3 direction, Color color) { + Line(origin, origin + direction, color); + } + /// <summary>\copydocref{Ray(Ray,float)}</summary> + public void Ray (Ray ray, float length, Color color) { + Line(ray.origin, ray.origin + ray.direction * length, color); + } + /// <summary>\copydocref{Arc(float3,float3,float3)}</summary> + /// <param name="color">Color of the object</param> + public void Arc (float3 center, float3 start, float3 end, Color color) { + PushColor(color); + var d1 = start - center; + var d2 = end - center; + var normal = math.cross(d2, d1); + + if (math.any(normal != 0) && math.all(math.isfinite(normal))) { + var m = Matrix4x4.TRS(center, Quaternion.LookRotation(d1, normal), Vector3.one); + var angle = Vector3.SignedAngle(d1, d2, normal) * Mathf.Deg2Rad; + PushMatrix(m); + CircleXZInternal(float3.zero, math.length(d1), 90 * Mathf.Deg2Rad, 90 * Mathf.Deg2Rad - angle); + PopMatrix(); + } + PopColor(); + } + /// <summary>\copydocref{CircleXZ(float3,float,float,float)}</summary> + /// <param name="color">Color of the object</param> + [System.Obsolete("Use Draw.xz.Circle instead")] + public void CircleXZ (float3 center, float radius, float startAngle, float endAngle, Color color) { + CircleXZInternal(center, radius, startAngle, endAngle, color); + } + /// <summary>\copydocref{CircleXZ(float3,float,float,float)}</summary> + /// <param name="color">Color of the object</param> + [System.Obsolete("Use Draw.xz.Circle instead")] + public void CircleXZ (float3 center, float radius, Color color) { + CircleXZ(center, radius, 0f, 2 * Mathf.PI, color); + } + + + /// <summary>\copydocref{Circle(float3,float3,float)}</summary> + public void Circle (float3 center, float3 normal, float radius, Color color) { + Reserve<Color32, CircleData>(); + Add(Command.Circle | Command.PushColorInline); + Add(ConvertColor(color)); + Add(new CircleData { center = center, normal = normal, radius = radius }); + } + + + + + + + + /// <summary>\copydocref{WireCylinder(float3,float3,float)}</summary> + public void WireCylinder (float3 bottom, float3 top, float radius, Color color) { + WireCylinder(bottom, top - bottom, math.length(top - bottom), radius, color); + } + /// <summary>\copydocref{WireCylinder(float3,float3,float,float)}</summary> + /// <param name="color">Color of the object</param> + public void WireCylinder (float3 position, float3 up, float height, float radius, Color color) { + up = math.normalizesafe(up); + if (math.all(up == 0) || math.any(math.isnan(up)) || math.isnan(height) || math.isnan(radius)) return; + PushColor(color); + + OrthonormalBasis(up, out var basis1, out var basis2); + + PushMatrix(new float4x4( + new float4(basis1 * radius, 0), + new float4(up * height, 0), + new float4(basis2 * radius, 0), + new float4(position, 1) + )); + + CircleXZInternal(float3.zero, 1); + if (height > 0) { + CircleXZInternal(new float3(0, 1, 0), 1); + Line(new float3(1, 0, 0), new float3(1, 1, 0)); + Line(new float3(-1, 0, 0), new float3(-1, 1, 0)); + Line(new float3(0, 0, 1), new float3(0, 1, 1)); + Line(new float3(0, 0, -1), new float3(0, 1, -1)); + } + PopMatrix(); + PopColor(); + } + /// <summary>\copydocref{WireCapsule(float3,float3,float)}</summary> + /// <param name="color">Color of the object</param> + public void WireCapsule (float3 start, float3 end, float radius, Color color) { + PushColor(color); + var dir = end - start; + var length = math.length(dir); + + if (length < 0.0001) { + // The endpoints are the same, we can't draw a capsule from this because we don't know its orientation. + // Draw a sphere as a fallback + WireSphere(start, radius); + } else { + var normalized_dir = dir / length; + + WireCapsule(start - normalized_dir*radius, normalized_dir, length + 2*radius, radius); + } + PopColor(); + } + /// <summary>\copydocref{WireCapsule(float3,float3,float,float)}</summary> + /// <param name="color">Color of the object</param> + public void WireCapsule (float3 position, float3 direction, float length, float radius, Color color) { + direction = math.normalizesafe(direction); + if (math.all(direction == 0) || math.any(math.isnan(direction)) || math.isnan(length) || math.isnan(radius)) return; + PushColor(color); + + if (radius <= 0) { + Line(position, position + direction * length); + } else { + length = math.max(length, radius*2); + OrthonormalBasis(direction, out var basis1, out var basis2); + + PushMatrix(new float4x4( + new float4(basis1, 0), + new float4(direction, 0), + new float4(basis2, 0), + new float4(position, 1) + )); + CircleXZInternal(new float3(0, radius, 0), radius); + PushMatrix(XZtoXYPlaneMatrix); + CircleXZInternal(new float3(0, 0, radius), radius, Mathf.PI, 2 * Mathf.PI); + PopMatrix(); + PushMatrix(XZtoYZPlaneMatrix); + CircleXZInternal(new float3(radius, 0, 0), radius, Mathf.PI*0.5f, Mathf.PI*1.5f); + PopMatrix(); + if (length > 0) { + var upperY = length - radius; + var lowerY = radius; + CircleXZInternal(new float3(0, upperY, 0), radius); + PushMatrix(XZtoXYPlaneMatrix); + CircleXZInternal(new float3(0, 0, upperY), radius, 0, Mathf.PI); + PopMatrix(); + PushMatrix(XZtoYZPlaneMatrix); + CircleXZInternal(new float3(upperY, 0, 0), radius, -Mathf.PI*0.5f, Mathf.PI*0.5f); + PopMatrix(); + Line(new float3(radius, lowerY, 0), new float3(radius, upperY, 0)); + Line(new float3(-radius, lowerY, 0), new float3(-radius, upperY, 0)); + Line(new float3(0, lowerY, radius), new float3(0, upperY, radius)); + Line(new float3(0, lowerY, -radius), new float3(0, upperY, -radius)); + } + PopMatrix(); + } + PopColor(); + } + /// <summary>\copydocref{WireSphere(float3,float)}</summary> + public void WireSphere (float3 position, float radius, Color color) { + PushColor(color); + SphereOutline(position, radius); + Circle(position, new float3(1, 0, 0), radius); + Circle(position, new float3(0, 1, 0), radius); + Circle(position, new float3(0, 0, 1), radius); + PopColor(); + } + /// <summary>\copydocref{Polyline(List<Vector3>,bool)}</summary> + /// <param name="color">Color of the object</param> + [BurstDiscard] + public void Polyline (List<Vector3> points, bool cycle, Color color) { + PushColor(color); + 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]); + PopColor(); + } + /// <summary>\copydocref{Polyline(List<Vector3>,bool)}</summary> + /// <param name="color">Color of the object</param> + [BurstDiscard] + public void Polyline (List<Vector3> points, Color color) { + Polyline(points, false, color); + } + /// <summary>\copydocref{Polyline(Vector3[],bool)}</summary> + /// <param name="color">Color of the object</param> + [BurstDiscard] + public void Polyline (Vector3[] points, bool cycle, Color color) { + PushColor(color); + 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]); + PopColor(); + } + /// <summary>\copydocref{Polyline(Vector3[],bool)}</summary> + /// <param name="color">Color of the object</param> + [BurstDiscard] + public void Polyline (Vector3[] points, Color color) { + Polyline(points, false, color); + } + /// <summary>\copydocref{Polyline(float3[],bool)}</summary> + /// <param name="color">Color of the object</param> + [BurstDiscard] + public void Polyline (float3[] points, bool cycle, Color color) { + PushColor(color); + 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]); + PopColor(); + } + /// <summary>\copydocref{Polyline(float3[],bool)}</summary> + /// <param name="color">Color of the object</param> + [BurstDiscard] + public void Polyline (float3[] points, Color color) { + Polyline(points, false, color); + } + /// <summary>\copydocref{Polyline(NativeArray<float3>,bool)}</summary> + /// <param name="color">Color of the object</param> + public void Polyline (NativeArray<float3> points, bool cycle, Color color) { + PushColor(color); + 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]); + PopColor(); + } + /// <summary>\copydocref{Polyline(NativeArray<float3>,bool)}</summary> + /// <param name="color">Color of the object</param> + public void Polyline (NativeArray<float3> points, Color color) { + Polyline(points, false, color); + } + + + /// <summary>\copydocref{WireBox(float3,float3)}</summary> + /// <param name="color">Color of the object</param> + public void WireBox (float3 center, float3 size, Color color) { + Reserve<Color32, BoxData>(); + Add(Command.WireBox | Command.PushColorInline); + Add(ConvertColor(color)); + Add(new BoxData { center = center, size = size }); + } + /// <summary>\copydocref{WireBox(float3,quaternion,float3)}</summary> + /// <param name="color">Color of the object</param> + public void WireBox (float3 center, quaternion rotation, float3 size, Color color) { + PushColor(color); + PushMatrix(float4x4.TRS(center, rotation, size)); + WireBox(float3.zero, new float3(1, 1, 1)); + PopMatrix(); + PopColor(); + } + /// <summary>\copydocref{WireBox(Bounds)}</summary> + public void WireBox (Bounds bounds, Color color) { + WireBox(bounds.center, bounds.size, color); + } + /// <summary>\copydocref{WireMesh(Mesh)}</summary> + public void WireMesh (Mesh mesh, Color color) { +#if UNITY_2020_1_OR_NEWER + if (mesh == null) throw new System.ArgumentNullException(); + PushColor(color); + + // Use a burst compiled function to draw the lines + // This is significantly faster than pure C# (about 5x). + var meshDataArray = Mesh.AcquireReadOnlyMeshData(mesh); + var meshData = meshDataArray[0]; + + JobWireMesh.JobWireMeshFunctionPointer(ref meshData, ref this); + meshDataArray.Dispose(); +#else + Debug.LogError("The WireMesh method is only suppored in Unity 2020.1 or later"); +#endif + PopColor(); + } + /// <summary>\copydocref{WireMesh(NativeArray<float3>,NativeArray<int>)}</summary> + public void WireMesh (NativeArray<float3> vertices, NativeArray<int> triangles, Color color) { + PushColor(color); +#if UNITY_2020_1_OR_NEWER + unsafe { + JobWireMesh.WireMesh((float3*)vertices.GetUnsafeReadOnlyPtr(), (int*)triangles.GetUnsafeReadOnlyPtr(), vertices.Length, triangles.Length, ref this); + } +#else + Debug.LogError("The WireMesh method is only suppored in Unity 2020.1 or later"); +#endif + PopColor(); + } + + /// <summary>\copydocref{Cross(float3,float)}</summary> + public void Cross (float3 position, float size, Color color) { + PushColor(color); + size *= 0.5f; + Line(position - new float3(size, 0, 0), position + new float3(size, 0, 0)); + Line(position - new float3(0, size, 0), position + new float3(0, size, 0)); + Line(position - new float3(0, 0, size), position + new float3(0, 0, size)); + PopColor(); + } + /// <summary>\copydocref{Cross(float3,float)}</summary> + public void Cross (float3 position, Color color) { + Cross(position, 1, color); + } + /// <summary>\copydocref{CrossXZ(float3,float)}</summary> + [System.Obsolete("Use Draw.xz.Cross instead")] + public void CrossXZ (float3 position, float size, Color color) { + PushColor(color); + size *= 0.5f; + Line(position - new float3(size, 0, 0), position + new float3(size, 0, 0)); + Line(position - new float3(0, 0, size), position + new float3(0, 0, size)); + PopColor(); + } + /// <summary>\copydocref{CrossXZ(float3,float)}</summary> + [System.Obsolete("Use Draw.xz.Cross instead")] + public void CrossXZ (float3 position, Color color) { + CrossXZ(position, 1, color); + } + /// <summary>\copydocref{CrossXY(float3,float)}</summary> + [System.Obsolete("Use Draw.xy.Cross instead")] + public void CrossXY (float3 position, float size, Color color) { + PushColor(color); + size *= 0.5f; + Line(position - new float3(size, 0, 0), position + new float3(size, 0, 0)); + Line(position - new float3(0, size, 0), position + new float3(0, size, 0)); + PopColor(); + } + /// <summary>\copydocref{CrossXY(float3,float)}</summary> + [System.Obsolete("Use Draw.xy.Cross instead")] + public void CrossXY (float3 position, Color color) { + CrossXY(position, 1, color); + } + /// <summary>\copydocref{Bezier(float3,float3,float3,float3)}</summary> + /// <param name="color">Color of the object</param> + public void Bezier (float3 p0, float3 p1, float3 p2, float3 p3, Color color) { + PushColor(color); + float3 prev = p0; + + for (int i = 1; i <= 20; i++) { + float t = i/20.0f; + float3 p = EvaluateCubicBezier(p0, p1, p2, p3, t); + Line(prev, p); + prev = p; + } + PopColor(); + } + + + /// <summary>\copydocref{Arrow(float3,float3)}</summary> + /// <param name="color">Color of the object</param> + public void Arrow (float3 from, float3 to, Color color) { + ArrowRelativeSizeHead(from, to, DEFAULT_UP, 0.2f, color); + } + /// <summary>\copydocref{Arrow(float3,float3,float3,float)}</summary> + /// <param name="color">Color of the object</param> + public void Arrow (float3 from, float3 to, float3 up, float headSize, Color color) { + PushColor(color); + var length_sq = math.lengthsq(to - from); + + if (length_sq > 0.000001f) { + ArrowRelativeSizeHead(from, to, up, headSize * math.rsqrt(length_sq)); + } + PopColor(); + } + /// <summary>\copydocref{ArrowRelativeSizeHead(float3,float3,float3,float)}</summary> + /// <param name="color">Color of the object</param> + public void ArrowRelativeSizeHead (float3 from, float3 to, float3 up, float headFraction, Color color) { + PushColor(color); + Line(from, to); + var dir = to - from; + + var normal = math.cross(dir, up); + // Pick a different up direction if the direction happened to be colinear with that one. + if (math.all(normal == 0)) normal = math.cross(new float3(1, 0, 0), dir); + // Pick a different up direction if up=(1,0,0) and thus the above check would have generated a zero vector again + if (math.all(normal == 0)) normal = math.cross(new float3(0, 1, 0), dir); + normal = math.normalizesafe(normal) * math.length(dir); + + Line(to, to - (dir + normal) * headFraction); + Line(to, to - (dir - normal) * headFraction); + PopColor(); + } + + + /// <summary>\copydocref{ArrowheadArc(float3,float3,float,float)}</summary> + /// <param name="color">Color of the object</param> + public void ArrowheadArc (float3 origin, float3 direction, float offset, float width, Color color) { + if (!math.any(direction)) return; + if (offset < 0) throw new System.ArgumentOutOfRangeException(nameof(offset)); + if (offset == 0) return; + PushColor(color); + + var rot = Quaternion.LookRotation(direction, DEFAULT_UP); + PushMatrix(Matrix4x4.TRS(origin, rot, Vector3.one)); + var a1 = math.PI * 0.5f - width * (0.5f * Mathf.Deg2Rad); + var a2 = math.PI * 0.5f + width * (0.5f * Mathf.Deg2Rad); + CircleXZInternal(float3.zero, offset, a1, a2); + var p1 = new float3(math.cos(a1), 0, math.sin(a1)) * offset; + var p2 = new float3(math.cos(a2), 0, math.sin(a2)) * offset; + const float sqrt2 = 1.4142f; + var p3 = new float3(0, 0, sqrt2 * offset); + Line(p1, p3); + Line(p3, p2); + PopMatrix(); + PopColor(); + } + /// <summary>\copydocref{ArrowheadArc(float3,float3,float,float)}</summary> + /// <param name="color">Color of the object</param> + public void ArrowheadArc (float3 origin, float3 direction, float offset, Color color) { + ArrowheadArc(origin, direction, offset, 60, color); + } + /// <summary>\copydocref{WireGrid(float3,quaternion,int2,float2)}</summary> + /// <param name="color">Color of the object</param> + public void WireGrid (float3 center, quaternion rotation, int2 cells, float2 totalSize, Color color) { + PushColor(color); + cells = math.max(cells, new int2(1, 1)); + PushMatrix(float4x4.TRS(center, rotation, new Vector3(totalSize.x, 0, totalSize.y))); + int w = cells.x; + int h = cells.y; + for (int i = 0; i <= w; i++) Line(new float3(i/(float)w - 0.5f, 0, -0.5f), new float3(i/(float)w - 0.5f, 0, 0.5f)); + for (int i = 0; i <= h; i++) Line(new float3(-0.5f, 0, i/(float)h - 0.5f), new float3(0.5f, 0, i/(float)h - 0.5f)); + PopMatrix(); + PopColor(); + } + + + /// <summary>\copydocref{WireRectangle(float3,quaternion,float2)}</summary> + public void WireRectangle (float3 center, quaternion rotation, float2 size, Color color) { + WirePlane(center, rotation, size, color); + } + /// <summary>\copydocref{WireRectangle(Rect)}</summary> + [System.Obsolete("Use Draw.xy.WireRectangle instead")] + public void WireRectangle (Rect rect, Color color) { + xy.WireRectangle(rect, color); + } + + + + + + + + /// <summary>\copydocref{WirePlane(float3,float3,float2)}</summary> + /// <param name="color">Color of the object</param> + public void WirePlane (float3 center, float3 normal, float2 size, Color color) { + PushColor(color); + if (math.any(normal)) { + WirePlane(center, Quaternion.LookRotation(calculateTangent(normal), normal), size); + } + PopColor(); + } + /// <summary>\copydocref{WirePlane(float3,quaternion,float2)}</summary> + /// <param name="color">Color of the object</param> + public void WirePlane (float3 center, quaternion rotation, float2 size, Color color) { + Reserve<Color32, PlaneData>(); + Add(Command.WirePlane | Command.PushColorInline); + Add(ConvertColor(color)); + Add(new PlaneData { center = center, rotation = rotation, size = size }); + } + + + + /// <summary>\copydocref{SolidBox(float3,float3)}</summary> + /// <param name="color">Color of the object</param> + public void SolidBox (float3 center, float3 size, Color color) { + Reserve<Color32, BoxData>(); + Add(Command.Box | Command.PushColorInline); + Add(ConvertColor(color)); + Add(new BoxData { center = center, size = size }); + } + /// <summary>\copydocref{SolidBox(Bounds)}</summary> + /// <param name="color">Color of the object</param> + public void SolidBox (Bounds bounds, Color color) { + SolidBox(bounds.center, bounds.size, color); + } + /// <summary>\copydocref{SolidBox(float3,quaternion,float3)}</summary> + /// <param name="color">Color of the object</param> + public void SolidBox (float3 center, quaternion rotation, float3 size, Color color) { + PushColor(color); + PushMatrix(float4x4.TRS(center, rotation, size)); + SolidBox(float3.zero, Vector3.one); + PopMatrix(); + PopColor(); + } + } +} diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/CommandBuilderExtensions.cs.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/CommandBuilderExtensions.cs.meta new file mode 100644 index 0000000..5cd5708 --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/CommandBuilderExtensions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 380787cc3a51649209786ec216737923 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Compatibility.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Compatibility.cs new file mode 100644 index 0000000..3b6ed49 --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Compatibility.cs @@ -0,0 +1,4 @@ + +// This file has been removed from the project. Since UnityPackages cannot +// delete files, only replace them, this message is left here to prevent old +// files from causing compiler errors diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Compatibility.cs.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Compatibility.cs.meta new file mode 100644 index 0000000..4997a27 --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Compatibility.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d53eb32d15c4e58a68061d3125d1b225 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Documentation.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Documentation.meta new file mode 100644 index 0000000..0210f04 --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Documentation.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: d5c481b98877873438239fbbf48a07ff +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Documentation/index.md b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Documentation/index.md new file mode 100644 index 0000000..03f6855 --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Documentation/index.md @@ -0,0 +1,7 @@ +ALINE +===== + +- Documentation: https://arongranberg.com/aline/docs +- Get started tutorial: https://arongranberg.com/aline/docs/getstarted.html +- Package website: https://arongranberg.com/aline +- Support forum: https://forum.arongranberg.com/ diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Documentation/index.md.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Documentation/index.md.meta new file mode 100644 index 0000000..d49ed2f --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Documentation/index.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 8596356ad2e8716268fca045817224e2 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Draw.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Draw.cs new file mode 100644 index 0000000..c427923 --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Draw.cs @@ -0,0 +1,1355 @@ +// This file is automatically generated by a script based on the CommandBuilder API +using Unity.Burst; +using UnityEngine; +using System.Collections.Generic; +using Unity.Collections; +using Unity.Mathematics; +using Unity.Collections.LowLevel.Unsafe; +using Unity.Jobs; + +namespace Pathfinding.Drawing { + /// <summary> + /// Methods for easily drawing things in the editor and in standalone games. + /// + /// See: getstarted (view in online documentation for working links) + /// </summary> + public static class Draw { + internal static CommandBuilder builder; + internal static CommandBuilder ingame_builder; + + + /// <summary> + /// Draws items in the editor if gizmos are enabled. + /// All drawing methods on the static Draw class are forwarded to this command builder. + /// + /// See: ingame (view in online documentation for working links) + /// </summary> + public static ref CommandBuilder editor { + get { + DrawingManager.Init(); + return ref builder; + } + } + + /// <summary>\copydocref{CommandBuilder.xy}</summary> + public static CommandBuilder2D xy { + get { + DrawingManager.Init(); + return new CommandBuilder2D(builder, true); + } + } + + /// <summary>\copydocref{CommandBuilder.xz}</summary> + public static CommandBuilder2D xz { + get { + DrawingManager.Init(); + return new CommandBuilder2D(builder, false); + } + } + + +#if UNITY_EDITOR + /// <summary> + /// \copydocref{Drawing::CommandBuilder::WithMatrix(Matrix4x4)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static CommandBuilder.ScopeMatrix WithMatrix (Matrix4x4 matrix) { + DrawingManager.Init(); + return builder.WithMatrix(matrix); + } +#else + [BurstDiscard] + public static CommandBuilder.ScopeEmpty WithMatrix (Matrix4x4 matrix) { + // Do nothing in standlone builds + return new CommandBuilder.ScopeEmpty(); + } +#endif + + +#if UNITY_EDITOR + /// <summary> + /// \copydocref{Drawing::CommandBuilder::WithMatrix(float3x3)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static CommandBuilder.ScopeMatrix WithMatrix (float3x3 matrix) { + DrawingManager.Init(); + return builder.WithMatrix(matrix); + } +#else + [BurstDiscard] + public static CommandBuilder.ScopeEmpty WithMatrix (float3x3 matrix) { + // Do nothing in standlone builds + return new CommandBuilder.ScopeEmpty(); + } +#endif + + +#if UNITY_EDITOR + /// <summary> + /// \copydocref{Drawing::CommandBuilder::WithColor(Color)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static CommandBuilder.ScopeColor WithColor (Color color) { + DrawingManager.Init(); + return builder.WithColor(color); + } +#else + [BurstDiscard] + public static CommandBuilder.ScopeEmpty WithColor (Color color) { + // Do nothing in standlone builds + return new CommandBuilder.ScopeEmpty(); + } +#endif + + +#if UNITY_EDITOR +#else +#endif + + +#if UNITY_EDITOR + /// <summary> + /// \copydocref{Drawing::CommandBuilder::WithLineWidth(float,bool)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static CommandBuilder.ScopeLineWidth WithLineWidth (float pixels, bool automaticJoins = true) { + DrawingManager.Init(); + return builder.WithLineWidth(pixels, automaticJoins); + } +#else + [BurstDiscard] + public static CommandBuilder.ScopeEmpty WithLineWidth (float pixels, bool automaticJoins = true) { + // Do nothing in standlone builds + return new CommandBuilder.ScopeEmpty(); + } +#endif + + +#if UNITY_EDITOR +#else +#endif + + +#if UNITY_EDITOR +#else +#endif + + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::PushMatrix(Matrix4x4)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void PushMatrix (Matrix4x4 matrix) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.PushMatrix(matrix); +#endif + } + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::PushMatrix(float4x4)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void PushMatrix (float4x4 matrix) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.PushMatrix(matrix); +#endif + } + + + + + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::PopMatrix()} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void PopMatrix () { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.PopMatrix(); +#endif + } + + + + + + + + + + + + + + + + + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::Line(float3,float3)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void Line (float3 a, float3 b) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.Line(a, b); +#endif + } + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::Line(Vector3,Vector3)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void Line (Vector3 a, Vector3 b) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.Line(a, b); +#endif + } + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::Line(Vector3,Vector3,Color)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void Line (Vector3 a, Vector3 b, Color color) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.Line(a, b, color); +#endif + } + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::Ray(float3,float3)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void Ray (float3 origin, float3 direction) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.Ray(origin, direction); +#endif + } + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::Ray(Ray,float)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void Ray (Ray ray, float length) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.Ray(ray, length); +#endif + } + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::Arc(float3,float3,float3)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void Arc (float3 center, float3 start, float3 end) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.Arc(center, start, end); +#endif + } + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::CircleXZ(float3,float,float,float)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + [System.Obsolete("Use Draw.xz.Circle instead")] + public static void CircleXZ (float3 center, float radius, float startAngle = 0f, float endAngle = 2 * Mathf.PI) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.CircleXZ(center, radius, startAngle, endAngle); +#endif + } + + + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::Circle(float3,float3,float)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void Circle (float3 center, float3 normal, float radius) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.Circle(center, normal, radius); +#endif + } + + + + + + + + + + + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::WireCylinder(float3,float3,float)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void WireCylinder (float3 bottom, float3 top, float radius) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.WireCylinder(bottom, top, radius); +#endif + } + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::WireCylinder(float3,float3,float,float)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void WireCylinder (float3 position, float3 up, float height, float radius) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.WireCylinder(position, up, height, radius); +#endif + } + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::WireCapsule(float3,float3,float)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void WireCapsule (float3 start, float3 end, float radius) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.WireCapsule(start, end, radius); +#endif + } + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::WireCapsule(float3,float3,float,float)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void WireCapsule (float3 position, float3 direction, float length, float radius) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.WireCapsule(position, direction, length, radius); +#endif + } + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::WireSphere(float3,float)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void WireSphere (float3 position, float radius) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.WireSphere(position, radius); +#endif + } + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::Polyline(List<Vector3>,bool)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void Polyline (List<Vector3> points, bool cycle = false) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.Polyline(points, cycle); +#endif + } + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::Polyline(Vector3[],bool)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void Polyline (Vector3[] points, bool cycle = false) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.Polyline(points, cycle); +#endif + } + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::Polyline(float3[],bool)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void Polyline (float3[] points, bool cycle = false) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.Polyline(points, cycle); +#endif + } + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::Polyline(NativeArray<float3>,bool)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void Polyline (NativeArray<float3> points, bool cycle = false) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.Polyline(points, cycle); +#endif + } + + + + + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::WireBox(float3,float3)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void WireBox (float3 center, float3 size) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.WireBox(center, size); +#endif + } + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::WireBox(float3,quaternion,float3)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void WireBox (float3 center, quaternion rotation, float3 size) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.WireBox(center, rotation, size); +#endif + } + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::WireBox(Bounds)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void WireBox (Bounds bounds) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.WireBox(bounds); +#endif + } + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::WireMesh(Mesh)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void WireMesh (Mesh mesh) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.WireMesh(mesh); +#endif + } + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::WireMesh(NativeArray<float3>,NativeArray<int>)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void WireMesh (NativeArray<float3> vertices, NativeArray<int> triangles) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.WireMesh(vertices, triangles); +#endif + } + + + + + + + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::Cross(float3,float)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void Cross (float3 position, float size = 1) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.Cross(position, size); +#endif + } + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::CrossXZ(float3,float)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + [System.Obsolete("Use Draw.xz.Cross instead")] + public static void CrossXZ (float3 position, float size = 1) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.CrossXZ(position, size); +#endif + } + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::CrossXY(float3,float)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + [System.Obsolete("Use Draw.xy.Cross instead")] + public static void CrossXY (float3 position, float size = 1) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.CrossXY(position, size); +#endif + } + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::Bezier(float3,float3,float3,float3)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void Bezier (float3 p0, float3 p1, float3 p2, float3 p3) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.Bezier(p0, p1, p2, p3); +#endif + } + + + + + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::Arrow(float3,float3)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void Arrow (float3 from, float3 to) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.Arrow(from, to); +#endif + } + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::Arrow(float3,float3,float3,float)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void Arrow (float3 from, float3 to, float3 up, float headSize) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.Arrow(from, to, up, headSize); +#endif + } + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::ArrowRelativeSizeHead(float3,float3,float3,float)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void ArrowRelativeSizeHead (float3 from, float3 to, float3 up, float headFraction) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.ArrowRelativeSizeHead(from, to, up, headFraction); +#endif + } + + + + + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::ArrowheadArc(float3,float3,float,float)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void ArrowheadArc (float3 origin, float3 direction, float offset, float width = 60) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.ArrowheadArc(origin, direction, offset, width); +#endif + } + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::WireGrid(float3,quaternion,int2,float2)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void WireGrid (float3 center, quaternion rotation, int2 cells, float2 totalSize) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.WireGrid(center, rotation, cells, totalSize); +#endif + } + + + + + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::WireRectangle(float3,quaternion,float2)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void WireRectangle (float3 center, quaternion rotation, float2 size) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.WireRectangle(center, rotation, size); +#endif + } + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::WireRectangle(Rect)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + [System.Obsolete("Use Draw.xy.WireRectangle instead")] + public static void WireRectangle (Rect rect) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.WireRectangle(rect); +#endif + } + + + + + + + + + + + + + + + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::WirePlane(float3,float3,float2)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void WirePlane (float3 center, float3 normal, float2 size) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.WirePlane(center, normal, size); +#endif + } + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::WirePlane(float3,quaternion,float2)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void WirePlane (float3 center, quaternion rotation, float2 size) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.WirePlane(center, rotation, size); +#endif + } + + + + + + + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::SolidBox(float3,float3)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void SolidBox (float3 center, float3 size) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.SolidBox(center, size); +#endif + } + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::SolidBox(Bounds)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void SolidBox (Bounds bounds) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.SolidBox(bounds); +#endif + } + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::SolidBox(float3,quaternion,float3)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void SolidBox (float3 center, quaternion rotation, float3 size) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.SolidBox(center, rotation, size); +#endif + } + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::Line(float3,float3,Color)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void Line (float3 a, float3 b, Color color) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.Line(a, b, color); +#endif + } + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::Ray(float3,float3,Color)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void Ray (float3 origin, float3 direction, Color color) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.Ray(origin, direction, color); +#endif + } + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::Ray(Ray,float,Color)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void Ray (Ray ray, float length, Color color) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.Ray(ray, length, color); +#endif + } + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::Arc(float3,float3,float3,Color)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void Arc (float3 center, float3 start, float3 end, Color color) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.Arc(center, start, end, color); +#endif + } + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::CircleXZ(float3,float,float,float,Color)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + [System.Obsolete("Use Draw.xz.Circle instead")] + public static void CircleXZ (float3 center, float radius, float startAngle, float endAngle, Color color) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.CircleXZ(center, radius, startAngle, endAngle, color); +#endif + } + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::CircleXZ(float3,float,Color)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + [System.Obsolete("Use Draw.xz.Circle instead")] + public static void CircleXZ (float3 center, float radius, Color color) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.CircleXZ(center, radius, color); +#endif + } + + + + + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::Circle(float3,float3,float,Color)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void Circle (float3 center, float3 normal, float radius, Color color) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.Circle(center, normal, radius, color); +#endif + } + + + + + + + + + + + + + + + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::WireCylinder(float3,float3,float,Color)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void WireCylinder (float3 bottom, float3 top, float radius, Color color) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.WireCylinder(bottom, top, radius, color); +#endif + } + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::WireCylinder(float3,float3,float,float,Color)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void WireCylinder (float3 position, float3 up, float height, float radius, Color color) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.WireCylinder(position, up, height, radius, color); +#endif + } + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::WireCapsule(float3,float3,float,Color)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void WireCapsule (float3 start, float3 end, float radius, Color color) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.WireCapsule(start, end, radius, color); +#endif + } + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::WireCapsule(float3,float3,float,float,Color)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void WireCapsule (float3 position, float3 direction, float length, float radius, Color color) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.WireCapsule(position, direction, length, radius, color); +#endif + } + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::WireSphere(float3,float,Color)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void WireSphere (float3 position, float radius, Color color) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.WireSphere(position, radius, color); +#endif + } + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::Polyline(List<Vector3>,bool,Color)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void Polyline (List<Vector3> points, bool cycle, Color color) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.Polyline(points, cycle, color); +#endif + } + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::Polyline(List<Vector3>,Color)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void Polyline (List<Vector3> points, Color color) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.Polyline(points, color); +#endif + } + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::Polyline(Vector3[],bool,Color)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void Polyline (Vector3[] points, bool cycle, Color color) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.Polyline(points, cycle, color); +#endif + } + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::Polyline(Vector3[],Color)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void Polyline (Vector3[] points, Color color) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.Polyline(points, color); +#endif + } + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::Polyline(float3[],bool,Color)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void Polyline (float3[] points, bool cycle, Color color) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.Polyline(points, cycle, color); +#endif + } + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::Polyline(float3[],Color)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void Polyline (float3[] points, Color color) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.Polyline(points, color); +#endif + } + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::Polyline(NativeArray<float3>,bool,Color)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void Polyline (NativeArray<float3> points, bool cycle, Color color) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.Polyline(points, cycle, color); +#endif + } + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::Polyline(NativeArray<float3>,Color)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void Polyline (NativeArray<float3> points, Color color) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.Polyline(points, color); +#endif + } + + + + + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::WireBox(float3,float3,Color)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void WireBox (float3 center, float3 size, Color color) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.WireBox(center, size, color); +#endif + } + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::WireBox(float3,quaternion,float3,Color)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void WireBox (float3 center, quaternion rotation, float3 size, Color color) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.WireBox(center, rotation, size, color); +#endif + } + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::WireBox(Bounds,Color)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void WireBox (Bounds bounds, Color color) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.WireBox(bounds, color); +#endif + } + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::WireMesh(Mesh,Color)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void WireMesh (Mesh mesh, Color color) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.WireMesh(mesh, color); +#endif + } + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::WireMesh(NativeArray<float3>,NativeArray<int>,Color)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void WireMesh (NativeArray<float3> vertices, NativeArray<int> triangles, Color color) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.WireMesh(vertices, triangles, color); +#endif + } + + + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::Cross(float3,float,Color)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void Cross (float3 position, float size, Color color) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.Cross(position, size, color); +#endif + } + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::Cross(float3,Color)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void Cross (float3 position, Color color) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.Cross(position, color); +#endif + } + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::CrossXZ(float3,float,Color)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + [System.Obsolete("Use Draw.xz.Cross instead")] + public static void CrossXZ (float3 position, float size, Color color) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.CrossXZ(position, size, color); +#endif + } + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::CrossXZ(float3,Color)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + [System.Obsolete("Use Draw.xz.Cross instead")] + public static void CrossXZ (float3 position, Color color) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.CrossXZ(position, color); +#endif + } + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::CrossXY(float3,float,Color)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + [System.Obsolete("Use Draw.xy.Cross instead")] + public static void CrossXY (float3 position, float size, Color color) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.CrossXY(position, size, color); +#endif + } + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::CrossXY(float3,Color)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + [System.Obsolete("Use Draw.xy.Cross instead")] + public static void CrossXY (float3 position, Color color) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.CrossXY(position, color); +#endif + } + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::Bezier(float3,float3,float3,float3,Color)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void Bezier (float3 p0, float3 p1, float3 p2, float3 p3, Color color) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.Bezier(p0, p1, p2, p3, color); +#endif + } + + + + + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::Arrow(float3,float3,Color)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void Arrow (float3 from, float3 to, Color color) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.Arrow(from, to, color); +#endif + } + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::Arrow(float3,float3,float3,float,Color)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void Arrow (float3 from, float3 to, float3 up, float headSize, Color color) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.Arrow(from, to, up, headSize, color); +#endif + } + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::ArrowRelativeSizeHead(float3,float3,float3,float,Color)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void ArrowRelativeSizeHead (float3 from, float3 to, float3 up, float headFraction, Color color) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.ArrowRelativeSizeHead(from, to, up, headFraction, color); +#endif + } + + + + + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::ArrowheadArc(float3,float3,float,float,Color)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void ArrowheadArc (float3 origin, float3 direction, float offset, float width, Color color) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.ArrowheadArc(origin, direction, offset, width, color); +#endif + } + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::ArrowheadArc(float3,float3,float,Color)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void ArrowheadArc (float3 origin, float3 direction, float offset, Color color) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.ArrowheadArc(origin, direction, offset, color); +#endif + } + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::WireGrid(float3,quaternion,int2,float2,Color)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void WireGrid (float3 center, quaternion rotation, int2 cells, float2 totalSize, Color color) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.WireGrid(center, rotation, cells, totalSize, color); +#endif + } + + + + + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::WireRectangle(float3,quaternion,float2,Color)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void WireRectangle (float3 center, quaternion rotation, float2 size, Color color) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.WireRectangle(center, rotation, size, color); +#endif + } + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::WireRectangle(Rect,Color)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + [System.Obsolete("Use Draw.xy.WireRectangle instead")] + public static void WireRectangle (Rect rect, Color color) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.WireRectangle(rect, color); +#endif + } + + + + + + + + + + + + + + + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::WirePlane(float3,float3,float2,Color)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void WirePlane (float3 center, float3 normal, float2 size, Color color) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.WirePlane(center, normal, size, color); +#endif + } + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::WirePlane(float3,quaternion,float2,Color)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void WirePlane (float3 center, quaternion rotation, float2 size, Color color) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.WirePlane(center, rotation, size, color); +#endif + } + + + + + + + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::SolidBox(float3,float3,Color)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void SolidBox (float3 center, float3 size, Color color) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.SolidBox(center, size, color); +#endif + } + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::SolidBox(Bounds,Color)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void SolidBox (Bounds bounds, Color color) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.SolidBox(bounds, color); +#endif + } + + /// <summary> + /// \copydocref{Drawing::CommandBuilder::SolidBox(float3,quaternion,float3,Color)} + /// Warning: This method cannot be used inside of Burst jobs. See job-system (view in online documentation for working links) instead. + /// </summary> + [BurstDiscard] + public static void SolidBox (float3 center, quaternion rotation, float3 size, Color color) { +#if UNITY_EDITOR + DrawingManager.Init(); + builder.SolidBox(center, rotation, size, color); +#endif + } + } +} diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Draw.cs.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Draw.cs.meta new file mode 100644 index 0000000..9a92f58 --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Draw.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 506739df886ce4ebb9b14b16d86b5e13 +timeCreated: 1492346087 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/DrawingData.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/DrawingData.cs new file mode 100644 index 0000000..646edec --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/DrawingData.cs @@ -0,0 +1,1712 @@ +using UnityEngine; +using System.Collections.Generic; +using Unity.Collections.LowLevel.Unsafe; +using Unity.Mathematics; +using System; +using Unity.Jobs; +using Unity.Collections; +using Unity.Burst; +using UnityEngine.Rendering; +using System.Diagnostics; +using Unity.Jobs.LowLevel.Unsafe; +using UnityEngine.Profiling; +using System.Linq; + +namespace Pathfinding.Drawing { + using Pathfinding.Drawing.Text; + using Unity.Profiling; + + public static class SharedDrawingData { + /// <summary> + /// Same as Time.time, but not updated as frequently. + /// Used since burst jobs cannot access Time.time. + /// </summary> + public static readonly Unity.Burst.SharedStatic<float> BurstTime = Unity.Burst.SharedStatic<float>.GetOrCreate<DrawingManager, BurstTimeKey>(4); + + private class BurstTimeKey {} + } + + /// <summary> + /// Used to cache drawing data over multiple frames. + /// This is useful as a performance optimization when you are drawing the same thing over multiple consecutive frames. + /// + /// <code> + /// private RedrawScope redrawScope; + /// + /// void Start () { + /// redrawScope = DrawingManager.GetRedrawScope(); + /// using (var builder = DrawingManager.GetBuilder(redrawScope)) { + /// builder.WireSphere(Vector3.zero, 1.0f, Color.red); + /// } + /// } + /// + /// void OnDestroy () { + /// redrawScope.Dispose(); + /// } + /// </code> + /// + /// See: <see cref="DrawingManager.GetRedrawScope"/> + /// </summary> + public struct RedrawScope : System.IDisposable { + // Stored as a GCHandle to allow storing this struct in an unmanaged ECS component or system + internal System.Runtime.InteropServices.GCHandle gizmos; + /// <summary> + /// ID of the scope. + /// Zero means no or invalid scope. + /// </summary> + internal int id; + + static int idCounter = 1; + + /// <summary>True if the scope has been created</summary> + public bool isValid => id != 0; + + internal RedrawScope (DrawingData gizmos, int id) { + this.gizmos = gizmos.gizmosHandle; + this.id = id; + } + + internal RedrawScope (DrawingData gizmos) { + this.gizmos = gizmos.gizmosHandle; + // Should be enough with 4 billion ids before they wrap around. + id = idCounter++; + } + + /// <summary> + /// Everything rendered with this scope and which is not older than one frame is drawn again. + /// This is useful if you for some reason cannot draw some items during a frame (e.g. some asynchronous process is modifying the contents) + /// but you still want to draw the same thing as the last frame to at least draw *something*. + /// + /// Note: The items age will be reset. So the next frame you can call + /// this method again to draw the items yet again. + /// </summary> + internal void Draw () { + if (gizmos.IsAllocated) { + if (gizmos.Target is DrawingData gizmosTarget) gizmosTarget.Draw(this); + } + } + + /// <summary> + /// Stops keeping all previously rendered items alive, and starts a new scope. + /// Equivalent to first calling Dispose on the old scope and then creating a new one. + /// </summary> + public void Rewind () { + GameObject associatedGameObject = null; + if (gizmos.IsAllocated) { + if (gizmos.Target is DrawingData gizmosTarget) associatedGameObject = gizmosTarget.GetAssociatedGameObject(this); + } + Dispose(); + this = DrawingManager.GetRedrawScope(associatedGameObject); + } + + internal void DrawUntilDispose (GameObject associatedGameObject) { + if (gizmos.Target is DrawingData gizmosTarget) gizmosTarget.DrawUntilDisposed(this, associatedGameObject); + } + + /// <summary> + /// Dispose the redraw scope to stop rendering the items. + /// + /// You must do this when you are done with the scope, even if it was never used to actually render anything. + /// The items will stop rendering immediately: the next camera to render will not render the items unless kept alive in some other way. + /// For example items are always rendered at least once. + /// </summary> + public void Dispose () { + if (gizmos.IsAllocated) { + if (gizmos.Target is DrawingData gizmosTarget) gizmosTarget.DisposeRedrawScope(this); + } + gizmos = default; + id = 0; + } + }; + + /// <summary> + /// Helper for drawing Gizmos in a performant way. + /// This is a replacement for the Unity Gizmos class as that is not very performant + /// when drawing very large amounts of geometry (for example a large grid graph). + /// These gizmos can be persistent, so if the data does not change, the gizmos + /// do not need to be updated. + /// + /// How to use + /// - Create a Hasher object and hash whatever data you will be using to draw the gizmos + /// Could be for example the positions of the vertices or something. Just as long as + /// if the gizmos should change, then the hash changes as well. + /// - Check if a cached mesh exists for that hash + /// - If not, then create a Builder object and call the drawing methods until you are done + /// and then call Finalize with a reference to a gizmos class and the hash you calculated before. + /// - Call gizmos.Draw with the hash. + /// - When you are done with drawing gizmos for this frame, call gizmos.FinalizeDraw + /// + /// <code> + /// var a = Vector3.zero; + /// var b = Vector3.one; + /// var color = Color.red; + /// var hasher = DrawingData.Hasher.Create(this); + /// + /// hasher.Add(a); + /// hasher.Add(b); + /// hasher.Add(color); + /// var gizmos = DrawingManager.instance.gizmos; + /// if (!gizmos.Draw(hasher)) { + /// using (var builder = gizmos.GetBuilder(hasher)) { + /// // Ideally something very complex, not just a single line + /// builder.Line(a, b, color); + /// } + /// } + /// </code> + /// </summary> + public class DrawingData { + /// <summary>Combines hashes into a single hash value</summary> + public struct Hasher : IEquatable<Hasher> { + ulong hash; + + public static Hasher NotSupplied => new Hasher { hash = ulong.MaxValue }; + + public static Hasher Create<T>(T init) { + var h = new Hasher(); + + h.Add(init); + return h; + } + + public void Add<T>(T hash) { + // Just a regular hash function. The + 12289 is to make sure that hashing zeros doesn't just produce a zero (and generally that hashing one X doesn't produce a hash of X) + // (with a struct we can't provide default initialization) + this.hash = (1572869UL * this.hash) ^ (ulong)hash.GetHashCode() + 12289; + } + + public ulong Hash { + get { + return hash; + } + } + + public override int GetHashCode () { + return (int)hash; + } + + public bool Equals (Hasher other) { + return hash == other.hash; + } + } + + internal struct ProcessedBuilderData { + public enum Type { + Invalid = 0, + Static, + Dynamic, + Persistent, + } + + public Type type; + public BuilderData.Meta meta; + bool submitted; + + // A single instance of a MeshBuffers struct. + // This needs to be stored in a NativeArray because we will use it as a pointer + // and it needs to be guaranteed to stay in the same position in memory. + public NativeArray<MeshBuffers> temporaryMeshBuffers; + JobHandle buildJob, splitterJob; + public List<MeshWithType> meshes; + + public bool isValid => type != Type.Invalid; + + public struct CapturedState { + public Matrix4x4 matrix; + public Color color; + } + + public struct MeshBuffers { + public UnsafeAppendBuffer splitterOutput, vertices, triangles, solidVertices, solidTriangles, textVertices, textTriangles, capturedState; + public Bounds bounds; + + public MeshBuffers(Allocator allocator) { + splitterOutput = new UnsafeAppendBuffer(0, 4, allocator); + vertices = new UnsafeAppendBuffer(0, 4, allocator); + triangles = new UnsafeAppendBuffer(0, 4, allocator); + solidVertices = new UnsafeAppendBuffer(0, 4, allocator); + solidTriangles = new UnsafeAppendBuffer(0, 4, allocator); + textVertices = new UnsafeAppendBuffer(0, 4, allocator); + textTriangles = new UnsafeAppendBuffer(0, 4, allocator); + capturedState = new UnsafeAppendBuffer(0, 4, allocator); + bounds = new Bounds(); + } + + public void Dispose () { + splitterOutput.Dispose(); + vertices.Dispose(); + triangles.Dispose(); + solidVertices.Dispose(); + solidTriangles.Dispose(); + textVertices.Dispose(); + textTriangles.Dispose(); + capturedState.Dispose(); + } + + static void DisposeIfLarge (ref UnsafeAppendBuffer ls) { + if (ls.Length*3 < ls.Capacity && ls.Capacity > 1024) { + var alloc = ls.Allocator; + ls.Dispose(); + ls = new UnsafeAppendBuffer(0, 4, alloc); + } + } + + public void DisposeIfLarge () { + DisposeIfLarge(ref splitterOutput); + DisposeIfLarge(ref vertices); + DisposeIfLarge(ref triangles); + DisposeIfLarge(ref solidVertices); + DisposeIfLarge(ref solidTriangles); + DisposeIfLarge(ref textVertices); + DisposeIfLarge(ref textTriangles); + DisposeIfLarge(ref capturedState); + } + } + + public unsafe UnsafeAppendBuffer* splitterOutputPtr => & ((MeshBuffers*)temporaryMeshBuffers.GetUnsafePtr())->splitterOutput; + + public void Init (Type type, BuilderData.Meta meta) { + submitted = false; + this.type = type; + this.meta = meta; + + if (meshes == null) meshes = new List<MeshWithType>(); + if (!temporaryMeshBuffers.IsCreated) { + temporaryMeshBuffers = new NativeArray<MeshBuffers>(1, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); + temporaryMeshBuffers[0] = new MeshBuffers(Allocator.Persistent); + } + } + + static int SubmittedJobs = 0; + + public void SetSplitterJob (DrawingData gizmos, JobHandle splitterJob) { + this.splitterJob = splitterJob; + if (type == Type.Static) { + var cameraInfo = new GeometryBuilder.CameraInfo(null); + unsafe { + buildJob = GeometryBuilder.Build(gizmos, (MeshBuffers*)NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(temporaryMeshBuffers), ref cameraInfo, splitterJob); + } + + SubmittedJobs++; + // ScheduleBatchedJobs is expensive, so only do it once in a while + if (SubmittedJobs % 8 == 0) { + MarkerScheduleJobs.Begin(); + JobHandle.ScheduleBatchedJobs(); + MarkerScheduleJobs.End(); + } + } + } + + public void SchedulePersistFilter (int version, int lastTickVersion, float time, int sceneModeVersion) { + if (type != Type.Persistent) throw new System.InvalidOperationException(); + + // If data was from a different game mode then it shouldn't live any longer. + // E.g. editor mode => game mode + if (meta.sceneModeVersion != sceneModeVersion) { + meta.version = -1; + return; + } + + // Guarantee that all drawing commands survive at least one frame + // Don't filter them until they have had the opportunity to be drawn once at least. + // (they may not actually have been drawn because no cameras may be active) + if (meta.version < lastTickVersion || submitted) { + splitterJob.Complete(); + meta.version = version; + + // If the command buffer is empty then this instance should not live longer + var splitterOutput = temporaryMeshBuffers[0].splitterOutput; + if (splitterOutput.Length == 0) { + meta.version = -1; + return; + } + + buildJob.Complete(); + unsafe { + splitterJob = new PersistentFilterJob { + buffer = &((MeshBuffers*)NativeArrayUnsafeUtility.GetUnsafePtr(temporaryMeshBuffers))->splitterOutput, + time = time, + }.Schedule(splitterJob); + } + } + } + + public bool IsValidForCamera (Camera camera, bool allowGizmos, bool allowCameraDefault) { + if (!allowGizmos && meta.isGizmos) return false; + + if (meta.cameraTargets != null) { + return meta.cameraTargets.Contains(camera); + } else { + return allowCameraDefault; + } + } + + public void Schedule (DrawingData gizmos, ref GeometryBuilder.CameraInfo cameraInfo) { + // The job for Static will already have been scheduled in SetSplitterJob + if (type != Type.Static) { + unsafe { + buildJob = GeometryBuilder.Build(gizmos, (MeshBuffers*)NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(temporaryMeshBuffers), ref cameraInfo, splitterJob); + } + } + } + + public void BuildMeshes (DrawingData gizmos) { + if (type == Type.Static && submitted) return; + buildJob.Complete(); + unsafe { + GeometryBuilder.BuildMesh(gizmos, meshes, (MeshBuffers*)temporaryMeshBuffers.GetUnsafePtr()); + } + submitted = true; + } + + public void CollectMeshes (List<RenderedMeshWithType> meshes) { + var itemMeshes = this.meshes; + var customMeshIndex = 0; + var capturedState = temporaryMeshBuffers[0].capturedState; + var maxCustomMeshes = capturedState.Length / UnsafeUtility.SizeOf<CapturedState>(); + + for (int i = 0; i < itemMeshes.Count; i++) { + Color color; + Matrix4x4 matrix; + int drawOrderIndex; + if ((itemMeshes[i].type & MeshType.Custom) != 0) { + UnityEngine.Assertions.Assert.IsTrue(customMeshIndex < maxCustomMeshes); + + // The color and orientation of custom meshes are stored in the captured state array. + // It is indexed in the same order as the custom meshes in the #meshes list. + unsafe { + var state = *((CapturedState*)capturedState.Ptr + customMeshIndex); + color = state.color; + matrix = state.matrix; + customMeshIndex += 1; + } + // Custom meshes are rendered *after* all similar builders. + // In practice this means all custom meshes are drawn after all dynamic items. + drawOrderIndex = meta.drawOrderIndex + 1; + } else { + // All other meshes use default colors and identity matrices + // since their data is already baked into the vertex colors and positions + color = Color.white; + matrix = Matrix4x4.identity; + drawOrderIndex = meta.drawOrderIndex; + } + meshes.Add(new RenderedMeshWithType { + mesh = itemMeshes[i].mesh, + type = itemMeshes[i].type, + drawingOrderIndex = drawOrderIndex, + color = color, + matrix = matrix, + }); + } + } + + void PoolMeshes (DrawingData gizmos, bool includeCustom) { + if (!isValid) throw new System.InvalidOperationException(); + var outIndex = 0; + for (int i = 0; i < meshes.Count; i++) { + // Custom meshes should only be pooled if the Pool flag is set. + // Otherwise they are supplied by the user and it's up to them how to handle it. + if ((meshes[i].type & MeshType.Custom) == 0 || (includeCustom && (meshes[i].type & MeshType.Pool) != 0)) { + gizmos.PoolMesh(meshes[i].mesh); + } else { + // Retain custom meshes + meshes[outIndex] = meshes[i]; + outIndex += 1; + } + } + meshes.RemoveRange(outIndex, meshes.Count - outIndex); + } + + public void PoolDynamicMeshes (DrawingData gizmos) { + if (type == Type.Static && submitted) return; + PoolMeshes(gizmos, false); + } + + public void Release (DrawingData gizmos) { + if (!isValid) throw new System.InvalidOperationException(); + PoolMeshes(gizmos, true); + // Clear custom meshes too + meshes.Clear(); + type = Type.Invalid; + splitterJob.Complete(); + buildJob.Complete(); + var bufs = this.temporaryMeshBuffers[0]; + bufs.DisposeIfLarge(); + this.temporaryMeshBuffers[0] = bufs; + } + + public void Dispose () { + if (isValid) throw new System.InvalidOperationException(); + splitterJob.Complete(); + buildJob.Complete(); + if (temporaryMeshBuffers.IsCreated) { + temporaryMeshBuffers[0].Dispose(); + temporaryMeshBuffers.Dispose(); + } + } + } + + internal struct SubmittedMesh { + public Mesh mesh; + public bool temporary; + } + + [BurstCompile] + internal struct BuilderData : IDisposable { + public enum State { + Free, + Reserved, + Initialized, + WaitingForSplitter, + WaitingForUserDefinedJob, + } + + public struct Meta { + public Hasher hasher; + public RedrawScope redrawScope1; + public RedrawScope redrawScope2; + public int version; + public bool isGizmos; + /// <summary>Used to invalidate gizmos when the scene mode changes</summary> + public int sceneModeVersion; + public int drawOrderIndex; + public Camera[] cameraTargets; + } + + public struct BitPackedMeta { + uint flags; + + const int UniqueIDBitshift = 17; + const int IsBuiltInFlagIndex = 16; + const int IndexMask = (1 << IsBuiltInFlagIndex) - 1; + const int MaxDataIndex = IndexMask; + public const int UniqueIdMask = (1 << (32 - UniqueIDBitshift)) - 1; + + + public BitPackedMeta (int dataIndex, int uniqueID, bool isBuiltInCommandBuilder) { + // Important to make ensure bitpacking doesn't collide + if (dataIndex > MaxDataIndex) throw new System.Exception("Too many command builders active. Are some command builders not being disposed?"); + UnityEngine.Assertions.Assert.IsTrue(uniqueID <= UniqueIdMask && uniqueID >= 0); + + flags = (uint)(dataIndex | uniqueID << UniqueIDBitshift | (isBuiltInCommandBuilder ? 1 << IsBuiltInFlagIndex : 0)); + } + + public int dataIndex { + get { + return (int)(flags & IndexMask); + } + } + + public int uniqueID { + get { + return (int)(flags >> UniqueIDBitshift); + } + } + + public bool isBuiltInCommandBuilder { + get { + return (flags & (1 << IsBuiltInFlagIndex)) != 0; + } + } + + public static bool operator== (BitPackedMeta lhs, BitPackedMeta rhs) { + return lhs.flags == rhs.flags; + } + + public static bool operator!= (BitPackedMeta lhs, BitPackedMeta rhs) { + return lhs.flags != rhs.flags; + } + + public override bool Equals (object obj) { + if (obj is BitPackedMeta meta) { + return flags == meta.flags; + } + return false; + } + + public override int GetHashCode () { + return (int)flags; + } + } + + public BitPackedMeta packedMeta; + public List<SubmittedMesh> meshes; + public NativeArray<UnsafeAppendBuffer> commandBuffers; + public State state { get; private set; } + // TODO? + public bool preventDispose; + JobHandle splitterJob; + JobHandle disposeDependency; + AllowedDelay disposeDependencyDelay; + System.Runtime.InteropServices.GCHandle disposeGCHandle; + public Meta meta; + + public void Reserve (int dataIndex, bool isBuiltInCommandBuilder) { + if (state != State.Free) throw new System.InvalidOperationException(); + state = BuilderData.State.Reserved; + packedMeta = new BitPackedMeta(dataIndex, (UniqueIDCounter++) & BitPackedMeta.UniqueIdMask, isBuiltInCommandBuilder); + } + + static int UniqueIDCounter = 0; + + public void Init (Hasher hasher, RedrawScope frameRedrawScope, RedrawScope customRedrawScope, bool isGizmos, int drawOrderIndex, int sceneModeVersion) { + if (state != State.Reserved) throw new System.InvalidOperationException(); + + meta = new Meta { + hasher = hasher, + redrawScope1 = frameRedrawScope, + redrawScope2 = customRedrawScope, + isGizmos = isGizmos, + version = 0, // Will be filled in later + drawOrderIndex = drawOrderIndex, + sceneModeVersion = sceneModeVersion, + cameraTargets = null, + }; + + if (meshes == null) meshes = new List<SubmittedMesh>(); + if (!commandBuffers.IsCreated) { +#if UNITY_2022_3_OR_NEWER + commandBuffers = new NativeArray<UnsafeAppendBuffer>(JobsUtility.ThreadIndexCount, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); +#else + commandBuffers = new NativeArray<UnsafeAppendBuffer>(JobsUtility.MaxJobThreadCount, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); +#endif + for (int i = 0; i < commandBuffers.Length; i++) commandBuffers[i] = new UnsafeAppendBuffer(0, 4, Allocator.Persistent); + } + + state = State.Initialized; + } + + public unsafe UnsafeAppendBuffer* bufferPtr { + get { + return (UnsafeAppendBuffer*)commandBuffers.GetUnsafePtr(); + } + } + + [BurstCompile] + [AOT.MonoPInvokeCallback(typeof(AnyBuffersWrittenToDelegate))] + unsafe static bool AnyBuffersWrittenTo (UnsafeAppendBuffer* buffers, int numBuffers) { + bool any = false; + + for (int i = 0; i < numBuffers; i++) { + any |= buffers[i].Length > 0; + } + return any; + } + + [BurstCompile] + [AOT.MonoPInvokeCallback(typeof(AnyBuffersWrittenToDelegate))] + unsafe static void ResetAllBuffers (UnsafeAppendBuffer* buffers, int numBuffers) { + for (int i = 0; i < numBuffers; i++) { + buffers[i].Reset(); + } + } + + unsafe delegate bool AnyBuffersWrittenToDelegate(UnsafeAppendBuffer* buffers, int numBuffers); + private readonly unsafe static AnyBuffersWrittenToDelegate AnyBuffersWrittenToInvoke = BurstCompiler.CompileFunctionPointer<AnyBuffersWrittenToDelegate>(AnyBuffersWrittenTo).Invoke; + unsafe delegate void ResetAllBuffersToDelegate(UnsafeAppendBuffer* buffers, int numBuffers); + private readonly unsafe static ResetAllBuffersToDelegate ResetAllBuffersToInvoke = BurstCompiler.CompileFunctionPointer<ResetAllBuffersToDelegate>(ResetAllBuffers).Invoke; + + public void SubmitWithDependency (System.Runtime.InteropServices.GCHandle gcHandle, JobHandle dependency, AllowedDelay allowedDelay) { + state = State.WaitingForUserDefinedJob; + disposeDependency = dependency; + disposeDependencyDelay = allowedDelay; + disposeGCHandle = gcHandle; + } + + public void Submit (DrawingData gizmos) { + if (state != State.Initialized) throw new System.InvalidOperationException(); + + unsafe { + // There are about 128 buffers we need to check and it's faster to do that using Burst + if (meshes.Count == 0 && !AnyBuffersWrittenToInvoke((UnsafeAppendBuffer*)commandBuffers.GetUnsafeReadOnlyPtr(), commandBuffers.Length)) { + // If no buffers have been written to then simply discard this builder + Release(); + return; + } + } + + meta.version = gizmos.version; + + // Command stream + // split to static, dynamic and persistent + // render static + // render dynamic per camera + // render persistent per camera + const int PersistentDrawOrderOffset = 1000000; + var tmpMeta = meta; + // Reserve some buffers. + // We need to set a deterministic order in which things are drawn to avoid flickering. + // The shaders use the z buffer most of the time, but there are still + // things which are not order independent. + // Static stuff is drawn first + tmpMeta.drawOrderIndex = meta.drawOrderIndex*3 + 0; + int staticBuffer = gizmos.processedData.Reserve(ProcessedBuilderData.Type.Static, tmpMeta); + // Dynamic stuff is drawn directly after the static stuff + // Note that any custom meshes will get this draw order index + 1. + tmpMeta.drawOrderIndex = meta.drawOrderIndex*3 + 1; + int dynamicBuffer = gizmos.processedData.Reserve(ProcessedBuilderData.Type.Dynamic, tmpMeta); + // Persistent stuff is always drawn after everything else + tmpMeta.drawOrderIndex = meta.drawOrderIndex + PersistentDrawOrderOffset; + int persistentBuffer = gizmos.processedData.Reserve(ProcessedBuilderData.Type.Persistent, tmpMeta); + + unsafe { + splitterJob = new StreamSplitter { + inputBuffers = commandBuffers, + staticBuffer = gizmos.processedData.Get(staticBuffer).splitterOutputPtr, + dynamicBuffer = gizmos.processedData.Get(dynamicBuffer).splitterOutputPtr, + persistentBuffer = gizmos.processedData.Get(persistentBuffer).splitterOutputPtr, + }.Schedule(); + } + + gizmos.processedData.Get(staticBuffer).SetSplitterJob(gizmos, splitterJob); + gizmos.processedData.Get(dynamicBuffer).SetSplitterJob(gizmos, splitterJob); + gizmos.processedData.Get(persistentBuffer).SetSplitterJob(gizmos, splitterJob); + + if (meshes.Count > 0) { + // Custom meshes may be affected by matrices and colors that are set in the command builders. + // Matrices may in theory be dynamic per camera (though this functionality is not used at the moment). + // The Command.CaptureState commands are marked as Dynamic so captured state will be written to + // the meshBuffers.capturedState array in the #dynamicBuffer. + var customMeshes = gizmos.processedData.Get(dynamicBuffer).meshes; + + // Copy meshes to render + for (int i = 0; i < meshes.Count; i++) customMeshes.Add(new MeshWithType { mesh = meshes[i].mesh, type = MeshType.Solid | MeshType.Custom | (meshes[i].temporary ? MeshType.Pool : 0) }); + meshes.Clear(); + } + + // TODO: Allocate 3 output objects and pipe splitter to them + + // Only meshes valid for all cameras have been submitted. + // Meshes that depend on the specific camera will be submitted just before rendering + // that camera. Line drawing depends on the exact camera. + // In particular when drawing circles different number of segments + // are used depending on the distance to the camera. + state = State.WaitingForSplitter; + } + + public void CheckJobDependency (DrawingData gizmos, bool allowBlocking) { + if (state == State.WaitingForUserDefinedJob && (disposeDependency.IsCompleted || (allowBlocking && disposeDependencyDelay == AllowedDelay.EndOfFrame))) { + disposeDependency.Complete(); + disposeDependency = default; + disposeGCHandle.Free(); + state = State.Initialized; + Submit(gizmos); + } + } + + public void Release () { + if (state == State.Free) throw new System.InvalidOperationException(); + state = BuilderData.State.Free; + ClearData(); + } + + void ClearData () { + // Wait for any jobs that might be running + // This is important to avoid memory corruption bugs + disposeDependency.Complete(); + splitterJob.Complete(); + meta = default; + disposeDependency = default; + preventDispose = false; + meshes.Clear(); + unsafe { + // There are about 128 buffers we need to reset and it's faster to do that using Burst + ResetAllBuffers((UnsafeAppendBuffer*)commandBuffers.GetUnsafePtr(), commandBuffers.Length); + } + } + + public void Dispose () { + if (state == State.WaitingForUserDefinedJob) { + disposeDependency.Complete(); + disposeGCHandle.Free(); + // We would call Submit here, but we are deleting the data anyway, so who cares. + state = State.WaitingForSplitter; + } + + if (state == State.Reserved || state == State.Initialized || state == State.WaitingForUserDefinedJob) { + UnityEngine.Debug.LogError("Drawing data is being destroyed, but a drawing instance is still active. Are you sure you have called Dispose on all drawing instances? This will cause a memory leak!"); + return; + } + + splitterJob.Complete(); + if (commandBuffers.IsCreated) { + for (int i = 0; i < commandBuffers.Length; i++) { + commandBuffers[i].Dispose(); + } + commandBuffers.Dispose(); + } + } + } + + internal struct BuilderDataContainer : IDisposable { + BuilderData[] data; + + public int memoryUsage { + get { + int sum = 0; + if (data != null) { + for (int i = 0; i < data.Length; i++) { + var cmds = data[i].commandBuffers; + for (int j = 0; j < cmds.Length; j++) { + sum += cmds[j].Capacity; + } + unsafe { + sum += data[i].commandBuffers.Length * sizeof(UnsafeAppendBuffer); + } + } + } + return sum; + } + } + + + public BuilderData.BitPackedMeta Reserve (bool isBuiltInCommandBuilder) { + if (data == null) data = new BuilderData[1]; + for (int i = 0; i < data.Length; i++) { + if (data[i].state == BuilderData.State.Free) { + data[i].Reserve(i, isBuiltInCommandBuilder); + return data[i].packedMeta; + } + } + + var newData = new BuilderData[data.Length * 2]; + data.CopyTo(newData, 0); + data = newData; + return Reserve(isBuiltInCommandBuilder); + } + + public void Release (BuilderData.BitPackedMeta meta) { + data[meta.dataIndex].Release(); + } + + public bool StillExists (BuilderData.BitPackedMeta meta) { + int index = meta.dataIndex; + + if (data == null || index >= data.Length) return false; + return data[index].packedMeta == meta; + } + + public ref BuilderData Get (BuilderData.BitPackedMeta meta) { + int index = meta.dataIndex; + + if (data[index].state == BuilderData.State.Free) throw new System.ArgumentException("Data is not reserved"); + if (data[index].packedMeta != meta) throw new System.ArgumentException("This command builder has already been disposed"); + return ref data[index]; + } + + public void DisposeCommandBuildersWithJobDependencies (DrawingData gizmos) { + if (data == null) return; + for (int i = 0; i < data.Length; i++) data[i].CheckJobDependency(gizmos, false); + MarkerAwaitUserDependencies.Begin(); + for (int i = 0; i < data.Length; i++) data[i].CheckJobDependency(gizmos, true); + MarkerAwaitUserDependencies.End(); + } + + public void ReleaseAllUnused () { + if (data == null) return; + for (int i = 0; i < data.Length; i++) { + if (data[i].state == BuilderData.State.WaitingForSplitter) { + data[i].Release(); + } + } + } + + public void Dispose () { + if (data != null) { + for (int i = 0; i < data.Length; i++) data[i].Dispose(); + } + // Ensures calling Dispose multiple times is a NOOP + data = null; + } + } + + internal struct ProcessedBuilderDataContainer { + ProcessedBuilderData[] data; + Dictionary<ulong, List<int> > hash2index; + Stack<int> freeSlots; + Stack<List<int> > freeLists; + + public int memoryUsage { + get { + int sum = 0; + if (data != null) { + for (int i = 0; i < data.Length; i++) { + var bufs = data[i].temporaryMeshBuffers; + for (int j = 0; j < bufs.Length; j++) { + var psum = 0; + psum += bufs[j].textVertices.Capacity; + psum += bufs[j].textTriangles.Capacity; + psum += bufs[j].solidVertices.Capacity; + psum += bufs[j].solidTriangles.Capacity; + psum += bufs[j].vertices.Capacity; + psum += bufs[j].triangles.Capacity; + psum += bufs[j].capturedState.Capacity; + psum += bufs[j].splitterOutput.Capacity; + sum += psum; + UnityEngine.Debug.Log(i + ":" + j + " " + psum); + } + } + } + return sum; + } + } + + public int Reserve (ProcessedBuilderData.Type type, BuilderData.Meta meta) { + if (data == null) { + data = new ProcessedBuilderData[0]; + freeSlots = new Stack<int>(); + freeLists = new Stack<List<int> >(); + hash2index = new Dictionary<ulong, List<int> >(); + } + if (freeSlots.Count == 0) { + var newData = new ProcessedBuilderData[math.max(4, data.Length*2)]; + data.CopyTo(newData, 0); + for (int i = data.Length; i < newData.Length; i++) freeSlots.Push(i); + data = newData; + } + int index = freeSlots.Pop(); + data[index].Init(type, meta); + if (!meta.hasher.Equals(Hasher.NotSupplied)) { + List<int> ls; + if (!hash2index.TryGetValue(meta.hasher.Hash, out ls)) { + if (freeLists.Count == 0) freeLists.Push(new List<int>()); + ls = hash2index[meta.hasher.Hash] = freeLists.Pop(); + } + ls.Add(index); + } + return index; + } + + public ref ProcessedBuilderData Get (int index) { + if (!data[index].isValid) throw new System.ArgumentException(); + return ref data[index]; + } + + void Release (DrawingData gizmos, int i) { + var h = data[i].meta.hasher.Hash; + + if (!data[i].meta.hasher.Equals(Hasher.NotSupplied)) { + if (hash2index.TryGetValue(h, out var ls)) { + ls.Remove(i); + if (ls.Count == 0) { + freeLists.Push(ls); + hash2index.Remove(h); + } + } + } + data[i].Release(gizmos); + freeSlots.Push(i); + } + + public void SubmitMeshes (DrawingData gizmos, Camera camera, int versionThreshold, bool allowGizmos, bool allowCameraDefault) { + if (data == null) return; + MarkerSchedule.Begin(); + var cameraInfo = new GeometryBuilder.CameraInfo(camera); + int c = 0; + for (int i = 0; i < data.Length; i++) { + if (data[i].isValid && data[i].meta.version >= versionThreshold && data[i].IsValidForCamera(camera, allowGizmos, allowCameraDefault)) { + c++; + data[i].Schedule(gizmos, ref cameraInfo); + } + } + + MarkerSchedule.End(); + + // Ensure all jobs start to be executed on the worker threads now + JobHandle.ScheduleBatchedJobs(); + + MarkerBuild.Begin(); + for (int i = 0; i < data.Length; i++) { + if (data[i].isValid && data[i].meta.version >= versionThreshold && data[i].IsValidForCamera(camera, allowGizmos, allowCameraDefault)) { + data[i].BuildMeshes(gizmos); + } + } + MarkerBuild.End(); + } + + /// <summary> + /// Remove any existing dynamic meshes since we know we will not need them after this frame. + /// We do not remove custom meshes or static ones because those may be kept between frames and cameras. + /// </summary> + public void PoolDynamicMeshes (DrawingData gizmos) { + if (data == null) return; + MarkerPool.Begin(); + for (int i = 0; i < data.Length; i++) { + if (data[i].isValid) { + data[i].PoolDynamicMeshes(gizmos); + } + } + MarkerPool.End(); + } + + public void CollectMeshes (int versionThreshold, List<RenderedMeshWithType> meshes, Camera camera, bool allowGizmos, bool allowCameraDefault) { + if (data == null) return; + for (int i = 0; i < data.Length; i++) { + if (data[i].isValid && data[i].meta.version >= versionThreshold && data[i].IsValidForCamera(camera, allowGizmos, allowCameraDefault)) { + data[i].CollectMeshes(meshes); + } + } + } + + public void FilterOldPersistentCommands (int version, int lastTickVersion, float time, int sceneModeVersion) { + if (data == null) return; + for (int i = 0; i < data.Length; i++) { + if (data[i].isValid && data[i].type == ProcessedBuilderData.Type.Persistent) { + data[i].SchedulePersistFilter(version, lastTickVersion, time, sceneModeVersion); + } + } + } + + public bool SetVersion (Hasher hasher, int version) { + if (data == null) return false; + + if (hash2index.TryGetValue(hasher.Hash, out var indices)) { + UnityEngine.Assertions.Assert.IsTrue(indices.Count > 0); + for (int id = 0; id < indices.Count; id++) { + var i = indices[id]; + UnityEngine.Assertions.Assert.IsTrue(data[i].isValid); + UnityEngine.Assertions.Assert.AreEqual(data[i].meta.hasher.Hash, hasher.Hash); + data[i].meta.version = version; + } + return true; + } else { + return false; + } + } + + public bool SetVersion (RedrawScope scope, int version) { + if (data == null) return false; + bool found = false; + + for (int i = 0; i < data.Length; i++) { + if (data[i].isValid && (data[i].meta.redrawScope1.id == scope.id || data[i].meta.redrawScope2.id == scope.id)) { + data[i].meta.version = version; + found = true; + } + } + return found; + } + + public bool SetCustomScope (Hasher hasher, RedrawScope scope) { + if (data == null) return false; + + if (hash2index.TryGetValue(hasher.Hash, out var indices)) { + UnityEngine.Assertions.Assert.IsTrue(indices.Count > 0); + for (int id = 0; id < indices.Count; id++) { + var i = indices[id]; + UnityEngine.Assertions.Assert.IsTrue(data[i].isValid); + UnityEngine.Assertions.Assert.AreEqual(data[i].meta.hasher.Hash, hasher.Hash); + data[i].meta.redrawScope2 = scope; + } + return true; + } else { + return false; + } + } + + public void ReleaseDataOlderThan (DrawingData gizmos, int version) { + if (data == null) return; + for (int i = 0; i < data.Length; i++) { + if (data[i].isValid && data[i].meta.version < version) { + Release(gizmos, i); + } + } + } + + public void ReleaseAllWithHash (DrawingData gizmos, Hasher hasher) { + if (data == null) return; + for (int i = 0; i < data.Length; i++) { + if (data[i].isValid && data[i].meta.hasher.Hash == hasher.Hash) { + Release(gizmos, i); + } + } + } + + public void Dispose (DrawingData gizmos) { + if (data == null) return; + for (int i = 0; i < data.Length; i++) { + if (data[i].isValid) Release(gizmos, i); + data[i].Dispose(); + } + // Ensures calling Dispose multiple times is a NOOP + data = null; + } + } + + [System.Flags] + internal enum MeshType { + Solid = 1 << 0, + Lines = 1 << 1, + Text = 1 << 2, + // Set if the mesh is not a built-in mesh. These may have non-identity matrices set. + Custom = 1 << 3, + // If set for a custom mesh, the mesh will be pooled. + // This is used for temporary custom meshes that are created by ALINE + Pool = 1 << 4, + BaseType = Solid | Lines | Text, + } + + internal struct MeshWithType { + public Mesh mesh; + public MeshType type; + } + + internal struct RenderedMeshWithType { + public Mesh mesh; + public MeshType type; + public int drawingOrderIndex; + // May only be set to non-white if type contains MeshType.Custom + public Color color; + // May only be set to a non-identity matrix if type contains MeshType.Custom + public Matrix4x4 matrix; + } + + internal BuilderDataContainer data; + internal ProcessedBuilderDataContainer processedData; + List<RenderedMeshWithType> meshes = new List<RenderedMeshWithType>(); + List<Mesh> cachedMeshes = new List<Mesh>(); + List<Mesh> stagingCachedMeshes = new List<Mesh>(); +#if USE_RAW_GRAPHICS_BUFFERS + List<Mesh> stagingCachedMeshesDelay = new List<Mesh>(); +#endif + int lastTimeLargestCachedMeshWasUsed = 0; + internal SDFLookupData fontData; + int currentDrawOrderIndex = 0; + + /// <summary> + /// Incremented every time the editor goes from play mode -> edit mode, or edit mode -> play mode. + /// Used to ensure that no WithDuration scopes survive this transition. + /// + /// Normally it is not important, but when Unity's enter play mode settings have reload domain disabled + /// then it can become important since this manager will survive the transition. + /// </summary> + internal int sceneModeVersion = 0; + + /// <summary> + /// Slightly adjusted scene mode version. + /// This takes into account `Application.isPlaying` too. It is possible for <see cref="sceneModeVersion"/> to be modified + /// and then some gizmos are drawn before the actual play mode change happens (with the old Application.isPlaying) mode. + /// + /// More precisely, what could happen without this adjustment is + /// 1. EditorApplication.playModeStateChanged (PlayModeStateChange.ExitingPlayMode) fires which increments sceneModeVersion. + /// 2. A final update loop runs with Application.isPlaying = true. + /// 3. During this loop, a new command builder is created with the new sceneModeVersion and Application.isPlaying=true and is drawn to using a WithDuration scope. + /// 4. The play mode changes to editor mode. + /// 5. The WithDuration scope survives! + /// + /// We cannot increment sceneModeVersion on PlayModeStateChange.ExitedPlayMode (not Exiting) instead, because some gizmos which we want to keep may + /// be drawn before that event fires. Yay, Unity is so helpful. + /// </summary> + int adjustedSceneModeVersion { + get { + return sceneModeVersion + (Application.isPlaying ? 1000 : 0); + } + } + + internal int GetNextDrawOrderIndex () { + currentDrawOrderIndex++; + return currentDrawOrderIndex; + } + + internal void PoolMesh (Mesh mesh) { + // Note: clearing the mesh here will deallocate the vertex/index buffers + // This is not good for performance as it will have to be allocated again (likely with the same size) in the next frame + //mesh.Clear(); + stagingCachedMeshes.Add(mesh); + } + + void SortPooledMeshes () { + // TODO: Is accessing the vertex count slow? + cachedMeshes.Sort((a, b) => b.vertexCount - a.vertexCount); + } + + internal Mesh GetMesh (int desiredVertexCount) { + if (cachedMeshes.Count > 0) { + // Do a binary search to find the smallest cached mesh which is larger or equal to the desired vertex count + // TODO: We should actually compare the byte size of the vertex buffer, not the number of vertices because + // the vertex size can change depending on the mesh attribute layout. + int mn = 0; + int mx = cachedMeshes.Count; + while (mx > mn + 1) { + int mid = (mn+mx)/2; + if (cachedMeshes[mid].vertexCount < desiredVertexCount) { + mx = mid; + } else { + mn = mid; + } + } + + var res = cachedMeshes[mn]; + if (mn == 0) lastTimeLargestCachedMeshWasUsed = version; + cachedMeshes.RemoveAt(mn); + return res; + } else { + var mesh = new Mesh { + hideFlags = HideFlags.DontSave + }; + mesh.MarkDynamic(); + return mesh; + } + } + + internal void LoadFontDataIfNecessary () { + if (fontData.material == null) { + var font = DefaultFonts.LoadDefaultFont(); + fontData.Dispose(); + fontData = new SDFLookupData(font); + } + } + + static float CurrentTime { + get { + return Application.isPlaying ? Time.time : Time.realtimeSinceStartup; + } + } + + static void UpdateTime () { + // Time.time cannot be accessed in the job system, so create a global variable which *can* be accessed. + // It's not updated as frequently, but it's only used for the WithDuration method, so it should be ok + SharedDrawingData.BurstTime.Data = CurrentTime; + } + + /// <summary> + /// Get an empty builder for queuing drawing commands. + /// + /// <code> + /// // Create a new CommandBuilder + /// using (var draw = DrawingManager.GetBuilder()) { + /// // Use the exact same API as the global Draw class + /// draw.WireBox(Vector3.zero, Vector3.one); + /// } + /// </code> + /// See: <see cref="Drawing.CommandBuilder"/> + /// </summary> + /// <param name="renderInGame">If true, this builder will be rendered in standalone games and in the editor even if gizmos are disabled. + /// If false, it will only be rendered in the editor when gizmos are enabled.</param> + public CommandBuilder GetBuilder (bool renderInGame = false) { + UpdateTime(); + return new CommandBuilder(this, Hasher.NotSupplied, frameRedrawScope, default, !renderInGame, false, adjustedSceneModeVersion); + } + + internal CommandBuilder GetBuiltInBuilder (bool renderInGame = false) { + UpdateTime(); + return new CommandBuilder(this, Hasher.NotSupplied, frameRedrawScope, default, !renderInGame, true, adjustedSceneModeVersion); + } + + /// <summary> + /// Get an empty builder for queuing drawing commands. + /// + /// See: <see cref="Drawing.CommandBuilder"/> + /// </summary> + /// <param name="renderInGame">If true, this builder will be rendered in standalone games and in the editor even if gizmos are disabled.</param> + public CommandBuilder GetBuilder (RedrawScope redrawScope, bool renderInGame = false) { + UpdateTime(); + return new CommandBuilder(this, Hasher.NotSupplied, frameRedrawScope, redrawScope, !renderInGame, false, adjustedSceneModeVersion); + } + + /// <summary> + /// Get an empty builder for queuing drawing commands. + /// + /// See: <see cref="Drawing.CommandBuilder"/> + /// </summary> + /// <param name="renderInGame">If true, this builder will be rendered in standalone games and in the editor even if gizmos are disabled.</param> + public CommandBuilder GetBuilder (Hasher hasher, RedrawScope redrawScope = default, bool renderInGame = false) { + // The user is going to rebuild the data with the given hash + // Let's clear the previous data with that hash since we know it is not needed any longer. + // Do not do this if a hash is not given. + if (!hasher.Equals(Hasher.NotSupplied)) DiscardData(hasher); + UpdateTime(); + return new CommandBuilder(this, hasher, frameRedrawScope, redrawScope, !renderInGame, false, adjustedSceneModeVersion); + } + + /// <summary>Material to use for surfaces</summary> + public Material surfaceMaterial; + + /// <summary>Material to use for lines</summary> + public Material lineMaterial; + + /// <summary>Material to use for text</summary> + public Material textMaterial; + + public DrawingSettings.Settings settings; + + public DrawingSettings.Settings settingsRef { + get { + if (settings == null) { + settings = DrawingSettings.DefaultSettings; + } + + return settings; + } + } + + public int version { get; private set; } = 1; + int lastTickVersion; + int lastTickVersion2; + Dictionary<int, GameObject> persistentRedrawScopes = new Dictionary<int, GameObject>(); +#if ALINE_TRACK_REDRAW_SCOPE_LEAKS + Dictionary<int, String> persistentRedrawScopeInfos = new Dictionary<int, String>(); +#endif + internal System.Runtime.InteropServices.GCHandle gizmosHandle; + + public RedrawScope frameRedrawScope; + + public GameObject GetAssociatedGameObject (RedrawScope scope) { + if (persistentRedrawScopes.TryGetValue(scope.id, out var go)) return go; + return null; + } + + struct Range { + public int start; + public int end; + } + + Dictionary<Camera, Range> cameraVersions = new Dictionary<Camera, Range>(); + + internal static readonly ProfilerMarker MarkerScheduleJobs = new ProfilerMarker("ScheduleJobs"); + internal static readonly ProfilerMarker MarkerAwaitUserDependencies = new ProfilerMarker("Await user dependencies"); + internal static readonly ProfilerMarker MarkerSchedule = new ProfilerMarker("Schedule"); + internal static readonly ProfilerMarker MarkerBuild = new ProfilerMarker("Build"); + internal static readonly ProfilerMarker MarkerPool = new ProfilerMarker("Pool"); + internal static readonly ProfilerMarker MarkerRelease = new ProfilerMarker("Release"); + internal static readonly ProfilerMarker MarkerBuildMeshes = new ProfilerMarker("Build Meshes"); + internal static readonly ProfilerMarker MarkerCollectMeshes = new ProfilerMarker("Collect Meshes"); + internal static readonly ProfilerMarker MarkerSortMeshes = new ProfilerMarker("Sort Meshes"); + internal static readonly ProfilerMarker LeakTracking = new ProfilerMarker("RedrawScope Leak Tracking"); + + void DiscardData (Hasher hasher) { + processedData.ReleaseAllWithHash(this, hasher); + } + + internal void OnChangingPlayMode () { + sceneModeVersion++; + +#if UNITY_EDITOR + // If we are in the editor, we schedule a callback to check if any RedrawScope objects were not disposed. + // OnChangingPlayMode will run before the scene is destroyed. So we know that any persistent redraw scopes + // that are alive right now should be destroyed soon. + // We wait a few updates to allow the scene to be destroyed before we check for leaks. + // EditorApplication.delayCall may be called before the scene has actually been destroyed. + // Usually it has, but in particular if the user double-clicks the play button to start and then immediately + // stop the game, then it may run before the scene has been destroyed. + var shouldBeDestroyed = this.persistentRedrawScopes.Keys.ToArray(); + UnityEditor.EditorApplication.CallbackFunction checkLeaks = null; + int remainingFrames = 2; + checkLeaks = () => { + if (remainingFrames > 0) { + remainingFrames--; + return; + } + UnityEditor.EditorApplication.delayCall -= checkLeaks; + int leaked = 0; + foreach (var v in shouldBeDestroyed) { + if (persistentRedrawScopes.ContainsKey(v)) leaked++; + } + if (leaked > 0) { +#if ALINE_TRACK_REDRAW_SCOPE_LEAKS + UnityEngine.Debug.LogError(leaked + " RedrawScope objects were not disposed. Make sure to dispose them when you are done with them, otherwise this will lead to a memory leak and potentially a performance issue."); + foreach (var v in shouldBeDestroyed) { + if (persistentRedrawScopes.ContainsKey(v)) { + UnityEngine.Debug.LogError("RedrawScope leaked. Allocated from:\n" + persistentRedrawScopeInfos[v]); + } + } +#else + UnityEngine.Debug.LogError(leaked + " RedrawScope objects were not disposed. Make sure to dispose them when you are done with them, otherwise this will lead to a memory leak and potentially a performance issue.\nEnable ALINE_TRACK_REDRAW_SCOPE_LEAKS in the scripting define symbols to track the leaks more accurately."); +#endif + foreach (var v in shouldBeDestroyed) { + persistentRedrawScopes.Remove(v); +#if ALINE_TRACK_REDRAW_SCOPE_LEAKS + persistentRedrawScopeInfos.Remove(v); +#endif + } + } + }; + UnityEditor.EditorApplication.delayCall += checkLeaks; +#endif + } + + /// <summary> + /// Schedules the meshes for the specified hash to be drawn. + /// Returns: False if there is no cached mesh for this hash, you may want to + /// submit one in that case. The draw command will be issued regardless of the return value. + /// </summary> + public bool Draw (Hasher hasher) { + if (hasher.Equals(Hasher.NotSupplied)) throw new System.ArgumentException("Invalid hash value"); + return processedData.SetVersion(hasher, version); + } + + /// <summary> + /// Schedules the meshes for the specified hash to be drawn. + /// Returns: False if there is no cached mesh for this hash, you may want to + /// submit one in that case. The draw command will be issued regardless of the return value. + /// + /// This overload will draw all meshes within the specified redraw scope. + /// Note that if they had been drawn with another redraw scope earlier they will be removed from that scope. + /// </summary> + public bool Draw (Hasher hasher, RedrawScope scope) { + if (hasher.Equals(Hasher.NotSupplied)) throw new System.ArgumentException("Invalid hash value"); + processedData.SetCustomScope(hasher, scope); + return processedData.SetVersion(hasher, version); + } + + /// <summary>Schedules all meshes that were drawn the last frame with this redraw scope to be drawn again</summary> + internal void Draw (RedrawScope scope) { + if (scope.isValid) processedData.SetVersion(scope, version); + } + + internal void DrawUntilDisposed (RedrawScope scope, GameObject associatedGameObject) { + if (scope.isValid) { + Draw(scope); + persistentRedrawScopes.Add(scope.id, associatedGameObject); +#if ALINE_TRACK_REDRAW_SCOPE_LEAKS && UNITY_EDITOR + LeakTracking.Begin(); + persistentRedrawScopeInfos[scope.id] = new System.Diagnostics.StackTrace().ToString(); + LeakTracking.End(); +#endif + } + } + + internal void DisposeRedrawScope (RedrawScope scope) { + if (scope.isValid) { + processedData.SetVersion(scope, -1); + persistentRedrawScopes.Remove(scope.id); +#if ALINE_TRACK_REDRAW_SCOPE_LEAKS && UNITY_EDITOR + persistentRedrawScopeInfos.Remove(scope.id); +#endif + } + } + + void RefreshRedrawScopes () { +#if UNITY_EDITOR && UNITY_2020_1_OR_NEWER + var currentStage = UnityEditor.SceneManagement.StageUtility.GetCurrentStage(); + var isInNonMainStage = currentStage != UnityEditor.SceneManagement.StageUtility.GetMainStage(); +#endif + foreach (var scope in persistentRedrawScopes) { +#if UNITY_EDITOR && UNITY_2020_1_OR_NEWER + // True if the scene is in isolation mode (e.g. focusing on a single prefab) and this object is not part of that sub-stage + var disabledDueToIsolationMode = isInNonMainStage && scope.Value != null && UnityEditor.SceneManagement.StageUtility.GetStage(scope.Value) != currentStage; + if (disabledDueToIsolationMode) continue; +#endif + processedData.SetVersion(new RedrawScope(this, scope.Key), version); + } + } + + public void TickFramePreRender () { + data.DisposeCommandBuildersWithJobDependencies(this); + // Remove persistent commands that have timed out. + // When not playing then persistent commands are never drawn twice + processedData.FilterOldPersistentCommands(version, lastTickVersion, CurrentTime, adjustedSceneModeVersion); + + RefreshRedrawScopes(); + + // All cameras rendered between the last tick and this one will have + // a version that is at least lastTickVersion + 1. + // However the user may want to reuse meshes from the previous frame (see Draw(Hasher)). + // This requires us to keep data from one more frame and thus we use lastTickVersion2 + 1 + // TODO: One frame should be enough, right? + processedData.ReleaseDataOlderThan(this, lastTickVersion2 + 1); + lastTickVersion2 = lastTickVersion; + lastTickVersion = version; + currentDrawOrderIndex = 0; + + // Pooled meshes from two frames ago can now be used. +#if USE_RAW_GRAPHICS_BUFFERS + // One would think that pooled meshes from only one frame ago can be used. + // And yes, Unity will allow this, but the GPU may still be working on the meshes from the previous frame. + // Therefore, when we try to write to the raw mesh vertex buffers Unity will block until the previous + // frame's GPU work is done, which may take a long time. + // Using "double buffering" for the meshes that are updated every frame is more efficient. + // When we use simplified methods for setting the vertex/index data we don't have to do this + // because Unity seems to manage an upload buffer or something for us. + cachedMeshes.AddRange(stagingCachedMeshesDelay); + // Move stagingCachedMeshes to stagingCachedMeshesDelay, and make stagingCachedMeshes an empty list. + stagingCachedMeshesDelay.Clear(); + var tmp = stagingCachedMeshesDelay; + stagingCachedMeshesDelay = stagingCachedMeshes; + stagingCachedMeshes = tmp; +#else + cachedMeshes.AddRange(stagingCachedMeshes); + stagingCachedMeshes.Clear(); +#endif + SortPooledMeshes(); + + // If the largest cached mesh hasn't been used in a while, then remove it to free up the memory + if (version - lastTimeLargestCachedMeshWasUsed > 60 && cachedMeshes.Count > 0) { + Mesh.DestroyImmediate(cachedMeshes[0]); + cachedMeshes.RemoveAt(0); + lastTimeLargestCachedMeshWasUsed = version; + } + + // TODO: Filter cameraVersions to avoid memory leak + } + + public void PostRenderCleanup () { + MarkerRelease.Begin(); + data.ReleaseAllUnused(); + MarkerRelease.End(); + version++; + } + + class MeshCompareByDrawingOrder : IComparer<RenderedMeshWithType> { + public int Compare (RenderedMeshWithType a, RenderedMeshWithType b) { + // Extract if the meshes are Solid/Lines/Text + var ta = (int)a.type & 0x7; + var tb = (int)b.type & 0x7; + return ta != tb ? ta - tb : a.drawingOrderIndex - b.drawingOrderIndex; + } + } + + static readonly MeshCompareByDrawingOrder meshSorter = new MeshCompareByDrawingOrder(); + // Temporary array, cached to avoid allocations + Plane[] frustrumPlanes = new Plane[6]; + // Temporary block, cached to avoid allocations + MaterialPropertyBlock customMaterialProperties = new MaterialPropertyBlock(); + + int totalMemoryUsage => this.data.memoryUsage + this.processedData.memoryUsage; + + void LoadMaterials () { + // Make sure the material references are correct + + // Note: When importing the package for the first time the asset database may not be up to date and Resources.Load may return null. + + if (surfaceMaterial == null) { + surfaceMaterial = Resources.Load<Material>("aline_surface"); + } + if (lineMaterial == null) { + lineMaterial = Resources.Load<Material>("aline_outline"); + } + if (fontData.material == null) { + var font = DefaultFonts.LoadDefaultFont(); + fontData.Dispose(); + fontData = new SDFLookupData(font); + } + } + + public DrawingData() { + gizmosHandle = System.Runtime.InteropServices.GCHandle.Alloc(this, System.Runtime.InteropServices.GCHandleType.Weak); + LoadMaterials(); + } + + static int CeilLog2 (int x) { + // Should use `math.ceillog2` whenever we next raise the minimum compatible version of the mathematics package. + // This variant is prone to floating point errors. + return (int)math.ceil(math.log2(x)); + } + + /// <summary> + /// Wrapper for different kinds of commands buffers. + /// + /// Annoyingly, they all use a CommandBuffer in the end, but the universal render pipeline wraps it in a RasterCommandBuffer, + /// and it's not possible to get the underlaying CommandBuffer. + /// </summary> + public struct CommandBufferWrapper { + public CommandBuffer cmd; +#if MODULE_RENDER_PIPELINES_UNIVERSAL_17_0_0_OR_NEWER + public bool allowDisablingWireframe; + public RasterCommandBuffer cmd2; +#endif + +#if UNITY_2023_1_OR_NEWER + public void SetWireframe (bool enable) { + if (cmd != null) { + cmd.SetWireframe(enable); + } +#if MODULE_RENDER_PIPELINES_UNIVERSAL_17_0_0_OR_NEWER + else if (cmd2 != null) { + if (allowDisablingWireframe) cmd2.SetWireframe(enable); + } +#endif + } +#endif + + public void DrawMesh (Mesh mesh, Matrix4x4 matrix, Material material, int submeshIndex, int shaderPass, MaterialPropertyBlock properties) { + if (cmd != null) { + cmd.DrawMesh(mesh, matrix, material, submeshIndex, shaderPass, properties); + } +#if MODULE_RENDER_PIPELINES_UNIVERSAL_17_0_0_OR_NEWER + else if (cmd2 != null) { + cmd2.DrawMesh(mesh, matrix, material, submeshIndex, shaderPass, properties); + } +#endif + } + } + + /// <summary>Call after all <see cref="Draw"/> commands for the frame have been done to draw everything.</summary> + /// <param name="allowCameraDefault">Indicates if built-in command builders and custom ones without a custom CommandBuilder.cameraTargets should render to this camera.</param> + public void Render (Camera cam, bool allowGizmos, CommandBufferWrapper commandBuffer, bool allowCameraDefault) { + LoadMaterials(); + + // Warn if the materials could not be found + if (surfaceMaterial == null || lineMaterial == null) { + // Note that when the package is installed Unity may start rendering things and call this method before it has initialized the Resources folder with the materials. + // We don't want to throw exceptions in that case because once the import finishes everything will be good. + // UnityEngine.Debug.LogWarning("Looks like you just installed ALINE. The ALINE package will start working after the next script recompilation."); + return; + } + + var planes = frustrumPlanes; + GeometryUtility.CalculateFrustumPlanes(cam, planes); + + if (!cameraVersions.TryGetValue(cam, out Range cameraRenderingRange)) { + cameraRenderingRange = new Range { start = int.MinValue, end = int.MinValue }; + } + + // Check if the last time the camera was rendered + // was during the current frame. + if (cameraRenderingRange.end > lastTickVersion) { + // In some cases a camera is rendered multiple times per frame. + // In this case we just extend the end of the drawing range up to the current version. + // The reasoning is that all times the camera is rendered in a frame + // all things should be drawn. + // If we did update the start of the range then things would only be drawn + // the first time the camera was rendered in the frame. + + // Sometimes the scene view will be rendered twice in a single frame + // due to some internal Unity tooltip code. + // Without this fix the scene view camera may end up showing no gizmos + // for a single frame. + cameraRenderingRange.end = version + 1; + } else { + // This is the common case: the previous time the camera was rendered + // it rendered all versions lower than cameraRenderingRange.end. + // So now we start by rendering from that version. + cameraRenderingRange = new Range { start = cameraRenderingRange.end, end = version + 1 }; + } + + // Don't show anything rendered before the last frame. + // If the camera has been turned off for a while and then suddenly starts rendering again + // we want to make sure that we don't render meshes from multiple frames. + // This happens often in the unity editor as the scene view and game view often skip + // rendering many frames when outside of play mode. + cameraRenderingRange.start = Mathf.Max(cameraRenderingRange.start, lastTickVersion2 + 1); + + var settings = settingsRef; + +#if UNITY_2023_1_OR_NEWER + bool skipDueToWireframe = false; + commandBuffer.SetWireframe(false); +#else + // If GL.wireframe is enabled (the Wireframe mode in the scene view settings) + // then I have found no way to draw gizmos in a good way. + // It's best to disable gizmos altogether to avoid drawing wireframe versions of gizmo meshes. + bool skipDueToWireframe = GL.wireframe; +#endif + + if (!skipDueToWireframe) { + MarkerBuildMeshes.Begin(); + processedData.SubmitMeshes(this, cam, cameraRenderingRange.start, allowGizmos, allowCameraDefault); + MarkerBuildMeshes.End(); + MarkerCollectMeshes.Begin(); + meshes.Clear(); + processedData.CollectMeshes(cameraRenderingRange.start, meshes, cam, allowGizmos, allowCameraDefault); + processedData.PoolDynamicMeshes(this); + MarkerCollectMeshes.End(); + MarkerSortMeshes.Begin(); + // Note that a stable sort is required as some meshes may have the same sorting index + // but those meshes will have a consistent ordering between them in the list + meshes.Sort(meshSorter); + MarkerSortMeshes.End(); + + int colorID = Shader.PropertyToID("_Color"); + int colorFadeID = Shader.PropertyToID("_FadeColor"); + var solidBaseColor = new Color(1, 1, 1, settings.solidOpacity); + var solidFadeColor = new Color(1, 1, 1, settings.solidOpacityBehindObjects); + var lineBaseColor = new Color(1, 1, 1, settings.lineOpacity); + var lineFadeColor = new Color(1, 1, 1, settings.lineOpacityBehindObjects); + var textBaseColor = new Color(1, 1, 1, settings.textOpacity); + var textFadeColor = new Color(1, 1, 1, settings.textOpacityBehindObjects); + + // The meshes list is already sorted as first surfaces, then lines, then text + for (int i = 0; i < meshes.Count;) { + int meshEndIndex = i+1; + var tp = meshes[i].type & MeshType.BaseType; + while (meshEndIndex < meshes.Count && (meshes[meshEndIndex].type & MeshType.BaseType) == tp) meshEndIndex++; + + Material mat; + customMaterialProperties.Clear(); + switch (tp) { + case MeshType.Solid: + mat = surfaceMaterial; + customMaterialProperties.SetColor(colorID, solidBaseColor); + customMaterialProperties.SetColor(colorFadeID, solidFadeColor); + break; + case MeshType.Lines: + mat = lineMaterial; + customMaterialProperties.SetColor(colorID, lineBaseColor); + customMaterialProperties.SetColor(colorFadeID, lineFadeColor); + break; + case MeshType.Text: + mat = fontData.material; + customMaterialProperties.SetColor(colorID, textBaseColor); + customMaterialProperties.SetColor(colorFadeID, textFadeColor); + break; + default: + throw new System.InvalidOperationException("Invalid mesh type"); + } + + for (int pass = 0; pass < mat.passCount; pass++) { + for (int j = i; j < meshEndIndex; j++) { + var mesh = meshes[j]; + if ((mesh.type & MeshType.Custom) != 0) { + // This mesh type may have a matrix set. So we need to handle that + if (GeometryUtility.TestPlanesAABB(planes, TransformBoundingBox(mesh.matrix, mesh.mesh.bounds))) { + // Custom meshes may have different colors + customMaterialProperties.SetColor(colorID, solidBaseColor * mesh.color); + commandBuffer.DrawMesh(mesh.mesh, mesh.matrix, mat, 0, pass, customMaterialProperties); + customMaterialProperties.SetColor(colorID, solidBaseColor); + } + } else if (GeometryUtility.TestPlanesAABB(planes, mesh.mesh.bounds)) { + // This mesh is drawn with an identity matrix + commandBuffer.DrawMesh(mesh.mesh, Matrix4x4.identity, mat, 0, pass, customMaterialProperties); + } + } + } + + i = meshEndIndex; + } + + meshes.Clear(); + } + + cameraVersions[cam] = cameraRenderingRange; + } + + /// <summary>Returns a new axis aligned bounding box that contains the given bounding box after being transformed by the matrix</summary> + static Bounds TransformBoundingBox (Matrix4x4 matrix, Bounds bounds) { + var mn = bounds.min; + var mx = bounds.max; + // Create the bounding box from the bounding box of the transformed + // 8 points of the original bounding box. + var newBounds = new Bounds(matrix.MultiplyPoint(mn), Vector3.zero); + + newBounds.Encapsulate(matrix.MultiplyPoint(new Vector3(mn.x, mn.y, mx.z))); + + newBounds.Encapsulate(matrix.MultiplyPoint(new Vector3(mn.x, mx.y, mn.z))); + newBounds.Encapsulate(matrix.MultiplyPoint(new Vector3(mn.x, mx.y, mx.z))); + + newBounds.Encapsulate(matrix.MultiplyPoint(new Vector3(mx.x, mn.y, mn.z))); + newBounds.Encapsulate(matrix.MultiplyPoint(new Vector3(mx.x, mn.y, mx.z))); + + newBounds.Encapsulate(matrix.MultiplyPoint(new Vector3(mx.x, mx.y, mn.z))); + newBounds.Encapsulate(matrix.MultiplyPoint(new Vector3(mx.x, mx.y, mx.z))); + return newBounds; + } + + /// <summary> + /// Destroys all cached meshes. + /// Used to make sure that no memory leaks happen in the Unity Editor. + /// </summary> + public void ClearData () { + gizmosHandle.Free(); + data.Dispose(); + processedData.Dispose(this); + + for (int i = 0; i < cachedMeshes.Count; i++) { + Mesh.DestroyImmediate(cachedMeshes[i]); + } + cachedMeshes.Clear(); + + UnityEngine.Assertions.Assert.IsTrue(meshes.Count == 0); + fontData.Dispose(); + } + } +} diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/DrawingData.cs.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/DrawingData.cs.meta new file mode 100644 index 0000000..5b42fe5 --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/DrawingData.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 7a3cd109c93704c7ba499b8653f402ac +timeCreated: 1472980867 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/DrawingManager.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/DrawingManager.cs new file mode 100644 index 0000000..3e40815 --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/DrawingManager.cs @@ -0,0 +1,788 @@ +#pragma warning disable 649 // Field `Drawing.GizmoContext.activeTransform' is never assigned to, and will always have its default value `null'. Not used outside of the unity editor. +using UnityEngine; +using System.Collections; +using System; +#if UNITY_EDITOR +using UnityEditor; +using UnityEditor.SceneManagement; +#endif +using System.Collections.Generic; +using Unity.Jobs; +using Unity.Mathematics; +using UnityEngine.Rendering; +using Unity.Profiling; +#if MODULE_RENDER_PIPELINES_UNIVERSAL +using UnityEngine.Rendering.Universal; +#endif +#if MODULE_RENDER_PIPELINES_HIGH_DEFINITION +using UnityEngine.Rendering.HighDefinition; +#endif + +namespace Pathfinding.Drawing { + /// <summary>Info about the current selection in the editor</summary> + public static class GizmoContext { +#if UNITY_EDITOR + static Transform activeTransform; +#endif + + static HashSet<Transform> selectedTransforms = new HashSet<Transform>(); + + static internal bool drawingGizmos; + static internal bool dirty; + private static int selectionSizeInternal; + + /// <summary>Number of top-level transforms that are selected</summary> + public static int selectionSize { + get { + Refresh(); + return selectionSizeInternal; + } + private set { + selectionSizeInternal = value; + } + } + + internal static void SetDirty () { + dirty = true; + } + + private static void Refresh () { +#if UNITY_EDITOR + if (!drawingGizmos) throw new System.Exception("Can only be used inside the ALINE library's gizmo drawing functions."); + if (dirty) { + dirty = false; + DrawingManager.MarkerRefreshSelectionCache.Begin(); + activeTransform = Selection.activeTransform; + selectedTransforms.Clear(); + var topLevel = Selection.transforms; + for (int i = 0; i < topLevel.Length; i++) selectedTransforms.Add(topLevel[i]); + selectionSize = topLevel.Length; + DrawingManager.MarkerRefreshSelectionCache.End(); + } +#endif + } + + /// <summary> + /// True if the component is selected. + /// This is a deep selection: even children of selected transforms are considered to be selected. + /// </summary> + public static bool InSelection (Component c) { + return InSelection(c.transform); + } + + /// <summary> + /// True if the transform is selected. + /// This is a deep selection: even children of selected transforms are considered to be selected. + /// </summary> + public static bool InSelection (Transform tr) { + Refresh(); + var leaf = tr; + while (tr != null) { + if (selectedTransforms.Contains(tr)) { + selectedTransforms.Add(leaf); + return true; + } + tr = tr.parent; + } + return false; + } + + /// <summary> + /// True if the component is shown in the inspector. + /// The active selection is the GameObject that is currently visible in the inspector. + /// </summary> + public static bool InActiveSelection (Component c) { + return InActiveSelection(c.transform); + } + + /// <summary> + /// True if the transform is shown in the inspector. + /// The active selection is the GameObject that is currently visible in the inspector. + /// </summary> + public static bool InActiveSelection (Transform tr) { +#if UNITY_EDITOR + Refresh(); + return tr.transform == activeTransform; +#else + return false; +#endif + } + } + + /// <summary> + /// Every object that wants to draw gizmos should implement this interface. + /// See: <see cref="Drawing.MonoBehaviourGizmos"/> + /// </summary> + public interface IDrawGizmos { + void DrawGizmos(); + } + + public enum DetectedRenderPipeline { + BuiltInOrCustom, + HDRP, + URP + } + + /// <summary> + /// Global script which draws debug items and gizmos. + /// If a Draw.* method has been used or if any script inheriting from the <see cref="Drawing.MonoBehaviourGizmos"/> class is in the scene then an instance of this script + /// will be created and put on a hidden GameObject. + /// + /// It will inject drawing logic into any cameras that are rendered. + /// + /// Usually you never have to interact with this class. + /// </summary> + [ExecuteAlways] + [AddComponentMenu("")] + public class DrawingManager : MonoBehaviour { + public DrawingData gizmos; + static List<IDrawGizmos> gizmoDrawers = new List<IDrawGizmos>(); + static Dictionary<System.Type, bool> gizmoDrawerTypes = new Dictionary<System.Type, bool>(); + static DrawingManager _instance; + bool framePassed; + int lastFrameCount = int.MinValue; + float lastFrameTime = -float.NegativeInfinity; + int lastFilterFrame; +#if UNITY_EDITOR + bool builtGizmos; +#endif + + /// <summary>True if OnEnable has been called on this instance and OnDisable has not</summary> + [SerializeField] + bool actuallyEnabled; + + RedrawScope previousFrameRedrawScope; + + /// <summary> + /// Allow rendering to cameras that render to RenderTextures. + /// By default cameras which render to render textures are never rendered to. + /// You may enable this if you wish. + /// + /// See: <see cref="Drawing.CommandBuilder.cameraTargets"/> + /// See: advanced (view in online documentation for working links) + /// </summary> + public static bool allowRenderToRenderTextures = false; + public static bool drawToAllCameras = false; + + /// <summary> + /// Multiply all line widths by this value. + /// This can be used to make lines thicker or thinner. + /// + /// This is primarily useful when generating screenshots, and you want to render at a higher resolution before scaling down the image. + /// + /// It is only read when a camera is being rendered. So it cannot be used to change line thickness on a per-item basis. + /// Use <see cref="Draw.WithLineWidth"/> for that. + /// </summary> + public static float lineWidthMultiplier = 1.0f; + + CommandBuffer commandBuffer; + + [System.NonSerialized] + DetectedRenderPipeline detectedRenderPipeline = DetectedRenderPipeline.BuiltInOrCustom; + +#if MODULE_RENDER_PIPELINES_UNIVERSAL + HashSet<ScriptableRenderer> scriptableRenderersWithPass = new HashSet<ScriptableRenderer>(); + AlineURPRenderPassFeature renderPassFeature; +#endif + + private static readonly ProfilerMarker MarkerALINE = new ProfilerMarker("ALINE"); + private static readonly ProfilerMarker MarkerCommandBuffer = new ProfilerMarker("Executing command buffer"); + private static readonly ProfilerMarker MarkerFrameTick = new ProfilerMarker("Frame Tick"); + private static readonly ProfilerMarker MarkerFilterDestroyedObjects = new ProfilerMarker("Filter destroyed objects"); + internal static readonly ProfilerMarker MarkerRefreshSelectionCache = new ProfilerMarker("Refresh Selection Cache"); + private static readonly ProfilerMarker MarkerGizmosAllowed = new ProfilerMarker("GizmosAllowed"); + private static readonly ProfilerMarker MarkerDrawGizmos = new ProfilerMarker("DrawGizmos"); + private static readonly ProfilerMarker MarkerSubmitGizmos = new ProfilerMarker("Submit Gizmos"); + + public static DrawingManager instance { + get { + if (_instance == null) Init(); + return _instance; + } + } + +#if UNITY_EDITOR + [InitializeOnLoadMethod] +#endif + public static void Init () { +#if ENABLE_UNITY_COLLECTIONS_CHECKS + if (Unity.Jobs.LowLevel.Unsafe.JobsUtility.IsExecutingJob) throw new System.Exception("Draw.* methods cannot be called from inside a job. See the documentation for info about how to use drawing functions from the Unity Job System."); +#endif + if (_instance != null) return; + + // Here one might try to look for existing instances of the class that haven't yet been enabled. + // However, this turns out to be tricky. + // Resources.FindObjectsOfTypeAll<T>() is the only call that includes HideInInspector GameObjects. + // But it is hard to distinguish between objects that are internal ones which will never be enabled and objects that will be enabled. + // Checking .gameObject.scene.isLoaded doesn't work reliably (object may be enabled and working even if isLoaded is false) + // Checking .gameObject.scene.isValid doesn't work reliably (object may be enabled and working even if isValid is false) + + // So instead we just always create a new instance. This is not a particularly heavy operation and it only happens once per game, so why not. + // The OnEnable call will clean up duplicate managers if there are any. + + var go = new GameObject("RetainedGizmos") { + hideFlags = HideFlags.DontSave | HideFlags.NotEditable | HideFlags.HideInInspector | HideFlags.HideInHierarchy + }; + _instance = go.AddComponent<DrawingManager>(); + if (Application.isPlaying) DontDestroyOnLoad(go); + } + + /// <summary>Detects which render pipeline is being used and configures them for rendering</summary> + void RefreshRenderPipelineMode () { + var pipelineType = RenderPipelineManager.currentPipeline != null? RenderPipelineManager.currentPipeline.GetType() : null; + +#if MODULE_RENDER_PIPELINES_HIGH_DEFINITION + if (pipelineType == typeof(HDRenderPipeline)) { + if (detectedRenderPipeline != DetectedRenderPipeline.HDRP) { + detectedRenderPipeline = DetectedRenderPipeline.HDRP; + if (!_instance.gameObject.TryGetComponent<CustomPassVolume>(out CustomPassVolume volume)) { + volume = _instance.gameObject.AddComponent<CustomPassVolume>(); + volume.isGlobal = true; + volume.injectionPoint = CustomPassInjectionPoint.AfterPostProcess; + volume.customPasses.Add(new AlineHDRPCustomPass()); + } + + var asset = GraphicsSettings.defaultRenderPipeline as HDRenderPipelineAsset; + if (asset != null) { + if (!asset.currentPlatformRenderPipelineSettings.supportCustomPass) { + Debug.LogWarning("A*: The current render pipeline has custom pass support disabled. The A* Pathfinding Project will not be able to render anything. Please enable custom pass support on your HDRenderPipelineAsset.", asset); + } + } + } + return; + } +#endif +#if MODULE_RENDER_PIPELINES_UNIVERSAL + if (pipelineType == typeof(UniversalRenderPipeline)) { + detectedRenderPipeline = DetectedRenderPipeline.URP; + return; + } +#endif + detectedRenderPipeline = DetectedRenderPipeline.BuiltInOrCustom; + } + +#if UNITY_EDITOR + void DelayedDestroy () { + EditorApplication.update -= DelayedDestroy; + // Check if the object still exists (it might have been destroyed in some other way already). + if (gameObject) GameObject.DestroyImmediate(gameObject); + } + + void OnPlayModeStateChanged (PlayModeStateChange change) { + if (change == PlayModeStateChange.ExitingEditMode || change == PlayModeStateChange.ExitingPlayMode) { + gizmos.OnChangingPlayMode(); + } + } +#endif + + void OnEnable () { + if (_instance == null) _instance = this; + + // Ensure we don't have duplicate managers + if (_instance != this) { + // We cannot destroy the object while it is being enabled, so we need to delay it a bit +#if UNITY_EDITOR + // This is only important in the editor to avoid a build-up of old managers. + // In an actual game at most 1 (though in practice zero) old managers will be laying around. + // It would be nice to use a coroutine for this instead, but unfortunately they do not work for objects marked with HideAndDontSave. + EditorApplication.update += DelayedDestroy; +#endif + return; + } + + actuallyEnabled = true; + if (gizmos == null) gizmos = new DrawingData(); + gizmos.frameRedrawScope = new RedrawScope(gizmos); + Draw.builder = gizmos.GetBuiltInBuilder(false); + Draw.ingame_builder = gizmos.GetBuiltInBuilder(true); + commandBuffer = new CommandBuffer(); + commandBuffer.name = "ALINE Gizmos"; + + // Callback when rendering with the built-in render pipeline + Camera.onPostRender += PostRender; + // Callback when rendering with a scriptable render pipeline +#if UNITY_2023_3_OR_NEWER + UnityEngine.Rendering.RenderPipelineManager.beginContextRendering += BeginContextRendering; +#else + UnityEngine.Rendering.RenderPipelineManager.beginFrameRendering += BeginFrameRendering; +#endif + UnityEngine.Rendering.RenderPipelineManager.beginCameraRendering += BeginCameraRendering; + UnityEngine.Rendering.RenderPipelineManager.endCameraRendering += EndCameraRendering; +#if UNITY_EDITOR + EditorApplication.update += OnEditorUpdate; + EditorApplication.playModeStateChanged += OnPlayModeStateChanged; +#endif + } + + void BeginContextRendering (ScriptableRenderContext context, List<Camera> cameras) { + RefreshRenderPipelineMode(); + } + + void BeginFrameRendering (ScriptableRenderContext context, Camera[] cameras) { + RefreshRenderPipelineMode(); + } + + void BeginCameraRendering (ScriptableRenderContext context, Camera camera) { +#if MODULE_RENDER_PIPELINES_UNIVERSAL + if (detectedRenderPipeline == DetectedRenderPipeline.URP) { + var data = camera.GetUniversalAdditionalCameraData(); + if (data != null) { + var renderer = data.scriptableRenderer; + if (renderPassFeature == null) { + renderPassFeature = ScriptableObject.CreateInstance<AlineURPRenderPassFeature>(); + } + renderPassFeature.AddRenderPasses(renderer); + } + } +#endif + } + + void OnDisable () { + if (!actuallyEnabled) return; + actuallyEnabled = false; + commandBuffer.Dispose(); + commandBuffer = null; + Camera.onPostRender -= PostRender; +#if UNITY_2023_3_OR_NEWER + UnityEngine.Rendering.RenderPipelineManager.beginContextRendering -= BeginContextRendering; +#else + UnityEngine.Rendering.RenderPipelineManager.beginFrameRendering -= BeginFrameRendering; +#endif + UnityEngine.Rendering.RenderPipelineManager.beginCameraRendering -= BeginCameraRendering; + UnityEngine.Rendering.RenderPipelineManager.endCameraRendering -= EndCameraRendering; +#if UNITY_EDITOR + EditorApplication.update -= OnEditorUpdate; + EditorApplication.playModeStateChanged -= OnPlayModeStateChanged; +#endif + // Gizmos can be null here if this GameObject was duplicated by a user in the hierarchy. + if (gizmos != null) { + Draw.builder.DiscardAndDisposeInternal(); + Draw.ingame_builder.DiscardAndDisposeInternal(); + gizmos.ClearData(); + } +#if MODULE_RENDER_PIPELINES_UNIVERSAL + if (renderPassFeature != null) { + ScriptableObject.DestroyImmediate(renderPassFeature); + renderPassFeature = null; + } +#endif + } + + // When enter play mode = reload scene & reload domain + // editor => play mode: OnDisable -> OnEnable (same object) + // play mode => editor: OnApplicationQuit (note: no OnDisable/OnEnable) + // When enter play mode = reload scene & !reload domain + // editor => play mode: Nothing + // play mode => editor: OnApplicationQuit + // When enter play mode = !reload scene & !reload domain + // editor => play mode: Nothing + // play mode => editor: OnApplicationQuit + // OnDestroy is never really called for this object (unless Unity or the game quits I quess) + + // TODO: Should run in OnDestroy. OnApplicationQuit runs BEFORE OnDestroy (which we do not want) + // private void OnApplicationQuit () { + // Debug.Log("OnApplicationQuit"); + // Draw.builder.DiscardAndDisposeInternal(); + // Draw.ingame_builder.DiscardAndDisposeInternal(); + // gizmos.ClearData(); + // Draw.builder = gizmos.GetBuiltInBuilder(false); + // Draw.ingame_builder = gizmos.GetBuiltInBuilder(true); + // } + + const float NO_DRAWING_TIMEOUT_SECS = 10; + + void OnEditorUpdate () { + framePassed = true; + CleanupIfNoCameraRendered(); + } + + void Update () { + if (actuallyEnabled) CleanupIfNoCameraRendered(); + } + + void CleanupIfNoCameraRendered () { + if (Time.frameCount > lastFrameCount + 1) { + // More than one frame old + // It is possible no camera is being rendered at all. + // Ensure we don't get any memory leaks from drawing items being queued every frame. + CheckFrameTicking(); + gizmos.PostRenderCleanup(); + + // Note: We do not always want to call the above method here + // because it is nicer to call it right after the cameras have been rendered. + // Otherwise drawing items queued before Update/OnEditorUpdate or after Update/OnEditorUpdate may end up + // in different frames (for the purposes of rendering gizmos) + } + + if (Time.realtimeSinceStartup - lastFrameTime > NO_DRAWING_TIMEOUT_SECS) { + // More than NO_DRAWING_TIMEOUT_SECS seconds since we drew the last frame. + // In the editor some script could be queuing drawing commands in e.g. EditorWindow.Update without the scene + // view or any game view being re-rendered. We discard these commands if nothing has been rendered for a long time. + Draw.builder.DiscardAndDisposeInternal(); + Draw.ingame_builder.DiscardAndDisposeInternal(); + Draw.builder = gizmos.GetBuiltInBuilder(false); + Draw.ingame_builder = gizmos.GetBuiltInBuilder(true); + lastFrameTime = Time.realtimeSinceStartup; + RemoveDestroyedGizmoDrawers(); + } + + // Avoid potential memory leak if gizmos are not being drawn + if (lastFilterFrame - Time.frameCount > 5) { + lastFilterFrame = Time.frameCount; + RemoveDestroyedGizmoDrawers(); + } + } + + internal void ExecuteCustomRenderPass (ScriptableRenderContext context, Camera camera) { + MarkerALINE.Begin(); + commandBuffer.Clear(); + SubmitFrame(camera, new DrawingData.CommandBufferWrapper { cmd = commandBuffer }, true); + context.ExecuteCommandBuffer(commandBuffer); + MarkerALINE.End(); + } + +#if MODULE_RENDER_PIPELINES_UNIVERSAL + internal void ExecuteCustomRenderGraphPass (DrawingData.CommandBufferWrapper cmd, Camera camera) { + MarkerALINE.Begin(); + SubmitFrame(camera, cmd, true); + MarkerALINE.End(); + } +#endif + + private void EndCameraRendering (ScriptableRenderContext context, Camera camera) { + if (detectedRenderPipeline == DetectedRenderPipeline.BuiltInOrCustom) { + // Execute the custom render pass after the camera has finished rendering. + // For the HDRP and URP the render pass will already have been executed. + // However for a custom render pipline we execute the rendering code here. + // This is only best effort. It's impossible to be compatible with all custom render pipelines. + // However it should work for most simple ones. + // For Unity's built-in render pipeline the EndCameraRendering method will never be called. + ExecuteCustomRenderPass(context, camera); + } + } + + void PostRender (Camera camera) { + // This method is only called when using Unity's built-in render pipeline + commandBuffer.Clear(); + SubmitFrame(camera, new DrawingData.CommandBufferWrapper { cmd = commandBuffer }, false); + MarkerCommandBuffer.Begin(); + Graphics.ExecuteCommandBuffer(commandBuffer); + MarkerCommandBuffer.End(); + } + + void CheckFrameTicking () { + MarkerFrameTick.Begin(); + if (Time.frameCount != lastFrameCount) { + framePassed = true; + lastFrameCount = Time.frameCount; + lastFrameTime = Time.realtimeSinceStartup; + previousFrameRedrawScope = gizmos.frameRedrawScope; + gizmos.frameRedrawScope = new RedrawScope(gizmos); + Draw.builder.DisposeInternal(); + Draw.ingame_builder.DisposeInternal(); + Draw.builder = gizmos.GetBuiltInBuilder(false); + Draw.ingame_builder = gizmos.GetBuiltInBuilder(true); + } else if (framePassed && Application.isPlaying) { + // Rendered frame passed without a game frame passing! + // This might mean the game is paused. + // Redraw gizmos while the game is paused. + // It might also just mean that we are rendering with multiple cameras. + previousFrameRedrawScope.Draw(); + } + + if (framePassed) { + gizmos.TickFramePreRender(); +#if UNITY_EDITOR + builtGizmos = false; +#endif + framePassed = false; + } + MarkerFrameTick.End(); + } + + internal void SubmitFrame (Camera camera, DrawingData.CommandBufferWrapper cmd, bool usingRenderPipeline) { +#if UNITY_EDITOR + bool isSceneViewCamera = SceneView.currentDrawingSceneView != null && SceneView.currentDrawingSceneView.camera == camera; +#else + bool isSceneViewCamera = false; +#endif + // Do not include when rendering to a texture unless this is a scene view camera + bool allowCameraDefault = allowRenderToRenderTextures || drawToAllCameras || camera.targetTexture == null || isSceneViewCamera; + + CheckFrameTicking(); + + Submit(camera, cmd, usingRenderPipeline, allowCameraDefault); + + gizmos.PostRenderCleanup(); + } + +#if UNITY_EDITOR + static readonly System.Reflection.MethodInfo IsGizmosAllowedForObject = typeof(UnityEditor.EditorGUIUtility).GetMethod("IsGizmosAllowedForObject", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic); + readonly System.Object[] cachedObjectParameterArray = new System.Object[1]; +#endif + + readonly Dictionary<System.Type, bool> typeToGizmosEnabled = new Dictionary<Type, bool>(); + + bool ShouldDrawGizmos (UnityEngine.Object obj) { +#if UNITY_EDITOR + // Use reflection to call EditorGUIUtility.IsGizmosAllowedForObject which is an internal method. + // It is exactly the information we want though. + // In case Unity has changed its API or something so that the method can no longer be found then just return true + cachedObjectParameterArray[0] = obj; + return IsGizmosAllowedForObject == null || (bool)IsGizmosAllowedForObject.Invoke(null, cachedObjectParameterArray); +#else + return true; +#endif + } + + static void RemoveDestroyedGizmoDrawers () { + MarkerFilterDestroyedObjects.Begin(); + int j = 0; + for (int i = 0; i < gizmoDrawers.Count; i++) { + var v = gizmoDrawers[i]; + if (v as MonoBehaviour) { + gizmoDrawers[j] = v; + j++; + } + } + gizmoDrawers.RemoveRange(j, gizmoDrawers.Count - j); + MarkerFilterDestroyedObjects.End(); + } + +#if UNITY_EDITOR + void DrawGizmos (bool usingRenderPipeline) { + GizmoContext.SetDirty(); + MarkerGizmosAllowed.Begin(); + typeToGizmosEnabled.Clear(); + + // Fill the typeToGizmosEnabled dict with info about which classes should be drawn +#if UNITY_2022_1_OR_NEWER + // In Unity 2022.1 we can use a new utility class which is more robust. + foreach (var tp in gizmoDrawerTypes) { + if (GizmoUtility.TryGetGizmoInfo(tp.Key, out var gizmoInfo)) { + typeToGizmosEnabled[tp.Key] = gizmoInfo.gizmoEnabled; + } else { + typeToGizmosEnabled[tp.Key] = true; + } + } +#else + // We take advantage of the fact that IsGizmosAllowedForObject only depends on the type of the object and if it is active and enabled + // and not the specific object instance. + // When using a render pipeline the ShouldDrawGizmos method cannot be used because it seems to occasionally crash Unity :( + // So we need these two separate cases. + if (!usingRenderPipeline) { + for (int i = gizmoDrawers.Count - 1; i >= 0; i--) { + var tp = gizmoDrawers[i].GetType(); + if (!typeToGizmosEnabled.ContainsKey(tp) && (gizmoDrawers[i] as MonoBehaviour).isActiveAndEnabled) { + typeToGizmosEnabled[tp] = ShouldDrawGizmos((UnityEngine.Object)gizmoDrawers[i]); + } + } + foreach (var tp in gizmoDrawerTypes) { + // Check if there were no enabled objects of that type at all + if (!typeToGizmosEnabled.ContainsKey(tp.Key)) typeToGizmosEnabled[tp.Key] = false; + } + } else { + foreach (var tp in gizmoDrawerTypes) { + typeToGizmosEnabled[tp.Key] = true; + } + } +#endif + + MarkerGizmosAllowed.End(); + + // Set the current frame's redraw scope to an empty scope. + // This is because gizmos are rendered every frame anyway so we never want to redraw them. + // The frame redraw scope is otherwise used when the game has been paused. + var frameRedrawScope = gizmos.frameRedrawScope; + gizmos.frameRedrawScope = default(RedrawScope); + +#if UNITY_EDITOR && UNITY_2020_1_OR_NEWER + var currentStage = StageUtility.GetCurrentStage(); + var isInNonMainStage = currentStage != StageUtility.GetMainStage(); +#endif + + // This would look nicer as a 'using' block, but built-in command builders + // cannot be disposed normally to prevent user error. + // The try-finally is equivalent to a 'using' block. + var gizmoBuilder = gizmos.GetBuiltInBuilder(); + // Replace Draw.builder with a custom one just for gizmos + var debugBuilder = Draw.builder; + MarkerDrawGizmos.Begin(); + GizmoContext.drawingGizmos = true; + try { + Draw.builder = gizmoBuilder; + if (usingRenderPipeline) { + for (int i = gizmoDrawers.Count - 1; i >= 0; i--) { + var mono = gizmoDrawers[i] as MonoBehaviour; +#if UNITY_EDITOR && UNITY_2020_1_OR_NEWER + // True if the scene is in isolation mode (e.g. focusing on a single prefab) and this object is not part of that sub-stage + var disabledDueToIsolationMode = isInNonMainStage && StageUtility.GetStage(mono.gameObject) != currentStage; +#else + var disabledDueToIsolationMode = false; +#endif +#if UNITY_2022_1_OR_NEWER + var gizmosEnabled = mono.isActiveAndEnabled && typeToGizmosEnabled[gizmoDrawers[i].GetType()]; +#else + var gizmosEnabled = mono.isActiveAndEnabled; +#endif + if (gizmosEnabled && (mono.hideFlags & HideFlags.HideInHierarchy) == 0 && !disabledDueToIsolationMode) { + try { + gizmoDrawers[i].DrawGizmos(); + } catch (System.Exception e) { + Debug.LogException(e, mono); + } + } + } + } else { + for (int i = gizmoDrawers.Count - 1; i >= 0; i--) { + var mono = gizmoDrawers[i] as MonoBehaviour; + if (mono.isActiveAndEnabled && (mono.hideFlags & HideFlags.HideInHierarchy) == 0 && typeToGizmosEnabled[gizmoDrawers[i].GetType()]) { +#if UNITY_EDITOR && UNITY_2020_1_OR_NEWER + // True if the scene is in isolation mode (e.g. focusing on a single prefab) and this object is not part of that sub-stage + var disabledDueToIsolationMode = isInNonMainStage && StageUtility.GetStage(mono.gameObject) != currentStage; +#else + var disabledDueToIsolationMode = false; +#endif + try { + if (!disabledDueToIsolationMode) gizmoDrawers[i].DrawGizmos(); + } catch (System.Exception e) { + Debug.LogException(e, mono); + } + } + } + } + } finally { + GizmoContext.drawingGizmos = false; + MarkerDrawGizmos.End(); + // Revert to the original builder + Draw.builder = debugBuilder; + gizmoBuilder.DisposeInternal(); + } + + gizmos.frameRedrawScope = frameRedrawScope; + + // Schedule jobs that may have been scheduled while drawing gizmos + JobHandle.ScheduleBatchedJobs(); + } +#endif + + /// <summary>Submit a camera for rendering.</summary> + /// <param name="allowCameraDefault">Indicates if built-in command builders and custom ones without a custom CommandBuilder.cameraTargets should render to this camera.</param> + void Submit (Camera camera, DrawingData.CommandBufferWrapper cmd, bool usingRenderPipeline, bool allowCameraDefault) { +#if UNITY_EDITOR + bool drawGizmos = Handles.ShouldRenderGizmos() || drawToAllCameras; + // Only build gizmos if a camera actually needs them. + // This is only done for the first camera that needs them each frame. + if (drawGizmos && !builtGizmos && allowCameraDefault) { + RemoveDestroyedGizmoDrawers(); + lastFilterFrame = Time.frameCount; + builtGizmos = true; + DrawGizmos(usingRenderPipeline); + } +#else + bool drawGizmos = false; +#endif + + MarkerSubmitGizmos.Begin(); + Draw.builder.DisposeInternal(); + Draw.ingame_builder.DisposeInternal(); + gizmos.Render(camera, drawGizmos, cmd, allowCameraDefault); + Draw.builder = gizmos.GetBuiltInBuilder(false); + Draw.ingame_builder = gizmos.GetBuiltInBuilder(true); + MarkerSubmitGizmos.End(); + } + + /// <summary> + /// Registers an object for gizmo drawing. + /// The DrawGizmos method on the object will be called every frame until it is destroyed (assuming there are cameras with gizmos enabled). + /// </summary> + public static void Register (IDrawGizmos item) { + var tp = item.GetType(); + + // Use reflection to figure out if the DrawGizmos method has not been overriden from the MonoBehaviourGizmos class. + // If it hasn't, then we know that this type will never draw gizmos and we can skip it. + // This improves performance by not having to keep track of objects and check if they are active and enabled every frame. + bool mayDrawGizmos; + if (gizmoDrawerTypes.TryGetValue(tp, out mayDrawGizmos)) { + } else { + var flags = System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic; + // Check for a public method first, and then an explicit interface implementation. + var m = tp.GetMethod("DrawGizmos", flags) ?? tp.GetMethod("Pathfinding.Drawing.IDrawGizmos.DrawGizmos", flags) ?? tp.GetMethod("Drawing.IDrawGizmos.DrawGizmos", flags); + if (m == null) { + throw new System.Exception("Could not find the DrawGizmos method in type " + tp.Name); + } + mayDrawGizmos = m.DeclaringType != typeof(MonoBehaviourGizmos); + gizmoDrawerTypes[tp] = mayDrawGizmos; + } + if (!mayDrawGizmos) return; + + gizmoDrawers.Add(item); + } + + /// <summary> + /// Get an empty builder for queuing drawing commands. + /// + /// <code> + /// // Create a new CommandBuilder + /// using (var draw = DrawingManager.GetBuilder()) { + /// // Use the exact same API as the global Draw class + /// draw.WireBox(Vector3.zero, Vector3.one); + /// } + /// </code> + /// See: <see cref="Drawing.CommandBuilder"/> + /// </summary> + /// <param name="renderInGame">If true, this builder will be rendered in standalone games and in the editor even if gizmos are disabled. + /// If false, it will only be rendered in the editor when gizmos are enabled.</param> + public static CommandBuilder GetBuilder(bool renderInGame = false) => instance.gizmos.GetBuilder(renderInGame); + + /// <summary> + /// Get an empty builder for queuing drawing commands. + /// + /// See: <see cref="Drawing.CommandBuilder"/> + /// </summary> + /// <param name="redrawScope">Scope for this command builder. See #GetRedrawScope.</param> + /// <param name="renderInGame">If true, this builder will be rendered in standalone games and in the editor even if gizmos are disabled. + /// If false, it will only be rendered in the editor when gizmos are enabled.</param> + public static CommandBuilder GetBuilder(RedrawScope redrawScope, bool renderInGame = false) => instance.gizmos.GetBuilder(redrawScope, renderInGame); + + /// <summary> + /// Get an empty builder for queuing drawing commands. + /// TODO: Example usage. + /// + /// See: <see cref="Drawing.CommandBuilder"/> + /// </summary> + /// <param name="hasher">Hash of whatever inputs you used to generate the drawing data.</param> + /// <param name="redrawScope">Scope for this command builder. See #GetRedrawScope.</param> + /// <param name="renderInGame">If true, this builder will be rendered in standalone games and in the editor even if gizmos are disabled.</param> + public static CommandBuilder GetBuilder(DrawingData.Hasher hasher, RedrawScope redrawScope = default, bool renderInGame = false) => instance.gizmos.GetBuilder(hasher, redrawScope, renderInGame); + + /// <summary> + /// A scope which can be used to draw things over multiple frames. + /// + /// You can use <see cref="GetBuilder(RedrawScope,bool)"/> to get a builder with a given redraw scope. + /// Everything drawn using the redraw scope will be drawn every frame until the redraw scope is disposed. + /// + /// <code> + /// private RedrawScope redrawScope; + /// + /// void Start () { + /// redrawScope = DrawingManager.GetRedrawScope(); + /// using (var builder = DrawingManager.GetBuilder(redrawScope)) { + /// builder.WireSphere(Vector3.zero, 1.0f, Color.red); + /// } + /// } + /// + /// void OnDestroy () { + /// redrawScope.Dispose(); + /// } + /// </code> + /// </summary> + /// <param name="associatedGameObject">If not null, the scope will only be drawn if gizmos for the associated GameObject are drawn. + /// This is useful in the unity editor when e.g. opening a prefab in isolation mode, to disable redraw scopes for objects outside the prefab. Has no effect in standalone builds.</param> + public static RedrawScope GetRedrawScope (GameObject associatedGameObject = null) { + var scope = new RedrawScope(instance.gizmos); + scope.DrawUntilDispose(associatedGameObject); + return scope; + } + } +} diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/DrawingManager.cs.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/DrawingManager.cs.meta new file mode 100644 index 0000000..84b4408 --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/DrawingManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9d8985ed50b5b4de0b58ae195b816e06 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 10000 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/DrawingSettings.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/DrawingSettings.cs new file mode 100644 index 0000000..7af8883 --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/DrawingSettings.cs @@ -0,0 +1,74 @@ +#pragma warning disable CS0169, CS0414 // The field 'DrawingSettings.version' is never used +using UnityEditor; +using UnityEngine; + +namespace Pathfinding.Drawing { + /// <summary>Stores ALINE project settings</summary> + public class DrawingSettings : ScriptableObject { + public const string SettingsPathCompatibility = "Assets/Settings/ALINE.asset"; + public const string SettingsName = "AstarGizmos"; + public const string SettingsPath = "Assets/Settings/Resources/" + SettingsName + ".asset"; + + /// <summary>Stores ALINE project settings</summary> + [System.Serializable] + public class Settings { + /// <summary>Opacity of lines when in front of objects</summary> + public float lineOpacity = 1.0f; + + /// <summary>Opacity of solid objects when in front of other objects</summary> + + public float solidOpacity = 0.55f; + + /// <summary>Opacity of text when in front of other objects</summary> + + public float textOpacity = 1.0f; + + /// <summary>Additional opacity multiplier of lines when behind or inside objects</summary> + + public float lineOpacityBehindObjects = 0.12f; + + /// <summary>Additional opacity multiplier of solid objects when behind or inside other objects</summary> + + public float solidOpacityBehindObjects = 0.45f; + + /// <summary>Additional opacity multiplier of text when behind or inside other objects</summary> + + public float textOpacityBehindObjects = 0.9f; + + /// <summary> + /// Resolution of curves, as a fraction of the default. + /// + /// The resolution of curves is dynamic based on the distance to the camera. + /// This setting will make the curves higher or lower resolution by a factor from the default. + /// </summary> + public float curveResolution = 1.0f; + } + + [SerializeField] + private int version; + public Settings settings; + + public static Settings DefaultSettings => new Settings(); + + public static DrawingSettings GetSettingsAsset () { +#if UNITY_EDITOR + System.IO.Directory.CreateDirectory(Application.dataPath + "/../" + System.IO.Path.GetDirectoryName(SettingsPath)); + var settings = AssetDatabase.LoadAssetAtPath<DrawingSettings>(SettingsPath); + if (settings == null && AssetDatabase.LoadAssetAtPath<DrawingSettings>(SettingsPathCompatibility) != null) { + AssetDatabase.MoveAsset(SettingsPathCompatibility, SettingsPath); + settings = AssetDatabase.LoadAssetAtPath<DrawingSettings>(SettingsPath); + } + if (settings == null) { + settings = ScriptableObject.CreateInstance<DrawingSettings>(); + settings.settings = DefaultSettings; + settings.version = 0; + AssetDatabase.CreateAsset(settings, SettingsPath); + AssetDatabase.SaveAssets(); + } +#else + var settings = Resources.Load<DrawingSettings>(SettingsName); +#endif + return settings; + } + } +} diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/DrawingSettings.cs.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/DrawingSettings.cs.meta new file mode 100644 index 0000000..4febb2c --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/DrawingSettings.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 98e3089fbc7bff2b78412546c703c554 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/DrawingUtilities.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/DrawingUtilities.cs new file mode 100644 index 0000000..8212c1d --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/DrawingUtilities.cs @@ -0,0 +1,122 @@ +using UnityEngine; +using Unity.Mathematics; +using Unity.Collections; +using System.Collections.Generic; + +namespace Pathfinding.Drawing { + /// <summary>Various high-level utilities that are useful when drawing things</summary> + public static class DrawingUtilities { + private static List<Component> componentBuffer = new List<Component>(); + + /// <summary> + /// Bounding box of a GameObject. + /// Sometimes you want to quickly draw the bounding box of an object. This is not always trivial as the object may have any number of children with colliders and renderers. + /// You can use this method to calculate the bounding box easily. + /// + /// The bounding box is calculated based on the colliders and renderers on this object and all its children. + /// + /// [Open online documentation to see images] + /// <code> + /// Draw.WireBox(DrawingUtilities.BoundsFrom(transform), Color.black); + /// </code> + /// + /// See: <see cref="BoundsFrom(Transform)"/> + /// </summary> + public static Bounds BoundsFrom (GameObject gameObject) { + return BoundsFrom(gameObject.transform); + } + + /// <summary> + /// Bounding box of a Transform. + /// Sometimes you want to quickly draw the bounding box of an object. This is not always trivial as the object may have any number of children with colliders and renderers. + /// You can use this method to calculate the bounding box easily. + /// + /// The bounding box is calculated based on the colliders and renderers on this object and all its children. + /// + /// [Open online documentation to see images] + /// <code> + /// Draw.WireBox(DrawingUtilities.BoundsFrom(transform), Color.black); + /// </code> + /// + /// See: <see cref="BoundsFrom(GameObject)"/> + /// </summary> + public static Bounds BoundsFrom (Transform transform) { + transform.gameObject.GetComponents(componentBuffer); + Bounds bounds = new Bounds(transform.position, Vector3.zero); + for (int i = 0; i < componentBuffer.Count; i++) { + var component = componentBuffer[i]; + if (component is Collider coll) bounds.Encapsulate(coll.bounds); + else if (component is Collider2D coll2D) bounds.Encapsulate(coll2D.bounds); + else if (component is MeshRenderer rend) bounds.Encapsulate(rend.bounds); + else if (component is SpriteRenderer rendSprite) bounds.Encapsulate(rendSprite.bounds); + } + componentBuffer.Clear(); + var children = transform.childCount; + for (int i = 0; i < children; i++) bounds.Encapsulate(BoundsFrom(transform.GetChild(i))); + return bounds; + } + + /// <summary> + /// Bounding box which contains all points in the list. + /// <code> + /// List<Vector3> points = new List<Vector3> { new Vector3(0, 0, 0), new Vector3(1, 0, 0), new Vector3(0, 1, 1) }; + /// Draw.WireBox(DrawingUtilities.BoundsFrom(points), Color.black); + /// </code> + /// + /// See: <see cref="BoundsFrom(Vector3"/>[]) + /// See: <see cref="BoundsFrom(NativeArray<float3>)"/> + /// </summary> + public static Bounds BoundsFrom (List<Vector3> points) { + if (points.Count == 0) throw new System.ArgumentException("At least 1 point is required"); + Vector3 mn = points[0]; + Vector3 mx = points[0]; + for (int i = 0; i < points.Count; i++) { + mn = Vector3.Min(mn, points[i]); + mx = Vector3.Max(mx, points[i]); + } + return new Bounds((mx + mn) * 0.5f, (mx - mn) * 0.5f); + } + + /// <summary> + /// Bounding box which contains all points in the array. + /// <code> + /// List<Vector3> points = new List<Vector3> { new Vector3(0, 0, 0), new Vector3(1, 0, 0), new Vector3(0, 1, 1) }; + /// Draw.WireBox(DrawingUtilities.BoundsFrom(points), Color.black); + /// </code> + /// + /// See: <see cref="BoundsFrom(List<Vector3>)"/> + /// See: <see cref="BoundsFrom(NativeArray<float3>)"/> + /// </summary> + public static Bounds BoundsFrom (Vector3[] points) { + if (points.Length == 0) throw new System.ArgumentException("At least 1 point is required"); + Vector3 mn = points[0]; + Vector3 mx = points[0]; + for (int i = 0; i < points.Length; i++) { + mn = Vector3.Min(mn, points[i]); + mx = Vector3.Max(mx, points[i]); + } + return new Bounds((mx + mn) * 0.5f, (mx - mn) * 0.5f); + } + + /// <summary> + /// Bounding box which contains all points in the array. + /// <code> + /// List<Vector3> points = new List<Vector3> { new Vector3(0, 0, 0), new Vector3(1, 0, 0), new Vector3(0, 1, 1) }; + /// Draw.WireBox(DrawingUtilities.BoundsFrom(points), Color.black); + /// </code> + /// + /// See: <see cref="BoundsFrom(List<Vector3>)"/> + /// See: <see cref="BoundsFrom(Vector3"/>[]) + /// </summary> + public static Bounds BoundsFrom (NativeArray<float3> points) { + if (points.Length == 0) throw new System.ArgumentException("At least 1 point is required"); + float3 mn = points[0]; + float3 mx = points[0]; + for (int i = 0; i < points.Length; i++) { + mn = math.min(mn, points[i]); + mx = math.max(mx, points[i]); + } + return new Bounds((mx + mn) * 0.5f, (mx - mn) * 0.5f); + } + } +} diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/DrawingUtilities.cs.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/DrawingUtilities.cs.meta new file mode 100644 index 0000000..26c9ab7 --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/DrawingUtilities.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8a836e4970215f18d8cb80d14ea06343 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Editor.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Editor.meta new file mode 100644 index 0000000..6e5f8f5 --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 101b6a55216144cd69830185b92ea408 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Editor/DrawingEditor.asmdef b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Editor/DrawingEditor.asmdef new file mode 100644 index 0000000..01266a4 --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Editor/DrawingEditor.asmdef @@ -0,0 +1,40 @@ +{ + "name": "DrawingEditor", + "rootNamespace": "", + "references": [ + "GUID:de4e6084e6d474788bb8c799d6b461eb", + "GUID:774e21169c4ac4ec8a01db9cdb98d33b", + "GUID:f4059aaf6c60a4a58a177a2609feb769" + ], + "includePlatforms": [ + "Editor" + ], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": true, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [ + "MODULE_BURST", + "MODULE_MATHEMATICS", + "MODULE_COLLECTIONS" + ], + "versionDefines": [ + { + "name": "com.unity.burst", + "expression": "1.2.1-preview", + "define": "MODULE_BURST" + }, + { + "name": "com.unity.mathematics", + "expression": "1.1.0", + "define": "MODULE_MATHEMATICS" + }, + { + "name": "com.unity.collections", + "expression": "0.4.0-preview", + "define": "MODULE_COLLECTIONS" + } + ], + "noEngineReferences": false +}
\ No newline at end of file diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Editor/DrawingEditor.asmdef.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Editor/DrawingEditor.asmdef.meta new file mode 100644 index 0000000..374821b --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Editor/DrawingEditor.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: cc297fb7ca30549a8b58bbc1a40d808a +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Editor/DrawingManagerEditor.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Editor/DrawingManagerEditor.cs new file mode 100644 index 0000000..1814e58 --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Editor/DrawingManagerEditor.cs @@ -0,0 +1,19 @@ +using UnityEngine; +using System.Collections; +using UnityEditor; + +namespace Pathfinding.Drawing { + [CustomEditor(typeof(DrawingManager))] + public class DrawingManagerEditor : Editor { + // Use this for initialization + void Start () { + } + + // Update is called once per frame + void Update () { + } + + void OnSceneGUI () { + } + } +} diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Editor/DrawingManagerEditor.cs.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Editor/DrawingManagerEditor.cs.meta new file mode 100644 index 0000000..1826527 --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Editor/DrawingManagerEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 981b658cf08ad4167af47647e3861f3c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Editor/DrawingSettingsEditor.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Editor/DrawingSettingsEditor.cs new file mode 100644 index 0000000..56acb6d --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Editor/DrawingSettingsEditor.cs @@ -0,0 +1,60 @@ +using System.Collections.Generic; +using UnityEditor; +using UnityEngine; + +namespace Pathfinding.Drawing { + /// <summary>Helper for adding project settings</summary> + static class ALINESettingsRegister { + const string PROVIDER_PATH = "Project/AstarGizmos"; + const string SETTINGS_LABEL = "A* Gizmos"; + + + [SettingsProvider] + public static SettingsProvider CreateMyCustomSettingsProvider () { + // First parameter is the path in the Settings window. + // Second parameter is the scope of this setting: it only appears in the Project Settings window. + var provider = new SettingsProvider(PROVIDER_PATH, SettingsScope.Project) { + // By default the last token of the path is used as display name if no label is provided. + label = SETTINGS_LABEL, + guiHandler = (searchContext) => + { + var settings = new SerializedObject(DrawingSettings.GetSettingsAsset()); + EditorGUILayout.HelpBox("Opacity of lines, solid objects and text drawn using ALINE. When drawing behind other objects, an additional opacity multiplier is applied.", MessageType.None); + EditorGUILayout.Separator(); + EditorGUILayout.LabelField("Lines", EditorStyles.boldLabel); + EditorGUILayout.Slider(settings.FindProperty("settings.lineOpacity"), 0, 1, new GUIContent("Opacity", "Opacity of lines when in front of objects")); + EditorGUILayout.Slider(settings.FindProperty("settings.lineOpacityBehindObjects"), 0, 1, new GUIContent("Opacity (occluded)", "Additional opacity multiplier of lines when behind or inside objects")); + EditorGUILayout.Separator(); + EditorGUILayout.LabelField("Solids", EditorStyles.boldLabel); + EditorGUILayout.Slider(settings.FindProperty("settings.solidOpacity"), 0, 1, new GUIContent("Opacity", "Opacity of solid objects when in front of other objects")); + EditorGUILayout.Slider(settings.FindProperty("settings.solidOpacityBehindObjects"), 0, 1, new GUIContent("Opacity (occluded)", "Additional opacity multiplier of solid objects when behind or inside other objects")); + EditorGUILayout.Separator(); + EditorGUILayout.LabelField("Text", EditorStyles.boldLabel); + EditorGUILayout.Slider(settings.FindProperty("settings.textOpacity"), 0, 1, new GUIContent("Opacity", "Opacity of text when in front of other objects")); + EditorGUILayout.Slider(settings.FindProperty("settings.textOpacityBehindObjects"), 0, 1, new GUIContent("Opacity (occluded)", "Additional opacity multiplier of text when behind or inside other objects")); + EditorGUILayout.Separator(); + EditorGUILayout.Slider(settings.FindProperty("settings.curveResolution"), 0.1f, 3f, new GUIContent("Curve resolution", "Higher values will make curves smoother, but also a bit slower to draw.")); + + settings.ApplyModifiedProperties(); + if (GUILayout.Button("Reset to default")) { + var def = DrawingSettings.DefaultSettings; + var current = DrawingSettings.GetSettingsAsset(); + current.settings.lineOpacity = def.lineOpacity; + current.settings.lineOpacityBehindObjects = def.lineOpacityBehindObjects; + current.settings.solidOpacity = def.solidOpacity; + current.settings.solidOpacityBehindObjects = def.solidOpacityBehindObjects; + current.settings.textOpacity = def.textOpacity; + current.settings.textOpacityBehindObjects = def.textOpacityBehindObjects; + current.settings.curveResolution = def.curveResolution; + EditorUtility.SetDirty(current); + } + }, + + // Populate the search keywords to enable smart search filtering and label highlighting: + keywords = new HashSet<string>(new[] { "Drawing", "Wire", "aline", "opacity" }) + }; + + return provider; + } + } +} diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Editor/DrawingSettingsEditor.cs.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Editor/DrawingSettingsEditor.cs.meta new file mode 100644 index 0000000..5afbd0e --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Editor/DrawingSettingsEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 82a30ddb6b38ed2f68348d601bc43a2a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/GeometryBuilder.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/GeometryBuilder.cs new file mode 100644 index 0000000..743b3bb --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/GeometryBuilder.cs @@ -0,0 +1,1152 @@ +using System; +using Unity.Collections.LowLevel.Unsafe; +using Unity.Mathematics; +using Unity.Jobs.LowLevel.Unsafe; +using UnityEngine; +using Unity.Burst; +using UnityEngine.Profiling; +using Unity.Collections; +using Unity.Jobs; + +namespace Pathfinding.Drawing { + using static DrawingData; + using static CommandBuilder; + using Pathfinding.Drawing.Text; + using Unity.Profiling; + using System.Collections.Generic; + using UnityEngine.Rendering; + + static class GeometryBuilder { + public struct CameraInfo { + public float3 cameraPosition; + public quaternion cameraRotation; + public float2 cameraDepthToPixelSize; + public bool cameraIsOrthographic; + + public CameraInfo(Camera camera) { + var tr = camera?.transform; + cameraPosition = tr != null ? (float3)tr.position : float3.zero; + cameraRotation = tr != null ? (quaternion)tr.rotation : quaternion.identity; + cameraDepthToPixelSize = (camera != null ? CameraDepthToPixelSize(camera) : 0); + cameraIsOrthographic = camera != null ? camera.orthographic : false; + } + } + + internal static unsafe JobHandle Build (DrawingData gizmos, ProcessedBuilderData.MeshBuffers* buffers, ref CameraInfo cameraInfo, JobHandle dependency) { + // Create a new builder and schedule it. + // Why is characterInfo passed as a pointer and a length instead of just a NativeArray? + // This is because passing it as a NativeArray invokes the safety system which adds some tracking to the NativeArray. + // This is normally not a problem, but we may be scheduling hundreds of jobs that use that particular NativeArray and this causes a bit of a slowdown + // in the safety checking system. Passing it as a pointer + length makes the whole scheduling code about twice as fast compared to passing it as a NativeArray. + return new GeometryBuilderJob { + buffers = buffers, + currentMatrix = Matrix4x4.identity, + currentLineWidthData = new LineWidthData { + pixels = 1, + automaticJoins = false, + }, + lineWidthMultiplier = DrawingManager.lineWidthMultiplier, + currentColor = (Color32)Color.white, + cameraPosition = cameraInfo.cameraPosition, + cameraRotation = cameraInfo.cameraRotation, + cameraDepthToPixelSize = cameraInfo.cameraDepthToPixelSize, + cameraIsOrthographic = cameraInfo.cameraIsOrthographic, + characterInfo = (SDFCharacter*)gizmos.fontData.characters.GetUnsafeReadOnlyPtr(), + characterInfoLength = gizmos.fontData.characters.Length, + maxPixelError = GeometryBuilderJob.MaxCirclePixelError / math.max(0.1f, gizmos.settingsRef.curveResolution), + }.Schedule(dependency); + } + + /// <summary> + /// Helper for determining how large a pixel is at a given depth. + /// A a distance D from the camera a pixel corresponds to roughly value.x * D + value.y world units. + /// Where value is the return value from this function. + /// </summary> + private static float2 CameraDepthToPixelSize (Camera camera) { + if (camera.orthographic) { + return new float2(0.0f, 2.0f * camera.orthographicSize / camera.pixelHeight); + } else { + return new float2(Mathf.Tan(camera.fieldOfView * Mathf.Deg2Rad * 0.5f) / (0.5f * camera.pixelHeight), 0.0f); + } + } + + private static NativeArray<T> ConvertExistingDataToNativeArray<T>(UnsafeAppendBuffer data) where T : struct { + unsafe { + var arr = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray<T>(data.Ptr, data.Length / UnsafeUtility.SizeOf<T>(), Allocator.Invalid); +#if ENABLE_UNITY_COLLECTIONS_CHECKS + NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref arr, AtomicSafetyHandle.GetTempMemoryHandle()); +#endif + return arr; + } + } + + internal static unsafe void BuildMesh (DrawingData gizmos, List<MeshWithType> meshes, ProcessedBuilderData.MeshBuffers* inputBuffers) { + if (inputBuffers->triangles.Length > 0) { + CommandBuilderSamplers.MarkerUpdateBuffer.Begin(); + var mesh = AssignMeshData<GeometryBuilderJob.Vertex>(gizmos, inputBuffers->bounds, inputBuffers->vertices, inputBuffers->triangles, MeshLayouts.MeshLayout); + meshes.Add(new MeshWithType { mesh = mesh, type = MeshType.Lines }); + CommandBuilderSamplers.MarkerUpdateBuffer.End(); + } + + if (inputBuffers->solidTriangles.Length > 0) { + var mesh = AssignMeshData<GeometryBuilderJob.Vertex>(gizmos, inputBuffers->bounds, inputBuffers->solidVertices, inputBuffers->solidTriangles, MeshLayouts.MeshLayout); + meshes.Add(new MeshWithType { mesh = mesh, type = MeshType.Solid }); + } + + if (inputBuffers->textTriangles.Length > 0) { + var mesh = AssignMeshData<GeometryBuilderJob.TextVertex>(gizmos, inputBuffers->bounds, inputBuffers->textVertices, inputBuffers->textTriangles, MeshLayouts.MeshLayoutText); + meshes.Add(new MeshWithType { mesh = mesh, type = MeshType.Text }); + } + } + + private static Mesh AssignMeshData<VertexType>(DrawingData gizmos, Bounds bounds, UnsafeAppendBuffer vertices, UnsafeAppendBuffer triangles, VertexAttributeDescriptor[] layout) where VertexType : struct { + CommandBuilderSamplers.MarkerConvert.Begin(); + var verticesView = ConvertExistingDataToNativeArray<VertexType>(vertices); + var trianglesView = ConvertExistingDataToNativeArray<int>(triangles); + CommandBuilderSamplers.MarkerConvert.End(); + var mesh = gizmos.GetMesh(verticesView.Length); + + CommandBuilderSamplers.MarkerSetLayout.Begin(); + // Resize the vertex buffer if necessary + // Note: also resized if the vertex buffer is significantly larger than necessary. + // This is because apparently when executing the command buffer Unity does something with the whole buffer for some reason (shows up as Mesh.CreateMesh in the profiler) + // TODO: This could potentially cause bad behaviour if multiple meshes are used each frame and they have differing sizes. + // We should query for meshes that already have an appropriately sized buffer. + // if (mesh.vertexCount < verticesView.Length || mesh.vertexCount > verticesView.Length * 2) { + + // } + // TODO: Use Mesh.GetVertexBuffer/Mesh.GetIndexBuffer once they stop being buggy. + // Currently they don't seem to get refreshed properly after resizing them (2022.2.0b1) + mesh.SetVertexBufferParams(math.ceilpow2(verticesView.Length), layout); + mesh.SetIndexBufferParams(math.ceilpow2(trianglesView.Length), IndexFormat.UInt32); + CommandBuilderSamplers.MarkerSetLayout.End(); + + CommandBuilderSamplers.MarkerUpdateVertices.Begin(); + // Update the mesh data + mesh.SetVertexBufferData(verticesView, 0, 0, verticesView.Length); + CommandBuilderSamplers.MarkerUpdateVertices.End(); + CommandBuilderSamplers.MarkerUpdateIndices.Begin(); + // Update the index buffer and assume all our indices are correct + mesh.SetIndexBufferData(trianglesView, 0, 0, trianglesView.Length, MeshUpdateFlags.DontValidateIndices); + CommandBuilderSamplers.MarkerUpdateIndices.End(); + + + CommandBuilderSamplers.MarkerSubmesh.Begin(); + mesh.subMeshCount = 1; + var submesh = new SubMeshDescriptor(0, trianglesView.Length, MeshTopology.Triangles) { + vertexCount = verticesView.Length, + bounds = bounds + }; + mesh.SetSubMesh(0, submesh, MeshUpdateFlags.DontRecalculateBounds | MeshUpdateFlags.DontNotifyMeshUsers); + mesh.bounds = bounds; + CommandBuilderSamplers.MarkerSubmesh.End(); + return mesh; + } + } + + /// <summary>Some static fields that need to be in a separate class because Burst doesn't support them</summary> + static class MeshLayouts { + internal static readonly VertexAttributeDescriptor[] MeshLayout = { + new VertexAttributeDescriptor(VertexAttribute.Position, VertexAttributeFormat.Float32, 3), + new VertexAttributeDescriptor(VertexAttribute.Normal, VertexAttributeFormat.Float32, 3), + new VertexAttributeDescriptor(VertexAttribute.Color, VertexAttributeFormat.UNorm8, 4), + new VertexAttributeDescriptor(VertexAttribute.TexCoord0, VertexAttributeFormat.Float32, 2), + }; + + internal static readonly VertexAttributeDescriptor[] MeshLayoutText = { + new VertexAttributeDescriptor(VertexAttribute.Position, VertexAttributeFormat.Float32, 3), + new VertexAttributeDescriptor(VertexAttribute.Color, VertexAttributeFormat.UNorm8, 4), + new VertexAttributeDescriptor(VertexAttribute.TexCoord0, VertexAttributeFormat.Float32, 2), + }; + } + + /// <summary> + /// Job to build the geometry from a stream of rendering commands. + /// + /// See: <see cref="CommandBuilder"/> + /// </summary> + // Note: Setting FloatMode to Fast causes visual artificats when drawing circles. + // I think it is because math.sin(float4) produces slightly different results + // for each component in the input. + [BurstCompile(FloatMode = FloatMode.Default)] + internal struct GeometryBuilderJob : IJob { + [NativeDisableUnsafePtrRestriction] + public unsafe ProcessedBuilderData.MeshBuffers* buffers; + + [NativeDisableUnsafePtrRestriction] + public unsafe SDFCharacter* characterInfo; + public int characterInfoLength; + + public Color32 currentColor; + public float4x4 currentMatrix; + public LineWidthData currentLineWidthData; + public float lineWidthMultiplier; + float3 minBounds; + float3 maxBounds; + public float3 cameraPosition; + public quaternion cameraRotation; + public float2 cameraDepthToPixelSize; + public float maxPixelError; + public bool cameraIsOrthographic; + + [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)] + public struct Vertex { + public float3 position; + public float3 uv2; + public Color32 color; + public float2 uv; + } + + [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)] + public struct TextVertex { + public float3 position; + public Color32 color; + public float2 uv; + } + + static unsafe void Add<T>(UnsafeAppendBuffer* buffer, T value) where T : unmanaged { + int size = UnsafeUtility.SizeOf<T>(); + // We know that the buffer has enough capacity, so we can just write to the buffer without + // having to add branches for the overflow case (like buffer->Add will do). +#if ENABLE_UNITY_COLLECTIONS_CHECKS + UnityEngine.Assertions.Assert.IsTrue(buffer->Length + size <= buffer->Capacity); +#endif + *(T*)(buffer->Ptr + buffer->Length) = value; + buffer->Length = buffer->Length + size; + } + + static unsafe void Reserve (UnsafeAppendBuffer* buffer, int size) { + var newSize = buffer->Length + size; + + if (newSize > buffer->Capacity) { + buffer->SetCapacity(math.max(newSize, buffer->Capacity * 2)); + } + } + + internal static float3 PerspectiveDivide (float4 p) { + return p.xyz * math.rcp(p.w); + } + + unsafe void AddText (System.UInt16* text, TextData textData, Color32 color) { + var pivot = PerspectiveDivide(math.mul(currentMatrix, new float4(textData.center, 1.0f))); + + AddTextInternal( + text, + pivot, + math.mul(cameraRotation, new float3(1, 0, 0)), + math.mul(cameraRotation, new float3(0, 1, 0)), + textData.alignment, + textData.sizeInPixels, + true, + textData.numCharacters, + color + ); + } + + unsafe void AddText3D (System.UInt16* text, TextData3D textData, Color32 color) { + var pivot = PerspectiveDivide(math.mul(currentMatrix, new float4(textData.center, 1.0f))); + var m = math.mul(currentMatrix, new float4x4(textData.rotation, float3.zero)); + + AddTextInternal( + text, + pivot, + m.c0.xyz, + m.c1.xyz, + textData.alignment, + textData.size, + false, + textData.numCharacters, + color + ); + } + + + unsafe void AddTextInternal (System.UInt16* text, float3 pivot, float3 right, float3 up, LabelAlignment alignment, float size, bool sizeIsInPixels, int numCharacters, Color32 color) { + var distance = math.abs(math.dot(pivot - cameraPosition, math.mul(cameraRotation, new float3(0, 0, 1)))); + var pixelSize = cameraDepthToPixelSize.x * distance + cameraDepthToPixelSize.y; + float fontWorldSize = size; + + if (sizeIsInPixels) fontWorldSize *= pixelSize; + + right *= fontWorldSize; + up *= fontWorldSize; + + // Calculate the total width (in pixels divided by fontSize) of the text + float maxWidth = 0; + float currentWidth = 0; + float numLines = 1; + + for (int i = 0; i < numCharacters; i++) { + var characterInfoIndex = text[i]; + if (characterInfoIndex == SDFLookupData.Newline) { + maxWidth = math.max(maxWidth, currentWidth); + currentWidth = 0; + numLines++; + } else { +#if ENABLE_UNITY_COLLECTIONS_CHECKS + if (characterInfoIndex >= characterInfoLength) throw new System.Exception("Invalid character. No info exists. This is a bug."); +#endif + currentWidth += characterInfo[characterInfoIndex].advance; + } + } + maxWidth = math.max(maxWidth, currentWidth); + + // Calculate the world space position of the text given the camera and text alignment + var pos = pivot; + pos -= right * maxWidth * alignment.relativePivot.x; + // Size of a character as a fraction of a whole line using the current font + const float FontCharacterFractionOfLine = 0.75f; + // Where the upper and lower parts of the text will be assuming we start to write at y=0 + var lower = 1 - numLines; + var upper = FontCharacterFractionOfLine; + var yAdjustment = math.lerp(lower, upper, alignment.relativePivot.y); + pos -= up * yAdjustment; + pos += math.mul(cameraRotation, new float3(1, 0, 0)) * (pixelSize * alignment.pixelOffset.x); + pos += math.mul(cameraRotation, new float3(0, 1, 0)) * (pixelSize * alignment.pixelOffset.y); + + var textVertices = &buffers->textVertices; + var textTriangles = &buffers->textTriangles; + + // Reserve all buffer space beforehand + Reserve(textVertices, numCharacters * VerticesPerCharacter * UnsafeUtility.SizeOf<TextVertex>()); + Reserve(textTriangles, numCharacters * TrianglesPerCharacter * UnsafeUtility.SizeOf<int>()); + + var lineStart = pos; + + for (int i = 0; i < numCharacters; i++) { + var characterInfoIndex = text[i]; + + if (characterInfoIndex == SDFLookupData.Newline) { + lineStart -= up; + pos = lineStart; + continue; + } + + // Get character rendering information from the font + SDFCharacter ch = characterInfo[characterInfoIndex]; + + int vertexIndexStart = textVertices->Length / UnsafeUtility.SizeOf<TextVertex>(); + + float3 v; + + v = pos + ch.vertexTopLeft.x * right + ch.vertexTopLeft.y * up; + minBounds = math.min(minBounds, v); + maxBounds = math.max(maxBounds, v); + Add(textVertices, new TextVertex { + position = v, + uv = ch.uvTopLeft, + color = color, + }); + + v = pos + ch.vertexTopRight.x * right + ch.vertexTopRight.y * up; + minBounds = math.min(minBounds, v); + maxBounds = math.max(maxBounds, v); + Add(textVertices, new TextVertex { + position = v, + uv = ch.uvTopRight, + color = color, + }); + + v = pos + ch.vertexBottomRight.x * right + ch.vertexBottomRight.y * up; + minBounds = math.min(minBounds, v); + maxBounds = math.max(maxBounds, v); + Add(textVertices, new TextVertex { + position = v, + uv = ch.uvBottomRight, + color = color, + }); + + v = pos + ch.vertexBottomLeft.x * right + ch.vertexBottomLeft.y * up; + minBounds = math.min(minBounds, v); + maxBounds = math.max(maxBounds, v); + Add(textVertices, new TextVertex { + position = v, + uv = ch.uvBottomLeft, + color = color, + }); + + Add(textTriangles, vertexIndexStart + 0); + Add(textTriangles, vertexIndexStart + 1); + Add(textTriangles, vertexIndexStart + 2); + + Add(textTriangles, vertexIndexStart + 0); + Add(textTriangles, vertexIndexStart + 2); + Add(textTriangles, vertexIndexStart + 3); + + // Advance character position + pos += right * ch.advance; + } + } + + float3 lastNormalizedLineDir; + float lastLineWidth; + + public const float MaxCirclePixelError = 0.5f; + + public const int VerticesPerCharacter = 4; + public const int TrianglesPerCharacter = 6; + + void AddLine (LineData line) { + // Store the line direction in the vertex. + // A line consists of 4 vertices. The line direction will be used to + // offset the vertices to create a line with a fixed pixel thickness + var a = PerspectiveDivide(math.mul(currentMatrix, new float4(line.a, 1.0f))); + var b = PerspectiveDivide(math.mul(currentMatrix, new float4(line.b, 1.0f))); + + float lineWidth = currentLineWidthData.pixels; + var normalizedLineDir = math.normalizesafe(b - a); + + if (math.any(math.isnan(normalizedLineDir))) throw new Exception("Nan line coordinates"); + if (lineWidth <= 0) { + return; + } + + // Update the bounding box + minBounds = math.min(minBounds, math.min(a, b)); + maxBounds = math.max(maxBounds, math.max(a, b)); + + unsafe { + var outlineVertices = &buffers->vertices; + + // Make sure there is enough allocated capacity for 4 more vertices + Reserve(outlineVertices, 4 * UnsafeUtility.SizeOf<Vertex>()); + + // Insert 4 vertices + // Doing it with pointers is faster, and this is the hottest + // code of the whole gizmo drawing process. + var ptr = (Vertex*)((byte*)outlineVertices->Ptr + outlineVertices->Length); + + var startLineDir = normalizedLineDir * lineWidth; + var endLineDir = normalizedLineDir * lineWidth; + + // If dot(last dir, this dir) >= 0 => use join + if (lineWidth > 1 && currentLineWidthData.automaticJoins && outlineVertices->Length > 2*UnsafeUtility.SizeOf<Vertex>()) { + // has previous vertex + Vertex* lastVertex1 = (Vertex*)(ptr - 1); + Vertex* lastVertex2 = (Vertex*)(ptr - 2); + + var cosAngle = math.dot(normalizedLineDir, lastNormalizedLineDir); + if (math.all(lastVertex2->position == a) && lastLineWidth == lineWidth && cosAngle >= -0.6f) { + // Safety: tangent cannot be 0 because cosAngle > -1 + var tangent = normalizedLineDir + lastNormalizedLineDir; + // From the law of cosines we get that + // tangent.magnitude = sqrt(2)*sqrt(1+cosAngle) + + // Create join! + // Trigonometry gives us + // joinRadius = lineWidth / (2*cos(alpha / 2)) + // Using half angle identity for cos we get + // joinRadius = lineWidth / (sqrt(2)*sqrt(1 + cos(alpha)) + // Since the tangent already has mostly the same factors we can simplify the calculation + // normalize(tangent) * joinRadius * 2 + // = tangent / (sqrt(2)*sqrt(1+cosAngle)) * joinRadius * 2 + // = tangent * lineWidth / (1 + cos(alpha) + var joinLineDir = tangent * lineWidth / (1 + cosAngle); + + startLineDir = joinLineDir; + lastVertex1->uv2 = startLineDir; + lastVertex2->uv2 = startLineDir; + } + } + + outlineVertices->Length = outlineVertices->Length + 4 * UnsafeUtility.SizeOf<Vertex>(); + *ptr++ = new Vertex { + position = a, + color = currentColor, + uv = new float2(0, 0), + uv2 = startLineDir, + }; + *ptr++ = new Vertex { + position = a, + color = currentColor, + uv = new float2(1, 0), + uv2 = startLineDir, + }; + + *ptr++ = new Vertex { + position = b, + color = currentColor, + uv = new float2(0, 1), + uv2 = endLineDir, + }; + *ptr++ = new Vertex { + position = b, + color = currentColor, + uv = new float2(1, 1), + uv2 = endLineDir, + }; + + lastNormalizedLineDir = normalizedLineDir; + lastLineWidth = lineWidth; + } + } + + /// <summary>Calculate number of steps to use for drawing a circle at the specified point and radius to get less than the specified pixel error.</summary> + internal static int CircleSteps (float3 center, float radius, float maxPixelError, ref float4x4 currentMatrix, float2 cameraDepthToPixelSize, float3 cameraPosition) { + var centerv4 = math.mul(currentMatrix, new float4(center, 1.0f)); + + if (math.abs(centerv4.w) < 0.0000001f) return 3; + var cc = PerspectiveDivide(centerv4); + // Take the maximum scale factor among the 3 axes. + // If the current matrix has a uniform scale then they are all the same. + var maxScaleFactor = math.sqrt(math.max(math.max(math.lengthsq(currentMatrix.c0.xyz), math.lengthsq(currentMatrix.c1.xyz)), math.lengthsq(currentMatrix.c2.xyz))) / centerv4.w; + var realWorldRadius = radius * maxScaleFactor; + var distance = math.length(cc - cameraPosition); + + var pixelSize = cameraDepthToPixelSize.x * distance + cameraDepthToPixelSize.y; + // realWorldRadius += pixelSize * this.currentLineWidthData.pixels * 0.5f; + var cosAngle = 1 - (maxPixelError * pixelSize) / realWorldRadius; + int steps = cosAngle < 0 ? 3 : (int)math.ceil(math.PI / (math.acos(cosAngle))); + return steps; + } + + void AddCircle (CircleData circle) { + // If the circle has a zero normal then just ignore it + if (math.all(circle.normal == 0)) return; + + circle.normal = math.normalize(circle.normal); + // Canonicalize + if (circle.normal.y < 0) circle.normal = -circle.normal; + + float3 tangent1; + if (math.all(math.abs(circle.normal - new float3(0, 1, 0)) < 0.001f)) { + // The normal was (almost) identical to (0, 1, 0) + tangent1 = new float3(0, 0, 1); + } else { + // Common case + tangent1 = math.normalizesafe(math.cross(circle.normal, new float3(0, 1, 0))); + } + + var ex = tangent1; + var ey = circle.normal; + var ez = math.cross(ey, ex); + var oldMatrix = currentMatrix; + + currentMatrix = math.mul(currentMatrix, new float4x4( + new float4(ex, 0) * circle.radius, + new float4(ey, 0) * circle.radius, + new float4(ez, 0) * circle.radius, + new float4(circle.center, 1) + )); + + AddCircle(new CircleXZData { + center = new float3(0, 0, 0), + radius = 1, + startAngle = 0, + endAngle = 2 * math.PI, + }); + + currentMatrix = oldMatrix; + } + + void AddDisc (CircleData circle) { + // If the circle has a zero normal then just ignore it + if (math.all(circle.normal == 0)) return; + + var steps = CircleSteps(circle.center, circle.radius, maxPixelError, ref currentMatrix, cameraDepthToPixelSize, cameraPosition); + + circle.normal = math.normalize(circle.normal); + float3 tangent1; + if (math.all(math.abs(circle.normal - new float3(0, 1, 0)) < 0.001f)) { + // The normal was (almost) identical to (0, 1, 0) + tangent1 = new float3(0, 0, 1); + } else { + // Common case + tangent1 = math.cross(circle.normal, new float3(0, 1, 0)); + } + + float invSteps = 1.0f / steps; + + unsafe { + var solidVertices = &buffers->solidVertices; + var solidTriangles = &buffers->solidTriangles; + Reserve(solidVertices, steps * UnsafeUtility.SizeOf<Vertex>()); + Reserve(solidTriangles, 3*(steps-2) * UnsafeUtility.SizeOf<int>()); + + var matrix = math.mul(currentMatrix, Matrix4x4.TRS(circle.center, Quaternion.LookRotation(circle.normal, tangent1), new Vector3(circle.radius, circle.radius, circle.radius))); + + var mn = minBounds; + var mx = maxBounds; + int vertexCount = solidVertices->Length / UnsafeUtility.SizeOf<Vertex>(); + + for (int i = 0; i < steps; i++) { + var t = math.lerp(0, 2*Mathf.PI, i * invSteps); + math.sincos(t, out float sin, out float cos); + + var p = PerspectiveDivide(math.mul(matrix, new float4(cos, sin, 0, 1))); + // Update the bounding box + mn = math.min(mn, p); + mx = math.max(mx, p); + + Add(solidVertices, new Vertex { + position = p, + color = currentColor, + uv = new float2(0, 0), + uv2 = new float3(0, 0, 0), + }); + } + + minBounds = mn; + maxBounds = mx; + + for (int i = 0; i < steps - 2; i++) { + Add(solidTriangles, vertexCount); + Add(solidTriangles, vertexCount + i + 1); + Add(solidTriangles, vertexCount + i + 2); + } + } + } + + void AddSphereOutline (SphereData circle) { + var centerv4 = math.mul(currentMatrix, new float4(circle.center, 1.0f)); + + if (math.abs(centerv4.w) < 0.0000001f) return; + var center = PerspectiveDivide(centerv4); + // Figure out the actual radius of the sphere after all the matrix multiplications. + // In case of a non-uniform scale, pick the largest radius + var maxScaleFactor = math.sqrt(math.max(math.max(math.lengthsq(currentMatrix.c0.xyz), math.lengthsq(currentMatrix.c1.xyz)), math.lengthsq(currentMatrix.c2.xyz))) / centerv4.w; + var realWorldRadius = circle.radius * maxScaleFactor; + + if (cameraIsOrthographic) { + var prevMatrix = this.currentMatrix; + this.currentMatrix = float4x4.identity; + AddCircle(new CircleData { + center = center, + normal = math.mul(this.cameraRotation, new float3(0, 0, 1)), + radius = realWorldRadius, + }); + this.currentMatrix = prevMatrix; + } else { + var dist = math.length(this.cameraPosition - center); + // Camera is inside the sphere, cannot draw + if (dist <= realWorldRadius) return; + + var offsetTowardsCamera = realWorldRadius * realWorldRadius / dist; + var outlineRadius = math.sqrt(realWorldRadius * realWorldRadius - offsetTowardsCamera * offsetTowardsCamera); + var normal = math.normalize(this.cameraPosition - center); + var prevMatrix = this.currentMatrix; + this.currentMatrix = float4x4.identity; + AddCircle(new CircleData { + center = center + normal * offsetTowardsCamera, + normal = normal, + radius = outlineRadius, + }); + this.currentMatrix = prevMatrix; + } + } + + void AddCircle (CircleXZData circle) { + circle.endAngle = math.clamp(circle.endAngle, circle.startAngle - Mathf.PI * 2, circle.startAngle + Mathf.PI * 2); + + unsafe { + var m = math.mul(currentMatrix, new float4x4( + new float4(circle.radius, 0, 0, 0), + new float4(0, circle.radius, 0, 0), + new float4(0, 0, circle.radius, 0), + new float4(circle.center, 1) + )); + var steps = CircleSteps(float3.zero, 1.0f, maxPixelError, ref m, cameraDepthToPixelSize, cameraPosition); + var lineWidth = currentLineWidthData.pixels; + if (lineWidth < 0) return; + + var byteSize = steps * 4 * UnsafeUtility.SizeOf<Vertex>(); + Reserve(&buffers->vertices, byteSize); + var ptr = (Vertex*)(buffers->vertices.Ptr + buffers->vertices.Length); + buffers->vertices.Length += byteSize; + math.sincos(circle.startAngle, out float sin0, out float cos0); + var prev = PerspectiveDivide(math.mul(m, new float4(cos0, 0, sin0, 1))); + var prevTangent = math.normalizesafe(math.mul(m, new float4(-sin0, 0, cos0, 0)).xyz) * lineWidth; + var invSteps = math.rcp(steps); + + for (int i = 1; i <= steps; i++) { + var t = math.lerp(circle.startAngle, circle.endAngle, i * invSteps); + math.sincos(t, out float sin, out float cos); + var next = PerspectiveDivide(math.mul(m, new float4(cos, 0, sin, 1))); + var tangent = math.normalizesafe(math.mul(m, new float4(-sin, 0, cos, 0)).xyz) * lineWidth; + *ptr++ = new Vertex { + position = prev, + color = currentColor, + uv = new float2(0, 0), + uv2 = prevTangent, + }; + *ptr++ = new Vertex { + position = prev, + color = currentColor, + uv = new float2(1, 0), + uv2 = prevTangent, + }; + *ptr++ = new Vertex { + position = next, + color = currentColor, + uv = new float2(0, 1), + uv2 = tangent, + }; + *ptr++ = new Vertex { + position = next, + color = currentColor, + uv = new float2(1, 1), + uv2 = tangent, + }; + + prev = next; + prevTangent = tangent; + } + + // Update the global bounds with the bounding box of the circle + var b0 = PerspectiveDivide(math.mul(m, new float4(-1, 0, 0, 1))); + var b1 = PerspectiveDivide(math.mul(m, new float4(0, -1, 0, 1))); + var b2 = PerspectiveDivide(math.mul(m, new float4(+1, 0, 0, 1))); + var b3 = PerspectiveDivide(math.mul(m, new float4(0, +1, 0, 1))); + minBounds = math.min(math.min(math.min(math.min(b0, b1), b2), b3), minBounds); + maxBounds = math.max(math.max(math.max(math.max(b0, b1), b2), b3), maxBounds); + } + } + + void AddDisc (CircleXZData circle) { + var steps = CircleSteps(circle.center, circle.radius, maxPixelError, ref currentMatrix, cameraDepthToPixelSize, cameraPosition); + + circle.endAngle = math.clamp(circle.endAngle, circle.startAngle - Mathf.PI * 2, circle.startAngle + Mathf.PI * 2); + + float invSteps = 1.0f / steps; + + unsafe { + var solidVertices = &buffers->solidVertices; + var solidTriangles = &buffers->solidTriangles; + Reserve(solidVertices, (2+steps) * UnsafeUtility.SizeOf<Vertex>()); + Reserve(solidTriangles, 3*steps * UnsafeUtility.SizeOf<int>()); + + var matrix = math.mul(currentMatrix, Matrix4x4.Translate(circle.center) * Matrix4x4.Scale(new Vector3(circle.radius, circle.radius, circle.radius))); + + var worldCenter = PerspectiveDivide(math.mul(matrix, new float4(0, 0, 0, 1))); + Add(solidVertices, new Vertex { + position = worldCenter, + color = currentColor, + uv = new float2(0, 0), + uv2 = new float3(0, 0, 0), + }); + + var mn = math.min(minBounds, worldCenter); + var mx = math.max(maxBounds, worldCenter); + int vertexCount = solidVertices->Length / UnsafeUtility.SizeOf<Vertex>(); + + for (int i = 0; i <= steps; i++) { + var t = math.lerp(circle.startAngle, circle.endAngle, i * invSteps); + math.sincos(t, out float sin, out float cos); + + var p = PerspectiveDivide(math.mul(matrix, new float4(cos, 0, sin, 1))); + // Update the bounding box + mn = math.min(mn, p); + mx = math.max(mx, p); + + Add(solidVertices, new Vertex { + position = p, + color = currentColor, + uv = new float2(0, 0), + uv2 = new float3(0, 0, 0), + }); + } + + minBounds = mn; + maxBounds = mx; + + for (int i = 0; i < steps; i++) { + // Center vertex + Add(solidTriangles, vertexCount - 1); + Add(solidTriangles, vertexCount + i + 0); + Add(solidTriangles, vertexCount + i + 1); + } + } + } + + void AddSolidTriangle (TriangleData triangle) { + unsafe { + var solidVertices = &buffers->solidVertices; + var solidTriangles = &buffers->solidTriangles; + Reserve(solidVertices, 3 * UnsafeUtility.SizeOf<Vertex>()); + Reserve(solidTriangles, 3 * UnsafeUtility.SizeOf<int>()); + var matrix = currentMatrix; + var a = PerspectiveDivide(math.mul(matrix, new float4(triangle.a, 1))); + var b = PerspectiveDivide(math.mul(matrix, new float4(triangle.b, 1))); + var c = PerspectiveDivide(math.mul(matrix, new float4(triangle.c, 1))); + int startVertex = solidVertices->Length / UnsafeUtility.SizeOf<Vertex>(); + + minBounds = math.min(math.min(math.min(minBounds, a), b), c); + maxBounds = math.max(math.max(math.max(maxBounds, a), b), c); + + Add(solidVertices, new Vertex { + position = a, + color = currentColor, + uv = new float2(0, 0), + uv2 = new float3(0, 0, 0), + }); + Add(solidVertices, new Vertex { + position = b, + color = currentColor, + uv = new float2(0, 0), + uv2 = new float3(0, 0, 0), + }); + Add(solidVertices, new Vertex { + position = c, + color = currentColor, + uv = new float2(0, 0), + uv2 = new float3(0, 0, 0), + }); + + Add(solidTriangles, startVertex + 0); + Add(solidTriangles, startVertex + 1); + Add(solidTriangles, startVertex + 2); + } + } + + void AddWireBox (BoxData box) { + var min = box.center - box.size * 0.5f; + var max = box.center + box.size * 0.5f; + AddLine(new LineData { a = new float3(min.x, min.y, min.z), b = new float3(max.x, min.y, min.z) }); + AddLine(new LineData { a = new float3(max.x, min.y, min.z), b = new float3(max.x, min.y, max.z) }); + AddLine(new LineData { a = new float3(max.x, min.y, max.z), b = new float3(min.x, min.y, max.z) }); + AddLine(new LineData { a = new float3(min.x, min.y, max.z), b = new float3(min.x, min.y, min.z) }); + + AddLine(new LineData { a = new float3(min.x, max.y, min.z), b = new float3(max.x, max.y, min.z) }); + AddLine(new LineData { a = new float3(max.x, max.y, min.z), b = new float3(max.x, max.y, max.z) }); + AddLine(new LineData { a = new float3(max.x, max.y, max.z), b = new float3(min.x, max.y, max.z) }); + AddLine(new LineData { a = new float3(min.x, max.y, max.z), b = new float3(min.x, max.y, min.z) }); + + AddLine(new LineData { a = new float3(min.x, min.y, min.z), b = new float3(min.x, max.y, min.z) }); + AddLine(new LineData { a = new float3(max.x, min.y, min.z), b = new float3(max.x, max.y, min.z) }); + AddLine(new LineData { a = new float3(max.x, min.y, max.z), b = new float3(max.x, max.y, max.z) }); + AddLine(new LineData { a = new float3(min.x, min.y, max.z), b = new float3(min.x, max.y, max.z) }); + } + + void AddPlane (PlaneData plane) { + var oldMatrix = currentMatrix; + + currentMatrix = math.mul(currentMatrix, float4x4.TRS(plane.center, plane.rotation, new float3(plane.size.x * 0.5f, 1, plane.size.y * 0.5f))); + + AddLine(new LineData { a = new float3(-1, 0, -1), b = new float3(1, 0, -1) }); + AddLine(new LineData { a = new float3(1, 0, -1), b = new float3(1, 0, 1) }); + AddLine(new LineData { a = new float3(1, 0, 1), b = new float3(-1, 0, 1) }); + AddLine(new LineData { a = new float3(-1, 0, 1), b = new float3(-1, 0, -1) }); + + currentMatrix = oldMatrix; + } + + internal static readonly float4[] BoxVertices = { + new float4(-1, -1, -1, 1), + new float4(-1, -1, +1, 1), + new float4(-1, +1, -1, 1), + new float4(-1, +1, +1, 1), + new float4(+1, -1, -1, 1), + new float4(+1, -1, +1, 1), + new float4(+1, +1, -1, 1), + new float4(+1, +1, +1, 1), + }; + + internal static readonly int[] BoxTriangles = { + // Bottom two triangles + 0, 1, 5, + 0, 5, 4, + + // Top + 7, 3, 2, + 7, 2, 6, + + // -X + 0, 1, 3, + 0, 3, 2, + + // +X + 4, 5, 7, + 4, 7, 6, + + // +Z + 1, 3, 7, + 1, 7, 5, + + // -Z + 0, 2, 6, + 0, 6, 4, + }; + + void AddBox (BoxData box) { + unsafe { + var solidVertices = &buffers->solidVertices; + var solidTriangles = &buffers->solidTriangles; + Reserve(solidVertices, BoxVertices.Length * UnsafeUtility.SizeOf<Vertex>()); + Reserve(solidTriangles, BoxTriangles.Length * UnsafeUtility.SizeOf<int>()); + + var scale = box.size * 0.5f; + var matrix = math.mul(currentMatrix, new float4x4( + new float4(scale.x, 0, 0, 0), + new float4(0, scale.y, 0, 0), + new float4(0, 0, scale.z, 0), + new float4(box.center, 1) + )); + + var mn = minBounds; + var mx = maxBounds; + int vertexOffset = solidVertices->Length / UnsafeUtility.SizeOf<Vertex>(); + var ptr = (Vertex*)(solidVertices->Ptr + solidVertices->Length); + for (int i = 0; i < BoxVertices.Length; i++) { + var p = PerspectiveDivide(math.mul(matrix, BoxVertices[i])); + // Update the bounding box + mn = math.min(mn, p); + mx = math.max(mx, p); + + *ptr++ = new Vertex { + position = p, + color = currentColor, + uv = new float2(0, 0), + uv2 = new float3(0, 0, 0), + }; + } + solidVertices->Length += BoxVertices.Length * UnsafeUtility.SizeOf<Vertex>(); + + minBounds = mn; + maxBounds = mx; + + var triPtr = (int*)(solidTriangles->Ptr + solidTriangles->Length); + for (int i = 0; i < BoxTriangles.Length; i++) { + *triPtr++ = vertexOffset + BoxTriangles[i]; + } + solidTriangles->Length += BoxTriangles.Length * UnsafeUtility.SizeOf<int>(); + } + } + + // AggressiveInlining because this is only called from a single location, and burst doesn't inline otherwise + [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + public void Next (ref UnsafeAppendBuffer.Reader reader, ref NativeArray<float4x4> matrixStack, ref NativeArray<Color32> colorStack, ref NativeArray<LineWidthData> lineWidthStack, ref int matrixStackSize, ref int colorStackSize, ref int lineWidthStackSize) { + var fullCmd = reader.ReadNext<Command>(); + var cmd = fullCmd & (Command)0xFF; + Color32 oldColor = default; + + if ((fullCmd & Command.PushColorInline) != 0) { + oldColor = currentColor; + currentColor = reader.ReadNext<Color32>(); + } + + switch (cmd) { + case Command.PushColor: +#if ENABLE_UNITY_COLLECTIONS_CHECKS + if (colorStackSize >= colorStack.Length) throw new System.Exception("Too deeply nested PushColor calls"); +#else + if (colorStackSize >= colorStack.Length) colorStackSize--; +#endif + colorStack[colorStackSize] = currentColor; + colorStackSize++; + currentColor = reader.ReadNext<Color32>(); + break; + case Command.PopColor: +#if ENABLE_UNITY_COLLECTIONS_CHECKS + if (colorStackSize <= 0) throw new System.Exception("PushColor and PopColor are not matched"); +#else + if (colorStackSize <= 0) break; +#endif + colorStackSize--; + currentColor = colorStack[colorStackSize]; + break; + case Command.PushMatrix: +#if ENABLE_UNITY_COLLECTIONS_CHECKS + if (matrixStackSize >= matrixStack.Length) throw new System.Exception("Too deeply nested PushMatrix calls"); +#else + if (matrixStackSize >= matrixStack.Length) matrixStackSize--; +#endif + matrixStack[matrixStackSize] = currentMatrix; + matrixStackSize++; + currentMatrix = math.mul(currentMatrix, reader.ReadNext<float4x4>()); + break; + case Command.PushSetMatrix: +#if ENABLE_UNITY_COLLECTIONS_CHECKS + if (matrixStackSize >= matrixStack.Length) throw new System.Exception("Too deeply nested PushMatrix calls"); +#else + if (matrixStackSize >= matrixStack.Length) matrixStackSize--; +#endif + matrixStack[matrixStackSize] = currentMatrix; + matrixStackSize++; + currentMatrix = reader.ReadNext<float4x4>(); + break; + case Command.PopMatrix: +#if ENABLE_UNITY_COLLECTIONS_CHECKS + if (matrixStackSize <= 0) throw new System.Exception("PushMatrix and PopMatrix are not matched"); +#else + if (matrixStackSize <= 0) break; +#endif + matrixStackSize--; + currentMatrix = matrixStack[matrixStackSize]; + break; + case Command.PushLineWidth: +#if ENABLE_UNITY_COLLECTIONS_CHECKS + if (lineWidthStackSize >= lineWidthStack.Length) throw new System.Exception("Too deeply nested PushLineWidth calls"); +#else + if (lineWidthStackSize >= lineWidthStack.Length) lineWidthStackSize--; +#endif + lineWidthStack[lineWidthStackSize] = currentLineWidthData; + lineWidthStackSize++; + currentLineWidthData = reader.ReadNext<LineWidthData>(); + currentLineWidthData.pixels *= lineWidthMultiplier; + break; + case Command.PopLineWidth: +#if ENABLE_UNITY_COLLECTIONS_CHECKS + if (lineWidthStackSize <= 0) throw new System.Exception("PushLineWidth and PopLineWidth are not matched"); +#else + if (lineWidthStackSize <= 0) break; +#endif + lineWidthStackSize--; + currentLineWidthData = lineWidthStack[lineWidthStackSize]; + break; + case Command.Line: + AddLine(reader.ReadNext<LineData>()); + break; + case Command.SphereOutline: + AddSphereOutline(reader.ReadNext<SphereData>()); + break; + case Command.CircleXZ: + AddCircle(reader.ReadNext<CircleXZData>()); + break; + case Command.Circle: + AddCircle(reader.ReadNext<CircleData>()); + break; + case Command.DiscXZ: + AddDisc(reader.ReadNext<CircleXZData>()); + break; + case Command.Disc: + AddDisc(reader.ReadNext<CircleData>()); + break; + case Command.Box: + AddBox(reader.ReadNext<BoxData>()); + break; + case Command.WirePlane: + AddPlane(reader.ReadNext<PlaneData>()); + break; + case Command.WireBox: + AddWireBox(reader.ReadNext<BoxData>()); + break; + case Command.SolidTriangle: + AddSolidTriangle(reader.ReadNext<TriangleData>()); + break; + case Command.PushPersist: + // This command does not need to be handled by the builder + reader.ReadNext<PersistData>(); + break; + case Command.PopPersist: + // This command does not need to be handled by the builder + break; + case Command.Text: + var data = reader.ReadNext<TextData>(); + unsafe { + System.UInt16* ptr = (System.UInt16*)reader.ReadNext(UnsafeUtility.SizeOf<System.UInt16>() * data.numCharacters); + AddText(ptr, data, currentColor); + } + break; + case Command.Text3D: + var data2 = reader.ReadNext<TextData3D>(); + unsafe { + System.UInt16* ptr = (System.UInt16*)reader.ReadNext(UnsafeUtility.SizeOf<System.UInt16>() * data2.numCharacters); + AddText3D(ptr, data2, currentColor); + } + break; + case Command.CaptureState: + unsafe { + buffers->capturedState.Add(new ProcessedBuilderData.CapturedState { + color = this.currentColor, + matrix = this.currentMatrix, + }); + } + break; + default: +#if ENABLE_UNITY_COLLECTIONS_CHECKS + throw new System.Exception("Unknown command"); +#else + break; +#endif + } + + if ((fullCmd & Command.PushColorInline) != 0) { + currentColor = oldColor; + } + } + + void CreateTriangles () { + // Create triangles for all lines + // A triangle consists of 3 indices + // A line (4 vertices) consists of 2 triangles, so 6 triangle indices + unsafe { + var outlineVertices = &buffers->vertices; + var outlineTriangles = &buffers->triangles; + var vertexCount = outlineVertices->Length / UnsafeUtility.SizeOf<Vertex>(); + // Each line is made out of 4 vertices + var lineCount = vertexCount / 4; + var trianglesSizeInBytes = lineCount * 6 * UnsafeUtility.SizeOf<int>(); + if (trianglesSizeInBytes >= outlineTriangles->Capacity) { + outlineTriangles->SetCapacity(math.ceilpow2(trianglesSizeInBytes)); + } + + int* ptr = (int*)outlineTriangles->Ptr; + for (int i = 0, vi = 0; i < lineCount; i++, vi += 4) { + // First triangle + *ptr++ = vi + 0; + *ptr++ = vi + 1; + *ptr++ = vi + 2; + + // Second triangle + *ptr++ = vi + 1; + *ptr++ = vi + 3; + *ptr++ = vi + 2; + } + outlineTriangles->Length = trianglesSizeInBytes; + } + } + + public const int MaxStackSize = 32; + + public void Execute () { + unsafe { + buffers->vertices.Reset(); + buffers->triangles.Reset(); + buffers->solidVertices.Reset(); + buffers->solidTriangles.Reset(); + buffers->textVertices.Reset(); + buffers->textTriangles.Reset(); + buffers->capturedState.Reset(); + } + + currentLineWidthData.pixels *= lineWidthMultiplier; + + minBounds = new float3(float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity); + maxBounds = new float3(float.NegativeInfinity, float.NegativeInfinity, float.NegativeInfinity); + + var matrixStack = new NativeArray<float4x4>(MaxStackSize, Allocator.Temp, NativeArrayOptions.UninitializedMemory); + var colorStack = new NativeArray<Color32>(MaxStackSize, Allocator.Temp, NativeArrayOptions.UninitializedMemory); + var lineWidthStack = new NativeArray<LineWidthData>(MaxStackSize, Allocator.Temp, NativeArrayOptions.UninitializedMemory); + int matrixStackSize = 0; + int colorStackSize = 0; + int lineWidthStackSize = 0; + + CommandBuilderSamplers.MarkerProcessCommands.Begin(); + unsafe { + var reader = buffers->splitterOutput.AsReader(); + while (reader.Offset < reader.Size) Next(ref reader, ref matrixStack, ref colorStack, ref lineWidthStack, ref matrixStackSize, ref colorStackSize, ref lineWidthStackSize); +#if ENABLE_UNITY_COLLECTIONS_CHECKS + if (reader.Offset != reader.Size) throw new Exception("Didn't reach the end of the buffer"); +#endif + } + CommandBuilderSamplers.MarkerProcessCommands.End(); + + CommandBuilderSamplers.MarkerCreateTriangles.Begin(); + CreateTriangles(); + CommandBuilderSamplers.MarkerCreateTriangles.End(); + + unsafe { + var outBounds = &buffers->bounds; + *outBounds = new Bounds((minBounds + maxBounds) * 0.5f, maxBounds - minBounds); + + if (math.any(math.isnan(outBounds->min)) && (buffers->vertices.Length > 0 || buffers->solidTriangles.Length > 0)) { + // Fall back to a bounding box that covers everything + *outBounds = new Bounds(Vector3.zero, new Vector3(float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity)); +#if ENABLE_UNITY_COLLECTIONS_CHECKS + throw new Exception("NaN bounds. A Draw.* command may have been given NaN coordinates."); +#endif + } + } + } + } +} diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/GeometryBuilder.cs.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/GeometryBuilder.cs.meta new file mode 100644 index 0000000..4b7c933 --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/GeometryBuilder.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f573c8457824d9c789371f94ffed6f71 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/MonoBehaviourGizmos.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/MonoBehaviourGizmos.cs new file mode 100644 index 0000000..5019118 --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/MonoBehaviourGizmos.cs @@ -0,0 +1,40 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +namespace Pathfinding.Drawing { + /// <summary> + /// Inherit from this class to draw gizmos. + /// See: getstarted (view in online documentation for working links) + /// </summary> + public abstract class MonoBehaviourGizmos : MonoBehaviour, IDrawGizmos { + public MonoBehaviourGizmos() { +#if UNITY_EDITOR + DrawingManager.Register(this); +#endif + } + + /// <summary> + /// An empty OnDrawGizmosSelected method. + /// Why an empty OnDrawGizmosSelected method? + /// This is because only objects with an OnDrawGizmos/OnDrawGizmosSelected method will show up in Unity's menu for enabling/disabling + /// the gizmos per object type (upper right corner of the scene view). So we need it here even though we don't use normal gizmos. + /// + /// By using OnDrawGizmosSelected instead of OnDrawGizmos we minimize the overhead of Unity calling this empty method. + /// </summary> + void OnDrawGizmosSelected () { + } + + /// <summary> + /// Draw gizmos for this object. + /// + /// The gizmos will be visible in the scene view, and the game view, if gizmos have been enabled. + /// + /// This method will only be called in the Unity Editor. + /// + /// See: \ref{Draw} + /// </summary> + public virtual void DrawGizmos () { + } + } +} diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/MonoBehaviourGizmos.cs.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/MonoBehaviourGizmos.cs.meta new file mode 100644 index 0000000..b648c0a --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/MonoBehaviourGizmos.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b51868c405ddc4a6eb7220be5fcad250 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/PackageTools.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/PackageTools.meta new file mode 100644 index 0000000..4639dcc --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/PackageTools.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 91729f9b2c19b9864abd2d85945af6ed +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/PackageTools/Editor.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/PackageTools/Editor.meta new file mode 100644 index 0000000..654846b --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/PackageTools/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f2bbed206b35f45109a573da43d01eaf +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/PackageTools/Editor/DependencyCheck.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/PackageTools/Editor/DependencyCheck.cs new file mode 100644 index 0000000..f070eb1 --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/PackageTools/Editor/DependencyCheck.cs @@ -0,0 +1,47 @@ +// Disable the warning: "Field 'DependencyCheck.Dependency.name' is never assigned to, and will always have its default value null" +#pragma warning disable 649 +using UnityEditor; +using System.Linq; + +namespace Pathfinding.Drawing.Util { + [InitializeOnLoad] + static class DependencyCheck { + struct Dependency { + public string name; + public string version; + } + + static DependencyCheck() { + var missingDependencies = new Dependency[] { +#if !MODULE_BURST + new Dependency { + name = "com.unity.burst", + version = "1.2.1-preview", + }, +#endif +#if !MODULE_MATHEMATICS + new Dependency { + name = "com.unity.mathematics", + version = "1.1.0", + }, +#endif +#if !MODULE_COLLECTIONS + new Dependency { + name = "com.unity.collections", + version = "0.4.0-preview", + }, +#endif + }; + + if (missingDependencies.Length > 0) { + string missing = string.Join(", ", missingDependencies.Select(p => p.name + " (" + p.version + ")")); + bool res = EditorUtility.DisplayDialog("Missing dependencies", "The packages " + missing + " are required by ALINE but they are not installed, or the installed versions are too old. Do you want to install the latest versions of the packages?", "Ok", "Cancel"); + if (res) { + foreach (var dep in missingDependencies) { + UnityEditor.PackageManager.Client.Add(dep.name); + } + } + } + } + } +} diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/PackageTools/Editor/DependencyCheck.cs.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/PackageTools/Editor/DependencyCheck.cs.meta new file mode 100644 index 0000000..40d3b5a --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/PackageTools/Editor/DependencyCheck.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ee520e93d6bcd7966baa1c0a2c932caa +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/PackageTools/Editor/PackageToolsEditor.asmdef b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/PackageTools/Editor/PackageToolsEditor.asmdef new file mode 100644 index 0000000..9bb80fc --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/PackageTools/Editor/PackageToolsEditor.asmdef @@ -0,0 +1,34 @@ +{ + "name": "DrawingPackageToolsEditor", + "rootNamespace": "", + "references": [ + "GUID:f4059aaf6c60a4a58a177a2609feb769" + ], + "includePlatforms": [ + "Editor" + ], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": true, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [ + { + "name": "com.unity.burst", + "expression": "1.2.1-preview", + "define": "MODULE_BURST" + }, + { + "name": "com.unity.mathematics", + "expression": "1.1.0", + "define": "MODULE_MATHEMATICS" + }, + { + "name": "com.unity.collections", + "expression": "0.4.0-preview", + "define": "MODULE_COLLECTIONS" + } + ], + "noEngineReferences": false +}
\ No newline at end of file diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/PackageTools/Editor/PackageToolsEditor.asmdef.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/PackageTools/Editor/PackageToolsEditor.asmdef.meta new file mode 100644 index 0000000..0074fb6 --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/PackageTools/Editor/PackageToolsEditor.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 7b1b525c1a052ef77996abb20f96d107 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Palette.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Palette.cs new file mode 100644 index 0000000..c6e9bd8 --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Palette.cs @@ -0,0 +1,124 @@ +using UnityEngine; + +namespace Pathfinding.Drawing { + /// <summary> + /// Collections of colors. + /// + /// The easiest way to use this class is to import it with a "using" statement: + /// + /// <code> + /// using Palette = Pathfinding.Drawing.Palette.Colorbrewer.Set1; + /// + /// class PaletteTest : MonoBehaviour { + /// public void Update () { + /// Draw.Line(new Vector3(0, 0, 0), new Vector3(1, 1, 1), Palette.Orange); + /// } + /// } + /// </code> + /// + /// Note: This class has relatively few color collections at the moment. More will be added in the future. + /// </summary> + public static class Palette { + /// <summary>Pure colors</summary> + public static class Pure { + public static readonly Color Yellow = new Color(1, 1, 0, 1); + public static readonly Color Clear = new Color(0, 0, 0, 0); + public static readonly Color Grey = new Color(0.5f, 0.5f, 0.5f, 1); + public static readonly Color Magenta = new Color(1, 0, 1, 1); + public static readonly Color Cyan = new Color(0, 1, 1, 1); + public static readonly Color Red = new Color(1, 0, 0, 1); + public static readonly Color Black = new Color(0, 0, 0, 1); + public static readonly Color White = new Color(1, 1, 1, 1); + public static readonly Color Blue = new Color(0, 0, 1, 1); + public static readonly Color Green = new Color(0, 1, 0, 1); + } + + /// <summary> + /// Colorbrewer colors. + /// See: http://colorbrewer2.org/ + /// </summary> + public static class Colorbrewer { + /// <summary>Set 1 - Qualitative</summary> + public static class Set1 { + public static readonly Color Red = new Color(228/255f, 26/255f, 28/255f, 1); + public static readonly Color Blue = new Color(55/255f, 126/255f, 184/255f, 1); + public static readonly Color Green = new Color(77/255f, 175/255f, 74/255f, 1); + public static readonly Color Purple = new Color(152/255f, 78/255f, 163/255f, 1); + public static readonly Color Orange = new Color(255/255f, 127/255f, 0/255f, 1); + public static readonly Color Yellow = new Color(255/255f, 255/255f, 51/255f, 1); + public static readonly Color Brown = new Color(166/255f, 86/255f, 40/255f, 1); + public static readonly Color Pink = new Color(247/255f, 129/255f, 191/255f, 1); + public static readonly Color Grey = new Color(153/255f, 153/255f, 153/255f, 1); + } + + /// <summary>Blues - Sequential</summary> + public static class Blues { + static readonly Color[] Colors = new Color[] { + new Color(43/255f, 140/255f, 190/255f), + + new Color(166/255f, 189/255f, 219/255f), + new Color(43/255f, 140/255f, 190/255f), + + new Color(236/255f, 231/255f, 242/255f), + new Color(166/255f, 189/255f, 219/255f), + new Color(43/255f, 140/255f, 190/255f), + + new Color(241/255f, 238/255f, 246/255f), + new Color(189/255f, 201/255f, 225/255f), + new Color(116/255f, 169/255f, 207/255f), + new Color(5/255f, 112/255f, 176/255f), + + new Color(241/255f, 238/255f, 246/255f), + new Color(189/255f, 201/255f, 225/255f), + new Color(116/255f, 169/255f, 207/255f), + new Color(43/255f, 140/255f, 190/255f), + new Color(4/255f, 90/255f, 141/255f), + + new Color(241/255f, 238/255f, 246/255f), + new Color(208/255f, 209/255f, 230/255f), + new Color(166/255f, 189/255f, 219/255f), + new Color(116/255f, 169/255f, 207/255f), + new Color(43/255f, 140/255f, 190/255f), + new Color(4/255f, 90/255f, 141/255f), + + new Color(241/255f, 238/255f, 246/255f), + new Color(208/255f, 209/255f, 230/255f), + new Color(166/255f, 189/255f, 219/255f), + new Color(116/255f, 169/255f, 207/255f), + new Color(54/255f, 144/255f, 192/255f), + new Color(5/255f, 112/255f, 176/255f), + new Color(3/255f, 78/255f, 123/255f), + + new Color(255/255f, 247/255f, 251/255f), + new Color(236/255f, 231/255f, 242/255f), + new Color(208/255f, 209/255f, 230/255f), + new Color(166/255f, 189/255f, 219/255f), + new Color(116/255f, 169/255f, 207/255f), + new Color(54/255f, 144/255f, 192/255f), + new Color(5/255f, 112/255f, 176/255f), + new Color(3/255f, 78/255f, 123/255f), + + new Color(255/255f, 247/255f, 251/255f), + new Color(236/255f, 231/255f, 242/255f), + new Color(208/255f, 209/255f, 230/255f), + new Color(166/255f, 189/255f, 219/255f), + new Color(116/255f, 169/255f, 207/255f), + new Color(54/255f, 144/255f, 192/255f), + new Color(5/255f, 112/255f, 176/255f), + new Color(4/255f, 90/255f, 141/255f), + new Color(2/255f, 56/255f, 88/255f), + }; + + /// <summary>Returns a color for the specified class.</summary> + /// <param name="classes">Number of classes. Must be between 1 and 9.</param> + /// <param name="index">Index of the color class. Must be between 0 and classes-1.</param> + public static Color GetColor (int classes, int index) { + if (index < 0 || index >= classes) throw new System.ArgumentOutOfRangeException("index", "Index must be less than classes and at least 0"); + if (classes <= 0 || classes > 9) throw new System.ArgumentOutOfRangeException("classes", "Only up to 9 classes are supported"); + + return Colors[(classes - 1)*classes/2 + index]; + } + } + } + } +} diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Palette.cs.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Palette.cs.meta new file mode 100644 index 0000000..2858eee --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Palette.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d6bc950098e69c91298e2e39d68b5c8c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/PersistentFilter.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/PersistentFilter.cs new file mode 100644 index 0000000..c54a5c3 --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/PersistentFilter.cs @@ -0,0 +1,123 @@ +using Unity.Collections.LowLevel.Unsafe; +using Unity.Mathematics; +using UnityEngine; +using Unity.Burst; +using Unity.Collections; +using Unity.Jobs; + +namespace Pathfinding.Drawing { + using static CommandBuilder; + + [BurstCompile] + internal struct PersistentFilterJob : IJob { + [NativeDisableUnsafePtrRestriction] + public unsafe UnsafeAppendBuffer* buffer; + public float time; + + public void Execute () { + var stackPersist = new NativeArray<bool>(GeometryBuilderJob.MaxStackSize, Allocator.Temp, NativeArrayOptions.ClearMemory); + var stackScope = new NativeArray<int>(GeometryBuilderJob.MaxStackSize, Allocator.Temp, NativeArrayOptions.ClearMemory); + + unsafe { + // Store in local variables for performance (makes it possible to use registers for a lot of fields) + var bufferPersist = *buffer; + + long writeOffset = 0; + long readOffset = 0; + bool shouldWrite = false; + int stackSize = 0; + long lastNonMetaWrite = -1; + + while (readOffset < bufferPersist.Length) { +#if ENABLE_UNITY_COLLECTIONS_CHECKS + UnityEngine.Assertions.Assert.IsTrue(readOffset + UnsafeUtility.SizeOf<Command>() <= bufferPersist.Length); +#endif + var cmd = *(Command*)((byte*)bufferPersist.Ptr + readOffset); + var cmdBit = 1 << ((int)cmd & 0xFF); + bool isMeta = (cmdBit & StreamSplitter.MetaCommands) != 0; + int size = StreamSplitter.CommandSizes[(int)cmd & 0xFF] + ((cmd & Command.PushColorInline) != 0 ? UnsafeUtility.SizeOf<Color32>() : 0); + + if ((cmd & (Command)0xFF) == Command.Text) { + // Very pretty way of reading the TextData struct right after the command label and optional Color32 + var data = *((TextData*)((byte*)bufferPersist.Ptr + readOffset + size) - 1); + // Add the size of the embedded string in the buffer + size += data.numCharacters * UnsafeUtility.SizeOf<System.UInt16>(); + } else if ((cmd & (Command)0xFF) == Command.Text3D) { + // Very pretty way of reading the TextData struct right after the command label and optional Color32 + var data = *((TextData3D*)((byte*)bufferPersist.Ptr + readOffset + size) - 1); + // Add the size of the embedded string in the buffer + size += data.numCharacters * UnsafeUtility.SizeOf<System.UInt16>(); + } + +#if ENABLE_UNITY_COLLECTIONS_CHECKS + UnityEngine.Assertions.Assert.IsTrue(readOffset + size <= bufferPersist.Length); + UnityEngine.Assertions.Assert.IsTrue(writeOffset + size <= bufferPersist.Length); +#endif + + if (shouldWrite || isMeta) { + if (!isMeta) lastNonMetaWrite = writeOffset; + if (writeOffset != readOffset) { + // We need to use memmove instead of memcpy because the source and destination regions may overlap + UnsafeUtility.MemMove((byte*)bufferPersist.Ptr + writeOffset, (byte*)bufferPersist.Ptr + readOffset, size); + } + writeOffset += size; + } + + if ((cmdBit & StreamSplitter.PushCommands) != 0) { + if ((cmd & (Command)0xFF) == Command.PushPersist) { + // Very pretty way of reading the PersistData struct right after the command label and optional Color32 + // (even though a PushColorInline command is not usually combined with PushPersist) + var data = *((PersistData*)((byte*)bufferPersist.Ptr + readOffset + size) - 1); + // Scopes only survive if this condition is true + shouldWrite = time <= data.endTime; + } + + stackScope[stackSize] = (int)(writeOffset - size); + stackPersist[stackSize] = shouldWrite; + stackSize++; + +#if ENABLE_UNITY_COLLECTIONS_CHECKS + if (stackSize >= GeometryBuilderJob.MaxStackSize) throw new System.Exception("Push commands are too deeply nested. This can happen if you have deeply nested WithMatrix or WithColor scopes."); +#else + if (stackSize >= GeometryBuilderJob.MaxStackSize) { + buffer->Length = 0; + return; + } +#endif + } else if ((cmdBit & StreamSplitter.PopCommands) != 0) { + stackSize--; +#if ENABLE_UNITY_COLLECTIONS_CHECKS + if (stackSize < 0) throw new System.Exception("Trying to issue a pop command but there is no corresponding push command"); +#else + if (stackSize < 0) { + buffer->Length = 0; + return; + } +#endif + // If a scope was pushed and later popped, but no actual draw commands were written to the buffers + // inside that scope then we erase the whole scope. + if ((int)lastNonMetaWrite < stackScope[stackSize]) { + writeOffset = (long)stackScope[stackSize]; + } + + shouldWrite = stackPersist[stackSize]; + } + + readOffset += size; + } + + bufferPersist.Length = (int)writeOffset; +#if ENABLE_UNITY_COLLECTIONS_CHECKS + if (stackSize != 0) throw new System.Exception("Inconsistent push/pop commands. Are your push and pop commands properly matched?"); +#else + if (stackSize != 0) { + buffer->Length = 0; + return; + } +#endif + + *buffer = bufferPersist; + } + } + } +} diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/PersistentFilter.cs.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/PersistentFilter.cs.meta new file mode 100644 index 0000000..3faffb7 --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/PersistentFilter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 352622dd645d47531b288c1991fab7eb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources.meta new file mode 100644 index 0000000..ce13108 --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 63cd37b8d1e6fcae7955d6b90543349e +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_common.cginc b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_common.cginc new file mode 100644 index 0000000..0064240 --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_common.cginc @@ -0,0 +1,76 @@ + +#ifdef UNITY_HDRP +#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl" +#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl" +#include "Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariables.hlsl" +#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl" + +// Unity does not define the UNITY_DECLARE_TEX2D macro when using HDRP, at least at the time of writing this. +// But luckily HDRP is only supported on platforms where separate sampler states are supported, so we can define it like this. +#if !defined(UNITY_DECLARE_TEX2D) +// This is copied from com.unity.shadergraph@14.0.6/Editor/Generation/Targets/BuiltIn/ShaderLibrary/Shim/HLSLSupportShim.hlsl +#define UNITY_DECLARE_TEX2D(tex) TEXTURE2D(tex); SAMPLER(sampler##tex) +#endif + +#if !defined(UNITY_SAMPLE_TEX2D) +#define UNITY_SAMPLE_TEX2D(tex,coord) SAMPLE_TEXTURE2D(tex, sampler##tex, coord) +#endif + +// This is not defined in HDRP either, but we do know that HDRP only supports these platforms (at least I think so...) +#define UNITY_SEPARATE_TEXTURE_SAMPLER + +#else +#include "UnityCG.cginc" + +// These exist in the render pipelines, but not in UnityCG +float4 TransformObjectToHClip(float3 x) { + return UnityObjectToClipPos(float4(x, 1.0)); +} + +half3 FastSRGBToLinear(half3 sRGB) { + return GammaToLinearSpace(sRGB); +} +#endif + +// Tranforms a direction from object to homogenous space +inline float4 UnityObjectToClipDirection(in float3 pos) { + // More efficient than computing M*VP matrix product + return mul(UNITY_MATRIX_VP, mul(UNITY_MATRIX_M, float4(pos, 0))); +} + +float lengthsq(float3 v) { + return dot(v,v); +} + +float4 ComputeScreenPos (float4 pos, float projectionSign) +{ + float4 o = pos * 0.5f; + o.xy = float2(o.x, o.y * projectionSign) + o.w; + o.zw = pos.zw; + return o; +} + + +// Converts to linear space from sRGB if linear is the current color space +inline float3 ConvertSRGBToDestinationColorSpace(float3 sRGB) { +#ifdef UNITY_COLORSPACE_GAMMA + return sRGB; +#else + return FastSRGBToLinear(sRGB); +#endif +} + +struct appdata_color { + float4 vertex : POSITION; + half4 color : COLOR; + float3 normal : NORMAL; + float2 uv : TEXCOORD0; + UNITY_VERTEX_INPUT_INSTANCE_ID +}; + +// Unity sadly does not include a bias macro for texture sampling. +#if defined(UNITY_SEPARATE_TEXTURE_SAMPLER) +#define UNITY_SAMPLE_TEX2D_BIAS(tex, uv, bias) tex.SampleBias(sampler##tex, uv, bias) +#else +#define UNITY_SAMPLE_TEX2D_BIAS(tex, uv, bias) tex2Dbias(float4(uv, 0, bias)) +#endif diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_common.cginc.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_common.cginc.meta new file mode 100644 index 0000000..51714ad --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_common.cginc.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 1b135520270114849b121a628fe61ba8 +timeCreated: 1472987846 +licenseType: Store +ShaderImporter: + defaultTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_common_line.cginc b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_common_line.cginc new file mode 100644 index 0000000..064f311 --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_common_line.cginc @@ -0,0 +1,107 @@ +#include "aline_common.cginc" + +struct LineData { + float3 start; + uint joinHintInstanceIndex; + float3 end; + float width; + float4 color; +}; + +static float2 vertexToSide[6] = { + float2(-1, -1), + float2(1, -1), + float2(1, 1), + + float2(-1, -1), + float2(1, 1), + float2(-1, 1), +}; + +struct line_v2f { + half4 col : COLOR; + noperspective float lineWidth: TEXCOORD3; + noperspective float uv : TEXCOORD4; + UNITY_VERTEX_OUTPUT_STEREO +}; + +// d = normalized distance to line +float lineAA(float d) { + d = max(min(d, 1.0), 0) * 1.116; + float v = 0.93124*d*d*d - 1.42215*d*d - 0.42715*d + 0.95316; + v /= 0.95316; + return max(v, 0); +} + + +float calculateLineAlpha(line_v2f i, float pixelWidth, float falloffTextureScreenPixels) { + float dist = abs((i.uv - 0.5)*2); + float falloffFractionOfWidth = falloffTextureScreenPixels/(pixelWidth*0.5); + float a = lineAA((abs(dist) - (1 - falloffFractionOfWidth))/falloffFractionOfWidth); + return a; +} + +line_v2f line_vert (appdata_color v, float pixelWidth, float lengthPadding, out float4 outpos : SV_POSITION) { + UNITY_SETUP_INSTANCE_ID(v); + line_v2f o; + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); + float4 Mv = TransformObjectToHClip(v.vertex.xyz); + // float4 Mv = UnityObjectToClipPos(v.vertex); + float3 n = normalize(v.normal); + float4 Mn = UnityObjectToClipDirection(n); + + // delta is the limit value of doing the calculation + // x1 = M*v + // x2 = M*(v + e*n) + // lim e->0 (x2/x2.w - x1/x1.w)/e + // Where M = UNITY_MATRIX_MVP, v = v.vertex, n = v.normal, e = a very small value + // We can calculate this limit as follows + // lim e->0 (M*(v + e*n))/(M*(v + e*n)).w - M*v/(M*v).w / e + // lim e->0 ((M*v).w*M*(v + e*n))/((M*v).w * (M*(v + e*n)).w) - M*v*(M*(v + e*n)).w/((M*(v + e*n)).w * (M*v).w) / e + // lim e->0 ((M*v).w*M*(v + e*n) - M*v*(M*(v + e*n)).w)/((M*v).w * (M*(v + e*n)).w) / e + // lim e->0 ((M*v).w*M*(v + e*n) - M*v*(M*(v + e*n)).w)/((M*v).w * (M*v).w) / e + // lim e->0 ((M*v).w*M*v + (M*v).w*e*n - M*v*(M*(v + e*n)).w)/((M*v).w * (M*v).w) / e + // lim e->0 ((M*v).w*M*v + (M*v).w*e*n - M*v*(M*v).w - M*v*(M*e*n).w)/((M*v).w * (M*v).w) / e + // lim e->0 ((M*v).w*M*e*n - M*v*(M*e*n).w)/((M*v).w * (M*v).w) / e + // lim e->0 ((M*v).w*M*n - M*v*(M*n).w)/((M*v).w * (M*v).w) + // lim e->0 M*n/(M*v).w - (M*v*(M*n).w)/((M*v).w * (M*v).w) + + // Previously the above calculation was done with just e = 0.001, however this could yield graphical artifacts + // at large coordinate values as the floating point coordinates would start to run out of precision. + // Essentially we calculate the normal of the line in screen space. + float4 delta = (Mn - Mv*Mn.w/Mv.w) / Mv.w; + + // The delta (direction of the line in screen space) needs to be normalized in pixel space. + // Otherwise it would look weird when stretched to a non-square viewport + delta.xy *= _ScreenParams.xy; + delta.xy = normalize(delta.xy); + // Handle DirectX properly. See https://docs.unity3d.com/Manual/SL-PlatformDifferences.html + float2 normalizedScreenSpaceNormal = float2(-delta.y, delta.x) * _ProjectionParams.x; + float2 screenSpaceNormal = normalizedScreenSpaceNormal / _ScreenParams.xy; + float4 sn = float4(screenSpaceNormal.x, screenSpaceNormal.y, 0, 0); + + // Left (-1) or Right (1) of the line + float side = (v.uv.x - 0.5)*2; + // Make the line wide + outpos = (Mv / Mv.w) + side*sn*pixelWidth*0.5; + + // -1 or +1 if this vertex is at the start or end of the line respectively. + float forwards = (v.uv.y - 0.5)*2; + // Add some additional length to the line (usually on the order of 0.5 px) + // to avoid occational 1 pixel holes in sequences of contiguous lines. + outpos.xy += forwards*(delta.xy / _ScreenParams.xy)*0.5*lengthPadding; + + // Multiply by w because homogeneous coordinates (it still needs to be clipped) + outpos *= Mv.w; + o.lineWidth = pixelWidth; + o.uv = v.uv.x; + return o; +} + +line_v2f line_vert_raw (appdata_color v, float4 tint, float pixelWidth, float lengthPadding, out float4 outpos) { + pixelWidth *= length(v.normal); + line_v2f o = line_vert(v, pixelWidth, lengthPadding, outpos); + o.col = v.color * tint; + o.col.rgb = ConvertSRGBToDestinationColorSpace(o.col.rgb); + return o; +}
\ No newline at end of file diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_common_line.cginc.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_common_line.cginc.meta new file mode 100644 index 0000000..3d3f3ea --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_common_line.cginc.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 605d8a659194d1a2695d7c0ecd7f1c36 +ShaderImporter: + externalObjects: {} + defaultTextures: [] + nonModifiableTextures: [] + preprocessorOverride: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_common_surface.cginc b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_common_surface.cginc new file mode 100644 index 0000000..8d44d01 --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_common_surface.cginc @@ -0,0 +1,21 @@ +#include "aline_common.cginc" + +struct v2f { + float4 pos : SV_POSITION; + float4 col : COLOR; + float2 uv : TEXCOORD0; + UNITY_VERTEX_OUTPUT_STEREO +}; + +v2f vert_base (appdata_color v, float4 tint, float scale) { + UNITY_SETUP_INSTANCE_ID(v); + v2f o; + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); + o.pos = TransformObjectToHClip(v.vertex.xyz); + + float4 worldSpace = mul(UNITY_MATRIX_M, v.vertex); + o.uv = float2 (worldSpace.x*scale,worldSpace.z*scale); + o.col = v.color * tint; + o.col.rgb = ConvertSRGBToDestinationColorSpace(o.col.rgb); + return o; +} diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_common_surface.cginc.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_common_surface.cginc.meta new file mode 100644 index 0000000..ee2358d --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_common_surface.cginc.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 74e11d375a4c443bebed3fd6b0bb0e10 +ShaderImporter: + externalObjects: {} + defaultTextures: [] + nonModifiableTextures: [] + preprocessorOverride: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_common_text.cginc b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_common_text.cginc new file mode 100644 index 0000000..026d4f1 --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_common_text.cginc @@ -0,0 +1,107 @@ +#include "aline_common.cginc" + +float4 _Color; +float4 _FadeColor; +UNITY_DECLARE_TEX2D(_MainTex); +UNITY_DECLARE_TEX2D(_FallbackTex); +float _FallbackAmount; +float _TransitionPoint; +float _MipBias; +float _GammaCorrection; + +struct vertex { + float4 pos : POSITION; + float4 color : COLOR; + float2 uv : TEXCOORD0; + UNITY_VERTEX_INPUT_INSTANCE_ID +}; + +struct v2f { + float4 col : COLOR; + float2 uv: TEXCOORD0; + UNITY_VERTEX_OUTPUT_STEREO +}; + +v2f vert_base (vertex v, float4 tint, out float4 outpos : SV_POSITION) { + UNITY_SETUP_INSTANCE_ID(v); + v2f o; + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); + o.uv = v.uv; + o.col = v.color * tint; + o.col.rgb = ConvertSRGBToDestinationColorSpace(o.col.rgb); + outpos = TransformObjectToHClip(v.pos.xyz); + return o; +} + +// float getAlpha2(float2 uv) { +// const float textureWidth = 1024; +// float pixelSize = 0.5 * length(float2(ddx(uv.x), ddy(uv.x))) * textureWidth; + +// // Depends on texture generation settings +// const float falloffPixels = 5; + +// float sample = UNITY_SAMPLE_TEX2D(_MainTex, uv).a; +// // float scale = 1.0 / fwidth(sample); +// float signedDistance1 = (0.5 - sample) * falloffPixels; +// float signedDistance2 = signedDistance1 / pixelSize; + +// return lineAA(signedDistance2 + 0.5); +// // float signedDistance = (sample - 0.5) * scale; +// // return fwidth(sample) * 10; +// // Use two different distance thresholds to get dynamically stroked text +// // float color = clamp(signedDistance + 0.5, 0.0, 1.0); +// // return color; +// } + +float getAlpha(float2 uv) { + float rawSignedDistance = UNITY_SAMPLE_TEX2D(_MainTex, uv).a; + float scale = 1.0 / fwidth(rawSignedDistance); + float thresholdedDistance = (rawSignedDistance - 0.5) * scale; + float color = clamp(thresholdedDistance + 0.5, 0.0, 1.0); + return color; +} + +// Shader modified from https://evanw.github.io/font-texture-generator/example-webgl/ +float4 frag (v2f i, float4 screenPos : VPOS) : SV_Target { + // float halfpixelSize = 0.5 * 0.5 * length(float2(ddx(i.uv.x), ddy(i.uv.x))); + // float fcolor0 = UNITY_SAMPLE_TEX2D(_FallbackTex, i.uv).a; + // float fcolor1 = UNITY_SAMPLE_TEX2D(_FallbackTex, i.uv + float2(halfpixelSize * 0.6, halfpixelSize * 0.3)).a; + // float fcolor2 = UNITY_SAMPLE_TEX2D(_FallbackTex, i.uv + float2(-halfpixelSize * 0.3, halfpixelSize * 0.6)).a; + // float fcolor3 = UNITY_SAMPLE_TEX2D(_FallbackTex, i.uv + float2(-halfpixelSize * 0.6, -halfpixelSize * 0.3)).a; + // float fcolor4 = UNITY_SAMPLE_TEX2D(_FallbackTex, i.uv + float2(halfpixelSize * 0.3, -halfpixelSize * 0.6)).a; + // float fallbackAlpha = (fcolor0 + fcolor1 + fcolor2 + fcolor3 + fcolor4) * 0.2; + // Bias the texture sampling to use a lower mipmap level. This makes the text much sharper and clearer. + float fallbackAlpha = UNITY_SAMPLE_TEX2D_BIAS(_FallbackTex, i.uv, _MipBias).a; + + // The fallback is used for small font sizes. + // Boost the alpha to make it more legible + fallbackAlpha *= 1.2; + + // Approximate size of one screen pixel in UV-space + float pixelSize = length(float2(ddx(i.uv.x), ddy(i.uv.x))); + // float pixelSize2 = length(float2(ddx(i.uv.y), ddy(i.uv.y))); + + // float color0 = getAlpha(i.uv); + // float color1 = getAlpha(i.uv + float2(halfpixelSize * 0.6, halfpixelSize * 0.3)); + // float color2 = getAlpha(i.uv + float2(-halfpixelSize * 0.3, halfpixelSize * 0.6)); + // float color3 = getAlpha(i.uv + float2(-halfpixelSize * 0.6, -halfpixelSize * 0.3)); + // float color4 = getAlpha(i.uv + float2(halfpixelSize * 0.3, -halfpixelSize * 0.6)); + // float color = (color0 + color1 + color2 + color3 + color4) * 0.2; + + float sdfAlpha = getAlpha(i.uv); + + // Transition from the SDF font to the fallback when the font's size on the screen + // starts getting smaller than the size in the texture. + float sdfTextureWidth = 1024; + // How sharp the transition from sdf to fallback is. + // A smaller value will make the transition cover a larger range of font sizes + float transitionSharpness = 10; + float blend = clamp(transitionSharpness*(_TransitionPoint*pixelSize*sdfTextureWidth - 1.0), 0, 1); + + float alpha = lerp(sdfAlpha, fallbackAlpha, blend * _FallbackAmount); + + float4 blendcolor = float4(1,1,1,1); + // blendcolor = lerp(float4(0, 1, 0, 1), float4(1, 0, 0, 1), blend); + + return blendcolor * i.col * float4(1, 1, 1, alpha); +}
\ No newline at end of file diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_common_text.cginc.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_common_text.cginc.meta new file mode 100644 index 0000000..699e27b --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_common_text.cginc.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: a4bc56dd8cf7ec1bebb90e3a60d73472 +ShaderIncludeImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_droid_sans_mono.mat b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_droid_sans_mono.mat new file mode 100644 index 0000000..4b6d156 --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_droid_sans_mono.mat @@ -0,0 +1,71 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 8 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: aline_droid_sans_mono + m_Shader: {fileID: 4800000, guid: 537f9312ab33ad434a5484e558bbbca2, type: 3} + m_Parent: {fileID: 0} + m_ModifiedSerializedProperties: 0 + m_ValidKeywords: [] + m_InvalidKeywords: [] + m_LightmapFlags: 5 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: {} + disabledShaderPasses: [] + m_LockedProperties: + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DecalTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _FallbackTex: + m_Texture: {fileID: 2800000, guid: a41cbdfe1465ffd70b661de460173296, type: 3} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _Falloff: + m_Texture: {fileID: 2800000, guid: 6267c94129d874d7bafe507aa753046a, type: 3} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 2800000, guid: cecd6d13eb22ad90fb21ac4b8b925742, type: 3} + m_Scale: {x: 2, y: 2} + m_Offset: {x: 0, y: 0} + m_Ints: [] + m_Floats: + - _FallbackAmount: 1 + - _GammaCorrection: 1.42 + - _InvFade: 1 + - _LengthPadding: 0.5 + - _MipBias: -1 + - _PixelWidth: 4.4 + - _Scale: -2.68 + - _Shininess: 0.7 + - _TransitionPoint: 1 + m_Colors: + - _Amb: {r: 0, g: 1, b: 0.33381772, a: 1} + - _Color: {r: 1, g: 1, b: 1, a: 1} + - _EmisColor: {r: 0.2, g: 0.2, b: 0.2, a: 0} + - _Emission: {r: 0, g: 0, b: 0, a: 1} + - _Emmission: {r: 0.4744898, g: 0.4744898, b: 0.4744898, a: 1} + - _FadeColor: {r: 1, g: 1, b: 1, a: 1} + - _Low: {r: 1, g: 1, b: 1, a: 0.7} + - _SpecColor: {r: 1, g: 1, b: 1, a: 1} + - _Tint: {r: 1, g: 1, b: 1, a: 1} + - _TintColor: {r: 0.5, g: 0.5, b: 0.5, a: 0.5} + m_BuildTextureStacks: [] +--- !u!1002 &2100001 +EditorExtensionImpl: + serializedVersion: 6 diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_droid_sans_mono.mat.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_droid_sans_mono.mat.meta new file mode 100644 index 0000000..e285f0b --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_droid_sans_mono.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: fb8c9e73fc8f8b86e9310790cf7e9df5 +timeCreated: 1442945121 +licenseType: Store +NativeFormatImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_fallback_font.png b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_fallback_font.png Binary files differnew file mode 100644 index 0000000..1aa41eb --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_fallback_font.png diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_fallback_font.png.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_fallback_font.png.meta new file mode 100644 index 0000000..07c2878 --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_fallback_font.png.meta @@ -0,0 +1,134 @@ +fileFormatVersion: 2 +guid: a41cbdfe1465ffd70b661de460173296 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + sRGBTexture: 0 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 5 + mipMapFadeDistanceEnd: 8 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + flipGreenChannel: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMasterTextureLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 2 + aniso: 8 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 1 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 2 + alphaIsTransparency: 0 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 1 + swizzle: 50462976 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 1024 + resizeAlgorithm: 0 + textureFormat: 1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: 1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Server + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: 1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: WebGL + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: 1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + pSDRemoveMatte: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_font.png b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_font.png Binary files differnew file mode 100644 index 0000000..8fa9b54 --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_font.png diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_font.png.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_font.png.meta new file mode 100644 index 0000000..d9c4c17 --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_font.png.meta @@ -0,0 +1,106 @@ +fileFormatVersion: 2 +guid: cecd6d13eb22ad90fb21ac4b8b925742 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + sRGBTexture: 0 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 10 + mipBias: -100 + wrapU: 1 + wrapV: 1 + wrapW: 1 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 2 + alphaIsTransparency: 0 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + singleChannelComponent: 0 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 1 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: 1 + textureCompression: 3 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: 1 + textureCompression: 3 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_font.ttf b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_font.ttf Binary files differnew file mode 100644 index 0000000..6e79dad --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_font.ttf diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_font.ttf.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_font.ttf.meta new file mode 100644 index 0000000..9784beb --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_font.ttf.meta @@ -0,0 +1,23 @@ +fileFormatVersion: 2 +guid: de5743d7052e68e1b968ac7e8398c2f8 +TrueTypeFontImporter: + externalObjects: {} + serializedVersion: 4 + fontSize: 16 + forceTextureCase: -2 + characterSpacing: 0 + characterPadding: 1 + includeFontData: 1 + fontName: Droid Sans Mono + fontNames: + - Droid Sans Mono + fallbackFontReferences: + - {fileID: 12800000, guid: 21fc0bad37ce24a57aa83faa292e340e, type: 3} + customCharacters: + fontRenderingMode: 0 + ascentCalculationMode: 1 + useLegacyBoundsCalculation: 0 + shouldRoundAdvanceValue: 1 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_outline.mat b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_outline.mat new file mode 100644 index 0000000..03960f6 --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_outline.mat @@ -0,0 +1,58 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 6 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: aline_outline + m_Shader: {fileID: 4800000, guid: 6c78a6dd468954643a87ebf0014bc305, type: 3} + m_ShaderKeywords: + m_LightmapFlags: 5 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: {} + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DecalTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _Falloff: + m_Texture: {fileID: 2800000, guid: 6267c94129d874d7bafe507aa753046a, type: 3} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 2800000, guid: 0ff27a4aaa0ce4d1ba6a39ce3c868a76, type: 3} + m_Scale: {x: 2, y: 2} + m_Offset: {x: 0, y: 0} + m_Floats: + - _InvFade: 1 + - _LengthPadding: 0.5 + - _PixelWidth: 4.4 + - _Scale: -2.68 + - _Shininess: 0.7 + m_Colors: + - _Amb: {r: 0, g: 1, b: 0.33381772, a: 1} + - _Color: {r: 1, g: 1, b: 1, a: 1} + - _EmisColor: {r: 0.2, g: 0.2, b: 0.2, a: 0} + - _Emission: {r: 0, g: 0, b: 0, a: 1} + - _Emmission: {r: 0.4744898, g: 0.4744898, b: 0.4744898, a: 1} + - _FadeColor: {r: 0.7075472, g: 0.7075472, b: 0.7075472, a: 0.12156863} + - _Low: {r: 1, g: 1, b: 1, a: 0.7} + - _SpecColor: {r: 1, g: 1, b: 1, a: 1} + - _Tint: {r: 1, g: 1, b: 1, a: 1} + - _TintColor: {r: 0.5, g: 0.5, b: 0.5, a: 0.5} + m_BuildTextureStacks: [] +--- !u!1002 &2100001 +EditorExtensionImpl: + serializedVersion: 6 diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_outline.mat.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_outline.mat.meta new file mode 100644 index 0000000..9cf2bee --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_outline.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 91035448860ba4e708919485c73f7edc +timeCreated: 1442945121 +licenseType: Store +NativeFormatImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_outline.shader b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_outline.shader new file mode 100644 index 0000000..b95a6f8 --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_outline.shader @@ -0,0 +1,165 @@ +Shader "Hidden/ALINE/Outline" { + Properties { + _Color ("Main Color", Color) = (1,1,1,0.5) + _FadeColor ("Fade Color", Color) = (1,1,1,0.3) + _PixelWidth ("Width (px)", Float) = 4 + _LengthPadding ("Length Padding (px)", Float) = 0 + } + + HLSLINCLUDE + float4 _Color; + float4 _FadeColor; + float _PixelWidth; + float _LengthPadding; + + // Number of screen pixels that the _Falloff texture corresponds to + static const float FalloffTextureScreenPixels = 2; + + #pragma vertex vert + #pragma fragment frag + ENDHLSL + + // First subshader is for the HighDefinitionRenderPipeline. + // The shader contents are identical except that it defines UNTIY_HDRP. + SubShader { + PackageRequirements { + "com.unity.render-pipelines.high-definition": "0.1" + } + Blend SrcAlpha OneMinusSrcAlpha + ZWrite Off + Offset -3, -50 + Tags { "IgnoreProjector"="True" "RenderType"="Overlay" "RenderPipeline"="HighDefinitionRenderPipeline" } + // With line joins some triangles can actually end up backwards, so disable culling + Cull Off + + // Render behind objects + Pass { + ZTest Greater + + HLSLPROGRAM + #define UNITY_HDRP + #include "aline_common_line.cginc" + + line_v2f vert (appdata_color v, out float4 outpos : SV_POSITION) { + return line_vert_raw(v, _Color * _FadeColor, _PixelWidth, _LengthPadding, outpos); + } + + half4 frag (line_v2f i, float4 screenPos : VPOS) : COLOR { + return i.col * float4(1,1,1, calculateLineAlpha(i, i.lineWidth, FalloffTextureScreenPixels)); + } + ENDHLSL + } + + // First pass writes to the Z buffer where the lines have a pretty high opacity + Pass { + ZTest LEqual + ZWrite On + ColorMask 0 + + HLSLPROGRAM + #define UNITY_HDRP + #include "aline_common_line.cginc" + + line_v2f vert (appdata_color v, out float4 outpos : SV_POSITION) { + line_v2f o = line_vert_raw(v, float4(1,1,1,1), _PixelWidth, _LengthPadding, outpos); + o.col = float4(1,1,1,1); + return o; + } + + half4 frag (line_v2f i, float4 screenPos : VPOS) : SV_Target { + float a = calculateLineAlpha(i, i.lineWidth, FalloffTextureScreenPixels); + if (a < 0.7) discard; + return float4(1,1,1,a); + } + ENDHLSL + } + + // Render in front of objects + Pass { + ZTest LEqual + + HLSLPROGRAM + #define UNITY_HDRP + #include "aline_common_line.cginc" + + line_v2f vert (appdata_color v, out float4 outpos : SV_POSITION) { + return line_vert_raw(v, _Color, _PixelWidth, _LengthPadding, outpos); + } + + half4 frag (line_v2f i, float4 screenPos : VPOS) : SV_Target { + UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(i); + return i.col * float4(1,1,1, calculateLineAlpha(i, i.lineWidth, FalloffTextureScreenPixels)); + } + ENDHLSL + } + } + + + SubShader { + Blend SrcAlpha OneMinusSrcAlpha + ZWrite Off + Offset -3, -50 + Tags { "IgnoreProjector"="True" "RenderType"="Overlay" } + // With line joins some triangles can actually end up backwards, so disable culling + Cull Off + + // Render behind objects + Pass { + ZTest Greater + + HLSLPROGRAM + #include "aline_common_line.cginc" + + line_v2f vert (appdata_color v, out float4 outpos : SV_POSITION) { + return line_vert_raw(v, _Color * _FadeColor, _PixelWidth, _LengthPadding, outpos); + } + + half4 frag (line_v2f i, float4 screenPos : VPOS) : SV_Target { + return i.col * float4(1,1,1, calculateLineAlpha(i, i.lineWidth, FalloffTextureScreenPixels)); + } + ENDHLSL + } + + // First pass writes to the Z buffer where the lines have a pretty high opacity + Pass { + ZTest LEqual + ZWrite On + ColorMask 0 + + HLSLPROGRAM + #include "aline_common_line.cginc" + + line_v2f vert (appdata_color v, out float4 outpos : SV_POSITION) { + line_v2f o = line_vert_raw(v, float4(1,1,1,1), _PixelWidth, _LengthPadding, outpos); + o.col = float4(1,1,1,1); + return o; + } + + half4 frag (line_v2f i, float4 screenPos : VPOS) : SV_Target { + float a = calculateLineAlpha(i, i.lineWidth, FalloffTextureScreenPixels); + if (a < 0.7) discard; + return float4(1,1,1,a); + } + ENDHLSL + } + + // Render in front of objects + Pass { + ZTest LEqual + + HLSLPROGRAM + #include "aline_common_line.cginc" + + line_v2f vert (appdata_color v, out float4 outpos : SV_POSITION) { + return line_vert_raw(v, _Color, _PixelWidth, _LengthPadding, outpos); + } + + half4 frag (line_v2f i, float4 screenPos : VPOS) : SV_Target { + UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(i); + return i.col * float4(1,1,1, calculateLineAlpha(i, i.lineWidth, FalloffTextureScreenPixels)); + } + ENDHLSL + } + } + Fallback Off +} diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_outline.shader.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_outline.shader.meta new file mode 100644 index 0000000..212dc4a --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_outline.shader.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 6c78a6dd468954643a87ebf0014bc305 +timeCreated: 1442945130 +licenseType: Store +ShaderImporter: + defaultTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_surface.mat b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_surface.mat new file mode 100644 index 0000000..a879e27 --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_surface.mat @@ -0,0 +1,52 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 6 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: aline_surface + m_Shader: {fileID: 4800000, guid: 6dad35c0b62e44c26ab244ad80deee1a, type: 3} + m_ShaderKeywords: + m_LightmapFlags: 5 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: {} + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DecalTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 0} + m_Scale: {x: 2, y: 2} + m_Offset: {x: 0, y: 0} + m_Floats: + - _InvFade: 1 + - _Scale: -2.68 + - _Shininess: 0.7 + m_Colors: + - _Amb: {r: 0, g: 1, b: 0.33381772, a: 1} + - _Color: {r: 1, g: 1, b: 1, a: 0.551} + - _EmisColor: {r: 0.2, g: 0.2, b: 0.2, a: 0} + - _Emission: {r: 0, g: 0, b: 0, a: 1} + - _Emmission: {r: 0.4744898, g: 0.4744898, b: 0.4744898, a: 1} + - _FadeColor: {r: 0.6568808, g: 0.6568808, b: 0.6568808, a: 0.448} + - _Low: {r: 1, g: 1, b: 1, a: 0.7} + - _SpecColor: {r: 1, g: 1, b: 1, a: 1} + - _Tint: {r: 1, g: 1, b: 1, a: 1} + - _TintColor: {r: 0.5, g: 0.5, b: 0.5, a: 0.5} + m_BuildTextureStacks: [] +--- !u!1002 &2100001 +EditorExtensionImpl: + serializedVersion: 6 diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_surface.mat.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_surface.mat.meta new file mode 100644 index 0000000..c96943f --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_surface.mat.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 5ce51318bbfb1466188b929a68a6bd3a diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_surface.shader b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_surface.shader new file mode 100644 index 0000000..37eec81 --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_surface.shader @@ -0,0 +1,174 @@ +Shader "Hidden/ALINE/Surface" { + Properties { + _Color ("Main Color", Color) = (1,1,1,0.5) + _MainTex ("Texture", 2D) = "white" { } + _Scale ("Scale", float) = 1 + _FadeColor ("Fade Color", Color) = (1,1,1,0.3) + } + + HLSLINCLUDE + float4 _MainTex_ST; + float4 _Color; + float4 _FadeColor; + float _Scale; + + #pragma vertex vert + #pragma fragment frag + ENDHLSL + + // First subshader is for the HighDefinitionRenderPipeline. + // The shader contents are identical except that it defines UNTIY_HDRP. + SubShader { + PackageRequirements { + "com.unity.render-pipelines.high-definition": "0.1" + } + Tags {"Queue"="Transparent+1" "IgnoreProjector"="True" "RenderType"="Transparent" "RenderPipeline"="HighDefinitionRenderPipeline"} + LOD 200 + Offset -2, -20 + Cull Off + + Pass { + // Z-write further back to avoid lines drawn at the same z-depth to partially clip the surface + Offset 0, 0 + ZWrite On + ColorMask 0 + + HLSLPROGRAM + #define UNITY_HDRP + #include "aline_common_surface.cginc" + + UNITY_DECLARE_TEX2D(_MainTex); + + v2f vert (appdata_color v) { + return vert_base(v, _Color, _Scale); + } + + float4 frag (v2f i) : SV_Target { + if (i.col.a < 0.3) discard; + return float4(1,1,1,1); + } + ENDHLSL + } + + + // Render behind + Pass { + ZWrite Off + ZTest Greater + Blend SrcAlpha OneMinusSrcAlpha + + HLSLPROGRAM + #define UNITY_HDRP + #include "aline_common_surface.cginc" + + UNITY_DECLARE_TEX2D(_MainTex); + + v2f vert (appdata_color v) { + return vert_base(v, _Color * _FadeColor, _Scale); + } + + float4 frag (v2f i) : SV_Target { + return UNITY_SAMPLE_TEX2D(_MainTex, i.uv) * i.col; + } + ENDHLSL + + } + + // Render in front + Pass { + ZWrite Off + ZTest LEqual + Blend SrcAlpha OneMinusSrcAlpha + + HLSLPROGRAM + #define UNITY_HDRP + #include "aline_common_surface.cginc" + + UNITY_DECLARE_TEX2D(_MainTex); + + v2f vert (appdata_color v) { + return vert_base(v, _Color, _Scale); + } + + float4 frag (v2f i) : SV_Target { + return UNITY_SAMPLE_TEX2D(_MainTex, i.uv) * i.col; + } + ENDHLSL + } + } + + SubShader { + Tags {"Queue"="Transparent+1" "IgnoreProjector"="True" "RenderType"="Transparent"} + LOD 200 + Offset -2, -20 + Cull Off + + Pass { + // Z-write further back to avoid lines drawn at the same z-depth to partially clip the surface + Offset 0, 0 + ZWrite On + ColorMask 0 + + HLSLPROGRAM + #include "aline_common_surface.cginc" + + UNITY_DECLARE_TEX2D(_MainTex); + + v2f vert (appdata_color v) { + return vert_base(v, _Color, _Scale); + } + + float4 frag (v2f i) : SV_Target { + if (i.col.a < 0.3) discard; + return float4(1,1,1,1); + } + ENDHLSL + } + + + // Render behind + Pass { + ZWrite Off + ZTest Greater + Blend SrcAlpha OneMinusSrcAlpha + + HLSLPROGRAM + #include "aline_common_surface.cginc" + + UNITY_DECLARE_TEX2D(_MainTex); + + v2f vert (appdata_color v) { + return vert_base(v, _Color * _FadeColor, _Scale); + } + + float4 frag (v2f i) : SV_Target { + return UNITY_SAMPLE_TEX2D(_MainTex, i.uv) * i.col; + } + ENDHLSL + + } + + // Render in front + Pass { + ZWrite Off + ZTest LEqual + Blend SrcAlpha OneMinusSrcAlpha + + HLSLPROGRAM + #include "aline_common_surface.cginc" + + UNITY_DECLARE_TEX2D(_MainTex); + + v2f vert (appdata_color v) { + return vert_base(v, _Color, _Scale); + } + + float4 frag (v2f i) : SV_Target { + return UNITY_SAMPLE_TEX2D(_MainTex, i.uv) * i.col; + } + ENDHLSL + } + } + + Fallback Off +} diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_surface.shader.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_surface.shader.meta new file mode 100644 index 0000000..52f87d2 --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_surface.shader.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 6dad35c0b62e44c26ab244ad80deee1a diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_text.shader b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_text.shader new file mode 100644 index 0000000..d308e80 --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_text.shader @@ -0,0 +1,88 @@ +Shader "Hidden/ALINE/Font" { + Properties { + _Color ("Main Color", Color) = (1,1,1,0.5) + _FadeColor ("Fade Color", Color) = (1,1,1,0.3) + _MainTex ("Texture", 2D) = "white" {} + _FallbackTex ("Fallback Texture", 2D) = "white" {} + _FallbackAmount ("Fallback Amount", Range(0,1)) = 1.0 + _TransitionPoint ("Transition Point", Range(0,5)) = 0.6 + _MipBias ("Mip Bias", Range(-2,0)) = -1 + _GammaCorrection ("Gamma Correction", Range(0,2)) = 1 + } + + // First subshader is for the HighDefinitionRenderPipeline. + // The shader contents are identical except that it defines UNTIY_HDRP. + SubShader { + PackageRequirements { + "com.unity.render-pipelines.high-definition": "0.1" + } + Blend SrcAlpha OneMinusSrcAlpha + ZWrite Off + Offset -3, -50 + Cull Off + Tags { "IgnoreProjector"="True" "RenderType"="Overlay" "RenderPipeline"="HighDefinitionRenderPipeline"} + + Pass { + ZTest Greater + + HLSLPROGRAM + #pragma vertex vert + #pragma fragment frag + #define UNITY_HDRP + #include "aline_common_text.cginc" + v2f vert (vertex v, out float4 outpos : SV_POSITION) { + return vert_base(v, _Color * _FadeColor, outpos); + } + ENDHLSL + } + + Pass { + ZTest LEqual + + HLSLPROGRAM + #pragma vertex vert + #pragma fragment frag + #define UNITY_HDRP + #include "aline_common_text.cginc" + v2f vert (vertex v, out float4 outpos : SV_POSITION) { + return vert_base(v, _Color, outpos); + } + ENDHLSL + } + } + + SubShader { + Blend SrcAlpha OneMinusSrcAlpha + ZWrite Off + Offset -3, -50 + Cull Off + Tags { "IgnoreProjector"="True" "RenderType"="Overlay" } + + Pass { + ZTest Greater + + HLSLPROGRAM + #pragma vertex vert + #pragma fragment frag + #include "aline_common_text.cginc" + v2f vert (vertex v, out float4 outpos : SV_POSITION) { + return vert_base(v, _Color * _FadeColor, outpos); + } + ENDHLSL + } + + Pass { + ZTest LEqual + + HLSLPROGRAM + #pragma vertex vert + #pragma fragment frag + #include "aline_common_text.cginc" + v2f vert (vertex v, out float4 outpos : SV_POSITION) { + return vert_base(v, _Color, outpos); + } + ENDHLSL + } + } + Fallback Off +} diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_text.shader.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_text.shader.meta new file mode 100644 index 0000000..28b9a8c --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_text.shader.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 537f9312ab33ad434a5484e558bbbca2 +timeCreated: 1442945130 +licenseType: Store +ShaderImporter: + defaultTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/SDFFont.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/SDFFont.cs new file mode 100644 index 0000000..731d4a0 --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/SDFFont.cs @@ -0,0 +1,230 @@ +using System.Collections.Generic; +using UnityEngine; +using Unity.Collections; +using Unity.Mathematics; + +namespace Pathfinding.Drawing { + namespace Text { + /// <summary>Represents a single character in a font texture</summary> + internal struct SDFCharacter { + public char codePoint; + float2 uvtopleft, uvbottomright; + float2 vtopleft, vbottomright; + public float advance; + + public float2 uvTopLeft => uvtopleft; + public float2 uvTopRight => new float2(uvbottomright.x, uvtopleft.y); + public float2 uvBottomLeft => new float2(uvtopleft.x, uvbottomright.y); + public float2 uvBottomRight => uvbottomright; + + public float2 vertexTopLeft => vtopleft; + public float2 vertexTopRight => new float2(vbottomright.x, vtopleft.y); + public float2 vertexBottomLeft => new float2(vtopleft.x, vbottomright.y); + public float2 vertexBottomRight => vbottomright; + + public SDFCharacter(char codePoint, int x, int y, int width, int height, int originX, int originY, int advance, int textureWidth, int textureHeight, float defaultSize) { + float2 texSize = new float2(textureWidth, textureHeight); + + this.codePoint = codePoint; + var uvMin = new float2(x, y) / texSize; + var uvMax = new float2(x + width, y + height) / texSize; + + // UV (0,0) is at the bottom-left in Unity + uvtopleft = new float2(uvMin.x, 1.0f - uvMin.y); + uvbottomright = new float2(uvMax.x, 1.0f - uvMax.y); + + var pivot = new float2(-originX, originY); + this.vtopleft = (pivot + new float2(0, 0)) / defaultSize; + this.vbottomright = (pivot + new float2(width, -height)) / defaultSize; + this.advance = advance / defaultSize; + } + } + + /// <summary>Represents an SDF font</summary> + internal struct SDFFont { + public string name; + public int size, width, height; + public bool bold, italic; + public SDFCharacter[] characters; + public UnityEngine.Material material; + } + + /// <summary>Optimzed lookup for accessing font data from the unity job system</summary> + internal struct SDFLookupData { + public NativeArray<SDFCharacter> characters; + Dictionary<char, int> lookup; + public Material material; + + public const System.UInt16 Newline = System.UInt16.MaxValue; + + public SDFLookupData (SDFFont font) { + // Create a native array with the character data. + // Note that the 'char' type is non-blittable in C# and this is required + // for the NativeArray constructor that takes a T[] to copy. + // However native arrays can store 'char's, so we copy them one by one instead. + int nonAscii = 0; + SDFCharacter questionMark = font.characters[0]; + + for (int i = 0; i < font.characters.Length; i++) { + if (font.characters[i].codePoint == '?') { + questionMark = font.characters[i]; + } + if (font.characters[i].codePoint >= 128) { + nonAscii++; + } + } + characters = new NativeArray<SDFCharacter>(128 + nonAscii, Allocator.Persistent); + for (int i = 0; i < characters.Length; i++) { + characters[i] = questionMark; + } + lookup = new Dictionary<char, int>(); + material = font.material; + + nonAscii = 0; + for (int i = 0; i < font.characters.Length; i++) { + var sdfChar = font.characters[i]; + int targetIndex = sdfChar.codePoint; + if (sdfChar.codePoint >= 128) { + targetIndex = 128 + nonAscii; + nonAscii++; + } + characters[targetIndex] = sdfChar; + lookup[sdfChar.codePoint] = targetIndex; + } + } + + public int GetIndex (char c) { + if (lookup.TryGetValue(c, out int index)) { + return index; + } else { + if (c == '\n') return Newline; + return lookup['?']; + } + } + + public void Dispose () { + if (characters.IsCreated) { + characters.Dispose(); + } + } + } + + static class DefaultFonts { + internal static SDFFont LoadDefaultFont () { + var font = new SDFFont { + name = "Droid Sans Mono", + size = 32, + bold = false, + italic = false, + width = 1024, + height = 128, + characters = null, + material = UnityEngine.Resources.Load<UnityEngine.Material>("aline_droid_sans_mono") + }; + + // Generated by https://evanw.github.io/font-texture-generator/ + SDFCharacter[] characters_Droid_Sans_Mono = { + new SDFCharacter(' ', 414, 79, 12, 12, 6, 6, 19, font.width, font.height, font.size), + new SDFCharacter('!', 669, 44, 16, 35, -2, 29, 19, font.width, font.height, font.size), + new SDFCharacter('"', 258, 79, 23, 20, 2, 29, 19, font.width, font.height, font.size), + new SDFCharacter('#', 919, 0, 30, 35, 5, 29, 19, font.width, font.height, font.size), + new SDFCharacter('$', 231, 0, 26, 38, 3, 30, 19, font.width, font.height, font.size), + new SDFCharacter('%', 393, 0, 31, 36, 6, 29, 19, font.width, font.height, font.size), + new SDFCharacter('&', 424, 0, 31, 36, 5, 29, 19, font.width, font.height, font.size), + new SDFCharacter('\'', 281, 79, 16, 20, -2, 29, 19, font.width, font.height, font.size), + new SDFCharacter('(', 115, 0, 22, 40, 1, 29, 19, font.width, font.height, font.size), + new SDFCharacter(')', 137, 0, 22, 40, 1, 29, 19, font.width, font.height, font.size), + new SDFCharacter('*', 159, 79, 27, 26, 4, 30, 19, font.width, font.height, font.size), + new SDFCharacter('+', 186, 79, 27, 26, 4, 24, 19, font.width, font.height, font.size), + new SDFCharacter(',', 240, 79, 18, 21, -1, 10, 19, font.width, font.height, font.size), + new SDFCharacter('-', 359, 79, 23, 15, 2, 16, 19, font.width, font.height, font.size), + new SDFCharacter('.', 315, 79, 17, 17, -1, 11, 19, font.width, font.height, font.size), + new SDFCharacter('/', 500, 44, 25, 35, 3, 29, 19, font.width, font.height, font.size), + new SDFCharacter('0', 569, 0, 27, 36, 4, 29, 19, font.width, font.height, font.size), + new SDFCharacter('1', 649, 44, 20, 35, 2, 29, 19, font.width, font.height, font.size), + new SDFCharacter('2', 313, 44, 27, 35, 4, 29, 19, font.width, font.height, font.size), + new SDFCharacter('3', 758, 0, 26, 36, 4, 29, 19, font.width, font.height, font.size), + new SDFCharacter('4', 60, 44, 29, 35, 5, 29, 19, font.width, font.height, font.size), + new SDFCharacter('5', 448, 44, 26, 35, 3, 29, 19, font.width, font.height, font.size), + new SDFCharacter('6', 596, 0, 27, 36, 4, 29, 19, font.width, font.height, font.size), + new SDFCharacter('7', 340, 44, 27, 35, 4, 29, 19, font.width, font.height, font.size), + new SDFCharacter('8', 623, 0, 27, 36, 4, 29, 19, font.width, font.height, font.size), + new SDFCharacter('9', 650, 0, 27, 36, 4, 29, 19, font.width, font.height, font.size), + new SDFCharacter(':', 861, 44, 16, 30, -2, 23, 19, font.width, font.height, font.size), + new SDFCharacter(';', 711, 44, 18, 34, 0, 23, 19, font.width, font.height, font.size), + new SDFCharacter('<', 77, 79, 27, 28, 4, 25, 19, font.width, font.height, font.size), + new SDFCharacter('=', 213, 79, 27, 21, 4, 22, 19, font.width, font.height, font.size), + new SDFCharacter('>', 104, 79, 27, 28, 4, 25, 19, font.width, font.height, font.size), + new SDFCharacter('?', 784, 0, 26, 36, 3, 29, 19, font.width, font.height, font.size), + new SDFCharacter('@', 200, 0, 31, 38, 6, 29, 19, font.width, font.height, font.size), + new SDFCharacter('A', 949, 0, 30, 35, 5, 29, 19, font.width, font.height, font.size), + new SDFCharacter('B', 89, 44, 28, 35, 4, 29, 19, font.width, font.height, font.size), + new SDFCharacter('C', 513, 0, 28, 36, 4, 29, 19, font.width, font.height, font.size), + new SDFCharacter('D', 117, 44, 28, 35, 4, 29, 19, font.width, font.height, font.size), + new SDFCharacter('E', 474, 44, 26, 35, 3, 29, 19, font.width, font.height, font.size), + new SDFCharacter('F', 525, 44, 25, 35, 2, 29, 19, font.width, font.height, font.size), + new SDFCharacter('G', 541, 0, 28, 36, 4, 29, 19, font.width, font.height, font.size), + new SDFCharacter('H', 367, 44, 27, 35, 4, 29, 19, font.width, font.height, font.size), + new SDFCharacter('I', 625, 44, 24, 35, 2, 29, 19, font.width, font.height, font.size), + new SDFCharacter('J', 550, 44, 25, 35, 4, 29, 19, font.width, font.height, font.size), + new SDFCharacter('K', 145, 44, 28, 35, 3, 29, 19, font.width, font.height, font.size), + new SDFCharacter('L', 575, 44, 25, 35, 2, 29, 19, font.width, font.height, font.size), + new SDFCharacter('M', 173, 44, 28, 35, 4, 29, 19, font.width, font.height, font.size), + new SDFCharacter('N', 394, 44, 27, 35, 4, 29, 19, font.width, font.height, font.size), + new SDFCharacter('O', 455, 0, 29, 36, 5, 29, 19, font.width, font.height, font.size), + new SDFCharacter('P', 421, 44, 27, 35, 3, 29, 19, font.width, font.height, font.size), + new SDFCharacter('Q', 38, 0, 29, 42, 5, 29, 19, font.width, font.height, font.size), + new SDFCharacter('R', 201, 44, 28, 35, 3, 29, 19, font.width, font.height, font.size), + new SDFCharacter('S', 677, 0, 27, 36, 4, 29, 19, font.width, font.height, font.size), + new SDFCharacter('T', 229, 44, 28, 35, 4, 29, 19, font.width, font.height, font.size), + new SDFCharacter('U', 257, 44, 28, 35, 4, 29, 19, font.width, font.height, font.size), + new SDFCharacter('V', 979, 0, 30, 35, 5, 29, 19, font.width, font.height, font.size), + new SDFCharacter('W', 888, 0, 31, 35, 6, 29, 19, font.width, font.height, font.size), + new SDFCharacter('X', 0, 44, 30, 35, 5, 29, 19, font.width, font.height, font.size), + new SDFCharacter('Y', 30, 44, 30, 35, 5, 29, 19, font.width, font.height, font.size), + new SDFCharacter('Z', 285, 44, 28, 35, 4, 29, 19, font.width, font.height, font.size), + new SDFCharacter('[', 159, 0, 21, 40, 0, 29, 19, font.width, font.height, font.size), + new SDFCharacter('\\', 600, 44, 25, 35, 3, 29, 19, font.width, font.height, font.size), + new SDFCharacter(']', 180, 0, 20, 40, 1, 29, 19, font.width, font.height, font.size), + new SDFCharacter('^', 131, 79, 28, 26, 4, 29, 19, font.width, font.height, font.size), + new SDFCharacter('_', 382, 79, 32, 14, 6, 3, 19, font.width, font.height, font.size), + new SDFCharacter('`', 297, 79, 18, 17, -1, 31, 19, font.width, font.height, font.size), + new SDFCharacter('a', 784, 44, 26, 30, 4, 23, 19, font.width, font.height, font.size), + new SDFCharacter('b', 285, 0, 27, 37, 4, 30, 19, font.width, font.height, font.size), + new SDFCharacter('c', 810, 44, 26, 30, 3, 23, 19, font.width, font.height, font.size), + new SDFCharacter('d', 312, 0, 27, 37, 4, 30, 19, font.width, font.height, font.size), + new SDFCharacter('e', 757, 44, 27, 30, 4, 23, 19, font.width, font.height, font.size), + new SDFCharacter('f', 704, 0, 27, 36, 4, 30, 19, font.width, font.height, font.size), + new SDFCharacter('g', 257, 0, 28, 37, 4, 23, 19, font.width, font.height, font.size), + new SDFCharacter('h', 810, 0, 26, 36, 3, 30, 19, font.width, font.height, font.size), + new SDFCharacter('i', 836, 0, 26, 36, 3, 30, 19, font.width, font.height, font.size), + new SDFCharacter('j', 0, 0, 23, 44, 4, 30, 19, font.width, font.height, font.size), + new SDFCharacter('k', 731, 0, 27, 36, 3, 30, 19, font.width, font.height, font.size), + new SDFCharacter('l', 862, 0, 26, 36, 3, 30, 19, font.width, font.height, font.size), + new SDFCharacter('m', 909, 44, 29, 29, 5, 23, 19, font.width, font.height, font.size), + new SDFCharacter('n', 995, 44, 26, 29, 3, 23, 19, font.width, font.height, font.size), + new SDFCharacter('o', 729, 44, 28, 30, 4, 23, 19, font.width, font.height, font.size), + new SDFCharacter('p', 339, 0, 27, 37, 4, 23, 19, font.width, font.height, font.size), + new SDFCharacter('q', 366, 0, 27, 37, 4, 23, 19, font.width, font.height, font.size), + new SDFCharacter('r', 52, 79, 25, 29, 2, 23, 19, font.width, font.height, font.size), + new SDFCharacter('s', 836, 44, 25, 30, 3, 23, 19, font.width, font.height, font.size), + new SDFCharacter('t', 685, 44, 26, 34, 4, 28, 19, font.width, font.height, font.size), + new SDFCharacter('u', 0, 79, 26, 29, 3, 23, 19, font.width, font.height, font.size), + new SDFCharacter('v', 938, 44, 29, 29, 5, 23, 19, font.width, font.height, font.size), + new SDFCharacter('w', 877, 44, 32, 29, 6, 23, 19, font.width, font.height, font.size), + new SDFCharacter('x', 967, 44, 28, 29, 4, 23, 19, font.width, font.height, font.size), + new SDFCharacter('y', 484, 0, 29, 36, 5, 23, 19, font.width, font.height, font.size), + new SDFCharacter('z', 26, 79, 26, 29, 3, 23, 19, font.width, font.height, font.size), + new SDFCharacter('{', 67, 0, 24, 40, 2, 29, 19, font.width, font.height, font.size), + new SDFCharacter('|', 23, 0, 15, 44, -2, 30, 19, font.width, font.height, font.size), + new SDFCharacter('}', 91, 0, 24, 40, 2, 29, 19, font.width, font.height, font.size), + new SDFCharacter('~', 332, 79, 27, 16, 4, 19, 19, font.width, font.height, font.size), + }; + + font.characters = characters_Droid_Sans_Mono; + + return font; + } + } + } +} diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/SDFFont.cs.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/SDFFont.cs.meta new file mode 100644 index 0000000..0b9ef42 --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/SDFFont.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 39c1356c4ace157b6acacc8dc12ef3f6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/StreamSplitter.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/StreamSplitter.cs new file mode 100644 index 0000000..c7bc754 --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/StreamSplitter.cs @@ -0,0 +1,204 @@ +using Unity.Collections.LowLevel.Unsafe; +using Unity.Mathematics; +using Unity.Jobs.LowLevel.Unsafe; +using UnityEngine; +using Unity.Burst; +using UnityEngine.Profiling; +using Unity.Collections; +using Unity.Jobs; + +namespace Pathfinding.Drawing { + using static CommandBuilder; + + [BurstCompile] + internal struct StreamSplitter : IJob { + public NativeArray<UnsafeAppendBuffer> inputBuffers; + [NativeDisableUnsafePtrRestriction] + public unsafe UnsafeAppendBuffer* staticBuffer, dynamicBuffer, persistentBuffer; + + internal static readonly int PushCommands = (1 << (int)Command.PushColor) | (1 << (int)Command.PushMatrix) | (1 << (int)Command.PushSetMatrix) | (1 << (int)Command.PushPersist) | (1 << (int)Command.PushLineWidth); + internal static readonly int PopCommands = (1 << (int)Command.PopColor) | (1 << (int)Command.PopMatrix) | (1 << (int)Command.PopPersist) | (1 << (int)Command.PopLineWidth); + internal static readonly int MetaCommands = PushCommands | PopCommands; + internal static readonly int DynamicCommands = (1 << (int)Command.SphereOutline) | (1 << (int)Command.CircleXZ) | (1 << (int)Command.Circle) | (1 << (int)Command.DiscXZ) | (1 << (int)Command.Disc) | (1 << (int)Command.Text) | (1 << (int)Command.Text3D) | (1 << (int)Command.CaptureState) | MetaCommands; + internal static readonly int StaticCommands = (1 << (int)Command.Line) | (1 << (int)Command.Box) | (1 << (int)Command.WirePlane) | (1 << (int)Command.WireBox) | (1 << (int)Command.SolidTriangle) | MetaCommands; + + internal static readonly int[] CommandSizes; + static StreamSplitter() { + // Size of all commands in bytes + CommandSizes = new int[22]; + CommandSizes[(int)Command.PushColor] = UnsafeUtility.SizeOf<Command>() + UnsafeUtility.SizeOf<Color32>(); + CommandSizes[(int)Command.PopColor] = UnsafeUtility.SizeOf<Command>() + 0; + CommandSizes[(int)Command.PushMatrix] = UnsafeUtility.SizeOf<Command>() + UnsafeUtility.SizeOf<float4x4>(); + CommandSizes[(int)Command.PushSetMatrix] = UnsafeUtility.SizeOf<Command>() + UnsafeUtility.SizeOf<float4x4>(); + CommandSizes[(int)Command.PopMatrix] = UnsafeUtility.SizeOf<Command>() + 0; + CommandSizes[(int)Command.Line] = UnsafeUtility.SizeOf<Command>() + UnsafeUtility.SizeOf<LineData>(); + CommandSizes[(int)Command.CircleXZ] = UnsafeUtility.SizeOf<Command>() + UnsafeUtility.SizeOf<CircleXZData>(); + CommandSizes[(int)Command.SphereOutline] = UnsafeUtility.SizeOf<Command>() + UnsafeUtility.SizeOf<SphereData>(); + CommandSizes[(int)Command.Circle] = UnsafeUtility.SizeOf<Command>() + UnsafeUtility.SizeOf<CircleData>(); + CommandSizes[(int)Command.Disc] = UnsafeUtility.SizeOf<Command>() + UnsafeUtility.SizeOf<CircleData>(); + CommandSizes[(int)Command.DiscXZ] = UnsafeUtility.SizeOf<Command>() + UnsafeUtility.SizeOf<CircleXZData>(); + CommandSizes[(int)Command.Box] = UnsafeUtility.SizeOf<Command>() + UnsafeUtility.SizeOf<BoxData>(); + CommandSizes[(int)Command.WirePlane] = UnsafeUtility.SizeOf<Command>() + UnsafeUtility.SizeOf<PlaneData>(); + CommandSizes[(int)Command.WireBox] = UnsafeUtility.SizeOf<Command>() + UnsafeUtility.SizeOf<BoxData>(); + CommandSizes[(int)Command.SolidTriangle] = UnsafeUtility.SizeOf<Command>() + UnsafeUtility.SizeOf<TriangleData>(); + CommandSizes[(int)Command.PushPersist] = UnsafeUtility.SizeOf<Command>() + UnsafeUtility.SizeOf<PersistData>(); + CommandSizes[(int)Command.PopPersist] = UnsafeUtility.SizeOf<Command>(); + CommandSizes[(int)Command.Text] = UnsafeUtility.SizeOf<Command>() + UnsafeUtility.SizeOf<TextData>(); // Dynamically sized + CommandSizes[(int)Command.Text3D] = UnsafeUtility.SizeOf<Command>() + UnsafeUtility.SizeOf<TextData3D>(); // Dynamically sized + CommandSizes[(int)Command.PushLineWidth] = UnsafeUtility.SizeOf<Command>() + UnsafeUtility.SizeOf<LineWidthData>(); + CommandSizes[(int)Command.PopLineWidth] = UnsafeUtility.SizeOf<Command>(); + CommandSizes[(int)Command.CaptureState] = UnsafeUtility.SizeOf<Command>(); + } + + public void Execute () { + var lastWriteStatic = -1; + var lastWriteDynamic = -1; + var lastWritePersist = -1; + var stackStatic = new NativeArray<int>(GeometryBuilderJob.MaxStackSize, Allocator.Temp, NativeArrayOptions.ClearMemory); + var stackDynamic = new NativeArray<int>(GeometryBuilderJob.MaxStackSize, Allocator.Temp, NativeArrayOptions.ClearMemory); + var stackPersist = new NativeArray<int>(GeometryBuilderJob.MaxStackSize, Allocator.Temp, NativeArrayOptions.ClearMemory); + + unsafe { + // Store in local variables for performance (makes it possible to use registers for a lot of fields) + var bufferStatic = *staticBuffer; + var bufferDynamic = *dynamicBuffer; + var bufferPersist = *persistentBuffer; + + bufferStatic.Reset(); + bufferDynamic.Reset(); + bufferPersist.Reset(); + + for (int i = 0; i < inputBuffers.Length; i++) { + int stackSize = 0; + int persist = 0; + var reader = inputBuffers[i].AsReader(); + + // Guarantee we have enough space for copying the whole buffer + if (bufferStatic.Capacity < bufferStatic.Length + reader.Size) bufferStatic.SetCapacity(math.ceilpow2(bufferStatic.Length + reader.Size)); + if (bufferDynamic.Capacity < bufferDynamic.Length + reader.Size) bufferDynamic.SetCapacity(math.ceilpow2(bufferDynamic.Length + reader.Size)); + if (bufferPersist.Capacity < bufferPersist.Length + reader.Size) bufferPersist.SetCapacity(math.ceilpow2(bufferPersist.Length + reader.Size)); + + // To ensure that even if exceptions are thrown the output buffers still point to valid memory regions + *staticBuffer = bufferStatic; + *dynamicBuffer = bufferDynamic; + *persistentBuffer = bufferPersist; + + while (reader.Offset < reader.Size) { + var cmd = *(Command*)((byte*)reader.Ptr + reader.Offset); + var cmdBit = 1 << ((int)cmd & 0xFF); + int size = CommandSizes[(int)cmd & 0xFF] + ((cmd & Command.PushColorInline) != 0 ? UnsafeUtility.SizeOf<Color32>() : 0); + bool isMeta = (cmdBit & MetaCommands) != 0; + + if ((cmd & (Command)0xFF) == Command.Text) { + // Very pretty way of reading the TextData struct right after the command label and optional Color32 + var data = *((TextData*)((byte*)reader.Ptr + reader.Offset + size) - 1); + // Add the size of the embedded string in the buffer + // TODO: Unaligned memory access performance penalties?? Update: Doesn't seem to be so bad on Intel at least. + size += data.numCharacters * UnsafeUtility.SizeOf<System.UInt16>(); + } else if ((cmd & (Command)0xFF) == Command.Text3D) { + // Very pretty way of reading the TextData struct right after the command label and optional Color32 + var data = *((TextData3D*)((byte*)reader.Ptr + reader.Offset + size) - 1); + // Add the size of the embedded string in the buffer + // TODO: Unaligned memory access performance penalties?? Update: Doesn't seem to be so bad on Intel at least. + size += data.numCharacters * UnsafeUtility.SizeOf<System.UInt16>(); + } + +#if ENABLE_UNITY_COLLECTIONS_CHECKS + UnityEngine.Assertions.Assert.IsTrue(reader.Offset + size <= reader.Size); +#endif + + if ((cmdBit & DynamicCommands) != 0 && persist == 0) { + if (!isMeta) lastWriteDynamic = bufferDynamic.Length; +#if ENABLE_UNITY_COLLECTIONS_CHECKS + UnityEngine.Assertions.Assert.IsTrue(bufferDynamic.Length + size <= bufferDynamic.Capacity); +#endif + UnsafeUtility.MemCpy((byte*)bufferDynamic.Ptr + bufferDynamic.Length, (byte*)reader.Ptr + reader.Offset, size); + bufferDynamic.Length = bufferDynamic.Length + size; + } + + if ((cmdBit & StaticCommands) != 0 && persist == 0) { + if (!isMeta) lastWriteStatic = bufferStatic.Length; +#if ENABLE_UNITY_COLLECTIONS_CHECKS + UnityEngine.Assertions.Assert.IsTrue(bufferStatic.Length + size <= bufferStatic.Capacity); +#endif + UnsafeUtility.MemCpy((byte*)bufferStatic.Ptr + bufferStatic.Length, (byte*)reader.Ptr + reader.Offset, size); + bufferStatic.Length = bufferStatic.Length + size; + } + + if ((cmdBit & MetaCommands) != 0 || persist > 0) { + if (persist > 0 && !isMeta) lastWritePersist = bufferPersist.Length; +#if ENABLE_UNITY_COLLECTIONS_CHECKS + UnityEngine.Assertions.Assert.IsTrue(bufferPersist.Length + size <= bufferPersist.Capacity); +#endif + UnsafeUtility.MemCpy((byte*)bufferPersist.Ptr + bufferPersist.Length, (byte*)reader.Ptr + reader.Offset, size); + bufferPersist.Length = bufferPersist.Length + size; + } + + if ((cmdBit & PushCommands) != 0) { + stackStatic[stackSize] = bufferStatic.Length - size; + stackDynamic[stackSize] = bufferDynamic.Length - size; + stackPersist[stackSize] = bufferPersist.Length - size; + stackSize++; + if ((cmd & (Command)0xFF) == Command.PushPersist) { + persist++; + } +#if ENABLE_UNITY_COLLECTIONS_CHECKS + if (stackSize >= GeometryBuilderJob.MaxStackSize) throw new System.Exception("Push commands are too deeply nested. This can happen if you have deeply nested WithMatrix or WithColor scopes."); +#else + if (stackSize >= GeometryBuilderJob.MaxStackSize) { + return; + } +#endif + } else if ((cmdBit & PopCommands) != 0) { + stackSize--; +#if ENABLE_UNITY_COLLECTIONS_CHECKS + if (stackSize < 0) throw new System.Exception("Trying to issue a pop command but there is no corresponding push command"); +#else + if (stackSize < 0) return; +#endif + // If a scope was pushed and later popped, but no actual draw commands were written to the buffers + // inside that scope then we erase the whole scope. + if (lastWriteStatic < stackStatic[stackSize]) { + bufferStatic.Length = stackStatic[stackSize]; + } + if (lastWriteDynamic < stackDynamic[stackSize]) { + bufferDynamic.Length = stackDynamic[stackSize]; + } + if (lastWritePersist < stackPersist[stackSize]) { + bufferPersist.Length = stackPersist[stackSize]; + } + if ((cmd & (Command)0xFF) == Command.PopPersist) { + persist--; +#if ENABLE_UNITY_COLLECTIONS_CHECKS + if (persist < 0) throw new System.Exception("Too many PopPersist commands. Are your PushPersist/PopPersist calls matched?"); +#else + if (persist < 0) return; +#endif + } + } + + reader.Offset += size; + } + +#if ENABLE_UNITY_COLLECTIONS_CHECKS + if (stackSize != 0) throw new System.Exception("Too few pop commands and too many push commands. Are your push and pop commands properly matched?"); + if (reader.Offset != reader.Size) throw new System.Exception("Did not end up at the end of the buffer. This is a bug."); +#else + if (stackSize != 0) return; + if (reader.Offset != reader.Size) return; +#endif + } + +#if ENABLE_UNITY_COLLECTIONS_CHECKS + if (bufferStatic.Length > bufferStatic.Capacity) throw new System.Exception("Buffer overrun. This is a bug"); + if (bufferDynamic.Length > bufferDynamic.Capacity) throw new System.Exception("Buffer overrun. This is a bug"); + if (bufferPersist.Length > bufferPersist.Capacity) throw new System.Exception("Buffer overrun. This is a bug"); +#endif + + *staticBuffer = bufferStatic; + *dynamicBuffer = bufferDynamic; + *persistentBuffer = bufferPersist; + } + } + } +} diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/StreamSplitter.cs.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/StreamSplitter.cs.meta new file mode 100644 index 0000000..fe8b1c8 --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/StreamSplitter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f7275807cc748c3ee8a86c63868e6a55 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/changelog.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/changelog.cs new file mode 100644 index 0000000..5807c2c --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/changelog.cs @@ -0,0 +1,283 @@ +/// <summary> +/// \page changelog Changelog +/// \order{-10} +/// +/// - 1.7.4 (2024-02-13) +/// - Fixed compatibility with HDRP render pipeline. +/// - Improved performance when there are many cameras rendered during the same frame. +/// +/// - 1.7.3 (2024-02-07) +/// - Improved performance when there are lots of components inheriting from <see cref="MonoBehaviourGizmos"/>, but they do not actually override the DrawGizmos method. +/// - Fixed compatibility with Universal Render Pipeline package version 15 and 16 (regression in 1.7.2). +/// +/// - 1.7.2 (2024-02-06) +/// - Improved performance of <see cref="Draw.WireCylinder"/> and <see cref="Draw.WireCapsule"/>. +/// - Fixed a memory leak that could happen if you used a lot of custom command builders. +/// - Added an option to the project settings to increase or decrease the resolution of circles. +/// - Improved compatibility with Universal Render Pipeline package version 17. +/// +/// - 1.7.1 (2023-11-14) +/// - Removed "com.unity.jobs" as a dependency, since it has been replaced by the collections package. +/// - Added support for rendering gizmos while the scene view is in wireframe mode. This is supported in Unity 2023.1 and up. +/// - Added <see cref="CommandBuilder.DashedLine"/>. +/// [Open online documentation to see images] +/// - Added <see cref="CommandBuilder.DashedPolyline"/>. +/// [Open online documentation to see images] +/// +/// - 1.7.0 (2023-10-17) +/// - Added a much more ergonomic way to draw using 2D coordinates. Take a look at 2d-drawing (view in online documentation for working links) for more info. +/// [Open online documentation to see images] +/// - Deprecated several methods like <see cref="Draw.CircleXY"/> and <see cref="Draw.CircleXZ"/> to instead use the new 2D methods (Draw.xy.Circle and Draw.xz.Circle). +/// The old ones will continue to work for the time being, but they will be removed in a future update. +/// - Removed some shader code which was not supported on WebGL. +/// - Added <see cref="CommandBuilder2D.WirePill"/> +/// [Open online documentation to see images] +/// - Added <see cref="CommandBuilder.SolidTriangle"/> +/// [Open online documentation to see images] +/// - Added an overload of <see cref="Draw.Polyline"/> which takes an IReadOnlyList<T>. +/// - Added <see cref="CommandBuilder.PolylineWithSymbol"/> +/// [Open online documentation to see images] +/// - Added an overload of <see cref="CommandBuilder.WireMesh"/> that takes a NativeArray with vertices, and one with triangles. +/// - Improved look of <see cref="Draw.ArrowheadArc"/> when using a line width greater than 1. +/// - Improved performance when there are lots of objects in the scene inheriting from <see cref="MonoBehaviourGizmos"/>. +/// - Significantly reduced main-thread load when drawing in many situations by improving the Color to Color32 conversion performance. +/// Turns out Unity's built-in one is not the fastest. +/// In Burst I've cranked it up even more by using a SIMDed conversion function. +/// Common improvements are around 10% faster, but in tight loops it can be up to 50% faster. +/// - Improved performance of <see cref="Draw.WireBox"/>. +/// - Improved performance of drawing circles and arcs. +/// - Fixed name collision when both the A* Pathfinding Project and ALINE were installed in a project. This could cause the warning "There are 2 settings providers with the same name Project/ALINE." to be logged to the console. +/// - Fixed Draw.WireBox reserving the wrong amount of memory, which could lead to an exception being thrown. +/// - Fixed lines would be drawn slightly incorrectly at very shallow camera angles. +/// - Fixed a memory leak which could happen if the game was not running, and the scene view was not being re-rendered, and a script was queuing drawing commands from an editor script repeatedly. +/// Drawing commands will now get discarded after 10 seconds if no rendering happens to avoid leaking memory indefinitely. +/// - Fixed a memory leak which could happen if the game was not running in the editor, and no cameras were being rendered (e.g. on a server). +/// - Fixed shader compilation errors when deploying for PlayStation 5. +/// - Fixed circles with a normal of exactly (0,-1,0) would not be rendered. +/// - Changed <see cref="RedrawScope"/> to continue drawing items until it is disposed, instead of requiring one to call the scope.Draw method every frame. +/// - Allow a <see cref="RedrawScope"/> to be stored in unmanaged ECS components and systems. +/// - Fixed <see cref="Draw.Arrow"/> would draw a slightly narrower arrow head when the line was pointed in certain directions. +/// - Added an overload for 3x3 matrices: <see cref="Draw.WithMatrix(float3x3)"/>. +/// - Changed the behaviour for <see cref="RedrawScope"/>s. Previously they would continue drawing as long as you called RedrawScope.Draw every frame. +/// Now they will continue drawing until you dispose them. This makes them just nicer to use for most cases. +/// This is a breaking change, but since RedrawSopes have so far been a completely undocumented feature, I expect that no, or very few people, use them. +/// - Fixed compatibility with XBox. +/// - Fixed only the base camera in a camera stack would render gizmos. +/// +/// - 1.6.4 (2022-09-17) +/// - <see cref="CommandBuilder.DisposeAfter"/> will now block on the given dependency before rendering the current frame by default. +/// This reduces the risk of flickering when using ECS systems as they may otherwise not have completed their work before the frame is rendered. +/// You can pass <see cref="AllowedDelay.Infinite"/> to disable this behavior for long-running jobs. +/// - Fixed recent regression causing drawing to fail in standalone builds. +/// +/// - 1.6.3 (2022-09-15) +/// - Added <see cref="LabelAlignment.withPixelOffset"/>. +/// - Fixed <see cref="LabelAlignment"/> had top and bottom alignment swapped. So for example <see cref="LabelAlignment.TopLeft"/> was actually <see cref="LabelAlignment.BottomLeft"/>. +/// - Fixed shaders would sometimes cause compilation errors, especially if you changed render pipelines. +/// - Improved sharpness of <see cref="Draw.Label2D"/> and <see cref="Draw.Label3D"/> when using small font-sizes. +/// <table> +/// <tr><td>Before</td><td>After</td></tr> +/// <tr> +/// <td> +/// [Open online documentation to see images] +/// </td> +/// <td> +/// [Open online documentation to see images] +/// </td> +/// </table> +/// - Text now fades out slightly when behind or inside other objects. The fade out amount can be controlled in the project settings: +/// [Open online documentation to see images] +/// - Fixed <see cref="Draw.Label2D"/> and <see cref="Draw.Label3D"/> font sizes would be incorrect (half as large) when the camera was in orthographic mode. +/// - Fixed <see cref="Draw.WireCapsule"/> and <see cref="Draw.WireCylinder"/> would render incorrectly in certain orientations. +/// +/// - 1.6.2 (2022-09-05) +/// - Fix typo causing prefabs to always be drawn in the scene view in Unity versions earlier than 2022.1, even if they were not even added to the scene. +/// +/// - 1.6.1 (2022-08-31) +/// - Fix vertex buffers not getting resized correctly. This could cause exceptions to be logged sometimes. Regression in 1.6. +/// +/// - 1.6 (2022-08-27) +/// - Fixed documentation and changelog URLs in the package manager. +/// - Fixed dragging a prefab into the scene view would instantiate it, but gizmos for scripts attached to it would not work. +/// - Fixed some edge cases in <see cref="Draw.WireCapsule"/> and <see cref="Draw.WireCapsule"/> which could cause NaNs and other subtle errors. +/// - Improved compatibility with WebGL as well as Intel GPUs on Mac. +/// - Added warning when using HDRP and custom passes are disabled. +/// - Improved performance of watching for destroyed objects. +/// - Reduced overhead when having lots of objects inheriting from <see cref="MonoBehaviourGizmos"/>. +/// - It's now possible to enable/disable gizmos for component types via the Unity Scene View Gizmos menu when using render pipelines in Unity 2022.1+. +/// In earlier versions of Unity, a limited API made this impossible. +/// - Made it possible to adjust the global opacity of gizmos in the Unity Project Settings. +/// [Open online documentation to see images] +/// +/// - 1.5.3 (2022-05-14) +/// - Breaking changes +/// - The minimum supported Unity version is now 2020.3. +/// - The URP 2D renderer now has support for all features required by ALINE. So the warning about it not being supported has been removed. +/// - Fixed windows newlines (\\n\\r) would show up as a newline and a question mark instead of just a newline. +/// - Fixed compilation errors when using the Unity.Collections package between version 0.8 and 0.11. +/// - Improved performance in some edge cases. +/// - Fixed <see cref="Draw.SolidMesh"/> with a non-white color could affect the color of unrelated rendered lines. Thanks Chris for finding and reporting the bug. +/// - Fixed an exception could be logged when drawing circles with a zero or negative line width. +/// - Fixed various compilation errors that could show up when using newer versions of the burst package. +/// +/// - 1.5.2 (2021-11-09) +/// - Fix gizmos would not show up until you selected the camera if you had just switched to the universal render pipeline. +/// - Improved performance of drawing lines by more efficiently sending the data to the shader. +/// This has the downside that shader target 4.5 is now required. I don't think this should be a big deal nowadays, but let me know if things don't work on your platform. +/// This was originally introduced in 1.5.0, but reverted in 1.5.1 due to some compatibility issues causing rendering to fail for some project configurations. I think those issues should be resolved now. +/// +/// - 1.5.1 (2021-10-28) +/// - Reverted "Improved performance of drawing lines by more efficiently sending the data to the shader." from 1.5.0. +/// It turns out this caused issues for some users and could result in gizmos not showing at all. +/// I'll try to figure out a solution and bring the performance improvements back. +/// +/// - 1.5 (2021-10-27) +/// - Added support FixedStrings in <see cref="Draw.Label2D(float3,FixedString32Bytes,float)"/>, which means it can be used inside burst jobs (C# managed strings cannot be used in burst jobs). +/// - Fixed a 'NativeArray has not been disposed' error message that could show up if the whole project's assets were re-imported. +/// - Added <see cref="Draw.SolidCircle"/>. +/// [Open online documentation to see images] +/// - Added <see cref="Draw.SolidCircleXZ"/>. +/// [Open online documentation to see images] +/// - Added <see cref="Draw.SolidArc"/>. +/// [Open online documentation to see images] +/// - Added <see cref="Draw.Label3D"/> +/// [Open online documentation to see images] +/// - Improved performance of <see cref="Draw.WirePlane"/> and <see cref="Draw.WireRectangle"/> by making them primitives instead of just calling <see cref="Draw.Line"/> 4 times. +/// - Improved performance in general by more efficiently re-using existing vertex buffers. +/// - Fixed some warnings related to ENABLE_UNITY_COLLECTIONS_CHECKS which burst would log when building a standalone player. +/// - Changed more functions in the <see cref="Draw"/> class to take a Unity.Mathematics.quaternion instead of a UnityEngine.Quaternion. +/// Implicit conversions exist in both directions, so there is no need to change your code. +/// +/// - 1.4.3 (2021-09-04) +/// - Fixed some debug printout had been included by mistake. A "Disposing" message could sometimes show up in the console. +/// +/// - 1.4.2 (2021-08-22) +/// - Reduced overhead in standalone builds if you have many objects in the scene. +/// - Fixed <see cref="Draw.WireCapsule(float3,float3,float)"/> could render incorrectly if the start and end parameters were identical. +/// - Fixed <see cref="Draw.WithDuration"/> scopes could survive until the next time the game started if no game or scene cameras were ever rendered while in edit mode. +/// - Added <see cref="Draw.SphereOutline(float3,float)"/>. +/// [Open online documentation to see images] +/// - <see cref="Draw.WireSphere(float3,float)"/> has changed to always include an outline of the sphere. This makes it a lot nicer to look at. +/// [Open online documentation to see images] +/// +/// - 1.4.1 (2021-02-28) +/// - Added <see cref="CommandBuilder.DisposeAfter"/> to dispose a command builder after a job has completed. +/// - Fixed gizmos would be rendered for other objects when the scene view was in prefab isolation mode. Now they will be hidden, which matches what Unity does. +/// - Fixed a deprecation warning when unity the HDRP package version 9.0 or higher. +/// - Improved docs for <see cref="RedrawScope"/>. +/// - Fixed documentation for scopes (e.g. <see cref="Draw.WithColor"/>) would show up as missing in the online documentation. +/// +/// - 1.4 (2021-01-27) +/// - Breaking changes +/// - <see cref="Draw.WireCapsule(float3,float3,float)"/> with the bottom/top parameterization was incorrect and the behavior did not match the documentation for it. +/// This method has been changed so that it now matches the documentation as this was the intended behavior all along. +/// The documentation and parameter names have also been clarified. +/// - Added <see cref="Draw.SolidRectangle(Rect)"/>. +/// - Fixed <see cref="Draw.SolidBox(float3,quaternion,float3)"/> and <see cref="Draw.WireBox(float3,quaternion,float3)"/> rendered a box that was offset by 0.5 times the size of the box. +/// This bug only applied to the overload with a rotation, not for example to <see cref="Draw.SolidBox(float3,float3)"/>. +/// - Fixed Draw.SolidMesh would always be rendered at the world origin with a white color. Now it picks up matrices and colors properly. +/// - Fixed a bug which could cause a greyed out object called 'RetainedGizmos' to appear in the scene hierarchy. +/// - Fixed some overloads of WireCylinder, WireCapsule, WireBox and SolidBox throwing errors when you tried to use them in a Burst job. +/// - Improved compatibility with some older versions of the Universal Render Pipeline. +/// +/// - 1.3.1 (2020-10-10) +/// - Improved performance in standalone builds by more aggressively compiling out drawing commands that would never render anything anyway. +/// - Reduced overhead in some cases, in particular when nothing is being rendered. +/// +/// - 1.3 (2020-09-12) +/// - Added support for line widths. +/// See <see cref="Draw.WithLineWidth"/>. +/// [Open online documentation to see images] +/// - Added warning message when using the Experimental URP 2D Renderer. The URP 2D renderer unfortunately does not have enough features yet +/// to be able to support ALINE. It doesn't have an extensible post processing system. The 2D renderer will be supported as soon as it is technically possible. +/// - Fixed <see cref="Draw.SolidPlane(float3,float3,float2)"/> and <see cref="Draw.WirePlane(float3,float3,float2)"/> not working for all normals. +/// - Fixed the culling bounding box for text and lines could be calculated incorrectly if text labels were used. +/// This could result in text and lines randomly disappearing when the camera was looking in particular directions. +/// - Renamed <see cref="Draw.PushPersist"/> and <see cref="Draw.PopPersist"/> to <see cref="Draw.PushDuration"/> and <see cref="Draw.PopDuration"/> for consistency with the <see cref="Draw.WithDuration"/> scope. +/// The previous names will still work, but they are marked as deprecated. +/// - Known bugs +/// - <see cref="Draw.SolidMesh(Mesh)"/> does not respect matrices and will always be drawn with the pivot at the world origin. +/// +/// - 1.2.3 (2020-07-26) +/// - Fixed solid drawing not working when using VR rendering. +/// - Fixed nothing was visible when using the Universal Render Pipeline and post processing was enabled. +/// Note that ALINE will render before post processing effects when using the URP. +/// This is because as far as I can tell the Universal Render Pipeline does not expose any way to render objects +/// after post processing effects because it renders to hidden textures that custom passes cannot access. +/// - Fixed drawing sometimes not working when using the High Definition Render Pipeline. +/// In contrast to the URP, ALINE can actually render after post processing effects with the HDRP since it has a nicer API. So it does that. +/// - Known bugs +/// - <see cref="Draw.SolidMesh(Mesh)"/> does not respect matrices and will always be drawn with the pivot at the world origin. +/// +/// - 1.2.2 (2020-07-11) +/// - Added <see cref="Draw.Arc(float3,float3,float3)"/>. +/// [Open online documentation to see images] +/// - Fixed drawing sometimes not working when using the Universal Render Pipeline, in particular when either HDR or anti-aliasing was enabled. +/// - Fixed drawing not working when using VR rendering. +/// - Hopefully fixed the issue that could sometimes cause "The ALINE package installation seems to be corrupt. Try reinstalling the package." to be logged when first installing +/// the package (even though the package wasn't corrupt at all). +/// - Incremented required burst package version from 1.3.0-preview.7 to 1.3.0. +/// - Fixed the offline documentation showing the wrong page instead of the get started guide. +/// +/// - 1.2.1 (2020-06-21) +/// - Breaking changes +/// - Changed the size parameter of Draw.WireRect to be a float2 instead of a float3. +/// It made no sense for it to be a float3 since a rectangle is two-dimensional. The y coordinate of the parameter was never used. +/// - Added <a href="ref:Draw.WirePlane(float3,float3,float2)">Draw.WirePlane</a>. +/// [Open online documentation to see images] +/// - Added <a href="ref:Draw.SolidPlane(float3,float3,float2)">Draw.SolidPlane</a>. +/// [Open online documentation to see images] +/// - Added <a href="ref:Draw.PlaneWithNormal(float3,float3,float2)">Draw.PlaneWithNormal</a>. +/// [Open online documentation to see images] +/// - Fixed Drawing.DrawingUtilities class missed an access modifier. Now all methods are properly public and can be accessed without any issues. +/// - Fixed an error could be logged after using the WireMesh method and then exiting/entering play mode. +/// - Fixed Draw.Arrow not drawing the arrowhead properly when the arrow's direction was a multiple of (0,1,0). +/// +/// - 1.2 (2020-05-22) +/// - Added page showing some advanced usages: advanced (view in online documentation for working links). +/// - Added <see cref="Drawing.Draw.WireMesh"/>. +/// [Open online documentation to see images] +/// - Added <see cref="Drawing.CommandBuilder.cameraTargets"/>. +/// - The WithDuration scope can now be used even outside of play mode. Outside of play mode it will use Time.realtimeSinceStartup to measure the duration. +/// - The WithDuration scope can now be used inside burst jobs and on different threads. +/// - Fixed WireCylinder and WireCapsule logging a warning if the normalized direction from the start to the end was exactly (1,1,1).normalized. Thanks Billy Attaway for reporting this. +/// - Fixed the documentation showing the wrong namespace for classes. It listed Pathfinding.Drawing but the correct namespace is just Drawing. +/// +/// - 1.1.1 (2020-05-04) +/// - Breaking changes +/// - The vertical alignment of Label2D has changed slightly. Previously the Top and Center alignments were a bit off from the actual top/center. +/// - Fixed conflicting assembly names when used in a project that also has the A* Pathfinding Project package installed. +/// - Fixed a crash when running on iOS. +/// - Improved alignment of <see cref="Drawing.Draw.Label2D"/> when using the Top or Center alignment. +/// +/// - 1.1 (2020-04-20) +/// - Added <see cref="Drawing.Draw.Label2D"/> which allows you to easily render text from your code. +/// It uses a signed distance field font renderer which allows you to render crisp text even at high resolution. +/// At very small font sizes it falls back to a regular font texture. +/// [Open online documentation to see images] +/// - Improved performance of drawing lines by about 5%. +/// - Fixed a potential crash after calling the Draw.Line(Vector3,Vector3,Color) method. +/// +/// - 1.0.2 (2020-04-09) +/// - Breaking changes +/// - A few breaking changes may be done as the package matures. I strive to keep these to as few as possible, while still not sacrificing good API design. +/// - Changed the behaviour of <see cref="Drawing.Draw.Arrow(float3,float3,float3,float)"/> to use an absolute size head. +/// This behaviour is probably the desired one more often when one wants to explicitly set the size. +/// The default Draw.Arrow(float3,float3) function which does not take a size parameter continues to use a relative head size of 20% of the length of the arrow. +/// [Open online documentation to see images] +/// - Added <see cref="Drawing.Draw.ArrowRelativeSizeHead"/> which uses a relative size head. +/// [Open online documentation to see images] +/// - Added <see cref="Drawing.DrawingManager.GetBuilder"/> instead of the unnecessarily convoluted DrawingManager.instance.gizmos.GetBuilder. +/// - Added <see cref="Drawing.Draw.CatmullRom(List<Vector3>)"/> for drawing a smooth curve through a list of points. +/// [Open online documentation to see images] +/// - Made it easier to draw things that are visible in standalone games. You can now use for example Draw.ingame.WireBox(Vector3.zero, Vector3.one) instead of having to create a custom command builder. +/// See ingame (view in online documentation for working links) for more details. +/// +/// - 1.0.1 (2020-04-06) +/// - Fix burst example scene not having using burst enabled (so it was much slower than it should have been). +/// - Fix text color in the SceneEditor example scene was so dark it was hard to read. +/// - Various minor documentation fixes. +/// +/// - 1.0 (2020-04-05) +/// - Initial release +/// </summary> diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/changelog.cs.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/changelog.cs.meta new file mode 100644 index 0000000..9695858 --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/changelog.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e1cf73b933cac2cfeb2af0f4d044b7b3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/package.json b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/package.json new file mode 100644 index 0000000..4c61880 --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/package.json @@ -0,0 +1,42 @@ +{ + "name": "com.arongranberg.aline", + "version": "1.0", + "displayName": "ALINE", + "description": "ALINE is a complete replacement for Unity's built-in Debug.Draw and Gizmo drawing functionality and much more. It is faster, has more features and has better rendering quality.", + "unity": "2020.3", + "unityRelease": "0f1", + "documentationUrl": "https://arongranberg.com/aline/docs", + "changelogUrl": "https://arongranberg.com/aline/docs/changelog.html", + "dependencies": { + "com.unity.burst": "1.7.2", + "com.unity.mathematics": "1.2.1", + "com.unity.collections": "1.4.0", + "com.unity.ugui": "1.0.0", + "com.unity.modules.ui": "1.0.0", + "com.unity.modules.physics": "1.0.0", + "com.unity.modules.physics2d": "1.0.0" + }, + "keywords": [ + "drawing", + "vector", + "canvas", + "debug", + "sketch", + "gizmo" + ], + "samples": [ + { + "displayName": "Example scenes", + "description": "Additional examples", + "path": "ExampleScenes~" + } + ], + "author": { + "name": "Aron Granberg", + "email": "aron.granberg@gmail.com", + "url": "https://www.arongranberg.com" + }, + "publishConfig": { + "registry": "https://arongranberg.com/packages_forward/" + } +}
\ No newline at end of file diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/package.json.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/package.json.meta new file mode 100644 index 0000000..2156512 --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/package.json.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 54784040eef3df2698c02df6c0ada6c1 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: |