/// <summary> /// Allocate links for each of the tile's polygons' vertices /// </summary> /// <param name="tile">A tile contains a set of polygons, which are linked to each other</param> public void ConnectIntLinks(ref MeshTile tile) { if (tile == null) { return; } PolyId polyBase = GetPolyRefBase(tile); //Iterate through all the polygons for (int i = 0; i < tile.Header.PolyCount; i++) { //The polygon links will end in a null link tile.Polys[i].FirstLink = Link.Null; //Avoid Off-Mesh Connection polygons if (tile.Polys[i].PolyType == PolygonType.OffMeshConnection) { continue; } //Build edge links for (int j = tile.Polys[i].VertCount - 1; j >= 0; j--) { //Skip hard and non-internal edges if (tile.Polys[i].Neis[j] == 0 || IsExternalLink(tile.Polys[i].Neis[j])) { continue; } //Allocate a new link if possible int idx = AllocLink(tile); //Allocation of link should be successful if (IsLinkAllocated(idx)) { //Initialize a new link PolyId id; PolyId.SetPolyIndex(ref polyBase, tile.Polys[i].Neis[j] - 1, out id); tile.Links[idx].Reference = id; tile.Links[idx].Edge = j; tile.Links[idx].Side = BoundarySide.Internal; tile.Links[idx].BMin = tile.Links[idx].BMax = 0; //Add to polygon's links list tile.Links[idx].Next = tile.Polys[i].FirstLink; tile.Polys[i].FirstLink = idx; } } } }
/// <summary> /// Find all the polygons within a certain bounding box. /// </summary> /// <param name="tile">Current tile</param> /// <param name="qbounds">The bounds</param> /// <param name="polys">List of polygons</param> /// <returns>Number of polygons found</returns> public int QueryPolygonsInTile(MeshTile tile, BBox3 qbounds, List <PolyId> polys) { if (tile.BVTree.Count != 0) { int node = 0; int end = tile.Header.BvNodeCount; Vector3 tbmin = tile.Header.Bounds.Min; Vector3 tbmax = tile.Header.Bounds.Max; //Clamp query box to world box Vector3 qbmin = qbounds.Min; Vector3 qbmax = qbounds.Max; PolyBounds b; float bminx = MathHelper.Clamp(qbmin.X, tbmin.X, tbmax.X) - tbmin.X; float bminy = MathHelper.Clamp(qbmin.Y, tbmin.Y, tbmax.Y) - tbmin.Y; float bminz = MathHelper.Clamp(qbmin.Z, tbmin.Z, tbmax.Z) - tbmin.Z; float bmaxx = MathHelper.Clamp(qbmax.X, tbmin.X, tbmax.X) - tbmin.X; float bmaxy = MathHelper.Clamp(qbmax.Y, tbmin.Y, tbmax.Y) - tbmin.Y; float bmaxz = MathHelper.Clamp(qbmax.Z, tbmin.Z, tbmax.Z) - tbmin.Z; const int MinMask = unchecked ((int)0xfffffffe); b.Min.X = (int)(bminx * tile.Header.BvQuantFactor) & MinMask; b.Min.Y = (int)(bminy * tile.Header.BvQuantFactor) & MinMask; b.Min.Z = (int)(bminz * tile.Header.BvQuantFactor) & MinMask; b.Max.X = (int)(bmaxx * tile.Header.BvQuantFactor + 1) | 1; b.Max.Y = (int)(bmaxy * tile.Header.BvQuantFactor + 1) | 1; b.Max.Z = (int)(bmaxz * tile.Header.BvQuantFactor + 1) | 1; //traverse tree PolyId polyBase = GetPolyRefBase(tile); while (node < end) { BVTree.Node bvNode = tile.BVTree[node]; bool overlap = PolyBounds.Overlapping(ref b, ref bvNode.Bounds); bool isLeafNode = bvNode.Index >= 0; if (isLeafNode && overlap) { if (polys.Count < polys.Capacity) { PolyId polyRef; PolyId.SetPolyIndex(ref polyBase, bvNode.Index, out polyRef); polys.Add(polyRef); } } if (overlap || isLeafNode) { node++; } else { int escapeIndex = -bvNode.Index; node += escapeIndex; } } return(polys.Count); } else { BBox3 b; PolyId polyBase = GetPolyRefBase(tile); for (int i = 0; i < tile.Header.PolyCount; i++) { var poly = tile.Polys[i]; //don't return off-mesh connection polygons if (poly.PolyType == PolygonType.OffMeshConnection) { continue; } //calculate polygon bounds b.Max = b.Min = tile.Verts[poly.Verts[0]]; for (int j = 1; j < poly.VertCount; j++) { Vector3 v = tile.Verts[poly.Verts[j]]; Vector3Extensions.ComponentMin(ref b.Min, ref v, out b.Min); Vector3Extensions.ComponentMax(ref b.Max, ref v, out b.Max); } if (BBox3.Overlapping(ref qbounds, ref b)) { if (polys.Count < polys.Capacity) { PolyId polyRef; PolyId.SetPolyIndex(ref polyBase, i, out polyRef); polys.Add(polyRef); } } } return(polys.Count); } }
/// <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 <PolyId> 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; PolyId 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)); PolyId id; PolyId.SetPolyIndex(ref polyBase, i, out id); con.Add(id); } break; } } }
/// <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> /// Begin creating off-mesh links between the tile polygons. /// </summary> /// <param name="tile">Current Tile</param> public void BaseOffMeshLinks(ref MeshTile tile) { if (tile == null) { return; } PolyId polyBase = GetPolyRefBase(tile); //Base off-mesh connection start points for (int i = 0; i < tile.Header.OffMeshConCount; i++) { int con = i; int poly = tile.OffMeshConnections[con].Poly; Vector3 extents = new Vector3(tile.OffMeshConnections[con].Radius, tile.Header.WalkableClimb, tile.OffMeshConnections[con].Radius); //Find polygon to connect to Vector3 p = tile.OffMeshConnections[con].Pos0; Vector3 nearestPt = new Vector3(); PolyId reference = FindNearestPolyInTile(tile, p, extents, ref nearestPt); if (reference == PolyId.Null) { continue; } //Do extra checks if ((nearestPt.X - p.X) * (nearestPt.X - p.X) + (nearestPt.Z - p.Z) * (nearestPt.Z - p.Z) > tile.OffMeshConnections[con].Radius * tile.OffMeshConnections[con].Radius) { continue; } //Make sure location is on current mesh tile.Verts[tile.Polys[poly].Verts[0]] = nearestPt; //Link off-mesh connection to target poly int idx = AllocLink(tile); if (IsLinkAllocated(idx)) { //Initialize a new link tile.Links[idx].Reference = reference; tile.Links[idx].Edge = 0; tile.Links[idx].Side = BoundarySide.Internal; tile.Links[idx].BMin = tile.Links[idx].BMax = 0; //Add to polygon's links list tile.Links[idx].Next = tile.Polys[poly].FirstLink; tile.Polys[poly].FirstLink = idx; } //Start end-point always conects back to off-mesh connection int tidx = AllocLink(tile); if (IsLinkAllocated(tidx)) { //Initialize a new link int landPolyIdx = reference.DecodePolyIndex(polyBits); PolyId id; PolyId.SetPolyIndex(ref polyBase, tile.OffMeshConnections[con].Poly, out id); tile.Links[idx].Reference = id; tile.Links[idx].Edge = 0xff; tile.Links[idx].Side = BoundarySide.Internal; tile.Links[idx].BMin = tile.Links[idx].BMax = 0; //Add to polygon's links list tile.Links[idx].Next = tile.Polys[landPolyIdx].FirstLink; tile.Polys[landPolyIdx].FirstLink = tidx; } } }