public PolyMeshField build(ContourSet contours) { if (null == contours || 0 == contours.size()) { Logger.LogError("[PolyMeshFieldBuild][build]Param Error"); return(null); } PolyMeshField result = new PolyMeshField(contours.boundsMin(), contours.boundsMax(), contours.cellSize(), contours.cellHeight(), mMaxVertsPerPoly); int sourceVertCount = 0; int maxPossiblePolygons = 0; int maxVertsPerContour = 0; for (int contourIndex = 0; contourIndex < contours.size(); ++contourIndex) { int count = contours.get(contourIndex).vertCount; sourceVertCount += count; //过 n 边形的一个顶点,能把n边形最多分成 n - 2 三角形 maxPossiblePolygons += count - 2; maxVertsPerContour = Math.Max(maxVertsPerContour, count); } if (sourceVertCount - 1 > DEFLAG) { Logger.LogError("[PolyMeshFieldBuilder][build]sourceVertCount out of Range|{0}|{1}", DEFLAG, sourceVertCount); return(null); } int[] globalVerts = new int[sourceVertCount * 3]; //存的是索引,存的是整个轮廓的 int globalVertCount = 0; int[] globalPolys = new int[maxPossiblePolygons * mMaxVertsPerPoly]; //存的也是索引 for (int i = 0; i < globalPolys.Length; ++i) { globalPolys[i] = PolyMeshField.NULL_INDEX; } int[] globalRegions = new int[maxPossiblePolygons]; int globalPolyCount = 0; int[] contourToGlobalIndicesMap = new int[maxVertsPerContour]; //存的也是索引 Dictionary <int, int> vertIndices = new Dictionary <int, int>(); List <int> workingIndices = new List <int>(maxVertsPerContour); List <int> workingTriangles = new List <int>(maxVertsPerContour); //TODO 为什么要+1? int[] workingPolys = new int[(maxVertsPerContour + 1) * mMaxVertsPerPoly]; int workingPolyCount = 0; int[] mergeInfo = new int[3]; int[] mergedPoly = new int[mMaxVertsPerPoly]; for (int contourIndex = 0; contourIndex < contours.size(); ++contourIndex) { Contour contour = contours.get(contourIndex); //4个数据一组,(x,y,z,regionID),最少需要三个顶点 if (contour.verts.Length < 3 * 4) { Logger.LogError("[PolyMeshFieldBuilder][build]Bad Contour|{0}", contour.regionID); continue; } //初始情况下,顶点索引对应的就是对应的顶点 /* * Indices : 0 1 2 3 4 5 6 .... * 0 1 2 3 4 5 6 .... */ workingIndices.Clear(); for (int i = 0; i < contour.vertCount; ++i) //这个顶点数是按四个一组的 { workingIndices.Add(i); } // for contour.vert //三角剖分 int triangleCount = triangulate(contour.verts, ref workingIndices, ref workingTriangles); if (triangleCount <= 0) { Logger.LogError("[PolyMeshField][build]Triangulate Contour|{0}", contour.regionID); continue; } for (int iContourVert = 0; iContourVert < contour.vertCount; ++iContourVert) { int pContourVert = iContourVert * 4; int vertHash = getHashCode(contour.verts[pContourVert], contour.verts[pContourVert + 1], contour.verts[pContourVert + 2]); //vertIndices 里面储存的是根据顶点xyz hash出来的key,对应的在全局顶点表中的索引 //全局顶点表 以 xyz 3个为一组,储存轮廓的顶点 int iGlobalVert = 0; if (!vertIndices.TryGetValue(vertHash, out iGlobalVert)) { iGlobalVert = globalVertCount; globalVertCount++; vertIndices.Add(vertHash, iGlobalVert); int newVertsBase = iGlobalVert * 3; globalVerts[newVertsBase] = contour.verts[pContourVert]; globalVerts[newVertsBase + 1] = contour.verts[pContourVert + 1]; globalVerts[newVertsBase + 2] = contour.verts[pContourVert + 2]; } //这个东东是临时用的,就是记录 轮廓中某个顶点的索引,对应的是在全局顶点表中索引 //Contour Vertex index -> global vertex index contourToGlobalIndicesMap[iContourVert] = iGlobalVert; } // for iContourVert for (int i = 0; i < workingPolys.Length; ++i) { workingPolys[i] = PolyMeshField.NULL_INDEX; } // for workingPolys //只有这个阶段是三角形 workingPolyCount = 0; for (int i = 0; i < triangleCount; ++i) { /* * workingTraingles 储存的是上一步三角剖分的三角形的顶点索引 * || * V * contourToGlobalIndicesMap 储存的是轮廓顶点索引,对应在全局顶点表的索引关系 * || * V * workingPolys 中 储存的则是全局的顶点索引 * * */ int polyIdxBase = workingPolyCount * mMaxVertsPerPoly; int triangleIdxBase = i * 3; workingPolys[polyIdxBase] = contourToGlobalIndicesMap[workingTriangles[triangleIdxBase]]; workingPolys[polyIdxBase + 1] = contourToGlobalIndicesMap[workingTriangles[triangleIdxBase + 1]]; workingPolys[polyIdxBase + 2] = contourToGlobalIndicesMap[workingTriangles[triangleIdxBase] + 2]; workingPolyCount++; } //triangleCount //合并三角形 if (mMaxVertsPerPoly > 3) { while (true) { int longestMergeEdge = -1; int pBestPolyA = -1; int iPolyAVert = -1; int pBestPolyB = -1; int iPolyBVert = -1; for (int iPolyA = 0; iPolyA < workingPolyCount - 1; ++iPolyA) { for (int iPolyB = iPolyA + 1; iPolyB < workingPolyCount; ++iPolyB) { //因为一个多边形最多有mMaxVertsPerPoly的点,所以 //多边形的真正起点为就是用多边形的索引乘以顶点数 //Can polyB merge with polyA? getPolyMergeInfo(iPolyA * mMaxVertsPerPoly, iPolyB * mMaxVertsPerPoly, workingPolys, globalVerts, result.maxVertsPerPoly(), out mergeInfo); if (mergeInfo[0] > longestMergeEdge) { longestMergeEdge = mergeInfo[0]; pBestPolyA = iPolyA * mMaxVertsPerPoly; iPolyAVert = mergeInfo[1]; pBestPolyB = iPolyB * mMaxVertsPerPoly; iPolyBVert = mergeInfo[2]; } } // for iPolyB } // for iPolyA if (longestMergeEdge <= 0) { break; } for (int i = 0; i < mergedPoly.Length; ++i) { mergedPoly[i] = PolyMeshField.NULL_INDEX; } int vertCountA = PolyMeshField.getPolyVertCount(pBestPolyA, workingPolys, result.maxVertsPerPoly()); int vertCountB = PolyMeshField.getPolyVertCount(pBestPolyB, workingPolys, result.maxVertsPerPoly()); int position = 0; for (int i = 0; i < vertCountA - 1; ++i) { //pBestPolyA 为多边形对应的基索引 //iPolyAVert 为共享的一个端点 //+1 指的是共享点下一个顶点开始 int polyIdx = pBestPolyA + ((iPolyAVert + 1 + i) % vertCountA); mergedPoly[position++] = workingPolys[polyIdx]; } // for vertCountA for (int i = 0; i < vertCountB - 1; ++i) { int polyIdx = pBestPolyB + ((iPolyBVert + 1 + i) % vertCountB); mergedPoly[position++] = workingPolys[polyIdx]; } //将合并之后的顶点拷到A指定的多边形 Array.Copy(mergedPoly, 0, workingPolys, pBestPolyA, mMaxVertsPerPoly); //将多边形B删除 Array.Copy(workingPolys, pBestPolyB + mMaxVertsPerPoly, workingPolys, pBestPolyB, workingPolys.Length - pBestPolyB - mMaxVertsPerPoly); workingPolyCount--; } // while true } // if MaxVertsPerPoly for (int i = 0; i < workingPolyCount; ++i) { Array.Copy(workingPolys, i * mMaxVertsPerPoly, globalPolys, globalPolyCount * mMaxVertsPerPoly, mMaxVertsPerPoly); globalRegions[globalPolyCount] = contour.regionID; globalPolyCount++; } } // for contours //xyz为一组 result.verts = new int[globalVertCount * 3]; Array.Copy(globalVerts, 0, result.verts, 0, globalVertCount * 3); result.polys = new int[globalPolyCount * mMaxVertsPerPoly * 2]; for (int iPoly = 0; iPoly < globalPolyCount; ++iPoly) { int pPoly = iPoly * mMaxVertsPerPoly; //第几个Poly的索引 for (int offset = 0; offset < mMaxVertsPerPoly; ++offset) { //result里的多边形是以 2 * mMaxVertsPerPoly为一组的 //第一组 mMaxVertsPerPoly 是多边形自身的数据 //第二组 mMaxVertsPertPol 是邻接多边形的数据 //而globalPolys就是以一组 mMaxVertsPerPoly result.polys[pPoly * 2 + offset] = globalPolys[pPoly + offset]; result.polys[pPoly * 2 + mMaxVertsPerPoly + offset] = PolyMeshField.NULL_INDEX; } } // for result.polyRegions = new int[globalPolyCount]; Array.Copy(globalRegions, 0, result.polyRegions, 0, globalPolyCount); buildAdjacencyData(result); return(result); } // build
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