private void ClosestPointOnPolyInTile(MeshTile tile, long ip, float posx, float posy, float posz, ref float[] closestPt) { Poly poly = tile.Polys[ip]; if (poly.Type == NavMeshBuilder.PolyTypeOffMeshConnection) { int v0 = poly.Verts[0] * 3; int v1 = poly.Verts[1] * 3; float d0 = Helper.VDist(posx, posy, posz, tile.Verts[v0 + 0], tile.Verts[v0 + 1], tile.Verts[v0 + 2]); float d1 = Helper.VDist(posx, posy, posz, tile.Verts[v1 + 0], tile.Verts[v1 + 1], tile.Verts[v1 + 2]); float u = d0 / (d0 + d1); Helper.VLerp(ref closestPt, tile.Verts[v0 + 0], tile.Verts[v0 + 1], tile.Verts[v0 + 2], tile.Verts[v1 + 0], tile.Verts[v1 + 1], tile.Verts[v1 + 2], u); return; } PolyDetail pd = tile.DetailMeshes[ip]; float[] verts = new float[NavMeshBuilder.VertsPerPoly * 3]; float[] edged = new float[NavMeshBuilder.VertsPerPoly]; float[] edget = new float[NavMeshBuilder.VertsPerPoly]; int nv = poly.VertCount; for (int i = 0; i < nv; i++) { Array.Copy(tile.Verts, poly.Verts[i] * 3, verts, i * 3, 3); } closestPt[0] = posx; closestPt[1] = posy; closestPt[2] = posz; if (!Helper.DistancePtPolyEdgesSqr(posx, posy, posz, verts, nv, ref edged, ref edget)) { float dmin = float.MaxValue; int imin = -1; for (int i = 0; i < nv; i++) { if (edged[i] < dmin) { dmin = edged[i]; imin = i; } } int va = imin * 3; int vb = ((imin + 1) % nv) * 3; Helper.VLerp(ref closestPt, verts[va + 0], verts[va + 1], verts[va + 2], verts[vb + 0], verts[vb + 1], verts[vb + 2], edget[imin]); } for (int j = 0; j < pd.TriCount; j++) { int t = (int)(pd.TriBase + j) * 4; float[] v = new float[9]; for (int k = 0; k < 3; k++) { if (tile.DetailTris[t + k] < poly.VertCount) { Array.Copy(tile.Verts, poly.Verts[tile.DetailTris[t + k]] * 3, v, k * 3, 3); //v[k] = tile.Verts[poly.Verts[tile.DetailTris[t + k]]*3]; } else { Array.Copy(tile.DetailVerts, (pd.VertBase + (tile.DetailTris[t + k] - poly.VertCount)) * 3, v, k * 3, 3); //v[k] = tile.DetailVerts[(pd.VertBase + (tile.DetailTris[t + k] - poly.VertCount))*3]; } } float h = 0; if (Helper.ClosestHeightPointTriangle(posx, posy, posz, v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7], v[8], ref h)) { closestPt[1] = h; break; } } }
public NavMeshBuilder(NavMeshCreateParams param) { if (param.Nvp > VertsPerPoly) { throw new ArgumentException("Too many Verts per Poly for NavMeshBuilder"); } if (param.VertCount >= 0xffff) { throw new ArgumentException("Too many total verticies for NavMeshBuilder"); } if (param.VertCount == 0 || param.Verts == null) { throw new ArgumentException("No vertices, cannot generate nav mesh"); } if (param.PolyCount == 0 || param.Polys == null) { throw new ArgumentException("No Polygons, cannot generate nav mesh"); } int nvp = param.Nvp; short[] offMeshConClass = new short[0]; int storedOffMeshConCount = 0; int offMeshConLinkCount = 0; if (param.OffMeshConCount > 0) { offMeshConClass = new short[param.OffMeshConCount * 2]; float hmin = float.MaxValue; float hmax = float.MinValue; if (param.DetailVerts != null && param.DetailVertsCount > 0) { for (int i = 0; i < param.DetailVertsCount; i++) { int h = i * 3 + 1; hmin = Math.Min(hmin, param.DetailVerts[h]); hmax = Math.Max(hmax, param.DetailVerts[h]); } } else { for (int i = 0; i < param.VertCount; i++) { int iv = i * 3; float h = param.BMin[1] + param.Verts[iv + 1] * param.Ch; hmin = Math.Min(hmin, h); hmax = Math.Max(hmax, h); } } hmin -= param.WalkableClimb; hmax += param.WalkableClimb; float[] bmin = new float[3], bmax = new float[3]; Array.Copy(param.BMin, bmin, 3); Array.Copy(param.BMax, bmax, 3); bmin[1] = hmin; bmax[1] = hmax; for (int i = 0; i < param.OffMeshConCount; i++) { int p0 = (i * 2 + 0) * 3; int p1 = (i * 2 + 1) * 3; offMeshConClass[i * 2 + 0] = ClassifyOffMeshPoint(param.OffMeshConVerts[p0 + 0], param.OffMeshConVerts[p0 + 1], param.OffMeshConVerts[p0 + 2], bmin, bmax); offMeshConClass[i * 2 + 1] = ClassifyOffMeshPoint(param.OffMeshConVerts[p1 + 0], param.OffMeshConVerts[p1 + 1], param.OffMeshConVerts[p1 + 2], bmin, bmax); if (offMeshConClass[i * 2 + 0] == 0xff) { if (param.OffMeshConVerts[p0 + 1] < bmin[1] || param.OffMeshConVerts[p0 + 1] > bmax[1]) { offMeshConClass[i * 2 + 0] = 0; } } if (offMeshConClass[i * 2 + 0] == 0xff) { offMeshConLinkCount++; } if (offMeshConClass[i * 2 + 1] == 0xff) { offMeshConLinkCount++; } if (offMeshConClass[i * 2 + 0] == 0xff) { storedOffMeshConCount++; } } } int totPolyCount = param.PolyCount + storedOffMeshConCount; int totVertCount = param.VertCount + storedOffMeshConCount * 2; int edgeCount = 0; int portalCount = 0; for (int i = 0; i < param.PolyCount; i++) { int p = i * 2 * nvp; for (int j = 0; j < nvp; j++) { if (param.Polys[p + j] == PolyMesh.MeshNullIdx) { break; } edgeCount++; if ((param.Polys[p + nvp + j] & 0x8000) != 0) { int dir = param.Polys[p + nvp + j] & 0xf; if (dir != 0xf) { portalCount++; } } } } int maxLinkCount = edgeCount + portalCount * 2 + offMeshConLinkCount * 2; int uniqueDetailVertCount = 0; int detailTryCount = 0; if (param.DetailMeshes != null) { detailTryCount = param.DetailTriCount; for (int i = 0; i < param.PolyCount; i++) { int p = i * nvp * 2; int ndv = (int)param.DetailMeshes[i * 4 + 1]; int nv = 0; for (int j = 0; j < nvp; j++) { if (param.Polys[p + j] == PolyMesh.MeshNullIdx) { break; } nv++; } ndv -= nv; uniqueDetailVertCount += ndv; } } else { uniqueDetailVertCount = 0; detailTryCount = 0; for (int i = 0; i < param.PolyCount; i++) { int p = i * nvp * 2; int nv = 0; for (int j = 0; j < nvp; j++) { if (param.Polys[p + j] == PolyMesh.MeshNullIdx) { break; } nv++; } detailTryCount += nv - 2; } } // Initialize the header and all nav data. Header = new MeshHeader { Magic = Helper.NavMeshMagic, Version = Helper.NavMeshVersion, X = param.TileX, Y = param.TileY, Layer = param.TileLayer, UserId = param.UserId, PolyCount = totPolyCount, VertCount = totVertCount, MaxLinkCount = maxLinkCount, DetailMeshCount = param.PolyCount, DetailVertCount = uniqueDetailVertCount, DetailTriCount = detailTryCount, BVQuantFactor = 1.0f / param.Cs, OffMeshBase = param.PolyCount, WalkableHeight = param.WalkableHeight, WalkableRadius = param.WalkableRadius, WalkableClimb = param.WalkableClimb, OffMeshConCount = storedOffMeshConCount, BVNodeCount = param.BuildBvTree ? param.PolyCount * 2 : 0, BMin = new float[3], BMax = new float[3] }; Array.Copy(param.BMin, Header.BMin, 3); Array.Copy(param.BMax, Header.BMax, 3); NavVerts = new float[totVertCount * 3]; NavPolys = new Poly[totPolyCount]; for (int i = 0; i < totPolyCount; i++) { NavPolys[i] = new Poly(); } NavLinks = new Link[maxLinkCount]; for (int i = 0; i < maxLinkCount; i++) { NavLinks[i] = new Link(); } NavDMeshes = new PolyDetail[param.PolyCount]; for (int i = 0; i < param.PolyCount; i++) { NavDMeshes[i] = new PolyDetail(); } NavDVerts = new float[3 * uniqueDetailVertCount]; NavDTris = new short[4 * detailTryCount]; NavBvTree = param.BuildBvTree ? new BVNode[param.PolyCount * 2] : new BVNode[0]; if (param.BuildBvTree) { for (int i = 0; i < param.PolyCount * 2; i++) { NavBvTree[i] = new BVNode(); } } OffMeshCons = new OffMeshConnection[storedOffMeshConCount]; for (int i = 0; i < storedOffMeshConCount; i++) { OffMeshCons[i] = new OffMeshConnection(); } int offMeshVertsBase = param.VertCount; int offMeshPolyBase = param.PolyCount; // store vertices // Mesh for (int i = 0; i < param.VertCount; i++) { int iv = i * 3; int v = i * 3; NavVerts[v + 0] = param.BMin[0] + param.Verts[iv + 0] * param.Cs; NavVerts[v + 1] = param.BMin[1] + param.Verts[iv + 1] * param.Ch; NavVerts[v + 2] = param.BMin[2] + param.Verts[iv + 2] * param.Cs; } // off-link int n = 0; for (int i = 0; i < param.OffMeshConCount; i++) { if (offMeshConClass[i * 2 + 0] == 0xff) { int linkv = i * 2 * 3; int v = (offMeshVertsBase + n * 2) * 3; Array.Copy(param.OffMeshConVerts, linkv, NavVerts, v, 3); Array.Copy(param.OffMeshConVerts, linkv + 3, NavVerts, v + 3, 3); n++; } } // store polygons // mesh int src = 0; for (int i = 0; i < param.PolyCount; i++) { Poly p = NavPolys[i]; p.VertCount = 0; p.Flags = param.PolyFlags[i]; p.Area = param.PolyAreas[i]; p.Type = PolyTypeGround; for (int j = 0; j < nvp; j++) { if (param.Polys[src + j] == PolyMesh.MeshNullIdx) { break; } p.Verts[j] = param.Polys[src + j]; if ((param.Polys[src + nvp + j] & 0x8000) != 0) { int dir = param.Polys[src + nvp + j] & 0xf; if (dir == 0xf) { p.Neis[j] = 0; } else if (dir == 0) { p.Neis[j] = ExtLink | 4; } else if (dir == 1) { p.Neis[j] = ExtLink | 2; } else if (dir == 2) { p.Neis[j] = ExtLink | 0; } else if (dir == 3) { p.Neis[j] = ExtLink | 6; } } else { p.Neis[j] = param.Polys[src + nvp + j] + 1; } p.VertCount++; } src += nvp * 2; } // off mesh n = 0; for (int i = 0; i < param.OffMeshConCount; i++) { if (offMeshConClass[i * 2 + 0] == 0xff) { Poly p = NavPolys[offMeshPolyBase + n]; p.VertCount = 2; p.Verts[0] = (offMeshVertsBase + n * 2 + 0); p.Verts[1] = (offMeshVertsBase + n * 2 + 1); p.Flags = param.OffMeshConFlags[i]; p.Area = (short)param.OffMeshConAreas[i]; p.Type = PolyTypeOffMeshConnection; n++; } } // Store detail meshes and verts if (param.DetailMeshes != null) { int vbase = 0; for (int i = 0; i < param.PolyCount; i++) { PolyDetail dtl = NavDMeshes[i]; int vb = (int)param.DetailMeshes[i * 4 + 0]; int ndv = (int)param.DetailMeshes[i * 4 + 1]; int nv = NavPolys[i].VertCount; dtl.VertBase = vbase; dtl.VertCount = (short)(ndv - nv); dtl.TriBase = param.DetailMeshes[i * 4 + 2]; dtl.TriCount = (short)param.DetailMeshes[i * 4 + 3]; if (ndv - nv > 0) { Array.Copy(param.DetailVerts, (vb + nv) * 3, NavDVerts, vbase * 3, (ndv - nv) * 3); vbase += (short)(ndv - nv); } } Array.Copy(param.DetailTris, NavDTris, param.DetailTriCount * 4); } else { // Create dummy detail mesh int tbase = 0; for (int i = 0; i < param.PolyCount; i++) { PolyDetail dtl = NavDMeshes[i]; int nv = NavPolys[i].VertCount; dtl.VertBase = 0; dtl.VertCount = 0; dtl.TriBase = tbase; dtl.TriCount = (short)(nv - 2); for (int j = 2; j < nv; j++) { int t = tbase * 4; NavDTris[t + 0] = 0; NavDTris[t + 1] = (short)(j - 1); NavDTris[t + 2] = (short)j; NavDTris[t + 3] = (1 << 2); if (j == 2) { NavDTris[t + 3] |= (1 << 0); } if (j == nv - 1) { NavDTris[t + 3] |= (1 << 4); } tbase++; } } } // Store and create BVTree if (param.BuildBvTree) { CreateBVTree(param.Verts, param.VertCount, param.Polys, param.PolyCount, nvp, param.Cs, param.Ch, param.PolyCount * 2); } // store off-mesh connections n = 0; for (int i = 0; i < param.OffMeshConCount; i++) { if (offMeshConClass[i * 2 + 0] == 0xff) { OffMeshConnection con = OffMeshCons[n]; con.Poly = offMeshPolyBase + n; int endPts = i * 2 * 3; Array.Copy(param.OffMeshConVerts, endPts, con.Pos, 0, 3); Array.Copy(param.OffMeshConVerts, endPts + 3, con.Pos, 3, 3); con.Rad = param.OffMeshConRad[i]; con.Flags = param.OffMeshConDir[i] > 0 ? OffMeshConBiDir : (short)0; con.Side = offMeshConClass[i * 2 + 1]; if (param.OffMeshConUserId != null) { con.UserId = param.OffMeshConUserId[i]; } n++; } } }