/** Creates a mesh of the surfaces of the navmesh for use in OnDrawGizmos in the editor */ public static void CreateNavmeshSurfaceVisualization(this NavmeshBase navmeshBase, NavmeshTile tile, GraphGizmoHelper helper) { // Vertex array might be a bit larger than necessary, but that's ok var vertices = PF.ArrayPool <Vector3> .Claim(tile.nodes.Length *3); var colors = PF.ArrayPool <Color> .Claim(tile.nodes.Length *3); for (int j = 0; j < tile.nodes.Length; j++) { var node = tile.nodes[j]; Int3 v0, v1, v2; node.GetVertices(out v0, out v1, out v2); vertices[j * 3 + 0] = (Vector3)v0; vertices[j * 3 + 1] = (Vector3)v1; vertices[j * 3 + 2] = (Vector3)v2; var color = helper.NodeColor(node); colors[j * 3 + 0] = colors[j * 3 + 1] = colors[j * 3 + 2] = color; } if (navmeshBase.showMeshSurface) { helper.DrawTriangles(vertices, colors, tile.nodes.Length); } if (navmeshBase.showMeshOutline) { helper.DrawWireTriangles(vertices, colors, tile.nodes.Length); } // Return lists to the pool PF.ArrayPool <Vector3> .Release(ref vertices); PF.ArrayPool <Color> .Release(ref colors); }
// Token: 0x06002696 RID: 9878 RVA: 0x001A8890 File Offset: 0x001A6A90 public void OnDrawGizmosSelected() { List <List <Vector3> > list = ListPool <List <Vector3> > .Claim(); this.GetContour(list); Color color = Color.Lerp(NavmeshCut.GizmoColor, Color.white, 0.5f); color.a *= 0.5f; Gizmos.color = color; NavmeshBase navmeshBase = (AstarPath.active != null) ? (AstarPath.active.data.recastGraph ?? AstarPath.active.data.navmesh) : null; GraphTransform graphTransform = (navmeshBase != null) ? navmeshBase.transform : GraphTransform.identityTransform; float y = this.GetY(graphTransform); float y2 = y - this.height * 0.5f; float y3 = y + this.height * 0.5f; for (int i = 0; i < list.Count; i++) { List <Vector3> list2 = list[i]; for (int j = 0; j < list2.Count; j++) { Vector3 vector = graphTransform.InverseTransform(list2[j]); Vector3 vector2 = graphTransform.InverseTransform(list2[(j + 1) % list2.Count]); Vector3 point = vector; Vector3 point2 = vector2; Vector3 point3 = vector; Vector3 point4 = vector2; point.y = (point2.y = y2); point3.y = (point4.y = y3); Gizmos.DrawLine(graphTransform.Transform(point), graphTransform.Transform(point2)); Gizmos.DrawLine(graphTransform.Transform(point3), graphTransform.Transform(point4)); Gizmos.DrawLine(graphTransform.Transform(point), graphTransform.Transform(point3)); } } ListPool <List <Vector3> > .Release(ref list); }
internal void Destroy() { if (this.nodes.Length > 0) { int tileIndex = NavmeshBase.GetTileIndex(this.nodes[0].GetVertexIndex(0)); uint graphIndex = this.nodes[0].GraphIndex; for (int i = 0; i < this.nodes.Length; i++) { TriangleMeshNode triangleMeshNode = this.nodes[i]; if (triangleMeshNode.connections != null) { for (int j = 0; j < triangleMeshNode.connections.Length; j++) { TriangleMeshNode triangleMeshNode2 = triangleMeshNode.connections[j].node as TriangleMeshNode; if (triangleMeshNode2 != null && triangleMeshNode2.GraphIndex == graphIndex && NavmeshBase.GetTileIndex(triangleMeshNode2.GetVertexIndex(0)) == tileIndex) { triangleMeshNode.connections[j].node = null; } } } } for (int k = 0; k < this.nodes.Length; k++) { this.nodes[k].Destroy(); } } this.nodes = null; ObjectPool <BBTree> .Release(ref this.bbTree); }
public RichFunnel() { left = Pathfinding.Util.ListPool <Vector3> .Claim(); right = Pathfinding.Util.ListPool <Vector3> .Claim(); nodes = new List <TriangleMeshNode>(); this.graph = null; }
public override void OnEnterPool() { left.Clear(); right.Clear(); nodes.Clear(); graph = null; currentNode = 0; checkForDestroyedNodesCounter = 0; }
/** Returns a rect containing the indices of all tiles touching the specified bounds. * \param rect Graph space rectangle (in graph space all tiles are on the XZ plane regardless of graph rotation and other transformations, the first tile has a corner at the origin) */ public static IntRect GetTouchingTilesInGraphSpace(this NavmeshBase self, Rect rect) { // Calculate world bounds of all affected tiles var r = new IntRect(Mathf.FloorToInt(rect.xMin / self.TileWorldSizeX), Mathf.FloorToInt(rect.yMin / self.TileWorldSizeZ), Mathf.FloorToInt(rect.xMax / self.TileWorldSizeX), Mathf.FloorToInt(rect.yMax / self.TileWorldSizeZ)); // Clamp to bounds r = IntRect.Intersection(r, new IntRect(0, 0, self.tileXCount - 1, self.tileZCount - 1)); return(r); }
public static void DrawUnwalkableNodes(this NavmeshBase navmeshBase, float size) { Gizmos.color = AstarColor.UnwalkableNode; navmeshBase.GetNodes(node => { if (!node.Walkable) { Gizmos.DrawCube((Vector3)node.position, Vector3.one * size); } }); }
/** Returns an XZ bounds object with the bounds of a group of tiles in graph space */ public static Bounds GetTileBoundsInGraphSpace(this NavmeshBase navmeshBase, int x, int z, int width = 1, int depth = 1) { var b = new Bounds(); b.SetMinMax( new Vector3(x * navmeshBase.TileWorldSizeX, 0, z * navmeshBase.TileWorldSizeZ), new Vector3((x + width) * navmeshBase.TileWorldSizeX, navmeshBase.forcedBoundsSize.y, (z + depth) * navmeshBase.TileWorldSizeZ) ); return(b); }
// Token: 0x060026A7 RID: 9895 RVA: 0x001A8E50 File Offset: 0x001A7050 private void OnDisable() { if (this.handler != null) { NavmeshClipper.RemoveEnableCallback(new Action <NavmeshClipper>(this.HandleOnEnableCallback), new Action <NavmeshClipper>(this.HandleOnDisableCallback)); this.forcedReloadRects.Clear(); NavmeshBase graph = this.handler.graph; graph.OnRecalculatedTiles = (Action <NavmeshTile[]>)Delegate.Remove(graph.OnRecalculatedTiles, new Action <NavmeshTile[]>(this.OnRecalculatedTiles)); } }
private NavmeshTile CreateTile(Voxelize vox, VoxelMesh mesh, int x, int z, int threadIndex) { if (mesh.tris == null) { throw new ArgumentNullException("mesh.tris"); } if (mesh.verts == null) { throw new ArgumentNullException("mesh.verts"); } if (mesh.tris.Length % 3 != 0) { throw new ArgumentException("Indices array's length must be a multiple of 3 (mesh.tris)"); } if (mesh.verts.Length >= 4095) { if (this.tileXCount * this.tileZCount == 1) { throw new ArgumentException("Too many vertices per tile (more than " + 4095 + ").\n<b>Try enabling tiling in the recast graph settings.</b>\n"); } throw new ArgumentException("Too many vertices per tile (more than " + 4095 + ").\n<b>Try reducing tile size or enabling ASTAR_RECAST_LARGER_TILES under the 'Optimizations' tab in the A* Inspector</b>"); } else { NavmeshTile navmeshTile = new NavmeshTile { x = x, z = z, w = 1, d = 1, tris = mesh.tris, bbTree = new BBTree() }; navmeshTile.vertsInGraphSpace = Utility.RemoveDuplicateVertices(mesh.verts, navmeshTile.tris); navmeshTile.verts = (Int3[])navmeshTile.vertsInGraphSpace.Clone(); this.transform.Transform(navmeshTile.verts); uint num = (uint)(this.active.data.graphs.Length + threadIndex); if (num > 255u) { throw new Exception("Graph limit reached. Multithreaded recast calculations cannot be done because a few scratch graph indices are required."); } int num2 = x + z * this.tileXCount; num2 <<= 12; TriangleMeshNode.SetNavmeshHolder((int)num, navmeshTile); object active = this.active; lock (active) { navmeshTile.nodes = base.CreateNodes(navmeshTile.tris, num2, num); } navmeshTile.bbTree.RebuildFrom(navmeshTile.nodes); NavmeshBase.CreateNodeConnections(navmeshTile.nodes); TriangleMeshNode.SetNavmeshHolder((int)num, null); return(navmeshTile); } }
/** Returns a rect containing the indices of all tiles touching the specified bounds */ public static IntRect GetTouchingTiles(this NavmeshBase self, Bounds bounds) { bounds = self.transform.InverseTransform(bounds); // Calculate world bounds of all affected tiles var r = new IntRect(Mathf.FloorToInt(bounds.min.x / self.TileWorldSizeX), Mathf.FloorToInt(bounds.min.z / self.TileWorldSizeZ), Mathf.FloorToInt(bounds.max.x / self.TileWorldSizeX), Mathf.FloorToInt(bounds.max.z / self.TileWorldSizeZ)); // Clamp to bounds r = IntRect.Intersection(r, new IntRect(0, 0, self.tileXCount - 1, self.tileZCount - 1)); return(r); }
private void FindGraph() { if (AstarPath.active != null) { NavmeshBase navmeshBase = AstarPath.active.data.recastGraph ?? AstarPath.active.data.navmesh; if (navmeshBase != null) { this.UseSpecifiedHandler(new TileHandler(navmeshBase)); this.handler.CreateTileTypesFromGraph(); } } }
// Token: 0x060026AA RID: 9898 RVA: 0x001A8F48 File Offset: 0x001A7148 private void FindGraph() { if (AstarPath.active != null) { NavmeshBase navmeshBase = AstarPath.active.data.FindGraphWhichInheritsFrom(typeof(NavmeshBase)) as NavmeshBase; if (navmeshBase != null) { this.UseSpecifiedHandler(new TileHandler(navmeshBase)); this.handler.CreateTileTypesFromGraph(); } } }
// Token: 0x060021A0 RID: 8608 RVA: 0x0018F05E File Offset: 0x0018D25E public RichFunnel Initialize(RichPath path, NavmeshBase graph) { if (graph == null) { throw new ArgumentNullException("graph"); } if (this.graph != null) { throw new InvalidOperationException("Trying to initialize an already initialized object. " + graph); } this.graph = graph; this.path = path; return(this); }
public TileHandler(NavmeshBase graph) { if (graph == null) { throw new ArgumentNullException("graph"); } if (graph.GetTiles() == null) { Debug.LogWarning("Creating a TileHandler for a graph with no tiles. Please scan the graph before creating a TileHandler"); } this.tileXCount = graph.tileXCount; this.tileZCount = graph.tileZCount; this.activeTileTypes = new TileHandler.TileType[this.tileXCount * this.tileZCount]; this.activeTileRotations = new int[this.activeTileTypes.Length]; this.activeTileOffsets = new int[this.activeTileTypes.Length]; this.reloadedInBatch = new bool[this.activeTileTypes.Length]; this.cuts = new GridLookup <NavmeshClipper>(new Int2(this.tileXCount, this.tileZCount)); this.graph = graph; }
// Token: 0x060026A5 RID: 9893 RVA: 0x001A8D18 File Offset: 0x001A6F18 public void UseSpecifiedHandler(TileHandler newHandler) { if (!base.enabled) { throw new InvalidOperationException("TileHandlerHelper is disabled"); } if (this.handler != null) { NavmeshClipper.RemoveEnableCallback(new Action <NavmeshClipper>(this.HandleOnEnableCallback), new Action <NavmeshClipper>(this.HandleOnDisableCallback)); NavmeshBase graph = this.handler.graph; graph.OnRecalculatedTiles = (Action <NavmeshTile[]>)Delegate.Remove(graph.OnRecalculatedTiles, new Action <NavmeshTile[]>(this.OnRecalculatedTiles)); } this.handler = newHandler; if (this.handler != null) { NavmeshClipper.AddEnableCallback(new Action <NavmeshClipper>(this.HandleOnEnableCallback), new Action <NavmeshClipper>(this.HandleOnDisableCallback)); NavmeshBase graph2 = this.handler.graph; graph2.OnRecalculatedTiles = (Action <NavmeshTile[]>)Delegate.Combine(graph2.OnRecalculatedTiles, new Action <NavmeshTile[]>(this.OnRecalculatedTiles)); } }
internal void Destroy() { if (nodes.Length > 0) { // Get this tile's index from the first node var tileIndex = NavmeshBase.GetTileIndex(nodes[0].GetVertexIndex(0)); var graphIndex = nodes[0].GraphIndex; // Destroy the nodes // To avoid removing connections one by one, which is very inefficient // we set all connections to other nodes in the same tile to null since // we already know that their connections will be destroyed as well. // This reduces the time it takes to destroy the nodes by approximately 50% for (int i = 0; i < nodes.Length; i++) { var node = nodes[i]; if (node.connections != null) { for (int j = 0; j < node.connections.Length; j++) { var otherMesh = node.connections[j].node as TriangleMeshNode; // Check if the nodes are in the same graph and the same tile if (otherMesh != null && otherMesh.GraphIndex == graphIndex && NavmeshBase.GetTileIndex(otherMesh.GetVertexIndex(0)) == tileIndex) { node.connections[j].node = null; } } } } // This will also remove old connections for (int i = 0; i < nodes.Length; i++) { nodes[i].Destroy(); } } nodes = null; graph = null; ObjectPool <BBTree> .Release(ref bbTree); }
/** Returns a bounds object with the bounding box of a group of tiles. * The bounding box is defined in world space. */ public static Bounds GetTileBounds(this NavmeshBase navmeshBase, int x, int z, int width = 1, int depth = 1) { return(navmeshBase.transform.Transform(navmeshBase.GetTileBoundsInGraphSpace(x, z, width, depth))); }
public NavmeshUpdateSettings(NavmeshBase graph) { this.graph = graph; }
// Token: 0x06002140 RID: 8512 RVA: 0x001890A0 File Offset: 0x001872A0 public void Initialize(Seeker seeker, Path path, bool mergePartEndpoints, bool simplificationMode) { if (path.error) { throw new ArgumentException("Path has an error"); } List <GraphNode> path2 = path.path; if (path2.Count == 0) { throw new ArgumentException("Path traverses no nodes"); } this.seeker = seeker; for (int i = 0; i < this.parts.Count; i++) { RichFunnel richFunnel = this.parts[i] as RichFunnel; RichSpecial richSpecial = this.parts[i] as RichSpecial; if (richFunnel != null) { ObjectPool <RichFunnel> .Release(ref richFunnel); } else if (richSpecial != null) { ObjectPool <RichSpecial> .Release(ref richSpecial); } } this.Clear(); this.Endpoint = path.vectorPath[path.vectorPath.Count - 1]; for (int j = 0; j < path2.Count; j++) { if (path2[j] is TriangleMeshNode) { NavmeshBase navmeshBase = AstarData.GetGraph(path2[j]) as NavmeshBase; if (navmeshBase == null) { throw new Exception("Found a TriangleMeshNode that was not in a NavmeshBase graph"); } RichFunnel richFunnel2 = ObjectPool <RichFunnel> .Claim().Initialize(this, navmeshBase); richFunnel2.funnelSimplification = simplificationMode; int num = j; uint graphIndex = path2[num].GraphIndex; while (j < path2.Count && (path2[j].GraphIndex == graphIndex || path2[j] is NodeLink3Node)) { j++; } j--; if (num == 0) { richFunnel2.exactStart = path.vectorPath[0]; } else { richFunnel2.exactStart = (Vector3)path2[mergePartEndpoints ? (num - 1) : num].position; } if (j == path2.Count - 1) { richFunnel2.exactEnd = path.vectorPath[path.vectorPath.Count - 1]; } else { richFunnel2.exactEnd = (Vector3)path2[mergePartEndpoints ? (j + 1) : j].position; } richFunnel2.BuildFunnelCorridor(path2, num, j); this.parts.Add(richFunnel2); } else if (NodeLink2.GetNodeLink(path2[j]) != null) { NodeLink2 nodeLink = NodeLink2.GetNodeLink(path2[j]); int num2 = j; uint graphIndex2 = path2[num2].GraphIndex; j++; while (j < path2.Count && path2[j].GraphIndex == graphIndex2) { j++; } j--; if (j - num2 > 1) { throw new Exception("NodeLink2 path length greater than two (2) nodes. " + (j - num2)); } if (j - num2 != 0) { RichSpecial item = ObjectPool <RichSpecial> .Claim().Initialize(nodeLink, path2[num2]); this.parts.Add(item); } } } }
public NavmeshUpdateSettings(NavmeshBase graph) { }
public static Bounds GetTileBoundsInGraphSpace(this NavmeshBase navmeshBase, IntRect rect) { return(navmeshBase.GetTileBoundsInGraphSpace(rect.xmin, rect.ymin, rect.Width, rect.Height)); }
public static void OnDrawGizmos(this NavmeshBase navmeshBase, Pathfinding.Util.RetainedGizmos gizmos, bool drawNodes) { if (!drawNodes) { return; } using (var helper = gizmos.GetSingleFrameGizmoHelper()) { var bounds = new Bounds(); bounds.SetMinMax(Vector3.zero, navmeshBase.forcedBoundsSize); // Draw a write cube using the latest transform // (this makes the bounds update immediately if some field is changed in the editor) helper.builder.DrawWireCube(navmeshBase.CalculateTransform(), bounds, Color.white); } if (navmeshBase.tiles != null) { // Update navmesh vizualizations for // the tiles that have been changed for (int i = 0; i < navmeshBase.tiles.Length; i++) { // This may happen if an exception has been thrown when the graph was scanned. // We don't want the gizmo code to start to throw exceptions as well then as // that would obscure the actual source of the error. if (navmeshBase.tiles[i] == null) { continue; } // Calculate a hash of the tile var hasher = new RetainedGizmos.Hasher(AstarPath.active); hasher.AddHash(navmeshBase.showMeshOutline ? 1 : 0); hasher.AddHash(navmeshBase.showMeshSurface ? 1 : 0); hasher.AddHash(navmeshBase.showNodeConnections ? 1 : 0); var nodes = navmeshBase.tiles[i].nodes; for (int j = 0; j < nodes.Length; j++) { hasher.HashNode(nodes[j]); } if (!gizmos.Draw(hasher)) { using (var helper = gizmos.GetGizmoHelper(hasher)) { if (navmeshBase.showMeshSurface || navmeshBase.showMeshOutline) { navmeshBase.CreateNavmeshSurfaceVisualization(navmeshBase.tiles[i], helper); } if (navmeshBase.showMeshSurface || navmeshBase.showMeshOutline) { CreateNavmeshOutlineVisualization(navmeshBase.tiles[i], helper); } if (navmeshBase.showNodeConnections) { for (int j = 0; j < nodes.Length; j++) { helper.DrawConnections(nodes[j]); } } } } gizmos.Draw(hasher); } } if (AstarPath.active.showUnwalkableNodes) { navmeshBase.DrawUnwalkableNodes(AstarPath.active.unwalkableNodeDebugSize); } }
/** Create a tile at tile index \a x, \a z from the mesh. * \version Since version 3.7.6 the implementation is thread safe */ public static NavmeshTile CreateTile(this RecastGraph self, Voxelize vox, VoxelMesh mesh, int x, int z, int threadIndex) { if (mesh.tris == null) { throw new System.ArgumentNullException("mesh.tris"); } if (mesh.verts == null) { throw new System.ArgumentNullException("mesh.verts"); } if (mesh.tris.Length % 3 != 0) { throw new System.ArgumentException("Indices array's length must be a multiple of 3 (mesh.tris)"); } if (mesh.verts.Length >= NavmeshBase.VertexIndexMask) { if (self.tileXCount * self.tileZCount == 1) { throw new System.ArgumentException("Too many vertices per tile (more than " + NavmeshBase.VertexIndexMask + ")." + "\n<b>Try enabling tiling in the recast graph settings.</b>\n"); } else { throw new System.ArgumentException("Too many vertices per tile (more than " + NavmeshBase.VertexIndexMask + ")." + "\n<b>Try reducing tile size or enabling ASTAR_RECAST_LARGER_TILES under the 'Optimizations' tab in the A* Inspector</b>"); } } // Create a new navmesh tile and assign its settings var tile = new NavmeshTile { x = x, z = z, w = 1, d = 1, tris = mesh.tris, bbTree = new BBTree(), graph = self, }; tile.vertsInGraphSpace = Utility.RemoveDuplicateVertices(mesh.verts, tile.tris); tile.verts = (Int3[])tile.vertsInGraphSpace.Clone(); self.transform.Transform(tile.verts); // Here we are faking a new graph // The tile is not added to any graphs yet, but to get the position queries from the nodes // to work correctly (not throw exceptions because the tile is not calculated) we fake a new graph // and direct the position queries directly to the tile // The thread index is added to make sure that if multiple threads are calculating tiles at the same time // they will not use the same temporary graph index uint temporaryGraphIndex = (uint)(AstarPath.active.data.graphs.Length + threadIndex); if (temporaryGraphIndex > GraphNode.MaxGraphIndex) { // Multithreaded tile calculations use fake graph indices, see above. throw new System.Exception("Graph limit reached. Multithreaded recast calculations cannot be done because a few scratch graph indices are required."); } TriangleMeshNode.SetNavmeshHolder((int)temporaryGraphIndex, tile); // We need to lock here because creating nodes is not thread safe // and we may be doing this from multiple threads at the same time tile.nodes = new TriangleMeshNode[tile.tris.Length / 3]; lock (AstarPath.active) { self.CreateNodes(tile.nodes, tile.tris, x + z * self.tileXCount, temporaryGraphIndex); } tile.bbTree.RebuildFrom(tile.nodes); NavmeshBase.CreateNodeConnections(tile.nodes); // Remove the fake graph TriangleMeshNode.SetNavmeshHolder((int)temporaryGraphIndex, null); return(tile); }