static rcSpan allocSpan(rcHeightfield hf) { // If running out of memory, allocate new page and update the freelist. if (hf.freelist == null || hf.freelist.next == null) { // Create new page. // Allocate memory for the new pool. rcSpanPool pool = new rcSpanPool(); pool.next = null; // Add the pool into the list of pools. pool.next = hf.pools; hf.pools = pool; // Add new items to the free list. rcSpan?freelist = hf.freelist; //rcSpan head = pool.items[0]; //rcSpan it = pool.items[RC_SPANS_PER_POOL]; int itIndex = RC_SPANS_PER_POOL; do { --itIndex; pool.items[itIndex].next = freelist; freelist = pool.items[itIndex]; }while (itIndex != 0); hf.freelist = pool.items[itIndex]; } // Pop item from in front of the free list. rcSpan it = hf.freelist; hf.freelist = hf.freelist.next; return(it); }
/// @par /// /// The span addition can be set to favor flags. If the span is merged to /// another span and the new @p smax is within @p flagMergeThr units /// from the existing span, the span flags are merged. /// /// @see rcHeightfield, rcSpan. static void rcAddSpan(rcContext ctx, rcHeightfield hf, int x, int y, ushort smin, ushort smax, byte area, int flagMergeThr) { // Debug.Assert(ctx != null, "rcContext is null"); addSpan(hf, x, y, smin, smax, area, flagMergeThr); }
static void freeSpan(rcHeightfield hf, rcSpan ptr) { if (ptr == null) { return; } // Add the node in front of the free list. ptr.next = hf.freelist; hf.freelist = ptr; }
/// @par /// /// No spans will be added if the triangle does not overlap the heightfield grid. /// /// @see rcHeightfield public static void rcRasterizeTriangle(rcContext ctx, float[] v0, int v0Start, float[] v1, int v1Start, float[] v2, int v2Start, byte area, rcHeightfield solid, int flagMergeThr) { Debug.Assert(ctx != null, "rcContext is null"); ctx.startTimer(rcTimerLabel.RC_TIMER_RASTERIZE_TRIANGLES); float ics = 1.0f/solid.cs; float ich = 1.0f/solid.ch; rasterizeTri(v0, v0Start, v1, v1Start, v2, v2Start, area, solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr); ctx.stopTimer(rcTimerLabel.RC_TIMER_RASTERIZE_TRIANGLES); }
static void addSpan(rcHeightfield hf, int x, int y, ushort smin, ushort smax, byte area, int flagMergeThr) { int idx = x + y * hf.width; rcSpan s = allocSpan(hf); s.smin = smin; s.smax = smax; s.area = area; s.next = null; // Empty cell, add the first span. if (hf.spans ![idx] == null)
/// @par /// /// No spans will be added if the triangle does not overlap the heightfield grid. /// /// @see rcHeightfield public static void rcRasterizeTriangle(rcContext ctx, float[] v0, int v0Start, float[] v1, int v1Start, float[] v2, int v2Start, byte area, rcHeightfield solid, int flagMergeThr) { Debug.Assert(ctx != null, "rcContext is null"); ctx.startTimer(rcTimerLabel.RC_TIMER_RASTERIZE_TRIANGLES); float ics = 1.0f / solid.cs; float ich = 1.0f / solid.ch; rasterizeTri(v0, v0Start, v1, v1Start, v2, v2Start, area, solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr); ctx.stopTimer(rcTimerLabel.RC_TIMER_RASTERIZE_TRIANGLES); }
/// @par /// /// Spans will only be added for triangles that overlap the heightfield grid. /// /// @see rcHeightfield public static void rcRasterizeTriangles(rcContext ctx, float[] verts, byte[] areas, int nt, rcHeightfield solid, int flagMergeThr) { Debug.Assert(ctx != null, "rcContext is null"); ctx.startTimer(rcTimerLabel.RC_TIMER_RASTERIZE_TRIANGLES); float ics = 1.0f / solid.cs; float ich = 1.0f / solid.ch; // Rasterize triangles. for (int i = 0; i < nt; ++i) { int v0Start = (i * 3 + 0) * 3; int v1Start = (i * 3 + 1) * 3; int v2Start = (i * 3 + 2) * 3; // Rasterize. rasterizeTri(verts, v0Start, verts, v1Start, verts, v2Start, areas[i], solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr); } ctx.stopTimer(rcTimerLabel.RC_TIMER_RASTERIZE_TRIANGLES); }
/// @par /// /// Spans will only be added for triangles that overlap the heightfield grid. /// /// @see rcHeightfield public static void rcRasterizeTriangles(rcContext ctx, float[] verts, int nv, int[] tris, byte[] areas, int nt, rcHeightfield solid, int flagMergeThr) { Debug.Assert(ctx != null, "rcContext is null"); ctx.startTimer(rcTimerLabel.RC_TIMER_RASTERIZE_TRIANGLES); float ics = 1.0f/solid.cs; float ich = 1.0f/solid.ch; // Rasterize triangles. for (int i = 0; i < nt; ++i) { int v0Start = tris[i*3+0]*3; int v1Start = tris[i*3+1]*3; int v2Start = tris[i*3+2]*3; // Rasterize. rasterizeTri(verts, v0Start, verts, v1Start, verts, v2Start, areas[i], solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr); } ctx.stopTimer(rcTimerLabel.RC_TIMER_RASTERIZE_TRIANGLES); }
/// @par /// /// For this filter, the clearance above the span is the distance from the span's /// maximum to the next higher span's minimum. (Same grid column.) /// /// @see rcHeightfield, rcConfig public static void rcFilterWalkableLowHeightSpans(rcContext ctx, int walkableHeight, rcHeightfield solid) { Debug.Assert(ctx != null, "rcContext is null"); ctx.startTimer(rcTimerLabel.RC_TIMER_FILTER_WALKABLE); int w = solid.width; int h = solid.height; int MAX_HEIGHT = 0xffff; // Remove walkable flag from spans which do not have enough // space above them for the agent to stand there. for (int y = 0; y < h; ++y) { for (int x = 0; x < w; ++x) { for (rcSpan s = solid.spans[x + y * w]; s != null; s = s.next) { int bot = (int)(s.smax); int top = s.next != null ? (int)(s.next.smin) : MAX_HEIGHT; if ((top - bot) <= walkableHeight) { s.area = RC_NULL_AREA; } } } } ctx.stopTimer(rcTimerLabel.RC_TIMER_FILTER_WALKABLE); }
static void addSpan(rcHeightfield hf, int x, int y, ushort smin, ushort smax, byte area, int flagMergeThr) { int idx = x + y * hf.width; rcSpan s = allocSpan(hf); s.smin = smin; s.smax = smax; s.area = area; s.next = null; // Empty cell, add the first span. if (hf.spans[idx] == null) { hf.spans[idx] = s; return; } rcSpan prev = null; rcSpan cur = hf.spans[idx]; // Insert and merge spans. while (cur != null) { if (cur.smin > s.smax) { // Current span is further than the new span, break. break; } else if (cur.smax < s.smin) { // Current span is before the new span advance. prev = cur; cur = cur.next; } else { // Merge flags. if (Math.Abs((int)s.smax - (int)cur.smax) <= flagMergeThr) { s.area = Math.Max(s.area, cur.area); } // Merge spans. if (cur.smin < s.smin) { s.smin = cur.smin; } if (cur.smax > s.smax) { s.smax = cur.smax; } // Remove current span. rcSpan next = cur.next; freeSpan(hf, cur); if (prev != null) { prev.next = next; } else { hf.spans[idx] = next; } cur = next; } } // Insert new span. if (prev != null) { s.next = prev.next; prev.next = s; } else { s.next = hf.spans[idx]; hf.spans[idx] = s; } }
static void rasterizeTri(float[] v0, int v0Start, float[] v1, int v1Start, float[] v2, int v2Start, byte area, rcHeightfield hf, float[] bmin, float[] bmax, float cs, float ics, float ich, int flagMergeThr) { int w = hf.width; int h = hf.height; float[] tmin = new float[3]; float[] tmax = new float[3]; float by = bmax[1] - bmin[1]; // Calculate the bounding box of the triangle. rcVcopy(tmin, 0, v0, v0Start); rcVcopy(tmax, 0, v0, v0Start); rcVmin(tmin, 0, v1, v1Start); rcVmin(tmin, 0, v2, v2Start); rcVmax(tmax, 0, v1, v1Start); rcVmax(tmax, 0, v2, v2Start); // If the triangle does not touch the bbox of the heightfield, skip the triagle. if (!overlapBounds(bmin, bmax, tmin, tmax)) { return; } // Calculate the footprint of the triangle on the grid's y-axis int y0 = (int)((tmin[2] - bmin[2]) * ics); int y1 = (int)((tmax[2] - bmin[2]) * ics); y0 = rcClamp(y0, 0, h - 1); y1 = rcClamp(y1, 0, h - 1); // Clip the triangle into all grid cells it touches. //float[] buf = new float[7*3*4]; float[] _in = new float[7 * 3]; float[] inrow = new float[7 * 3]; float[] p1 = new float[7 * 3]; float[] p2 = new float[7 * 3]; rcVcopy(_in, 0, v0, v0Start); rcVcopy(_in, 1 * 3, v1, v1Start); rcVcopy(_in, 2 * 3, v2, v2Start); int nvrow = 0; int nvIn = 3; for (int y = y0; y <= y1; ++y) { // Clip polygon to row. Store the remaining polygon as well float cz = bmin[2] + y * cs; dividePoly(_in, nvIn, inrow, ref nvrow, p1, ref nvIn, cz + cs, 2); //rcSwap(_in, p1); float[] tmp = _in; _in = p1; p1 = tmp; if (nvrow < 3) { continue; } // find the horizontal bounds in the row float minX = inrow[0], maxX = inrow[0]; for (int i = 1; i < nvrow; ++i) { if (minX > inrow[i * 3]) { minX = inrow[i * 3]; } if (maxX < inrow[i * 3]) { maxX = inrow[i * 3]; } } int x0 = (int)((minX - bmin[0]) * ics); int x1 = (int)((maxX - bmin[0]) * ics); x0 = rcClamp(x0, 0, w - 1); x1 = rcClamp(x1, 0, w - 1); int nv = 0; int nv2 = nvrow; for (int x = x0; x <= x1; ++x) { // Clip polygon to column. store the remaining polygon as well float cx = bmin[0] + x * cs; dividePoly(inrow, nv2, p1, ref nv, p2, ref nv2, cx + cs, 0); //rcSwap(inrow, p2); tmp = inrow; inrow = p2; p2 = tmp; if (nv < 3) { continue; } // Calculate min and max of the span. float smin = p1[1], smax = p1[1]; for (int i = 1; i < nv; ++i) { smin = Math.Min(smin, p1[i * 3 + 1]); smax = Math.Max(smax, p1[i * 3 + 1]); } smin -= bmin[1]; smax -= bmin[1]; // Skip the span if it is outside the heightfield bbox if (smax < 0.0f) { continue; } if (smin > by) { continue; } // Clamp the span to the heightfield bbox. if (smin < 0.0f) { smin = 0; } if (smax > by) { smax = by; } // Snap the span to the heightfield height grid. ushort ismin = (ushort)rcClamp((int)Math.Floor(smin * ich), 0, RC_SPAN_MAX_HEIGHT); ushort ismax = (ushort)rcClamp((int)Math.Ceiling(smax * ich), (int)ismin + 1, RC_SPAN_MAX_HEIGHT); addSpan(hf, x, y, ismin, ismax, area, flagMergeThr); } } }
static void addSpan(rcHeightfield hf, int x, int y, ushort smin, ushort smax, byte area, int flagMergeThr) { int idx = x + y*hf.width; rcSpan s = allocSpan(hf); s.smin = smin; s.smax = smax; s.area = area; s.next = null; // Empty cell, add the first span. if (hf.spans[idx] == null) { hf.spans[idx] = s; return; } rcSpan prev = null; rcSpan cur = hf.spans[idx]; // Insert and merge spans. while (cur != null) { if (cur.smin > s.smax) { // Current span is further than the new span, break. break; } else if (cur.smax < s.smin) { // Current span is before the new span advance. prev = cur; cur = cur.next; } else { // Merge spans. if (cur.smin < s.smin) s.smin = cur.smin; if (cur.smax > s.smax) s.smax = cur.smax; // Merge flags. if (Math.Abs((int)s.smax - (int)cur.smax) <= flagMergeThr){ s.area = Math.Max(s.area, cur.area); } // Remove current span. rcSpan next = cur.next; freeSpan(hf, cur); if (prev != null) prev.next = next; else hf.spans[idx] = next; cur = next; } } // Insert new span. if (prev != null) { s.next = prev.next; prev.next = s; } else { s.next = hf.spans[idx]; hf.spans[idx] = s; } }
/// @par /// /// For this filter, the clearance above the span is the distance from the span's /// maximum to the next higher span's minimum. (Same grid column.) /// /// @see rcHeightfield, rcConfig public static void rcFilterWalkableLowHeightSpans(rcContext ctx, int walkableHeight, rcHeightfield solid) { Debug.Assert(ctx != null, "rcContext is null"); ctx.startTimer(rcTimerLabel.RC_TIMER_FILTER_WALKABLE); int w = solid.width; int h = solid.height; int MAX_HEIGHT = 0xffff; // Remove walkable flag from spans which do not have enough // space above them for the agent to stand there. for (int y = 0; y < h; ++y) { for (int x = 0; x < w; ++x) { for (rcSpan s = solid.spans[x + y*w]; s != null; s = s.next) { int bot = (int)(s.smax); int top = s.next != null ? (int)(s.next.smin) : MAX_HEIGHT; if ((top - bot) <= walkableHeight) { s.area = RC_NULL_AREA; } } } } ctx.stopTimer(rcTimerLabel.RC_TIMER_FILTER_WALKABLE); }
/// @par /// /// Allows the formation of walkable regions that will flow over low lying /// objects such as curbs, and up structures such as stairways. /// /// Two neighboring spans are walkable if: <tt>rcAbs(currentSpan.smax - neighborSpan.smax) < waklableClimb</tt> /// /// @warning Will override the effect of #rcFilterLedgeSpans. So if both filters are used, call /// #rcFilterLedgeSpans after calling this filter. /// /// @see rcHeightfield, rcConfig public static void rcFilterLowHangingWalkableObstacles(rcContext ctx, int walkableClimb, rcHeightfield solid) { Debug.Assert(ctx != null, "rcContext is null"); ctx.startTimer(rcTimerLabel.RC_TIMER_FILTER_LOW_OBSTACLES); int w = solid.width; int h = solid.height; for (int y = 0; y < h; ++y) { for (int x = 0; x < w; ++x) { rcSpan ps = null; bool previousWalkable = false; byte previousArea = RC_NULL_AREA; for (rcSpan s = solid.spans[x + y * w]; s != null; ps = s, s = s.next) { bool walkable = s.area != RC_NULL_AREA; // If current span is not walkable, but there is walkable // span just below it, mark the span above it walkable too. if (!walkable && previousWalkable) { if (Math.Abs((int)s.smax - (int)ps.smax) <= walkableClimb) { s.area = previousArea; } } // Copy walkable flag so that it cannot propagate // past multiple non-walkable objects. previousWalkable = walkable; previousArea = s.area; } } } ctx.stopTimer(rcTimerLabel.RC_TIMER_FILTER_LOW_OBSTACLES); }
/// @par /// /// The span addition can be set to favor flags. If the span is merged to /// another span and the new @p smax is within @p flagMergeThr units /// from the existing span, the span flags are merged. /// /// @see rcHeightfield, rcSpan. static void rcAddSpan(rcContext ctx, rcHeightfield hf, int x, int y, ushort smin, ushort smax, byte area, int flagMergeThr) { // Debug.Assert(ctx != null, "rcContext is null"); addSpan(hf, x,y, smin, smax, area, flagMergeThr); }
static void rasterizeTri(float[] v0, int v0Start, float[] v1, int v1Start, float[] v2, int v2Start, byte area, rcHeightfield hf, float[] bmin, float[] bmax, float cs, float ics, float ich, int flagMergeThr) { int w = hf.width; int h = hf.height; float[] tmin = new float[3]; float[] tmax = new float[3]; float by = bmax[1] - bmin[1]; // Calculate the bounding box of the triangle. rcVcopy(tmin, 0, v0, v0Start); rcVcopy(tmax, 0, v0, v0Start); rcVmin(tmin, 0, v1, v1Start); rcVmin(tmin, 0, v2, v2Start); rcVmax(tmax, 0, v1, v1Start); rcVmax(tmax, 0, v2, v2Start); // If the triangle does not touch the bbox of the heightfield, skip the triagle. if (!overlapBounds(bmin, bmax, tmin, tmax)) return; // Calculate the footprint of the triangle on the grid's y-axis int y0 = (int)((tmin[2] - bmin[2])*ics); int y1 = (int)((tmax[2] - bmin[2])*ics); y0 = rcClamp(y0, 0, h-1); y1 = rcClamp(y1, 0, h-1); // Clip the triangle into all grid cells it touches. //float[] buf = new float[7*3*4]; float[] _in = new float[7*3]; float[] inrow = new float[7*3]; float[] p1 = new float[7*3]; float[] p2 = new float[7*3]; rcVcopy(_in,0 , v0, v0Start); rcVcopy(_in,1*3, v1, v1Start); rcVcopy(_in,2*3, v2, v2Start); int nvrow = 0; int nvIn = 3; for (int y = y0; y <= y1; ++y) { // Clip polygon to row. Store the remaining polygon as well float cz = bmin[2] + y*cs; dividePoly(_in, nvIn, inrow, ref nvrow, p1, ref nvIn, cz+cs, 2); //rcSwap(_in, p1); float[] tmp = _in; _in = p1; p1 = tmp; if (nvrow < 3) continue; // find the horizontal bounds in the row float minX = inrow[0], maxX = inrow[0]; for (int i=1; i<nvrow; ++i) { if (minX > inrow[i*3]) minX = inrow[i*3]; if (maxX < inrow[i*3]) maxX = inrow[i*3]; } int x0 = (int)((minX - bmin[0])*ics); int x1 = (int)((maxX - bmin[0])*ics); x0 = rcClamp(x0, 0, w-1); x1 = rcClamp(x1, 0, w-1); int nv = 0; int nv2 = nvrow; for (int x = x0; x <= x1; ++x) { // Clip polygon to column. store the remaining polygon as well float cx = bmin[0] + x*cs; dividePoly(inrow, nv2, p1, ref nv, p2, ref nv2, cx+cs, 0); //rcSwap(inrow, p2); tmp = inrow; inrow = p2; p2 = tmp; if (nv < 3) { continue; } // Calculate min and max of the span. float smin = p1[1], smax = p1[1]; for (int i = 1; i < nv; ++i) { smin = Math.Min(smin, p1[i*3+1]); smax = Math.Max(smax, p1[i*3+1]); } smin -= bmin[1]; smax -= bmin[1]; // Skip the span if it is outside the heightfield bbox if (smax < 0.0f) continue; if (smin > by) continue; // Clamp the span to the heightfield bbox. if (smin < 0.0f) smin = 0; if (smax > by) smax = by; // Snap the span to the heightfield height grid. ushort ismin = (ushort)rcClamp((int)Math.Floor(smin * ich), 0, RC_SPAN_MAX_HEIGHT); ushort ismax = (ushort)rcClamp((int)Math.Ceiling(smax * ich), (int)ismin+1, RC_SPAN_MAX_HEIGHT); addSpan(hf, x, y, ismin, ismax, area, flagMergeThr); } } }
static rcSpan allocSpan(rcHeightfield hf) { // If running out of memory, allocate new page and update the freelist. if (hf.freelist == null || hf.freelist.next == null) { // Create new page. // Allocate memory for the new pool. //rcSpanPool* pool = (rcSpanPool*)rcAlloc(sizeof(rcSpanPool), RC_ALLOC_PERM); rcSpanPool pool = new rcSpanPool(); if (pool == null) return null; pool.next = null; // Add the pool into the list of pools. pool.next = hf.pools; hf.pools = pool; // Add new items to the free list. rcSpan freelist = hf.freelist; //rcSpan head = pool.items[0]; //rcSpan it = pool.items[RC_SPANS_PER_POOL]; int itIndex = RC_SPANS_PER_POOL; do { --itIndex; pool.items[itIndex].next = freelist; freelist = pool.items[itIndex]; } while (itIndex != 0); hf.freelist = pool.items[itIndex]; } // Pop item from in front of the free list. rcSpan it = hf.freelist; hf.freelist = hf.freelist.next; return it; }
/// @par /// /// Allows the formation of walkable regions that will flow over low lying /// objects such as curbs, and up structures such as stairways. /// /// Two neighboring spans are walkable if: <tt>rcAbs(currentSpan.smax - neighborSpan.smax) < waklableClimb</tt> /// /// @warning Will override the effect of #rcFilterLedgeSpans. So if both filters are used, call /// #rcFilterLedgeSpans after calling this filter. /// /// @see rcHeightfield, rcConfig public static void rcFilterLowHangingWalkableObstacles(rcContext ctx, int walkableClimb, rcHeightfield solid) { Debug.Assert(ctx != null, "rcContext is null"); ctx.startTimer(rcTimerLabel.RC_TIMER_FILTER_LOW_OBSTACLES); int w = solid.width; int h = solid.height; for (int y = 0; y < h; ++y) { for (int x = 0; x < w; ++x) { rcSpan ps = null; bool previousWalkable = false; byte previousArea = RC_NULL_AREA; for (rcSpan s = solid.spans[x + y*w]; s != null; ps = s, s = s.next) { bool walkable = s.area != RC_NULL_AREA; // If current span is not walkable, but there is walkable // span just below it, mark the span above it walkable too. if (!walkable && previousWalkable) { if (Math.Abs((int)s.smax - (int)ps.smax) <= walkableClimb){ s.area = previousArea; } } // Copy walkable flag so that it cannot propagate // past multiple non-walkable objects. previousWalkable = walkable; previousArea = s.area; } } } ctx.stopTimer(rcTimerLabel.RC_TIMER_FILTER_LOW_OBSTACLES); }
/// @par /// /// A ledge is a span with one or more neighbors whose maximum is further away than @p walkableClimb /// from the current span's maximum. /// This method removes the impact of the overestimation of conservative voxelization /// so the resulting mesh will not have regions hanging in the air over ledges. /// /// A span is a ledge if: <tt>rcAbs(currentSpan.smax - neighborSpan.smax) > walkableClimb</tt> /// /// @see rcHeightfield, rcConfig public static void rcFilterLedgeSpans(rcContext ctx, int walkableHeight, int walkableClimb, rcHeightfield solid) { Debug.Assert(ctx != null, "rcContext is null"); ctx.startTimer(rcTimerLabel.RC_TIMER_FILTER_BORDER); int w = solid.width; int h = solid.height; int MAX_HEIGHT = 0xffff; // Mark border spans. for (int y = 0; y < h; ++y) { for (int x = 0; x < w; ++x) { for (rcSpan s = solid.spans[x + y * w]; s != null; s = s.next) { // Skip non walkable spans. if (s.area == RC_NULL_AREA) { continue; } int bot = (int)(s.smax); int top = s.next != null ? (int)(s.next.smin) : MAX_HEIGHT; // Find neighbours minimum height. int minh = MAX_HEIGHT; // Min and max height of accessible neighbours. int asmin = s.smax; int asmax = s.smax; for (int dir = 0; dir < 4; ++dir) { int dx = x + rcGetDirOffsetX(dir); int dy = y + rcGetDirOffsetY(dir); // Skip neighbours which are out of bounds. if (dx < 0 || dy < 0 || dx >= w || dy >= h) { minh = Math.Min(minh, -walkableClimb - bot); continue; } // From minus infinity to the first span. rcSpan ns = solid.spans[dx + dy * w]; int nbot = -walkableClimb; int ntop = ns != null ? (int)ns.smin : MAX_HEIGHT; // Skip neightbour if the gap between the spans is too small. if (Math.Min(top, ntop) - Math.Max(bot, nbot) > walkableHeight) { minh = Math.Min(minh, nbot - bot); } // Rest of the spans. for (ns = solid.spans[dx + dy * w]; ns != null; ns = ns.next) { nbot = (int)ns.smax; ntop = ns.next != null ? (int)ns.next.smin : MAX_HEIGHT; // Skip neightbour if the gap between the spans is too small. if (Math.Min(top, ntop) - Math.Max(bot, nbot) > walkableHeight) { minh = Math.Min(minh, nbot - bot); // Find min/max accessible neighbour height. if (Math.Abs(nbot - bot) <= walkableClimb) { if (nbot < asmin) { asmin = nbot; } if (nbot > asmax) { asmax = nbot; } } } } } // The current span is close to a ledge if the drop to any // neighbour span is less than the walkableClimb. if (minh < -walkableClimb) { s.area = RC_NULL_AREA; } // If the difference between all neighbours is too large, // we are at steep slope, mark the span as ledge. if ((asmax - asmin) > walkableClimb) { s.area = RC_NULL_AREA; } } } } ctx.stopTimer(rcTimerLabel.RC_TIMER_FILTER_BORDER); }
/// @par /// /// A ledge is a span with one or more neighbors whose maximum is further away than @p walkableClimb /// from the current span's maximum. /// This method removes the impact of the overestimation of conservative voxelization /// so the resulting mesh will not have regions hanging in the air over ledges. /// /// A span is a ledge if: <tt>rcAbs(currentSpan.smax - neighborSpan.smax) > walkableClimb</tt> /// /// @see rcHeightfield, rcConfig public static void rcFilterLedgeSpans(rcContext ctx, int walkableHeight, int walkableClimb, rcHeightfield solid) { Debug.Assert(ctx != null, "rcContext is null"); ctx.startTimer(rcTimerLabel.RC_TIMER_FILTER_BORDER); int w = solid.width; int h = solid.height; int MAX_HEIGHT = 0xffff; // Mark border spans. for (int y = 0; y < h; ++y) { for (int x = 0; x < w; ++x) { for (rcSpan s = solid.spans[x + y*w]; s != null; s = s.next) { // Skip non walkable spans. if (s.area == RC_NULL_AREA){ continue; } int bot = (int)(s.smax); int top = s.next != null ? (int)(s.next.smin) : MAX_HEIGHT; // Find neighbours minimum height. int minh = MAX_HEIGHT; // Min and max height of accessible neighbours. int asmin = s.smax; int asmax = s.smax; for (int dir = 0; dir < 4; ++dir) { int dx = x + rcGetDirOffsetX(dir); int dy = y + rcGetDirOffsetY(dir); // Skip neighbours which are out of bounds. if (dx < 0 || dy < 0 || dx >= w || dy >= h) { minh = Math.Min(minh, -walkableClimb - bot); continue; } // From minus infinity to the first span. rcSpan ns = solid.spans[dx + dy*w]; int nbot = -walkableClimb; int ntop = ns != null ? (int)ns.smin : MAX_HEIGHT; // Skip neightbour if the gap between the spans is too small. if (Math.Min(top,ntop) - Math.Max(bot,nbot) > walkableHeight) minh = Math.Min(minh, nbot - bot); // Rest of the spans. for (ns = solid.spans[dx + dy*w]; ns != null; ns = ns.next) { nbot = (int)ns.smax; ntop = ns.next != null ? (int)ns.next.smin : MAX_HEIGHT; // Skip neightbour if the gap between the spans is too small. if (Math.Min(top,ntop) - Math.Max(bot,nbot) > walkableHeight) { minh = Math.Min(minh, nbot - bot); // Find min/max accessible neighbour height. if (Math.Abs(nbot - bot) <= walkableClimb) { if (nbot < asmin) asmin = nbot; if (nbot > asmax) asmax = nbot; } } } } // The current span is close to a ledge if the drop to any // neighbour span is less than the walkableClimb. if (minh < -walkableClimb){ s.area = RC_NULL_AREA; } // If the difference between all neighbours is too large, // we are at steep slope, mark the span as ledge. if ((asmax - asmin) > walkableClimb) { s.area = RC_NULL_AREA; } } } } ctx.stopTimer(rcTimerLabel.RC_TIMER_FILTER_BORDER); }
/// @par /// /// Allows the formation of walkable regions that will flow over low lying /// objects such as curbs, and up structures such as stairways. /// /// Two neighboring spans are walkable if: <tt>rcAbs(currentSpan.smax - neighborSpan.smax) < waklableClimb</tt> /// /// @warning Will override the effect of #rcFilterLedgeSpans. So if both filters are used, call /// #rcFilterLedgeSpans after calling this filter. /// /// @see rcHeightfield, rcConfig public static void rcFilterLowHangingWalkableObstacles(rcContext ctx, int walkableClimb, rcHeightfield solid) { Debug.Assert(ctx != null, "rcContext is null"); ctx.startTimer(rcTimerLabel.RC_TIMER_FILTER_LOW_OBSTACLES); int w = solid.width; int h = solid.height; for (int y = 0; y < h; ++y) { for (int x = 0; x < w; ++x) { rcSpan?ps = null; bool previousWalkable = false; byte previousArea = RC_NULL_AREA; for (rcSpan?s = solid.spans ![x + y * w]; s != null; ps = s, s = s.next)