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"); }
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); } }