コード例 #1
0
        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");
        }
コード例 #2
0
        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;
            }
        }
        /// <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; 
        }
        /* 参考 :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; 
        }
        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);  
            }
        }