Пример #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);
        }
Пример #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();
                    }
                }
            }
        }
Пример #3
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);
                }
            }
        }
Пример #4
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);
 }
Пример #5
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);
 }
        /* 参考 :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(); 
        }
        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; 
        }
        /// <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; 
                    }
                }
            }
        }
Пример #10
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);
        }
Пример #11
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;
            }
        }
Пример #12
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);
        }
Пример #13
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);
            }
        }
Пример #14
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
Пример #15
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