Пример #1
0
        private int GetCornerHeight(int x, int y, int i, int dir, CompactHeightfield cfh, ref bool isBorderVertex)
        {
            CompactSpan s    = cfh.Spans[i];
            int         ch   = s.Y;
            int         dirp = (dir + 1) & 0x3;

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

            regs[0] = (uint)(cfh.Spans[i].Reg | (cfh.Areas[i] << 16));

            if (s.GetCon(dir) != CompactHeightfield.NotConnected)
            {
                int         ax    = x + Helper.GetDirOffsetX(dir);
                int         ay    = y + Helper.GetDirOffsetY(dir);
                int         ai    = (int)cfh.Cells[ax + ay * cfh.Width].Index + s.GetCon(dir);
                CompactSpan aspan = cfh.Spans[ai];
                ch      = Math.Max(ch, aspan.Y);
                regs[1] = (uint)(cfh.Spans[ai].Reg | (cfh.Areas[ai] << 16));
                if (aspan.GetCon(dirp) != CompactHeightfield.NotConnected)
                {
                    int         ax2 = ax + Helper.GetDirOffsetX(dirp);
                    int         ay2 = ay + Helper.GetDirOffsetY(dirp);
                    int         ai2 = (int)cfh.Cells[ax2 + ay2 * cfh.Width].Index + aspan.GetCon(dirp);
                    CompactSpan as2 = cfh.Spans[ai2];
                    ch      = Math.Max(ch, as2.Y);
                    regs[2] = (uint)(cfh.Spans[ai2].Reg | (cfh.Areas[ai2] << 16));
                }
            }
            if (s.GetCon(dirp) != CompactHeightfield.NotConnected)
            {
                int         ax    = x + Helper.GetDirOffsetX(dirp);
                int         ay    = y + Helper.GetDirOffsetY(dirp);
                int         ai    = (int)cfh.Cells[ax + ay * cfh.Width].Index + s.GetCon(dirp);
                CompactSpan aspan = cfh.Spans[ai];
                ch      = Math.Max(ch, aspan.Y);
                regs[3] = (uint)(cfh.Spans[ai].Reg | (cfh.Areas[ai] << 16));
                if (aspan.GetCon(dir) != CompactHeightfield.NotConnected)
                {
                    int         ax2 = ax + Helper.GetDirOffsetX(dir);
                    int         ay2 = ay + Helper.GetDirOffsetY(dir);
                    int         ai2 = (int)cfh.Cells[ax2 + ay2 * cfh.Width].Index + aspan.GetCon(dir);
                    CompactSpan as2 = cfh.Spans[ai2];
                    ch      = Math.Max(ch, as2.Y);
                    regs[2] = (uint)(cfh.Spans[ai2].Reg | (cfh.Areas[ai2] << 16));
                }
            }

            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;

                bool twoSameExts  = (regs[a] & regs[b] & CompactHeightfield.BorderReg) != 0 && regs[a] == regs[b];
                bool twoInts      = ((regs[c] | regs[d]) & CompactHeightfield.BorderReg) == 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);
        }
Пример #2
0
        public ContourSet(CompactHeightfield cfh, float maxError, int maxEdgeLen, int buildFlags = BuildContourFlags.ContourTessWallEdges)
        {
            int w          = cfh.Width;
            int h          = cfh.Height;
            int borderSize = cfh.BorderSize;

            BMin = new float[3];
            BMax = new float[3];
            Array.Copy(cfh.BMin, BMin, 3);
            Array.Copy(cfh.BMax, BMax, 3);

            if (borderSize > 0)
            {
                float pad = borderSize * cfh.Cs;
                BMin[0] += pad;
                BMin[2] += pad;
                BMax[0] -= pad;
                BMax[2] -= pad;
            }

            Cs         = cfh.Cs;
            Ch         = cfh.Ch;
            Width      = cfh.Width - cfh.BorderSize * 2;
            Height     = cfh.Height - cfh.BorderSize * 2;
            BorderSize = cfh.BorderSize;
            int maxContours = Math.Max(cfh.MaxRegions, 8);

            Conts = new Contour[maxContours];
            for (int i = 0; i < maxContours; i++)
            {
                Conts[i] = new Contour();
            }
            NConts = 0;

            char[] flags = new char[cfh.SpanCount];

            for (int y = 0; y < h; y++)
            {
                for (int x = 0; x < w; x++)
                {
                    CompactCell c = cfh.Cells[x + y * w];
                    for (int i = (int)c.Index, ni = (int)(c.Index + c.Count); i < ni; i++)
                    {
                        if (i == 4782)
                        {
                            int z = 0;
                        }
                        int         res = 0;
                        CompactSpan s   = cfh.Spans[i];
                        if (s.Reg == 0 || (s.Reg & CompactHeightfield.BorderReg) != 0)
                        {
                            flags[i] = (char)0;
                            continue;
                        }
                        for (int dir = 0; dir < 4; dir++)
                        {
                            int r = 0;
                            if (s.GetCon(dir) != CompactHeightfield.NotConnected)
                            {
                                int ax = x + Helper.GetDirOffsetX(dir);
                                int ay = y + Helper.GetDirOffsetY(dir);
                                int ai = (int)cfh.Cells[ax + ay * w].Index + s.GetCon(dir);
                                r = cfh.Spans[ai].Reg;
                            }
                            if (r == cfh.Spans[i].Reg)
                            {
                                res |= (1 << dir);
                            }
                        }
                        flags[i] = (char)(res ^ 0xf);
                    }
                }
            }

            IntArray verts      = new IntArray(256);
            IntArray simplified = new IntArray(64);

            for (int y = 0; y < h; y++)
            {
                for (int x = 0; x < w; x++)
                {
                    CompactCell c = cfh.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] = (char)0;
                            continue;
                        }

                        int reg = cfh.Spans[i].Reg;
                        if (reg == 0 || (reg & CompactHeightfield.BorderReg) != 0)
                        {
                            continue;
                        }
                        uint area = cfh.Areas[i];

                        verts.Resize(0);
                        simplified.Resize(0);

                        WalkContour(x, y, i, cfh, ref flags, ref verts);

                        SimplifyContour(ref verts, ref simplified, maxError, maxEdgeLen, buildFlags);
                        RemoveDegenerateSegments(ref simplified);

                        if ((simplified.Size / 4) >= 3)
                        {
                            // We need more space than we allocated...
                            if (NConts >= maxContours)
                            {
                                int oldMax = maxContours;
                                maxContours *= 2;
                                Contour[] newConts = new Contour[maxContours];
                                for (int j = 0; j < maxContours; j++)
                                {
                                    newConts[j] = new Contour();
                                }
                                for (int j = 0; j < NConts; j++)
                                {
                                    newConts[j] = Conts[j];
                                }
                                Conts = newConts;
                            }
                            Contour cont = Conts[NConts++];
                            cont.NVerts = simplified.Size / 4;
                            cont.Verts  = new int[cont.NVerts * 4];
                            Array.Copy(simplified.ToArray(), cont.Verts, cont.NVerts * 4);
                            if (borderSize > 0)
                            {
                                for (int j = 0; j < cont.NVerts; j++)
                                {
                                    int v = j * 4;
                                    cont.Verts[v + 0] -= borderSize;
                                    cont.Verts[v + 2] -= borderSize;
                                }
                            }

                            cont.NRVerts = verts.Size / 4;
                            cont.RVerts  = new int[cont.NRVerts * 4];
                            Array.Copy(verts.ToArray(), cont.RVerts, cont.NRVerts * 4);
                            if (borderSize > 0)
                            {
                                for (int j = 0; j < cont.NRVerts; j++)
                                {
                                    int v = j * 4;
                                    cont.RVerts[v + 0] -= borderSize;
                                    cont.RVerts[v + 2] -= borderSize;
                                }
                            }

                            cont.Reg  = reg;
                            cont.Area = (short)area;
                        }
                    }
                }
            }

            // check and merge droppings
            for (int i = 0; i < NConts; i++)
            {
                Contour cont = Conts[i];
                if (CalcAreaOfPolygon2D(cont.Verts, cont.NVerts) < 0)
                {
                    int mergeIdx = -1;
                    for (int j = 0; j < NConts; j++)
                    {
                        if (i == j)
                        {
                            continue;
                        }
                        if (Conts[j].NVerts > 0 && Conts[j].Reg == cont.Reg)
                        {
                            if (CalcAreaOfPolygon2D(Conts[j].Verts, Conts[j].NVerts) > 0)
                            {
                                mergeIdx = j;
                                break;
                            }
                        }
                    }
                    if (mergeIdx == -1)
                    {
                        // error
                    }
                    else
                    {
                        Contour mcont = Conts[mergeIdx];
                        int     ia = 0, ib = 0;
                        GetClosestIndices(mcont.Verts, mcont.NVerts, cont.Verts, cont.NVerts, ref ia, ref ib);
                        if (ia == -1 || ib == -1)
                        {
                            // bad merge
                            continue;
                        }
                        if (!MergeContours(ref mcont, ref cont, ia, ib))
                        {
                            // Merge failed
                            continue;
                        }
                    }
                }
            }
        }
Пример #3
0
        private void WalkContour(int x, int y, int i, CompactHeightfield cfh, ref char[] flags, ref IntArray points)
        {
            char dir = (char)0;

            while ((flags[i] & (1 << dir)) == 0)
            {
                dir++;
            }

            char startDir = dir;
            char tempDir  = dir;
            int  starti   = i;

            uint area = cfh.Areas[i];
            int  iter = 0;

            while (++iter < 40000)
            {
                if ((flags[i] & (1 << dir)) > 0)
                {
                    bool isBorderVertex = false;
                    bool isAreaBorder   = false;
                    int  px             = x;
                    int  py             = GetCornerHeight(x, y, i, tempDir, cfh, ref isBorderVertex);
                    int  pz             = y;

                    if (dir == (char)0)
                    {
                        pz++;
                    }
                    else if (dir == (char)1)
                    {
                        px++;
                        pz++;
                    }
                    else if (dir == (char)2)
                    {
                        px++;
                    }

                    int         r = 0;
                    CompactSpan s = cfh.Spans[i];
                    if (s.GetCon(dir) != CompactHeightfield.NotConnected)
                    {
                        int ax = x + Helper.GetDirOffsetX(dir);
                        int ay = y + Helper.GetDirOffsetY(dir);
                        int ai = (int)cfh.Cells[ax + ay * cfh.Width].Index + s.GetCon(dir);
                        r = cfh.Spans[ai].Reg;
                        if (area != cfh.Areas[ai])
                        {
                            isAreaBorder = true;
                        }
                    }
                    if (isBorderVertex)
                    {
                        r |= BorderVertex;
                    }
                    if (isAreaBorder)
                    {
                        r |= AreaBorder;
                    }

                    points.Push(px);
                    points.Push(py);
                    points.Push(pz);
                    points.Push(r);

                    flags[i] &= (char)~(1 << dir);
                    dir       = (char)((dir + 1) & 0x3); // rotate CW
                }
                else
                {
                    int         ni = -1;
                    int         nx = x + Helper.GetDirOffsetX(dir);
                    int         ny = y + Helper.GetDirOffsetY(dir);
                    CompactSpan s  = cfh.Spans[i];
                    if (s.GetCon(dir) != CompactHeightfield.NotConnected)
                    {
                        CompactCell nc = cfh.Cells[nx + ny * cfh.Width];
                        ni = (int)nc.Index + s.GetCon(dir);
                    }
                    if (ni == -1)
                    {
                        // error
                        return;
                    }
                    x   = nx;
                    y   = ny;
                    i   = ni;
                    dir = (char)((dir + 3) & 0x3);
                }
                if (starti == i && startDir == dir)
                {
                    break;
                }
            }
        }