コード例 #1
0
        private bool FilterSmallRegions(int minRegionArea, int mergeRegionArea, ref int[] srcReg)
        {
            int w = Width;
            int h = Height;

            int nreg = MaxRegions + 1;

            Region[] regions = new Region[nreg];

            for (int i = 0; i < nreg; i++)
            {
                regions[i] = new Region(i);
            }

            for (int y = 0; y < h; y++)
            {
                for (int x = 0; x < w; x++)
                {
                    CompactCell c = Cells[x + y * w];
                    for (int i = (int)c.Index, ni = (int)(c.Index + c.Count); i < ni; i++)
                    {
                        int r = srcReg[i];
                        if (r <= 0 || r >= nreg)
                        {
                            continue;
                        }
                        Region reg = regions[r];
                        reg.SpanCount++;

                        for (int j = (int)c.Index; j < ni; j++)
                        {
                            if (i == j)
                            {
                                continue;
                            }
                            int floorId = srcReg[j];
                            if (floorId <= 0 || floorId >= nreg)
                            {
                                continue;
                            }
                            AddUniqueFloorRegion(ref reg, floorId);
                        }

                        if (reg.Connections.Size > 0)
                        {
                            continue;
                        }

                        reg.AreaType = (short)Areas[i];

                        int ndir = -1;
                        for (int dir = 0; dir < 4; dir++)
                        {
                            if (IsSolidEdge(srcReg, x, y, i, dir))
                            {
                                ndir = dir;
                                break;
                            }
                        }

                        if (ndir != -1)
                        {
                            WalkContour(x, y, i, ndir, ref srcReg, ref reg);
                        }
                    }
                }
            }

            // remove too small regions.
            IntArray stack = new IntArray(32);
            IntArray trace = new IntArray(32);

            for (int i = 0; i < nreg; i++)
            {
                Region reg = regions[i];
                if (reg.Id <= 0 || (reg.Id & BorderReg) != 0)
                {
                    continue;
                }
                if (reg.SpanCount == 0)
                {
                    continue;
                }
                if (reg.Visited)
                {
                    continue;
                }

                bool connectsToBorder = false;
                int  spanCount        = 0;
                stack.Resize(0);
                trace.Resize(0);

                reg.Visited = true;
                stack.Push(i);

                while (stack.Size > 0)
                {
                    int ri = stack.Pop();

                    Region creg = regions[ri];
                    spanCount += creg.SpanCount;
                    trace.Push(ri);

                    for (int j = 0; j < creg.Connections.Size; j++)
                    {
                        if ((creg.Connections[j] & BorderReg) != 0)
                        {
                            connectsToBorder = true;
                            continue;
                        }
                        Region neireg = regions[creg.Connections[j]];
                        if (neireg.Visited)
                        {
                            continue;
                        }
                        if (neireg.Id <= 0 || (neireg.Id & BorderReg) != 0)
                        {
                            continue;
                        }
                        stack.Push(neireg.Id);
                        neireg.Visited = true;
                    }
                }

                // if the region's size is too small, remove it.
                if (spanCount < minRegionArea && !connectsToBorder)
                {
                    for (int j = 0; j < trace.Size; j++)
                    {
                        regions[trace[j]].SpanCount = 0;
                        regions[trace[j]].Id        = 0;
                    }
                }
            }

            int mergeCount = 0;

            do
            {
                mergeCount = 0;
                for (int i = 0; i < nreg; i++)
                {
                    Region reg = regions[i];
                    if (reg.Id <= 0 || (reg.Id & BorderReg) != 0)
                    {
                        continue;
                    }
                    if (reg.SpanCount == 0)
                    {
                        continue;
                    }

                    if (reg.SpanCount > mergeRegionArea && IsRegionConnectedToBorder(reg))
                    {
                        continue;
                    }

                    // small region with more than 1 connection
                    // or region which is not connected to a border at all.
                    // find smallest neighbor
                    int smallest = int.MaxValue;
                    int mergeId  = reg.Id;
                    for (int j = 0; j < reg.Connections.Size; j++)
                    {
                        if ((reg.Connections[j] & BorderReg) != 0)
                        {
                            continue;
                        }
                        Region mreg = regions[reg.Connections[j]];
                        if (mreg.Id <= 0 || (mreg.Id & BorderReg) != 0)
                        {
                            continue;
                        }
                        if (mreg.SpanCount < smallest && CanMergeWithRegion(reg, mreg) && CanMergeWithRegion(mreg, reg))
                        {
                            smallest = mreg.SpanCount;
                            mergeId  = mreg.Id;
                        }
                    }
                    if (mergeId != reg.Id)
                    {
                        int    oldId  = reg.Id;
                        Region target = regions[mergeId];

                        if (MergeRegions(ref target, ref reg))
                        {
                            for (int j = 0; j < nreg; j++)
                            {
                                if (regions[j].Id == 0 || (regions[j].Id & BorderReg) != 0)
                                {
                                    continue;
                                }
                                if (regions[j].Id == oldId)
                                {
                                    regions[j].Id = mergeId;
                                }
                                Region reg2 = regions[j];
                                ReplaceNeighbor(ref reg2, oldId, mergeId);
                            }
                            mergeCount++;
                        }
                    }
                }
            } while (mergeCount > 0);

            // compress regions Ids
            for (int i = 0; i < nreg; i++)
            {
                regions[i].Remap = false;
                if (regions[i].Id == 0 || (regions[i].Id & BorderReg) != 0)
                {
                    continue;
                }
                regions[i].Remap = true;
            }

            int regIdGen = 0;

            for (int i = 0; i < nreg; i++)
            {
                if (!regions[i].Remap)
                {
                    continue;
                }

                int oldId = regions[i].Id;
                int newId = ++regIdGen;
                for (int j = i; j < nreg; j++)
                {
                    if (regions[j].Id == oldId)
                    {
                        regions[j].Id    = newId;
                        regions[j].Remap = false;
                    }
                }
            }

            MaxRegions = regIdGen;

            // remap regions
            for (int i = 0; i < SpanCount; i++)
            {
                if ((srcReg[i] & BorderReg) == 0)
                {
                    srcReg[i] = regions[srcReg[i]].Id;
                }
            }

            return(true);
        }
コード例 #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);
                }
            }
        }
コード例 #3
0
        private bool FloodRegion(int x, int y, int i, int level, int r, ref int[] srcReg, ref int[] srcDist, ref IntArray stack)
        {
            int  w    = Width;
            uint area = Areas[i];

            stack.Resize(0);
            stack.Push(x);
            stack.Push(y);
            stack.Push(i);
            srcReg[i]  = r;
            srcDist[i] = 0;

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

            while (stack.Size > 0)
            {
                int ci = stack.Pop();
                int cy = stack.Pop();
                int cx = stack.Pop();

                CompactSpan cs = Spans[ci];

                int ar = 0;
                for (int dir = 0; dir < 4; dir++)
                {
                    if (cs.GetCon(dir) != NotConnected)
                    {
                        int ax = cx + Helper.GetDirOffsetX(dir);
                        int ay = cy + Helper.GetDirOffsetY(dir);
                        int ai = (int)Cells[ax + ay * w].Index + cs.GetCon(dir);
                        if (Areas[ai] != area)
                        {
                            continue;
                        }
                        int nr = srcReg[ai];
                        if ((nr & BorderReg) != 0)
                        {
                            continue;
                        }
                        if (nr != 0 && nr != r)
                        {
                            ar = nr;
                        }
                        CompactSpan aspan = Spans[ai];

                        int dir2 = (dir + 1) & 0x3;
                        if (aspan.GetCon(dir2) != NotConnected)
                        {
                            int ax2 = ax + Helper.GetDirOffsetX(dir2);
                            int ay2 = ay + Helper.GetDirOffsetY(dir2);
                            int ai2 = (int)Cells[ax2 + ay2 * w].Index + aspan.GetCon(dir2);
                            if (Areas[ai2] != area)
                            {
                                continue;
                            }
                            int nr2 = srcReg[ai2];
                            if (nr2 != 0 && nr2 != r)
                            {
                                ar = nr2;
                            }
                        }
                    }
                }
                if (ar != 0)
                {
                    srcReg[ci] = 0;
                    continue;
                }
                count++;

                for (int dir = 0; dir < 4; dir++)
                {
                    if (cs.GetCon(dir) != NotConnected)
                    {
                        int ax = cx + Helper.GetDirOffsetX(dir);
                        int ay = cy + Helper.GetDirOffsetY(dir);
                        int ai = (int)Cells[ax + ay * w].Index + cs.GetCon(dir);
                        if (Areas[ai] != area)
                        {
                            continue;
                        }
                        if (Dist[ai] >= lev && srcReg[ai] == 0)
                        {
                            srcReg[ai]  = r;
                            srcDist[ai] = 0;
                            stack.Push(ax);
                            stack.Push(ay);
                            stack.Push(ai);
                        }
                    }
                }
            }
            return(count > 0);
        }