Exemple #1
0
        }     //buildRawContour

        /*
         *  以Span为s , direction 为 0 举例,这个函数要寻找的就是s,ds,ns,里面,floor最大值
         *      ds
         *      ns  s
         *
         *  如果direction为1的话
         *          ns ds
         *          s
         *
         */
        private static int getCornerHeight(OpenHeightSpan span, int direction)
        {
            //OpenHeightSpan的floor 就真的是floor了,而不是
            //像 SolidHeightSpan那样对应的其实是顶部
            int            maxFloor = span.floor();
            OpenHeightSpan dSpan    = null;
            //顺时针
            int            directionOffset = NMGenUtility.ClockwiseRotateDir(direction);
            OpenHeightSpan nSpan           = span.getNeighbor(direction);

            if (nSpan != null)
            {
                maxFloor = Math.Max(maxFloor, nSpan.floor());
                dSpan    = nSpan.getNeighbor(directionOffset);
            }

            nSpan = span.getNeighbor(directionOffset);
            if (nSpan != null)
            {
                maxFloor = Math.Max(maxFloor, nSpan.floor());
                if (null == dSpan)
                {
                    dSpan = nSpan.getNeighbor(direction);
                }
            }

            if (dSpan != null)
            {
                maxFloor = Math.Max(maxFloor, dSpan.floor());
            }

            return(maxFloor);
        }
Exemple #2
0
        public void getDetailedRegionMap(ref int[] outData, int insertIndex)
        {
            for (int i = 0; i < 8; ++i)
            {
                outData[insertIndex + i] = NULL_REGION;
            }

            OpenHeightSpan nSpan  = null;
            OpenHeightSpan nnSpan = null;

            for (int dir = 0; dir < 4; dir++)
            {
                nSpan = getNeighbor(dir);
                if (nSpan != null)
                {
                    outData[insertIndex + dir] = nSpan.regionID();
                    nnSpan = nSpan.getNeighbor((dir + 1) & 0x3);
                    if (nnSpan != null)
                    {
                        outData[insertIndex + dir + 4] = nnSpan.regionID();
                    }

                    nnSpan = nSpan.getNeighbor((dir + 3) & 0x3);
                    if (nnSpan != null)
                    {
                        outData[insertIndex + ((dir + 3) & 0x3) + 4] = nnSpan.regionID();
                    }
                }
            }
        }
        public void printDistanceField()
        {
            Logger.Log("[OpenHeightfield][printDistanceField]Log Start");
            Logger.Log("[OpenHeightfield][printDistanceField]Distance Field Spans|{0}", mSpanCount);

            int depth = -1;

            Logger.Log("\t");
            for (int width = 0; width < this.width(); ++width)
            {
                Logger.Log("{0}\t", width);
            }

            OpenHeightFieldIterator iter = GetEnumerator();

            while (iter.MoveNext())
            {
                OpenHeightSpan span = iter.Current;
                if (iter.depthIndex() != depth)
                {
                    Logger.LogWarning("\n{0}\t", ++depth);
                }
                Logger.Log("{0}\t", span.distanceToBorder());
            }

            Logger.Log("[OpenHeightfield][printDistanceField]Log End");
        }
            }     // MoveNext

            public void Reset()
            {
                mNextWidth     = 0;
                mNextDepth     = 0;
                mNext          = null;
                mIsReverseIter = false;
            }
        public bool addData(int widthIndex, int depthIndex, OpenHeightSpan span)
        {
            if (widthIndex < 0 ||
                widthIndex >= width() ||
                depthIndex < 0 ||
                depthIndex >= depth())
            {
                Logger.LogWarning("[OpenHeightfield][addData]width|depth|{0}|{1}|{2}|{3}", widthIndex, depthIndex, width(), depth());
                return(false);
            }

            if (mSpans == null || span == null)
            {
                Logger.LogWarning("[OpenHeightfield][addData]mSpan or Span null|{0}|{1}|{2}|{3}", widthIndex, depthIndex, width(), depth());
                return(false);
            }

            int            gridIndex = GetGridIndex(widthIndex, depthIndex);
            OpenHeightSpan currentSpan;

            if (!mSpans.TryGetValue(gridIndex, out currentSpan))
            {
                mSpans.Add(gridIndex, span);
                return(true);
            }
            else
            {
                Logger.LogWarning("[OpenHeightfield][addData]Span already in|{0}|{1}|{2}|{3}", widthIndex, depthIndex, width(), depth());
                return(false);
            }
        }
Exemple #6
0
        private static bool isSameRegion(OpenHeightSpan span, int dir)
        {
            if (span == null)
            {
                return(false);
            }

            return((span.flags & (1 << dir)) == 0);
        }
        public OpenHeightSpan getData(int widthIndex, int depthIndex)
        {
            OpenHeightSpan retSpan = null;

            if (mSpans != null)
            {
                mSpans.TryGetValue(GetGridIndex(widthIndex, depthIndex), out retSpan);
            }
            return(retSpan);
        }
Exemple #8
0
        /// <summary>
        /// </summary>
        /// <param name="startSpan"></param>
        /// <param name="borderDirection"></param>
        /// <param name="newRegionID"></param>
        private void partialFloodRegion(OpenHeightSpan startSpan,
                                        int borderDirection,
                                        int newRegionID)
        {
            int antiBorderDirection = NMGenUtility.AntiDir(borderDirection);
            int regionID            = startSpan.regionID();

            startSpan.setRegionID(newRegionID);
            startSpan.setDistanceToRegionCore(0);  //???所以这个值 没啥卵用啊,一直都是0的
            mwOpenSpans.Push(startSpan);
            mwBorderDistance.Push(0);

            while (mwOpenSpans.Count > 0)
            {
                OpenHeightSpan span     = mwOpenSpans.Pop();
                int            distance = mwBorderDistance.Pop();

                for (int i = 0; i < 4; ++i)
                {
                    OpenHeightSpan nSpan = span.getNeighbor(i);
                    if (null == nSpan ||
                        nSpan.regionID() != regionID)
                    {
                        continue;
                    }

                    int nDistance = distance;
                    if (i == borderDirection)   //null region所在的方向
                    {
                        //这里是不是应该小于等于0呢?
                        // 以这个距离Border为0作为边界,将大于0那一侧的Span全部变成
                        // 新的Region。然后小于等于0那一侧的作为旧的Region保留下来。
                        if (0 == distance)
                        {
                            continue;
                        }
                        nDistance--;
                    }
                    else if (i == antiBorderDirection)
                    {
                        nDistance++;
                    }

                    //注意上面的if-else,如果都不是这两个方向的Span
                    //会直接被重新设置为新的Region


                    nSpan.setRegionID(newRegionID);
                    nSpan.setDistanceToRegionCore(0);

                    mwOpenSpans.Push(nSpan);
                    mwBorderDistance.Push(nDistance);
                }
            }
        }
            public void ReverseReset()
            {
                if (mOpenHeightfield != null)
                {
                    mNextWidth = mOpenHeightfield.width();
                    mNextDepth = mOpenHeightfield.depth();
                }

                mNext          = null;
                mIsReverseIter = true;
            }
Exemple #10
0
        public void setNeighbor(int direction, OpenHeightSpan neighbor)
        {
            switch (direction)
            {
            case 0: mNeighborConnection0 = neighbor; break;

            case 1: mNeighborConnection0 = neighbor; break;

            case 2: mNeighborConnection0 = neighbor; break;

            case 3: mNeighborConnection0 = neighbor; break;
            }
        }
Exemple #11
0
 private static int getNonNullBorderDrection(OpenHeightSpan span)
 {
     for (int dir = 0; dir < 4; ++dir)
     {
         OpenHeightSpan nSpan = span.getNeighbor(dir);
         if (nSpan != null &&
             nSpan.regionID() != NULL_REGION)
         {
             return(dir);
         }
     }
     return(-1);
 }
Exemple #12
0
 private static int getRegionEdgeDirection(OpenHeightSpan span)
 {
     for (int dir = 0; dir < 4; ++dir)
     {
         OpenHeightSpan nSpan = span.getNeighbor(dir);
         if (null == nSpan ||
             nSpan.regionID() != span.regionID())
         {
             return(dir);
         }
     }
     return(-1);
 }
        /// <summary>
        /// 必须nSpan是存在的,如果不存在的话,span2Border的值自然就0了
        /// </summary>
        /// <param name="span2Border"></param>
        /// <param name="nSpan"></param>
        /// <param name="isAxisNeighbor"></param>
        /// <returns></returns>
        private int calcMiniDistanceToBorder( int span2Border, OpenHeightSpan nSpan , bool isAxisNeighbor)
        {
            if( nSpan != null  )
            {
                int nDist = nSpan.distanceToBorder(); 
                if( nDist == NEEDS_INIT )
                {
                    nDist = isAxisNeighbor ? 1 : 2; 
                }
                else
                {
                    nDist = isAxisNeighbor ? nDist + 2 : nDist + 3; 
                }
                span2Border = Math.Min(span2Border, nDist); 
            }
            else
            {
                Logger.LogWarning("[OpenHeightfieldBuilder][calcMiniDistanceToBorder]nSpan null"); 
            }

            return span2Border; 
        }
        public void generateNeighborLinks(OpenHeightfield field)
        {
            if( field == null )
            {
                Logger.LogError("[OpenHeightfieldBuilder][generateNeighborLinks]field Empty"); 
                return; 
            }

            OpenHeightfield.OpenHeightFieldIterator iter = field.GetEnumerator(); 
            while( iter.MoveNext() )
            {
                OpenHeightSpan span = iter.Current; 
                for(int dir = 0; dir < 4; ++dir)
                {
                    //邻居的GirdIndex
                    int nWidthIndex = (iter.widthIndex() + BoundeField.getDirOffsetWidth(dir));
                    int nDepthIndex = (iter.depthIndex() + BoundeField.getDirOffsetDepth(dir)); 
                    
                    for(OpenHeightSpan nSpan = field.getData(nWidthIndex,nDepthIndex);
                        nSpan != null;
                        nSpan = nSpan.next())
                    {
                        int maxFloor = Math.Max(span.floor(), nSpan.floor());
                        int minCeling = Math.Min(span.ceiling(), nSpan.ceiling()); 

                        
                        if( (minCeling - maxFloor) >= mMinTraversableHeight   //邻居之间的通道足够高,可以通过 
                            && Math.Abs(nSpan.floor() - span.floor()) <= mMaxTraversableStep )  //两邻居之间的落差足够小
                        {
                            span.setNeighbor(dir, nSpan);
                            break; 
                        }
                    }

                }
            }
        }
        private void calcBorderDistanceBounds()
        {
            if (0 == mSpanCount)
            {
                return;
            }

            mMinBorderDistance = int.MaxValue;
            mMaxBorderDistance = UNKNOWN;

            OpenHeightFieldIterator iter = GetEnumerator();

            while (iter.MoveNext())
            {
                OpenHeightSpan span = iter.Current;
                mMinBorderDistance = Math.Min(mMinBorderDistance, span.distanceToBorder());
                mMaxBorderDistance = Math.Max(mMaxBorderDistance, span.distanceToBorder());
            }

            if (mMinBorderDistance == int.MaxValue)
            {
                mMinBorderDistance = UNKNOWN;
            }
        }
Exemple #16
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;
            }
        }
        public void generateRegions( OpenHeightfield field )
        {
            if (null == field)
            {
                return;
            }

            //这个距离,控制生成的网络有多贴近实际的模型
            int minDist = mTraversableAreaBorderSize + field.minBorderDistance();
            int expandIterations = 4 + (mTraversableAreaBorderSize * 2); //TODO emmm

            //排除奇数
            int dist = (field.maxBorderDistance() - 1) & ~1 ;

            List<OpenHeightSpan> floodedSpans = new List<OpenHeightSpan>(1024);
            Stack<OpenHeightSpan> workingStack = new Stack<OpenHeightSpan>(1024);

            OpenHeightfield.OpenHeightFieldIterator iter = field.GetEnumerator();

            int nextRegionID = 1;
            //高于这个距离的体素都得生成Regions,那剩下的体素怎么办呢?
            while ( dist > minDist )  
            {
                iter.Reset();
                floodedSpans.Clear(); 

                while( iter.MoveNext() )
                {
                    OpenHeightSpan span = iter.Current; 
                    if( span.regionID() == NULL_REGION
                        && span.distanceToBorder() >= dist )
                    {
                        floodedSpans.Add(span); 
                    }
                }

                if( nextRegionID > 1 )
                {
                    //大于1表示已经至少存在1个region,先去尝试一下合并
                    if( dist > 0 )
                    {
                        expandRegions(floodedSpans, expandIterations);     
                    }
                    else  //这里不太可能会走到吧?除非minDist == 0
                    {
                        expandRegions(floodedSpans, -1); 
                    }
                }

                //剩下的可能要生成新的Region
                foreach( OpenHeightSpan span in floodedSpans )
                {
                    if( null == span 
                        || span.regionID() != NULL_REGION )
                    {
                        continue; 
                    }

                    //TODO ????
                    int fillTo = Math.Max(dist - 2, minDist); 
                    if( floodNewRegion(span,fillTo,nextRegionID,workingStack ))
                    {
                        nextRegionID++; 
                    }
                }

                //更新深度
                dist = Math.Max(dist - 2, 0); 
            }  //while dist > minDist

            //最后一篇循环
            iter.Reset();
            floodedSpans.Clear(); 
            while( iter.MoveNext() )
            {
                OpenHeightSpan span = iter.Current; 
                if( span.distanceToBorder() >= minDist
                    && span.regionID() == NULL_REGION )
                {
                    floodedSpans.Add(span); 
                }
            }

            if( minDist > 0 )
            {
                expandRegions(floodedSpans, expandIterations * 8); 
            }
            else
            {
                expandRegions(floodedSpans, -1); 
            }
            field.setRegionCount(nextRegionID);

            //后处理
            foreach( IOpenHeightFieldAlgorithm algorithm in mRegionAlgorithms )
            {
                algorithm.apply(field);  
            }
        }
Exemple #18
0
        /// <summary>
        /// 重新设置对应的Region,根据拐点的情况
        /// </summary>
        /// <param name="referenceSpan"></param>
        /// <param name="borderDirection"></param>
        /// <param name="cornerDirection"></param>
        /// <returns></returns>
        private int selectedRegionID(OpenHeightSpan referenceSpan,
                                     int borderDirection,
                                     int cornerDirection)
        {
            referenceSpan.getDetailedRegionMap(ref mwNeighborRegions, 0);

            /*
             * Initial example state:
             *
             * a - Known region.
             * x - Null region.
             * u - Unknown, not checked yet.
             *
             *     u u u
             *     u a x
             *     u a a
             */

            int antiBorderDirection = NMGenUtility.AntiDir(borderDirection);
            int antiCornerDirection = NMGenUtility.AntiDir(cornerDirection);
            int regionID            = mwNeighborRegions[antiBorderDirection];

            if (regionID == referenceSpan.regionID() ||
                NULL_REGION == regionID)
            {
                /*
                 * The region away from the border is either a null region
                 * or the same region.  So we keep the current region.
                 *
                 *     u u u      u u u
                 *     a a x  or  x a x  <-- Potentially bad, but stuck with it.
                 *     u a a      u a a
                 */
                return(referenceSpan.regionID());
            }

            int potentialRegion = regionID;

            regionID = mwNeighborRegions[antiCornerDirection];
            if (regionID == referenceSpan.regionID() ||
                NULL_REGION == regionID)
            {
                /*
                 * The region opposite from the corner direction is
                 * either a null region or the same region.  So we
                 * keep the current region.
                 *
                 *     u a u      u x u
                 *     b a x  or  b a x
                 *     u a a      u a a
                 */
                return(referenceSpan.regionID());
            }

            int potentialCount = 0;
            int currentCount   = 0;

            for (int i = 0; i < 8; ++i)
            {
                if (mwNeighborRegions[i] == referenceSpan.regionID())
                {
                    currentCount++;
                }
                else if (mwNeighborRegions[i] == potentialRegion)
                {
                    potentialCount++;
                }
            }

            return(potentialCount < currentCount
                ? referenceSpan.regionID()
                : potentialRegion);
        }
Exemple #19
0
        /// <summary>
        /// 就是Reference所属的Region分成两部分来看。分别是backTwo和Reference。
        /// 然后再分别和backOne所属的Region来检测连接。
        /// </summary>
        /// <param name="referenceSpan"></param>
        /// <param name="borderDirection"></param>
        /// <returns></returns>
        private bool processOuterCorner(OpenHeightSpan referenceSpan, int borderDirection)
        {
            bool hasMultiRegions = false;
            //比如 borderDirection 是 方向1的话 ,那么 backOne 就是 0

            /*
             *    r 是 referenceSpan , x 是 border span,1、2就是对应的backOne和backTwo
             *    2 x
             *    1 r
             */

            //backOne 和 backTwo 沿着borderDir方向的两个Span
            int            backOneDirection    = (borderDirection + 3) & 0x3;
            int            antiBorderDirection = (borderDirection + 2) & 0x3;
            OpenHeightSpan backOne             = referenceSpan.getNeighbor(backOneDirection);
            OpenHeightSpan backTwo             = backOne.getNeighbor(borderDirection);
            OpenHeightSpan testSpan;

            if (backOne.regionID() != referenceSpan.regionID() &&
                backTwo.regionID() == referenceSpan.regionID())
            {
                /*
                 *     Example:
                 *
                 *     a a x x x a
                 *     a a x x a a
                 *     b b a a a a
                 *     b b a a a a
                 *
                 */

                /*
                 * 有问题的布局
                 *    a  x
                 *    b  a
                 *
                 * 需要转换成下面两种
                 *  b x      a x
                 *  b a      b b
                 *
                 */

                hasMultiRegions = true;

                /*
                 *    2 x           2 x
                 *    1 r   =>    t 1 r
                 *
                 */
                testSpan = backOne.getNeighbor(backOneDirection);
                //检查 backTwo 和 backOne 有多少个连接
                int backTwoConnections = 0;
                if (testSpan != null &&
                    testSpan.regionID() == backOne.regionID())
                {
                    // b 是 backOne , a 是 backTwo

                    /*
                     *      a x
                     *    t b a
                     *
                     *       ||
                     *
                     *      a x
                     *    b b a
                     *
                     */
                    backTwoConnections++;

                    testSpan = testSpan.getNeighbor(borderDirection);
                    if (testSpan != null &&
                        testSpan.regionID() == backOne.regionID())
                    {
                        /*
                         *
                         *    t a x
                         *    b b a
                         *
                         *      ||
                         *
                         *    b a x
                         *    b b a
                         *
                         */
                        backTwoConnections++;
                    }
                }  // backTwo - backOne

                //检查 reference span 和 backOne 有多少连接
                int referenceConnections = 0;
                testSpan = backOne.getNeighbor(antiBorderDirection);
                if (testSpan != null &&
                    testSpan.regionID() == backOne.regionID())
                {
                    /*
                     *      a x
                     *      b a
                     *      t
                     *
                     *      ||
                     *
                     *      a x
                     *      b a
                     *      b
                     */

                    referenceConnections++;
                    //TODO  这个方向我修改过,应该是转向才对,感觉原作者是笔误
                    testSpan = testSpan.getNeighbor((borderDirection + 1) & 0x3);
                    if (testSpan != null &&
                        testSpan.regionID() == backOne.regionID())
                    {
                        /*
                         *      a x
                         *      b a
                         *      b t
                         *
                         *
                         *      ||
                         *
                         *      a x
                         *      b a
                         *      b b
                         *
                         */
                        if (testSpan != null &&
                            testSpan.regionID() == backOne.regionID())
                        {
                            referenceConnections++;
                        }
                    }
                } // Reference-backOne


                //变成这个拐点
                if (referenceConnections > backTwoConnections)
                {
                    referenceSpan.setRegionID(backOne.regionID());
                }
                else
                {
                    backTwo.setRegionID(backOne.regionID());
                }
            }
            else if (backOne.regionID() == referenceSpan.regionID() &&
                     backTwo.regionID() == referenceSpan.regionID())
            {
                //源码本来的注释

                /*
                 * Potential dangerous short wrap.
                 *
                 *  a x
                 *  a a
                 *
                 *  Example of actual problem configuration:
                 *
                 *  b b x x
                 *  b a x x <- Short wrap.
                 *  b a a a
                 *
                 *  In the above case, the short wrap around the corner of the
                 *  null region has been demonstrated to cause self-intersecting
                 *  polygons during polygon formation.
                 *
                 *  This algorithm detects whether or not one (and only one)
                 *  of the axis neighbors of the corner should be re-assigned to
                 *  a more appropriate region.
                 *
                 *  In the above example, the following configuration is more
                 *  appropriate:
                 *
                 *  b b x x
                 *  b b x x <- Change to this row.
                 *  b a a a
                 */

                /*
                 *    2 x
                 *    1 r
                 *
                 *    ||
                 *
                 *    a x
                 *    a a
                 *
                 *
                 */

                //以1为borderDirection的话,那么相对于backTwo来说
                //borderDirection 为 2 ,所以+1 ,CornerDirection 为3,所以+2,
                int selectedRegion = selectedRegionID(backTwo,
                                                      (borderDirection + 1) & 0x3,
                                                      (borderDirection + 2) & 0x3
                                                      );

                if (backTwo.regionID() == selectedRegion)
                {
                    //backTwo不用改变,尝试改变一下reference
                    selectedRegion = selectedRegionID(referenceSpan,
                                                      borderDirection,
                                                      (borderDirection + 3) & 0x3); //
                    if (selectedRegion != referenceSpan.regionID())
                    {
                        referenceSpan.setRegionID(selectedRegion);
                        hasMultiRegions = true;
                    }
                }
                else
                {
                    backTwo.setRegionID(selectedRegion);
                    hasMultiRegions = true;
                }
            }
            else
            {
                hasMultiRegions = true;
            }

            return(hasMultiRegions);
        }
        /* 参考 :http://www.cnblogs.com/wantnon/p/4947067.html 
        *       http://fab.cba.mit.edu/classes/S62.12/docs/Meijster_distance.pdf
        * 具体算法名:Saito算法
        * 因为初始化值都是未知的,通过一个大约估计的初始化,来重复来回算一个大致准确的距离
        * 值 ?
        */
        public void generateDistanceField(OpenHeightfield field)
        {
            if (field == null)
            {
                Logger.LogError("[OpenHeightfieldBuilder][generateNeighborLinks]field Empty");
                return;
            }

          

            /*
             * 先将OpenHeightField的Span数据转换成0和1的二值图。如果是边缘Span
             * 那么就是0,否则就是NEEDS_INIT。
             */


            OpenHeightfield.OpenHeightFieldIterator iter = field.GetEnumerator(); 
            while( iter.MoveNext() )
            {
                OpenHeightSpan span = iter.Current;
                bool isBorder = false; 

                for(int dir = 0; dir < 4; ++dir)
                {
                    OpenHeightSpan nSpan = span.getNeighbor(dir); 
                    if( null == nSpan
                        || nSpan.getNeighbor(dir == 3 ? 0 : dir + 1) == null) 
                    {
                        //如果8个邻居有任何一个缺失的话,那么就是边界Border
                        isBorder = true;
                        break;
                    }
                }

                if( isBorder )
                {
                    //自己就是边界Border
                    span.setDistanceToBorder(BORDER); 
                }
                else
                {
                    //需要再次计算
                    span.setDistanceToBorder(NEEDS_INIT);
                }
            } //while


            /*
             * 逆时针访问?
             *     
             *    (-1,1)  (0,1)  (1,1)
             *    (-1,0)    x    (1,0)
             *    (-1,-1) (0,-1) (1,-1)
             */

            //Pass 1
            //顺序访问 (-1,0) (-1,-1) (0,-1) (1,-1)
            iter.Reset(); 
            while( iter.MoveNext() )
            {
                OpenHeightSpan span = iter.Current;
                int selfDist = span.distanceToBorder(); 

                if( selfDist == BORDER)
                {
                    continue; 
                }

                //(-1,0)
                OpenHeightSpan nSpan = span.getNeighbor(0);
                selfDist = calcMiniDistanceToBorder(selfDist, nSpan, true);

                //(-1,-1),左下角的领域
                nSpan = nSpan.getNeighbor(3); //领域0的领域3,也就是原Span的左下角
                selfDist = calcMiniDistanceToBorder(selfDist, nSpan, false);

                //(0,-1)
                nSpan = span.getNeighbor(3);
                selfDist = calcMiniDistanceToBorder(selfDist, nSpan, true);

                //(1,-1) 
                nSpan = nSpan.getNeighbor(2);
                selfDist = calcMiniDistanceToBorder(selfDist, nSpan, true);
                
                span.setDistanceToBorder(selfDist); 

            } // while


            //Pass 2
            //顺序访问 (1,0) (1,1) (0,1) (-1,1)
            //注意这个是反向遍历
            iter.ReverseReset(); 
            while( iter.MoveNext() )
            {
                OpenHeightSpan span = iter.Current;
                int selfDist = span.distanceToBorder();
                if( selfDist == BORDER )
                {
                    continue; 
                }

                /* 
                 * 因为经过Pass1之后 ,所有的Span要么就是Border
                 * 要么就是有个大概值的,不会等于NEED_INIT的。
                 * 所以直接按照按照上面的流程跑
                 */

                //(1,0)
                OpenHeightSpan nSpan = span.getNeighbor(2);
                selfDist = calcMiniDistanceToBorder(selfDist, nSpan, true);

                //(1,1)
                nSpan = nSpan.getNeighbor(1);
                selfDist = calcMiniDistanceToBorder(selfDist, nSpan, false);

                //(0,1)
                nSpan = span.getNeighbor(1);
                selfDist = calcMiniDistanceToBorder(selfDist, nSpan, true);

                //(-1,1)
                nSpan = nSpan.getNeighbor(0);
                selfDist = calcMiniDistanceToBorder(selfDist, nSpan, false);

                span.setDistanceToBorder(selfDist); 
            }

            field.clearBorderDistanceBounds(); 
        }
Exemple #21
0
 public void setNext(OpenHeightSpan value)
 {
     mNext = value;
 }
        public OpenHeightfield build(SolidHeightfield sourceField ,
            bool performFullGeneration )
        {
            if( sourceField == null )
            {
                Logger.LogError("[OpenHeightfieldBuilder][build] sourceField null "); 
                return null; 
            }

            OpenHeightfield result = new OpenHeightfield(
                sourceField.boundsMin(),
                sourceField.boundsMax(),
                sourceField.cellSize(),
                sourceField.cellHeight()
                );
            
            for(int depthIndex = 0; depthIndex < sourceField.depth(); depthIndex++)
            {
                for(int widthIndex = 0; widthIndex < sourceField.width(); widthIndex++)
                {
                    OpenHeightSpan baseSpan = null;
                    OpenHeightSpan previousSpan = null;

                    for (HeightSpan span = sourceField.getData(widthIndex, depthIndex);
                         span != null;
                         span = span.next()
                        )
                    {
                        if ( span.flags() != mFilterFlags )
                        {
                            continue; 
                        }

                        //当前Solid Span的max对应的是对应OpenSpan的floor
                        int floor = span.max();  
                        //下一个Next Solid Span的min对应当前OpenSpan的Ceil。
                        int ceiling = (span.next() != null
                            ? span.next().min()
                            : int.MaxValue) ;

                        //对应的Open Span
                        OpenHeightSpan oSpan = new OpenHeightSpan(floor,
                            (ceiling - floor )
                            ); 
                        if( baseSpan == null  )
                        {
                            baseSpan = oSpan; 
                        }
                        if( previousSpan != null )
                        {
                            previousSpan.setNext(oSpan); 
                        }
                        previousSpan = oSpan;
                        result.incrementSpanCount(); 
                    } //for 
                    if( baseSpan != null )
                    {
                        result.addData(widthIndex, depthIndex, baseSpan); 
                    }
                }//for
            } //for

            if( performFullGeneration )
            {
                generateNeighborLinks(result);
                generateDistanceField(result);
                blurDistanceField(result);
                generateRegions(result);  
            }

            return result; 
        }
        public void blurDistanceField(OpenHeightfield field)
        {
            if( null == field )
            {
                return; 
            }

            if( mSmoothingThreshold <= 0 )
            {
                return; 
            }

            //Span => Distance
            Dictionary<OpenHeightSpan, int> blurResults = new Dictionary<OpenHeightSpan, int>();

            OpenHeightfield.OpenHeightFieldIterator iter = field.GetEnumerator(); 
            while( iter.MoveNext() )
            {
                OpenHeightSpan span = iter.Current;
                int origDist = span.distanceToBorder(); 
                if( origDist <= mSmoothingThreshold )  //这里也会Border引进去
                {
                    blurResults.Add(span, mSmoothingThreshold);
                    continue; 
                }

                int workingDist = origDist; 
                //将9个格子的距离加起来
                for(int dir = 0; dir < 4; ++dir)
                {
                    OpenHeightSpan nSpan = span.getNeighbor(dir); 
                    if( null == nSpan )
                    {
                        //这是不知道能不能看作自己占的比较增加了?但是为啥是 * 2 呢?
                        workingDist += origDist * 2; 
                    }
                    else
                    {
                        workingDist += nSpan.distanceToBorder();
                        nSpan = nSpan.getNeighbor( (dir+1) & 0x3 );   //对角线的
                        if( null == nSpan )
                        {
                            workingDist += origDist; 
                        }
                        else
                        {
                            workingDist += nSpan.distanceToBorder();  
                        }
                    }
                }  //for 

                if( blurResults.ContainsKey(span) )
                {
                    //除以9是平均呢,但是加上五就真的是不知道为什么了
                    blurResults[span] = (workingDist + 5) / 9; 
                }

            }  //while

            //更新一下距离值 
            foreach( var blurIter in blurResults )
            {
                blurIter.Key.setDistanceToBorder(blurIter.Value);   
            }
        }
        private static bool floodNewRegion( OpenHeightSpan rootSpan,
            int fillToDist ,
            int regionID,
            Stack<OpenHeightSpan> workingStack )
        {
            workingStack.Clear();

            workingStack.Push(rootSpan);
            rootSpan.setRegionID(regionID);
            rootSpan.setDistanceToRegionCore(0);

            int regionSize = 0; 

            //广度优先搜索
            while( workingStack.Count > 0 )
            {
                OpenHeightSpan span = workingStack.Pop();

                //表示当前Region是否已经在所属Region附近
                bool isOnRegionBorder = false; 
                for( int dir = 0; dir < 4; ++dir )
                {
                    OpenHeightSpan nSpan = span.getNeighbor(dir); 
                    if( null == nSpan )
                    {
                        continue; 
                    }

                    if( nSpan.regionID() != NULL_REGION
                        && nSpan.regionID() != regionID )
                    {
                        isOnRegionBorder = true;
                        break;
                    }

                    //对角线邻居
                    nSpan = nSpan.getNeighbor((dir + 1) & 0x3); 
                    if( nSpan != null 
                        && nSpan.regionID() != NULL_REGION 
                        && nSpan.regionID() != regionID )
                    {
                        isOnRegionBorder = true;
                        break; 
                    }

                }  //for 

                //那么,它就不能成为新的Region
                if( isOnRegionBorder )
                {
                    span.setRegionID(NULL_REGION);
                    continue; 
                }

                //到这里,表明要新增加一个Region了
                regionSize++; 

                for( int dir = 0;  dir < 4;++dir )
                {
                    OpenHeightSpan nSpan = span.getNeighbor(dir); 
                    if( nSpan != null 
                        && nSpan.distanceToBorder() >= fillToDist  //
                        && nSpan.regionID() == NULL_REGION )
                    {
                        nSpan.setRegionID(regionID);
                        nSpan.setDistanceToRegionCore(0);  //如果是同一区域的话 ?离中心是0的?
                        workingStack.Push(nSpan);
                    }
                }

            } // while


            return regionSize > 0; 
        }
Exemple #25
0
        /// <summary>
        /// 为了防止 Null Region 内含在有效Region内,这阻碍了凸包的生成
        /// </summary>
        /// <param name="startSpan"></param>
        /// <param name="startDirection"></param>
        /// <returns></returns>
        private bool processNullRegion(OpenHeightSpan startSpan, int startDirection)
        {
            /*
             *  这段直接翻译源码的:
             *  这个算法遍历这个轮廓。正如它所做的,这个算法检测并且修复一些危险的
             *  Span Configurations。
             *
             *  遍历轮廓:一个很好的可视化方向就是,想像一个机器人面向一堵墙,并坐
             *  在地板上。它会采取以下措施来绕过这堵墙:
             *  1. 如果有一堵墙位于它的前面,顺时针转90度,直到他前面不再是墙。
             *  2. 向前一步。
             *  3. 逆时针转向90度
             *  4. 重复从1开始的步骤 ,直到它发现自己位于原来的起点,还有原来的朝向。
             *
             */

            /*
             * 算法在遍历的同时,检测锐角(90度) 和 钝角(270)拐点。如果
             * 一个完整的轮廓被检测完整,并且 钝角拐点比锐角拐点多。那
             * 么 null Region 就在这个轮廓里面。否则就在轮廓外面。
             *
             */


            //环绕null region 走一圈,最后走回自己的起点
            int borderRegionID = startSpan.regionID();

            OpenHeightSpan span  = startSpan;
            OpenHeightSpan nSpan = null;
            int            dir   = startDirection;

            int  loopCount          = 0;
            int  acuteCornerCount   = 0;
            int  obtuseCornerCount  = 0;
            int  stepsWithoutBorder = 0;
            bool borderSeenLastLoop = false;
            bool isBorder           = true;

            bool hasSingleConnection = true;

            while (++loopCount < int.MaxValue)
            {
                //初始方向就是面向的Null Region,所以一开始是肯定是isBorder的
                nSpan = span.getNeighbor(dir);
                if (null == nSpan)
                {
                    isBorder = true;
                }
                else
                {
                    nSpan.flags = 1;
                    if (NULL_REGION == nSpan.regionID())
                    {
                        isBorder = true;
                    }
                    else
                    {
                        isBorder = false;
                        if (nSpan.regionID() != borderRegionID)
                        {
                            hasSingleConnection = false;
                        }
                    }
                } // else


                if (isBorder)
                {
                    if (borderSeenLastLoop)
                    {
                        /*
                         *  a x
                         *  x x
                         */

                        //其实这个应用用inner来描述更加准确 ,表明a被x包围了
                        acuteCornerCount++;
                    }
                    else if (stepsWithoutBorder > 1)
                    {
                        /*
                         *  a a
                         *  a x
                         *
                         */
                        obtuseCornerCount++;  //相对地,我觉得这个应该用outer来描述更加准确,表明a正在包围x
                        stepsWithoutBorder = 0;

                        //处理钝角的各种异常情况
                        if (processOuterCorner(span, dir))
                        {
                            hasSingleConnection = false;
                        }
                    }

                    dir = NMGenUtility.ClockwiseRotateDir(dir);
                    borderSeenLastLoop = true;
                    stepsWithoutBorder = 0;
                }
                else   //注意,不是边界,才会进行移动,如果是边界的话,只会进行转向
                {
                    span = nSpan;
                    dir  = NMGenUtility.CClockwiseRotateDir(dir); //逆时针转向一下
                    borderSeenLastLoop = false;
                    stepsWithoutBorder++;
                } // else

                //回到原方位了
                if (startSpan == span &&
                    startDirection == dir)
                {
                    return(hasSingleConnection &&
                           obtuseCornerCount > acuteCornerCount);
                }
            } // while

            return(false);
        }
Exemple #26
0
        //遍历轮廓,找邻接的Region
        private static void findRegionConnections(OpenHeightSpan startSpan,
                                                  int startDirection,
                                                  ref List <int> outConnections)
        {
            OpenHeightSpan span             = startSpan;
            int            dir              = startDirection;
            int            lastEdgeRegionID = NULL_REGION;

            OpenHeightSpan nSpan = span.getNeighbor(dir);

            if (nSpan != null)
            {
                lastEdgeRegionID = nSpan.regionID();
            }

            //连接的Region,至少存在一个NULL_REGION
            outConnections.Add(lastEdgeRegionID);

            int loopCount = 0;

            while (++loopCount < ushort.MaxValue)
            {
                nSpan = span.getNeighbor(dir);
                int currEdgeRegionID = NULL_REGION;   //默认nSpan是null的
                if (null == nSpan ||
                    nSpan.regionID() != span.regionID())
                {
                    if (nSpan != null)
                    {
                        currEdgeRegionID = nSpan.regionID();
                    }
                    if (currEdgeRegionID != lastEdgeRegionID)
                    {
                        outConnections.Add(currEdgeRegionID);
                        lastEdgeRegionID = currEdgeRegionID;
                    }

                    //顺时针转向下一个
                    dir = NMGenUtility.ClockwiseRotateDir(dir);
                }
                else
                {
                    //这个分支代表 Region是相同的
                    span = nSpan;
                    //逆时针转一下
                    dir = NMGenUtility.CClockwiseRotateDir(dir);
                }

                if (startSpan == span &&
                    startDirection == dir)
                {
                    break;
                }
            } //while

            //TODO 为啥会存在首尾相同呢?因为退出条件是原来的那个点么
            int connectionsCnt = outConnections.Count;

            if (connectionsCnt > 1 &&
                outConnections[0] == outConnections[connectionsCnt - 1])
            {
                outConnections.RemoveAt(connectionsCnt - 1);
            }
        }
Exemple #27
0
        private void buildRawContours(OpenHeightSpan startSpan,
                                      int startWidthIndex,
                                      int startDepthIndex,
                                      int startDirection,
                                      ref List <int> outContourVerts
                                      )
        {
            OpenHeightSpan span      = startSpan;
            int            dir       = startDirection;
            int            spanX     = startWidthIndex;
            int            spanZ     = startDepthIndex;
            int            loopCount = 0;

            while (++loopCount < ushort.MaxValue)
            {
                //这个Edge的一个Span
                if (!isSameRegion(span, dir))
                {
                    //span
                    int px = spanX;
                    int py = getCornerHeight(span, dir);
                    int pz = spanZ;

                    /*
                     * 这里取的是点,所以需要做这些偏移来记录点,而不是记录对应的Span
                     * 这里需要结合 :  方向 + 某个点 ,来理解 为什么要加这个偏移
                     *
                     *
                     *     * --- *
                     *     |  S  |
                     *     ----—-*
                     *
                     *    dir = 0 的时候,需要取左上角的点
                     *    dir = 1 的时候,需要取右上角的点
                     *    dir = 2 的时候,需要取右下角的点
                     *    dir = 3 的时候,取的就是参考点
                     *
                     *    以左下角的点为参考点,就是 dir方向对应的顺时针的点
                     *
                     */


                    /*
                     * Update the px and pz values based on current direction.
                     * The update is such that the corner being represented is
                     * clockwise from the edge the direction is currently pointing
                     * toward.
                     */

                    switch (dir)
                    {
                    case 0: pz++; break;

                    case 1: px++; pz++; break;

                    case 2: px++; break;
                    }

                    int            regionThisDirection = NULL_REGION;
                    OpenHeightSpan nSpan = span.getNeighbor(dir);
                    if (nSpan != null)
                    {
                        regionThisDirection = nSpan.regionID();
                    }

                    //这是轮廓的点
                    outContourVerts.Add(px);
                    outContourVerts.Add(py);
                    outContourVerts.Add(pz);
                    outContourVerts.Add(regionThisDirection);

                    span.flags &= ~(1 << dir);  //清除dir对应的位
                    dir         = NMGenUtility.ClockwiseRotateDir(dir);
                } //isSameRegion
                else
                {
                    //这段就是步进到下一个同Region的Span,很好理解。
                    span = span.getNeighbor(dir);
                    switch (dir)
                    {
                    case 0: spanX--; break;

                    case 1: spanZ++; break;

                    case 2: spanX++; break;

                    case 3: spanZ--; break;
                    }
                    dir = NMGenUtility.CClockwiseRotateDir(dir);
                } // no the SameRegion

                if (span == startSpan &&
                    dir == startDirection)
                {
                    break;
                }
            } //while
        }     //buildRawContour
Exemple #28
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
            public bool MoveNext()
            {
                if (mOpenHeightfield == null)
                {
                    Logger.LogError("[OpenHeightfield][OpenHeightFieldIterator][MoveNext]Null");
                    return(false);
                }

                //公有逻辑
                if (mNext != null)
                {
                    if (mNext.next() != null)
                    {
                        mNext = mNext.next();
                        return(true);
                    }
                    else
                    {
                        if (mIsReverseIter)
                        {
                            mNextWidth--;
                        }
                        else
                        {
                            mNextWidth++;
                        }
                    }
                }

                if (mIsReverseIter)
                {
                    #region 反向遍历
                    for (int depthIndex = mNextDepth; depthIndex >= 0; --depthIndex)
                    {
                        for (int widthIndex = mNextWidth; widthIndex >= 0; --widthIndex)
                        {
                            OpenHeightSpan span = mOpenHeightfield.getData(widthIndex, depthIndex);
                            if (span != null)
                            {
                                mNext      = span;
                                mNextWidth = widthIndex;
                                mNextDepth = depthIndex;
                                return(true);
                            }
                        }
                        mNextWidth = 0;
                    }

                    mNext      = null;
                    mNextDepth = -1;
                    mNextWidth = -1;

                    return(false);

                    #endregion
                }
                else
                {
                    #region 正向遍历
                    for (int depthIndex = mNextDepth; depthIndex < mOpenHeightfield.depth(); ++depthIndex)
                    {
                        for (int widthIndex = mNextWidth; widthIndex < mOpenHeightfield.width(); widthIndex++)
                        {
                            OpenHeightSpan span = mOpenHeightfield.getData(widthIndex, depthIndex);
                            if (span != null)
                            {
                                mNext      = span;
                                mNextWidth = widthIndex;
                                mNextDepth = depthIndex;
                                return(true);
                            }
                        }
                        mNextWidth = 0;
                    }

                    mNext      = null;
                    mNextDepth = -1;
                    mNextWidth = -1;

                    return(false);

                    #endregion
                } // if-else
            }     // MoveNext
        /// <summary>
        /// TODO 解决那两个地方为啥是加2
        /// </summary>
        /// <param name="inoutSpans"></param>
        /// <param name="maxIterations"></param>
        private void expandRegions(List<OpenHeightSpan> inoutSpans ,int maxIterations)
        {
            if( inoutSpans.Count == 0 )
            {
                return; 
            }

            int iterCount = 0; 
            while(true)
            {
                int skipped = 0; 

                for(int iSpan = 0; iSpan < inoutSpans.Count; ++iSpan)
                {
                    OpenHeightSpan span = inoutSpans[iSpan]; 
                    if( null == span )
                    {
                        skipped++;
                        continue; 
                    }

                    int spanRegion = NULL_REGION;
                    int regionCenterDist = int.MaxValue; 

                    //看看邻居的RegionID情况
                    for(int dir = 0; dir < 4; ++dir)
                    {
                        OpenHeightSpan nSpan = span.getNeighbor(dir); 
                        if( null == nSpan )
                        {
                            continue; 
                        }

                        if( nSpan.regionID() > NULL_REGION  )
                        {
                            //TODO 为啥是+2
                            //初始的话,这个值都是0的。
                            if( nSpan.distanceToRegionCore() + 2 < regionCenterDist  )
                            {
                                int sameRegionCount = 0; 
                                if( mUseConservativeExpansion )
                                {
                                    for( int ndir = 0; ndir < 4; ++ndir )
                                    {
                                        OpenHeightSpan nnSpan = nSpan.getNeighbor(ndir); 
                                        if( null == nnSpan )
                                        {
                                            continue; 
                                        }

                                        if( nnSpan.regionID() == nSpan.regionID() )
                                        {
                                            sameRegionCount++; 
                                        }
                                    }     
                                }

                                //如果轴对齐邻居的邻居的Region相同,也就是多于1个体素就可以了
                                if( !mUseConservativeExpansion
                                    || sameRegionCount > 1)
                                {
                                    //TODO 为啥要加2
                                    spanRegion = nSpan.regionID();
                                    regionCenterDist = nSpan.distanceToRegionCore() + 2; 
                                }
                            }
                        }
                    } // for 

                    if( spanRegion != NULL_REGION )
                    {
                        inoutSpans[iSpan] = null;
                        span.setRegionID(spanRegion); 
                    }
                    else
                    {
                        skipped++; 
                    }
                } // for 

                if( skipped == inoutSpans.Count )
                {
                    break; 
                }

                if( maxIterations != -1)
                {
                    iterCount++; 
                    if( iterCount > maxIterations )
                    {
                        break; 
                    }
                }
            }
        }