Uses the PolyMesh polygon data for pathfinding
Пример #1
0
        /// <summary>
        /// Find the distance from a point to a triangle.
        /// </summary>
        /// <param name="indexPoly">Current polygon's index</param>
        /// <param name="pos">Current position</param>
        /// <param name="h">Resulting height</param>
        /// <returns>True, if a height is found. False, if otherwise.</returns>
        public bool ClosestHeight(int indexPoly, Vector3 pos, out float h)
        {
            NavPoly 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);
        }
Пример #2
0
        public virtual float GetCost(Vector3 a, Vector3 b,
			NavPolyId prevRef, NavTile prevTile, NavPoly prevPoly,
			NavPolyId curRef, NavTile curTile, NavPoly curPoly,
			NavPolyId nextRef, NavTile nextTile, NavPoly nextPoly)
        {
            return (a - b).Length() * areaCost[(int)curPoly.Area.Id];
        }
Пример #3
0
 public virtual float GetCost(Vector3 a, Vector3 b,
                              NavPolyId prevRef, NavTile prevTile, NavPoly prevPoly,
                              NavPolyId curRef, NavTile curTile, NavPoly curPoly,
                              NavPolyId nextRef, NavTile nextTile, NavPoly nextPoly)
 {
     return((a - b).Length() * areaCost[(int)curPoly.Area.Id]);
 }
Пример #4
0
        /// <summary>
        /// Allocate links for each of the tile's polygons' vertices
        /// </summary>
        public void ConnectIntLinks()
        {
            //Iterate through all the polygons
            for (int i = 0; i < PolyCount; i++)
            {
                NavPoly p = Polys[i];

                //Avoid Off-Mesh Connection polygons
                if (p.PolyType == NavPolyType.OffMeshConnection)
                {
                    continue;
                }

                //Build edge links
                for (int j = p.VertCount - 1; j >= 0; j--)
                {
                    //Skip hard and non-internal edges
                    if (p.Neis[j] == 0 || Link.IsExternal(p.Neis[j]))
                    {
                        continue;
                    }

                    NavPolyId id;
                    idManager.SetPolyIndex(ref baseRef, p.Neis[j] - 1, out id);

                    //Initialize a new link
                    Link link = new Link();
                    link.Reference = id;
                    link.Edge      = j;
                    link.Side      = BoundarySide.Internal;
                    link.BMin      = link.BMax = 0;
                    p.Links.Add(link);
                }
            }
        }
Пример #5
0
        /// <summary>
        /// Find the closest point on an offmesh connection, which is in between the two points.
        /// </summary>
        /// <param name="poly">Current polygon</param>
        /// <param name="pos">Current position</param>
        /// <param name="closest">Resulting point that is closest.</param>
        public void ClosestPointOnPolyOffMeshConnection(NavPoly 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);
        }
Пример #6
0
        /// <summary>song
        /// 根据ploy,找到中心点
        /// </summary>
        /// <param name="poly"></param>
        /// <param name="center"></param>
        public void centerPoint(NavPoly poly, out Vector3 center)
        {
            center = new Vector3(0, 0, 0);

            int numPolyVerts = poly.VertCount;

            for (int i = 0; i < numPolyVerts; i++)
            {
                center += Verts[poly.Verts[i]];
            }
            center /= numPolyVerts;
        }
Пример #7
0
        /// <summary>
        /// Begin creating off-mesh links between the tile polygons.
        /// </summary>
        public void BaseOffMeshLinks()
        {
            //Base off-mesh connection start points
            for (int i = 0; i < OffMeshConnectionCount; i++)
            {
                int con = i;
                OffMeshConnection omc = OffMeshConnections[con];

                Vector3 extents = new Vector3(omc.Radius, WalkableClimb, omc.Radius);

                //Find polygon to connect to
                Vector3   p         = omc.Pos0;
                Vector3   nearestPt = new Vector3();
                NavPolyId reference = FindNearestPoly(p, extents, ref nearestPt);
                if (reference == NavPolyId.Null)
                {
                    continue;
                }

                //Do extra checks
                if ((nearestPt.X - p.X) * (nearestPt.X - p.X) + (nearestPt.Z - p.Z) * (nearestPt.Z - p.Z) >
                    OffMeshConnections[con].Radius * OffMeshConnections[con].Radius)
                {
                    continue;
                }

                NavPoly poly = this.Polys[omc.Poly];

                //Make sure location is on current mesh
                Verts[poly.Verts[0]] = nearestPt;

                Link link = new Link();
                link.Reference = reference;
                link.Edge      = 0;
                link.Side      = BoundarySide.Internal;
                poly.Links.Add(link);

                //Start end-point always conects back to off-mesh connection
                int       landPolyIdx = idManager.DecodePolyIndex(ref reference);
                NavPolyId id;
                idManager.SetPolyIndex(ref baseRef, OffMeshConnections[con].Poly, out id);

                Link link2 = new Link();
                link2.Reference = id;
                link2.Edge      = 0xff;
                link2.Side      = BoundarySide.Internal;
                Polys[landPolyIdx].Links.Add(link2);
            }
        }
Пример #8
0
        public void ClosestPointOnPoly(NavPoly poly, Vector3 pos, ref Vector3 closest, out bool posOverPoly)
        {
            int indexPoly = 0;

            for (int i = 0; i < Polys.Length; i++)
            {
                if (Polys[i] == poly)
                {
                    indexPoly = i;
                    break;
                }
            }

            ClosestPointOnPoly(indexPoly, pos, ref closest, out posOverPoly);
        }
Пример #9
0
        /// <summary>
        /// Given a point, find the closest point on that poly.
        /// </summary>
        /// <param name="poly">The current polygon.</param>
        /// <param name="pos">The current position</param>
        /// <param name="closest">Reference to the closest point</param>
        public void ClosestPointOnPolyBoundary(NavPoly poly, Vector3 pos, out Vector3 closest, out bool insidePoly)
        {
            insidePoly = false;

            //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;
                insidePoly = false;
            }
            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]);
            }
        }
Пример #10
0
        /// <summary>
        /// Given a point, find the closest point on that poly.
        /// </summary>
        /// <param name="indexPoly">The current poly's index</param>
        /// <param name="pos">The current position</param>
        /// <param name="closest">Reference to the closest point</param>
        public void ClosestPointOnPoly(int indexPoly, Vector3 pos, ref Vector3 closest)
        {
            NavPoly poly = Polys[indexPoly];

            //Off-mesh connections don't have detail polygons
            if (Polys[indexPoly].PolyType == NavPolyType.OffMeshConnection)
            {
                ClosestPointOnPolyOffMeshConnection(poly, pos, out closest);
                return;
            }

            ClosestPointOnPolyBoundary(poly, pos, out closest);

            float h;

            if (ClosestHeight(indexPoly, pos, out h))
            {
                closest.Y = h;
            }
        }
Пример #11
0
        /// <summary>
        /// Given a point, find the closest point on that poly.
        /// </summary>
        /// <param name="poly">The current polygon.</param>
        /// <param name="pos">The current position</param>
        /// <param name="closest">Reference to the closest point</param>
        public void ClosestPointOnPoly(NavPoly poly, Vector3 pos, ref Vector3 closest)
        {
            bool posOverPoly;

            ClosestPointOnPoly(poly, pos, ref closest, out posOverPoly);
        }
Пример #12
0
        /// <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);
                }
            }
        }
Пример #13
0
 public virtual bool PassFilter(NavPolyId polyId, NavTile tile, NavPoly poly)
 {
     return true;
 }
Пример #14
0
        /// <summary>
        /// Get edge midpoint between two prolygons
        /// </summary>
        /// <param name="from">"From" polygon reference</param>
        /// <param name="fromPoly">"From" polygon data</param>
        /// <param name="fromTile">"From" mesh tile</param>
        /// <param name="to">"To" polygon reference</param>
        /// <param name="toPoly">"To" polygon data</param>
        /// <param name="toTile">"To" mesh tile</param>
        /// <param name="mid">Edge midpoint</param>
        /// <returns>True, if midpoint found. False, if otherwise.</returns>
        public bool GetEdgeMidPoint(NavPolyId from, NavPoly fromPoly, NavTile fromTile, NavPolyId to, NavPoly toPoly, NavTile toTile, ref Vector3 mid)
        {
            Vector3 left = new Vector3();
            Vector3 right = new Vector3();
            if (!GetPortalPoints(from, fromPoly, fromTile, to, toPoly, toTile, ref left, ref right))
                return false;

            mid = (left + right) * 0.5f;

            return true;
        }
Пример #15
0
 public virtual bool PassFilter(NavPolyId polyId, NavTile tile, NavPoly poly)
 {
     return(true);
 }
Пример #16
0
 /// <summary>
 /// Only use this function if it is known that the provided polygon reference is valid.
 /// </summary>
 /// <param name="reference">Polygon reference</param>
 /// <param name="tile">Resulting tile</param>
 /// <param name="poly">Resulting poly</param>
 public void TryGetTileAndPolyByRefUnsafe(NavPolyId reference, out NavTile tile, out NavPoly poly)
 {
     int salt, polyIndex, tileIndex;
     idManager.Decode(ref reference, out polyIndex, out tileIndex, out salt);
     tile = tileList[tileIndex];
     poly = tileList[tileIndex].Polys[polyIndex];
 }
Пример #17
0
        /// <summary>
        /// Retrieve the tile and poly based off of a polygon reference
        /// </summary>
        /// <param name="reference">Polygon reference</param>
        /// <param name="tile">Resulting tile</param>
        /// <param name="poly">Resulting poly</param>
        /// <returns>True if tile and poly successfully retrieved</returns>
        public bool TryGetTileAndPolyByRef(NavPolyId reference, out NavTile tile, out NavPoly poly)
        {
            tile = null;
            poly = null;

            if (reference == NavPolyId.Null)
                return false;

            //Get tile and poly indices
            int salt, polyIndex, tileIndex;
            idManager.Decode(ref reference, out polyIndex, out tileIndex, out salt);

            //Make sure indices are valid
            if (tileIndex >= maxTiles)
                return false;

            if (tileList[tileIndex].Salt != salt)
                return false;

            if (polyIndex >= tileList[tileIndex].PolyCount)
                return false;

            //Retrieve tile and poly
            tile = tileList[tileIndex];
            poly = tileList[tileIndex].Polys[polyIndex];
            return true;
        }
Пример #18
0
        /// <summary>
        /// Find points on the left and right side.
        /// </summary>
        /// <param name="from">"From" polygon reference</param>
        /// <param name="fromPoly">"From" polygon data</param>
        /// <param name="fromTile">"From" mesh tile</param>
        /// <param name="to">"To" polygon reference</param>
        /// <param name="toPoly">"To" polygon data</param>
        /// <param name="toTile">"To" mesh tile</param>
        /// <param name="left">Resulting point on the left side</param>
        /// <param name="right">Resulting point on the right side</param>
        /// <returns>True, if points found. False, if otherwise.</returns>
        public bool GetPortalPoints(NavPolyId from, NavPoly fromPoly, NavTile fromTile, NavPolyId to, NavPoly toPoly, NavTile toTile, ref Vector3 left, ref Vector3 right)
        {
            //find the link that points to the 'to' polygon
            Link link = null;
            foreach (Link fromLink in fromPoly.Links)
            {
                if (fromLink.Reference == to)
                {
                    link = fromLink;
                    break;
                }
            }

            if (link == null)
                return false;

            //handle off-mesh connections
            if (fromPoly.PolyType == NavPolyType.OffMeshConnection)
            {
                //find link that points to first vertex
                foreach (Link fromLink in fromPoly.Links)
                {
                    if (fromLink.Reference == to)
                    {
                        int v = fromLink.Edge;
                        left = fromTile.Verts[fromPoly.Verts[v]];
                        right = fromTile.Verts[fromPoly.Verts[v]];
                        return true;
                    }
                }

                return false;
            }

            if (toPoly.PolyType == NavPolyType.OffMeshConnection)
            {
                //find link that points to first vertex
                foreach (Link toLink in toPoly.Links)
                {
                    if (toLink.Reference == from)
                    {
                        int v = toLink.Edge;
                        left = toTile.Verts[toPoly.Verts[v]];
                        right = toTile.Verts[toPoly.Verts[v]];
                        return true;
                    }
                }

                return false;
            }

            //find portal vertices
            int v0 = fromPoly.Verts[link.Edge];
            int v1 = fromPoly.Verts[(link.Edge + 1) % fromPoly.VertCount];
            left = fromTile.Verts[v0];
            right = fromTile.Verts[v1];

            //if the link is at the tile boundary, clamp the vertices to tile width
            if (link.Side != BoundarySide.Internal)
            {
                //unpack portal limits
                if (link.BMin != 0 || link.BMax != 255)
                {
                    float s = 1.0f / 255.0f;
                    float tmin = link.BMin * s;
                    float tmax = link.BMax * s;
                    left = Vector3.Lerp(fromTile.Verts[v0], fromTile.Verts[v1], tmin);
                    right = Vector3.Lerp(fromTile.Verts[v0], fromTile.Verts[v1], tmax);
                }
            }

            return true;
        }
Пример #19
0
        /// <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 NavPoly[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 NavPoly();
                navPolys[i].VertCount = 0;
                navPolys[i].Tag = polyMesh.Polys[i].Tag;
                navPolys[i].Area = polyMesh.Polys[i].Area;
                navPolys[i].PolyType = NavPolyType.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] = new NavPoly();
                    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 = NavPolyType.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] = new OffMeshConnection ();

                    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++;
                }
            }
        }