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 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 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