/* * Support for slicing mesh */ protected bool ComputeSlicePlaneCurves(DMesh3 mesh, DMeshAABBTree3 spatial, double z, bool is_solid, out Polygon2d[] loops, out PolyLine2d[] curves) { Func <Vector3d, double> planeF = (v) => { return(v.z - z); }; // find list of triangles that intersect this z-value PlaneIntersectionTraversal planeIntr = new PlaneIntersectionTraversal(mesh, z); spatial.DoTraversal(planeIntr); List <int> triangles = planeIntr.triangles; // compute intersection iso-curves, which produces a 3D graph of undirected edges MeshIsoCurves iso = new MeshIsoCurves(mesh, planeF) { WantGraphEdgeInfo = true }; iso.Compute(triangles); DGraph3 graph = iso.Graph; if (graph.EdgeCount == 0) { loops = new Polygon2d[0]; curves = new PolyLine2d[0]; return(false); } // if this is a closed solid, any open spurs in the graph are errors if (is_solid) { DGraph3Util.ErodeOpenSpurs(graph); } // [RMS] debug visualization //DGraph2 graph2 = new DGraph2(); //Dictionary<int, int> mapV = new Dictionary<int, int>(); //foreach (int vid in graph.VertexIndices()) // mapV[vid] = graph2.AppendVertex(graph.GetVertex(vid).xy); //foreach (int eid in graph.EdgeIndices()) // graph2.AppendEdge(mapV[graph.GetEdge(eid).a], mapV[graph.GetEdge(eid).b]); //SVGWriter svg = new SVGWriter(); //svg.AddGraph(graph2, SVGWriter.Style.Outline("black", 0.05f)); //foreach (int vid in graph2.VertexIndices()) { // if (graph2.IsJunctionVertex(vid)) // svg.AddCircle(new Circle2d(graph2.GetVertex(vid), 0.25f), SVGWriter.Style.Outline("red", 0.1f)); // else if (graph2.IsBoundaryVertex(vid)) // svg.AddCircle(new Circle2d(graph2.GetVertex(vid), 0.25f), SVGWriter.Style.Outline("blue", 0.1f)); //} //svg.Write(string.Format("c:\\meshes\\EXPORT_SLICE_{0}.svg", z)); // extract loops and open curves from graph DGraph3Util.Curves c = DGraph3Util.ExtractCurves(graph, false, iso.ShouldReverseGraphEdge); loops = new Polygon2d[c.Loops.Count]; for (int li = 0; li < loops.Length; ++li) { DCurve3 loop = c.Loops[li]; loops[li] = new Polygon2d(); foreach (Vector3d v in loop.Vertices) { loops[li].AppendVertex(v.xy); } } curves = new PolyLine2d[c.Paths.Count]; for (int pi = 0; pi < curves.Length; ++pi) { DCurve3 span = c.Paths[pi]; curves[pi] = new PolyLine2d(); foreach (Vector3d v in span.Vertices) { curves[pi].AppendVertex(v.xy); } } return(true); }
public MeshRepairOrientation(DMesh3 mesh3, DMeshAABBTree3 spatial = null) { Mesh = mesh3; this.spatial = spatial; }
public static DMesh3 UnityMeshToDMesh(Mesh mesh, bool bSwapLeftright) { Vector3[] vertices = mesh.vertices; Vector3[] normals = mesh.normals; Color32[] colors32 = mesh.colors32; Color[] colors = mesh.colors; Vector2[] uv = mesh.uv; bool bNormals = (normals.Length == mesh.vertexCount); bool bColors = (colors.Length == mesh.vertexCount || colors32.Length == mesh.vertexCount); bool bByteColors = (colors32.Length == mesh.vertexCount); bool bUVs = (uv.Length == mesh.vertexCount); DMesh3 dmesh = new DMesh3(bNormals, bColors, bUVs, false); for (int i = 0; i < mesh.vertexCount; ++i) { Vector3d v = vertices[i]; if (bSwapLeftright) { v.x = -v.x; v.z = -v.z; } NewVertexInfo vInfo = new NewVertexInfo(v); if (bNormals) { vInfo.bHaveN = true; vInfo.n = normals[i]; if (bSwapLeftright) { vInfo.n.x = -vInfo.n.x; vInfo.n.z = -vInfo.n.z; } } if (bColors) { vInfo.bHaveC = true; if (bByteColors) { vInfo.c = new Colorf(colors32[i].r, colors32[i].g, colors32[i].b, 255); } else { vInfo.c = colors[i]; } } if (bUVs) { vInfo.bHaveUV = true; vInfo.uv = uv[i]; } int vid = dmesh.AppendVertex(vInfo); if (vid != i) { throw new InvalidOperationException("UnityUtil.UnityMeshToDMesh: indices weirdness..."); } } int[] triangles = mesh.triangles; for (int i = 0; i < triangles.Length / 3; ++i) { dmesh.AppendTriangle(triangles[3 * i], triangles[3 * i + 1], triangles[3 * i + 2]); } return(dmesh); }
public AutoHoleFill(DMesh3 mesh, EdgeLoop fillLoop) { this.Mesh = mesh; this.FillLoop = fillLoop; }
public void AddMesh(DMesh3 mesh) { AddMesh(mesh, PrintMeshOptions.Default()); }
// Ctor public TerrainChunk(int _arrX, int _arrY, int terrainSize) { //this can be done here because it wont ever need to be modified. List <int> inds = new List <int>(); #region Init xPos = _arrX * (numXVerts - 1); yPos = _arrY * (numXVerts - 1); arrayX = _arrX; arrayY = _arrY; dMesh = new DMesh3(MeshComponents.VertexNormals | MeshComponents.VertexColors); int indexTracker = 0; for (int i = 0; i < numXVerts; i++) { for (int j = 0; j < numXVerts; j++) { Vector3d v = new Vector3d((float)i + xPos, 0, (float)j + yPos); //vertices.Add(new BasicVertex(Convert.ToV3(v), new Vector3(0, 0, 0), new Vector3(0, 1, 0))); dMesh.AppendVertex(new NewVertexInfo(v, new Vector3f(0, 1, 0))); //fancy uv stuff. float uvu, uvv; uvu = ((1f / (numXVerts - 1)) * i); uvv = 1 - ((1f / (numXVerts - 1)) * j); Vertex vert = new Vertex() { x = (float)v.x, y = (float)v.y, z = (float)v.z, u = uvu, v = uvv, n1 = 0.0f, n2 = 1.0f, n3 = 0.0f, index = indexTracker }; vertices.Add(vert); vertNeedsCollisionUpdate.Add(false); indexTracker++; } } for (int j = 0; j < numXVerts - 1; j++) { for (int i = 0; i < numXVerts - 1; i++) { int i1, i2, i3, i4, i5, i6; int row1 = i * numXVerts; int row2 = (i + 1) * numXVerts; i1 = row1 + j; i2 = row1 + j + 1; i3 = row2 + j; i4 = row2 + j + 1; i5 = row2 + j; i6 = row1 + j + 1; if (i == numXVerts - 1 && j == numXVerts - 1) { Console.Write(i4 + " " + i5 + " " + i6); } dMesh.AppendTriangle(i1, i2, i3); dMesh.AppendTriangle(i4, i5, i6); inds.Add(i1); inds.Add(i2); inds.Add(i3); inds.Add(i4); inds.Add(i5); inds.Add(i6); } } dMesh.EnableVertexNormals(new Vector3f(0, 1, 0)); dMeshAABB = new DMeshAABBTree3(dMesh); dMeshAABB.Build(); #endregion // // InitRendering(inds); }
public static void test_remesh_constraints_vertcurves() { int Slices = 16; DMesh3 mesh = TestUtil.MakeCappedCylinder(false, Slices); MeshUtil.ScaleMesh(mesh, Frame3f.Identity, new Vector3f(1, 2, 1)); //DMesh3 mesh = TestUtil.MakeRemeshedCappedCylinder(0.25); //DMesh3 mesh = TestUtil.MakeRemeshedCappedCylinder(1.0); mesh.CheckValidity(); AxisAlignedBox3d bounds = mesh.CachedBounds; // construct mesh projection target DMesh3 meshCopy = new DMesh3(mesh); meshCopy.CheckValidity(); DMeshAABBTree3 tree = new DMeshAABBTree3(meshCopy); tree.Build(); MeshProjectionTarget mesh_target = new MeshProjectionTarget() { Mesh = meshCopy, Spatial = tree }; // cylinder projection target CylinderProjectionTarget cyl_target = new CylinderProjectionTarget() { Cylinder = new Cylinder3d(new Vector3d(0, 1, 0), Vector3d.AxisY, 1, 2) }; //IProjectionTarget target = mesh_target; IProjectionTarget target = cyl_target; // construct projection target circles CircleProjectionTarget bottomCons = new CircleProjectionTarget() { Circle = new Circle3d(bounds.Center, 1.0) }; bottomCons.Circle.Center.y = bounds.Min.y; CircleProjectionTarget topCons = new CircleProjectionTarget() { Circle = new Circle3d(bounds.Center, 1.0) }; topCons.Circle.Center.y = bounds.Max.y; if (WriteDebugMeshes) { TestUtil.WriteDebugMesh(mesh, "remesh_analytic_constraints_test_before.obj"); } // construct constraint set MeshConstraints cons = new MeshConstraints(); //EdgeRefineFlags useFlags = EdgeRefineFlags.NoFlip | EdgeRefineFlags.NoCollapse; EdgeRefineFlags useFlags = EdgeRefineFlags.NoFlip; bool bConstrainVertices = true; foreach (int eid in mesh.EdgeIndices()) { double fAngle = MeshUtil.OpeningAngleD(mesh, eid); if (fAngle > 30.0f) { Index2i ev = mesh.GetEdgeV(eid); Vector3d ev0 = mesh.GetVertex(ev[0]); Vector3d ev1 = mesh.GetVertex(ev[1]); CircleProjectionTarget loopTarget = null; if (ev0.y > bounds.Center.y && ev1.y > bounds.Center.y) { loopTarget = topCons; } else if (ev0.y < bounds.Center.y && ev1.y < bounds.Center.y) { loopTarget = bottomCons; } cons.SetOrUpdateEdgeConstraint(eid, new EdgeConstraint(useFlags, loopTarget)); if (bConstrainVertices && loopTarget != null) { cons.SetOrUpdateVertexConstraint(ev[0], new VertexConstraint(loopTarget)); cons.SetOrUpdateVertexConstraint(ev[1], new VertexConstraint(loopTarget)); } } } Remesher r = new Remesher(mesh); //r.SetExternalConstraints(cons); r.SetProjectionTarget(target); r.Precompute(); r.ENABLE_PROFILING = true; var stopwatch = Stopwatch.StartNew(); //double fResScale = 1.0f; double fResScale = 0.5f; r.EnableFlips = r.EnableSplits = r.EnableCollapses = true; r.MinEdgeLength = 0.1f * fResScale; r.MaxEdgeLength = 0.2f * fResScale; r.EnableSmoothing = true; r.SmoothSpeedT = 1.0f; try { for (int k = 0; k < 20; ++k) { r.BasicRemeshPass(); mesh.CheckValidity(); } } catch { // continue; } stopwatch.Stop(); System.Console.WriteLine("Second Pass Timing: " + stopwatch.Elapsed); if (WriteDebugMeshes) { TestUtil.WriteDebugMesh(mesh, "remesh_analytic_constraints_test_after.obj"); } }
public MeshTopology(DMesh3 mesh) { Mesh = mesh; }
// [RMS] this only tests some basic cases... public static void test_Laplacian() { // compact version DMesh3 mesh = new DMesh3(TestUtil.MakeRemeshedCappedCylinder(1.0), true); Debug.Assert(mesh.IsCompact); AxisAlignedBox3d bounds = mesh.GetBounds(); TestUtil.WriteDebugMesh(mesh, "___CG_before.obj"); List <IMesh> result_meshes = new List <IMesh>(); // make uniform laplacian matrix int N = mesh.VertexCount; SymmetricSparseMatrix M = new SymmetricSparseMatrix(); //DenseMatrix M = new DenseMatrix(N, N); double[] Px = new double[N], Py = new double[N], Pz = new double[N]; int[] nbr_counts = new int[N]; for (int vid = 0; vid < N; ++vid) { nbr_counts[vid] = mesh.GetVtxEdgeCount(vid); } int ti = MeshQueries.FindNearestTriangle_LinearSearch(mesh, new Vector3d(2, 5, 2)); int v_pin = mesh.GetTriangle(ti).a; List <int> constraints = new List <int>() { v_pin }; double consW = 10; double consBottom = 10; foreach (int vid in constraints) { result_meshes.Add(TestUtil.MakeMarker(mesh.GetVertex(vid), (vid == 0) ? 0.2f : 0.1f, Colorf.Red)); } for (int vid = 0; vid < N; ++vid) { int n = nbr_counts[vid]; Vector3d v = mesh.GetVertex(vid), c = Vector3d.Zero; Px[vid] = v.x; Py[vid] = v.y; Pz[vid] = v.z; bool bottom = (v.y - bounds.Min.y) < 0.01f; double sum_w = 0; foreach (int nbrvid in mesh.VtxVerticesItr(vid)) { int n2 = nbr_counts[nbrvid]; // weight options //double w = -1; double w = -1.0 / Math.Sqrt(n + n2); //double w = -1.0 / n; M.Set(vid, nbrvid, w); c += w * mesh.GetVertex(nbrvid); sum_w += w; } sum_w = -sum_w; M.Set(vid, vid, sum_w); // add soft constraints if (constraints.Contains(vid)) { M.Set(vid, vid, sum_w + consW); } else if (bottom) { M.Set(vid, vid, sum_w + consBottom); } } // compute laplacians double[] MLx = new double[N], MLy = new double[N], MLz = new double[N]; M.Multiply(Px, MLx); M.Multiply(Py, MLy); M.Multiply(Pz, MLz); DiagonalMatrix Preconditioner = new DiagonalMatrix(N); for (int i = 0; i < N; i++) { Preconditioner.Set(i, i, 1.0 / M[i, i]); } MLy[v_pin] += consW * 0.5f; MLx[v_pin] += consW * 0.5f; MLz[v_pin] += consW * 0.5f; bool useXAsGuess = true; // preconditioned SparseSymmetricCG SolverX = new SparseSymmetricCG() { B = MLx, X = Px, MultiplyF = M.Multiply, PreconditionMultiplyF = Preconditioner.Multiply, UseXAsInitialGuess = useXAsGuess }; // initial solution SparseSymmetricCG SolverY = new SparseSymmetricCG() { B = MLy, X = Py, MultiplyF = M.Multiply, UseXAsInitialGuess = useXAsGuess }; // neither of those SparseSymmetricCG SolverZ = new SparseSymmetricCG() { B = MLz, MultiplyF = M.Multiply }; bool bx = SolverX.Solve(); bool by = SolverY.Solve(); bool bz = SolverZ.Solve(); for (int vid = 0; vid < mesh.VertexCount; ++vid) { Vector3d newV = new Vector3d(SolverX.X[vid], SolverY.X[vid], SolverZ.X[vid]); mesh.SetVertex(vid, newV); } result_meshes.Add(mesh); TestUtil.WriteDebugMeshes(result_meshes, "___CG_result.obj"); }
static void Main(string[] args) { CommandArgumentSet arguments = new CommandArgumentSet(); arguments.Register("-tcount", int.MaxValue); arguments.Register("-percent", 50.0f); arguments.Register("-v", false); arguments.Register("-output", ""); if (arguments.Parse(args) == false) { return; } if (arguments.Filenames.Count != 1) { print_usage(); return; } string inputFilename = arguments.Filenames[0]; if (!File.Exists(inputFilename)) { System.Console.WriteLine("File {0} does not exist", inputFilename); return; } string outputFilename = Path.GetFileNameWithoutExtension(inputFilename); string format = Path.GetExtension(inputFilename); outputFilename = outputFilename + ".reduced" + format; if (arguments.Saw("-output")) { outputFilename = arguments.Strings["-output"]; } int triCount = int.MaxValue; if (arguments.Saw("-tcount")) { triCount = arguments.Integers["-tcount"]; } float percent = 50.0f; if (arguments.Saw("-percent")) { percent = arguments.Floats["-percent"]; } bool verbose = false; if (arguments.Saw("-v")) { verbose = arguments.Flags["-v"]; } List <DMesh3> meshes; try { DMesh3Builder builder = new DMesh3Builder(); IOReadResult result = StandardMeshReader.ReadFile(inputFilename, ReadOptions.Defaults, builder); if (result.code != IOCode.Ok) { System.Console.WriteLine("Error reading {0} : {1}", inputFilename, result.message); return; } meshes = builder.Meshes; } catch (Exception e) { System.Console.WriteLine("Exception reading {0} : {1}", inputFilename, e.Message); return; } if (meshes.Count == 0) { System.Console.WriteLine("file did not contain any valid meshes"); return; } DMesh3 mesh = meshes[0]; for (int k = 1; k < meshes.Count; ++k) { MeshEditor.Append(mesh, meshes[k]); } if (mesh.TriangleCount == 0) { System.Console.WriteLine("mesh does not contain any triangles"); return; } if (verbose) { System.Console.WriteLine("initial mesh contains {0} triangles", mesh.TriangleCount); } Reducer r = new Reducer(mesh); if (triCount < int.MaxValue) { if (verbose) { System.Console.Write("reducing to {0} triangles...", triCount); } r.ReduceToTriangleCount(triCount); } else { int nT = (int)((float)mesh.TriangleCount * percent / 100.0f); nT = MathUtil.Clamp(nT, 1, mesh.TriangleCount); if (verbose) { System.Console.Write("reducing to {0} triangles...", nT); } r.ReduceToTriangleCount(nT); } if (verbose) { System.Console.WriteLine("done!"); } try { IOWriteResult wresult = StandardMeshWriter.WriteMesh(outputFilename, mesh, WriteOptions.Defaults); if (wresult.code != IOCode.Ok) { System.Console.WriteLine("Error writing {0} : {1}", inputFilename, wresult.message); return; } } catch (Exception e) { System.Console.WriteLine("Exception reading {0} : {1}", inputFilename, e.Message); return; } return; }
public fMesh(DMesh3 source) { mesh = UnityUtil.DMeshToUnityMesh(source, false); }
private static List <Polygon2d> decompose_cluster_up(DMesh3 mesh, double minArea) { optimize_mesh(mesh); mesh.CompactInPlace(); mesh.DiscardTriangleGroups(); mesh.EnableTriangleGroups(0); Dictionary <int, double> areas = new Dictionary <int, double>(); Dictionary <int, HashSet <int> > trisets = new Dictionary <int, HashSet <int> >(); HashSet <int> active_groups = new HashSet <int>(); Action <int, int> add_tri_to_group = (tid, gid) => { mesh.SetTriangleGroup(tid, gid); areas[gid] = areas[gid] + mesh.GetTriArea(tid); trisets[gid].Add(tid); }; Action <int, int> add_group_to_group = (gid, togid) => { var set = trisets[togid]; foreach (int tid in trisets[gid]) { mesh.SetTriangleGroup(tid, togid); set.Add(tid); } areas[togid] += areas[gid]; active_groups.Remove(gid); }; Func <IEnumerable <int>, int> find_min_area_group = (tri_itr) => { int min_gid = -1; double min_area = double.MaxValue; foreach (int tid in tri_itr) { int gid = mesh.GetTriangleGroup(tid); double a = areas[gid]; if (a < min_area) { min_area = a; min_gid = gid; } } return(min_gid); }; foreach (int eid in MeshIterators.InteriorEdges(mesh)) { Index2i et = mesh.GetEdgeT(eid); if (mesh.GetTriangleGroup(et.a) != 0 || mesh.GetTriangleGroup(et.b) != 0) { continue; } int gid = mesh.AllocateTriangleGroup(); areas[gid] = 0; trisets[gid] = new HashSet <int>(); active_groups.Add(gid); add_tri_to_group(et.a, gid); add_tri_to_group(et.b, gid); } foreach (int tid in mesh.TriangleIndices()) { if (mesh.GetTriangleGroup(tid) != 0) { continue; } int gid = find_min_area_group(mesh.TriTrianglesItr(tid)); add_tri_to_group(tid, gid); } IndexPriorityQueue pq = new IndexPriorityQueue(mesh.MaxGroupID); foreach (var pair in areas) { pq.Insert(pair.Key, (float)pair.Value); } while (pq.Count > 0) { int gid = pq.First; pq.Remove(gid); if (areas[gid] > minArea) // ?? { break; } List <int> nbr_groups = find_neighbour_groups(mesh, gid, trisets[gid]); int min_gid = -1; double min_area = double.MaxValue; foreach (int ngid in nbr_groups) { double a = areas[ngid]; if (a < min_area) { min_area = a; min_gid = ngid; } } if (min_gid != -1) { add_group_to_group(gid, min_gid); pq.Remove(min_gid); pq.Insert(min_gid, (float)areas[min_gid]); } } List <Polygon2d> result = new List <Polygon2d>(); int[][] sets = FaceGroupUtil.FindTriangleSetsByGroup(mesh); foreach (var set in sets) { result.Add(make_poly(mesh, set)); } return(result); }
public void Compute(PrintSettings printSettings) { int N = meshCopies.Count(); slicer = new MeshPlanarSlicerPro() { LayerHeightMM = printSettings.LayerHeightMM, // [RMS] 1.5 here is a hack. If we don't leave a bit of space then often the filament gets squeezed right at // inside/outside transitions, which is bad. Need a better way to handle. OpenPathDefaultWidthMM = printSettings.NozzleDiameterMM * 1.5, SetMinZValue = 0, SliceFactoryF = PlanarSlicePro.FactoryF }; if (printSettings.OpenMode == PrintSettings.OpenMeshMode.Clipped) { slicer.DefaultOpenPathMode = PrintMeshOptions.OpenPathsModes.Clipped; } else if (printSettings.OpenMode == PrintSettings.OpenMeshMode.Embedded) { slicer.DefaultOpenPathMode = PrintMeshOptions.OpenPathsModes.Embedded; } else if (printSettings.OpenMode == PrintSettings.OpenMeshMode.Ignored) { slicer.DefaultOpenPathMode = PrintMeshOptions.OpenPathsModes.Ignored; } if (printSettings.StartLayers > 0) { int start_layers = printSettings.StartLayers; double std_layer_height = printSettings.LayerHeightMM; double start_layer_height = printSettings.StartLayerHeightMM; slicer.LayerHeightF = (layer_i) => { return((layer_i < start_layers) ? start_layer_height : std_layer_height); }; } try { MeshAssembly = new PrintMeshAssembly(); for (int k = 0; k < N; ++k) { DMesh3 mesh = meshCopies[k]; //Frame3f mapF = meshToScene[k]; PrintMeshSettings settings = new PrintMeshSettings(); var options = new PrintMeshOptions { IsSupport = (settings.ObjectType == PrintMeshSettings.ObjectTypes.Support), IsCavity = (settings.ObjectType == PrintMeshSettings.ObjectTypes.Cavity), IsCropRegion = (settings.ObjectType == PrintMeshSettings.ObjectTypes.CropRegion), IsOpen = settings.OuterShellOnly, OpenPathMode = PrintMeshSettings.Convert(settings.OpenMeshMode), Extended = new ExtendedPrintMeshOptions() { ClearanceXY = settings.Clearance, OffsetXY = settings.OffsetXY } }; //Vector3f scale = localScale[k]; //MeshTransforms.Scale(mesh, scale.x, scale.y, scale.z); //MeshTransforms.FromFrame(mesh, mapF); //MeshTransforms.FlipLeftRightCoordSystems(mesh); //MeshTransforms.ConvertYUpToZUp(mesh); var decomposer = new MeshAssembly(mesh) { HasNoVoids = settings.NoVoids }; decomposer.Decompose(); MeshAssembly.AddMeshes(decomposer.ClosedSolids, options); PrintMeshOptions openOptions = options.Clone(); MeshAssembly.AddMeshes(decomposer.OpenMeshes, openOptions); } if (slicer.Add(MeshAssembly) == false) { throw new Exception("error adding PrintMeshAssembly to Slicer!!"); } // set clip box Box2d clip_box = new Box2d( Vector2d.Zero, new Vector2d(printSettings.BedSizeXMM / 2, printSettings.BedSizeYMM / 2)); slicer.ValidRegions = new List <GeneralPolygon2d>() { new GeneralPolygon2d(new Polygon2d(clip_box.ComputeVertices())) }; SliceStack = slicer.Compute(); Success = true; } catch (Exception e) { //DebugUtil.Log("GeometrySlicer.Compute: exception: " + e.Message); System.Diagnostics.Debugger.Break(); Success = false; } Finished = true; }
static string GenerateGCodeForMeshes(PrintMeshAssembly meshes) { AxisAlignedBox3d bounds = meshes.TotalBounds; double top_z = bounds.Depth; // configure settings RepRapSettings settings = new RepRapSettings(RepRap.Models.Unknown); settings.GenerateSupport = false; settings.EnableBridging = false; int nSpeed = 1200; // foam //int nSpeed = 700; // wood settings.RapidTravelSpeed = nSpeed; settings.RapidExtrudeSpeed = nSpeed; settings.CarefulExtrudeSpeed = nSpeed; settings.OuterPerimeterSpeedX = 1.0; settings.ZTravelSpeed = nSpeed; settings.RetractSpeed = nSpeed; settings.LayerHeightMM = 4.0; settings.Machine.NozzleDiamMM = 6.35; settings.Machine.BedSizeXMM = 240; settings.Machine.BedSizeYMM = 190; settings.RetractDistanceMM = 1; settings.EnableRetraction = true; settings.ShellsFillNozzleDiamStepX = 0.5; settings.SolidFillNozzleDiamStepX = 0.9; settings.SolidFillBorderOverlapX = 0.5; LastSettings = settings.CloneAs <SingleMaterialFFFSettings>(); System.Console.WriteLine("Slicing..."); // slice meshes MeshPlanarMillSlicer slicer = new MeshPlanarMillSlicer() { LayerHeightMM = settings.LayerHeightMM, ToolDiameter = settings.Machine.NozzleDiamMM, ExpandStockAmount = 0.4 * settings.Machine.NozzleDiamMM }; slicer.Add(meshes); MeshPlanarMillSlicer.Result sliceResult = slicer.Compute(); PlanarSliceStack slices = sliceResult.Clearing; System.Console.WriteLine("Generating GCode..."); ToolpathSet accumPaths; GCodeFile genGCode = generate_cnc_test(sliceResult, settings, out accumPaths); System.Console.WriteLine("Writing GCode..."); string sWritePath = "../../../sample_output/generated.nc"; StandardGCodeWriter writer = new StandardGCodeWriter() { CommentStyle = StandardGCodeWriter.CommentStyles.Bracket }; using (StreamWriter w = new StreamWriter(sWritePath)) { writer.WriteFile(genGCode, w); } //DMesh3 tube_mesh = GenerateTubeMeshesForGCode(sWritePath, settings.Machine.NozzleDiamMM); DMesh3 tube_mesh = GenerateTubeMeshesForGCode(sWritePath, 0.4); StandardMeshWriter.WriteMesh("../../../sample_output/generated_tubes.obj", tube_mesh, WriteOptions.Defaults); if (SHOW_RELOADED_GCODE_PATHS == false) { View.SetPaths(accumPaths, settings); View.PathDiameterMM = (float)settings.Machine.NozzleDiamMM; } slices.Add(sliceResult.HorizontalFinish.Slices); slices.Slices.Sort((a, b) => { return(a.Z.CompareTo(b.Z)); }); View.SetSlices(slices); View.CurrentLayer = slices.Slices.Count - 1; return(sWritePath); }
public override Schematic WriteSchematic() { DMesh3 mesh = StandardMeshReader.ReadMesh(_path); AxisAlignedBox3d bounds = mesh.CachedBounds; DMeshAABBTree3 spatial = new DMeshAABBTree3(mesh, autoBuild: true); double cellsize = mesh.CachedBounds.MaxDim / mGridSize; ShiftGridIndexer3 indexer = new ShiftGridIndexer3(bounds.Min, cellsize); MeshSignedDistanceGrid sdf = new MeshSignedDistanceGrid(mesh, cellsize); sdf.Compute(); Bitmap3 bmp = new Bitmap3(sdf.Dimensions); Schematic schematic = new Schematic(); if (mWindingNumber != 0) { spatial.WindingNumber(Vector3d.Zero); // seed cache outside of parallel eval using (ProgressBar progressbar = new ProgressBar()) { List <Vector3i> list = bmp.Indices().ToList(); int count = 0; gParallel.ForEach(bmp.Indices(), (idx) => { Vector3d v = indexer.FromGrid(idx); bmp.SafeSet(idx, spatial.WindingNumber(v) > mWindingNumber); count++; progressbar.Report(count / (float)list.Count); }); } if (!mExcavate) { foreach (Vector3i idx in bmp.Indices()) { if (bmp.Get(idx)) { schematic.Blocks.Add(new Voxel((ushort)idx.x, (ushort)idx.y, (ushort)idx.z, Color.White.ColorToUInt())); } } } } else { using (ProgressBar progressbar = new ProgressBar()) { int count = bmp.Indices().Count(); List <Vector3i> list = bmp.Indices().ToList(); for (int i = 0; i < count; i++) { Vector3i idx = list[i]; float f = sdf[idx.x, idx.y, idx.z]; bool isInside = f < 0; bmp.Set(idx, (f < 0)); if (!mExcavate && isInside) { schematic.Blocks.Add(new Voxel((ushort)idx.x, (ushort)idx.y, (ushort)idx.z, Color.White.ColorToUInt())); } progressbar.Report((i / (float)count)); } } } if (mExcavate) { foreach (Vector3i idx in bmp.Indices()) { if (bmp.Get(idx) && IsBlockConnectedToAir(bmp, idx)) { schematic.Blocks.Add(new Voxel((ushort)idx.x, (ushort)idx.y, (ushort)idx.z, Color.White.ColorToUInt())); } } } return(schematic); }
public static fMesh DMeshToUnityMesh(DMesh3 m, bool bSwapLeftRight, bool bAllowLargeMeshes = false) { if (bSwapLeftRight) { throw new Exception("[RMSNOTE] I think this conversion is wrong, see MeshTransforms.SwapLeftRight. Just want to know if this code is ever hit."); } if (bAllowLargeMeshes == false) { if (m.MaxVertexID > 65000 || m.MaxTriangleID > 65000) { Debug.Log("[UnityUtil.DMeshToUnityMesh] attempted to import object larger than 65000 verts/tris, not supported by Unity!"); return(null); } } Mesh unityMesh = new Mesh(); Vector3[] vertices = dvector_to_vector3(m.VerticesBuffer); Vector3[] normals = (m.HasVertexNormals) ? dvector_to_vector3(m.NormalsBuffer) : null; unityMesh.vertices = vertices; if (m.HasVertexNormals) { unityMesh.normals = normals; } if (m.HasVertexColors) { unityMesh.colors = dvector_to_color(m.ColorsBuffer); } if (m.HasVertexUVs) { unityMesh.uv = dvector_to_vector2(m.UVBuffer); } if (bAllowLargeMeshes && (m.MaxVertexID > 65000 || m.TriangleCount > 65000)) { unityMesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32; } if (m.IsCompactT) { unityMesh.triangles = dvector_to_int(m.TrianglesBuffer); } else { int[] triangles = new int[m.TriangleCount * 3]; int ti = 0; for (int k = 0; k < m.MaxTriangleID; ++k) { if (m.IsTriangle(k)) { Index3i t = m.GetTriangle(k); int j = 3 * ti; triangles[j] = t.a; triangles[j + 1] = t.b; triangles[j + 2] = t.c; ti++; } } unityMesh.triangles = triangles; } if (m.HasVertexNormals == false) { unityMesh.RecalculateNormals(); } return(new fMesh(unityMesh)); }
static void HandleGenColOptions(string[] args) { CommandLine.Parse <GenColOptions>(args, (opts, gOpts) => { if (opts.InputFile == null) { Console.WriteLine("Please provide input file with -i --input"); return; } if (opts.OutputFile == null) { Console.WriteLine("Please provide output file with -o --output"); return; } var inputFileInfos = new FileInfo(opts.InputFile); var outputFileInfos = new FileInfo(opts.OutputFile); if (!inputFileInfos.Exists) { Console.WriteLine("Input file does not exists"); return; } if (inputFileInfos.Extension == ".ydr") { var ydr = new YdrFile(); ydr.Load(inputFileInfos.FullName); var modelData = GenCol_GetModelData(ydr.Drawable.DrawableModelsX); var bComposite = GenCol_CreateBoundComposite(modelData); DMesh3 mesh; if (opts.Mode == "copy") { mesh = new DMesh3(); var triangles = new List <DefaultConvexFace <VertexVector3> >(); for (int g = 0; g < modelData.Geometries.Count; g++) { var mGeometry = modelData.Geometries[g]; for (int i = 0; i < mGeometry.Indices.Count - 2; i += 3) { var vert1 = mGeometry.Vertices[mGeometry.Indices[i + 0]]; var vert2 = mGeometry.Vertices[mGeometry.Indices[i + 1]]; var vert3 = mGeometry.Vertices[mGeometry.Indices[i + 2]]; var triangle = new DefaultConvexFace <VertexVector3>() { Vertices = new VertexVector3[] { vert1, vert2, vert3, } }; triangles.Add(triangle); } } mesh = GenCol_CreateMesh(triangles); } else { var hull = ConvexHull.Create(modelData.Vertices); var hullTriangles = hull.Result.Faces.ToList(); mesh = GenCol_CreateMesh(hullTriangles); } GenCol_Reshape(mesh, opts.Smooth, opts.TriangleCount); mesh = GenCol_CleanVertices(mesh); var quantum = (modelData.BbMax - modelData.BbMin) / (2 ^ opts.Qantum); var bGeometry = new BoundGeometry { Type = 4, Vertices = new ResourceSimpleArray <BoundVertex>(), BoundingBoxCenter = (RAGE_Vector3)modelData.BsCenter, BoundingSphereRadius = modelData.BsRadius, BoundingBoxMin = (RAGE_Vector3)modelData.BbMin, BoundingBoxMax = (RAGE_Vector3)modelData.BbMax, CenterGravity = new RAGE_Vector3(0.0f, 0.0f, 0.0f), CenterGeometry = new RAGE_Vector3(0.0f, 0.0f, 0.0f), Margin = 0.04f, Quantum = new RAGE_Vector3(quantum.X, quantum.Y, quantum.Z), Polygons = new ResourceSimpleArray <BoundPolygon>(), Materials = new ResourceSimpleArray <BoundMaterial>(), MaterialColours = new ResourceSimpleArray <uint_r>(), PolygonMaterialIndices = new ResourceSimpleArray <byte_r>(), Unknown_78h_Data = new ResourceSimpleArray <BoundVertex>(), }; var material = new BoundMaterial(); bGeometry.Materials.Add(material); var matColour = new uint_r { Value = 0 }; bGeometry.MaterialColours.Add(matColour); var meshVertices = mesh.Vertices().ToList(); for (int i = 0; i < meshVertices.Count; i++) { var vertex = meshVertices[i]; var bVertex = new BoundVertex { X = Convert.ToInt16(vertex.x / quantum.X), Y = Convert.ToInt16(vertex.y / quantum.Y), Z = Convert.ToInt16(vertex.z / quantum.Z), }; bGeometry.Vertices.Add(bVertex); bGeometry.Unknown_78h_Data.Add(bVertex); } var meshTriangles = mesh.Triangles().ToList(); for (int i = 0; i < meshTriangles.Count; i++) { var polygon = new BoundPolygon(); var triangle = new BoundPolygonTriangle(); triangle.TriArea = 0.0f; int vidx1 = meshTriangles[i].a; int vidx2 = meshTriangles[i].b; int vidx3 = meshTriangles[i].c; if (vidx1 == -1 || vidx2 == -1 || vidx3 == -1) { continue; } triangle.TriIndex1 = (ushort)((triangle.TriIndex1 & ~0x7FFF) | (vidx1 & 0x7FFF)); triangle.TriIndex2 = (ushort)((triangle.TriIndex2 & ~0x7FFF) | (vidx2 & 0x7FFF)); triangle.TriIndex3 = (ushort)((triangle.TriIndex3 & ~0x7FFF) | (vidx3 & 0x7FFF)); triangle.EdgeIndex1 = 0; triangle.EdgeIndex2 = 1; triangle.EdgeIndex3 = 2; polygon.data = new byte[16]; int offset = 0; byte[] bytes = BitConverter.GetBytes(triangle.TriArea); Buffer.BlockCopy(bytes, 0, polygon.data, offset, bytes.Length); offset += bytes.Length; bytes = BitConverter.GetBytes(triangle.TriIndex1); Buffer.BlockCopy(bytes, 0, polygon.data, offset, bytes.Length); offset += bytes.Length; bytes = BitConverter.GetBytes(triangle.TriIndex2); Buffer.BlockCopy(bytes, 0, polygon.data, offset, bytes.Length); offset += bytes.Length; bytes = BitConverter.GetBytes(triangle.TriIndex3); Buffer.BlockCopy(bytes, 0, polygon.data, offset, bytes.Length); offset += bytes.Length; bytes = BitConverter.GetBytes(triangle.EdgeIndex1); Buffer.BlockCopy(bytes, 0, polygon.data, offset, bytes.Length); offset += bytes.Length; bytes = BitConverter.GetBytes(triangle.EdgeIndex2); Buffer.BlockCopy(bytes, 0, polygon.data, offset, bytes.Length); offset += bytes.Length; bytes = BitConverter.GetBytes(triangle.EdgeIndex3); Buffer.BlockCopy(bytes, 0, polygon.data, offset, bytes.Length); offset += bytes.Length; bGeometry.Polygons.Add(polygon); var matIndex = new byte_r { Value = 0 }; bGeometry.PolygonMaterialIndices.Add(matIndex); } bComposite.Children.Add(bGeometry); if (outputFileInfos.Extension == ".ybn") { var ybn = new YbnFile { Bound = bComposite }; ybn.Save(opts.OutputFile); } else if (outputFileInfos.Extension == ".ydr") { ydr.Drawable.Bound = bComposite; ydr.Save(opts.OutputFile); } else { Console.WriteLine("Output file type not valid"); } } else { Console.WriteLine("Input file type not valid"); } }); }
public bool ImportAutoUpdate(PrintMeshSO so) { SourceFilePath = so.SourceFilePath; if (!File.Exists(SourceFilePath)) { ErrorMessage = "MeshImporter.ImportAutoUpdate: file does not exist"; return(false); } DMesh3Builder builder = new DMesh3Builder(); StandardMeshReader reader = new StandardMeshReader() { MeshBuilder = builder }; long timestamp = File.GetLastWriteTime(SourceFilePath).Ticks; IOReadResult result = reader.Read(SourceFilePath, ReadOptions.Defaults); if (result.code != IOCode.Ok) { ErrorMessage = "MeshImporter.ImportAutoUpdate: failed with message " + result.message; return(false); } if (builder.Meshes.Count == 0) { ErrorMessage = "MeshImporter.ImportAutoUpdate: no meshes in file!"; return(false); } if (builder.Meshes.Count != 1) { ErrorMessage = "MeshImporter.ImportAutoUpdate: can only auto-update from file with single mesh!"; return(false); } DMesh3 mesh = builder.Meshes[0]; // unity xforms MeshTransforms.ConvertZUpToYUp(mesh); MeshTransforms.FlipLeftRightCoordSystems(mesh); // wait for any active tools to finish // [TODO] do we need to do this? while (CC.ActiveContext.ToolManager.HasActiveTool()) { Thread.Sleep(1000); } if (CC.ActiveScene.SceneObjects.Contains(so) == false) { ErrorMessage = "MeshImporter.ImportAutoUpdate: SO no longer exists"; return(false); } // change event?? so.LastReadFileTimestamp = timestamp; ThreadMailbox.PostToMainThread(() => { so.ReplaceMesh(mesh, true); }); return(true); }
public static void test_remesh_constraints_fixedverts() { int Slices = 128; DMesh3 mesh = TestUtil.MakeCappedCylinder(false, Slices); MeshUtil.ScaleMesh(mesh, Frame3f.Identity, new Vector3f(1, 2, 1)); mesh.CheckValidity(); AxisAlignedBox3d bounds = mesh.CachedBounds; // construct mesh projection target DMesh3 meshCopy = new DMesh3(mesh); meshCopy.CheckValidity(); DMeshAABBTree3 tree = new DMeshAABBTree3(meshCopy); tree.Build(); MeshProjectionTarget target = new MeshProjectionTarget() { Mesh = meshCopy, Spatial = tree }; if (WriteDebugMeshes) { TestUtil.WriteDebugMesh(mesh, "remesh_fixed_constraints_test_before.obj"); } // construct constraint set MeshConstraints cons = new MeshConstraints(); //EdgeRefineFlags useFlags = EdgeRefineFlags.NoFlip | EdgeRefineFlags.NoCollapse; EdgeRefineFlags useFlags = EdgeRefineFlags.NoFlip; foreach (int eid in mesh.EdgeIndices()) { double fAngle = MeshUtil.OpeningAngleD(mesh, eid); if (fAngle > 30.0f) { cons.SetOrUpdateEdgeConstraint(eid, new EdgeConstraint(useFlags)); Index2i ev = mesh.GetEdgeV(eid); int nSetID0 = (mesh.GetVertex(ev[0]).y > bounds.Center.y) ? 1 : 2; int nSetID1 = (mesh.GetVertex(ev[1]).y > bounds.Center.y) ? 1 : 2; cons.SetOrUpdateVertexConstraint(ev[0], new VertexConstraint(true, nSetID0)); cons.SetOrUpdateVertexConstraint(ev[1], new VertexConstraint(true, nSetID1)); } } Remesher r = new Remesher(mesh); r.Precompute(); r.SetExternalConstraints(cons); r.SetProjectionTarget(target); var stopwatch = Stopwatch.StartNew(); //double fResScale = 1.0f; double fResScale = 0.5f; r.EnableFlips = r.EnableSplits = r.EnableCollapses = true; r.MinEdgeLength = 0.1f * fResScale; r.MaxEdgeLength = 0.2f * fResScale; r.EnableSmoothing = true; r.SmoothSpeedT = 0.5f; try { for (int k = 0; k < 20; ++k) { r.BasicRemeshPass(); mesh.CheckValidity(); } } catch { // ignore } stopwatch.Stop(); System.Console.WriteLine("Second Pass Timing: " + stopwatch.Elapsed); if (WriteDebugMeshes) { TestUtil.WriteDebugMesh(mesh, "remesh_fixed_constraints_test_after.obj"); } }
public MinimalHoleFill(DMesh3 mesh, EdgeLoop fillLoop) { this.Mesh = mesh; this.FillLoop = fillLoop; }
// NO DOES NOT WORK. DOES NOT FIND EDGE SPANS THAT ARE IN PLANE BUT HAVE DIFFERENT NORMAL! // NEED TO COLLECT UP SPANS USING NORMAL HISTOGRAM NORMALS! // ALSO NEED TO ACTUALLY CHECK FOR COPLANARITY, NOT JUST SAME NORMAL!! Dictionary <Vector3d, List <EdgeSpan> > find_coplanar_span_sets(DMesh3 mesh, EdgeLoop loop) { double dot_thresh = 0.999; Dictionary <Vector3d, List <EdgeSpan> > span_sets = new Dictionary <Vector3d, List <EdgeSpan> >(); int NV = loop.Vertices.Length; int NE = loop.Edges.Length; Vector3d[] edge_normals = new Vector3d[NE]; for (int k = 0; k < NE; ++k) { edge_normals[k] = mesh.GetTriNormal(mesh.GetEdgeT(loop.Edges[k]).a); } // find coplanar verts // [RMS] this is wrong, if normals vary smoothly enough we will mark non-coplanar spans as coplanar bool[] vert_coplanar = new bool[NV]; int nc = 0; for (int k = 0; k < NV; ++k) { int prev = (k == 0) ? NV - 1 : k - 1; if (edge_normals[k].Dot(ref edge_normals[prev]) > dot_thresh) { vert_coplanar[k] = true; nc++; } } if (nc < 2) { return(null); } int iStart = 0; while (vert_coplanar[iStart]) { iStart++; } int iPrev = iStart; int iCur = iStart + 1; while (iCur != iStart) { if (vert_coplanar[iCur] == false) { iPrev = iCur; iCur = (iCur + 1) % NV; continue; } List <int> edges = new List <int>() { loop.Edges[iPrev] }; int span_start_idx = iCur; while (vert_coplanar[iCur]) { edges.Add(loop.Edges[iCur]); iCur = (iCur + 1) % NV; } if (edges.Count > 1) { Vector3d span_n = edge_normals[span_start_idx]; EdgeSpan span = EdgeSpan.FromEdges(mesh, edges); span.CheckValidity(); foreach (var pair in span_sets) { if (pair.Key.Dot(ref span_n) > dot_thresh) { span_n = pair.Key; break; } } List <EdgeSpan> found; if (span_sets.TryGetValue(span_n, out found) == false) { span_sets[span_n] = new List <EdgeSpan>() { span } } ; else { found.Add(span); } } } return(span_sets); }
public bool Apply() { // do a simple fill SimpleHoleFiller simplefill = new SimpleHoleFiller(Mesh, FillLoop); int fill_gid = Mesh.AllocateTriangleGroup(); bool bOK = simplefill.Fill(fill_gid); if (bOK == false) { return(false); } if (FillLoop.Vertices.Length <= 3) { FillTriangles = simplefill.NewTriangles; FillVertices = new int[0]; return(true); } // extract the simple fill mesh as a submesh, via RegionOperator, so we can backsub later HashSet <int> intial_fill_tris = new HashSet <int>(simplefill.NewTriangles); regionop = new RegionOperator(Mesh, simplefill.NewTriangles, (submesh) => { submesh.ComputeTriMaps = true; }); fillmesh = regionop.Region.SubMesh; // for each boundary vertex, compute the exterior angle sum // we will use this to compute gaussian curvature later boundaryv = new HashSet <int>(MeshIterators.BoundaryEdgeVertices(fillmesh)); exterior_angle_sums = new Dictionary <int, double>(); if (IgnoreBoundaryTriangles == false) { foreach (int sub_vid in boundaryv) { double angle_sum = 0; int base_vid = regionop.Region.MapVertexToBaseMesh(sub_vid); foreach (int tid in regionop.BaseMesh.VtxTrianglesItr(base_vid)) { if (intial_fill_tris.Contains(tid) == false) { Index3i et = regionop.BaseMesh.GetTriangle(tid); int idx = IndexUtil.find_tri_index(base_vid, ref et); angle_sum += regionop.BaseMesh.GetTriInternalAngleR(tid, idx); } } exterior_angle_sums[sub_vid] = angle_sum; } } // try to guess a reasonable edge length that will give us enough geometry to work with in simplify pass double loop_mine, loop_maxe, loop_avge, fill_mine, fill_maxe, fill_avge; MeshQueries.EdgeLengthStatsFromEdges(Mesh, FillLoop.Edges, out loop_mine, out loop_maxe, out loop_avge); MeshQueries.EdgeLengthStats(fillmesh, out fill_mine, out fill_maxe, out fill_avge); double remesh_target_len = loop_avge; if (fill_maxe / remesh_target_len > 10) { remesh_target_len = fill_maxe / 10; } //double remesh_target_len = Math.Min(loop_avge, fill_avge / 4); // remesh up to target edge length, ideally gives us some triangles to work with RemesherPro remesh1 = new RemesherPro(fillmesh); remesh1.SmoothSpeedT = 1.0; MeshConstraintUtil.FixAllBoundaryEdges(remesh1); //remesh1.SetTargetEdgeLength(remesh_target_len / 2); // would this speed things up? on large regions? //remesh1.FastestRemesh(); remesh1.SetTargetEdgeLength(remesh_target_len); remesh1.FastestRemesh(); /* * first round: collapse to minimal mesh, while flipping to try to * get to ballpark minimal mesh. We stop these passes as soon as * we have done two rounds where we couldn't do another collapse * * This is the most unstable part of the algorithm because there * are strong ordering effects. maybe we could sort the edges somehow?? */ int zero_collapse_passes = 0; int collapse_passes = 0; while (collapse_passes++ < 20 && zero_collapse_passes < 2) { // collapse pass int NE = fillmesh.MaxEdgeID; int collapses = 0; for (int ei = 0; ei < NE; ++ei) { if (fillmesh.IsEdge(ei) == false || fillmesh.IsBoundaryEdge(ei)) { continue; } Index2i ev = fillmesh.GetEdgeV(ei); bool a_bdry = boundaryv.Contains(ev.a), b_bdry = boundaryv.Contains(ev.b); if (a_bdry && b_bdry) { continue; } int keepv = (a_bdry) ? ev.a : ev.b; int otherv = (keepv == ev.a) ? ev.b : ev.a; Vector3d newv = fillmesh.GetVertex(keepv); if (MeshUtil.CheckIfCollapseCreatesFlip(fillmesh, ei, newv)) { continue; } DMesh3.EdgeCollapseInfo info; MeshResult result = fillmesh.CollapseEdge(keepv, otherv, out info); if (result == MeshResult.Ok) { collapses++; } } if (collapses == 0) { zero_collapse_passes++; } else { zero_collapse_passes = 0; } // flip pass. we flip in these cases: // 1) if angle between current triangles is too small (slightly more than 90 degrees, currently) // 2) if angle between flipped triangles is smaller than between current triangles // 3) if flipped edge length is shorter *and* such a flip won't flip the normal NE = fillmesh.MaxEdgeID; Vector3d n1, n2, on1, on2; for (int ei = 0; ei < NE; ++ei) { if (fillmesh.IsEdge(ei) == false || fillmesh.IsBoundaryEdge(ei)) { continue; } bool do_flip = false; Index2i ev = fillmesh.GetEdgeV(ei); MeshUtil.GetEdgeFlipNormals(fillmesh, ei, out n1, out n2, out on1, out on2); double dot_cur = n1.Dot(n2); double dot_flip = on1.Dot(on2); if (n1.Dot(n2) < 0.1 || dot_flip > dot_cur + MathUtil.Epsilonf) { do_flip = true; } if (do_flip == false) { Index2i otherv = fillmesh.GetEdgeOpposingV(ei); double len_e = fillmesh.GetVertex(ev.a).Distance(fillmesh.GetVertex(ev.b)); double len_flip = fillmesh.GetVertex(otherv.a).Distance(fillmesh.GetVertex(otherv.b)); if (len_flip < len_e) { if (MeshUtil.CheckIfEdgeFlipCreatesFlip(fillmesh, ei) == false) { do_flip = true; } } } if (do_flip) { DMesh3.EdgeFlipInfo info; MeshResult result = fillmesh.FlipEdge(ei, out info); } } } // Sometimes, for some reason, we have a remaining interior vertex (have only ever seen one?) // Try to force removal of such vertices, even if it makes ugly mesh remove_remaining_interior_verts(); // enable/disable passes. bool DO_FLATTER_PASS = true; bool DO_CURVATURE_PASS = OptimizeDevelopability && true; bool DO_AREA_PASS = OptimizeDevelopability && OptimizeTriangles && true; /* * In this pass we repeat the flipping iterations from the previous pass. * * Note that because of the always-flip-if-dot-is-small case (commented), * this pass will frequently not converge, as some number of edges will * be able to flip back and forth (because neither has large enough dot). * This is not ideal, but also, if we remove this behavior, then we * generally get worse fills. This case basically introduces a sort of * randomization factor that lets us escape local minima... * */ HashSet <int> remaining_edges = new HashSet <int>(fillmesh.EdgeIndices()); HashSet <int> updated_edges = new HashSet <int>(); int flatter_passes = 0; int zero_flips_passes = 0; while (flatter_passes++ < 40 && zero_flips_passes < 2 && remaining_edges.Count() > 0 && DO_FLATTER_PASS) { zero_flips_passes++; foreach (int ei in remaining_edges) { if (fillmesh.IsBoundaryEdge(ei)) { continue; } bool do_flip = false; Index2i ev = fillmesh.GetEdgeV(ei); Vector3d n1, n2, on1, on2; MeshUtil.GetEdgeFlipNormals(fillmesh, ei, out n1, out n2, out on1, out on2); double dot_cur = n1.Dot(n2); double dot_flip = on1.Dot(on2); if (flatter_passes < 20 && dot_cur < 0.1) // this check causes oscillatory behavior { do_flip = true; } if (dot_flip > dot_cur + MathUtil.Epsilonf) { do_flip = true; } if (do_flip) { DMesh3.EdgeFlipInfo info; MeshResult result = fillmesh.FlipEdge(ei, out info); if (result == MeshResult.Ok) { zero_flips_passes = 0; add_all_edges(ei, updated_edges); } } } var tmp = remaining_edges; remaining_edges = updated_edges; updated_edges = tmp; updated_edges.Clear(); } int curvature_passes = 0; if (DO_CURVATURE_PASS) { curvatures = new double[fillmesh.MaxVertexID]; foreach (int vid in fillmesh.VertexIndices()) { update_curvature(vid); } remaining_edges = new HashSet <int>(fillmesh.EdgeIndices()); updated_edges = new HashSet <int>(); /* * In this pass we try to minimize gaussian curvature at all the vertices. * This will recover sharp edges, etc, and do lots of good stuff. * However, this pass will not make much progress if we are not already * relatively close to a minimal mesh, so it really relies on the previous * passes getting us in the ballpark. */ while (curvature_passes++ < 40 && remaining_edges.Count() > 0 && DO_CURVATURE_PASS) { foreach (int ei in remaining_edges) { if (fillmesh.IsBoundaryEdge(ei)) { continue; } Index2i ev = fillmesh.GetEdgeV(ei); Index2i ov = fillmesh.GetEdgeOpposingV(ei); int find_other = fillmesh.FindEdge(ov.a, ov.b); if (find_other != DMesh3.InvalidID) { continue; } double total_curv_cur = curvature_metric_cached(ev.a, ev.b, ov.a, ov.b); if (total_curv_cur < MathUtil.ZeroTolerancef) { continue; } DMesh3.EdgeFlipInfo info; MeshResult result = fillmesh.FlipEdge(ei, out info); if (result != MeshResult.Ok) { continue; } double total_curv_flip = curvature_metric_eval(ev.a, ev.b, ov.a, ov.b); bool keep_flip = total_curv_flip < total_curv_cur - MathUtil.ZeroTolerancef; if (keep_flip == false) { result = fillmesh.FlipEdge(ei, out info); } else { update_curvature(ev.a); update_curvature(ev.b); update_curvature(ov.a); update_curvature(ov.b); add_all_edges(ei, updated_edges); } } var tmp = remaining_edges; remaining_edges = updated_edges; updated_edges = tmp; updated_edges.Clear(); } } //System.Console.WriteLine("collapse {0} flatter {1} curvature {2}", collapse_passes, flatter_passes, curvature_passes); /* * In this final pass, we try to improve triangle quality. We flip if * the flipped triangles have better total aspect ratio, and the * curvature doesn't change **too** much. The .DevelopabilityTolerance * parameter determines what is "too much" curvature change. */ if (DO_AREA_PASS) { remaining_edges = new HashSet <int>(fillmesh.EdgeIndices()); updated_edges = new HashSet <int>(); int area_passes = 0; while (remaining_edges.Count() > 0 && area_passes < 20) { area_passes++; foreach (int ei in remaining_edges) { if (fillmesh.IsBoundaryEdge(ei)) { continue; } Index2i ev = fillmesh.GetEdgeV(ei); Index2i ov = fillmesh.GetEdgeOpposingV(ei); int find_other = fillmesh.FindEdge(ov.a, ov.b); if (find_other != DMesh3.InvalidID) { continue; } double total_curv_cur = curvature_metric_cached(ev.a, ev.b, ov.a, ov.b); double a = aspect_metric(ei); if (a > 1) { continue; } DMesh3.EdgeFlipInfo info; MeshResult result = fillmesh.FlipEdge(ei, out info); if (result != MeshResult.Ok) { continue; } double total_curv_flip = curvature_metric_eval(ev.a, ev.b, ov.a, ov.b); bool keep_flip = Math.Abs(total_curv_cur - total_curv_flip) < DevelopabilityTolerance; if (keep_flip == false) { result = fillmesh.FlipEdge(ei, out info); } else { update_curvature(ev.a); update_curvature(ev.b); update_curvature(ov.a); update_curvature(ov.b); add_all_edges(ei, updated_edges); } } var tmp = remaining_edges; remaining_edges = updated_edges; updated_edges = tmp; updated_edges.Clear(); } } regionop.BackPropropagate(); FillTriangles = regionop.CurrentBaseTriangles; FillVertices = regionop.CurrentBaseInteriorVertices().ToArray(); return(true); }
// Use this for initialization public override void Awake() { // if we need to auto-configure Rift vs Vive vs (?) VR, we need // to do this before any other F3 setup, because MainCamera will change // and we are caching that in a lot of places... if (AutoConfigVR) { VRCameraRig = gs.VRPlatform.AutoConfigureVR(); } // restore any settings SceneGraphConfig.RestorePreferences(); // set up some defaults // this will move the ground plane down, but the bunnies will be floating... //SceneGraphConfig.InitialSceneTranslate = -4.0f * Vector3f.AxisY; SceneGraphConfig.DefaultSceneCurveVisualDegrees = 0.5f; SceneGraphConfig.DefaultPivotVisualDegrees = 2.3f; SceneGraphConfig.DefaultAxisGizmoVisualDegrees = 25.0f; PolyCurveSO.DefaultHitWidthMultiplier = 2.5f; SceneOptions options = new SceneOptions(); options.UseSystemMouseCursor = false; options.Use2DCockpit = false; options.EnableTransforms = true; options.EnableCockpit = true; options.EnableDefaultLighting = false; options.CockpitInitializer = new SetupOrthoVRCockpit(); options.MouseCameraControls = new MayaCameraHotkeys() { MousePanSpeed = 5.0f, MouseZoomSpeed = 5.0f }; options.SpatialCameraRig = VRCameraRig; // very verbose options.LogLevel = 2; // hacks for stuff #if F3_ENABLE_TEXT_MESH_PRO SceneGraphConfig.TextLabelZOffset = -0.01f; #else SceneGraphConfig.TextLabelZOffset = -0.3f; #endif context = new FContext(); OG.Context = context; OrthogenUI.ActiveContext = context; context.Start(options); // Set up standard scene lighting if enabled if (options.EnableDefaultLighting) { GameObject lighting = GameObject.Find("SceneLighting"); if (lighting == null) { lighting = new GameObject("SceneLighting"); } SceneLightingSetup setup = lighting.AddComponent <SceneLightingSetup>(); setup.Context = context; setup.ShadowLightCount = 0; setup.AdjustShadowDistance = false; setup.LightDistance = 1000.0f; // related to total scene scale... } // override sun so that it doesn't stick to one of the scene lights RenderSettings.sun = GameObject.Find("SunLight").GetComponent <Light>(); //GameObjectFactory.CurveRendererSource = new VectrosityCurveRendererFactory(); // set up ground plane geometry (optional) GameObject boundsObject = GameObject.Find("Bounds"); if (boundsObject != null) { context.Scene.AddWorldBoundsObject(boundsObject); } /* * ORTHOGEN-SPECIFIC SETUP STARTS HERE */ // set up scene and tools like Orthogen wants them OGActions.InitializeVRUsageContext(); OrthogenMaterials.InitializeMaterials(); OrthogenMaterials.ScanMaterial = new UnitySOMaterial(MaterialUtil.SafeLoadMaterial("scan_material")); //OrthogenMaterials.RectifiedLegMaterial = OrthogenMaterials.ScanMaterial; OGActions.InitializeF3Scene(context); OGActions.InitializeF3Tools(context); OGActions.InitializeF3VRTools(context); OGActions.PostConfigureTools_Demo(); OGActions.ConfigurePlatformInput_VR(); /* * optional things specific to demo app */ // ground plane stays below socket as it is updated DemoActions.AddRepositionGroundPlaneOnSocketEdit(); /* * import sample mesh */ bool do_scan_demo = true; // load sample mesh string assetPath = Application.dataPath; string samplesPath = Path.Combine(assetPath, "..", "sample_files"); //string sampleFile = Path.Combine(samplesPath, "sample_socket_off.obj"); string sampleFile = Path.Combine(samplesPath, "sample_socket_1.obj"); if (do_scan_demo) { sampleFile = Path.Combine(samplesPath, "scan_1_remesh.obj"); } if (File.Exists(sampleFile) == false) { sampleFile = Path.Combine(samplesPath, "sample_socket_1.obj"); } DMesh3 mesh = StandardMeshReader.ReadMesh(sampleFile); // read sample file from Resources instead //MemoryStream sampleFileStream = FResources.LoadBinary("sample_socket_1"); //DMesh3 mesh = StandardMeshReader.ReadMesh(sampleFileStream, "obj"); if (mesh.HasVertexColors == false) { mesh.EnableVertexColors(Colorf.Silver); } // transform to our coordinate system double scale = Units.MetersTo(Units.Linear.Millimeters); // this mesh is in meters, so scale to mm MeshTransforms.FlipLeftRightCoordSystems(mesh); // convert to unity coordinate system MeshTransforms.Scale(mesh, scale); if (do_scan_demo) { OGActions.SetSizeMode(OGActions.SizeModes.RealSize); } else { OGActions.SetSizeMode(OGActions.SizeModes.DemoSize); } // initialize the datamodel OGActions.BeginSocketDesignFromScan(Context, mesh); // set up my UI tests/etc configure_unity_ui(); // [RMS] do this next frame because SteamVR needs a chance to set up and position the cockpit OGActions.RecenterVRView(true); add_vr_head(context); // dgraph tests //DGTest.test(Debug.Log); }
Vector3d get_tri_normal(DMesh3 mesh, Index3i tri) { return(MathUtil.Normal(mesh.GetVertex(tri.a), mesh.GetVertex(tri.b), mesh.GetVertex(tri.c))); }
/// <summary> /// Converts g3.DMesh3 to UnityEngine.Mesh. /// The DMesh3 must be compact. If neccesary - run Compactify first. /// </summary> /// <param name="mesh">Dmesh3</param> /// <param name="project"> Should the mesh be projected into virgis projection DEFAULT true</param> /// <returns>UnityEngine.Mesh</returns> public static Mesh ToMesh(this DMesh3 mesh, Boolean project = true) { Mesh unityMesh = new Mesh(); unityMesh.MarkDynamic(); unityMesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32; if (project && !mesh.Transform(AppState.instance.mapProj)) { throw new Exception("Mesh Projection Failed"); } Vector3[] vertices = new Vector3[mesh.VertexCount]; Color[] colors = new Color[mesh.VertexCount]; Vector2[] uvs = new Vector2[mesh.VertexCount]; Vector3[] normals = new Vector3[mesh.VertexCount]; NewVertexInfo data; for (int i = 0; i < mesh.VertexCount; i++) { if (mesh.IsVertex(i)) { data = mesh.GetVertexAll(i); vertices[i] = (Vector3)data.v; if (data.bHaveC) { colors[i] = (Color)data.c; } if (data.bHaveUV) { uvs[i] = (Vector2)data.uv; } if (data.bHaveN) { normals[i] = (Vector3)data.n; } } } unityMesh.vertices = vertices; if (mesh.HasVertexColors) { unityMesh.SetColors(colors); } if (mesh.HasVertexUVs) { unityMesh.SetUVs(0, uvs); } if (mesh.HasVertexNormals) { unityMesh.SetNormals(normals); } int[] triangles = new int[mesh.TriangleCount * 3]; int j = 0; foreach (Index3i tri in mesh.Triangles()) { triangles[j * 3] = tri.a; triangles[j * 3 + 1] = tri.b; triangles[j * 3 + 2] = tri.c; j++; } unityMesh.triangles = triangles; return(unityMesh); }
double get_tri_area(DMesh3 mesh, ref Index3i tri) { return(MathUtil.Area(mesh.GetVertex(tri.a), mesh.GetVertex(tri.b), mesh.GetVertex(tri.c))); }
void process() { DMesh3 useSourceMesh = SourceMesh; // try to do simple mesh repairs if (useSourceMesh.CachedIsClosed == false) { useSourceMesh = new DMesh3(SourceMesh); // [TODO] should remove duplicate triangles here? RemoveDuplicateTriangles dupes = new RemoveDuplicateTriangles(useSourceMesh); dupes.Apply(); // close cracks MergeCoincidentEdges merge = new MergeCoincidentEdges(useSourceMesh); //merge.OnlyUniquePairs = true; merge.Apply(); } //Util.WriteDebugMesh(useSourceMesh, "c:\\scratch\\__FIRST_MERGE.obj"); DMesh3[] components = MeshConnectedComponents.Separate(useSourceMesh); List <DMesh3> solidComps = new List <DMesh3>(); foreach (DMesh3 mesh in components) { // [TODO] check if this is a mesh w/ cracks, in which case we // can do other processing? bool closed = mesh.CachedIsClosed; if (closed == false) { OpenMeshes.Add(mesh); continue; } solidComps.Add(mesh); } if (solidComps.Count == 0) { return; } if (solidComps.Count == 1) { ClosedSolids = new List <DMesh3>() { solidComps[0] }; } if (HasNoVoids) { // each solid is a separate solid ClosedSolids = process_solids_novoid(solidComps); } else { ClosedSolids = process_solids(solidComps); } }
double get_tri_aspect(DMesh3 mesh, ref Index3i tri) { return(MathUtil.AspectRatio(mesh.GetVertex(tri.a), mesh.GetVertex(tri.b), mesh.GetVertex(tri.c))); }
public static void TagAsFailureMesh(DMesh3 mesh) { mesh.AttachMetadata("DMESHOP_FAILURE_MESH", new object()); }
/// <summary> /// Find regions of the input meshes that are horizontal, returns binned by Z-value. /// Currently only finds upward-facing regions. /// </summary> protected Dictionary <double, List <PlanarRegion> > FindPlanarZRegions(double minDimension, double dotNormalTol = 0.999) { Dictionary <double, List <PlanarRegion> > Regions = new Dictionary <double, List <PlanarRegion> >(); SpinLock region_lock = new SpinLock(); gParallel.ForEach(Meshes, (sliceMesh) => { if (sliceMesh.options.IsCavity == false) { return; } DMesh3 mesh = sliceMesh.mesh; HashSet <int> planar_tris = new HashSet <int>(); foreach (int tid in mesh.TriangleIndices()) { Vector3d n = mesh.GetTriNormal(tid); double dot = n.Dot(Vector3d.AxisZ); if (dot > dotNormalTol) { planar_tris.Add(tid); } } MeshConnectedComponents regions = new MeshConnectedComponents(mesh); regions.FilterF = planar_tris.Contains; regions.FindConnectedT(); foreach (var c in regions) { AxisAlignedBox3d bounds = MeshMeasurements.BoundsT(mesh, c.Indices); if (bounds.Width > minDimension && bounds.Height > minDimension) { double z = Math.Round(bounds.Center.z, PrecisionDigits); PlanarRegion planar = new PlanarRegion() { Mesh = mesh, Z = z, Triangles = c.Indices }; bool taken = false; region_lock.Enter(ref taken); List <PlanarRegion> zregions; if (Regions.TryGetValue(z, out zregions)) { zregions.Add(planar); } else { zregions = new List <PlanarRegion>() { planar }; Regions[z] = zregions; } region_lock.Exit(); } } }); return(Regions); }