/** Updates shortcuts to the first graph of different types. * Hard coding references to some graph types is not really a good thing imo. I want to keep it dynamic and flexible. * But these references ease the use of the system, so I decided to keep them. It is the only reference to specific graph types in the pathfinding core.\n */ public void UpdateShortcuts () { navmesh = (NavMeshGraph)FindGraphOfType (typeof(NavMeshGraph)); #if !ASTAR_NO_GRID_GRAPH gridGraph = (GridGraph)FindGraphOfType (typeof(GridGraph)); #endif #if !ASTAR_NO_POINT_GRAPH pointGraph = (PointGraph)FindGraphOfType (typeof(PointGraph)); #endif recastGraph = (RecastGraph)FindGraphOfType (typeof(RecastGraph)); }
/** Updates shortcuts to the first graph of different types. * Hard coding references to some graph types is not really a good thing imo. I want to keep it dynamic and flexible. * But these references ease the use of the system, so I decided to keep them. It is the only reference to specific graph types in the pathfinding core.\n */ public void UpdateShortcuts () { navmesh = (NavMeshGraph)FindGraphOfType (typeof(NavMeshGraph)); gridGraph = (GridGraph)FindGraphOfType (typeof(GridGraph)); pointGraph = (PointGraph)FindGraphOfType (typeof(PointGraph)); recastGraph = (RecastGraph)FindGraphOfType (typeof(RecastGraph)); }
/** Creates an outline of the navmesh for use in OnDrawGizmos in the editor */ static Mesh CreateNavmeshOutlineVisualization (RecastGraph.NavmeshTile tile) { var sharedEdges = new bool[3]; var mesh = new Mesh(); mesh.hideFlags = HideFlags.DontSave; var colorList = ListPool<Color32>.Claim(); var edgeList = ListPool<Int3>.Claim(); for (int j = 0; j < tile.nodes.Length; j++) { sharedEdges[0] = sharedEdges[1] = sharedEdges[2] = false; var node = tile.nodes[j]; for (int c = 0; c < node.connections.Length; c++) { var other = node.connections[c] as TriangleMeshNode; // Loop throgh neighbours to figure // out which edges are shared if (other != null && other.GraphIndex == node.GraphIndex) { for (int v = 0; v < 3; v++) { for (int v2 = 0; v2 < 3; v2++) { if (node.GetVertexIndex(v) == other.GetVertexIndex((v2+1)%3) && node.GetVertexIndex((v+1)%3) == other.GetVertexIndex(v2)) { // Found a shared edge with the other node sharedEdges[v] = true; v = 3; break; } } } } } for (int v = 0; v < 3; v++) { if (!sharedEdges[v]) { edgeList.Add(node.GetVertex(v)); edgeList.Add(node.GetVertex((v+1)%3)); var color = (Color32)AstarColor.GetAreaColor(node.Area); colorList.Add(color); colorList.Add(color); } } } // Use pooled lists to avoid excessive allocations var vertices = ListPool<Vector3>.Claim(edgeList.Count*2); var colors = ListPool<Color32>.Claim(edgeList.Count*2); var normals = ListPool<Vector3>.Claim(edgeList.Count*2); var tris = ListPool<int>.Claim(edgeList.Count*3); // Loop through each endpoint of the lines // and add 2 vertices for each for (int j = 0; j < edgeList.Count; j++) { var vertex = (Vector3)edgeList[j]; vertices.Add(vertex); vertices.Add(vertex); // Encode the side of the line // in the alpha component var color = colorList[j]; colors.Add(new Color32(color.r, color.g, color.b, 0)); colors.Add(new Color32(color.r, color.g, color.b, 255)); } // Loop through each line and add // one normal for each vertex for (int j = 0; j < edgeList.Count; j += 2) { var lineDir = (Vector3)(edgeList[j+1] - edgeList[j]); lineDir.Normalize(); // Store the line direction in the normals // 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 normals.Add(lineDir); normals.Add(lineDir); normals.Add(lineDir); normals.Add(lineDir); } // Setup triangle indices // A triangle consists of 3 indices // A line (4 vertices) consists of 2 triangles, so 6 triangle indices for (int j = 0, v = 0; j < edgeList.Count*3; j += 6, v += 4) { // First triangle tris.Add(v+0); tris.Add(v+1); tris.Add(v+2); // Second triangle tris.Add(v+1); tris.Add(v+3); tris.Add(v+2); } // Set all data on the mesh mesh.SetVertices(vertices); mesh.SetTriangles(tris, 0); mesh.SetColors(colors); mesh.SetNormals(normals); // Upload all data and mark the mesh as unreadable mesh.UploadMeshData(true); // Release the lists back to the pool ListPool<Color32>.Release(colorList); ListPool<Int3>.Release(edgeList); ListPool<Vector3>.Release(vertices); ListPool<Color32>.Release(colors); ListPool<Vector3>.Release(normals); ListPool<int>.Release(tris); return mesh; }
/** Exports the INavmesh graph to a .obj file */ public static void ExportToFile (RecastGraph target) { //INavmesh graph = (INavmesh)target; if (target == null) return; RecastGraph.NavmeshTile[] tiles = target.GetTiles(); if (tiles == null) { if (EditorUtility.DisplayDialog("Scan graph before exporting?", "The graph does not contain any mesh data. Do you want to scan it?", "Ok", "Cancel")) { AstarPathEditor.MenuScan(); tiles = target.GetTiles(); if (tiles == null) return; } else { return; } } string path = EditorUtility.SaveFilePanel("Export .obj", "", "navmesh.obj", "obj"); if (path == "") return; //Generate .obj var sb = new System.Text.StringBuilder(); string name = System.IO.Path.GetFileNameWithoutExtension(path); sb.Append("g ").Append(name).AppendLine(); //Vertices start from 1 int vCount = 1; //Define single texture coordinate to zero sb.Append("vt 0 0\n"); for (int t = 0; t < tiles.Length; t++) { RecastGraph.NavmeshTile tile = tiles[t]; if (tile == null) continue; Int3[] vertices = tile.verts; //Write vertices for (int i = 0; i < vertices.Length; i++) { var v = (Vector3)vertices[i]; sb.Append(string.Format("v {0} {1} {2}\n", -v.x, v.y, v.z)); } //Write triangles TriangleMeshNode[] nodes = tile.nodes; for (int i = 0; i < nodes.Length; i++) { TriangleMeshNode node = nodes[i]; if (node == null) { Debug.LogError("Node was null or no TriangleMeshNode. Critical error. Graph type " + target.GetType().Name); return; } if (node.GetVertexArrayIndex(0) < 0 || node.GetVertexArrayIndex(0) >= vertices.Length) throw new System.Exception("ERR"); sb.Append(string.Format("f {0}/1 {1}/1 {2}/1\n", (node.GetVertexArrayIndex(0) + vCount), (node.GetVertexArrayIndex(1) + vCount), (node.GetVertexArrayIndex(2) + vCount))); } vCount += vertices.Length; } string obj = sb.ToString(); using (var sw = new System.IO.StreamWriter(path)) { sw.Write(obj); } }
/** Creates a mesh of the surfaces of the navmesh for use in OnDrawGizmos in the editor */ Mesh CreateNavmeshSurfaceVisualization (RecastGraph.NavmeshTile tile) { var mesh = new Mesh(); mesh.hideFlags = HideFlags.DontSave; var vertices = ListPool<Vector3>.Claim(tile.verts.Length); var colors = ListPool<Color32>.Claim(tile.verts.Length); for (int j = 0; j < tile.verts.Length; j++) { vertices.Add((Vector3)tile.verts[j]); colors.Add(new Color32()); } // TODO: Uses AstarPath.active var debugPathData = AstarPath.active.debugPathData; for (int j = 0; j < tile.nodes.Length; j++) { var node = tile.nodes[j]; for (int v = 0; v < 3; v++) { var color = target.NodeColor(node, debugPathData); colors[node.GetVertexArrayIndex(v)] = (Color32)color;//(Color32)AstarColor.GetAreaColor(node.Area); } } mesh.SetVertices(vertices); mesh.SetTriangles(tile.tris, 0); mesh.SetColors(colors); // Upload all data and mark the mesh as unreadable mesh.UploadMeshData(true); // Return lists to the pool ListPool<Vector3>.Release(vertices); ListPool<Color32>.Release(colors); return mesh; }
public void RemoveConnectionsFromTo(RecastGraph.NavmeshTile a, RecastGraph.NavmeshTile b) { if (a == null || b == null) { return; } if (a == b) { return; } int num = b.x + b.z * this.tileXCount; for (int i = 0; i < a.nodes.Length; i++) { TriangleMeshNode triangleMeshNode = a.nodes[i]; if (triangleMeshNode.connections != null) { for (int j = 0; j < triangleMeshNode.connections.Length; j++) { TriangleMeshNode triangleMeshNode2 = triangleMeshNode.connections[j] as TriangleMeshNode; if (triangleMeshNode2 != null) { int num2 = triangleMeshNode2.GetVertexIndex(0); num2 = (num2 >> 12 & 524287); if (num2 == num) { triangleMeshNode.RemoveConnection(triangleMeshNode.connections[j]); j--; } } } } } }
public void RemoveConnectionsFromTile(RecastGraph.NavmeshTile tile) { if (tile.x > 0) { int num = tile.x - 1; for (int i = tile.z; i < tile.z + tile.d; i++) { this.RemoveConnectionsFromTo(this.tiles[num + i * this.tileXCount], tile); } } if (tile.x + tile.w < this.tileXCount) { int num2 = tile.x + tile.w; for (int j = tile.z; j < tile.z + tile.d; j++) { this.RemoveConnectionsFromTo(this.tiles[num2 + j * this.tileXCount], tile); } } if (tile.z > 0) { int num3 = tile.z - 1; for (int k = tile.x; k < tile.x + tile.w; k++) { this.RemoveConnectionsFromTo(this.tiles[k + num3 * this.tileXCount], tile); } } if (tile.z + tile.d < this.tileZCount) { int num4 = tile.z + tile.d; for (int l = tile.x; l < tile.x + tile.w; l++) { this.RemoveConnectionsFromTo(this.tiles[l + num4 * this.tileXCount], tile); } } }
public void ConnectTileWithNeighbours(RecastGraph.NavmeshTile tile) { if (tile.x > 0) { int num = tile.x - 1; for (int i = tile.z; i < tile.z + tile.d; i++) { this.ConnectTiles(this.tiles[num + i * this.tileXCount], tile); } } if (tile.x + tile.w < this.tileXCount) { int num2 = tile.x + tile.w; for (int j = tile.z; j < tile.z + tile.d; j++) { this.ConnectTiles(this.tiles[num2 + j * this.tileXCount], tile); } } if (tile.z > 0) { int num3 = tile.z - 1; for (int k = tile.x; k < tile.x + tile.w; k++) { this.ConnectTiles(this.tiles[k + num3 * this.tileXCount], tile); } } if (tile.z + tile.d < this.tileZCount) { int num4 = tile.z + tile.d; for (int l = tile.x; l < tile.x + tile.w; l++) { this.ConnectTiles(this.tiles[l + num4 * this.tileXCount], tile); } } }
private void ConnectTiles(RecastGraph.NavmeshTile tile1, RecastGraph.NavmeshTile tile2) { if (tile1 == null) { return; } if (tile2 == null) { return; } if (tile1.nodes == null) { throw new ArgumentException("tile1 does not contain any nodes"); } if (tile2.nodes == null) { throw new ArgumentException("tile2 does not contain any nodes"); } int num = Mathf.Clamp(tile2.x, tile1.x, tile1.x + tile1.w - 1); int num2 = Mathf.Clamp(tile1.x, tile2.x, tile2.x + tile2.w - 1); int num3 = Mathf.Clamp(tile2.z, tile1.z, tile1.z + tile1.d - 1); int num4 = Mathf.Clamp(tile1.z, tile2.z, tile2.z + tile2.d - 1); int num5; int i; int num6; int num7; float num8; if (num == num2) { num5 = 2; i = 0; num6 = num3; num7 = num4; num8 = (float)this.tileSizeZ * this.cellSize; } else { if (num3 != num4) { throw new ArgumentException("Tiles are not adjacent (neither x or z coordinates match)"); } num5 = 0; i = 2; num6 = num; num7 = num2; num8 = (float)this.tileSizeX * this.cellSize; } if (Math.Abs(num6 - num7) != 1) { UnityEngine.Debug.Log(string.Concat(new object[] { tile1.x, " ", tile1.z, " ", tile1.w, " ", tile1.d, "\n", tile2.x, " ", tile2.z, " ", tile2.w, " ", tile2.d, "\n", num, " ", num3, " ", num2, " ", num4 })); throw new ArgumentException(string.Concat(new object[] { "Tiles are not adjacent (tile coordinates must differ by exactly 1. Got '", num6, "' and '", num7, "')" })); } int num9 = (int)Math.Round((double)(((float)Math.Max(num6, num7) * num8 + this.forcedBounds.min[num5]) * 1000f)); TriangleMeshNode[] nodes = tile1.nodes; TriangleMeshNode[] nodes2 = tile2.nodes; for (int j = 0; j < nodes.Length; j++) { TriangleMeshNode triangleMeshNode = nodes[j]; int vertexCount = triangleMeshNode.GetVertexCount(); for (int k = 0; k < vertexCount; k++) { Int3 vertex = triangleMeshNode.GetVertex(k); Int3 vertex2 = triangleMeshNode.GetVertex((k + 1) % vertexCount); if (Math.Abs(vertex[num5] - num9) < 2 && Math.Abs(vertex2[num5] - num9) < 2) { int num10 = Math.Min(vertex[i], vertex2[i]); int num11 = Math.Max(vertex[i], vertex2[i]); if (num10 != num11) { for (int l = 0; l < nodes2.Length; l++) { TriangleMeshNode triangleMeshNode2 = nodes2[l]; int vertexCount2 = triangleMeshNode2.GetVertexCount(); for (int m = 0; m < vertexCount2; m++) { Int3 vertex3 = triangleMeshNode2.GetVertex(m); Int3 vertex4 = triangleMeshNode2.GetVertex((m + 1) % vertexCount); if (Math.Abs(vertex3[num5] - num9) < 2 && Math.Abs(vertex4[num5] - num9) < 2) { int num12 = Math.Min(vertex3[i], vertex4[i]); int num13 = Math.Max(vertex3[i], vertex4[i]); if (num12 != num13) { if (num11 > num12 && num10 < num13 && ((vertex == vertex3 && vertex2 == vertex4) || (vertex == vertex4 && vertex2 == vertex3) || Polygon.DistanceSegmentSegment3D((Vector3)vertex, (Vector3)vertex2, (Vector3)vertex3, (Vector3)vertex4) < this.walkableClimb * this.walkableClimb)) { uint costMagnitude = (uint)(triangleMeshNode.position - triangleMeshNode2.position).costMagnitude; triangleMeshNode.AddConnection(triangleMeshNode2, costMagnitude); triangleMeshNode2.AddConnection(triangleMeshNode, costMagnitude); } } } } } } } } } }