private void BuildContours() { ContourSet cset = ContourSet.Build(mBuildContext , mBuildContext.CompactField , mConfig.EdgeMaxDeviation , mConfig.MaxEdgeLength , mConfig.ContourOptions); if (cset == null) { FinalizeAbort("Aborted at contour set build."); return; } if (cset.Count < 1) { FinalizeNoResult("Completed after contour build. No useable contours generated."); return; } mBuildContext.Contours = cset; if (PostProcess() && PostContoursCheck() && PostCompactFieldCheck()) { mBuildContext.Log("Built contour set. Contour count: " + cset.Count, this); mState = NMGenState.PolyMeshBuild; } }
/// <summary> /// Returns human friendly text for the specified state. /// </summary> /// <param name="state">The state.</param> /// <returns>Human friendly text.</returns> public static string ToLabel(NMGenState state) { switch (state) { case NMGenState.Aborted: return("Aborted."); case NMGenState.CompactFieldBuild: return("Building compact heightfield."); case NMGenState.Complete: return("Complete"); case NMGenState.ContourBuild: return("Building contours."); case NMGenState.DetailMeshBuild: return("Building detail mesh."); case NMGenState.HeightfieldBuild: return("Building heightfield."); case NMGenState.PolyMeshBuild: return("Building polygon mesh."); case NMGenState.RegionBuild: return("Building regions."); case NMGenState.NoResult: return("No result."); } return("Unhandled state: " + state); }
private void FinalizeComplete() { if ((mResultOptions & NMGenAssetFlag.Heightfield) == 0 && mBuildContext.Heightfield != null) { mBuildContext.Heightfield.RequestDisposal(); mBuildContext.Heightfield = null; } if ((mResultOptions & NMGenAssetFlag.CompactField) == 0 && mBuildContext.CompactField != null) { mBuildContext.CompactField.RequestDisposal(); mBuildContext.CompactField = null; } if ((mResultOptions & NMGenAssetFlag.ContourSet) == 0 && mBuildContext.Contours != null) { mBuildContext.Contours.RequestDisposal(); mBuildContext.Contours = null; } // Polymesh is always kept. if ((mResultOptions & NMGenAssetFlag.DetailMesh) == 0 && mBuildContext.DetailMesh != null) { mBuildContext.DetailMesh.RequestDisposal(); mBuildContext.DetailMesh = null; } mBuildContext.Log("Build Complete. Result: " + mResultOptions, this); mState = NMGenState.Complete; }
/// <summary> /// Process the build context. /// </summary> /// <remarks> /// <para> /// The flags will be applied during the <see cref="NMGenState.PolyMeshBuild"/> /// state. /// </para> /// </remarks> /// <param name="state">The current build state.</param> /// <param name="context">The context to process.</param> /// <returns>False on error, otherwise true.</returns> public override bool ProcessBuild(NMGenContext context, NMGenState state) { if (state != NMGenState.PolyMeshBuild) { return(true); } PolyMesh mesh = context.PolyMesh; PolyMeshData data = mesh.GetData(false); if (data.polyCount == 0) { return(true); } bool applied = false; for (int i = 0; i < mAreas.Length; i++) { byte area = mAreas[i]; ushort flags = mFlags[i]; int marked = 0; for (int iPoly = 0; iPoly < data.polyCount; iPoly++) { if (data.areas[iPoly] == area) { data.flags[iPoly] |= flags; marked++; } } if (marked > 0) { string msg = string.Format( "{0} : Added '0x{1:X}' flag(s) to {2} poylgons assigned to area {3}." , Name, flags, marked, area); context.Log(msg, this); applied = true; } } if (applied) { mesh.Load(data); } else { context.Log(Name + ": No flags applied.", this); } return(true); }
/// <summary> /// Runs all the processors in order of priority. (Ascending) /// </summary> /// <remarks> /// <para> /// A return value of false indicates the build should be aborted. /// </para> /// </remarks> /// <param name="state">The current state of the build.</param> /// <param name="context">The build context.</param> /// <returns>False if the build should abort.</returns> public bool Process(NMGenContext context, NMGenState state) { foreach (INMGenProcessor p in mProcessors) { if (!p.ProcessBuild(context, state)) { return(false); } } return(true); }
/// <summary> /// Performs a single build step. /// </summary> /// <remarks> /// <para> /// The result state will represent either a finished state or the build step that /// will be performed during the next call to the method. /// </para> /// </remarks> /// <returns>The state at the end of the build step.</returns> public NMGenState Build() { switch (mState) { case NMGenState.Initialized: mBuildContext.Log("Build: " + mTileText, this); mProcessors.LogProcessors(mBuildContext); mState = NMGenState.HeightfieldBuild; break; case NMGenState.HeightfieldBuild: //UnityEngine.Debug.Log("hfb"); BuildHeightfield(); break; case NMGenState.CompactFieldBuild: //UnityEngine.Debug.Log("cfb"); BuildCompactField(); break; case NMGenState.RegionBuild: //UnityEngine.Debug.Log("rb"); BuildRegions(); break; case NMGenState.ContourBuild: //UnityEngine.Debug.Log("cb"); BuildContours(); break; case NMGenState.PolyMeshBuild: BuildPolyMesh(); break; case NMGenState.DetailMeshBuild: BuildDetailMesh(); break; } return(mState); }
/// <summary> /// Process the build context. /// </summary> /// <remarks> /// <para> /// The flags will be applied during the <see cref="NMGenState.PolyMeshBuild"/> /// state. /// </para> /// </remarks> /// <param name="state">The current build state.</param> /// <param name="context">The context to process.</param> /// <returns>False on error, otherwise true.</returns> public override bool ProcessBuild(NMGenContext context, NMGenState state) { if (state != NMGenState.PolyMeshBuild) return true; PolyMesh mesh = context.PolyMesh; PolyMeshData data = mesh.GetData(false); if (data.polyCount == 0) return true; bool applied = false; for (int i = 0; i < mAreas.Length; i++) { byte area = mAreas[i]; ushort flags = mFlags[i]; int marked = 0; for (int iPoly = 0; iPoly < data.polyCount; iPoly++) { if (data.areas[iPoly] == area) { data.flags[iPoly] |= flags; marked++; } } if (marked > 0) { string msg = string.Format( "{0} : Added '0x{1:X}' flag(s) to {2} poylgons assigned to area {3}." , Name, flags, marked, area); context.Log(msg, this); applied = true; } } if (applied) mesh.Load(data); else context.Log(Name + ": No flags applied.", this); return true; }
/// <summary> /// Process the build context. /// </summary> /// <remarks> /// <para> /// Will be applied during the <see cref="NMGenState.HeightfieldBuild"/> state. /// </para> /// </remarks> /// <param name="state">The current build state.</param> /// <param name="context">The context to process.</param> /// <returns>False on error, otherwise true.</returns> public override bool ProcessBuild(NMGenContext context, NMGenState state) { if (state != NMGenState.HeightfieldBuild) return true; if (context.Heightfield.MarkLowObstaclesWalkable(context , context.Config.WalkableStep)) { context.Log(Name + ": Marked low obstacles as walklable.", this); return true; } context.Log(Name + ": Mark low obstacles failed.", this); return false; }
/// <summary> /// Process the build context. /// </summary> /// <remarks> /// <para> /// Will be applied during the <see cref="NMGenState.HeightfieldBuild"/> state. /// </para> /// </remarks> /// <param name="state">The current build state.</param> /// <param name="context">The context to process.</param> /// <returns>False on error, otherwise true.</returns> public override bool ProcessBuild(NMGenContext context, NMGenState state) { if (state != NMGenState.HeightfieldBuild) return true; if (context.Heightfield.MarkLowHeightSpansNotWalkable(context , context.Config.WalkableHeight)) { context.Log(Name + ": Marked low height spans as not walklable.", this); return true; } context.Log(Name + ": Mark low height spans failed.", this); return false; }
/// <summary> /// Process the build context. /// </summary> /// <remarks> /// <para> /// The area will be applied during the <see cref="NMGenState.CompactFieldBuild"/> state. /// </para> /// </remarks> /// <param name="state">The current build state.</param> /// <param name="context">The context to process.</param> /// <returns>False on error, otherwise true.</returns> public override bool ProcessBuild(NMGenContext context, NMGenState state) { if (state != NMGenState.CompactFieldBuild) return true; if (context.CompactField.MarkBoxArea(context, mBoundsMin, mBoundsMax, Area)) { context.Log(string.Format("{0} : Marked box area: Area: {1}, Priority: {2}" , Name, Area, Priority) , this); return true; } context.Log(Name + ": Failed to mark box area.", this); return false; }
private void BuildCompactField() { Heightfield hf = mBuildContext.Heightfield; CompactHeightfield chf = CompactHeightfield.Build(mBuildContext , hf , mConfig.WalkableHeight , mConfig.WalkableStep); if (CanDispose(NMGenAssetFlag.Heightfield)) { hf.RequestDisposal(); mBuildContext.Heightfield = null; } if (chf == null) { FinalizeAbort("Aborted at compact heightfield build."); return; } if (chf.SpanCount < 1) { FinalizeNoResult("Complete at compact heightfield build. No spans."); return; } mBuildContext.CompactField = chf; // Note: Post process is done before eroding the walkable area // so that the processors can stamp additional obstructions into // the heightfield. if (PostProcess() && PostCompactFieldCheck()) { if (mConfig.WalkableRadius > 0) { chf = mBuildContext.CompactField; chf.ErodeWalkableArea(mBuildContext, mConfig.WalkableRadius); mBuildContext.Log("Eroded walkable area by radius: " + mConfig.walkableRadius , this); } mBuildContext.Log("Built compact heightfield. Spans: " + chf.SpanCount, this); mState = NMGenState.RegionBuild; } }
/// <summary> /// Process the build context. /// </summary> /// <remarks> /// <para> /// Will be applied during the <see cref="NMGenState.HeightfieldBuild"/> state. /// </para> /// </remarks> /// <param name="state">The current build state.</param> /// <param name="context">The context to process.</param> /// <returns>False on error, otherwise true.</returns> public override bool ProcessBuild(NMGenContext context, NMGenState state) { if (state != NMGenState.HeightfieldBuild) { return(true); } if (context.Heightfield.MarkLowObstaclesWalkable(context , context.Config.WalkableStep)) { context.Log(Name + ": Marked low obstacles as walklable.", this); return(true); } context.Log(Name + ": Mark low obstacles failed.", this); return(false); }
private void BuildRegions() { CompactHeightfield chf = mBuildContext.CompactField; chf.BuildDistanceField(mBuildContext); mBuildContext.Log("Built distance field. Max Distance: " + chf.MaxDistance, this); if (mConfig.UseMonotone) { if (!chf.BuildRegionsMonotone(mBuildContext , mConfig.BorderSize , mConfig.MinRegionArea , mConfig.MergeRegionArea)) { FinalizeAbort("Monotone region generation failed."); return; } } else { if (!chf.BuildRegions(mBuildContext , mConfig.BorderSize , mConfig.MinRegionArea , mConfig.MergeRegionArea)) { FinalizeAbort("Region generation failed."); return; } } if (PostProcess() && PostCompactFieldCheck()) { if (chf.MaxRegion < 2) { // Null region counts as a region. So expect // at least 2. FinalizeNoResult("Completed after region build. No useable regions formed."); return; } mBuildContext.Log("Generated regions. Region Count: " + chf.MaxRegion, this); // Success. mState = NMGenState.ContourBuild; } }
/// <summary> /// Process the build context. /// </summary> /// <remarks> /// <para> /// Will be applied during the <see cref="NMGenState.HeightfieldBuild"/> state. /// </para> /// </remarks> /// <param name="state">The current build state.</param> /// <param name="context">The context to process.</param> /// <returns>False on error, otherwise true.</returns> public override bool ProcessBuild(NMGenContext context, NMGenState state) { if (state != NMGenState.HeightfieldBuild) { return(true); } if (context.Heightfield.MarkLowHeightSpansNotWalkable(context , context.Config.WalkableHeight)) { context.Log(Name + ": Marked low height spans as not walklable.", this); return(true); } context.Log(Name + ": Mark low height spans failed.", this); return(false); }
/// <summary> /// Process the build context. /// </summary> /// <remarks> /// <para> /// The area will be applied during the <see cref="NMGenState.CompactFieldBuild"/> /// state. /// </para> /// </remarks> /// <param name="state">The current build state.</param> /// <param name="context">The context to process.</param> /// <returns>False on error, otherwise true.</returns> public override bool ProcessBuild(NMGenContext context, NMGenState state) { if (state != NMGenState.CompactFieldBuild) return true; if (context.CompactField.MarkConvexPolyArea(context, verts, ymin, ymax, Area)) { context.Log(string.Format( "{0}: Marked convex polygon area: Area: {1}, Priority: {2}" , Name, Area, Priority) , this); return true; } context.Log(Name + ": Failed to mark convex polygon area.", this); return false; }
/// <summary> /// Process the build context. /// </summary> /// <remarks> /// <para> /// The area will be applied during the <see cref="NMGenState.CompactFieldBuild"/> state. /// </para> /// </remarks> /// <param name="state">The current build state.</param> /// <param name="context">The context to process.</param> /// <returns>False on error, otherwise true.</returns> public override bool ProcessBuild(NMGenContext context, NMGenState state) { if (state != NMGenState.CompactFieldBuild) { return(true); } if (context.CompactField.MarkBoxArea(context, mBoundsMin, mBoundsMax, Area)) { context.Log(string.Format("{0} : Marked box area: Area: {1}, Priority: {2}" , Name, Area, Priority) , this); return(true); } context.Log(Name + ": Failed to mark box area.", this); return(false); }
/// <summary> /// Process the build context. /// </summary> /// <remarks> /// <para> /// The area will be applied during the <see cref="NMGenState.CompactFieldBuild"/> /// state. /// </para> /// </remarks> /// <param name="state">The current build state.</param> /// <param name="context">The context to process.</param> /// <returns>False on error, otherwise true.</returns> public override bool ProcessBuild(NMGenContext context, NMGenState state) { if (state != NMGenState.CompactFieldBuild) return true; if (context.CompactField.MarkCylinderArea(context , mCenterBase, mRadius, mHeight , Area)) { context.Log(string.Format("{0} : Marked box area: Area: {1}, Priority: {2}" , Name, Area, Priority) , this); return true; } context.Log(Name + ": Failed to mark cylinder area.", this); return false; }
/// <summary> /// Process the build context. /// </summary> /// <remarks> /// <para> /// The flags will be applied during the <see cref="NMGenState.PolyMeshBuild"/> state. /// </para> /// </remarks> /// <param name="state">The current build state.</param> /// <param name="context">The context to process.</param> /// <returns>True</returns> public override bool ProcessBuild(NMGenContext context, NMGenState state) { if (state != NMGenState.PolyMeshBuild) return true; PolyMeshData data = context.PolyMesh.GetData(false); for (int i = 0; i < data.flags.Length; i++) { data.flags[i] |= mFlags; } context.PolyMesh.Load(data); context.Log(string.Format("{0}: Applied flag(s) to all polys. Flag(s): 0x{1:X}" , Name, mFlags) , this); return true; }
private IncrementalBuilder(NMGenTileParams tileConfig , NMGenParams config , NMGenAssetFlag resultOptions , InputGeometry source , ProcessorSet processors) { mConfig = config; mTileConfig = tileConfig; mGeometry = source; mProcessors = processors; mResultOptions = resultOptions; mBuildContext = new NMGenContext(tileConfig.TileX, tileConfig.TileZ, mConfig.Clone()); mTileText = string.Format("({0},{1})", tileConfig.TileX, tileConfig.TileZ); mState = NMGenState.Initialized; }
/// <summary> /// Process the build context. /// </summary> /// <remarks> /// <para> /// The area will be applied during the <see cref="NMGenState.CompactFieldBuild"/> /// state. /// </para> /// </remarks> /// <param name="state">The current build state.</param> /// <param name="context">The context to process.</param> /// <returns>False on error, otherwise true.</returns> public override bool ProcessBuild(NMGenContext context, NMGenState state) { if (state != NMGenState.CompactFieldBuild) { return(true); } if (context.CompactField.MarkConvexPolyArea(context, verts, ymin, ymax, Area)) { context.Log(string.Format( "{0}: Marked convex polygon area: Area: {1}, Priority: {2}" , Name, Area, Priority) , this); return(true); } context.Log(Name + ": Failed to mark convex polygon area.", this); return(false); }
/// <summary> /// Process the build context. /// </summary> /// <remarks> /// <para> /// The area will be applied during the <see cref="NMGenState.CompactFieldBuild"/> /// state. /// </para> /// </remarks> /// <param name="state">The current build state.</param> /// <param name="context">The context to process.</param> /// <returns>False on error, otherwise true.</returns> public override bool ProcessBuild(NMGenContext context, NMGenState state) { if (state != NMGenState.CompactFieldBuild) { return(true); } if (context.CompactField.MarkCylinderArea(context , mCenterBase, mRadius, mHeight , Area)) { context.Log(string.Format("{0} : Marked box area: Area: {1}, Priority: {2}" , Name, Area, Priority) , this); return(true); } context.Log(Name + ": Failed to mark cylinder area.", this); return(false); }
/// <summary> /// Process the build context. /// </summary> /// <remarks> /// <para> /// The flags will be applied during the <see cref="NMGenState.PolyMeshBuild"/> state. /// </para> /// </remarks> /// <param name="state">The current build state.</param> /// <param name="context">The context to process.</param> /// <returns>True</returns> public override bool ProcessBuild(NMGenContext context, NMGenState state) { if (state != NMGenState.PolyMeshBuild) { return(true); } PolyMeshData data = context.PolyMesh.GetData(false); for (int i = 0; i < data.flags.Length; i++) { data.flags[i] |= mFlags; } context.PolyMesh.Load(data); context.Log(string.Format("{0}: Applied flag(s) to all polys. Flag(s): 0x{1:X}" , Name, mFlags) , this); return(true); }
/// <summary> /// Returns a progress value associated with the specified state. /// </summary> /// <remarks> /// <para> /// The value will be between 0 and 1.0, suitable for providing build progress feedback. /// </para> /// </remarks> /// <param name="state">The state.</param> /// <returns>A progress value for the state.</returns> public static float ToProgress(NMGenState state) { const float inc = 1 / 6f; switch (state) { case NMGenState.Initialized: return(0); case NMGenState.HeightfieldBuild: return(inc * 1); case NMGenState.CompactFieldBuild: return(inc * 2); case NMGenState.RegionBuild: return(inc * 3); case NMGenState.ContourBuild: return(inc * 4); case NMGenState.PolyMeshBuild: return(inc * 5); case NMGenState.DetailMeshBuild: return(inc * 6); case NMGenState.Complete: return(1); case NMGenState.Aborted: return(1); case NMGenState.NoResult: return(1); } return(1); }
private void BuildHeightfield() { int width; int depth; NMGen.DeriveSizeOfCellGrid(mTileConfig.BoundsMin , mTileConfig.BoundsMax , mConfig.XZCellSize , out width , out depth); Heightfield hf = Heightfield.Create(width, depth , mTileConfig.BoundsMin, mTileConfig.BoundsMax , mConfig.XZCellSize, mConfig.YCellSize); hf.AddTriangles(mBuildContext , mGeometry.Mesh , mTileConfig.boundsMin , mTileConfig.boundsMax , mConfig.WalkableStep); // Merge for any spans less than step. if (hf.GetSpanCount() < 1) { FinalizeNoResult("Complete at heightfield build. No spans."); return; } mBuildContext.Heightfield = hf; if (PostProcess() && PostHeightfieldCheck()) { mBuildContext.Log("Voxelized triangles. Span count: " + hf.GetSpanCount(), this); mState = NMGenState.CompactFieldBuild; } }
private void BuildPolyMesh() { ContourSet cset = mBuildContext.Contours; PolyMesh polyMesh = PolyMesh.Build(mBuildContext , cset , mConfig.MaxVertsPerPoly , mConfig.WalkableHeight , mConfig.WalkableRadius , mConfig.WalkableStep); if (CanDispose(NMGenAssetFlag.ContourSet)) { cset.RequestDisposal(); mBuildContext.Contours = null; } if (polyMesh == null) { FinalizeAbort("Aborted at poly mesh build."); return; } if (polyMesh.PolyCount < 1) { FinalizeNoResult("Aborted after poly mesh build. No polygons generated."); return; } mBuildContext.PolyMesh = polyMesh; if (PostProcess() & PostPolyMeshCheck() & PostCompactFieldCheck()) { mBuildContext.Log("Built poly mesh. PolyCount: " + polyMesh.PolyCount, this); mState = NMGenState.DetailMeshBuild; } }
/// <summary> /// Process the build context. /// </summary> /// <param name="state">The current build state.</param> /// <param name="context">The context to process.</param> /// <returns>False if the build should abort. Otherwise true.</returns> public abstract bool ProcessBuild(NMGenContext context, NMGenState state);
/// <summary> /// Runs all the processors in order of priority. (Ascending) /// </summary> /// <remarks> /// <para> /// A return value of false indicates the build should be aborted. /// </para> /// </remarks> /// <param name="state">The current state of the build.</param> /// <param name="context">The build context.</param> /// <returns>False if the build should abort.</returns> public bool Process(NMGenContext context, NMGenState state) { foreach (INMGenProcessor p in mProcessors) { if (!p.ProcessBuild(context, state)) return false; } return true; }
private void FinalizeAbort(string message) { mBuildContext.Log(message, this); DisposeAssets(); mState = NMGenState.Aborted; }
private void FinalizeNoResult(string message) { mBuildContext.Log(message, this); DisposeAssets(); mState = NMGenState.NoResult; }
/// <summary> /// Performs a single build step. /// </summary> /// <remarks> /// <para> /// The result state will represent either a finished state or the build step that /// will be performed during the next call to the method. /// </para> /// </remarks> /// <returns>The state at the end of the build step.</returns> public NMGenState Build() { switch (mState) { case NMGenState.Initialized: mBuildContext.Log("Build: " + mTileText, this); mProcessors.LogProcessors(mBuildContext); mState = NMGenState.HeightfieldBuild; break; case NMGenState.HeightfieldBuild: //UnityEngine.Debug.Log("hfb"); BuildHeightfield(); break; case NMGenState.CompactFieldBuild: //UnityEngine.Debug.Log("cfb"); BuildCompactField(); break; case NMGenState.RegionBuild: //UnityEngine.Debug.Log("rb"); BuildRegions(); break; case NMGenState.ContourBuild: //UnityEngine.Debug.Log("cb"); BuildContours(); break; case NMGenState.PolyMeshBuild: BuildPolyMesh(); break; case NMGenState.DetailMeshBuild: BuildDetailMesh(); break; } return mState; }
/// <summary> /// Returns human friendly text for the specified state. /// </summary> /// <param name="state">The state.</param> /// <returns>Human friendly text.</returns> public static string ToLabel(NMGenState state) { switch (state) { case NMGenState.Aborted: return "Aborted."; case NMGenState.CompactFieldBuild: return "Building compact heightfield."; case NMGenState.Complete: return "Complete"; case NMGenState.ContourBuild: return "Building contours."; case NMGenState.DetailMeshBuild: return "Building detail mesh."; case NMGenState.HeightfieldBuild: return "Building heightfield."; case NMGenState.PolyMeshBuild: return "Building polygon mesh."; case NMGenState.RegionBuild: return "Building regions."; case NMGenState.NoResult: return "No result."; } return "Unhandled state: " + state; }
/// <summary> /// Returns a progress value associated with the specified state. /// </summary> /// <remarks> /// <para> /// The value will be between 0 and 1.0, suitable for providing build progress feedback. /// </para> /// </remarks> /// <param name="state">The state.</param> /// <returns>A progress value for the state.</returns> public static float ToProgress(NMGenState state) { const float inc = 1 / 6f; switch (state) { case NMGenState.Initialized: return 0; case NMGenState.HeightfieldBuild: return inc * 1; case NMGenState.CompactFieldBuild: return inc * 2; case NMGenState.RegionBuild: return inc * 3; case NMGenState.ContourBuild: return inc * 4; case NMGenState.PolyMeshBuild: return inc * 5; case NMGenState.DetailMeshBuild: return inc * 6; case NMGenState.Complete: return 1; case NMGenState.Aborted: return 1; case NMGenState.NoResult: return 1; } return 1; }