/// <summary> /// Rasterizes a triangle using conservative voxelization. /// </summary> /// <param name="a">The first vertex of the triangle.</param> /// <param name="b">The second vertex of the triangle.</param> /// <param name="c">The third vertex of the triangle.</param> /// <param name="area">The area flags for the triangle.</param> public void RasterizeTriangle(ref Vector3 a, ref Vector3 b, ref Vector3 c, Area area) { //distances buffer for ClipPolygonToBounds float[] distances = new float[12]; float invCellSize = 1f / cellSize; float invCellHeight = 1f / cellHeight; float boundHeight = bounds.Max.Y - bounds.Min.Y; //calculate the triangle's bounding box BBox3 bbox; Triangle3.GetBoundingBox(ref a, ref b, ref c, out bbox); //make sure that the triangle is at least in one cell. if (!BBox3.Overlapping(ref bbox, ref bounds)) { return; } //figure out which rows. int z0 = (int)((bbox.Min.Z - bounds.Min.Z) * invCellSize); int z1 = (int)((bbox.Max.Z - bounds.Min.Z) * invCellSize); //clamp to the field boundaries. MathHelper.Clamp(ref z0, 0, length - 1); MathHelper.Clamp(ref z1, 0, length - 1); Vector3[] inVerts = new Vector3[7], outVerts = new Vector3[7], inRowVerts = new Vector3[7]; for (int z = z0; z <= z1; z++) { //copy the original vertices to the array. inVerts[0] = a; inVerts[1] = b; inVerts[2] = c; //clip the triangle to the row int nvrow = 3; float cz = bounds.Min.Z + z * cellSize; nvrow = MathHelper.ClipPolygonToPlane(inVerts, outVerts, distances, nvrow, 0, 1, -cz); if (nvrow < 3) { continue; } nvrow = MathHelper.ClipPolygonToPlane(outVerts, inRowVerts, distances, nvrow, 0, -1, cz + cellSize); if (nvrow < 3) { continue; } float minX = inRowVerts[0].X, maxX = minX; for (int i = 1; i < nvrow; i++) { float vx = inRowVerts[i].X; if (minX > vx) { minX = vx; } if (maxX < vx) { maxX = vx; } } int x0 = (int)((minX - bounds.Min.X) * invCellSize); int x1 = (int)((maxX - bounds.Min.X) * invCellSize); MathHelper.Clamp(ref x0, 0, width - 1); MathHelper.Clamp(ref x1, 0, width - 1); for (int x = x0; x <= x1; x++) { //clip the triangle to the column int nv = nvrow; float cx = bounds.Min.X + x * cellSize; nv = MathHelper.ClipPolygonToPlane(inRowVerts, outVerts, distances, nv, 1, 0, -cx); if (nv < 3) { continue; } nv = MathHelper.ClipPolygonToPlane(outVerts, inVerts, distances, nv, -1, 0, cx + cellSize); if (nv < 3) { continue; } //calculate the min/max of the polygon float polyMin = inVerts[0].Y, polyMax = polyMin; for (int i = 1; i < nv; i++) { float y = inVerts[i].Y; polyMin = Math.Min(polyMin, y); polyMax = Math.Max(polyMax, y); } //normalize span bounds to bottom of heightfield float boundMinY = bounds.Min.Y; polyMin -= boundMinY; polyMax -= boundMinY; //if the spans are outside the heightfield, skip. if (polyMax < 0f || polyMin > boundHeight) { continue; } //clamp the span to the heightfield. if (polyMin < 0) { polyMin = 0; } if (polyMax > boundHeight) { polyMax = boundHeight; } //snap to grid int spanMin = (int)(polyMin * invCellHeight); int spanMax = (int)Math.Ceiling(polyMax * invCellHeight); //add the span cells[z * width + x].AddSpan(new Span(spanMin, spanMax, area)); } } }