/// <summary> /// Gets the result of the completed task. /// </summary> /// <param name="result">The result of the completed task.</param> /// <returns>True if the result is available, false if the task should abort with no /// result. (I.e. An internal abort.)</returns> protected override bool GetResult(out TileBuildAssets result) { BuildContext logger = new BuildContext(); result = new TileBuildAssets(); NavmeshTileBuildData tbd = NMBuild.GetBuildData(logger, mTileX, mTileZ , mPolyData, (mDetailData == null ? null : mDetailData), mConnections , mBVTreeEnabled); AddMessages(logger.GetMessages()); if (tbd == null) { return(false); } NavmeshTileData td = NavmeshTileData.Create(tbd); if (td.Size == 0) { AddMessage(string.Format( "Could not create {2} object. Cause unknown." + " Tile: ({0},{1})" , mTileX, mTileZ, td.GetType().Name)); return(false); } result = new TileBuildAssets(mTileX, mTileZ, td, tbd.PolyCount); return(true); }
/// <summary> /// Loads a single-tile navigation mesh from the provided data. /// </summary> /// <param name="buildData">The tile build data.</param> /// <param name="buildConfig">The build information. (Optional)</param> /// <returns>The <see cref="NavStatus"/> flags for the operation.</returns> public NavStatus Load(NavmeshTileBuildData buildData, NavmeshBuildInfo buildConfig) { if (buildData == null || buildData.IsDisposed) { return(NavStatus.Failure | NavStatus.InvalidParam); } Navmesh navmesh; NavStatus status = Navmesh.Create(buildData, out navmesh); if ((status & NavStatus.Sucess) == 0) { return(status); } mDataPack = navmesh.GetSerializedMesh(); if (mDataPack == null) { return(NavStatus.Failure); } mBuildInfo = buildConfig; mVersion++; return(NavStatus.Sucess); }
private bool BuildSingleTile() { TileBuildData tdata = mBuild.BuildData; NMGenConfig config = mBuild.Config; InputGeometry geom = mBuild.InputGeom; mContext.ResetLog(); /* * Design note: * * Not using the build task since it doesn't provide enough progress * feedback for a single tile. * */ // Create the NMGen builder. IncrementalBuilder builder = IncrementalBuilder.Create(config.GetConfig() , config.ResultOptions , geom , mBuild.NMGenProcessors); if (builder == null) { mContext.PostError("Unexpected failure creating NMGen builder.", mBuild); tdata.SetAsFailed(0, 0); return(false); } else if (builder.IsFinished) { if (builder.State == NMGenState.NoResult) { mContext.PostError("NMGen build did not produce a result. (Early exit.)" , builder.GetMessages(), mBuild); tdata.SetAsFailed(0, 0); return(false); } else { mContext.PostError("Unexpected NMGen builder completion." , builder.GetMessages(), mBuild); tdata.SetAsFailed(0, 0); return(false); } } mBuild.BuildData.SetAsInProgress(0, 0); // Run the NMGen builder. while (!builder.IsFinished) { if (EditorUtility.DisplayCancelableProgressBar("Build Single Tile Mesh" , IncrementalBuilder.ToLabel(builder.State) , IncrementalBuilder.ToProgress(builder.State))) { return(false); } builder.Build(); } // Handle NMGen failures. mContext.Log(builder.GetMessages()); // Single tile build. So go ahead an record. switch (builder.State) { case NMGenState.Aborted: mContext.PostError("NMGen build failed.", mBuild); tdata.SetAsFailed(0, 0); return(false); case NMGenState.NoResult: mContext.PostError("NMGen build did not produce a result.", mBuild); tdata.SetAsFailed(0, 0); return(false); } mContext.Log(string.Format("Completed NMGen build: {0} polygons." , builder.Result.PolyMesh.PolyCount) , mBuild); // Build the tile. NMGenAssets result = builder.Result; if (result.DetailMesh == null) { Debug.LogError("result.DetailMesh ==null!"); } NavmeshTileBuildData tbd = org.critterai.nmbuild.NMBuild.GetBuildData( mContext, 0, 0 , result.PolyMesh.GetData(false), result.DetailMesh.GetData(false) , mBuild.Connections , (config.BuildFlags & NMGenBuildFlag.BVTreeEnabled) != 0); if (tbd == null) { // No need to log the error. The above method takes care of that. tdata.SetAsFailed(0, 0); return(false); } NavmeshTileData td = NavmeshTileData.Create(tbd); if (td.Size == 0) { mContext.PostError( "Could not create {0} object. Cause unknown." + typeof(NavmeshTileData) , mBuild); tdata.SetAsFailed(0, 0); return(false); } // Finalize the tile. tdata.SetWorkingData(0, 0, result.PolyMesh, result.DetailMesh); tdata.SetWorkingData(0, 0, td, tbd.PolyCount); mContext.PostTrace("Completed single tile build.", mBuild); return(true); }
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); }
public static extern bool dtnmBuildTileData(NavmeshTileBuildData sourceData , [In, Out] NavmeshTileData resultTile);
public static extern bool dtnmBuildTileData(NavmeshTileBuildData sourceData , [In, Out] NavmeshTileData resultTile);
/// <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); }
public static extern NavStatus dtnmBuildSingleTileMesh( NavmeshTileBuildData buildData , ref IntPtr resultMesh);
public static extern NavStatus dtnmBuildSingleTileMesh( NavmeshTileBuildData buildData , ref IntPtr resultMesh);