Exemplo n.º 1
0
            public SolidHeightfieldIterator(SolidHeightfield field)
            {
                if (field == null)
                {
                    Logger.LogError("[SolidHeightFieldIterator][ctor]filed Empty");
                    return;
                }

                mSoldHeightfield = field;

                Reset();
            }
Exemplo n.º 2
0
 public void reset()
 {
     voxelizationTime  = UNDEFINED;
     regionGenTime     = UNDEFINED;
     contourGenTime    = UNDEFINED;
     polyGenTime       = UNDEFINED;
     finalMeshGenTime  = UNDEFINED;
     mSolidHeightfield = null;
     mOpenHeightfield  = null;
     mContours         = null;
     mPolyMesh         = null;
 }
        /// <summary>
        /// 确保垂直方向上两个Span之间的区域不会卡头
        /// </summary>
        /// <param name="field"></param>
        private void markLowHeightSpans(SolidHeightfield field)
        {
            SolidHeightfield.SolidHeightfieldIterator iter = field.GetEnumerator();
            while (iter.MoveNext())
            {
                HeightSpan span = iter.Current;

                if ((span.flags() & SpanFlags.WALKABLE) == 0)
                {
                    continue;
                }

                int spanFloor   = span.max(); //SolidSpan的max,其实就是OpenSpan的底部
                int spanCeiling = (span.next() != null)
                    ? span.next().min() : int.MaxValue;

                if (spanCeiling - spanFloor <= mMinTraversableHeight)
                {
                    span.setFlags(span.flags() & ~SpanFlags.WALKABLE);
                }
            }
        }
        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; 
        }
Exemplo n.º 5
0
 public void setSolidHeightfield(SolidHeightfield field)
 {
     mSolidHeightfield = field;
 }
Exemplo n.º 6
0
        public TriangleMesh build(float[] vertices, int[] indices, IntermediateData outIntermediateData)
        {
            if (outIntermediateData != null)
            {
                outIntermediateData.reset();
            }

            long timerStart = 0;

            if (outIntermediateData != null)
            {
                timerStart = System.DateTime.Now.Ticks;
            }

            SolidHeightfield solidField = mSolidHeightFieldBuilder.build(vertices, indices);

            if (solidField == null ||
                !solidField.hasSpans())
            {
                return(null);
            }

            if (outIntermediateData != null)
            {
                outIntermediateData.voxelizationTime = System.DateTime.Now.Ticks - timerStart;
            }

            if (outIntermediateData != null)
            {
                outIntermediateData.setSolidHeightfield(solidField);
            }

            if (outIntermediateData != null)
            {
                timerStart = System.DateTime.Now.Ticks;
            }

            OpenHeightfield openField = mOpenHeightFieldBuilder.build(solidField, false);

            if (null == openField)
            {
                return(null);
            }

            if (outIntermediateData != null)
            {
                outIntermediateData.setOpenHeightfield(openField);
            }

            mOpenHeightFieldBuilder.generateNeighborLinks(openField);
            mOpenHeightFieldBuilder.generateDistanceField(openField);
            mOpenHeightFieldBuilder.blurDistanceField(openField);
            mOpenHeightFieldBuilder.generateRegions(openField);

            if (outIntermediateData != null)
            {
                outIntermediateData.regionGenTime = System.DateTime.Now.Ticks - timerStart;
            }

            if (outIntermediateData != null)
            {
                timerStart = System.DateTime.Now.Ticks;
            }

            ContourSet contours = mContourSetBuilder.build(openField);

            if (null == contours)
            {
                return(null);
            }

            if (outIntermediateData != null)
            {
                outIntermediateData.contourGenTime = System.DateTime.Now.Ticks - timerStart;
            }

            if (outIntermediateData != null)
            {
                outIntermediateData.setContours(contours);
            }

            if (outIntermediateData != null)
            {
                timerStart = System.DateTime.Now.Ticks;
            }

            PolyMeshField polMesh = mPolyMeshBuilder.build(contours);

            if (null == polMesh)
            {
                return(null);
            }

            if (null != outIntermediateData)
            {
                outIntermediateData.polyGenTime = System.DateTime.Now.Ticks - timerStart;
            }

            if (outIntermediateData != null)
            {
                outIntermediateData.setPolyMesh(polMesh);
            }

            if (outIntermediateData != null)
            {
                timerStart = System.DateTime.Now.Ticks;
            }

            TriangleMesh mesh = mTriangleMeshBuilder.build(polMesh, openField);

            if (outIntermediateData != null &&
                mesh != null)
            {
                outIntermediateData.finalMeshGenTime = System.DateTime.Now.Ticks - timerStart;
            }

            return(mesh);
        }
        //vertices是以(x,y,z)(x,y,z)三组三组为一个顶点
        public SolidHeightfield build(float[] vertices, int[] indices)
        {
            if (vertices == null ||
                indices == null ||
                vertices.Length % 3 != 0 ||
                indices.Length % 3 != 0)
            {
                Logger.LogError("[SolidHeightfieldBuilder][build]invalid");
                return(null);
            }

            SolidHeightfield result = new SolidHeightfield(mCellSize, mCellHeight);

            //用作分母,方便后面计算的
            float inverseCellSize   = 1 / result.cellSize();
            float inverseCellHeight = 1 / result.cellHeight();


            float xmin = vertices[0];
            float ymin = vertices[1];
            float zmin = vertices[2];
            float xmax = vertices[0];
            float ymax = vertices[1];
            float zmax = vertices[2];

            //遍历所有顶点,找出最大的Bounds
            for (int i = 3; i < vertices.Length; i += 3)
            {
                xmax = Math.Max(vertices[i], xmax);
                ymax = Math.Max(vertices[i + 1], ymax);
                zmax = Math.Max(vertices[i + 2], zmax);

                xmin = Math.Min(vertices[i], xmin);
                ymin = Math.Min(vertices[i + 1], ymin);
                zmin = Math.Min(vertices[i + 2], zmin);
            }

            result.setBounds(xmin, ymin, zmin, xmax, ymax, zmax);

            //判断哪些多边形的表面是可以行走的,坡度不能太大
            int[] polyFlags = markInputMeshWalkableFlags(vertices, indices);

            //开始对每个面进行体素化
            int polyCount = indices.Length / 3;

            for (int iPoly = 0; iPoly < polyCount; iPoly++)
            {
                voxelizeTriangle(iPoly,
                                 vertices,
                                 indices,
                                 polyFlags[iPoly],
                                 inverseCellSize,
                                 inverseCellHeight,
                                 result);
            }

            markLowHeightSpans(result);

            if (mClipLedges)
            {
                markLedgeSpans(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
                                       );
                }
            }
        }
        /// <summary>
        /// 边缘裁剪,那种像断崖式的Span也不可走的
        /// </summary>
        /// <param name="field"></param>
        private void markLedgeSpans(SolidHeightfield field)
        {
            SolidHeightfield.SolidHeightfieldIterator iter = field.GetEnumerator();
            while (iter.MoveNext())
            {
                HeightSpan span = iter.Current;

                if ((span.flags() & SpanFlags.WALKABLE) == 0)
                {
                    continue;
                }

                int widthIndex = iter.widthIndex();
                int depthIndex = iter.depthIndex();

                int currFloor   = span.max();
                int currCeiling = (span.next() != null)
                    ? span.next().min() : int.MaxValue;


                int minDistanceToNeighbor = int.MaxValue;

                for (int dir = 0; dir < 4; dir++)
                {
                    int nWidthIndex = widthIndex
                                      + BoundeField.getDirOffsetWidth(dir);
                    int nDepthIndex = depthIndex
                                      + BoundeField.getDirOffsetDepth(dir);


                    HeightSpan nSpan = field.getData(nWidthIndex, nDepthIndex);
                    if (null == nSpan)
                    {
                        //TODO 这里没有搞懂为啥是 -mMaxTraversableStep - currFloor,是为了比-mMaxTraversableStep更小,以便直接判断不能行走吗?
                        // 用大可行的距离,再减去currFloor,得到的肯定是一个更小的值。
                        // currFloor - mMaxTraversableStep 是一个最大允许的落差地板距离。注意这里只考虑下落的情况
                        minDistanceToNeighbor = Math.Min(minDistanceToNeighbor, -mMaxTraversableStep - currFloor);
                        continue;
                    }


                    /*
                     *  先考虑一种特殊情况 ,那就是
                     *  那就是nSpan.min也比currFloor要高,那么对应的
                     *  的邻居相当于也是没有Floor的,所以默认取-mMaxTraversableStep吧。
                     */

                    int nFloor   = -mMaxTraversableStep;
                    int nCeiling = nSpan.min();

                    //当前Span所处列的currCeiling和邻居的nCeiling相比,取最低的
                    if (Math.Min(currCeiling, nCeiling) - currFloor > mMinTraversableHeight)
                    {
                        minDistanceToNeighbor = Math.Min(minDistanceToNeighbor, (nFloor - currFloor));
                    }

                    for (nSpan = field.getData(nWidthIndex, nDepthIndex); nSpan != null; nSpan = nSpan.next())
                    {
                        nFloor   = nSpan.max(); //现在才开始用max考虑真正存在的Floor
                        nCeiling = (nSpan.next() != null)
                            ? nSpan.next().min() : int.MaxValue;

                        if (Math.Min(currCeiling, nCeiling) - Math.Max(currFloor, nFloor) > mMinTraversableHeight)
                        {
                            minDistanceToNeighbor = Math.Min(minDistanceToNeighbor, (nFloor - currFloor));
                        }
                    }
                }

                //如果最近的距离比较最大掉落还小的放在,那么就是不可行走的
                if (minDistanceToNeighbor < -mMaxTraversableStep)
                {
                    span.setFlags(span.flags() & ~SpanFlags.WALKABLE);
                }
            }
        }