public DMesh3 remesh_region(int iterations, DMesh3 mesh, double min, double max, double angle) { int[] tris = GetTrisOnPositiveSide(mesh, new Frame3f(Vector3F.Zero, Vector3F.AxisY)); RegionRemesher r = new RegionRemesher(mesh, tris); r.Region.SubMesh.CheckValidity(true); r.Precompute(); r.EnableFlips = r.EnableSplits = r.EnableCollapses = true; r.MinEdgeLength = min; r.MaxEdgeLength = max; r.EnableSmoothing = true; r.SmoothSpeedT = 1.0f; for (int k = 0; k < iterations; ++k) { r.BasicRemeshPass(); mesh.CheckValidity(); } r.BackPropropagate(); for (int k = 0; k < iterations; ++k) { r.BasicRemeshPass(); mesh.CheckValidity(); } r.BackPropropagate(); return(mesh); }
public static void test_remesh_region() { int Slices = 16; DMesh3 mesh = TestUtil.MakeCappedCylinder(false, Slices); MeshUtil.ScaleMesh(mesh, Frame3f.Identity, new Vector3f(1, 2, 1)); mesh.CheckValidity(); int[] tris = TestUtil.GetTrisOnPositiveSide(mesh, new Frame3f(Vector3f.Zero, Vector3f.AxisY)); RegionRemesher r = new RegionRemesher(mesh, tris); r.Region.SubMesh.CheckValidity(true); TestUtil.WriteTestOutputMesh(r.Region.SubMesh, "remesh_region_submesh.obj"); r.Precompute(); 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; for (int k = 0; k < 5; ++k) { r.BasicRemeshPass(); mesh.CheckValidity(); } TestUtil.WriteTestOutputMesh(r.Region.SubMesh, "remesh_region_submesh_refined.obj"); r.BackPropropagate(); TestUtil.WriteTestOutputMesh(mesh, "remesh_region_submesh_merged_1.obj"); for (int k = 0; k < 5; ++k) { r.BasicRemeshPass(); mesh.CheckValidity(); } r.BackPropropagate(); TestUtil.WriteTestOutputMesh(mesh, "remesh_region_submesh_merged_2.obj"); }
void smooth_and_remesh(MeshFaceSelection tris) { if (EnableLaplacianSmooth) { LaplacianMeshSmoother.RegionSmooth(Mesh, tris, 2, 2, false); } if (RemeshAfterSmooth) { tris.ExpandToOneRingNeighbours(2); tris.LocalOptimize(true, true); MeshProjectionTarget target = MeshProjectionTarget.Auto(Mesh, tris, 5); RegionRemesher remesh2 = new RegionRemesher(Mesh, tris); remesh2.SetTargetEdgeLength(TargetEdgeLength); remesh2.SmoothSpeedT = 1.0; remesh2.SetProjectionTarget(target); if (ConfigureRemesherF != null) { ConfigureRemesherF(remesh2, false); } for (int k = 0; k < 10; ++k) { remesh2.BasicRemeshPass(); } remesh2.BackPropropagate(); FillTriangles = remesh2.CurrentBaseTriangles; } else { FillTriangles = tris.ToArray(); } }
void smooth_and_remesh_preserve(MeshFaceSelection tris, bool bFinal) { if (EnableLaplacianSmooth) { LaplacianMeshSmoother.RegionSmooth(Mesh, tris, 2, 2, true); } if (RemeshAfterSmooth) { MeshProjectionTarget target = (bFinal) ? MeshProjectionTarget.Auto(Mesh, tris, 5) : null; RegionRemesher remesh2 = new RegionRemesher(Mesh, tris); remesh2.SetTargetEdgeLength(TargetEdgeLength); remesh2.SmoothSpeedT = 1.0; remesh2.SetProjectionTarget(target); if (ConfigureRemesherF != null) { ConfigureRemesherF(remesh2, false); } for (int k = 0; k < 10; ++k) { remesh2.BasicRemeshPass(); } remesh2.BackPropropagate(); FillTriangles = remesh2.CurrentBaseTriangles; } else { FillTriangles = tris.ToArray(); } }
// This function does local remeshing around a boundary loop within a fixed # of // rings, to try to 'massage' it into a cleaner shape/topology // [TODO] use geodesic distance instead of fixed # of rings? public static void cleanup_boundary(NGonsCore.geometry3Sharp.mesh.DMesh3 mesh, EdgeLoop loop, double target_edge_len, int nRings = 3) { Debug.Assert(loop.IsBoundaryLoop()); MeshFaceSelection roi = new MeshFaceSelection(mesh); roi.SelectVertexOneRings(loop.Vertices); for (int i = 0; i < nRings; ++i) { roi.ExpandToOneRingNeighbours(); } roi.LocalOptimize(true, true); RegionRemesher r = new RegionRemesher(mesh, roi.ToArray()); r.Precompute(); r.EnableFlips = r.EnableSplits = r.EnableCollapses = true; r.MinEdgeLength = target_edge_len; r.MaxEdgeLength = 2 * target_edge_len; r.EnableSmoothing = true; r.SmoothSpeedT = 0.1f; for (int k = 0; k < nRings * 3; ++k) { r.BasicRemeshPass(); } Debug.Assert(mesh.CheckValidity()); r.BackPropropagate(); }
/// <summary> /// Cut a "partial" hole, ie we cut the mesh with the polygon once, and then /// extrude downwards to a planar version of the cut boundary. /// /// Currently only supports extruding downwards from topmost intersection. /// /// </summary> protected bool CutPartialHole(DMesh3 mesh, HoleInfo hi, Vector3d translate, bool bUpwards) { if (hi.IsVertical == false) { throw new Exception("unsupported!"); } Vector3d basePoint = CombinedBounds.Center - CombinedBounds.Extents.y * Vector3d.AxisY + translate; // do we need to compute spatial DS for each hole? not super efficient... DMeshAABBTree3 spatial = new DMeshAABBTree3(mesh, true); Vector3d direction = (bUpwards) ? Vector3d.AxisY : -Vector3d.AxisY; Vector3d center = basePoint + new Vector3d(hi.XZOffset.x, 0, hi.XZOffset.y) - 10000 * direction; Ray3d ray = new Ray3d(center, direction); int hit_tid = spatial.FindNearestHitTriangle(ray); if (hit_tid == DMesh3.InvalidID) { return(false); } IntrRay3Triangle3 intersection = MeshQueries.TriangleIntersection(mesh, hit_tid, ray); Vector3d inter_pos = ray.PointAt(intersection.RayParameter); Frame3f projectFrame = new Frame3f(ray.Origin, ray.Direction); int nVerts = 32; if (hi.Vertices != 0) { nVerts = hi.Vertices; } double angleShiftRad = hi.AxisAngleD * MathUtil.Deg2Rad; Polygon2d circle = Polygon2d.MakeCircle(hi.Radius, nVerts, angleShiftRad); try { EdgeLoop loop = null; MeshInsertProjectedPolygon insert = new MeshInsertProjectedPolygon(mesh, circle, projectFrame, hit_tid) { SimplifyInsertion = false }; if (insert.Insert()) { loop = insert.InsertedLoop; // [RMS] do we need to simplify for this one? //if (loop.VertexCount > circle.VertexCount) // loop = simplify_loop(mesh, loop, circle.VertexCount); MeshEditor editor = new MeshEditor(mesh); Vector3d base_pos = inter_pos; base_pos.y = basePoint.y + hi.PartialHoleBaseHeight; int N = loop.VertexCount; int[] newLoop = new int[N]; for (int k = 0; k < N; ++k) { newLoop[k] = mesh.AppendVertex(mesh, loop.Vertices[k]); Vector3d cur_v = mesh.GetVertex(newLoop[k]); cur_v.y = base_pos.y; mesh.SetVertex(newLoop[k], cur_v); } int base_vid = mesh.AppendVertex(base_pos); int[] fan_tris = editor.AddTriangleFan_OrderedVertexLoop(base_vid, newLoop); FaceGroupUtil.SetGroupID(mesh, fan_tris, hi.PartialHoleGroupID); int[] stitch_tris = editor.StitchLoop(loop.Vertices, newLoop); // need to remesh fan region because otherwise we get pathological cases RegionRemesher remesh = new RegionRemesher(mesh, fan_tris); remesh.SetTargetEdgeLength(2.0); remesh.SmoothSpeedT = 1.0; remesh.PreventNormalFlips = true; for (int k = 0; k < 25; ++k) { remesh.BasicRemeshPass(); } //remesh.EnableCollapses = remesh.EnableFlips = remesh.EnableSplits = false; //for (int k = 0; k < 20; ++k) // remesh.BasicRemeshPass(); remesh.BackPropropagate(); return(true); } else { return(false); } } catch (Exception e) { f3.DebugUtil.Log("partial hole {0} failed!! {1}", hi.nHole, e.Message); return(false); } }
public bool Apply() { EdgeLoop useLoop = null; if (FillLoop == null) { MeshBoundaryLoops loops = new MeshBoundaryLoops(Mesh, true); if (loops.Count == 0) { return(false); } if (BorderHintTris != null) { useLoop = select_loop_tris_hint(loops); } if (useLoop == null && loops.MaxVerticesLoopIndex >= 0) { useLoop = loops[loops.MaxVerticesLoopIndex]; } } else { useLoop = FillLoop; } if (useLoop == null) { return(false); } // step 1: do stupid hole fill SimpleHoleFiller filler = new SimpleHoleFiller(Mesh, useLoop); if (filler.Fill() == false) { return(false); } if (useLoop.Vertices.Length <= 3) { FillTriangles = filler.NewTriangles; FillVertices = new int[0]; return(true); } MeshFaceSelection tris = new MeshFaceSelection(Mesh); tris.Select(filler.NewTriangles); // extrude initial fill surface (this is used in socketgen for example) if (OffsetDistance > 0) { MeshExtrudeFaces extrude = new MeshExtrudeFaces(Mesh, tris); extrude.ExtrudedPositionF = (v, n, vid) => { return(v + OffsetDistance * OffsetDirection); }; if (!extrude.Extrude()) { return(false); } tris.Select(extrude.JoinTriangles); } // if we aren't trying to stay inside hole, expand out a bit, // which allows us to clean up ugly edges if (ConstrainToHoleInterior == false) { tris.ExpandToOneRingNeighbours(2); tris.LocalOptimize(true, true); } // remesh the initial coarse fill region if (RemeshBeforeSmooth) { RegionRemesher remesh = new RegionRemesher(Mesh, tris); remesh.SetTargetEdgeLength(TargetEdgeLength); remesh.EnableSmoothing = (SmoothAlpha > 0); remesh.SmoothSpeedT = SmoothAlpha; if (ConfigureRemesherF != null) { ConfigureRemesherF(remesh, true); } for (int k = 0; k < InitialRemeshPasses; ++k) { remesh.BasicRemeshPass(); } remesh.BackPropropagate(); tris = new MeshFaceSelection(Mesh); tris.Select(remesh.CurrentBaseTriangles); if (ConstrainToHoleInterior == false) { tris.LocalOptimize(true, true); } } if (ConstrainToHoleInterior) { for (int k = 0; k < SmoothSolveIterations; ++k) { smooth_and_remesh_preserve(tris, k == SmoothSolveIterations - 1); tris = new MeshFaceSelection(Mesh); tris.Select(FillTriangles); } } else { smooth_and_remesh(tris); tris = new MeshFaceSelection(Mesh); tris.Select(FillTriangles); } MeshVertexSelection fill_verts = new MeshVertexSelection(Mesh); fill_verts.SelectInteriorVertices(tris); FillVertices = fill_verts.ToArray(); return(true); }
public void Close_Flat() { double minlen, maxlen, avglen; MeshQueries.EdgeLengthStats(Mesh, out minlen, out maxlen, out avglen, 1000); double target_edge_len = (TargetEdgeLen <= 0) ? avglen : TargetEdgeLen; // massage around boundary loop cleanup_boundary(Mesh, InitialBorderLoop, avglen, 3); // find new border loop // [TODO] this just assumes there is only one!! MeshBoundaryLoops loops = new MeshBoundaryLoops(Mesh); EdgeLoop fill_loop = loops.Loops[0]; int extrude_group = (ExtrudeGroup == -1) ? Mesh.AllocateTriangleGroup() : ExtrudeGroup; int fill_group = (FillGroup == -1) ? Mesh.AllocateTriangleGroup() : FillGroup; // decide on projection plane //AxisAlignedBox3d loopbox = fill_loop.GetBounds(); //Vector3d topPt = loopbox.Center; //if ( bIsUpper ) { // topPt.y = loopbox.Max.y + 0.25 * dims.y; //} else { // topPt.y = loopbox.Min.y - 0.25 * dims.y; //} //Frame3f plane = new Frame3f((Vector3f)topPt); // extrude loop to this plane MeshExtrusion extrude = new MeshExtrusion(Mesh, fill_loop); extrude.PositionF = (v, n, i) => { return(FlatClosePlane.ProjectToPlane((Vector3F)v, 1)); }; extrude.Extrude(extrude_group); MeshValidation.IsBoundaryLoop(Mesh, extrude.NewLoop); Debug.Assert(Mesh.CheckValidity()); // smooth the extrude loop MeshLoopSmooth loop_smooth = new MeshLoopSmooth(Mesh, extrude.NewLoop); loop_smooth.ProjectF = (v, i) => { return(FlatClosePlane.ProjectToPlane((Vector3F)v, 1)); }; loop_smooth.Alpha = 0.5f; loop_smooth.Rounds = 100; loop_smooth.Smooth(); Debug.Assert(Mesh.CheckValidity()); // fill result SimpleHoleFiller filler = new SimpleHoleFiller(Mesh, extrude.NewLoop); filler.Fill(fill_group); Debug.Assert(Mesh.CheckValidity()); // make selection for remesh region MeshFaceSelection remesh_roi = new MeshFaceSelection(Mesh); remesh_roi.Select(extrude.NewTriangles); remesh_roi.Select(filler.NewTriangles); remesh_roi.ExpandToOneRingNeighbours(); remesh_roi.ExpandToOneRingNeighbours(); remesh_roi.LocalOptimize(true, true); int[] new_roi = remesh_roi.ToArray(); // get rid of extrude group FaceGroupUtil.SetGroupToGroup(Mesh, extrude_group, 0); /* clean up via remesh * - constrain loop we filled to itself */ RegionRemesher r = new RegionRemesher(Mesh, new_roi); DCurve3 top_curve = mesh.MeshUtil.ExtractLoopV(Mesh, extrude.NewLoop.Vertices); DCurveProjectionTarget curve_target = new DCurveProjectionTarget(top_curve); int[] top_loop = (int[])extrude.NewLoop.Vertices.Clone(); r.Region.MapVerticesToSubmesh(top_loop); MeshConstraintUtil.ConstrainVtxLoopTo(r.Constraints, r.Mesh, top_loop, curve_target); DMeshAABBTree3 spatial = new DMeshAABBTree3(Mesh); spatial.Build(); MeshProjectionTarget target = new MeshProjectionTarget(Mesh, spatial); r.SetProjectionTarget(target); bool bRemesh = true; if (bRemesh) { r.Precompute(); r.EnableFlips = r.EnableSplits = r.EnableCollapses = true; r.MinEdgeLength = target_edge_len; r.MaxEdgeLength = 2 * target_edge_len; r.EnableSmoothing = true; r.SmoothSpeedT = 1.0f; for (int k = 0; k < 40; ++k) { r.BasicRemeshPass(); } r.SetProjectionTarget(null); r.SmoothSpeedT = 0.25f; for (int k = 0; k < 10; ++k) { r.BasicRemeshPass(); } Debug.Assert(Mesh.CheckValidity()); r.BackPropropagate(); } // smooth around the join region to clean up ugliness smooth_region(Mesh, r.Region.BaseBorderV, 3); }
protected void do_flatten(DMesh3 mesh) { double BAND_HEIGHT = flatten_band_height; Vector3d down_axis = -Vector3d.AxisY; double dot_thresh = 0.2; AxisAlignedBox3d bounds = mesh.CachedBounds; DMeshAABBTree3 spatial = new DMeshAABBTree3(mesh, true); Ray3d ray = new Ray3d(bounds.Center - 2 * bounds.Height * Vector3d.AxisY, Vector3d.AxisY); int hit_tid = spatial.FindNearestHitTriangle(ray); Frame3f hitF; MeshQueries.RayHitPointFrame(mesh, spatial, ray, out hitF); Vector3d basePt = hitF.Origin; Frame3f basePlane = new Frame3f(basePt, Vector3f.AxisY); MeshConnectedComponents components = new MeshConnectedComponents(mesh) { FilterF = (tid) => { if (mesh.GetTriangleGroup(tid) != LastExtrudeOuterGroupID) { return(false); } Vector3d n, c; double a; mesh.GetTriInfo(tid, out n, out a, out c); double h = Math.Abs(c.y - basePt.y); if (h > BAND_HEIGHT) { return(false); } if (n.Dot(down_axis) < dot_thresh) { return(false); } return(true); }, SeedFilterF = (tid) => { return(tid == hit_tid); } }; components.FindConnectedT(); MeshFaceSelection all_faces = new MeshFaceSelection(mesh); foreach (var comp in components) { MeshVertexSelection vertices = new MeshVertexSelection(mesh); vertices.SelectTriangleVertices(comp.Indices); foreach (int vid in vertices) { Vector3d v = mesh.GetVertex(vid); v = basePlane.ProjectToPlane((Vector3f)v, 2); mesh.SetVertex(vid, v); } all_faces.SelectVertexOneRings(vertices); } all_faces.ExpandToOneRingNeighbours(3, (tid) => { return(mesh.GetTriangleGroup(tid) == LastExtrudeOuterGroupID); }); RegionRemesher r = new RegionRemesher(mesh, all_faces); r.SetProjectionTarget(MeshProjectionTarget.Auto(mesh)); r.SetTargetEdgeLength(2.0f); r.SmoothSpeedT = 1.0f; for (int k = 0; k < 10; ++k) { r.BasicRemeshPass(); } r.SetProjectionTarget(null); r.SmoothSpeedT = 1.0f; for (int k = 0; k < 10; ++k) { r.BasicRemeshPass(); } r.BackPropropagate(); }