public Boundary(BoundarySide side, GraphicsDevice graphics) { this.graphics = graphics; this.side = side; switch (side) { case BoundarySide.Left: box = new Rectangle((Game1.gameWidth / 2) - (Tetrimino.tileSize * 5) - boundaryWidth, (Game1.gameHeight - Tetrimino.tileSize * 25), boundaryWidth, Tetrimino.tileSize * 21); break; case BoundarySide.Right: box = new Rectangle((Game1.gameWidth / 2) + (Tetrimino.tileSize * 5), (Game1.gameHeight - Tetrimino.tileSize * 25), boundaryWidth, Tetrimino.tileSize * 21); break; case BoundarySide.Bottom: box = new Rectangle((Game1.gameWidth / 2) - (Tetrimino.tileSize * 5) - boundaryWidth, (Game1.gameHeight - Tetrimino.tileSize * 4), Tetrimino.tileSize * 10 + boundaryWidth * 2, boundaryWidth); break; case BoundarySide.Top: box = new Rectangle((Game1.gameWidth / 2) - (Tetrimino.tileSize * 5) - boundaryWidth, (Game1.gameHeight - Tetrimino.tileSize * 24 - 2), Tetrimino.tileSize * 10 + boundaryWidth * 2, 2); break; } texture = new Texture2D(graphics, box.Width, box.Height); Color[] color = new Color[box.Width * box.Height]; for (int i = 0; i < color.Length; i++) { color[i] = new Color(0, 0, 0); } texture.SetData(color); }
/// <summary> /// Search for neighbor polygons in the tile. /// </summary> /// <param name="va">Vertex A</param> /// <param name="vb">Vertex B</param> /// <param name="side">Polygon edge</param> /// <param name="con">Resulting Connection polygon</param> /// <param name="conarea">Resulting Connection area</param> public void FindConnectingPolys(Vector3 va, Vector3 vb, BoundarySide side, List <NavPolyId> con, List <float> conarea) { Vector2 amin, amax; CalcSlabEndPoints(va, vb, out amin, out amax, side); float apos = GetSlabCoord(va, side); //Remove links pointing to 'side' and compact the links array Vector2 bmin, bmax; //Iterate through all the tile's polygons for (int i = 0; i < PolyCount; i++) { int numPolyVerts = Polys[i].VertCount; //Iterate through all the vertices for (int j = 0; j < numPolyVerts; j++) { //Skip edges which do not point to the right side if (Polys[i].Neis[j] != (Link.External | (int)side)) { continue; } //Grab two adjacent vertices Vector3 vc = Verts[Polys[i].Verts[j]]; Vector3 vd = Verts[Polys[i].Verts[(j + 1) % numPolyVerts]]; float bpos = GetSlabCoord(vc, side); //Segments are not close enough if (Math.Abs(apos - bpos) > 0.01f) { continue; } //Check if the segments touch CalcSlabEndPoints(vc, vd, out bmin, out bmax, side); //Skip if slabs don't overlap if (!OverlapSlabs(amin, amax, bmin, bmax, 0.01f, WalkableClimb)) { continue; } //Add return value if (con.Count < con.Capacity) { conarea.Add(Math.Max(amin.X, bmin.X)); conarea.Add(Math.Min(amax.X, bmax.X)); NavPolyId id; idManager.SetPolyIndex(ref baseRef, i, out id); con.Add(id); } break; } } }
/// <summary> /// Gets the side in the exact opposite direction as a specified side. /// </summary> /// <remarks> /// The value <see cref="BoundarySide.Internal"/> will always return <see cref="BoundarySide.Internal"/>. /// </remarks> /// <param name="side">A side.</param> /// <returns>The opposite side.</returns> public static BoundarySide GetOpposite(this BoundarySide side) { if (side == BoundarySide.Internal) { return(BoundarySide.Internal); } return((BoundarySide)((int)(side + 4) % 8)); }
public static string ToStringEx(this BoundarySide side) { switch (side) { case BoundarySide.Low: return("Low"); case BoundarySide.High: return("High"); case BoundarySide.All: return("ALL"); default: return(""); } }
/// <summary> /// Return the proper slab coordinate value depending on the 'side' value. /// </summary> /// <param name="va">Vertex A</param> /// <param name="side">The side</param> /// <returns>Slab coordinate value</returns> public float GetSlabCoord(Vector3 va, BoundarySide side) { if (side == BoundarySide.PlusX || side == BoundarySide.MinusX) { return(va.X); } else if (side == BoundarySide.PlusZ || side == BoundarySide.MinusZ) { return(va.Z); } return(0); }
/// <summary> /// Gets the neighboring tile at that position /// </summary> /// <param name="x">The x-coordinate</param> /// <param name="y">The y-coordinate</param> /// <param name="side">The side value</param> /// <param name="tiles">An array of MeshTiles</param> /// <returns>The number of tiles satisfying the condition</returns> public IEnumerable <MeshTile> GetNeighborTilesAt(int x, int y, BoundarySide side) { int nx = x, ny = y; switch (side) { case BoundarySide.PlusX: nx++; break; case BoundarySide.PlusXPlusZ: nx++; ny++; break; case BoundarySide.PlusZ: ny++; break; case BoundarySide.MinusXPlusZ: nx--; ny++; break; case BoundarySide.MinusX: nx--; break; case BoundarySide.MinusXMinusZ: nx--; ny--; break; case BoundarySide.MinusZ: ny--; break; case BoundarySide.PlusXMinusZ: nx++; ny--; break; } return(GetTilesAt(nx, ny)); }
/// <summary> /// Gets the neighboring tile at that position /// </summary> /// <param name="x">The x-coordinate</param> /// <param name="y">The y-coordinate</param> /// <param name="side">The side value</param> /// <param name="tiles">An array of MeshTiles</param> /// <returns>The number of tiles satisfying the condition</returns> public int GetNeighbourTilesAt(int x, int y, BoundarySide side, MeshTile[] tiles) { int nx = x, ny = y; switch (side) { case BoundarySide.PlusX: nx++; break; case BoundarySide.PlusXPlusZ: nx++; ny++; break; case BoundarySide.PlusZ: ny++; break; case BoundarySide.MinusXPlusZ: nx--; ny++; break; case BoundarySide.MinusX: nx--; break; case BoundarySide.MinusXMinusZ: nx--; ny--; break; case BoundarySide.MinusZ: ny--; break; case BoundarySide.PlusXMinusZ: nx++; ny--; break; } return(GetTilesAt(nx, ny, tiles)); }
/// <summary> /// Search for neighbor polygons in the tile. /// </summary> /// <param name="va">Vertex A</param> /// <param name="vb">Vertex B</param> /// <param name="tile">Current tile</param> /// <param name="side">Polygon edge</param> /// <param name="con">Resulting Connection polygon</param> /// <param name="conarea">Resulting Connection area</param> public void FindConnectingPolys(Vector3 va, Vector3 vb, MeshTile tile, BoundarySide side, List<int> con, List<float> conarea) { if (tile == null) return; Vector2 amin = Vector2.Zero; Vector2 amax = Vector2.Zero; CalcSlabEndPoints(va, vb, amin, amax, side); float apos = GetSlabCoord(va, side); //Remove links pointing to 'side' and compact the links array Vector2 bmin = Vector2.Zero; Vector2 bmax = Vector2.Zero; int polyBase = GetPolyRefBase(tile); //Iterate through all the tile's polygons for (int i = 0; i < tile.Header.PolyCount; i++) { int numPolyVerts = tile.Polys[i].VertCount; //Iterate through all the vertices for (int j = 0; j < numPolyVerts; j++) { //Skip edges which do not point to the right side if (tile.Polys[i].Neis[j] != (Link.External | (int)side)) continue; //Grab two adjacent vertices Vector3 vc = tile.Verts[tile.Polys[i].Verts[j]]; Vector3 vd = tile.Verts[tile.Polys[i].Verts[(j + 1) % numPolyVerts]]; float bpos = GetSlabCoord(vc, side); //Segments are not close enough if (Math.Abs(apos - bpos) > 0.01f) continue; //Check if the segments touch CalcSlabEndPoints(vc, vd, bmin, bmax, side); //Skip if slabs don't overlap if (!OverlapSlabs(amin, amax, bmin, bmax, 0.01f, tile.Header.WalkableClimb)) continue; //Add return value if (con.Count < con.Capacity) { conarea.Add(Math.Max(amin.X, bmin.X)); conarea.Add(Math.Min(amax.X, bmax.X)); con.Add(GetReference(polyBase, i)); } break; } } }
/// <summary> /// Connect polygons from two different tiles. /// </summary> /// <param name="tile">Current Tile</param> /// <param name="target">Target Tile</param> /// <param name="side">Polygon edge</param> public void ConnectExtLinks(ref MeshTile tile, ref MeshTile target, BoundarySide side) { if (tile == null) return; //Connect border links for (int i = 0; i < tile.Header.PolyCount; i++) { int numPolyVerts = tile.Polys[i].VertCount; for (int j = 0; j < numPolyVerts; j++) { //Skip non-portal edges if ((tile.Polys[i].Neis[j] & Link.External) == 0) continue; BoundarySide dir = (BoundarySide)(tile.Polys[i].Neis[j] & 0xff); if (side != BoundarySide.Internal && dir != side) continue; //Create new links Vector3 va = tile.Verts[tile.Polys[i].Verts[j]]; Vector3 vb = tile.Verts[tile.Polys[i].Verts[(j + 1) % numPolyVerts]]; List<int> nei = new List<int>(4); List<float> neia = new List<float>(4 * 2); FindConnectingPolys(va, vb, target, dir.GetOpposite(), nei, neia); //Iterate through neighbors for (int k = 0; k < nei.Count; k++) { //Allocate a new link if possible int idx = AllocLink(tile); if (IsLinkAllocated(idx)) { tile.Links[idx].Reference = nei[k]; tile.Links[idx].Edge = j; tile.Links[idx].Side = dir; tile.Links[idx].Next = tile.Polys[i].FirstLink; tile.Polys[i].FirstLink = idx; //Compress portal limits to a value if (dir == BoundarySide.PlusX || dir == BoundarySide.MinusX) { float tmin = (neia[k * 2 + 0] - va.Z) / (vb.Z - va.Z); float tmax = (neia[k * 2 + 1] - va.Z) / (vb.Z - va.Z); if (tmin > tmax) { float temp = tmin; tmin = tmax; tmax = temp; } tile.Links[idx].BMin = (int)(MathHelper.Clamp(tmin, 0.0f, 1.0f) * 255.0f); tile.Links[idx].BMax = (int)(MathHelper.Clamp(tmax, 0.0f, 1.0f) * 255.0f); } else if (dir == BoundarySide.PlusZ || dir == BoundarySide.MinusZ) { float tmin = (neia[k * 2 + 0] - va.X) / (vb.X - va.X); float tmax = (neia[k * 2 + 1] - va.X) / (vb.X - va.X); if (tmin > tmax) { float temp = tmin; tmin = tmax; tmax = temp; } tile.Links[idx].BMin = (int)(MathHelper.Clamp(tmin, 0.0f, 1.0f) * 255.0f); tile.Links[idx].BMax = (int)(MathHelper.Clamp(tmax, 0.0f, 1.0f) * 255.0f); } } } } } }
/// <summary> /// Connect Off-Mesh links between polygons from two different tiles. /// </summary> /// <param name="tile">Current Tile</param> /// <param name="target">Target Tile</param> /// <param name="side">Polygon edge</param> public void ConnectExtOffMeshLinks(ref MeshTile tile, ref MeshTile target, BoundarySide side) { if (tile == null) return; //Connect off-mesh links, specifically links which land from target tile to this tile BoundarySide oppositeSide = side.GetOpposite(); //Iterate through all the off-mesh connections of target tile for (int i = 0; i < target.Header.OffMeshConCount; i++) { OffMeshConnection targetCon = target.OffMeshConnections[i]; if (targetCon.Side != oppositeSide) continue; Poly targetPoly = target.Polys[targetCon.Poly]; //Skip off-mesh connections which start location could not be connected at all if (!IsLinkAllocated(targetPoly.FirstLink)) continue; Vector3 extents = new Vector3(targetCon.Radius, target.Header.WalkableClimb, targetCon.Radius); //Find polygon to connect to Vector3 p = targetCon.Pos1; Vector3 nearestPt = new Vector3(); int reference = FindNearestPolyInTile(tile, p, extents, ref nearestPt); if (reference == 0) continue; //Further checks if ((nearestPt.X - p.X) * (nearestPt.X - p.X) + (nearestPt.Z - p.Z) * (nearestPt.Z - p.Z) > (targetCon.Radius * targetCon.Radius)) continue; //Make sure the location is on the current mesh target.Verts[targetPoly.Verts[1]] = nearestPt; //Link off-mesh connection to target poly int idx = AllocLink(target); if (IsLinkAllocated(idx)) { target.Links[idx].Reference = reference; target.Links[idx].Edge = i; target.Links[idx].Side = oppositeSide; target.Links[idx].BMin = target.Links[idx].BMax = 0; //add to linked list target.Links[idx].Next = target.Polys[i].FirstLink; target.Polys[i].FirstLink = idx; } //link target poly to off-mesh connection if ((targetCon.Flags & OffMeshConnectionFlags.Bidirectional) != 0) { int tidx = AllocLink(tile); if (IsLinkAllocated(tidx)) { int landPolyIdx = DecodePolyIdPoly(reference); tile.Links[tidx].Reference = GetReference(GetPolyRefBase(target), targetCon.Poly); tile.Links[tidx].Edge = 0xff; tile.Links[tidx].Side = side; tile.Links[tidx].BMin = tile.Links[tidx].BMax = 0; //add to linked list tile.Links[tidx].Next = tile.Polys[landPolyIdx].FirstLink; tile.Polys[landPolyIdx].FirstLink = tidx; } } } }
/// <summary> /// Initializes a new instance of the <see cref="NavMeshBuilder" /> class. /// Add all the PolyMesh and PolyMeshDetail attributes to the Navigation Mesh. /// Then, add Off-Mesh connection support. /// </summary> /// <param name="polyMesh">The PolyMesh</param> /// <param name="polyMeshDetail">The PolyMeshDetail</param> /// <param name="offMeshCons">Offmesh connection data</param> /// <param name="settings">The settings used to build.</param> public NavMeshBuilder(PolyMesh polyMesh, PolyMeshDetail polyMeshDetail, OffMeshConnection[] offMeshCons, NavMeshGenerationSettings settings) { if (settings.VertsPerPoly > PathfindingCommon.VERTS_PER_POLYGON) throw new InvalidOperationException("The number of vertices per polygon is above SharpNav's limit"); if (polyMesh.VertCount == 0) throw new InvalidOperationException("The provided PolyMesh has no vertices."); if (polyMesh.PolyCount == 0) throw new InvalidOperationException("The provided PolyMesh has not polys."); int nvp = settings.VertsPerPoly; //classify off-mesh connection points BoundarySide[] offMeshSides = new BoundarySide[offMeshCons.Length * 2]; int storedOffMeshConCount = 0; int offMeshConLinkCount = 0; if (offMeshCons.Length > 0) { //find height bounds float hmin = float.MaxValue; float hmax = -float.MaxValue; if (polyMeshDetail != null) { for (int i = 0; i < polyMeshDetail.VertCount; i++) { float h = polyMeshDetail.Verts[i].Y; hmin = Math.Min(hmin, h); hmax = Math.Max(hmax, h); } } else { for (int i = 0; i < polyMesh.VertCount; i++) { PolyVertex iv = polyMesh.Verts[i]; float h = polyMesh.Bounds.Min.Y + iv.Y * settings.CellHeight; hmin = Math.Min(hmin, h); hmax = Math.Max(hmax, h); } } hmin -= settings.MaxClimb; hmax += settings.MaxClimb; BBox3 bounds = polyMesh.Bounds; bounds.Min.Y = hmin; bounds.Max.Y = hmax; for (int i = 0; i < offMeshCons.Length; i++) { Vector3 p0 = offMeshCons[i].Pos0; Vector3 p1 = offMeshCons[i].Pos1; offMeshSides[i * 2 + 0] = BoundarySideExtensions.FromPoint(p0, bounds); offMeshSides[i * 2 + 1] = BoundarySideExtensions.FromPoint(p1, bounds); //off-mesh start position isn't touching mesh if (offMeshSides[i * 2 + 0] == BoundarySide.Internal) { if (p0.Y < bounds.Min.Y || p0.Y > bounds.Max.Y) offMeshSides[i * 2 + 0] = 0; } //count number of links to allocate if (offMeshSides[i * 2 + 0] == BoundarySide.Internal) offMeshConLinkCount++; if (offMeshSides[i * 2 + 1] == BoundarySide.Internal) offMeshConLinkCount++; if (offMeshSides[i * 2 + 0] == BoundarySide.Internal) storedOffMeshConCount++; } } //off-mesh connections stored as polygons, adjust values int totPolyCount = polyMesh.PolyCount + storedOffMeshConCount; int totVertCount = polyMesh.VertCount + storedOffMeshConCount * 2; //find portal edges int edgeCount = 0; int portalCount = 0; for (int i = 0; i < polyMesh.PolyCount; i++) { PolyMesh.Polygon p = polyMesh.Polys[i]; for (int j = 0; j < nvp; j++) { if (p.Vertices[j] == PolyMesh.NullId) break; edgeCount++; if (PolyMesh.IsBoundaryEdge(p.NeighborEdges[j])) { int dir = p.NeighborEdges[j] % 16; if (dir != 15) portalCount++; } } } int maxLinkCount = edgeCount + portalCount * 2 + offMeshConLinkCount * 2; //find unique detail vertices int uniqueDetailVertCount = 0; int detailTriCount = 0; if (polyMeshDetail != null) { detailTriCount = polyMeshDetail.TrisCount; for (int i = 0; i < polyMesh.PolyCount; i++) { int numDetailVerts = polyMeshDetail.Meshes[i].VertexCount; int numPolyVerts = polyMesh.Polys[i].VertexCount; uniqueDetailVertCount += numDetailVerts - numPolyVerts; } } else { uniqueDetailVertCount = 0; detailTriCount = 0; for (int i = 0; i < polyMesh.PolyCount; i++) { int numPolyVerts = polyMesh.Polys[i].VertexCount; uniqueDetailVertCount += numPolyVerts - 2; } } //allocate data header = new PathfindingCommon.NavMeshInfo(); navVerts = new Vector3[totVertCount]; navPolys = new Poly[totPolyCount]; navDMeshes = new PolyMeshDetail.MeshData[polyMesh.PolyCount]; navDVerts = new Vector3[uniqueDetailVertCount]; navDTris = new PolyMeshDetail.TriangleData[detailTriCount]; offMeshConnections = new OffMeshConnection[storedOffMeshConCount]; //store header //HACK TiledNavMesh should figure out the X/Y/layer instead of the user maybe? header.X = 0; header.Y = 0; header.Layer = 0; header.PolyCount = totPolyCount; header.VertCount = totVertCount; header.MaxLinkCount = maxLinkCount; header.Bounds = polyMesh.Bounds; header.DetailMeshCount = polyMesh.PolyCount; header.DetailVertCount = uniqueDetailVertCount; header.DetailTriCount = detailTriCount; header.OffMeshBase = polyMesh.PolyCount; header.WalkableHeight = settings.AgentHeight; header.WalkableRadius = settings.AgentRadius; header.WalkableClimb = settings.MaxClimb; header.OffMeshConCount = storedOffMeshConCount; header.BvNodeCount = settings.BuildBoundingVolumeTree ? polyMesh.PolyCount * 2 : 0; header.BvQuantFactor = 1f / settings.CellSize; int offMeshVertsBase = polyMesh.VertCount; int offMeshPolyBase = polyMesh.PolyCount; //store vertices for (int i = 0; i < polyMesh.VertCount; i++) { PolyVertex iv = polyMesh.Verts[i]; navVerts[i].X = polyMesh.Bounds.Min.X + iv.X * settings.CellSize; navVerts[i].Y = polyMesh.Bounds.Min.Y + iv.Y * settings.CellHeight; navVerts[i].Z = polyMesh.Bounds.Min.Z + iv.Z * settings.CellSize; } //off-mesh link vertices int n = 0; for (int i = 0; i < offMeshCons.Length; i++) { //only store connections which start from this tile if (offMeshSides[i * 2 + 0] == BoundarySide.Internal) { navVerts[offMeshVertsBase + (n * 2 + 0)] = offMeshCons[i].Pos0; navVerts[offMeshVertsBase + (n * 2 + 1)] = offMeshCons[i].Pos1; n++; } } //store polygons for (int i = 0; i < polyMesh.PolyCount; i++) { navPolys[i] = new Poly(); navPolys[i].VertCount = 0; navPolys[i].Tag = polyMesh.Polys[i].Tag; navPolys[i].Area = polyMesh.Polys[i].Area; navPolys[i].PolyType = PolygonType.Ground; navPolys[i].Verts = new int[nvp]; navPolys[i].Neis = new int[nvp]; for (int j = 0; j < nvp; j++) { if (polyMesh.Polys[i].Vertices[j] == PolyMesh.NullId) break; navPolys[i].Verts[j] = polyMesh.Polys[i].Vertices[j]; if (PolyMesh.IsBoundaryEdge(polyMesh.Polys[i].NeighborEdges[j])) { //border or portal edge int dir = polyMesh.Polys[i].NeighborEdges[j] % 16; if (dir == 0xf) //border navPolys[i].Neis[j] = 0; else if (dir == 0) //portal x- navPolys[i].Neis[j] = Link.External | 4; else if (dir == 1) //portal z+ navPolys[i].Neis[j] = Link.External | 2; else if (dir == 2) //portal x+ navPolys[i].Neis[j] = Link.External | 0; else if (dir == 3) //portal z- navPolys[i].Neis[j] = Link.External | 6; } else { //normal connection navPolys[i].Neis[j] = polyMesh.Polys[i].NeighborEdges[j] + 1; } navPolys[i].VertCount++; } } //off-mesh connection vertices n = 0; for (int i = 0; i < offMeshCons.Length; i++) { //only store connections which start from this tile if (offMeshSides[i * 2 + 0] == BoundarySide.Internal) { navPolys[offMeshPolyBase + n].VertCount = 2; navPolys[offMeshPolyBase + n].Verts = new int[nvp]; navPolys[offMeshPolyBase + n].Verts[0] = offMeshVertsBase + (n * 2 + 0); navPolys[offMeshPolyBase + n].Verts[1] = offMeshVertsBase + (n * 2 + 1); navPolys[offMeshPolyBase + n].Tag = offMeshCons[i].Flags; navPolys[offMeshPolyBase + n].Area = polyMesh.Polys[offMeshCons[i].Poly].Area; //HACK is this correct? navPolys[offMeshPolyBase + n].PolyType = PolygonType.OffMeshConnection; n++; } } //store detail meshes and vertices if (polyMeshDetail != null) { int vbase = 0; List<Vector3> storedDetailVerts = new List<Vector3>(); for (int i = 0; i < polyMesh.PolyCount; i++) { int vb = polyMeshDetail.Meshes[i].VertexIndex; int numDetailVerts = polyMeshDetail.Meshes[i].VertexCount; int numPolyVerts = navPolys[i].VertCount; navDMeshes[i].VertexIndex = vbase; navDMeshes[i].VertexCount = numDetailVerts - numPolyVerts; navDMeshes[i].TriangleIndex = polyMeshDetail.Meshes[i].TriangleIndex; navDMeshes[i].TriangleCount = polyMeshDetail.Meshes[i].TriangleCount; //Copy detail vertices //first 'nv' verts are equal to nav poly verts //the rest are detail verts for (int j = 0; j < navDMeshes[i].VertexCount; j++) { storedDetailVerts.Add(polyMeshDetail.Verts[vb + numPolyVerts + j]); } vbase += numDetailVerts - numPolyVerts; } navDVerts = storedDetailVerts.ToArray(); //store triangles for (int j = 0; j < polyMeshDetail.TrisCount; j++) navDTris[j] = polyMeshDetail.Tris[j]; } else { //create dummy detail mesh by triangulating polys int tbase = 0; for (int i = 0; i < polyMesh.PolyCount; i++) { int numPolyVerts = navPolys[i].VertCount; navDMeshes[i].VertexIndex = 0; navDMeshes[i].VertexCount = 0; navDMeshes[i].TriangleIndex = tbase; navDMeshes[i].TriangleCount = numPolyVerts - 2; //triangulate polygon for (int j = 2; j < numPolyVerts; j++) { navDTris[tbase].VertexHash0 = 0; navDTris[tbase].VertexHash1 = j - 1; navDTris[tbase].VertexHash2 = j; //bit for each edge that belongs to the poly boundary navDTris[tbase].Flags = 1 << 2; if (j == 2) navDTris[tbase].Flags |= 1 << 0; if (j == numPolyVerts - 1) navDTris[tbase].Flags |= 1 << 4; tbase++; } } } //store and create BV tree if (settings.BuildBoundingVolumeTree) { //build tree navBvTree = new BVTree(polyMesh.Verts, polyMesh.Polys, nvp, settings.CellSize, settings.CellHeight); } //store off-mesh connections n = 0; for (int i = 0; i < offMeshConnections.Length; i++) { //only store connections which start from this tile if (offMeshSides[i * 2 + 0] == BoundarySide.Internal) { offMeshConnections[n].Poly = offMeshPolyBase + n; //copy connection end points offMeshConnections[n].Pos0 = offMeshCons[i].Pos0; offMeshConnections[n].Pos1 = offMeshCons[i].Pos1; offMeshConnections[n].Radius = offMeshCons[i].Radius; offMeshConnections[n].Flags = offMeshCons[i].Flags; offMeshConnections[n].Side = offMeshSides[i * 2 + 1]; offMeshConnections[n].Tag = offMeshCons[i].Tag; n++; } } }
/// <summary> /// Find the slab endpoints based off of the 'side' value. /// </summary> /// <param name="va">Vertex A</param> /// <param name="vb">Vertex B</param> /// <param name="bmin">Minimum bounds</param> /// <param name="bmax">Maximum bounds</param> /// <param name="side">The side</param> public void CalcSlabEndPoints(Vector3 va, Vector3 vb, Vector2 bmin, Vector2 bmax, BoundarySide side) { if (side == BoundarySide.PlusX || side == BoundarySide.MinusX) { if (va.Z < vb.Z) { bmin.X = va.Z; bmin.Y = va.Y; bmax.X = vb.Z; bmax.Y = vb.Y; } else { bmin.X = vb.Z; bmin.Y = vb.Y; bmax.X = va.Z; bmax.Y = va.Y; } } else if (side == BoundarySide.PlusZ || side == BoundarySide.MinusZ) { if (va.X < vb.X) { bmin.X = va.X; bmin.Y = va.Y; bmax.X = vb.X; bmax.Y = vb.Y; } else { bmin.X = vb.X; bmin.Y = vb.Y; bmax.X = va.X; bmax.Y = va.Y; } } }
public IEnumerable<MeshTile> GetNeighborTilesAt(Vector2i location, BoundarySide side) { return GetNeighborTilesAt(location.X, location.Y, side); }
public IEnumerable <MeshTile> GetNeighborTilesAt(Vector2i location, BoundarySide side) { return(GetNeighborTilesAt(location.X, location.Y, side)); }
/// <summary> /// Connect Off-Mesh links between polygons from two different tiles. /// </summary> /// <param name="tile">Current Tile</param> /// <param name="target">Target Tile</param> /// <param name="side">Polygon edge</param> public void ConnectExtOffMeshLinks(ref MeshTile tile, ref MeshTile target, BoundarySide side) { if (tile == null) { return; } //Connect off-mesh links, specifically links which land from target tile to this tile BoundarySide oppositeSide = side.GetOpposite(); //Iterate through all the off-mesh connections of target tile for (int i = 0; i < target.Header.OffMeshConCount; i++) { OffMeshConnection targetCon = target.OffMeshConnections[i]; if (targetCon.Side != oppositeSide) { continue; } Poly targetPoly = target.Polys[targetCon.Poly]; //Skip off-mesh connections which start location could not be connected at all if (!IsLinkAllocated(targetPoly.FirstLink)) { continue; } Vector3 extents = new Vector3(targetCon.Radius, target.Header.WalkableClimb, targetCon.Radius); //Find polygon to connect to Vector3 p = targetCon.Pos1; Vector3 nearestPt = new Vector3(); PolyId reference = FindNearestPolyInTile(tile, p, extents, ref nearestPt); if (reference == PolyId.Null) { continue; } //Further checks if ((nearestPt.X - p.X) * (nearestPt.X - p.X) + (nearestPt.Z - p.Z) * (nearestPt.Z - p.Z) > (targetCon.Radius * targetCon.Radius)) { continue; } //Make sure the location is on the current mesh target.Verts[targetPoly.Verts[1]] = nearestPt; //Link off-mesh connection to target poly int idx = AllocLink(target); if (IsLinkAllocated(idx)) { target.Links[idx].Reference = reference; target.Links[idx].Edge = i; target.Links[idx].Side = oppositeSide; target.Links[idx].BMin = target.Links[idx].BMax = 0; //add to linked list target.Links[idx].Next = target.Polys[i].FirstLink; target.Polys[i].FirstLink = idx; } //link target poly to off-mesh connection if ((targetCon.Flags & OffMeshConnectionFlags.Bidirectional) != 0) { int tidx = AllocLink(tile); if (IsLinkAllocated(tidx)) { int landPolyIdx = reference.DecodePolyIndex(polyBits); PolyId id; id = GetPolyRefBase(target); PolyId.SetPolyIndex(ref id, targetCon.Poly, out id); tile.Links[tidx].Reference = id; tile.Links[tidx].Edge = 0xff; tile.Links[tidx].Side = side; tile.Links[tidx].BMin = tile.Links[tidx].BMax = 0; //add to linked list tile.Links[tidx].Next = tile.Polys[landPolyIdx].FirstLink; tile.Polys[landPolyIdx].FirstLink = tidx; } } } }
/// <summary> /// Initializes a new instance of the <see cref="NavMeshBuilder" /> class. /// Add all the PolyMesh and PolyMeshDetail attributes to the Navigation Mesh. /// Then, add Off-Mesh connection support. /// </summary> /// <param name="polyMesh">The PolyMesh</param> /// <param name="polyMeshDetail">The PolyMeshDetail</param> /// <param name="offMeshCons">Offmesh connection data</param> /// <param name="settings">The settings used to build.</param> public NavMeshBuilder(PolyMesh polyMesh, PolyMeshDetail polyMeshDetail, OffMeshConnection[] offMeshCons, NavMeshGenerationSettings settings) { if (settings.VertsPerPoly > PathfindingCommon.VERTS_PER_POLYGON) { throw new InvalidOperationException("The number of vertices per polygon is above SharpNav's limit"); } if (polyMesh.VertCount == 0) { throw new InvalidOperationException("The provided PolyMesh has no vertices."); } if (polyMesh.PolyCount == 0) { throw new InvalidOperationException("The provided PolyMesh has not polys."); } int nvp = settings.VertsPerPoly; //classify off-mesh connection points BoundarySide[] offMeshSides = new BoundarySide[offMeshCons.Length * 2]; int storedOffMeshConCount = 0; int offMeshConLinkCount = 0; if (offMeshCons.Length > 0) { //find height bounds float hmin = float.MaxValue; float hmax = -float.MaxValue; if (polyMeshDetail != null) { for (int i = 0; i < polyMeshDetail.VertCount; i++) { float h = polyMeshDetail.Verts[i].Y; hmin = Math.Min(hmin, h); hmax = Math.Max(hmax, h); } } else { for (int i = 0; i < polyMesh.VertCount; i++) { PolyVertex iv = polyMesh.Verts[i]; float h = polyMesh.Bounds.Min.Y + iv.Y * settings.CellHeight; hmin = Math.Min(hmin, h); hmax = Math.Max(hmax, h); } } hmin -= settings.MaxClimb; hmax += settings.MaxClimb; BBox3 bounds = polyMesh.Bounds; bounds.Min.Y = hmin; bounds.Max.Y = hmax; for (int i = 0; i < offMeshCons.Length; i++) { Vector3 p0 = offMeshCons[i].Pos0; Vector3 p1 = offMeshCons[i].Pos1; offMeshSides[i * 2 + 0] = BoundarySideExtensions.FromPoint(p0, bounds); offMeshSides[i * 2 + 1] = BoundarySideExtensions.FromPoint(p1, bounds); //off-mesh start position isn't touching mesh if (offMeshSides[i * 2 + 0] == BoundarySide.Internal) { if (p0.Y < bounds.Min.Y || p0.Y > bounds.Max.Y) { offMeshSides[i * 2 + 0] = 0; } } //count number of links to allocate if (offMeshSides[i * 2 + 0] == BoundarySide.Internal) { offMeshConLinkCount++; } if (offMeshSides[i * 2 + 1] == BoundarySide.Internal) { offMeshConLinkCount++; } if (offMeshSides[i * 2 + 0] == BoundarySide.Internal) { storedOffMeshConCount++; } } } //off-mesh connections stored as polygons, adjust values int totPolyCount = polyMesh.PolyCount + storedOffMeshConCount; int totVertCount = polyMesh.VertCount + storedOffMeshConCount * 2; //find portal edges int edgeCount = 0; int portalCount = 0; for (int i = 0; i < polyMesh.PolyCount; i++) { PolyMesh.Polygon p = polyMesh.Polys[i]; for (int j = 0; j < nvp; j++) { if (p.Vertices[j] == PolyMesh.NullId) { break; } edgeCount++; if (PolyMesh.IsBoundaryEdge(p.NeighborEdges[j])) { int dir = p.NeighborEdges[j] % 16; if (dir != 15) { portalCount++; } } } } int maxLinkCount = edgeCount + portalCount * 2 + offMeshConLinkCount * 2; //find unique detail vertices int uniqueDetailVertCount = 0; int detailTriCount = 0; if (polyMeshDetail != null) { detailTriCount = polyMeshDetail.TrisCount; for (int i = 0; i < polyMesh.PolyCount; i++) { int numDetailVerts = polyMeshDetail.Meshes[i].VertexCount; int numPolyVerts = polyMesh.Polys[i].VertexCount; uniqueDetailVertCount += numDetailVerts - numPolyVerts; } } else { uniqueDetailVertCount = 0; detailTriCount = 0; for (int i = 0; i < polyMesh.PolyCount; i++) { int numPolyVerts = polyMesh.Polys[i].VertexCount; uniqueDetailVertCount += numPolyVerts - 2; } } //allocate data header = new PathfindingCommon.NavMeshInfo(); navVerts = new Vector3[totVertCount]; navPolys = new Poly[totPolyCount]; navDMeshes = new PolyMeshDetail.MeshData[polyMesh.PolyCount]; navDVerts = new Vector3[uniqueDetailVertCount]; navDTris = new PolyMeshDetail.TriangleData[detailTriCount]; offMeshConnections = new OffMeshConnection[storedOffMeshConCount]; //store header //HACK TiledNavMesh should figure out the X/Y/layer instead of the user maybe? header.X = 0; header.Y = 0; header.Layer = 0; header.PolyCount = totPolyCount; header.VertCount = totVertCount; header.MaxLinkCount = maxLinkCount; header.Bounds = polyMesh.Bounds; header.DetailMeshCount = polyMesh.PolyCount; header.DetailVertCount = uniqueDetailVertCount; header.DetailTriCount = detailTriCount; header.OffMeshBase = polyMesh.PolyCount; header.WalkableHeight = settings.AgentHeight; header.WalkableRadius = settings.AgentRadius; header.WalkableClimb = settings.MaxClimb; header.OffMeshConCount = storedOffMeshConCount; header.BvNodeCount = settings.BuildBoundingVolumeTree ? polyMesh.PolyCount * 2 : 0; header.BvQuantFactor = 1f / settings.CellSize; int offMeshVertsBase = polyMesh.VertCount; int offMeshPolyBase = polyMesh.PolyCount; //store vertices for (int i = 0; i < polyMesh.VertCount; i++) { PolyVertex iv = polyMesh.Verts[i]; navVerts[i].X = polyMesh.Bounds.Min.X + iv.X * settings.CellSize; navVerts[i].Y = polyMesh.Bounds.Min.Y + iv.Y * settings.CellHeight; navVerts[i].Z = polyMesh.Bounds.Min.Z + iv.Z * settings.CellSize; } //off-mesh link vertices int n = 0; for (int i = 0; i < offMeshCons.Length; i++) { //only store connections which start from this tile if (offMeshSides[i * 2 + 0] == BoundarySide.Internal) { navVerts[offMeshVertsBase + (n * 2 + 0)] = offMeshCons[i].Pos0; navVerts[offMeshVertsBase + (n * 2 + 1)] = offMeshCons[i].Pos1; n++; } } //store polygons for (int i = 0; i < polyMesh.PolyCount; i++) { navPolys[i] = new Poly(); navPolys[i].VertCount = 0; navPolys[i].Tag = polyMesh.Polys[i].Tag; navPolys[i].Area = polyMesh.Polys[i].Area; navPolys[i].PolyType = PolygonType.Ground; navPolys[i].Verts = new int[nvp]; navPolys[i].Neis = new int[nvp]; for (int j = 0; j < nvp; j++) { if (polyMesh.Polys[i].Vertices[j] == PolyMesh.NullId) { break; } navPolys[i].Verts[j] = polyMesh.Polys[i].Vertices[j]; if (PolyMesh.IsBoundaryEdge(polyMesh.Polys[i].NeighborEdges[j])) { //border or portal edge int dir = polyMesh.Polys[i].NeighborEdges[j] % 16; if (dir == 0xf) //border { navPolys[i].Neis[j] = 0; } else if (dir == 0) //portal x- { navPolys[i].Neis[j] = Link.External | 4; } else if (dir == 1) //portal z+ { navPolys[i].Neis[j] = Link.External | 2; } else if (dir == 2) //portal x+ { navPolys[i].Neis[j] = Link.External | 0; } else if (dir == 3) //portal z- { navPolys[i].Neis[j] = Link.External | 6; } } else { //normal connection navPolys[i].Neis[j] = polyMesh.Polys[i].NeighborEdges[j] + 1; } navPolys[i].VertCount++; } } //off-mesh connection vertices n = 0; for (int i = 0; i < offMeshCons.Length; i++) { //only store connections which start from this tile if (offMeshSides[i * 2 + 0] == BoundarySide.Internal) { navPolys[offMeshPolyBase + n].VertCount = 2; navPolys[offMeshPolyBase + n].Verts = new int[nvp]; navPolys[offMeshPolyBase + n].Verts[0] = offMeshVertsBase + (n * 2 + 0); navPolys[offMeshPolyBase + n].Verts[1] = offMeshVertsBase + (n * 2 + 1); navPolys[offMeshPolyBase + n].Tag = offMeshCons[i].Flags; navPolys[offMeshPolyBase + n].Area = polyMesh.Polys[offMeshCons[i].Poly].Area; //HACK is this correct? navPolys[offMeshPolyBase + n].PolyType = PolygonType.OffMeshConnection; n++; } } //store detail meshes and vertices if (polyMeshDetail != null) { int vbase = 0; List <Vector3> storedDetailVerts = new List <Vector3>(); for (int i = 0; i < polyMesh.PolyCount; i++) { int vb = polyMeshDetail.Meshes[i].VertexIndex; int numDetailVerts = polyMeshDetail.Meshes[i].VertexCount; int numPolyVerts = navPolys[i].VertCount; navDMeshes[i].VertexIndex = vbase; navDMeshes[i].VertexCount = numDetailVerts - numPolyVerts; navDMeshes[i].TriangleIndex = polyMeshDetail.Meshes[i].TriangleIndex; navDMeshes[i].TriangleCount = polyMeshDetail.Meshes[i].TriangleCount; //Copy detail vertices //first 'nv' verts are equal to nav poly verts //the rest are detail verts for (int j = 0; j < navDMeshes[i].VertexCount; j++) { storedDetailVerts.Add(polyMeshDetail.Verts[vb + numPolyVerts + j]); } vbase += numDetailVerts - numPolyVerts; } navDVerts = storedDetailVerts.ToArray(); //store triangles for (int j = 0; j < polyMeshDetail.TrisCount; j++) { navDTris[j] = polyMeshDetail.Tris[j]; } } else { //create dummy detail mesh by triangulating polys int tbase = 0; for (int i = 0; i < polyMesh.PolyCount; i++) { int numPolyVerts = navPolys[i].VertCount; navDMeshes[i].VertexIndex = 0; navDMeshes[i].VertexCount = 0; navDMeshes[i].TriangleIndex = tbase; navDMeshes[i].TriangleCount = numPolyVerts - 2; //triangulate polygon for (int j = 2; j < numPolyVerts; j++) { navDTris[tbase].VertexHash0 = 0; navDTris[tbase].VertexHash1 = j - 1; navDTris[tbase].VertexHash2 = j; //bit for each edge that belongs to the poly boundary navDTris[tbase].Flags = 1 << 2; if (j == 2) { navDTris[tbase].Flags |= 1 << 0; } if (j == numPolyVerts - 1) { navDTris[tbase].Flags |= 1 << 4; } tbase++; } } } //store and create BV tree if (settings.BuildBoundingVolumeTree) { //build tree navBvTree = new BVTree(polyMesh.Verts, polyMesh.Polys, nvp, settings.CellSize, settings.CellHeight); } //store off-mesh connections n = 0; for (int i = 0; i < offMeshConnections.Length; i++) { //only store connections which start from this tile if (offMeshSides[i * 2 + 0] == BoundarySide.Internal) { offMeshConnections[n].Poly = offMeshPolyBase + n; //copy connection end points offMeshConnections[n].Pos0 = offMeshCons[i].Pos0; offMeshConnections[n].Pos1 = offMeshCons[i].Pos1; offMeshConnections[n].Radius = offMeshCons[i].Radius; offMeshConnections[n].Flags = offMeshCons[i].Flags; offMeshConnections[n].Side = offMeshSides[i * 2 + 1]; offMeshConnections[n].Tag = offMeshCons[i].Tag; n++; } } }
/// <summary> /// Gets the neighboring tile at that position /// </summary> /// <param name="x">The x-coordinate</param> /// <param name="y">The y-coordinate</param> /// <param name="side">The side value</param> /// <param name="tiles">An array of MeshTiles</param> /// <returns>The number of tiles satisfying the condition</returns> public int GetNeighbourTilesAt(int x, int y, BoundarySide side, MeshTile[] tiles) { int nx = x, ny = y; switch (side) { case BoundarySide.PlusX: nx++; break; case BoundarySide.PlusXPlusZ: nx++; ny++; break; case BoundarySide.PlusZ: ny++; break; case BoundarySide.MinusXPlusZ: nx--; ny++; break; case BoundarySide.MinusX: nx--; break; case BoundarySide.MinusXMinusZ: nx--; ny--; break; case BoundarySide.MinusZ: ny--; break; case BoundarySide.PlusXMinusZ: nx++; ny--; break; } return GetTilesAt(nx, ny, tiles); }
/// <summary> /// Gets the neighboring tile at that position /// </summary> /// <param name="x">The x-coordinate</param> /// <param name="y">The y-coordinate</param> /// <param name="side">The side value</param> /// <param name="tiles">An array of MeshTiles</param> /// <returns>The number of tiles satisfying the condition</returns> public IEnumerable<MeshTile> GetNeighborTilesAt(int x, int y, BoundarySide side) { int nx = x, ny = y; switch (side) { case BoundarySide.PlusX: nx++; break; case BoundarySide.PlusXPlusZ: nx++; ny++; break; case BoundarySide.PlusZ: ny++; break; case BoundarySide.MinusXPlusZ: nx--; ny++; break; case BoundarySide.MinusX: nx--; break; case BoundarySide.MinusXMinusZ: nx--; ny--; break; case BoundarySide.MinusZ: ny--; break; case BoundarySide.PlusXMinusZ: nx++; ny--; break; } return GetTilesAt(nx, ny); }
/// <summary> /// Connect polygons from two different tiles. /// </summary> /// <param name="target">Target Tile</param> /// <param name="side">Polygon edge</param> public void ConnectExtLinks(NavTile target, BoundarySide side) { //Connect border links for (int i = 0; i < PolyCount; i++) { int numPolyVerts = Polys[i].VertCount; for (int j = 0; j < numPolyVerts; j++) { //Skip non-portal edges if ((Polys[i].Neis[j] & Link.External) == 0) { continue; } BoundarySide dir = (BoundarySide)(Polys[i].Neis[j] & 0xff); if (side != BoundarySide.Internal && dir != side) { continue; } //Create new links Vector3 va = Verts[Polys[i].Verts[j]]; Vector3 vb = Verts[Polys[i].Verts[(j + 1) % numPolyVerts]]; List <NavPolyId> nei = new List <NavPolyId>(4); List <float> neia = new List <float>(4 * 2); target.FindConnectingPolys(va, vb, dir.GetOpposite(), nei, neia); //Iterate through neighbors for (int k = 0; k < nei.Count; k++) { Link link = new Link(); link.Reference = nei[k]; link.Edge = j; link.Side = dir; Polys[i].Links.Add(link); //Compress portal limits to a value if (dir == BoundarySide.PlusX || dir == BoundarySide.MinusX) { float tmin = (neia[k * 2 + 0] - va.Z) / (vb.Z - va.Z); float tmax = (neia[k * 2 + 1] - va.Z) / (vb.Z - va.Z); if (tmin > tmax) { float temp = tmin; tmin = tmax; tmax = temp; } link.BMin = (int)(MathHelper.Clamp(tmin, 0.0f, 1.0f) * 255.0f); link.BMax = (int)(MathHelper.Clamp(tmax, 0.0f, 1.0f) * 255.0f); } else if (dir == BoundarySide.PlusZ || dir == BoundarySide.MinusZ) { float tmin = (neia[k * 2 + 0] - va.X) / (vb.X - va.X); float tmax = (neia[k * 2 + 1] - va.X) / (vb.X - va.X); if (tmin > tmax) { float temp = tmin; tmin = tmax; tmax = temp; } link.BMin = (int)(MathHelper.Clamp(tmin, 0.0f, 1.0f) * 255.0f); link.BMax = (int)(MathHelper.Clamp(tmax, 0.0f, 1.0f) * 255.0f); } } } } }
/// <summary> /// Connect Off-Mesh links between polygons from two different tiles. /// </summary> /// <param name="target">Target Tile</param> /// <param name="side">Polygon edge</param> public void ConnectExtOffMeshLinks(NavTile target, BoundarySide side) { //Connect off-mesh links, specifically links which land from target tile to this tile BoundarySide oppositeSide = side.GetOpposite(); //Iterate through all the off-mesh connections of target tile for (int i = 0; i < target.OffMeshConnectionCount; i++) { OffMeshConnection targetCon = target.OffMeshConnections[i]; if (targetCon.Side != oppositeSide) { continue; } NavPoly targetPoly = target.Polys[targetCon.Poly]; //Skip off-mesh connections which start location could not be connected at all if (targetPoly.Links.Count == 0) { continue; } Vector3 extents = new Vector3(targetCon.Radius, target.WalkableClimb, targetCon.Radius); //Find polygon to connect to Vector3 p = targetCon.Pos1; Vector3 nearestPt = new Vector3(); NavPolyId reference = FindNearestPoly(p, extents, ref nearestPt); if (reference == NavPolyId.Null) { continue; } //Further checks if ((nearestPt.X - p.X) * (nearestPt.X - p.X) + (nearestPt.Z - p.Z) * (nearestPt.Z - p.Z) > (targetCon.Radius * targetCon.Radius)) { continue; } //Make sure the location is on the current mesh target.Verts[targetPoly.Verts[1]] = nearestPt; //Link off-mesh connection to target poly Link link = new Link(); link.Reference = reference; link.Edge = i; link.Side = oppositeSide; target.Polys[i].Links.Add(link); //link target poly to off-mesh connection if ((targetCon.Flags & OffMeshConnectionFlags.Bidirectional) != 0) { int landPolyIdx = idManager.DecodePolyIndex(ref reference); NavPolyId id; id = target.baseRef; idManager.SetPolyIndex(ref id, targetCon.Poly, out id); Link bidiLink = new Link(); bidiLink.Reference = id; bidiLink.Edge = 0xff; bidiLink.Side = side; Polys[landPolyIdx].Links.Add(bidiLink); } } }
/// <summary> /// Return the proper slab coordinate value depending on the 'side' value. /// </summary> /// <param name="va">Vertex A</param> /// <param name="side">The side</param> /// <returns>Slab coordinate value</returns> public float GetSlabCoord(Vector3 va, BoundarySide side) { if (side == BoundarySide.PlusX || side == BoundarySide.MinusX) return va.X; else if (side == BoundarySide.PlusZ || side == BoundarySide.MinusZ) return va.Z; return 0; }
/// <summary> /// Build a tile and link all the polygons togther, both internally and externally. /// Make sure to link off-mesh connections as well. /// </summary> /// <param name="data">Navigation Mesh data</param> /// <param name="lastRef">Last polygon reference</param> /// <param name="result">Last tile reference</param> public void AddTile(NavMeshBuilder data, PolyId lastRef, out PolyId result) { result = PolyId.Null; //make sure data is in right format PathfindingCommon.NavMeshInfo header = data.Header; //make sure location is free if (GetTileAt(header.X, header.Y, header.Layer) != null) { return; } //allocate a tile MeshTile tile = null; if (lastRef == PolyId.Null) { if (nextFree != null) { tile = nextFree; nextFree = tile.Next; tile.Next = null; } } else { //try to relocate tile to specific index with the same salt int tileIndex = lastRef.DecodeTileIndex(polyBits, tileBits); if (tileIndex >= maxTiles) { return; } //try to find specific tile id from free list MeshTile target = tiles[tileIndex]; MeshTile prev = null; tile = nextFree; while (tile != null && tile != target) { prev = tile; tile = tile.Next; } //couldn't find correct location if (tile != target) { return; } //remove from freelist if (prev == null) { nextFree = tile.Next; } else { prev.Next = tile.Next; } //restore salt tile.Salt = lastRef.DecodeSalt(polyBits, tileBits, saltBits); } //make sure we could allocate a tile if (tile == null) { return; } //insert tile into position LookUp Table (lut) int h = ComputeTileHash(header.X, header.Y, tileLookupTableMask); tile.Next = posLookup[h]; posLookup[h] = tile; if (header.BvNodeCount == 0) { tile.BVTree = null; } //patch header tile.Verts = data.NavVerts; tile.Polys = data.NavPolys; tile.DetailMeshes = data.NavDMeshes; tile.DetailVerts = data.NavDVerts; tile.DetailTris = data.NavDTris; tile.BVTree = data.NavBvTree; tile.OffMeshConnections = data.OffMeshCons; //build links freelist tile.LinksFreeList = 0; tile.Links = new Link[header.MaxLinkCount]; for (int i = 0; i < header.MaxLinkCount; i++) { tile.Links[i] = new Link(); } tile.Links[header.MaxLinkCount - 1].Next = Link.Null; for (int i = 0; i < header.MaxLinkCount - 1; i++) { tile.Links[i].Next = i + 1; } //init tile tile.Header = header; tile.Data = data; ConnectIntLinks(ref tile); BaseOffMeshLinks(ref tile); //create connections with neighbor tiles MeshTile[] neis = new MeshTile[32]; int nneis; //connect with layers in current tile nneis = GetTilesAt(header.X, header.Y, neis); for (int j = 0; j < nneis; j++) { if (neis[j] != tile) { ConnectExtLinks(ref tile, ref neis[j], BoundarySide.Internal); ConnectExtLinks(ref neis[j], ref tile, BoundarySide.Internal); } ConnectExtOffMeshLinks(ref tile, ref neis[j], BoundarySide.Internal); ConnectExtOffMeshLinks(ref neis[j], ref tile, BoundarySide.Internal); } //connect with neighbour tiles for (int i = 0; i < 8; i++) { BoundarySide b = (BoundarySide)i; BoundarySide bo = b.GetOpposite(); nneis = GetNeighbourTilesAt(header.X, header.Y, b, neis); for (int j = 0; j < nneis; j++) { ConnectExtLinks(ref tile, ref neis[j], b); ConnectExtLinks(ref neis[j], ref tile, bo); ConnectExtOffMeshLinks(ref tile, ref neis[j], b); ConnectExtOffMeshLinks(ref neis[j], ref tile, bo); } } result = GetTileRef(tile); }
/// <summary> /// Connect polygons from two different tiles. /// </summary> /// <param name="tile">Current Tile</param> /// <param name="target">Target Tile</param> /// <param name="side">Polygon edge</param> public void ConnectExtLinks(ref MeshTile tile, ref MeshTile target, BoundarySide side) { if (tile == null) { return; } //Connect border links for (int i = 0; i < tile.Header.PolyCount; i++) { int numPolyVerts = tile.Polys[i].VertCount; for (int j = 0; j < numPolyVerts; j++) { //Skip non-portal edges if ((tile.Polys[i].Neis[j] & Link.External) == 0) { continue; } BoundarySide dir = (BoundarySide)(tile.Polys[i].Neis[j] & 0xff); if (side != BoundarySide.Internal && dir != side) { continue; } //Create new links Vector3 va = tile.Verts[tile.Polys[i].Verts[j]]; Vector3 vb = tile.Verts[tile.Polys[i].Verts[(j + 1) % numPolyVerts]]; List <PolyId> nei = new List <PolyId>(4); List <float> neia = new List <float>(4 * 2); FindConnectingPolys(va, vb, target, dir.GetOpposite(), nei, neia); //Iterate through neighbors for (int k = 0; k < nei.Count; k++) { //Allocate a new link if possible int idx = AllocLink(tile); if (IsLinkAllocated(idx)) { tile.Links[idx].Reference = nei[k]; tile.Links[idx].Edge = j; tile.Links[idx].Side = dir; tile.Links[idx].Next = tile.Polys[i].FirstLink; tile.Polys[i].FirstLink = idx; //Compress portal limits to a value if (dir == BoundarySide.PlusX || dir == BoundarySide.MinusX) { float tmin = (neia[k * 2 + 0] - va.Z) / (vb.Z - va.Z); float tmax = (neia[k * 2 + 1] - va.Z) / (vb.Z - va.Z); if (tmin > tmax) { float temp = tmin; tmin = tmax; tmax = temp; } tile.Links[idx].BMin = (int)(MathHelper.Clamp(tmin, 0.0f, 1.0f) * 255.0f); tile.Links[idx].BMax = (int)(MathHelper.Clamp(tmax, 0.0f, 1.0f) * 255.0f); } else if (dir == BoundarySide.PlusZ || dir == BoundarySide.MinusZ) { float tmin = (neia[k * 2 + 0] - va.X) / (vb.X - va.X); float tmax = (neia[k * 2 + 1] - va.X) / (vb.X - va.X); if (tmin > tmax) { float temp = tmin; tmin = tmax; tmax = temp; } tile.Links[idx].BMin = (int)(MathHelper.Clamp(tmin, 0.0f, 1.0f) * 255.0f); tile.Links[idx].BMax = (int)(MathHelper.Clamp(tmax, 0.0f, 1.0f) * 255.0f); } } } } } }
/// <summary> /// Build a tile and link all the polygons togther, both internally and externally. /// Make sure to link off-mesh connections as well. /// </summary> /// <param name="data">Navigation Mesh data</param> /// <param name="lastRef">Last polygon reference</param> /// <param name="result">Last tile reference</param> public PolyId AddTile(NavMeshBuilder data) { //make sure data is in right format PathfindingCommon.NavMeshInfo header = data.Header; //make sure location is free if (GetTileAt(header.X, header.Y, header.Layer) != null) { return(PolyId.Null); } PolyId newTileId = GetNextTileRef(); MeshTile tile = new MeshTile(new Vector2i(header.X, header.Y), header.Layer, idManager, newTileId); tile.Salt = idManager.DecodeSalt(ref newTileId); if (header.BvNodeCount == 0) { tile.BVTree = null; } //patch header tile.Verts = data.NavVerts; tile.Polys = data.NavPolys; tile.PolyCount = header.PolyCount; tile.DetailMeshes = data.NavDMeshes; tile.DetailVerts = data.NavDVerts; tile.DetailTris = data.NavDTris; tile.BVTree = data.NavBvTree; tile.OffMeshConnections = data.OffMeshCons; tile.OffMeshConnectionCount = header.OffMeshConCount; tile.BvQuantFactor = header.BvQuantFactor; tile.BvNodeCount = header.BvNodeCount; tile.Bounds = header.Bounds; tile.WalkableClimb = header.WalkableClimb; //create connections within tile tile.ConnectIntLinks(); tile.BaseOffMeshLinks(); //create connections with neighbor tiles //connect with layers in current tile foreach (MeshTile layerTile in GetTilesAt(header.X, header.Y)) { if (layerTile != tile) { tile.ConnectExtLinks(layerTile, BoundarySide.Internal); layerTile.ConnectExtLinks(tile, BoundarySide.Internal); } tile.ConnectExtOffMeshLinks(layerTile, BoundarySide.Internal); layerTile.ConnectExtOffMeshLinks(tile, BoundarySide.Internal); } //connect with neighbor tiles for (int i = 0; i < 8; i++) { BoundarySide b = (BoundarySide)i; BoundarySide bo = b.GetOpposite(); foreach (MeshTile neighborTile in GetNeighborTilesAt(header.X, header.Y, b)) { tile.ConnectExtLinks(neighborTile, b); neighborTile.ConnectExtLinks(tile, bo); tile.ConnectExtOffMeshLinks(neighborTile, b); neighborTile.ConnectExtOffMeshLinks(tile, bo); } } AddTileAt(tile, GetNextTileRef()); return(newTileId); }