private void HandleClear() { // Abort any in-progress builds. Context.AbortAllReqests("User requested."); TileBuildData tdata = Context.Build.BuildData; int w = tdata.Width; int d = tdata.Depth; bool needsBaking = (tdata.NeedsBakingCount() == 0) ? false : true; for (int tx = 0; tx < w; tx++) { for (int tz = 0; tz < d; tz++) { if (needsBaking) { tdata.ClearUnbaked(tx, tz); } else { tdata.Reset(tx, tz); } } } }
private void OnGUIStatusGrid(float areaHeight) { TileBuildData tdata = Context.Build.BuildData; for (int tx = 0; tx < tdata.Width; tx++) { for (int tz = 0; tz < tdata.Depth; tz++) { Vector3 origin = new Vector3(tx * GridCellSize, areaHeight - tz * GridCellSize); mCellVerts[0] = origin; mCellVerts[1] = origin; mCellVerts[1].x += GridCellSize; mCellVerts[2] = origin; mCellVerts[2].x += GridCellSize; mCellVerts[2].y -= GridCellSize; mCellVerts[3] = origin; mCellVerts[3].y -= GridCellSize; Color c = ToColor(tdata.GetState(tx, tz)); Handles.DrawSolidRectangleWithOutline(mCellVerts, c, Color.black); } } OnGUISelection(areaHeight); }
private void HandleBake() { const string Category = "Bake To Target"; NavmeshBuild build = Context.Build; // Caller checks for null. TileBuildData tdata = build.BuildData; // Double check. if (tdata.BakeableCount() == 0) { Debug.LogWarning(Category + ": No tiles were produced. (All tiles empty?)", build); return; } if (Context.TaskCount > 0) { Debug.LogWarning(Category + ": There are in-progress background builds." + " The tiles associated with these builds will not be baked." + " In-progress builds: " + Context.TaskCount , build); } NavmeshParams nconfig; NavmeshTileData[] tiles; bool success = tdata.GetMeshBuildData(build.TileSetDefinition.BoundsMin , build.TileSetDefinition.TileWorldSize , out nconfig, out tiles); if (!success) { Logger.PostError("Bake to target: Error creating navigation mesh from build data." , Context.Build); return; } NavStatus status = build.BuildTarget.Load(nconfig, tiles, NMBEditorUtil.GetConfig(build)); if ((status & NavStatus.Failure) == 0) { build.BuildData.SetAsBaked(); EditorUtility.SetDirty((Object)build.BuildTarget); } else { Logger.PostError("Bake to target: Target reported failure." , (Object)Context.Build.BuildTarget); } }
private void HandleBuildRequest(bool forceAll) { TileSelection sel = Context.Selection; int w; int d; int ix = 0; int iz = 0; int priority; if (!forceAll && sel.Validate()) { TileZone zone = sel.Zone; w = zone.xmax + 1; d = zone.zmax + 1; ix = zone.xmin; iz = zone.zmin; priority = BuildTaskProcessor.MediumPriority; } else { TileBuildData tdata = Context.Build.BuildData; w = tdata.Width; d = tdata.Depth; priority = BuildTaskProcessor.LowPriority; } // Note: The iteration order appears odd, but it makes for better // progress visualizations. Filling downward. for (int tz = d - 1; tz >= iz; tz--) { for (int tx = ix; tx < w; tx++) { if (!Context.QueueTask(tx, tz, priority--, Logger)) { Logger.PostError(string.Format("Build task failed: ({0},{1})", tx, tz) , Context.Build); } } } }
public static bool OnGUIStandardButtons(ControlContext context , DebugViewContext debugContext , bool resetAllowed) { NavmeshBuild build = context.Build; if (!build) { return(false); } TileBuildData tdata = build.BuildData; GUILayout.FlexibleSpace(); // Note: It is assumed that you should't get any debug display options unless // you can reset the build. So they are inside this condition. if (resetAllowed) { if (build.BuildState == NavmeshBuildState.Buildable || build.HasInputData) { // One or more debug display options are allowed. GUILayout.Label("Show"); } // Always call these. debugContext.OnGUIMeshDisplayOptions(); debugContext.OnGUIDebugExtras(); GUILayout.Space(MarginSize); GUIStyle style = (tdata != null && tdata.NeedsBakingCount() == 0) ? ControlUtil.HighlightedButton : GUI.skin.button; return(OnGUIResetButton(context, debugContext, style)); } return(false); }
protected override void OnGUIButtons() { DebugContext.SetViews(ViewOption.Mesh); NavmeshBuild build = Context.Build; if (!build) { // Build deleted. return; } TileBuildData tdata = Context.Build.BuildData; TileBuildState bstate = tdata.GetState(0, 0); bool canBake = (bstate == TileBuildState.Built || bstate == TileBuildState.Baked); bool isBuilding = (Context.TaskCount > 0); ControlUtil.BeginButtonArea(Context.ButtonArea); EditorGUIUtility.LookLikeControls(75); bool guiEnabled = GUI.enabled; GUI.enabled = !isBuilding; GUIStyle style = (canBake || isBuilding) ? GUI.skin.button : ControlUtil.HighlightedButton; if (GUILayout.Button("Build", style)) { mProgress = 0; mLastTime = EditorApplication.timeSinceStartup; if (!Context.QueueTask(0, 0, BuildTaskProcessor.LowPriority, Logger)) { Logger.PostError("Build task failed.", Context.Build); } } GUI.enabled = !isBuilding && canBake; style = GUI.enabled ? ControlUtil.HighlightedButton : GUI.skin.button; if (GUILayout.Button("Bake", style)) { HandleBake(); } GUILayout.Space(ControlUtil.MarginSize); GUI.enabled = isBuilding; if (GUILayout.Button("Abort Build")) { Context.AbortAllReqests("User requested."); } GUI.enabled = guiEnabled; if (OnGUIStandardButtons()) { // Special case. Build was discarded. ControlUtil.EndButtonArea(); return; } ControlUtil.OnGUIStandardButtons(Context, DebugContext, true); ControlUtil.EndButtonArea(); }
private void HandleWorkingNavmesh(TileSelection selection) { NavmeshBuild build = selection.Build; TileBuildData tdata = build.BuildData; if (mDebugObject == null) { Navmesh navmesh = null; if (tdata.BakeableCount() == 0) { // Nothing to display. return; } bool success = true; TileSetDefinition tdef = build.TileSetDefinition; NavmeshParams nconfig; NavmeshTileData[] tiles; if (tdef == null) { tiles = new NavmeshTileData[1] { tdata.GetTileData(0, 0) }; nconfig = NavUtil.DeriveConfig(tiles[0]); } else { TileZone zone; if (selection.HasSelection) { zone = selection.Zone; } else { zone = new TileZone(0, 0, tdef.Width - 1, tdef.Depth - 1); } success = tdata.GetMeshBuildData(tdef.BoundsMin.ToUnityVector3(), tdef.TileWorldSize, zone , out nconfig, out tiles); } NavStatus status = NavStatus.Sucess; if (success) { status = Navmesh.Create(nconfig, out navmesh); if ((status & NavStatus.Failure) == 0) { foreach (NavmeshTileData tile in tiles) { uint trash; status = navmesh.AddTile(tile, Navmesh.NullTile, out trash); if ((status & NavStatus.Sucess) == 0) { navmesh = null; break; } } } } if ((status & NavStatus.Sucess) == 0) { Show = MeshDebugOption.None; // Use property. Debug.LogError("Mesh Debug View: Error creating working navigation mesh: " + status + ". Disabled display.", build); } else { mDebugObject = navmesh; } } if (mDebugObject != null) { Navmesh nm = ( Navmesh )mDebugObject; NavDebug.Draw(nm, NavmeshSceneDraw.Instance.ColorByArea); } }
public void OnRenderObject(NavmeshBuild build, TileSelection selection) { if (!build) { return; } TileBuildData tdata = build.BuildData; if (!mEnabled || mShow == MeshDebugOption.None || tdata == null || // This restriction is appropriate. build != selection.Build) // Important error check. { return; } INavmeshData target = build.BuildTarget; if (target != null && target.HasNavmesh && NavmeshSceneDraw.Instance.IsShown(target)) { // Don't overdraw the target mesh's display. It has priority. return; } if (tdata.Version != mLastVersion) { // Build data has changed. Clear debug object. mLastVersion = tdata.Version; mDebugObject = null; } int tx = 0; int tz = 0; int size = 0; if (tdata.IsTiled) { tx = selection.SelectedX; tz = selection.SelectedZ; size = selection.ZoneSize; } if (mLastX != tx || mLastZ != tz || mLastSize != size) { // Change in selection. Clear debug object. mLastX = tx; mLastZ = tz; mLastSize = size; mDebugObject = null; // Debug.Log("Clear debug on selection change."); } if (mShow == MeshDebugOption.WorkingMesh) { HandleWorkingNavmesh(selection); return; } else if (tdata.IsTiled && !selection.Validate()) { // The mesh is tiled with no valid selection. // Can't display any of the other meshes. mLastX = -1; mLastZ = -1; mLastSize = -1; return; } // Can only display a single tile for all other display options. // Choose the tile to display. switch (mShow) { case MeshDebugOption.PolyMesh: HandlePolyMesh(build, tx, tz); break; case MeshDebugOption.Detailmesh: HandleDetailMesh(build, tx, tz); break; case MeshDebugOption.InputGeometry: if (build.TileSetDefinition != null) { HandleInputGeom(build, tx, tz); } break; } }
private void ManageTileRequests() { TileBuildData tdata = mContext.Build.BuildData; mLogger.ResetLog(); // Due to concurrency with the input build, this method // does not log things via the context. List <TileBuildTask> requests = mContext.TileTasks; for (int i = requests.Count - 1; i >= 0; i--) { TileBuildTask item = requests[i]; if (item.IsFinished) { requests.RemoveAt(i); NavmeshBuild build = mContext.Build; if (!build) { // Asset was deleted. continue; } string tileText = string.Format("({0},{1})", item.TileX, item.TileZ); switch (item.TaskState) { case BuildTaskState.Aborted: mLogger.Log(item.Messages); mLogger.PostError("Tile build failed: " + tileText, build); tdata.SetAsFailed(item.TileX, item.TileZ); break; case BuildTaskState.Complete: TileBuildAssets r = item.Result; string msg; if (r.NoResult) { msg = "Tile build complete. Tile is empty: " + tileText; tdata.SetAsEmpty(r.TileX, r.TileZ); } else { msg = "Tile build complete: " + tileText; tdata.SetWorkingData(r.TileX, r.TileZ, r.Tile, r.PolyCount); } mLogger.PostTrace(msg, item.Messages, build); break; } } } }
private void ManageNMGenRequests() { mLogger.ResetLog(); TileBuildData tdata = mContext.Build.BuildData; // Due to concurrency with the input build, this method // does not log things via the context. List <NMGenTask> requests = mContext.NMGenTasks; for (int i = requests.Count - 1; i >= 0; i--) { NMGenTask item = requests[i]; if (item.IsFinished) { requests.RemoveAt(i); NavmeshBuild build = mContext.Build; if (!build) { // Asset was deleted. continue; } string tileText = string.Format("({0},{1})", item.TileX, item.TileZ); switch (item.TaskState) { case BuildTaskState.Aborted: mLogger.Log(item.Messages); mLogger.PostError("Tile build failed: " + tileText, build); tdata.SetAsFailed(item.TileX, item.TileZ); break; case BuildTaskState.Complete: NMGenAssets r = item.Result; if (r.NoResult) { mLogger.PostTrace("NMGen build complete. Tile is empty: " + tileText , item.Messages, build); tdata.SetAsEmpty(r.TileX, r.TileZ); } else { tdata.SetWorkingData(r.TileX, r.TileZ, r.PolyMesh, r.DetailMesh); mLogger.PostTrace("NMGen build complete: " + tileText , item.Messages, build); mContext.QueueTask(mLogger, r.TileX, r.TileZ , r.PolyMesh, r.DetailMesh , (build.Config.BuildFlags & NMGenBuildFlag.BVTreeEnabled) != 0 , item.Priority); } break; } } else if (item.TaskState == BuildTaskState.InProgress && tdata.GetState(item.TileX, item.TileZ) != TileBuildState.InProgress) { // Transition to the in-progress state. tdata.SetAsInProgress(item.TileX, item.TileZ); } } }
protected override void OnGUIMain() { if (mBlackLabel == null) { // Need to initialize shared style. mBlackLabel = new GUIStyle(GUI.skin.label); mBlackLabel.normal.textColor = Color.black; mBlackLabel.fontStyle = FontStyle.Bold; } TileSelection selection = Context.Selection; selection.Validate(); TileBuildData tdata = Context.Build.BuildData; if (tdata == null) { return; } Rect mainArea = Context.MainArea; // The box and shift makes it look better. GUI.Box(mainArea, ""); mainArea.x += MarginSize; mainArea.y += MarginSize; // Draw the status grid. // Note: View is expanded by one grid size in order to minimize // grid/slider overlap. Rect view = new Rect(0, 0, tdata.Width * GridCellSize + GridCellSize , tdata.Depth * GridCellSize + GridCellSize); mScrollPos = GUI.BeginScrollView(mainArea, mScrollPos, view); OnGUIStatusGrid(view.height - GridCellSize); GUI.EndScrollView(); OnGUIMainStandard(); if (IsBaseBusy) { return; } // Handle the mouse, including click selection. Event evt = Event.current; Vector2 mousePos = evt.mousePosition; if (mainArea.Contains(mousePos)) { Vector2 gridPos = mousePos; gridPos.x -= mainArea.xMin - mScrollPos.x; gridPos.y -= mainArea.yMin - mScrollPos.y; int x = Mathf.FloorToInt(gridPos.x / GridCellSize); // For the depth, we need to invert the y-axis. int z = tdata.Depth - Mathf.FloorToInt(gridPos.y / GridCellSize) - 1; if (x < tdata.Width && z >= 0 && z < tdata.Depth) { GUI.Label(new Rect(mousePos.x - 20, mousePos.y - 20, 120, 25) , "(" + x + "," + z + "): " + tdata.GetState(x, z) , mBlackLabel); mMouseX = x; mMouseZ = z; if (evt.type == EventType.MouseDown && evt.button == 0) { if (selection.SelectedX == mMouseX && selection.SelectedZ == mMouseZ) { // Clicked on same tile. Deselect. selection.ClearSelection(); } else { selection.SetSelection(mMouseX, mMouseZ); } } } else { mMouseX = TileSelection.NoSelection; mMouseZ = TileSelection.NoSelection; } } else { mMouseX = TileSelection.NoSelection; mMouseZ = TileSelection.NoSelection; } }
protected override void OnGUIButtons() { DebugContext.SetViews(ViewOption.Grid | ViewOption.Selection | ViewOption.Mesh); NavmeshBuild build = Context.Build; if (!build) { return; } TileBuildData tdata = build.BuildData; if (tdata == null) { return; } TileSelection selection = Context.Selection; bool hasSelection = selection.Validate(); bool needBaking = (tdata.NeedsBakingCount() > 0); int activeCount = Context.TaskCount; int bakeableCount = tdata.BakeableCount(); bool origGUIEnabled = GUI.enabled; bool guiEnabled = !IsBaseBusy; GUI.enabled = guiEnabled; ControlUtil.BeginButtonArea(Context.ButtonArea); if (GUILayout.Button("Build All")) { HandleBuildRequest(true); } GUI.enabled = guiEnabled && hasSelection; if (GUILayout.Button("Build Zone")) { HandleBuildRequest(false); } //////////////////////////////////////////////////////////////////// GUILayout.Space(MarginSize); // Only disable baking if there is nothing at all that can be baked. GUI.enabled = guiEnabled && activeCount == 0 && (bakeableCount > 0); GUIStyle style = (bakeableCount > 0 && activeCount == 0) ? ControlUtil.HighlightedButton : GUI.skin.button; if (GUILayout.Button("Bake All", style)) { HandleBake(); } //////////////////////////////////////////////////////////////////// GUILayout.Space(MarginSize); // Note: Technically only the last condition is needed. But checking the // other conditions first saves processing time. GUI.enabled = guiEnabled && activeCount == 0 && tdata.GetStateCount(TileBuildState.NotBuilt) < tdata.Width * tdata.Depth; if (GUILayout.Button((needBaking ? "Revert Unbaked" : "Clear All"))) { HandleClear(); } GUI.enabled = guiEnabled && (activeCount != 0); if (GUILayout.Button("Abort Builds")) { Context.AbortAllReqests("User requested."); } //////////////////////////////////////////////////////////////////// GUILayout.Space(ControlUtil.MarginSize); GUI.enabled = guiEnabled; if (OnGUIStandardButtons()) { // Special case. Build was discarded. ControlUtil.EndButtonArea(); return; } /////////////////////////////////////////////////////////////////// GUILayout.Space(MarginSize); GUI.enabled = guiEnabled && hasSelection; EditorGUIUtility.LookLikeControls(100); selection.ZoneSize = EditorGUILayout.IntField("Zone Size", selection.ZoneSize); EditorGUIUtility.LookLikeControls(); GUI.enabled = guiEnabled; //////////////////////////////////////////////////////////////////// GUILayout.Space(MarginSize); GUILayout.Label("Bakeable Tiles: " + bakeableCount); ControlUtil.OnGUIStandardButtons(Context, DebugContext, true); ControlUtil.EndButtonArea(); GUI.enabled = origGUIEnabled; }
private void OnGUISelection(float areaHeight) { Vector3 origin; int xSize = GridCellSize; int ySize = GridCellSize; TileSelection selection = Context.Selection; if (selection.Validate()) { // Draw the tile marker. origin = new Vector3(selection.SelectedX * GridCellSize , areaHeight - selection.SelectedZ * GridCellSize); mCellVerts[0] = origin; mCellVerts[1] = origin; mCellVerts[1].x += GridCellSize; mCellVerts[2] = origin; mCellVerts[2].x += GridCellSize; mCellVerts[2].y -= GridCellSize; mCellVerts[3] = origin; mCellVerts[3].y -= GridCellSize; Handles.DrawSolidRectangleWithOutline(mCellVerts , Color.clear , new Color(0.93f, 0.58f, 0.11f)); // Orange. TileZone zone = selection.Zone; origin = new Vector3(zone.xmin * GridCellSize, areaHeight - zone.zmin * GridCellSize); xSize = zone.Width * GridCellSize; ySize = zone.Depth * GridCellSize; } else { origin = new Vector3(0, areaHeight); TileBuildData tdata = Context.Build.BuildData; xSize = tdata.Width * GridCellSize; ySize = tdata.Depth * GridCellSize; } mCellVerts[0] = origin; mCellVerts[1] = origin; mCellVerts[1].x += xSize; mCellVerts[2] = origin; mCellVerts[2].x += xSize; mCellVerts[2].y -= ySize; mCellVerts[3] = origin; mCellVerts[3].y -= ySize; Handles.DrawSolidRectangleWithOutline(mCellVerts , Color.clear , ControlUtil.SelectionColor); }
public bool QueueTask(int tx, int tz, int priority, BuildContext logger) { // Check for existing task and purge it. NavmeshBuild build = Build; if (!build) { return(false); } TileBuildData tdata = build.BuildData; if (build.TileSetDefinition == null && (tx > 0 || tz > 0)) { logger.LogError("Tile build requested, but no tile set found.", this); return(false); } if (AbortRequest(tx, tz, "Overriden by new task.")) { tdata.ClearUnbaked(tx, tz); logger.LogWarning(string.Format( "Existing build task overridden by new task. ({0}, {1})" , tx, tz), this); } IncrementalBuilder builder; NMGenConfig config = build.Config; if (build.TileSetDefinition == null) { InputGeometry geom = build.InputGeom; if (geom == null) { logger.LogError("Input geometry not available.", this); tdata.SetAsFailed(tx, tz); return(false); } builder = IncrementalBuilder.Create(config.GetConfig() , config.ResultOptions , geom , build.NMGenProcessors); } else { builder = IncrementalBuilder.Create(tx, tz , config.ResultOptions , build.TileSetDefinition , build.NMGenProcessors); } if (builder == null) { logger.LogError(string.Format("Tile set did not produce a builder. Tile: ({0},{1})" , tx, tz) , this); return(false); } NMGenTask task = NMGenTask.Create(builder, priority); if (!mTaskProcessor.QueueTask(task)) { logger.LogError(string.Format("Processor rejected task. Tile: ({0},{1})" , tx, tz), this); return(false); } mNMGenTasks.Add(task); tdata.SetAsQueued(tx, tz); return(true); }
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 bool BuildMultiTiled() { TileSetDefinition tdef = mBuild.TileSetDefinition; TileBuildData tdata = mBuild.BuildData; mContext.ResetLog(); int total = tdef.Width * tdef.Depth; string msg = string.Format("Multi-tile build: {0} tiles ({1}x{2})" , total, tdef.Width, tdef.Depth); mContext.Log(msg, mBuild); int count = 0; // For the progress bar. for (int tx = 0; tx < tdef.Width; tx++) { for (int tz = 0; tz < tdef.Depth; tz++) { count++; string tileText = string.Format("({0},{1})", tx, tz); if (EditorUtility.DisplayCancelableProgressBar("Multi-tiled Build & Bake" , string.Format("Tile: {0} ({1} of {2})", tileText, count, total) , (float)count / total)) { return(false); } // Create the NMGen builder. IncrementalBuilder builder = IncrementalBuilder.Create(tx, tz , mBuild.Config.ResultOptions , mBuild.TileSetDefinition , mBuild.NMGenProcessors); if (builder == null) { mContext.PostError( "Unexpected failure creating NMGen builder: Tile: " + tileText , mBuild); tdata.SetAsFailed(tx, tz); return(false); } mBuild.BuildData.SetAsInProgress(tx, tz); // Create and run the build task. NMGenTask ntask = NMGenTask.Create(builder, 0); ntask.Run(); if (ntask.TaskState == BuildTaskState.Aborted) { mContext.PostError("NMGen build task failed: Tile: " + tileText , ntask.Messages, mBuild); tdata.SetAsFailed(tx, tz); return(false); } NMGenAssets nr = ntask.Result; if (nr.NoResult) { mContext.PostTrace("NMGen complete. Empty tile: " + tileText , builder.GetMessages() , mBuild); tdata.SetAsEmpty(tx, tz); continue; } msg = string.Format("NMGen complete. Tile {0} has {1} polygons." , tileText, nr.PolyMesh.PolyCount); mContext.PostTrace(msg, builder.GetMessages(), mBuild); TileBuildTask ttask = TileBuildTask.Create(tx, tz , nr.PolyMesh.GetData(false), nr.DetailMesh.GetData(false) , mBuild.Connections , (mBuild.Config.BuildFlags & NMGenBuildFlag.BVTreeEnabled) != 0 , false, 0); ttask.Run(); if (ttask.TaskState == BuildTaskState.Aborted) { mContext.PostError("Tile build task failed: Tile: " + tileText , ttask.Messages , mBuild); tdata.SetAsFailed(tx, tz); return(false); } TileBuildAssets tr = ttask.Result; tdata.SetWorkingData(tx, tz, nr.PolyMesh, nr.DetailMesh); tdata.SetWorkingData(tx, tz, tr.Tile, tr.PolyCount); } } int bakeable = tdata.BakeableCount(); if (bakeable == 0) { mContext.PostError("Build did not produce any usuable tiles. (All tiles empty?)" , mBuild); return(false); } msg = string.Format("Tile build complete. {0} tiles produced. {1} empty tiles." , bakeable, tdata.GetStateCount(TileBuildState.Empty)); mContext.PostTrace(msg, mBuild); return(true); }
public static void OnGUIPrimary(NavmeshBuild build , NMGenConfig config , bool includeSlope) { if (!build) { return; } bool guiEnabled = GUI.enabled; EditorGUIUtility.LookLikeControls(155); float xz = config.XZCellSize; float y = config.YCellSize; float a = xz * xz; float effective; ////////////////////////////////////////////////////////////// GUILayout.Label("Agent Settings"); GUILayout.Space(MarginSize); TileBuildData tdata = build.BuildData; GUI.enabled = guiEnabled && (tdata == null); effective = (float)Mathf.Ceil(config.WalkableHeight / y) * y; config.WalkableHeight = EditorGUILayout.FloatField( NMGenConfig.HeightLabel + Effective(effective) , config.WalkableHeight); effective = (float)Mathf.Floor(config.WalkableStep / y) * y; config.WalkableStep = EditorGUILayout.FloatField( NMGenConfig.StepLabel + Effective(effective) , config.WalkableStep); effective = (float)Mathf.Ceil(config.WalkableRadius / xz) * xz; config.WalkableRadius = EditorGUILayout.FloatField( NMGenConfig.RadiusLabel + Effective(effective) , config.WalkableRadius); GUI.enabled = guiEnabled; if (includeSlope) { config.WalkableSlope = EditorGUILayout.FloatField( NMGenConfig.SlopeLabel , config.WalkableSlope); } ///////////////////////////////////////////////////////////////// GUILayout.Space(2 * MarginSize); GUILayout.Label("Resolution and Tile Settings"); GUILayout.Space(MarginSize); GUI.enabled = guiEnabled && (tdata == null); config.XZCellSize = EditorGUILayout.FloatField( NMGenConfig.XZSizeLabel , config.XZCellSize); config.YCellSize = EditorGUILayout.FloatField( NMGenConfig.YSizeLabel , config.YCellSize); config.TileSize = EditorGUILayout.IntField( NMGenConfig.TileSizeLabel + " (" + config.TileSize * config.XZCellSize + ")" , config.TileSize); config.BorderSize = EditorGUILayout.IntField( NMGenConfig.HFBorderLabel , config.BorderSize); GUI.enabled = guiEnabled; int derBorderSize = NMGenConfig.DeriveBorderSize(config); float derXZ = NMGenConfig.DeriveXZCellSize(config); float derY = NMGenConfig.DeriveYCellSize(config); if ((config.TileSize == 0 && config.BorderSize != derBorderSize) || config.BorderSize < derBorderSize || config.XZCellSize > derXZ || config.YCellSize > derY) { GUILayout.Space(MarginSize); System.Text.StringBuilder sb = new System.Text.StringBuilder(); sb.AppendLine("Recommendations:"); if (config.XZCellSize > derXZ) { sb.AppendLine(NMGenConfig.XZSizeLabel + " of " + derXZ + " or less."); } if (config.YCellSize > derY) { sb.AppendLine(NMGenConfig.YSizeLabel + " of " + derY + " or less."); } if (config.TileSize == 0 && config.BorderSize != derBorderSize) { sb.AppendLine("Border Size of " + derBorderSize + "."); } else if (config.BorderSize < derBorderSize) { sb.AppendLine("Border Size of " + derBorderSize + " or higher."); } GUILayout.Box(sb.ToString().Trim(), EditorUtil.HelpStyle, GUILayout.ExpandWidth(true)); } if (build.HasInputData) { InputGeometry geom = build.InputGeom; Vector3 bmin = geom.BoundsMin; Vector3 bmax = geom.BoundsMax; float w = bmax.x - bmin.x; float d = bmax.z - bmin.z; GUILayout.Space(MarginSize); int tw = Mathf.CeilToInt(w / xz); int td = Mathf.CeilToInt(d / xz); GUILayout.Label(string.Format("Cells: {0:N0} ({1:N0} x {2:N0})" , tw * td, tw, td)); if (config.TileSize > 0) { tw = Mathf.Max(1, Mathf.CeilToInt((float)tw / config.TileSize)); td = Mathf.Max(1, Mathf.CeilToInt((float)td / config.TileSize)); } else { tw = 1; td = 1; } GUILayout.Label(string.Format("Tiles: {0:N0} ({1:N0} x {2:N0})" , tw * td, tw, td)); } ///////////////////////////////////////////////////////////////// GUILayout.Space(2 * MarginSize); GUILayout.Label("Miscellaneous Settings"); GUILayout.Space(MarginSize); config.DetailSampleDistance = EditorGUILayout.FloatField( NMGenConfig.DetailSampleLabel , config.DetailSampleDistance); config.DetailMaxDeviation = EditorGUILayout.FloatField( NMGenConfig.DetailDevLabel , config.DetailMaxDeviation); effective = Mathf.Ceil(config.MinRegionArea / a) * a; config.MinRegionArea = EditorGUILayout.FloatField( NMGenConfig.IslandRegionLabel + Effective(effective) , config.MinRegionArea); }