Beispiel #1
0
        public void apply(OpenHeightfield field)
        {
            if (null == field)
            {
                Logger.LogError("[FilterOutSmallRegions][apply]field null");
                return;
            }

            if (field.regionCount() < 2)
            {
                Logger.LogError("[FilterOutSmallRegions][apply]RegionCnt|{0}", field.regionCount());
                return;
            }

            //PS:索引即是对应的ID
            Region[] regions = new Region[field.regionCount()];
            for (int i = 0; i < field.regionCount(); ++i)
            {
                regions[i] = new Region(i);
            }

            #region 收集邻接信息
            OpenHeightfield.OpenHeightFieldIterator iter = field.GetEnumerator();
            while (iter.MoveNext())
            {
                OpenHeightSpan span = iter.Current;
                if (span.regionID() <= NULL_REGION)
                {
                    continue;
                }

                //索引即RegionID
                Region region = regions[span.regionID()];
                region.spanCount++;

                for (OpenHeightSpan nextHigherSpan = span.next();
                     nextHigherSpan != null;
                     nextHigherSpan = nextHigherSpan.next()
                     )
                {
                    int nextHigherSpanRegionID = nextHigherSpan.regionID();
                    if (nextHigherSpanRegionID <= NULL_REGION)
                    {
                        continue;
                    }
                    //因为是同属一个Grid的,所以肯定是重叠的
                    if (!region.overlappingRegions.Contains(nextHigherSpanRegionID))
                    {
                        region.overlappingRegions.Add(nextHigherSpanRegionID);
                    }
                } //for

                if (region.connections.Count > 0)
                {
                    continue;
                }

                int edgeDirection = getRegionEdgeDirection(span);
                if (edgeDirection != -1)
                {
                    findRegionConnections(span, edgeDirection, ref region.connections);
                }
            } // while

            #endregion

            #region 清理孤岛Region
            for (int regionID = 1; regionID < field.regionCount(); ++regionID)
            {
                Region region = regions[regionID];
                if (0 == region.spanCount)
                {
                    continue;
                }

                // 有且仅有一个 Null Region 邻居
                if (region.connections.Count == 1 &&
                    region.connections[0] == NULL_REGION)
                {
                    if (region.spanCount < mMinUnconnectedRegionSize)
                    {
                        region.resetWithID(0);
                    }
                }
            }  //for

            #endregion

            #region 合并小的Region
            int mergeCount;
            do
            {
                mergeCount = 0;

                foreach (Region region in regions)
                {
                    if (region.id <= NULL_REGION ||
                        region.spanCount == 0)
                    {
                        continue;
                    }

                    if (region.spanCount > mMergeRegionSize)
                    {
                        continue;
                    }

                    Region targetMergeRegion = null;
                    int    smallestSizeFound = int.MaxValue;

                    foreach (int nRegionID in region.connections)
                    {
                        if (nRegionID <= 0)
                        {
                            continue;
                        }

                        Region nRegion = regions[nRegionID];
                        if (nRegion.spanCount < smallestSizeFound &&
                            canMerge(region, nRegion))
                        {
                            targetMergeRegion = nRegion;
                            smallestSizeFound = nRegion.spanCount;
                        }
                    } // foreach nRegionID

                    if (targetMergeRegion != null &&
                        mergeRegions(targetMergeRegion, region))     //为啥是反过来Merge。。。
                    {
                        int oldRegionID = region.id;
                        region.resetWithID(targetMergeRegion.id);

                        foreach (Region r in regions)
                        {
                            if (r.id <= NULL_REGION)
                            {
                                continue;
                            }
                            if (r.id == oldRegionID)
                            {
                                r.id = targetMergeRegion.id;
                            }
                            else
                            {
                                replaceNeighborRegionID(r, oldRegionID, targetMergeRegion.id);
                            }
                        } // foreach regions

                        mergeCount++;
                    } // if mergerRegion
                }     // foreach region
            } while (mergeCount > 0);

            #endregion

            #region  re-map 区域ID,保持ID连接
            foreach (Region region in regions)
            {
                if (region.id >= NULL_REGION)
                {
                    region.remap = true;
                }
            }

            int currRegionID = NULL_REGION;
            foreach (Region region in regions)
            {
                if (!region.remap)
                {
                    continue;
                }
                currRegionID++;
                int oldID = region.id;

                foreach (Region r in regions)
                {
                    if (r.id == oldID)
                    {
                        r.id    = currRegionID;
                        r.remap = false;
                    }
                } //foreach
            }     //foreach

            field.setRegionCount(currRegionID + 1);

            iter.Reset();
            while (iter.MoveNext())
            {
                OpenHeightSpan span = iter.Current;
                if (NULL_REGION == span.regionID())
                {
                    continue;
                }
                else
                {
                    //真正re-map一下
                    span.setRegionID(regions[span.regionID()].id);
                }
            }

            #endregion
        }  //apply
Beispiel #2
0
        public void apply(OpenHeightfield field)
        {
            if (null == field)
            {
                Logger.LogError("[CLeanNullRegionBorders][apply]field null");
                return;
            }

            int nextRegionID = field.regionCount();

            OpenHeightfield.OpenHeightFieldIterator iter = field.GetEnumerator();
            while (iter.MoveNext())
            {
                OpenHeightSpan span = iter.Current;
                if (span.flags != 0)
                {
                    continue;
                }

                span.flags = 1;

                OpenHeightSpan workingSpan   = null;
                int            edgeDirection = -1;

                if (NULL_REGION == span.regionID())
                {
                    //找到Border Span第一个非空的邻居
                    edgeDirection = getNonNullBorderDrection(span);
                    if (-1 == edgeDirection)
                    {
                        continue;
                    }

                    //起点Span为有所属Region的Span
                    //而行走方向为 null Region所在的方向
                    workingSpan = span.getNeighbor(edgeDirection);
                    //转180度
                    edgeDirection = NMGenUtility.AntiDir(edgeDirection);
                }
                else if (!mUseOnlyNullSpans)
                {
                    //起点Span为 null Region的Span
                    //而行走方向即为有所属 Region 的 Span

                    edgeDirection = getNullBorderDrection(span);
                    if (-1 == edgeDirection)
                    {
                        continue;
                    }
                    workingSpan = span;
                }
                else
                {
                    continue;
                }

                //上面两个分支都会保证workingSpan是有所属Region的
                //而Dir 即是 Region 为 Null Region的Dir
                bool isEncompassedNullRegion = processNullRegion(workingSpan, edgeDirection);
                if (isEncompassedNullRegion)   //确定以这个Span为起点的Region,是一个单一Region,并且内含了一个Null Region
                {
                    //如果是完全包含了不可走的NullRegion ,就将这个Region分割成两个Region
                    partialFloodRegion(workingSpan, edgeDirection, nextRegionID);
                    nextRegionID++;
                }
            }

            field.setRegionCount(nextRegionID);

            iter.Reset();
            while (iter.MoveNext())
            {
                iter.Current.flags = 0;
            }
        }
Beispiel #3
0
        public ContourSet build(OpenHeightfield sourceField)
        {
            if (null == sourceField ||
                0 == sourceField.regionCount())
            {
                Logger.LogError("[ContourSetBuilder][build]sourceField Invalid");
                return(null);
            }

            ContourSet result = new ContourSet(sourceField.boundsMin(),
                                               sourceField.boundsMax(),
                                               sourceField.cellSize(),
                                               sourceField.cellHeight(),
                                               sourceField.regionCount());

            int discardedContours = 0;

            /*
             *  If a span has no connections to external regions or is
             *  completely surrounded by other regions (a single span island),
             *  its flag will be zero.
             *
             *  If a span is connected to one or more external regions then the
             *  flag will be a 4 bit value where connections are recorded as
             *  follows:
             *      bit1 = neighbor0
             *      bit2 = neighbor1
             *      bit3 = neighbor2
             *      bit4 = neighbor3
             *  With the meaning of the bits as follows:
             *      0 = neighbor in same region.
             *      1 = neighbor not in same region. (Neighbor may be the null
             *      region or a real region.)
             */
            OpenHeightfield.OpenHeightFieldIterator iter = sourceField.GetEnumerator();
            while (iter.MoveNext())
            {
                OpenHeightSpan span = iter.Current;
                span.flags = 0;   //默认没有与任何外部Region相连
                if (NULL_REGION == span.regionID())
                {
                    continue;
                }

                for (int dir = 0; dir < 4; ++dir)
                {
                    int            nRegionID = NULL_REGION;
                    OpenHeightSpan nSpan     = span.getNeighbor(dir);
                    if (nSpan != null)
                    {
                        nRegionID = nSpan.regionID();
                    }
                    //这里是反常操作,先将相同的当作是1,然后统一位反
                    if (nRegionID == span.regionID())
                    {
                        span.flags |= 1 << dir;
                    }
                } // for

                //1111111
                span.flags ^= 0xf;
                if (span.flags == 0xf)   //证明四个邻居都不在同一个Region或者是一个孤岛Span
                {
                    //重置这个位置
                    span.flags = 0;
                    discardedContours++;
                    Logger.LogWarning("[ContourSetBuilder][apply]Island Span|{0}|{1}", span.regionID(), discardedContours);
                }
            }  //while iter

            List <int> workingRawVerts        = new List <int>(256);
            List <int> workingSimplifiedVerts = new List <int>(64);

            iter.Reset();
            while (iter.MoveNext())
            {
                OpenHeightSpan span = iter.Current;
                if (NULL_REGION == span.regionID() ||
                    0 == span.flags)      //flag等于0的话,是前面那些孤岛Span
                {
                    continue;
                }

                workingRawVerts.Clear();
                workingSimplifiedVerts.Clear();

                //找到第一个不是同一个Region的Span
                int startDir = 0;
                while (isSameRegion(span, startDir))
                {
                    startDir++;
                }

                buildRawContours(span,
                                 iter.widthIndex(),
                                 iter.depthIndex(),
                                 startDir,
                                 ref workingRawVerts
                                 );

                generateSimplifiedContour(span.regionID(),
                                          workingRawVerts,
                                          ref workingSimplifiedVerts);

                //TODO 为什么小于12个顶点就不行呢?
                if (workingSimplifiedVerts.Count < 12)
                {
                    Logger.LogWarning("[ContourSetBuilder][build]Discarded Contour|{0}|{1}|", span.regionID(), discardedContours);
                    discardedContours++;
                }
                else
                {
                    result.add(new Contour(span.regionID(),
                                           workingRawVerts,
                                           workingSimplifiedVerts));
                }
            } // while iter

            if (discardedContours > 0)
            {
                Logger.LogWarning("[ContourSetBuilder][build]Contours not generated|{0}|", discardedContours);
            }

            if (result.size() + discardedContours
                != sourceField.regionCount() - 1)
            {
                for (int regionID = 1; regionID < sourceField.regionCount(); ++regionID)
                {
                    int regionMatches = 0;
                    for (int iContour = 0; iContour < result.size(); ++iContour)
                    {
                        if (regionID == result.get(iContour).regionID)
                        {
                            regionMatches++;
                        }
                    }

                    if (regionMatches > 1)
                    {
                        Logger.LogError("[ContourSetBuilder][build]More than |{0}|{1}", regionID, regionMatches);
                    }
                } // for

                for (int iContour = 0; iContour < result.size(); ++iContour)
                {
                    Contour contour = result.get(iContour);
                    if (contour.regionID <= 0)
                    {
                        Logger.LogError("[ContourSetBuilder][build]null region contour");
                    }
                    else if (contour.regionID >= sourceField.regionCount())
                    {
                        Logger.LogError("[ContourSetBuilder][build]contour out of range|{0}", contour.regionID);
                    }
                } // for

                Logger.LogError("[ContourSetBuilder][build]Error{0}|{1}|{2}|{3}",
                                sourceField.regionCount() - 1,
                                result.size() + discardedContours,
                                result.size(),
                                discardedContours
                                );

                return(null);
            }


            return(result);
        } // build