Example #1
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;
    }
Example #2
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;
    }
Example #3
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);
            }
        }
    }
Example #4
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;
        }
    }
Example #5
0
    static ushort getHeight(float fx, float fy, float fz,
								    float cs, float ics, float ch,
								    rcHeightPatch hp)
    {
        int ix = (int)Math.Floor(fx*ics + 0.01f);
        int iz = (int)Math.Floor(fz*ics + 0.01f);
        ix = rcClamp(ix-hp.xmin, 0, hp.width - 1);
        iz = rcClamp(iz-hp.ymin, 0, hp.height - 1);
        ushort h = hp.data[ix+iz*hp.width];
        if (h == RC_UNSET_HEIGHT)
        {
            // Special case when data might be bad.
            // Find nearest neighbour pixel which has valid height.
            int[] off/*[8*2]*/ = new int[] { -1,0, -1,-1, 0,-1, 1,-1, 1,0, 1,1, 0,1, -1,1};
            float dmin = float.MaxValue;
            for (int i = 0; i < 8; ++i)
            {
                int nx = ix+off[i*2+0];
                int nz = iz+off[i*2+1];
                if (nx < 0 || nz < 0 || nx >= hp.width || nz >= hp.height) {
                    continue;
                }
                ushort nh = hp.data[nx+nz*hp.width];
                if (nh == RC_UNSET_HEIGHT){
                    continue;
                }

                float d = Math.Abs(nh*ch - fy);
                if (d < dmin)
                {
                    h = nh;
                    dmin = d;
                }

        /*			const float dx = (nx+0.5f)*cs - fx;
                const float dz = (nz+0.5f)*cs - fz;
                const float d = dx*dx+dz*dz;
                if (d < dmin)
                {
                    h = nh;
                    dmin = d;
                } */
            }
        }
        return h;
    }