/** Releases contents of a contour set to caches */ static void ReleaseContours(VoxelContourSet cset) { for (int i = 0; i < cset.conts.Count; i++) { VoxelContour cont = cset.conts[i]; ReleaseIntArr(cont.verts); ReleaseIntArr(cont.rverts); } cset.conts = null; }
/// <summary>Releases contents of a contour set to caches</summary> static void ReleaseContours(VoxelContourSet cset) { for (int i = 0; i < cset.conts.Count; i++) { VoxelContour cont = cset.conts[i]; Pathfinding.Util.ArrayPool <int> .Release(ref cont.verts); Pathfinding.Util.ArrayPool <int> .Release(ref cont.rverts); } cset.conts = null; }
public static bool MergeContours(ref VoxelContour ca, ref VoxelContour cb, int ia, int ib) { int maxVerts = ca.nverts + cb.nverts + 2; int[] verts = Pathfinding.Util.ArrayPool <int> .Claim(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++; } Pathfinding.Util.ArrayPool <int> .Release(ref ca.verts); Pathfinding.Util.ArrayPool <int> .Release(ref cb.verts); ca.verts = verts; ca.nverts = nv; cb.verts = Pathfinding.Util.ArrayPool <int> .Claim(0); cb.nverts = 0; return(true); }
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); }
/** Builds a polygon mesh from a contour set. * \param nvp Maximum allowed vertices per polygon. \note Currently locked to 3 */ public void BuildPolyMesh(VoxelContourSet cset, int nvp, out VoxelMesh mesh) { AstarProfiler.StartProfile("Build Poly Mesh"); nvp = 3; int maxVertices = 0; int maxTris = 0; int maxVertsPerCont = 0; for (int i = 0; i < cset.conts.Count; i++) { // Skip null contours. if (cset.conts[i].nverts < 3) { continue; } maxVertices += cset.conts[i].nverts; maxTris += cset.conts[i].nverts - 2; maxVertsPerCont = AstarMath.Max(maxVertsPerCont, cset.conts[i].nverts); } if (maxVertices >= 65534) { Debug.LogWarning("To many vertices for unity to render - Unity might screw up rendering, but hopefully the navmesh will work ok"); //mesh = new VoxelMesh (); //yield break; //return; } //int[] vflags = new int[maxVertices]; /** \todo Could be cached to avoid allocations */ Int3[] verts = new Int3[maxVertices]; /** \todo Could be cached to avoid allocations */ int[] polys = new int[maxTris * nvp]; //int[] regs = new int[maxTris]; //int[] areas = new int[maxTris]; Pathfinding.Util.Memory.MemSet <int> (polys, 0xff, sizeof(int)); //for (int i=0;i<polys.Length;i++) { // polys[i] = 0xff; //} //int[] nexVert = new int[maxVertices]; //int[] firstVert = new int[VERTEX_BUCKET_COUNT]; int[] indices = new int[maxVertsPerCont]; int[] tris = new int[maxVertsPerCont * 3]; //ushort[] polys int vertexIndex = 0; int polyIndex = 0; for (int i = 0; i < cset.conts.Count; i++) { VoxelContour cont = cset.conts[i]; //Skip null contours if (cont.nverts < 3) { continue; } for (int j = 0; j < cont.nverts; j++) { indices[j] = j; cont.verts[j * 4 + 2] /= voxelArea.width; } //yield return (GameObject.FindObjectOfType (typeof(MonoBehaviour)) as MonoBehaviour).StartCoroutine ( //Triangulate (cont.nverts, cont.verts, indices, tris); int ntris = Triangulate(cont.nverts, cont.verts, ref indices, ref tris); /*if (ntris > cont.nverts-2) { * Debug.LogError (ntris + " "+cont.nverts+" "+cont.verts.Length+" "+(cont.nverts-2)); * } * * if (ntris > maxVertsPerCont) { * Debug.LogError (ntris*3 + " "+maxVertsPerCont); * } * * int tmp = polyIndex; * * Debug.Log (maxTris + " "+polyIndex+" "+polys.Length+" "+ntris+" "+(ntris*3) + " " + cont.nverts);*/ int startIndex = vertexIndex; for (int j = 0; j < ntris * 3; polyIndex++, j++) { //@Error sometimes polys[polyIndex] = tris[j] + startIndex; } /*int tmp2 = polyIndex; * if (tmp+ntris*3 != tmp2) { * Debug.LogWarning (tmp+" "+(tmp+ntris*3)+" "+tmp2+" "+ntris*3); * }*/ for (int j = 0; j < cont.nverts; vertexIndex++, j++) { verts[vertexIndex] = new Int3(cont.verts[j * 4], cont.verts[j * 4 + 1], cont.verts[j * 4 + 2]); } } mesh = new VoxelMesh(); //yield break; Int3[] trimmedVerts = new Int3[vertexIndex]; for (int i = 0; i < vertexIndex; i++) { trimmedVerts[i] = verts[i]; } int[] trimmedTris = new int[polyIndex]; System.Buffer.BlockCopy(polys, 0, trimmedTris, 0, polyIndex * sizeof(int)); //for (int i=0;i<polyIndex;i++) { // trimmedTris[i] = polys[i]; //} mesh.verts = trimmedVerts; mesh.tris = trimmedTris; /*for (int i=0;i<mesh.tris.Length/3;i++) { * * int p = i*3; * * int p1 = mesh.tris[p]; * int p2 = mesh.tris[p+1]; * int p3 = mesh.tris[p+2]; * * //Debug.DrawLine (ConvertPosCorrZ (mesh.verts[p1].x,mesh.verts[p1].y,mesh.verts[p1].z),ConvertPosCorrZ (mesh.verts[p2].x,mesh.verts[p2].y,mesh.verts[p2].z),Color.yellow); * //Debug.DrawLine (ConvertPosCorrZ (mesh.verts[p1].x,mesh.verts[p1].y,mesh.verts[p1].z),ConvertPosCorrZ (mesh.verts[p3].x,mesh.verts[p3].y,mesh.verts[p3].z),Color.yellow); * //Debug.DrawLine (ConvertPosCorrZ (mesh.verts[p3].x,mesh.verts[p3].y,mesh.verts[p3].z),ConvertPosCorrZ (mesh.verts[p2].x,mesh.verts[p2].y,mesh.verts[p2].z),Color.yellow); * * //Debug.DrawLine (ConvertPosCorrZ (verts[p1],0,verts[p1+2]),ConvertPosCorrZ (verts[p2],0,verts[p2+2]),Color.blue); * //Debug.DrawLine (ConvertPosCorrZ (verts[p1],0,verts[p1+2]),ConvertPosCorrZ (verts[p3],0,verts[p3+2]),Color.blue); * //Debug.DrawLine (ConvertPosCorrZ (verts[p2],0,verts[p2+2]),ConvertPosCorrZ (verts[p3],0,verts[p3+2]),Color.blue); * * }*/ AstarProfiler.EndProfile("Build Poly Mesh"); }
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 = Pathfinding.Util.ListPool <int> .Claim(256); //new List<int> (256); List <int> simplified = Pathfinding.Util.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 = Pathfinding.Util.ArrayPool <int> .Claim(simplified.Count); //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 } } } Pathfinding.Util.ListPool <int> .Release(ref verts); Pathfinding.Util.ListPool <int> .Release(ref 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"); }
/** Builds a polygon mesh from a contour set. * * \param cset contour set to build a mesh from. * \param nvp Maximum allowed vertices per polygon. \warning Currently locked to 3. * \param mesh Results will be written to this mesh. */ public void BuildPolyMesh(VoxelContourSet cset, int nvp, out VoxelMesh mesh) { AstarProfiler.StartProfile("Build Poly Mesh"); nvp = 3; int maxVertices = 0; int maxTris = 0; int maxVertsPerCont = 0; for (int i = 0; i < cset.conts.Count; i++) { // Skip null contours. if (cset.conts[i].nverts < 3) { continue; } maxVertices += cset.conts[i].nverts; maxTris += cset.conts[i].nverts - 2; maxVertsPerCont = System.Math.Max(maxVertsPerCont, cset.conts[i].nverts); } Int3[] verts = ArrayPool <Int3> .Claim(maxVertices); int[] polys = ArrayPool <int> .Claim(maxTris *nvp); int[] areas = ArrayPool <int> .Claim(maxTris); Memory.MemSet <int>(polys, 0xff, sizeof(int)); int[] indices = ArrayPool <int> .Claim(maxVertsPerCont); int[] tris = ArrayPool <int> .Claim(maxVertsPerCont *3); int vertexIndex = 0; int polyIndex = 0; int areaIndex = 0; for (int i = 0; i < cset.conts.Count; i++) { VoxelContour cont = cset.conts[i]; // Skip degenerate contours if (cont.nverts < 3) { continue; } for (int j = 0; j < cont.nverts; j++) { indices[j] = j; // Convert the z coordinate from the form z*voxelArea.width which is used in other places for performance cont.verts[j * 4 + 2] /= voxelArea.width; } // Triangulate the contour int ntris = Triangulate(cont.nverts, cont.verts, ref indices, ref tris); // Assign the correct vertex indices int startIndex = vertexIndex; for (int j = 0; j < ntris * 3; polyIndex++, j++) { //@Error sometimes polys[polyIndex] = tris[j] + startIndex; } // Mark all triangles generated by this contour // as having the area cont.area for (int j = 0; j < ntris; areaIndex++, j++) { areas[areaIndex] = cont.area; } // Copy the vertex positions for (int j = 0; j < cont.nverts; vertexIndex++, j++) { verts[vertexIndex] = new Int3(cont.verts[j * 4], cont.verts[j * 4 + 1], cont.verts[j * 4 + 2]); } } mesh = new VoxelMesh { verts = Memory.ShrinkArray(verts, vertexIndex), tris = Memory.ShrinkArray(polys, polyIndex), areas = Memory.ShrinkArray(areas, areaIndex) }; ArrayPool <Int3> .Release(ref verts); ArrayPool <int> .Release(ref polys); ArrayPool <int> .Release(ref areas); ArrayPool <int> .Release(ref indices); ArrayPool <int> .Release(ref tris); AstarProfiler.EndProfile("Build Poly Mesh"); }
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 = Pathfinding.Util.ListPool<int>.Claim(256);//new List<int> (256); List<int> simplified = Pathfinding.Util.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 } } } Pathfinding.Util.ListPool<int>.Release(verts); Pathfinding.Util.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; }
/** Builds a polygon mesh from a contour set. * * \param cset contour set to build a mesh from. * \param nvp Maximum allowed vertices per polygon. \warning Currently locked to 3. * \param mesh Results will be written to this mesh. */ public void BuildPolyMesh(VoxelContourSet cset, int nvp, out VoxelMesh mesh) { AstarProfiler.StartProfile("Build Poly Mesh"); nvp = 3; int maxVertices = 0; int maxTris = 0; int maxVertsPerCont = 0; for (int i = 0; i < cset.conts.Count; i++) { // Skip null contours. if (cset.conts[i].nverts < 3) { continue; } maxVertices += cset.conts[i].nverts; maxTris += cset.conts[i].nverts - 2; maxVertsPerCont = AstarMath.Max(maxVertsPerCont, cset.conts[i].nverts); } if (maxVertices >= 65534) { Debug.LogWarning("To many vertices for unity to render - Unity might screw up rendering, but hopefully the navmesh will work ok"); //mesh = new VoxelMesh (); //yield break; //return; } /** \todo Could be cached to avoid allocations */ Int3[] verts = new Int3[maxVertices]; /** \todo Could be cached to avoid allocations */ int[] polys = new int[maxTris * nvp]; Pathfinding.Util.Memory.MemSet <int> (polys, 0xff, sizeof(int)); int[] indices = new int[maxVertsPerCont]; int[] tris = new int[maxVertsPerCont * 3]; int vertexIndex = 0; int polyIndex = 0; for (int i = 0; i < cset.conts.Count; i++) { VoxelContour cont = cset.conts[i]; //Skip null contours if (cont.nverts < 3) { continue; } for (int j = 0; j < cont.nverts; j++) { indices[j] = j; cont.verts[j * 4 + 2] /= voxelArea.width; } int ntris = Triangulate(cont.nverts, cont.verts, ref indices, ref tris); int startIndex = vertexIndex; for (int j = 0; j < ntris * 3; polyIndex++, j++) { //@Error sometimes polys[polyIndex] = tris[j] + startIndex; } for (int j = 0; j < cont.nverts; vertexIndex++, j++) { verts[vertexIndex] = new Int3(cont.verts[j * 4], cont.verts[j * 4 + 1], cont.verts[j * 4 + 2]); } } mesh = new VoxelMesh(); //yield break; Int3[] trimmedVerts = new Int3[vertexIndex]; for (int i = 0; i < vertexIndex; i++) { trimmedVerts[i] = verts[i]; } int[] trimmedTris = new int[polyIndex]; System.Buffer.BlockCopy(polys, 0, trimmedTris, 0, polyIndex * sizeof(int)); mesh.verts = trimmedVerts; mesh.tris = trimmedTris; // Some debugging /*for (int i=0;i<mesh.tris.Length/3;i++) { * * int p = i*3; * * int p1 = mesh.tris[p]; * int p2 = mesh.tris[p+1]; * int p3 = mesh.tris[p+2]; * * //Debug.DrawLine (ConvertPosCorrZ (mesh.verts[p1].x,mesh.verts[p1].y,mesh.verts[p1].z),ConvertPosCorrZ (mesh.verts[p2].x,mesh.verts[p2].y,mesh.verts[p2].z),Color.yellow); * //Debug.DrawLine (ConvertPosCorrZ (mesh.verts[p1].x,mesh.verts[p1].y,mesh.verts[p1].z),ConvertPosCorrZ (mesh.verts[p3].x,mesh.verts[p3].y,mesh.verts[p3].z),Color.yellow); * //Debug.DrawLine (ConvertPosCorrZ (mesh.verts[p3].x,mesh.verts[p3].y,mesh.verts[p3].z),ConvertPosCorrZ (mesh.verts[p2].x,mesh.verts[p2].y,mesh.verts[p2].z),Color.yellow); * * //Debug.DrawLine (ConvertPosCorrZ (verts[p1],0,verts[p1+2]),ConvertPosCorrZ (verts[p2],0,verts[p2+2]),Color.blue); * //Debug.DrawLine (ConvertPosCorrZ (verts[p1],0,verts[p1+2]),ConvertPosCorrZ (verts[p3],0,verts[p3+2]),Color.blue); * //Debug.DrawLine (ConvertPosCorrZ (verts[p2],0,verts[p2+2]),ConvertPosCorrZ (verts[p3],0,verts[p3+2]),Color.blue); * * }*/ AstarProfiler.EndProfile("Build Poly Mesh"); }
/** Builds a polygon mesh from a contour set. * * \param cset contour set to build a mesh from. * \param nvp Maximum allowed vertices per polygon. \warning Currently locked to 3. * \param mesh Results will be written to this mesh. */ public void BuildPolyMesh(VoxelContourSet cset, int nvp, out VoxelMesh mesh) { AstarProfiler.StartProfile("Build Poly Mesh"); nvp = 3; int maxVertices = 0; int maxTris = 0; int maxVertsPerCont = 0; for (int i = 0; i < cset.conts.Count; i++) { // Skip null contours. if (cset.conts[i].nverts < 3) { continue; } maxVertices += cset.conts[i].nverts; maxTris += cset.conts[i].nverts - 2; maxVertsPerCont = System.Math.Max(maxVertsPerCont, cset.conts[i].nverts); } if (maxVertices >= 65534) { Debug.LogWarning("To many vertices for unity to render - Unity might screw up rendering, but hopefully the navmesh will work ok"); } /** \todo Could be cached to avoid allocations */ Int3[] verts = new Int3[maxVertices]; /** \todo Could be cached to avoid allocations */ int[] polys = new int[maxTris * nvp]; int[] areas = new int[maxTris]; Pathfinding.Util.Memory.MemSet <int>(polys, 0xff, sizeof(int)); int[] indices = new int[maxVertsPerCont]; int[] tris = new int[maxVertsPerCont * 3]; int vertexIndex = 0; int polyIndex = 0; int areaIndex = 0; for (int i = 0; i < cset.conts.Count; i++) { VoxelContour cont = cset.conts[i]; // Skip degenerate contours if (cont.nverts < 3) { continue; } for (int j = 0; j < cont.nverts; j++) { indices[j] = j; cont.verts[j * 4 + 2] /= voxelArea.width; } // Triangulate the contour int ntris = Triangulate(cont.nverts, cont.verts, ref indices, ref tris); // Assign the correct vertex indices int startIndex = vertexIndex; for (int j = 0; j < ntris * 3; polyIndex++, j++) { //@Error sometimes polys[polyIndex] = tris[j] + startIndex; } // Mark all triangles generated by this contour // as having the area cont.area for (int j = 0; j < ntris; areaIndex++, j++) { areas[areaIndex] = cont.area; } // Copy the vertex positions for (int j = 0; j < cont.nverts; vertexIndex++, j++) { verts[vertexIndex] = new Int3(cont.verts[j * 4], cont.verts[j * 4 + 1], cont.verts[j * 4 + 2]); } } mesh = new VoxelMesh(); Int3[] trimmedVerts = new Int3[vertexIndex]; for (int i = 0; i < vertexIndex; i++) { trimmedVerts[i] = verts[i]; } int[] trimmedTris = new int[polyIndex]; int[] trimmedAreas = new int[areaIndex]; System.Buffer.BlockCopy(polys, 0, trimmedTris, 0, polyIndex * sizeof(int)); System.Buffer.BlockCopy(areas, 0, trimmedAreas, 0, areaIndex * sizeof(int)); mesh.verts = trimmedVerts; mesh.tris = trimmedTris; mesh.areas = trimmedAreas; AstarProfiler.EndProfile("Build Poly Mesh"); }