示例#1
0
        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);
            }
        }
示例#2
0
        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);
        }
示例#3
0
        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;
            }
        }
示例#4
0
        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));
        }
示例#5
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);
        }
示例#6
0
        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;
                            }
                        }
                    }
                }
            }
        }
示例#7
0
        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);
        }
示例#8
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();
            }
        }
示例#9
0
        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);
        }
示例#10
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();
        }
示例#11
0
        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;
        }
        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();
        }
        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();
            }
        }
        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 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);
        }
示例#20
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;
                        }
                    }
                }
            }
        }