Example #1
0
    public static bool mergeContours(ref rcContour ca, ref rcContour cb, int ia, int ib)
    {
        int maxVerts = ca.nverts + cb.nverts + 2;

        int[] verts = new int[maxVerts * 4];//(int*)rcAlloc(sizeof(int)*maxVerts*4, RC_ALLOC_PERM);
        if (verts == null)
        {
            return(false);
        }

        int nv = 0;

        // Copy contour A.
        for (int i = 0; i <= ca.nverts; ++i)
        {
            //int* dst = &verts[nv*4];
            int dstIndex = nv * 4;
            int srcIndex = ((ia + i) % ca.nverts) * 4;
            for (int j = 0; j < 4; ++j)
            {
                verts[dstIndex + j] = ca.verts[srcIndex + j];
            }
            nv++;
        }

        // Copy contour B
        for (int i = 0; i <= cb.nverts; ++i)
        {
            int dstIndex = nv * 4;
            int srcIndex = ((ib + i) % cb.nverts) * 4;
            //int* dst = &verts[nv*4];
            //const int* src = &cb.verts[((ib+i)%cb.nverts)*4];
            for (int j = 0; j < 4; ++j)
            {
                verts[dstIndex + j] = cb.verts[srcIndex + j];
            }
            nv++;
        }

        ca.verts  = verts;
        ca.nverts = nv;

        cb.verts  = null;
        cb.nverts = 0;

        return(true);
    }
Example #2
0
 // Finds the lowest leftmost vertex of a contour.
 static void findLeftMostVertex(rcContour contour, ref int minx, ref int minz, ref int leftmost)
 {
     minx     = contour.verts[0];
     minz     = contour.verts[2];
     leftmost = 0;
     for (int i = 1; i < contour.nverts; i++)
     {
         int x = contour.verts[i * 4 + 0];
         int z = contour.verts[i * 4 + 2];
         if (x < minx || (x == minx && z < minz))
         {
             minx     = x;
             minz     = z;
             leftmost = i;
         }
     }
 }
Example #3
0
    /// @par
    ///
    /// The raw contours will match the region outlines exactly. The @p maxError and @p maxEdgeLen
    /// parameters control how closely the simplified contours will match the raw contours.
    ///
    /// Simplified contours are generated such that the vertices for portals between areas match up.
    /// (They are considered mandatory vertices.)
    ///
    /// Setting @p maxEdgeLength to zero will disabled the edge length feature.
    ///
    /// See the #rcConfig documentation for more information on the configuration parameters.
    ///
    /// @see rcAllocContourSet, rcCompactHeightfield, rcContourSet, rcConfig
    public static bool rcBuildContours(rcContext ctx, rcCompactHeightfield chf, double maxError, int maxEdgeLen, rcContourSet cset, int buildFlags = 1)
    {
        Debug.Assert(ctx != null, "rcContext is null");

        int w          = chf.width;
        int h          = chf.height;
        int borderSize = chf.borderSize;

        ctx.startTimer(rcTimerLabel.RC_TIMER_BUILD_CONTOURS);

        rcVcopy(cset.bmin, chf.bmin);
        rcVcopy(cset.bmax, chf.bmax);
        if (borderSize > 0)
        {
            // If the heightfield was build with bordersize, remove the offset.
            float pad = borderSize * chf.cs;
            cset.bmin[0] += pad;
            cset.bmin[2] += pad;
            cset.bmax[0] -= pad;
            cset.bmax[2] -= pad;
        }
        cset.cs         = chf.cs;
        cset.ch         = chf.ch;
        cset.width      = chf.width - chf.borderSize * 2;
        cset.height     = chf.height - chf.borderSize * 2;
        cset.borderSize = chf.borderSize;
        cset.maxError   = (float)maxError;

        int maxContours = Math.Max((int)chf.maxRegions, 8);

        cset.conts = new rcContour[maxContours];
        for (var i = 0; i < maxContours; ++i)
        {
            cset.conts[i] = new rcContour();
        }
        cset.nconts = 0;

        byte[] flags = new byte[chf.spanCount];
        if (flags == null)
        {
            ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildContours: Out of memory 'flags' " + chf.spanCount);
            return(false);
        }

        ctx.startTimer(rcTimerLabel.RC_TIMER_BUILD_CONTOURS_TRACE);

        // Mark boundaries.
        for (int y = 0; y < h; ++y)
        {
            for (int x = 0; x < w; ++x)
            {
                rcCompactCell c = chf.cells[x + y * w];
                for (int i = (int)c.index, ni = (int)(c.index + c.count); i < ni; ++i)
                {
                    byte          res = 0;
                    rcCompactSpan s   = chf.spans[i];
                    if (chf.spans[i].reg == 0 || (chf.spans[i].reg & RC_BORDER_REG) != 0)
                    {
                        flags[i] = 0;
                        continue;
                    }
                    for (int dir = 0; dir < 4; ++dir)
                    {
                        ushort r = 0;
                        if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
                        {
                            int ax = x + rcGetDirOffsetX(dir);
                            int ay = y + rcGetDirOffsetY(dir);
                            int ai = (int)chf.cells[ax + ay * w].index + rcGetCon(s, dir);
                            r = chf.spans[ai].reg;
                        }
                        if (r == chf.spans[i].reg)
                        {
                            res |= (byte)(1 << dir);
                        }
                    }
                    flags[i] = (byte)(res ^ 0xf); // Inverse, mark non connected edges.
                }
            }
        }

        ctx.stopTimer(rcTimerLabel.RC_TIMER_BUILD_CONTOURS_TRACE);

        List <int> verts      = new(256);
        List <int> simplified = new(64);

        for (int y = 0; y < h; ++y)
        {
            for (int x = 0; x < w; ++x)
            {
                rcCompactCell c = chf.cells[x + y * w];
                for (int i = (int)c.index, ni = (int)(c.index + c.count); i < ni; ++i)
                {
                    if (flags[i] == 0 || flags[i] == 0xf)
                    {
                        flags[i] = 0;
                        continue;
                    }
                    ushort reg = chf.spans[i].reg;
                    if (reg == 0 || (reg & RC_BORDER_REG) != 0)
                    {
                        continue;
                    }
                    byte area = chf.areas[i];

                    verts.Clear();
                    simplified.Clear();

                    ctx.startTimer(rcTimerLabel.RC_TIMER_BUILD_CONTOURS_TRACE);
                    walkContour(x, y, i, chf, flags, verts);
                    ctx.stopTimer(rcTimerLabel.RC_TIMER_BUILD_CONTOURS_TRACE);

                    ctx.startTimer(rcTimerLabel.RC_TIMER_BUILD_CONTOURS_SIMPLIFY);
                    simplifyContour(verts, simplified, maxError, maxEdgeLen, buildFlags);
                    removeDegenerateSegments(simplified);
                    ctx.stopTimer(rcTimerLabel.RC_TIMER_BUILD_CONTOURS_SIMPLIFY);

                    // Store region.contour remap info.
                    // Create contour.
                    if (simplified.Count / 4 >= 3)
                    {
                        if (cset.nconts >= maxContours)
                        {
                            // Allocate more contours.
                            // This can happen when there are tiny holes in the heightfield.
                            int oldMax = maxContours;
                            maxContours *= 2;
                            rcContour[] newConts = new rcContour[maxContours];
                            for (int j = 0; j < cset.nconts; ++j)
                            {
                                newConts[j] = cset.conts[j];
                                // Reset source pointers to prevent data deletion.
                                cset.conts[j].verts  = null;
                                cset.conts[j].rverts = null;
                            }
                            cset.conts = newConts;

                            ctx.log(rcLogCategory.RC_LOG_WARNING, "rcBuildContours: Expanding max contours from " + oldMax + " to " + maxContours);
                        }

                        int contId = cset.nconts;

                        if (contId == 7)
                        {
                        }
                        cset.nconts++;
                        rcContour cont = cset.conts[contId];

                        cont.nverts = simplified.Count / 4;
                        cont.verts  = new int[cont.nverts * 4];
                        if (cont.verts == null)
                        {
                            ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildContours: Out of memory 'verts' " + cont.nverts);
                            return(false);
                        }

                        for (int j = 0; j < cont.nverts * 4; ++j)
                        {
                            cont.verts[j] = simplified[j];
                        }
                        if (borderSize > 0)
                        {
                            // If the heightfield was build with bordersize, remove the offset.
                            for (int j = 0; j < cont.nverts; ++j)
                            {
                                cont.verts[j * 4]     -= borderSize;
                                cont.verts[j * 4 + 2] -= borderSize;
                            }
                        }

                        cont.nrverts = verts.Count / 4;
                        cont.rverts  = new int[cont.nrverts * 4];
                        if (cont.rverts == null)
                        {
                            ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildContours: Out of memory 'rverts' " + cont.nrverts);
                            return(false);
                        }

                        for (int j = 0; j < cont.nrverts * 4; ++j)
                        {
                            cont.rverts[j] = verts[j];
                        }
                        if (borderSize > 0)
                        {
                            // If the heightfield was build with bordersize, remove the offset.
                            for (int j = 0; j < cont.nrverts; ++j)
                            {
                                cont.rverts[j * 4]     -= borderSize;
                                cont.rverts[j * 4 + 2] -= borderSize;
                            }
                        }

                        cont.reg  = reg;
                        cont.area = area;

                        cset.conts[contId] = cont;
                    }
                }
            }
        }

        // Merge holes if needed.
        if (cset.nconts > 0)
        {
            // Calculate winding of all polygons.
            sbyte[] winding = new sbyte[cset.nconts];

            int nholes = 0;
            for (int i = 0; i < cset.nconts; ++i)
            {
                rcContour cont = cset.conts[i];
                // If the contour is wound backwards, it is a hole.
                winding[i] = (sbyte)(calcAreaOfPolygon2D(cont.verts, cont.nverts) < 0 ? -1 : 1);
                if (winding[i] < 0)
                {
                    nholes++;
                }
            }

            if (nholes > 0)
            {
                // Collect outline contour and holes contours per region.
                // We assume that there is one outline and multiple holes.
                int nregions = chf.maxRegions + 1;
                rcContourRegion[] regions = new rcContourRegion[nregions];
                for (var i = 0; i < nregions; ++i)
                {
                    regions[i] = new rcContourRegion();
                }

                rcContourHole[] holes = new rcContourHole[cset.nconts];
                for (var i = 0; i < cset.nconts; ++i)
                {
                    holes[i] = new rcContourHole();
                }

                for (int i = 0; i < cset.nconts; ++i)
                {
                    rcContour cont = cset.conts[i];
                    // Positively would contours are outlines, negative holes.
                    if (winding[i] > 0)
                    {
                        regions[cont.reg].outline = cont;
                    }
                    else
                    {
                        regions[cont.reg].nholes++;
                    }
                }
                int index = 0;
                for (int i = 0; i < nregions; i++)
                {
                    if (regions[i].nholes > 0)
                    {
                        regions[i].holes = new rcContourHole[cset.nconts];
                        Array.Copy(holes, index, regions[i].holes, 0, cset.nconts - index);
                        index            += regions[i].nholes;
                        regions[i].nholes = 0;
                    }
                }
                for (int i = 0; i < cset.nconts; ++i)
                {
                    rcContour       cont = cset.conts[i];
                    rcContourRegion reg  = regions[cont.reg];
                    if (winding[i] < 0)
                    {
                        reg.holes[reg.nholes++].contour = cont;
                    }
                }

                // Finally merge each regions holes into the outline.
                for (int i = 0; i < nregions; i++)
                {
                    rcContourRegion reg = regions[i];
                    if (reg.nholes == 0)
                    {
                        continue;
                    }

                    if (reg.outline.verts != null)
                    {
                        mergeRegionHoles(ctx, reg);
                    }
                    else
                    {
                        // The region does not have an outline.
                        // This can happen if the contour becaomes selfoverlapping because of
                        // too aggressive simplification settings.
                        ctx.log(rcLogCategory.RC_LOG_ERROR, string.Format("rcBuildContours: Bad outline for region {0}, contour simplification is likely too aggressive.", i));
                    }
                }
            }
        }

        return(true);
    }
Example #4
0
    static void mergeRegionHoles(rcContext ctx, rcContourRegion region)
    {
        // Sort holes from left to right.
        for (int i = 0; i < region.nholes; i++)
        {
            findLeftMostVertex(region.holes[i].contour, ref region.holes[i].minx, ref region.holes[i].minz, ref region.holes[i].leftmost);
        }

        var list = region.holes.ToList();

        list.RemoveAll(p => p.contour == null);
        list.Sort(new ContourHoldCompare <rcContourHole>());
        region.holes = list.ToArray();

        int maxVerts = region.outline.nverts;

        for (int i = 0; i < region.nholes; i++)
        {
            maxVerts += region.holes[i].contour.nverts;
        }

        rcPotentialDiagonal[] diags = new rcPotentialDiagonal[maxVerts];

        rcContour outline = region.outline;

        // Merge holes into the outline one by one.
        for (int i = 0; i < region.nholes; i++)
        {
            rcContour hole = region.holes[i].contour;

            int index      = -1;
            int bestVertex = region.holes[i].leftmost;
            for (int iter = 0; iter < hole.nverts; iter++)
            {
                // Find potential diagonals.
                // The 'best' vertex must be in the cone described by 3 cosequtive vertices of the outline.
                // ..o j-1
                //   |
                //   |   * best
                //   |
                // j o-----o j+1
                //         :
                int ndiags      = 0;
                int cornerIndex = bestVertex * 4;
                for (int j = 0; j < outline.nverts; j++)
                {
                    if (inCone(j, outline.nverts, outline.verts, hole.verts, cornerIndex))
                    {
                        int dx = outline.verts[j * 4 + 0] - hole.verts[cornerIndex + 0];
                        int dz = outline.verts[j * 4 + 2] - hole.verts[cornerIndex + 2];
                        diags[ndiags]      = new rcPotentialDiagonal();
                        diags[ndiags].vert = j;
                        diags[ndiags].dist = dx * dx + dz * dz;
                        ndiags++;
                    }
                }

                List <rcPotentialDiagonal> sortedDiags = new();
                for (var gg = 0; gg < ndiags; ++gg)
                {
                    sortedDiags.Add(diags[gg]);
                }

                // Sort potential diagonals by distance, we want to make the connection as short as possible.
                sortedDiags.Sort(new PotentialDiagonalCompare <rcPotentialDiagonal>());

                // Find a diagonal that is not intersecting the outline not the remaining holes.
                index = -1;
                for (int j = 0; j < ndiags; j++)
                {
                    int  ptStart   = sortedDiags[j].vert * 4;
                    bool intersect = intersectSegCountour(outline.verts, ptStart, hole.verts, cornerIndex, sortedDiags[i].vert, outline.nverts, outline.verts, 0);
                    for (int k = i; k < region.nholes && !intersect; k++)
                    {
                        intersect |= intersectSegCountour(outline.verts, ptStart, hole.verts, cornerIndex, -1, region.holes[k].contour.nverts, region.holes[k].contour.verts, 0);
                    }
                    if (!intersect)
                    {
                        index = sortedDiags[j].vert;
                        break;
                    }
                }

                // If found non-intersecting diagonal, stop looking.
                if (index != -1)
                {
                    break;
                }
                // All the potential diagonals for the current vertex were intersecting, try next vertex.
                bestVertex = (bestVertex + 1) % hole.nverts;
            }

            if (index == -1)
            {
                //ctx->log(RC_LOG_WARNING, "mergeHoles: Failed to find merge points for %p and %p.", region.outline, hole);
                continue;
            }
            if (!mergeContours(ref region.outline, ref hole, index, bestVertex))
            {
                //ctx->log(RC_LOG_WARNING, "mergeHoles: Failed to merge contours %p and %p.", region.outline, hole);
                continue;
            }
        }
    }
        /// @par
        ///
        /// The raw contours will match the region outlines exactly. The @p maxError and @p maxEdgeLen
        /// parameters control how closely the simplified contours will match the raw contours.
        ///
        /// Simplified contours are generated such that the vertices for portals between areas match up.
        /// (They are considered mandatory vertices.)
        ///
        /// Setting @p maxEdgeLength to zero will disabled the edge length feature.
        ///
        /// See the #rcConfig documentation for more information on the configuration parameters.
        ///
        /// @see rcAllocContourSet, rcCompactHeightfield, rcContourSet, rcConfig
        public static bool rcBuildContours(rcContext ctx, rcCompactHeightfield chf,
                                           float maxError, int maxEdgeLen,
                                           rcContourSet cset, int buildFlags)
        {
            Debug.Assert(ctx != null, "rcContext is null");

            int w          = chf.width;
            int h          = chf.height;
            int borderSize = chf.borderSize;

            ctx.startTimer(rcTimerLabel.RC_TIMER_BUILD_CONTOURS);

            rcVcopy(cset.bmin, chf.bmin);
            rcVcopy(cset.bmax, chf.bmax);
            if (borderSize > 0)
            {
                // If the heightfield was build with bordersize, remove the offset.
                float pad = borderSize * chf.cs;
                cset.bmin[0] += pad;
                cset.bmin[2] += pad;
                cset.bmax[0] -= pad;
                cset.bmax[2] -= pad;
            }
            cset.cs         = chf.cs;
            cset.ch         = chf.ch;
            cset.width      = chf.width - chf.borderSize * 2;
            cset.height     = chf.height - chf.borderSize * 2;
            cset.borderSize = chf.borderSize;

            int maxContours = Math.Max((int)chf.maxRegions, 8);

            //cset.conts = (rcContour*)rcAlloc(sizeof(rcContour)*maxContours, RC_ALLOC_PERM);
            cset.conts = new rcContour[maxContours];
            //if (cset.conts == null)
//		return false;
            cset.nconts = 0;

            //rcScopedDelete<byte> flags = (byte*)rcAlloc(sizeof(byte)*chf.spanCount, RC_ALLOC_TEMP);
            byte[] flags = new byte[chf.spanCount];
            if (flags == null)
            {
                ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildContours: Out of memory 'flags' " + chf.spanCount);
                return(false);
            }

            ctx.startTimer(rcTimerLabel.RC_TIMER_BUILD_CONTOURS_TRACE);

            // Mark boundaries.
            for (int y = 0; y < h; ++y)
            {
                for (int x = 0; x < w; ++x)
                {
                    rcCompactCell c = chf.cells[x + y * w];
                    for (int i = (int)c.index, ni = (int)(c.index + c.count); i < ni; ++i)
                    {
                        byte          res = 0;
                        rcCompactSpan s   = chf.spans[i];
                        if (chf.spans[i].reg == 0 || (chf.spans[i].reg & RC_BORDER_REG) != 0)
                        {
                            flags[i] = 0;
                            continue;
                        }
                        for (int dir = 0; dir < 4; ++dir)
                        {
                            ushort r = 0;
                            if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
                            {
                                int ax = x + rcGetDirOffsetX(dir);
                                int ay = y + rcGetDirOffsetY(dir);
                                int ai = (int)chf.cells[ax + ay * w].index + rcGetCon(s, dir);
                                r = chf.spans[ai].reg;
                            }
                            if (r == chf.spans[i].reg)
                            {
                                res |= (byte)(1 << dir);
                            }
                        }
                        flags[i] = (byte)(res ^ 0xf); // Inverse, mark non connected edges.
                    }
                }
            }

            ctx.stopTimer(rcTimerLabel.RC_TIMER_BUILD_CONTOURS_TRACE);

            //List<int> verts(256);
            List <int> verts = new List <int>();

            verts.Capacity = 256;
            //List<int> simplified(64);
            List <int> simplified = new List <int>();

            simplified.Capacity = 64;

            for (int y = 0; y < h; ++y)
            {
                for (int x = 0; x < w; ++x)
                {
                    rcCompactCell c = chf.cells[x + y * w];
                    for (int i = (int)c.index, ni = (int)(c.index + c.count); i < ni; ++i)
                    {
                        if (flags[i] == 0 || flags[i] == 0xf)
                        {
                            flags[i] = 0;
                            continue;
                        }
                        ushort reg = chf.spans[i].reg;
                        if (reg == 0 || (reg & RC_BORDER_REG) != 0)
                        {
                            continue;
                        }
                        byte area = chf.areas[i];

                        //verts.resize(0);
                        //simplified.resize(0);
                        verts.Clear();
                        simplified.Clear();

                        ctx.startTimer(rcTimerLabel.RC_TIMER_BUILD_CONTOURS_TRACE);
                        walkContour(x, y, i, chf, flags, verts);
                        ctx.stopTimer(rcTimerLabel.RC_TIMER_BUILD_CONTOURS_TRACE);

                        ctx.startTimer(rcTimerLabel.RC_TIMER_BUILD_CONTOURS_SIMPLIFY);
                        simplifyContour(verts, simplified, maxError, maxEdgeLen, buildFlags);
                        removeDegenerateSegments(simplified);
                        ctx.stopTimer(rcTimerLabel.RC_TIMER_BUILD_CONTOURS_SIMPLIFY);


                        // Store region.contour remap info.
                        // Create contour.
                        if (simplified.Count / 4 >= 3)
                        {
                            if (cset.nconts >= maxContours)
                            {
                                // Allocate more contours.
                                // This can happen when there are tiny holes in the heightfield.
                                int oldMax = maxContours;
                                maxContours *= 2;
                                rcContour[] newConts = new rcContour[maxContours];// (rcContour*)rcAlloc(sizeof(rcContour) * maxContours, RC_ALLOC_PERM);
                                for (int j = 0; j < cset.nconts; ++j)
                                {
                                    newConts[j] = cset.conts[j];
                                    // Reset source pointers to prevent data deletion.
                                    cset.conts[j].verts  = null;
                                    cset.conts[j].rverts = null;
                                }
                                //rcFree(cset.conts);
                                cset.conts = newConts;

                                ctx.log(rcLogCategory.RC_LOG_WARNING, "rcBuildContours: Expanding max contours from " + oldMax + " to " + maxContours);
                            }

                            int contId = cset.nconts;
                            cset.nconts++;
                            rcContour cont = cset.conts[contId];

                            cont.nverts = simplified.Count / 4;
                            cont.verts  = new int[cont.nverts * 4]; //(int*)rcAlloc(sizeof(int)*cont.nverts*4, RC_ALLOC_PERM);
                            if (cont.verts == null)
                            {
                                ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildContours: Out of memory 'verts' " + cont.nverts);
                                return(false);
                            }
                            //memcpy(cont.verts, &simplified[0], sizeof(int)*cont.nverts*4);
                            for (int j = 0; j < cont.nverts * 4; ++j)
                            {
                                cont.verts[j] = simplified[j];
                            }
                            if (borderSize > 0)
                            {
                                // If the heightfield was build with bordersize, remove the offset.
                                for (int j = 0; j < cont.nverts; ++j)
                                {
                                    //int* v = &cont.verts[j*4];
                                    cont.verts[j * 4]     -= borderSize;
                                    cont.verts[j * 4 + 2] -= borderSize;
                                    //v[0] -= borderSize;
                                    //v[2] -= borderSize;
                                }
                            }

                            cont.nrverts = verts.Count / 4;
                            cont.rverts  = new int[cont.nrverts * 4];//(int*)rcAlloc(sizeof(int)*cont.nrverts*4, RC_ALLOC_PERM);
                            if (cont.rverts == null)
                            {
                                ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildContours: Out of memory 'rverts' " + cont.nrverts);
                                return(false);
                            }
                            //memcpy(cont.rverts, &verts[0], sizeof(int)*cont.nrverts*4);
                            for (int j = 0; j < cont.nrverts * 4; ++j)
                            {
                                cont.rverts[j] = verts[j];
                            }
                            if (borderSize > 0)
                            {
                                // If the heightfield was build with bordersize, remove the offset.
                                for (int j = 0; j < cont.nrverts; ++j)
                                {
                                    //int* v = &cont.rverts[j*4];
                                    cont.rverts[j * 4]     -= borderSize;
                                    cont.rverts[j * 4 + 2] -= borderSize;
                                }
                            }

/*					cont.cx = cont.cy = cont.cz = 0;
 *                                      for (int i = 0; i < cont.nverts; ++i)
 *                                      {
 *                                              cont.cx += cont.verts[i*4+0];
 *                                              cont.cy += cont.verts[i*4+1];
 *                                              cont.cz += cont.verts[i*4+2];
 *                                      }
 *                                      cont.cx /= cont.nverts;
 *                                      cont.cy /= cont.nverts;
 *                                      cont.cz /= cont.nverts;*/

                            cont.reg  = reg;
                            cont.area = area;

                            cset.conts[contId] = cont;
                        }
                    }
                }
            }

            // 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 < cset.nconts; ++i)
            {
                rcContour cont = cset.conts[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 < cset.nconts; ++j)
                    {
                        if (i == j)
                        {
                            continue;
                        }
                        if (cset.conts[j].nverts != 0 && cset.conts[j].reg == cont.reg)
                        {
                            // Make sure the polygon is correctly oriented.
                            if (calcAreaOfPolygon2D(cset.conts[j].verts, cset.conts[j].nverts) != 0)
                            {
                                mergeIdx = j;
                                break;
                            }
                        }
                    }
                    if (mergeIdx == -1)
                    {
                        ctx.log(rcLogCategory.RC_LOG_WARNING, "rcBuildContours: Could not find merge target for bad contour " + i);
                    }
                    else
                    {
                        rcContour mcont = cset.conts[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)
                        {
                            ctx.log(rcLogCategory.RC_LOG_WARNING, "rcBuildContours: Failed to find merge points for " + i + " and " + mergeIdx);
                            continue;
                        }
                        if (!mergeContours(ref mcont, ref cont, ia, ib))
                        {
                            ctx.log(rcLogCategory.RC_LOG_WARNING, "rcBuildContours: Failed to merge contours " + i + " and " + mergeIdx);
                            continue;
                        }
                        cset.conts[mergeIdx] = mcont;
                        cset.conts[i]        = cont;
                    }
                }
            }

            ctx.stopTimer(rcTimerLabel.RC_TIMER_BUILD_CONTOURS);

            return(true);
        }
Example #6
0
    /// @par
    ///
    /// The raw contours will match the region outlines exactly. The @p maxError and @p maxEdgeLen
    /// parameters control how closely the simplified contours will match the raw contours.
    ///
    /// Simplified contours are generated such that the vertices for portals between areas match up. 
    /// (They are considered mandatory vertices.)
    ///
    /// Setting @p maxEdgeLength to zero will disabled the edge length feature.
    /// 
    /// See the #rcConfig documentation for more information on the configuration parameters.
    /// 
    /// @see rcAllocContourSet, rcCompactHeightfield, rcContourSet, rcConfig
    public static bool rcBuildContours(rcContext ctx, rcCompactHeightfield chf,
					 float maxError, int maxEdgeLen,
					 rcContourSet cset, int buildFlags)
    {
        Debug.Assert(ctx != null, "rcContext is null");

        int w = chf.width;
        int h = chf.height;
        int borderSize = chf.borderSize;

        ctx.startTimer(rcTimerLabel.RC_TIMER_BUILD_CONTOURS);

        rcVcopy(cset.bmin, chf.bmin);
        rcVcopy(cset.bmax, chf.bmax);
        if (borderSize > 0)
        {
        // If the heightfield was build with bordersize, remove the offset.
        float pad = borderSize*chf.cs;
        cset.bmin[0] += pad;
        cset.bmin[2] += pad;
        cset.bmax[0] -= pad;
        cset.bmax[2] -= pad;
        }
        cset.cs = chf.cs;
        cset.ch = chf.ch;
        cset.width = chf.width - chf.borderSize*2;
        cset.height = chf.height - chf.borderSize*2;
        cset.borderSize = chf.borderSize;

        int maxContours = Math.Max((int)chf.maxRegions, 8);
        //cset.conts = (rcContour*)rcAlloc(sizeof(rcContour)*maxContours, RC_ALLOC_PERM);
        cset.conts = new rcContour[maxContours];
        //if (cset.conts == null)
        //		return false;
        cset.nconts = 0;

        //rcScopedDelete<byte> flags = (byte*)rcAlloc(sizeof(byte)*chf.spanCount, RC_ALLOC_TEMP);
        byte[] flags = new byte[chf.spanCount];
        if (flags == null)
        {
        ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildContours: Out of memory 'flags' " + chf.spanCount);
        return false;
        }

        ctx.startTimer(rcTimerLabel.RC_TIMER_BUILD_CONTOURS_TRACE);

        // Mark boundaries.
        for (int y = 0; y < h; ++y)
        {
        for (int x = 0; x < w; ++x)
        {
            rcCompactCell c = chf.cells[x+y*w];
            for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
            {
                byte res = 0;
                rcCompactSpan s = chf.spans[i];
                if (chf.spans[i].reg == 0 || (chf.spans[i].reg & RC_BORDER_REG) != 0)
                {
                    flags[i] = 0;
                    continue;
                }
                for (int dir = 0; dir < 4; ++dir)
                {
                    ushort r = 0;
                    if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
                    {
                        int ax = x + rcGetDirOffsetX(dir);
                        int ay = y + rcGetDirOffsetY(dir);
                        int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, dir);
                        r = chf.spans[ai].reg;
                    }
                    if (r == chf.spans[i].reg)
                        res |= (byte)(1 << dir);
                }
                flags[i] = (byte)(res ^ 0xf); // Inverse, mark non connected edges.
            }
        }
        }

        ctx.stopTimer(rcTimerLabel.RC_TIMER_BUILD_CONTOURS_TRACE);

        //List<int> verts(256);
        List<int> verts = new List<int>();
        verts.Capacity = 256;
        //List<int> simplified(64);
        List<int> simplified = new List<int>();
        simplified.Capacity = 64;

        for (int y = 0; y < h; ++y)
        {
        for (int x = 0; x < w; ++x)
        {
            rcCompactCell c = chf.cells[x+y*w];
            for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
            {
                if (flags[i] == 0 || flags[i] == 0xf)
                {
                    flags[i] = 0;
                    continue;
                }
                ushort reg = chf.spans[i].reg;
                if (reg == 0 || (reg & RC_BORDER_REG) != 0) {
                    continue;
                }
                byte area = chf.areas[i];

                //verts.resize(0);
                //simplified.resize(0);
                verts.Clear();
                simplified.Clear();

                ctx.startTimer(rcTimerLabel.RC_TIMER_BUILD_CONTOURS_TRACE);
                walkContour(x, y, i, chf, flags, verts);
                ctx.stopTimer(rcTimerLabel.RC_TIMER_BUILD_CONTOURS_TRACE);

                ctx.startTimer(rcTimerLabel.RC_TIMER_BUILD_CONTOURS_SIMPLIFY);
                simplifyContour(verts, simplified, maxError, maxEdgeLen, buildFlags);
                removeDegenerateSegments(simplified);
                ctx.stopTimer(rcTimerLabel.RC_TIMER_BUILD_CONTOURS_SIMPLIFY);

                // Store region.contour remap info.
                // Create contour.
                if (simplified.Count/4 >= 3)
                {
                    if (cset.nconts >= maxContours)
                    {
                        // Allocate more contours.
                        // This can happen when there are tiny holes in the heightfield.
                        int oldMax = maxContours;
                        maxContours *= 2;
                        rcContour[] newConts = new rcContour[maxContours];// (rcContour*)rcAlloc(sizeof(rcContour) * maxContours, RC_ALLOC_PERM);
                        for (int j = 0; j < cset.nconts; ++j)
                        {
                            newConts[j] = cset.conts[j];
                            // Reset source pointers to prevent data deletion.
                            cset.conts[j].verts = null;
                            cset.conts[j].rverts = null;
                        }
                        //rcFree(cset.conts);
                        cset.conts = newConts;

                        ctx.log(rcLogCategory.RC_LOG_WARNING, "rcBuildContours: Expanding max contours from " +  oldMax + " to "+ maxContours);
                    }

                    int contId = cset.nconts;
                        cset.nconts++;
                    rcContour cont = cset.conts[contId];

                    cont.nverts = simplified.Count/4;
                    cont.verts = new int[cont.nverts * 4]; //(int*)rcAlloc(sizeof(int)*cont.nverts*4, RC_ALLOC_PERM);
                    if (cont.verts == null)
                    {
                        ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildContours: Out of memory 'verts' " + cont.nverts);
                        return false;
                    }
                    //memcpy(cont.verts, &simplified[0], sizeof(int)*cont.nverts*4);
                    for (int j = 0; j < cont.nverts * 4; ++j) {
                        cont.verts[j] = simplified[j];
                    }
                    if (borderSize > 0)
                    {
                        // If the heightfield was build with bordersize, remove the offset.
                        for (int j = 0; j < cont.nverts; ++j)
                        {
                            //int* v = &cont.verts[j*4];
                            cont.verts[j * 4] -= borderSize;
                            cont.verts[j*4  + 2] -= borderSize;
                            //v[0] -= borderSize;
                            //v[2] -= borderSize;
                        }
                    }

                    cont.nrverts = verts.Count/4;
                    cont.rverts = new int[cont.nrverts * 4];//(int*)rcAlloc(sizeof(int)*cont.nrverts*4, RC_ALLOC_PERM);
                    if (cont.rverts == null)
                    {
                        ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildContours: Out of memory 'rverts' " + cont.nrverts);
                        return false;
                    }
                    //memcpy(cont.rverts, &verts[0], sizeof(int)*cont.nrverts*4);
                    for (int j = 0; j < cont.nrverts * 4; ++j) {
                        cont.rverts[j] = verts[j];
                    }
                    if (borderSize > 0)
                    {
                        // If the heightfield was build with bordersize, remove the offset.
                        for (int j = 0; j < cont.nrverts; ++j)
                        {
                            //int* v = &cont.rverts[j*4];
                            cont.rverts[j * 4] -= borderSize;
                            cont.rverts[j * 4 + 2] -= borderSize;
                        }
                    }

        /*					cont.cx = cont.cy = cont.cz = 0;
                    for (int i = 0; i < cont.nverts; ++i)
                    {
                        cont.cx += cont.verts[i*4+0];
                        cont.cy += cont.verts[i*4+1];
                        cont.cz += cont.verts[i*4+2];
                    }
                    cont.cx /= cont.nverts;
                    cont.cy /= cont.nverts;
                    cont.cz /= cont.nverts;*/

                    cont.reg = reg;
                    cont.area = area;

                    cset.conts[contId] = cont;
                }
            }
        }
        }

        // 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 < cset.nconts; ++i)
        {
        rcContour cont = cset.conts[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 < cset.nconts; ++j)
            {
                if (i == j) continue;
                if (cset.conts[j].nverts != 0 && cset.conts[j].reg == cont.reg)
                {
                    // Make sure the polygon is correctly oriented.
                    if (calcAreaOfPolygon2D(cset.conts[j].verts, cset.conts[j].nverts) != 0)
                    {
                        mergeIdx = j;
                        break;
                    }
                }
            }
            if (mergeIdx == -1)
            {
                ctx.log(rcLogCategory.RC_LOG_WARNING, "rcBuildContours: Could not find merge target for bad contour " + i);
            }
            else
            {
                rcContour mcont = cset.conts[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)
                {
                    ctx.log(rcLogCategory.RC_LOG_WARNING, "rcBuildContours: Failed to find merge points for " +  i + " and " + mergeIdx);
                    continue;
                }
                if (!mergeContours(ref mcont,ref cont, ia, ib))
                {
                    ctx.log(rcLogCategory.RC_LOG_WARNING, "rcBuildContours: Failed to merge contours " + i + " and " + mergeIdx);
                    continue;
                }
                cset.conts[mergeIdx] = mcont;
                cset.conts[i] = cont;
            }
        }
        }

        ctx.stopTimer(rcTimerLabel.RC_TIMER_BUILD_CONTOURS);

        return true;
    }
Example #7
0
    public static bool mergeContours(ref rcContour ca, ref rcContour cb, int ia, int ib)
    {
        int maxVerts = ca.nverts + cb.nverts + 2;
        int[] verts = new int[maxVerts * 4];//(int*)rcAlloc(sizeof(int)*maxVerts*4, RC_ALLOC_PERM);
        if (verts == null)
        return false;

        int nv = 0;

        // Copy contour A.
        for (int i = 0; i <= ca.nverts; ++i)
        {
        //int* dst = &verts[nv*4];
        int dstIndex = nv*4;
        int srcIndex = ((ia+i)%ca.nverts)*4;
        for (int j=0;i<4;++i){
            verts[dstIndex + j] = ca.verts[srcIndex + j];
        }
        nv++;
        }

        // Copy contour B
        for (int i = 0; i <= cb.nverts; ++i)
        {
        int dstIndex = nv*4;
        int srcIndex = ((ib+i)%cb.nverts)*4;
        //int* dst = &verts[nv*4];
        //const int* src = &cb.verts[((ib+i)%cb.nverts)*4];
        for (int j=0;j<4;++j){
            verts[dstIndex + j] = cb.verts[srcIndex + j];
        }
        nv++;
        }

        ca.verts = verts;
        ca.nverts = nv;

        cb.verts = null;
        cb.nverts = 0;

        return true;
    }