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
Esempio n. 2
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