/// <summary> /// Find all the polygons within a certain bounding box. /// </summary> /// <param name="tile">Current tile</param> /// <param name="qbounds">The bounds</param> /// <param name="polys">List of polygons</param> /// <returns>Number of polygons found</returns> public int QueryPolygonsInTile(MeshTile tile, BBox3 qbounds, List <PolyId> polys) { if (tile.BVTree.Count != 0) { int node = 0; int end = tile.Header.BvNodeCount; Vector3 tbmin = tile.Header.Bounds.Min; Vector3 tbmax = tile.Header.Bounds.Max; //Clamp query box to world box Vector3 qbmin = qbounds.Min; Vector3 qbmax = qbounds.Max; PolyBounds b; float bminx = MathHelper.Clamp(qbmin.X, tbmin.X, tbmax.X) - tbmin.X; float bminy = MathHelper.Clamp(qbmin.Y, tbmin.Y, tbmax.Y) - tbmin.Y; float bminz = MathHelper.Clamp(qbmin.Z, tbmin.Z, tbmax.Z) - tbmin.Z; float bmaxx = MathHelper.Clamp(qbmax.X, tbmin.X, tbmax.X) - tbmin.X; float bmaxy = MathHelper.Clamp(qbmax.Y, tbmin.Y, tbmax.Y) - tbmin.Y; float bmaxz = MathHelper.Clamp(qbmax.Z, tbmin.Z, tbmax.Z) - tbmin.Z; const int MinMask = unchecked ((int)0xfffffffe); b.Min.X = (int)(bminx * tile.Header.BvQuantFactor) & MinMask; b.Min.Y = (int)(bminy * tile.Header.BvQuantFactor) & MinMask; b.Min.Z = (int)(bminz * tile.Header.BvQuantFactor) & MinMask; b.Max.X = (int)(bmaxx * tile.Header.BvQuantFactor + 1) | 1; b.Max.Y = (int)(bmaxy * tile.Header.BvQuantFactor + 1) | 1; b.Max.Z = (int)(bmaxz * tile.Header.BvQuantFactor + 1) | 1; //traverse tree PolyId polyBase = GetPolyRefBase(tile); while (node < end) { BVTree.Node bvNode = tile.BVTree[node]; bool overlap = PolyBounds.Overlapping(ref b, ref bvNode.Bounds); bool isLeafNode = bvNode.Index >= 0; if (isLeafNode && overlap) { if (polys.Count < polys.Capacity) { PolyId polyRef; PolyId.SetPolyIndex(ref polyBase, bvNode.Index, out polyRef); polys.Add(polyRef); } } if (overlap || isLeafNode) { node++; } else { int escapeIndex = -bvNode.Index; node += escapeIndex; } } return(polys.Count); } else { BBox3 b; PolyId polyBase = GetPolyRefBase(tile); for (int i = 0; i < tile.Header.PolyCount; i++) { var poly = tile.Polys[i]; //don't return off-mesh connection polygons if (poly.PolyType == PolygonType.OffMeshConnection) { continue; } //calculate polygon bounds b.Max = b.Min = tile.Verts[poly.Verts[0]]; for (int j = 1; j < poly.VertCount; j++) { Vector3 v = tile.Verts[poly.Verts[j]]; Vector3Extensions.ComponentMin(ref b.Min, ref v, out b.Min); Vector3Extensions.ComponentMax(ref b.Max, ref v, out b.Max); } if (BBox3.Overlapping(ref qbounds, ref b)) { if (polys.Count < polys.Capacity) { PolyId polyRef; PolyId.SetPolyIndex(ref polyBase, i, out polyRef); polys.Add(polyRef); } } } return(polys.Count); } }
/// <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)); } } }