private static Navmesh GenerateNavmesh() { #region Generate Neighboring polygon data //Then generate neighboring polygon data, by parsing the face list MyVector3 <bool> sharedVertex; //For the current face, what vertices are shared with the other face? int sharedVertices; //if goes to 2, edge is shared for (ushort q = 0; q < faces.Count; q++) { //Index of face and neighborPoly refer to the same polygon. neighborPolys.Add(new MyVector3 <ushort>()); neighborPolys[q].x = Navmesh.NullIndex; neighborPolys[q].y = Navmesh.NullIndex; neighborPolys[q].z = Navmesh.NullIndex; //Compare this face with every other face for (ushort w = 0; w < faces.Count; w++) { if (w != q) { sharedVertices = 0; sharedVertex = new MyVector3 <bool>(); //Go from left to right in the face MyVector3 for (int j = 0; j <= 2; j++) { //And compare each index with every other index for (int k = 0; k <= 2; k++) { if (faces[q][j] == faces[w][k]) { //If we find a matching index, update stuff (only for the current face, dont bother with other face, can optimise but will be confusing) sharedVertices++; sharedVertex[j] = true; //could break out of the for loop now, as face will not list the same index twice } } } if (sharedVertices > 2) { ReportError("error: more than 2 vertices shared between polys " + q + " and " + w); } //Check if these faces are sharing an edge if (sharedVertices == 2) { //get the Leftmost Right-To-Left Pair in the neighborPolys MyVector3 //options are: edge 0-1, 1-2, and 2-0, respectively indexing neighboringPolys. (i.e. if index 1 of neighboring polys is 45, that means that the current polygon and polygon 45 share the edge face[1] <-> face[2] if (sharedVertex[0] == true) { if (sharedVertex[1] == true) { neighborPolys[q][0] = w; //I.e. tell this face's MyVector3 of neighboring polygons that the edge made up by vertices at 0 and 1 is shared between polygon q and w } else { neighborPolys[q][2] = w; } } else { neighborPolys[q][1] = w; } } } //End iterating through other faces } } //End iterating through each face #endregion //Now, Load these into Critter AI and create a navmesh navData = new NavmeshTileBuildData(maxPolyVerts, maxPolys, maxVertsPerPoly, 0, 0, 0); #region LoadBase //Get the min and max bounds from the vertex positions float lowest; float highest; //Find the bounds of the mesh. iterate through the x, y and z axes for (int axis = 0; axis <= 2; axis++) { lowest = UPPER_LIMIT; //set to inital values that they do not reach highest = LOWER_LIMIT; //iterate through every vertex to find highest and lowest value of this axis for (int i = 0; i < vertices.Count; i++) { if (vertices[i][axis] < lowest) { lowest = vertices[i][axis]; } if (vertices[i][axis] > highest) { highest = vertices[i][axis]; } } if (axis == 0) //x { boundsMin.x = lowest; boundsMax.x = highest; } else if (axis == 1) { boundsMin.y = lowest; boundsMax.y = highest; } else if (axis == 2) { boundsMin.z = lowest; boundsMax.z = highest; } } bool sucess; sucess = navData.LoadBase(tileX, tileZ, tileLayer, tileUserId, boundsMin, boundsMax, xzCellSize, yCellSize, walkableHeight, walkableRadius, walkableStep, bvTreeEnabled); if (!sucess) { ReportError("Error, LoadBase returned false"); } #endregion #region LoadPolys vertCount = vertices.Count; polyCount = faces.Count; //Convert vertices from world space to grid space polyVerts = new ushort[vertCount * 3]; for (int i = 0; i < vertCount; i++) { polyVerts[3 * i + 0] = (ushort)Math.Round((vertices[i].x - boundsMin.x) / xzCellSize); polyVerts[3 * i + 1] = (ushort)Math.Round((vertices[i].y - boundsMin.y) / yCellSize); polyVerts[3 * i + 2] = (ushort)Math.Round((vertices[i].z - boundsMin.z) / xzCellSize); } //build polys array (http://www.critterai.org/projects/cainav/doc/html/B8C2F0F4.htm) polys = new ushort[6 * polyCount]; int ind = 0; int faceNo = 0; while (faceNo < polyCount) { polys[ind + 0] = faces[faceNo].x; polys[ind + 1] = faces[faceNo].y; polys[ind + 2] = faces[faceNo].z; polys[ind + 3] = neighborPolys[faceNo].x; polys[ind + 4] = neighborPolys[faceNo].y; polys[ind + 5] = neighborPolys[faceNo].z; ind += 6; faceNo++; } //Fill polyflags array with default flags polyFlags = new ushort[polyCount]; for (int i = 0; i < polyCount; i++) { polyFlags[i] = 1; //custom user flag } //Fill polyAreas array polyAreas = new byte[polyCount]; for (int i = 0; i < polyCount; i++) { polyAreas[i] = 1; } sucess = navData.LoadPolys(polyVerts, vertCount, polys, polyFlags, polyAreas, polyCount); if (!sucess) { ReportError("Error, LoadPolys returned false"); } #endregion //Build the Navmesh using the navData NavStatus status = Navmesh.Create(navData, out navmesh); if (status != NavStatus.Sucess) { ReportError("Navmesh build status was " + status.ToString()); } return(navmesh); }
/// <summary> /// Creates a standard <see cref="NavmeshTileBuildData"/> object from the provided /// parameters. /// </summary> /// <remarks> /// <para> /// Errors will be logged to the build context. /// </para> /// </remarks> /// <param name="tx">The x-index of the tile.</param> /// <param name="tz">The z-index of the tile.</param> /// <param name="polyMesh">The polygon mesh data.</param> /// <param name="detailMesh">The detail mesh data. (Optional)</param> /// <param name="connections">The off-mesh connections. (Null allowed.)</param> /// <param name="bvTreeEnabled">True if bounding volumes should be generated.</param> /// <param name="context">The build context.</param> /// <returns>The tile build data, or null on error.</returns> public static NavmeshTileBuildData GetBuildData(BuildContext context , int tx, int tz , PolyMeshData polyMesh, PolyMeshDetailData detailMesh , ConnectionSet connections , bool bvTreeEnabled) { if (context == null) { // Silent. return(null); } Vector3[] verts = null; float[] radii = null; byte[] dirs = null; byte[] areas = null; ushort[] flags = null; uint[] userIds = null; Vector3 bmin = polyMesh.boundsMin; Vector3 bmax = polyMesh.boundsMax; int connCount = (connections == null) ? 0 : connections.GetConnections(bmin.x, bmin.z, bmax.x, bmax.z , out verts, out radii, out dirs, out areas, out flags, out userIds); NavmeshTileBuildData result = new NavmeshTileBuildData( polyMesh.vertCount , polyMesh.polyCount , polyMesh.maxVertsPerPoly , (detailMesh == null ? 0 : detailMesh.vertCount) , (detailMesh == null ? 0 : detailMesh.triCount) , connCount); if (!result.LoadBase(tx, tz, 0, 0 , polyMesh.boundsMin , polyMesh.boundsMax , polyMesh.xzCellSize , polyMesh.yCellSize , polyMesh.walkableHeight , polyMesh.walkableRadius , polyMesh.walkableStep , bvTreeEnabled)) { context.LogError("Base data load failed. Bad configuration data or internal error." , null); return(null); } if (!result.LoadPolys(polyMesh.verts , polyMesh.vertCount , polyMesh.polys , polyMesh.flags , polyMesh.areas , polyMesh.polyCount)) { context.LogError("Polygon load failed. Bad mesh data or internal error.", null); return(null); } if (detailMesh != null) { if (!result.LoadDetail(detailMesh.verts , detailMesh.vertCount , detailMesh.tris , detailMesh.triCount , detailMesh.meshes , detailMesh.meshCount)) { context.LogError("Detail load failed. Bad mesh data or internal error.", null); return(null); } } if (connCount > 0) { if (!result.LoadConns(verts, radii, dirs, areas, flags, userIds, connCount)) { context.LogError("Off-mesh connection load failed. Bad data or internal error." , null); return(null); } } return(result); }