예제 #1
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;
    }
예제 #2
0
    public static void paintRectRegion(int minx, int maxx, int miny, int maxy, ushort regId,
							    rcCompactHeightfield chf, ushort[] srcReg)
    {
        int w = chf.width;
        for (int y = miny; y < maxy; ++y)
        {
            for (int x = minx; x < maxx; ++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)
                        srcReg[i] = regId;
                }
            }
        }
    }
예제 #3
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);
    }
예제 #4
0
    static int getCornerHeight(int x, int y, int i, int dir,
						   rcCompactHeightfield chf,
						   ref bool isBorderVertex)
    {
        rcCompactSpan s = chf.spans[i];
        int ch = (int)s.y;
        int dirp = (dir+1) & 0x3;

        uint[] regs = new uint[] {0,0,0,0};

        // Combine region and area codes in order to prevent
        // border vertices which are in between two areas to be removed.
        regs[0] = (uint)( chf.spans[i].reg | (chf.areas[i] << 16) );

        if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
        {
        int ax = x + rcGetDirOffsetX(dir);
        int ay = y + rcGetDirOffsetY(dir);
        int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(s, dir);
        rcCompactSpan aSpan = chf.spans[ai];
        ch = Math.Max(ch, (int)aSpan.y);
        regs[1] = (uint)( chf.spans[ai].reg | (chf.areas[ai] << 16) );
        if (rcGetCon(aSpan, dirp) != RC_NOT_CONNECTED)
        {
            int ax2 = ax + rcGetDirOffsetX(dirp);
            int ay2 = ay + rcGetDirOffsetY(dirp);
            int ai2 = (int)chf.cells[ax2+ay2*chf.width].index + rcGetCon(aSpan, dirp);
            rcCompactSpan as2 = chf.spans[ai2];
            ch = Math.Max(ch, (int)as2.y);
            regs[2] =  (uint)(chf.spans[ai2].reg | (chf.areas[ai2] << 16));
        }
        }
        if (rcGetCon(s, dirp) != RC_NOT_CONNECTED)
        {
        int ax = x + rcGetDirOffsetX(dirp);
        int ay = y + rcGetDirOffsetY(dirp);
        int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(s, dirp);
        rcCompactSpan aSpan = chf.spans[ai];
        ch = Math.Max(ch, (int)aSpan.y);
        regs[3] = (uint)(chf.spans[ai].reg | (chf.areas[ai] << 16));
        if (rcGetCon(aSpan, dir) != RC_NOT_CONNECTED)
        {
            int ax2 = ax + rcGetDirOffsetX(dir);
            int ay2 = ay + rcGetDirOffsetY(dir);
            int ai2 = (int)chf.cells[ax2+ay2*chf.width].index + rcGetCon(aSpan, dir);
            rcCompactSpan as2 = chf.spans[ai2];
            ch = Math.Max(ch, (int)as2.y);
            regs[2] = (uint)(chf.spans[ai2].reg | (chf.areas[ai2] << 16));
        }
        }

        // Check if the vertex is special edge vertex, these vertices will be removed later.
        for (int j = 0; j < 4; ++j)
        {
        int a = j;
        int b = (j+1) & 0x3;
        int c = (j+2) & 0x3;
        int d = (j+3) & 0x3;

        // The vertex is a border vertex there are two same exterior cells in a row,
        // followed by two interior cells and none of the regions are out of bounds.
        bool twoSameExts = (regs[a] & regs[b] & RC_BORDER_REG) != 0 && regs[a] == regs[b];
        bool twoInts = ((regs[c] | regs[d]) & RC_BORDER_REG) == 0;
        bool intsSameArea = (regs[c]>>16) == (regs[d]>>16);
        bool noZeros = regs[a] != 0 && regs[b] != 0 && regs[c] != 0 && regs[d] != 0;
        if (twoSameExts && twoInts && intsSameArea && noZeros)
        {
            isBorderVertex = true;
            break;
        }
        }

        return ch;
    }
예제 #5
0
    public static void walkContour(int x, int y, int i,
						rcCompactHeightfield chf,
						byte[] flags, List<int> points)
    {
        // Choose the first non-connected edge
        byte dir = 0;
        while ((flags[i] & (1 << dir)) == 0)
        dir++;

        byte startDir = dir;
        int starti = i;

        byte area = chf.areas[i];

        int iter = 0;
        while (++iter < 40000)
        {
        if ((flags[i] & (1 << dir)) != 0)
        {
            // Choose the edge corner
            bool isBorderVertex = false;
            bool isAreaBorder = false;
            int px = x;
            int py = getCornerHeight(x, y, i, dir, chf,ref isBorderVertex);
            int pz = y;
            switch(dir)
            {
                case 0: pz++; break;
                case 1: px++; pz++; break;
                case 2: px++; break;
            }
            int r = 0;
            rcCompactSpan s = chf.spans[i];
            if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
            {
                int ax = x + rcGetDirOffsetX(dir);
                int ay = y + rcGetDirOffsetY(dir);
                int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(s, dir);
                r = (int)chf.spans[ai].reg;
                if (area != chf.areas[ai])
                    isAreaBorder = true;
            }
            if (isBorderVertex)
                r |= RC_BORDER_VERTEX;
            if (isAreaBorder)
                r |= RC_AREA_BORDER;
            points.Add(px);
            points.Add(py);
            points.Add(pz);
            points.Add(r);

            flags[i] &= (byte)( ~(1 << dir) ); // Remove visited edges
            dir = (byte)( (dir+1) & 0x3);  // Rotate CW
        }
        else
        {
            int ni = -1;
            int nx = x + rcGetDirOffsetX(dir);
            int ny = y + rcGetDirOffsetY(dir);
            rcCompactSpan s = chf.spans[i];
            if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
            {
                rcCompactCell nc = chf.cells[nx+ny*chf.width];
                ni = (int)nc.index + rcGetCon(s, dir);
            }
            if (ni == -1)
            {
                // Should not happen.
                return;
            }
            x = nx;
            y = ny;
            i = ni;
            dir = (byte)((dir+3) & 0x3);	// Rotate CCW
        }

        if (starti == i && startDir == dir)
        {
            break;
        }
        }
    }
예제 #6
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;
    }
예제 #7
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;
    }
예제 #8
0
    static void getHeightData(rcCompactHeightfield chf,
						      ushort[] poly, int polyStart, int npoly,
						      ushort[] verts, int bs,
						      rcHeightPatch hp, List<int> stack,
						      int region)
    {
        // Note: Reads to the compact heightfield are offset by border size (bs)
        // since border size offset is already removed from the polymesh vertices.

        //stack.resize(0);
        //memset(hp.data, 0xff, sizeof(ushort)*hp.width*hp.height);
        stack.Clear();
        for (int i=0;i<hp.data.Length;++i){
            hp.data[i] = 0xffff;
        }
        bool empty = true;

        // Copy the height from the same region, and mark region borders
        // as seed points to fill the rest.
        for (int hy = 0; hy < hp.height; hy++)
        {
            int y = hp.ymin + hy + bs;
            for (int hx = 0; hx < hp.width; hx++)
            {
                int x = hp.xmin + hx + bs;
                rcCompactCell c = chf.cells[x+y*chf.width];
                for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
                {
                    rcCompactSpan s = chf.spans[i];
                    if (s.reg == region)
                    {
                        // Store height
                        hp.data[hx + hy*hp.width] = s.y;
                        empty = false;

                        // If any of the neighbours is not in same region,
                        // add the current location as flood fill start
                        bool border = false;
                        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*chf.width].index + rcGetCon(s, dir);
                                rcCompactSpan aSpan = chf.spans[ai];
                                if (aSpan.reg != region)
                                {
                                    border = true;
                                    break;
                                }
                            }
                        }
                        if (border)
                        {
                            stack.Add(x);
                            stack.Add(y);
                            stack.Add(i);
                        }
                        break;
                    }
                }
            }
        }

        // if the polygon does not contain any points from the current region (rare, but happens)
        // then use the cells closest to the polygon vertices as seeds to fill the height field
        if (empty){
            getHeightDataSeedsFromVertices(chf, poly, polyStart, npoly, verts, bs, hp, stack);
        }

        const int RETRACT_SIZE = 256;
        int head = 0;

        while (head*3 < stack.Count)
        {
            int cx = stack[head*3+0];
            int cy = stack[head*3+1];
            int ci = stack[head*3+2];
            head++;
            if (head >= RETRACT_SIZE)
            {
                head = 0;
                if (stack.Count > RETRACT_SIZE*3){
                    //memmove(&stack[0], &stack[RETRACT_SIZE*3], sizeof(int)*(stack.Count-RETRACT_SIZE*3));
                    for (int i=0;i<stack.Count - RETRACT_SIZE*3;++i){
                        stack[i] = stack[RETRACT_SIZE*3 + i];
                    }
                }
                //stack.resize(stack.Count-RETRACT_SIZE*3);
                int newSize =stack.Count - RETRACT_SIZE*3;
                Debug.Assert(newSize > 0,"Resizing under zero");
                stack.RemoveRange(newSize, stack.Count - newSize);
            }

            rcCompactSpan cs = chf.spans[ci];
            for (int dir = 0; dir < 4; ++dir)
            {
                if (rcGetCon(cs, dir) == RC_NOT_CONNECTED)
                    continue;

                int ax = cx + rcGetDirOffsetX(dir);
                int ay = cy + rcGetDirOffsetY(dir);
                int hx = ax - hp.xmin - bs;
                int hy = ay - hp.ymin - bs;

                if (hx < 0 || hx >= hp.width || hy < 0 || hy >= hp.height)
                    continue;

                if (hp.data[hx + hy*hp.width] != RC_UNSET_HEIGHT)
                    continue;

                int ai = (int)chf.cells[ax + ay*chf.width].index + rcGetCon(cs, dir);
                rcCompactSpan aSpan = chf.spans[ai];

                hp.data[hx + hy*hp.width] = aSpan.y;

                stack.Add(ax);
                stack.Add(ay);
                stack.Add(ai);
            }
        }
    }
예제 #9
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);
    }
예제 #10
0
    static ushort[] boxBlur(rcCompactHeightfield chf, int thr,
							       ushort[] src, ushort[] dst)
    {
        int w = chf.width;
        int h = chf.height;

        thr *= 2;

        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];
                    ushort cd = src[i];
                    if (cd <= thr)
                    {
                        dst[i] = cd;
                        continue;
                    }

                    int d = (int)cd;
                    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);
                            d += (int)src[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);
                                d += (int)src[ai2];
                            }
                            else
                            {
                                d += cd;
                            }
                        }
                        else
                        {
                            d += cd*2;
                        }
                    }
                    dst[i] = (ushort)((d+5)/9);
                }
            }
        }
        return dst;
    }
예제 #11
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;
    }
예제 #12
0
    static void walkContour(int x, int y, int i, int dir,
						    rcCompactHeightfield chf,
						    ushort[] srcReg,
						    List<int> cont)
    {
        int startDir = dir;
        int starti = i;

        rcCompactSpan ss = chf.spans[i];
        ushort curReg = 0;
        if (rcGetCon(ss, dir) != RC_NOT_CONNECTED)
        {
            int ax = x + rcGetDirOffsetX(dir);
            int ay = y + rcGetDirOffsetY(dir);
            int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(ss, dir);
            curReg = srcReg[ai];
        }
        cont.Add(curReg);

        int iter = 0;
        while (++iter < 40000)
        {
            rcCompactSpan s = chf.spans[i];

            if (isSolidEdge(chf, srcReg, x, y, i, dir))
            {
                // Choose the edge corner
                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*chf.width].index + rcGetCon(s, dir);
                    r = srcReg[ai];
                }
                if (r != curReg)
                {
                    curReg = r;
                    cont.Add(curReg);
                }

                dir = (dir+1) & 0x3;  // Rotate CW
            }
            else
            {
                int ni = -1;
                int nx = x + rcGetDirOffsetX(dir);
                int ny = y + rcGetDirOffsetY(dir);
                if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
                {
                    rcCompactCell nc = chf.cells[nx+ny*chf.width];
                    ni = (int)nc.index + rcGetCon(s, dir);
                }
                if (ni == -1)
                {
                    // Should not happen.
                    return;
                }
                x = nx;
                y = ny;
                i = ni;
                dir = (dir+3) & 0x3;	// Rotate CCW
            }

            if (starti == i && startDir == dir)
            {
                break;
            }
        }

        // Remove adjacent duplicates.
        if (cont.Count > 1)
        {
            for (int j = 0; j < cont.Count; )
            {
                int nj = (j+1) % cont.Count;
                if (cont[j] == cont[nj])
                {
                    for (int k = j; k < cont.Count-1; ++k)
                        cont[k] = cont[k+1];
                    rccsPop(cont);
                }
                else
                    ++j;
            }
        }
    }
예제 #13
0
    // the levels per stack (2 in our case) as a bit shift
    static void sortCellsByLevel(ushort startLevel,
							      rcCompactHeightfield chf,
							      ushort[] srcReg,
							      uint nbStacks, List<int>[] stacks,
							      ushort loglevelsPerStack)
    {
        int w = chf.width;
        int h = chf.height;
        startLevel = (ushort)(startLevel >> loglevelsPerStack);

        for (uint j=0; j<nbStacks; ++j)
            stacks[j].Clear();

        // put all cells in the level range into the appropriate stacks
        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 || srcReg[i] != 0)
                        continue;

                    int level = chf.dist[i] >> loglevelsPerStack;
                    int sId = startLevel - level;
                    if (sId >= (int)nbStacks)
                        continue;
                    if (sId < 0)
                        sId = 0;

                    stacks[sId].Add(x);
                    stacks[sId].Add(y);
                    stacks[sId].Add(i);
                }
            }
        }
    }
예제 #14
0
    static bool isSolidEdge(rcCompactHeightfield chf, ushort[] srcReg,
						    int x, int y, int i, int dir)
    {
        rcCompactSpan s = chf.spans[i];
        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*chf.width].index + rcGetCon(s, dir);
            r = srcReg[ai];
        }
        if (r == srcReg[i])
            return false;
        return true;
    }
예제 #15
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);
    }
예제 #16
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);
        }
    }
예제 #17
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;
    }
예제 #18
0
    static ushort[] expandRegions(int maxIter, ushort level,
									     rcCompactHeightfield chf,
									     ushort[] srcReg, ushort[] srcDist,
									     ushort[] dstReg, ushort[] dstDist, 
									     List<int> stack,
									     bool fillStack)
    {
        int w = chf.width;
        int h = chf.height;

        if (fillStack)
        {
            // Find cells revealed by the raised level.
            stack.Clear();
            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.dist[i] >= level && srcReg[i] == 0 && chf.areas[i] != RC_NULL_AREA)
                        {
                            stack.Add(x);
                            stack.Add(y);
                            stack.Add(i);
                        }
                    }
                }
            }
        }
        else // use cells in the input stack
        {
            // mark all cells which already have a region
            for (int j=0; j<stack.Count; j+=3)
            {
                int i = stack[j+2];
                if (srcReg[i] != 0)
                    stack[j+2] = -1;
            }
        }

        int iter = 0;
        while (stack.Count > 0)
        {
            int failed = 0;

            //memcpy(dstReg, srcReg, sizeof(ushort)*chf.spanCount);
            for (int i=0;i<chf.spanCount;++i){
                dstReg[i] = srcReg[i];
            }
            //memcpy(dstDist, srcDist, sizeof(ushort)*chf.spanCount);
            for (int i=0;i<chf.spanCount;++i){
                dstDist[i] = srcDist[i];
            }
            for (int j = 0; j < stack.Count; j += 3)
            {
                int x = stack[j+0];
                int y = stack[j+1];
                int i = stack[j+2];
                if (i < 0)
                {
                    failed++;
                    continue;
                }

                ushort r = srcReg[i];
                ushort d2 = 0xffff;
                byte area = chf.areas[i];
                rcCompactSpan s = chf.spans[i];
                for (int dir = 0; dir < 4; ++dir)
                {
                    if (rcGetCon(s, dir) == RC_NOT_CONNECTED)
                        continue;
                    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] != area) continue;
                    if (srcReg[ai] > 0 && (srcReg[ai] & RC_BORDER_REG) == 0)
                    {
                        if ((int)srcDist[ai]+2 < (int)d2)
                        {
                            r = srcReg[ai];
                            d2 = (ushort)(srcDist[ai]+2);
                        }
                    }
                }
                if (r != 0)
                {
                    stack[j+2] = -1; // mark as used
                    dstReg[i] = r;
                    dstDist[i] = d2;
                }
                else
                {
                    failed++;
                }
            }

            // rcSwap source and dest.
            rcSwap(ref srcReg, ref dstReg);
            rcSwap(ref srcDist, ref dstDist);

            if (failed*3 == stack.Count)
                break;

            if (level > 0)
            {
                ++iter;
                if (iter >= maxIter)
                    break;
            }
        }

        return srcReg;
    }
예제 #19
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;
    }
예제 #20
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;
    }
예제 #21
0
    static void getHeightDataSeedsFromVertices(rcCompactHeightfield chf,
						      ushort[] poly, int polyStart, int npoly,
						      ushort[] verts, int bs,
						      rcHeightPatch hp, List<int> stack)
    {
        // Floodfill the heightfield to get 2D height data,
        // starting at vertex locations as seeds.

        // Note: Reads to the compact heightfield are offset by border size (bs)
        // since border size offset is already removed from the polymesh vertices.

        //memset(hp.data, 0, sizeof(ushort)*hp.width*hp.height);
        for (int i=0;i<hp.data.Length;++i){
            hp.data[i] = 0;
        }

        //tack.resize(0);
        stack.Clear();

        int[] offset = new int[9*2]
        {
            0,0, -1,-1, 0,-1, 1,-1, 1,0, 1,1, 0,1, -1,1, -1,0,
        };

        // Use poly vertices as seed points for the flood fill.
        for (int j = 0; j < npoly; ++j)
        {
            int cx = 0, cz = 0, ci =-1;
            int dmin = RC_UNSET_HEIGHT;
            for (int k = 0; k < 9; ++k)
            {
                int ax = (int)verts[poly[polyStart + j]*3+0] + offset[k*2+0];
                int ay = (int)verts[poly[polyStart + j]*3+1];
                int az = (int)verts[poly[polyStart + j]*3+2] + offset[k*2+1];
                if (ax < hp.xmin || ax >= hp.xmin+hp.width ||
                    az < hp.ymin || az >= hp.ymin+hp.height)
                    continue;

                rcCompactCell c = chf.cells[(ax+bs)+(az+bs)*chf.width];
                for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
                {
                    rcCompactSpan s = chf.spans[i];
                    int d = Math.Abs(ay - (int)s.y);
                    if (d < dmin)
                    {
                        cx = ax;
                        cz = az;
                        ci = i;
                        dmin = d;
                    }
                }
            }
            if (ci != -1)
            {
                stack.Add(cx);
                stack.Add(cz);
                stack.Add(ci);
            }
        }

        // Find center of the polygon using flood fill.
        int pcx = 0, pcz = 0;
        for (int j = 0; j < npoly; ++j)
        {
            pcx += (int)verts[poly[polyStart + j]*3+0];
            pcz += (int)verts[poly[polyStart + j]*3+2];
        }
        pcx /= npoly;
        pcz /= npoly;

        for (int i = 0; i < stack.Count; i += 3)
        {
            int cx = stack[i+0];
            int cy = stack[i+1];
            int idx = cx-hp.xmin+(cy-hp.ymin)*hp.width;
            hp.data[idx] = 1;
        }

        while (stack.Count > 0)
        {
            int ci = stack[stack.Count - 1];
            stack.RemoveAt(stack.Count - 1);
            int cy = stack[stack.Count - 1];
            stack.RemoveAt(stack.Count - 1);
            int cx = stack[stack.Count - 1];
            stack.RemoveAt(stack.Count - 1);

            // Check if close to center of the polygon.
            if (Math.Abs(cx-pcx) <= 1 && Math.Abs(cy-pcz) <= 1)
            {
                //stack.resize(0);
                stack.Clear();
                stack.Add(cx);
                stack.Add(cy);
                stack.Add(ci);
                break;
            }

            rcCompactSpan cs = chf.spans[ci];

            for (int dir = 0; dir < 4; ++dir)
            {
                if (rcGetCon(cs, dir) == RC_NOT_CONNECTED)
                    continue;

                int ax = cx + rcGetDirOffsetX(dir);
                int ay = cy + rcGetDirOffsetY(dir);

                if (ax < hp.xmin || ax >= (hp.xmin+hp.width) ||
                    ay < hp.ymin || ay >= (hp.ymin+hp.height))
                    continue;

                if (hp.data[ax-hp.xmin+(ay-hp.ymin)*hp.width] != 0)
                    continue;

                int ai = (int)chf.cells[(ax+bs)+(ay+bs)*chf.width].index + rcGetCon(cs, dir);

                int idx = ax-hp.xmin+(ay-hp.ymin)*hp.width;
                hp.data[idx] = 1;

                stack.Add(ax);
                stack.Add(ay);
                stack.Add(ai);
            }
        }

        //memset(hp.data, 0xff, sizeof(ushort)*hp.width*hp.height);
        for (int i=0;i<hp.data.Length;++i){
            hp.data[i] = 0xffff;
        }

        // Mark start locations.
        for (int i = 0; i < stack.Count; i += 3)
        {
            int cx = stack[i+0];
            int cy = stack[i+1];
            int ci = stack[i+2];
            int idx = cx-hp.xmin+(cy-hp.ymin)*hp.width;
            rcCompactSpan cs = chf.spans[ci];
            hp.data[idx] = cs.y;

            // getHeightData seeds are given in coordinates with borders
            stack[i+0] += bs;
            stack[i+1] += bs;
        }
    }
예제 #22
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;
    }
예제 #23
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;
    }
예제 #24
0
    static bool floodRegion(int x, int y, int i,
						    ushort level, ushort r,
						    rcCompactHeightfield chf,
						    ushort[] srcReg, ushort[] srcDist,
						    List<int> stack)
    {
        int w = chf.width;

        byte area = chf.areas[i];

        // Flood fill mark region.
        //stack.resize(0);
        stack.Clear();
        stack.Add((int)x);
        stack.Add((int)y);
        stack.Add((int)i);
        srcReg[i] = r;
        srcDist[i] = 0;

        ushort lev = (ushort)(level >= 2 ? level-2 : 0);
        int count = 0;

        while (stack.Count > 0)
        {
            int ci = rccsPop(stack);
            int cy = rccsPop(stack);
            int cx = rccsPop(stack);

            rcCompactSpan cs = chf.spans[ci];

            // Check if any of the neighbours already have a valid region set.
            ushort ar = 0;
            for (int dir = 0; dir < 4; ++dir)
            {
                // 8 connected
                if (rcGetCon(cs, dir) != RC_NOT_CONNECTED)
                {
                    int ax = cx + rcGetDirOffsetX(dir);
                    int ay = cy + rcGetDirOffsetY(dir);
                    int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(cs, dir);
                    if (chf.areas[ai] != area)
                        continue;
                    ushort nr = srcReg[ai];
                    if ((nr & RC_BORDER_REG) != 0) // Do not take borders into account.
                        continue;
                    if (nr != 0 && nr != r)
                    {
                        ar = nr;
                        break;
                    }

                    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] != area)
                            continue;
                        ushort nr2 = srcReg[ai2];
                        if (nr2 != 0 && nr2 != r)
                        {
                            ar = nr2;
                            break;
                        }
                    }
                }
            }
            if (ar != 0)
            {
                srcReg[ci] = 0;
                continue;
            }
            count++;

            // Expand neighbours.
            for (int dir = 0; dir < 4; ++dir)
            {
                if (rcGetCon(cs, dir) != RC_NOT_CONNECTED)
                {
                    int ax = cx + rcGetDirOffsetX(dir);
                    int ay = cy + rcGetDirOffsetY(dir);
                    int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(cs, dir);
                    if (chf.areas[ai] != area)
                        continue;
                    if (chf.dist[ai] >= lev && srcReg[ai] == 0)
                    {
                        srcReg[ai] = r;
                        srcDist[ai] = 0;
                        stack.Add(ax);
                        stack.Add(ay);
                        stack.Add(ai);
                    }
                }
            }
        }

        return count > 0;
    }