public void BuildContours (float maxError, int maxEdgeLength, VoxelContourSet cset, int buildFlags) { AstarProfiler.StartProfile ("Build Contours"); AstarProfiler.StartProfile ("- Init"); int w = voxelArea.width; int d = voxelArea.depth; int wd = w*d; //cset.bounds = voxelArea.bounds; int maxContours = Mathf.Max (8/*Max Regions*/,8); //cset.conts = new VoxelContour[maxContours]; List<VoxelContour> contours = new List<VoxelContour>(maxContours); AstarProfiler.EndProfile ("- Init"); AstarProfiler.StartProfile ("- Mark Boundaries"); //cset.nconts = 0; //NOTE: This array may contain any data, but since we explicitly set all data in it before we use it, it's OK. ushort[] flags = voxelArea.tmpUShortArr; if (flags.Length < voxelArea.compactSpanCount) { flags = voxelArea.tmpUShortArr = new ushort[voxelArea.compactSpanCount]; } // Mark boundaries. (@?) for (int z=0;z < wd;z += voxelArea.width) { for (int x=0;x < voxelArea.width;x++) { CompactVoxelCell c = voxelArea.compactCells[x+z]; for (int i= (int)c.index, ci = (int)(c.index+c.count); i < ci; i++) { ushort res = 0; CompactVoxelSpan s = voxelArea.compactSpans[i]; if (s.reg == 0 || (s.reg & BorderReg) == BorderReg) { flags[i] = 0; continue; } for (int dir=0;dir < 4; dir++) { int r = 0; if (s.GetConnection (dir) != NotConnected) { int nx = x + voxelArea.DirectionX[dir]; int nz = z + voxelArea.DirectionZ[dir]; int ni = (int)voxelArea.compactCells[nx+nz].index + s.GetConnection (dir); r = voxelArea.compactSpans[ni].reg; } //@TODO - Why isn't this inside the previous IF if (r == s.reg) { res |= (ushort)(1 << dir); } } //Inverse, mark non connected edges. flags[i] = (ushort)(res ^ 0xf); } } } AstarProfiler.EndProfile ("- Mark Boundaries"); AstarProfiler.StartProfile ("- Simplify Contours"); List<int> verts = ListPool<int>.Claim(256);//new List<int> (256); List<int> simplified = ListPool<int>.Claim(64);//new List<int> (64); for (int z=0;z < wd;z += voxelArea.width) { for (int x=0;x < voxelArea.width;x++) { CompactVoxelCell c = voxelArea.compactCells[x+z]; for (int i= (int)c.index, ci = (int)(c.index+c.count); i < ci; i++) { //CompactVoxelSpan s = voxelArea.compactSpans[i]; if (flags[i] == 0 || flags[i] == 0xf) { flags[i] = 0; continue; } int reg = voxelArea.compactSpans[i].reg; if (reg == 0 || (reg & BorderReg) == BorderReg) { continue; } int area = voxelArea.areaTypes[i]; verts.Clear (); simplified.Clear (); WalkContour(x, z, i, flags, verts); SimplifyContour(verts, simplified, maxError, maxEdgeLength, buildFlags); RemoveDegenerateSegments (simplified); VoxelContour contour = new VoxelContour (); contour.verts = ClaimIntArr(simplified.Count,false);//simplified.ToArray (); for (int j=0;j<simplified.Count;j++) contour.verts[j] = simplified[j]; #if ASTAR_RECAST_INCLUDE_RAW_VERTEX_CONTOUR //Not used at the moment, just debug stuff contour.rverts = ClaimIntArr(verts.Count); for (int j=0;j<verts.Count;j++) contour.rverts[j] = verts[j]; #endif contour.nverts = simplified.Count/4; contour.reg = reg; contour.area = area; contours.Add (contour); #if ASTARDEBUG for (int q=0, j = (simplified.Count/4)-1;q<(simplified.Count/4);j=q, q++) { int i4 = q*4; int j4 = j*4; Vector3 p1 = Vector3.Scale ( new Vector3 ( simplified[i4+0], simplified[i4+1], (simplified[i4+2]/(float)voxelArea.width) ), cellScale) +voxelOffset; Vector3 p2 = Vector3.Scale ( new Vector3 ( simplified[j4+0], simplified[j4+1], (simplified[j4+2]/(float)voxelArea.width) ) ,cellScale) +voxelOffset; if (CalcAreaOfPolygon2D(contour.verts, contour.nverts) > 0) { Debug.DrawLine (p1,p2,AstarMath.IntToColor (reg,0.5F)); } else { Debug.DrawLine (p1,p2,Color.red); } } #endif } } } ListPool<int>.Release(verts); ListPool<int>.Release(simplified); AstarProfiler.EndProfile ("- Simplify Contours"); AstarProfiler.StartProfile ("- Fix Contours"); // Check and merge droppings. // Sometimes the previous algorithms can fail and create several contours // per area. This pass will try to merge the holes into the main region. for (int i = 0; i < contours.Count; i++) { VoxelContour cont = contours[i]; // Check if the contour is would backwards. if (CalcAreaOfPolygon2D(cont.verts, cont.nverts) < 0) { // Find another contour which has the same region ID. int mergeIdx = -1; for (int j = 0; j < contours.Count; j++) { if (i == j) continue; if (contours[j].nverts > 0 && contours[j].reg == cont.reg) { // Make sure the polygon is correctly oriented. if (CalcAreaOfPolygon2D(contours[j].verts, contours[j].nverts) > 0) { mergeIdx = j; break; } } } if (mergeIdx == -1) { Debug.LogError ("rcBuildContours: Could not find merge target for bad contour "+i+"."); } else { // Debugging //Debug.LogWarning ("Fixing contour"); VoxelContour mcont = contours[mergeIdx]; // Merge by closest points. int ia = 0, ib = 0; GetClosestIndices(mcont.verts, mcont.nverts, cont.verts, cont.nverts, ref ia, ref ib); if (ia == -1 || ib == -1) { Debug.LogWarning ("rcBuildContours: Failed to find merge points for "+i+" and "+mergeIdx+"."); continue; } #if ASTARDEBUG int p4 = ia*4; int p42 = ib*4; Vector3 p12 = Vector3.Scale ( new Vector3 ( mcont.verts[p4+0], mcont.verts[p4+1], (mcont.verts[p4+2]/(float)voxelArea.width) ), cellScale) +voxelOffset; Vector3 p22 = Vector3.Scale ( new Vector3 ( cont.verts[p42+0], cont.verts[p42+1], (cont.verts[p42+2]/(float)voxelArea.width) ) ,cellScale) +voxelOffset; Debug.DrawLine (p12,p22,Color.green); #endif if (!MergeContours(ref mcont, ref cont, ia, ib)) { Debug.LogWarning ("rcBuildContours: Failed to merge contours "+i+" and "+mergeIdx+"."); continue; } contours[mergeIdx] = mcont; contours[i] = cont; #if ASTARDEBUG Debug.Log (mcont.nverts); for (int q=0, j = (mcont.nverts)-1;q<(mcont.nverts);j=q, q++) { int i4 = q*4; int j4 = j*4; Vector3 p1 = Vector3.Scale ( new Vector3 ( mcont.verts[i4+0], mcont.verts[i4+1], (mcont.verts[i4+2]/(float)voxelArea.width) ), cellScale) +voxelOffset; Vector3 p2 = Vector3.Scale ( new Vector3 ( mcont.verts[j4+0], mcont.verts[j4+1], (mcont.verts[j4+2]/(float)voxelArea.width) ) ,cellScale) +voxelOffset; Debug.DrawLine (p1,p2,Color.red); //} } #endif } } } cset.conts = contours; AstarProfiler.EndProfile ("- Fix Contours"); AstarProfiler.EndProfile ("Build Contours"); }
public static bool MergeContours(ref VoxelContour ca, ref VoxelContour cb, int ia, int ib) { int maxVerts = ca.nverts + cb.nverts + 2; int[] verts = ClaimIntArr(maxVerts*4,false);//new int[maxVerts*4]; //if (!verts) // return false; int nv = 0; // Copy contour A. for (int i = 0; i <= ca.nverts; i++) { int dst = nv*4; int src = ((ia+i) % ca.nverts)*4; verts[dst+0] = ca.verts[src+0]; verts[dst+1] = ca.verts[src+1]; verts[dst+2] = ca.verts[src+2]; verts[dst+3] = ca.verts[src+3]; nv++; } // Copy contour B for (int i = 0; i <= cb.nverts; i++) { int dst = nv*4; int src = ((ib+i) % cb.nverts)*4; verts[dst+0] = cb.verts[src+0]; verts[dst+1] = cb.verts[src+1]; verts[dst+2] = cb.verts[src+2]; verts[dst+3] = cb.verts[src+3]; nv++; } //rcFree(ca.verts); //rcFree(cb.verts); ReleaseIntArr(ca.verts); ReleaseIntArr(cb.verts); ca.verts = verts; ca.nverts = nv; cb.verts = emptyArr; cb.nverts = 0; return true; }