/// @par /// /// This is just the beginning of the process of fully building a compact heightfield. /// Various filters may be applied, then the distance field and regions built. /// E.g: #rcBuildDistanceField and #rcBuildRegions /// /// See the #rcConfig documentation for more information on the configuration parameters. /// /// @see rcAllocCompactHeightfield, rcHeightfield, rcCompactHeightfield, rcConfig public static CompactHeightfield buildCompactHeightfield(Context ctx, int walkableHeight, int walkableClimb, Heightfield hf) { ctx.startTimer("BUILD_COMPACTHEIGHTFIELD"); CompactHeightfield chf = new CompactHeightfield(); int w = hf.width; int h = hf.height; int spanCount = getHeightFieldSpanCount(ctx, hf); // Fill in header. chf.width = w; chf.height = h; chf.spanCount = spanCount; chf.walkableHeight = walkableHeight; chf.walkableClimb = walkableClimb; chf.maxRegions = 0; RecastVectors.copy(chf.bmin, hf.bmin); RecastVectors.copy(chf.bmax, hf.bmax); chf.bmax[1] += walkableHeight * hf.ch; chf.cs = hf.cs; chf.ch = hf.ch; chf.cells = new CompactCell[w * h]; chf.spans = new CompactSpan[spanCount]; chf.areas = new int[spanCount]; int MAX_HEIGHT = 0xffff; for (int i = 0; i < chf.cells.Length; i++) { chf.cells[i] = new CompactCell(); } for (int i = 0; i < chf.spans.Length; i++) { chf.spans[i] = new CompactSpan(); } // Fill in cells and spans. int idx = 0; for (int y = 0; y < h; ++y) { for (int x = 0; x < w; ++x) { Span s = hf.spans[x + y * w]; // If there are no spans at this cell, just leave the data to index=0, count=0. if (s == null) { continue; } CompactCell c = chf.cells[x + y * w]; c.index = idx; c.count = 0; while (s != null) { if (s.area != RecastConstants.RC_NULL_AREA) { int bot = s.smax; int top = s.next != null ? (int)s.next.smin : MAX_HEIGHT; chf.spans[idx].y = RecastCommon.clamp(bot, 0, 0xffff); chf.spans[idx].h = RecastCommon.clamp(top - bot, 0, 0xff); chf.areas[idx] = s.area; idx++; c.count++; } s = s.next; } } } // Find neighbour connections. int MAX_LAYERS = RecastConstants.RC_NOT_CONNECTED - 1; int tooHighNeighbour = 0; for (int y = 0; y < h; ++y) { for (int x = 0; x < w; ++x) { CompactCell c = chf.cells[x + y * w]; for (int i = c.index, ni = c.index + c.count; i < ni; ++i) { CompactSpan s = chf.spans[i]; for (int dir = 0; dir < 4; ++dir) { RecastCommon.SetCon(s, dir, RecastConstants.RC_NOT_CONNECTED); int nx = x + RecastCommon.GetDirOffsetX(dir); int ny = y + RecastCommon.GetDirOffsetY(dir); // First check that the neighbour cell is in bounds. if (nx < 0 || ny < 0 || nx >= w || ny >= h) { continue; } // Iterate over all neighbour spans and check if any of the is // accessible from current cell. CompactCell nc = chf.cells[nx + ny * w]; for (int k = nc.index, nk = nc.index + nc.count; k < nk; ++k) { CompactSpan ns = chf.spans[k]; int bot = Math.Max(s.y, ns.y); int top = Math.Min(s.y + s.h, ns.y + ns.h); // Check that the gap between the spans is walkable, // and that the climb height between the gaps is not too high. if ((top - bot) >= walkableHeight && Math.Abs(ns.y - s.y) <= walkableClimb) { // Mark direction as walkable. int lidx = k - nc.index; if (lidx < 0 || lidx > MAX_LAYERS) { tooHighNeighbour = Math.Max(tooHighNeighbour, lidx); continue; } RecastCommon.SetCon(s, dir, lidx); break; } } } } } } if (tooHighNeighbour > MAX_LAYERS) { throw new Exception("rcBuildCompactHeightfield: Heightfield has too many layers " + tooHighNeighbour + " (max: " + MAX_LAYERS + ")"); } ctx.stopTimer("BUILD_COMPACTHEIGHTFIELD"); return(chf); }
private static void rasterizeTri(float[] verts, int v0, int v1, int v2, int area, Heightfield 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. RecastVectors.copy(tmin, verts, v0 * 3); RecastVectors.copy(tmax, verts, v0 * 3); RecastVectors.min(tmin, verts, v1 * 3); RecastVectors.min(tmin, verts, v2 * 3); RecastVectors.max(tmax, verts, v1 * 3); RecastVectors.max(tmax, verts, v2 * 3); // 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 = RecastCommon.clamp(y0, 0, h - 1); y1 = RecastCommon.clamp(y1, 0, h - 1); // Clip the triangle into all grid cells it touches. float[] buf = new float[7 * 3 * 4]; int @in = 0; int inrow = 7 * 3; int p1 = inrow + 7 * 3; int p2 = p1 + 7 * 3; RecastVectors.copy(buf, 0, verts, v0 * 3); RecastVectors.copy(buf, 3, verts, v1 * 3); RecastVectors.copy(buf, 6, verts, v2 * 3); int nvrow, 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; int[] nvrowin = dividePoly(buf, @in, nvIn, inrow, p1, cz + cs, 2); nvrow = nvrowin[0]; nvIn = nvrowin[1]; { int temp = @in; @in = p1; p1 = temp; } if (nvrow < 3) { continue; } // find the horizontal bounds in the row float minX = buf[inrow], maxX = buf[inrow]; for (int i = 1; i < nvrow; ++i) { if (minX > buf[inrow + i * 3]) { minX = buf[inrow + i * 3]; } if (maxX < buf[inrow + i * 3]) { maxX = buf[inrow + i * 3]; } } int x0 = (int)((minX - bmin[0]) * ics); int x1 = (int)((maxX - bmin[0]) * ics); x0 = RecastCommon.clamp(x0, 0, w - 1); x1 = RecastCommon.clamp(x1, 0, w - 1); int nv, 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; int[] nvnv2 = dividePoly(buf, inrow, nv2, p1, p2, cx + cs, 0); nv = nvnv2[0]; nv2 = nvnv2[1]; { int temp = inrow; inrow = p2; p2 = temp; } if (nv < 3) { continue; } // Calculate min and max of the span. float smin = buf[p1 + 1], smax = buf[p1 + 1]; for (int i = 1; i < nv; ++i) { smin = Math.Min(smin, buf[p1 + i * 3 + 1]); smax = Math.Max(smax, buf[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. int ismin = RecastCommon.clamp((int)Math.Floor(smin * ich), 0, RecastConstants.RC_SPAN_MAX_HEIGHT); int ismax = RecastCommon.clamp((int)Math.Ceiling(smax * ich), ismin + 1, RecastConstants.RC_SPAN_MAX_HEIGHT); addSpan(hf, x, y, ismin, ismax, area, flagMergeThr); } } }