Beispiel #1
0
    /// @par
    ///
    /// For this filter, the clearance above the span is the distance from the span's
    /// maximum to the next higher span's minimum. (Same grid column.)
    ///
    /// @see rcHeightfield, rcConfig
    public static void rcFilterWalkableLowHeightSpans(rcContext ctx, int walkableHeight, rcHeightfield solid)
    {
        Debug.Assert(ctx != null, "rcContext is null");

        ctx.startTimer(rcTimerLabel.RC_TIMER_FILTER_WALKABLE);

        int w          = solid.width;
        int h          = solid.height;
        int MAX_HEIGHT = 0xffff;

        // Remove walkable flag from spans which do not have enough
        // space above them for the agent to stand there.
        for (int y = 0; y < h; ++y)
        {
            for (int x = 0; x < w; ++x)
            {
                for (rcSpan s = solid.spans[x + y * w]; s != null; s = s.next)
                {
                    int bot = (int)(s.smax);
                    int top = s.next != null ? (int)(s.next.smin) : MAX_HEIGHT;
                    if ((top - bot) <= walkableHeight)
                    {
                        s.area = RC_NULL_AREA;
                    }
                }
            }
        }

        ctx.stopTimer(rcTimerLabel.RC_TIMER_FILTER_WALKABLE);
    }
Beispiel #2
0
        /// @par
        ///
        /// Basically, any spans that are closer to a boundary or obstruction than the specified radius
        /// are marked as unwalkable.
        ///
        /// This method is usually called immediately after the heightfield has been built.
        ///
        /// @see rcCompactHeightfield, rcBuildCompactHeightfield, rcConfig::walkableRadius
        public static bool rcErodeWalkableArea(rcContext ctx, int radius, rcCompactHeightfield chf)
        {
            Debug.Assert(ctx != null, "rcContext is null");

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

            ctx.startTimer(rcTimerLabel.RC_TIMER_ERODE_AREA);

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

            // Init distance.
            for (int i = 0; i < chf.spanCount; ++i)
            {
                dist[i] = 0xff;
            }
            //	memset(dist, 0xff, sizeof(byte)*chf.spanCount);

            // Mark boundary cells.
            for (int y = 0; y < h; ++y)
            {
                for (int x = 0; x < w; ++x)
                {
                    rcCompactCell c = chf.cells ![x + y * w];
Beispiel #3
0
 /// @par
 ///
 /// The span addition can be set to favor flags. If the span is merged to
 /// another span and the new @p smax is within @p flagMergeThr units
 /// from the existing span, the span flags are merged.
 ///
 /// @see rcHeightfield, rcSpan.
 static void rcAddSpan(rcContext ctx, rcHeightfield hf, int x, int y,
                       ushort smin, ushort smax,
                       byte area, int flagMergeThr)
 {
     //	Debug.Assert(ctx != null, "rcContext is null");
     addSpan(hf, x, y, smin, smax, area, flagMergeThr);
 }
Beispiel #4
0
        public MapBuilder()
        {
            m_maxWalkableAngle = 70.0f;

            m_terrainBuilder = new TerrainBuilder();
            m_rcContext      = new rcContext(false);

            discoverTiles();
        }
Beispiel #5
0
        public MapBuilder(VMapManager2 vm)
        {
            _vmapManager = vm;

            m_maxWalkableAngle = 70.0f;

            m_terrainBuilder = new TerrainBuilder(vm);
            m_rcContext      = new rcContext(false);

            discoverTiles();
        }
Beispiel #6
0
        public MapBuilder(VMapManager2 vm, bool debugMaps)
        {
            _vmapManager = vm;
            _debugMaps   = debugMaps;

            m_maxWalkableAngle = 70.0f;

            m_terrainBuilder = new TerrainBuilder(vm);
            m_rcContext      = new rcContext();

            discoverTiles();
        }
Beispiel #7
0
    /// @par
    /// 
    /// This is usually the second to the last step in creating a fully built
    /// compact heightfield.  This step is required before regions are built
    /// using #rcBuildRegions or #rcBuildRegionsMonotone.
    /// 
    /// After this step, the distance data is available via the rcCompactHeightfield::maxDistance
    /// and rcCompactHeightfield::dist fields.
    ///
    /// @see rcCompactHeightfield, rcBuildRegions, rcBuildRegionsMonotone
    public static bool rcBuildDistanceField(rcContext ctx, rcCompactHeightfield chf)
    {
        Debug.Assert(ctx != null, "rcContext is null");

        ctx.startTimer(rcTimerLabel.RC_TIMER_BUILD_DISTANCEFIELD);

        chf.dist = null;

        //ushort* src = (ushort*)rcAlloc(sizeof(ushort)*chf.spanCount, RC_ALLOC_TEMP);
        ushort[] src = new ushort[chf.spanCount];
        if (src == null)
        {
            ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildDistanceField: Out of memory 'src' ("+chf.spanCount+").");
            return false;
        }
        //ushort* dst = (ushort*)rcAlloc(sizeof(ushort)*chf.spanCount, RC_ALLOC_TEMP);
        ushort[] dst = new ushort[chf.spanCount];
        if (dst == null)
        {
            ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildDistanceField: Out of memory 'dst' ("+chf.spanCount+").");
            //rcFree(src);
            return false;
        }

        ushort maxDist = 0;

        ctx.startTimer(rcTimerLabel.RC_TIMER_BUILD_DISTANCEFIELD_DIST);

        calculateDistanceField(ctx, chf, src, ref maxDist);
        chf.maxDistance = maxDist;

        ctx.stopTimer(rcTimerLabel.RC_TIMER_BUILD_DISTANCEFIELD_DIST);

        ctx.startTimer(rcTimerLabel.RC_TIMER_BUILD_DISTANCEFIELD_BLUR);

        // Blur
        if (boxBlur(chf, 1, src, dst) != src){
            rcSwap(ref src,ref dst);
        }

        // Store distance.
        chf.dist = src;

        ctx.stopTimer(rcTimerLabel.RC_TIMER_BUILD_DISTANCEFIELD_BLUR);

        ctx.stopTimer(rcTimerLabel.RC_TIMER_BUILD_DISTANCEFIELD);

        //rcFree(dst);
        dst = null;

        return true;
    }
Beispiel #8
0
    /// @par
    ///
    /// No spans will be added if the triangle does not overlap the heightfield grid.
    ///
    /// @see rcHeightfield
    public static void rcRasterizeTriangle(rcContext ctx, float[] v0, int v0Start, float[] v1, int v1Start, float[] v2, int v2Start,
						     byte area, rcHeightfield solid,
						     int flagMergeThr)
    {
        Debug.Assert(ctx != null, "rcContext is null");

        ctx.startTimer(rcTimerLabel.RC_TIMER_RASTERIZE_TRIANGLES);

        float ics = 1.0f/solid.cs;
        float ich = 1.0f/solid.ch;
        rasterizeTri(v0, v0Start, v1, v1Start, v2, v2Start, area, solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr);

        ctx.stopTimer(rcTimerLabel.RC_TIMER_RASTERIZE_TRIANGLES);
    }
Beispiel #9
0
    /// @par
    ///
    /// No spans will be added if the triangle does not overlap the heightfield grid.
    ///
    /// @see rcHeightfield
    public static void rcRasterizeTriangle(rcContext ctx, float[] v0, int v0Start, float[] v1, int v1Start, float[] v2, int v2Start,
                                           byte area, rcHeightfield solid,
                                           int flagMergeThr)
    {
        Debug.Assert(ctx != null, "rcContext is null");

        ctx.startTimer(rcTimerLabel.RC_TIMER_RASTERIZE_TRIANGLES);

        float ics = 1.0f / solid.cs;
        float ich = 1.0f / solid.ch;

        rasterizeTri(v0, v0Start, v1, v1Start, v2, v2Start, area, solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr);

        ctx.stopTimer(rcTimerLabel.RC_TIMER_RASTERIZE_TRIANGLES);
    }
Beispiel #10
0
        /// @par
        ///
        /// See the #rcConfig documentation for more information on the configuration parameters.
        ///
        /// @see rcAllocHeightfieldLayerSet, rcCompactHeightfield, rcHeightfieldLayerSet, rcConfig
        public static bool rcBuildHeightfieldLayers(rcContext ctx, rcCompactHeightfield chf,
                                                    int borderSize, int walkableHeight,
                                                    rcHeightfieldLayerSet lset)
        {
            Debug.Assert(ctx != null, "rcContext is null");

            ctx.startTimer(rcTimerLabel.RC_TIMER_BUILD_LAYERS);

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

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

            for (int i = 0; i < chf.spanCount; ++i)
            {
                srcReg[i] = 0xff;
            }

            int nsweeps = chf.width;

            rcLayerSweepSpan[] sweeps = new rcLayerSweepSpan[nsweeps];
            if (sweeps == null)
            {
                ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'sweeps' " + nsweeps);
                return(false);
            }


            // Partition walkable area into monotone regions.
            int[] prevCount = new int[256];
            byte  regId     = 0;

            for (int y = borderSize; y < h - borderSize; ++y)
            {
                //memset to 0 is done by C# alloc
                //memset(prevCount,0,sizeof(int)*regId);

                byte sweepId = 0;

                for (int x = borderSize; x < w - borderSize; ++x)
                {
                    rcCompactCell c = chf.cells ![x + y * w];
Beispiel #11
0
        /// @par
        ///
        /// Allows the formation of walkable regions that will flow over low lying
        /// objects such as curbs, and up structures such as stairways.
        ///
        /// Two neighboring spans are walkable if: <tt>rcAbs(currentSpan.smax - neighborSpan.smax) < waklableClimb</tt>
        ///
        /// @warning Will override the effect of #rcFilterLedgeSpans.  So if both filters are used, call
        /// #rcFilterLedgeSpans after calling this filter.
        ///
        /// @see rcHeightfield, rcConfig
        public static void rcFilterLowHangingWalkableObstacles(rcContext ctx, int walkableClimb, rcHeightfield solid)
        {
            Debug.Assert(ctx != null, "rcContext is null");

            ctx.startTimer(rcTimerLabel.RC_TIMER_FILTER_LOW_OBSTACLES);

            int w = solid.width;
            int h = solid.height;

            for (int y = 0; y < h; ++y)
            {
                for (int x = 0; x < w; ++x)
                {
                    rcSpan?ps = null;
                    bool   previousWalkable = false;
                    byte   previousArea     = RC_NULL_AREA;

                    for (rcSpan?s = solid.spans ![x + y * w]; s != null; ps = s, s = s.next)
Beispiel #12
0
    /// @par
    ///
    /// Spans will only be added for triangles that overlap the heightfield grid.
    ///
    /// @see rcHeightfield
    public static void rcRasterizeTriangles(rcContext ctx, float[] verts, int nv,
						      int[] tris, byte[] areas, int nt,
						      rcHeightfield solid, int flagMergeThr)
    {
        Debug.Assert(ctx != null, "rcContext is null");

        ctx.startTimer(rcTimerLabel.RC_TIMER_RASTERIZE_TRIANGLES);

        float ics = 1.0f/solid.cs;
        float ich = 1.0f/solid.ch;
        // Rasterize triangles.
        for (int i = 0; i < nt; ++i)
        {
            int v0Start = tris[i*3+0]*3;
            int v1Start = tris[i*3+1]*3;
            int v2Start = tris[i*3+2]*3;
            // Rasterize.
            rasterizeTri(verts, v0Start, verts, v1Start, verts, v2Start, areas[i], solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr);
        }

        ctx.stopTimer(rcTimerLabel.RC_TIMER_RASTERIZE_TRIANGLES);
    }
Beispiel #13
0
    /// @par
    ///
    /// Spans will only be added for triangles that overlap the heightfield grid.
    ///
    /// @see rcHeightfield
    public static void rcRasterizeTriangles(rcContext ctx, float[] verts, byte[] areas, int nt,
                                            rcHeightfield solid, int flagMergeThr)
    {
        Debug.Assert(ctx != null, "rcContext is null");

        ctx.startTimer(rcTimerLabel.RC_TIMER_RASTERIZE_TRIANGLES);

        float ics = 1.0f / solid.cs;
        float ich = 1.0f / solid.ch;

        // Rasterize triangles.
        for (int i = 0; i < nt; ++i)
        {
            int v0Start = (i * 3 + 0) * 3;
            int v1Start = (i * 3 + 1) * 3;
            int v2Start = (i * 3 + 2) * 3;
            // Rasterize.
            rasterizeTri(verts, v0Start, verts, v1Start, verts, v2Start, areas[i], solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr);
        }

        ctx.stopTimer(rcTimerLabel.RC_TIMER_RASTERIZE_TRIANGLES);
    }
Beispiel #14
0
    /// @par
    ///
    /// Allows the formation of walkable regions that will flow over low lying
    /// objects such as curbs, and up structures such as stairways.
    ///
    /// Two neighboring spans are walkable if: <tt>rcAbs(currentSpan.smax - neighborSpan.smax) < waklableClimb</tt>
    ///
    /// @warning Will override the effect of #rcFilterLedgeSpans.  So if both filters are used, call
    /// #rcFilterLedgeSpans after calling this filter.
    ///
    /// @see rcHeightfield, rcConfig
    public static void rcFilterLowHangingWalkableObstacles(rcContext ctx, int walkableClimb, rcHeightfield solid)
    {
        Debug.Assert(ctx != null, "rcContext is null");

        ctx.startTimer(rcTimerLabel.RC_TIMER_FILTER_LOW_OBSTACLES);

        int w = solid.width;
        int h = solid.height;

        for (int y = 0; y < h; ++y)
        {
            for (int x = 0; x < w; ++x)
            {
                rcSpan ps = null;
                bool   previousWalkable = false;
                byte   previousArea     = RC_NULL_AREA;

                for (rcSpan s = solid.spans[x + y * w]; s != null; ps = s, s = s.next)
                {
                    bool walkable = s.area != RC_NULL_AREA;
                    // If current span is not walkable, but there is walkable
                    // span just below it, mark the span above it walkable too.
                    if (!walkable && previousWalkable)
                    {
                        if (Math.Abs((int)s.smax - (int)ps.smax) <= walkableClimb)
                        {
                            s.area = previousArea;
                        }
                    }
                    // Copy walkable flag so that it cannot propagate
                    // past multiple non-walkable objects.
                    previousWalkable = walkable;
                    previousArea     = s.area;
                }
            }
        }

        ctx.stopTimer(rcTimerLabel.RC_TIMER_FILTER_LOW_OBSTACLES);
    }
Beispiel #15
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;
    }
 public rcContext Get()
 {
     return dataContext ?? (dataContext =new rcContext());
 }
Beispiel #17
0
    static void delaunayHull(rcContext ctx, int npts, float[] pts,
						     int nhull, int[] hull,
						     List<int> tris, List<int> edges)
    {
        int nfaces = 0;
        int nedges = 0;
        int maxEdges = npts*10;
        //edges.resize(maxEdges*4);
        edges.Capacity = maxEdges * 4;

        for (int i = 0, j = nhull-1; i < nhull; j=i++){
            addEdge(ctx, edges, ref nedges, maxEdges, hull[j],hull[i], EdgeValues.HULL, EdgeValues.UNDEF);
        }

        int currentEdge = 0;
        while (currentEdge < nedges)
        {
            if (edges[currentEdge*4+2] == EdgeValues.UNDEF){
                completeFacet(ctx, pts, npts, edges,ref nedges, maxEdges, ref nfaces, currentEdge);
            }
            if (edges[currentEdge*4+3] == EdgeValues.UNDEF){
                completeFacet(ctx, pts, npts, edges, ref nedges, maxEdges, ref nfaces, currentEdge);
            }
            currentEdge++;
        }

        // Create tris
        //tris.resize(nfaces*4);
        tris.Capacity = nfaces*4;
        tris.Clear();
        for (int i = 0; i < nfaces*4; ++i){
            //tris[i] = -1;
            tris.Add(-1);
        }

        for (int i = 0; i < nedges; ++i)
        {
            //const int* e = &edges[i*4];
            int edgeIndex = i*4;
            if (edges[edgeIndex + 3] >= 0)
            {
                // Left face
                //int* t = &tris[e[3]*4];
                int tIndex = edges[edgeIndex +3]*4;
                if (tris[tIndex + 0] == -1)
                {
                    tris[tIndex + 0] = edges[edgeIndex +0];
                    tris[tIndex + 1] = edges[edgeIndex +1];
                }
                else if (tris[tIndex + 0] == edges[edgeIndex +1])
                    tris[tIndex + 2] = edges[edgeIndex +0];
                else if (tris[tIndex + 1] == edges[edgeIndex +0])
                    tris[tIndex + 2] = edges[edgeIndex +1];
            }
            if (edges[edgeIndex +2] >= 0)
            {
                // Right
                //int* t = &tris[e[2]*4];
                int tIndex = edges[edgeIndex + 2]*4;
                if (tris[tIndex + 0] == -1)
                {
                    tris[tIndex + 0] = edges[edgeIndex + 1];
                    tris[tIndex + 1] = edges[edgeIndex + 0];
                }
                else if (tris[tIndex + 0] == edges[edgeIndex + 0])
                    tris[tIndex + 2] = edges[edgeIndex + 1];
                else if (tris[tIndex + 1] == edges[edgeIndex + 1])
                    tris[tIndex + 2] = edges[edgeIndex + 0];
            }
        }

        for (int i = 0; i < tris.Count/4; ++i)
        {
            //int* t = &tris[i*4];
            int tIndex = i*4;
            if (tris[tIndex + 0] == -1 || tris[tIndex + 1] == -1 || tris[tIndex + 2] == -1)
            {
                ctx.log(rcLogCategory.RC_LOG_WARNING, "delaunayHull: Removing dangling face "+i+" [" + tris[tIndex + 0] + "," + tris[tIndex + 1] + "," + tris[tIndex + 2] + "].");
                tris[tIndex + 0] = tris[tris.Count-4];
                tris[tIndex + 1] = tris[tris.Count-3];
                tris[tIndex + 2] = tris[tris.Count-2];
                tris[tIndex + 3] = tris[tris.Count-1];
                //tris.resize(tris.Count-4);
                //tris.Capacity = tris.Count - 4;
                tris.RemoveRange(tris.Count - 4, 4);
                --i;
            }
        }
    }
Beispiel #18
0
    static void completeFacet(rcContext ctx, float[] pts, int npts, List<int> edges, ref int nedges, int maxEdges, ref int nfaces, int e)
    {
        const float EPS = 1e-5f;

        //int[] edge = &edges[e*4];
        int edgeIndex = e*4;

        // Cache s and t.
        int s,t;
        if (edges[edgeIndex + 2] == EdgeValues.UNDEF)
        {
            s = edges[edgeIndex + 0];
            t = edges[edgeIndex + 1];
        }
        else if (edges[edgeIndex + 3] == EdgeValues.UNDEF)
        {
            s = edges[edgeIndex + 1];
            t = edges[edgeIndex + 0];
        }
        else
        {
            // Edge already completed.
            return;
        }

        // Find best point on left of edge.
        int pt = npts;
        float[] c = new float[] {0,0,0};
        float r = -1.0f;
        for (int u = 0; u < npts; ++u)
        {
            if (u == s || u == t) {
                continue;
            }
            if (vcross2(pts,s*3, pts, t*3, pts, u*3) > EPS)
            {
                if (r < 0)
                {
                    // The circle is not updated yet, do it now.
                    pt = u;
                    circumCircle(pts,s*3, pts,t*3, pts,u*3, c, 0,ref r);
                    continue;
                }
                float d = vdist2(c, 0 , pts, u*3);
                float tol = 0.001f;
                if (d > r*(1+tol))
                {
                    // Outside current circumcircle, skip.
                    continue;
                }
                else if (d < r*(1-tol))
                {
                    // Inside safe circumcircle, update circle.
                    pt = u;
                    circumCircle(pts,s*3, pts,t*3, pts,u*3, c, 0,ref r);
                }
                else
                {
                    // Inside epsilon circum circle, do extra tests to make sure the edge is valid.
                    // s-u and t-u cannot overlap with s-pt nor t-pt if they exists.
                    if (overlapEdges(pts, edges, nedges, s,u))
                        continue;
                    if (overlapEdges(pts, edges, nedges, t,u))
                        continue;
                    // Edge is valid.
                    pt = u;
                    circumCircle(pts,s*3, pts,t*3, pts,u*3, c, 0, ref r);
                }
            }
        }

        // Add new triangle or update edge info if s-t is on hull.
        if (pt < npts)
        {
            // Update face information of edge being completed.
            updateLeftFace(edges,e*4, s, t, nfaces);

            // Add new edge or update face info of old edge.
            e = findEdge(edges, nedges, pt, s);
            if (e == EdgeValues.UNDEF)
                addEdge(ctx, edges, ref nedges, maxEdges, pt, s, nfaces, EdgeValues.UNDEF);
            else
                updateLeftFace(edges,e*4, pt, s, nfaces);

            // Add new edge or update face info of old edge.
            e = findEdge(edges, nedges, t, pt);
            if (e == EdgeValues.UNDEF)
                addEdge(ctx, edges, ref nedges, maxEdges, t, pt, nfaces, EdgeValues.UNDEF);
            else
                updateLeftFace(edges,e*4, t, pt, nfaces);

            nfaces++;
        }
        else
        {
            updateLeftFace(edges,e*4, s, t, EdgeValues.HULL);
        }
    }
Beispiel #19
0
    static int addEdge(rcContext ctx, int[] edges, ref int nedges, int maxEdges, int s, int t, int l, int r)
    {
        if (nedges >= maxEdges)
        {
            ctx.log(rcLogCategory.RC_LOG_ERROR, "addEdge: Too many edges ("+nedges+"/"+maxEdges+").");
            return EdgeValues.UNDEF;
        }

        // Add edge if not already in the triangulation.
        int e = findEdge(edges, nedges, s, t);
        if (e == EdgeValues.UNDEF)
        {
            //int* edge = &edges[nedges*4];
            int edgeIndex = nedges*4;
            edges[edgeIndex + 0] = s;
            edges[edgeIndex + 1] = t;
            edges[edgeIndex + 2] = l;
            edges[edgeIndex + 3] = r;
            return nedges++;
        }
        else
        {
            return EdgeValues.UNDEF;
        }
    }
        /// @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);
        }
Beispiel #21
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;
            }
        }
    }
Beispiel #22
0
    /// @par
    ///
    /// The value of spacial parameters are in world units.
    /// 
    /// @see rcCompactHeightfield, rcMedianFilterWalkableArea
    public static void rcMarkBoxArea(rcContext ctx, float[] bmin, float[] bmax, byte areaId,
                       rcCompactHeightfield chf)
    {
        Debug.Assert(ctx != null, "rcContext is null");

        ctx.startTimer(rcTimerLabel.RC_TIMER_MARK_BOX_AREA);

        int minx = (int)((bmin[0] - chf.bmin[0]) / chf.cs);
        int miny = (int)((bmin[1] - chf.bmin[1]) / chf.ch);
        int minz = (int)((bmin[2] - chf.bmin[2]) / chf.cs);
        int maxx = (int)((bmax[0] - chf.bmin[0]) / chf.cs);
        int maxy = (int)((bmax[1] - chf.bmin[1]) / chf.ch);
        int maxz = (int)((bmax[2] - chf.bmin[2]) / chf.cs);

        if (maxx < 0) return;
        if (minx >= chf.width) return;
        if (maxz < 0) return;
        if (minz >= chf.height) return;

        if (minx < 0) minx = 0;
        if (maxx >= chf.width) maxx = chf.width - 1;
        if (minz < 0) minz = 0;
        if (maxz >= chf.height) maxz = chf.height - 1;

        for (int z = minz; z <= maxz; ++z) {
            for (int x = minx; x <= maxx; ++x) {
                rcCompactCell c = chf.cells[x + z * chf.width];
                for (int i = (int)c.index, ni = (int)(c.index + c.count); i < ni; ++i) {
                    rcCompactSpan s = chf.spans[i];
                    if ((int)s.y >= miny && (int)s.y <= maxy) {
                        if (chf.areas[i] != RC_NULL_AREA)
                            chf.areas[i] = areaId;
                    }
                }
            }
        }

        ctx.stopTimer(rcTimerLabel.RC_TIMER_MARK_BOX_AREA);
    }
Beispiel #23
0
    /// @par
    ///
    /// The span addition can be set to favor flags. If the span is merged to
    /// another span and the new @p smax is within @p flagMergeThr units
    /// from the existing span, the span flags are merged.
    ///
    /// @see rcHeightfield, rcSpan.
    static void rcAddSpan(rcContext ctx, rcHeightfield hf, int x, int y,
			       ushort smin, ushort smax,
			       byte area, int flagMergeThr)
    {
        //	Debug.Assert(ctx != null, "rcContext is null");
        addSpan(hf, x,y, smin, smax, area, flagMergeThr);
    }
Beispiel #24
0
    /// @par
    ///
    /// The value of spacial parameters are in world units.
    ///
    /// @see rcCompactHeightfield, rcMedianFilterWalkableArea
    static public void rcMarkCylinderArea(rcContext ctx, float[] pos,
                                          float r, float h, byte areaId,
                                          rcCompactHeightfield chf)
    {
        Debug.Assert(ctx != null, "rcContext is null");

        ctx.startTimer(rcTimerLabel.RC_TIMER_MARK_CYLINDER_AREA);

        float[] bmin = new float[3];
        float[] bmax = new float[3];
        bmin[0] = pos[0] - r;
        bmin[1] = pos[1];
        bmin[2] = pos[2] - r;
        bmax[0] = pos[0] + r;
        bmax[1] = pos[1] + h;
        bmax[2] = pos[2] + r;
        float r2 = r * r;

        int minx = (int)((bmin[0] - chf.bmin[0]) / chf.cs);
        int miny = (int)((bmin[1] - chf.bmin[1]) / chf.ch);
        int minz = (int)((bmin[2] - chf.bmin[2]) / chf.cs);
        int maxx = (int)((bmax[0] - chf.bmin[0]) / chf.cs);
        int maxy = (int)((bmax[1] - chf.bmin[1]) / chf.ch);
        int maxz = (int)((bmax[2] - chf.bmin[2]) / chf.cs);

        if (maxx < 0)
        {
            return;
        }
        if (minx >= chf.width)
        {
            return;
        }
        if (maxz < 0)
        {
            return;
        }
        if (minz >= chf.height)
        {
            return;
        }

        if (minx < 0)
        {
            minx = 0;
        }
        if (maxx >= chf.width)
        {
            maxx = chf.width - 1;
        }
        if (minz < 0)
        {
            minz = 0;
        }
        if (maxz >= chf.height)
        {
            maxz = chf.height - 1;
        }


        for (int z = minz; z <= maxz; ++z)
        {
            for (int x = minx; x <= maxx; ++x)
            {
                rcCompactCell c = chf.cells[x + z * chf.width];
                for (int i = (int)c.index, ni = (int)(c.index + c.count); i < ni; ++i)
                {
                    rcCompactSpan s = chf.spans[i];

                    if (chf.areas[i] == RC_NULL_AREA)
                    {
                        continue;
                    }

                    if ((int)s.y >= miny && (int)s.y <= maxy)
                    {
                        float sx = chf.bmin[0] + (x + 0.5f) * chf.cs;
                        float sz = chf.bmin[2] + (z + 0.5f) * chf.cs;
                        float dx = sx - pos[0];
                        float dz = sz - pos[2];

                        if (dx * dx + dz * dz < r2)
                        {
                            chf.areas[i] = areaId;
                        }
                    }
                }
            }
        }

        ctx.stopTimer(rcTimerLabel.RC_TIMER_MARK_CYLINDER_AREA);
    }
Beispiel #25
0
    /// @par
    ///
    /// The value of spacial parameters are in world units.
    ///
    /// The y-values of the polygon vertices are ignored. So the polygon is effectively
    /// projected onto the xz-plane at @p hmin, then extruded to @p hmax.
    ///
    /// @see rcCompactHeightfield, rcMedianFilterWalkableArea
    public static void rcMarkConvexPolyArea(rcContext ctx, float[] verts, int nverts,
                                            float hmin, float hmax, byte areaId,
                                            rcCompactHeightfield chf)
    {
        Debug.Assert(ctx != null, "rcContext is null");

        ctx.startTimer(rcTimerLabel.RC_TIMER_MARK_CONVEXPOLY_AREA);

        float[] bmin = new float[3];
        float[] bmax = new float[3];
        rcVcopy(bmin, verts);
        rcVcopy(bmax, verts);
        for (int i = 1; i < nverts; ++i)
        {
            int vStart = i * 3;
            rcVmin(bmin, 0, verts, vStart);
            rcVmax(bmax, 0, verts, vStart);
        }
        bmin[1] = hmin;
        bmax[1] = hmax;

        int minx = (int)((bmin[0] - chf.bmin[0]) / chf.cs);
        int miny = (int)((bmin[1] - chf.bmin[1]) / chf.ch);
        int minz = (int)((bmin[2] - chf.bmin[2]) / chf.cs);
        int maxx = (int)((bmax[0] - chf.bmin[0]) / chf.cs);
        int maxy = (int)((bmax[1] - chf.bmin[1]) / chf.ch);
        int maxz = (int)((bmax[2] - chf.bmin[2]) / chf.cs);

        if (maxx < 0)
        {
            return;
        }
        if (minx >= chf.width)
        {
            return;
        }
        if (maxz < 0)
        {
            return;
        }
        if (minz >= chf.height)
        {
            return;
        }

        if (minx < 0)
        {
            minx = 0;
        }
        if (maxx >= chf.width)
        {
            maxx = chf.width - 1;
        }
        if (minz < 0)
        {
            minz = 0;
        }
        if (maxz >= chf.height)
        {
            maxz = chf.height - 1;
        }


        // TODO: Optimize.
        for (int z = minz; z <= maxz; ++z)
        {
            for (int x = minx; x <= maxx; ++x)
            {
                rcCompactCell c = chf.cells[x + z * chf.width];
                for (int i = (int)c.index, ni = (int)(c.index + c.count); i < ni; ++i)
                {
                    rcCompactSpan s = chf.spans[i];
                    if (chf.areas[i] == RC_NULL_AREA)
                    {
                        continue;
                    }
                    if ((int)s.y >= miny && (int)s.y <= maxy)
                    {
                        float[] p = new float[3];
                        p[0] = chf.bmin[0] + (x + 0.5f) * chf.cs;
                        p[1] = 0;
                        p[2] = chf.bmin[2] + (z + 0.5f) * chf.cs;

                        if (pointInPoly(nverts, verts, p))
                        {
                            chf.areas[i] = areaId;
                        }
                    }
                }
            }
        }

        ctx.stopTimer(rcTimerLabel.RC_TIMER_MARK_CONVEXPOLY_AREA);
    }
Beispiel #26
0
    /// @par
    ///
    /// The value of spacial parameters are in world units.
    ///
    /// @see rcCompactHeightfield, rcMedianFilterWalkableArea
    public static void rcMarkBoxArea(rcContext ctx, float[] bmin, float[] bmax, byte areaId,
                                     rcCompactHeightfield chf)
    {
        Debug.Assert(ctx != null, "rcContext is null");

        ctx.startTimer(rcTimerLabel.RC_TIMER_MARK_BOX_AREA);

        int minx = (int)((bmin[0] - chf.bmin[0]) / chf.cs);
        int miny = (int)((bmin[1] - chf.bmin[1]) / chf.ch);
        int minz = (int)((bmin[2] - chf.bmin[2]) / chf.cs);
        int maxx = (int)((bmax[0] - chf.bmin[0]) / chf.cs);
        int maxy = (int)((bmax[1] - chf.bmin[1]) / chf.ch);
        int maxz = (int)((bmax[2] - chf.bmin[2]) / chf.cs);

        if (maxx < 0)
        {
            return;
        }
        if (minx >= chf.width)
        {
            return;
        }
        if (maxz < 0)
        {
            return;
        }
        if (minz >= chf.height)
        {
            return;
        }

        if (minx < 0)
        {
            minx = 0;
        }
        if (maxx >= chf.width)
        {
            maxx = chf.width - 1;
        }
        if (minz < 0)
        {
            minz = 0;
        }
        if (maxz >= chf.height)
        {
            maxz = chf.height - 1;
        }

        for (int z = minz; z <= maxz; ++z)
        {
            for (int x = minx; x <= maxx; ++x)
            {
                rcCompactCell c = chf.cells[x + z * chf.width];
                for (int i = (int)c.index, ni = (int)(c.index + c.count); i < ni; ++i)
                {
                    rcCompactSpan s = chf.spans[i];
                    if ((int)s.y >= miny && (int)s.y <= maxy)
                    {
                        if (chf.areas[i] != RC_NULL_AREA)
                        {
                            chf.areas[i] = areaId;
                        }
                    }
                }
            }
        }

        ctx.stopTimer(rcTimerLabel.RC_TIMER_MARK_BOX_AREA);
    }
Beispiel #27
0
    /// @par
    ///
    /// This filter is usually applied after applying area id's using functions
    /// such as #rcMarkBoxArea, #rcMarkConvexPolyArea, and #rcMarkCylinderArea.
    ///
    /// @see rcCompactHeightfield
    public static bool rcMedianFilterWalkableArea(rcContext ctx, rcCompactHeightfield chf)
    {
        Debug.Assert(ctx != null, "rcContext is null");

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

        ctx.startTimer(rcTimerLabel.RC_TIMER_MEDIAN_AREA);

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

        // Init distance.
        for (int i = 0; i < chf.spanCount; ++i)
        {
            areas[i] = 0xff;
        }
        //memset(areas, 0xff, sizeof(byte)*chf.spanCount);

        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)
                {
                    rcCompactSpan s = chf.spans[i];
                    if (chf.areas[i] == RC_NULL_AREA)
                    {
                        areas[i] = chf.areas[i];
                        continue;
                    }

                    byte[] nei = new byte[9];
                    for (int j = 0; j < 9; ++j)
                    {
                        nei[j] = chf.areas[i];
                    }

                    for (int dir = 0; dir < 4; ++dir)
                    {
                        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);
                            if (chf.areas[ai] != RC_NULL_AREA)
                            {
                                nei[dir * 2 + 0] = chf.areas[ai];
                            }

                            rcCompactSpan aSpan = chf.spans[ai];
                            int           dir2  = (dir + 1) & 0x3;
                            if (rcGetCon(aSpan, dir2) != RC_NOT_CONNECTED)
                            {
                                int ax2 = ax + rcGetDirOffsetX(dir2);
                                int ay2 = ay + rcGetDirOffsetY(dir2);
                                int ai2 = (int)chf.cells[ax2 + ay2 * w].index + rcGetCon(aSpan, dir2);
                                if (chf.areas[ai2] != RC_NULL_AREA)
                                {
                                    nei[dir * 2 + 1] = chf.areas[ai2];
                                }
                            }
                        }
                    }
                    insertSort(nei, 9);
                    areas[i] = nei[4];
                }
            }
        }

        chf.areas = areas;

        ctx.stopTimer(rcTimerLabel.RC_TIMER_MEDIAN_AREA);

        return(true);
    }
Beispiel #28
0
    static void calculateDistanceField( rcContext ctx, rcCompactHeightfield chf, ushort[] src, ref ushort maxDist)
    {
        int w = chf.width;
        int h = chf.height;

        // Init distance and points.
        for (int i = 0; i < chf.spanCount; ++i)
            src[i] = 0xffff;

        // Mark boundary cells.
        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)
                {
                    rcCompactSpan s = chf.spans[i];
                    byte area = chf.areas[i];

                    int nc = 0;
                    for (int dir = 0; dir < 4; ++dir)
                    {
                        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);
                            if (area == chf.areas[ai])
                                nc++;
                        }
                    }
                    if (nc != 4)
                        src[i] = 0;
                }
            }
        }
        // Pass 1
        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)
                {
                    rcCompactSpan s = chf.spans[i];

                    if (rcGetCon(s, 0) != RC_NOT_CONNECTED)
                    {
                        // (-1,0)
                        int ax = x + rcGetDirOffsetX(0);
                        int ay = y + rcGetDirOffsetY(0);
                        int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 0);
                        rcCompactSpan aSpan = chf.spans[ai];
                        if (src[ai]+2 < src[i]){
                            src[i] = (ushort)(src[ai]+2);
                        }

                        // (-1,-1)
                        if (rcGetCon(aSpan, 3) != RC_NOT_CONNECTED)
                        {
                            int aax = ax + rcGetDirOffsetX(3);
                            int aay = ay + rcGetDirOffsetY(3);
                            int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(aSpan, 3);
                            if (src[aai]+3 < src[i]){
                                src[i] = (ushort)(src[aai]+3);
                            }
                        }
                    }
                    if (rcGetCon(s, 3) != RC_NOT_CONNECTED)
                    {
                        // (0,-1)
                        int ax = x + rcGetDirOffsetX(3);
                        int ay = y + rcGetDirOffsetY(3);
                        int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 3);
                        rcCompactSpan aSpan = chf.spans[ai];
                        if (src[ai]+2 < src[i]){
                            src[i] = (ushort)(src[ai]+2);
                        }

                        // (1,-1)
                        if (rcGetCon(aSpan, 2) != RC_NOT_CONNECTED)
                        {
                            int aax = ax + rcGetDirOffsetX(2);
                            int aay = ay + rcGetDirOffsetY(2);
                            int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(aSpan, 2);
                            if (src[aai]+3 < src[i]){
                                src[i] = (ushort)(src[aai]+3);
                            }
                        }
                    }
                }
            }
        }
        // Pass 2
        for (int y = h-1; y >= 0; --y)
        {
            for (int x = w-1; x >= 0; --x)
            {
                rcCompactCell c = chf.cells[x+y*w];
                for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
                {
                    rcCompactSpan s = chf.spans[i];

                    if (rcGetCon(s, 2) != RC_NOT_CONNECTED)
                    {
                        // (1,0)
                        int ax = x + rcGetDirOffsetX(2);
                        int ay = y + rcGetDirOffsetY(2);
                        int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 2);
                        rcCompactSpan aSpan = chf.spans[ai];
                        if (src[ai]+2 < src[i]){
                            src[i] = (ushort)(src[ai]+2);
                        }

                        // (1,1)
                        if (rcGetCon(aSpan, 1) != RC_NOT_CONNECTED)
                        {
                            int aax = ax + rcGetDirOffsetX(1);
                            int aay = ay + rcGetDirOffsetY(1);
                            int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(aSpan, 1);
                            if (src[aai]+3 < src[i]){
                                src[i] = (ushort)(src[aai]+3);
                            }
                        }
                    }
                    if (rcGetCon(s, 1) != RC_NOT_CONNECTED)
                    {
                        // (0,1)
                        int ax = x + rcGetDirOffsetX(1);
                        int ay = y + rcGetDirOffsetY(1);
                        int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 1);
                        rcCompactSpan aSpan = chf.spans[ai];
                        if (src[ai]+2 < src[i]){
                            src[i] = (ushort)(src[ai]+2);
                        }

                        // (-1,1)
                        if (rcGetCon(aSpan, 0) != RC_NOT_CONNECTED)
                        {
                            int aax = ax + rcGetDirOffsetX(0);
                            int aay = ay + rcGetDirOffsetY(0);
                            int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(aSpan, 0);
                            if (src[aai]+3 < src[i]){
                                src[i] = (ushort)(src[aai]+3);
                            }
                        }
                    }
                }
            }
        }
        maxDist = 0;
        for (int i = 0; i < chf.spanCount; ++i){
            maxDist = Math.Max(src[i], maxDist);
        }
    }
Beispiel #29
0
    /// @par
    ///
    /// Basically, any spans that are closer to a boundary or obstruction than the specified radius
    /// are marked as unwalkable.
    ///
    /// This method is usually called immediately after the heightfield has been built.
    ///
    /// @see rcCompactHeightfield, rcBuildCompactHeightfield, rcConfig::walkableRadius
    public static bool rcErodeWalkableArea(rcContext ctx, int radius, rcCompactHeightfield chf)
    {
        Debug.Assert(ctx != null, "rcContext is null");

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

        ctx.startTimer(rcTimerLabel.RC_TIMER_ERODE_AREA);

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

        // Init distance.
        for (int i = 0; i < chf.spanCount; ++i)
        {
            dist[i] = 0xff;
        }
        //	memset(dist, 0xff, sizeof(byte)*chf.spanCount);

        // Mark boundary cells.
        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 (chf.areas[i] == RC_NULL_AREA)
                    {
                        dist[i] = 0;
                    }
                    else
                    {
                        rcCompactSpan s  = chf.spans[i];
                        int           nc = 0;
                        for (int dir = 0; dir < 4; ++dir)
                        {
                            if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
                            {
                                int nx   = x + rcGetDirOffsetX(dir);
                                int ny   = y + rcGetDirOffsetY(dir);
                                int nidx = (int)chf.cells[nx + ny * w].index + rcGetCon(s, dir);
                                if (chf.areas[nidx] != RC_NULL_AREA)
                                {
                                    nc++;
                                }
                            }
                        }
                        // At least one missing neighbour.
                        if (nc != 4)
                        {
                            dist[i] = 0;
                        }
                    }
                }
            }
        }

        byte nd = 0;

        // Pass 1
        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)
                {
                    rcCompactSpan s = chf.spans[i];

                    if (rcGetCon(s, 0) != RC_NOT_CONNECTED)
                    {
                        // (-1,0)
                        int           ax    = x + rcGetDirOffsetX(0);
                        int           ay    = y + rcGetDirOffsetY(0);
                        int           ai    = (int)chf.cells[ax + ay * w].index + rcGetCon(s, 0);
                        rcCompactSpan aSpan = chf.spans[ai];
                        nd = (byte)Math.Min((int)dist[ai] + 2, 255);
                        if (nd < dist[i])
                        {
                            dist[i] = nd;
                        }

                        // (-1,-1)
                        if (rcGetCon(aSpan, 3) != RC_NOT_CONNECTED)
                        {
                            int aax = ax + rcGetDirOffsetX(3);
                            int aay = ay + rcGetDirOffsetY(3);
                            int aai = (int)chf.cells[aax + aay * w].index + rcGetCon(aSpan, 3);
                            nd = (byte)Math.Min((int)dist[aai] + 3, 255);
                            if (nd < dist[i])
                            {
                                dist[i] = nd;
                            }
                        }
                    }
                    if (rcGetCon(s, 3) != RC_NOT_CONNECTED)
                    {
                        // (0,-1)
                        int           ax    = x + rcGetDirOffsetX(3);
                        int           ay    = y + rcGetDirOffsetY(3);
                        int           ai    = (int)chf.cells[ax + ay * w].index + rcGetCon(s, 3);
                        rcCompactSpan aSpan = chf.spans[ai];
                        nd = (byte)Math.Min((int)dist[ai] + 2, 255);
                        if (nd < dist[i])
                        {
                            dist[i] = nd;
                        }

                        // (1,-1)
                        if (rcGetCon(aSpan, 2) != RC_NOT_CONNECTED)
                        {
                            int aax = ax + rcGetDirOffsetX(2);
                            int aay = ay + rcGetDirOffsetY(2);
                            int aai = (int)chf.cells[aax + aay * w].index + rcGetCon(aSpan, 2);
                            nd = (byte)Math.Min((int)dist[aai] + 3, 255);
                            if (nd < dist[i])
                            {
                                dist[i] = nd;
                            }
                        }
                    }
                }
            }
        }

        // Pass 2
        for (int y = h - 1; y >= 0; --y)
        {
            for (int x = w - 1; x >= 0; --x)
            {
                rcCompactCell c = chf.cells[x + y * w];
                for (int i = (int)c.index, ni = (int)(c.index + c.count); i < ni; ++i)
                {
                    rcCompactSpan s = chf.spans[i];

                    if (rcGetCon(s, 2) != RC_NOT_CONNECTED)
                    {
                        // (1,0)
                        int           ax    = x + rcGetDirOffsetX(2);
                        int           ay    = y + rcGetDirOffsetY(2);
                        int           ai    = (int)chf.cells[ax + ay * w].index + rcGetCon(s, 2);
                        rcCompactSpan aSpan = chf.spans[ai];
                        nd = (byte)Math.Min((int)dist[ai] + 2, 255);
                        if (nd < dist[i])
                        {
                            dist[i] = nd;
                        }

                        // (1,1)
                        if (rcGetCon(aSpan, 1) != RC_NOT_CONNECTED)
                        {
                            int aax = ax + rcGetDirOffsetX(1);
                            int aay = ay + rcGetDirOffsetY(1);
                            int aai = (int)chf.cells[aax + aay * w].index + rcGetCon(aSpan, 1);
                            nd = (byte)Math.Min((int)dist[aai] + 3, 255);
                            if (nd < dist[i])
                            {
                                dist[i] = nd;
                            }
                        }
                    }
                    if (rcGetCon(s, 1) != RC_NOT_CONNECTED)
                    {
                        // (0,1)
                        int           ax    = x + rcGetDirOffsetX(1);
                        int           ay    = y + rcGetDirOffsetY(1);
                        int           ai    = (int)chf.cells[ax + ay * w].index + rcGetCon(s, 1);
                        rcCompactSpan aSpan = chf.spans[ai];
                        nd = (byte)Math.Min((int)dist[ai] + 2, 255);
                        if (nd < dist[i])
                        {
                            dist[i] = nd;
                        }

                        // (-1,1)
                        if (rcGetCon(aSpan, 0) != RC_NOT_CONNECTED)
                        {
                            int aax = ax + rcGetDirOffsetX(0);
                            int aay = ay + rcGetDirOffsetY(0);
                            int aai = (int)chf.cells[aax + aay * w].index + rcGetCon(aSpan, 0);
                            nd = (byte)Math.Min((int)dist[aai] + 3, 255);
                            if (nd < dist[i])
                            {
                                dist[i] = nd;
                            }
                        }
                    }
                }
            }
        }

        byte thr = (byte)(radius * 2);

        for (int i = 0; i < chf.spanCount; ++i)
        {
            if (dist[i] < thr)
            {
                chf.areas[i] = RC_NULL_AREA;
            }
        }

        ctx.stopTimer(rcTimerLabel.RC_TIMER_ERODE_AREA);

        return(true);
    }
Beispiel #30
0
    /// @par
    ///
    /// The value of spacial parameters are in world units.
    /// 
    /// The y-values of the polygon vertices are ignored. So the polygon is effectively 
    /// projected onto the xz-plane at @p hmin, then extruded to @p hmax.
    /// 
    /// @see rcCompactHeightfield, rcMedianFilterWalkableArea
    public static void rcMarkConvexPolyArea(rcContext ctx, float[] verts, int nverts,
                              float hmin, float hmax, byte areaId,
                              rcCompactHeightfield chf)
    {
        Debug.Assert(ctx != null, "rcContext is null");

        ctx.startTimer(rcTimerLabel.RC_TIMER_MARK_CONVEXPOLY_AREA);

        float[] bmin = new float[3];
        float[] bmax = new float[3];
        rcVcopy(bmin, verts);
        rcVcopy(bmax, verts);
        for (int i = 1; i < nverts; ++i) {
            int vStart = i * 3;
            rcVmin(bmin, 0, verts, vStart);
            rcVmax(bmax, 0, verts, vStart);
        }
        bmin[1] = hmin;
        bmax[1] = hmax;

        int minx = (int)((bmin[0] - chf.bmin[0]) / chf.cs);
        int miny = (int)((bmin[1] - chf.bmin[1]) / chf.ch);
        int minz = (int)((bmin[2] - chf.bmin[2]) / chf.cs);
        int maxx = (int)((bmax[0] - chf.bmin[0]) / chf.cs);
        int maxy = (int)((bmax[1] - chf.bmin[1]) / chf.ch);
        int maxz = (int)((bmax[2] - chf.bmin[2]) / chf.cs);

        if (maxx < 0) return;
        if (minx >= chf.width) return;
        if (maxz < 0) return;
        if (minz >= chf.height) return;

        if (minx < 0) minx = 0;
        if (maxx >= chf.width) maxx = chf.width - 1;
        if (minz < 0) minz = 0;
        if (maxz >= chf.height) maxz = chf.height - 1;

        // TODO: Optimize.
        for (int z = minz; z <= maxz; ++z) {
            for (int x = minx; x <= maxx; ++x) {
                rcCompactCell c = chf.cells[x + z * chf.width];
                for (int i = (int)c.index, ni = (int)(c.index + c.count); i < ni; ++i) {
                    rcCompactSpan s = chf.spans[i];
                    if (chf.areas[i] == RC_NULL_AREA)
                        continue;
                    if ((int)s.y >= miny && (int)s.y <= maxy) {
                        float[] p = new float[3];
                        p[0] = chf.bmin[0] + (x + 0.5f) * chf.cs;
                        p[1] = 0;
                        p[2] = chf.bmin[2] + (z + 0.5f) * chf.cs;

                        if (pointInPoly(nverts, verts, p)) {
                            chf.areas[i] = areaId;
                        }
                    }
                }
            }
        }

        ctx.stopTimer(rcTimerLabel.RC_TIMER_MARK_CONVEXPOLY_AREA);
    }
Beispiel #31
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);
    }
Beispiel #32
0
    /// @par 
    /// 
    /// Basically, any spans that are closer to a boundary or obstruction than the specified radius 
    /// are marked as unwalkable.
    ///
    /// This method is usually called immediately after the heightfield has been built.
    ///
    /// @see rcCompactHeightfield, rcBuildCompactHeightfield, rcConfig::walkableRadius
    public static bool rcErodeWalkableArea(rcContext ctx, int radius, rcCompactHeightfield chf)
    {
        Debug.Assert(ctx != null, "rcContext is null");

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

        ctx.startTimer(rcTimerLabel.RC_TIMER_ERODE_AREA);

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

        // Init distance.
        for (int i=0; i < chf.spanCount; ++i) {
            dist[i] = 0xff;
        }
        //	memset(dist, 0xff, sizeof(byte)*chf.spanCount);

        // Mark boundary cells.
        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 (chf.areas[i] == RC_NULL_AREA) {
                        dist[i] = 0;
                    } else {
                        rcCompactSpan s = chf.spans[i];
                        int nc = 0;
                        for (int dir = 0; dir < 4; ++dir) {
                            if (rcGetCon(s, dir) != RC_NOT_CONNECTED) {
                                int nx = x + rcGetDirOffsetX(dir);
                                int ny = y + rcGetDirOffsetY(dir);
                                int nidx = (int)chf.cells[nx + ny * w].index + rcGetCon(s, dir);
                                if (chf.areas[nidx] != RC_NULL_AREA) {
                                    nc++;
                                }
                            }
                        }
                        // At least one missing neighbour.
                        if (nc != 4)
                            dist[i] = 0;
                    }
                }
            }
        }

        byte nd = 0;

        // Pass 1
        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) {
                    rcCompactSpan s = chf.spans[i];

                    if (rcGetCon(s, 0) != RC_NOT_CONNECTED) {
                        // (-1,0)
                        int ax = x + rcGetDirOffsetX(0);
                        int ay = y + rcGetDirOffsetY(0);
                        int ai = (int)chf.cells[ax + ay * w].index + rcGetCon(s, 0);
                        rcCompactSpan aSpan = chf.spans[ai];
                        nd = (byte)Math.Min((int)dist[ai] + 2, 255);
                        if (nd < dist[i])
                            dist[i] = nd;

                        // (-1,-1)
                        if (rcGetCon(aSpan, 3) != RC_NOT_CONNECTED) {
                            int aax = ax + rcGetDirOffsetX(3);
                            int aay = ay + rcGetDirOffsetY(3);
                            int aai = (int)chf.cells[aax + aay * w].index + rcGetCon(aSpan, 3);
                            nd = (byte)Math.Min((int)dist[aai] + 3, 255);
                            if (nd < dist[i])
                                dist[i] = nd;
                        }
                    }
                    if (rcGetCon(s, 3) != RC_NOT_CONNECTED) {
                        // (0,-1)
                        int ax = x + rcGetDirOffsetX(3);
                        int ay = y + rcGetDirOffsetY(3);
                        int ai = (int)chf.cells[ax + ay * w].index + rcGetCon(s, 3);
                        rcCompactSpan aSpan = chf.spans[ai];
                        nd = (byte)Math.Min((int)dist[ai] + 2, 255);
                        if (nd < dist[i])
                            dist[i] = nd;

                        // (1,-1)
                        if (rcGetCon(aSpan, 2) != RC_NOT_CONNECTED) {
                            int aax = ax + rcGetDirOffsetX(2);
                            int aay = ay + rcGetDirOffsetY(2);
                            int aai = (int)chf.cells[aax + aay * w].index + rcGetCon(aSpan, 2);
                            nd = (byte)Math.Min((int)dist[aai] + 3, 255);
                            if (nd < dist[i])
                                dist[i] = nd;
                        }
                    }
                }
            }
        }

        // Pass 2
        for (int y = h - 1; y >= 0; --y) {
            for (int x = w - 1; x >= 0; --x) {
                rcCompactCell c = chf.cells[x + y * w];
                for (int i = (int)c.index, ni = (int)(c.index + c.count); i < ni; ++i) {
                    rcCompactSpan s = chf.spans[i];

                    if (rcGetCon(s, 2) != RC_NOT_CONNECTED) {
                        // (1,0)
                        int ax = x + rcGetDirOffsetX(2);
                        int ay = y + rcGetDirOffsetY(2);
                        int ai = (int)chf.cells[ax + ay * w].index + rcGetCon(s, 2);
                        rcCompactSpan aSpan = chf.spans[ai];
                        nd = (byte)Math.Min((int)dist[ai] + 2, 255);
                        if (nd < dist[i])
                            dist[i] = nd;

                        // (1,1)
                        if (rcGetCon(aSpan, 1) != RC_NOT_CONNECTED) {
                            int aax = ax + rcGetDirOffsetX(1);
                            int aay = ay + rcGetDirOffsetY(1);
                            int aai = (int)chf.cells[aax + aay * w].index + rcGetCon(aSpan, 1);
                            nd = (byte)Math.Min((int)dist[aai] + 3, 255);
                            if (nd < dist[i])
                                dist[i] = nd;
                        }
                    }
                    if (rcGetCon(s, 1) != RC_NOT_CONNECTED) {
                        // (0,1)
                        int ax = x + rcGetDirOffsetX(1);
                        int ay = y + rcGetDirOffsetY(1);
                        int ai = (int)chf.cells[ax + ay * w].index + rcGetCon(s, 1);
                        rcCompactSpan aSpan = chf.spans[ai];
                        nd = (byte)Math.Min((int)dist[ai] + 2, 255);
                        if (nd < dist[i])
                            dist[i] = nd;

                        // (-1,1)
                        if (rcGetCon(aSpan, 0) != RC_NOT_CONNECTED) {
                            int aax = ax + rcGetDirOffsetX(0);
                            int aay = ay + rcGetDirOffsetY(0);
                            int aai = (int)chf.cells[aax + aay * w].index + rcGetCon(aSpan, 0);
                            nd = (byte)Math.Min((int)dist[aai] + 3, 255);
                            if (nd < dist[i])
                                dist[i] = nd;
                        }
                    }
                }
            }
        }

        byte thr = (byte)(radius * 2);
        for (int i = 0; i < chf.spanCount; ++i)
            if (dist[i] < thr)
                chf.areas[i] = RC_NULL_AREA;

        ctx.stopTimer(rcTimerLabel.RC_TIMER_ERODE_AREA);

        return true;
    }
Beispiel #33
0
    /// @see rcAllocPolyMeshDetail, rcPolyMeshDetail
    static bool rcMergePolyMeshDetails(rcContext ctx, rcPolyMeshDetail[] meshes, int nmeshes, ref rcPolyMeshDetail mesh)
    {
        Debug.Assert(ctx != null, "rcContext is null");

        ctx.startTimer(rcTimerLabel.RC_TIMER_MERGE_POLYMESHDETAIL);

        int maxVerts = 0;
        int maxTris = 0;
        int maxMeshes = 0;

        for (int i = 0; i < nmeshes; ++i)
        {
            if (meshes[i] == null) {
                continue;
            }
            maxVerts += meshes[i].nverts;
            maxTris += meshes[i].ntris;
            maxMeshes += meshes[i].nmeshes;
        }

        mesh.nmeshes = 0;
        //mesh.meshes = (uint*)rcAlloc(sizeof(uint)*maxMeshes*4, RC_ALLOC_PERM);
        mesh.meshes = new uint[maxMeshes*4];
        if (mesh.meshes == null)
        {
            ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'pmdtl.meshes' ("+maxMeshes*4+").");
            return false;
        }

        mesh.ntris = 0;
        //mesh.tris = (byte*)rcAlloc(sizeof(byte)*maxTris*4, RC_ALLOC_PERM);
        mesh.tris = new byte[maxTris*4];
        if (mesh.tris == null)
        {
            ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'dmesh.tris' (" + maxTris*4 + ").");
            return false;
        }

        mesh.nverts = 0;
        //mesh.verts = (float*)rcAlloc(sizeof(float)*maxVerts*3, RC_ALLOC_PERM);
        mesh.verts = new float[maxVerts*3];
        if (mesh.verts == null)
        {
            ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'dmesh.verts' ("+maxVerts*3+").");
            return false;
        }

        // Merge datas.
        for (int i = 0; i < nmeshes; ++i)
        {
            rcPolyMeshDetail dm = meshes[i];
            if (dm == null) {
                continue;
            }
            for (int j = 0; j < dm.nmeshes; ++j)
            {
                //uint* dst = &mesh.meshes[mesh.nmeshes*4];
                //uint* src = &dm.meshes[j*4];
                int dstIndex = mesh.nmeshes*4;
                int srcIndex = j*4;
                mesh.meshes[dstIndex + 0] = (uint)mesh.nverts + dm.meshes[srcIndex + 0];
                mesh.meshes[dstIndex + 1] = dm.meshes[srcIndex + 1];
                mesh.meshes[dstIndex + 2] = (uint)mesh.ntris + dm.meshes[srcIndex + 2];
                mesh.meshes[dstIndex + 3] = dm.meshes[srcIndex + 3];
                mesh.nmeshes++;
            }

            for (int k = 0; k < dm.nverts; ++k)
            {
                rcVcopy(mesh.verts,mesh.nverts*3, dm.verts, k*3);
                mesh.nverts++;
            }
            for (int k = 0; k < dm.ntris; ++k)
            {
                mesh.tris[mesh.ntris*4+0] = dm.tris[k*4+0];
                mesh.tris[mesh.ntris*4+1] = dm.tris[k*4+1];
                mesh.tris[mesh.ntris*4+2] = dm.tris[k*4+2];
                mesh.tris[mesh.ntris*4+3] = dm.tris[k*4+3];
                mesh.ntris++;
            }
        }

        ctx.stopTimer(rcTimerLabel.RC_TIMER_MERGE_POLYMESHDETAIL);

        return true;
    }
Beispiel #34
0
    /// @par
    ///
    /// Allows the formation of walkable regions that will flow over low lying 
    /// objects such as curbs, and up structures such as stairways. 
    /// 
    /// Two neighboring spans are walkable if: <tt>rcAbs(currentSpan.smax - neighborSpan.smax) < waklableClimb</tt>
    /// 
    /// @warning Will override the effect of #rcFilterLedgeSpans.  So if both filters are used, call
    /// #rcFilterLedgeSpans after calling this filter. 
    ///
    /// @see rcHeightfield, rcConfig
    public static void rcFilterLowHangingWalkableObstacles(rcContext ctx, int walkableClimb, rcHeightfield solid)
    {
        Debug.Assert(ctx != null, "rcContext is null");

        ctx.startTimer(rcTimerLabel.RC_TIMER_FILTER_LOW_OBSTACLES);

        int w = solid.width;
        int h = solid.height;

        for (int y = 0; y < h; ++y)
        {
            for (int x = 0; x < w; ++x)
            {
                rcSpan ps = null;
                bool previousWalkable = false;
                byte previousArea = RC_NULL_AREA;

                for (rcSpan s = solid.spans[x + y*w]; s != null; ps = s, s = s.next)
                {
                    bool walkable = s.area != RC_NULL_AREA;
                    // If current span is not walkable, but there is walkable
                    // span just below it, mark the span above it walkable too.
                    if (!walkable && previousWalkable)
                    {
                        if (Math.Abs((int)s.smax - (int)ps.smax) <= walkableClimb){
                            s.area = previousArea;
                        }
                    }
                    // Copy walkable flag so that it cannot propagate
                    // past multiple non-walkable objects.
                    previousWalkable = walkable;
                    previousArea = s.area;
                }
            }
        }

        ctx.stopTimer(rcTimerLabel.RC_TIMER_FILTER_LOW_OBSTACLES);
    }
Beispiel #35
0
    static bool buildPolyDetail(rcContext ctx, float[] _in, int nin,
							    float sampleDist, float sampleMaxError,
							    rcCompactHeightfield chf,rcHeightPatch hp,
							    float[] verts, ref int nverts, List<int> tris,
							    List<int> edges, List<int> samples)
    {
        const int MAX_VERTS = 127;
        const int MAX_TRIS = 255;	// Max tris for delaunay is 2n-2-k (n=num verts, k=num hull verts).
        const int MAX_VERTS_PER_EDGE = 32;
        float[] edge = new float[(MAX_VERTS_PER_EDGE+1)*3];
        int[] hull = new int[MAX_VERTS];
        int nhull = 0;

        nverts = 0;

        for (int i = 0; i < nin; ++i){
            rcVcopy(verts,i*3, _in,i*3);
        }
        nverts = nin;

        float cs = chf.cs;
        float ics = 1.0f/cs;

        // Tessellate outlines.
        // This is done in separate pass in order to ensure
        // seamless height values across the ply boundaries.
        if (sampleDist > 0)
        {
            for (int i = 0, j = nin-1; i < nin; j=i++)
            {
                //const float* vj = &in[j*3];
                //const float* vi = &in[i*3];
                int vjStart = j*3;
                int viStart = i*3;
                bool swapped = false;
                // Make sure the segments are always handled in same order
                // using lexological sort or else there will be seams.
                if (Math.Abs(_in[vjStart]-_in[viStart]) < 1e-6f)
                {
                    if (_in[vjStart + 2] > _in[viStart + 2])
                    {
                        rcSwap(ref vjStart,ref viStart);
                        swapped = true;
                    }
                }
                else
                {
                    if (_in[vjStart] > _in[viStart])
                    {
                        rcSwap(ref vjStart,ref viStart);
                        swapped = true;
                    }
                }
                // Create samples along the edge.
                float dx = _in[viStart] - _in[vjStart];//vi[0] - vj[0];
                float dy = _in[viStart+1] - _in[vjStart+1];//vi[1] - vj[1];
                float dz = _in[viStart+2] - _in[vjStart+2];//vi[2] - vj[2];
                float d = (float)Math.Sqrt(dx*dx + dz*dz);
                int nn = 1 + (int)Math.Floor(d/sampleDist);
                if (nn >= MAX_VERTS_PER_EDGE) {
                    nn = MAX_VERTS_PER_EDGE-1;
                }
                if (nverts+nn >= MAX_VERTS){
                    nn = MAX_VERTS-1-nverts;
                }

                for (int k = 0; k <= nn; ++k)
                {
                    float u = (float)k/(float)nn;
                    //float* pos = &edge[k*3];
                    int posStart = k*3;
                    edge[posStart + 0] = _in[vjStart + 0] + dx*u;
                    edge[posStart + 1] = _in[vjStart + 1] + dy*u;
                    edge[posStart + 2] = _in[vjStart + 2] + dz*u;
                    edge[posStart + 1] = getHeight(edge[posStart + 0],edge[posStart + 1],edge[posStart + 2], cs, ics, chf.ch, hp)*chf.ch;
                }
                // Simplify samples.
                int[] idx = new int[MAX_VERTS_PER_EDGE];// {0,nn};
                idx[1] = nn;
                int nidx = 2;
                for (int k = 0; k < nidx-1; )
                {
                    int a = idx[k];
                    int b = idx[k+1];
                    //float* va = &edge[a*3];
                    //float* vb = &edge[b*3];
                    int vaStart = a*3;
                    int vbStart = b*3;
                    // Find maximum deviation along the segment.
                    float maxd = 0;
                    int maxi = -1;
                    for (int m = a+1; m < b; ++m)
                    {
                        int ptStart = m * 3;
                        float dev = distancePtSeg(edge, ptStart, edge, vaStart,edge, vbStart);
                        if (dev > maxd)
                        {
                            maxd = dev;
                            maxi = m;
                        }
                    }
                    // If the max deviation is larger than accepted error,
                    // add new point, else continue to next segment.
                    if (maxi != -1 && maxd > sampleMaxError * sampleMaxError)
                    {
                        for (int m = nidx; m > k; --m)
                            idx[m] = idx[m-1];
                        idx[k+1] = maxi;
                        nidx++;
                    }
                    else
                    {
                        ++k;
                    }
                }

                hull[nhull++] = j;
                // Add new vertices.
                if (swapped)
                {
                    for (int k = nidx-2; k > 0; --k)
                    {
                        //rcVcopy(&verts[nverts*3], &edge[idx[k]*3]);
                        rcVcopy(verts,nverts*3,edge,idx[k]*3);
                        hull[nhull++] = nverts;
                        nverts++;
                    }
                }
                else
                {
                    for (int k = 1; k < nidx-1; ++k)
                    {
                        //rcVcopy(&verts[nverts*3], &edge[idx[k]*3]);
                        rcVcopy(verts,nverts*3,edge,idx[k]*3);
                        hull[nhull++] = nverts;
                        nverts++;
                    }
                }
            }
        }

        // Tessellate the base mesh.
        //edges.resize(0);
        //tris.resize(0);
        edges.Clear();
        tris.Clear();

        delaunayHull(ctx, nverts, verts, nhull, hull, tris, edges);

        if (tris.Count == 0)
        {
            // Could not triangulate the poly, make sure there is some valid data there.
            ctx.log(rcLogCategory.RC_LOG_WARNING, "buildPolyDetail: Could not triangulate polygon, adding default data.");
            for (int i = 2; i < nverts; ++i)
            {
                tris.Add(0);
                tris.Add(i-1);
                tris.Add(i);
                tris.Add(0);
            }
            return true;
        }

        if (sampleDist > 0)
        {
            // Create sample locations in a grid.
            float[] bmin = new float[3];
            float[] bmax = new float[3];
            rcVcopy(bmin, _in);
            rcVcopy(bmax, _in);
            for (int i = 1; i < nin; ++i)
            {
                rcVmin(bmin, 0, _in,i*3);
                rcVmax(bmax, 0, _in,i*3);
            }
            int x0 = (int)Math.Floor(bmin[0]/sampleDist);
            int x1 = (int)Math.Ceiling(bmax[0]/sampleDist);
            int z0 = (int)Math.Floor(bmin[2]/sampleDist);
            int z1 = (int)Math.Ceiling(bmax[2]/sampleDist);
            //samples.resize(0);
            samples.Clear();
            for (int z = z0; z < z1; ++z)
            {
                for (int x = x0; x < x1; ++x)
                {
                    float[] pt = new float[3];
                    pt[0] = x*sampleDist;
                    pt[1] = (bmax[1]+bmin[1])*0.5f;
                    pt[2] = z*sampleDist;
                    // Make sure the samples are not too close to the edges.
                    if (distToPoly(nin,_in,pt) > -sampleDist/2) {
                        continue;
                    }
                    samples.Add(x);
                    samples.Add(getHeight(pt[0], pt[1], pt[2], cs, ics, chf.ch, hp));
                    samples.Add(z);
                    samples.Add(0); // Not added
                }
            }

            // Add the samples starting from the one that has the most
            // error. The procedure stops when all samples are added
            // or when the max error is within treshold.
            int nsamples = samples.Count/4;
            for (int iter = 0; iter < nsamples; ++iter)
            {
                if (nverts >= MAX_VERTS){
                    break;
                }

                // Find sample with most error.
                float[] bestpt = new float[] {0.0f,0.0f,0.0f};
                float bestd = 0;
                int besti = -1;
                for (int i = 0; i < nsamples; ++i)
                {
                    // int* s = &samples[i*4];
                    int sStart = i*4;
                    if (samples[sStart + 3] != 0)
                        continue; // skip added.
                    float[] pt = new float[3];
                    // The sample location is jittered to get rid of some bad triangulations
                    // which are cause by symmetrical data from the grid structure.
                    pt[0] = samples[sStart + 0]*sampleDist + getJitterX(i)*cs*0.1f;
                    pt[1] = samples[sStart + 1]*chf.ch;
                    pt[2] = samples[sStart + 2]*sampleDist + getJitterY(i)*cs*0.1f;
                    float d = distToTriMesh(pt, verts, nverts, tris, tris.Count/4);
                    if (d < 0)
                        continue; // did not hit the mesh.
                    if (d > bestd)
                    {
                        bestd = d;
                        besti = i;
                        rcVcopy(bestpt,pt);
                    }
                }
                // If the max error is within accepted threshold, stop tesselating.
                if (bestd <= sampleMaxError || besti == -1)
                    break;
                // Mark sample as added.
                samples[besti*4+3] = 1;
                // Add the new sample point.
                rcVcopy(verts,nverts*3,bestpt,0);
                nverts++;

                // Create new triangulation.
                // TODO: Incremental add instead of full rebuild.
                //edges.resize(0);
                //tris.resize(0);
                edges.Clear();
                tris.Clear();
                delaunayHull(ctx, nverts, verts, nhull, hull, tris, edges);
            }
        }

        int ntris = tris.Count/4;
        if (ntris > MAX_TRIS)
        {
            //tris.resize(MAX_TRIS*4);
            tris.RemoveRange(MAX_TRIS*4, tris.Count - MAX_TRIS*4);
            ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildPolyMeshDetail: Shrinking triangle count from "+ntris+" to max "+MAX_TRIS+".");
        }

        return true;
    }
Beispiel #36
0
    /// @par
    ///
    /// A ledge is a span with one or more neighbors whose maximum is further away than @p walkableClimb
    /// from the current span's maximum.
    /// This method removes the impact of the overestimation of conservative voxelization 
    /// so the resulting mesh will not have regions hanging in the air over ledges.
    /// 
    /// A span is a ledge if: <tt>rcAbs(currentSpan.smax - neighborSpan.smax) > walkableClimb</tt>
    /// 
    /// @see rcHeightfield, rcConfig
    public static void rcFilterLedgeSpans(rcContext ctx, int walkableHeight, int walkableClimb,
						    rcHeightfield solid)
    {
        Debug.Assert(ctx != null, "rcContext is null");

        ctx.startTimer(rcTimerLabel.RC_TIMER_FILTER_BORDER);

        int w = solid.width;
        int h = solid.height;
        int MAX_HEIGHT = 0xffff;

        // Mark border spans.
        for (int y = 0; y < h; ++y)
        {
            for (int x = 0; x < w; ++x)
            {
                for (rcSpan s = solid.spans[x + y*w]; s != null; s = s.next)
                {
                    // Skip non walkable spans.
                    if (s.area == RC_NULL_AREA){
                        continue;
                    }

                    int bot = (int)(s.smax);
                    int top = s.next != null ? (int)(s.next.smin) : MAX_HEIGHT;

                    // Find neighbours minimum height.
                    int minh = MAX_HEIGHT;

                    // Min and max height of accessible neighbours.
                    int asmin = s.smax;
                    int asmax = s.smax;

                    for (int dir = 0; dir < 4; ++dir)
                    {
                        int dx = x + rcGetDirOffsetX(dir);
                        int dy = y + rcGetDirOffsetY(dir);
                        // Skip neighbours which are out of bounds.
                        if (dx < 0 || dy < 0 || dx >= w || dy >= h)
                        {
                            minh = Math.Min(minh, -walkableClimb - bot);
                            continue;
                        }

                        // From minus infinity to the first span.
                        rcSpan ns = solid.spans[dx + dy*w];
                        int nbot = -walkableClimb;
                        int ntop = ns != null ? (int)ns.smin : MAX_HEIGHT;
                        // Skip neightbour if the gap between the spans is too small.
                        if (Math.Min(top,ntop) - Math.Max(bot,nbot) > walkableHeight)
                            minh = Math.Min(minh, nbot - bot);

                        // Rest of the spans.
                        for (ns = solid.spans[dx + dy*w]; ns != null; ns = ns.next)
                        {
                            nbot = (int)ns.smax;
                            ntop = ns.next != null ? (int)ns.next.smin : MAX_HEIGHT;
                            // Skip neightbour if the gap between the spans is too small.
                            if (Math.Min(top,ntop) - Math.Max(bot,nbot) > walkableHeight)
                            {
                                minh = Math.Min(minh, nbot - bot);

                                // Find min/max accessible neighbour height.
                                if (Math.Abs(nbot - bot) <= walkableClimb)
                                {
                                    if (nbot < asmin) asmin = nbot;
                                    if (nbot > asmax) asmax = nbot;
                                }

                            }
                        }
                    }

                    // The current span is close to a ledge if the drop to any
                    // neighbour span is less than the walkableClimb.
                    if (minh < -walkableClimb){
                        s.area = RC_NULL_AREA;
                    }

                    // If the difference between all neighbours is too large,
                    // we are at steep slope, mark the span as ledge.
                    if ((asmax - asmin) > walkableClimb)
                    {
                        s.area = RC_NULL_AREA;
                    }
                }
            }
        }

        ctx.stopTimer(rcTimerLabel.RC_TIMER_FILTER_BORDER);
    }
Beispiel #37
0
    /// @par
    ///
    /// See the #rcConfig documentation for more information on the configuration parameters.
    ///
    /// @see rcAllocPolyMeshDetail, rcPolyMesh, rcCompactHeightfield, rcPolyMeshDetail, rcConfig
    public static bool rcBuildPolyMeshDetail(rcContext ctx, rcPolyMesh mesh, rcCompactHeightfield chf,
						       float sampleDist, float sampleMaxError,
						       rcPolyMeshDetail dmesh)
    {
        Debug.Assert(ctx != null, "rcContext is null");

        ctx.startTimer(rcTimerLabel.RC_TIMER_BUILD_POLYMESHDETAIL);

        if (mesh.nverts == 0 || mesh.npolys == 0)
            return true;

        int nvp = mesh.nvp;
        float cs = mesh.cs;
        float ch = mesh.ch;
        float[] orig = mesh.bmin;
        int borderSize = mesh.borderSize;

        List<int> edges = new List<int>();
        List<int> tris = new List<int>();
        List<int> stack = new List<int>();
        List<int> samples = new List<int>();
        edges.Capacity = 64;
        tris.Capacity = 512;
        stack.Capacity = 512;
        samples.Capacity = 512;
        float[] verts = new float[256*3];
        rcHeightPatch hp = new rcHeightPatch();
        int nPolyVerts = 0;
        int maxhw = 0, maxhh = 0;

        //rcScopedDelete<int> bounds = (int*)rcAlloc(sizeof(int)*mesh.npolys*4, RC_ALLOC_TEMP);
        int[] bounds = new int[mesh.npolys*4];
        if (bounds == null)
        {
            ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'bounds' ("+ mesh.npolys*4+").");
            return false;
        }
        //rcScopedDelete<float> poly = (float*)rcAlloc(sizeof(float)*nvp*3, RC_ALLOC_TEMP);
        float[] poly = new float[nvp*3];
        if (poly == null)
        {
            ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'poly' ("+nvp*3+").");
            return false;
        }

        // Find max size for a polygon area.
        for (int i = 0; i < mesh.npolys; ++i)
        {
            //ushort* p = &mesh.polys[i*nvp*2];
            int pStart = i*nvp*2;
            //int& xmin = bounds[i*4+0];
            //int& xmax = bounds[i*4+1];
            //int& ymin = bounds[i*4+2];
            //int& ymax = bounds[i*4+3];
            int xmin = i*4+0;
            int xmax = i*4+1;
            int ymin = i*4+2;
            int ymax = i*4+3;
            bounds[xmin] = chf.width;
            bounds[xmax] = 0;
            bounds[ymin] = chf.height;
            bounds[ymax] = 0;
            for (int j = 0; j < nvp; ++j)
            {
                if(mesh.polys[pStart + j] == RC_MESH_NULL_IDX)
                    break;
                //t ushort* v = &mesh.verts[p[j]*3];
                int vIndex = mesh.polys[pStart + j] * 3;
                bounds[xmin] = Math.Min(bounds[xmin], (int)mesh.verts[vIndex + 0]);
                bounds[xmax] = Math.Max(bounds[xmax], (int)mesh.verts[vIndex + 0]);
                bounds[ymin] = Math.Min(bounds[ymin], (int)mesh.verts[vIndex + 2]);
                bounds[ymax] = Math.Max(bounds[ymax], (int)mesh.verts[vIndex + 2]);
                nPolyVerts++;
            }
            bounds[xmin] = Math.Max(0,bounds[xmin]-1);
            bounds[xmax] = Math.Min(chf.width,bounds[xmax]+1);
            bounds[ymin] = Math.Max(0,bounds[ymin]-1);
            bounds[ymax] = Math.Min(chf.height,bounds[ymax]+1);
            if (bounds[xmin] >= bounds[xmax] || bounds[ymin] >= bounds[ymax]) continue;
            maxhw = Math.Max(maxhw, bounds[xmax]-bounds[xmin]);
            maxhh = Math.Max(maxhh, bounds[ymax]-bounds[ymin]);
        }

        //hp.data = (ushort*)rcAlloc(sizeof(ushort)*maxhw*maxhh, RC_ALLOC_TEMP);
        hp.data = new ushort[maxhh*maxhw];
        if (hp.data == null)
        {
            ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'hp.data' ("+maxhw*maxhh+").");
            return false;
        }

        dmesh.nmeshes = mesh.npolys;
        dmesh.nverts = 0;
        dmesh.ntris = 0;
        //dmesh.meshes = (uint*)rcAlloc(sizeof(uint)*dmesh.nmeshes*4, RC_ALLOC_PERM);
        dmesh.meshes = new uint[dmesh.nmeshes*4];
        if (dmesh.meshes == null)
        {
            ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'dmesh.meshes' ("+dmesh.nmeshes*4+").");
            return false;
        }

        int vcap = nPolyVerts+nPolyVerts/2;
        int tcap = vcap*2;

        dmesh.nverts = 0;
        //dmesh.verts = (float*)rcAlloc(sizeof(float)*vcap*3, RC_ALLOC_PERM);
        dmesh.verts = new float[vcap*3];
        if (dmesh.verts == null)
        {
            ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'dmesh.verts' ("+vcap*3+").");
            return false;
        }
        dmesh.ntris = 0;
        //dmesh.tris = (byte*)rcAlloc(sizeof(byte*)*tcap*4, RC_ALLOC_PERM);
        dmesh.tris = new byte[tcap*4];
        if (dmesh.tris == null)
        {
            ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'dmesh.tris' ("+tcap*4+").");
            return false;
        }

        for (int i = 0; i < mesh.npolys; ++i)
        {
            //const ushort* p = &mesh.polys[i*nvp*2];
            int pIndex = i*nvp*2;

            // Store polygon vertices for processing.
            int npoly = 0;
            for (int j = 0; j < nvp; ++j)
            {
                if(mesh.polys[pIndex + j] == RC_MESH_NULL_IDX)
                    break;
                //const ushort* v = &mesh.verts[p[j]*3];
                int vIndex = mesh.polys[pIndex + j] * 3;
                poly[j*3+0] = mesh.verts[vIndex + 0]*cs;
                poly[j*3+1] = mesh.verts[vIndex + 1]*ch;
                poly[j*3+2] = mesh.verts[vIndex + 2]*cs;
                npoly++;
            }

            // Get the height data from the area of the polygon.
            hp.xmin = bounds[i*4+0];
            hp.ymin = bounds[i*4+2];
            hp.width = bounds[i*4+1]-bounds[i*4+0];
            hp.height = bounds[i*4+3]-bounds[i*4+2];
            getHeightData(chf, mesh.polys, pIndex, npoly, mesh.verts, borderSize, hp, stack, mesh.regs[i]);

            // Build detail mesh.
            int nverts = 0;
            if (!buildPolyDetail(ctx, poly, npoly,
                                 sampleDist, sampleMaxError,
                                 chf, hp, verts, ref nverts, tris,
                                 edges, samples))
            {
                return false;
            }

            // Move detail verts to world space.
            for (int j = 0; j < nverts; ++j)
            {
                verts[j*3+0] += orig[0];
                verts[j*3+1] += orig[1] + chf.ch; // Is this offset necessary?
                verts[j*3+2] += orig[2];
            }
            // Offset poly too, will be used to flag checking.
            for (int j = 0; j < npoly; ++j)
            {
                poly[j*3+0] += orig[0];
                poly[j*3+1] += orig[1];
                poly[j*3+2] += orig[2];
            }

            // Store detail submesh.
            int ntris = tris.Count/4;

            dmesh.meshes[i*4+0] = (uint)dmesh.nverts;
            dmesh.meshes[i*4+1] = (uint)nverts;
            dmesh.meshes[i*4+2] = (uint)dmesh.ntris;
            dmesh.meshes[i*4+3] = (uint)ntris;

            // Store vertices, allocate more memory if necessary.
            if (dmesh.nverts+nverts > vcap)
            {
                while (dmesh.nverts+nverts > vcap){
                    vcap += 256;
                }

                //float* newv = (float*)rcAlloc(sizeof(float)*vcap*3, RC_ALLOC_PERM);
                float[] newv = new float[vcap*3];
                if (newv == null)
                {
                    ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'newv' ("+vcap*3+").");
                    return false;
                }
                if (dmesh.nverts != 0){
                    //memcpy(newv, dmesh.verts, sizeof(float)*3*dmesh.nverts);
                    for (int j=0;j<3*dmesh.nverts;++j){
                        newv[j] = dmesh.verts[j];
                    }
                }
                //rcFree(dmesh.verts);
                //dmesh.verts = null;
                dmesh.verts = newv;
            }
            for (int j = 0; j < nverts; ++j)
            {
                dmesh.verts[dmesh.nverts*3+0] = verts[j*3+0];
                dmesh.verts[dmesh.nverts*3+1] = verts[j*3+1];
                dmesh.verts[dmesh.nverts*3+2] = verts[j*3+2];
                dmesh.nverts++;
            }

            // Store triangles, allocate more memory if necessary.
            if (dmesh.ntris+ntris > tcap)
            {
                while (dmesh.ntris+ntris > tcap){
                    tcap += 256;
                }
                //byte* newt = (byte*)rcAlloc(sizeof(byte)*tcap*4, RC_ALLOC_PERM);
                byte[] newt = new byte[tcap*4];
                if (newt == null)
                {
                    ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'newt' ("+tcap*4+").");
                    return false;
                }
                if (dmesh.ntris != 0){
                    //memcpy(newt, dmesh.tris, sizeof(byte)*4*dmesh.ntris);
                    for (int j = 0;j<4*dmesh.ntris;++j){
                        newt[j] = dmesh.tris[j];
                    }
                }
                //rcFree(dmesh.tris);
                dmesh.tris = newt;
            }
            for (int j = 0; j < ntris; ++j)
            {
                //const int* t = &tris[j*4];
                int tIndex = j*4;
                dmesh.tris[dmesh.ntris*4+0] = (byte)tris[tIndex + 0];
                dmesh.tris[dmesh.ntris*4+1] = (byte)tris[tIndex + 1];
                dmesh.tris[dmesh.ntris*4+2] = (byte)tris[tIndex + 2];
                dmesh.tris[dmesh.ntris*4+3] = getTriFlags(verts, tris[tIndex + 0]*3, verts, tris[tIndex + 1]*3, verts, tris[tIndex + 2]*3, poly, 0, npoly);
                dmesh.ntris++;
            }
        }

        ctx.stopTimer(rcTimerLabel.RC_TIMER_BUILD_POLYMESHDETAIL);

        return true;
    }
Beispiel #38
0
    /// @par
    /// 
    /// Non-null regions will consist of connected, non-overlapping walkable spans that form a single contour.
    /// Contours will form simple polygons.
    /// 
    /// If multiple regions form an area that is smaller than @p minRegionArea, then all spans will be
    /// re-assigned to the zero (null) region.
    /// 
    /// Partitioning can result in smaller than necessary regions. @p mergeRegionArea helps 
    /// reduce unecessarily small regions.
    /// 
    /// See the #rcConfig documentation for more information on the configuration parameters.
    /// 
    /// The region data will be available via the rcCompactHeightfield::maxRegions
    /// and rcCompactSpan::reg fields.
    /// 
    /// @warning The distance field must be created using #rcBuildDistanceField before attempting to build regions.
    /// 
    /// @see rcCompactHeightfield, rcCompactSpan, rcBuildDistanceField, rcBuildRegionsMonotone, rcConfig
    public static bool rcBuildRegionsMonotone(rcContext ctx, rcCompactHeightfield chf,
							    int borderSize, int minRegionArea, int mergeRegionArea)
    {
        Debug.Assert(ctx != null, "rcContext is null");

        ctx.startTimer(rcTimerLabel.RC_TIMER_BUILD_REGIONS);

        int w = chf.width;
        int h = chf.height;
        ushort id = 1;

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

        int nsweeps = Math.Max(chf.width,chf.height);
        rcSweepSpan[] sweeps = new rcSweepSpan[nsweeps];
        rccsArrayItemsCreate(sweeps);
        if (sweeps == null)
        {
            ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildRegionsMonotone: Out of memory 'sweeps' ("+nsweeps+").");
            return false;
        }

        // Mark border regions.
        if (borderSize > 0)
        {
            // Make sure border will not overflow.
            int bw = Math.Min(w, borderSize);
            int bh = Math.Min(h, borderSize);
            // Paint regions
            paintRectRegion(0, bw, 0, h, (ushort)(id|RC_BORDER_REG), chf, srcReg); id++;
            paintRectRegion(w-bw, w, 0, h, (ushort)(id|RC_BORDER_REG), chf, srcReg); id++;
            paintRectRegion(0, w, 0, bh, (ushort)(id|RC_BORDER_REG), chf, srcReg); id++;
            paintRectRegion(0, w, h-bh, h, (ushort)(id|RC_BORDER_REG), chf, srcReg); id++;

            chf.borderSize = borderSize;
        }

        List<int> prev = new List<int>();//256
        prev.Capacity = 256;
        // Sweep one line at a time.
        for (int y = borderSize; y < h-borderSize; ++y)
        {
            // Collect spans from this row.
            rccsResizeList(prev, id+1);
            for (int i=0;i<id;++i){
                prev[i] = 0;
            }
            ushort rid = 1;

            for (int x = borderSize; x < w-borderSize; ++x)
            {
                rcCompactCell c = chf.cells[x+y*w];

                for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
                {
                    rcCompactSpan s = chf.spans[i];
                    if (chf.areas[i] == RC_NULL_AREA) continue;

                    // -x
                    ushort previd = 0;
                    if (rcGetCon(s, 0) != RC_NOT_CONNECTED)
                    {
                        int ax = x + rcGetDirOffsetX(0);
                        int ay = y + rcGetDirOffsetY(0);
                        int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 0);
                        if ((srcReg[ai] & RC_BORDER_REG) == 0 && chf.areas[i] == chf.areas[ai])
                            previd = srcReg[ai];
                    }

                    if (previd == 0)
                    {
                        previd = rid++;
                        sweeps[previd].rid = previd;
                        sweeps[previd].ns = 0;
                        sweeps[previd].nei = 0;
                    }

                    // -y
                    if (rcGetCon(s,3) != RC_NOT_CONNECTED)
                    {
                        int ax = x + rcGetDirOffsetX(3);
                        int ay = y + rcGetDirOffsetY(3);
                        int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 3);
                        if (srcReg[ai] != 0 && (srcReg[ai] & RC_BORDER_REG) == 0 && chf.areas[i] == chf.areas[ai])
                        {
                            ushort nr = srcReg[ai];
                            if (sweeps[previd].nei == 0 || sweeps[previd].nei == nr)
                            {
                                sweeps[previd].nei = nr;
                                sweeps[previd].ns++;
                                prev[nr]++;
                            }
                            else
                            {
                                sweeps[previd].nei = RC_NULL_NEI;
                            }
                        }
                    }

                    srcReg[i] = previd;
                }
            }

            // Create unique ID.
            for (int i = 1; i < rid; ++i)
            {
                if (sweeps[i].nei != RC_NULL_NEI && sweeps[i].nei != 0 &&
                    prev[sweeps[i].nei] == (int)sweeps[i].ns)
                {
                    sweeps[i].id = sweeps[i].nei;
                }
                else
                {
                    sweeps[i].id = id++;
                }
            }

            // Remap IDs
            for (int x = borderSize; x < w-borderSize; ++x)
            {
                rcCompactCell c = chf.cells[x+y*w];

                for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
                {
                    if (srcReg[i] > 0 && srcReg[i] < rid)
                        srcReg[i] = sweeps[srcReg[i]].id;
                }
            }
        }

        ctx.startTimer(rcTimerLabel.RC_TIMER_BUILD_REGIONS_FILTER);

        // Filter out small regions.
        chf.maxRegions = id;
        if (!filterSmallRegions(ctx, minRegionArea, mergeRegionArea, ref chf.maxRegions, chf, srcReg))
            return false;

        ctx.stopTimer(rcTimerLabel.RC_TIMER_BUILD_REGIONS_FILTER);

        // Store the result out.
        for (int i = 0; i < chf.spanCount; ++i)
            chf.spans[i].reg = srcReg[i];

        ctx.stopTimer(rcTimerLabel.RC_TIMER_BUILD_REGIONS);

        return true;
    }
Beispiel #39
0
    public static bool canRemoveVertex(rcContext ctx, rcPolyMesh mesh, ushort rem)
    {
        int nvp = mesh.nvp;

        // Count number of polygons to remove.
        int numRemovedVerts = 0;
        int numTouchedVerts = 0;
        int numRemainingEdges = 0;
        for (int i = 0; i < mesh.npolys; ++i) {
            //ushort* p = &mesh.polys[i*nvp*2];
            int pIndex = i * nvp * 2;
            int nv = countPolyVerts(mesh.polys, i * nvp * 2, nvp);
            int numRemoved = 0;
            int numVerts = 0;
            for (int j = 0; j < nv; ++j) {
                if (mesh.polys[pIndex + j] == rem) {
                    numTouchedVerts++;
                    numRemoved++;
                }
                numVerts++;
            }
            if (numRemoved != 0) {
                numRemovedVerts += numRemoved;
                numRemainingEdges += numVerts - (numRemoved + 1);
            }
        }

        // There would be too few edges remaining to create a polygon.
        // This can happen for example when a tip of a triangle is marked
        // as deletion, but there are no other polys that share the vertex.
        // In this case, the vertex should not be removed.
        if (numRemainingEdges <= 2)
            return false;

        // Find edges which share the removed vertex.
        int maxEdges = numTouchedVerts * 2;
        int nedges = 0;
        //rcScopedDelete<int> edges = (int*)rcAlloc(sizeof(int)*maxEdges*3, RC_ALLOC_TEMP);
        int[] edges = new int[maxEdges * 3];
        if (edges == null) {
            ctx.log(rcLogCategory.RC_LOG_WARNING, "canRemoveVertex: Out of memory 'edges' " + maxEdges * 3);
            return false;
        }

        for (int i = 0; i < mesh.npolys; ++i) {
            //ushort* p = &mesh.polys[i*nvp*2];
            int pIndex = i * nvp * 2;
            int nv = countPolyVerts(mesh.polys, pIndex, nvp);

            // Collect edges which touches the removed vertex.
            for (int j = 0, k = nv - 1; j < nv; k = j++) {
                if (mesh.polys[pIndex + j] == rem || mesh.polys[pIndex + k] == rem) {
                    // Arrange edge so that a=rem.
                    int a = mesh.polys[pIndex + j], b = mesh.polys[pIndex + k];
                    if (b == rem) {
                        rcSwap(ref a, ref b);
                    }

                    // Check if the edge exists
                    bool exists = false;
                    for (int m = 0; m < nedges; ++m) {
                        //int* e = &edges[m*3];
                        int eIndex = m * 3;
                        if (edges[eIndex + 1] == b) {
                            // Exists, increment vertex share count.
                            edges[eIndex + 2]++;
                            exists = true;
                        }
                    }
                    // Add new edge.
                    if (!exists) {
                        //int* e = &edges[nedges*3];
                        int eIndex = nedges * 3;
                        edges[eIndex + 0] = a;
                        edges[eIndex + 1] = b;
                        edges[eIndex + 2] = 1;
                        nedges++;
                    }
                }
            }
        }

        // There should be no more than 2 open edges.
        // This catches the case that two non-adjacent polygons
        // share the removed vertex. In that case, do not remove the vertex.
        int numOpenEdges = 0;
        for (int i = 0; i < nedges; ++i) {
            if (edges[i * 3 + 2] < 2)
                numOpenEdges++;
        }
        if (numOpenEdges > 2)
            return false;

        return true;
    }
Beispiel #40
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;
    }
Beispiel #41
0
    /// @par
    ///
    /// A ledge is a span with one or more neighbors whose maximum is further away than @p walkableClimb
    /// from the current span's maximum.
    /// This method removes the impact of the overestimation of conservative voxelization
    /// so the resulting mesh will not have regions hanging in the air over ledges.
    ///
    /// A span is a ledge if: <tt>rcAbs(currentSpan.smax - neighborSpan.smax) > walkableClimb</tt>
    ///
    /// @see rcHeightfield, rcConfig
    public static void rcFilterLedgeSpans(rcContext ctx, int walkableHeight, int walkableClimb,
                                          rcHeightfield solid)
    {
        Debug.Assert(ctx != null, "rcContext is null");

        ctx.startTimer(rcTimerLabel.RC_TIMER_FILTER_BORDER);

        int w          = solid.width;
        int h          = solid.height;
        int MAX_HEIGHT = 0xffff;

        // Mark border spans.
        for (int y = 0; y < h; ++y)
        {
            for (int x = 0; x < w; ++x)
            {
                for (rcSpan s = solid.spans[x + y * w]; s != null; s = s.next)
                {
                    // Skip non walkable spans.
                    if (s.area == RC_NULL_AREA)
                    {
                        continue;
                    }

                    int bot = (int)(s.smax);
                    int top = s.next != null ? (int)(s.next.smin) : MAX_HEIGHT;

                    // Find neighbours minimum height.
                    int minh = MAX_HEIGHT;

                    // Min and max height of accessible neighbours.
                    int asmin = s.smax;
                    int asmax = s.smax;

                    for (int dir = 0; dir < 4; ++dir)
                    {
                        int dx = x + rcGetDirOffsetX(dir);
                        int dy = y + rcGetDirOffsetY(dir);
                        // Skip neighbours which are out of bounds.
                        if (dx < 0 || dy < 0 || dx >= w || dy >= h)
                        {
                            minh = Math.Min(minh, -walkableClimb - bot);
                            continue;
                        }

                        // From minus infinity to the first span.
                        rcSpan ns   = solid.spans[dx + dy * w];
                        int    nbot = -walkableClimb;
                        int    ntop = ns != null ? (int)ns.smin : MAX_HEIGHT;
                        // Skip neightbour if the gap between the spans is too small.
                        if (Math.Min(top, ntop) - Math.Max(bot, nbot) > walkableHeight)
                        {
                            minh = Math.Min(minh, nbot - bot);
                        }

                        // Rest of the spans.
                        for (ns = solid.spans[dx + dy * w]; ns != null; ns = ns.next)
                        {
                            nbot = (int)ns.smax;
                            ntop = ns.next != null ? (int)ns.next.smin : MAX_HEIGHT;
                            // Skip neightbour if the gap between the spans is too small.
                            if (Math.Min(top, ntop) - Math.Max(bot, nbot) > walkableHeight)
                            {
                                minh = Math.Min(minh, nbot - bot);

                                // Find min/max accessible neighbour height.
                                if (Math.Abs(nbot - bot) <= walkableClimb)
                                {
                                    if (nbot < asmin)
                                    {
                                        asmin = nbot;
                                    }
                                    if (nbot > asmax)
                                    {
                                        asmax = nbot;
                                    }
                                }
                            }
                        }
                    }

                    // The current span is close to a ledge if the drop to any
                    // neighbour span is less than the walkableClimb.
                    if (minh < -walkableClimb)
                    {
                        s.area = RC_NULL_AREA;
                    }

                    // If the difference between all neighbours is too large,
                    // we are at steep slope, mark the span as ledge.
                    if ((asmax - asmin) > walkableClimb)
                    {
                        s.area = RC_NULL_AREA;
                    }
                }
            }
        }

        ctx.stopTimer(rcTimerLabel.RC_TIMER_FILTER_BORDER);
    }
Beispiel #42
0
    public static bool rcCopyPolyMesh(rcContext ctx, rcPolyMesh src, rcPolyMesh dst)
    {
        Debug.Assert(ctx != null, "rcContext is null");

        // Destination must be empty.
        Debug.Assert(dst.verts == null);
        Debug.Assert(dst.polys == null);
        Debug.Assert(dst.regs == null);
        Debug.Assert(dst.areas == null);
        Debug.Assert(dst.flags == null);

        dst.nverts = src.nverts;
        dst.npolys = src.npolys;
        dst.maxpolys = src.npolys;
        dst.nvp = src.nvp;
        rcVcopy(dst.bmin, src.bmin);
        rcVcopy(dst.bmax, src.bmax);
        dst.cs = src.cs;
        dst.ch = src.ch;
        dst.borderSize = src.borderSize;

        //dst.verts = (ushort*)rcAlloc(sizeof(ushort)*src.nverts*3, RC_ALLOC_PERM);
        dst.verts = new ushort[src.nverts * 3];
        if (dst.verts == null) {
            ctx.log(rcLogCategory.RC_LOG_ERROR, "rcCopyPolyMesh: Out of memory 'dst.verts' (" + src.nverts * 3 + ").");
            return false;
        }
        //memcpy(dst.verts, src.verts, sizeof(ushort)*src.nverts*3);
        for (int i = 0; i < src.nverts * 3; ++i) {
            dst.verts[i] = src.verts[i];
        }

        //dst.polys = (ushort*)rcAlloc(sizeof(ushort)*src.npolys*2*src.nvp, RC_ALLOC_PERM);
        dst.polys = new ushort[src.npolys * 2 * src.nvp];
        if (dst.polys == null) {
            ctx.log(rcLogCategory.RC_LOG_ERROR, "rcCopyPolyMesh: Out of memory 'dst.polys' (" + src.npolys * 2 * src.nvp + ").");
            return false;
        }
        //memcpy(dst.polys, src.polys, sizeof(ushort)*src.npolys*2*src.nvp);
        for (int i = 0; i < src.npolys * 2 * src.nvp; ++i) {
            dst.polys[i] = src.polys[i];
        }

        //dst.regs = (ushort*)rcAlloc(sizeof(ushort)*src.npolys, RC_ALLOC_PERM);
        dst.regs = new ushort[src.npolys];
        if (dst.regs == null) {
            ctx.log(rcLogCategory.RC_LOG_ERROR, "rcCopyPolyMesh: Out of memory 'dst.regs' (" + src.npolys + ").");
            return false;
        }
        //memcpy(dst.regs, src.regs, sizeof(ushort)*src.npolys);
        for (int i = 0; i < src.npolys; ++i) {
            dst.regs[i] = src.regs[i];
        }

        //dst.areas = (byte*)rcAlloc(sizeof(byte)*src.npolys, RC_ALLOC_PERM);
        dst.areas = new byte[src.npolys];
        if (dst.areas == null) {
            ctx.log(rcLogCategory.RC_LOG_ERROR, "rcCopyPolyMesh: Out of memory 'dst.areas' (" + src.npolys + ").");
            return false;
        }
        //memcpy(dst.areas, src.areas, sizeof(byte)*src.npolys);
        for (int i = 0; i < src.npolys; ++i) {
            dst.areas[i] = src.areas[i];
        }

        //dst.flags = (ushort*)rcAlloc(sizeof(ushort)*src.npolys, RC_ALLOC_PERM);
        dst.flags = new ushort[src.npolys];
        if (dst.flags != null) {
            ctx.log(rcLogCategory.RC_LOG_ERROR, "rcCopyPolyMesh: Out of memory 'dst.flags' (" + src.npolys + ").");
            return false;
        }
        //memcpy(dst.flags, src.flags, sizeof(byte)*src.npolys);
        for (int i = 0; i < src.npolys; ++i) {
            dst.flags[i] = src.flags[i];
        }

        return true;
    }
Beispiel #43
0
    static bool filterSmallRegions(rcContext ctx, int minRegionArea, int mergeRegionSize,
							       ref ushort maxRegionId,
							       rcCompactHeightfield chf,
							       ushort[] srcReg)
    {
        int w = chf.width;
        int h = chf.height;

        int nreg = maxRegionId+1;

        rcRegion[] regions = new rcRegion[nreg];
        if (regions == null)
        {
            ctx.log(rcLogCategory.RC_LOG_ERROR, "filterSmallRegions: Out of memory 'regions' (" +nreg+ ").");
            return false;
        }

        // Construct regions
        for (int i = 0; i < nreg; ++i){
            regions[i] = new rcRegion((ushort) i);
        }

        // Find edge of a region and find connections around the contour.
        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)
                {
                    ushort r = srcReg[i];
                    if (r == 0 || r >= nreg)
                        continue;

                    rcRegion reg = regions[r];
                    reg.spanCount++;

                    // Update floors.
                    for (int j = (int)c.index; j < ni; ++j)
                    {
                        if (i == j) continue;
                        ushort floorId = srcReg[j];
                        if (floorId == 0 || floorId >= nreg)
                            continue;
                        addUniqueFloorRegion(reg, floorId);
                    }

                    // Have found contour
                    if (reg.connections.Count > 0)
                        continue;

                    reg.areaType = chf.areas[i];

                    // Check if this cell is next to a border.
                    int ndir = -1;
                    for (int dir = 0; dir < 4; ++dir)
                    {
                        if (isSolidEdge(chf, srcReg, x, y, i, dir))
                        {
                            ndir = dir;
                            break;
                        }
                    }

                    if (ndir != -1)
                    {
                        // The cell is at border.
                        // Walk around the contour to find all the neighbours.
                        walkContour(x, y, i, ndir, chf, srcReg, reg.connections);
                    }
                }
            }
        }

        // Remove too small regions.
        List<int> stack = new List<int>();//(32);
        List<int> trace= new List<int>();//(32);
        stack.Capacity = 32;
        trace.Capacity = 32;
        for (int i = 0; i < nreg; ++i)
        {
            rcRegion reg = regions[i];
            if (reg.id == 0 || (reg.id & RC_BORDER_REG) != 0)
                continue;
            if (reg.spanCount == 0)
                continue;
            if (reg.visited)
                continue;

            // Count the total size of all the connected regions.
            // Also keep track of the regions connects to a tile border.
            bool connectsToBorder = false;
            int spanCount = 0;
            stack.Clear();
            trace.Clear();

            reg.visited = true;
            stack.Add(i);

            while (stack.Count != 0)
            {
                // Pop
                int ri = rccsPop(stack);

                rcRegion creg = regions[ri];

                spanCount += creg.spanCount;
                trace.Add(ri);

                for (int j = 0; j < creg.connections.Count; ++j)
                {
                    if ((creg.connections[j] & RC_BORDER_REG) != 0)
                    {
                        connectsToBorder = true;
                        continue;
                    }
                    rcRegion neireg = regions[creg.connections[j]];
                    if (neireg.visited)
                        continue;
                    if (neireg.id == 0 || (neireg.id & RC_BORDER_REG) != 0)
                        continue;
                    // Visit
                    stack.Add(neireg.id);
                    neireg.visited = true;
                }
            }

            // If the accumulated regions size is too small, remove it.
            // Do not remove areas which connect to tile borders
            // as their size cannot be estimated correctly and removing them
            // can potentially remove necessary areas.
            if (spanCount < minRegionArea && !connectsToBorder)
            {
                // Kill all visited regions.
                for (int j = 0; j < trace.Count; ++j)
                {
                    regions[trace[j]].spanCount = 0;
                    regions[trace[j]].id = 0;
                }
            }
        }

        // Merge too small regions to neighbour regions.
        int mergeCount = 0 ;
        do
        {
            mergeCount = 0;
            for (int i = 0; i < nreg; ++i)
            {
                rcRegion reg = regions[i];
                if (reg.id == 0 || (reg.id & RC_BORDER_REG) != 0)
                    continue;
                if (reg.spanCount == 0)
                    continue;

                // Check to see if the region should be merged.
                if (reg.spanCount > mergeRegionSize && isRegionConnectedToBorder(reg))
                    continue;

                // Small region with more than 1 connection.
                // Or region which is not connected to a border at all.
                // Find smallest neighbour region that connects to this one.
                int smallest = 0xfffffff;
                ushort mergeId = reg.id;
                for (int j = 0; j < reg.connections.Count; ++j)
                {
                    if ((reg.connections[j] & RC_BORDER_REG) != 0)
                        continue;
                    rcRegion mreg = regions[reg.connections[j]];
                    if (mreg.id == 0 || (mreg.id & RC_BORDER_REG) != 0)
                        continue;
                    if (mreg.spanCount < smallest &&
                        canMergeWithRegion(reg, mreg) &&
                        canMergeWithRegion(mreg, reg))
                    {
                        smallest = mreg.spanCount;
                        mergeId = mreg.id;
                    }
                }
                // Found new id.
                if (mergeId != reg.id)
                {
                    ushort oldId = reg.id;
                    rcRegion target = regions[mergeId];

                    // Merge neighbours.
                    if (   mergeRegions(target, reg))
                    {
                        // Fixup regions pointing to current region.
                        for (int j = 0; j < nreg; ++j)
                        {

                            if (regions[j].id == 0 || (regions[j].id & RC_BORDER_REG) != 0)
                                continue;
                            // If another region was already merged into current region
                            // change the nid of the previous region too.
                            if (regions[j].id == oldId)
                                regions[j].id = mergeId;
                            // Replace the current region with the new one if the
                            // current regions is neighbour.
                            replaceNeighbour(regions[j], oldId, mergeId);
                        }
                        mergeCount++;
                    }

                }
            }
        }
        while (mergeCount > 0);

        // Compress region Ids.
        for (int i = 0; i < nreg; ++i)
        {
            regions[i].remap = false;
            if (regions[i].id == 0)
                continue;       // Skip nil regions.
            if ((regions[i].id & RC_BORDER_REG) != 0)
                continue;    // Skip external regions.
            regions[i].remap = true;
        }

        ushort regIdGen = 0;
        for (int i = 0; i < nreg; ++i)
        {
            if (!regions[i].remap)
                continue;
            ushort oldId = regions[i].id;
            ushort newId = ++regIdGen;
            for (int j = i; j < nreg; ++j)
            {
                if (regions[j].id == oldId)
                {
                    regions[j].id = newId;
                    regions[j].remap = false;
                }
            }
        }
        maxRegionId = regIdGen;

        // Remap regions.
        for (int i = 0; i < chf.spanCount; ++i)
        {
            if ((srcReg[i] & RC_BORDER_REG) == 0)
                srcReg[i] = regions[srcReg[i]].id;
        }

        return true;
    }
Beispiel #44
0
    /// @see rcAllocPolyMesh, rcPolyMesh
    public static bool rcMergePolyMeshes(rcContext ctx, ref rcPolyMesh[] meshes, int nmeshes, rcPolyMesh mesh)
    {
        Debug.Assert(ctx != null, "rcContext is null");

        if (nmeshes == 0 || meshes == null)
            return true;

        ctx.startTimer(rcTimerLabel.RC_TIMER_MERGE_POLYMESH);

        mesh.nvp = meshes[0].nvp;
        mesh.cs = meshes[0].cs;
        mesh.ch = meshes[0].ch;
        rcVcopy(mesh.bmin, meshes[0].bmin);
        rcVcopy(mesh.bmax, meshes[0].bmax);

        int maxVerts = 0;
        int maxPolys = 0;
        int maxVertsPerMesh = 0;
        for (int i = 0; i < nmeshes; ++i) {
            rcVmin(mesh.bmin, meshes[i].bmin);
            rcVmax(mesh.bmax, meshes[i].bmax);
            maxVertsPerMesh = Math.Max(maxVertsPerMesh, meshes[i].nverts);
            maxVerts += meshes[i].nverts;
            maxPolys += meshes[i].npolys;
        }

        mesh.nverts = 0;
        //mesh.verts = (ushort*)rcAlloc(sizeof(ushort)*maxVerts*3, RC_ALLOC_PERM);
        mesh.verts = new ushort[maxVerts * 3];
        if (mesh.verts == null) {
            ctx.log(rcLogCategory.RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'mesh.verts' " + maxVerts * 3);
            return false;
        }

        mesh.npolys = 0;
        //mesh.polys = (ushort*)rcAlloc(sizeof(ushort)*maxPolys*2*mesh.nvp, RC_ALLOC_PERM);
        mesh.polys = new ushort[maxPolys * 2 * mesh.nvp];
        if (mesh.polys == null) {
            ctx.log(rcLogCategory.RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'mesh.polys' " + maxPolys * 2 * mesh.nvp);
            return false;
        }
        //memset(mesh.polys, 0xff, sizeof(ushort)*maxPolys*2*mesh.nvp);
        for (int i = 0; i < maxPolys * 2 * mesh.nvp; ++i) {
            mesh.polys[i] = 0xffff;
        }

        //mesh.regs = (ushort*)rcAlloc(sizeof(ushort)*maxPolys, RC_ALLOC_PERM);
        mesh.regs = new ushort[maxPolys];
        if (mesh.regs == null) {
            ctx.log(rcLogCategory.RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'mesh.regs' " + maxPolys);
            return false;
        }
        //memset(mesh.regs, 0, sizeof(ushort)*maxPolys);

        //mesh.areas = (byte*)rcAlloc(sizeof(byte)*maxPolys, RC_ALLOC_PERM);
        mesh.areas = new byte[maxPolys];
        if (mesh.areas == null) {
            ctx.log(rcLogCategory.RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'mesh.areas' " + maxPolys);
            return false;
        }
        //memset(mesh.areas, 0, sizeof(byte)*maxPolys);

        //mesh.flags = (ushort*)rcAlloc(sizeof(ushort)*maxPolys, RC_ALLOC_PERM);
        mesh.flags = new ushort[maxPolys];
        if (mesh.flags == null) {
            ctx.log(rcLogCategory.RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'mesh.flags' " + maxPolys);
            return false;
        }
        //memset(mesh.flags, 0, sizeof(ushort)*maxPolys);

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

        //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, "rcMergePolyMeshes: Out of memory 'firstVert' " + VERTEX_BUCKET_COUNT);
            return false;
        }
        for (int i = 0; i < VERTEX_BUCKET_COUNT; ++i) {
            firstVert[i] = -1;
        }

        //rcScopedDelete<ushort> vremap = (ushort*)rcAlloc(sizeof(ushort)*maxVertsPerMesh, RC_ALLOC_PERM);
        ushort[] vremap = new ushort[maxVertsPerMesh];
        if (vremap == null) {
            ctx.log(rcLogCategory.RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'vremap' " + maxVertsPerMesh);
            return false;
        }
        //memset(vremap, 0, sizeof(ushort)*maxVertsPerMesh);

        for (int i = 0; i < nmeshes; ++i) {
            rcPolyMesh pmesh = meshes[i];

            ushort ox = (ushort)Math.Floor((pmesh.bmin[0] - mesh.bmin[0]) / mesh.cs + 0.5f);
            ushort oz = (ushort)Math.Floor((pmesh.bmin[2] - mesh.bmin[2]) / mesh.cs + 0.5f);

            bool isMinX = (ox == 0);
            bool isMinZ = (oz == 0);
            bool isMaxX = ((ushort)Math.Floor((mesh.bmax[0] - pmesh.bmax[0]) / mesh.cs + 0.5f)) == 0;
            bool isMaxZ = ((ushort)Math.Floor((mesh.bmax[2] - pmesh.bmax[2]) / mesh.cs + 0.5f)) == 0;
            bool isOnBorder = (isMinX || isMinZ || isMaxX || isMaxZ);

            for (int j = 0; j < pmesh.nverts; ++j) {
                //ushort* v = &pmesh.verts[j*3];
                int vIndex = j * 3;
                vremap[j] = addVertex((ushort)(pmesh.verts[vIndex + 0] + ox), pmesh.verts[vIndex + 1], (ushort)(pmesh.verts[vIndex + 2] + oz),
                                      mesh.verts, firstVert, nextVert, ref mesh.nverts);
            }

            for (int j = 0; j < pmesh.npolys; ++j) {
                //ushort* tgt = &mesh.polys[mesh.npolys*2*mesh.nvp];
                //ushort* src = &pmesh.polys[j*2*mesh.nvp];
                int tgtIndex = mesh.npolys * 2 * mesh.nvp;
                int srcIndex = j * 2 * mesh.nvp;

                mesh.regs[mesh.npolys] = pmesh.regs[j];
                mesh.areas[mesh.npolys] = pmesh.areas[j];
                mesh.flags[mesh.npolys] = pmesh.flags[j];
                mesh.npolys++;
                for (int k = 0; k < mesh.nvp; ++k) {
                    if (pmesh.polys[srcIndex + k] == RC_MESH_NULL_IDX) {
                        break;
                    }
                    mesh.polys[tgtIndex + k] = vremap[pmesh.polys[srcIndex + k]];
                }

                if (isOnBorder) {
                    for (int k = mesh.nvp; k < mesh.nvp * 2; ++k) {
                        if ((pmesh.polys[srcIndex + k] & 0x8000) != 0 && (pmesh.polys[srcIndex + k] != 0xffff)) {
                            ushort dir = (ushort)(pmesh.polys[srcIndex + k] & 0xf);
                            switch (dir) {
                                case 0: // Portal x-
                                    if (isMinX)
                                        mesh.polys[tgtIndex + k] = pmesh.polys[srcIndex + k];
                                    break;
                                case 1: // Portal z+
                                    if (isMaxZ)
                                        mesh.polys[tgtIndex + k] = pmesh.polys[srcIndex + k];
                                    break;
                                case 2: // Portal x+
                                    if (isMaxX)
                                        mesh.polys[tgtIndex + k] = pmesh.polys[srcIndex + k];
                                    break;
                                case 3: // Portal z-
                                    if (isMinZ)
                                        mesh.polys[tgtIndex + k] = pmesh.polys[srcIndex + k];
                                    break;
                            }
                        }
                    }
                }
            }
        }

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

        if (mesh.nverts > 0xffff) {
            ctx.log(rcLogCategory.RC_LOG_ERROR, "rcMergePolyMeshes: 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, "rcMergePolyMeshes: The resulting mesh has too many polygons " + mesh.npolys + " (max " + 0xffff + "). Data can be corrupted.");
        }

        ctx.stopTimer(rcTimerLabel.RC_TIMER_MERGE_POLYMESH);

        return true;
    }
Beispiel #45
0
    /// @par
    ///
    /// The value of spacial parameters are in world units.
    /// 
    /// @see rcCompactHeightfield, rcMedianFilterWalkableArea
    public static void rcMarkCylinderArea(rcContext ctx, float[] pos,
                            float r, float h, byte areaId,
                            rcCompactHeightfield chf)
    {
        Debug.Assert(ctx != null, "rcContext is null");

        ctx.startTimer(rcTimerLabel.RC_TIMER_MARK_CYLINDER_AREA);

        float[] bmin = new float[3];
        float[] bmax = new float[3];
        bmin[0] = pos[0] - r;
        bmin[1] = pos[1];
        bmin[2] = pos[2] - r;
        bmax[0] = pos[0] + r;
        bmax[1] = pos[1] + h;
        bmax[2] = pos[2] + r;
        float r2 = r * r;

        int minx = (int)((bmin[0] - chf.bmin[0]) / chf.cs);
        int miny = (int)((bmin[1] - chf.bmin[1]) / chf.ch);
        int minz = (int)((bmin[2] - chf.bmin[2]) / chf.cs);
        int maxx = (int)((bmax[0] - chf.bmin[0]) / chf.cs);
        int maxy = (int)((bmax[1] - chf.bmin[1]) / chf.ch);
        int maxz = (int)((bmax[2] - chf.bmin[2]) / chf.cs);

        if (maxx < 0) return;
        if (minx >= chf.width) return;
        if (maxz < 0) return;
        if (minz >= chf.height) return;

        if (minx < 0) minx = 0;
        if (maxx >= chf.width) maxx = chf.width - 1;
        if (minz < 0) minz = 0;
        if (maxz >= chf.height) maxz = chf.height - 1;

        for (int z = minz; z <= maxz; ++z) {
            for (int x = minx; x <= maxx; ++x) {
                rcCompactCell c = chf.cells[x + z * chf.width];
                for (int i = (int)c.index, ni = (int)(c.index + c.count); i < ni; ++i) {
                    rcCompactSpan s = chf.spans[i];

                    if (chf.areas[i] == RC_NULL_AREA)
                        continue;

                    if ((int)s.y >= miny && (int)s.y <= maxy) {
                        float sx = chf.bmin[0] + (x + 0.5f) * chf.cs;
                        float sz = chf.bmin[2] + (z + 0.5f) * chf.cs;
                        float dx = sx - pos[0];
                        float dz = sz - pos[2];

                        if (dx * dx + dz * dz < r2) {
                            chf.areas[i] = areaId;
                        }
                    }
                }
            }
        }

        ctx.stopTimer(rcTimerLabel.RC_TIMER_MARK_CYLINDER_AREA);
    }
Beispiel #46
0
    /// @par
    ///
    /// See the #rcConfig documentation for more information on the configuration parameters.
    ///
    /// @see rcAllocHeightfieldLayerSet, rcCompactHeightfield, rcHeightfieldLayerSet, rcConfig
    public static bool rcBuildHeightfieldLayers(rcContext ctx, rcCompactHeightfield chf,
                                                int borderSize, int walkableHeight,
                                                rcHeightfieldLayerSet lset)
    {
        Debug.Assert(ctx != null, "rcContext is null");

        ctx.startTimer(rcTimerLabel.RC_TIMER_BUILD_LAYERS);

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

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

        for (int i = 0; i < chf.spanCount; ++i)
        {
            srcReg[i] = 0xff;
        }

        int nsweeps = chf.width;

        rcLayerSweepSpan[] sweeps = new rcLayerSweepSpan[nsweeps];
        if (sweeps == null)
        {
            ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'sweeps' " + nsweeps);
            return(false);
        }


        // Partition walkable area into monotone regions.
        int[] prevCount = new int[256];
        byte  regId     = 0;

        for (int y = borderSize; y < h - borderSize; ++y)
        {
            //memset to 0 is done by C# alloc
            //memset(prevCount,0,sizeof(int)*regId);

            byte sweepId = 0;

            for (int x = borderSize; x < w - borderSize; ++x)
            {
                rcCompactCell c = chf.cells[x + y * w];

                for (int i = (int)c.index, ni = (int)(c.index + c.count); i < ni; ++i)
                {
                    rcCompactSpan s = chf.spans[i];
                    if (chf.areas[i] == RC_NULL_AREA)
                    {
                        continue;
                    }

                    byte sid = 0xff;

                    // -x
                    if (rcGetCon(s, 0) != RC_NOT_CONNECTED)
                    {
                        int ax = x + rcGetDirOffsetX(0);
                        int ay = y + rcGetDirOffsetY(0);
                        int ai = (int)chf.cells[ax + ay * w].index + rcGetCon(s, 0);
                        if (chf.areas[ai] != RC_NULL_AREA && srcReg[ai] != 0xff)
                        {
                            sid = srcReg[ai];
                        }
                    }

                    if (sid == 0xff)
                    {
                        sid             = sweepId++;
                        sweeps[sid].nei = (byte)0xff;
                        sweeps[sid].ns  = 0;
                    }

                    // -y
                    if (rcGetCon(s, 3) != RC_NOT_CONNECTED)
                    {
                        int  ax = x + rcGetDirOffsetX(3);
                        int  ay = y + rcGetDirOffsetY(3);
                        int  ai = (int)chf.cells[ax + ay * w].index + rcGetCon(s, 3);
                        byte nr = srcReg[ai];
                        if (nr != 0xff)
                        {
                            // Set neighbour when first valid neighbour is encoutered.
                            if (sweeps[sid].ns == 0)
                            {
                                sweeps[sid].nei = nr;
                            }

                            if (sweeps[sid].nei == nr)
                            {
                                // Update existing neighbour
                                sweeps[sid].ns++;
                                prevCount[nr]++;
                            }
                            else
                            {
                                // This is hit if there is nore than one neighbour.
                                // Invalidate the neighbour.
                                sweeps[sid].nei = 0xff;
                            }
                        }
                    }

                    srcReg[i] = sid;
                }
            }

            // Create unique ID.
            for (int i = 0; i < sweepId; ++i)
            {
                // If the neighbour is set and there is only one continuous connection to it,
                // the sweep will be merged with the previous one, else new region is created.
                if (sweeps[i].nei != 0xff && prevCount[sweeps[i].nei] == (int)sweeps[i].ns)
                {
                    sweeps[i].id = sweeps[i].nei;
                }
                else
                {
                    if (regId == 255)
                    {
                        ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildHeightfieldLayers: Region ID overflow.");
                        return(false);
                    }
                    sweeps[i].id = regId++;
                }
            }

            // Remap local sweep ids to region ids.
            for (int x = borderSize; x < w - borderSize; ++x)
            {
                rcCompactCell c = chf.cells[x + y * w];
                for (int i = (int)c.index, ni = (int)(c.index + c.count); i < ni; ++i)
                {
                    if (srcReg[i] != 0xff)
                    {
                        srcReg[i] = sweeps[srcReg[i]].id;
                    }
                }
            }
        }

        // Allocate and init layer regions.
        int nregs = (int)regId;

        rcLayerRegion[] regs = new rcLayerRegion[nregs];
        if (regs == null)
        {
            ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'regs' " + nregs);
            return(false);
        }
        //memset(regs, 0, sizeof(rcLayerRegion)*nregs);
        for (int i = 0; i < nregs; ++i)
        {
            regs[i].layerId = 0xff;
            regs[i].ymin    = 0xffff;
            regs[i].ymax    = 0;
        }

        // Find region neighbours and overlapping regions.
        for (int y = 0; y < h; ++y)
        {
            for (int x = 0; x < w; ++x)
            {
                rcCompactCell c = chf.cells[x + y * w];

                byte[] lregs  = new byte[RC_MAX_LAYERS];
                int    nlregs = 0;

                for (int i = (int)c.index, ni = (int)(c.index + c.count); i < ni; ++i)
                {
                    rcCompactSpan s  = chf.spans[i];
                    byte          ri = srcReg[i];
                    if (ri == 0xff)
                    {
                        continue;
                    }

                    regs[ri].ymin = Math.Min(regs[ri].ymin, s.y);
                    regs[ri].ymax = Math.Max(regs[ri].ymax, s.y);

                    // Collect all region layers.
                    if (nlregs < RC_MAX_LAYERS)
                    {
                        lregs[nlregs++] = ri;
                    }

                    // Update neighbours
                    for (int dir = 0; dir < 4; ++dir)
                    {
                        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);
                            byte rai = srcReg[ai];
                            if (rai != 0xff && rai != ri)
                            {
                                addUnique(regs[ri].neis, ref regs[ri].nneis, rai);
                            }
                        }
                    }
                }

                // Update overlapping regions.
                for (int i = 0; i < nlregs - 1; ++i)
                {
                    for (int j = i + 1; j < nlregs; ++j)
                    {
                        if (lregs[i] != lregs[j])
                        {
                            rcLayerRegion ri = regs[lregs[i]];
                            rcLayerRegion rj = regs[lregs[j]];
                            addUnique(ri.layers, ref ri.nlayers, lregs[j]);
                            addUnique(rj.layers, ref rj.nlayers, lregs[i]);
                        }
                    }
                }
            }
        }

        // Create 2D layers from regions.
        byte layerId = 0;

        const int MAX_STACK = 64;

        byte[] stack  = new byte[MAX_STACK];
        int    nstack = 0;

        for (int i = 0; i < nregs; ++i)
        {
            rcLayerRegion root = regs[i];
            // Skip alreadu visited.
            if (root.layerId != 0xff)
            {
                continue;
            }

            // Start search.
            root.layerId  = layerId;
            root.baseFlag = 1;

            nstack          = 0;
            stack[nstack++] = (byte)i;

            while (nstack != 0)
            {
                // Pop front
                rcLayerRegion reg = regs[stack[0]];
                nstack--;
                for (int j = 0; j < nstack; ++j)
                {
                    stack[j] = stack[j + 1];
                }

                int nneis = (int)reg.nneis;
                for (int j = 0; j < nneis; ++j)
                {
                    byte          nei  = reg.neis[j];
                    rcLayerRegion regn = regs[nei];
                    // Skip already visited.
                    if (regn.layerId != 0xff)
                    {
                        continue;
                    }
                    // Skip if the neighbour is overlapping root region.
                    if (contains(root.layers, root.nlayers, nei))
                    {
                        continue;
                    }
                    // Skip if the height range would become too large.
                    int ymin = Math.Min(root.ymin, regn.ymin);
                    int ymax = Math.Max(root.ymax, regn.ymax);
                    if ((ymax - ymin) >= 255)
                    {
                        continue;
                    }

                    if (nstack < MAX_STACK)
                    {
                        // Deepen
                        stack[nstack++] = (byte)nei;

                        // Mark layer id
                        regn.layerId = layerId;
                        // Merge current layers to root.
                        for (int k = 0; k < regn.nlayers; ++k)
                        {
                            addUnique(root.layers, ref root.nlayers, regn.layers[k]);
                        }
                        root.ymin = Math.Min(root.ymin, regn.ymin);
                        root.ymax = Math.Max(root.ymax, regn.ymax);
                    }
                }
            }

            layerId++;
        }

        // Merge non-overlapping regions that are close in height.
        ushort mergeHeight = (ushort)(walkableHeight * 4);

        for (int i = 0; i < nregs; ++i)
        {
            rcLayerRegion ri = regs[i];
            if (ri.baseFlag == 0)
            {
                continue;
            }

            byte newId = ri.layerId;

            for (; ;)
            {
                byte oldId = 0xff;

                for (int j = 0; j < nregs; ++j)
                {
                    if (i == j)
                    {
                        continue;
                    }
                    rcLayerRegion rj = regs[j];
                    if (rj.baseFlag == 0)
                    {
                        continue;
                    }

                    // Skip if teh regions are not close to each other.
                    if (!overlapRange(ri.ymin,
                                      (ushort)(ri.ymax + mergeHeight),
                                      rj.ymin,
                                      (ushort)(rj.ymax + mergeHeight)))
                    {
                        continue;
                    }
                    // Skip if the height range would become too large.
                    int ymin = Math.Min(ri.ymin, rj.ymin);
                    int ymax = Math.Max(ri.ymax, rj.ymax);
                    if ((ymax - ymin) >= 255)
                    {
                        continue;
                    }

                    // Make sure that there is no overlap when mergin 'ri' and 'rj'.
                    bool overlap = false;
                    // Iterate over all regions which have the same layerId as 'rj'
                    for (int k = 0; k < nregs; ++k)
                    {
                        if (regs[k].layerId != rj.layerId)
                        {
                            continue;
                        }
                        // Check if region 'k' is overlapping region 'ri'
                        // Index to 'regs' is the same as region id.
                        if (contains(ri.layers, ri.nlayers, (byte)k))
                        {
                            overlap = true;
                            break;
                        }
                    }
                    // Cannot merge of regions overlap.
                    if (overlap)
                    {
                        continue;
                    }

                    // Can merge i and j.
                    oldId = rj.layerId;
                    break;
                }

                // Could not find anything to merge with, stop.
                if (oldId == 0xff)
                {
                    break;
                }

                // Merge
                for (int j = 0; j < nregs; ++j)
                {
                    rcLayerRegion rj = regs[j];
                    if (rj.layerId == oldId)
                    {
                        rj.baseFlag = 0;
                        // Remap layerIds.
                        rj.layerId = newId;
                        // Add overlaid layers from 'rj' to 'ri'.
                        for (int k = 0; k < rj.nlayers; ++k)
                        {
                            addUnique(ri.layers, ref ri.nlayers, rj.layers[k]);
                        }
                        // Update heigh bounds.
                        ri.ymin = Math.Min(ri.ymin, rj.ymin);
                        ri.ymax = Math.Max(ri.ymax, rj.ymax);
                    }
                }
            }
        }

        // Compact layerIds
        byte[] remap = new byte[256];
        //memset(remap, 0, 256);

        // Find number of unique layers.
        layerId = 0;
        for (int i = 0; i < nregs; ++i)
        {
            remap[regs[i].layerId] = 1;
        }
        for (int i = 0; i < 256; ++i)
        {
            if (remap[i] != 0)
            {
                remap[i] = layerId++;
            }
            else
            {
                remap[i] = 0xff;
            }
        }
        // Remap ids.
        for (int i = 0; i < nregs; ++i)
        {
            regs[i].layerId = remap[regs[i].layerId];
        }

        // No layers, return empty.
        if (layerId == 0)
        {
            ctx.stopTimer(rcTimerLabel.RC_TIMER_BUILD_LAYERS);
            return(true);
        }

        // Create layers.
        Debug.Assert(lset.layers == null, "Assert lset.layers == 0");

        int lw = w - borderSize * 2;
        int lh = h - borderSize * 2;

        // Build contracted bbox for layers.
        float[] bmin = new float[3];
        float[] bmax = new float[3];
        rcVcopy(bmin, chf.bmin);
        rcVcopy(bmax, chf.bmax);
        bmin[0] += borderSize * chf.cs;
        bmin[2] += borderSize * chf.cs;
        bmax[0] -= borderSize * chf.cs;
        bmax[2] -= borderSize * chf.cs;

        lset.nlayers = (int)layerId;

        //lset.layers = (rcHeightfieldLayer*)rcAlloc(sizeof(rcHeightfieldLayer)*lset.nlayers, RC_ALLOC_PERM);
        lset.layers = new rcHeightfieldLayer[lset.nlayers];
        if (lset.layers == null)
        {
            ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'layers' " + lset.nlayers);
            return(false);
        }
        //memset(lset.layers, 0, sizeof(rcHeightfieldLayer)*lset.nlayers);


        // Store layers.
        for (int i = 0; i < lset.nlayers; ++i)
        {
            byte curId = (byte)i;

            // Allocate memory for the current layer.
            rcHeightfieldLayer layer = lset.layers[i];
            //memset(layer, 0, sizeof(rcHeightfieldLayer));

            int gridSize = sizeof(byte) * lw * lh;

            layer.heights = new byte[gridSize];//(byte*)rcAlloc(gridSize, RC_ALLOC_PERM);
            if (layer.heights == null)
            {
                ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'heights' " + gridSize);
                return(false);
            }
            //memset(layer.heights, 0xff, gridSize);
            for (int j = 0; j < gridSize; ++j)
            {
                layer.heights[j] = 0xFF;
            }

            layer.areas = new byte[gridSize];// (byte*)rcAlloc(gridSize, RC_ALLOC_PERM);
            if (layer.areas == null)
            {
                ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'areas' " + gridSize);
                return(false);
            }
            //memset(layer.areas, 0, gridSize);

            layer.cons = new byte[gridSize];//  (byte*)rcAlloc(gridSize, RC_ALLOC_PERM);
            if (layer.cons == null)
            {
                ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'cons' " + gridSize);
                return(false);
            }
            //memset(layer.cons, 0, gridSize);

            // Find layer height bounds.
            int hmin = 0, hmax = 0;
            for (int j = 0; j < nregs; ++j)
            {
                if (regs[j].baseFlag != 0 && regs[j].layerId == curId)
                {
                    hmin = (int)regs[j].ymin;
                    hmax = (int)regs[j].ymax;
                }
            }

            layer.width  = lw;
            layer.height = lh;
            layer.cs     = chf.cs;
            layer.ch     = chf.ch;

            // Adjust the bbox to fit the heighfield.
            rcVcopy(layer.bmin, bmin);
            rcVcopy(layer.bmax, bmax);
            layer.bmin[1] = bmin[1] + hmin * chf.ch;
            layer.bmax[1] = bmin[1] + hmax * chf.ch;
            layer.hmin    = hmin;
            layer.hmax    = hmax;

            // Update usable data region.
            layer.minx = layer.width;
            layer.maxx = 0;
            layer.miny = layer.height;
            layer.maxy = 0;

            // Copy height and area from compact heighfield.
            for (int y = 0; y < lh; ++y)
            {
                for (int x = 0; x < lw; ++x)
                {
                    int           cx = borderSize + x;
                    int           cy = borderSize + y;
                    rcCompactCell c  = chf.cells[cx + cy * w];
                    for (int j = (int)c.index, nj = (int)(c.index + c.count); j < nj; ++j)
                    {
                        rcCompactSpan s = chf.spans[j];
                        // Skip unassigned regions.
                        if (srcReg[j] == 0xff)
                        {
                            continue;
                        }
                        // Skip of does nto belong to current layer.
                        byte lid = regs[srcReg[j]].layerId;
                        if (lid != curId)
                        {
                            continue;
                        }

                        // Update data bounds.
                        layer.minx = Math.Min(layer.minx, x);
                        layer.maxx = Math.Max(layer.maxx, x);
                        layer.miny = Math.Min(layer.miny, y);
                        layer.maxy = Math.Max(layer.maxy, y);

                        // Store height and area type.
                        int idx = x + y * lw;
                        layer.heights[idx] = (byte)(s.y - hmin);
                        layer.areas[idx]   = chf.areas[j];

                        // Check connection.
                        byte portal = 0;
                        byte con    = 0;
                        for (int dir = 0; dir < 4; ++dir)
                        {
                            if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
                            {
                                int  ax   = cx + rcGetDirOffsetX(dir);
                                int  ay   = cy + rcGetDirOffsetY(dir);
                                int  ai   = (int)chf.cells[ax + ay * w].index + rcGetCon(s, dir);
                                byte alid = srcReg[ai] != (byte)0xff ? regs[srcReg[ai]].layerId : (byte)0xff;
                                // Portal mask
                                if (chf.areas[ai] != RC_NULL_AREA && lid != alid)
                                {
                                    portal |= (byte)(1 << dir);
                                    // Update height so that it matches on both sides of the portal.
                                    rcCompactSpan aSpan = chf.spans[ai];
                                    if (aSpan.y > hmin)
                                    {
                                        layer.heights[idx] = Math.Max(layer.heights[idx], (byte)(aSpan.y - hmin));
                                    }
                                }
                                // Valid connection mask
                                if (chf.areas[ai] != RC_NULL_AREA && lid == alid)
                                {
                                    int nx = ax - borderSize;
                                    int ny = ay - borderSize;
                                    if (nx >= 0 && ny >= 0 && nx < lw && ny < lh)
                                    {
                                        con |= (byte)(1 << dir);
                                    }
                                }
                            }
                        }

                        layer.cons[idx] = (byte)((portal << 4) | con);
                    }
                }
            }

            if (layer.minx > layer.maxx)
            {
                layer.minx = layer.maxx = 0;
            }
            if (layer.miny > layer.maxy)
            {
                layer.miny = layer.maxy = 0;
            }
        }

        ctx.stopTimer(rcTimerLabel.RC_TIMER_BUILD_LAYERS);

        return(true);
    }
Beispiel #47
0
    /// @par
    ///
    /// For this filter, the clearance above the span is the distance from the span's 
    /// maximum to the next higher span's minimum. (Same grid column.)
    /// 
    /// @see rcHeightfield, rcConfig
    public static void rcFilterWalkableLowHeightSpans(rcContext ctx, int walkableHeight, rcHeightfield solid)
    {
        Debug.Assert(ctx != null, "rcContext is null");

        ctx.startTimer(rcTimerLabel.RC_TIMER_FILTER_WALKABLE);

        int w = solid.width;
        int h = solid.height;
        int MAX_HEIGHT = 0xffff;

        // Remove walkable flag from spans which do not have enough
        // space above them for the agent to stand there.
        for (int y = 0; y < h; ++y)
        {
            for (int x = 0; x < w; ++x)
            {
                for (rcSpan s = solid.spans[x + y*w]; s != null; s = s.next)
                {
                    int bot = (int)(s.smax);
                    int top = s.next != null ? (int)(s.next.smin) : MAX_HEIGHT;
                    if ((top - bot) <= walkableHeight) {
                        s.area = RC_NULL_AREA;
                    }
                }
            }
        }

        ctx.stopTimer(rcTimerLabel.RC_TIMER_FILTER_WALKABLE);
    }
Beispiel #48
0
    /// @par
    ///
    /// This filter is usually applied after applying area id's using functions
    /// such as #rcMarkBoxArea, #rcMarkConvexPolyArea, and #rcMarkCylinderArea.
    /// 
    /// @see rcCompactHeightfield
    public static bool rcMedianFilterWalkableArea(rcContext ctx, rcCompactHeightfield chf)
    {
        Debug.Assert(ctx != null, "rcContext is null");

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

        ctx.startTimer(rcTimerLabel.RC_TIMER_MEDIAN_AREA);

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

        // Init distance.
        for (int i = 0; i < chf.spanCount; ++i) {
            areas[i] = 0xff;
        }
        //memset(areas, 0xff, sizeof(byte)*chf.spanCount);

        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) {
                    rcCompactSpan s = chf.spans[i];
                    if (chf.areas[i] == RC_NULL_AREA) {
                        areas[i] = chf.areas[i];
                        continue;
                    }

                    byte[] nei = new byte[9];
                    for (int j = 0; j < 9; ++j)
                        nei[j] = chf.areas[i];

                    for (int dir = 0; dir < 4; ++dir) {
                        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);
                            if (chf.areas[ai] != RC_NULL_AREA)
                                nei[dir * 2 + 0] = chf.areas[ai];

                            rcCompactSpan aSpan = chf.spans[ai];
                            int dir2 = (dir + 1) & 0x3;
                            if (rcGetCon(aSpan, dir2) != RC_NOT_CONNECTED) {
                                int ax2 = ax + rcGetDirOffsetX(dir2);
                                int ay2 = ay + rcGetDirOffsetY(dir2);
                                int ai2 = (int)chf.cells[ax2 + ay2 * w].index + rcGetCon(aSpan, dir2);
                                if (chf.areas[ai2] != RC_NULL_AREA)
                                    nei[dir * 2 + 1] = chf.areas[ai2];
                            }
                        }
                    }
                    insertSort(nei, 9);
                    areas[i] = nei[4];
                }
            }
        }

        chf.areas = areas;
        //memcpy(chf.areas, areas, sizeof(byte)*chf.spanCount);

        //rcFree(areas);

        ctx.stopTimer(rcTimerLabel.RC_TIMER_MEDIAN_AREA);

        return true;
    }
Beispiel #49
0
    public static bool removeVertex(rcContext ctx, rcPolyMesh mesh, ushort rem, int maxTris)
    {
        int nvp = mesh.nvp;

        // Count number of polygons to remove.
        int numRemovedVerts = 0;
        for (int i = 0; i < mesh.npolys; ++i) {
            //ushort* p = &mesh.polys[i*nvp*2];
            int pIndex = i * nvp * 2;
            int nv = countPolyVerts(mesh.polys, pIndex, nvp);
            for (int j = 0; j < nv; ++j) {
                if (mesh.polys[pIndex + j] == rem)
                    numRemovedVerts++;
            }
        }

        int nedges = 0;
        //rcScopedDelete<int> edges = (int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp*4, RC_ALLOC_TEMP);
        int[] edges = new int[numRemovedVerts * nvp * 4];
        if (edges == null) {
            ctx.log(rcLogCategory.RC_LOG_WARNING, "removeVertex: Out of memory 'edges' " + numRemovedVerts * nvp * 4);
            return false;
        }

        int nhole = 0;
        //rcScopedDelete<int> hole = (int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp, RC_ALLOC_TEMP);
        int[] hole = new int[numRemovedVerts * nvp];
        if (hole == null) {
            ctx.log(rcLogCategory.RC_LOG_WARNING, "removeVertex: Out of memory 'hole' " + numRemovedVerts * nvp);
            return false;
        }

        int nhreg = 0;
        //rcScopedDelete<int> hreg = (int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp, RC_ALLOC_TEMP);
        int[] hreg = new int[numRemovedVerts * nvp];
        if (hreg == null) {
            ctx.log(rcLogCategory.RC_LOG_WARNING, "removeVertex: Out of memory 'hreg' " + numRemovedVerts * nvp);
            return false;
        }

        int nharea = 0;
        //rcScopedDelete<int> harea = (int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp, RC_ALLOC_TEMP);
        int[] harea = new int[numRemovedVerts * nvp];
        if (harea == null) {
            ctx.log(rcLogCategory.RC_LOG_WARNING, "removeVertex: Out of memory 'harea' " + numRemovedVerts * nvp);
            return false;
        }

        for (int i = 0; i < mesh.npolys; ++i) {
            //ushort* p = &mesh.polys[i*nvp*2];
            int pIndex = i * nvp * 2;
            int nv = countPolyVerts(mesh.polys, pIndex, nvp);
            bool hasRem = false;
            for (int j = 0; j < nv; ++j)
                if (mesh.polys[pIndex + j] == rem) hasRem = true;
            if (hasRem) {
                // Collect edges which does not touch the removed vertex.
                for (int j = 0, k = nv - 1; j < nv; k = j++) {
                    if (mesh.polys[pIndex + j] != rem && mesh.polys[pIndex + k] != rem) {
                        //int[] e = &edges[nedges*4];
                        int eIndex = nedges * 4;
                        edges[eIndex + 0] = mesh.polys[pIndex + k];
                        edges[eIndex + 1] = mesh.polys[pIndex + j];
                        edges[eIndex + 2] = mesh.regs[i];
                        edges[eIndex + 3] = mesh.areas[i];
                        nedges++;
                    }
                }
                // Remove the polygon.
                //ushort* p2 = &mesh.polys[(mesh.npolys-1)*nvp*2];
                int p2Index = (mesh.npolys - 1) * nvp * 2;
                if (mesh.polys[pIndex] != mesh.polys[p2Index]) {
                    //memcpy(p,p2,sizeof(ushort)*nvp);
                    for (int j = 0; j < nvp; ++j) {
                        mesh.polys[pIndex + j] = mesh.polys[p2Index + j];
                    }
                }
                //memset(p+nvp,0xff,sizeof(ushort)*nvp);
                for (int j = 0; j < nvp; ++j) {
                    mesh.polys[pIndex + nvp + j] = 0xffff;
                }
                mesh.regs[i] = mesh.regs[mesh.npolys - 1];
                mesh.areas[i] = mesh.areas[mesh.npolys - 1];
                mesh.npolys--;
                --i;
            }
        }

        // Remove vertex.
        for (int i = (int)rem; i < mesh.nverts; ++i) {
            mesh.verts[i * 3 + 0] = mesh.verts[(i + 1) * 3 + 0];
            mesh.verts[i * 3 + 1] = mesh.verts[(i + 1) * 3 + 1];
            mesh.verts[i * 3 + 2] = mesh.verts[(i + 1) * 3 + 2];
        }
        mesh.nverts--;

        // Adjust indices to match the removed vertex layout.
        for (int i = 0; i < mesh.npolys; ++i) {
            //ushort* p = &mesh.polys[i*nvp*2];
            int pIndex = i * nvp * 2;
            int nv = countPolyVerts(mesh.polys, i * nvp * 2, nvp);
            for (int j = 0; j < nv; ++j) {
                if (mesh.polys[pIndex + j] > rem) {
                    mesh.polys[pIndex + j]--;
                }
            }
        }
        for (int i = 0; i < nedges; ++i) {
            if (edges[i * 4 + 0] > rem) {
                edges[i * 4 + 0]--;
            }
            if (edges[i * 4 + 1] > rem) {
                edges[i * 4 + 1]--;
            }
        }

        if (nedges == 0) {
            return true;
        }

        // Start with one vertex, keep appending connected
        // segments to the start and end of the hole.
        pushBack(edges[0], hole, ref nhole);
        pushBack(edges[2], hreg, ref nhreg);
        pushBack(edges[3], harea, ref nharea);

        while (nedges != 0) {
            bool match = false;

            for (int i = 0; i < nedges; ++i) {
                int ea = edges[i * 4 + 0];
                int eb = edges[i * 4 + 1];
                int r = edges[i * 4 + 2];
                int a = edges[i * 4 + 3];
                bool add = false;
                if (hole[0] == eb) {
                    // The segment matches the beginning of the hole boundary.
                    pushFront(ea, hole, ref nhole);
                    pushFront(r, hreg, ref nhreg);
                    pushFront(a, harea, ref nharea);
                    add = true;
                } else if (hole[nhole - 1] == ea) {
                    // The segment matches the end of the hole boundary.
                    pushBack(eb, hole, ref nhole);
                    pushBack(r, hreg, ref nhreg);
                    pushBack(a, harea, ref nharea);
                    add = true;
                }
                if (add) {
                    // The edge segment was added, remove it.
                    edges[i * 4 + 0] = edges[(nedges - 1) * 4 + 0];
                    edges[i * 4 + 1] = edges[(nedges - 1) * 4 + 1];
                    edges[i * 4 + 2] = edges[(nedges - 1) * 4 + 2];
                    edges[i * 4 + 3] = edges[(nedges - 1) * 4 + 3];
                    --nedges;
                    match = true;
                    --i;
                }
            }

            if (!match)
                break;
        }

        //rcScopedDelete<int> tris = (int*)rcAlloc(sizeof(int)*nhole*3, RC_ALLOC_TEMP);
        int[] tris = new int[nhole * 3];
        if (tris == null) {
            ctx.log(rcLogCategory.RC_LOG_WARNING, "removeVertex: Out of memory 'tris' " + nhole * 3);
            return false;
        }

        //rcScopedDelete<int> tverts = (int*)rcAlloc(sizeof(int)*nhole*4, RC_ALLOC_TEMP);
        int[] tverts = new int[nhole * 4];
        if (tverts == null) {
            ctx.log(rcLogCategory.RC_LOG_WARNING, "removeVertex: Out of memory 'tverts' " + nhole * 4);
            return false;
        }

        //rcScopedDelete<int> thole = (int*)rcAlloc(sizeof(int)*nhole, RC_ALLOC_TEMP);
        int[] thole = new int[nhole];
        if (tverts == null) {
            ctx.log(rcLogCategory.RC_LOG_WARNING, "removeVertex: Out of memory 'thole' " + nhole);
            return false;
        }

        // Generate temp vertex array for triangulation.
        for (int i = 0; i < nhole; ++i) {
            int pi = hole[i];
            tverts[i * 4 + 0] = mesh.verts[pi * 3 + 0];
            tverts[i * 4 + 1] = mesh.verts[pi * 3 + 1];
            tverts[i * 4 + 2] = mesh.verts[pi * 3 + 2];
            tverts[i * 4 + 3] = 0;
            thole[i] = i;
        }

        // Triangulate the hole.
        int ntris = triangulate(nhole, tverts, thole, tris);
        if (ntris < 0) {
            ntris = -ntris;
            ctx.log(rcLogCategory.RC_LOG_WARNING, "removeVertex: triangulate() returned bad results.");
        }

        // Merge the hole triangles back to polygons.
        //rcScopedDelete<ushort> polys = (ushort*)rcAlloc(sizeof(ushort)*(ntris+1)*nvp, RC_ALLOC_TEMP);
        ushort[] polys = new ushort[(ntris + 1) * nvp];
        if (polys == null) {
            ctx.log(rcLogCategory.RC_LOG_ERROR, "removeVertex: Out of memory 'polys' " + (ntris + 1) * nvp);
            return false;
        }
        //rcScopedDelete<ushort> pregs = (ushort*)rcAlloc(sizeof(ushort)*ntris, RC_ALLOC_TEMP);
        ushort[] pregs = new ushort[ntris];
        if (pregs == null) {
            ctx.log(rcLogCategory.RC_LOG_ERROR, "removeVertex: Out of memory 'pregs' " + ntris);
            return false;
        }
        //rcScopedDelete<byte> pareas = (byte*)rcAlloc(sizeof(byte)*ntris, RC_ALLOC_TEMP);
        byte[] pareas = new byte[ntris];
        if (pregs == null) {
            ctx.log(rcLogCategory.RC_LOG_ERROR, "removeVertex: Out of memory 'pareas' " + ntris);
            return false;
        }

        int tmpPolyIndex = ntris * nvp;
        //ushort* tmpPoly = &polys[ntris*nvp];

        // Build initial polygons.
        int npolys = 0;
        //memset(polys, 0xff, ntris*nvp*sizeof(ushort));
        for (int i = 0; i < ntris * nvp; ++i) {
            polys[i] = 0xffff;
        }
        for (int j = 0; j < ntris; ++j) {
            //int* t = &tris[j*3];
            int tIndex = 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)hole[tris[tIndex + 0]];
                polys[npolys * nvp + 1] = (ushort)hole[tris[tIndex + 1]];
                polys[npolys * nvp + 2] = (ushort)hole[tris[tIndex + 2]];
                pregs[npolys] = (ushort)hreg[tris[tIndex + 0]];
                pareas[npolys] = (byte)harea[tris[tIndex + 0]];
                npolys++;
            }
        }
        if (npolys == 0) {
            return true;
        }

        // 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) {
                        int pkIndex = k * nvp;
                        //ushort* pk = &polys[k*nvp];
                        int ea = 0;
                        int 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* last = &polys[(npolys-1)*nvp];
                    int lastIndex = (npolys - 1) * nvp;
                    if (polys[pbIndex] != polys[lastIndex]) {
                        //memcpy(pb, last, sizeof(ushort)*nvp);
                        for (int j = 0; j < nvp; ++j) {
                            polys[pbIndex + j] = polys[lastIndex + j];
                        }
                    }
                    pregs[bestPb] = pregs[npolys - 1];
                    pareas[bestPb] = pareas[npolys - 1];
                    npolys--;
                } else {
                    // Could not merge any polygons, stop.
                    break;
                }
            }
        }

        // Store polygons.
        for (int i = 0; i < npolys; ++i) {
            if (mesh.npolys >= maxTris) break;
            //ushort* p = &mesh.polys[mesh.npolys*nvp*2];
            int pIndex = mesh.npolys * nvp * 2;
            for (int j = 0; j < nvp * 2; ++j) {
                polys[pIndex + j] = 0xffff;
            }
            //memset(p,0xff,sizeof(ushort)*nvp*2);
            for (int j = 0; j < nvp; ++j) {
                polys[pIndex + j] = polys[i * nvp + j];
            }
            mesh.regs[mesh.npolys] = pregs[i];
            mesh.areas[mesh.npolys] = pareas[i];
            mesh.npolys++;
            if (mesh.npolys > maxTris) {
                ctx.log(rcLogCategory.RC_LOG_ERROR, "removeVertex: Too many polygons " + mesh.npolys + " (max:" + maxTris + ")");
                return false;
            }
        }

        return true;
    }
Beispiel #50
0
    /// @par
    /// 
    /// Non-null regions will consist of connected, non-overlapping walkable spans that form a single contour.
    /// Contours will form simple polygons.
    /// 
    /// If multiple regions form an area that is smaller than @p minRegionArea, then all spans will be
    /// re-assigned to the zero (null) region.
    /// 
    /// Watershed partitioning can result in smaller than necessary regions, especially in diagonal corridors. 
    /// @p mergeRegionArea helps reduce unecessarily small regions.
    /// 
    /// See the #rcConfig documentation for more information on the configuration parameters.
    /// 
    /// The region data will be available via the rcCompactHeightfield::maxRegions
    /// and rcCompactSpan::reg fields.
    /// 
    /// @warning The distance field must be created using #rcBuildDistanceField before attempting to build regions.
    /// 
    /// @see rcCompactHeightfield, rcCompactSpan, rcBuildDistanceField, rcBuildRegionsMonotone, rcConfig
    public static bool rcBuildRegions(rcContext ctx, rcCompactHeightfield chf,
					    int borderSize, int minRegionArea, int mergeRegionArea)
    {
        Debug.Assert(ctx != null, "rcContext is null");

        ctx.startTimer(rcTimerLabel.RC_TIMER_BUILD_REGIONS);

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

        //rcScopedDelete<ushort> buf = (ushort*)rcAlloc(sizeof(ushort)*chf.spanCount*4, RC_ALLOC_TEMP);
        /*
        ushort[] buf = new ushort[chf.spanCount*4];
        if (buf == null)
        {
            ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildRegions: Out of memory 'tmp' ("+chf.spanCount*4+").");
            return false;
        }
        */
        ctx.startTimer(rcTimerLabel.RC_TIMER_BUILD_REGIONS_WATERSHED);

        const int LOG_NB_STACKS = 3;
        const int NB_STACKS = 1 << LOG_NB_STACKS;
        List<int>[] lvlStacks = new List<int>[NB_STACKS];
        for (int i = 0; i < NB_STACKS; ++i) {
            lvlStacks[i] = new List<int>();
            //rccsResizeList(lvlStacks[i], 1024);
            lvlStacks[i].Capacity = 1024;
        }

        List<int> stack = new List<int>();//(1024);
        List<int> visited = new List<int>();//(1024);
        stack.Capacity = 1024;
        visited.Capacity = 1024;
        //rccResizeList(stack, 1024);
        //rccResizeList(visited, 1024);

        ushort[] srcReg = new ushort[chf.spanCount];
        ushort[] srcDist = new ushort[chf.spanCount];//buf+chf.spanCount;
        ushort[] dstReg = new ushort[chf.spanCount];// buf+chf.spanCount*2;
        ushort[] dstDist = new ushort[chf.spanCount];//buf+chf.spanCount*3;

        //memset(srcReg, 0, sizeof(ushort)*chf.spanCount);
        //memset(srcDist, 0, sizeof(ushort)*chf.spanCount);

        ushort regionId = 1;
        ushort level = (ushort)((chf.maxDistance+1) & ~1);

        // TODO: Figure better formula, expandIters defines how much the
        // watershed "overflows" and simplifies the regions. Tying it to
        // agent radius was usually good indication how greedy it could be.
        //	const int expandIters = 4 + walkableRadius * 2;
        const int expandIters = 8;

        if (borderSize > 0)
        {
            // Make sure border will not overflow.
            int bw = Math.Min(w, borderSize);
            int bh = Math.Min(h, borderSize);
            // Paint regions
            paintRectRegion(0, bw, 0, h,(ushort)( regionId|RC_BORDER_REG ), chf, srcReg); regionId++;
            paintRectRegion(w - bw, w, 0, h, (ushort)(regionId | RC_BORDER_REG), chf, srcReg); regionId++;
            paintRectRegion(0, w, 0, bh, (ushort)(regionId | RC_BORDER_REG), chf, srcReg); regionId++;
            paintRectRegion(0, w, h - bh, h, (ushort)(regionId | RC_BORDER_REG), chf, srcReg); regionId++;

            chf.borderSize = borderSize;
        }

        int sId = -1;
        while (level > 0)
        {
            level = (ushort)(level >= 2 ? level-2 : 0);
            sId = (sId+1) & (NB_STACKS-1);

        //		ctx.startTimer(rcTimerLabel.RC_TIMER_DIVIDE_TO_LEVELS);

            if (sId == 0)
                sortCellsByLevel(level, chf, srcReg, NB_STACKS, lvlStacks, 1);
            else
                appendStacks(lvlStacks[sId-1], lvlStacks[sId], srcReg); // copy left overs from last level

        //		ctx.stopTimer(rcTimerLabel.RC_TIMER_DIVIDE_TO_LEVELS);

            ctx.startTimer(rcTimerLabel.RC_TIMER_BUILD_REGIONS_EXPAND);

            // Expand current regions until no empty connected cells found.
            if (expandRegions(expandIters, level, chf, srcReg, srcDist, dstReg, dstDist, lvlStacks[sId], false) != srcReg)
            {
                rcSwap(ref srcReg,ref dstReg);
                rcSwap(ref srcDist,ref dstDist);
            }

            ctx.stopTimer(rcTimerLabel.RC_TIMER_BUILD_REGIONS_EXPAND);

            ctx.startTimer(rcTimerLabel.RC_TIMER_BUILD_REGIONS_FLOOD);

            // Mark new regions with IDs.
            for (int j=0; j<lvlStacks[sId].Count; j+=3)
            {
                int x = lvlStacks[sId][j];
                int y = lvlStacks[sId][j+1];
                int i = lvlStacks[sId][j+2];
                if (i >= 0 && srcReg[i] == 0)
                {
                    if (floodRegion(x, y, i, level, regionId, chf, srcReg, srcDist, stack))
                        regionId++;
                }
            }

            ctx.stopTimer(rcTimerLabel.RC_TIMER_BUILD_REGIONS_FLOOD);
        }

        // Expand current regions until no empty connected cells found.
        if (expandRegions(expandIters*8, 0, chf, srcReg, srcDist, dstReg, dstDist, stack, true) != srcReg)
        {
            rcSwap(ref srcReg,ref dstReg);
            rcSwap(ref srcDist,ref dstDist);
        }

        ctx.stopTimer(rcTimerLabel.RC_TIMER_BUILD_REGIONS_WATERSHED);

        ctx.startTimer(rcTimerLabel.RC_TIMER_BUILD_REGIONS_FILTER);

        // Filter out small regions.
        chf.maxRegions = regionId;
        if (!filterSmallRegions(ctx, minRegionArea, mergeRegionArea, ref chf.maxRegions, chf, srcReg))
            return false;

        ctx.stopTimer(rcTimerLabel.RC_TIMER_BUILD_REGIONS_FILTER);

        // Write the result out.
        for (int i = 0; i < chf.spanCount; ++i)
            chf.spans[i].reg = srcReg[i];

        ctx.stopTimer(rcTimerLabel.RC_TIMER_BUILD_REGIONS);

        return true;
    }