void OnEnable() { mInstance = (MeshEditor)target; }
public virtual bool Apply() { DMesh3 testAgainstMesh = Mesh; if (InsideMode == CalculationMode.RayParity) { MeshBoundaryLoops loops = new MeshBoundaryLoops(testAgainstMesh); if (loops.Count > 0) { testAgainstMesh = new DMesh3(Mesh); foreach (var loop in loops) { if (Cancelled()) { return(false); } SimpleHoleFiller filler = new SimpleHoleFiller(testAgainstMesh, loop); filler.Fill(); } } } DMeshAABBTreePro spatial = (Spatial != null && testAgainstMesh == Mesh) ? Spatial : new DMeshAABBTreePro(testAgainstMesh, true); if (InsideMode == CalculationMode.AnalyticWindingNumber) { spatial.WindingNumber(Vector3d.Zero); } else if (InsideMode == CalculationMode.FastWindingNumber) { spatial.FastWindingNumber(Vector3d.Zero); } if (Cancelled()) { return(false); } // ray directions List <Vector3d> ray_dirs = null; int NR = 0; if (InsideMode == CalculationMode.SimpleOcclusionTest) { ray_dirs = new List <Vector3d>(); ray_dirs.Add(Vector3d.AxisX); ray_dirs.Add(-Vector3d.AxisX); ray_dirs.Add(Vector3d.AxisY); ray_dirs.Add(-Vector3d.AxisY); ray_dirs.Add(Vector3d.AxisZ); ray_dirs.Add(-Vector3d.AxisZ); NR = ray_dirs.Count; } Func <Vector3d, bool> isOccludedF = (pt) => { if (InsideMode == CalculationMode.RayParity) { return(spatial.IsInside(pt)); } else if (InsideMode == CalculationMode.AnalyticWindingNumber) { return(spatial.WindingNumber(pt) > WindingIsoValue); } else if (InsideMode == CalculationMode.FastWindingNumber) { return(spatial.FastWindingNumber(pt) > WindingIsoValue); } else { for (int k = 0; k < NR; ++k) { int hit_tid = spatial.FindNearestHitTriangle(new Ray3d(pt, ray_dirs[k])); if (hit_tid == DMesh3.InvalidID) { return(false); } } return(true); } }; bool cancel = false; BitArray vertices = null; if (PerVertex) { vertices = new BitArray(Mesh.MaxVertexID); MeshNormals normals = null; if (Mesh.HasVertexNormals == false) { normals = new MeshNormals(Mesh); normals.Compute(); } gParallel.ForEach(Mesh.VertexIndices(), (vid) => { if (cancel) { return; } if (vid % 10 == 0) { cancel = Cancelled(); } Vector3d c = Mesh.GetVertex(vid); Vector3d n = (normals == null) ? Mesh.GetVertexNormal(vid) : normals[vid]; c += n * NormalOffset; vertices[vid] = isOccludedF(c); }); } if (Cancelled()) { return(false); } RemovedT = new List <int>(); SpinLock removeLock = new SpinLock(); gParallel.ForEach(Mesh.TriangleIndices(), (tid) => { if (cancel) { return; } if (tid % 10 == 0) { cancel = Cancelled(); } bool inside = false; if (PerVertex) { Index3i tri = Mesh.GetTriangle(tid); inside = vertices[tri.a] || vertices[tri.b] || vertices[tri.c]; } else { Vector3d c = Mesh.GetTriCentroid(tid); Vector3d n = Mesh.GetTriNormal(tid); c += n * NormalOffset; inside = isOccludedF(c); } if (inside) { bool taken = false; removeLock.Enter(ref taken); RemovedT.Add(tid); removeLock.Exit(); } }); if (Cancelled()) { return(false); } if (RemovedT.Count > 0) { MeshEditor editor = new MeshEditor(Mesh); bool bOK = editor.RemoveTriangles(RemovedT, true); RemoveFailed = (bOK == false); } return(true); }
protected virtual DMesh3 compute_inset(DMesh3 meshIn) { double unsigned_offset = Math.Abs(distance); DMeshAABBTree3 use_spatial = new DMeshAABBTree3(meshIn, true); if (is_invalidated()) { return(null); } CachingMeshSDF sdf = new CachingMeshSDF(meshIn, grid_cell_size, use_spatial); sdf.MaxOffsetDistance = (float)unsigned_offset; sdf.CancelF = is_invalidated; sdf.Initialize(); if (is_invalidated()) { return(null); } var sdf_iso = new CachingMeshSDFImplicit(sdf); // currently MCPro-Continuation does not work w/ non-zero // isovalues, so we have to shift our target offset externally ImplicitOffset3d iso = new ImplicitOffset3d() { A = sdf_iso, Offset = -distance }; MarchingCubesPro c = new MarchingCubesPro(); c.Implicit = iso; c.Bounds = cached_sdf_bounds; c.CubeSize = mesh_cell_size; c.Bounds.Expand(distance + 3 * c.CubeSize); c.CancelF = is_invalidated; c.GenerateContinuation(offset_seeds(meshIn, -distance)); if (is_invalidated()) { return(null); } Reducer r = new Reducer(c.Mesh); r.FastCollapsePass(c.CubeSize * 0.5, 3, true); if (is_invalidated()) { return(null); } if (min_component_volume > 0) { MeshEditor.RemoveSmallComponents(c.Mesh, min_component_volume, min_component_volume); } if (is_invalidated()) { return(null); } return(c.Mesh); }
public virtual void Update() { base.begin_update(); int start_timestamp = this.CurrentInputTimestamp; if (MeshSource == null) { throw new Exception("CombineMeshesOp: must set valid MeshSource to compute!"); } ResultMesh = null; try { DMesh3 meshIn = new DMesh3(MeshSource.GetDMeshUnsafe()); if (orient_nested_shells) { MeshConnectedComponents comp = new MeshConnectedComponents(meshIn); comp.FindConnectedT(); DSubmesh3Set subMeshes = new DSubmesh3Set(meshIn, comp); List <DMesh3> curMeshes = new List <DMesh3>(); foreach (var submesh in subMeshes) { curMeshes.Add(submesh.SubMesh); } MeshSpatialSort sort = new MeshSpatialSort(); foreach (var mesh in curMeshes) { sort.AddMesh(mesh, mesh); } sort.Sort(); ResultMesh = new DMesh3(); MeshEditor editor = new MeshEditor(ResultMesh); foreach (var solid in sort.Solids) { DMesh3 outer = solid.Outer.Mesh; if (!is_outward_oriented(outer)) { outer.ReverseOrientation(); } editor.AppendMesh(outer, ResultMesh.AllocateTriangleGroup()); foreach (var hole in solid.Cavities) { if (hole.Mesh.CachedIsClosed && is_outward_oriented(hole.Mesh) == true) { hole.Mesh.ReverseOrientation(); } editor.AppendMesh(hole.Mesh, ResultMesh.AllocateTriangleGroup()); } } } else { ResultMesh = meshIn; } base.complete_update(); } catch (Exception e) { PostOnOperatorException(e); ResultMesh = base.make_failure_output(MeshSource.GetDMeshUnsafe()); base.complete_update(); } }
bool remove_loners() { bool bOK = MeshEditor.RemoveIsolatedTriangles(Mesh); return(true); }
// extracts all MeshFilter objects from input GameObject and appends them, then passes to // function MakeSOFunc (if null, creates basic MeshSO). Then optionally adds to Scene, // preserving existing 3D position if desired (default true) public static TransformableSO ImportExistingUnityGO(GameObject go, FScene scene, bool bAddToScene = true, bool bKeepWorldPosition = true, bool bRecenterFrame = true, Func <DMesh3, SOMaterial, TransformableSO> MakeSOFunc = null) { List <MeshFilter> filters = new List <MeshFilter>(); List <GameObject> children = new List <GameObject>() { go }; UnityUtil.CollectAllChildren(go, children); foreach (var cgo in children) { if (cgo.GetComponent <MeshFilter>() != null) { filters.Add(cgo.GetComponent <MeshFilter>()); } } if (filters.Count == 0) { throw new Exception("SceneUtil.ImportExistingUnityGO: no meshes!!"); } DMesh3 CombineMesh = new DMesh3(MeshComponents.VertexNormals | MeshComponents.VertexColors); MeshEditor editor = new MeshEditor(CombineMesh); int gid = 0; foreach (MeshFilter mesh in filters) { fMesh uMesh = new fMesh(mesh.sharedMesh); using (var imesh = uMesh.CreateCachedIMesh()) { editor.AppendMesh(imesh, ++gid); } } Vector3f scale = go.GetLocalScale(); AxisAlignedBox3d bounds = CombineMesh.CachedBounds; // bounds.Center is wrt local frame of input go // ie offset from origin in local coordinates // if we want to move frame to center of mesh, we have to re-center it at origin // in local coordinates if (bRecenterFrame) { MeshTransforms.Translate(CombineMesh, -bounds.Center.x, -bounds.Center.y, -bounds.Center.z); } TransformableSO newSO = (MakeSOFunc != null) ? MakeSOFunc(CombineMesh, scene.DefaultMeshSOMaterial) : new DMeshSO().Create(CombineMesh, scene.DefaultMeshSOMaterial); if (bAddToScene) { scene.AddSceneObject(newSO, false); } if (bKeepWorldPosition) { // compute world rotation/location. If we re-centered the mesh, we need // to offset by the transform we applied above in local coordinates // (hence we have to rotate & scale) if (go.transform.parent != null) { throw new Exception("UnitySceneUtil.ImportExistingUnityGO: Not handling case where GO has a parent transform"); } Frame3f goFrameW = UnityUtil.GetGameObjectFrame(go, CoordSpace.WorldCoords); Vector3f originW = goFrameW.Origin; if (bRecenterFrame) { originW += goFrameW.Rotation * (scale * (Vector3f)bounds.Center); // offset initial frame to be at center of mesh } // convert world frame and offset to scene coordinates Frame3f goFrameS = scene.ToSceneFrame(goFrameW); Vector3f boundsCenterS = scene.ToSceneP(originW); // translate new object to position in scene Frame3f curF = newSO.GetLocalFrame(CoordSpace.SceneCoords); curF.Origin += boundsCenterS; newSO.SetLocalFrame(curF, CoordSpace.SceneCoords); // apply rotation (around current origin) curF = newSO.GetLocalFrame(CoordSpace.SceneCoords); curF.RotateAround(curF.Origin, goFrameS.Rotation); newSO.SetLocalFrame(curF, CoordSpace.SceneCoords); // apply local scale newSO.SetLocalScale(scale); } return(newSO); }
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 static void test_autorepair_thingi10k() { //const string THINGIROOT = "E:\\Thingi10K\\"; string WRITEPATH = "E:\\Thingi10K\\repair_fails\\"; //string[] files = File.ReadAllLines("E:\\Thingi10K\\current\\thingi10k_open.txt"); string[] files = File.ReadAllLines("C:\\git\\gsGeometryTests\\test_output\\thingi10k_autorepair_failures.txt"); //string[] files = new string[] { // "E:\\Thingi10K\\raw_meshes\\37011.stl" //}; SafeListBuilder <string> failures = new SafeListBuilder <string>(); int count = 0; int MAX_NUM_FILES = 10000; gParallel.ForEach(files, (filename) => { if (count > MAX_NUM_FILES) { return; } int i = count; Interlocked.Increment(ref count); if (i % 10 == 0) { System.Console.WriteLine("{0} / {1}", i, files.Length); } long start_ticks = DateTime.Now.Ticks; DMesh3Builder builder = new DMesh3Builder(); StandardMeshReader reader = new StandardMeshReader() { MeshBuilder = builder }; IOReadResult result = reader.Read(filename, ReadOptions.Defaults); if (result.code != IOCode.Ok) { System.Console.WriteLine("{0} FAILED TO READ!", filename); failures.SafeAdd(filename); return; } DMesh3 mesh = builder.Meshes[0]; for (int k = 1; k < builder.Meshes.Count; ++k) { MeshEditor.Append(mesh, builder.Meshes[k]); } DMesh3 before = new DMesh3(mesh); try { MeshAutoRepair repair = new MeshAutoRepair(mesh); repair.Apply(); } catch (Exception e) { System.Console.WriteLine("EXCEPTION {0} : {1}", filename, e.Message); failures.SafeAdd(filename); return; } if (mesh.IsClosed() == false) { failures.SafeAdd(filename); Util.WriteDebugMesh(before, WRITEPATH + Path.GetFileNameWithoutExtension(filename) + ".obj"); Util.WriteDebugMesh(mesh, WRITEPATH + Path.GetFileNameWithoutExtension(filename) + ".failed.obj"); return; } else { if (mesh.CheckValidity(false, FailMode.ReturnOnly) == false) { System.Console.WriteLine("INVALID {0}", filename); failures.SafeAdd(filename); Util.WriteDebugMesh(before, WRITEPATH + Path.GetFileNameWithoutExtension(filename) + ".obj"); Util.WriteDebugMesh(mesh, WRITEPATH + Path.GetFileNameWithoutExtension(filename) + ".invalid.obj"); return; } } }); //foreach (string failure in failures.Result) { // System.Console.WriteLine("FAIL: {0}", failure); //} System.Console.WriteLine("repaired {0} of {1}", files.Length - failures.Result.Count, files.Length); TestUtil.WriteTestOutputStrings(make_strings(failures), "thingi10k_autorepair_failures_new.txt"); }
public Mesh Repair(Mesh sourceMesh, CancellationToken cancellationToken) { var inMesh = sourceMesh; try { if (WeldVertices) { inMesh = sourceMesh.Copy(cancellationToken); inMesh.CleanAndMerge(); if (!FaceOrientation && RemoveMode == RemoveModes.None && !WeldEdges && !FillHoles) { return(inMesh); } } var mesh = inMesh.ToDMesh3(); int repeatCount = 0; int erosionIterations = 5; double repairTolerance = MathUtil.ZeroTolerancef; double minEdgeLengthTol = 0.0001; repeat_all: if (FaceOrientation) { // make sure orientation of connected components is consistent // TODO: what about mobius strip problems? RepairOrientation(mesh, cancellationToken, true); } if (RemoveMode != RemoveModes.None) { // Remove parts of the mesh we don't want before we bother with anything else // TODO: maybe we need to repair orientation first? if we want to use MWN (MeshWindingNumber)... RemoveInside(mesh); cancellationToken.ThrowIfCancellationRequested(); } if (WeldEdges || FillHoles) { // Do safe close-cracks to handle easy cases RepairCracks(mesh, true, repairTolerance); if (mesh.IsClosed()) { goto all_done; } cancellationToken.ThrowIfCancellationRequested(); // Collapse tiny edges and then try easy cases again, and // then allow for handling of ambiguous cases CollapseAllDegenerateEdges(mesh, cancellationToken, repairTolerance * 0.5, true); cancellationToken.ThrowIfCancellationRequested(); RepairCracks(mesh, true, 2 * repairTolerance); cancellationToken.ThrowIfCancellationRequested(); RepairCracks(mesh, false, 2 * repairTolerance); cancellationToken.ThrowIfCancellationRequested(); if (mesh.IsClosed()) { goto all_done; } // Possibly we have joined regions with different orientation (is it?), fix that // TODO: mobius strips again RepairOrientation(mesh, cancellationToken, true); cancellationToken.ThrowIfCancellationRequested(); // get rid of any remaining single-triangles before we start filling holes MeshEditor.RemoveIsolatedTriangles(mesh); } if (FillHoles) { // Ok, fill simple holes. int nRemainingBowties = 0; FillTrivialHoles(mesh, cancellationToken, out int nHoles, out bool bSawSpans); cancellationToken.ThrowIfCancellationRequested(); if (mesh.IsClosed()) { goto all_done; } // Now fill harder holes. If we saw spans, that means boundary loops could // not be resolved in some cases, do we disconnect bowties and try again. FillAnyHoles(mesh, cancellationToken, out nHoles, out bSawSpans); cancellationToken.ThrowIfCancellationRequested(); if (bSawSpans) { DisconnectBowties(mesh, out nRemainingBowties); FillAnyHoles(mesh, cancellationToken, out nHoles, out bSawSpans); } cancellationToken.ThrowIfCancellationRequested(); if (mesh.IsClosed()) { goto all_done; } // We may have a closed mesh now but it might still have bowties (eg // tetrahedra sharing vtx case). So disconnect those. DisconnectBowties(mesh, out nRemainingBowties); cancellationToken.ThrowIfCancellationRequested(); // If the mesh is not closed, we will do one more round to try again. if (repeatCount == 0 && mesh.IsClosed() == false) { repeatCount++; goto repeat_all; } // Ok, we didn't get anywhere on our first repeat. If we are still not // closed, we will try deleting boundary triangles and repeating. // Repeat this N times. if (repeatCount <= erosionIterations && mesh.IsClosed() == false) { repeatCount++; var bdry_faces = new MeshFaceSelection(mesh); foreach (int eid in MeshIterators.BoundaryEdges(mesh)) { bdry_faces.SelectEdgeTris(eid); } MeshEditor.RemoveTriangles(mesh, bdry_faces, true); goto repeat_all; } } all_done: // and do a final clean up of the model if (FillHoles) { // Remove tiny edges if (minEdgeLengthTol > 0) { CollapseAllDegenerateEdges(mesh, cancellationToken, minEdgeLengthTol, false); } cancellationToken.ThrowIfCancellationRequested(); // finally do global orientation RepairOrientation(mesh, cancellationToken, true); cancellationToken.ThrowIfCancellationRequested(); } return(mesh.ToMesh()); } catch (OperationCanceledException) { return(inMesh); } }
public static void test_write_solids() { //string FORMAT = ".obj"; string FORMAT = ".g3mesh"; string WRITEPATH = "E:\\Thingi10K\\closed\\"; string[] files = File.ReadAllLines("E:\\Thingi10K\\current\\thingi10k_closed.txt"); SafeListBuilder <string> failures = new SafeListBuilder <string>(); if (!Directory.Exists(WRITEPATH)) { Directory.CreateDirectory(WRITEPATH); } int k = 0; gParallel.ForEach(files, (filename) => { int i = k; Interlocked.Increment(ref k); if (i % 500 == 0) { System.Console.WriteLine("{0} : {1}", i, files.Length); } long start_ticks = DateTime.Now.Ticks; DMesh3Builder builder = new DMesh3Builder(); StandardMeshReader reader = new StandardMeshReader() { MeshBuilder = builder }; IOReadResult result = reader.Read(filename, ReadOptions.Defaults); if (result.code != IOCode.Ok) { System.Console.WriteLine("{0} FAILED!", filename); failures.SafeAdd(filename); return; } DMesh3 combineMesh = new DMesh3(); if (builder.Meshes.Count == 1) { combineMesh = builder.Meshes[0]; } else { foreach (DMesh3 mesh in builder.Meshes) { MeshEditor.Append(combineMesh, mesh); } } if (combineMesh.IsClosed() == false) { MergeCoincidentEdges closeCracks = new MergeCoincidentEdges(combineMesh); closeCracks.Apply(); } if (combineMesh.IsClosed() == false) { System.Console.WriteLine("NOT CLOSED: {0}", filename); return; } string outPath = Path.Combine(WRITEPATH, Path.GetFileNameWithoutExtension(filename) + FORMAT); StandardMeshWriter.WriteMesh(outPath, combineMesh, WriteOptions.Defaults); }); }
public static void test_tube_generator() { Polygon2d circle_path = Polygon2d.MakeCircle(50, 64); PolyLine2d arc_path = new PolyLine2d(circle_path.Vertices.Take(circle_path.VertexCount / 2)); Polygon2d irreg_path = new Polygon2d(); for (int k = 0; k < circle_path.VertexCount; ++k) { irreg_path.AppendVertex(circle_path[k]); k += k / 2; } PolyLine2d irreg_arc_path = new PolyLine2d(irreg_path.Vertices.Take(circle_path.VertexCount - 1)); Polygon2d square_profile = Polygon2d.MakeCircle(7, 32); square_profile.Translate(4 * Vector2d.One); //square_profile[0] = 20 * square_profile[0].Normalized; bool no_shared = true; WriteGeneratedMesh( new TubeGenerator(circle_path, Frame3f.Identity, square_profile) { WantUVs = true, NoSharedVertices = no_shared }, "tubegen_loop_standarduv.obj"); WriteGeneratedMesh( new TubeGenerator(irreg_path, Frame3f.Identity, square_profile) { WantUVs = true, NoSharedVertices = no_shared }, "tubegen_irregloop_standarduv.obj"); WriteGeneratedMesh( new TubeGenerator(arc_path, Frame3f.Identity, square_profile) { WantUVs = true, NoSharedVertices = no_shared }, "tubegen_arc_standarduv.obj"); WriteGeneratedMesh( new TubeGenerator(irreg_arc_path, Frame3f.Identity, square_profile) { WantUVs = true, NoSharedVertices = no_shared }, "tubegen_irregarc_standarduv.obj"); // append tube border around each hole of input mesh DMesh3 inMesh = TestUtil.LoadTestInputMesh("n_holed_bunny.obj"); Polygon2d bdrycirc = Polygon2d.MakeCircle(0.25, 6); MeshBoundaryLoops loops = new MeshBoundaryLoops(inMesh); foreach (EdgeLoop loop in loops) { DCurve3 curve = loop.ToCurve().ResampleSharpTurns(); TubeGenerator gen = new TubeGenerator(curve, bdrycirc) { NoSharedVertices = false }; MeshEditor.Append(inMesh, gen.Generate().MakeDMesh()); } TestUtil.WriteTestOutputMesh(inMesh, "boundary_tubes.obj"); }
void Awake() { _target = target as MeshEditor; _target.LoadMesh(); ReloadSerializedProperties(); }
public static void test_uv_insert_segment() { DMesh3 mesh = TestUtil.LoadTestInputMesh("plane_250v.obj"); mesh.EnableVertexUVs(Vector2f.Zero); MeshTransforms.ConvertYUpToZUp(mesh); DMeshAABBTree3 spatial = new DMeshAABBTree3(mesh); spatial.Build(); int tid = spatial.FindNearestTriangle(Vector3d.Zero); //Polygon2d poly = Polygon2d.MakeRectangle(Vector2d.Zero, 5, 5); Polygon2d poly = Polygon2d.MakeCircle(5, 13); //PolyLine2d poly = new PolyLine2d( new Vector2d[] { -5 * Vector2d.One, 5 * Vector2d.One }); //int tri_edge0 = mesh.GetTriEdge(tid, 0); //Index2i edge0_tris = mesh.GetEdgeT(tri_edge0); //Index2i edge0_verts = mesh.GetEdgeV(tri_edge0); //Vector3d v0 = mesh.GetVertex(edge0_verts.a), v1 = mesh.GetVertex(edge0_verts.b); //Vector3d c = mesh.GetTriCentroid(tid); //Polygon2d poly = new Polygon2d(new Vector2d[] { // Vector2d.Lerp(v0.xy, v1.xy, -0.25), // Vector2d.Lerp(v0.xy, v1.xy, 1.5), // c.xy //}); MeshInsertUVPolyCurve insert = new MeshInsertUVPolyCurve(mesh, poly); insert.Apply(); Polygon2d test_poly = new Polygon2d(); List <double> distances = new List <double>(); List <int> nearests = new List <int>(); for (int i = 0; i < insert.Loops[0].VertexCount; ++i) { Vector2d v = mesh.GetVertex(insert.Loops[0].Vertices[i]).xy; test_poly.AppendVertex(v); int iNear; double fNear; distances.Add(poly.DistanceSquared(v, out iNear, out fNear)); nearests.Add(iNear); } System.Console.WriteLine("inserted loop poly has {0} edges", insert.Loops[0].EdgeCount); // find a triangle connected to loop that is inside the polygon // [TODO] maybe we could be a bit more robust about this? at least // check if triangle is too degenerate... int seed_tri = -1; for (int i = 0; i < insert.Loops[0].EdgeCount; ++i) { Index2i et = mesh.GetEdgeT(insert.Loops[0].Edges[i]); Vector3d ca = mesh.GetTriCentroid(et.a); bool in_a = poly.Contains(ca.xy); Vector3d cb = mesh.GetTriCentroid(et.b); bool in_b = poly.Contains(cb.xy); if (in_a && in_b == false) { seed_tri = et.a; break; } else if (in_b && in_a == false) { seed_tri = et.b; break; } } Util.gDevAssert(seed_tri != -1); // flood-fill inside loop HashSet <int> loopEdges = new HashSet <int>(insert.Loops[0].Edges); MeshFaceSelection sel = new MeshFaceSelection(mesh); sel.FloodFill(seed_tri, null, (eid) => { return(loopEdges.Contains(eid) == false); }); // delete inside loop MeshEditor editor = new MeshEditor(mesh); editor.RemoveTriangles(sel, true); MeshTransforms.ConvertZUpToYUp(mesh); TestUtil.WriteTestOutputMesh(mesh, "insert_uv_segment.obj"); //OBJWriter writer = new OBJWriter(); //var s = new System.IO.StreamWriter(Program.TEST_OUTPUT_PATH + "mesh_local_param.obj", false); //List<WriteMesh> wm = new List<WriteMesh>() { new WriteMesh(mesh) }; //WriteOptions opt = new WriteOptions() { // bCombineMeshes = false, bWriteGroups = false, bPerVertexColors = true, bPerVertexUVs = true, // AsciiHeaderFunc = () => { return "mttllib checkerboard.mtl\r\nusemtl checkerboard\r\n"; } //}; //writer.Write(s, wm, opt); //s.Close(); }
protected virtual DMesh3 update_step_2(DMesh3 meshIn) { double unsigned_offset = Math.Abs(distance); int exact_cells = (int)(unsigned_offset / grid_cell_size) + 1; // only use spatial DS if we are computing enough cells bool compute_spatial = GenerateClosedMeshOp.MeshSDFShouldUseSpatial( input_spatial, exact_cells, grid_cell_size, input_mesh_edge_stats.z) != null; DMeshAABBTree3 use_spatial = (compute_spatial) ? new DMeshAABBTree3(meshIn, true) : null; MeshSignedDistanceGrid sdf = new MeshSignedDistanceGrid(meshIn, grid_cell_size, use_spatial) { ExactBandWidth = exact_cells }; if (use_spatial != null) { sdf.NarrowBandMaxDistance = unsigned_offset + grid_cell_size; sdf.ComputeMode = MeshSignedDistanceGrid.ComputeModes.NarrowBand_SpatialFloodFill; } sdf.CancelF = is_invalidated; sdf.Compute(); if (is_invalidated()) { return(null); } var iso = new DenseGridTrilinearImplicit(sdf.Grid, sdf.GridOrigin, sdf.CellSize); MarchingCubes c = new MarchingCubes(); c.Implicit = iso; if (op_type == OperationTypes.Close) { c.IsoValue = -distance; } else { c.IsoValue = distance; } c.Bounds = cached_sdf_bounds; c.CubeSize = mesh_cell_size; c.Bounds.Expand(distance + 3 * c.CubeSize); c.RootMode = MarchingCubes.RootfindingModes.LerpSteps; c.RootModeSteps = 5; c.CancelF = is_invalidated; c.Generate(); if (is_invalidated()) { return(null); } Reducer r = new Reducer(c.Mesh); r.FastCollapsePass(c.CubeSize * 0.5, 3, true); if (is_invalidated()) { return(null); } if (min_component_volume > 0) { MeshEditor.RemoveSmallComponents(c.Mesh, min_component_volume, min_component_volume); } if (is_invalidated()) { return(null); } return(c.Mesh); }
async Task complete_import(string sFilename, DMesh3Builder builder, Action <string> onCompletedF) { AxisAlignedBox3d bounds = AxisAlignedBox3d.Empty; foreach (DMesh3 mesh in builder.Meshes) { bounds.Contain(mesh.CachedBounds); } Vector3d centerPt = bounds.Center; Vector3d basePt = centerPt - bounds.Height * 0.5f * Vector3d.AxisY; Vector3d vTranslate = basePt; await Task.Run(() => { foreach (DMesh3 mesh in builder.Meshes) { MeshTransforms.Translate(mesh, -vTranslate); } }); bool bFirst = (CC.Objects.PrintMeshes.Count == 0); Vector3d postTranslate = Vector3d.Zero; switch (CCPreferences.ImportTransformMode) { case CCPreferences.ImportTransformModes.AutoCenterAll: break; case CCPreferences.ImportTransformModes.AutoCenterFirst: if (bFirst) { CCState.SceneImportTransform = vTranslate; } postTranslate = vTranslate - CCState.SceneImportTransform; break; case CCPreferences.ImportTransformModes.NoAutoCenter: postTranslate = vTranslate; break; } // compact input meshes await Task.Run(() => { gParallel.ForEach(builder.Meshes, (mesh) => { MeshEditor.RemoveUnusedVertices(mesh); }); gParallel.ForEach(Interval1i.Range(builder.Meshes.Count), (k) => { if (builder.Meshes[k].IsCompact == false) { builder.Meshes[k] = new DMesh3(builder.Meshes[k], true); } }); }); string sBaseName = Path.GetFileNameWithoutExtension(sFilename); foreach (DMesh3 mesh in builder.Meshes) { PrintMeshSO meshSO = new PrintMeshSO(); meshSO.Create(mesh, CCMaterials.PrintMeshMaterial); meshSO.UpDirection = UpDirection.ZUp; Frame3f f = meshSO.GetLocalFrame(CoordSpace.ObjectCoords); f.Origin = f.Origin + (Vector3f)postTranslate; meshSO.SetLocalFrame(f, CoordSpace.ObjectCoords); // if only one mesh, we can keep a reference if (builder.Meshes.Count == 1) { meshSO.SourceFilePath = sFilename; meshSO.LastReadFileTimestamp = File.GetLastWriteTime(SourceFilePath).Ticks; } meshSO.Name = UniqueNames.GetNext(sBaseName); CCActions.AddNewPrintMesh(meshSO); } if (onCompletedF != null) { onCompletedF(sFilename); } }
private void OnPostRender() { List <MeshEditor> toDraw = new List <MeshEditor>(); foreach (var controller in FindObjectsOfType <WandController>()) { MeshEditor closest = null; float dist = float.MaxValue; foreach (var m in FindObjectsOfType <MeshEditor>()) { if (m.gameObject.GetComponent <ObjectID>()) { m.gameObject.GetComponent <ObjectID>().OutlineRenderer.enabled = false; } var d = Vector3.Distance(controller.transform.position, m.transform.position); if (d < dist) { dist = d; closest = m; } } if (closest != null && !toDraw.Contains(closest)) { toDraw.Add(closest); } } foreach (var m in toDraw) { try { Material mat = new Material(Shader.Find("Sprites/Default")); mat.color = Color.green; Transform trans = m.transform; if (m.GetComponent <ObjectID>()) { m.GetComponent <ObjectID>().OutlineRenderer.enabled = true; } GL.PushMatrix(); mat.SetPass(0); GL.Begin(GL.TRIANGLES); switch (ModeSelector.CurrentMode) { case ModeSelector.EditMode.VERTEX: // Set your materials foreach (var v in m.GetComponent <MeshFilter>().mesh.vertices) { GL.Vertex(trans.TransformPoint(v + (1 / trans.lossyScale.x) * new Vector3(0, 0.015f, 0))); GL.Vertex(trans.TransformPoint(v + (1 / trans.lossyScale.x) * new Vector3(-.006f, -0.004f, 0))); GL.Vertex(trans.TransformPoint(v + (1 / trans.lossyScale.x) * new Vector3(.006f, -0.004f, 0))); } break; case ModeSelector.EditMode.EDGE: // Set your materials if (m.Edges == null || m.Edges.Count == 0 || trans == null) { continue; } foreach (var e in m.Edges) { GL.Vertex(trans.TransformPoint(e.center + (1 / trans.lossyScale.x) * new Vector3(0, 0.015f, 0))); GL.Vertex(trans.TransformPoint(e.center + (1 / trans.lossyScale.x) * new Vector3(-.006f, -0.004f, 0))); GL.Vertex(trans.TransformPoint(e.center + (1 / trans.lossyScale.x) * new Vector3(.006f, -0.004f, 0))); } break; case ModeSelector.EditMode.FACE: if (m.GetComponent <ObjectID>()) { m.GetComponent <ObjectID>().OutlineRenderer.material = FindObjectOfType <FaceTool>().OutlineMaterial; } if (m.Faces == null || m.Faces.Length == 0 || trans == null) { continue; } foreach (var f in m.Faces) { GL.Vertex(trans.TransformPoint(f.center + (1 / trans.lossyScale.x) * new Vector3(0, 0.015f, 0))); GL.Vertex(trans.TransformPoint(f.center + (1 / trans.lossyScale.x) * new Vector3(-.006f, -0.004f, 0))); GL.Vertex(trans.TransformPoint(f.center + (1 / trans.lossyScale.x) * new Vector3(.006f, -0.004f, 0))); } break; case ModeSelector.EditMode.OBJECT: break; } } finally { GL.End(); GL.PopMatrix(); } } if (!DrawSelection) { return; } foreach (var e in FindObjectOfType <SelectionTool>().Selection) { try { Material mat = new Material(Shader.Find("Sprites/Default")); mat.color = Color.red; GL.PushMatrix(); mat.SetPass(0); GL.Begin(GL.TRIANGLES); Transform trans = e.Editor.transform; if (e is MeshEditor.Edge) { GL.Vertex(trans.TransformPoint(e.center + (1 / trans.lossyScale.x) * new Vector3(0, 0.015f, 0))); GL.Vertex(trans.TransformPoint(e.center + (1 / trans.lossyScale.x) * new Vector3(-.009f, -0.006f, 0))); GL.Vertex(trans.TransformPoint(e.center + (1 / trans.lossyScale.x) * new Vector3(.009f, -0.006f, 0))); } else if (e is MeshEditor.VertexGroup) { GL.Vertex((e as MeshEditor.VertexGroup).WorldPosition + (1 / 1.5f * trans.lossyScale.x) * new Vector3(0, 0.015f, 0)); GL.Vertex((e as MeshEditor.VertexGroup).WorldPosition + (1 / 1.5f * trans.lossyScale.x) * new Vector3(-.009f, -0.006f, 0)); GL.Vertex((e as MeshEditor.VertexGroup).WorldPosition + (1 / 1.5f * trans.lossyScale.x) * new Vector3(.009f, -0.006f, 0)); } else if (e is MeshEditor.Face) { GL.Vertex(trans.TransformPoint(e.center + (1 / trans.lossyScale.x) * new Vector3(0, 0.015f, 0))); GL.Vertex(trans.TransformPoint(e.center + (1 / trans.lossyScale.x) * new Vector3(-.009f, -0.006f, 0))); GL.Vertex(trans.TransformPoint(e.center + (1 / trans.lossyScale.x) * new Vector3(.009f, -0.006f, 0))); } } finally { GL.End(); GL.PopMatrix(); } } }
protected virtual DMesh3 compute_wrap() { cache_input_sdfs(); if (is_invalidated()) { return(null); } BoundedImplicitFunction3d iso = null; if (op_type == OpTypes.Union) { iso = new ImplicitNaryUnion3d() { Children = new List <BoundedImplicitFunction3d>(cached_isos) }; } else if (op_type == OpTypes.Intersection) { iso = new ImplicitNaryIntersection3d() { Children = new List <BoundedImplicitFunction3d>(cached_isos) }; } else if (op_type == OpTypes.Difference) { iso = new ImplicitNaryDifference3d() { A = cached_isos[0], BSet = new List <BoundedImplicitFunction3d>(cached_isos.Skip(1)) }; } MarchingCubes c = new MarchingCubes(); c.Implicit = iso; c.IsoValue = 0; c.Bounds = iso.Bounds(); c.CubeSize = mesh_cell_size; c.Bounds.Expand(3 * c.CubeSize); c.RootMode = MarchingCubes.RootfindingModes.Bisection; c.RootModeSteps = 5; c.CancelF = is_invalidated; c.Generate(); if (is_invalidated()) { return(null); } Reducer r = new Reducer(c.Mesh); r.FastCollapsePass(c.CubeSize / 2, 3, true); if (is_invalidated()) { return(null); } if (min_component_volume > 0) { MeshEditor.RemoveSmallComponents(c.Mesh, min_component_volume, min_component_volume); } if (is_invalidated()) { return(null); } return(c.Mesh); }
public bool Insert() { Func <int, bool> is_contained_v = (vid) => { Vector3d v = Mesh.GetVertex(vid); Vector2f vf2 = ProjectFrame.ToPlaneUV((Vector3f)v, 2); return(Polygon.Contains(vf2)); }; var vertexROI = new MeshVertexSelection(Mesh); Index3i seedT = Mesh.GetTriangle(SeedTriangle); // if a seed vert of seed triangle is containd in polygon, we will // flood-fill out from there, this gives a better ROI. // If not, we will try flood-fill from the seed triangles. var seed_verts = new List <int>(); for (int j = 0; j < 3; ++j) { if (is_contained_v(seedT[j])) { seed_verts.Add(seedT[j]); } } if (seed_verts.Count == 0) { seed_verts.Add(seedT.a); seed_verts.Add(seedT.b); seed_verts.Add(seedT.c); } // flood-fill out from seed vertices until we have found all vertices // contained in polygon vertexROI.FloodFill(seed_verts.ToArray(), is_contained_v); // convert vertex ROI to face ROI var faceROI = new MeshFaceSelection(Mesh, vertexROI, 1); faceROI.ExpandToOneRingNeighbours(); faceROI.FillEars(true); // this might be a good idea... // construct submesh var regionOp = new RegionOperator(Mesh, faceROI); DSubmesh3 roiSubmesh = regionOp.Region; DMesh3 roiMesh = roiSubmesh.SubMesh; // save 3D positions of unmodified mesh var initialPositions = new Vector3d[roiMesh.MaxVertexID]; // map roi mesh to plane MeshTransforms.PerVertexTransform(roiMesh, roiMesh.VertexIndices(), (v, vid) => { Vector2f uv = ProjectFrame.ToPlaneUV((Vector3f)v, 2); initialPositions[vid] = v; return(new Vector3d(uv.x, uv.y, 0)); }); // save a copy of 2D mesh and construct bvtree. we will use // this later to project back to 3d // [TODO] can we use a better spatial DS here, that takes advantage of 2D? var projectMesh = new DMesh3(roiMesh); var projecter = new DMeshAABBTree3(projectMesh, true); var insertUV = new MeshInsertUVPolyCurve(roiMesh, Polygon); //insertUV.Validate() bool bOK = insertUV.Apply(); if (!bOK) { throw new Exception("insertUV.Apply() failed"); } if (SimplifyInsertion) { insertUV.Simplify(); } int[] insertedPolyVerts = insertUV.CurveVertices; // grab inserted loop, assuming it worked EdgeLoop insertedLoop = null; if (insertUV.Loops.Count == 1) { insertedLoop = insertUV.Loops[0]; } // find interior triangles var interiorT = new List <int>(); foreach (int tid in roiMesh.TriangleIndices()) { Vector3d centroid = roiMesh.GetTriCentroid(tid); if (Polygon.Contains(centroid.xy)) { interiorT.Add(tid); } } if (RemovePolygonInterior) { var editor = new MeshEditor(roiMesh); editor.RemoveTriangles(interiorT, true); InteriorTriangles = null; } else { InteriorTriangles = interiorT.ToArray(); } // map back to 3d Vector3d a = Vector3d.Zero, b = Vector3d.Zero, c = Vector3d.Zero; foreach (int vid in roiMesh.VertexIndices()) { // [TODO] somehow re-use exact positions from regionOp maps? // construct new 3D pos w/ barycentric interpolation Vector3d v = roiMesh.GetVertex(vid); int tid = projecter.FindNearestTriangle(v); Index3i tri = projectMesh.GetTriangle(tid); projectMesh.GetTriVertices(tid, ref a, ref b, ref c); Vector3d bary = MathUtil.BarycentricCoords(ref v, ref a, ref b, ref c); Vector3d pos = bary.x * initialPositions[tri.a] + bary.y * initialPositions[tri.b] + bary.z * initialPositions[tri.c]; roiMesh.SetVertex(vid, pos); } bOK = BackPropagate(regionOp, insertedPolyVerts, insertedLoop); return(bOK); }
protected virtual DMesh3 compute_wrap() { DMesh3 meshIn = MeshSource.GetDMeshUnsafe(); double unsigned_offset = Math.Abs(distance); if (cached_sdf == null || unsigned_offset > cached_sdf_max_offset || grid_cell_size != cached_sdf.CellSize) { DMeshAABBTree3 use_spatial = input_spatial; CachingMeshSDF sdf = new CachingMeshSDF(meshIn, grid_cell_size, use_spatial); sdf.MaxOffsetDistance = 2 * (float)unsigned_offset; sdf.CancelF = is_invalidated; sdf.Initialize(); if (is_invalidated()) { return(null); } cached_sdf = sdf; cached_sdf_max_offset = unsigned_offset; cached_sdf_bounds = meshIn.CachedBounds; } var grid_iso = new CachingMeshSDFImplicit(cached_sdf); // currently MCPro-Continuation does not work w/ non-zero // isovalues, so we have to shift our target offset externally var iso = new ImplicitOffset3d() { A = grid_iso, Offset = distance }; MarchingCubesPro c = new MarchingCubesPro(); c.Implicit = iso; c.Bounds = cached_sdf_bounds; c.CubeSize = mesh_cell_size; c.Bounds.Expand(distance + 3 * c.CubeSize); c.CancelF = is_invalidated; c.GenerateContinuation(offset_seeds(meshIn, distance)); if (is_invalidated()) { return(null); } Reducer r = new Reducer(c.Mesh); r.FastCollapsePass(c.CubeSize * 0.5, 3, true); if (is_invalidated()) { return(null); } if (min_component_volume > 0) { MeshEditor.RemoveSmallComponents(c.Mesh, min_component_volume, min_component_volume); } if (is_invalidated()) { return(null); } DMesh3 offsetMesh = c.Mesh; MeshConnectedComponents comp = new MeshConnectedComponents(offsetMesh); comp.FindConnectedT(); if (is_invalidated()) { return(null); } DSubmesh3Set subMeshes = new DSubmesh3Set(offsetMesh, comp); if (is_invalidated()) { return(null); } MeshSpatialSort sort = new MeshSpatialSort(); foreach (var subMesh in subMeshes) { sort.AddMesh(subMesh.SubMesh, subMesh); } sort.Sort(); if (is_invalidated()) { return(null); } DMesh3 outerMesh = new DMesh3(); foreach (var solid in sort.Solids) { DMesh3 outer = solid.Outer.Mesh; //if (is_outward_oriented(outer) == false) // outer.ReverseOrientation(); MeshEditor.Append(outerMesh, outer); } if (is_invalidated()) { return(null); } return(compute_inset(outerMesh)); }
public bool Apply() { bool do_checks = false; if (do_checks) { Mesh.CheckValidity(); } /* * Remove parts of the mesh we don't want before we bother with anything else * TODO: maybe we need to repair orientation first? if we want to use MWN... */ do_remove_inside(); if (Cancelled()) { return(false); } int repeat_count = 0; repeat_all: /* * make sure orientation of connected components is consistent * TODO: what about mobius strip problems? */ repair_orientation(false); if (Cancelled()) { return(false); } /* * Do safe close-cracks to handle easy cases */ repair_cracks(true, RepairTolerance); if (Mesh.IsClosed()) { goto all_done; } if (Cancelled()) { return(false); } /* * Collapse tiny edges and then try easy cases again, and * then allow for handling of ambiguous cases */ collapse_all_degenerate_edges(RepairTolerance * 0.5, true); if (Cancelled()) { return(false); } repair_cracks(true, 2 * RepairTolerance); if (Cancelled()) { return(false); } repair_cracks(false, 2 * RepairTolerance); if (Cancelled()) { return(false); } if (Mesh.IsClosed()) { goto all_done; } /* * Possibly we have joined regions with different orientation (is it?), fix that * TODO: mobius strips again */ repair_orientation(false); if (Cancelled()) { return(false); } if (do_checks) { Mesh.CheckValidity(); } // get rid of any remaining single-triangles before we start filling holes remove_loners(); /* * Ok, fill simple holes. */ int nRemainingBowties = 0; int nHoles; bool bSawSpans; fill_trivial_holes(out nHoles, out bSawSpans); if (Cancelled()) { return(false); } if (Mesh.IsClosed()) { goto all_done; } /* * Now fill harder holes. If we saw spans, that means boundary loops could * not be resolved in some cases, do we disconnect bowties and try again. */ fill_any_holes(out nHoles, out bSawSpans); if (Cancelled()) { return(false); } if (bSawSpans) { disconnect_bowties(out nRemainingBowties); fill_any_holes(out nHoles, out bSawSpans); } if (Cancelled()) { return(false); } if (Mesh.IsClosed()) { goto all_done; } /* * We may have a closed mesh now but it might still have bowties (eg * tetrahedra sharing vtx case). So disconnect those. */ disconnect_bowties(out nRemainingBowties); if (Cancelled()) { return(false); } /* * If the mesh is not closed, we will do one more round to try again. */ if (repeat_count == 0 && Mesh.IsClosed() == false) { repeat_count++; goto repeat_all; } /* * Ok, we didn't get anywhere on our first repeat. If we are still not * closed, we will try deleting boundary triangles and repeating. * Repeat this N times. */ if (repeat_count <= ErosionIterations && Mesh.IsClosed() == false) { repeat_count++; MeshFaceSelection bdry_faces = new MeshFaceSelection(Mesh); foreach (int eid in MeshIterators.BoundaryEdges(Mesh)) { bdry_faces.SelectEdgeTris(eid); } MeshEditor.RemoveTriangles(Mesh, bdry_faces, true); goto repeat_all; } all_done: /* * Remove tiny edges */ if (MinEdgeLengthTol > 0) { collapse_all_degenerate_edges(MinEdgeLengthTol, false); } if (Cancelled()) { return(false); } /* * finally do global orientation */ repair_orientation(true); if (Cancelled()) { return(false); } if (do_checks) { Mesh.CheckValidity(); } /* * Might as well compact output mesh... */ Mesh = new DMesh3(Mesh, true); MeshNormals.QuickCompute(Mesh); return(true); }
protected virtual void compute_hollow() { double offset_distance = -wall_thickness; if (cached_sdf == null || wall_thickness > cached_sdf_max_offset || grid_cell_size != cached_sdf.CellSize) { DMesh3 meshIn = MeshSource.GetDMeshUnsafe(); int exact_cells = (int)((wall_thickness) / grid_cell_size) + 1; // only use spatial DS if we are computing enough cells DMeshAABBTree3 use_spatial = GenerateClosedMeshOp.MeshSDFShouldUseSpatial(input_spatial, exact_cells, grid_cell_size, input_mesh_edge_stats.z); MeshSignedDistanceGrid sdf = new MeshSignedDistanceGrid(meshIn, grid_cell_size, use_spatial) { ExactBandWidth = exact_cells }; if (use_spatial != null) { sdf.NarrowBandMaxDistance = wall_thickness + grid_cell_size; sdf.ComputeMode = MeshSignedDistanceGrid.ComputeModes.NarrowBand_SpatialFloodFill; } sdf.CancelF = is_invalidated; sdf.Compute(); if (is_invalidated()) { return; } cached_sdf = sdf; cached_sdf_max_offset = wall_thickness; cached_sdf_bounds = meshIn.CachedBounds; } var iso = new DenseGridTrilinearImplicit(cached_sdf.Grid, cached_sdf.GridOrigin, cached_sdf.CellSize); ImplicitOffset3d shell_field = new ImplicitOffset3d() { A = iso, Offset = offset_distance }; ImplicitFunction3d use_iso = shell_field; if (enable_infill) { GridDistanceField grid_df = new GridDistanceField() { CellSize = infill_spacing, Radius = infill_thickness * 0.5, Origin = cached_sdf.GridOrigin }; ImplicitDifference3d diff = new ImplicitDifference3d() { A = shell_field, B = grid_df }; use_iso = diff; } MarchingCubes c = new MarchingCubes(); c.Implicit = use_iso; c.IsoValue = 0; c.Bounds = cached_sdf_bounds; c.CubeSize = mesh_cell_size; c.Bounds.Expand(offset_distance + 3 * c.CubeSize); c.RootMode = MarchingCubes.RootfindingModes.LerpSteps; c.RootModeSteps = 5; c.CancelF = is_invalidated; c.Generate(); if (is_invalidated()) { return; } Reducer r = new Reducer(c.Mesh); r.FastCollapsePass(c.CubeSize * 0.5, 3, true); if (is_invalidated()) { return; } //r.ReduceToTriangleCount(c.Mesh.TriangleCount / 5); //if (is_invalidated()) // return; if (min_component_volume > 0) { MeshEditor.RemoveSmallComponents(c.Mesh, min_component_volume, min_component_volume); } if (is_invalidated()) { return; } c.Mesh.AttachMetadata("is_partial", new object()); ResultMesh = c.Mesh; }