/// <summary> /// Draws a debug visualization of the specified navigation mesh tile. /// </summary> /// <remarks> /// <para> /// This method is safe to call on empty tile locations. /// </para> /// </remarks> /// <param name="mesh">The mesh.</param> /// <param name="tx">The tile grid x-location.</param> /// <param name="tz">The tile grid z-location.</param> /// <param name="layer">The tile layer.</param> /// <param name="colorByArea"> /// If true, will be colored by polygon area. If false, will be colored by tile index. /// </param> public static void Draw(Navmesh mesh, int tx, int tz, int layer, bool colorByArea) { NavmeshTile tile = mesh.GetTile(tx, tz, layer); if (tile == null) { // No tile at grid location. return; } Draw(tile, null, null, 0 , colorByArea ? -1 : tx.GetHashCode() ^ tz.GetHashCode() ^ layer.GetHashCode()); }
private void UpdateTileType(NavmeshTile tile) { int x = tile.x; int z = tile.z; Int3 tileSize = (Int3) new Vector3(this.graph.TileWorldSizeX, 0f, this.graph.TileWorldSizeZ); Int3 centerOffset = -((Int3)this.graph.GetTileBoundsInGraphSpace(x, z, 1, 1).min + new Int3(tileSize.x * tile.w / 2, 0, tileSize.z * tile.d / 2)); TileHandler.TileType tileType = new TileHandler.TileType(tile.vertsInGraphSpace, tile.tris, tileSize, centerOffset, tile.w, tile.d); int num = x + z * this.tileXCount; this.activeTileTypes[num] = tileType; this.activeTileRotations[num] = 0; this.activeTileOffsets[num] = 0; }
private void SetConfigFromTargetIntern(Navmesh navmesh) { NavmeshBuildInfo targetConfig = BuildTarget.BuildInfo; NMGenParams currConfig = mConfig.GetConfig(); // Note: Must ensure exact match with original configuration. // So this process is using the fields and trusting the // original configuration to have valid values. currConfig.tileSize = targetConfig.tileSize; currConfig.walkableHeight = targetConfig.walkableHeight; currConfig.walkableRadius = targetConfig.walkableRadius; currConfig.walkableStep = targetConfig.walkableStep; currConfig.xzCellSize = targetConfig.xzCellSize; currConfig.yCellSize = targetConfig.yCellSize; currConfig.borderSize = targetConfig.borderSize; mBoundsMin = navmesh.GetConfig().origin; int maxTiles = navmesh.GetMaxTiles(); // Make sure the maximum bounds fits the target mesh. // Note: Will not shrink the existing max bounds. for (int i = 0; i < maxTiles; i++) { NavmeshTile tile = navmesh.GetTile(i); if (tile == null) { continue; } NavmeshTileHeader tileHeader = tile.GetHeader(); if (tileHeader.polyCount == 0) { continue; } mBoundsMax = Vector3.Max(mBoundsMax, tileHeader.boundsMax); } mConfig.SetConfig(currConfig); mIsDirty = true; }
/// <summary> /// Gets the centroids for the polygons that are part of the tile. /// </summary> private static int GetCentroids(NavmeshTile tile , uint[] polyRefs , int polyCount , UnityEngine.Vector3[] centroids) { NavmeshTileHeader header = tile.GetHeader(); if (header.polyCount < 1) { return(0); } uint polyBase = tile.GetBasePolyRef(); NavmeshPoly[] polys = new NavmeshPoly[header.polyCount]; tile.GetPolys(polys); Vector3[] verts = new Vector3[header.vertCount]; tile.GetVerts(verts); UnityEngine.Vector3[] vverts = VectorHelper.ToUnityVector3Array(ref verts); int resultCount = 0; for (int i = 0; i < header.polyCount; i++) { uint polyRef = polyBase | ( uint )i; int iResult = IsInList(polyRef, polyRefs, polyCount); if (iResult == -1) { continue; } resultCount++; NavmeshPoly poly = polys[i]; centroids[iResult] = GetCentroid(vverts, poly.indices, poly.vertCount); } return(resultCount); }
public void CreateTileTypesFromGraph() { NavmeshTile[] tiles = this.graph.GetTiles(); if (tiles == null) { return; } if (!this.isValid) { throw new InvalidOperationException("Graph tiles are invalid (number of tiles is not equal to width*depth of the graph). You need to create a new tile handler if you have changed the graph."); } for (int i = 0; i < this.tileZCount; i++) { for (int j = 0; j < this.tileXCount; j++) { NavmeshTile tile = tiles[j + i * this.tileXCount]; this.UpdateTileType(tile); } } }
public static void OutputVertices() { NavmeshTile tile = navmesh.GetTile(0); float[] headerVerts = new float[tile.GetHeader().vertCount * 3]; org.critterai.Vector3[] buffer = new org.critterai.Vector3[tile.GetHeader().vertCount * 3]; tile.GetVerts(buffer); string filename = "Vertices.txt"; if (File.Exists(filename)) { File.Delete(filename); } File.AppendAllText(filename, "Left List is Raw (vertcount = " + vertCount + "), Right List is From Header (vertcount = " + tile.GetHeader().vertCount + ") (you might see data loss, as converted in/out of grid space.) \n\n"); for (int i = 0; i < vertCount; i++) { File.AppendAllText(filename, "Vertex " + i + ": x: " + vertices[i].x + " y: " + vertices[i].y + " z: " + vertices[i].z + "\t\t|"); File.AppendAllText(filename, "Vertex " + i + ": x: " + buffer[3 * i + 0].x + " y: " + headerVerts[3 * i + 1] + " z: " + headerVerts[3 * i + 2] + "\n"); } }
/// <summary> /// Draws a debug visualization of an individual navmesh tile. /// </summary> /// <remarks> /// <para> /// The tile will be checked to see if it is in use before it is drawn. So there is no /// need for caller to do so. /// </para> /// </remarks> private static void Draw(NavmeshTile tile , NavmeshQuery query, uint[] markPolys, int markPolyCount , int colorId) { NavmeshTileHeader header = tile.GetHeader(); // Keep this check. Less trouble for clients. if (header.polyCount < 1) { return; } DebugDraw.SimpleMaterial.SetPass(0); uint polyBase = tile.GetBasePolyRef(); NavmeshPoly[] polys = new NavmeshPoly[header.polyCount]; tile.GetPolys(polys); Vector3[] verts = new Vector3[header.vertCount]; tile.GetVerts(verts); NavmeshDetailMesh[] meshes = new NavmeshDetailMesh[header.detailMeshCount]; tile.GetDetailMeshes(meshes); byte[] detailTris = new byte[header.detailTriCount * 4]; tile.GetDetailTris(detailTris); Vector3[] detailVerts = new Vector3[header.detailVertCount]; tile.GetDetailVerts(detailVerts); GL.Begin(GL.TRIANGLES); for (int i = 0; i < header.polyCount; i++) { NavmeshPoly poly = polys[i]; if (poly.Type == NavmeshPolyType.OffMeshConnection) { continue; } NavmeshDetailMesh mesh = meshes[i]; Color color = GetStandardColor(polyBase | (uint)i , poly.Area, colorId , query, markPolys, markPolyCount); GL.Color(color); for (int j = 0; j < mesh.triCount; j++) { int pTri = (int)(mesh.triBase + j) * 4; for (int k = 0; k < 3; k++) { // Note: iVert and pVert refer to different // arrays. int iVert = detailTris[pTri + k]; if (iVert < poly.vertCount) { // Get the vertex from the main vertices. int pVert = poly.indices[iVert]; GL.Vertex(verts[pVert]); } else { // Get the vertex from the detail vertices. int pVert = (int) (mesh.vertBase + iVert - poly.vertCount); GL.Vertex(detailVerts[pVert]); } } } } GL.End(); NavmeshLink[] links = new NavmeshLink[header.maxLinkCount]; tile.GetLinks(links); GL.Begin(GL.LINES); DrawPolyBoundaries(header , polys , verts , meshes , detailTris , detailVerts , links , new Color(0, 0.2f, 0.25f, 0.13f) , true); DrawPolyBoundaries(header , polys , verts , meshes , detailTris , detailVerts , links , new Color(0.65f, 0.2f, 0, 0.9f) , false); if (header.connCount == 0) { GL.End(); return; } NavmeshConnection[] conns = new NavmeshConnection[header.connCount]; tile.GetConnections(conns); for (int i = 0; i < header.polyCount; i++) { NavmeshPoly poly = polys[i]; if (poly.Type != NavmeshPolyType.OffMeshConnection) { continue; } Color color = GetStandardColor(polyBase | (uint)i , poly.Area, colorId , query, markPolys, markPolyCount); // Note: Alpha of less than one doesn't look good because connections tend to // overlay a lot of geometry, resulting is off color transitions. color.a = 1; GL.Color(color); NavmeshConnection conn = conns[i - header.connBase]; Vector3 va = verts[poly.indices[0]]; Vector3 vb = verts[poly.indices[1]]; // Check to see if start and end end-points have links. bool startSet = false; bool endSet = false; for (uint k = poly.firstLink; k != Navmesh.NullLink; k = links[k].next) { if (links[k].edge == 0) { startSet = true; } if (links[k].edge == 1) { endSet = true; } } // For linked endpoints: Draw a line between on-mesh location and endpoint, // and draw circle at the endpoint. // For un-linked endpoints: Draw a small red x-marker. if (startSet) { GL.Vertex(va); GL.Vertex(conn.endpoints[0]); DebugDraw.AppendCircle(conn.endpoints[0], conn.radius); } else { GL.Color(Color.red); DebugDraw.AppendXMarker(conn.endpoints[0], 0.1f); GL.Color(color); } if (endSet) { GL.Vertex(vb); GL.Vertex(conn.endpoints[1]); DebugDraw.AppendCircle(conn.endpoints[1], conn.radius); } else { GL.Color(Color.red); DebugDraw.AppendXMarker(conn.endpoints[1], 0.1f); GL.Color(color); } DebugDraw.AppendArc(conn.endpoints[0], conn.endpoints[1] , 0.25f , conn.IsBiDirectional ? 0.6f : 0 , 0.6f); } GL.End(); }
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"); //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 NavmeshTile 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(tile); 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)System.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)System.Math.Round(z*tileSizeZ*cellSize*Int3.FloatPrecision))) Debug.LogWarning ("Possible numerical imprecision. Consider adjusting tileSize and/or cellSize"); Int3 offset = (Int3)(new Vector3((x * tileSizeX * cellSize),0,(z * tileSizeZ * cellSize)) + forcedBounds.min); for (int i=0;i<verts.Length;i++) { verts[i] += offset; } } 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); //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; }
public void RemoveConnectionsFromTo (NavmeshTile a, NavmeshTile b) { if (a == null || b == null) return; //Same tile, possibly from a large tile (one spanning several x,z tile coordinates) if (a == b) return; int tileIdx = b.x + b.z*tileXCount; for (int i=0;i<a.nodes.Length;i++) { TriangleMeshNode node = a.nodes[i]; if (node.connections == null) continue; for (int j=0;;j++) { //Length will not be constant if connections are removed if (j >= node.connections.Length) break; TriangleMeshNode other = node.connections[j] as TriangleMeshNode; //Only evaluate TriangleMeshNodes if (other == null) continue; int tileIdx2 = other.GetVertexIndex(0); tileIdx2 = (tileIdx2 >> TileIndexOffset) & TileIndexMask; if (tileIdx2 == tileIdx) { node.RemoveConnection (node.connections[j]); j--; } } } }
/// <summary> /// Draws a debug visualization of an individual navmesh tile. /// </summary> /// <remarks> /// <para> /// The tile will be checked to see if it is in use before it is drawn. So there is no /// need for caller to do so. /// </para> /// </remarks> private static void Draw(NavmeshTile tile , NavmeshQuery query, uint[] markPolys, int markPolyCount , int colorId) { NavmeshTileHeader header = tile.GetHeader(); // Keep this check. Less trouble for clients. if (header.polyCount < 1) return; DebugDraw.SimpleMaterial.SetPass(0); uint polyBase = tile.GetBasePolyRef(); NavmeshPoly[] polys = new NavmeshPoly[header.polyCount]; tile.GetPolys(polys); Vector3[] verts = new Vector3[header.vertCount]; tile.GetVerts(verts); NavmeshDetailMesh[] meshes = new NavmeshDetailMesh[header.detailMeshCount]; tile.GetDetailMeshes(meshes); byte[] detailTris = new byte[header.detailTriCount * 4]; tile.GetDetailTris(detailTris); Vector3[] detailVerts = new Vector3[header.detailVertCount]; tile.GetDetailVerts(detailVerts); GL.Begin(GL.TRIANGLES); for (int i = 0; i < header.polyCount; i++) { NavmeshPoly poly = polys[i]; if (poly.Type == NavmeshPolyType.OffMeshConnection) continue; NavmeshDetailMesh mesh = meshes[i]; Color color = GetStandardColor(polyBase | (uint)i , poly.Area, colorId , query, markPolys, markPolyCount); GL.Color(color); for (int j = 0; j < mesh.triCount; j++) { int pTri = (int)(mesh.triBase + j) * 4; for (int k = 0; k < 3; k++) { // Note: iVert and pVert refer to different // arrays. int iVert = detailTris[pTri + k]; if (iVert < poly.vertCount) { // Get the vertex from the main vertices. int pVert = poly.indices[iVert]; GL.Vertex(verts[pVert]); } else { // Get the vertex from the detail vertices. int pVert = (int) (mesh.vertBase + iVert - poly.vertCount); GL.Vertex(detailVerts[pVert]); } } } } GL.End(); NavmeshLink[] links = new NavmeshLink[header.maxLinkCount]; tile.GetLinks(links); GL.Begin(GL.LINES); DrawPolyBoundaries(header , polys , verts , meshes , detailTris , detailVerts , links , new Color(0, 0.2f, 0.25f, 0.13f) , true); DrawPolyBoundaries(header , polys , verts , meshes , detailTris , detailVerts , links , new Color(0.65f, 0.2f, 0, 0.9f) , false); if (header.connCount == 0) { GL.End(); return; } NavmeshConnection[] conns = new NavmeshConnection[header.connCount]; tile.GetConnections(conns); for (int i = 0; i < header.polyCount; i++) { NavmeshPoly poly = polys[i]; if (poly.Type != NavmeshPolyType.OffMeshConnection) continue; Color color = GetStandardColor(polyBase | (uint)i , poly.Area, colorId , query, markPolys, markPolyCount); // Note: Alpha of less than one doesn't look good because connections tend to // overlay a lot of geometry, resulting is off color transitions. color.a = 1; GL.Color(color); NavmeshConnection conn = conns[i - header.connBase]; Vector3 va = verts[poly.indices[0]]; Vector3 vb = verts[poly.indices[1]]; // Check to see if start and end end-points have links. bool startSet = false; bool endSet = false; for (uint k = poly.firstLink; k != Navmesh.NullLink; k = links[k].next) { if (links[k].edge == 0) startSet = true; if (links[k].edge == 1) endSet = true; } // For linked endpoints: Draw a line between on-mesh location and endpoint, // and draw circle at the endpoint. // For un-linked endpoints: Draw a small red x-marker. if (startSet) { GL.Vertex(va); GL.Vertex(conn.endpoints[0]); DebugDraw.AppendCircle(conn.endpoints[0], conn.radius); } else { GL.Color(Color.red); DebugDraw.AppendXMarker(conn.endpoints[0], 0.1f); GL.Color(color); } if (endSet) { GL.Vertex(vb); GL.Vertex(conn.endpoints[1]); DebugDraw.AppendCircle(conn.endpoints[1], conn.radius); } else { GL.Color(Color.red); DebugDraw.AppendXMarker(conn.endpoints[1], 0.1f); GL.Color(color); } DebugDraw.AppendArc(conn.endpoints[0], conn.endpoints[1] , 0.25f , conn.IsBiDirectional ? 0.6f : 0 , 0.6f); } GL.End(); }
public void ConnectTileWithNeighbours (NavmeshTile tile) { if (tile.x > 0) { int x = tile.x-1; for (int z=tile.z;z<tile.z+tile.d;z++) ConnectTiles (tiles[x + z*tileXCount], tile); } if (tile.x+tile.w < tileXCount) { int x = tile.x+tile.w; for (int z=tile.z;z<tile.z+tile.d;z++) ConnectTiles (tiles[x + z*tileXCount], tile); } if (tile.z > 0) { int z = tile.z-1; for (int x=tile.x;x<tile.x+tile.w;x++) ConnectTiles (tiles[x + z*tileXCount], tile); } if (tile.z+tile.d < tileZCount) { int z = tile.z+tile.d; for (int x=tile.x;x<tile.x+tile.w;x++) ConnectTiles (tiles[x + z*tileXCount], tile); } }
public void RemoveConnectionsFromTile (NavmeshTile tile) { if (tile.x > 0) { int x = tile.x-1; for (int z=tile.z;z<tile.z+tile.d;z++) RemoveConnectionsFromTo (tiles[x + z*tileXCount], tile); } if (tile.x+tile.w < tileXCount) { int x = tile.x+tile.w; for (int z=tile.z;z<tile.z+tile.d;z++) RemoveConnectionsFromTo (tiles[x + z*tileXCount], tile); } if (tile.z > 0) { int z = tile.z-1; for (int x=tile.x;x<tile.x+tile.w;x++) RemoveConnectionsFromTo (tiles[x + z*tileXCount], tile); } if (tile.z+tile.d < tileZCount) { int z = tile.z+tile.d; for (int x=tile.x;x<tile.x+tile.w;x++) RemoveConnectionsFromTo (tiles[x + z*tileXCount], tile); } //if (tile.z > 0) RemoveConnectionsFromTo (tiles[tile.x + (tile.z-1)*tileXCount], tile); //if (tile.x < tileXCount-1) RemoveConnectionsFromTo (tiles[tile.x+1 + tile.z*tileXCount], tile); //if (tile.z < tileZCount-1) RemoveConnectionsFromTo (tiles[tile.x + (tile.z+1)*tileXCount], tile); }
static NavmeshTile NewEmptyTile (int x, int z) { NavmeshTile tile = new NavmeshTile(); tile.x = x; tile.z = z; tile.w = 1; tile.d = 1; tile.verts = new Int3[0]; tile.tris = new int[0]; tile.nodes = new TriangleMeshNode[0]; tile.bbTree = new BBTree(tile); return tile; }
/// <summary> /// Gets the centroids for the polygons that are part of the tile. /// </summary> private static int GetCentroids(NavmeshTile tile , uint[] polyRefs , int polyCount , Vector3[] centroids) { NavmeshTileHeader header = tile.GetHeader(); if (header.polyCount < 1) return 0; uint polyBase = tile.GetBasePolyRef(); NavmeshPoly[] polys = new NavmeshPoly[header.polyCount]; tile.GetPolys(polys); Vector3[] verts = new Vector3[header.vertCount]; tile.GetVerts(verts); int resultCount = 0; for (int i = 0; i < header.polyCount; i++) { uint polyRef = polyBase | (uint)i; int iResult = IsInList(polyRef, polyRefs, polyCount); if (iResult == -1) continue; resultCount++; NavmeshPoly poly = polys[i]; centroids[iResult] = GetCentroid(verts, poly.indices, poly.vertCount); } return resultCount; }
internal bool CanLoadFromTarget(BuildContext context, bool fullCheck) { INavmeshData target = BuildTarget; if (target == null || !target.HasNavmesh) { if (context != null) { context.LogError("Build target does not have an existing navigation mesh.", this); } return(false); } NavmeshBuildInfo targetConfig = target.BuildInfo; // Note: The tile size is checked since the original builder // may have supported a tile size not supported by the the standard build. if (targetConfig == null || targetConfig.tileSize >= 0 && targetConfig.tileSize < MinAllowedTileSize) { if (context != null) { context.LogError("Unavailable or unsupported build target configuration.", this); } return(false); } if (!fullCheck) { return(true); } Navmesh nm = target.GetNavmesh(); if (nm == null) { if (context != null) { context.LogError( "Build target does not have an existing navigation mesh. (It lied.)", this); } return(false); } NavmeshParams nmConfig = nm.GetConfig(); if (nmConfig.maxTiles < 2) { if (context != null) { context.LogError("Target navigation mesh is not tiled.", this); } return(false); } int tileCount = 0; for (int i = 0; i < nmConfig.maxTiles; i++) { NavmeshTile tile = nm.GetTile(i); if (tile == null) { continue; } NavmeshTileHeader header = tile.GetHeader(); if (header.polyCount == 0) { continue; } tileCount++; if (header.layer > 0) { if (context != null) { context.LogError( "Target navigation mesh contains layered tiles. (Not supported.)", this); } return(false); } } if (tileCount < 2) { if (context != null) { context.LogError( "Target navigation mesh is either not tiled or has no tiles loaded.", this); } return(false); } return(true); }
/** Generate connections between the two tiles. * The tiles must be adjacent. */ void ConnectTiles (NavmeshTile tile1, NavmeshTile tile2) { if (tile1 == null) return;//throw new System.ArgumentNullException ("tile1"); if (tile2 == null) return;//throw new System.ArgumentNullException ("tile2"); if (tile1.nodes == null) throw new System.ArgumentException ("tile1 does not contain any nodes"); if (tile2.nodes == null) throw new System.ArgumentException ("tile2 does not contain any nodes"); int t1x = Mathf.Clamp(tile2.x,tile1.x,tile1.x+tile1.w-1); int t2x = Mathf.Clamp(tile1.x,tile2.x,tile2.x+tile2.w-1); int t1z = Mathf.Clamp(tile2.z,tile1.z,tile1.z+tile1.d-1); int t2z = Mathf.Clamp(tile1.z,tile2.z,tile2.z+tile2.d-1); int coord, altcoord; int t1coord, t2coord; float tcs; if (t1x == t2x) { coord = 2; altcoord = 0; t1coord = t1z; t2coord = t2z; tcs = tileSizeZ*cellSize; } else if (t1z == t2z) { coord = 0; altcoord = 2; t1coord = t1x; t2coord = t2x; tcs = tileSizeX*cellSize; } else { throw new System.ArgumentException ("Tiles are not adjacent (neither x or z coordinates match)"); } if (System.Math.Abs (t1coord-t2coord) != 1) { Debug.Log (tile1.x + " " + tile1.z + " " + tile1.w + " " + tile1.d + "\n"+ tile2.x + " " + tile2.z + " " + tile2.w + " " + tile2.d+"\n"+ t1x + " " + t1z + " " + t2x + " " + t2z); throw new System.ArgumentException ("Tiles are not adjacent (tile coordinates must differ by exactly 1. Got '" + t1coord + "' and '" + t2coord + "')"); } //Midpoint between the two tiles int midpoint = (int)System.Math.Round ((System.Math.Max(t1coord, t2coord) * tcs + forcedBounds.min[coord]) * Int3.Precision); #if ASTARDEBUG Vector3 v1 = new Vector3(-100,0,-100); Vector3 v2 = new Vector3(100,0,100); v1[coord] = midpoint*Int3.PrecisionFactor; v2[coord] = midpoint*Int3.PrecisionFactor; Debug.DrawLine (v1,v2,Color.magenta); #endif TriangleMeshNode[] nodes1 = tile1.nodes; TriangleMeshNode[] nodes2 = tile2.nodes; //Find adjacent nodes on the border between the tiles for (int i=0;i<nodes1.Length;i++) { TriangleMeshNode node = nodes1[i]; int av = node.GetVertexCount (); for (int a=0;a<av;a++) { Int3 ap1 = node.GetVertex (a); Int3 ap2 = node.GetVertex ((a+1) % av); if (System.Math.Abs(ap1[coord] - midpoint) < 2 && System.Math.Abs (ap2[coord] - midpoint) < 2) { #if ASTARDEBUG Debug.DrawLine ((Vector3)ap1, (Vector3)ap2, Color.red); #endif int minalt = System.Math.Min (ap1[altcoord], ap2[altcoord]); int maxalt = System.Math.Max (ap1[altcoord], ap2[altcoord]); //Degenerate edge if (minalt == maxalt) continue; for (int j=0;j<nodes2.Length;j++) { TriangleMeshNode other = nodes2[j]; int bv = other.GetVertexCount (); for (int b=0;b<bv;b++) { Int3 bp1 = other.GetVertex(b); Int3 bp2 = other.GetVertex((b+1) % av); if(System.Math.Abs(bp1[coord] - midpoint) < 2 && System.Math.Abs(bp2[coord] - midpoint) < 2) { int minalt2 = System.Math.Min (bp1[altcoord], bp2[altcoord]); int maxalt2 = System.Math.Max (bp1[altcoord], bp2[altcoord]); //Degenerate edge if (minalt2 == maxalt2) continue; if (maxalt > minalt2 && minalt < maxalt2) { //Adjacent //Test shortest distance between the segments (first test if they are equal since that is much faster) if ((ap1 == bp1 && ap2 == bp2) || (ap1 == bp2 && ap2 == bp1) || Polygon.DistanceSegmentSegment3D ((Vector3)ap1, (Vector3)ap2, (Vector3)bp1, (Vector3)bp2) < walkableClimb*walkableClimb) { uint cost = (uint)(node.position - other.position).costMagnitude; node.AddConnection (other, cost); other.AddConnection (node, cost); } } } } } } } } }
/** 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 override void DeserializeExtraInfo (GraphSerializationContext ctx) { //NavMeshGraph.DeserializeMeshNodes (this,nodes,bytes); System.IO.BinaryReader reader = ctx.reader; tileXCount = reader.ReadInt32(); if (tileXCount < 0) return; tileZCount = reader.ReadInt32(); tiles = new NavmeshTile[tileXCount * tileZCount]; //Make sure mesh nodes can reference this graph TriangleMeshNode.SetNavmeshHolder (ctx.graphIndex, this); for (int z=0;z<tileZCount;z++) { for (int x=0;x<tileXCount;x++) { int tileIndex = x + z*tileXCount; int tx = reader.ReadInt32(); if (tx < 0) throw new System.Exception ("Invalid tile coordinates (x < 0)"); int tz = reader.ReadInt32(); if (tz < 0) throw new System.Exception ("Invalid tile coordinates (z < 0)"); // This is not the origin of a large tile. Refer back to that tile. if (tx != x || tz != z) { tiles[tileIndex] = tiles[tz*tileXCount + tx]; continue; } NavmeshTile tile = new NavmeshTile (); tile.x = tx; tile.z = tz; tile.w = reader.ReadInt32(); tile.d = reader.ReadInt32(); tile.bbTree = new BBTree (tile); tiles[tileIndex] = tile; int trisCount = reader.ReadInt32 (); if (trisCount % 3 != 0) throw new System.Exception ("Corrupt data. Triangle indices count must be divisable by 3. Got " + trisCount); tile.tris = new int[trisCount]; for (int i=0;i<tile.tris.Length;i++) tile.tris[i] = reader.ReadInt32(); tile.verts = new Int3[reader.ReadInt32()]; for (int i=0;i<tile.verts.Length;i++) { tile.verts[i] = new Int3 (reader.ReadInt32(), reader.ReadInt32(), reader.ReadInt32()); } int nodeCount = reader.ReadInt32(); tile.nodes = new TriangleMeshNode[nodeCount]; //Prepare for storing in vertex indices tileIndex <<= TileIndexOffset; for (int i=0;i<tile.nodes.Length;i++) { TriangleMeshNode node = new TriangleMeshNode (active); tile.nodes[i] = node; node.GraphIndex = (uint)ctx.graphIndex; node.DeserializeNode (ctx); node.v0 = tile.tris[i*3+0] | tileIndex; node.v1 = tile.tris[i*3+1] | tileIndex; node.v2 = tile.tris[i*3+2] | tileIndex; node.UpdatePositionFromVertices(); tile.bbTree.Insert (node); } } } }
public void CreateQuery(float fMoveSpeed, float fTurnSpeed) { this.mTurnSpeed = fTurnSpeed; this.mMoveSpeed = fMoveSpeed; NavStatus status = NavmeshQuery.Create(navmesh, mMaxQueryNodes, out query); if ((status & NavStatus.Sucess) == 0) { Debug.LogError( fileName + ": Aborted initialization. Failed query creation: " + status.ToString()); mCrowdManager = null; return; } mCrowdManager = CrowdManager.Create(mMaxCrowdAgents, mMaxAgentRadius, navmesh); if (mCrowdManager == null) { Debug.LogError(fileName + ": Aborted initialization. Failed crowd creation."); } CrowdAvoidanceParams mCrowdParam = CrowdAvoidanceParams.CreateStandardMedium(); mCrowdManager.SetAvoidanceConfig(0, mCrowdParam); mCrowdAgentParams = new CrowdAgentParams(); mCrowdAgentParams.avoidanceType = 0; mCrowdAgentParams.collisionQueryRange = 3.2f; mCrowdAgentParams.height = 1.8f; mCrowdAgentParams.maxAcceleration = 8.0f; mCrowdAgentParams.maxSpeed = this.mMoveSpeed; mCrowdAgentParams.pathOptimizationRange = 12.0f; mCrowdAgentParams.radius = 1.0f; mCrowdAgentParams.separationWeight = 2.0f; mCrowdAgentParams.updateFlags = CrowdUpdateFlags.AnticipateTurns | CrowdUpdateFlags.ObstacleAvoidance | CrowdUpdateFlags.CrowdSeparation | CrowdUpdateFlags.OptimizeVis | CrowdUpdateFlags.OptimizeTopo; polyResult = new uint[300]; pointResult = new Vector3[300]; tileBufferPoints = new Vector3[3000]; pointResultBuffer = new Vector3[300]; polyResultBuffer = new uint[300]; NavmeshTile tile = navmesh.GetTile(0); int count = tile.GetVerts(tileBufferPoints); Debug.Log("Tile " + tile.GetTileRef() + " count:" + count); if (count > 3000) { tileBufferPoints = new Vector3[count]; } tileBufferRef = -1; //NavmeshPoly[] polys=new NavmeshPoly[3000]; //int polyCount; //polyCount=tile.GetPolys(polys); //for (int i = 0; i < polyCount; i++) //{ // NavmeshPoly poly = polys[i]; // //if (poly.Type == NavmeshPolyType.OffMeshConnection) // { // Debug.Log("Poly" + i+"type"+poly.Type.ToString()); // } //} }