public bool QueueTask(BuildContext context , int tx, int tz , PolyMesh polyMesh, PolyMeshDetail detailMesh , bool bvTreeEnabled , int priority) { TileBuildTask task = TileBuildTask.Create(tx, tz , polyMesh.GetData(false) , (detailMesh == null ? null : detailMesh.GetData(true)) , Build.Connections , bvTreeEnabled , true , priority); if (!mTaskProcessor.QueueTask(task)) { context.LogError("Task processor rejected task.", this); return false; } mTileTasks.Add(task); return true; }
/// <summary> /// Erodes the walkable area within the heightfield by the specified radius. /// </summary> /// <remarks> /// <para> /// Basically, any spans that are closer to a boundary or obstruction than the specified /// radius are marked as unwalkable. /// </para> /// <para> /// This method is usually called immediately after the heightfield has been created. /// </para> /// </remarks> /// <param name="context">The context to use during the operation. </param> /// <param name="radius">The radius to apply. [Units: Spans]</param> /// <returns>True if the operation completed successfully.</returns> public bool ErodeWalkableArea(BuildContext context, int radius) { if (IsDisposed) return false; return CompactHeightfieldEx.nmcfErodeWalkableArea(context.root , radius , this); }
/// <summary> /// Builds a detail mesh from the provided polygon mesh. /// </summary> /// <param name="context">The context to use for the operation.</param> /// <param name="polyMesh">The source polygon mesh.</param> /// <param name="field">The compact heightfield used to build the polygon mesh.</param> /// <param name="detailSampleDistance"> /// The sample distance to use when sampling the surface height of the polygon mesh. /// </param> /// <param name="detailMaxDeviation"> /// The maximum the surface of the detail mesh should deviate from the heightfield data. /// </param> /// <returns>A new detail mesh, or null on error.</returns> public static PolyMeshDetail Build(BuildContext context , PolyMesh polyMesh, CompactHeightfield field , float detailSampleDistance, float detailMaxDeviation) { if (context == null || polyMesh == null || polyMesh.IsDisposed || field == null || field.IsDisposed || detailSampleDistance < 0 || detailMaxDeviation < 0) { return null; } PolyMeshDetail result = new PolyMeshDetail(AllocType.External); if (PolyMeshDetailEx.rcpdBuildPolyMeshDetail(context.root , ref polyMesh.root , field , detailSampleDistance , detailMaxDeviation , result)) { return result; } return null; }
/// <summary> /// Builds a contour set from the region outlines in the provided <see cref="CompactHeightfield"/>. /// </summary> /// <remarks> /// <para> /// The raw contours will match the region outlines exactly. The edgeMaxDeviation /// and maxEdgeLength parameters control how closely the simplified contours will match /// the raw contours. /// </para> /// <para> /// Simplified contours are generated such that the vertices for portals between areas /// match up. (They are considered mandatory vertices.) /// </para> /// <para> /// Setting maxEdgeLength to zero will disabled the feature. /// </para> /// </remarks> /// <param name="context">The context to use for the build.</param> /// <param name="field">The field to use for the build.(Must have region data.)</param> /// <param name="edgeMaxDeviation"> /// The maximum distance a simplified edge may deviate from the raw contour's vertices. /// [Limit: >= 0] /// </param> /// <param name="maxEdgeLength"> /// The maximum allowed length of a simplified edge. [Limit: >= 0] /// </param> /// <param name="flags">The build flags.</param> /// <returns>The contour set, or null on failure.</returns> public static ContourSet Build(BuildContext context, CompactHeightfield field , float edgeMaxDeviation, int maxEdgeLength, ContourBuildFlags flags) { if (context == null || field == null || field.IsDisposed || edgeMaxDeviation < 0 || maxEdgeLength < 0) { return null; } ContourSetEx root = new ContourSetEx(); if (ContourSetEx.nmcsBuildSet(context.root, field , edgeMaxDeviation, maxEdgeLength , root , flags)) { return new ContourSet(root); } return null; }
/// <summary> /// Builds the distance field for the heightfield. /// </summary> /// <remarks> /// <para> /// This method must be called before attempting to build region data. /// </para> /// <para> /// The distance data is avaiable via <see cref="MaxDistance"/> and /// <see cref="GetDistanceData"/>. /// </para> /// </remarks> /// <param name="context">The context to use duing the operation.</param> /// <returns>True if the operation completed successfully.</returns> public bool BuildDistanceField(BuildContext context) { if (IsDisposed) return false; return CompactHeightfieldEx.nmcfBuildDistanceField(context.root, this); }
/// <summary> /// Creates a compact open heightfield from a solid heightfield. /// </summary> /// <param name="context">The context to use duing the operation.</param> /// <param name="sourceField"> /// The solid heighfield to derive the compact heightfield from. /// </param> /// <param name="walkableHeight"> /// The minimum floor to ceiling height that is still considered walkable. /// [Limit: >= <see cref="NMGen.MinWalkableHeight"/>] /// </param> /// <param name="walkableStep"> /// The maximum floor to floor step that is still considered walkable.</param> /// <returns>True if the operation completed successfully.</returns> public static CompactHeightfield Build(BuildContext context , Heightfield sourceField , int walkableHeight , int walkableStep) { if (context == null || sourceField == null || sourceField.IsDisposed || walkableHeight < NMGen.MinWalkableHeight || walkableStep < 0) { return null; } CompactHeightfield field = new CompactHeightfield(); if (CompactHeightfieldEx.nmcfBuildField(context.root , walkableHeight , walkableStep , sourceField.root , field)) { return field; } return null; }
/// <summary> /// Voxelizes the triangles in the provided mesh into the heightfield. /// </summary> /// <param name="context">The context to use for the operation</param> /// <param name="mesh">The triangle mesh.</param> /// <param name="areas"> /// The ids of the areas the triangles belong to. /// [Limit: <= <see cref="NMGen.MaxArea"/>] [Size: >= mesh.triCount] /// </param> /// <param name="flagMergeThreshold"> /// The distance where the walkable flag is favored over the non-walkable flag. /// [Limit: >= 0] [Normal: 1] /// </param> /// <returns>True if the operation was successful.</returns> public bool AddTriangles(BuildContext context, TriangleMesh mesh, byte[] areas , int flagMergeThreshold) { if (IsDisposed) return false; return HeightfieldEx.nmhfRasterizeTriMesh(context.root , mesh.verts , mesh.vertCount , mesh.tris , areas , mesh.triCount , root , flagMergeThreshold); }
/// <summary> /// Applies the area to the all spans within the specified convex polygon. /// </summary> /// <remarks> /// <para> /// The y-values of the polygon vertices are ignored. So the polygon is effectively /// projected onto the xz-plane at yMin, then extruded to yMax. /// </para> /// <para> /// The method will return false if the polygon is completely outside of the heightfield. /// </para> /// </remarks> /// <param name="context">The context to use duing the operation.</param> /// <param name="verts">The vertices of the polygon [Length: vertCount]</param> /// <param name="yMin">The height of the base of the polygon.</param> /// <param name="yMax">The height of the top of the polygon.</param> /// <param name="area">The area to apply.</param> /// <returns>True if the operation completed successfully.</returns> public bool MarkConvexPolyArea(BuildContext context , Vector3[] verts, float yMin, float yMax , byte area) { if (IsDisposed) return false; return CompactHeightfieldEx.nmcfMarkConvexPolyArea(context.root , verts , verts.Length , yMin , yMax , area , this); }
/// <summary> /// Marks non-walkable spans as walkable if their maximum is within walkableStep of a /// walkable neighbor. /// </summary> /// <remarks> /// <para> /// Example of test: <c>Math.Abs(currentSpan.Max - neighborSpan.Max) < walkableStep</c> /// </para> /// <para /// >Allows the formation of walkable regions that will flow over low lying objects such /// as curbs, and up structures such as stairways. /// </para> /// </remarks> /// <param name="context">The context to use for the operation</param> /// <param name="walkableStep"> /// The maximum allowed difference between span maximum's for the step to be considered /// waklable. [Limit: > 0] /// </param> /// <returns>True if the operation was successful.</returns> public bool MarkLowObstaclesWalkable(BuildContext context, int walkableStep) { if (IsDisposed) return false; return HeightfieldEx.nmhfFilterLowHangingWalkableObstacles(context.root , walkableStep , root); }
/// <summary> /// Marks walkable spans as not walkable if the clearence above the span is less than the /// specified height. /// </summary> /// <remarks> /// <para> /// For this method, the clearance above the span is the distance from the span's maximum /// to the next higher span's minimum. (Same column.) /// </para> /// </remarks> /// <param name="context">The context to use for the operation</param> /// <param name="walkableHeight"> /// The maximum allowed floor to ceiling height that is considered still walkable. /// [Limit: > <see cref="NMGen.MinWalkableHeight"/>] /// </param> /// <returns>True if the operation was successful.</returns> public bool MarkLowHeightSpansNotWalkable(BuildContext context, int walkableHeight) { if (IsDisposed) return false; return HeightfieldEx.nmhfFilterWalkableLowHeightSpans(context.root , walkableHeight , root); }
/// <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; }
/// <summary> /// Builds polygon mesh from the provided contours. /// </summary> /// <remarks> /// <para> /// The values of the CellSize-based parameters will be converted to world units. /// </para> /// </remarks> /// <param name="context">The context to use for the operation.</param> /// <param name="contours">The contours to use to build the mesh.</param> /// <param name="maxVertsPerPoly"> /// The maximum allowed vertices for a polygon. /// [Limits: 3 <= value <= <see cref="NMGen.MaxAllowedVertsPerPoly"/>] /// </param> /// <param name="walkableHeight"> /// The walkable height used to build the contour data. /// [Limit: >= <see cref="NMGen.MinWalkableHeight"/>] [Units: YCellSize] /// </param> /// <param name="walkableRadius"> /// The radius used to erode the walkable area covered by the contours. /// [Limit: >= 0] [Units: XZCellSize] /// </param> /// <param name="walkableStep"> /// The walkable step used to build /// the contour data. [Limit: >= 0] [Units: YCellSize]</param> /// <returns>The generated polygon mesh, or null if there were errors.</returns> public static PolyMesh Build(BuildContext context, ContourSet contours , int maxVertsPerPoly, int walkableHeight, int walkableRadius, int walkableStep) { if (context == null || contours == null) return null; PolyMesh result = new PolyMesh(AllocType.External); if (!PolyMeshEx.rcpmBuildFromContourSet(context.root , contours.root , maxVertsPerPoly , ref result.root , ref result.mMaxVerts)) { return null; } result.mWalkableHeight = walkableHeight * contours.YCellSize; result.mWalkableRadius = walkableRadius * contours.XZCellSize; result.mWalkableStep = walkableStep * contours.YCellSize; return result; }
/// <summary> /// Creates a builder. /// </summary> /// <remarks> /// <para> /// No validation is performed and the builder will use the parameters directly /// during the build. /// </para> /// <para> /// Builders created using this method are not guarenteed to produce a usable result. /// </para> /// <para> /// It is the responsibility of the caller to ensure thread safely if /// <paramref name="isThreadSafe"/> is set to true. /// </para> /// <para> /// <b>Warning:</b> If walkable slope if greather than zero then the builder will /// apply <see cref="NMGen.ClearUnwalkableTriangles"/> directly to the areas parameter. /// </para> /// </remarks> /// <param name="mesh">The triangle mesh to use for the build.</param> /// <param name="areas">The triangle areas. (Null not permitted.)</param> /// <param name="walkableSlope">The walkable slope. /// (See <see cref="NMGenParams.WalkableSlope"/>)</param> /// <param name="isThreadSafe">True if the builder can run safely on its own thread.</param> /// <returns>A builder, or null on error.</returns> public static InputGeometryBuilder UnsafeCreate(TriangleMesh mesh , byte[] areas , float walkableSlope , bool isThreadSafe) { if (mesh == null || areas == null || mesh.triCount < 0) return null; walkableSlope = System.Math.Min(NMGen.MaxAllowedSlope, walkableSlope); if (walkableSlope > 0) { BuildContext context = new BuildContext(); if (!NMGen.ClearUnwalkableTriangles(context, mesh, walkableSlope, areas)) return null; } ChunkyTriMeshBuilder builder = ChunkyTriMeshBuilder.Create(mesh, areas, 32768); if (builder == null) return null; Vector3 bmin; Vector3 bmax; mesh.GetBounds(out bmin, out bmax); return new InputGeometryBuilder(builder, bmin, bmax, isThreadSafe); }
/// <summary> /// Applies a median filter to the walkable areas. (Removes noise.) /// </summary> /// <param name="context">The context to use duing the operation. </param> /// <returns>True if the operation completed successfully.</returns> public bool ApplyMedianFilter(BuildContext context) { if (IsDisposed) return false; return CompactHeightfieldEx.nmcfMedianFilterWalkableArea(context.root, this); }
/// <summary> /// Voxelizes the triangles from the provided <see cref="ChunkyTriMesh"/> into the /// heightfield. /// </summary> /// <remarks> /// <para> /// The chunks that are voxelized is controled by the bounds parameters. /// </para> /// </remarks> /// <param name="context">The build context.</param> /// <param name="mesh">The mesh.</param> /// <param name="boundsMin">The minimum bounds for the mesh query.</param> /// <param name="boundsMax">The maximum bounds for the mesh query.</param> /// <param name="flagMergeThreshold"> /// The distance where the walkable flag is favored over the non-walkable flag. /// [Limit: >= 0] [Normal: 1] /// </param> /// <returns>True if the operation was successful.</returns> public bool AddTriangles(BuildContext context, ChunkyTriMesh mesh , Vector3 boundsMin, Vector3 boundsMax , int flagMergeThreshold) { if (IsDisposed || mesh == null || mesh.IsDisposed) return false; List<ChunkyTriMeshNode> nodeList = new List<ChunkyTriMeshNode>(); int triCount = mesh.GetChunks(boundsMin.x, boundsMin.z , boundsMax.x, boundsMax.z , nodeList); if (triCount == 0) return true; return HeightfieldEx.nmhfRasterizeNodes(context.root , mesh.verts , mesh.tris , mesh.areas , nodeList.ToArray() , nodeList.Count , root , flagMergeThreshold); }
/// <summary> /// Applies the area to all spans within the specified bounding box. (AABB) /// </summary> /// <remarks> /// <para> /// The method will return false if the AABB is completely outside of the heightfield. /// </para> /// </remarks> /// <param name="context">The context to use duing the operation.</param> /// <param name="boundsMin">The minimum bounds of the AABB.</param> /// <param name="boundsMax">The maximum bounds of the AABB. </param> /// <param name="area">The area to apply.</param> /// <returns>True if the operation completed successfully.</returns> public bool MarkBoxArea(BuildContext context , Vector3 boundsMin, Vector3 boundsMax , byte area) { if (IsDisposed) return false; return CompactHeightfieldEx.nmcfMarkBoxArea(context.root , ref boundsMin , ref boundsMax , area , this); }
/// <summary> /// Voxelizes the provided triangles into the heightfield. /// </summary> /// <remarks> /// <para> /// Unlike many other methods in the library, the arrays must be sized exactly to the /// content. If you need to pass buffers, use the method that takes a /// <see cref="TriangleMesh"/> object. /// </para> /// </remarks> /// <param name="context">The context to use for the operation</param> /// <param name="verts">The vertices. [Length: >= vertCount] (No buffering allowed.)</param> /// <param name="tris"> /// The triangles. [(vertAIndex, vertBIndex, vertCIndex) * triCount] /// </param> /// <param name="areas"> /// The ids of the areas the triangles belong to. /// [Limit: <= <see cref="NMGen.MaxArea"/>] [Size: >= triCount] /// </param> /// <param name="flagMergeThreshold"> /// The distance where the walkable flag is favored over the non-walkable flag. /// [Limit: >= 0] [Normal: 1] /// </param> /// <returns>True if the operation was successful.</returns> public bool AddTriangles(BuildContext context , Vector3[] verts, ushort[] tris, byte[] areas , int flagMergeThreshold) { if (IsDisposed) return false; return HeightfieldEx.nmhfRasterizeTriMeshShort(context.root , verts , verts.Length / 3 , tris , areas , tris.Length / 3 , root , flagMergeThreshold); }
/// <summary> /// Applied the area to all spans within the specified cylinder. /// </summary> /// <remarks> /// <para> /// The method will return false if the cylinder is completely outside of the heightfield. /// </para> /// </remarks> /// <param name="context">The context to use duing the operation.</param> /// <param name="centerBase">The center of the base of the cylinder.</param> /// <param name="radius">The radius of the cylinder.</param> /// <param name="height">The height of the cylinder.</param> /// <param name="area">The area to apply.</param> /// <returns>True if the operation completed successfully.</returns> public bool MarkCylinderArea(BuildContext context , Vector3 centerBase, float radius, float height , byte area) { if (IsDisposed) return false; return CompactHeightfieldEx.nmcfMarkCylinderArea(context.root , ref centerBase , radius , height , area , this); }
/// <summary> /// Voxelizes the provided triangles into the heightfield. /// </summary> /// <param name="context">The context to use for the operation</param> /// <param name="verts">The triangles. [(vertA, vertB, vertC) * triCount]</param> /// <param name="areas"> /// The ids of the areas the triangles belong to. /// [Limit: <= <see cref="NMGen.MaxArea"/>] [Size: >= triCount] /// </param> /// <param name="triCount">The number of triangles in the vertex array.</param> /// <param name="flagMergeThreshold"> /// The distance where the walkable flag is favored over the non-walkable flag. /// [Limit: >= 0] [Normal: 1] /// </param> /// <returns>True if the operation was successful.</returns> public bool AddTriangles(BuildContext context , Vector3[] verts, byte[] areas, int triCount , int flagMergeThreshold) { if (IsDisposed) return false; return HeightfieldEx.nmhfRasterizeTriangles(context.root , verts, areas, triCount , root , flagMergeThreshold); }
/// <summary> /// Builds region data for the heightfield using simple monotone partitioning. /// </summary> /// <remarks> /// <para> /// Non-null regions consist of connected, non-overlapping walkable spans that form a /// single contour. /// </para> /// <para> /// The region data is available via <see cref="MaxRegion"/> and <see cref="GetSpanData"/>. /// </para> /// <para> /// If a region forms an area that is smaller than <paramref name="minRegionArea"/>, /// all spans in the region is set to <see cref="NMGen.NullRegion"/>. /// </para> /// <para> /// Partitioning can result in smaller than necessary regions, especially /// in diagonal corridors. <paramref name="mergeRegionArea"/> helps reduce unecessarily /// small regions. /// </para> /// </remarks> /// <param name="context">The context to use duing the operation.</param> /// <param name="borderSize">The AABB border size to apply.</param> /// <param name="minRegionArea"> /// The minimum area allowed for unconnected (island) regions. [Units: Spans] /// </param> /// <param name="mergeRegionArea"> /// The maximum region size that will be considered for merging with another region. /// [Units: Spans] /// </param> /// <returns>True if the operation completed successfully.</returns> public bool BuildRegionsMonotone(BuildContext context , int borderSize, int minRegionArea, int mergeRegionArea) { if (IsDisposed) return false; return CompactHeightfieldEx.nmcfBuildRegionsMonotone(context.root , this , borderSize , minRegionArea , mergeRegionArea); }
/// <summary> /// Appends messages from the specified context to the current context. /// </summary> /// <param name="fromContext">The context to append the messages from.</param> public void AppendMessages(BuildContext fromContext) { if (fromContext == null || fromContext.MessageCount == 0) return; string[] msgs = fromContext.GetMessages(); foreach (string msg in msgs) { BuildContextEx.nmbcLog(root, msg); } }
/// <summary> /// Set the area of all triangles with a slope below the specified value to /// <see cref="MaxArea"/>. /// </summary> /// <param name="context">The context to use duing the operation.</param> /// <param name="mesh">The source mesh.</param> /// <param name="walkableSlope">The maximum walkable slope.</param> /// <param name="areas"> /// The areas associated with each triangle. [Length: >= mesh.triCount] (In/Out) /// </param> /// <returns>True if the operation was successful.</returns> public static bool MarkWalkableTriangles(BuildContext context, TriangleMesh mesh , float walkableSlope , byte[] areas) { if (mesh == null || context == null || areas == null || areas.Length < mesh.triCount) { return false; } NMGenEx.nmgMarkWalkableTriangles(context.root , walkableSlope , mesh.verts , mesh.vertCount , mesh.tris , mesh.triCount , areas); return true; }
/// <summary> /// Tests the operation of the context by adding up to 100 test messages. /// </summary> /// <remarks> /// <para> /// The only purpose of this method is to permit testing.</para> /// </remarks> /// <param name="context">The context to test.</param> /// <param name="count">The number of messages to add. (Limit <100)</param> public static void LoadTestMessages(BuildContext context, int count) { if (context != null) BuildContextEx.nmgTestContext(context.root, Math.Min(100, count)); }
/// <summary> /// Builds a layer set from the <see cref="CompactHeightfield"/>. /// </summary> /// <param name="context">The context to use duing the operation.</param> /// <param name="field">The source field.</param> /// <returns>The resulting layer set, or null on failure.</returns> public static HeightfieldLayerSet Build(BuildContext context, CompactHeightfield field) { if (context == null) return null; IntPtr ptr = IntPtr.Zero; int layerCount = HeightfieldLayserSetEx.nmlsBuildLayers(context.root , field , field.BorderSize , field.WalkableHeight , ref ptr); if (ptr == IntPtr.Zero) return null; return new HeightfieldLayerSet(ptr, layerCount); }
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; }