/// <summary> /// Floodfill heightfield to get 2D height data, starting at vertex locations /// </summary> /// <param name="compactField">Original heightfield data</param> /// <param name="poly">Polygon in PolyMesh</param> /// <param name="polyCount">Number of vertices per polygon</param> /// <param name="verts">PolyMesh Vertices</param> /// <param name="borderSize">Heightfield border size</param> /// <param name="hp">HeightPatch which extracts heightfield data</param> /// <param name="stack">Temporary stack of CompactSpanReferences</param> private void GetHeightDataSeedsFromVertices(CompactHeightfield compactField, PolyMesh.Polygon poly, int polyCount, PolyVertex[] verts, int borderSize, HeightPatch hp, List<CompactSpanReference> stack) { hp.SetAll(0); //use poly vertices as seed points for (int j = 0; j < polyCount; j++) { var csr = new CompactSpanReference(0, 0, -1); int dmin = int.MaxValue; var v = verts[poly.Vertices[j]]; for (int k = 0; k < 9; k++) { //get vertices and offset x and z coordinates depending on current drection int ax = v.X + VertexOffset[k * 2 + 0]; int ay = v.Y; int az = v.Z + VertexOffset[k * 2 + 1]; //skip if out of bounds if (ax < hp.X || ax >= hp.X + hp.Width || az < hp.Y || az >= hp.Y + hp.Length) continue; //get new cell CompactCell c = compactField.Cells[(az + borderSize) * compactField.Width + (ax + borderSize)]; //loop through all the spans for (int i = c.StartIndex, end = c.StartIndex + c.Count; i < end; i++) { CompactSpan s = compactField.Spans[i]; //find minimum y-distance int d = Math.Abs(ay - s.Minimum); if (d < dmin) { csr = new CompactSpanReference(ax, az, i); dmin = d; } } } //only add if something new found if (csr.Index != -1) { stack.Add(csr); } } //find center of polygon using flood fill int pcx = 0, pcz = 0; for (int j = 0; j < polyCount; j++) { var v = verts[poly.Vertices[j]]; pcx += v.X; pcz += v.Z; } pcx /= polyCount; pcz /= polyCount; //stack groups 3 elements as one part foreach (var cell in stack) { int idx = (cell.Y - hp.Y) * hp.Width + (cell.X - hp.X); hp[idx] = 1; } //process the entire stack while (stack.Count > 0) { var cell = stack[stack.Count - 1]; stack.RemoveAt(stack.Count - 1); //check if close to center of polygon if (Math.Abs(cell.X - pcx) <= 1 && Math.Abs(cell.Y - pcz) <= 1) { //clear the stack and add a new group stack.Clear(); stack.Add(cell); break; } CompactSpan cs = compactField[cell]; //check all four directions for (var dir = Direction.West; dir <= Direction.South; dir++) { //skip if disconnected if (!cs.IsConnected(dir)) continue; //get neighbor int ax = cell.X + dir.GetHorizontalOffset(); int ay = cell.Y + dir.GetVerticalOffset(); //skip if out of bounds if (ax < hp.X || ax >= (hp.X + hp.Width) || ay < hp.Y || ay >= (hp.Y + hp.Length)) continue; if (hp[(ay - hp.Y) * hp.Width + (ax - hp.X)] != 0) continue; //get the new index int ai = compactField.Cells[(ay + borderSize) * compactField.Width + (ax + borderSize)].StartIndex + CompactSpan.GetConnection(ref cs, dir); //save data int idx = (ay - hp.Y) * hp.Width + (ax - hp.X); hp[idx] = 1; //push to stack stack.Add(new CompactSpanReference(ax, ay, ai)); } } //clear the heightpatch hp.Clear(); //mark start locations for (int i = 0; i < stack.Count; i++) { var c = stack[i]; //set new heightpatch data int idx = (c.Y - hp.Y) * hp.Width + (c.X - hp.X); CompactSpan cs = compactField.Spans[c.Index]; hp[idx] = cs.Minimum; stack[i] = new CompactSpanReference(c.X + borderSize, c.Y + borderSize, c.Index); } }
private void GetHeightData(CompactHeightfield compactField, PolyMesh.Polygon poly, int polyCount, PolyVertex[] verts, int borderSize, HeightPatch hp) { var stack = new List<CompactSpanReference>(); bool empty = true; hp.Clear(); for (int y = 0; y < hp.Length; y++) { int hy = hp.Y + y + borderSize; for (int x = 0; x < hp.Width; x++) { int hx = hp.X + x + borderSize; var cells = compactField.Cells[hy * compactField.Width + hx]; for (int i = cells.StartIndex, end = cells.StartIndex + cells.Count; i < end; i++) { var span = compactField.Spans[i]; if (span.Region == poly.RegionId) { hp[x, y] = span.Minimum; empty = false; bool border = false; for (var dir = Direction.West; dir <= Direction.South; dir++) { if (span.IsConnected(dir)) { int ax = hx + dir.GetHorizontalOffset(); int ay = hy + dir.GetVerticalOffset(); int ai = compactField.Cells[ay * compactField.Width + ax].StartIndex + CompactSpan.GetConnection(ref span, dir); if (compactField.Spans[ai].Region != poly.RegionId) { border = true; break; } } } if (border) stack.Add(new CompactSpanReference(hx, hy, i)); break; } } } } if (empty) GetHeightDataSeedsFromVertices(compactField, poly, polyCount, verts, borderSize, hp, stack); const int RetractSize = 256; int head = 0; while (head < stack.Count) { var cell = stack[head++]; var cs = compactField[cell]; if (head >= RetractSize) { head = 0; if (stack.Count > RetractSize) { for (int i = 0; i < stack.Count - RetractSize; i++) stack[i] = stack[i + RetractSize]; } int targetSize = stack.Count % RetractSize; while (stack.Count > targetSize) stack.RemoveAt(stack.Count - 1); } //loop in all four directions for (var dir = Direction.West; dir <= Direction.South; dir++) { //skip if (!cs.IsConnected(dir)) continue; int ax = cell.X + dir.GetHorizontalOffset(); int ay = cell.Y + dir.GetVerticalOffset(); int hx = ax - hp.X - borderSize; int hy = ay - hp.Y - borderSize; if (hx < 0 || hx >= hp.Width || hy < 0 || hy >= hp.Length) continue; //only continue if height is unset if (hp.IsSet(hy * hp.Width + hx)) continue; //get new span int ai = compactField.Cells[ay * compactField.Width + ax].StartIndex + CompactSpan.GetConnection(ref cs, dir); CompactSpan ds = compactField.Spans[ai]; hp[hx, hy] = ds.Minimum; stack.Add(new CompactSpanReference(ax, ay, ai)); } } }