/// <summary> /// Patch header pointers /// </summary> /// <param name="header">Header</param> public void Patch(MeshHeader header) { Verts = new Vector3[header.VertCount]; Polys = new Poly[header.PolyCount]; Links = new Link[header.MaxLinkCount]; DetailMeshes = new PolyDetail[header.DetailMeshCount]; DetailVerts = new Vector3[header.DetailVertCount]; DetailTris = new Int4[header.DetailTriCount]; BvTree = new BVNode[header.BvNodeCount]; OffMeshCons = new OffMeshConnection[header.OffMeshConCount]; }
public static bool CreateNavMeshData(NavMeshCreateParams param, out MeshData outData) { outData = null; if (param.nvp > DT_VERTS_PER_POLYGON) { return(false); } if (param.VertCount >= 0xffff) { return(false); } if (param.VertCount == 0 || param.Verts == null) { return(false); } if (param.polyCount == 0 || param.Polys == null) { return(false); } int nvp = param.nvp; // Classify off-mesh connection points. We store only the connections // whose start point is inside the tile. int[] offMeshConClass = null; int storedOffMeshConCount = 0; int offMeshConLinkCount = 0; if (param.offMeshConCount > 0) { offMeshConClass = new int[param.offMeshConCount * 2]; // Find tight heigh bounds, used for culling out off-mesh start locations. float hmin = float.MaxValue; float hmax = float.MinValue; if (param.detailVerts != null && param.detailVertsCount > 0) { for (int i = 0; i < param.detailVertsCount; ++i) { var h = param.detailVerts[i].Y; hmin = Math.Min(hmin, h); hmax = Math.Max(hmax, h); } } else { for (int i = 0; i < param.VertCount; ++i) { var iv = param.Verts[i]; float h = param.bmin.Y + iv.Y * param.ch; hmin = Math.Min(hmin, h); hmax = Math.Max(hmax, h); } } hmin -= param.walkableClimb; hmax += param.walkableClimb; Vector3 bmin = param.bmin; Vector3 bmax = param.bmax; bmin.Y = hmin; bmax.Y = hmax; for (int i = 0; i < param.offMeshConCount; ++i) { var p0 = param.offMeshCon[i].Start; var p1 = param.offMeshCon[i].End; offMeshConClass[i + 0] = ClassifyOffMeshPoint(p0, bmin, bmax); offMeshConClass[i + 1] = ClassifyOffMeshPoint(p1, bmin, bmax); // Zero out off-mesh start positions which are not even potentially touching the mesh. if (offMeshConClass[i * 2] == 0xff && (p0.Y <bmin.Y || p0.Y> bmax.Y)) { offMeshConClass[i * 2] = 0; } // Count how many links should be allocated for off-mesh connections. if (offMeshConClass[i * 2] == 0xff) { offMeshConLinkCount++; } if (offMeshConClass[(i * 2) + 1] == 0xff) { offMeshConLinkCount++; } if (offMeshConClass[i * 2] == 0xff) { storedOffMeshConCount++; } } } // Off-mesh connectionss are stored as polygons, adjust values. int totPolyCount = param.polyCount + storedOffMeshConCount; int totVertCount = param.VertCount + storedOffMeshConCount * 2; // Find portal edges which are at tile borders. int edgeCount = 0; int portalCount = 0; for (int i = 0; i < param.polyCount; ++i) { var p = param.Polys[i]; for (int j = 0; j < nvp; ++j) { if (p[j] == MESH_NULL_IDX) { break; } edgeCount++; if ((p[nvp + j] & 0x8000) != 0) { var dir = p[nvp + j] & 0xf; if (dir != 0xf) { portalCount++; } } } } int maxLinkCount = edgeCount + portalCount * 2 + offMeshConLinkCount * 2; // Find unique detail vertices. int uniqueDetailVertCount = 0; int detailTriCount = 0; if (param.detailMeshes != null) { // Has detail mesh, count unique detail vertex count and use input detail tri count. detailTriCount = param.detailTriCount; for (int i = 0; i < param.polyCount; ++i) { var p = param.Polys[i]; var ndv = param.detailMeshes[i].Y; int nv = 0; for (int j = 0; j < nvp; ++j) { if (p[j] == MESH_NULL_IDX) { break; } nv++; } ndv -= nv; uniqueDetailVertCount += ndv; } } else { // No input detail mesh, build detail mesh from nav polys. uniqueDetailVertCount = 0; // No extra detail verts. detailTriCount = 0; for (int i = 0; i < param.polyCount; ++i) { var p = param.Polys[i]; int nv = 0; for (int j = 0; j < nvp; ++j) { if (p[j] == MESH_NULL_IDX) { break; } nv++; } detailTriCount += nv - 2; } } MeshData data = new MeshData { // Store header Header = new MeshHeader { Magic = DT_NAVMESH_MAGIC, Version = DT_NAVMESH_VERSION, X = param.tileX, Y = param.tileY, Layer = param.tileLayer, UserId = param.userId, PolyCount = totPolyCount, VertCount = totVertCount, MaxLinkCount = maxLinkCount, BMin = param.bmin, BMax = param.bmax, DetailMeshCount = param.polyCount, DetailVertCount = uniqueDetailVertCount, DetailTriCount = detailTriCount, 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 } }; int offMeshVertsBase = param.VertCount; int offMeshPolyBase = param.polyCount; // Store vertices // Mesh vertices for (int i = 0; i < param.VertCount; ++i) { var iv = param.Verts[i]; var v = new Vector3 { X = param.bmin.X + iv.X * param.cs, Y = param.bmin.Y + iv.Y * param.ch, Z = param.bmin.Z + iv.Z * param.cs }; data.NavVerts.Add(v); } // Off-mesh link vertices. int n = 0; for (int i = 0; i < param.offMeshConCount; ++i) { // Only store connections which start from this tile. if (offMeshConClass[i * 2] == 0xff) { var linkv = param.offMeshCon[i]; data.NavVerts.Add(linkv.Start); data.NavVerts.Add(linkv.End); n++; } } // Store polygons // Mesh polys int srcIndex = 0; for (int i = 0; i < param.polyCount; ++i) { var src = param.Polys[srcIndex]; Poly p = new Poly { VertCount = 0, Flags = param.PolyFlags[i], Area = param.PolyAreas[i], Type = PolyTypes.DT_POLYTYPE_GROUND, }; for (int j = 0; j < nvp; ++j) { if (src[j] == MESH_NULL_IDX) { break; } p.Verts[j] = src[j]; if ((src[nvp + j] & 0x8000) != 0) { // Border or portal edge. var dir = src[nvp + j] & 0xf; if (dir == 0xf) // Border { p.Neis[j] = 0; } else if (dir == 0) // Portal x- { p.Neis[j] = DT_EXT_LINK | 4; } else if (dir == 1) // Portal z+ { p.Neis[j] = DT_EXT_LINK | 2; } else if (dir == 2) // Portal x+ { p.Neis[j] = DT_EXT_LINK; } else if (dir == 3) // Portal z- { p.Neis[j] = DT_EXT_LINK | 6; } } else { // Normal connection p.Neis[j] = src[nvp + j] + 1; } p.VertCount++; } data.NavPolys.Add(p); srcIndex++; } // Off-mesh connection vertices. n = 0; for (int i = 0; i < param.offMeshConCount; ++i) { // Only store connections which start from this tile. if (offMeshConClass[i * 2] == 0xff) { Poly p = new Poly { Flags = (SamplePolyFlagTypes)param.offMeshCon[i].FlagTypes, Area = (SamplePolyAreas)param.offMeshCon[i].AreaType, Type = PolyTypes.DT_POLYTYPE_OFFMESH_CONNECTION }; p.Verts[0] = (offMeshVertsBase + (n * 2) + 0); p.Verts[1] = (offMeshVertsBase + (n * 2) + 1); p.VertCount = 2; data.NavPolys.Add(p); n++; } } // Store detail meshes and vertices. // The nav polygon vertices are stored as the first vertices on each mesh. // We compress the mesh data by skipping them and using the navmesh coordinates. if (param.detailMeshes != null) { for (int i = 0; i < param.polyCount; ++i) { int vb = param.detailMeshes[i][0]; int ndv = param.detailMeshes[i][1]; int nv = data.NavPolys[i].VertCount; PolyDetail dtl = new PolyDetail { VertBase = data.NavDVerts.Count, VertCount = (ndv - nv), TriBase = param.detailMeshes[i][2], TriCount = param.detailMeshes[i][3] }; // Copy vertices except the first 'nv' verts which are equal to nav poly verts. if (ndv - nv != 0) { var verts = param.detailVerts.Skip(vb + nv).Take(ndv - nv); data.NavDVerts.AddRange(verts); } data.NavDMeshes.Add(dtl); } // Store triangles. data.NavDTris.AddRange(param.detailTris); } else { // Create dummy detail mesh by triangulating polys. int tbase = 0; for (int i = 0; i < param.polyCount; ++i) { int nv = data.NavPolys[i].VertCount; PolyDetail dtl = new PolyDetail { VertBase = 0, VertCount = 0, TriBase = tbase, TriCount = (nv - 2) }; // Triangulate polygon (local indices). for (int j = 2; j < nv; ++j) { var t = new Int4 { X = 0, Y = (j - 1), Z = j, // Bit for each edge that belongs to poly boundary. W = (1 << 2) }; if (j == 2) { t.W |= (1 << 0); } if (j == nv - 1) { t.W |= (1 << 4); } tbase++; data.NavDTris.Add(t); } data.NavDMeshes.Add(dtl); } } // Store and create BVtree. if (param.buildBvTree) { CreateBVTree(param, out var nodes); data.NavBvtree.AddRange(nodes); } // Store Off-Mesh connections. n = 0; for (int i = 0; i < param.offMeshConCount; ++i) { // Only store connections which start from this tile. if (offMeshConClass[i * 2] == 0xff) { // Copy connection end-points. var endPts1 = param.offMeshCon[i].Start; var endPts2 = param.offMeshCon[i].End; var con = new OffMeshConnection { poly = offMeshPolyBase + n, rad = param.offMeshCon[i].Radius, flags = param.offMeshCon[i].Direction != 0 ? DT_OFFMESH_CON_BIDIR : 0, side = offMeshConClass[i * 2 + 1], start = endPts1, end = endPts2, userId = param.offMeshCon[i].Id, }; data.OffMeshCons.Add(con); n++; } } outData = data; return(true); }