public void UpdateAreaInit (GraphUpdateObject o) { if (!o.updatePhysics) { return; } if (!dynamic) { throw new System.Exception ("Recast graph must be marked as dynamic to enable graph updates"); } AstarProfiler.Reset (); AstarProfiler.StartProfile ("UpdateAreaInit"); AstarProfiler.StartProfile ("CollectMeshes"); RelevantGraphSurface.UpdateAllPositions (); List<ExtraMesh> extraMeshes; Bounds b = o.bounds; b.center -= forcedBounds.min; //Calculate world bounds of all affected tiles IntRect r = new IntRect (Mathf.FloorToInt (b.min.x / (tileSizeX*cellSize)), Mathf.FloorToInt (b.min.z / (tileSizeZ*cellSize)), Mathf.FloorToInt (b.max.x / (tileSizeX*cellSize)), Mathf.FloorToInt (b.max.z / (tileSizeZ*cellSize))); //Clamp to bounds r = IntRect.Intersection (r, new IntRect (0,0,tileXCount-1,tileZCount-1)); Bounds tileBounds = new Bounds(); Vector3 forcedBoundsMin = forcedBounds.min; Vector3 forcedBoundsMax = forcedBounds.max; float tcsx = tileSizeX*cellSize; float tcsz = tileSizeZ*cellSize; tileBounds.SetMinMax(new Vector3 (r.xmin*tcsx, 0, r.ymin*tcsz) + forcedBoundsMin, new Vector3 ((r.xmax+1)*tcsx + forcedBoundsMin.x, forcedBoundsMax.y, (r.ymax+1)*tcsz + forcedBoundsMin.z) ); int voxelCharacterRadius = Mathf.CeilToInt (characterRadius/cellSize); int borderSize = voxelCharacterRadius + 3; //Expand borderSize voxels on each side tileBounds.Expand (new Vector3 (borderSize,0,borderSize)*cellSize*2); Debug.DrawLine (tileBounds.min, tileBounds.max); //Debug.Break (); if (!CollectMeshes (out extraMeshes, tileBounds)) { //return; } Voxelize vox = globalVox; if (vox == null) { //Create the voxelizer and set all settings vox = new Voxelize (cellHeight, cellSize, walkableClimb, walkableHeight, maxSlope); vox.maxEdgeLength = maxEdgeLength; if (dynamic) globalVox = vox; } vox.inputExtraMeshes = extraMeshes; AstarProfiler.EndProfile ("CollectMeshes"); AstarProfiler.EndProfile ("UpdateAreaInit"); }
protected void BuildTileMesh (Voxelize vox, int x, int z) { AstarProfiler.StartProfile ("Build Tile"); AstarProfiler.StartProfile ("Init"); //World size of tile float tcsx = tileSizeX*cellSize; float tcsz = tileSizeZ*cellSize; int voxelCharacterRadius = Mathf.CeilToInt (characterRadius/cellSize); Vector3 forcedBoundsMin = forcedBounds.min; Vector3 forcedBoundsMax = forcedBounds.max; Bounds bounds = new Bounds (); bounds.SetMinMax(new Vector3 (x*tcsx, 0, z*tcsz) + forcedBoundsMin, new Vector3 ((x+1)*tcsx + forcedBoundsMin.x, forcedBoundsMax.y, (z+1)*tcsz + forcedBoundsMin.z) ); vox.borderSize = voxelCharacterRadius + 3; //Expand borderSize voxels on each side bounds.Expand (new Vector3 (vox.borderSize,0,vox.borderSize)*cellSize*2); vox.forcedBounds = bounds; vox.width = tileSizeX + vox.borderSize*2; vox.depth = tileSizeZ + vox.borderSize*2; if (!useTiles && relevantGraphSurfaceMode == RelevantGraphSurfaceMode.OnlyForCompletelyInsideTile) { // This best reflects what the user would actually want vox.relevantGraphSurfaceMode = RelevantGraphSurfaceMode.RequireForAll; } else { vox.relevantGraphSurfaceMode = relevantGraphSurfaceMode; } vox.minRegionSize = Mathf.RoundToInt(minRegionSize / (cellSize*cellSize)); #if ASTARDEBUG Debug.Log ("Building Tile " + x+","+z); Console.WriteLine ("Recast Graph -- Voxelizing"); #endif AstarProfiler.EndProfile ("Init"); //Init voxelizer vox.Init (); vox.CollectMeshes (); vox.VoxelizeInput (); AstarProfiler.StartProfile ("Filter Ledges"); if (importMode) { if (System.IO.File.Exists(Application.dataPath+"/tile."+x+"."+z)) { System.IO.FileStream fs = System.IO.File.OpenRead (Application.dataPath+"/tile."+x+"."+z); byte[] bytes = new byte[fs.Length]; fs.Read (bytes,0,(int)fs.Length); VoxelArea tmpVox = new VoxelArea(vox.width,vox.depth); Pathfinding.Voxels.VoxelSerializeUtility.DeserializeVoxelAreaData (bytes,tmpVox); Pathfinding.Voxels.VoxelSerializeUtility.MergeVoxelAreaData(tmpVox,vox.voxelArea,vox.voxelWalkableClimb); } } if (exportMode) { System.IO.FileStream fs = System.IO.File.Create(Application.dataPath+"/tile."+x+"."+z); byte[] bytes = Pathfinding.Voxels.VoxelSerializeUtility.SerializeVoxelAreaData(vox.voxelArea); fs.Write(bytes,0,bytes.Length); fs.Close(); } vox.FilterLedges (vox.voxelWalkableHeight, vox.voxelWalkableClimb, vox.cellSize, vox.cellHeight, vox.forcedBounds.min); AstarProfiler.EndProfile ("Filter Ledges"); AstarProfiler.StartProfile ("Filter Low Height Spans"); vox.FilterLowHeightSpans (vox.voxelWalkableHeight, vox.cellSize, vox.cellHeight, vox.forcedBounds.min); AstarProfiler.EndProfile ("Filter Low Height Spans"); vox.BuildCompactField (); vox.BuildVoxelConnections (); #if ASTARDEBUG Console.WriteLine ("Recast Graph -- Eroding"); #endif vox.ErodeWalkableArea (voxelCharacterRadius); #if ASTARDEBUG Console.WriteLine ("Recast Graph -- Building Distance Field"); #endif vox.BuildDistanceField (); #if ASTARDEBUG Console.WriteLine ("Recast Graph -- Building Regions"); #endif vox.BuildRegions (); #if ASTARDEBUG Console.WriteLine ("Recast Graph -- Building Contours"); #endif VoxelContourSet cset = new VoxelContourSet (); vox.BuildContours (contourMaxError,1,cset,Voxelize.RC_CONTOUR_TESS_WALL_EDGES); #if ASTARDEBUG Console.WriteLine ("Recast Graph -- Building Poly Mesh"); #endif VoxelMesh mesh; vox.BuildPolyMesh (cset,3,out mesh); #if ASTARDEBUG Console.WriteLine ("Recast Graph -- Building Nodes"); #endif //Vector3[] vertices = new Vector3[mesh.verts.Length]; AstarProfiler.StartProfile ("Build Nodes"); //matrix = Matrix4x4.TRS (vox.voxelOffset,Quaternion.identity,Int3.Precision*vox.cellScale); //Position the vertices correctly in the world for (int i=0;i<mesh.verts.Length;i++) { //Note the multiplication is Scalar multiplication of vectors mesh.verts[i] = ((mesh.verts[i]*Int3.Precision) * vox.cellScale) + (Int3)vox.voxelOffset; //Debug.DrawRay (matrix.MultiplyPoint3x4(vertices[i]),Vector3.up,Color.red); } #if ASTARDEBUG Console.WriteLine ("Recast Graph -- Generating Nodes"); #endif //NavMeshGraph.GenerateNodes (this,vertices,mesh.tris, out _vectorVertices, out _vertices); /*NavmeshTile prevTile = tiles[x + z*tileXCount]; if (prevTile != null) { for (int i=0;i<prevTile.nodes.Length;i++) { prevTile.nodes[i].v0 = -1; prevTile.nodes[i].v1 = -1; prevTile.nodes[i].v2 = -1; } }*/ NavmeshTile tile = CreateTile (vox, mesh, x,z); tiles[tile.x + tile.z*tileXCount] = tile; AstarProfiler.EndProfile ("Build Nodes"); #if ASTARDEBUG Console.WriteLine ("Recast Graph -- Done"); #endif AstarProfiler.EndProfile ("Build Tile"); }
/** Create a tile at tile index \a x , \a z from the mesh. * \warning This implementation is not thread safe. It uses cached variables to improve performance */ NavmeshTile CreateTile (Voxelize vox, VoxelMesh mesh, int x, int z) { if (mesh.tris == null) throw new System.ArgumentNullException ("The mesh must be valid. tris is null."); if (mesh.verts == null) throw new System.ArgumentNullException ("The mesh must be valid. verts is null."); //Create a new navmesh tile and assign its settings NavmeshTile tile = new NavmeshTile(); tile.x = x; tile.z = z; tile.w = 1; tile.d = 1; tile.tris = mesh.tris; tile.verts = mesh.verts; tile.bbTree = new BBTree(tile); if (tile.tris.Length % 3 != 0) throw new System.ArgumentException ("Indices array's length must be a multiple of 3 (mesh.tris)"); if (tile.verts.Length >= VertexIndexMask) throw new System.ArgumentException ("Too many vertices per tile (more than "+VertexIndexMask+")." + "\nTry enabling ASTAR_RECAST_LARGER_TILES under the 'Optimizations' tab in the A* Inspector"); //Dictionary<Int3, int> firstVerts = new Dictionary<Int3, int> (); Dictionary<Int3, int> firstVerts = cachedInt3_int_dict; firstVerts.Clear(); int[] compressedPointers = new int[tile.verts.Length]; int count = 0; for (int i=0;i<tile.verts.Length;i++) { try { firstVerts.Add (tile.verts[i], count); compressedPointers[i] = count; tile.verts[count] = tile.verts[i]; count++; } catch { //There are some cases, rare but still there, that vertices are identical compressedPointers[i] = firstVerts[tile.verts[i]]; } } for (int i=0;i<tile.tris.Length;i++) { tile.tris[i] = compressedPointers[tile.tris[i]]; } Int3[] compressed = new Int3[count]; for (int i=0;i<count;i++) compressed[i] = tile.verts[i]; tile.verts = compressed; TriangleMeshNode[] nodes = new TriangleMeshNode[tile.tris.Length/3]; tile.nodes = nodes; //Here we are faking a new graph //The tile is not added to any graphs yet, but to get the position querys 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 int graphIndex = AstarPath.active.astarData.graphs.Length; TriangleMeshNode.SetNavmeshHolder (graphIndex, tile); //This index will be ORed to the triangle indices int tileIndex = x + z*tileXCount; tileIndex <<= TileIndexOffset; //Create nodes and assign triangle indices for (int i=0;i<nodes.Length;i++) { TriangleMeshNode node = new TriangleMeshNode(active); nodes[i] = node; node.GraphIndex = (uint)graphIndex; node.v0 = tile.tris[i*3+0] | tileIndex; node.v1 = tile.tris[i*3+1] | tileIndex; node.v2 = tile.tris[i*3+2] | tileIndex; //Degenerate triangles might ocurr, but they will not cause any large troubles anymore //if (Polygon.IsColinear (node.GetVertex(0), node.GetVertex(1), node.GetVertex(2))) { // Debug.Log ("COLINEAR!!!!!!"); //} //Make sure the triangle is clockwise if (!Polygon.IsClockwise (node.GetVertex(0), node.GetVertex(1), node.GetVertex(2))) { int tmp = node.v0; node.v0 = node.v2; node.v2 = tmp; } node.Walkable = true; node.Penalty = initialPenalty; node.UpdatePositionFromVertices(); tile.bbTree.Insert (node); } CreateNodeConnections (tile.nodes); //Remove the fake graph TriangleMeshNode.SetNavmeshHolder (graphIndex, null); return tile; }
public void UpdateAreaInit (GraphUpdateObject o) { if (!o.updatePhysics) { return; } if (!dynamic) { throw new System.Exception ("Recast graph must be marked as dynamic to enable graph updates"); } AstarProfiler.Reset (); AstarProfiler.StartProfile ("UpdateAreaInit"); AstarProfiler.StartProfile ("CollectMeshes"); RelevantGraphSurface.UpdateAllPositions (); //Calculate world bounds of all affected tiles IntRect touchingTiles = GetTouchingTiles ( o.bounds ); Bounds tileBounds = GetTileBounds (touchingTiles); int voxelCharacterRadius = Mathf.CeilToInt (characterRadius/cellSize); int borderSize = voxelCharacterRadius + 3; //Expand borderSize voxels on each side tileBounds.Expand (new Vector3 (borderSize,0,borderSize)*cellSize*2); List<ExtraMesh> extraMeshes; CollectMeshes (out extraMeshes, tileBounds); Voxelize vox = globalVox; if (vox == null) { //Create the voxelizer and set all settings vox = new Voxelize (cellHeight, cellSize, walkableClimb, walkableHeight, maxSlope); vox.maxEdgeLength = maxEdgeLength; if (dynamic) globalVox = vox; } vox.inputExtraMeshes = extraMeshes; AstarProfiler.EndProfile ("CollectMeshes"); AstarProfiler.EndProfile ("UpdateAreaInit"); }
protected void ScanAllTiles (OnScanStatus statusCallback) { #if ASTARDEBUG Console.WriteLine ("Recast Graph -- Collecting Meshes"); #endif AstarProfiler.StartProfile ("Finding Meshes"); List<ExtraMesh> extraMeshes; System.Console.WriteLine ("Collecting Meshes"); CollectMeshes (out extraMeshes, forcedBounds); AstarProfiler.EndProfile ("Finding Meshes"); //---- //Voxel grid size int gw = (int)(forcedBounds.size.x/cellSize + 0.5f); int gd = (int)(forcedBounds.size.z/cellSize + 0.5f); if (!useTiles) { tileSizeX = gw; tileSizeZ = gd; } else { tileSizeX = editorTileSize; tileSizeZ = editorTileSize; } //Number of tiles int tw = (gw + tileSizeX-1) / tileSizeX; int td = (gd + tileSizeZ-1) / tileSizeZ; tileXCount = tw; tileZCount = td; if (tileXCount * tileZCount > TileIndexMask+1) { throw new System.Exception ("Too many tiles ("+(tileXCount * tileZCount)+") maximum is "+(TileIndexMask+1)+ "\nTry disabling ASTAR_RECAST_LARGER_TILES under the 'Optimizations' tab in the A* inspector."); } tiles = new NavmeshTile[tileXCount*tileZCount]; #if ASTARDEBUG Console.WriteLine ("Recast Graph -- Creating Voxel Base"); #endif //Create the voxelizer and set all settings Voxelize vox = new Voxelize (cellHeight, cellSize, walkableClimb, walkableHeight, maxSlope); vox.inputExtraMeshes = extraMeshes; vox.maxEdgeLength = maxEdgeLength; int lastUp = -1; System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch(); watch.Start(); //Generate all tiles for (int z=0;z<td;z++) { for (int x=0;x<tw;x++) { int tileNum = z*tileXCount + x; System.Console.WriteLine ("Generating Tile #"+(tileNum) + " of " + td*tw); //Call statusCallback only 10 times since it is very slow in the editor if ((tileNum*10/tiles.Length > lastUp || watch.ElapsedMilliseconds > 2000) && statusCallback != null) { lastUp = tileNum*10/tiles.Length; watch.Reset(); watch.Start(); statusCallback (new Progress (AstarMath.MapToRange (0.1f, 0.9f, tileNum/(float)tiles.Length), "Building Tile " + tileNum + "/" + tiles.Length)); } BuildTileMesh (vox, x,z); } } System.Console.WriteLine ("Assigning Graph Indices"); if (statusCallback != null) statusCallback (new Progress (0.9f, "Connecting tiles")); //Assign graph index to nodes uint graphIndex = (uint)AstarPath.active.astarData.GetGraphIndex (this); GraphNodeDelegateCancelable del = delegate (GraphNode n) { n.GraphIndex = graphIndex; return true; }; GetNodes (del); for (int z=0;z<td;z++) { for (int x=0;x<tw;x++) { System.Console.WriteLine ("Connecing Tile #"+(z*tileXCount + x) + " of " + td*tw); if (x < tw-1) ConnectTiles (tiles[x + z*tileXCount], tiles[x+1 + z*tileXCount]); if (z < td-1) ConnectTiles (tiles[x + z*tileXCount], tiles[x + (z+1)*tileXCount]); } } AstarProfiler.PrintResults (); }
private RecastGraph.NavmeshTile CreateTile(Voxelize vox, VoxelMesh mesh, int x, int z) { if (mesh.tris == null) { throw new ArgumentNullException("mesh.tris"); } if (mesh.verts == null) { throw new ArgumentNullException("mesh.verts"); } RecastGraph.NavmeshTile navmeshTile = new RecastGraph.NavmeshTile(); navmeshTile.x = x; navmeshTile.z = z; navmeshTile.w = 1; navmeshTile.d = 1; navmeshTile.tris = mesh.tris; navmeshTile.verts = mesh.verts; navmeshTile.bbTree = new BBTree(); if (navmeshTile.tris.Length % 3 != 0) { throw new ArgumentException("Indices array's length must be a multiple of 3 (mesh.tris)"); } if (navmeshTile.verts.Length >= 4095) { throw new ArgumentException("Too many vertices per tile (more than " + 4095 + ").\nTry enabling ASTAR_RECAST_LARGER_TILES under the 'Optimizations' tab in the A* Inspector"); } Dictionary<Int3, int> dictionary = this.cachedInt3_int_dict; dictionary.Clear(); int[] array = new int[navmeshTile.verts.Length]; int num = 0; for (int i = 0; i < navmeshTile.verts.Length; i++) { if (!dictionary.ContainsKey(navmeshTile.verts[i])) { dictionary.Add(navmeshTile.verts[i], num); array[i] = num; navmeshTile.verts[num] = navmeshTile.verts[i]; num++; } else { array[i] = dictionary[navmeshTile.verts[i]]; } } for (int j = 0; j < navmeshTile.tris.Length; j++) { navmeshTile.tris[j] = array[navmeshTile.tris[j]]; } Int3[] array2 = new Int3[num]; for (int k = 0; k < num; k++) { array2[k] = navmeshTile.verts[k]; } navmeshTile.verts = array2; TriangleMeshNode[] array3 = new TriangleMeshNode[navmeshTile.tris.Length / 3]; navmeshTile.nodes = array3; int graphIndex = AstarPath.active.astarData.graphs.Length; TriangleMeshNode.SetNavmeshHolder(graphIndex, navmeshTile); int num2 = x + z * this.tileXCount; num2 <<= 12; for (int l = 0; l < array3.Length; l++) { TriangleMeshNode triangleMeshNode = new TriangleMeshNode(this.active); array3[l] = triangleMeshNode; triangleMeshNode.GraphIndex = (uint)graphIndex; triangleMeshNode.v0 = (navmeshTile.tris[l * 3] | num2); triangleMeshNode.v1 = (navmeshTile.tris[l * 3 + 1] | num2); triangleMeshNode.v2 = (navmeshTile.tris[l * 3 + 2] | num2); if (!Polygon.IsClockwise(triangleMeshNode.GetVertex(0), triangleMeshNode.GetVertex(1), triangleMeshNode.GetVertex(2))) { int v = triangleMeshNode.v0; triangleMeshNode.v0 = triangleMeshNode.v2; triangleMeshNode.v2 = v; } triangleMeshNode.Walkable = true; triangleMeshNode.Penalty = this.initialPenalty; triangleMeshNode.UpdatePositionFromVertices(); } navmeshTile.bbTree.RebuildFrom(array3); this.CreateNodeConnections(navmeshTile.nodes); TriangleMeshNode.SetNavmeshHolder(graphIndex, null); return navmeshTile; }
protected void ScanAllTiles (OnScanStatus statusCallback) { #if ASTARDEBUG System.Console.WriteLine ("Recast Graph -- Collecting Meshes"); #endif //---- //Voxel grid size int gw = (int)(forcedBounds.size.x/cellSize + 0.5f); int gd = (int)(forcedBounds.size.z/cellSize + 0.5f); if (!useTiles) { tileSizeX = gw; tileSizeZ = gd; } else { tileSizeX = editorTileSize; tileSizeZ = editorTileSize; } //Number of tiles int tw = (gw + tileSizeX-1) / tileSizeX; int td = (gd + tileSizeZ-1) / tileSizeZ; tileXCount = tw; tileZCount = td; if (tileXCount * tileZCount > TileIndexMask+1) { throw new System.Exception ("Too many tiles ("+(tileXCount * tileZCount)+") maximum is "+(TileIndexMask+1)+ "\nTry disabling ASTAR_RECAST_LARGER_TILES under the 'Optimizations' tab in the A* inspector."); } tiles = new NavmeshTile[tileXCount*tileZCount]; #if ASTARDEBUG System.Console.WriteLine ("Recast Graph -- Creating Voxel Base"); #endif // If this is true, just fill the graph with empty tiles if ( scanEmptyGraph ) { for (int z=0;z<td;z++) { for (int x=0;x<tw;x++) { tiles[z*tileXCount + x] = NewEmptyTile(x,z); } } return; } AstarProfiler.StartProfile ("Finding Meshes"); List<ExtraMesh> extraMeshes; #if !NETFX_CORE || UNITY_EDITOR System.Console.WriteLine ("Collecting Meshes"); #endif CollectMeshes (out extraMeshes, forcedBounds); AstarProfiler.EndProfile ("Finding Meshes"); // A walkableClimb higher than walkableHeight can cause issues when generating the navmesh since then it can in some cases // Both be valid for a character to walk under an obstacle and climb up on top of it (and that cannot be handled with navmesh without links) // The editor scripts also enforce this but we enforce it here too just to be sure walkableClimb = Mathf.Min (walkableClimb, walkableHeight); //Create the voxelizer and set all settings var vox = new Voxelize (cellHeight, cellSize, walkableClimb, walkableHeight, maxSlope); vox.inputExtraMeshes = extraMeshes; vox.maxEdgeLength = maxEdgeLength; int lastInfoCallback = -1; var watch = System.Diagnostics.Stopwatch.StartNew(); //Generate all tiles for (int z=0;z<td;z++) { for (int x=0;x<tw;x++) { int tileNum = z*tileXCount + x; #if !NETFX_CORE || UNITY_EDITOR System.Console.WriteLine ("Generating Tile #"+(tileNum) + " of " + td*tw); #endif //Call statusCallback only 10 times since it is very slow in the editor if (statusCallback != null && (tileNum*10/tiles.Length > lastInfoCallback || watch.ElapsedMilliseconds > 2000)) { lastInfoCallback = tileNum*10/tiles.Length; watch.Reset(); watch.Start(); statusCallback (new Progress (AstarMath.MapToRange (0.1f, 0.9f, tileNum/(float)tiles.Length), "Building Tile " + tileNum + "/" + tiles.Length)); } BuildTileMesh (vox, x,z); } } #if !NETFX_CORE System.Console.WriteLine ("Assigning Graph Indices"); #endif if (statusCallback != null) statusCallback (new Progress (0.9f, "Connecting tiles")); //Assign graph index to nodes uint graphIndex = (uint)AstarPath.active.astarData.GetGraphIndex (this); GraphNodeDelegateCancelable del = delegate (GraphNode n) { n.GraphIndex = graphIndex; return true; }; GetNodes (del); for (int z=0;z<td;z++) { for (int x=0;x<tw;x++) { #if !NETFX_CORE System.Console.WriteLine ("Connecing Tile #"+(z*tileXCount + x) + " of " + td*tw); #endif if (x < tw-1) ConnectTiles (tiles[x + z*tileXCount], tiles[x+1 + z*tileXCount]); if (z < td-1) ConnectTiles (tiles[x + z*tileXCount], tiles[x + (z+1)*tileXCount]); } } AstarProfiler.PrintResults (); }
protected void ScanAllTiles(OnScanStatus statusCallback) { int num = (int)(this.forcedBounds.size.x / this.cellSize + 0.5f); int num2 = (int)(this.forcedBounds.size.z / this.cellSize + 0.5f); if (!this.useTiles) { this.tileSizeX = num; this.tileSizeZ = num2; } else { this.tileSizeX = this.editorTileSize; this.tileSizeZ = this.editorTileSize; } int num3 = (num + this.tileSizeX - 1) / this.tileSizeX; int num4 = (num2 + this.tileSizeZ - 1) / this.tileSizeZ; this.tileXCount = num3; this.tileZCount = num4; if (this.tileXCount * this.tileZCount > 524288) { throw new Exception(string.Concat(new object[] { "Too many tiles (", this.tileXCount * this.tileZCount, ") maximum is ", 524288, "\nTry disabling ASTAR_RECAST_LARGER_TILES under the 'Optimizations' tab in the A* inspector." })); } this.tiles = new RecastGraph.NavmeshTile[this.tileXCount * this.tileZCount]; if (this.scanEmptyGraph) { for (int i = 0; i < num4; i++) { for (int j = 0; j < num3; j++) { this.tiles[i * this.tileXCount + j] = RecastGraph.NewEmptyTile(j, i); } } return; } Console.WriteLine("Collecting Meshes"); List<ExtraMesh> inputExtraMeshes; this.CollectMeshes(out inputExtraMeshes, this.forcedBounds); this.walkableClimb = Mathf.Min(this.walkableClimb, this.walkableHeight); Voxelize voxelize = new Voxelize(this.cellHeight, this.cellSize, this.walkableClimb, this.walkableHeight, this.maxSlope); voxelize.inputExtraMeshes = inputExtraMeshes; voxelize.maxEdgeLength = this.maxEdgeLength; int num5 = -1; Stopwatch stopwatch = Stopwatch.StartNew(); for (int k = 0; k < num4; k++) { for (int l = 0; l < num3; l++) { int num6 = k * this.tileXCount + l; Console.WriteLine(string.Concat(new object[] { "Generating Tile #", num6, " of ", num4 * num3 })); if (statusCallback != null && (num6 * 10 / this.tiles.Length > num5 || stopwatch.ElapsedMilliseconds > 2000L)) { num5 = num6 * 10 / this.tiles.Length; stopwatch.Reset(); stopwatch.Start(); statusCallback(new Progress(AstarMath.MapToRange(0.1f, 0.9f, (float)num6 / (float)this.tiles.Length), string.Concat(new object[] { "Building Tile ", num6, "/", this.tiles.Length }))); } this.BuildTileMesh(voxelize, l, k); } } Console.WriteLine("Assigning Graph Indices"); if (statusCallback != null) { statusCallback(new Progress(0.9f, "Connecting tiles")); } uint graphIndex = (uint)AstarPath.active.astarData.GetGraphIndex(this); GraphNodeDelegateCancelable del = delegate(GraphNode n) { n.GraphIndex = graphIndex; return true; }; this.GetNodes(del); for (int m = 0; m < num4; m++) { for (int n2 = 0; n2 < num3; n2++) { Console.WriteLine(string.Concat(new object[] { "Connecing Tile #", m * this.tileXCount + n2, " of ", num4 * num3 })); if (n2 < num3 - 1) { this.ConnectTiles(this.tiles[n2 + m * this.tileXCount], this.tiles[n2 + 1 + m * this.tileXCount]); } if (m < num4 - 1) { this.ConnectTiles(this.tiles[n2 + m * this.tileXCount], this.tiles[n2 + (m + 1) * this.tileXCount]); } } } }
protected void BuildTileMesh(Voxelize vox, int x, int z) { float num = (float)this.tileSizeX * this.cellSize; float num2 = (float)this.tileSizeZ * this.cellSize; int num3 = Mathf.CeilToInt(this.characterRadius / this.cellSize); Vector3 min = this.forcedBounds.min; Vector3 max = this.forcedBounds.max; Bounds forcedBounds = default(Bounds); forcedBounds.SetMinMax(new Vector3((float)x * num, 0f, (float)z * num2) + min, new Vector3((float)(x + 1) * num + min.x, max.y, (float)(z + 1) * num2 + min.z)); vox.borderSize = num3 + 3; forcedBounds.Expand(new Vector3((float)vox.borderSize, 0f, (float)vox.borderSize) * this.cellSize * 2f); vox.forcedBounds = forcedBounds; vox.width = this.tileSizeX + vox.borderSize * 2; vox.depth = this.tileSizeZ + vox.borderSize * 2; if (!this.useTiles && this.relevantGraphSurfaceMode == RecastGraph.RelevantGraphSurfaceMode.OnlyForCompletelyInsideTile) { vox.relevantGraphSurfaceMode = RecastGraph.RelevantGraphSurfaceMode.RequireForAll; } else { vox.relevantGraphSurfaceMode = this.relevantGraphSurfaceMode; } vox.minRegionSize = Mathf.RoundToInt(this.minRegionSize / (this.cellSize * this.cellSize)); vox.Init(); vox.CollectMeshes(); vox.VoxelizeInput(); vox.FilterLedges(vox.voxelWalkableHeight, vox.voxelWalkableClimb, vox.cellSize, vox.cellHeight, vox.forcedBounds.min); vox.FilterLowHeightSpans(vox.voxelWalkableHeight, vox.cellSize, vox.cellHeight, vox.forcedBounds.min); vox.BuildCompactField(); vox.BuildVoxelConnections(); vox.ErodeWalkableArea(num3); vox.BuildDistanceField(); vox.BuildRegions(); VoxelContourSet cset = new VoxelContourSet(); vox.BuildContours(this.contourMaxError, 1, cset, 1); VoxelMesh mesh; vox.BuildPolyMesh(cset, 3, out mesh); for (int i = 0; i < mesh.verts.Length; i++) { mesh.verts[i] = mesh.verts[i] * 1000 * vox.cellScale + (Int3)vox.voxelOffset; } RecastGraph.NavmeshTile navmeshTile = this.CreateTile(vox, mesh, x, z); this.tiles[navmeshTile.x + navmeshTile.z * this.tileXCount] = navmeshTile; }
public void UpdateAreaInit(GraphUpdateObject o) { if (!o.updatePhysics) { return; } if (!this.dynamic) { throw new Exception("Recast graph must be marked as dynamic to enable graph updates"); } RelevantGraphSurface.UpdateAllPositions(); IntRect touchingTiles = this.GetTouchingTiles(o.bounds); Bounds tileBounds = this.GetTileBounds(touchingTiles); int num = Mathf.CeilToInt(this.characterRadius / this.cellSize); int num2 = num + 3; tileBounds.Expand(new Vector3((float)num2, 0f, (float)num2) * this.cellSize * 2f); List<ExtraMesh> inputExtraMeshes; this.CollectMeshes(out inputExtraMeshes, tileBounds); Voxelize voxelize = this.globalVox; if (voxelize == null) { voxelize = new Voxelize(this.cellHeight, this.cellSize, this.walkableClimb, this.walkableHeight, this.maxSlope); voxelize.maxEdgeLength = this.maxEdgeLength; if (this.dynamic) { this.globalVox = voxelize; } } voxelize.inputExtraMeshes = inputExtraMeshes; }
protected void BuildTileMesh (Voxelize vox, int x, int z) { AstarProfiler.StartProfile("Build Tile"); AstarProfiler.StartProfile("Init"); //World size of tile float tcsx = tileSizeX*cellSize; float tcsz = tileSizeZ*cellSize; int voxelCharacterRadius = Mathf.CeilToInt(characterRadius/cellSize); Vector3 forcedBoundsMin = forcedBounds.min; Vector3 forcedBoundsMax = forcedBounds.max; var bounds = new Bounds(); bounds.SetMinMax(new Vector3(x*tcsx, 0, z*tcsz) + forcedBoundsMin, new Vector3((x+1)*tcsx + forcedBoundsMin.x, forcedBoundsMax.y, (z+1)*tcsz + forcedBoundsMin.z) ); vox.borderSize = voxelCharacterRadius + 3; //Expand borderSize voxels on each side bounds.Expand(new Vector3(vox.borderSize, 0, vox.borderSize)*cellSize*2); vox.forcedBounds = bounds; vox.width = tileSizeX + vox.borderSize*2; vox.depth = tileSizeZ + vox.borderSize*2; if (!useTiles && relevantGraphSurfaceMode == RelevantGraphSurfaceMode.OnlyForCompletelyInsideTile) { // This best reflects what the user would actually want vox.relevantGraphSurfaceMode = RelevantGraphSurfaceMode.RequireForAll; } else { vox.relevantGraphSurfaceMode = relevantGraphSurfaceMode; } vox.minRegionSize = Mathf.RoundToInt(minRegionSize / (cellSize*cellSize)); #if ASTARDEBUG Debug.Log("Building Tile " + x+","+z); System.Console.WriteLine("Recast Graph -- Voxelizing"); #endif AstarProfiler.EndProfile("Init"); //Init voxelizer vox.Init(); vox.CollectMeshes(); vox.VoxelizeInput(); AstarProfiler.StartProfile("Filter Ledges"); vox.FilterLedges(vox.voxelWalkableHeight, vox.voxelWalkableClimb, vox.cellSize, vox.cellHeight, vox.forcedBounds.min); AstarProfiler.EndProfile("Filter Ledges"); AstarProfiler.StartProfile("Filter Low Height Spans"); vox.FilterLowHeightSpans(vox.voxelWalkableHeight, vox.cellSize, vox.cellHeight, vox.forcedBounds.min); AstarProfiler.EndProfile("Filter Low Height Spans"); vox.BuildCompactField(); vox.BuildVoxelConnections(); #if ASTARDEBUG System.Console.WriteLine("Recast Graph -- Eroding"); #endif vox.ErodeWalkableArea(voxelCharacterRadius); #if ASTARDEBUG System.Console.WriteLine("Recast Graph -- Building Distance Field"); #endif vox.BuildDistanceField(); #if ASTARDEBUG System.Console.WriteLine("Recast Graph -- Building Regions"); #endif vox.BuildRegions(); #if ASTARDEBUG System.Console.WriteLine("Recast Graph -- Building Contours"); #endif var cset = new VoxelContourSet(); vox.BuildContours(contourMaxError, 1, cset, Voxelize.RC_CONTOUR_TESS_WALL_EDGES); #if ASTARDEBUG System.Console.WriteLine("Recast Graph -- Building Poly Mesh"); #endif VoxelMesh mesh; vox.BuildPolyMesh(cset, 3, out mesh); #if ASTARDEBUG System.Console.WriteLine("Recast Graph -- Building Nodes"); #endif //Vector3[] vertices = new Vector3[mesh.verts.Length]; AstarProfiler.StartProfile("Build Nodes"); // Debug code //matrix = Matrix4x4.TRS (vox.voxelOffset,Quaternion.identity,Int3.Precision*vox.cellScale); //Position the vertices correctly in the world for (int i = 0; i < mesh.verts.Length; i++) { //Note the multiplication is Scalar multiplication of vectors mesh.verts[i] = ((mesh.verts[i]*Int3.Precision) * vox.cellScale) + (Int3)vox.voxelOffset; // Debug code //Debug.DrawRay (matrix.MultiplyPoint3x4(vertices[i]),Vector3.up,Color.red); } #if ASTARDEBUG System.Console.WriteLine("Recast Graph -- Generating Nodes"); #endif NavmeshTile tile = CreateTile(vox, mesh, x, z); tiles[tile.x + tile.z*tileXCount] = tile; AstarProfiler.EndProfile("Build Nodes"); #if ASTARDEBUG System.Console.WriteLine("Recast Graph -- Done"); #endif AstarProfiler.EndProfile("Build Tile"); }
protected IEnumerable<Progress> ScanAllTiles () { // Voxel grid size int gw = (int)(forcedBounds.size.x/cellSize + 0.5f); int gd = (int)(forcedBounds.size.z/cellSize + 0.5f); if (!useTiles) { tileSizeX = gw; tileSizeZ = gd; } else { tileSizeX = editorTileSize; tileSizeZ = editorTileSize; } // Number of tiles int tw = (gw + tileSizeX-1) / tileSizeX; int td = (gd + tileSizeZ-1) / tileSizeZ; tileXCount = tw; tileZCount = td; if (tileXCount * tileZCount > TileIndexMask+1) { throw new System.Exception ("Too many tiles ("+(tileXCount * tileZCount)+") maximum is "+(TileIndexMask+1)+ "\nTry disabling ASTAR_RECAST_LARGER_TILES under the 'Optimizations' tab in the A* inspector."); } tiles = new NavmeshTile[tileXCount*tileZCount]; // If this is true, just fill the graph with empty tiles if (scanEmptyGraph) { FillWithEmptyTiles(); yield break; } yield return new Progress(0, "Finding Meshes"); List<ExtraMesh> extraMeshes; CollectMeshes (out extraMeshes, forcedBounds); // A walkableClimb higher than walkableHeight can cause issues when generating the navmesh since then it can in some cases // Both be valid for a character to walk under an obstacle and climb up on top of it (and that cannot be handled with navmesh without links) // The editor scripts also enforce this but we enforce it here too just to be sure walkableClimb = Mathf.Min (walkableClimb, walkableHeight); // Create the voxelizer and set all settings var vox = new Voxelize (cellHeight, cellSize, walkableClimb, walkableHeight, maxSlope); vox.inputExtraMeshes = extraMeshes; vox.maxEdgeLength = maxEdgeLength; // Generate all tiles for (int z=0;z<td;z++) { for (int x=0;x<tw;x++) { int tileNum = z*tileXCount + x; yield return new Progress(Mathf.Lerp(0.1f, 0.9f, tileNum/(float)tiles.Length), "Generating Tile " + tileNum + "/" + tiles.Length); BuildTileMesh (vox, x,z); } } yield return new Progress(0.9f, "Assigning Graph Indices"); // Assign graph index to nodes uint graphIndex = (uint)AstarPath.active.astarData.GetGraphIndex (this); GetNodes (node => { node.GraphIndex = graphIndex; return true; }); for (int z=0;z<td;z++) { for (int x=0;x<tw;x++) { yield return new Progress(Mathf.Lerp(0.9f, 1.0f, (z*tileXCount+x)/(float)tiles.Length), "Connecting Tile #"+(z*tileXCount + x) + "/" + tiles.Length); // Connect with tile at (x+1,z) and (x,z+1) if (x < tw-1) ConnectTiles (tiles[x + z*tileXCount], tiles[x+1 + z*tileXCount]); if (z < td-1) ConnectTiles (tiles[x + z*tileXCount], tiles[x + (z+1)*tileXCount]); } } }