示例#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);
    }
        /// @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);
        }
示例#3
0
    /// @par
    ///
    /// @note If the mesh data is to be used to construct a Detour navigation mesh, then the upper 
    /// limit must be retricted to <= #DT_VERTS_PER_POLYGON.
    ///
    /// @see rcAllocPolyMesh, rcContourSet, rcPolyMesh, rcConfig
    public static bool rcBuildPolyMesh(rcContext ctx, rcContourSet cset, int nvp, rcPolyMesh mesh)
    {
        Debug.Assert(ctx != null, "rcContext is null");

        ctx.startTimer(rcTimerLabel.RC_TIMER_BUILD_POLYMESH);

        rcVcopy(mesh.bmin, cset.bmin);
        rcVcopy(mesh.bmax, cset.bmax);
        mesh.cs = cset.cs;
        mesh.ch = cset.ch;
        mesh.borderSize = cset.borderSize;

        int maxVertices = 0;
        int maxTris = 0;
        int maxVertsPerCont = 0;
        for (int i = 0; i < cset.nconts; ++i) {
            // Skip null contours.
            if (cset.conts[i].nverts < 3) continue;
            maxVertices += cset.conts[i].nverts;
            maxTris += cset.conts[i].nverts - 2;
            maxVertsPerCont = Math.Max(maxVertsPerCont, cset.conts[i].nverts);
        }

        if (maxVertices >= 0xfffe) {
            ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildPolyMesh: Too many vertices " + maxVertices);
            return false;
        }

        //rcScopedDelete<byte> vflags = (byte*)rcAlloc(sizeof(byte)*maxVertices, RC_ALLOC_TEMP);
        byte[] vflags = new byte[maxVertices];
        if (vflags == null) {
            ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'vflags' " + maxVertices);
            return false;
        }
        //memset(vflags, 0, maxVertices);

        //mesh.verts = (ushort*)rcAlloc(sizeof(ushort)*maxVertices*3, RC_ALLOC_PERM);
        mesh.verts = new ushort[maxVertices * 3];
        if (mesh.verts == null) {
            ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.verts' " + maxVertices);
            return false;
        }
        //mesh.polys = (ushort*)rcAlloc(sizeof(ushort)*maxTris*nvp*2, RC_ALLOC_PERM);
        mesh.polys = new ushort[maxTris * nvp * 2];
        if (mesh.polys == null) {
            ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.polys' " + maxTris * nvp * 2);
            return false;
        }
        //mesh.regs = (ushort*)rcAlloc(sizeof(ushort)*maxTris, RC_ALLOC_PERM);
        mesh.regs = new ushort[maxTris];
        if (mesh.regs == null) {
            ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.regs' " + maxTris);
            return false;
        }
        //mesh.areas = (byte*)rcAlloc(sizeof(byte)*maxTris, RC_ALLOC_PERM);
        mesh.areas = new byte[maxTris];
        if (mesh.areas == null) {
            ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.areas' " + maxTris);
            return false;
        }

        mesh.nverts = 0;
        mesh.npolys = 0;
        mesh.nvp = nvp;
        mesh.maxpolys = maxTris;

        //memset(mesh.verts, 0, sizeof(ushort)*maxVertices*3);
        //memset(mesh.polys, 0xff, sizeof(ushort)*maxTris*nvp*2);
        for (int i = 0; i < maxTris * nvp * 2; ++i) {
            mesh.polys[i] = 0xffff;
        }
        //memset(mesh.regs, 0, sizeof(ushort)*maxTris);
        //memset(mesh.areas, 0, sizeof(byte)*maxTris);

        //rcScopedDelete<int> nextVert = (int*)rcAlloc(sizeof(int)*maxVertices, RC_ALLOC_TEMP);
        int[] nextVert = new int[maxVertices];
        if (nextVert == null) {
            ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'nextVert' " + maxVertices);
            return false;
        }
        //memset(nextVert, 0, sizeof(int)*maxVertices);

        //rcScopedDelete<int> firstVert = (int*)rcAlloc(sizeof(int)*VERTEX_BUCKET_COUNT, RC_ALLOC_TEMP);
        int[] firstVert = new int[VERTEX_BUCKET_COUNT];
        if (firstVert == null) {
            ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'firstVert' " + VERTEX_BUCKET_COUNT);
            return false;
        }
        for (int i = 0; i < VERTEX_BUCKET_COUNT; ++i)
            firstVert[i] = -1;

        //rcScopedDelete<int> indices = (int*)rcAlloc(sizeof(int)*maxVertsPerCont, RC_ALLOC_TEMP);
        int[] indices = new int[maxVertsPerCont];
        if (indices == null) {
            ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'indices' " + maxVertsPerCont);
            return false;
        }
        //rcScopedDelete<int> tris = (int*)rcAlloc(sizeof(int)*maxVertsPerCont*3, RC_ALLOC_TEMP);
        int[] tris = new int[maxVertsPerCont * 3];
        if (tris == null) {
            ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'tris' " + maxVertsPerCont * 3);
            return false;
        }
        //rcScopedDelete<ushort> polys = (ushort*)rcAlloc(sizeof(ushort)*(maxVertsPerCont+1)*nvp, RC_ALLOC_TEMP);
        ushort[] polys = new ushort[(maxVertsPerCont + 1) * nvp];
        if (polys == null) {
            ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'polys' " + (maxVertsPerCont + 1) * nvp);
            return false;
        }
        int tmpPolyIndex = maxVertsPerCont * nvp;
        //ushort[] tmpPoly = &polys[maxVertsPerCont*nvp];

        for (int i = 0; i < cset.nconts; ++i) {
            rcContour cont = cset.conts[i];

            // Skip null contours.
            if (cont.nverts < 3)
                continue;

            // Triangulate contour
            for (int j = 0; j < cont.nverts; ++j)
                indices[j] = j;

            int ntris = triangulate(cont.nverts, cont.verts, indices, tris);
            if (ntris <= 0) {
                // Bad triangulation, should not happen.
                /*			printf("\tconst float bmin[3] = {%ff,%ff,%ff};\n", cset.bmin[0], cset.bmin[1], cset.bmin[2]);
                            printf("\tconst float cs = %ff;\n", cset.cs);
                            printf("\tconst float ch = %ff;\n", cset.ch);
                            printf("\tconst int verts[] = {\n");
                            for (int k = 0; k < cont.nverts; ++k)
                            {
                                const int* v = &cont.verts[k*4];
                                printf("\t\t%d,%d,%d,%d,\n", v[0], v[1], v[2], v[3]);
                            }
                            printf("\t};\n\tconst int nverts = sizeof(verts)/(sizeof(int)*4);\n");*/
                ctx.log(rcLogCategory.RC_LOG_WARNING, "rcBuildPolyMesh: Bad triangulation Contour " + i);
                ntris = -ntris;
            }

            // Add and merge vertices.
            for (int j = 0; j < cont.nverts; ++j) {
                int vIndex = j * 4;
                //const int* v = &cont.verts[j*4];
                indices[j] = addVertex((ushort)cont.verts[vIndex + 0], (ushort)cont.verts[vIndex + 1], (ushort)cont.verts[vIndex + 2],
                                       mesh.verts, firstVert, nextVert, ref  mesh.nverts);
                if ((cont.verts[vIndex + 3] & RC_BORDER_VERTEX) != 0) {
                    // This vertex should be removed.
                    vflags[indices[j]] = 1;
                }
            }

            // Build initial polygons.
            int npolys = 0;
            //memset(polys, 0xff, maxVertsPerCont*nvp*sizeof(ushort));
            for (int j = 0; j < nvp * maxVertsPerCont; ++j) {
                polys[j] = 0xffff;
            }
            for (int j = 0; j < ntris; ++j) {
                int tIndex = j * 3;
                //int* t = &tris[j*3];
                if (tris[tIndex + 0] != tris[tIndex + 1] && tris[tIndex + 0] != tris[tIndex + 2] && tris[tIndex + 1] != tris[tIndex + 2]) {
                    polys[npolys * nvp + 0] = (ushort)indices[tris[tIndex + 0]];
                    polys[npolys * nvp + 1] = (ushort)indices[tris[tIndex + 1]];
                    polys[npolys * nvp + 2] = (ushort)indices[tris[tIndex + 2]];
                    npolys++;
                }
            }
            if (npolys == 0) {
                continue;
            }

            // Merge polygons.
            if (nvp > 3) {
                for (; ; ) {
                    // Find best polygons to merge.
                    int bestMergeVal = 0;
                    int bestPa = 0, bestPb = 0, bestEa = 0, bestEb = 0;

                    for (int j = 0; j < npolys - 1; ++j) {
                        int pjIndex = j * nvp;
                        //ushort* pj = &polys[j*nvp];
                        for (int k = j + 1; k < npolys; ++k) {
                            //ushort* pk = &polys[k*nvp];
                            int pkIndex = k * nvp;
                            int ea = 0, eb = 0;
                            int v = getPolyMergeValue(polys, pjIndex, polys, pkIndex, mesh.verts, ref ea, ref eb, nvp);
                            if (v > bestMergeVal) {
                                bestMergeVal = v;
                                bestPa = j;
                                bestPb = k;
                                bestEa = ea;
                                bestEb = eb;
                            }
                        }
                    }

                    if (bestMergeVal > 0) {
                        // Found best, merge.
                        //ushort* pa = &polys[bestPa*nvp];
                        //ushort* pb = &polys[bestPb*nvp];
                        int paIndex = bestPa * nvp;
                        int pbIndex = bestPb * nvp;
                        mergePolys(polys, paIndex, polys, pbIndex, bestEa, bestEb, polys, tmpPolyIndex, nvp);
                        //ushort* lastPoly = &polys[(npolys-1)*nvp];
                        int lastPolyIndex = (npolys - 1) * nvp;
                        if (pbIndex != lastPolyIndex) {
                            //memcpy(pb, lastPoly, sizeof(ushort)*nvp);
                            for (int j = 0; j < nvp; ++j) {
                                polys[pbIndex + j] = polys[lastPolyIndex + j];
                            }
                        }
                        npolys--;
                    } else {
                        // Could not merge any polygons, stop.
                        break;
                    }
                }
            }

            // Store polygons.
            for (int j = 0; j < npolys; ++j) {
                //ushort* p = &mesh.polys[mesh.npolys*nvp*2];
                //ushort* q = &polys[j*nvp];
                int pIndex = mesh.npolys * nvp * 2;
                int qIndex = j * nvp;
                for (int k = 0; k < nvp; ++k) {
                    mesh.polys[pIndex + k] = polys[qIndex + k];
                }
                mesh.regs[mesh.npolys] = cont.reg;
                mesh.areas[mesh.npolys] = cont.area;
                mesh.npolys++;
                if (mesh.npolys > maxTris) {
                    ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildPolyMesh: Too many polygons " + mesh.npolys + " max " + maxTris);
                    return false;
                }
            }
        }

        // Remove edge vertices.
        for (int i = 0; i < mesh.nverts; ++i) {
            if (vflags[i] != 0) {
                if (!canRemoveVertex(ctx, mesh, (ushort)i)) {
                    continue;
                }
                if (!removeVertex(ctx, mesh, (ushort)i, maxTris)) {
                    // Failed to remove vertex
                    ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildPolyMesh: Failed to remove edge vertex " + i);
                    return false;
                }
                // Remove vertex
                // Note: mesh.nverts is already decremented inside removeVertex()!
                // Fixup vertex flags
                for (int j = i; j < mesh.nverts; ++j)
                    vflags[j] = vflags[j + 1];
                --i;
            }
        }

        // Calculate adjacency.
        if (!buildMeshAdjacency(mesh.polys, mesh.npolys, mesh.nverts, nvp)) {
            ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildPolyMesh: Adjacency failed.");
            return false;
        }

        // Find portal edges
        if (mesh.borderSize > 0) {
            int w = cset.width;
            int h = cset.height;
            for (int i = 0; i < mesh.npolys; ++i) {
                int pIndex = i * 2 * nvp;
                //ushort* p = &mesh.polys[i*2*nvp];
                for (int j = 0; j < nvp; ++j) {
                    if (mesh.polys[pIndex + j] == RC_MESH_NULL_IDX) {
                        break;
                    }
                    // Skip connected edges.
                    if (mesh.polys[pIndex + nvp + j] != RC_MESH_NULL_IDX) {
                        continue;
                    }
                    int nj = j + 1;
                    if (nj >= nvp || mesh.polys[pIndex + nj] == RC_MESH_NULL_IDX) nj = 0;
                    //ushort* va = &mesh.verts[mesh.polys[pIndex + j]*3];
                    //ushort* vb = &mesh.verts[mesh.polys[pIndex + nj]*3];
                    int vaIndex = mesh.polys[pIndex + j] * 3;
                    int vbIndex = mesh.polys[pIndex + nj] * 3;

                    if ((int)mesh.verts[vaIndex + 0] == 0 && (int)mesh.verts[vbIndex + 0] == 0)
                        mesh.polys[pIndex + nvp + j] = 0x8000 | 0;
                    else if ((int)mesh.verts[vaIndex + 2] == h && (int)mesh.verts[vbIndex + 2] == h)
                        mesh.polys[pIndex + nvp + j] = 0x8000 | 1;
                    else if ((int)mesh.verts[vaIndex + 0] == w && (int)mesh.verts[vbIndex + 0] == w)
                        mesh.polys[pIndex + nvp + j] = 0x8000 | 2;
                    else if ((int)mesh.verts[vaIndex + 2] == 0 && (int)mesh.verts[vbIndex + 2] == 0)
                        mesh.polys[pIndex + nvp + j] = 0x8000 | 3;
                }
            }
        }

        // Just allocate the mesh flags array. The user is resposible to fill it.
        //mesh.flags = (ushort*)rcAlloc(sizeof(ushort)*mesh.npolys, RC_ALLOC_PERM);
        mesh.flags = new ushort[mesh.npolys];
        if (mesh.flags == null) {
            ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.flags' " + mesh.npolys);
            return false;
        }
        //memset(mesh.flags, 0, sizeof(ushort) * mesh.npolys);

        if (mesh.nverts > 0xffff) {
            ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildPolyMesh: The resulting mesh has too many vertices " + mesh.nverts + "(max " + 0xffff + ") Data can be corrupted.");
        }
        if (mesh.npolys > 0xffff) {
            ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildPolyMesh: The resulting mesh has too many polygons " + mesh.npolys + " (max " + 0xffff + "). Data can be corrupted.");
        }

        ctx.stopTimer(rcTimerLabel.RC_TIMER_BUILD_POLYMESH);

        return true;
    }
示例#4
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;
    }