Example #1
0
        private bool BuildPolyDetail(float[] inArray, int nin, float sampleDist, float sampleMaxError, CompactHeightfield chf, HeightPatch hp, ref float[] verts, out int nverts, ref IntArray tris, ref IntArray edges, ref IntArray samples)
        {
            int MaxVerts        = 127;
            int MaxTris         = 255;
            int MaxVertsPerEdge = 32;

            float[] edge  = new float[(MaxVertsPerEdge + 1) * 3];
            int[]   hull  = new int[MaxVerts];
            int     nhull = 0;

            nverts = 0;

            for (int i = 0; i < nin; i++)
            {
                Array.Copy(inArray, i * 3, verts, i * 3, 3);
            }

            nverts = nin;

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

            if (sampleDist > 0)
            {
                for (int i = 0, j = nin - 1; i < nin; j = i++)
                {
                    int vj = j * 3;
                    int vi = i * 3;

                    bool swapped = false;
                    if (Math.Abs(inArray[vj + 0] - inArray[vi + 0]) < 1e-6f)
                    {
                        if (inArray[vj + 2] > inArray[vi + 2])
                        {
                            int temp = vj;
                            vj      = vi;
                            vi      = temp;
                            swapped = true;
                        }
                    }
                    else
                    {
                        if (inArray[vj + 0] > inArray[vi + 0])
                        {
                            int temp = vj;
                            vj      = vi;
                            vi      = temp;
                            swapped = true;
                        }
                    }

                    float dx = inArray[vi + 0] - inArray[vj + 0];
                    float dy = inArray[vi + 1] - inArray[vj + 1];
                    float dz = inArray[vi + 2] - inArray[vj + 2];
                    float d  = (float)Math.Sqrt(dx * dx + dz * dz);
                    int   nn = 1 + (int)Math.Floor(d / sampleDist);
                    if (nn >= MaxVertsPerEdge)
                    {
                        nn = MaxVertsPerEdge - 1;
                    }
                    if (nverts + nn >= MaxVerts)
                    {
                        nn = MaxVerts - 1 - nverts;
                    }

                    for (int k = 0; k <= nn; k++)
                    {
                        float u   = (float)k / (float)nn;
                        int   pos = k * 3;
                        edge[pos + 0] = inArray[vj + 0] + dx * u;
                        edge[pos + 1] = inArray[vj + 1] + dy * u;
                        edge[pos + 2] = inArray[vj + 2] + dz * u;
                        edge[pos + 1] = GetHeight(edge[pos + 0], edge[pos + 1], edge[pos + 2], cs, ics, chf.Ch, hp) * chf.Ch;
                    }

                    int[] idx = new int[MaxVertsPerEdge];
                    idx[0] = 0; idx[1] = nn;
                    int nidx = 2;
                    for (int k = 0; k < nidx - 1;)
                    {
                        int a  = idx[k];
                        int b  = idx[k + 1];
                        int va = a * 3;
                        int vb = b * 3;

                        float maxd = 0;
                        int   maxi = -1;
                        for (int m = a + 1; m < b; m++)
                        {
                            float dev = DistancePtSeg(edge[m * 3 + 0], edge[m * 3 + 1], edge[m * 3 + 2], edge[va + 0], edge[va + 1], edge[va + 2], edge[vb + 0], edge[vb + 1], edge[vb + 2]);
                            if (dev > maxd)
                            {
                                maxd = dev;
                                maxi = m;
                            }
                        }

                        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;
                    if (swapped)
                    {
                        for (int k = nidx - 2; k > 0; k--)
                        {
                            Array.Copy(edge, idx[k] * 3, verts, nverts * 3, 3);
                            hull[nhull++] = nverts;
                            nverts++;
                        }
                    }
                    else
                    {
                        for (int k = 1; k < nidx - 1; k++)
                        {
                            Array.Copy(edge, idx[k] * 3, verts, nverts * 3, 3);
                            hull[nhull++] = nverts;
                            nverts++;
                        }
                    }
                }
            }

            edges.Resize(0);
            tris.Resize(0);

            DelaunayHull(nverts, verts, nhull, hull, ref tris, ref edges);

            if (tris.Size == 0)
            {
                // error add default data
                for (int i = 2; i < nverts; i++)
                {
                    tris.Push(0);
                    tris.Push(i - 1);
                    tris.Push(i);
                    tris.Push(0);
                }
                return(true);
            }

            if (sampleDist > 0)
            {
                float[] bmin = new float[3], bmax = new float[3];
                Array.Copy(inArray, 0, bmin, 0, 3);
                Array.Copy(inArray, 0, bmax, 0, 3);

                for (int i = 1; i < nin; i++)
                {
                    bmin[0] = Math.Min(bmin[0], inArray[i * 3 + 0]);
                    bmin[1] = Math.Min(bmin[1], inArray[i * 3 + 1]);
                    bmin[2] = Math.Min(bmin[2], inArray[i * 3 + 2]);
                    bmax[0] = Math.Max(bmax[0], inArray[i * 3 + 0]);
                    bmax[1] = Math.Max(bmax[1], inArray[i * 3 + 1]);
                    bmax[2] = Math.Max(bmax[2], inArray[i * 3 + 2]);
                }

                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);
                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;

                        if (DistToPoly(nin, inArray, pt[0], pt[1], pt[2]) > -sampleDist / 2)
                        {
                            continue;
                        }
                        samples.Push(x);
                        samples.Push(GetHeight(pt[0], pt[1], pt[2], cs, ics, chf.Ch, hp));
                        samples.Push(z);
                        samples.Push(0);
                    }
                }

                int nsamples = samples.Size / 4;
                for (int iter = 0; iter < nsamples; iter++)
                {
                    if (nverts >= MaxVerts)
                    {
                        break;
                    }

                    float[] bestpt = { 0, 0, 0 };
                    float   bestd  = 0;
                    int     besti  = -1;
                    for (int i = 0; i < nsamples; i++)
                    {
                        int s = i * 4;
                        if (samples[s + 3] != 0)
                        {
                            continue;
                        }
                        float[] pt = new float[3];

                        pt[0] = samples[s + 0] * sampleDist + GetJitterX(i) * cs * 0.1f;
                        pt[1] = samples[s + 1] * chf.Ch;
                        pt[2] = samples[s + 2] * sampleDist + GetJitterY(i) * cs * 0.1f;
                        float d = DistToTriMesh(pt[0], pt[1], pt[2], verts, nverts, tris, tris.Size / 4);
                        if (d < 0)
                        {
                            continue;        // didn't hit the mesh
                        }
                        if (d > bestd)
                        {
                            bestd = d;
                            besti = i;
                            Array.Copy(pt, 0, bestpt, 0, 3);
                        }
                    }
                    if (bestd <= sampleMaxError || besti == -1)
                    {
                        break;
                    }
                    samples[besti * 4 + 3] = 1;
                    Array.Copy(bestpt, 0, verts, nverts * 3, 3);
                    nverts++;

                    edges.Resize(0);
                    tris.Resize(0);
                    DelaunayHull(nverts, verts, nhull, hull, ref tris, ref edges);
                }
            }

            int ntris = tris.Size / 4;

            if (ntris > MaxTris)
            {
                // error, shrink
                tris.Resize(MaxTris * 4);
            }
            return(true);
        }
Example #2
0
        private void GetHeightData(CompactHeightfield chf, int[] p, int npoly, int[] verts, int bs, ref HeightPatch hp, ref IntArray stack)
        {
            for (int i = 0; i < hp.Width * hp.Height; i++)
            {
                hp.Data[i] = 0;
            }

            stack.Resize(0);

            int[] offset = { 0, 0, -1, -1, 0, -1, 1, -1, 1, 0, 1, 1, 0, 1, -1, 1, -1, 0 };

            for (int j = 0; j < npoly; j++)
            {
                int cx = 0, cz = 0, ci = -1;

                int dmin = UnsetHeight;
                for (int k = 0; k < 9; k++)
                {
                    int ax = verts[p[j] * 3 + 0] + offset[k * 2 + 0];
                    int ay = verts[p[j] * 3 + 1];
                    int az = verts[p[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;
                    }

                    CompactCell c = chf.Cells[(ax + bs) + (az + bs) * chf.Width];
                    for (int i = (int)c.Index, ni = (int)(c.Index + c.Count); i < ni; i++)
                    {
                        CompactSpan s = chf.Spans[i];
                        int         d = Math.Abs(ay - s.Y);
                        if (d < dmin)
                        {
                            cx   = ax;
                            cz   = az;
                            ci   = i;
                            dmin = d;
                        }
                    }
                }
                if (ci != -1)
                {
                    stack.Push(cx);
                    stack.Push(cz);
                    stack.Push(ci);
                }
            }

            int pcx = 0, pcz = 0;

            for (int j = 0; j < npoly; j++)
            {
                pcx += verts[p[j] * 3 + 0];
                pcz += verts[p[j] * 3 + 2];
            }
            pcx /= npoly;
            pcz /= npoly;

            for (int i = 0; i < stack.Size; 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.Size > 0)
            {
                int ci = stack.Pop();
                int cy = stack.Pop();
                int cx = stack.Pop();

                if (Math.Abs(cx - pcx) <= 1 && Math.Abs(cy - pcz) <= 1)
                {
                    stack.Resize(0);
                    stack.Push(cx);
                    stack.Push(cy);
                    stack.Push(ci);
                    break;
                }

                CompactSpan cs = chf.Spans[ci];

                for (int dir = 0; dir < 4; dir++)
                {
                    if (cs.GetCon(dir) == CompactHeightfield.NotConnected)
                    {
                        continue;
                    }

                    int ax = cx + Helper.GetDirOffsetX(dir);
                    int ay = cy + Helper.GetDirOffsetY(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 + cs.GetCon(dir);

                    int idx = ax - hp.XMin + (ay - hp.YMin) * hp.Width;
                    hp.Data[idx] = 1;

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

            for (int i = 0; i < hp.Data.Length; i++)
            {
                hp.Data[i] = UnsetHeight;
            }

            for (int i = 0; i < stack.Size; 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;
                CompactSpan cs  = chf.Spans[ci];
                hp.Data[idx] = cs.Y;
            }

            int RetractSize = 256;
            int head        = 0;

            while (head * 3 < stack.Size)
            {
                int cx = stack[head * 3 + 0];
                int cy = stack[head * 3 + 1];
                int ci = stack[head * 3 + 2];
                head++;
                if (head >= RetractSize)
                {
                    head = 0;
                    if (stack.Size > RetractSize * 3)
                    {
                        Array.Copy(stack.Data, RetractSize * 3, stack.Data, 0, stack.Size - RetractSize * 3);
                    }
                    stack.Resize(stack.Size - RetractSize * 3);
                }

                CompactSpan cs = chf.Spans[ci];
                for (int dir = 0; dir < 4; dir++)
                {
                    if (cs.GetCon(dir) == CompactHeightfield.NotConnected)
                    {
                        continue;
                    }

                    int ax = cx + Helper.GetDirOffsetX(dir);
                    int ay = cy + Helper.GetDirOffsetY(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] != UnsetHeight)
                    {
                        continue;
                    }

                    int ai = (int)chf.Cells[(ax + bs) + (ay + bs) * chf.Width].Index + cs.GetCon(dir);

                    CompactSpan aspan = chf.Spans[ai];
                    int         idx   = ax - hp.XMin + (ay - hp.YMin) * hp.Width;
                    hp.Data[idx] = aspan.Y;

                    stack.Push(ax);
                    stack.Push(ay);
                    stack.Push(ai);
                }
            }
        }
Example #3
0
        public DetailPolyMesh(PolyMesh mesh, CompactHeightfield chf, float sampleDist, float sampleMaxError)
        {
            if (mesh.NVerts == 0 || mesh.NPolys == 0)
            {
                return;
            }

            int   nvp = mesh.Nvp;
            float cs  = mesh.Cs;
            float ch  = mesh.Ch;

            float[] orig       = mesh.BMin;
            int     borderSize = mesh.BorderSize;

            IntArray edges   = new IntArray(64);
            IntArray tris    = new IntArray(512);
            IntArray stack   = new IntArray(512);
            IntArray samples = new IntArray(512);

            float[]     verts = new float[256 * 3];
            HeightPatch hp = new HeightPatch();
            int         nPolyVerts = 0;
            int         maxhw = 0, maxhh = 0;

            int[]   bounds = new int[mesh.NPolys * 4];
            float[] poly   = new float[nvp * 3];

            for (int i = 0; i < mesh.NPolys; i++)
            {
                int p    = i * nvp * 2;
                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[p + j] == PolyMesh.MeshNullIdx)
                    {
                        break;
                    }
                    int v = mesh.Polys[p + j] * 3;
                    bounds[xmin] = Math.Min(bounds[xmin], mesh.Verts[v + 0]);
                    bounds[xmax] = Math.Max(bounds[xmax], mesh.Verts[v + 0]);
                    bounds[ymin] = Math.Min(bounds[ymin], mesh.Verts[v + 2]);
                    bounds[ymax] = Math.Max(bounds[ymax], mesh.Verts[v + 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 = new int[maxhw * maxhh];

            NMeshes = mesh.NPolys;
            //NVerts = 0;
            //NTris = 0;
            Meshes = new long[NMeshes * 4];

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

            NVerts = 0;
            Verts  = new float[vcap * 3];
            NTris  = 0;
            Tris   = new short[tcap * 4];
            int nverts;

            for (int i = 0; i < mesh.NPolys; i++)
            {
                int p     = i * nvp * 2;
                int npoly = 0;
                for (int j = 0; j < nvp; j++)
                {
                    if (mesh.Polys[p + j] == PolyMesh.MeshNullIdx)
                    {
                        break;
                    }
                    int v = mesh.Polys[p + j] * 3;
                    poly[j * 3 + 0] = mesh.Verts[v + 0] * cs;
                    poly[j * 3 + 1] = mesh.Verts[v + 1] * ch;
                    poly[j * 3 + 2] = mesh.Verts[v + 2] * cs;
                    npoly++;
                }

                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];

                int[] tempPoly = new int[nvp];
                Array.Copy(mesh.Polys, p, tempPoly, 0, nvp);

                GetHeightData(chf, tempPoly, npoly, mesh.Verts, borderSize, ref hp, ref stack);

                if (!BuildPolyDetail(poly, npoly, sampleDist, sampleMaxError, chf, hp, ref verts, out nverts, ref tris, ref edges, ref samples))
                {
                    return;
                }

                for (int j = 0; j < nverts; j++)
                {
                    verts[j * 3 + 0] += orig[0];
                    verts[j * 3 + 1] += orig[1] + chf.Ch;
                    verts[j * 3 + 2] += orig[2];
                }
                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];
                }

                int ntris = tris.Size / 4;

                Meshes[i * 4 + 0] = NVerts;
                Meshes[i * 4 + 1] = nverts;
                Meshes[i * 4 + 2] = NTris;
                Meshes[i * 4 + 3] = ntris;

                if (NVerts + nverts > vcap)
                {
                    while (NVerts + nverts > vcap)
                    {
                        vcap += 256;
                    }

                    float[] newv = new float[vcap * 3];
                    if (NVerts > 0)
                    {
                        Array.Copy(Verts, newv, 3 * NVerts);
                    }

                    Verts = newv;
                }

                for (int j = 0; j < nverts; j++)
                {
                    Verts[NVerts * 3 + 0] = verts[j * 3 + 0];
                    Verts[NVerts * 3 + 1] = verts[j * 3 + 1];
                    Verts[NVerts * 3 + 2] = verts[j * 3 + 2];
                    NVerts++;
                }

                if (NTris + ntris > tcap)
                {
                    while (NTris + ntris > tcap)
                    {
                        tcap += 256;
                    }
                    short[] newt = new short[tcap * 4];
                    if (NTris > 0)
                    {
                        Array.Copy(Tris, newt, 4 * NTris);
                    }

                    Tris = newt;
                }
                for (int j = 0; j < ntris; j++)
                {
                    int t = j * 4;
                    Tris[NTris * 4 + 0] = (short)tris[t + 0];
                    Tris[NTris * 4 + 1] = (short)tris[t + 1];
                    Tris[NTris * 4 + 2] = (short)tris[t + 2];
                    Tris[NTris * 4 + 3] = GetTriFlags(verts, tris[t + 0] * 3, verts, tris[t + 1] * 3, verts, tris[t + 2] * 3, poly, npoly);
                    NTris++;
                }
            }
        }
Example #4
0
        private int GetHeight(float fx, float fy, float fz, float cs, float ics, float ch, HeightPatch hp)
        {
            int ix = (int)Math.Floor(fx * ics + 0.01f);
            int iz = (int)Math.Floor(fz * ics + 0.01f);

            ix = Math.Max(0, Math.Min(hp.Width, ix - hp.XMin));
            iz = Math.Max(0, Math.Min(hp.Height, iz - hp.YMin));
            int h = hp.Data[ix + iz * hp.Width];

            if (h == UnsetHeight)
            {
                int[] offset = { -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 + offset[i * 2 + 0];
                    int nz = iz + offset[i * 2 + 1];
                    if (nx < 0 || nz < 0 || nx >= hp.Width || nz >= hp.Height)
                    {
                        continue;
                    }
                    int nh = hp.Data[nx + nz * hp.Width];
                    if (nh == UnsetHeight)
                    {
                        continue;
                    }

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