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)); }
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); }
private static List <Triangle> FindAndFixTJuntions(VoxelizationInput input, List <Triangle> triangles) { // When one triangle shares two vertices with the edge of another triangle, but does not also // share the edge with the triangle, there is a T-Junction present. FixStart: int counter = 0; foreach (Triangle t in triangles) { foreach (Triangle other in triangles) { if (t == other) { continue; } Vector3 junctionPoint; int edge; if (t.HasTJunction(other, VERTEX_EPSILON, out edge, out junctionPoint)) { Debug.WriteLine(counter + " FOUND T-JUNCTION!"); counter++; Triangle t0 = null, t1 = null; switch (edge) { case 0: // Edge 0 --- 1 t0 = new Triangle(junctionPoint, t.v2, t.v0); t1 = new Triangle(junctionPoint, t.v1, t.v2); break; case 1: // Edge 1 --- 2 t0 = new Triangle(junctionPoint, t.v0, t.v1); t1 = new Triangle(junctionPoint, t.v2, t.v0); break; case 2: // Edge 2 --- 0 t0 = new Triangle(junctionPoint, t.v1, t.v2); t1 = new Triangle(junctionPoint, t.v0, t.v1); break; default: throw new NotImplementedException(); } int index = triangles.IndexOf(t); triangles.Remove(t); triangles.Insert(index, t0); triangles.Insert(index, t1); goto FixStart; } } } return(triangles); }
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); }
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()); }
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); }
protected override void OnClosing(CancelEventArgs e) { string settingPath = Path.Combine(Path.GetDirectoryName(Application.ExecutablePath), "Oxel.Settings.xml"); VoxelizationInput.Save(settingPath, m_operations.Input); if (m_operations != null) { m_operations.Dispose(); m_operations = null; } base.OnClosing(e); }
internal VoxelizationInput Clone(VoxelizationInput input) { input.m_voxelLevel = m_voxelLevel; input.m_minVolume = m_minVolume; input.m_type = m_type; input.m_retriangulate = m_retriangulate; input.m_minOcclusion = m_minOcclusion; input.m_removeTop = m_removeTop; input.m_removeBottom = m_removeBottom; input.m_upAxis = m_upAxis; input.OriginalMesh = OriginalMesh; input.Octree = Octree; return(input); }
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); }
public static bool Save(string file, VoxelizationInput settings) { try { XmlSerializer x = new XmlSerializer(typeof(VoxelizationInput)); using (StreamWriter myWriter = new StreamWriter(file)) { x.Serialize(myWriter, settings); } } catch (Exception ex) { Debug.WriteLine(ex.Message); return(false); } return(true); }
private static List <Edge> FindDistinctiveEdges(VoxelizationInput input, List <Triangle> triangles) { List <Edge> edges = new List <Edge>(); foreach (Triangle t in triangles) { // TODO NDarnell Winding Order edges.Add(new Edge(t.v0, t.v1)); edges.Add(new Edge(t.v1, t.v2)); edges.Add(new Edge(t.v2, t.v0)); } List <Edge> distinctEdges = (from e in edges where edges.FindAll(ed => e.Equals(ed, VERTEX_EPSILON)).Count == 1 select e).ToList(); return(distinctEdges); }
public static VoxelizationInput Load(string file) { try { if (!File.Exists(file)) { return(null); } VoxelizationInput settings = null; XmlSerializer mySerializer = new XmlSerializer(typeof(VoxelizationInput)); using (FileStream myFileStream = new FileStream(file, FileMode.Open)) { settings = (VoxelizationInput)mySerializer.Deserialize(myFileStream); } return(settings); } catch (Exception ex) { Debug.WriteLine(ex.Message); return(null); } }
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); }
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); }
internal VoxelizationInput Clone() { VoxelizationInput input = new VoxelizationInput(); return(Clone(input)); }
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(); } } }
public static Mesh Retriangulate(VoxelizationInput input, Mesh mesh, out List <List <Edge> > debugLoops) { debugLoops = new List <List <Edge> >(); try { Triangle[] triangles = Triangle.ToTriangleArray(mesh.Indicies, mesh.Vertices); Debug.WriteLine("Sorting triangles based on plane."); List <PolygonTriangles> planeLookup = SortTrianglesIntoPlanes(triangles); Debug.WriteLine("Triangles sorted by planes: Done."); List <Vector4> processedVerticies = new List <Vector4>(); List <int> processedIndicies = new List <int>(); foreach (PolygonTriangles polygon in planeLookup) { #if DEBUGGING_TJUNCTIONS if (!Vector3Ex.AlmostEquals(polygon.Plane.Normal, Vector3.UnitY)) { continue; } #endif // Before we can begin margining edges we need to ensure that all shared collinear edges // have a mate, one thing that can prevent this is the presence of T-Junctions, so first // we need to fix them by splitting polygons that produce them. polygon.Triangles = FindAndFixTJuntions(input, polygon.Triangles); // We build a list of distinctive edges because a distinctive edge is either part of the inner loop // or the outer loop of a group of polygons, because if two polygons share an edge that are co-planer // that edge can't be on the inside or the outside of the plane. List <Edge> distinctEdges = FindDistinctiveEdges(input, polygon.Triangles); debugLoops.Add(distinctEdges.ToList()); // Merge the edges together that are collinear. List <List <Edge> > outterLoops = FindOuterLoops(distinctEdges); //allLoops.AddRange(outterLoops); Vector3 right = Vector3Ex.GetRight(polygon.Plane.Normal); Vector3 U, V; Vector3Ex.GetBasisVectors(polygon.Plane.Normal, right, out U, out V); Vector3 planeOrigin = polygon.Plane.PointOnPlane; foreach (List <Edge> outterEdgeLoop in outterLoops) { List <Vector2> outerLoopTransformed = new List <Vector2>(); for (int i = 0; i < outterEdgeLoop.Count; i++) { Edge e = outterEdgeLoop[i]; Vector2 uv = Vector3Ex.Calc2DPoint(e.v0, U, V); outerLoopTransformed.Add(uv); } //Debug.Assert(outerLoopTransformed.Count > 2); if (outerLoopTransformed.Count > 2) { List <Poly2Tri.PolygonPoint> polyPoints = new List <Poly2Tri.PolygonPoint>(); foreach (var pt in outerLoopTransformed) { polyPoints.Add(new Poly2Tri.PolygonPoint(pt.X, pt.Y)); } Poly2Tri.Polygon p = new Poly2Tri.Polygon(polyPoints); Poly2Tri.P2T.Triangulate(Poly2Tri.TriangulationAlgorithm.DTSweep, p); int currentVerticies = processedVerticies.Count; foreach (var dt in p.Triangles) { processedIndicies.Add((currentVerticies + 0)); processedIndicies.Add((currentVerticies + 1)); processedIndicies.Add((currentVerticies + 2)); Vector3 pt0 = (((float)dt.Points._0.X * U) + ((float)dt.Points._0.Y * V)) - planeOrigin; Vector3 pt1 = (((float)dt.Points._1.X * U) + ((float)dt.Points._1.Y * V)) - planeOrigin; Vector3 pt2 = (((float)dt.Points._2.X * U) + ((float)dt.Points._2.Y * V)) - planeOrigin; processedVerticies.Add(new Vector4(pt0, 1)); processedVerticies.Add(new Vector4(pt1, 1)); processedVerticies.Add(new Vector4(pt2, 1)); currentVerticies += 3; } } else { Debug.WriteLine("outer loop problem!"); } } Debug.WriteLine("Distinct Edges " + processedIndicies.Count / 3); } Mesh retriangulatedMesh = new Mesh(); retriangulatedMesh.Vertices = processedVerticies.ToArray(); retriangulatedMesh.Indicies = processedIndicies.ToArray(); return(retriangulatedMesh); } catch (Exception) { return(null); } }
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); }
public void Initialize(VoxelizationInput input) { Input = input; }
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); }
public WaitHandle GenerateOccluder(Action <VoxelizationProgress> progress, Action done) { ManualResetEvent waitHandle = new ManualResetEvent(false); if (Context == null || Context.OriginalMesh == null) { Logger.DisplayError("Please Open a mesh first."); waitHandle.Set(); return(waitHandle); } Input.Octree = Context.Octree; Input.OriginalMesh = Context.OriginalMesh; VoxelizationInput input = Input.Clone(); VoxelizationOutput output = null; IOccluderGenerator occluder; switch (input.Type) { case OcclusionType.Octree: occluder = new OccluderOctree(); break; case OcclusionType.BoxExpansion: occluder = new OccluderBoxExpansion(); break; //case OcclusionType.SimulatedAnnealing: // occluder = new OccluderBoxExpansion(); // break; case OcclusionType.BruteForce: occluder = new OccluderBoxExpansion(); break; default: throw new Exception("Unknown occluder type."); } Thread thread = new Thread(() => { INativeWindow window = new OpenTK.NativeWindow(); IGraphicsContext gl = new GraphicsContext(new GraphicsMode(32, 24, 8), window.WindowInfo); gl.MakeCurrent(window.WindowInfo); while (window.Exists) { window.ProcessEvents(); try { RobustVoxelizer voxelizer = new RobustVoxelizer(512, 512); output = voxelizer.Voxelize(input, progress); voxelizer.Dispose(); output = occluder.Generate(input, progress); } catch (System.Exception ex) { Debug.WriteLine(ex.ToString()); } window.Close(); break; } gl.MakeCurrent(null); if (Context.OccluderMesh != null) { Context.OccluderMesh.Dispose(); } Context.Octree = output.Octree; Context.OccluderMesh = output.OccluderMesh; Context.VoxelizationOutput = output; waitHandle.Set(); done(); }); thread.IsBackground = true; thread.SetApartmentState(ApartmentState.STA); thread.Start(); return(waitHandle); }