public bool MoveNext() { if (mNext != null) { //下一个的下一个 if (mNext.next() != null) { mNext = mNext.next(); return(true); } else { //当前列已经没有下一个,移动到下一个Grid mNextWidth++; //寻找x方向的下一个 } } if (mSoldHeightfield != null) { for (int depthIndex = mNextDepth; depthIndex < mSoldHeightfield.depth(); depthIndex++) { for (int widthIndex = mNextWidth; widthIndex < mSoldHeightfield.width(); widthIndex++) { int gridIndex = mSoldHeightfield.GetGridIndex(widthIndex, depthIndex); HeightSpan span = mSoldHeightfield.mSpans[gridIndex]; if (span != null) { mNext = span; mNextWidth = widthIndex; mNextDepth = depthIndex; return(true); } } mNextWidth = 0; } } mNext = null; mNextDepth = -1; mNextWidth = -1; return(false); }
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 ); } } }