private void CreateSolidVolume(VoxelizingOctree tree, VoxelizingOctreeCell cell, Vector3i volumeLength) { if (cell.Status == CellStatus.Inside) { int min_x = cell.VoxelBounds.MinX + tree.WorldVoxelOffset.X; int min_y = cell.VoxelBounds.MinY + tree.WorldVoxelOffset.Y; int min_z = cell.VoxelBounds.MinZ + tree.WorldVoxelOffset.Z; int max_x = cell.VoxelBounds.MaxX + tree.WorldVoxelOffset.X; int max_y = cell.VoxelBounds.MaxY + tree.WorldVoxelOffset.Y; int max_z = cell.VoxelBounds.MaxZ + tree.WorldVoxelOffset.Z; for (int x = min_x; x < max_x; x++) { for (int y = min_y; y < max_y; y++) { for (int z = min_z; z < max_z; z++) { SetVoxel(x, y, z, 1); } } } } foreach (var child in cell.Children) { CreateSolidVolume(tree, child, volumeLength); } }
private bool RecursiveSolveStatus(VoxelizingOctreeCell cell, int maxDepth) { if (maxDepth < 0) { return(false); } if (cell.IsLeaf && cell.IsIntersecting) { return(false); } switch (cell.Status) { case CellStatus.Unknown: SolveStatus(cell); return(true); case CellStatus.Intersecting: case CellStatus.IntersectingBounds: for (int i = 0; i < cell.Children.Count; i++) { RecursiveSolveStatus(cell.Children[i], maxDepth - 1); } return(true); } return(false); }
public void TestTriangleIntersection() { VoxelizingOctreeCell p = this; while (p != null) { for (int i = 0; i < p.Triangles.Count; i++) { Triangle t = p.Triangles[i]; if (Intersects(ref t)) { Status = CellStatus.Intersecting; return; } } p = p.Parent; } // If we're not intersecting any triangles make sure we're also not intersecting the mesh bounds. if (IntersectsMeshBounds()) { Status = CellStatus.IntersectingBounds; } }
public VoxelizingOctreeCell(VoxelizingOctree tree, VoxelizingOctreeCell root, Vector3 center, float length, int level) { Tree = tree; Root = root; Center = center; Length = length; Level = level; float half_length = length / 2.0f; Bounds = new AABBf(); Bounds.MinX = center.X - half_length; Bounds.MinY = center.Y - half_length; Bounds.MinZ = center.Z - half_length; Bounds.MaxX = center.X + half_length; Bounds.MaxY = center.Y + half_length; Bounds.MaxZ = center.Z + half_length; VoxelBounds = new AABBi( (int)Math.Round((Bounds.MinX - tree.VoxelBounds.MinX) / tree.SmallestVoxelSideLength, MidpointRounding.AwayFromZero), (int)Math.Round((Bounds.MinY - tree.VoxelBounds.MinY) / tree.SmallestVoxelSideLength, MidpointRounding.AwayFromZero), (int)Math.Round((Bounds.MinZ - tree.VoxelBounds.MinZ) / tree.SmallestVoxelSideLength, MidpointRounding.AwayFromZero), (int)Math.Round((Bounds.MaxX - tree.VoxelBounds.MinX) / tree.SmallestVoxelSideLength, MidpointRounding.AwayFromZero), (int)Math.Round((Bounds.MaxY - tree.VoxelBounds.MinY) / tree.SmallestVoxelSideLength, MidpointRounding.AwayFromZero), (int)Math.Round((Bounds.MaxZ - tree.VoxelBounds.MinZ) / tree.SmallestVoxelSideLength, MidpointRounding.AwayFromZero)); }
public bool GenerateOctree(MeshData mesh) { if (mesh == null) { return(false); } // Create a list of triangles from the list of faces in the model List <Triangle> triangles = new List <Triangle>(); for (int i = 0; i < mesh.Tris.Length; i++) { Tri face = mesh.Tris[i]; Triangle tri = new Triangle(); tri.v0 = mesh.Vertices[face.P1.Vertex]; tri.v1 = mesh.Vertices[face.P2.Vertex]; tri.v2 = mesh.Vertices[face.P3.Vertex]; triangles.Add(tri); } // Determine the axis-aligned bounding box for the triangles Vector3 center; CreateUniformBoundingBox(triangles, out MeshBounds, out VoxelBounds, out center, out SideLength); { SmallestVoxelSideLength = SideLength; for (int i = 1; i < m_maxLevels; i++) { SmallestVoxelSideLength *= 0.5; } VoxelSize = new Vector3i(); VoxelSize.X = (Int32)Math.Pow(2, m_maxLevels); VoxelSize.Y = (Int32)Math.Pow(2, m_maxLevels); VoxelSize.Z = (Int32)Math.Pow(2, m_maxLevels); } m_root = new VoxelizingOctreeCell(this, null, center, SideLength, 0); m_root.Root = m_root; m_root.Triangles = new List <Triangle>(triangles); m_root.Status = CellStatus.IntersectingBounds; m_root.RecursiveSubdivide(m_maxLevels - 1); WorldVoxelOffset = new Vector3i( 0 - Root.VoxelBounds.MinX, 0 - Root.VoxelBounds.MinY, 0 - Root.VoxelBounds.MinZ); return(true); }
private void PropagateStatus(VoxelizingOctreeCell seed, CellStatus status, VoxelizingOctreeCell current) { Vector4[] cell_bounds_verts = new Vector4[8]; AABBf bounds = current.Bounds; cell_bounds_verts[0] = new Vector4(bounds.MaxX, bounds.MaxY, bounds.MaxZ, 1); cell_bounds_verts[1] = new Vector4(bounds.MaxX, bounds.MaxY, bounds.MinZ, 1); cell_bounds_verts[2] = new Vector4(bounds.MinX, bounds.MaxY, bounds.MinZ, 1); cell_bounds_verts[3] = new Vector4(bounds.MinX, bounds.MaxY, bounds.MaxZ, 1); cell_bounds_verts[4] = new Vector4(bounds.MaxX, bounds.MinY, bounds.MaxZ, 1); cell_bounds_verts[5] = new Vector4(bounds.MaxX, bounds.MinY, bounds.MinZ, 1); cell_bounds_verts[6] = new Vector4(bounds.MinX, bounds.MinY, bounds.MinZ, 1); cell_bounds_verts[7] = new Vector4(bounds.MinX, bounds.MinY, bounds.MaxZ, 1); // Loop over the number of cube faces for (int i = 0; i < 6; i++) { // Loop over the 8 vertices that make up the corners of a voxel cell. If any of the corners is visible // the voxel will have the status accumulated into it. for (int n = 0; n < 8; n++) { Vector4 result; Vector4.Transform(ref cell_bounds_verts[n], ref cubemapProjections[i], out result); float x = result.X / result.W; float y = result.Y / result.W; float z = result.Z / result.W; if (x >= -1.0f && x <= 1.0f && y >= -1.0f && y <= 1.0f) { int depthBufferX = (int)(((x + 1.0f) / 2.0f) * (CubemapWidth - 1)); int depthBufferY = (int)(((y + 1.0f) / 2.0f) * (CubemapHeight - 1)); float sampledDepth = cubemapDepthBuffers[i][depthBufferY * CubemapWidth + depthBufferX]; float ndc_sampledDepth = ((sampledDepth * 2.0f) - 1.0f); if (z > -1.0f && z < ndc_sampledDepth) { // Accumulate the status on the cell must overcome threshold to be confirmed as inside or outside. // If enough other voxels propagate the same status to the cell, it becomes a cell of that type. if (current.AccumulateStatus(status)) { return; } } } } } }
public bool Subdivide() { float quarter_length = Length / 4.0f; int stop = 8; for (int x = -1; x <= 1; x += 2) { for (int y = -1; y <= 1; y += 2) { for (int z = -1; z <= 1; z += 2) { VoxelizingOctreeCell newCell = new VoxelizingOctreeCell(Tree, Root, Center + new Vector3(x * quarter_length, y * quarter_length, z * quarter_length), quarter_length * 2.0f, Level + 1 ); newCell.Parent = this; newCell.EncloseTriangles(this); if (newCell.IsOutsideMeshBounds()) { newCell.Status = CellStatus.Outside; } if (!newCell.IsIntersecting) { stop--; } Children.Add(newCell); } } } if (stop == 0) { //Debug.Assert(!IsIntersecting); if (IsIntersecting) { //Debugger.Break(); } Children.Clear(); } return(stop != 0); }
public void EncloseTriangles(VoxelizingOctreeCell parent) { for (int i = 0; i < parent.Triangles.Count; i++) { Triangle t = parent.Triangles[i]; if (Contains(ref t)) { Triangles.Add(t); Parent.Triangles.RemoveAt(i); i--; } } if (parent.IsIntersecting) { TestTriangleIntersection(); } }
private void SolveStatus(VoxelizingOctreeCell cell) { const int MIN_INSIDE_FACES = 4; const float MIN_INSIDE_PERCENTAGE = 0.03f; int cubemap_sides_seeing_inside = 0; for (int i = 0; i < 6; i++) { RenderCubeSide(i, cell, cubemapDirections[i], cubemapUps[i]); ReadDepthBuffer(i); float backfacePercentage = CalculateBackfacePercentage(cubemapColorBuffers[i], CubemapWidth, CubemapHeight); if (backfacePercentage > MIN_INSIDE_PERCENTAGE) { cubemap_sides_seeing_inside++; } } // This is a seed cell so go ahead and mark the status we believe it to be. if (cubemap_sides_seeing_inside >= MIN_INSIDE_FACES || cubemap_sides_seeing_inside == 0) { if (cubemap_sides_seeing_inside >= MIN_INSIDE_FACES) { cell.Status = CellStatus.Inside; } else // cubemap_sides_seeing_inside == 0 { cell.Status = CellStatus.Outside; } RecursivePropagateStatus(cell, cell.Status, m_input.Octree.Root); } else { // Unable to solve status exactly. } // Restore the standard back buffer. //GL.Ext.FramebufferTexture2D(FramebufferTarget.FramebufferExt, FramebufferAttachment.ColorAttachment0Ext, TextureTarget.Texture2D, 0, 0); //GL.Ext.FramebufferRenderbuffer(FramebufferTarget.FramebufferExt, FramebufferAttachment.DepthAttachmentExt, RenderbufferTarget.RenderbufferExt, 0); GL.Ext.BindFramebuffer(FramebufferTarget.FramebufferExt, 0); }
private void RenderCubeSide(int cubemapIndex, VoxelizingOctreeCell cell, Vector3 look, Vector3 up) { AABBf root_bounds = cell.Root.Bounds; float root_length = root_bounds.MaxX - root_bounds.MinX; float s_div2 = (cell.Bounds.MaxX - cell.Bounds.MinX) * 0.5f; GL.MatrixMode(MatrixMode.Projection); Matrix4 perspective = Matrix4.CreatePerspectiveFieldOfView(MathHelper.DegreesToRadians(90.0f), 1.0f, s_div2, root_length); GL.LoadMatrix(ref perspective); Matrix4 modelView = Matrix4.LookAt(cell.Center, cell.Center + look, up); GL.MatrixMode(MatrixMode.Modelview); GL.LoadMatrix(ref modelView); //cubemapProjections[cubemapIndex] = perspective * modelView; cubemapProjections[cubemapIndex] = modelView * perspective; GL.PushMatrix(); GL.Ext.BindFramebuffer(FramebufferTarget.FramebufferExt, CubemapFboHandle[cubemapIndex]); // since there's only 1 Color buffer attached this is not explicitly required GL.DrawBuffer((DrawBufferMode)FramebufferAttachment.ColorAttachment0Ext); GL.ClearColor(0.0f, 0.0f, 0.0f, 0f); GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit); GL.Ext.FramebufferTexture2D(FramebufferTarget.FramebufferExt, FramebufferAttachment.ColorAttachment0Ext, TextureTarget.Texture2D, CubemapColorTexture[cubemapIndex], 0); GL.Ext.FramebufferRenderbuffer(FramebufferTarget.FramebufferExt, FramebufferAttachment.DepthAttachmentExt, RenderbufferTarget.RenderbufferExt, CubemapDepthRenderbuffer[cubemapIndex]); GL.PushAttrib(AttribMask.ViewportBit); GL.Viewport(0, 0, CubemapWidth, CubemapHeight); DrawTwoSidedOriginalMesh(); GL.PopAttrib(); GL.PopMatrix(); }
private void RecursivePropagateStatus(VoxelizingOctreeCell seed, CellStatus status, VoxelizingOctreeCell current) { if (current.IsLeaf && current.IsIntersecting) { return; } switch (current.Status) { case CellStatus.Unknown: // Only propogate status to unknown cells PropagateStatus(seed, status, current); return; case CellStatus.Intersecting: case CellStatus.IntersectingBounds: for (int i = current.Children.Count - 1; i >= 0; i--) { RecursivePropagateStatus(seed, status, current.Children[i]); } return; } }
private bool RecursiveSolveStatus(VoxelizingOctreeCell cell, int maxDepth) { if (maxDepth < 0) return false; if (cell.IsLeaf && cell.IsIntersecting) return false; switch (cell.Status) { case CellStatus.Unknown: SolveStatus(cell); return true; case CellStatus.Intersecting: case CellStatus.IntersectingBounds: for (int i = 0; i < cell.Children.Count; i++) { RecursiveSolveStatus(cell.Children[i], maxDepth - 1); } return true; } return false; }
public bool Subdivide() { float quarter_length = Length / 4.0f; int stop = 8; for (int x = -1; x <= 1; x += 2) { for (int y = -1; y <= 1; y += 2) { for (int z = -1; z <= 1; z += 2) { VoxelizingOctreeCell newCell = new VoxelizingOctreeCell(Tree, Root, Center + new Vector3(x * quarter_length, y * quarter_length, z * quarter_length), quarter_length * 2.0f, Level + 1 ); newCell.Parent = this; newCell.EncloseTriangles(this); if (newCell.IsOutsideMeshBounds()) newCell.Status = CellStatus.Outside; if (!newCell.IsIntersecting) stop--; Children.Add(newCell); } } } if (stop == 0) { //Debug.Assert(!IsIntersecting); if (IsIntersecting) { //Debugger.Break(); } Children.Clear(); } return stop != 0; }
public bool GenerateOctree(MeshData mesh) { if (mesh == null) return false; // Create a list of triangles from the list of faces in the model List<Triangle> triangles = new List<Triangle>(); for (int i = 0; i < mesh.Tris.Length; i++) { Tri face = mesh.Tris[i]; Triangle tri = new Triangle(); tri.v0 = mesh.Vertices[face.P1.Vertex]; tri.v1 = mesh.Vertices[face.P2.Vertex]; tri.v2 = mesh.Vertices[face.P3.Vertex]; triangles.Add(tri); } // Determine the axis-aligned bounding box for the triangles Vector3 center; CreateUniformBoundingBox(triangles, out MeshBounds, out VoxelBounds, out center, out SideLength); { SmallestVoxelSideLength = SideLength; for (int i = 1; i < m_maxLevels; i++) SmallestVoxelSideLength *= 0.5; VoxelSize = new Vector3i(); VoxelSize.X = (Int32)Math.Pow(2, m_maxLevels); VoxelSize.Y = (Int32)Math.Pow(2, m_maxLevels); VoxelSize.Z = (Int32)Math.Pow(2, m_maxLevels); } m_root = new VoxelizingOctreeCell(this, null, center, SideLength, 0); m_root.Root = m_root; m_root.Triangles = new List<Triangle>(triangles); m_root.Status = CellStatus.IntersectingBounds; m_root.RecursiveSubdivide(m_maxLevels - 1); WorldVoxelOffset = new Vector3i( 0 - Root.VoxelBounds.MinX, 0 - Root.VoxelBounds.MinY, 0 - Root.VoxelBounds.MinZ); return true; }
private void RecursivePropagateStatus(VoxelizingOctreeCell seed, CellStatus status, VoxelizingOctreeCell current) { if (current.IsLeaf && current.IsIntersecting) return; switch (current.Status) { case CellStatus.Unknown: // Only propogate status to unknown cells PropagateStatus(seed, status, current); return; case CellStatus.Intersecting: case CellStatus.IntersectingBounds: for (int i = current.Children.Count - 1; i >= 0; i--) { RecursivePropagateStatus(seed, status, current.Children[i]); } return; } }
private void SolveStatus(VoxelizingOctreeCell cell) { const int MIN_INSIDE_FACES = 4; const float MIN_INSIDE_PERCENTAGE = 0.03f; int cubemap_sides_seeing_inside = 0; for (int i = 0; i < 6; i++) { RenderCubeSide(i, cell, cubemapDirections[i], cubemapUps[i]); ReadDepthBuffer(i); float backfacePercentage = CalculateBackfacePercentage(cubemapColorBuffers[i], CubemapWidth, CubemapHeight); if (backfacePercentage > MIN_INSIDE_PERCENTAGE) cubemap_sides_seeing_inside++; } // This is a seed cell so go ahead and mark the status we believe it to be. if (cubemap_sides_seeing_inside >= MIN_INSIDE_FACES || cubemap_sides_seeing_inside == 0) { if (cubemap_sides_seeing_inside >= MIN_INSIDE_FACES) cell.Status = CellStatus.Inside; else // cubemap_sides_seeing_inside == 0 cell.Status = CellStatus.Outside; RecursivePropagateStatus(cell, cell.Status, m_input.Octree.Root); } else { // Unable to solve status exactly. } // Restore the standard back buffer. //GL.Ext.FramebufferTexture2D(FramebufferTarget.FramebufferExt, FramebufferAttachment.ColorAttachment0Ext, TextureTarget.Texture2D, 0, 0); //GL.Ext.FramebufferRenderbuffer(FramebufferTarget.FramebufferExt, FramebufferAttachment.DepthAttachmentExt, RenderbufferTarget.RenderbufferExt, 0); GL.Ext.BindFramebuffer(FramebufferTarget.FramebufferExt, 0); }
public VoxelizationOutput Generate(VoxelizationInput input, Action <VoxelizationProgress> progress) { this.input = input; VoxelizationOutput output = new VoxelizationOutput(); output.Octree = input.Octree; List <List <VoxelizingOctreeCell> > cellList = new List <List <VoxelizingOctreeCell> >(); input.Octree.AccumulateChildren(out cellList); VolumeAccumulator volume = new VolumeAccumulator(); VolumeAccumulator[] volumeAtLevel = new VolumeAccumulator[input.Octree.MaxLevels]; for (int i = 0; i < input.Octree.MaxLevels; i++) { List <VoxelizingOctreeCell> childernAtDepth = cellList[i]; VolumeAccumulator levelVolumeTotal = new VolumeAccumulator(); Parallel.For(0, childernAtDepth.Count, () => new VolumeAccumulator(), (n, loop, partial) => { VoxelizingOctreeCell cell = childernAtDepth[n]; float sideLength = cell.Length; switch (cell.Status) { case CellStatus.Inside: partial.InsideTotal += (sideLength * sideLength * sideLength); break; case CellStatus.Outside: partial.OutsideTotal += (sideLength * sideLength * sideLength); break; case CellStatus.Intersecting: case CellStatus.IntersectingBounds: if (cell.IsLeaf) { partial.IntersectingTotal += (sideLength * sideLength * sideLength); } break; } return(partial); }, partial => { lock (levelVolumeTotal) { levelVolumeTotal.InsideTotal += partial.InsideTotal; levelVolumeTotal.OutsideTotal += partial.OutsideTotal; levelVolumeTotal.IntersectingTotal += partial.IntersectingTotal; } }); volume.InsideTotal += levelVolumeTotal.InsideTotal; volume.OutsideTotal += levelVolumeTotal.OutsideTotal; volume.IntersectingTotal += levelVolumeTotal.IntersectingTotal; volumeAtLevel[i] = levelVolumeTotal; } Debug.WriteLine("Percentage of inner volume at each octree level"); for (int i = 0; i < input.Octree.MaxLevels; i++) { Debug.WriteLine("Level {0}: Inner Volume {1}%", i, (volumeAtLevel[i].InsideTotal / volume.InsideTotal) * 100); } // A good check to perform is to compare the ratio of intersecting volume leaf nodes to the total volume // we've determined is inside. A tool could use this ratio to automatically determine a good octree level // by iterative optimization. If a mesh for example fails to get at least a 1 : 0.5 ratio of intersecting:inner // volume ratio it's a good bet that the octree does not subdivide enough levels in order to find enough inner volume // to meet our occlusion needs. If further subdivision up to some maximum, lets say 8 fails to ever meet this ratio // one could say the mesh is not a good candidate for automating occluder generation. Debug.WriteLine(""); float intersecting_inside_ratio = volume.InsideTotal / volume.IntersectingTotal; Debug.WriteLine("Intersecting : Inner = 1:{0}", intersecting_inside_ratio); Debug.WriteLine("Inner / (Inner + Intersecting) = {0}", volume.InsideTotal / (volume.InsideTotal + volume.IntersectingTotal)); const float MINIMUM_INTERSECTING_TO_INSIDE_RATIO = 0.25f; AABBf meshBounds = input.Octree.MeshBounds; double dX = meshBounds.MaxX - meshBounds.MinX; double dY = meshBounds.MaxY - meshBounds.MinY; double dZ = meshBounds.MaxZ - meshBounds.MinZ; double reduction = 0.5; for (int i = 0; i <= input.Octree.MaxLevels * 2; i++) { reduction *= 0.5; } dX = dX * reduction; dY = dY * reduction; dZ = dZ * reduction; if (intersecting_inside_ratio > MINIMUM_INTERSECTING_TO_INSIDE_RATIO) { List <AABBi> innerBounds = new List <AABBi>(); float innerVolumeGathered = 0.0f; for (int i = 0; i < input.Octree.MaxLevels; i++) { for (int n = 0; n < cellList[i].Count; n++) { if (cellList[i][n].Status == CellStatus.Inside) { AABBf bound = cellList[i][n].Bounds; AABBi bi = new AABBi(); bi.MaxX = (int)Math.Round(((double)bound.MaxX - (double)meshBounds.MinX) / dX, MidpointRounding.AwayFromZero); bi.MaxY = (int)Math.Round(((double)bound.MaxY - (double)meshBounds.MinY) / dY, MidpointRounding.AwayFromZero); bi.MaxZ = (int)Math.Round(((double)bound.MaxZ - (double)meshBounds.MinZ) / dZ, MidpointRounding.AwayFromZero); bi.MinX = (int)Math.Round(((double)bound.MinX - (double)meshBounds.MinX) / dX, MidpointRounding.AwayFromZero); bi.MinY = (int)Math.Round(((double)bound.MinY - (double)meshBounds.MinY) / dY, MidpointRounding.AwayFromZero); bi.MinZ = (int)Math.Round(((double)bound.MinZ - (double)meshBounds.MinZ) / dZ, MidpointRounding.AwayFromZero); innerBounds.Add(bi); } } innerVolumeGathered += volumeAtLevel[i].InsideTotal / volume.InsideTotal; if (innerVolumeGathered > input.MinimumVolume) { break; } } Debug.WriteLine("Enough inner volume found {0}%", innerVolumeGathered * 100.0f); Mesh mesh = MeshBuilder.BuildMesh(innerBounds); for (int i = 0; i < mesh.Vertices.Length; i++) { mesh.Vertices[i].X = (float)(((double)meshBounds.MinX) + (mesh.Vertices[i].X * dX)); mesh.Vertices[i].Y = (float)(((double)meshBounds.MinY) + (mesh.Vertices[i].Y * dY)); mesh.Vertices[i].Z = (float)(((double)meshBounds.MinZ) + (mesh.Vertices[i].Z * dZ)); } if (input.Retriangulate) { Mesh triangulatedMesh = MeshOptimizer.Retriangulate(input, mesh, out output.DebugLines); if (triangulatedMesh != null) { mesh = triangulatedMesh; } } mesh = PolygonFilter.Filter(input, mesh); output.OccluderMesh = new RenderableMesh(mesh, true); } else { Debug.WriteLine("Not enough inner volume found to continue."); } return(output); }
private void PropagateStatus(VoxelizingOctreeCell seed, CellStatus status, VoxelizingOctreeCell current) { Vector4[] cell_bounds_verts = new Vector4[8]; AABBf bounds = current.Bounds; cell_bounds_verts[0] = new Vector4(bounds.MaxX, bounds.MaxY, bounds.MaxZ, 1); cell_bounds_verts[1] = new Vector4(bounds.MaxX, bounds.MaxY, bounds.MinZ, 1); cell_bounds_verts[2] = new Vector4(bounds.MinX, bounds.MaxY, bounds.MinZ, 1); cell_bounds_verts[3] = new Vector4(bounds.MinX, bounds.MaxY, bounds.MaxZ, 1); cell_bounds_verts[4] = new Vector4(bounds.MaxX, bounds.MinY, bounds.MaxZ, 1); cell_bounds_verts[5] = new Vector4(bounds.MaxX, bounds.MinY, bounds.MinZ, 1); cell_bounds_verts[6] = new Vector4(bounds.MinX, bounds.MinY, bounds.MinZ, 1); cell_bounds_verts[7] = new Vector4(bounds.MinX, bounds.MinY, bounds.MaxZ, 1); // Loop over the number of cube faces for (int i = 0; i < 6; i++) { // Loop over the 8 vertices that make up the corners of a voxel cell. If any of the corners is visible // the voxel will have the status accumulated into it. for (int n = 0; n < 8; n++) { Vector4 result; Vector4.Transform(ref cell_bounds_verts[n], ref cubemapProjections[i], out result); float x = result.X / result.W; float y = result.Y / result.W; float z = result.Z / result.W; if (x >= -1.0f && x <= 1.0f && y >= -1.0f && y <= 1.0f) { int depthBufferX = (int)(((x + 1.0f) / 2.0f) * (CubemapWidth - 1)); int depthBufferY = (int)(((y + 1.0f) / 2.0f) * (CubemapHeight - 1)); float sampledDepth = cubemapDepthBuffers[i][depthBufferY * CubemapWidth + depthBufferX]; float ndc_sampledDepth = ((sampledDepth * 2.0f) - 1.0f); if (z > -1.0f && z < ndc_sampledDepth) { // Accumulate the status on the cell must overcome threshold to be confirmed as inside or outside. // If enough other voxels propagate the same status to the cell, it becomes a cell of that type. if (current.AccumulateStatus(status)) return; } } } } }