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; }
/// <summary> /// 边缘裁剪,那种像断崖式的Span也不可走的 /// </summary> /// <param name="field"></param> private void markLedgeSpans(SolidHeightfield field) { SolidHeightfield.SolidHeightfieldIterator iter = field.GetEnumerator(); while (iter.MoveNext()) { HeightSpan span = iter.Current; if ((span.flags() & SpanFlags.WALKABLE) == 0) { continue; } int widthIndex = iter.widthIndex(); int depthIndex = iter.depthIndex(); int currFloor = span.max(); int currCeiling = (span.next() != null) ? span.next().min() : int.MaxValue; int minDistanceToNeighbor = int.MaxValue; for (int dir = 0; dir < 4; dir++) { int nWidthIndex = widthIndex + BoundeField.getDirOffsetWidth(dir); int nDepthIndex = depthIndex + BoundeField.getDirOffsetDepth(dir); HeightSpan nSpan = field.getData(nWidthIndex, nDepthIndex); if (null == nSpan) { //TODO 这里没有搞懂为啥是 -mMaxTraversableStep - currFloor,是为了比-mMaxTraversableStep更小,以便直接判断不能行走吗? // 用大可行的距离,再减去currFloor,得到的肯定是一个更小的值。 // currFloor - mMaxTraversableStep 是一个最大允许的落差地板距离。注意这里只考虑下落的情况 minDistanceToNeighbor = Math.Min(minDistanceToNeighbor, -mMaxTraversableStep - currFloor); continue; } /* * 先考虑一种特殊情况 ,那就是 * 那就是nSpan.min也比currFloor要高,那么对应的 * 的邻居相当于也是没有Floor的,所以默认取-mMaxTraversableStep吧。 */ int nFloor = -mMaxTraversableStep; int nCeiling = nSpan.min(); //当前Span所处列的currCeiling和邻居的nCeiling相比,取最低的 if (Math.Min(currCeiling, nCeiling) - currFloor > mMinTraversableHeight) { minDistanceToNeighbor = Math.Min(minDistanceToNeighbor, (nFloor - currFloor)); } for (nSpan = field.getData(nWidthIndex, nDepthIndex); nSpan != null; nSpan = nSpan.next()) { nFloor = nSpan.max(); //现在才开始用max考虑真正存在的Floor nCeiling = (nSpan.next() != null) ? nSpan.next().min() : int.MaxValue; if (Math.Min(currCeiling, nCeiling) - Math.Max(currFloor, nFloor) > mMinTraversableHeight) { minDistanceToNeighbor = Math.Min(minDistanceToNeighbor, (nFloor - currFloor)); } } } //如果最近的距离比较最大掉落还小的放在,那么就是不可行走的 if (minDistanceToNeighbor < -mMaxTraversableStep) { span.setFlags(span.flags() & ~SpanFlags.WALKABLE); } } }