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; 
        }
        //按面来体素化?
        private static void voxelizeTriangle(int polyIndex,
                                             float[] vertices,
                                             int[] indices,
                                             int polyFlags,
                                             float inverseCellSize,
                                             float inverseCellHeight,
                                             SolidHeightfield inoutField) //本来就是引用的话,传进来就会被修改
        {
            int pPoly = polyIndex * 3;

            //一个面的三个点
            float[] triVerts = new float[]
            {
                vertices[indices[pPoly] * 3],         //VertA x
                vertices[indices[pPoly] * 3 + 1],     //VertA y
                vertices[indices[pPoly] * 3 + 2],     //VertA z
                vertices[indices[pPoly + 1] * 3],     //VertB x
                vertices[indices[pPoly + 1] * 3 + 1], //VertB y
                vertices[indices[pPoly + 1] * 3 + 2], //VertB z
                vertices[indices[pPoly + 2] * 3],     //VertC x
                vertices[indices[pPoly + 2] * 3 + 1], //VertC y
                vertices[indices[pPoly + 2] * 3 + 2], //VertC z
            };

            //三角面的xz投影包围盒
            float[] triBoundsMin = new float[] {
                triVerts[0], triVerts[1], triVerts[2]
            };
            float[] triBoundsMax = new float[] {
                triVerts[0], triVerts[1], triVerts[2]
            };

            // int vertPointer = 3  相当于  int i = 1 ,因为是从第二个顶点开始算起,数组长度总共为9
            // Loop through all vertices to determine the actual bounding box.
            for (int vertPointer = 3; vertPointer < 9; vertPointer += 3)
            {
                triBoundsMin[0] = Math.Min(triBoundsMin[0], triVerts[vertPointer]);
                triBoundsMin[1] = Math.Min(triBoundsMin[1], triVerts[vertPointer + 1]);
                triBoundsMin[2] = Math.Min(triBoundsMin[2], triVerts[vertPointer + 2]);
                triBoundsMax[0] = Math.Min(triBoundsMax[1], triVerts[vertPointer]);
                triBoundsMax[1] = Math.Min(triBoundsMax[2], triVerts[vertPointer + 1]);
                triBoundsMax[2] = Math.Min(triBoundsMax[3], triVerts[vertPointer + 2]);
            }

            if (!inoutField.overlaps(triBoundsMin, triBoundsMax))
            {
                return;
            }

            //将三角形的坐标转换成对应的cell坐标系,就是具体对应到哪个width和depth的Column
            //两个坐标相差得到距离,再除以cell的大小,得到每个坐标对应落在哪个Cell
            int triWidthMin = (int)((triBoundsMin[0] - inoutField.boundsMin()[0]) * inverseCellSize);
            int triDepthMin = (int)((triBoundsMin[2] - inoutField.boundsMin()[2]) * inverseCellSize);
            int triWidthMax = (int)((triBoundsMax[0] - inoutField.boundsMin()[0]) * inverseCellSize);
            int triDepthMax = (int)((triBoundsMax[2] - inoutField.boundsMin()[2]) * inverseCellSize);

            triWidthMin = clamp(triWidthMin, 0, inoutField.width() - 1);
            triDepthMin = clamp(triDepthMin, 0, inoutField.depth() - 1);
            triWidthMax = clamp(triWidthMax, 0, inoutField.width() - 1);
            triDepthMax = clamp(triWidthMax, 0, inoutField.depth() - 1);



            //从论文的图示来看,三角形与矩形的交点组成的凸包最多有7个点。
            float[] inVerts    = new float[21];
            float[] outVerts   = new float[21];
            float[] inrowVerts = new float[21];


            float fieldHeight = inoutField.boundsMax()[1] - inoutField.boundsMin()[1];

            //http://www.sunshine2k.de/coding/java/SutherlandHodgman/SutherlandHodgman.html
            for (int depthIndex = triDepthMin; depthIndex <= triDepthMax; ++depthIndex)
            {
                Array.Copy(triVerts, 0, inVerts, 0, triVerts.Length);

                int intermediateVertCount = 3;
                //将体素depth坐标为 depthIndex 对应的 Edge 转变为 笛卡尔坐标的 z.
                //其实就是从体素坐标系转变为 笛卡尔坐标系
                float rowWorldZ = inoutField.boundsMin()[2]
                                  + (depthIndex * inoutField.cellSize());

                //用 z = rowWorldZ的直线裁剪三角形
                intermediateVertCount = clipPoly(inVerts,
                                                 intermediateVertCount,
                                                 ref outVerts,
                                                 0,
                                                 1,
                                                 -rowWorldZ);

                if (intermediateVertCount < 3)
                {
                    continue;
                }

                //用 z = -(rowWorldZ + inoutField.cellSize) 的直线裁剪三角形
                intermediateVertCount = clipPoly(outVerts,
                                                 intermediateVertCount,
                                                 ref inrowVerts,
                                                 0,
                                                 -1,
                                                 rowWorldZ + inoutField.cellSize());

                if (intermediateVertCount < 3)
                {
                    continue;
                }

                for (int widthIndex = triWidthMin; widthIndex <= triWidthMax; ++widthIndex)
                {
                    int vertCount = intermediateVertCount;
                    //将体素width坐标为 widthIndex 转变为 笛卡尔坐标的 x
                    float colWorldX = inoutField.boundsMin()[0]
                                      + (widthIndex * inoutField.cellSize());

                    //用直线 x = colWorldX的直线进行裁剪
                    vertCount = clipPoly(inrowVerts, vertCount, ref outVerts, 1, 0, -colWorldX);
                    if (vertCount < 3)
                    {
                        continue;
                    }

                    //用直线 x = -(colWorldX + CellSize) 进行裁剪
                    vertCount = clipPoly(outVerts,
                                         vertCount,
                                         ref inrowVerts,
                                         -1,
                                         0,
                                         colWorldX + inoutField.cellSize());

                    if (vertCount < 3)
                    {
                        continue;
                    }

                    float heightMin = inVerts[1];
                    float heightMax = inVerts[1];

                    for (int i = 1; i < vertCount; ++i)
                    {
                        heightMin = Math.Min(heightMin, inVerts[i * 3 + 1]);
                        heightMax = Math.Min(heightMax, inVerts[i * 3 + 1]);
                    }


                    //heigtMin和heighMax都是坐标,所以需要将其转换成相对于 inoutField 地板的高度,也就是一个标量
                    heightMin -= inoutField.boundsMin()[1];
                    heightMax -= inoutField.boundsMin()[1];

                    if (heightMax < 0.0f || heightMin > fieldHeight)
                    {
                        Logger.LogWarning("[SolidHeightfieldBuilder][voxelizeTriangle]Invalid|{0}|{1}|{2}|{3}|{4}", widthIndex, depthIndex, heightMin, heightMax, fieldHeight);
                        continue;
                    }

                    if (heightMin < 0.0f)
                    {
                        Logger.LogWarning("[SolidHeightfieldBuilder][voxelizeTriangle]heigtMin Change|{0}|{1}|{2}|{3}|", widthIndex, depthIndex, heightMin, inoutField.boundsMin()[1]);
                        heightMin = inoutField.boundsMin()[1];
                    }
                    if (heightMax > fieldHeight)
                    {
                        Logger.LogWarning("[SolidHeightfieldBuilder][voxelizeTriangle]heightMax Change|{0}|{1}|{2}|{3}|", widthIndex, depthIndex, heightMax, fieldHeight, inoutField.boundsMin()[1]);
                        heightMax = inoutField.boundsMax()[1];
                    }

                    //将坐标重新转换为Grid的坐标,其实也是表示这三角面最低点和最高点,分别属于哪两个体素
                    int heightIndexMin = clamp(
                        (int)Math.Floor(heightMin * inverseCellHeight),
                        0,
                        short.MaxValue
                        );
                    int heightIndexMax = clamp(
                        (int)Math.Ceiling(heightMax * inverseCellHeight),
                        0,
                        short.MaxValue
                        );

                    inoutField.addData(widthIndex,
                                       depthIndex,
                                       heightIndexMin,
                                       heightIndexMax,
                                       polyFlags
                                       );
                }
            }
        }