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);
        }
Example #2
0
        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;
                    }
                }
            }
        }