Exemplo n.º 1
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);
    }
Exemplo n.º 2
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;
            }
        }
    }