public static bool MergeContours(NativeList <int> verts, ref VoxelContour ca, ref VoxelContour cb, int ia, int ib) { // Note: this will essentially leave junk data in the verts array where the contours were previously. // This shouldn't be a big problem because MergeContours is normally not called for that many contours (usually none). int nv = 0; var startIndex = verts.Length; // Copy contour A. for (int i = 0; i <= ca.nverts; i++) { int src = ca.vertexStartIndex + ((ia + i) % ca.nverts) * 4; verts.Add(verts[src + 0]); verts.Add(verts[src + 1]); verts.Add(verts[src + 2]); verts.Add(verts[src + 3]); nv++; } // Copy contour B for (int i = 0; i <= cb.nverts; i++) { int src = cb.vertexStartIndex + ((ib + i) % cb.nverts) * 4; verts.Add(verts[src + 0]); verts.Add(verts[src + 1]); verts.Add(verts[src + 2]); verts.Add(verts[src + 3]); nv++; } ca.vertexStartIndex = startIndex; ca.nverts = nv; cb.vertexStartIndex = 0; cb.nverts = 0; return(true); }
public void Execute() { // Maximum allowed vertices per polygon. Currently locked to 3. var nvp = 3; int maxVertices = 0; int maxTris = 0; int maxVertsPerCont = 0; for (int i = 0; i < contours.Length; i++) { // Skip null contours. if (contours[i].nverts < 3) { continue; } maxVertices += contours[i].nverts; maxTris += contours[i].nverts - 2; maxVertsPerCont = System.Math.Max(maxVertsPerCont, contours[i].nverts); } var verts = new NativeArray <Int3>(maxVertices, Allocator.Temp); var polys = new NativeArray <int>(maxTris * nvp, Allocator.Temp); var areas = new NativeArray <int>(maxTris, Allocator.Temp); for (int i = 0; i < polys.Length; i++) { polys[i] = 0xff; } var indices = new NativeArray <int>(maxVertsPerCont, Allocator.Temp); var tris = new NativeArray <int>(maxVertsPerCont * 3, Allocator.Temp); int vertexIndex = 0; int polyIndex = 0; int areaIndex = 0; for (int i = 0; i < contours.Length; i++) { VoxelContour cont = contours[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 contourVertices[cont.vertexStartIndex + j * 4 + 2] /= field.width; } // Triangulate the contour int ntris = Triangulate(cont.nverts, contourVertices.AsArray().GetSubArray(cont.vertexStartIndex, cont.nverts * 4), 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( contourVertices[cont.vertexStartIndex + j * 4], contourVertices[cont.vertexStartIndex + j * 4 + 1], contourVertices[cont.vertexStartIndex + j * 4 + 2] ); } } // TODO: Avoid allocating two arrays for each type mesh.verts.Clear(); mesh.tris.Clear(); mesh.areas.Clear(); // TODO: Why reduce capacity? mesh.verts.Capacity = vertexIndex; mesh.tris.Capacity = polyIndex; mesh.areas.Capacity = areaIndex; mesh.verts.AddRange(verts.GetSubArray(0, vertexIndex)); mesh.tris.AddRange(polys.GetSubArray(0, polyIndex)); mesh.areas.AddRange(areas.GetSubArray(0, areaIndex)); }
public void Execute() { outputContours.Clear(); outputVerts.Clear(); int w = field.width; int d = field.depth; int wd = w * d; //cset.bounds = field.bounds; const ushort BorderReg = VoxelUtilityBurst.BorderReg; //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. var flags = new NativeArray <ushort>(field.spans.Length, Allocator.Temp, NativeArrayOptions.UninitializedMemory); // Mark boundaries. (@?) for (int z = 0; z < wd; z += field.width) { for (int x = 0; x < field.width; x++) { CompactVoxelCell c = field.cells[x + z]; for (int i = (int)c.index, ci = (int)(c.index + c.count); i < ci; i++) { ushort res = 0; CompactVoxelSpan s = field.spans[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) != CompactVoxelField.NotConnected) { int ni = (int)field.cells[field.GetNeighbourIndex(x + z, dir)].index + s.GetConnection(dir); r = field.spans[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); } } } NativeList <int> verts = new NativeList <int>(256, Allocator.Temp); NativeList <int> simplified = new NativeList <int>(64, Allocator.Temp); for (int z = 0; z < wd; z += field.width) { for (int x = 0; x < field.width; x++) { CompactVoxelCell c = field.cells[x + z]; for (int i = (int)c.index, ci = (int)(c.index + c.count); i < ci; i++) { //CompactVoxelSpan s = field.spans[i]; if (flags[i] == 0 || flags[i] == 0xf) { flags[i] = 0; continue; } int reg = field.spans[i].reg; if (reg == 0 || (reg & BorderReg) == BorderReg) { continue; } int area = field.areaTypes[i]; verts.Clear(); simplified.Clear(); WalkContour(x, z, i, flags, verts); SimplifyContour(verts, simplified, maxError, buildFlags); RemoveDegenerateSegments(simplified); VoxelContour contour = new VoxelContour(); contour.vertexStartIndex = outputVerts.Length; outputVerts.AddRange(simplified); // #if ASTAR_RECAST_INCLUDE_RAW_VERTEX_CONTOUR // //Not used at the moment, just debug stuff // contour.rverts = ClaimIntArr(verts.Length); // for (int j = 0; j < verts.Length; j++) contour.rverts[j] = verts[j]; // #endif contour.nverts = simplified.Length / 4; contour.reg = reg; contour.area = area; outputContours.Add(contour); // #if ASTARDEBUG // for (int q = 0, j = (simplified.Length/4)-1; q < (simplified.Length/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)field.width) // ), // cellScale) // +voxelOffset; // Vector3 p2 = Vector3.Scale( // new Vector3( // simplified[j4+0], // simplified[j4+1], // (simplified[j4+2]/(float)field.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 } } } verts.Dispose(); simplified.Dispose(); // Check and merge droppings. // Sometimes the previous algorithms can fail and create several outputContours // per area. This pass will try to merge the holes into the main region. for (int i = 0; i < outputContours.Length; i++) { VoxelContour cont = outputContours[i]; // Check if the contour is would backwards. if (CalcAreaOfPolygon2D(outputVerts, cont.vertexStartIndex, cont.nverts) < 0) { // Find another contour which has the same region ID. int mergeIdx = -1; for (int j = 0; j < outputContours.Length; j++) { if (i == j) { continue; } if (outputContours[j].nverts > 0 && outputContours[j].reg == cont.reg) { // Make sure the polygon is correctly oriented. if (CalcAreaOfPolygon2D(outputVerts, outputContours[j].vertexStartIndex, outputContours[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 = outputContours[mergeIdx]; // Merge by closest points. int ia = 0, ib = 0; GetClosestIndices(outputVerts, mcont.vertexStartIndex, mcont.nverts, cont.vertexStartIndex, 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 (!MergeContours(outputVerts, ref mcont, ref cont, ia, ib)) { //Debug.LogWarning("rcBuildContours: Failed to merge contours "+i+" and "+mergeIdx+"."); continue; } outputContours[mergeIdx] = mcont; outputContours[i] = cont; } } } }