Ejemplo n.º 1
0
        public VoxelizationOutput Voxelize(VoxelizationInput input, Action<VoxelizationProgress> progress)
        {
            // Setup VBO state
            GL.EnableClientState(ArrayCap.VertexArray);
            GL.EnableClientState(ArrayCap.IndexArray);

            m_input = input;

            VoxelizationOutput output = new VoxelizationOutput();
            output.Octree = input.Octree;

            VoxelizationProgress vp = new VoxelizationProgress();
            vp.Status = "Voxelizing mesh with " + input.Octree.MaxLevels + " subdivision levels";
            progress(vp);

            GL.PushAttrib(AttribMask.AllAttribBits);
            for (int i = 0; i <= input.Octree.MaxLevels; i++)
            {
                vp.Progress = (i / (input.Octree.MaxLevels + 1.0f));
                vp.Status = "Voxelizing octree level " + i;
                progress(vp);
                RecursiveSolveStatus(input.Octree.Root, i);
            }
            GL.PopAttrib();

            vp.Progress = 1;
            vp.Status = "Done voxelizing mesh";
            progress(vp);

            return output;
        }
Ejemplo n.º 2
0
        public static void Run(string[] args)
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);

            ProgramOptions options = new ProgramOptions();

            string settingPath = Path.Combine(Path.GetDirectoryName(Application.ExecutablePath), "Oxel.Settings.xml");
            VoxelizationInput input = VoxelizationInput.Load(settingPath);
            if (input == null)
                input = new VoxelizationInput();

            if (args.Contains("-c"))
            {
                // Make sure user can see console output
                AttachConsole(PARENT_PROCESS_ID);

                input.Clone(options);

                if (!CommandLineParser.Parse<ProgramOptions>(args, ref options))
                    return;

                options.Clone(input);
            }
            else
            {
                CommandLineParser.Parse<VoxelizationInput>(args, ref input);
            }

            if (options.UseCommandLine)
            {
                Logger.IsCommandLine = true;

                Operations operations = new Operations();
                operations.Initialize(input);
                operations.Open(options.InputMesh, input.WindingOrder);
                WaitHandle waitHandle = operations.GenerateOccluder((VoxelizationProgress vp) => {
                    string coverage =
                        String.Format("Volume Coverage     : {0,5:0.##}%", (100 * vp.VolumeCoverage)) + "    " +
                        String.Format("Silhouette Coverage : {0,5:0.##}%", (100 * vp.SilhouetteCoverage));

                    if (!String.IsNullOrEmpty(vp.Status))
                        Console.WriteLine(vp.Status + "\r\n");

                    Console.WriteLine(coverage);
                }, new Action(() => { }));
                waitHandle.WaitOne();
                operations.Save(options.OutputMesh);
            }
            else
            {
                using (MainWindow window = new MainWindow(input))
                {
                    window.ShowDialog();
                }
            }
        }
Ejemplo n.º 3
0
        public static Mesh Filter(VoxelizationInput input, Mesh mesh)
        {
            // Remove the top and bottom polygons
            if (input.RemoveTop || input.RemoveBottom)
            {
                Vector3 upAxis, downAxis;
                if (input.UpAxis == UpAxis.Y)
                {
                    upAxis = Vector3.UnitY;
                    downAxis = -Vector3.UnitY;
                }
                else // if (input.UpAxis == UpAxis.Z)
                {
                    upAxis = Vector3.UnitZ;
                    downAxis = -Vector3.UnitZ;
                }

                List<Triangle> filteredTrianlges = new List<Triangle>();

                Triangle[] triangles = Triangle.ToTriangleArray(mesh.Indicies, mesh.Vertices);
                foreach (Triangle t in triangles)
                {
                    Plane p = t.Plane;
                    Vector3 normal = t.NormalCounterClockwise();

                    // Remove all top polygon
                    if (input.RemoveTop)
                    {
                        if (Vector3Ex.AlmostEquals(ref normal, ref upAxis))
                            continue;
                    }

                    // Remove all bottom polygons that are with-in one voxel of the mesh bounds.
                    if (input.RemoveBottom)
                    {
                        if (Vector3Ex.AlmostEquals(ref normal, ref downAxis))
                        {
                            Vector3 closestPoint = input.Octree.MeshBounds.ClosestPointOnSurface(t.Center);
                            float distance = (t.Center - closestPoint).Length;
                            if (distance <= input.Octree.SmallestVoxelSideLength)
                                continue;
                        }
                    }

                    filteredTrianlges.Add(t);
                }

                Triangle.FromTriangleArray(filteredTrianlges, out mesh.Indicies, out mesh.Vertices);
            }

            return mesh;
        }
Ejemplo n.º 4
0
        public MainWindow(VoxelizationInput input)
        {
            InitializeComponent();

            LinkLabel label = new LinkLabel();
            label.Text = "Bug/Feature?";
            label.BackColor = Color.Transparent;
            label.LinkColor = Color.Blue;
            label.ActiveLinkColor = Color.Blue;
            label.DisabledLinkColor = Color.Blue;
            label.VisitedLinkColor = Color.Blue;
            label.LinkClicked += (s, e) =>
            {
                Process.Start("mailto:[email protected]?subject=[Oxel] Bug/Feature");
            };
            ToolStripControlHost host = new ToolStripControlHost(label);
            host.Alignment = ToolStripItemAlignment.Right;
            m_menu.SuspendLayout();
            m_menu.Items.Add(host);
            m_menu.ResumeLayout(true);

            m_gl = new GLControl(new GraphicsMode(32, 24, 8));
            m_gl.BackColor = System.Drawing.Color.Black;
            m_gl.Dock = System.Windows.Forms.DockStyle.Fill;
            m_gl.Location = new System.Drawing.Point(0, 0);
            m_gl.Name = "m_gl";
            m_gl.Size = new System.Drawing.Size(716, 516);
            m_gl.TabIndex = 2;
            m_gl.VSync = false;
            m_gl.Load += new System.EventHandler(this.m_gl_Load);
            m_gl.Paint += new System.Windows.Forms.PaintEventHandler(this.m_gl_Paint);
            m_gl.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.m_gl_KeyPress);
            m_gl.MouseDown += new System.Windows.Forms.MouseEventHandler(this.m_gl_MouseDown);
            m_gl.MouseMove += new System.Windows.Forms.MouseEventHandler(this.m_gl_MouseMove);
            m_gl.MouseUp += new System.Windows.Forms.MouseEventHandler(this.m_gl_MouseUp);
            m_gl.MouseWheel += new MouseEventHandler(m_gl_MouseWheel);
            m_gl.Resize += new System.EventHandler(this.m_gl_Resize);

            this.splitContainer1.Panel1.Controls.Add(this.m_gl);

            m_operations = new Operations();
            m_operations.Initialize(input);

            m_propertyGrid.SelectedObject = m_operations.Input;
            m_operations.Input.PropertyChanged += new PropertyChangedEventHandler(vp_PropertyChanged);
        }
        AABBi SimulatedAnnealingFill(VoxelizationInput input, SilhouetteOcclusionValidator sov, VoxelField volume, ref Vector3i densestVoxel, byte fillByte, List<Occluder> currentOccluders)
        {
            AABBi current = new AABBi(densestVoxel.X, densestVoxel.Y, densestVoxel.Z, densestVoxel.X + 1, densestVoxel.Y + 1, densestVoxel.Z + 1);
            AABBi next = new AABBi(0, 0, 0, 0, 0, 0);

            int iteration = -1;

            List<AABBi> relevantOccluders = GetRelevantOccluders(input, currentOccluders);

            List<AABBi> occluders = relevantOccluders.ToList();
            occluders.Add(current);
            long coverage = MeasureOccluderOcclusion(sov, input, occluders);

            double coolignAlpha = 0.999;
            double temperature = 400.0;
            double epsilon = 0.001;

            Random random = new Random(1337);

            int maxItterations = 1000;

            long delta = 0;
            while (temperature > epsilon && iteration < maxItterations)
            {
                iteration++;

                ComputeNext(random, current, next, volume, ref densestVoxel, delta, temperature);

                occluders = relevantOccluders.ToList();
                occluders.Add(next);
                delta = MeasureOccluderOcclusion(sov, input, occluders) - coverage;

                if (delta < 0)
                {
                    next.Clone(current);
                    coverage = delta + coverage;
                }
                else
                {
                    double probability = random.NextDouble();

                    if (probability < Math.Exp(-delta / temperature))
                    {
                        next.Clone(current);
                        coverage = delta + coverage;
                    }
                }

                temperature *= coolignAlpha;

                if (iteration % 400 == 0)
                    Console.WriteLine(coverage);
            }

            FillRange(volume,
                new Vector3i(current.MinX, current.MinY, current.MinZ),
                new Vector3i(current.MaxX, current.MaxY, current.MaxZ),
                fillByte);

            return current;
        }
        long MeasureOccluderOcclusion(SilhouetteOcclusionValidator sov, VoxelizationInput input, List<AABBi> occluderBounds)
        {
            Mesh mesh = BuildMeshFromBoxes(input, occluderBounds);
            RenderableMesh renderable = new RenderableMesh(mesh, true);

            long sideCoverage, topCoverage;
            sov.ComputeCoverage(renderable, input.Octree.MeshBounds, out sideCoverage, out topCoverage);

            renderable.Dispose();

            return sideCoverage + topCoverage;
        }
        List<AABBi> GetRelevantOccluders(VoxelizationInput input, List<Occluder> occluders)
        {
            var occluderBounds =
                from occluder in occluders
                where occluder.DeltaOcclusion > input.MinimumOcclusion
                orderby occluder.DeltaOcclusion descending
                select occluder.Bounds;

            return occluderBounds.ToList();
        }
        public virtual VoxelizationOutput Generate(VoxelizationInput input, Action<VoxelizationProgress> progress)
        {
            VoxelizationProgress vp = new VoxelizationProgress();

            DateTime start = DateTime.Now;

            vp.Status = "Building voxel field from octree";
            progress(vp);

            VoxelField voxelField = new VoxelField(input.Octree);

            Byte fillByte = 2;
            float oldPercent = 1.0f;
            float newPercent = 1.0f;

            List<Occluder> occluders = new List<Occluder>();

            vp.Status = "Calculating original mesh silhouette coverage";
            progress(vp);

            SilhouetteOcclusionValidator sov = new SilhouetteOcclusionValidator(1024, 1024);

            long groundSideCoverage, groundTopCoverage;
            sov.ComputeCoverage(input.OriginalMesh, input.Octree.MeshBounds, out groundSideCoverage, out groundTopCoverage);
            long totalCoverage = groundSideCoverage + groundTopCoverage;
            if (totalCoverage == 0)
                totalCoverage = 1;

            vp.Status = "Fitting boxes into mesh...";
            progress(vp);

            long oldOcclusion = 0;

            do
            {
                Vector3i densestVoxel = FindHighestDensityVoxel(voxelField);

                AABBi occluderBounds;
                if (input.Type == OcclusionType.BoxExpansion)
                {
                    occluderBounds = ExpandAndFillBox(voxelField, ref densestVoxel, fillByte);
                }
                //else if (input.Type == OcclusionType.SimulatedAnnealing)
                //{
                //    occluderBounds = SimulatedAnnealingFill(input, sov, voxelField, ref densestVoxel, fillByte, occluders);
                //}
                else if (input.Type == OcclusionType.BruteForce)
                {
                    occluderBounds = BruteForceFill(input, sov, voxelField, densestVoxel, fillByte, occluders);
                }
                else
                {
                    throw new Exception("Unknown occluder generation type!");
                }

                List<AABBi> relevantOccluders = GetRelevantOccluders(input, occluders);
                relevantOccluders.Add(occluderBounds);

                long newOcclusion = MeasureOccluderOcclusion(sov, input, relevantOccluders);

                Occluder occluder = new Occluder();
                occluder.Bounds = occluderBounds;
                occluder.DeltaOcclusion = (newOcclusion - oldOcclusion) / (double)totalCoverage;

                occluders.Add(occluder);

                if (occluder.DeltaOcclusion > input.MinimumOcclusion)
                    oldOcclusion = newOcclusion;

                Debug.WriteLine("Coverage " + occluder.DeltaOcclusion);
                Debug.WriteLine("Bounds (" + occluder.Bounds.MinX + "x" + occluder.Bounds.MaxX + " " + occluder.Bounds.MinY + "x" + occluder.Bounds.MaxY + " " + occluder.Bounds.MinZ + "x" + occluder.Bounds.MaxZ + ")");

                oldPercent = newPercent;
                newPercent = MeasureUnboxedVoxels(voxelField);

                Debug.WriteLine("(" + densestVoxel.X + "," + densestVoxel.Y + "," + densestVoxel.Z + ")\tCoverage=" + ((1 - newPercent) * 100) + "%\tDelta=" + ((oldPercent - newPercent) * 100) + "%");

                vp.Progress = Math.Min(((1 - newPercent) / input.MinimumVolume), 1.0f);
                vp.SilhouetteCoverage = oldOcclusion / (double)totalCoverage;
                vp.VolumeCoverage = 1 - newPercent;
                vp.Status = String.Format("Occlusion Progress : {0:0.##}%", (100 * vp.Progress));

                progress(vp);

            } while (newPercent > (1 - input.MinimumVolume));

            Mesh mesh = BuildMeshFromBoxes(input, GetRelevantOccluders(input, occluders));

            VoxelizationOutput output = new VoxelizationOutput();

            if (input.Retriangulate)
            {
                vp.Status = "Retriangulating occluder mesh";
                progress(vp);

                Mesh triangulatedMesh = MeshOptimizer.Retriangulate(input, mesh, out output.DebugLines);
                if (triangulatedMesh != null)
                    mesh = triangulatedMesh;
            }

            vp.Status = "Filtering polygons";
            progress(vp);

            mesh = PolygonFilter.Filter(input, mesh);

            vp.Status = "Generating final occlusion mesh";
            progress(vp);

            // Prepare the output
            output.Octree = input.Octree;
            output.TimeTaken = DateTime.Now - start;
            output.VolumeCoverage = 1 - newPercent;
            output.SilhouetteCoverage = oldOcclusion / (double)totalCoverage;
            output.OccluderMesh = new RenderableMesh(mesh, true);

            vp.Status = "Cleanup...";
            progress(vp);

            sov.Dispose();

            return output;
        }
        Mesh BuildMeshFromBoxes(VoxelizationInput input, List<AABBi> boxes)
        {
            // Build the mesh, this will also remove all false triangle loops and collinear point triangles.
            Vector3 deltaP = new Vector3((float)input.Octree.SmallestVoxelSideLength, (float)input.Octree.SmallestVoxelSideLength, (float)input.Octree.SmallestVoxelSideLength);

            return MeshBuilder.BuildMesh(input.Octree.VoxelBounds, deltaP, boxes);
        }
        AABBi BruteForceFill(VoxelizationInput input, SilhouetteOcclusionValidator sov, VoxelField voxelField, Vector3i densestVoxel, byte fillByte, List<Occluder> currentOccluders)
        {
            Object syncroot = new Object();
            Int64 largestVolume = 1;
            AABBi largestOccluder = new AABBi(densestVoxel.X, densestVoxel.Y, densestVoxel.Z, densestVoxel.X + 1, densestVoxel.Y + 1, densestVoxel.Z + 1);

            int MaxTopOccluders = 2000;
            List<AABBi> bestOccluders = new List<AABBi>(MaxTopOccluders);

            Parallel.For(densestVoxel.Z + 1, voxelField.VoxelSize.Z, max_z =>
            {
                for (Int32 min_z = densestVoxel.Z; min_z >= 0; --min_z)
                {
                    for (Int32 max_y = densestVoxel.Y + 1; max_y < voxelField.VoxelSize.Y; ++max_y)
                    {
                        for (Int32 min_y = densestVoxel.Y; min_y >= 0; --min_y)
                        {
                            for (Int32 max_x = densestVoxel.X + 1; max_x < voxelField.VoxelSize.X; ++max_x)
                            {
                                for (Int32 min_x = densestVoxel.X; min_x >= 0; --min_x)
                                {
                                    Int32 dx = max_x - min_x;
                                    Int32 dy = max_y - min_y;
                                    Int32 dz = max_z - min_z;
                                    Int64 volume = dx * dy * dz;

                                    if (TestRangeForFreeSpace(voxelField, new AABBi(min_x, min_y, min_z, max_x, max_y, max_z)))
                                    {
                                        lock (syncroot)
                                        {
                                            if (volume > largestVolume)
                                            {
                                                largestVolume = volume;
                                                largestOccluder = new AABBi(min_x, min_y, min_z, max_x, max_y, max_z);
                                                if (bestOccluders.Count >= MaxTopOccluders)
                                                    bestOccluders.RemoveAt(MaxTopOccluders - 1);
                                                bestOccluders.Insert(0, largestOccluder);
                                            }
                                        }
                                    }
                                    else
                                    {
                                        // if we can't expand outward any further there's no point in checking more.
                                        break;
                                    }
                                }
                            }
                        }
                    }
                }

                Debug.WriteLine("Checked " + max_z);
            });

            List<AABBi> relevantOccluders = GetRelevantOccluders(input, currentOccluders);

            long bestCoverage = 0;
            AABBi bestCoverageVolume = largestOccluder;
            foreach (AABBi occluder in bestOccluders)
            {
                List<AABBi> tempOccluders = relevantOccluders.ToList();
                tempOccluders.Add(occluder);
                long coverage = MeasureOccluderOcclusion(sov, input, tempOccluders);
                if (coverage > bestCoverage)
                {
                    bestCoverage = coverage;
                    bestCoverageVolume = occluder;
                }
            }

            FillRange(voxelField, bestCoverageVolume, fillByte);

            return bestCoverageVolume;
        }
Ejemplo n.º 11
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;
        }