public void ReplaceTile (int x, int z, int w, int d, Int3[] verts, int[] tris, bool worldSpace) { if(x + w > tileXCount || z+d > tileZCount || x < 0 || z < 0) { throw new System.ArgumentException ("Tile is placed at an out of bounds position or extends out of the graph bounds ("+x+", " + z + " [" + w + ", " + d+ "] " + tileXCount + " " + tileZCount + ")"); } if (w < 1 || d < 1) throw new System.ArgumentException ("width and depth must be greater or equal to 1. Was " + w + ", " + d); //Remove previous tiles for (int cz=z; cz < z+d;cz++) { for (int cx=x; cx < x+w;cx++) { NavmeshTile otile = tiles[cx + cz*tileXCount]; if (otile == null) continue; //Remove old tile connections RemoveConnectionsFromTile (otile); for (int i=0;i<otile.nodes.Length;i++) { otile.nodes[i].Destroy(); } for (int qz=otile.z; qz < otile.z+otile.d;qz++) { for (int qx=otile.x; qx < otile.x+otile.w;qx++) { NavmeshTile qtile = tiles[qx + qz*tileXCount]; if (qtile == null || qtile != otile) throw new System.Exception("This should not happen"); if (qz < z || qz >= z+d || qx < x || qx >= x+w) { //if out of this tile's bounds, replace with empty tile tiles[qx + qz*tileXCount] = NewEmptyTile(qx,qz); if (batchTileUpdate) { batchUpdatedTiles.Add (qx + qz*tileXCount); } } else { //Will be replaced by the new tile tiles[qx + qz*tileXCount] = null; } } } } } //Create a new navmesh tile and assign its settings var tile = new NavmeshTile(); tile.x = x; tile.z = z; tile.w = w; tile.d = d; tile.tris = tris; tile.verts = verts; tile.bbTree = new BBTree(); if (tile.tris.Length % 3 != 0) throw new System.ArgumentException ("Triangle array's length must be a multiple of 3 (tris)"); if (tile.verts.Length > 0xFFFF) throw new System.ArgumentException ("Too many vertices per tile (more than 65535)"); if (!worldSpace) { if (!Mathf.Approximately (x*tileSizeX*cellSize*Int3.FloatPrecision, (float)Math.Round(x*tileSizeX*cellSize*Int3.FloatPrecision))) Debug.LogWarning ("Possible numerical imprecision. Consider adjusting tileSize and/or cellSize"); if (!Mathf.Approximately (z*tileSizeZ*cellSize*Int3.FloatPrecision, (float)Math.Round(z*tileSizeZ*cellSize*Int3.FloatPrecision))) Debug.LogWarning ("Possible numerical imprecision. Consider adjusting tileSize and/or cellSize"); var offset = (Int3)(new Vector3((x * tileSizeX * cellSize),0,(z * tileSizeZ * cellSize)) + forcedBounds.min); for (int i=0;i<verts.Length;i++) { verts[i] += offset; } } var 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++) { var 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 occur, 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.RebuildFrom(nodes); CreateNodeConnections (tile.nodes); //Set tile for (int cz=z; cz < z+d;cz++) { for (int cx=x; cx < x+w;cx++) { tiles[cx + cz*tileXCount] = tile; } } if (batchTileUpdate) { batchUpdatedTiles.Add (x + z*tileXCount); } else { ConnectTileWithNeighbours(tile); /*if (x > 0) ConnectTiles (tiles[(x-1) + z*tileXCount], tile); if (z > 0) ConnectTiles (tiles[x + (z-1)*tileXCount], tile); if (x < tileXCount-1) ConnectTiles (tiles[(x+1) + z*tileXCount], tile); if (z < tileZCount-1) ConnectTiles (tiles[x + (z+1)*tileXCount], tile);*/ } //Remove the fake graph TriangleMeshNode.SetNavmeshHolder (graphIndex, null); //Real graph index //TODO, could this step be changed for this function, is a fake index required? graphIndex = AstarPath.active.astarData.GetGraphIndex (this); for (int i=0;i<nodes.Length;i++) nodes[i].GraphIndex = (uint)graphIndex; }
/** 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 ("mesh.tris"); if (mesh.verts == null) throw new System.ArgumentNullException ("mesh.verts"); //Create a new navmesh tile and assign its settings var 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(); 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(); var compressedPointers = new int[tile.verts.Length]; int count = 0; for (int i=0;i<tile.verts.Length;i++) { if (!firstVerts.ContainsKey(tile.verts[i])) { firstVerts.Add (tile.verts[i], count); compressedPointers[i] = count; tile.verts[count] = tile.verts[i]; count++; } else { // 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]]; } var compressed = new Int3[count]; for (int i=0;i<count;i++) compressed[i] = tile.verts[i]; tile.verts = compressed; var 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++) { var 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 occur, 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.RebuildFrom(nodes); CreateNodeConnections (tile.nodes); //Remove the fake graph TriangleMeshNode.SetNavmeshHolder (graphIndex, null); return tile; }