Advertisement
Guest User

Untitled

a guest
Feb 20th, 2020
3,177
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 13.23 KB | None | 0 0
  1. using UnityEngine;
  2. using System.Collections.Generic;
  3. #if UNITY_5_5_OR_NEWER
  4. using UnityEngine.Profiling;
  5. #endif
  6.  
  7. namespace Pathfinding.Util {
  8.     /** Helper for drawing Gizmos in a performant way.
  9.      * This is a replacement for the Unity Gizmos class as that is not very performant
  10.      * when drawing very large amounts of geometry (for example a large grid graph).
  11.      * These gizmos can be persistent, so if the data does not change, the gizmos
  12.      * do not need to be updated.
  13.      *
  14.      * How to use
  15.      * - Create a Hasher object and hash whatever data you will be using to draw the gizmos
  16.      *      Could be for example the positions of the vertices or something. Just as long as
  17.      *      if the gizmos should change, then the hash changes as well.
  18.      * - Check if a cached mesh exists for that hash
  19.      * - If not, then create a Builder object and call the drawing methods until you are done
  20.      *      and then call Finalize with a reference to a gizmos class and the hash you calculated before.
  21.      * - Call gizmos.Draw with the hash.
  22.      * - When you are done with drawing gizmos for this frame, call gizmos.FinalizeDraw
  23.      *
  24.      * \code
  25.      * var a = Vector3.zero;
  26.      * var b = Vector3.one;
  27.      * var color = Color.red;
  28.      * var hasher = new RetainedGizmos.Hasher();
  29.      * hasher.AddHash(a.GetHashCode());
  30.      * hasher.AddHash(b.GetHashCode());
  31.      * hasher.AddHash(color.GetHashCode());
  32.      * if (!gizmos.Draw(hasher)) {
  33.      *     using (var helper = gizmos.GetGizmoHelper(active, hasher)) {
  34.      *         builder.DrawLine(a, b, color);
  35.      *         builder.Finalize(gizmos, hasher);
  36.      *     }
  37.      * }
  38.      * \endcode
  39.      */
  40.     public class RetainedGizmos {
  41.         /** Combines hashes into a single hash value */
  42.         public struct Hasher {
  43.             ulong hash;
  44.             bool includePathSearchInfo;
  45.             bool includeAreaInfo;
  46.             PathHandler debugData;
  47.  
  48.             public Hasher (AstarPath active) {
  49.                 hash = 0;
  50.                 this.debugData = active.debugPathData;
  51.                 includePathSearchInfo = debugData != null && (active.debugMode == GraphDebugMode.F || active.debugMode == GraphDebugMode.G || active.debugMode == GraphDebugMode.H || active.showSearchTree);
  52.                 includeAreaInfo = active.debugMode == GraphDebugMode.Areas;
  53.                 AddHash((int)active.debugMode);
  54.                 AddHash(active.debugFloor.GetHashCode());
  55.                 AddHash(active.debugRoof.GetHashCode());
  56.                 AddHash(AstarColor.ColorHash());
  57.             }
  58.  
  59.             public void AddHash (int hash) {
  60.                 this.hash = (1572869UL * this.hash) ^ (ulong)hash;
  61.             }
  62.  
  63.             public void HashNode (GraphNode node) {
  64.                 AddHash(node.GetGizmoHashCode());
  65.                 if (includeAreaInfo) AddHash((int)node.Area);
  66.  
  67.                 if (includePathSearchInfo) {
  68.                     var pathNode = debugData.GetPathNode(node.NodeIndex);
  69.                     AddHash((int)pathNode.pathID);
  70.                     AddHash(pathNode.pathID == debugData.PathID ? 1 : 0);
  71.                     AddHash((int)pathNode.F);
  72.                 }
  73.             }
  74.  
  75.             public ulong Hash {
  76.                 get {
  77.                     return hash;
  78.                 }
  79.             }
  80.         }
  81.  
  82.         /** Helper for drawing gizmos */
  83.         public class Builder : IAstarPooledObject {
  84.             List<Vector3> lines = new List<Vector3>();
  85.             List<Color32> lineColors = new List<Color32>();
  86.             List<Mesh> meshes = new List<Mesh>();
  87.  
  88.             public void DrawMesh (RetainedGizmos gizmos, Vector3[] vertices, List<int> triangles, Color[] colors) {
  89.                 var mesh = gizmos.GetMesh();
  90.  
  91.                 // Set all data on the mesh
  92.                 mesh.vertices = vertices;
  93.                 mesh.SetTriangles(triangles, 0);
  94.                 mesh.colors = colors;
  95.  
  96.                 // Upload all data
  97.                 mesh.UploadMeshData(false);
  98.                 meshes.Add(mesh);
  99.             }
  100.  
  101.             /** Draws a wire cube after being transformed the specified transformation */
  102.             public void DrawWireCube (GraphTransform tr, Bounds bounds, Color color) {
  103.                 var min = bounds.min;
  104.                 var max = bounds.max;
  105.  
  106.                 DrawLine(tr.Transform(new Vector3(min.x, min.y, min.z)), tr.Transform(new Vector3(max.x, min.y, min.z)), color);
  107.                 DrawLine(tr.Transform(new Vector3(max.x, min.y, min.z)), tr.Transform(new Vector3(max.x, min.y, max.z)), color);
  108.                 DrawLine(tr.Transform(new Vector3(max.x, min.y, max.z)), tr.Transform(new Vector3(min.x, min.y, max.z)), color);
  109.                 DrawLine(tr.Transform(new Vector3(min.x, min.y, max.z)), tr.Transform(new Vector3(min.x, min.y, min.z)), color);
  110.  
  111.                 DrawLine(tr.Transform(new Vector3(min.x, max.y, min.z)), tr.Transform(new Vector3(max.x, max.y, min.z)), color);
  112.                 DrawLine(tr.Transform(new Vector3(max.x, max.y, min.z)), tr.Transform(new Vector3(max.x, max.y, max.z)), color);
  113.                 DrawLine(tr.Transform(new Vector3(max.x, max.y, max.z)), tr.Transform(new Vector3(min.x, max.y, max.z)), color);
  114.                 DrawLine(tr.Transform(new Vector3(min.x, max.y, max.z)), tr.Transform(new Vector3(min.x, max.y, min.z)), color);
  115.  
  116.                 DrawLine(tr.Transform(new Vector3(min.x, min.y, min.z)), tr.Transform(new Vector3(min.x, max.y, min.z)), color);
  117.                 DrawLine(tr.Transform(new Vector3(max.x, min.y, min.z)), tr.Transform(new Vector3(max.x, max.y, min.z)), color);
  118.                 DrawLine(tr.Transform(new Vector3(max.x, min.y, max.z)), tr.Transform(new Vector3(max.x, max.y, max.z)), color);
  119.                 DrawLine(tr.Transform(new Vector3(min.x, min.y, max.z)), tr.Transform(new Vector3(min.x, max.y, max.z)), color);
  120.             }
  121.  
  122.             public void DrawLine (Vector3 start, Vector3 end, Color color) {
  123.                 lines.Add(start);
  124.                 lines.Add(end);
  125.                 var col32 = (Color32)color;
  126.                 lineColors.Add(col32);
  127.                 lineColors.Add(col32);
  128.             }
  129.  
  130.             public void Submit (RetainedGizmos gizmos, Hasher hasher) {
  131.                 SubmitLines(gizmos, hasher.Hash);
  132.                 SubmitMeshes(gizmos, hasher.Hash);
  133.             }
  134.  
  135.             void SubmitMeshes (RetainedGizmos gizmos, ulong hash) {
  136.                 for (int i = 0; i < meshes.Count; i++) {
  137.                     gizmos.meshes.Add(new MeshWithHash { hash = hash, mesh = meshes[i], lines = false });
  138.                     gizmos.existingHashes.Add(hash);
  139.                 }
  140.             }
  141.  
  142.             void SubmitLines (RetainedGizmos gizmos, ulong hash) {
  143.                 // Unity only supports 65535 vertices per mesh. 65532 used because MaxLineEndPointsPerBatch needs to be even.
  144.                 const int MaxLineEndPointsPerBatch = 65532/2;
  145.                 int batches = (lines.Count + MaxLineEndPointsPerBatch - 1)/MaxLineEndPointsPerBatch;
  146.  
  147.                 for (int batch = 0; batch < batches; batch++) {
  148.                     int startIndex = MaxLineEndPointsPerBatch * batch;
  149.                     int endIndex = Mathf.Min(startIndex + MaxLineEndPointsPerBatch, lines.Count);
  150.                     int lineEndPointCount = endIndex - startIndex;
  151.                     UnityEngine.Assertions.Assert.IsTrue(lineEndPointCount % 2 == 0);
  152.  
  153.                     // Use pooled lists to avoid excessive allocations
  154.                     var vertices = ListPool<Vector3>.Claim (lineEndPointCount*2);
  155.                     var colors = ListPool<Color32>.Claim (lineEndPointCount*2);
  156.                     var normals = ListPool<Vector3>.Claim (lineEndPointCount*2);
  157.                     var uv = ListPool<Vector2>.Claim (lineEndPointCount*2);
  158.                     var tris = ListPool<int>.Claim (lineEndPointCount*3);
  159.                     // Loop through each endpoint of the lines
  160.                     // and add 2 vertices for each
  161.                     for (int j = startIndex; j < endIndex; j++) {
  162.                         var vertex = (Vector3)lines[j];
  163.                         vertices.Add(vertex);
  164.                         vertices.Add(vertex);
  165.  
  166.                         var color = (Color32)lineColors[j];
  167.                         colors.Add(color);
  168.                         colors.Add(color);
  169.                         uv.Add(new Vector2(0, 0));
  170.                         uv.Add(new Vector2(1, 0));
  171.                     }
  172.  
  173.                     // Loop through each line and add
  174.                     // one normal for each vertex
  175.                     for (int j = startIndex; j < endIndex; j += 2) {
  176.                         var lineDir = (Vector3)(lines[j+1] - lines[j]);
  177.                         // Store the line direction in the normals.
  178.                         // A line consists of 4 vertices. The line direction will be used to
  179.                         // offset the vertices to create a line with a fixed pixel thickness
  180.                         normals.Add(lineDir);
  181.                         normals.Add(lineDir);
  182.                         normals.Add(lineDir);
  183.                         normals.Add(lineDir);
  184.                     }
  185.  
  186.                     // Setup triangle indices
  187.                     // A triangle consists of 3 indices
  188.                     // A line (4 vertices) consists of 2 triangles, so 6 triangle indices
  189.                     for (int j = 0, v = 0; j < lineEndPointCount*3; j += 6, v += 4) {
  190.                         // First triangle
  191.                         tris.Add(v+0);
  192.                         tris.Add(v+1);
  193.                         tris.Add(v+2);
  194.  
  195.                         // Second triangle
  196.                         tris.Add(v+1);
  197.                         tris.Add(v+3);
  198.                         tris.Add(v+2);
  199.                     }
  200.  
  201.                     var mesh = gizmos.GetMesh();
  202.  
  203.                     // Set all data on the mesh
  204.                     mesh.SetVertices(vertices);
  205.                     mesh.SetTriangles(tris, 0);
  206.                     mesh.SetColors(colors);
  207.                     mesh.SetNormals(normals);
  208.                     mesh.SetUVs(0, uv);
  209.  
  210.                     // Upload all data
  211.                     mesh.UploadMeshData(false);
  212.  
  213.                     // Release the lists back to the pool
  214.                     ListPool<Vector3>.Release (ref vertices);
  215.                     ListPool<Color32>.Release (ref colors);
  216.                     ListPool<Vector3>.Release (ref normals);
  217.                     ListPool<Vector2>.Release (ref uv);
  218.                     ListPool<int>.Release (ref tris);
  219.  
  220.                     gizmos.meshes.Add(new MeshWithHash { hash = hash, mesh = mesh, lines = true });
  221.                     gizmos.existingHashes.Add(hash);
  222.                 }
  223.             }
  224.  
  225.             void IAstarPooledObject.OnEnterPool () {
  226.                 lines.Clear();
  227.                 lineColors.Clear();
  228.                 meshes.Clear();
  229.             }
  230.         }
  231.  
  232.         struct MeshWithHash {
  233.             public ulong hash;
  234.             public Mesh mesh;
  235.             public bool lines;
  236.         }
  237.  
  238.         List<MeshWithHash> meshes = new List<MeshWithHash>();
  239.         HashSet<ulong> usedHashes = new HashSet<ulong>();
  240.         HashSet<ulong> existingHashes = new HashSet<ulong>();
  241.         Stack<Mesh> cachedMeshes = new Stack<Mesh>();
  242.  
  243.         public GraphGizmoHelper GetSingleFrameGizmoHelper (AstarPath active) {
  244.             var uniqHash = new RetainedGizmos.Hasher();
  245.  
  246.             uniqHash.AddHash(Time.realtimeSinceStartup.GetHashCode());
  247.             Draw(uniqHash);
  248.             return GetGizmoHelper(active, uniqHash);
  249.         }
  250.  
  251.         public GraphGizmoHelper GetGizmoHelper (AstarPath active, Hasher hasher) {
  252.             var helper = ObjectPool<GraphGizmoHelper>.Claim ();
  253.  
  254.             helper.Init(active, hasher, this);
  255.             return helper;
  256.         }
  257.  
  258.         void PoolMesh (Mesh mesh) {
  259.             mesh.Clear();
  260.             cachedMeshes.Push(mesh);
  261.         }
  262.  
  263.         Mesh GetMesh () {
  264.             if (cachedMeshes.Count > 0) {
  265.                 return cachedMeshes.Pop();
  266.             } else {
  267.                 return new Mesh {
  268.                            hideFlags = HideFlags.DontSave
  269.                 };
  270.             }
  271.         }
  272.  
  273.         /** Material to use for the navmesh in the editor */
  274.         public Material surfaceMaterial;
  275.  
  276.         /** Material to use for the navmesh outline in the editor */
  277.         public Material lineMaterial;
  278.  
  279.         /** True if there already is a mesh with the specified hash */
  280.         public bool HasCachedMesh (Hasher hasher) {
  281.             return existingHashes.Contains(hasher.Hash);
  282.         }
  283.  
  284.         /** Schedules the meshes for the specified hash to be drawn.
  285.          * \returns False if there is no cached mesh for this hash, you may want to
  286.          *  submit one in that case. The draw command will be issued regardless of the return value.
  287.          */
  288.         public bool Draw (Hasher hasher) {
  289.             usedHashes.Add(hasher.Hash);
  290.             return HasCachedMesh(hasher);
  291.         }
  292.  
  293.         /** Schedules all meshes that were drawn the last frame (last time FinalizeDraw was called) to be drawn again.
  294.          * Also draws any new meshes that have been added since FinalizeDraw was last called.
  295.          */
  296.         public void DrawExisting () {
  297.             for (int i = 0; i < meshes.Count; i++) {
  298.                 usedHashes.Add(meshes[i].hash);
  299.             }
  300.         }
  301.  
  302.         /** Call after all #Draw commands for the frame have been done to draw everything */
  303.         public void FinalizeDraw () {
  304.             RemoveUnusedMeshes(meshes);
  305.  
  306. #if UNITY_EDITOR
  307.             // Make sure the material references are correct
  308.             if (surfaceMaterial == null) surfaceMaterial = UnityEditor.AssetDatabase.LoadAssetAtPath(EditorResourceHelper.editorAssets + "/Materials/Navmesh.mat", typeof(Material)) as Material;
  309.             if (lineMaterial == null) lineMaterial = UnityEditor.AssetDatabase.LoadAssetAtPath(EditorResourceHelper.editorAssets + "/Materials/NavmeshOutline.mat", typeof(Material)) as Material;
  310. #endif
  311.  
  312.             var cam = Camera.current;
  313.             var planes = GeometryUtility.CalculateFrustumPlanes(cam);
  314.  
  315.             // Silently do nothing if the materials are not set
  316.             if (surfaceMaterial == null || lineMaterial == null) return;
  317.  
  318.             Profiler.BeginSample("Draw Retained Gizmos");
  319.             // First surfaces, then lines
  320.             for (int matIndex = 0; matIndex <= 1; matIndex++) {
  321.                 var mat = matIndex == 0 ? surfaceMaterial : lineMaterial;
  322.                 for (int pass = 0; pass < mat.passCount; pass++) {
  323.                     mat.SetPass(pass);
  324.                     for (int i = 0; i < meshes.Count; i++) {
  325.                         if (meshes[i].lines == (mat == lineMaterial) && GeometryUtility.TestPlanesAABB(planes, meshes[i].mesh.bounds)) {
  326.                             Graphics.DrawMeshNow(meshes[i].mesh, Matrix4x4.identity);
  327.                         }
  328.                     }
  329.                 }
  330.             }
  331.  
  332.             usedHashes.Clear();
  333.             Profiler.EndSample();
  334.         }
  335.  
  336.         /** Destroys all cached meshes.
  337.          * Used to make sure that no memory leaks happen in the Unity Editor.
  338.          */
  339.         public void ClearCache () {
  340.             usedHashes.Clear();
  341.             RemoveUnusedMeshes(meshes);
  342.  
  343.             while (cachedMeshes.Count > 0) {
  344.                 Mesh.DestroyImmediate(cachedMeshes.Pop());
  345.             }
  346.  
  347.             UnityEngine.Assertions.Assert.IsTrue(meshes.Count == 0);
  348.         }
  349.  
  350.         void RemoveUnusedMeshes (List<MeshWithHash> meshList) {
  351.             // Walk the array with two pointers
  352.             // i pointing to the entry that should be filled with something
  353.             // and j pointing to the entry that is a potential candidate for
  354.             // filling the entry at i.
  355.             // When j reaches the end of the list it will be reduced in size
  356.             for (int i = 0, j = 0; i < meshList.Count;) {
  357.                 if (j == meshList.Count) {
  358.                     j--;
  359.                     meshList.RemoveAt(j);
  360.                 } else if (usedHashes.Contains(meshList[j].hash)) {
  361.                     meshList[i] = meshList[j];
  362.                     i++;
  363.                     j++;
  364.                 } else {
  365.                     PoolMesh(meshList[j].mesh);
  366.                     existingHashes.Remove(meshList[j].hash);
  367.                     j++;
  368.                 }
  369.             }
  370.         }
  371.     }
  372. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement