}// func

        private static void getPolyMergeInfo(int polyAPointer, int polyBPointer,
                                             int[] polys, int[] verts,
                                             int maxVertsPerPoly, out int[] outResult)
        {
            outResult = new int[3];

            outResult[0] = -1;
            outResult[1] = -1;
            outResult[2] = -1;

            int vertCountA = PolyMeshField.getPolyVertCount(polyAPointer, polys, maxVertsPerPoly);
            int vertCountB = PolyMeshField.getPolyVertCount(polyBPointer, polys, maxVertsPerPoly);

            //减2 是因为合并了一条边,少了两个顶点
            if (vertCountA + vertCountB - 2 > maxVertsPerPoly)
            {
                return;
            }

            for (int iPolyVertA = 0; iPolyVertA < vertCountA; ++iPolyVertA)
            {
                int iVertA     = polys[polyAPointer + iPolyVertA];
                int iVertANext = polys[polyAPointer + getNextIndex(iPolyVertA, vertCountA)];

                for (int iPolyVertB = 0; iPolyVertB < vertCountB; ++iPolyVertB)
                {
                    int iVertB     = polys[polyBPointer + iPolyVertB];
                    int iVertBNext = polys[polyBPointer + getNextIndex(iPolyVertB, vertCountB)];

                    /*
                     *    同一种顶点顺序,两个点是重合的话, 就是共享边了。
                     *    A/B+1
                     *     \
                     *      \
                     *      A+1/B
                     */

                    if (iVertA == iVertBNext &&
                        iVertANext == iVertB)
                    {
                        outResult[1] = iPolyVertA;
                        outResult[2] = iPolyVertB;
                    }
                } //iPolyB
            }     // iPolyA


            if (-1 == outResult[1])
            {
                return;
            }

            //对顺时针包含的多边形有效
            int pSharedVertMinus;
            int pSharedVert;
            int pSharedVertPlus;

            //为什么是3呢?是因为xyz为一组
            //A-1
            pSharedVertMinus = polys[polyAPointer + getPreviousIndex(outResult[1], vertCountA)] * 3;
            //A
            pSharedVert = polys[polyAPointer + outResult[1]] * 3;
            //????TODO,用B的,还是B+2了卧槽。。。
            //我艹,B+2 对应的就是图上的A+1,这个并不是真是意义是A点的下一个点
            pSharedVertPlus = polys[polyBPointer + ((outResult[2] + 2) % vertCountB)] * 3;

            if (!isLeft(verts[pSharedVert], verts[pSharedVert + 2],
                        verts[pSharedVertMinus], verts[pSharedVertMinus + 2],
                        verts[pSharedVertPlus], verts[pSharedVertPlus + 2]
                        ))
            {
                return;
            }

            pSharedVertMinus = polys[polyBPointer + getPreviousIndex(outResult[2], vertCountB)] * 3;
            pSharedVert      = polys[polyBPointer + outResult[2]] * 3;
            pSharedVertPlus  = polys[polyAPointer + ((outResult[1] + 2) % vertCountA)] * 3;

            if (!isLeft(verts[pSharedVert], verts[pSharedVert + 2],
                        verts[pSharedVertMinus], verts[pSharedVertMinus + 2],
                        verts[pSharedVertPlus], verts[pSharedVertPlus + 2]))
            {
                return;
            }

            //共享边
            pSharedVertMinus = polys[polyAPointer + outResult[1]] * 3;
            pSharedVert      = polys[polyAPointer + getNextIndex(outResult[1], vertCountA)] * 3;

            int deltaX = verts[pSharedVertMinus + 0] - verts[pSharedVert + 0];
            int deltaZ = verts[pSharedVertMinus + 2] - verts[pSharedVert + 2];

            outResult[0] = deltaX * deltaX + deltaZ * deltaZ;
        }
        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