From 8722a9920c1f6119bf6e769cba270e63097f8e25 Mon Sep 17 00:00:00 2001
From: chai <215380520@qq.com>
Date: Thu, 23 May 2024 10:08:29 +0800
Subject: + astar project
---
 .../com.arongranberg.astar/Drawing/ALINE.asmdef    |   82 +
 .../Drawing/ALINE.asmdef.meta                      |    7 +
 .../Drawing/AlineHDRPCustomPass.cs                 |   31 +
 .../Drawing/AlineHDRPCustomPass.cs.meta            |   11 +
 .../Drawing/AlineURPRenderPassFeature.cs           |   88 +
 .../Drawing/AlineURPRenderPassFeature.cs.meta      |   11 +
 .../Drawing/CommandBuilder.cs                      | 3062 ++++++++++++++++++++
 .../Drawing/CommandBuilder.cs.meta                 |   11 +
 .../Drawing/CommandBuilder2D.cs                    |  420 +++
 .../Drawing/CommandBuilder2D.cs.meta               |   11 +
 .../Drawing/CommandBuilder2DExtensions.cs          |  587 ++++
 .../Drawing/CommandBuilder2DExtensions.cs.meta     |   11 +
 .../Drawing/CommandBuilderExtensions.cs            |  490 ++++
 .../Drawing/CommandBuilderExtensions.cs.meta       |   11 +
 .../Drawing/Compatibility.cs                       |    4 +
 .../Drawing/Compatibility.cs.meta                  |   11 +
 .../Drawing/Documentation.meta                     |    8 +
 .../Drawing/Documentation/index.md                 |    7 +
 .../Drawing/Documentation/index.md.meta            |    7 +
 .../com.arongranberg.astar/Drawing/Draw.cs         | 1355 +++++++++
 .../com.arongranberg.astar/Drawing/Draw.cs.meta    |   12 +
 .../com.arongranberg.astar/Drawing/DrawingData.cs  | 1712 +++++++++++
 .../Drawing/DrawingData.cs.meta                    |   12 +
 .../Drawing/DrawingManager.cs                      |  788 +++++
 .../Drawing/DrawingManager.cs.meta                 |   11 +
 .../Drawing/DrawingSettings.cs                     |   74 +
 .../Drawing/DrawingSettings.cs.meta                |   11 +
 .../Drawing/DrawingUtilities.cs                    |  122 +
 .../Drawing/DrawingUtilities.cs.meta               |   11 +
 .../com.arongranberg.astar/Drawing/Editor.meta     |    8 +
 .../Drawing/Editor/DrawingEditor.asmdef            |   40 +
 .../Drawing/Editor/DrawingEditor.asmdef.meta       |    7 +
 .../Drawing/Editor/DrawingManagerEditor.cs         |   19 +
 .../Drawing/Editor/DrawingManagerEditor.cs.meta    |   11 +
 .../Drawing/Editor/DrawingSettingsEditor.cs        |   60 +
 .../Drawing/Editor/DrawingSettingsEditor.cs.meta   |   11 +
 .../Drawing/GeometryBuilder.cs                     | 1152 ++++++++
 .../Drawing/GeometryBuilder.cs.meta                |   11 +
 .../Drawing/MonoBehaviourGizmos.cs                 |   40 +
 .../Drawing/MonoBehaviourGizmos.cs.meta            |   11 +
 .../Drawing/PackageTools.meta                      |    8 +
 .../Drawing/PackageTools/Editor.meta               |    8 +
 .../Drawing/PackageTools/Editor/DependencyCheck.cs |   47 +
 .../PackageTools/Editor/DependencyCheck.cs.meta    |   11 +
 .../PackageTools/Editor/PackageToolsEditor.asmdef  |   34 +
 .../Editor/PackageToolsEditor.asmdef.meta          |    7 +
 .../com.arongranberg.astar/Drawing/Palette.cs      |  124 +
 .../com.arongranberg.astar/Drawing/Palette.cs.meta |   11 +
 .../Drawing/PersistentFilter.cs                    |  123 +
 .../Drawing/PersistentFilter.cs.meta               |   11 +
 .../com.arongranberg.astar/Drawing/Resources.meta  |    8 +
 .../Drawing/Resources/aline_common.cginc           |   76 +
 .../Drawing/Resources/aline_common.cginc.meta      |    9 +
 .../Drawing/Resources/aline_common_line.cginc      |  107 +
 .../Drawing/Resources/aline_common_line.cginc.meta |   10 +
 .../Drawing/Resources/aline_common_surface.cginc   |   21 +
 .../Resources/aline_common_surface.cginc.meta      |   10 +
 .../Drawing/Resources/aline_common_text.cginc      |  107 +
 .../Drawing/Resources/aline_common_text.cginc.meta |    7 +
 .../Drawing/Resources/aline_droid_sans_mono.mat    |   71 +
 .../Resources/aline_droid_sans_mono.mat.meta       |    8 +
 .../Drawing/Resources/aline_fallback_font.png      |  Bin 0 -> 30818 bytes
 .../Drawing/Resources/aline_fallback_font.png.meta |  134 +
 .../Drawing/Resources/aline_font.png               |  Bin 0 -> 36393 bytes
 .../Drawing/Resources/aline_font.png.meta          |  106 +
 .../Drawing/Resources/aline_font.ttf               |  Bin 0 -> 117072 bytes
 .../Drawing/Resources/aline_font.ttf.meta          |   23 +
 .../Drawing/Resources/aline_outline.mat            |   58 +
 .../Drawing/Resources/aline_outline.mat.meta       |    8 +
 .../Drawing/Resources/aline_outline.shader         |  165 ++
 .../Drawing/Resources/aline_outline.shader.meta    |    9 +
 .../Drawing/Resources/aline_surface.mat            |   52 +
 .../Drawing/Resources/aline_surface.mat.meta       |    2 +
 .../Drawing/Resources/aline_surface.shader         |  174 ++
 .../Drawing/Resources/aline_surface.shader.meta    |    2 +
 .../Drawing/Resources/aline_text.shader            |   88 +
 .../Drawing/Resources/aline_text.shader.meta       |    9 +
 .../com.arongranberg.astar/Drawing/SDFFont.cs      |  230 ++
 .../com.arongranberg.astar/Drawing/SDFFont.cs.meta |   11 +
 .../Drawing/StreamSplitter.cs                      |  204 ++
 .../Drawing/StreamSplitter.cs.meta                 |   11 +
 .../com.arongranberg.astar/Drawing/changelog.cs    |  283 ++
 .../Drawing/changelog.cs.meta                      |   11 +
 .../com.arongranberg.astar/Drawing/package.json    |   42 +
 .../Drawing/package.json.meta                      |    7 +
 85 files changed, 12795 insertions(+)
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/ALINE.asmdef
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/ALINE.asmdef.meta
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/AlineHDRPCustomPass.cs
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/AlineHDRPCustomPass.cs.meta
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/AlineURPRenderPassFeature.cs
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/AlineURPRenderPassFeature.cs.meta
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/CommandBuilder.cs
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/CommandBuilder.cs.meta
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/CommandBuilder2D.cs
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/CommandBuilder2D.cs.meta
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/CommandBuilder2DExtensions.cs
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/CommandBuilder2DExtensions.cs.meta
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/CommandBuilderExtensions.cs
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/CommandBuilderExtensions.cs.meta
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Compatibility.cs
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Compatibility.cs.meta
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Documentation.meta
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Documentation/index.md
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Documentation/index.md.meta
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Draw.cs
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Draw.cs.meta
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/DrawingData.cs
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/DrawingData.cs.meta
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/DrawingManager.cs
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/DrawingManager.cs.meta
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/DrawingSettings.cs
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/DrawingSettings.cs.meta
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/DrawingUtilities.cs
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/DrawingUtilities.cs.meta
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Editor.meta
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Editor/DrawingEditor.asmdef
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Editor/DrawingEditor.asmdef.meta
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Editor/DrawingManagerEditor.cs
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Editor/DrawingManagerEditor.cs.meta
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Editor/DrawingSettingsEditor.cs
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Editor/DrawingSettingsEditor.cs.meta
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/GeometryBuilder.cs
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/GeometryBuilder.cs.meta
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/MonoBehaviourGizmos.cs
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/MonoBehaviourGizmos.cs.meta
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/PackageTools.meta
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/PackageTools/Editor.meta
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/PackageTools/Editor/DependencyCheck.cs
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/PackageTools/Editor/DependencyCheck.cs.meta
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/PackageTools/Editor/PackageToolsEditor.asmdef
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/PackageTools/Editor/PackageToolsEditor.asmdef.meta
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Palette.cs
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Palette.cs.meta
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/PersistentFilter.cs
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/PersistentFilter.cs.meta
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources.meta
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_common.cginc
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_common.cginc.meta
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_common_line.cginc
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_common_line.cginc.meta
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_common_surface.cginc
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_common_surface.cginc.meta
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_common_text.cginc
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_common_text.cginc.meta
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_droid_sans_mono.mat
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_droid_sans_mono.mat.meta
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_fallback_font.png
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_fallback_font.png.meta
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_font.png
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_font.png.meta
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_font.ttf
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_font.ttf.meta
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_outline.mat
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_outline.mat.meta
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_outline.shader
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_outline.shader.meta
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_surface.mat
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_surface.mat.meta
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_surface.shader
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_surface.shader.meta
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_text.shader
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_text.shader.meta
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/SDFFont.cs
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/SDFFont.cs.meta
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/StreamSplitter.cs
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/StreamSplitter.cs.meta
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/changelog.cs
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/changelog.cs.meta
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/package.json
 create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/package.json.meta
(limited to 'Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing')
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 {
+	/// Custom High Definition Render Pipeline Render Pass for ALINE
+	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 {
+	/// Custom Universal Render Pipeline Render Pass for ALINE
+	public class AlineURPRenderPassFeature : ScriptableRendererFeature {
+		/// Custom Universal Render Pipeline Render Pass for ALINE
+		public class AlineURPRenderPass : ScriptableRenderPass {
+			/// This method is called before executing the render pass
+#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();
+				var resourceData = frameData.Get();
+
+				using (IRasterRenderGraphBuilder builder = renderGraph.AddRasterRenderPass("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 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;
+		}
+
+		/// This method is called when setting up the renderer once per-camera
+		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;
+
+	/// 
+	/// Specifies text alignment relative to an anchor point.
+	///
+	/// 
+	/// Draw.Label2D(transform.position, "Hello World", 14, LabelAlignment.TopCenter);
+	/// 
+	/// 
+	/// // Draw the label 20 pixels below the object
+	/// Draw.Label2D(transform.position, "Hello World", 14, LabelAlignment.TopCenter.withPixelOffset(0, -20));
+	/// 
+	///
+	/// See: 
+	/// See: 
+	/// 
+	public struct LabelAlignment {
+		/// 
+		/// 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.
+		/// 
+		public float2 relativePivot;
+		/// How much to move the text in screen-space
+		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) };
+
+		/// 
+		/// Moves the text by the specified amount of pixels in screen-space.
+		///
+		/// 
+		/// // Draw the label 20 pixels below the object
+		/// Draw.Label2D(transform.position, "Hello World", 14, LabelAlignment.TopCenter.withPixelOffset(0, -20));
+		/// 
+		/// 
+		public LabelAlignment withPixelOffset (float x, float y) {
+			return new LabelAlignment {
+					   relativePivot = this.relativePivot,
+					   pixelOffset = new float2(x, y),
+			};
+		}
+	}
+
+	/// Maximum allowed delay for a job that is drawing to a command buffer
+	public enum AllowedDelay {
+		/// 
+		/// 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.
+		/// 
+		EndOfFrame,
+		/// 
+		/// 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.
+		/// 
+		Infinite,
+	}
+
+	/// Some static fields that need to be in a separate class because Burst doesn't support them
+	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");
+	}
+
+	/// 
+	/// 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 using statement which automatically calls the Dispose method.
+	///
+	/// 
+	/// // 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);
+	/// }
+	/// 
+	///
+	/// Warning: You must call either  or  when you are done with this object to avoid memory leaks.
+	/// 
+	[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;
+			}
+		}
+
+		/// 
+		/// Wrapper for drawing in the XY plane.
+		///
+		/// 
+		/// var p1 = new Vector2(0, 1);
+		/// var p2 = new Vector2(5, 7);
+		///
+		/// // Draw it in the XY plane
+		/// Draw.xy.Line(p1, p2);
+		///
+		/// // Draw it in the XZ plane
+		/// Draw.xz.Line(p1, p2);
+		/// 
+		///
+		/// See: 2d-drawing (view in online documentation for working links)
+		/// See: 
+		/// 
+		public CommandBuilder2D xy => new CommandBuilder2D(this, true);
+
+		/// 
+		/// Wrapper for drawing in the XZ plane.
+		///
+		/// 
+		/// var p1 = new Vector2(0, 1);
+		/// var p2 = new Vector2(5, 7);
+		///
+		/// // Draw it in the XY plane
+		/// Draw.xy.Line(p1, p2);
+		///
+		/// // Draw it in the XZ plane
+		/// Draw.xz.Line(p1, p2);
+		/// 
+		///
+		/// See: 2d-drawing (view in online documentation for working links)
+		/// See: 
+		/// 
+		public CommandBuilder2D xz => new CommandBuilder2D(this, false);
+
+		static readonly float3 DEFAULT_UP = new float3(0, 1, 0);
+
+		/// 
+		/// 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 .
+		/// 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.
+		///
+		/// 
+		/// 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();
+		/// 
+		///
+		/// See: advanced (view in online documentation for working links)
+		/// 
+		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;
+				}
+			}
+		}
+
+		/// Submits this command builder for rendering
+		public void Dispose () {
+			if (uniqueID.isBuiltInCommandBuilder) throw new System.Exception("You cannot dispose a built-in command builder");
+			DisposeInternal();
+		}
+
+		/// 
+		/// 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)
+		/// 
+		/// The job that must complete before this command builder is disposed.
+		/// 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}.
+		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;
+			}
+		}
+
+		/// 
+		/// 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  method) then you must call this method to avoid
+		/// memory leaks.
+		/// 
+		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;
+			}
+		}
+
+		/// 
+		/// 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.
+		/// 
+		public void Preallocate (int size) {
+			Reserve(size);
+		}
+
+		/// Internal rendering command
+		[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;
+		}
+
+		/// Holds rendering data for a line
+		internal struct LineData {
+			public float3 a, b;
+		}
+
+		internal struct LineDataV3 {
+			public Vector3 a, b;
+		}
+
+		/// Holds rendering data for a circle
+		internal struct CircleXZData {
+			public float3 center;
+			public float radius, startAngle, endAngle;
+		}
+
+		/// Holds rendering data for a circle
+		internal struct CircleData {
+			public float3 center;
+			public float3 normal;
+			public float radius;
+		}
+
+		/// Holds rendering data for a sphere
+		internal struct SphereData {
+			public float3 center;
+			public float radius;
+		}
+
+		/// Holds rendering data for a box
+		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;
+		}
+
+		/// Ensures the buffer has room for at least N more bytes
+		[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() where A : struct {
+			Reserve(UnsafeUtility.SizeOf() + UnsafeUtility.SizeOf());
+		}
+
+		[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
+		internal void Reserve() where A : struct where B : struct {
+			Reserve(UnsafeUtility.SizeOf() * 2 + UnsafeUtility.SizeOf() + UnsafeUtility.SizeOf());
+		}
+
+		[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
+		internal void Reserve() where A : struct where B : struct where C : struct {
+			Reserve(UnsafeUtility.SizeOf() * 3 + UnsafeUtility.SizeOf() + UnsafeUtility.SizeOf() + UnsafeUtility.SizeOf());
+		}
+
+		/// 
+		/// Converts a Color to a Color32.
+		/// This method is faster than Unity's native color conversion, especially when using Burst.
+		/// 
+		[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 value) where T : struct {
+			int num = UnsafeUtility.SizeOf();
+			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;
+				}
+			}
+		}
+
+		/// 
+		/// Scope that does nothing.
+		/// Used for optimization in standalone builds.
+		/// 
+		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;
+				}
+			}
+		}
+
+		/// 
+		/// 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.
+		///
+		/// 
+		/// 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);
+		/// }
+		/// 
+		///
+		/// See: 
+		/// 
+		[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 };
+			}
+		}
+
+		/// 
+		/// 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.
+		///
+		/// 
+		/// 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);
+		/// }
+		/// 
+		///
+		/// See: 
+		/// 
+		[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 };
+			}
+		}
+
+		/// 
+		/// Scope to draw multiple things with the same color.
+		///
+		/// 
+		/// 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));
+		///     }
+		/// }
+		/// 
+		///
+		/// 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.
+		/// 
+		[BurstDiscard]
+		public ScopeColor WithColor (Color color) {
+			PushColor(color);
+			unsafe {
+				return new ScopeColor { builder = this };
+			}
+		}
+
+		/// 
+		/// 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.
+		///
+		/// 
+		/// void Update () {
+		///     using (Draw.WithDuration(1.0f)) {
+		///         var offset = Time.time;
+		///         Draw.Line(new Vector3(offset, 0, 0), new Vector3(offset, 0, 1));
+		///     }
+		/// }
+		/// 
+		///
+		/// 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.
+		/// 
+		/// How long the drawn items should persist in seconds.
+
+		[BurstDiscard]
+		public ScopePersist WithDuration (float duration) {
+			PushDuration(duration);
+			unsafe {
+				return new ScopePersist { builder = this };
+			}
+		}
+
+		/// 
+		/// 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.
+		/// 
+		/// Line width in pixels
+		/// If true then sequences of lines that are adjacent will be automatically joined at their vertices. This typically produces nicer polylines without weird gaps.
+		[BurstDiscard]
+		public ScopeLineWidth WithLineWidth (float pixels, bool automaticJoins = true) {
+			PushLineWidth(pixels, automaticJoins);
+			unsafe {
+				return new ScopeLineWidth { builder = this };
+			}
+		}
+
+		/// 
+		/// 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.
+		///
+		/// 
+		/// 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);
+		///     }
+		/// }
+		/// 
+		///
+		/// [Open online documentation to see videos]
+		/// 
+		[BurstDiscard]
+		public ScopeMatrix InLocalSpace (Transform transform) {
+			return WithMatrix(transform.localToWorldMatrix);
+		}
+
+		/// 
+		/// 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: 
+		/// 
+		[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)));
+		}
+
+		/// 
+		/// Multiply all coordinates until the next PopMatrix with the given matrix.
+		/// This differs from  in that this stacks with all previously pushed matrices while  does not.
+		/// 
+		public void PushMatrix (Matrix4x4 matrix) {
+			Reserve();
+			Add(Command.PushMatrix);
+			Add(matrix);
+		}
+
+		/// 
+		/// Multiply all coordinates until the next PopMatrix with the given matrix.
+		/// This differs from  in that this stacks with all previously pushed matrices while  does not.
+		/// 
+		public void PushMatrix (float4x4 matrix) {
+			Reserve();
+			Add(Command.PushMatrix);
+			Add(matrix);
+		}
+
+		/// 
+		/// Multiply all coordinates until the next PopMatrix with the given matrix.
+		/// This differs from  in that this sets the current matrix directly while  stacks with all previously pushed matrices.
+		/// 
+		public void PushSetMatrix (Matrix4x4 matrix) {
+			Reserve();
+			Add(Command.PushSetMatrix);
+			Add((float4x4)matrix);
+		}
+
+		/// 
+		/// Multiply all coordinates until the next PopMatrix with the given matrix.
+		/// This differs from  in that this sets the current matrix directly while  stacks with all previously pushed matrices.
+		/// 
+		public void PushSetMatrix (float4x4 matrix) {
+			Reserve();
+			Add(Command.PushSetMatrix);
+			Add(matrix);
+		}
+
+		/// Pops a matrix from the stack
+		public void PopMatrix () {
+			Reserve(4);
+			Add(Command.PopMatrix);
+		}
+
+		/// 
+		/// 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.
+		/// 
+		public void PushColor (Color color) {
+			Reserve();
+			Add(Command.PushColor);
+			Add(ConvertColor(color));
+		}
+
+		/// Pops a color from the stack
+		public void PopColor () {
+			Reserve(4);
+			Add(Command.PopColor);
+		}
+
+		/// 
+		/// 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.
+		/// 
+		public void PushDuration (float duration) {
+			Reserve();
+			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 });
+		}
+
+		/// Pops a duration scope from the stack
+		public void PopDuration () {
+			Reserve(4);
+			Add(Command.PopPersist);
+		}
+
+		/// 
+		/// 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 
+		/// 
+		[System.Obsolete("Renamed to PushDuration for consistency")]
+		public void PushPersist (float duration) {
+			PushDuration(duration);
+		}
+
+		/// 
+		/// Pops a persist scope from the stack.
+		/// Deprecated: Renamed to 
+		/// 
+		[System.Obsolete("Renamed to PopDuration for consistency")]
+		public void PopPersist () {
+			PopDuration();
+		}
+
+		/// 
+		/// 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.
+		/// 
+		/// Line width in pixels
+		/// If true then sequences of lines that are adjacent will be automatically joined at their vertices. This typically produces nicer polylines without weird gaps.
+		public void PushLineWidth (float pixels, bool automaticJoins = true) {
+			if (pixels < 0) throw new System.ArgumentOutOfRangeException("pixels", "Line width must be positive");
+
+			Reserve();
+			Add(Command.PushLineWidth);
+			Add(new LineWidthData { pixels = pixels, automaticJoins = automaticJoins });
+		}
+
+		/// Pops a line width scope from the stack
+		public void PopLineWidth () {
+			Reserve(4);
+			Add(Command.PopLineWidth);
+		}
+
+		/// 
+		/// Draws a line between two points.
+		///
+		/// [Open online documentation to see images]
+		///
+		/// 
+		/// void Update () {
+		///     Draw.Line(Vector3.zero, Vector3.up);
+		/// }
+		/// 
+		/// 
+		public void Line (float3 a, float3 b) {
+			Reserve();
+			Add(Command.Line);
+			Add(new LineData { a = a, b = b });
+		}
+
+		/// 
+		/// Draws a line between two points.
+		///
+		/// [Open online documentation to see images]
+		///
+		/// 
+		/// void Update () {
+		///     Draw.Line(Vector3.zero, Vector3.up);
+		/// }
+		/// 
+		/// 
+		public void Line (Vector3 a, Vector3 b) {
+			Reserve();
+			// 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;
+			}
+		}
+
+		/// 
+		/// Draws a line between two points.
+		///
+		/// [Open online documentation to see images]
+		///
+		/// 
+		/// void Update () {
+		///     Draw.Line(Vector3.zero, Vector3.up);
+		/// }
+		/// 
+		/// 
+		public void Line (Vector3 a, Vector3 b, Color color) {
+			Reserve();
+			// 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;
+			}
+		}
+
+		/// 
+		/// 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]
+		///
+		/// 
+		/// Draw.Ray(Vector3.zero, Vector3.up);
+		/// 
+		/// 
+		public void Ray (float3 origin, float3 direction) {
+			Line(origin, origin + direction);
+		}
+
+		/// 
+		/// Draws a ray with a given length.
+		///
+		/// [Open online documentation to see images]
+		///
+		/// 
+		/// Draw.Ray(Camera.main.ScreenPointToRay(Vector3.zero), 10);
+		/// 
+		/// 
+		public void Ray (Ray ray, float length) {
+			Line(ray.origin, ray.origin + ray.direction * length);
+		}
+
+		/// 
+		/// 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]
+		/// 
+		/// 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);
+		/// 
+		///
+		/// See: 
+		/// 
+		/// Center of the imaginary circle that the arc is part of.
+		/// Starting point of the arc.
+		/// End point of the arc.
+		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();
+			}
+		}
+
+		/// 
+		/// 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: 
+		/// See: 
+		/// 
+		/// Center of the circle or arc.
+		/// Radius of the circle or arc.
+		/// Starting angle in radians. 0 corrsponds to the positive X axis.
+		/// End angle in radians.
+		[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();
+			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();
+			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);
+
+		/// 
+		/// 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: 
+		/// 
+		/// Center of the circle or arc.
+		/// Radius of the circle or arc.
+		/// Starting angle in radians. 0 corrsponds to the positive X axis.
+		/// End angle in radians.
+		[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();
+		}
+
+		/// 
+		/// Draws a circle.
+		///
+		/// [Open online documentation to see images]
+		///
+		/// Note: This overload does not allow you to draw an arc. For that purpose use ,  or  instead.
+		/// 
+		public void Circle (float3 center, float3 normal, float radius) {
+			Reserve();
+			Add(Command.Circle);
+			Add(new CircleData { center = center, normal = normal, radius = radius });
+		}
+
+		/// 
+		/// 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]
+		/// 
+		/// 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);
+		/// 
+		///
+		/// See: 
+		/// 
+		/// Center of the imaginary circle that the arc is part of.
+		/// Starting point of the arc.
+		/// End point of the arc.
+		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();
+			}
+		}
+
+		/// 
+		/// 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: 
+		/// See: 
+		/// 
+		/// Center of the disc or solid arc.
+		/// Radius of the disc or solid arc.
+		/// Starting angle in radians. 0 corrsponds to the positive X axis.
+		/// End angle in radians.
+		[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();
+			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();
+			Add(Command.DiscXZ | Command.PushColorInline);
+			Add(ConvertColor(color));
+			Add(new CircleXZData { center = center, radius = radius, startAngle = startAngle, endAngle = endAngle });
+		}
+
+		/// 
+		/// 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: 
+		/// See: 
+		/// 
+		/// Center of the disc or solid arc.
+		/// Radius of the disc or solid arc.
+		/// Starting angle in radians. 0 corrsponds to the positive X axis.
+		/// End angle in radians.
+		[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();
+		}
+
+		/// 
+		/// Draws a disc.
+		///
+		/// [Open online documentation to see images]
+		///
+		/// Note: This overload does not allow you to draw an arc. For that purpose use  or  instead.
+		/// 
+		public void SolidCircle (float3 center, float3 normal, float radius) {
+			Reserve();
+			Add(Command.Disc);
+			Add(new CircleData { center = center, normal = normal, radius = radius });
+		}
+
+		/// 
+		/// 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]
+		/// 
+		public void SphereOutline (float3 center, float radius) {
+			Reserve();
+			Add(Command.SphereOutline);
+			Add(new SphereData { center = center, radius = radius });
+		}
+
+		/// 
+		/// Draws a cylinder.
+		/// The cylinder's bottom circle will be centered at the bottom parameter and similarly for the top circle.
+		///
+		/// 
+		/// // 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);
+		/// 
+		///
+		/// [Open online documentation to see images]
+		/// 
+		public void WireCylinder (float3 bottom, float3 top, float radius) {
+			WireCylinder(bottom, top - bottom, math.length(top - bottom), radius);
+		}
+
+		/// 
+		/// Draws a cylinder.
+		///
+		/// 
+		/// // 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);
+		/// 
+		///
+		/// [Open online documentation to see images]
+		/// 
+		/// The center of the cylinder's "bottom" circle.
+		/// The cylinder's main axis. Does not have to be normalized. If zero, nothing will be drawn.
+		/// The length of the cylinder, as measured along it's main axis.
+		/// The radius of the cylinder.
+		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();
+		}
+
+		/// 
+		/// 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.
+		/// 
+		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);
+		}
+
+		/// 
+		/// Draws a capsule with a (start,end) parameterization.
+		///
+		/// The behavior of this method matches common Unity APIs such as Physics.CheckCapsule.
+		///
+		/// 
+		/// // 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);
+		/// 
+		///
+		/// [Open online documentation to see images]
+		/// 
+		/// Center of the start hemisphere of the capsule.
+		/// Center of the end hemisphere of the capsule.
+		/// Radius of the capsule.
+		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
+		/// 
+		/// Draws a capsule with a (position,direction/length) parameterization.
+		///
+		/// 
+		/// // 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);
+		/// 
+		///
+		/// [Open online documentation to see images]
+		/// 
+		/// One endpoint of the capsule. This is at the edge of the capsule, not at the center of one of the hemispheres.
+		/// The main axis of the capsule. Does not have to be normalized. If zero, nothing will be drawn.
+		/// Distance between the two endpoints of the capsule. The length will be clamped to be at least 2*radius.
+		/// The radius of the capsule.
+		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();
+			}
+		}
+
+		/// 
+		/// Draws a wire sphere.
+		///
+		/// [Open online documentation to see images]
+		///
+		/// 
+		/// // Draw a wire sphere at the origin with a radius of 0.5
+		/// Draw.WireSphere(Vector3.zero, 0.5f, Color.black);
+		/// 
+		///
+		/// See: 
+		/// 
+		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);
+		}
+
+		/// 
+		/// Draws lines through a sequence of points.
+		///
+		/// [Open online documentation to see images]
+		/// 
+		/// // 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);
+		/// 
+		/// 
+		/// Sequence of points to draw lines through
+		/// If true a line will be drawn from the last point in the sequence back to the first point.
+		[BurstDiscard]
+		public void Polyline (List points, bool cycle = false) {
+			for (int i = 0; i < points.Count - 1; i++) {
+				Line(points[i], points[i+1]);
+			}
+			if (cycle && points.Count > 1) Line(points[points.Count - 1], points[0]);
+		}
+
+		/// 
+		/// Draws lines through a sequence of points.
+		///
+		/// [Open online documentation to see images]
+		/// 
+		/// // 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);
+		/// 
+		/// 
+		/// Sequence of points to draw lines through
+		/// If true a line will be drawn from the last point in the sequence back to the first point.
+		public void Polyline(T points, bool cycle = false) where T : IReadOnlyList {
+			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]);
+		}
+
+		/// 
+		/// Draws lines through a sequence of points.
+		///
+		/// [Open online documentation to see images]
+		/// 
+		/// // 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);
+		/// 
+		/// 
+		/// Sequence of points to draw lines through
+		/// If true a line will be drawn from the last point in the sequence back to the first point.
+		[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]);
+		}
+
+		/// 
+		/// Draws lines through a sequence of points.
+		///
+		/// [Open online documentation to see images]
+		/// 
+		/// // 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);
+		/// 
+		/// 
+		/// Sequence of points to draw lines through
+		/// If true a line will be drawn from the last point in the sequence back to the first point.
+		[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]);
+		}
+
+		/// 
+		/// Draws lines through a sequence of points.
+		///
+		/// [Open online documentation to see images]
+		/// 
+		/// // 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);
+		/// 
+		/// 
+		/// Sequence of points to draw lines through
+		/// If true a line will be drawn from the last point in the sequence back to the first point.
+		public void Polyline (NativeArray points, bool cycle = false) {
+			for (int i = 0; i < points.Length - 1; i++) {
+				Line(points[i], points[i+1]);
+			}
+			if (cycle && points.Length > 1) Line(points[points.Length - 1], points[0]);
+		}
+
+		/// Determines the symbol to use for 
+		public enum SymbolDecoration {
+			/// 
+			/// 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]
+			/// 
+			None,
+			/// 
+			/// An arrowhead symbol.
+			///
+			/// [Open online documentation to see images]
+			/// 
+			ArrowHead,
+			/// 
+			/// A circle symbol.
+			///
+			/// [Open online documentation to see images]
+			/// 
+			Circle,
+		}
+
+		/// 
+		/// Draws a dashed line between two points.
+		///
+		/// 
+		/// Draw.DashedPolyline(points, 0.1f, 0.1f, color);
+		/// 
+		///
+		/// [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: 
+		/// 
+		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);
+		}
+
+		/// 
+		/// Draws a dashed line through a sequence of points.
+		///
+		/// 
+		/// Draw.DashedPolyline(points, 0.1f, 0.1f, color);
+		/// 
+		///
+		/// [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  struct directly.
+		///
+		/// 
+		/// 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]);
+		///     }
+		/// }
+		/// 
+		///
+		/// See: 
+		/// See: 
+		/// 
+		public void DashedPolyline (List 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]);
+			}
+		}
+
+		/// 
+		/// Helper for drawing a polyline with symbols at regular intervals.
+		///
+		/// 
+		/// 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));
+		/// 
+		///
+		/// [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  helper function instead.
+		///
+		/// 
+		/// 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]);
+		///     }
+		/// }
+		/// 
+		///
+		/// [Open online documentation to see images]
+		/// 
+		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;
+
+			/// Create a new polyline with symbol generator.
+			/// The symbol to use
+			/// The size of the symbol. In case of a circle, this is the diameter.
+			/// The padding on both sides of the symbol between the symbol and the line.
+			/// The spacing between symbols. This is the distance between the centers of the symbols.
+			/// If true, the symbols will be reversed. For cicles this has no effect, but arrowhead symbols will be reversed.
+			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;
+			}
+
+			/// 
+			/// Move to a new point.
+			///
+			/// This will draw the symbols and line segments between the previous point and the new point.
+			/// 
+			/// 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.
+			/// The next point in the polyline to move to.
+			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;
+			}
+		}
+
+		/// 
+		/// Draws the outline of a box which is axis-aligned.
+		///
+		/// [Open online documentation to see images]
+		/// 
+		/// Center of the box
+		/// Width of the box along all dimensions
+		public void WireBox (float3 center, float3 size) {
+			Reserve();
+			Add(Command.WireBox);
+			Add(new BoxData { center = center, size = size });
+		}
+
+		/// 
+		/// Draws the outline of a box.
+		///
+		/// [Open online documentation to see images]
+		/// 
+		/// Center of the box
+		/// Rotation of the box
+		/// Width of the box along all dimensions
+		public void WireBox (float3 center, quaternion rotation, float3 size) {
+			PushMatrix(float4x4.TRS(center, rotation, size));
+			WireBox(float3.zero, new float3(1, 1, 1));
+			PopMatrix();
+		}
+
+		/// 
+		/// Draws the outline of a box.
+		///
+		/// [Open online documentation to see images]
+		/// 
+		public void WireBox (Bounds bounds) {
+			WireBox(bounds.center, bounds.size);
+		}
+
+		/// 
+		/// Draws a wire mesh.
+		/// Every single edge of the mesh will be drawn using a  command.
+		///
+		/// 
+		/// var go = GameObject.CreatePrimitive(PrimitiveType.Sphere);
+		/// go.transform.position = new Vector3(0, 0, 0);
+		/// using (Draw.InLocalSpace(go.transform)) {
+		///     Draw.WireMesh(go.GetComponent().sharedMesh, color);
+		/// }
+		/// 
+		/// [Open online documentation to see images]
+		///
+		/// See: 
+		///
+		/// Version: Supported in Unity 2020.1 or later.
+		/// 
+		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
+		}
+
+		/// 
+		/// Draws a wire mesh.
+		/// Every single edge of the mesh will be drawn using a  command.
+		///
+		/// 
+		/// var go = GameObject.CreatePrimitive(PrimitiveType.Sphere);
+		/// go.transform.position = new Vector3(0, 0, 0);
+		/// using (Draw.InLocalSpace(go.transform)) {
+		///     Draw.WireMesh(go.GetComponent().sharedMesh, color);
+		/// }
+		/// 
+		/// [Open online documentation to see images]
+		///
+		/// See: 
+		///
+		/// Version: Supported in Unity 2020.1 or later.
+		/// 
+		public void WireMesh (NativeArray vertices, NativeArray 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
+		/// Helper job for 
+		[BurstCompile]
+		class JobWireMesh {
+			public delegate void JobWireMeshDelegate(ref Mesh.MeshData rawMeshData, ref CommandBuilder draw);
+
+			public static readonly JobWireMeshDelegate JobWireMeshFunctionPointer = BurstCompiler.CompileFunctionPointer(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(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(maxIndices, Allocator.Temp, NativeArrayOptions.UninitializedMemory);
+				var verts = new NativeArray(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
+
+		/// 
+		/// Draws a solid mesh.
+		/// The mesh will be drawn with a solid color.
+		///
+		/// 
+		/// var go = GameObject.CreatePrimitive(PrimitiveType.Sphere);
+		/// go.transform.position = new Vector3(0, 0, 0);
+		/// using (Draw.InLocalSpace(go.transform)) {
+		///     Draw.SolidMesh(go.GetComponent().sharedMesh, color);
+		/// }
+		/// 
+		/// [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: 
+		/// 
+		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);
+		}
+
+		/// 
+		/// 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?
+		/// 
+		[BurstDiscard]
+		public void SolidMesh (List vertices, List triangles, List 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);
+		}
+
+		/// 
+		/// 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?
+		/// 
+		[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);
+		}
+
+		/// 
+		/// Draws a 3D cross.
+		///
+		/// [Open online documentation to see images]
+		/// 
+		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));
+		}
+
+		/// 
+		/// Draws a cross in the XZ plane.
+		///
+		/// [Open online documentation to see images]
+		/// 
+		[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));
+		}
+
+		/// 
+		/// Draws a cross in the XY plane.
+		///
+		/// [Open online documentation to see images]
+		/// 
+		[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));
+		}
+
+		/// Returns a point on a cubic bezier curve. t is clamped between 0 and 1
+		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;
+		}
+
+		/// 
+		/// 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
+		/// 
+		/// Start point
+		/// First control point
+		/// Second control point
+		/// End point
+		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;
+			}
+		}
+
+		/// 
+		/// 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: 
+		/// 
+		/// The curve will smoothly pass through each point in the list in order.
+		public void CatmullRom (List 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]);
+			}
+		}
+
+		/// 
+		/// 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: 
+		/// 
+		/// First control point
+		/// Second control point. Start of the curve.
+		/// Third control point. End of the curve.
+		/// Fourth control point.
+		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);
+		}
+
+		/// 
+		/// 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: 
+		/// See: 
+		/// 
+		/// Base of the arrow.
+		/// Head of the arrow.
+		public void Arrow (float3 from, float3 to) {
+			ArrowRelativeSizeHead(from, to, DEFAULT_UP, 0.2f);
+		}
+
+		/// 
+		/// Draws an arrow between two points.
+		///
+		/// [Open online documentation to see images]
+		///
+		/// See: 
+		/// See: 
+		/// 
+		/// Base of the arrow.
+		/// Head of the arrow.
+		/// Up direction of the world, the arrowhead plane will be as perpendicular as possible to this direction. Defaults to Vector3.up.
+		/// The size of the arrowhead in world units.
+		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));
+			}
+		}
+
+		/// 
+		/// 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: 
+		/// 
+		/// Base of the arrow.
+		/// Head of the arrow.
+		/// Up direction of the world, the arrowhead plane will be as perpendicular as possible to this direction.
+		/// The length of the arrowhead is the distance between from and to multiplied by this fraction. Should be between 0 and 1.
+		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);
+		}
+
+		/// 
+		/// Draws an arrowhead at a point.
+		///
+		/// 
+		/// Draw.Arrowhead(Vector3.zero, Vector3.forward, 0.75f, color);
+		/// 
+		/// [Open online documentation to see images]
+		///
+		/// See: 
+		/// See: 
+		/// 
+		/// Center of the arrowhead.
+		/// Direction the arrow is pointing.
+		/// Distance from the center to each corner of the arrowhead.
+		public void Arrowhead (float3 center, float3 direction, float radius) {
+			Arrowhead(center, direction, DEFAULT_UP, radius);
+		}
+
+		/// 
+		/// Draws an arrowhead at a point.
+		///
+		/// 
+		/// Draw.Arrowhead(Vector3.zero, Vector3.forward, 0.75f, color);
+		/// 
+		/// [Open online documentation to see images]
+		///
+		/// See: 
+		/// See: 
+		/// 
+		/// Center of the arrowhead.
+		/// Direction the arrow is pointing.
+		/// Up direction of the world, the arrowhead plane will be as perpendicular as possible to this direction. Defaults to Vector3.up. Must be normalized.
+		/// Distance from the center to each corner of the arrowhead.
+		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);
+		}
+
+		/// 
+		/// 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: 
+		/// 
+		/// Point around which the arc is centered
+		/// Direction the arrow is pointing
+		/// Distance from origin that the arrow starts.
+		/// Width of the arrowhead in degrees (defaults to 60). Should be between 0 and 90.
+		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();
+		}
+
+		/// 
+		/// Draws a grid of lines.
+		///
+		/// 
+		/// Draw.xz.WireGrid(Vector3.zero, new int2(3, 3), new float2(1, 1), color);
+		/// 
+		/// [Open online documentation to see images]
+		/// 
+		/// Center of the grid
+		/// Rotation of the grid. The grid will be aligned to the X and Z axes of the rotation.
+		/// Number of cells of the grid. Should be greater than 0.
+		/// Total size of the grid along the X and Z axes.
+		public void WireGrid (float3 center, 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();
+		}
+
+		/// 
+		/// Draws a triangle outline.
+		///
+		/// 
+		/// Draw.WireTriangle(new Vector3(-0.5f, 0, 0), new Vector3(0, 1, 0), new Vector3(0.5f, 0, 0), Color.black);
+		/// 
+		/// [Open online documentation to see images]
+		///
+		/// See: 
+		/// See: 
+		/// See: 
+		/// 
+		/// First corner of the triangle
+		/// Second corner of the triangle
+		/// Third corner of the triangle
+		public void WireTriangle (float3 a, float3 b, float3 c) {
+			Line(a, b);
+			Line(b, c);
+			Line(c, a);
+		}
+
+		/// 
+		/// Draws a rectangle outline.
+		/// The rectangle will be aligned to the X and Z axes.
+		///
+		/// 
+		/// Draw.xz.WireRectangle(new Vector3(0f, 0, 0), new Vector2(1, 1), Color.black);
+		/// 
+		/// [Open online documentation to see images]
+		///
+		/// See: 
+		/// 
+		[System.Obsolete("Use Draw.xz.WireRectangle instead")]
+		public void WireRectangleXZ (float3 center, float2 size) {
+			WireRectangle(center, quaternion.identity, size);
+		}
+
+		/// 
+		/// Draws a rectangle outline.
+		/// The rectangle will be oriented along the rotation's X and Z axes.
+		///
+		/// 
+		/// Draw.WireRectangle(new Vector3(0f, 0, 0), Quaternion.identity, new Vector2(1, 1), Color.black);
+		/// 
+		/// [Open online documentation to see images]
+		///
+		/// This is identical to , but this name is added for consistency.
+		///
+		/// See: 
+		/// 
+		public void WireRectangle (float3 center, quaternion rotation, float2 size) {
+			WirePlane(center, rotation, size);
+		}
+
+		/// 
+		/// Draws a rectangle outline.
+		/// The rectangle corners are assumed to be in XY space.
+		/// This is particularly useful when combined with .
+		///
+		/// 
+		/// using (Draw.InScreenSpace(Camera.main)) {
+		///     Draw.xy.WireRectangle(new Rect(10, 10, 100, 100), Color.black);
+		/// }
+		/// 
+		/// [Open online documentation to see images]
+		///
+		/// See: 
+		/// See: 
+		/// See: 
+		/// 
+		[System.Obsolete("Use Draw.xy.WireRectangle instead")]
+		public void WireRectangle (Rect rect) {
+			xy.WireRectangle(rect);
+		}
+
+
+		/// 
+		/// Draws a triangle outline.
+		///
+		/// 
+		/// Draw.WireTriangle(Vector3.zero, Quaternion.identity, 0.5f, color);
+		/// 
+		/// [Open online documentation to see images]
+		///
+		/// Note: This is a convenience wrapper for 
+		///
+		/// See: 
+		/// 
+		/// Center of the triangle.
+		/// Rotation of the triangle. The first vertex will be radius units in front of center as seen from the rotation's point of view.
+		/// Distance from the center to each vertex.
+		public void WireTriangle (float3 center, quaternion rotation, float radius) {
+			WirePolygon(center, 3, rotation, radius);
+		}
+
+		/// 
+		/// Draws a pentagon outline.
+		///
+		/// 
+		/// Draw.WirePentagon(Vector3.zero, Quaternion.identity, 0.5f, color);
+		/// 
+		/// [Open online documentation to see images]
+		///
+		/// Note: This is a convenience wrapper for 
+		/// 
+		/// Center of the polygon.
+		/// Rotation of the polygon. The first vertex will be radius units in front of center as seen from the rotation's point of view.
+		/// Distance from the center to each vertex.
+		public void WirePentagon (float3 center, quaternion rotation, float radius) {
+			WirePolygon(center, 5, rotation, radius);
+		}
+
+		/// 
+		/// Draws a hexagon outline.
+		///
+		/// 
+		/// Draw.WireHexagon(Vector3.zero, Quaternion.identity, 0.5f, color);
+		/// 
+		/// [Open online documentation to see images]
+		///
+		/// Note: This is a convenience wrapper for 
+		/// 
+		/// Center of the polygon.
+		/// Rotation of the polygon. The first vertex will be radius units in front of center as seen from the rotation's point of view.
+		/// Distance from the center to each vertex.
+		public void WireHexagon (float3 center, quaternion rotation, float radius) {
+			WirePolygon(center, 6, rotation, radius);
+		}
+
+		/// 
+		/// Draws a regular polygon outline.
+		///
+		/// 
+		/// 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);
+		/// 
+		/// [Open online documentation to see images]
+		///
+		/// See: 
+		/// See: 
+		/// See: 
+		/// 
+		/// Center of the polygon.
+		/// Number of corners (and sides) of the polygon.
+		/// Rotation of the polygon. The first vertex will be radius units in front of center as seen from the rotation's point of view.
+		/// Distance from the center to each vertex.
+		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();
+		}
+
+		/// 
+		/// Draws a solid rectangle.
+		/// The rectangle corners are assumed to be in XY space.
+		/// This is particularly useful when combined with .
+		///
+		/// Behind the scenes this is implemented using .
+		///
+		/// 
+		/// using (Draw.InScreenSpace(Camera.main)) {
+		///     Draw.xy.SolidRectangle(new Rect(10, 10, 100, 100), Color.black);
+		/// }
+		/// 
+		/// [Open online documentation to see images]
+		///
+		/// See: 
+		/// See: 
+		/// See: 
+		/// 
+		[System.Obsolete("Use Draw.xy.SolidRectangle instead")]
+		public void SolidRectangle (Rect rect) {
+			xy.SolidRectangle(rect);
+		}
+
+		/// 
+		/// Draws a solid plane.
+		///
+		/// 
+		/// Draw.SolidPlane(new float3(0, 0, 0), new float3(0, 1, 0), 1.0f, color);
+		/// 
+		/// [Open online documentation to see images]
+		/// 
+		/// Center of the visualized plane.
+		/// Direction perpendicular to the plane. If this is (0,0,0) then nothing will be rendered.
+		/// Width and height of the visualized plane.
+		public void SolidPlane (float3 center, float3 normal, float2 size) {
+			if (math.any(normal)) {
+				SolidPlane(center, Quaternion.LookRotation(calculateTangent(normal), normal), size);
+			}
+		}
+
+		/// 
+		/// Draws a solid plane.
+		///
+		/// The plane will lie in the XZ plane with respect to the rotation.
+		///
+		/// 
+		/// Draw.SolidPlane(new float3(0, 0, 0), new float3(0, 1, 0), 1.0f, color);
+		/// 
+		/// [Open online documentation to see images]
+		/// 
+		/// Center of the visualized plane.
+		/// Width and height of the visualized plane.
+		public void SolidPlane (float3 center, quaternion rotation, float2 size) {
+			PushMatrix(float4x4.TRS(center, rotation, new float3(size.x, 0, size.y)));
+			Reserve();
+			Add(Command.Box);
+			Add(new BoxData { center = 0, size = 1 });
+			PopMatrix();
+		}
+
+		/// Returns an arbitrary vector which is orthogonal to the given one
+		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;
+		}
+
+		/// 
+		/// Draws a wire plane.
+		///
+		/// 
+		/// Draw.WirePlane(new float3(0, 0, 0), new float3(0, 1, 0), 1.0f, color);
+		/// 
+		/// [Open online documentation to see images]
+		/// 
+		/// Center of the visualized plane.
+		/// Direction perpendicular to the plane. If this is (0,0,0) then nothing will be rendered.
+		/// Width and height of the visualized plane.
+		public void WirePlane (float3 center, float3 normal, float2 size) {
+			if (math.any(normal)) {
+				WirePlane(center, Quaternion.LookRotation(calculateTangent(normal), normal), size);
+			}
+		}
+
+		/// 
+		/// Draws a wire plane.
+		///
+		/// This is identical to , but it is included for consistency.
+		///
+		/// 
+		/// Draw.WirePlane(new float3(0, 0, 0), new float3(0, 1, 0), 1.0f, color);
+		/// 
+		/// [Open online documentation to see images]
+		/// 
+		/// Center of the visualized plane.
+		/// Rotation of the plane. The plane will lie in the XZ plane with respect to the rotation.
+		/// Width and height of the visualized plane.
+		public void WirePlane (float3 center, quaternion rotation, float2 size) {
+			Reserve();
+			Add(Command.WirePlane);
+			Add(new PlaneData { center = center, rotation = rotation, size = size });
+		}
+
+		/// 
+		/// Draws a plane and a visualization of its normal.
+		///
+		/// 
+		/// Draw.PlaneWithNormal(new float3(0, 0, 0), new float3(0, 1, 0), 1.0f, color);
+		/// 
+		/// [Open online documentation to see images]
+		/// 
+		/// Center of the visualized plane.
+		/// Direction perpendicular to the plane. If this is (0,0,0) then nothing will be rendered.
+		/// Width and height of the visualized plane.
+		public void PlaneWithNormal (float3 center, float3 normal, float2 size) {
+			if (math.any(normal)) {
+				PlaneWithNormal(center, Quaternion.LookRotation(calculateTangent(normal), normal), size);
+			}
+		}
+
+		/// 
+		/// Draws a plane and a visualization of its normal.
+		///
+		/// 
+		/// Draw.PlaneWithNormal(new float3(0, 0, 0), new float3(0, 1, 0), 1.0f, color);
+		/// 
+		/// [Open online documentation to see images]
+		/// 
+		/// Center of the visualized plane.
+		/// Rotation of the plane. The plane will lie in the XZ plane with respect to the rotation.
+		/// Width and height of the visualized plane.
+		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);
+		}
+
+		/// 
+		/// Draws a solid triangle.
+		///
+		/// 
+		/// Draw.xy.SolidTriangle(new float2(-0.43f, -0.25f), new float2(0, 0.5f), new float2(0.43f, -0.25f), color);
+		/// 
+		/// [Open online documentation to see images]
+		///
+		/// Note: If you are going to be drawing lots of triangles it's better to use  instead as it will be more efficient.
+		///
+		/// See: 
+		/// See: 
+		/// 
+		/// First corner of the triangle.
+		/// Second corner of the triangle.
+		/// Third corner of the triangle.
+		public void SolidTriangle (float3 a, float3 b, float3 c) {
+			Reserve();
+			Add(Command.SolidTriangle);
+			Add(new TriangleData { a = a, b = b, c = c });
+		}
+
+		/// 
+		/// Draws a solid box.
+		///
+		/// 
+		/// Draw.SolidBox(new float3(0, 0, 0), new float3(1, 1, 1), color);
+		/// 
+		/// [Open online documentation to see images]
+		/// 
+		/// Center of the box
+		/// Width of the box along all dimensions
+		public void SolidBox (float3 center, float3 size) {
+			Reserve();
+			Add(Command.Box);
+			Add(new BoxData { center = center, size = size });
+		}
+
+		/// 
+		/// Draws a solid box.
+		///
+		/// 
+		/// Draw.SolidBox(new float3(0, 0, 0), new float3(1, 1, 1), color);
+		/// 
+		/// [Open online documentation to see images]
+		/// 
+		/// Bounding box of the box
+		public void SolidBox (Bounds bounds) {
+			SolidBox(bounds.center, bounds.size);
+		}
+
+		/// 
+		/// Draws a solid box.
+		///
+		/// 
+		/// Draw.SolidBox(new float3(0, 0, 0), new float3(1, 1, 1), color);
+		/// 
+		/// [Open online documentation to see images]
+		/// 
+		/// Center of the box
+		/// Rotation of the box
+		/// Width of the box along all dimensions
+		public void SolidBox (float3 center, quaternion rotation, float3 size) {
+			PushMatrix(float4x4.TRS(center, rotation, size));
+			SolidBox(float3.zero, Vector3.one);
+			PopMatrix();
+		}
+
+		/// 
+		/// Draws a label in 3D space.
+		///
+		/// The default alignment is .
+		///
+		/// 
+		/// Draw.Label3D(new float3(0.2f, -1f, 0.2f), Quaternion.Euler(45, -110, -90), "Label", 1, LabelAlignment.Center, color);
+		/// 
+		/// [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 (?).
+		/// 
+		/// Position in 3D space.
+		/// Rotation in 3D space.
+		/// Text to display.
+		/// 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.
+		public void Label3D (float3 position, quaternion rotation, string text, float size) {
+			Label3D(position, rotation, text, size, LabelAlignment.MiddleLeft);
+		}
+
+		/// 
+		/// Draws a label in 3D space.
+		///
+		/// 
+		/// Draw.Label3D(new float3(0.2f, -1f, 0.2f), Quaternion.Euler(45, -110, -90), "Label", 1, LabelAlignment.Center, color);
+		/// 
+		/// [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.
+		/// 
+		/// Position in 3D space.
+		/// Rotation in 3D space.
+		/// Text to display.
+		/// 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.
+		/// How to align the text relative to the given position.
+		public void Label3D (float3 position, quaternion rotation, string text, float size, LabelAlignment alignment) {
+			AssertBufferExists();
+			var g = gizmos.Target as DrawingData;
+			Reserve();
+			Add(Command.Text3D);
+			Add(new TextData3D { center = position, rotation = rotation, numCharacters = text.Length, size = size, alignment = alignment });
+
+			Reserve(UnsafeUtility.SizeOf() * 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);
+			}
+		}
+
+		/// 
+		/// Draws a label in 3D space aligned with the camera.
+		///
+		/// The default alignment is .
+		///
+		/// 
+		/// Draw.Label2D(Vector3.zero, "Label", 48, LabelAlignment.Center, color);
+		/// 
+		/// [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 (?).
+		/// 
+		/// Position in 3D space.
+		/// Text to display.
+		/// 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.
+		public void Label2D (float3 position, string text, float sizeInPixels = 14) {
+			Label2D(position, text, sizeInPixels, LabelAlignment.MiddleLeft);
+		}
+
+		/// 
+		/// Draws a label in 3D space aligned with the camera.
+		///
+		/// 
+		/// Draw.Label2D(Vector3.zero, "Label", 48, LabelAlignment.Center, color);
+		/// 
+		/// [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.
+		/// 
+		/// Position in 3D space.
+		/// Text to display.
+		/// 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.
+		/// How to align the text relative to the given position.
+		public void Label2D (float3 position, string text, float sizeInPixels, LabelAlignment alignment) {
+			AssertBufferExists();
+			var g = gizmos.Target as DrawingData;
+			Reserve();
+			Add(Command.Text);
+			Add(new TextData { center = position, numCharacters = text.Length, sizeInPixels = sizeInPixels, alignment = alignment });
+
+			Reserve(UnsafeUtility.SizeOf() * 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
+		/// 
+		/// Draws a label in 3D space aligned with the camera.
+		///
+		/// 
+		/// // 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);
+		/// }
+		/// 
+		/// [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.
+		/// 
+		/// Position in 3D space.
+		/// Text to display.
+		/// 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.
+		public void Label2D (float3 position, ref FixedString32Bytes text, float sizeInPixels = 14) {
+			Label2D(position, ref text, sizeInPixels, LabelAlignment.MiddleLeft);
+		}
+
+		/// \copydocref{Label2D(float3,FixedString32Bytes,float)}
+		public void Label2D (float3 position, ref FixedString64Bytes text, float sizeInPixels = 14) {
+			Label2D(position, ref text, sizeInPixels, LabelAlignment.MiddleLeft);
+		}
+
+		/// \copydocref{Label2D(float3,FixedString32Bytes,float)}
+		public void Label2D (float3 position, ref FixedString128Bytes text, float sizeInPixels = 14) {
+			Label2D(position, ref text, sizeInPixels, LabelAlignment.MiddleLeft);
+		}
+
+		/// \copydocref{Label2D(float3,FixedString32Bytes,float)}
+		public void Label2D (float3 position, ref FixedString512Bytes text, float sizeInPixels = 14) {
+			Label2D(position, ref text, sizeInPixels, LabelAlignment.MiddleLeft);
+		}
+
+		/// 
+		/// Draws a label in 3D space aligned with the camera.
+		///
+		/// 
+		/// // 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);
+		/// }
+		/// 
+		/// [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.
+		/// 
+		/// Position in 3D space.
+		/// Text to display.
+		/// 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.
+		/// How to align the text relative to the given position.
+		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
+		}
+
+		/// \copydocref{Label2D(float3,FixedString32Bytes,float,LabelAlignment)}
+		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
+		}
+
+		/// \copydocref{Label2D(float3,FixedString32Bytes,float,LabelAlignment)}
+		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
+		}
+
+		/// \copydocref{Label2D(float3,FixedString32Bytes,float,LabelAlignment)}
+		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
+		}
+
+		/// \copydocref{Label2D(float3,FixedString32Bytes,float,LabelAlignment)}
+		internal unsafe void Label2D (float3 position, byte* text, int byteCount, float sizeInPixels, LabelAlignment alignment) {
+#if MODULE_COLLECTIONS_0_12_0_OR_NEWER
+			AssertBufferExists();
+			Reserve();
+			Add(Command.Text);
+			Add(new TextData { center = position, numCharacters = byteCount, sizeInPixels = sizeInPixels, alignment = alignment });
+
+			Reserve(UnsafeUtility.SizeOf() * 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
+		/// 
+		/// Draws a label in 3D space.
+		///
+		/// 
+		/// // 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);
+		/// }
+		/// 
+		/// [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.
+		/// 
+		/// Position in 3D space.
+		/// Rotation in 3D space.
+		/// Text to display.
+		/// 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.
+		public void Label3D (float3 position, quaternion rotation, ref FixedString32Bytes text, float size) {
+			Label3D(position, rotation, ref text, size, LabelAlignment.MiddleLeft);
+		}
+
+		/// \copydocref{Label3D(float3,quaternion,FixedString32Bytes,float)}
+		public void Label3D (float3 position, quaternion rotation, ref FixedString64Bytes text, float size) {
+			Label3D(position, rotation, ref text, size, LabelAlignment.MiddleLeft);
+		}
+
+		/// \copydocref{Label3D(float3,quaternion,FixedString32Bytes,float)}
+		public void Label3D (float3 position, quaternion rotation, ref FixedString128Bytes text, float size) {
+			Label3D(position, rotation, ref text, size, LabelAlignment.MiddleLeft);
+		}
+
+		/// \copydocref{Label3D(float3,quaternion,FixedString32Bytes,float)}
+		public void Label3D (float3 position, quaternion rotation, ref FixedString512Bytes text, float size) {
+			Label3D(position, rotation, ref text, size, LabelAlignment.MiddleLeft);
+		}
+
+		/// 
+		/// Draws a label in 3D space.
+		///
+		/// 
+		/// // 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);
+		/// }
+		/// 
+		/// [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.
+		/// 
+		/// Position in 3D space.
+		/// Rotation in 3D space.
+		/// Text to display.
+		/// 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.
+		/// How to align the text relative to the given position.
+		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
+		}
+
+		/// \copydocref{Label3D(float3,quaternion,FixedString32Bytes,float,LabelAlignment)}
+		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
+		}
+
+		/// \copydocref{Label3D(float3,quaternion,FixedString32Bytes,float,LabelAlignment)}
+		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
+		}
+
+		/// \copydocref{Label3D(float3,quaternion,FixedString32Bytes,float,LabelAlignment)}
+		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
+		}
+
+		/// \copydocref{Label3D(float3,quaternion,FixedString32Bytes,float,LabelAlignment)}
+		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();
+			Add(Command.Text3D);
+			Add(new TextData3D { center = position, rotation = rotation, numCharacters = byteCount, size = size, alignment = alignment });
+
+			Reserve(UnsafeUtility.SizeOf() * 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 {
+	/// 
+	/// 2D wrapper for a .
+	///
+	/// 
+	/// var p1 = new Vector2(0, 1);
+	/// var p2 = new Vector2(5, 7);
+	///
+	/// // Draw it in the XY plane
+	/// Draw.xy.Line(p1, p2);
+	///
+	/// // Draw it in the XZ plane
+	/// Draw.xz.Line(p1, p2);
+	/// 
+	///
+	/// See: 2d-drawing (view in online documentation for working links)
+	/// See: 
+	/// See: 
+	/// 
+	public partial struct CommandBuilder2D {
+		/// The wrapped command builder
+		private CommandBuilder draw;
+		/// True if drawing in the XY plane, false if drawing in the XZ plane
+		bool xy;
+
+		static readonly float3 XY_UP = new float3(0, 0, 1);
+		static readonly float3 XZ_UP = new float3(0, 1, 0);
+		static readonly quaternion XY_TO_XZ_ROTATION =  quaternion.RotateX(-math.PI*0.5f);
+		static readonly quaternion XZ_TO_XZ_ROTATION =  quaternion.identity;
+		static readonly float4x4 XZ_TO_XY_MATRIX = new float4x4(new float4(1, 0, 0, 0), new float4(0, 0, 1, 0), new float4(0, 1, 0, 0), new float4(0, 0, 0, 1));
+
+		public CommandBuilder2D(CommandBuilder draw, bool xy) {
+			this.draw = draw;
+			this.xy = xy;
+		}
+
+		/// 
+		/// Draws a line between two points.
+		///
+		/// [Open online documentation to see images]
+		///
+		/// 
+		/// void Update () {
+		///     Draw.Line(Vector3.zero, Vector3.up);
+		/// }
+		/// 
+		/// 
+		public void Line (float2 a, float2 b) {
+			draw.Reserve();
+			// Add(Command.Line);
+			// Add(new LineData { a = a, b = b });
+
+			// The code below is equivalent to the commented out code above.
+			// But drawing lines is the most common operation so it needs to be really fast.
+			// Having this hardcoded improves line rendering performance by about 8%.
+			unsafe {
+				var buffer = draw.buffer;
+				var bufferSize = buffer->Length;
+				var newLen = bufferSize + 4 + 24;
+#if ENABLE_UNITY_COLLECTIONS_CHECKS
+				UnityEngine.Assertions.Assert.IsTrue(newLen <= buffer->Capacity);
+#endif
+				var ptr = (byte*)buffer->Ptr + bufferSize;
+				*(Command*)ptr = Command.Line;
+				var lineData = (LineData*)(ptr + 4);
+				if (xy) {
+					lineData->a = new float3(a, 0);
+					lineData->b = new float3(b, 0);
+				} else {
+					lineData->a = new float3(a.x, 0, a.y);
+					lineData->b = new float3(b.x, 0, b.y);
+				}
+				buffer->Length = newLen;
+			}
+		}
+
+		/// 
+		/// Draws a line between two points.
+		///
+		/// [Open online documentation to see images]
+		///
+		/// 
+		/// void Update () {
+		///     Draw.Line(Vector3.zero, Vector3.up);
+		/// }
+		/// 
+		/// 
+		public void Line (float2 a, float2 b, Color color) {
+			draw.Reserve();
+			// Add(Command.Line);
+			// Add(new LineData { a = a, b = b });
+
+			// The code below is equivalent to the commented out code above.
+			// But drawing lines is the most common operation so it needs to be really fast.
+			// Having this hardcoded improves line rendering performance by about 8%.
+			unsafe {
+				var buffer = draw.buffer;
+				var bufferSize = buffer->Length;
+				var newLen = bufferSize + 4 + 24 + 4;
+#if ENABLE_UNITY_COLLECTIONS_CHECKS
+				UnityEngine.Assertions.Assert.IsTrue(newLen <= buffer->Capacity);
+#endif
+				var ptr = (byte*)buffer->Ptr + bufferSize;
+				*(Command*)ptr = Command.Line | Command.PushColorInline;
+				*(uint*)(ptr + 4) = CommandBuilder.ConvertColor(color);
+				var lineData = (LineData*)(ptr + 8);
+				if (xy) {
+					lineData->a = new float3(a, 0);
+					lineData->b = new float3(b, 0);
+				} else {
+					lineData->a = new float3(a.x, 0, a.y);
+					lineData->b = new float3(b.x, 0, b.y);
+				}
+				buffer->Length = newLen;
+			}
+		}
+
+		/// 
+		/// Draws a line between two points.
+		///
+		/// [Open online documentation to see images]
+		///
+		/// 
+		/// void Update () {
+		///     Draw.Line(Vector3.zero, Vector3.up);
+		/// }
+		/// 
+		/// 
+		public void Line (float3 a, float3 b) {
+			draw.Line(a, b);
+		}
+
+		/// 
+		/// Draws a circle.
+		///
+		/// You can draw an arc by supplying the startAngle and endAngle parameters.
+		///
+		/// [Open online documentation to see images]
+		///
+		/// See: 
+		/// See: 
+		/// 
+		/// Center of the circle or arc.
+		/// Radius of the circle or arc.
+		/// Starting angle in radians. 0 corrsponds to the positive X axis.
+		/// End angle in radians.
+		public void Circle (float2 center, float radius, float startAngle = 0f, float endAngle = 2 * math.PI) {
+			Circle(xy ? new float3(center, 0) : new float3(center.x, 0, center.y), radius, startAngle, endAngle);
+		}
+
+		/// 
+		/// Draws a circle.
+		///
+		/// You can draw an arc by supplying the startAngle and endAngle parameters.
+		///
+		/// [Open online documentation to see images]
+		///
+		/// See: 
+		/// See: 
+		/// 
+		/// Center of the circle or arc.
+		/// Radius of the circle or arc.
+		/// Starting angle in radians. 0 corrsponds to the positive X axis.
+		/// End angle in radians.
+		public void Circle (float3 center, float radius, float startAngle = 0f, float endAngle = 2 * math.PI) {
+			if (xy) {
+				draw.PushMatrix(XZ_TO_XY_MATRIX);
+				draw.CircleXZInternal(new float3(center.x, center.z, center.y), radius, startAngle, endAngle);
+				draw.PopMatrix();
+			} else {
+				draw.CircleXZInternal(center, radius, startAngle, endAngle);
+			}
+		}
+
+		/// \copydocref{SolidCircle(float3,float,float,float)}
+		public void SolidCircle (float2 center, float radius, float startAngle = 0f, float endAngle = 2 * math.PI) {
+			SolidCircle(xy ? new float3(center, 0) : new float3(center.x, 0, center.y), radius, startAngle, endAngle);
+		}
+
+		/// 
+		/// Draws a disc.
+		///
+		/// You can draw an arc by supplying the startAngle and endAngle parameters.
+		///
+		/// [Open online documentation to see images]
+		///
+		/// See: 
+		/// See: 
+		/// 
+		/// Center of the disc or solid arc.
+		/// Radius of the disc or solid arc.
+		/// Starting angle in radians. 0 corrsponds to the positive X axis.
+		/// End angle in radians.
+		public void SolidCircle (float3 center, float radius, float startAngle = 0f, float endAngle = 2 * math.PI) {
+			if (xy) draw.PushMatrix(XZ_TO_XY_MATRIX);
+			draw.SolidCircleXZInternal(new float3(center.x, -center.z, center.y), radius, startAngle, endAngle);
+			if (xy) draw.PopMatrix();
+		}
+
+		/// 
+		/// Draws a wire pill in 2D.
+		///
+		/// 
+		/// Draw.xy.WirePill(new float2(-0.5f, -0.5f), new float2(0.5f, 0.5f), 0.5f, color);
+		/// 
+		///
+		/// [Open online documentation to see images]
+		///
+		/// See: 
+		/// 
+		/// Center of the first circle of the capsule.
+		/// Center of the second circle of the capsule.
+		/// Radius of the capsule.
+		public void WirePill (float2 a, float2 b, float radius) {
+			WirePill(a, b - a, math.length(b - a), radius);
+		}
+
+		/// 
+		/// Draws a wire pill in 2D.
+		///
+		/// 
+		/// Draw.xy.WirePill(new float2(-0.5f, -0.5f), new float2(1, 1), 1, 0.5f, color);
+		/// 
+		///
+		/// [Open online documentation to see images]
+		///
+		/// See: 
+		/// 
+		/// Center of the first circle of the capsule.
+		/// The main axis of the capsule. Does not have to be normalized. If zero, a circle will be drawn.
+		/// Length of the main axis of the capsule, from circle center to circle center. If zero, a circle will be drawn.
+		/// Radius of the capsule.
+		public void WirePill (float2 position, float2 direction, float length, float radius) {
+			direction = math.normalizesafe(direction);
+
+			if (radius <= 0) {
+				Line(position, position + direction * length);
+			} else if (length <= 0 || math.all(direction == 0)) {
+				Circle(position, radius);
+			} else {
+				float4x4 m;
+				if (xy) {
+					m = new float4x4(
+						new float4(direction, 0, 0),
+						new float4(math.cross(new float3(direction, 0), XY_UP), 0),
+						new float4(0, 0, 1, 0),
+						new float4(position, 0, 1)
+						);
+				} else {
+					m = new float4x4(
+						new float4(direction.x, 0, direction.y, 0),
+						new float4(0, 1, 0, 0),
+						new float4(math.cross(new float3(direction.x, 0, direction.y), XZ_UP), 0),
+						new float4(position.x, 0, position.y, 1)
+						);
+				}
+				draw.PushMatrix(m);
+				Circle(new float2(0, 0), radius, 0.5f * math.PI, 1.5f * math.PI);
+				Line(new float2(0, -radius), new float2(length, -radius));
+				Circle(new float2(length, 0), radius, -0.5f * math.PI, 0.5f * math.PI);
+				Line(new float2(0, radius), new float2(length, radius));
+				draw.PopMatrix();
+			}
+		}
+
+		/// \copydocref{CommandBuilder.Polyline(List<Vector3>,bool)}
+		[BurstDiscard]
+		public void Polyline (List points, bool cycle = false) {
+			for (int i = 0; i < points.Count - 1; i++) {
+				Line(points[i], points[i+1]);
+			}
+			if (cycle && points.Count > 1) Line(points[points.Count - 1], points[0]);
+		}
+
+		/// \copydocref{CommandBuilder.Polyline(Vector3[],bool)}
+		[BurstDiscard]
+		public void Polyline (Vector2[] points, bool cycle = false) {
+			for (int i = 0; i < points.Length - 1; i++) {
+				Line(points[i], points[i+1]);
+			}
+			if (cycle && points.Length > 1) Line(points[points.Length - 1], points[0]);
+		}
+
+		/// \copydocref{CommandBuilder.Polyline(float3[],bool)}
+		[BurstDiscard]
+		public void Polyline (float2[] points, bool cycle = false) {
+			for (int i = 0; i < points.Length - 1; i++) {
+				Line(points[i], points[i+1]);
+			}
+			if (cycle && points.Length > 1) Line(points[points.Length - 1], points[0]);
+		}
+
+		/// \copydocref{CommandBuilder.Polyline(NativeArray<float3>,bool)}
+		public void Polyline (NativeArray points, bool cycle = false) {
+			for (int i = 0; i < points.Length - 1; i++) {
+				Line(points[i], points[i+1]);
+			}
+			if (cycle && points.Length > 1) Line(points[points.Length - 1], points[0]);
+		}
+
+		/// 
+		/// Draws a 2D cross.
+		///
+		/// 
+		/// Draw.xz.Cross(float3.zero, color);
+		/// 
+		/// [Open online documentation to see images]
+		///
+		/// See: 
+		/// 
+		public void Cross (float2 position, float size = 1) {
+			size *= 0.5f;
+			Line(position - new float2(size, 0), position + new float2(size, 0));
+			Line(position - new float2(0, size), position + new float2(0, size));
+		}
+
+		/// 
+		/// Draws a rectangle outline.
+		/// The rectangle will be oriented along the rotation's X and Z axes.
+		///
+		/// 
+		/// Draw.xz.WireRectangle(new Vector3(0f, 0, 0), new Vector2(1, 1), Color.black);
+		/// 
+		/// [Open online documentation to see images]
+		///
+		/// This is identical to , but this name is added for consistency.
+		///
+		/// See: 
+		/// 
+		public void WireRectangle (float3 center, float2 size) {
+			draw.WirePlane(center, xy ? XY_TO_XZ_ROTATION : XZ_TO_XZ_ROTATION, size);
+		}
+
+		/// 
+		/// Draws a rectangle outline.
+		/// This is particularly useful when combined with .
+		///
+		/// 
+		/// using (Draw.InScreenSpace(Camera.main)) {
+		///     Draw.xy.WireRectangle(new Rect(10, 10, 100, 100), Color.black);
+		/// }
+		/// 
+		/// [Open online documentation to see images]
+		///
+		/// See: 
+		/// See: 
+		/// 
+		public void WireRectangle (Rect rect) {
+			float2 min = rect.min;
+			float2 max = rect.max;
+
+			Line(new float2(min.x, min.y), new float2(max.x, min.y));
+			Line(new float2(max.x, min.y), new float2(max.x, max.y));
+			Line(new float2(max.x, max.y), new float2(min.x, max.y));
+			Line(new float2(min.x, max.y), new float2(min.x, min.y));
+		}
+
+		/// 
+		/// Draws a solid rectangle.
+		/// This is particularly useful when combined with .
+		///
+		/// Behind the scenes this is implemented using .
+		///
+		/// 
+		/// using (Draw.InScreenSpace(Camera.main)) {
+		///     Draw.xy.SolidRectangle(new Rect(10, 10, 100, 100), Color.black);
+		/// }
+		/// 
+		/// [Open online documentation to see images]
+		///
+		/// See: 
+		/// See: 
+		/// See: 
+		/// 
+		public void SolidRectangle (Rect rect) {
+			draw.SolidPlane(new float3(rect.center.x, rect.center.y, 0.0f), xy ? XY_TO_XZ_ROTATION : XZ_TO_XZ_ROTATION, new float2(rect.width, rect.height));
+		}
+
+		/// 
+		/// Draws a grid of lines.
+		///
+		/// 
+		/// Draw.xz.WireGrid(Vector3.zero, new int2(3, 3), new float2(1, 1), color);
+		/// 
+		/// [Open online documentation to see images]
+		///
+		/// See: 
+		/// 
+		/// Center of the grid
+		/// Number of cells of the grid. Should be greater than 0.
+		/// Total size of the grid along the X and Z axes.
+		public void WireGrid (float2 center, int2 cells, float2 totalSize) {
+			draw.WireGrid(xy ? new float3(center, 0) : new float3(center.x, 0, center.y), xy ? XY_TO_XZ_ROTATION : XZ_TO_XZ_ROTATION, cells, totalSize);
+		}
+
+		/// 
+		/// Draws a grid of lines.
+		///
+		/// 
+		/// Draw.xz.WireGrid(Vector3.zero, new int2(3, 3), new float2(1, 1), color);
+		/// 
+		/// [Open online documentation to see images]
+		///
+		/// See: 
+		/// 
+		/// Center of the grid
+		/// Number of cells of the grid. Should be greater than 0.
+		/// Total size of the grid along the X and Z axes.
+		public void WireGrid (float3 center, int2 cells, float2 totalSize) {
+			draw.WireGrid(center, xy ? XY_TO_XZ_ROTATION : XZ_TO_XZ_ROTATION, cells, totalSize);
+		}
+	}
+}
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 {
+		/// \copydocref{CommandBuilder.WithMatrix(Matrix4x4)}
+		[BurstDiscard]
+		public ScopeMatrix WithMatrix (Matrix4x4 matrix) {
+			return draw.WithMatrix(matrix);
+		}
+		/// \copydocref{CommandBuilder.WithMatrix(float3x3)}
+		[BurstDiscard]
+		public ScopeMatrix WithMatrix (float3x3 matrix) {
+			return draw.WithMatrix(matrix);
+		}
+		/// \copydocref{CommandBuilder.WithColor(Color)}
+		[BurstDiscard]
+		public ScopeColor WithColor (Color color) {
+			return draw.WithColor(color);
+		}
+
+		/// \copydocref{CommandBuilder.WithLineWidth(float,bool)}
+		[BurstDiscard]
+		public ScopeLineWidth WithLineWidth (float pixels, bool automaticJoins = true) {
+			return draw.WithLineWidth(pixels, automaticJoins);
+		}
+
+
+		/// \copydocref{CommandBuilder.PushMatrix(Matrix4x4)}
+		public void PushMatrix (Matrix4x4 matrix) {
+			draw.PushMatrix(matrix);
+		}
+		/// \copydocref{CommandBuilder.PushMatrix(float4x4)}
+		public void PushMatrix (float4x4 matrix) {
+			draw.PushMatrix(matrix);
+		}
+
+
+		/// \copydocref{CommandBuilder.PopMatrix()}
+		public void PopMatrix () {
+			draw.PopMatrix();
+		}
+
+
+
+
+
+
+
+
+		/// \copydocref{CommandBuilder.Line(Vector3,Vector3)}
+		public void Line (Vector3 a, Vector3 b) {
+			draw.Line(a, b);
+		}
+		/// \copydocref{CommandBuilder.Line(Vector3,Vector3)}
+		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));
+		}
+		/// \copydocref{CommandBuilder.Line(Vector3,Vector3,Color)}
+		public void Line (Vector3 a, Vector3 b, Color color) {
+			draw.Line(a, b, color);
+		}
+		/// \copydocref{CommandBuilder.Line(Vector3,Vector3,Color)}
+		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);
+		}
+		/// \copydocref{CommandBuilder.Ray(float3,float3)}
+		public void Ray (float3 origin, float3 direction) {
+			draw.Ray(origin, direction);
+		}
+		/// \copydocref{CommandBuilder.Ray(float3,float3)}
+		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));
+		}
+		/// \copydocref{CommandBuilder.Ray(Ray,float)}
+		public void Ray (Ray ray, float length) {
+			draw.Ray(ray, length);
+		}
+		/// \copydocref{CommandBuilder.Arc(float3,float3,float3)}
+		public void Arc (float3 center, float3 start, float3 end) {
+			draw.Arc(center, start, end);
+		}
+		/// \copydocref{CommandBuilder.Arc(float3,float3,float3)}
+		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));
+		}
+
+
+
+
+		/// \copydocref{CommandBuilder.Polyline(List<Vector3>,bool)}
+		[BurstDiscard]
+		public void Polyline (List points, bool cycle = false) {
+			draw.Polyline(points, cycle);
+		}
+		/// \copydocref{CommandBuilder.Polyline(Vector3[],bool)}
+		[BurstDiscard]
+		public void Polyline (Vector3[] points, bool cycle = false) {
+			draw.Polyline(points, cycle);
+		}
+		/// \copydocref{CommandBuilder.Polyline(float3[],bool)}
+		[BurstDiscard]
+		public void Polyline (float3[] points, bool cycle = false) {
+			draw.Polyline(points, cycle);
+		}
+		/// \copydocref{CommandBuilder.Polyline(NativeArray<float3>,bool)}
+		public void Polyline (NativeArray points, bool cycle = false) {
+			draw.Polyline(points, cycle);
+		}
+
+
+
+		/// \copydocref{CommandBuilder.Cross(float3,float)}
+		public void Cross (float3 position, float size = 1) {
+			draw.Cross(position, size);
+		}
+		/// \copydocref{CommandBuilder.Bezier(float3,float3,float3,float3)}
+		public void Bezier (float3 p0, float3 p1, float3 p2, float3 p3) {
+			draw.Bezier(p0, p1, p2, p3);
+		}
+		/// \copydocref{CommandBuilder.Bezier(float3,float3,float3,float3)}
+		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));
+		}
+
+
+
+		/// \copydocref{CommandBuilder.Arrow(float3,float3)}
+		public void Arrow (float3 from, float3 to) {
+			ArrowRelativeSizeHead(from, to, xy ? XY_UP : XZ_UP, 0.2f);
+		}
+		/// \copydocref{CommandBuilder.Arrow(float3,float3)}
+		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));
+		}
+		/// \copydocref{CommandBuilder.Arrow(float3,float3,float3,float)}
+		public void Arrow (float3 from, float3 to, float3 up, float headSize) {
+			draw.Arrow(from, to, up, headSize);
+		}
+		/// \copydocref{CommandBuilder.Arrow(float3,float3,float3,float)}
+		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);
+		}
+		/// \copydocref{CommandBuilder.ArrowRelativeSizeHead(float3,float3,float3,float)}
+		public void ArrowRelativeSizeHead (float3 from, float3 to, float3 up, float headFraction) {
+			draw.ArrowRelativeSizeHead(from, to, up, headFraction);
+		}
+		/// \copydocref{CommandBuilder.ArrowRelativeSizeHead(float3,float3,float3,float)}
+		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);
+		}
+
+
+
+
+		/// \copydocref{CommandBuilder.ArrowheadArc(float3,float3,float,float)}
+		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();
+		}
+		/// \copydocref{CommandBuilder.ArrowheadArc(float3,float3,float,float)}
+		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);
+		}
+
+
+		/// \copydocref{CommandBuilder.WireRectangle(float3,quaternion,float2)}
+		public void WireRectangle (float3 center, quaternion rotation, float2 size) {
+			draw.WireRectangle(center, rotation, size);
+		}
+		/// \copydocref{CommandBuilder.WireRectangle(float3,quaternion,float2)}
+		public void WireRectangle (float2 center, quaternion rotation, float2 size) {
+			WireRectangle(xy ? new float3(center, 0) : new float3(center.x, 0, center.y), rotation, size);
+		}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+		/// \copydocref{Ray(float3,float3)}
+		public void Ray (float3 origin, float3 direction, Color color) {
+			draw.Ray(origin, direction, color);
+		}
+		/// \copydocref{Ray(float2,float2)}
+		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);
+		}
+		/// \copydocref{Ray(Ray,float)}
+		public void Ray (Ray ray, float length, Color color) {
+			draw.Ray(ray, length, color);
+		}
+		/// \copydocref{Arc(float3,float3,float3)}
+		public void Arc (float3 center, float3 start, float3 end, Color color) {
+			draw.Arc(center, start, end, color);
+		}
+		/// \copydocref{Arc(float2,float2,float2)}
+		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);
+		}
+
+
+
+
+
+
+		/// \copydocref{Polyline(List<Vector3>,bool)}
+		[BurstDiscard]
+		public void Polyline (List points, bool cycle, Color color) {
+			draw.Polyline(points, cycle, color);
+		}
+		/// \copydocref{Polyline(List<Vector3>,bool)}
+		[BurstDiscard]
+		public void Polyline (List points, Color color) {
+			Polyline(points, false, color);
+		}
+		/// \copydocref{Polyline(Vector3[],bool)}
+		[BurstDiscard]
+		public void Polyline (Vector3[] points, bool cycle, Color color) {
+			draw.Polyline(points, cycle, color);
+		}
+		/// \copydocref{Polyline(Vector3[],bool)}
+		[BurstDiscard]
+		public void Polyline (Vector3[] points, Color color) {
+			Polyline(points, false, color);
+		}
+		/// \copydocref{Polyline(float3[],bool)}
+		[BurstDiscard]
+		public void Polyline (float3[] points, bool cycle, Color color) {
+			draw.Polyline(points, cycle, color);
+		}
+		/// \copydocref{Polyline(float3[],bool)}
+		[BurstDiscard]
+		public void Polyline (float3[] points, Color color) {
+			Polyline(points, false, color);
+		}
+		/// \copydocref{Polyline(NativeArray<float3>,bool)}
+		public void Polyline (NativeArray points, bool cycle, Color color) {
+			draw.Polyline(points, cycle, color);
+		}
+		/// \copydocref{Polyline(NativeArray<float3>,bool)}
+		public void Polyline (NativeArray points, Color color) {
+			Polyline(points, false, color);
+		}
+
+
+
+		/// \copydocref{Cross(float3,float)}
+		public void Cross (float3 position, float size, Color color) {
+			draw.Cross(position, size, color);
+		}
+		/// \copydocref{Cross(float3,float)}
+		public void Cross (float3 position, Color color) {
+			Cross(position, 1, color);
+		}
+		/// \copydocref{Bezier(float3,float3,float3,float3)}
+		public void Bezier (float3 p0, float3 p1, float3 p2, float3 p3, Color color) {
+			draw.Bezier(p0, p1, p2, p3, color);
+		}
+		/// \copydocref{Bezier(float2,float2,float2,float2)}
+		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);
+		}
+
+
+
+		/// \copydocref{Arrow(float3,float3)}
+		public void Arrow (float3 from, float3 to, Color color) {
+			ArrowRelativeSizeHead(from, to, xy ? XY_UP : XZ_UP, 0.2f, color);
+		}
+		/// \copydocref{Arrow(float2,float2)}
+		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);
+		}
+		/// \copydocref{Arrow(float3,float3,float3,float)}
+		public void Arrow (float3 from, float3 to, float3 up, float headSize, Color color) {
+			draw.Arrow(from, to, up, headSize, color);
+		}
+		/// \copydocref{Arrow(float2,float2,float2,float)}
+		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);
+		}
+		/// \copydocref{ArrowRelativeSizeHead(float3,float3,float3,float)}
+		public void ArrowRelativeSizeHead (float3 from, float3 to, float3 up, float headFraction, Color color) {
+			draw.ArrowRelativeSizeHead(from, to, up, headFraction, color);
+		}
+		/// \copydocref{ArrowRelativeSizeHead(float2,float2,float2,float)}
+		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);
+		}
+
+
+
+
+		/// \copydocref{ArrowheadArc(float3,float3,float,float)}
+		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();
+		}
+		/// \copydocref{ArrowheadArc(float3,float3,float,float)}
+		public void ArrowheadArc (float3 origin, float3 direction, float offset, Color color) {
+			ArrowheadArc(origin, direction, offset, 60, color);
+		}
+		/// \copydocref{ArrowheadArc(float2,float2,float,float)}
+		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);
+		}
+		/// \copydocref{ArrowheadArc(float2,float2,float,float)}
+		public void ArrowheadArc (float2 origin, float2 direction, float offset, Color color) {
+			ArrowheadArc(origin, direction, offset, 60, color);
+		}
+
+
+		/// \copydocref{WireRectangle(float3,quaternion,float2)}
+		public void WireRectangle (float3 center, quaternion rotation, float2 size, Color color) {
+			draw.WireRectangle(center, rotation, size, color);
+		}
+		/// \copydocref{WireRectangle(float2,quaternion,float2)}
+		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);
+		}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+		/// \copydocref{Line(float3,float3)}
+		public void Line (float3 a, float3 b, Color color) {
+			draw.Line(a, b, color);
+		}
+		/// \copydocref{Circle(float2,float,float,float)}
+		/// Color of the object
+		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);
+		}
+		/// \copydocref{Circle(float2,float,float,float)}
+		/// Color of the object
+		public void Circle (float2 center, float radius, Color color) {
+			Circle(center, radius, 0f, 2 * math.PI, color);
+		}
+		/// \copydocref{Circle(float3,float,float,float)}
+		/// Color of the object
+		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();
+		}
+		/// \copydocref{Circle(float3,float,float,float)}
+		/// Color of the object
+		public void Circle (float3 center, float radius, Color color) {
+			Circle(center, radius, 0f, 2 * math.PI, color);
+		}
+
+
+
+
+		/// \copydocref{WirePill(float2,float2,float)}
+		/// Color of the object
+		public void WirePill (float2 a, float2 b, float radius, Color color) {
+			WirePill(a, b - a, math.length(b - a), radius, color);
+		}
+		/// \copydocref{WirePill(float2,float2,float,float)}
+		/// Color of the object
+		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();
+		}
+		/// \copydocref{Polyline(List<Vector2>,bool)}
+		[BurstDiscard]
+		public void Polyline (List 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();
+		}
+		/// \copydocref{Polyline(List<Vector2>,bool)}
+		[BurstDiscard]
+		public void Polyline (List points, Color color) {
+			Polyline(points, false, color);
+		}
+		/// \copydocref{Polyline(Vector2[],bool)}
+		[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();
+		}
+		/// \copydocref{Polyline(Vector2[],bool)}
+		[BurstDiscard]
+		public void Polyline (Vector2[] points, Color color) {
+			Polyline(points, false, color);
+		}
+		/// \copydocref{Polyline(float2[],bool)}
+		[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();
+		}
+		/// \copydocref{Polyline(float2[],bool)}
+		[BurstDiscard]
+		public void Polyline (float2[] points, Color color) {
+			Polyline(points, false, color);
+		}
+		/// \copydocref{Polyline(NativeArray<float2>,bool)}
+		public void Polyline (NativeArray 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();
+		}
+		/// \copydocref{Polyline(NativeArray<float2>,bool)}
+		public void Polyline (NativeArray points, Color color) {
+			Polyline(points, false, color);
+		}
+		/// \copydocref{Cross(float2,float)}
+		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();
+		}
+		/// \copydocref{Cross(float2,float)}
+		public void Cross (float2 position, Color color) {
+			Cross(position, 1, color);
+		}
+		/// \copydocref{WireRectangle(float3,float2)}
+		public void WireRectangle (float3 center, float2 size, Color color) {
+			draw.WirePlane(center, xy ? XY_TO_XZ_ROTATION : XZ_TO_XZ_ROTATION, size, color);
+		}
+		/// \copydocref{WireRectangle(Rect)}
+		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();
+		}
+
+		/// \copydocref{WireGrid(float2,int2,float2)}
+		/// Color of the object
+		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);
+		}
+		/// \copydocref{WireGrid(float3,int2,float2)}
+		/// Color of the object
+		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 {
+		/// \copydocref{Line(float3,float3)}
+		public void Line (float3 a, float3 b, Color color) {
+			Reserve();
+			Add(Command.Line | Command.PushColorInline);
+			Add(ConvertColor(color));
+			Add(new LineData { a = a, b = b });
+		}
+		/// \copydocref{Ray(float3,float3)}
+		public void Ray (float3 origin, float3 direction, Color color) {
+			Line(origin, origin + direction, color);
+		}
+		/// \copydocref{Ray(Ray,float)}
+		public void Ray (Ray ray, float length, Color color) {
+			Line(ray.origin, ray.origin + ray.direction * length, color);
+		}
+		/// \copydocref{Arc(float3,float3,float3)}
+		/// Color of the object
+		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();
+		}
+		/// \copydocref{CircleXZ(float3,float,float,float)}
+		/// Color of the object
+		[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);
+		}
+		/// \copydocref{CircleXZ(float3,float,float,float)}
+		/// Color of the object
+		[System.Obsolete("Use Draw.xz.Circle instead")]
+		public void CircleXZ (float3 center, float radius, Color color) {
+			CircleXZ(center, radius, 0f, 2 * Mathf.PI, color);
+		}
+
+
+		/// \copydocref{Circle(float3,float3,float)}
+		public void Circle (float3 center, float3 normal, float radius, Color color) {
+			Reserve();
+			Add(Command.Circle | Command.PushColorInline);
+			Add(ConvertColor(color));
+			Add(new CircleData { center = center, normal = normal, radius = radius });
+		}
+
+
+
+
+
+
+
+		/// \copydocref{WireCylinder(float3,float3,float)}
+		public void WireCylinder (float3 bottom, float3 top, float radius, Color color) {
+			WireCylinder(bottom, top - bottom, math.length(top - bottom), radius, color);
+		}
+		/// \copydocref{WireCylinder(float3,float3,float,float)}
+		/// Color of the object
+		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();
+		}
+		/// \copydocref{WireCapsule(float3,float3,float)}
+		/// Color of the object
+		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();
+		}
+		/// \copydocref{WireCapsule(float3,float3,float,float)}
+		/// Color of the object
+		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();
+		}
+		/// \copydocref{WireSphere(float3,float)}
+		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();
+		}
+		/// \copydocref{Polyline(List<Vector3>,bool)}
+		/// Color of the object
+		[BurstDiscard]
+		public void Polyline (List 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();
+		}
+		/// \copydocref{Polyline(List<Vector3>,bool)}
+		/// Color of the object
+		[BurstDiscard]
+		public void Polyline (List points, Color color) {
+			Polyline(points, false, color);
+		}
+		/// \copydocref{Polyline(Vector3[],bool)}
+		/// Color of the object
+		[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();
+		}
+		/// \copydocref{Polyline(Vector3[],bool)}
+		/// Color of the object
+		[BurstDiscard]
+		public void Polyline (Vector3[] points, Color color) {
+			Polyline(points, false, color);
+		}
+		/// \copydocref{Polyline(float3[],bool)}
+		/// Color of the object
+		[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();
+		}
+		/// \copydocref{Polyline(float3[],bool)}
+		/// Color of the object
+		[BurstDiscard]
+		public void Polyline (float3[] points, Color color) {
+			Polyline(points, false, color);
+		}
+		/// \copydocref{Polyline(NativeArray<float3>,bool)}
+		/// Color of the object
+		public void Polyline (NativeArray 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();
+		}
+		/// \copydocref{Polyline(NativeArray<float3>,bool)}
+		/// Color of the object
+		public void Polyline (NativeArray points, Color color) {
+			Polyline(points, false, color);
+		}
+
+
+		/// \copydocref{WireBox(float3,float3)}
+		/// Color of the object
+		public void WireBox (float3 center, float3 size, Color color) {
+			Reserve();
+			Add(Command.WireBox | Command.PushColorInline);
+			Add(ConvertColor(color));
+			Add(new BoxData { center = center, size = size });
+		}
+		/// \copydocref{WireBox(float3,quaternion,float3)}
+		/// Color of the object
+		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();
+		}
+		/// \copydocref{WireBox(Bounds)}
+		public void WireBox (Bounds bounds, Color color) {
+			WireBox(bounds.center, bounds.size, color);
+		}
+		/// \copydocref{WireMesh(Mesh)}
+		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();
+		}
+		/// \copydocref{WireMesh(NativeArray<float3>,NativeArray<int>)}
+		public void WireMesh (NativeArray vertices, NativeArray 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();
+		}
+
+		/// \copydocref{Cross(float3,float)}
+		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();
+		}
+		/// \copydocref{Cross(float3,float)}
+		public void Cross (float3 position, Color color) {
+			Cross(position, 1, color);
+		}
+		/// \copydocref{CrossXZ(float3,float)}
+		[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();
+		}
+		/// \copydocref{CrossXZ(float3,float)}
+		[System.Obsolete("Use Draw.xz.Cross instead")]
+		public void CrossXZ (float3 position, Color color) {
+			CrossXZ(position, 1, color);
+		}
+		/// \copydocref{CrossXY(float3,float)}
+		[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();
+		}
+		/// \copydocref{CrossXY(float3,float)}
+		[System.Obsolete("Use Draw.xy.Cross instead")]
+		public void CrossXY (float3 position, Color color) {
+			CrossXY(position, 1, color);
+		}
+		/// \copydocref{Bezier(float3,float3,float3,float3)}
+		/// Color of the object
+		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();
+		}
+
+
+		/// \copydocref{Arrow(float3,float3)}
+		/// Color of the object
+		public void Arrow (float3 from, float3 to, Color color) {
+			ArrowRelativeSizeHead(from, to, DEFAULT_UP, 0.2f, color);
+		}
+		/// \copydocref{Arrow(float3,float3,float3,float)}
+		/// Color of the object
+		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();
+		}
+		/// \copydocref{ArrowRelativeSizeHead(float3,float3,float3,float)}
+		/// Color of the object
+		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();
+		}
+
+
+		/// \copydocref{ArrowheadArc(float3,float3,float,float)}
+		/// Color of the object
+		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();
+		}
+		/// \copydocref{ArrowheadArc(float3,float3,float,float)}
+		/// Color of the object
+		public void ArrowheadArc (float3 origin, float3 direction, float offset, Color color) {
+			ArrowheadArc(origin, direction, offset, 60, color);
+		}
+		/// \copydocref{WireGrid(float3,quaternion,int2,float2)}
+		/// Color of the object
+		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();
+		}
+
+
+		/// \copydocref{WireRectangle(float3,quaternion,float2)}
+		public void WireRectangle (float3 center, quaternion rotation, float2 size, Color color) {
+			WirePlane(center, rotation, size, color);
+		}
+		/// \copydocref{WireRectangle(Rect)}
+		[System.Obsolete("Use Draw.xy.WireRectangle instead")]
+		public void WireRectangle (Rect rect, Color color) {
+			xy.WireRectangle(rect, color);
+		}
+
+
+
+
+
+
+
+		/// \copydocref{WirePlane(float3,float3,float2)}
+		/// Color of the object
+		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();
+		}
+		/// \copydocref{WirePlane(float3,quaternion,float2)}
+		/// Color of the object
+		public void WirePlane (float3 center, quaternion rotation, float2 size, Color color) {
+			Reserve();
+			Add(Command.WirePlane | Command.PushColorInline);
+			Add(ConvertColor(color));
+			Add(new PlaneData { center = center, rotation = rotation, size = size });
+		}
+
+
+
+		/// \copydocref{SolidBox(float3,float3)}
+		/// Color of the object
+		public void SolidBox (float3 center, float3 size, Color color) {
+			Reserve();
+			Add(Command.Box | Command.PushColorInline);
+			Add(ConvertColor(color));
+			Add(new BoxData { center = center, size = size });
+		}
+		/// \copydocref{SolidBox(Bounds)}
+		/// Color of the object
+		public void SolidBox (Bounds bounds, Color color) {
+			SolidBox(bounds.center, bounds.size, color);
+		}
+		/// \copydocref{SolidBox(float3,quaternion,float3)}
+		/// Color of the object
+		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 {
+	/// 
+	/// Methods for easily drawing things in the editor and in standalone games.
+	///
+	/// See: getstarted (view in online documentation for working links)
+	/// 
+	public static class Draw {
+		internal static CommandBuilder builder;
+		internal static CommandBuilder ingame_builder;
+
+
+		/// 
+		/// 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)
+		/// 
+		public static ref CommandBuilder editor {
+			get {
+				DrawingManager.Init();
+				return ref builder;
+			}
+		}
+
+		/// \copydocref{CommandBuilder.xy}
+		public static CommandBuilder2D xy {
+			get {
+				DrawingManager.Init();
+				return new CommandBuilder2D(builder, true);
+			}
+		}
+
+		/// \copydocref{CommandBuilder.xz}
+		public static CommandBuilder2D xz {
+			get {
+				DrawingManager.Init();
+				return new CommandBuilder2D(builder, false);
+			}
+		}
+
+
+#if UNITY_EDITOR
+		/// 
+		/// \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.
+		/// 
+		[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
+		/// 
+		/// \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.
+		/// 
+		[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
+		/// 
+		/// \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.
+		/// 
+		[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
+		/// 
+		/// \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.
+		/// 
+		[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
+
+
+		/// 
+		/// \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.
+		/// 
+		[BurstDiscard]
+		public static void PushMatrix (Matrix4x4 matrix) {
+#if UNITY_EDITOR
+			DrawingManager.Init();
+			builder.PushMatrix(matrix);
+#endif
+		}
+
+		/// 
+		/// \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.
+		/// 
+		[BurstDiscard]
+		public static void PushMatrix (float4x4 matrix) {
+#if UNITY_EDITOR
+			DrawingManager.Init();
+			builder.PushMatrix(matrix);
+#endif
+		}
+
+
+
+
+
+		/// 
+		/// \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.
+		/// 
+		[BurstDiscard]
+		public static void PopMatrix () {
+#if UNITY_EDITOR
+			DrawingManager.Init();
+			builder.PopMatrix();
+#endif
+		}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+		/// 
+		/// \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.
+		/// 
+		[BurstDiscard]
+		public static void Line (float3 a, float3 b) {
+#if UNITY_EDITOR
+			DrawingManager.Init();
+			builder.Line(a, b);
+#endif
+		}
+
+		/// 
+		/// \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.
+		/// 
+		[BurstDiscard]
+		public static void Line (Vector3 a, Vector3 b) {
+#if UNITY_EDITOR
+			DrawingManager.Init();
+			builder.Line(a, b);
+#endif
+		}
+
+		/// 
+		/// \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.
+		/// 
+		[BurstDiscard]
+		public static void Line (Vector3 a, Vector3 b, Color color) {
+#if UNITY_EDITOR
+			DrawingManager.Init();
+			builder.Line(a, b, color);
+#endif
+		}
+
+		/// 
+		/// \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.
+		/// 
+		[BurstDiscard]
+		public static void Ray (float3 origin, float3 direction) {
+#if UNITY_EDITOR
+			DrawingManager.Init();
+			builder.Ray(origin, direction);
+#endif
+		}
+
+		/// 
+		/// \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.
+		/// 
+		[BurstDiscard]
+		public static void Ray (Ray ray, float length) {
+#if UNITY_EDITOR
+			DrawingManager.Init();
+			builder.Ray(ray, length);
+#endif
+		}
+
+		/// 
+		/// \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.
+		/// 
+		[BurstDiscard]
+		public static void Arc (float3 center, float3 start, float3 end) {
+#if UNITY_EDITOR
+			DrawingManager.Init();
+			builder.Arc(center, start, end);
+#endif
+		}
+
+		/// 
+		/// \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.
+		/// 
+		[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
+		}
+
+
+
+		/// 
+		/// \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.
+		/// 
+		[BurstDiscard]
+		public static void Circle (float3 center, float3 normal, float radius) {
+#if UNITY_EDITOR
+			DrawingManager.Init();
+			builder.Circle(center, normal, radius);
+#endif
+		}
+
+
+
+
+
+
+
+
+
+
+
+		/// 
+		/// \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.
+		/// 
+		[BurstDiscard]
+		public static void WireCylinder (float3 bottom, float3 top, float radius) {
+#if UNITY_EDITOR
+			DrawingManager.Init();
+			builder.WireCylinder(bottom, top, radius);
+#endif
+		}
+
+		/// 
+		/// \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.
+		/// 
+		[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
+		}
+
+		/// 
+		/// \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.
+		/// 
+		[BurstDiscard]
+		public static void WireCapsule (float3 start, float3 end, float radius) {
+#if UNITY_EDITOR
+			DrawingManager.Init();
+			builder.WireCapsule(start, end, radius);
+#endif
+		}
+
+		/// 
+		/// \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.
+		/// 
+		[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
+		}
+
+		/// 
+		/// \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.
+		/// 
+		[BurstDiscard]
+		public static void WireSphere (float3 position, float radius) {
+#if UNITY_EDITOR
+			DrawingManager.Init();
+			builder.WireSphere(position, radius);
+#endif
+		}
+
+		/// 
+		/// \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.
+		/// 
+		[BurstDiscard]
+		public static void Polyline (List points, bool cycle = false) {
+#if UNITY_EDITOR
+			DrawingManager.Init();
+			builder.Polyline(points, cycle);
+#endif
+		}
+
+		/// 
+		/// \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.
+		/// 
+		[BurstDiscard]
+		public static void Polyline (Vector3[] points, bool cycle = false) {
+#if UNITY_EDITOR
+			DrawingManager.Init();
+			builder.Polyline(points, cycle);
+#endif
+		}
+
+		/// 
+		/// \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.
+		/// 
+		[BurstDiscard]
+		public static void Polyline (float3[] points, bool cycle = false) {
+#if UNITY_EDITOR
+			DrawingManager.Init();
+			builder.Polyline(points, cycle);
+#endif
+		}
+
+		/// 
+		/// \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.
+		/// 
+		[BurstDiscard]
+		public static void Polyline (NativeArray points, bool cycle = false) {
+#if UNITY_EDITOR
+			DrawingManager.Init();
+			builder.Polyline(points, cycle);
+#endif
+		}
+
+
+
+
+
+		/// 
+		/// \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.
+		/// 
+		[BurstDiscard]
+		public static void WireBox (float3 center, float3 size) {
+#if UNITY_EDITOR
+			DrawingManager.Init();
+			builder.WireBox(center, size);
+#endif
+		}
+
+		/// 
+		/// \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.
+		/// 
+		[BurstDiscard]
+		public static void WireBox (float3 center, quaternion rotation, float3 size) {
+#if UNITY_EDITOR
+			DrawingManager.Init();
+			builder.WireBox(center, rotation, size);
+#endif
+		}
+
+		/// 
+		/// \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.
+		/// 
+		[BurstDiscard]
+		public static void WireBox (Bounds bounds) {
+#if UNITY_EDITOR
+			DrawingManager.Init();
+			builder.WireBox(bounds);
+#endif
+		}
+
+		/// 
+		/// \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.
+		/// 
+		[BurstDiscard]
+		public static void WireMesh (Mesh mesh) {
+#if UNITY_EDITOR
+			DrawingManager.Init();
+			builder.WireMesh(mesh);
+#endif
+		}
+
+		/// 
+		/// \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.
+		/// 
+		[BurstDiscard]
+		public static void WireMesh (NativeArray vertices, NativeArray triangles) {
+#if UNITY_EDITOR
+			DrawingManager.Init();
+			builder.WireMesh(vertices, triangles);
+#endif
+		}
+
+
+
+
+
+
+
+		/// 
+		/// \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.
+		/// 
+		[BurstDiscard]
+		public static void Cross (float3 position, float size = 1) {
+#if UNITY_EDITOR
+			DrawingManager.Init();
+			builder.Cross(position, size);
+#endif
+		}
+
+		/// 
+		/// \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.
+		/// 
+		[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
+		}
+
+		/// 
+		/// \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.
+		/// 
+		[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
+		}
+
+		/// 
+		/// \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.
+		/// 
+		[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
+		}
+
+
+
+
+
+		/// 
+		/// \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.
+		/// 
+		[BurstDiscard]
+		public static void Arrow (float3 from, float3 to) {
+#if UNITY_EDITOR
+			DrawingManager.Init();
+			builder.Arrow(from, to);
+#endif
+		}
+
+		/// 
+		/// \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.
+		/// 
+		[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
+		}
+
+		/// 
+		/// \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.
+		/// 
+		[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
+		}
+
+
+
+
+
+		/// 
+		/// \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.
+		/// 
+		[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
+		}
+
+		/// 
+		/// \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.
+		/// 
+		[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
+		}
+
+
+
+
+
+		/// 
+		/// \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.
+		/// 
+		[BurstDiscard]
+		public static void WireRectangle (float3 center, quaternion rotation, float2 size) {
+#if UNITY_EDITOR
+			DrawingManager.Init();
+			builder.WireRectangle(center, rotation, size);
+#endif
+		}
+
+		/// 
+		/// \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.
+		/// 
+		[BurstDiscard]
+		[System.Obsolete("Use Draw.xy.WireRectangle instead")]
+		public static void WireRectangle (Rect rect) {
+#if UNITY_EDITOR
+			DrawingManager.Init();
+			builder.WireRectangle(rect);
+#endif
+		}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+		/// 
+		/// \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.
+		/// 
+		[BurstDiscard]
+		public static void WirePlane (float3 center, float3 normal, float2 size) {
+#if UNITY_EDITOR
+			DrawingManager.Init();
+			builder.WirePlane(center, normal, size);
+#endif
+		}
+
+		/// 
+		/// \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.
+		/// 
+		[BurstDiscard]
+		public static void WirePlane (float3 center, quaternion rotation, float2 size) {
+#if UNITY_EDITOR
+			DrawingManager.Init();
+			builder.WirePlane(center, rotation, size);
+#endif
+		}
+
+
+
+
+
+
+
+		/// 
+		/// \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.
+		/// 
+		[BurstDiscard]
+		public static void SolidBox (float3 center, float3 size) {
+#if UNITY_EDITOR
+			DrawingManager.Init();
+			builder.SolidBox(center, size);
+#endif
+		}
+
+		/// 
+		/// \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.
+		/// 
+		[BurstDiscard]
+		public static void SolidBox (Bounds bounds) {
+#if UNITY_EDITOR
+			DrawingManager.Init();
+			builder.SolidBox(bounds);
+#endif
+		}
+
+		/// 
+		/// \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.
+		/// 
+		[BurstDiscard]
+		public static void SolidBox (float3 center, quaternion rotation, float3 size) {
+#if UNITY_EDITOR
+			DrawingManager.Init();
+			builder.SolidBox(center, rotation, size);
+#endif
+		}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+		/// 
+		/// \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.
+		/// 
+		[BurstDiscard]
+		public static void Line (float3 a, float3 b, Color color) {
+#if UNITY_EDITOR
+			DrawingManager.Init();
+			builder.Line(a, b, color);
+#endif
+		}
+
+		/// 
+		/// \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.
+		/// 
+		[BurstDiscard]
+		public static void Ray (float3 origin, float3 direction, Color color) {
+#if UNITY_EDITOR
+			DrawingManager.Init();
+			builder.Ray(origin, direction, color);
+#endif
+		}
+
+		/// 
+		/// \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.
+		/// 
+		[BurstDiscard]
+		public static void Ray (Ray ray, float length, Color color) {
+#if UNITY_EDITOR
+			DrawingManager.Init();
+			builder.Ray(ray, length, color);
+#endif
+		}
+
+		/// 
+		/// \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.
+		/// 
+		[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
+		}
+
+		/// 
+		/// \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.
+		/// 
+		[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
+		}
+
+		/// 
+		/// \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.
+		/// 
+		[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
+		}
+
+
+
+
+
+		/// 
+		/// \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.
+		/// 
+		[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
+		}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+		/// 
+		/// \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.
+		/// 
+		[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
+		}
+
+		/// 
+		/// \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.
+		/// 
+		[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
+		}
+
+		/// 
+		/// \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.
+		/// 
+		[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
+		}
+
+		/// 
+		/// \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.
+		/// 
+		[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
+		}
+
+		/// 
+		/// \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.
+		/// 
+		[BurstDiscard]
+		public static void WireSphere (float3 position, float radius, Color color) {
+#if UNITY_EDITOR
+			DrawingManager.Init();
+			builder.WireSphere(position, radius, color);
+#endif
+		}
+
+		/// 
+		/// \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.
+		/// 
+		[BurstDiscard]
+		public static void Polyline (List points, bool cycle, Color color) {
+#if UNITY_EDITOR
+			DrawingManager.Init();
+			builder.Polyline(points, cycle, color);
+#endif
+		}
+
+		/// 
+		/// \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.
+		/// 
+		[BurstDiscard]
+		public static void Polyline (List points, Color color) {
+#if UNITY_EDITOR
+			DrawingManager.Init();
+			builder.Polyline(points, color);
+#endif
+		}
+
+		/// 
+		/// \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.
+		/// 
+		[BurstDiscard]
+		public static void Polyline (Vector3[] points, bool cycle, Color color) {
+#if UNITY_EDITOR
+			DrawingManager.Init();
+			builder.Polyline(points, cycle, color);
+#endif
+		}
+
+		/// 
+		/// \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.
+		/// 
+		[BurstDiscard]
+		public static void Polyline (Vector3[] points, Color color) {
+#if UNITY_EDITOR
+			DrawingManager.Init();
+			builder.Polyline(points, color);
+#endif
+		}
+
+		/// 
+		/// \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.
+		/// 
+		[BurstDiscard]
+		public static void Polyline (float3[] points, bool cycle, Color color) {
+#if UNITY_EDITOR
+			DrawingManager.Init();
+			builder.Polyline(points, cycle, color);
+#endif
+		}
+
+		/// 
+		/// \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.
+		/// 
+		[BurstDiscard]
+		public static void Polyline (float3[] points, Color color) {
+#if UNITY_EDITOR
+			DrawingManager.Init();
+			builder.Polyline(points, color);
+#endif
+		}
+
+		/// 
+		/// \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.
+		/// 
+		[BurstDiscard]
+		public static void Polyline (NativeArray points, bool cycle, Color color) {
+#if UNITY_EDITOR
+			DrawingManager.Init();
+			builder.Polyline(points, cycle, color);
+#endif
+		}
+
+		/// 
+		/// \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.
+		/// 
+		[BurstDiscard]
+		public static void Polyline (NativeArray points, Color color) {
+#if UNITY_EDITOR
+			DrawingManager.Init();
+			builder.Polyline(points, color);
+#endif
+		}
+
+
+
+
+
+		/// 
+		/// \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.
+		/// 
+		[BurstDiscard]
+		public static void WireBox (float3 center, float3 size, Color color) {
+#if UNITY_EDITOR
+			DrawingManager.Init();
+			builder.WireBox(center, size, color);
+#endif
+		}
+
+		/// 
+		/// \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.
+		/// 
+		[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
+		}
+
+		/// 
+		/// \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.
+		/// 
+		[BurstDiscard]
+		public static void WireBox (Bounds bounds, Color color) {
+#if UNITY_EDITOR
+			DrawingManager.Init();
+			builder.WireBox(bounds, color);
+#endif
+		}
+
+		/// 
+		/// \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.
+		/// 
+		[BurstDiscard]
+		public static void WireMesh (Mesh mesh, Color color) {
+#if UNITY_EDITOR
+			DrawingManager.Init();
+			builder.WireMesh(mesh, color);
+#endif
+		}
+
+		/// 
+		/// \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.
+		/// 
+		[BurstDiscard]
+		public static void WireMesh (NativeArray vertices, NativeArray triangles, Color color) {
+#if UNITY_EDITOR
+			DrawingManager.Init();
+			builder.WireMesh(vertices, triangles, color);
+#endif
+		}
+
+
+
+		/// 
+		/// \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.
+		/// 
+		[BurstDiscard]
+		public static void Cross (float3 position, float size, Color color) {
+#if UNITY_EDITOR
+			DrawingManager.Init();
+			builder.Cross(position, size, color);
+#endif
+		}
+
+		/// 
+		/// \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.
+		/// 
+		[BurstDiscard]
+		public static void Cross (float3 position, Color color) {
+#if UNITY_EDITOR
+			DrawingManager.Init();
+			builder.Cross(position, color);
+#endif
+		}
+
+		/// 
+		/// \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.
+		/// 
+		[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
+		}
+
+		/// 
+		/// \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.
+		/// 
+		[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
+		}
+
+		/// 
+		/// \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.
+		/// 
+		[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
+		}
+
+		/// 
+		/// \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.
+		/// 
+		[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
+		}
+
+		/// 
+		/// \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.
+		/// 
+		[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
+		}
+
+
+
+
+
+		/// 
+		/// \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.
+		/// 
+		[BurstDiscard]
+		public static void Arrow (float3 from, float3 to, Color color) {
+#if UNITY_EDITOR
+			DrawingManager.Init();
+			builder.Arrow(from, to, color);
+#endif
+		}
+
+		/// 
+		/// \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.
+		/// 
+		[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
+		}
+
+		/// 
+		/// \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.
+		/// 
+		[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
+		}
+
+
+
+
+
+		/// 
+		/// \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.
+		/// 
+		[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
+		}
+
+		/// 
+		/// \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.
+		/// 
+		[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
+		}
+
+		/// 
+		/// \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.
+		/// 
+		[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
+		}
+
+
+
+
+
+		/// 
+		/// \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.
+		/// 
+		[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
+		}
+
+		/// 
+		/// \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.
+		/// 
+		[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
+		}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+		/// 
+		/// \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.
+		/// 
+		[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
+		}
+
+		/// 
+		/// \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.
+		/// 
+		[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
+		}
+
+
+
+
+
+
+
+		/// 
+		/// \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.
+		/// 
+		[BurstDiscard]
+		public static void SolidBox (float3 center, float3 size, Color color) {
+#if UNITY_EDITOR
+			DrawingManager.Init();
+			builder.SolidBox(center, size, color);
+#endif
+		}
+
+		/// 
+		/// \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.
+		/// 
+		[BurstDiscard]
+		public static void SolidBox (Bounds bounds, Color color) {
+#if UNITY_EDITOR
+			DrawingManager.Init();
+			builder.SolidBox(bounds, color);
+#endif
+		}
+
+		/// 
+		/// \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.
+		/// 
+		[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 {
+		/// 
+		/// Same as Time.time, but not updated as frequently.
+		/// Used since burst jobs cannot access Time.time.
+		/// 
+		public static readonly Unity.Burst.SharedStatic BurstTime = Unity.Burst.SharedStatic.GetOrCreate(4);
+
+		private class BurstTimeKey {}
+	}
+
+	/// 
+	/// 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.
+	///
+	/// 
+	/// 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();
+	/// }
+	/// 
+	///
+	/// See: 
+	/// 
+	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;
+		/// 
+		/// ID of the scope.
+		/// Zero means no or invalid scope.
+		/// 
+		internal int id;
+
+		static int idCounter = 1;
+
+		/// True if the scope has been created
+		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++;
+		}
+
+		/// 
+		/// 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.
+		/// 
+		internal void Draw () {
+			if (gizmos.IsAllocated) {
+				if (gizmos.Target is DrawingData gizmosTarget) gizmosTarget.Draw(this);
+			}
+		}
+
+		/// 
+		/// 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.
+		/// 
+		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);
+		}
+
+		/// 
+		/// 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.
+		/// 
+		public void Dispose () {
+			if (gizmos.IsAllocated) {
+				if (gizmos.Target is DrawingData gizmosTarget) gizmosTarget.DisposeRedrawScope(this);
+			}
+			gizmos = default;
+			id = 0;
+		}
+	};
+
+	/// 
+	/// 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
+	///
+	/// 
+	/// 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);
+	///     }
+	/// }
+	/// 
+	/// 
+	public class DrawingData {
+		/// Combines hashes into a single hash value
+		public struct Hasher : IEquatable {
+			ulong hash;
+
+			public static Hasher NotSupplied => new Hasher { hash = ulong.MaxValue };
+
+			public static Hasher Create(T init) {
+				var h = new Hasher();
+
+				h.Add(init);
+				return h;
+			}
+
+			public void Add(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 temporaryMeshBuffers;
+			JobHandle buildJob, splitterJob;
+			public List 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();
+				if (!temporaryMeshBuffers.IsCreated) {
+					temporaryMeshBuffers = new NativeArray(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 meshes) {
+				var itemMeshes = this.meshes;
+				var customMeshIndex = 0;
+				var capturedState = temporaryMeshBuffers[0].capturedState;
+				var maxCustomMeshes = capturedState.Length / UnsafeUtility.SizeOf();
+
+				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;
+				/// Used to invalidate gizmos when the scene mode changes
+				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 meshes;
+			public NativeArray 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();
+				if (!commandBuffers.IsCreated) {
+#if UNITY_2022_3_OR_NEWER
+					commandBuffers = new NativeArray(JobsUtility.ThreadIndexCount, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
+#else
+					commandBuffers = new NativeArray(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(AnyBuffersWrittenTo).Invoke;
+			unsafe delegate void ResetAllBuffersToDelegate(UnsafeAppendBuffer* buffers, int numBuffers);
+			private readonly unsafe static ResetAllBuffersToDelegate ResetAllBuffersToInvoke = BurstCompiler.CompileFunctionPointer(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 > hash2index;
+			Stack freeSlots;
+			Stack > 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();
+					freeLists = new Stack >();
+					hash2index = new Dictionary >();
+				}
+				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 ls;
+					if (!hash2index.TryGetValue(meta.hasher.Hash, out ls)) {
+						if (freeLists.Count == 0) freeLists.Push(new List());
+						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();
+			}
+
+			/// 
+			/// 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.
+			/// 
+			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 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 meshes = new List();
+		List cachedMeshes = new List();
+		List stagingCachedMeshes = new List();
+#if USE_RAW_GRAPHICS_BUFFERS
+		List stagingCachedMeshesDelay = new List();
+#endif
+		int lastTimeLargestCachedMeshWasUsed = 0;
+		internal SDFLookupData fontData;
+		int currentDrawOrderIndex = 0;
+
+		/// 
+		/// 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.
+		/// 
+		internal int sceneModeVersion = 0;
+
+		/// 
+		/// Slightly adjusted scene mode version.
+		/// This takes into account `Application.isPlaying` too. It is possible for  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.
+		/// 
+		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;
+		}
+
+		/// 
+		/// Get an empty builder for queuing drawing commands.
+		///
+		/// 
+		/// // 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);
+		/// }
+		/// 
+		/// See: 
+		/// 
+		/// 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.
+		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);
+		}
+
+		/// 
+		/// Get an empty builder for queuing drawing commands.
+		///
+		/// See: 
+		/// 
+		/// If true, this builder will be rendered in standalone games and in the editor even if gizmos are disabled.
+		public CommandBuilder GetBuilder (RedrawScope redrawScope, bool renderInGame = false) {
+			UpdateTime();
+			return new CommandBuilder(this, Hasher.NotSupplied, frameRedrawScope, redrawScope, !renderInGame, false, adjustedSceneModeVersion);
+		}
+
+		/// 
+		/// Get an empty builder for queuing drawing commands.
+		///
+		/// See: 
+		/// 
+		/// If true, this builder will be rendered in standalone games and in the editor even if gizmos are disabled.
+		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);
+		}
+
+		/// Material to use for surfaces
+		public Material surfaceMaterial;
+
+		/// Material to use for lines
+		public Material lineMaterial;
+
+		/// Material to use for text
+		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 persistentRedrawScopes = new Dictionary();
+#if ALINE_TRACK_REDRAW_SCOPE_LEAKS
+		Dictionary persistentRedrawScopeInfos = new Dictionary();
+#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 cameraVersions = new Dictionary();
+
+		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
+		}
+
+		/// 
+		/// 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.
+		/// 
+		public bool Draw (Hasher hasher) {
+			if (hasher.Equals(Hasher.NotSupplied)) throw new System.ArgumentException("Invalid hash value");
+			return processedData.SetVersion(hasher, version);
+		}
+
+		/// 
+		/// 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.
+		/// 
+		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);
+		}
+
+		/// Schedules all meshes that were drawn the last frame with this redraw scope to be drawn again
+		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 {
+			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("aline_surface");
+			}
+			if (lineMaterial == null) {
+				lineMaterial = Resources.Load("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));
+		}
+
+		/// 
+		/// 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.
+		/// 
+		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
+			}
+		}
+
+		/// Call after all  commands for the frame have been done to draw everything.
+		/// Indicates if built-in command builders and custom ones without a custom CommandBuilder.cameraTargets should render to this camera.
+		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;
+		}
+
+		/// Returns a new axis aligned bounding box that contains the given bounding box after being transformed by the matrix
+		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;
+		}
+
+		/// 
+		/// Destroys all cached meshes.
+		/// Used to make sure that no memory leaks happen in the Unity Editor.
+		/// 
+		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 {
+	/// Info about the current selection in the editor
+	public static class GizmoContext {
+#if UNITY_EDITOR
+		static Transform activeTransform;
+#endif
+
+		static HashSet selectedTransforms = new HashSet();
+
+		static internal bool drawingGizmos;
+		static internal bool dirty;
+		private static int selectionSizeInternal;
+
+		/// Number of top-level transforms that are selected
+		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
+		}
+
+		/// 
+		/// True if the component is selected.
+		/// This is a deep selection: even children of selected transforms are considered to be selected.
+		/// 
+		public static bool InSelection (Component c) {
+			return InSelection(c.transform);
+		}
+
+		/// 
+		/// True if the transform is selected.
+		/// This is a deep selection: even children of selected transforms are considered to be selected.
+		/// 
+		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;
+		}
+
+		/// 
+		/// True if the component is shown in the inspector.
+		/// The active selection is the GameObject that is currently visible in the inspector.
+		/// 
+		public static bool InActiveSelection (Component c) {
+			return InActiveSelection(c.transform);
+		}
+
+		/// 
+		/// True if the transform is shown in the inspector.
+		/// The active selection is the GameObject that is currently visible in the inspector.
+		/// 
+		public static bool InActiveSelection (Transform tr) {
+#if UNITY_EDITOR
+			Refresh();
+			return tr.transform == activeTransform;
+#else
+			return false;
+#endif
+		}
+	}
+
+	/// 
+	/// Every object that wants to draw gizmos should implement this interface.
+	/// See: 
+	/// 
+	public interface IDrawGizmos {
+		void DrawGizmos();
+	}
+
+	public enum DetectedRenderPipeline {
+		BuiltInOrCustom,
+		HDRP,
+		URP
+	}
+
+	/// 
+	/// Global script which draws debug items and gizmos.
+	/// If a Draw.* method has been used or if any script inheriting from the  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.
+	/// 
+	[ExecuteAlways]
+	[AddComponentMenu("")]
+	public class DrawingManager : MonoBehaviour {
+		public DrawingData gizmos;
+		static List gizmoDrawers = new List();
+		static Dictionary gizmoDrawerTypes = new Dictionary();
+		static DrawingManager _instance;
+		bool framePassed;
+		int lastFrameCount = int.MinValue;
+		float lastFrameTime = -float.NegativeInfinity;
+		int lastFilterFrame;
+#if UNITY_EDITOR
+		bool builtGizmos;
+#endif
+
+		/// True if OnEnable has been called on this instance and OnDisable has not
+		[SerializeField]
+		bool actuallyEnabled;
+
+		RedrawScope previousFrameRedrawScope;
+
+		/// 
+		/// 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: advanced (view in online documentation for working links)
+		/// 
+		public static bool allowRenderToRenderTextures = false;
+		public static bool drawToAllCameras = false;
+
+		/// 
+		/// 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  for that.
+		/// 
+		public static float lineWidthMultiplier = 1.0f;
+
+		CommandBuffer commandBuffer;
+
+		[System.NonSerialized]
+		DetectedRenderPipeline detectedRenderPipeline = DetectedRenderPipeline.BuiltInOrCustom;
+
+#if MODULE_RENDER_PIPELINES_UNIVERSAL
+		HashSet scriptableRenderersWithPass = new HashSet();
+		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() 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();
+			if (Application.isPlaying) DontDestroyOnLoad(go);
+		}
+
+		/// Detects which render pipeline is being used and configures them for rendering
+		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(out CustomPassVolume volume)) {
+						volume = _instance.gameObject.AddComponent();
+						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 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();
+					}
+					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 typeToGizmosEnabled = new Dictionary();
+
+		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
+
+		/// Submit a camera for rendering.
+		/// Indicates if built-in command builders and custom ones without a custom CommandBuilder.cameraTargets should render to this camera.
+		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();
+		}
+
+		/// 
+		/// 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).
+		/// 
+		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);
+		}
+
+		/// 
+		/// Get an empty builder for queuing drawing commands.
+		///
+		/// 
+		/// // 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);
+		/// }
+		/// 
+		/// See: 
+		/// 
+		/// 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.
+		public static CommandBuilder GetBuilder(bool renderInGame = false) => instance.gizmos.GetBuilder(renderInGame);
+
+		/// 
+		/// Get an empty builder for queuing drawing commands.
+		///
+		/// See: 
+		/// 
+		/// Scope for this command builder. See #GetRedrawScope.
+		/// 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.
+		public static CommandBuilder GetBuilder(RedrawScope redrawScope, bool renderInGame = false) => instance.gizmos.GetBuilder(redrawScope, renderInGame);
+
+		/// 
+		/// Get an empty builder for queuing drawing commands.
+		/// TODO: Example usage.
+		///
+		/// See: 
+		/// 
+		/// Hash of whatever inputs you used to generate the drawing data.
+		/// Scope for this command builder. See #GetRedrawScope.
+		/// If true, this builder will be rendered in standalone games and in the editor even if gizmos are disabled.
+		public static CommandBuilder GetBuilder(DrawingData.Hasher hasher, RedrawScope redrawScope = default, bool renderInGame = false) => instance.gizmos.GetBuilder(hasher, redrawScope, renderInGame);
+
+		/// 
+		/// A scope which can be used to draw things over multiple frames.
+		///
+		/// You can use  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.
+		///
+		/// 
+		/// 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();
+		/// }
+		/// 
+		/// 
+		/// 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.
+		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 {
+	/// Stores ALINE project settings
+	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";
+
+		/// Stores ALINE project settings
+		[System.Serializable]
+		public class Settings {
+			/// Opacity of lines when in front of objects
+			public float lineOpacity = 1.0f;
+
+			/// Opacity of solid objects when in front of other objects
+
+			public float solidOpacity = 0.55f;
+
+			/// Opacity of text when in front of other objects
+
+			public float textOpacity = 1.0f;
+
+			/// Additional opacity multiplier of lines when behind or inside objects
+
+			public float lineOpacityBehindObjects = 0.12f;
+
+			/// Additional opacity multiplier of solid objects when behind or inside other objects
+
+			public float solidOpacityBehindObjects = 0.45f;
+
+			/// Additional opacity multiplier of text when behind or inside other objects
+
+			public float textOpacityBehindObjects = 0.9f;
+
+			/// 
+			/// 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.
+			/// 
+			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(SettingsPath);
+			if (settings == null && AssetDatabase.LoadAssetAtPath(SettingsPathCompatibility) != null) {
+				AssetDatabase.MoveAsset(SettingsPathCompatibility, SettingsPath);
+				settings = AssetDatabase.LoadAssetAtPath(SettingsPath);
+			}
+			if (settings == null) {
+				settings = ScriptableObject.CreateInstance();
+				settings.settings = DefaultSettings;
+				settings.version = 0;
+				AssetDatabase.CreateAsset(settings, SettingsPath);
+				AssetDatabase.SaveAssets();
+			}
+#else
+			var settings = Resources.Load(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 {
+	/// Various high-level utilities that are useful when drawing things
+	public static class DrawingUtilities {
+		private static List componentBuffer = new List();
+
+		/// 
+		/// 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]
+		/// 
+		/// Draw.WireBox(DrawingUtilities.BoundsFrom(transform), Color.black);
+		/// 
+		///
+		/// See: 
+		/// 
+		public static Bounds BoundsFrom (GameObject gameObject) {
+			return BoundsFrom(gameObject.transform);
+		}
+
+		/// 
+		/// 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]
+		/// 
+		/// Draw.WireBox(DrawingUtilities.BoundsFrom(transform), Color.black);
+		/// 
+		///
+		/// See: 
+		/// 
+		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;
+		}
+
+		/// 
+		/// Bounding box which contains all points in the list.
+		/// 
+		/// List points = new List { new Vector3(0, 0, 0), new Vector3(1, 0, 0), new Vector3(0, 1, 1) };
+		/// Draw.WireBox(DrawingUtilities.BoundsFrom(points), Color.black);
+		/// 
+		///
+		/// See: [])
+		/// See: 
+		/// 
+		public static Bounds BoundsFrom (List 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);
+		}
+
+		/// 
+		/// Bounding box which contains all points in the array.
+		/// 
+		/// List points = new List { new Vector3(0, 0, 0), new Vector3(1, 0, 0), new Vector3(0, 1, 1) };
+		/// Draw.WireBox(DrawingUtilities.BoundsFrom(points), Color.black);
+		/// 
+		///
+		/// See: 
+		/// See: 
+		/// 
+		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);
+		}
+
+		/// 
+		/// Bounding box which contains all points in the array.
+		/// 
+		/// List points = new List { new Vector3(0, 0, 0), new Vector3(1, 0, 0), new Vector3(0, 1, 1) };
+		/// Draw.WireBox(DrawingUtilities.BoundsFrom(points), Color.black);
+		/// 
+		///
+		/// See: 
+		/// See: [])
+		/// 
+		public static Bounds BoundsFrom (NativeArray 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 {
+	/// Helper for adding project settings
+	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(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);
+		}
+
+		/// 
+		/// 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.
+		/// 
+		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 ConvertExistingDataToNativeArray(UnsafeAppendBuffer data) where T : struct {
+			unsafe {
+				var arr = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray(data.Ptr, data.Length / UnsafeUtility.SizeOf(), Allocator.Invalid);
+#if ENABLE_UNITY_COLLECTIONS_CHECKS
+				NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref arr, AtomicSafetyHandle.GetTempMemoryHandle());
+#endif
+				return arr;
+			}
+		}
+
+		internal static unsafe void BuildMesh (DrawingData gizmos, List meshes, ProcessedBuilderData.MeshBuffers* inputBuffers) {
+			if (inputBuffers->triangles.Length > 0) {
+				CommandBuilderSamplers.MarkerUpdateBuffer.Begin();
+				var mesh = AssignMeshData(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(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(gizmos, inputBuffers->bounds, inputBuffers->textVertices, inputBuffers->textTriangles, MeshLayouts.MeshLayoutText);
+				meshes.Add(new MeshWithType { mesh = mesh, type = MeshType.Text });
+			}
+		}
+
+		private static Mesh AssignMeshData(DrawingData gizmos, Bounds bounds, UnsafeAppendBuffer vertices, UnsafeAppendBuffer triangles, VertexAttributeDescriptor[] layout) where VertexType : struct {
+			CommandBuilderSamplers.MarkerConvert.Begin();
+			var verticesView = ConvertExistingDataToNativeArray(vertices);
+			var trianglesView = ConvertExistingDataToNativeArray(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;
+		}
+	}
+
+	/// Some static fields that need to be in a separate class because Burst doesn't support them
+	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),
+		};
+	}
+
+	/// 
+	/// Job to build the geometry from a stream of rendering commands.
+	///
+	/// See: 
+	/// 
+	// 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(UnsafeAppendBuffer* buffer, T value) where T : unmanaged {
+			int size = UnsafeUtility.SizeOf();
+			// 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());
+			Reserve(textTriangles, numCharacters * TrianglesPerCharacter * UnsafeUtility.SizeOf());
+
+			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();
+
+				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());
+
+				// 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()) {
+					// 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();
+				*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;
+			}
+		}
+
+		/// Calculate number of steps to use for drawing a circle at the specified point and radius to get less than the specified pixel error.
+		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());
+				Reserve(solidTriangles, 3*(steps-2) * UnsafeUtility.SizeOf());
+
+				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();
+
+				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();
+				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());
+				Reserve(solidTriangles, 3*steps * UnsafeUtility.SizeOf());
+
+				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();
+
+				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());
+				Reserve(solidTriangles, 3 * UnsafeUtility.SizeOf());
+				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();
+
+				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());
+				Reserve(solidTriangles, BoxTriangles.Length * UnsafeUtility.SizeOf());
+
+				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();
+				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();
+
+				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();
+			}
+		}
+
+		// 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 matrixStack, ref NativeArray colorStack, ref NativeArray lineWidthStack, ref int matrixStackSize, ref int colorStackSize, ref int lineWidthStackSize) {
+			var fullCmd = reader.ReadNext();
+			var cmd = fullCmd & (Command)0xFF;
+			Color32 oldColor = default;
+
+			if ((fullCmd & Command.PushColorInline) != 0) {
+				oldColor = currentColor;
+				currentColor = reader.ReadNext();
+			}
+
+			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();
+				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());
+				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();
+				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();
+				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());
+				break;
+			case Command.SphereOutline:
+				AddSphereOutline(reader.ReadNext());
+				break;
+			case Command.CircleXZ:
+				AddCircle(reader.ReadNext());
+				break;
+			case Command.Circle:
+				AddCircle(reader.ReadNext());
+				break;
+			case Command.DiscXZ:
+				AddDisc(reader.ReadNext());
+				break;
+			case Command.Disc:
+				AddDisc(reader.ReadNext());
+				break;
+			case Command.Box:
+				AddBox(reader.ReadNext());
+				break;
+			case Command.WirePlane:
+				AddPlane(reader.ReadNext());
+				break;
+			case Command.WireBox:
+				AddWireBox(reader.ReadNext());
+				break;
+			case Command.SolidTriangle:
+				AddSolidTriangle(reader.ReadNext());
+				break;
+			case Command.PushPersist:
+				// This command does not need to be handled by the builder
+				reader.ReadNext();
+				break;
+			case Command.PopPersist:
+				// This command does not need to be handled by the builder
+				break;
+			case Command.Text:
+				var data = reader.ReadNext();
+				unsafe {
+					System.UInt16* ptr = (System.UInt16*)reader.ReadNext(UnsafeUtility.SizeOf() * data.numCharacters);
+					AddText(ptr, data, currentColor);
+				}
+				break;
+			case Command.Text3D:
+				var data2 = reader.ReadNext();
+				unsafe {
+					System.UInt16* ptr = (System.UInt16*)reader.ReadNext(UnsafeUtility.SizeOf() * 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();
+				// Each line is made out of 4 vertices
+				var lineCount = vertexCount / 4;
+				var trianglesSizeInBytes = lineCount * 6 * UnsafeUtility.SizeOf();
+				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(MaxStackSize, Allocator.Temp, NativeArrayOptions.UninitializedMemory);
+			var colorStack = new NativeArray(MaxStackSize, Allocator.Temp, NativeArrayOptions.UninitializedMemory);
+			var lineWidthStack = new NativeArray(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 {
+	/// 
+	/// Inherit from this class to draw gizmos.
+	/// See: getstarted (view in online documentation for working links)
+	/// 
+	public abstract class MonoBehaviourGizmos : MonoBehaviour, IDrawGizmos {
+		public MonoBehaviourGizmos() {
+#if UNITY_EDITOR
+			DrawingManager.Register(this);
+#endif
+		}
+
+		/// 
+		/// 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.
+		/// 
+		void OnDrawGizmosSelected () {
+		}
+
+		/// 
+		/// 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}
+		/// 
+		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 {
+	/// 
+	/// Collections of colors.
+	///
+	/// The easiest way to use this class is to import it with a "using" statement:
+	///
+	/// 
+	/// 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);
+	///     }
+	/// }
+	/// 
+	///
+	/// Note: This class has relatively few color collections at the moment. More will be added in the future.
+	/// 
+	public static class Palette {
+		/// Pure colors
+		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);
+		}
+
+		/// 
+		/// Colorbrewer colors.
+		/// See: http://colorbrewer2.org/
+		/// 
+		public static class Colorbrewer {
+			/// Set 1 - Qualitative
+			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);
+			}
+
+			/// Blues - Sequential
+			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),
+				};
+
+				/// Returns a color for the specified class.
+				/// Number of classes. Must be between 1 and 9.
+				/// Index of the color class. Must be between 0 and classes-1.
+				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(GeometryBuilderJob.MaxStackSize, Allocator.Temp, NativeArrayOptions.ClearMemory);
+			var stackScope = new NativeArray(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() <= 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() : 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();
+					} 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();
+					}
+
+#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
new file mode 100644
index 0000000..1aa41eb
Binary files /dev/null and b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_fallback_font.png differ
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
new file mode 100644
index 0000000..8fa9b54
Binary files /dev/null and b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_font.png differ
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
new file mode 100644
index 0000000..6e79dad
Binary files /dev/null and b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Drawing/Resources/aline_font.ttf differ
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 {
+		/// Represents a single character in a font texture
+		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;
+			}
+		}
+
+		/// Represents an SDF font
+		internal struct SDFFont {
+			public string name;
+			public int size, width, height;
+			public bool bold, italic;
+			public SDFCharacter[] characters;
+			public UnityEngine.Material material;
+		}
+
+		/// Optimzed lookup for accessing font data from the unity job system
+		internal struct SDFLookupData {
+			public NativeArray characters;
+			Dictionary 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(128 + nonAscii, Allocator.Persistent);
+				for (int i = 0; i < characters.Length; i++) {
+					characters[i] = questionMark;
+				}
+				lookup = new Dictionary();
+				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("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 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() + UnsafeUtility.SizeOf();
+			CommandSizes[(int)Command.PopColor] = UnsafeUtility.SizeOf() + 0;
+			CommandSizes[(int)Command.PushMatrix] = UnsafeUtility.SizeOf() + UnsafeUtility.SizeOf();
+			CommandSizes[(int)Command.PushSetMatrix] = UnsafeUtility.SizeOf() + UnsafeUtility.SizeOf();
+			CommandSizes[(int)Command.PopMatrix] = UnsafeUtility.SizeOf() + 0;
+			CommandSizes[(int)Command.Line] = UnsafeUtility.SizeOf() + UnsafeUtility.SizeOf();
+			CommandSizes[(int)Command.CircleXZ] = UnsafeUtility.SizeOf() + UnsafeUtility.SizeOf