// Copyright (c) 2013-2015 Robert Rouhani and other contributors (see CONTRIBUTORS file). // Licensed under the MIT License - https://raw.github.com/Robmaister/SharpNav/master/LICENSE using System; using System.Collections.Generic; using SharpNav.Geometry; #if MONOGAME using Vector3 = Microsoft.Xna.Framework.Vector3; #elif OPENTK using Vector3 = OpenTK.Vector3; #elif SHARPDX using Vector3 = SharpDX.Vector3; #endif namespace SharpNav { /// /// This file contains various methods to rasterize objects into voxel data. /// public partial class Heightfield { //private ConcurrentQueue> spanQueue; /// /// Rasterizes several triangles at once from an indexed array with per-triangle area flags. /// /// An array of vertices. /// An array of indices. /// An array of area flags, one for each triangle. public void RasterizeTrianglesIndexedWithAreas(Vector3[] verts, int[] inds, Area[] areas) { RasterizeTrianglesIndexedWithAreas(verts, inds, 0, 1, 0, inds.Length / 3, areas); } /// /// Rasterizes several triangles at once from an indexed array with per-triangle area flags. /// /// An array of vertices. /// An array of indices. /// An offset into the vertex array. /// The number of array elements that make up a vertex. A value of 0 is interpreted as tightly-packed data (one Vector3 per vertex). /// An offset into the index array. /// The number of triangles to rasterize. /// An array of area flags, one for each triangle. public void RasterizeTrianglesIndexedWithAreas(Vector3[] verts, int[] inds, int vertOffset, int vertStride, int indexOffset, int triCount, Area[] areas) { int indexEnd = triCount * 3 + indexOffset; if (verts == null) throw new ArgumentNullException("verts"); if (inds == null) throw new ArgumentNullException("inds"); if (indexEnd > inds.Length) throw new ArgumentOutOfRangeException("indexCount", "The specified index offset and length end outside the provided index array."); if (vertOffset < 0) throw new ArgumentOutOfRangeException("vertOffset", "vertOffset must be greater than or equal to 0."); if (vertStride < 0) throw new ArgumentOutOfRangeException("vertStride", "vertStride must be greater than or equal to 0."); else if (vertStride == 0) vertStride = 1; if (areas.Length < triCount) throw new ArgumentException("There must be at least as many AreaFlags as there are triangles.", "areas"); for (int i = indexOffset, j = 0; i < indexEnd; i += 3, j++) { int indA = inds[i] * vertStride + vertOffset; int indB = inds[i + 1] * vertStride + vertOffset; int indC = inds[i + 2] * vertStride + vertOffset; RasterizeTriangle(ref verts[indA], ref verts[indB], ref verts[indC], areas[j]); } } /// /// Rasterizes several triangles at once from an indexed array with per-triangle area flags. /// /// An array of vertices. /// An array of indices. /// An array of area flags, one for each triangle. public void RasterizeTrianglesIndexedWithAreas(float[] verts, int[] inds, Area[] areas) { RasterizeTrianglesIndexedWithAreas(verts, inds, 0, 3, 0, inds.Length / 3, areas); } /// /// Rasterizes several triangles at once from an indexed array with per-triangle area flags. /// /// An array of vertices. /// An array of indices. /// An offset into the vertex array. /// The number of array elements that make up a vertex. A value of 0 is interpreted as tightly-packed data (3 floats per vertex). /// An offset into the index array. /// The number of triangles to rasterize. /// An array of area flags, one for each triangle. public void RasterizeTrianglesIndexedWithAreas(float[] verts, int[] inds, int floatOffset, int floatStride, int indexOffset, int triCount, Area[] areas) { int indexEnd = triCount * 3 + indexOffset; if (verts == null) throw new ArgumentNullException("verts"); if (inds == null) throw new ArgumentNullException("inds"); if (indexEnd > inds.Length) throw new ArgumentOutOfRangeException("indexCount", "The specified index offset and length end outside the provided index array."); if (floatOffset < 0) throw new ArgumentOutOfRangeException("floatOffset", "floatOffset must be greater than or equal to 0."); if (floatStride < 0) throw new ArgumentOutOfRangeException("floatStride", "floatStride must be greater than or equal to 0."); else if (floatStride == 0) floatStride = 3; if (areas.Length < triCount) throw new ArgumentException("There must be at least as many AreaFlags as there are triangles.", "areas"); Vector3 a, b, c; for (int i = indexOffset, j = 0; i < indexEnd; i += 3, j++) { int indA = inds[i] * floatStride + floatOffset; int indB = inds[i + 1] * floatStride + floatOffset; int indC = inds[i + 2] * floatStride + floatOffset; a.X = verts[indA]; a.Y = verts[indA + 1]; a.Z = verts[indA + 2]; b.X = verts[indB]; b.Y = verts[indB + 1]; b.Z = verts[indB + 2]; c.X = verts[indC]; c.Y = verts[indC + 1]; c.Z = verts[indC + 2]; RasterizeTriangle(ref a, ref b, ref c, areas[j]); } } /// /// Rasterizes several triangles at once with per-triangle area flags. /// /// An array of triangles. /// An array of area flags, one for each triangle. public void RasterizeTrianglesWithAreas(Triangle3[] tris, Area[] areas) { RasterizeTrianglesWithAreas(tris, 0, tris.Length, areas); } /// /// Rasterizes several triangles at once with per-triangle area flags. /// /// An array of triangles. /// An offset into the array. /// The number of triangles to rasterize, starting from the offset. /// An array of area flags, one for each triangle. public void RasterizeTrianglesWithAreas(Triangle3[] tris, int triOffset, int triCount, Area[] areas) { int triEnd = triOffset + triCount; if (tris == null) throw new ArgumentNullException("verts"); if (triOffset < 0) throw new ArgumentOutOfRangeException("triOffset", "triOffset must be greater than or equal to 0."); if (triCount < 0) throw new ArgumentOutOfRangeException("triCount", "triCount must be greater than or equal to 0."); if (triEnd > tris.Length) throw new ArgumentOutOfRangeException("triCount", "The specified offset and count end outside the bounds of the provided array."); if (areas.Length < triCount) throw new ArgumentException("There must be at least as many AreaFlags as there are triangles.", "areas"); for (int i = triOffset, j = 0; i < triEnd; i++, j++) RasterizeTriangle(ref tris[i].A, ref tris[i].B, ref tris[i].C, areas[j]); } /// /// Rasterizes several triangles at once with per-triangle area flags. /// /// /// If the length of the array is not a multiple of 3, the extra vertices at the end will be skipped. /// /// An array of vertices. /// An array of area flags, one for each triangle. public void RasterizeTrianglesWithAreas(Vector3[] verts, Area[] areas) { RasterizeTrianglesWithAreas(verts, 0, 1, verts.Length / 3, areas); } /// /// Rasterizes several triangles at once with per-triangle area flags. /// /// An array of vertices. /// An offset into the array. /// The number of array elements that make up a vertex. A value of 0 is interpreted as tightly-packed data (1 Vector3 per vertex). /// The number of triangles to rasterize, starting from the offset. /// An array of area flags, one for each triangle. public void RasterizeTrianglesWithAreas(Vector3[] verts, int vertOffset, int vertStride, int triCount, Area[] areas) { if (verts == null) throw new ArgumentNullException("verts"); if (vertOffset < 0) throw new ArgumentOutOfRangeException("vertOffset", "vertOffset must be greater than or equal to 0."); if (triCount < 0) throw new ArgumentOutOfRangeException("triCount", "triCount must be greater than or equal to 0."); if (vertStride < 0) throw new ArgumentOutOfRangeException("vertStride", "vertStride must be greater than or equal to 0."); else if (vertStride == 0) vertStride = 1; int vertEnd = triCount * vertStride + vertOffset; if (vertEnd > verts.Length) throw new ArgumentOutOfRangeException("triCount", "The specified offset, count, and stride end outside the bounds of the provided array."); if (areas.Length < triCount) throw new ArgumentException("There must be at least as many AreaFlags as there are triangles.", "areas"); for (int i = vertOffset, j = 0; i < vertEnd; i += vertStride * 3, j++) RasterizeTriangle(ref verts[i], ref verts[i + vertStride], ref verts[i + vertStride * 2], areas[j]); } /// /// Rasterizes several triangles at once with per-triangle area flags. /// /// /// If the length of the array is not a multiple of 9, the extra floats at the end will be skipped. /// /// An array of vertices. /// An array of area flags, one for each triangle. public void RasterizeTrianglesWithAreas(float[] verts, Area[] areas) { RasterizeTrianglesWithAreas(verts, 0, 3, verts.Length / 9, areas); } /// /// Rasterizes several triangles at once with per-triangle area flags. /// /// An array of vertices. /// An offset into the array. /// The number of array elements that make up a vertex. A value of 0 is interpreted as tightly-packed data (3 floats per vertex). /// The number of triangles to rasterize. /// An array of area flags, one for each triangle. public void RasterizeTrianglesWithAreas(float[] verts, int floatOffset, int floatStride, int triCount, Area[] areas) { if (verts == null) throw new ArgumentNullException("verts"); if (floatOffset < 0) throw new ArgumentOutOfRangeException("floatOffset", "floatOffset must be greater than or equal to 0."); if (triCount < 0) throw new ArgumentOutOfRangeException("triCount", "triCount must be greater than or equal to 0."); if (floatStride < 0) throw new ArgumentOutOfRangeException("floatStride", "floatStride must be a positive integer."); else if (floatStride == 0) floatStride = 3; int floatEnd = triCount * (floatStride * 3) + floatOffset; if (floatEnd > verts.Length) throw new ArgumentOutOfRangeException("triCount", "The specified offset, count, and stride end outside the bounds of the provided array."); if (areas.Length < triCount) throw new ArgumentException("There must be at least as many AreaFlags as there are triangles.", "areas"); Vector3 a, b, c; for (int i = floatOffset, j = 0; i < floatEnd; i += floatStride * 3, j++) { int floatStride2 = floatStride * 2; a.X = verts[i]; a.Y = verts[i + 1]; a.Z = verts[i + 2]; b.X = verts[i + floatStride]; b.Y = verts[i + floatStride + 1]; b.Z = verts[i + floatStride + 2]; c.X = verts[i + floatStride2]; c.Y = verts[i + floatStride2 + 1]; c.Z = verts[i + floatStride2 + 2]; RasterizeTriangle(ref a, ref b, ref c, areas[j]); } } /// /// Rasterizes several triangles at once from an indexed array. /// /// An array of vertices. /// An array of indices. public void RasterizeTrianglesIndexed(Vector3[] verts, int[] inds) { RasterizeTrianglesIndexed(verts, inds, 0, 1, 0, inds.Length / 3, Area.Default); } /// /// Rasterizes several triangles at once from an indexed array. /// /// An array of vertices. /// An array of indices. /// The area flags for all the triangles. public void RasterizeTrianglesIndexed(Vector3[] verts, int[] inds, Area area) { RasterizeTrianglesIndexed(verts, inds, 0, 1, 0, inds.Length / 3, area); } /// /// Rasterizes several triangles at once from an indexed array. /// /// An array of vertices. /// An array of indices. /// An offset into the vertex array. /// The number of array elements that make up a vertex. A value of 0 is interpreted as tightly-packed data (one Vector3 per vertex). /// An offset into the index array. /// The number of triangles to rasterize. public void RasterizeTrianglesIndexed(Vector3[] verts, int[] inds, int vertOffset, int vertStride, int indexOffset, int triCount) { RasterizeTrianglesIndexed(verts, inds, vertOffset, vertStride, indexOffset, triCount, Area.Default); } /// /// Rasterizes several triangles at once from an indexed array. /// /// An array of vertices. /// An array of indices. /// An offset into the vertex array. /// The number of array elements that make up a vertex. A value of 0 is interpreted as tightly-packed data (one Vector3 per vertex). /// An offset into the index array. /// The number of triangles to rasterize. /// The area flags for all of the triangles. public void RasterizeTrianglesIndexed(Vector3[] verts, int[] inds, int vertOffset, int vertStride, int indexOffset, int triCount, Area area) { int indexEnd = triCount * 3 + indexOffset; if (verts == null) throw new ArgumentNullException("verts"); if (inds == null) throw new ArgumentNullException("inds"); if (indexEnd > inds.Length) throw new ArgumentOutOfRangeException("indexCount", "The specified index offset and length end outside the provided index array."); if (vertOffset < 0) throw new ArgumentOutOfRangeException("vertOffset", "vertOffset must be greater than or equal to 0."); if (vertStride < 0) throw new ArgumentOutOfRangeException("vertStride", "vertStride must be greater than or equal to 0."); else if (vertStride == 0) vertStride = 1; for (int i = indexOffset; i < indexEnd; i += 3) { int indA = inds[i] * vertStride + vertOffset; int indB = inds[i + 1] * vertStride + vertOffset; int indC = inds[i + 2] * vertStride + vertOffset; RasterizeTriangle(ref verts[indA], ref verts[indB], ref verts[indC], area); } } /// /// Rasterizes several triangles at once from an indexed array. /// /// An array of vertices. /// An array of indices. public void RasterizeTrianglesIndexed(float[] verts, int[] inds) { RasterizeTrianglesIndexed(verts, inds, 0, 3, 0, inds.Length / 3, Area.Default); } /// /// Rasterizes several triangles at once from an indexed array. /// /// An array of vertices. /// An array of indices. /// The area flags for all the triangles. public void RasterizeTrianglesIndexed(float[] verts, int[] inds, Area area) { RasterizeTrianglesIndexed(verts, inds, 0, 3, 0, inds.Length / 3, area); } /// /// Rasterizes several triangles at once from an indexed array. /// /// An array of vertices. /// An array of indices. /// An offset into the vertex array. /// The number of array elements that make up a vertex. A value of 0 is interpreted as tightly-packed data (3 floats per vertex). /// An offset into the index array. /// The number of triangles to rasterize. public void RasterizeTrianglesIndexed(float[] verts, int[] inds, int floatOffset, int floatStride, int indexOffset, int triCount) { RasterizeTrianglesIndexed(verts, inds, floatOffset, floatStride, indexOffset, triCount, Area.Default); } /// /// Rasterizes several triangles at once from an indexed array. /// /// An array of vertices. /// An array of indices. /// An offset into the vertex array. /// The number of array elements that make up a vertex. A value of 0 is interpreted as tightly-packed data (3 floats per vertex). /// An offset into the index array. /// The number of triangles to rasterize. /// The area flags for all of the triangles. public void RasterizeTrianglesIndexed(float[] verts, int[] inds, int floatOffset, int floatStride, int indexOffset, int triCount, Area area) { int indexEnd = triCount * 3 + indexOffset; if (verts == null) throw new ArgumentNullException("verts"); if (inds == null) throw new ArgumentNullException("inds"); if (indexEnd > inds.Length) throw new ArgumentOutOfRangeException("indexCount", "The specified index offset and length end outside the provided index array."); if (floatOffset < 0) throw new ArgumentOutOfRangeException("floatOffset", "floatOffset must be greater than or equal to 0."); if (floatStride < 0) throw new ArgumentOutOfRangeException("floatStride", "floatStride must be greater than or equal to 0."); else if (floatStride == 0) floatStride = 3; Vector3 a, b, c; for (int i = indexOffset; i < indexEnd; i += 3) { int indA = inds[i] * floatStride + floatOffset; int indB = inds[i + 1] * floatStride + floatOffset; int indC = inds[i + 2] * floatStride + floatOffset; a.X = verts[indA]; a.Y = verts[indA + 1]; a.Z = verts[indA + 2]; b.X = verts[indB]; b.Y = verts[indB + 1]; b.Z = verts[indB + 2]; c.X = verts[indC]; c.Y = verts[indC + 1]; c.Z = verts[indC + 2]; RasterizeTriangle(ref a, ref b, ref c, area); } } /// /// Rasterizes several triangles at once. /// /// A collection of triangles. public void RasterizeTriangles(IEnumerable tris) { RasterizeTriangles(tris, Area.Default); } /// /// Rasterizes several triangles at once. /// /// A collection of triangles. /// The area flags for all of the triangles. public void RasterizeTriangles(IEnumerable tris, Area area) { foreach (var t in tris) { Triangle3 tref = t; RasterizeTriangle(ref tref, area); } /* Parallel.ForEach(tris, t => { RasterizeTriangle(ref t, area); }); */ } /// /// Rasterizes several triangles at once. /// /// An array of triangles. public void RasterizeTriangles(Triangle3[] tris) { RasterizeTriangles(tris, 0, tris.Length, Area.Default); } /// /// Rasterizes several triangles at once. /// /// An array of triangles. /// The area flags for all of the triangles. public void RasterizeTriangles(Triangle3[] tris, Area area) { RasterizeTriangles(tris, 0, tris.Length, area); } /// /// Rasterizes several triangles at once. /// /// An array of triangles. /// An offset into the array. /// The number of triangles to rasterize, starting from the offset. public void RasterizeTriangles(Triangle3[] tris, int triOffset, int triCount) { RasterizeTriangles(tris, triOffset, triCount, Area.Default); } /// /// Rasterizes several triangles at once. /// /// An array of triangles. /// An offset into the array. /// The number of triangles to rasterize, starting from the offset. /// The area flags for all of the triangles. public void RasterizeTriangles(Triangle3[] tris, int triOffset, int triCount, Area area) { int triEnd = triOffset + triCount; if (tris == null) throw new ArgumentNullException("verts"); if (triOffset < 0) throw new ArgumentOutOfRangeException("triOffset", "triOffset must be greater than or equal to 0."); if (triCount < 0) throw new ArgumentOutOfRangeException("triCount", "triCount must be greater than or equal to 0."); if (triEnd > tris.Length) throw new ArgumentOutOfRangeException("triCount", "The specified offset and count end outside the bounds of the provided array."); int numBatches = 8; int threads = (triCount / numBatches) + 1; /*spanQueue = new ConcurrentQueue>(); bool allProcessed; var task = Task.Factory.StartNew(() => { while (true) { if (spanQueue.IsEmpty) Thread.Sleep(1); Tuple spanEntry; while (spanQueue.TryDequeue(out spanEntry)) cells[spanEntry.Item2 * width + spanEntry.Item1].AddSpan(spanEntry.Item3); } });*/ //Parallel.For(0, threads, i => for (int i = 0; i < threads; i++) { int start = triOffset + i * numBatches; int end = triOffset + (i + 1) * numBatches; if (end > triEnd) end = triEnd; for (int j = start; j < end; j++) { Triangle3 t = tris[j]; RasterizeTriangle(ref t.A, ref t.B, ref t.C, area); } } //}); } /// /// Rasterizes several triangles at once. /// /// /// If the length of the array is not a multiple of 3, the extra vertices at the end will be skipped. /// /// An array of vertices. public void RasterizeTriangles(Vector3[] verts) { RasterizeTriangles(verts, 0, 1, verts.Length / 3, Area.Default); } /// /// Rasterizes several triangles at once. /// /// /// If the length of the array is not a multiple of 3, the extra vertices at the end will be skipped. /// /// An array of vertices. /// The area flags for all of the triangles. public void RasterizeTriangles(Vector3[] verts, Area area) { RasterizeTriangles(verts, 0, 1, verts.Length / 3, area); } /// /// Rasterizes several triangles at once. /// /// An array of vertices. /// An offset into the array. /// The number of array elements that make up a vertex. A value of 0 is interpreted as tightly-packed data (1 Vector3 per vertex). /// The number of triangles to rasterize, starting from the offset. public void RasterizeTriangles(Vector3[] verts, int vertOffset, int vertStride, int triCount) { RasterizeTriangles(verts, vertOffset, vertStride, triCount, Area.Default); } /// /// Rasterizes several triangles at once. /// /// An array of vertices. /// An offset into the array. /// The number of array elements that make up a vertex. A value of 0 is interpreted as tightly-packed data (1 Vector3 per vertex). /// The number of triangles to rasterize, starting from the offset. /// The area flags for all of the triangles. public void RasterizeTriangles(Vector3[] verts, int vertOffset, int vertStride, int triCount, Area area) { int vertEnd = triCount * vertStride + vertOffset; if (verts == null) throw new ArgumentNullException("verts"); if (vertOffset < 0) throw new ArgumentOutOfRangeException("vertOffset", "vertOffset must be greater than or equal to 0."); if (triCount < 0) throw new ArgumentOutOfRangeException("triCount", "triCount must be greater than or equal to 0."); if (vertStride < 0) throw new ArgumentOutOfRangeException("vertStride", "vertStride must be greater than or equal to 0."); else if (vertStride == 0) vertStride = 1; if (vertEnd > verts.Length) throw new ArgumentOutOfRangeException("triCount", "The specified offset, count, and stride end outside the bounds of the provided array."); //Parallel.For(0, triCount, i => { for (int i = 0; i < triCount; i++) { i = vertOffset + (i * vertStride * 3); RasterizeTriangle(ref verts[i], ref verts[i + vertStride], ref verts[i + vertStride * 2], area); } //}); } /// /// Rasterizes several triangles at once. /// /// /// If the length of the array is not a multiple of 9, the extra floats at the end will be skipped. /// /// An array of vertices. public void RasterizeTriangles(float[] verts) { RasterizeTriangles(verts, 0, 3, verts.Length / 9, Area.Default); } /// /// Rasterizes several triangles at once. /// /// /// If the length of the array is not a multiple of 9, the extra floats at the end will be skipped. /// /// An array of vertices. /// The area flags for all of the triangles. public void RasterizeTriangles(float[] verts, Area area) { RasterizeTriangles(verts, 0, 3, verts.Length / 9, area); } /// /// Rasterizes several triangles at once. /// /// An array of vertices. /// An offset into the array. /// The number of array elements that make up a vertex. A value of 0 is interpreted as tightly-packed data (3 floats per vertex). /// The number of triangles to rasterize. public void RasterizeTriangles(float[] verts, int floatOffset, int floatStride, int triCount) { RasterizeTriangles(verts, floatOffset, floatStride, triCount, Area.Default); } /// /// Rasterizes several triangles at once. /// /// An array of vertices. /// An offset into the array. /// The number of array elements that make up a vertex. A value of 0 is interpreted as tightly-packed data (3 floats per vertex). /// The number of triangles to rasterize. /// The area flags for all of the triangles. public void RasterizeTriangles(float[] verts, int floatOffset, int floatStride, int triCount, Area area) { int floatEnd = triCount * (floatStride * 3) + floatOffset; if (verts == null) throw new ArgumentNullException("verts"); if (floatOffset < 0) throw new ArgumentOutOfRangeException("floatOffset", "floatOffset must be greater than or equal to 0."); if (triCount < 0) throw new ArgumentOutOfRangeException("triCount", "triCount must be greater than or equal to 0."); if (floatStride < 0) throw new ArgumentOutOfRangeException("floatStride", "floatStride must be a positive integer."); else if (floatStride == 0) floatStride = 3; if (floatEnd > verts.Length) throw new ArgumentOutOfRangeException("triCount", "The specified offset, count, and stride end outside the bounds of the provided array."); Vector3 a, b, c; //Parallel.For(0, triCount, i => for (int i = 0; i < triCount; i++) { i = floatOffset + (i * floatStride * 3); int floatStride2 = floatStride * 2; a.X = verts[i]; a.Y = verts[i + 1]; a.Z = verts[i + 2]; b.X = verts[i + floatStride]; b.Y = verts[i + floatStride + 1]; b.Z = verts[i + floatStride + 2]; c.X = verts[i + floatStride2]; c.Y = verts[i + floatStride2 + 1]; c.Z = verts[i + floatStride2 + 2]; RasterizeTriangle(ref a, ref b, ref c, area); } //}); } /// /// Rasterizes a triangle using conservative voxelization. /// /// The triangle as a struct. public void RasterizeTriangle(ref Triangle3 tri) { RasterizeTriangle(ref tri.A, ref tri.B, ref tri.C, Area.Default); } /// /// Rasterizes a triangle using conservative voxelization. /// /// The triangle as a struct. /// The area flags for the triangle. public void RasterizeTriangle(ref Triangle3 tri, Area area) { RasterizeTriangle(ref tri.A, ref tri.B, ref tri.C, area); } /// /// Rasterizes a triangle using conservative voxelization. /// /// The X component of the first vertex of the triangle. /// The Y component of the first vertex of the triangle. /// The Z component of the first vertex of the triangle. /// The X component of the second vertex of the triangle. /// The Y component of the second vertex of the triangle. /// The Z component of the second vertex of the triangle. /// The X component of the third vertex of the triangle. /// The Y component of the third vertex of the triangle. /// The Z component of the third vertex of the triangle. public void RasterizeTriangle(float ax, float ay, float az, float bx, float by, float bz, float cx, float cy, float cz) { RasterizeTriangle(ax, ay, az, bx, by, bz, cx, cy, cz, Area.Default); } /// /// Rasterizes a triangle using conservative voxelization. /// /// The X component of the first vertex of the triangle. /// The Y component of the first vertex of the triangle. /// The Z component of the first vertex of the triangle. /// The X component of the second vertex of the triangle. /// The Y component of the second vertex of the triangle. /// The Z component of the second vertex of the triangle. /// The X component of the third vertex of the triangle. /// The Y component of the third vertex of the triangle. /// The Z component of the third vertex of the triangle. /// The area flags for the triangle. public void RasterizeTriangle(float ax, float ay, float az, float bx, float by, float bz, float cx, float cy, float cz, Area area) { Vector3 a, b, c; a.X = ax; a.Y = ay; a.Z = az; b.X = bx; b.Y = by; b.Z = bz; c.X = cx; c.Y = cy; c.Z = cz; RasterizeTriangle(ref a, ref b, ref c, area); } /// /// Rasterizes a triangle using conservative voxelization. /// /// The first vertex of the triangle. /// The second vertex of the triangle. /// The third vertex of the triangle. public void RasterizeTriangle(ref Vector3 a, ref Vector3 b, ref Vector3 c) { RasterizeTriangle(ref a, ref b, ref c, Area.Default); } /// /// Rasterizes a triangle using conservative voxelization. /// /// The first vertex of the triangle. /// The second vertex of the triangle. /// The third vertex of the triangle. /// The area flags for the triangle. 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)); } } } } }