// Copyright (c) 2014-2015 Robert Rouhani and other contributors (see CONTRIBUTORS file). // Licensed under the MIT License - https://raw.github.com/Robmaister/SharpNav/master/LICENSE using SharpNav.Collections; using SharpNav.Geometry; #if MONOGAME using Vector3 = Microsoft.Xna.Framework.Vector3; #elif OPENTK using Vector3 = OpenTK.Vector3; #elif SHARPDX using Vector3 = SharpDX.Vector3; #endif namespace SharpNav.Pathfinding { /// /// The MeshTile contains the map data for pathfinding /// public class MeshTile { /// /// Gets or sets the counter describing modifications to the tile /// public int Salt { get; set; } /// /// Gets or sets the index to the next free link /// public int LinksFreeList { get; set; } /// /// Gets or sets the header /// public PathfindingCommon.NavMeshInfo Header { get; set; } /// /// Gets or sets the PolyMesh polygons /// public Poly[] Polys { get; set; } /// /// Gets or sets the PolyMesh vertices /// public Vector3[] Verts { get; set; } /// /// Gets or sets the links between polygons /// public Link[] Links { get; set; } /// /// Gets or sets the PolyMeshDetail data /// public PolyMeshDetail.MeshData[] DetailMeshes { get; set; } /// /// Gets or sets the PolyMeshDetail vertices /// public Vector3[] DetailVerts { get; set; } /// /// Gets or sets the PolyMeshDetail triangles /// public PolyMeshDetail.TriangleData[] DetailTris { get; set; } /// /// Gets or sets the OffmeshConnections /// public OffMeshConnection[] OffMeshConnections { get; set; } /// /// Gets or sets the bounding volume tree /// public BVTree BVTree { get; set; } /// /// Gets or sets the NavMeshBuilder data /// public NavMeshBuilder Data { get; set; } /// /// Gets or sets the next MeshTile /// public MeshTile Next { get; set; } /// /// Given a point, find the closest point on that poly. /// /// The current polygon. /// The current position /// Reference to the closest point public void ClosestPointOnPoly(Poly poly, Vector3 pos, ref Vector3 closest) { int indexPoly = 0; for (int i = 0; i < Polys.Length; i++) { if (Polys[i] == poly) { indexPoly = i; break; } } ClosestPointOnPoly(indexPoly, pos, ref closest); } /// /// Given a point, find the closest point on that poly. /// /// The current poly's index /// The current position /// Reference to the closest point public void ClosestPointOnPoly(int indexPoly, Vector3 pos, ref Vector3 closest) { Poly poly = Polys[indexPoly]; //Off-mesh connections don't have detail polygons if (Polys[indexPoly].PolyType == PolygonType.OffMeshConnection) { ClosestPointOnPolyOffMeshConnection(poly, pos, out closest); return; } ClosestPointOnPolyBoundary(poly, pos, out closest); float h; if (ClosestHeight(indexPoly, pos, out h)) closest.Y = h; } /// /// Given a point, find the closest point on that poly. /// /// The current polygon. /// The current position /// Reference to the closest point public void ClosestPointOnPolyBoundary(Poly poly, Vector3 pos, out Vector3 closest) { //Clamp point to be inside the polygon Vector3[] verts = new Vector3[PathfindingCommon.VERTS_PER_POLYGON]; float[] edgeDistance = new float[PathfindingCommon.VERTS_PER_POLYGON]; float[] edgeT = new float[PathfindingCommon.VERTS_PER_POLYGON]; int numPolyVerts = poly.VertCount; for (int i = 0; i < numPolyVerts; i++) verts[i] = Verts[poly.Verts[i]]; bool inside = Distance.PointToPolygonEdgeSquared(pos, verts, numPolyVerts, edgeDistance, edgeT); if (inside) { //Point is inside the polygon closest = pos; } else { //Point is outside the polygon //Clamp to nearest edge float minDistance = float.MaxValue; int minIndex = -1; for (int i = 0; i < numPolyVerts; i++) { if (edgeDistance[i] < minDistance) { minDistance = edgeDistance[i]; minIndex = i; } } Vector3 va = verts[minIndex]; Vector3 vb = verts[(minIndex + 1) % numPolyVerts]; closest = Vector3.Lerp(va, vb, edgeT[minIndex]); } } /// /// Find the distance from a point to a triangle. /// /// Current polygon's index /// Current position /// Resulting height /// True, if a height is found. False, if otherwise. public bool ClosestHeight(int indexPoly, Vector3 pos, out float h) { Poly poly = Polys[indexPoly]; PolyMeshDetail.MeshData pd = DetailMeshes[indexPoly]; //find height at the location for (int j = 0; j < DetailMeshes[indexPoly].TriangleCount; j++) { PolyMeshDetail.TriangleData t = DetailTris[pd.TriangleIndex + j]; Vector3[] v = new Vector3[3]; for (int k = 0; k < 3; k++) { if (t[k] < poly.VertCount) v[k] = Verts[poly.Verts[t[k]]]; else v[k] = DetailVerts[pd.VertexIndex + (t[k] - poly.VertCount)]; } if (Distance.PointToTriangle(pos, v[0], v[1], v[2], out h)) return true; } h = float.MaxValue; return false; } /// /// Find the closest point on an offmesh connection, which is in between the two points. /// /// Current polygon /// Current position /// Resulting point that is closest. public void ClosestPointOnPolyOffMeshConnection(Poly poly, Vector3 pos, out Vector3 closest) { Vector3 v0 = Verts[poly.Verts[0]]; Vector3 v1 = Verts[poly.Verts[1]]; float d0 = (pos - v0).Length(); float d1 = (pos - v1).Length(); float u = d0 / (d0 + d1); closest = Vector3.Lerp(v0, v1, u); } } }