public static Rhino.Geometry.Point3d[] ToRhinoPts(this DenseGridTrilinearImplicit Value) { int dimX = Value.Grid.ni; int dimY = Value.Grid.nj; int dimZ = Value.Grid.nk; int counter = 0; Rhino.Geometry.Point3d origin = Value.GridOrigin.ToRhinoPt(); double size = Value.CellSize; Rhino.Geometry.Point3d[] Pts = new Rhino.Geometry.Point3d[dimX * dimY * dimZ]; for (int k = 0; k < dimZ; k++) { for (int j = 0; j < dimY; j++) { for (int i = 0; i < dimX; i++) { Pts[counter] = origin + new Rhino.Geometry.Point3d(i * size, j * size, k * size); counter++; } } } return(Pts); }
public static void test_marching_cubes_levelset() { //DMesh3 mesh = TestUtil.LoadTestInputMesh("bunny_solid.obj"); DMesh3 mesh = TestUtil.LoadTestInputMesh("bunny_overlap_solids.obj"); //Sphere3Generator_NormalizedCube gen = new Sphere3Generator_NormalizedCube() { EdgeVertices = 100, Radius = 5 }; //DMesh3 mesh = gen.Generate().MakeDMesh(); AxisAlignedBox3d bounds = mesh.CachedBounds; int numcells = 128; double cellsize = bounds.MaxDim / numcells; MeshSignedDistanceGrid levelSet = new MeshSignedDistanceGrid(mesh, cellsize); levelSet.ExactBandWidth = 3; //levelSet.InsideMode = MeshSignedDistanceGrid.InsideModes.CrossingCount; levelSet.UseParallel = true; levelSet.ComputeMode = MeshSignedDistanceGrid.ComputeModes.NarrowBandOnly; levelSet.Compute(); var iso = new DenseGridTrilinearImplicit(levelSet.Grid, levelSet.GridOrigin, levelSet.CellSize); MarchingCubes c = new MarchingCubes(); c.Implicit = iso; c.Bounds = mesh.CachedBounds; c.Bounds.Expand(c.Bounds.MaxDim * 0.1); c.CubeSize = c.Bounds.MaxDim / 128; //c.CubeSize = levelSet.CellSize; c.Generate(); TestUtil.WriteTestOutputMesh(c.Mesh, "marching_cubes_levelset.obj"); }
protected override void SolveInstance(IGH_DataAccess DA) { Grid3f_goo goo = null; List <Curve> crvs = new List <Curve>(); DA.GetData(0, ref goo); DA.GetDataList(1, crvs); DenseGridTrilinearImplicit grid = new DenseGridTrilinearImplicit(new DenseGrid3f(goo.Value.Grid), new g3.Vector3f(goo.Value.GridOrigin), goo.Value.CellSize); var pts = grid.ToRhinoPts(); int numCurves = crvs.Count; Parallel.For(0, pts.Length, i => { var pt = pts[i]; double[] distances = new double[numCurves]; for (int j = 0; j < crvs.Count; j++) { double param = 0; var crv = crvs[j]; bool res = crv.ClosestPoint(pt, out param); double tempDouble = crv.PointAt(param).DistanceTo(pt); distances[j] = tempDouble; } grid.Grid.Buffer[i] = (float)distances.Min(); }); DA.SetData(0, grid); }
void cache_input_sdfs() { gParallel.ForEach(Interval1i.Range(mesh_sources.Count), (k) => { if (cached_sdfs[k] != null) { return; } if (is_invalidated()) { return; } DMesh3 source_mesh = mesh_sources[k].GetDMeshUnsafe(); Vector3d expand = source_mesh.CachedBounds.Extents; int exact_cells = 2; MeshSignedDistanceGrid sdf = new MeshSignedDistanceGrid(source_mesh, grid_cell_size) { ExactBandWidth = exact_cells, ComputeMode = MeshSignedDistanceGrid.ComputeModes.FullGrid, ExpandBounds = expand }; sdf.CancelF = is_invalidated; sdf.Compute(); if (is_invalidated()) { return; } cached_sdfs[k] = sdf; cached_isos[k] = new DenseGridTrilinearImplicit(sdf.Grid, sdf.GridOrigin, sdf.CellSize); }); }
public static DMesh3 BooleanUnion(DMesh3 mesh, int num_cells) { // credits to geometry3sharp team double cell_size = mesh.CachedBounds.MaxDim / num_cells; MeshSignedDistanceGrid sdf = new MeshSignedDistanceGrid(mesh, cell_size); sdf.Compute(); var iso = new DenseGridTrilinearImplicit(sdf.Grid, sdf.GridOrigin, sdf.CellSize); DMesh3 outputMesh = GenerateMeshF(iso, num_cells); return(outputMesh); }
void generate_mesh(DenseGrid3f supportGrid, DenseGridTrilinearImplicit distanceField) { DenseGridTrilinearImplicit volume = new DenseGridTrilinearImplicit( supportGrid, GridOrigin, CellSize); BoundedImplicitFunction3d inputF = volume; if (SubtractMesh) { BoundedImplicitFunction3d sub = distanceField; if (SubtractMeshOffset > 0) { sub = new ImplicitOffset3d() { A = distanceField, Offset = SubtractMeshOffset } } ; ImplicitDifference3d subtract = new ImplicitDifference3d() { A = volume, B = sub }; inputF = subtract; } ImplicitHalfSpace3d cutPlane = new ImplicitHalfSpace3d() { Origin = Vector3d.Zero, Normal = Vector3d.AxisY }; ImplicitDifference3d cut = new ImplicitDifference3d() { A = inputF, B = cutPlane }; MarchingCubes mc = new MarchingCubes() { Implicit = cut, Bounds = grid_bounds, CubeSize = CellSize }; mc.Bounds.Min.y = -2 * mc.CubeSize; mc.Bounds.Min.x -= 2 * mc.CubeSize; mc.Bounds.Min.z -= 2 * mc.CubeSize; mc.Bounds.Max.x += 2 * mc.CubeSize; mc.Bounds.Max.z += 2 * mc.CubeSize; mc.Generate(); SupportMesh = mc.Mesh; } }
public static Mesh HollowOut(Mesh inMesh, double distance) { // Convert to DMesh3 var mesh = inMesh.ToDMesh3(); // Create instance of BoundedImplicitFunction3d interface int numcells = 64; double meshCellsize = mesh.CachedBounds.MaxDim / numcells; var levelSet = new MeshSignedDistanceGrid(mesh, meshCellsize) { ExactBandWidth = (int)(distance / meshCellsize) + 1 }; levelSet.Compute(); // Outer shell var implicitMesh = new DenseGridTrilinearImplicit(levelSet.Grid, levelSet.GridOrigin, levelSet.CellSize); // Offset shell var insetMesh = GenerateMeshF( new ImplicitOffset3d() { A = implicitMesh, Offset = -distance }, 128); // make sure it is a reasonable number of polygons var reducer = new Reducer(insetMesh); reducer.ReduceToTriangleCount(Math.Max(inMesh.Faces.Count / 2, insetMesh.TriangleCount / 10)); // Convert to PolygonMesh and reverse faces var interior = insetMesh.ToMesh(); interior.ReverseFaces(); // Combine the original mesh with the reversed offset resulting in hollow var combinedMesh = inMesh.Copy(CancellationToken.None); combinedMesh.CopyFaces(interior); return(combinedMesh); }
/// <summary> /// uses marching cubes to help smooth the mesh after using the remesher /// experimental /// </summary> /// <param name="edgeLength"></param> /// <param name="smoothSpeed"></param> /// <param name="iterations"></param> /// <param name="cells"></param> public void Smooth(double edgeLength, double smoothSpeed, double iterations, double cells) { //Use the Remesher class to do a basic remeshing DMesh3 mesh = new DMesh3(_mesh); Remesher r = new Remesher(mesh); r.PreventNormalFlips = true; r.SetTargetEdgeLength(edgeLength); r.SmoothSpeedT = smoothSpeed; r.SetProjectionTarget(MeshProjectionTarget.Auto(mesh)); for (int k = 0; k < iterations; k++) { r.BasicRemeshPass(); } //marching cubes int num_cells = (int)cells; if (cells > 0) { double cell_size = mesh.CachedBounds.MaxDim / num_cells; MeshSignedDistanceGrid sdf = new MeshSignedDistanceGrid(mesh, cell_size); sdf.Compute(); var iso = new DenseGridTrilinearImplicit(sdf.Grid, sdf.GridOrigin, sdf.CellSize); MarchingCubes c = new MarchingCubes(); c.Implicit = iso; c.Bounds = mesh.CachedBounds; c.CubeSize = c.Bounds.MaxDim / cells; c.Bounds.Expand(3 * c.CubeSize); c.Generate(); _smoothMesh = c.Mesh; } else { _smoothMesh = mesh; } _displayMesh = DMeshToMeshGeometry(_smoothMesh); _moldMesh = null; }
private static DMesh3 VoxelizeMesh(int numCells, DMesh3 mesh) { var cellSize = mesh.CachedBounds.MaxDim / numCells; var sdf = new MeshSignedDistanceGrid(mesh, cellSize); sdf.Compute(); var iso = new DenseGridTrilinearImplicit(sdf.Grid, sdf.GridOrigin, sdf.CellSize); var marchingCubesOperation = new MarchingCubes { Implicit = iso, Bounds = mesh.CachedBounds }; marchingCubesOperation.CubeSize = marchingCubesOperation.Bounds.MaxDim / numCells; marchingCubesOperation.Bounds.Expand(2.5 * marchingCubesOperation.CubeSize); marchingCubesOperation.Generate(); mesh = marchingCubesOperation.Mesh; return(mesh); }
public static void test_1() { DMesh3 mesh = TestUtil.LoadTestInputMesh("bunny_solid.obj"); int num_cells = 64; double cell_size = mesh.CachedBounds.MaxDim / num_cells; MeshSignedDistanceGrid sdf = new MeshSignedDistanceGrid(mesh, cell_size) { ExactBandWidth = 5 }; sdf.Compute(); var iso = new DenseGridTrilinearImplicit(sdf.Grid, sdf.GridOrigin, sdf.CellSize); var skel_field = new DistanceFieldToSkeletalField() { DistanceField = iso, FalloffDistance = 5 * cell_size }; var offset_field = new ImplicitOffset3d() { A = skel_field, Offset = DistanceFieldToSkeletalField.ZeroIsocontour }; MarchingCubesPro c = new MarchingCubesPro(); //c.Implicit = iso; //c.Implicit = skel_field; //c.IsoValue = DistanceFieldToSkeletalField.ZeroIsocontour; c.Implicit = offset_field; c.Bounds = mesh.CachedBounds; c.CubeSize = c.Bounds.MaxDim / 128; c.Bounds.Expand(3 * c.CubeSize); //c.RootMode = MarchingCubesPro.RootfindingModes.Bisection; c.ParallelCompute = false; c.Generate(); //c.GenerateContinuation(mesh.Vertices()); TestUtil.WriteTestOutputMesh(c.Mesh, "mcpro_output.obj"); }
protected override void SolveInstance(IGH_DataAccess DA) { Point3d pt = Point3d.Origin; int ni = 0; int nj = 0; int nk = 0; double cellSize = 0; DA.GetData(0, ref pt); DA.GetData(1, ref ni); DA.GetData(2, ref nj); DA.GetData(3, ref nk); DA.GetData(4, ref cellSize); DenseGrid3f grid = new DenseGrid3f(ni, nj, nk, 0); DenseGridTrilinearImplicit grd = new DenseGridTrilinearImplicit(grid, pt.ToVec3d(), cellSize); DA.SetData(0, grd); }
protected override void SolveInstance(IGH_DataAccess DA) { int num_cells = 128; DMesh3_goo dMsh_goo = null; DA.GetData(0, ref dMsh_goo); DA.GetData(1, ref num_cells); DMesh3 dMsh_copy = new DMesh3(dMsh_goo.Value); double cell_size = dMsh_copy.CachedBounds.MaxDim / num_cells; MeshSignedDistanceGrid sdf = new MeshSignedDistanceGrid(dMsh_copy, cell_size); sdf.Compute(); var iso = new DenseGridTrilinearImplicit(sdf.Grid, sdf.GridOrigin, sdf.CellSize); Grid3f_goo goo = iso; DA.SetData(0, goo); }
void fill_vertical_spans(DenseGrid3f supportGrid, DenseGridTrilinearImplicit distanceField) { int ni = supportGrid.ni, nj = supportGrid.nj, nk = supportGrid.nk; float dx = (float)CellSize; Vector3f origin = this.GridOrigin; // sweep values down, column by column for (int k = 0; k < nk; ++k) { for (int i = 0; i < ni; ++i) { bool in_support = false; for (int j = nj - 1; j >= 0; j--) { float fcur = supportGrid[i, j, k]; if (fcur >= 0) { Vector3d cell_center = get_cell_center(i, j, k); if (in_support) { bool is_inside = distanceField.Value(ref cell_center) < 0; if (is_inside) { supportGrid[i, j, k] = -3; in_support = false; } else { supportGrid[i, j, k] = -1; } } } else { in_support = true; } } } } }
/// <summary> /// compute SDF for the scan object, and then compute offset iso-contours /// </summary> void compute_offset_meshes() { int sdf_cells = 128; int mesh_cells = 128; double max_offset = inner_offset + thickness; if (max_offset > cached_sdf_max_offset) { DMesh3 meshIn = new DMesh3(MeshSource.GetIMesh(), MeshHints.IsCompact, MeshComponents.None); MeshTransforms.FromFrame(meshIn, cachedInputsTransform); // [RMS] reduce this mesh? speeds up SDF quite a bit... Reducer r = new Reducer(meshIn); r.ReduceToTriangleCount(2500); double cell_size = meshIn.CachedBounds.MaxDim / sdf_cells; int exact_cells = (int)((max_offset) / cell_size) + 1; MeshSignedDistanceGrid sdf = new MeshSignedDistanceGrid(meshIn, cell_size) { ExactBandWidth = exact_cells }; sdf.Compute(); cached_sdf = sdf; cached_sdf_max_offset = max_offset; cached_sdf_bounds = meshIn.CachedBounds; cached_inner_sdf_offset = 0; cached_outer_sdf_offset = 0; } if (cached_inner_sdf_offset != inner_offset || cached_outer_sdf_offset != max_offset) { var iso = new DenseGridTrilinearImplicit(cached_sdf.Grid, cached_sdf.GridOrigin, cached_sdf.CellSize); MarchingCubes c = new MarchingCubes() { Implicit = iso }; c.Bounds = cached_sdf_bounds; c.CubeSize = c.Bounds.MaxDim / mesh_cells; c.Bounds.Expand(max_offset + 3 * c.CubeSize); if (cached_inner_sdf_offset != inner_offset) { c.IsoValue = inner_offset; c.Generate(); InnerOffsetMesh = c.Mesh; Reducer reducer = new Reducer(InnerOffsetMesh); reducer.ReduceToEdgeLength(c.CubeSize / 2); InnerOffsetMeshSpatial = new DMeshAABBTree3(InnerOffsetMesh, true); cached_inner_sdf_offset = inner_offset; } if (cached_outer_sdf_offset != max_offset) { c.IsoValue = inner_offset + thickness; c.Generate(); OuterOffsetMesh = c.Mesh; Reducer reducer = new Reducer(OuterOffsetMesh); reducer.ReduceToEdgeLength(c.CubeSize / 2); OuterOffsetMeshSpatial = new DMeshAABBTree3(OuterOffsetMesh, true); cached_outer_sdf_offset = max_offset; } } //Util.WriteDebugMesh(MeshSource.GetIMesh(), "c:\\scratch\\__OFFESTS_orig.obj"); //Util.WriteDebugMesh(InnerOffsetMesh, "c:\\scratch\\__OFFESTS_inner.obj"); //Util.WriteDebugMesh(OuterOffsetMesh, "c:\\scratch\\__OFFESTS_outer.obj"); }
public void Initialize(MeshWindingNumberGrid mwnGrid) { WindingGrid = new DenseGridTrilinearImplicit(mwnGrid.Grid, mwnGrid.GridOrigin, mwnGrid.CellSize); WindingIsoValue = mwnGrid.WindingIsoValue; }
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); }
void process_version2(DenseGrid3f supportGrid, DenseGridTrilinearImplicit distanceField) { int ni = supportGrid.ni, nj = supportGrid.nj, nk = supportGrid.nk; float dx = (float)CellSize; Vector3f origin = this.GridOrigin; // sweep values down layer by layer DenseGrid2f prev = supportGrid.get_slice(nj - 1, 1); DenseGrid2f tmp = new DenseGrid2f(prev); Bitmap3 bmp = new Bitmap3(new Vector3i(ni, nj, nk)); for (int j = nj - 2; j >= 0; j--) { // skeletonize prev layer DenseGrid2i prev_skel = binarize(prev, 0.0f); skeletonize(prev_skel, null, 2); //dilate_loners(prev_skel, null, 2); if (j == 0) { dilate(prev_skel, null, true); dilate(prev_skel, null, true); } for (int k = 1; k < nk - 1; ++k) { for (int i = 1; i < ni - 1; ++i) { bmp[new Vector3i(i, j, k)] = (prev_skel[i, k] == 1) ? true : false; } } smooth(prev, tmp, 0.5f, 5); DenseGrid2f cur = supportGrid.get_slice(j, 1); cur.set_min(prev); for (int k = 1; k < nk - 1; ++k) { for (int i = 1; i < ni - 1; ++i) { float skelf = prev_skel[i, k] > 0 ? -1.0f : int.MaxValue; cur[i, k] = Math.Min(cur[i, k], skelf); if (cur[i, k] < 0) { Vector3d cell_center = new Vector3f(i * dx, j * dx, k * dx) + origin; if (distanceField.Value(ref cell_center) < -CellSize) { cur[i, k] = 1; } } } } for (int k = 1; k < nk - 1; ++k) { for (int i = 1; i < ni - 1; ++i) { if (is_loner(prev_skel, i, k)) { foreach (Vector2i d in gIndices.GridOffsets8) { float f = 1.0f / (float)Math.Sqrt(d.x * d.x + d.y * d.y); cur[i + d.x, k + d.y] += -0.25f * f; } } } } for (int k = 1; k < nk - 1; ++k) { for (int i = 1; i < ni - 1; ++i) { supportGrid[i, j, k] = cur[i, k]; } } prev.swap(cur); } VoxelSurfaceGenerator gen = new VoxelSurfaceGenerator() { Voxels = bmp }; gen.Generate(); Util.WriteDebugMesh(gen.Meshes[0], "c:\\scratch\\binary.obj"); }
void process_version1(DenseGrid3f supportGrid, DenseGridTrilinearImplicit distanceField) { int ni = supportGrid.ni, nj = supportGrid.nj, nk = supportGrid.nk; float dx = (float)CellSize; Vector3f origin = this.GridOrigin; // sweep values down, column by column for (int k = 0; k < nk; ++k) { for (int i = 0; i < ni; ++i) { bool in_support = false; for (int j = nj - 1; j >= 0; j--) { float fcur = supportGrid[i, j, k]; if (fcur >= 0) { Vector3d cell_center = new Vector3f(i * dx, j * dx, k * dx) + origin; if (in_support) { bool is_inside = distanceField.Value(ref cell_center) < 0; if (is_inside) { supportGrid[i, j, k] = -3; in_support = false; } else { supportGrid[i, j, k] = -1; } } } else { in_support = true; } } } } // skeletonize each layer // todo: would be nice to skeletonize the 3D volume.. ? DenseGrid3i binary = new DenseGrid3i(ni, nj, nk, 0); foreach (Vector3i idx in binary.Indices()) { binary[idx] = (supportGrid[idx] < 0) ? 1 : 0; } for (int j = 0; j < nj; ++j) { skeletonize_layer(binary, j); } // debug thing //VoxelSurfaceGenerator voxgen = new VoxelSurfaceGenerator() { // Voxels = binary.get_bitmap() //}; //voxgen.Generate(); //Util.WriteDebugMesh(voxgen.makemesh(), "c:\\scratch\\binary.obj"); // for skeleton voxels, we add some power for (int j = 0; j < nj; ++j) { for (int k = 1; k < nk - 1; ++k) { for (int i = 1; i < ni - 1; ++i) { if (binary[i, j, k] > 0) { supportGrid[i, j, k] = -3; } //else // supportGrid[i, j, k] = 1; // clear non-skeleton voxels } } } // power up the ground-plane voxels for (int k = 0; k < nk; ++k) { for (int i = 0; i < ni; ++i) { if (supportGrid[i, 0, k] < 0) { supportGrid[i, 0, k] = -5; } } } #if true DenseGrid3f smoothed = new DenseGrid3f(supportGrid); float nbr_weight = 0.5f; for (int iter = 0; iter < 15; ++iter) { // add some mass to skeleton voxels for (int j = 0; j < nj; ++j) { for (int k = 1; k < nk - 1; ++k) { for (int i = 1; i < ni - 1; ++i) { if (binary[i, j, k] > 0) { supportGrid[i, j, k] = supportGrid[i, j, k] - nbr_weight / 25.0f; } } } } for (int j = 0; j < nj; ++j) { for (int k = 1; k < nk - 1; ++k) { for (int i = 1; i < ni - 1; ++i) { int neg = 0; float avg = 0, w = 0; for (int n = 0; n < 8; ++n) { int xi = i + gIndices.GridOffsets8[n].x; int zi = k + gIndices.GridOffsets8[n].y; float f = supportGrid[xi, j, zi]; if (f < 0) { neg++; } avg += nbr_weight * f; w += nbr_weight; } if (neg > -1) { avg += supportGrid[i, j, k]; w += 1.0f; smoothed[i, j, k] = avg / w; } else { smoothed[i, j, k] = supportGrid[i, j, k]; } } } } supportGrid.swap(smoothed); } #endif // hard-enforce that skeleton voxels stay inside //for (int j = 0; j < nj; ++j) { // for (int k = 1; k < nk - 1; ++k) { // for (int i = 1; i < ni - 1; ++i) { // if (binary[i, j, k] > 0) // supportGrid[i, j, k] = Math.Min(supportGrid[i, j, k], - 1); // } // } //} }
void generate_support(Vector3f origin, float dx, int ni, int nj, int nk, DenseGrid3f supportGrid) { supportGrid.resize(ni, nj, nk); supportGrid.assign(1); // sentinel if (DebugPrint) { System.Console.WriteLine("start"); } bool CHECKERBOARD = false; System.Console.WriteLine("Computing SDF"); // compute unsigned SDF MeshSignedDistanceGrid sdf = new MeshSignedDistanceGrid(Mesh, CellSize) { ComputeSigns = true, ExactBandWidth = 3, /*,ComputeMode = MeshSignedDistanceGrid.ComputeModes.FullGrid*/ }; sdf.CancelF = Cancelled; sdf.Compute(); if (Cancelled()) { return; } var distanceField = new DenseGridTrilinearImplicit(sdf.Grid, sdf.GridOrigin, sdf.CellSize); double angle = MathUtil.Clamp(OverhangAngleDeg, 0.01, 89.99); double cos_thresh = Math.Cos(angle * MathUtil.Deg2Rad); System.Console.WriteLine("Marking overhangs"); // Compute narrow-band distances. For each triangle, we find its grid-coord-bbox, // and compute exact distances within that box. The intersection_count grid // is also filled in this computation double ddx = (double)dx; double ox = (double)origin[0], oy = (double)origin[1], oz = (double)origin[2]; Vector3d va = Vector3d.Zero, vb = Vector3d.Zero, vc = Vector3d.Zero; foreach (int tid in Mesh.TriangleIndices()) { if (tid % 100 == 0 && Cancelled()) { break; } Mesh.GetTriVertices(tid, ref va, ref vb, ref vc); Vector3d normal = MathUtil.Normal(ref va, ref vb, ref vc); if (normal.Dot(-Vector3d.AxisY) < cos_thresh) { continue; } // real ijk coordinates of va/vb/vc double fip = (va[0] - ox) / ddx, fjp = (va[1] - oy) / ddx, fkp = (va[2] - oz) / ddx; double fiq = (vb[0] - ox) / ddx, fjq = (vb[1] - oy) / ddx, fkq = (vb[2] - oz) / ddx; double fir = (vc[0] - ox) / ddx, fjr = (vc[1] - oy) / ddx, fkr = (vc[2] - oz) / ddx; // clamped integer bounding box of triangle plus exact-band int exact_band = 0; int i0 = MathUtil.Clamp(((int)MathUtil.Min(fip, fiq, fir)) - exact_band, 0, ni - 1); int i1 = MathUtil.Clamp(((int)MathUtil.Max(fip, fiq, fir)) + exact_band + 1, 0, ni - 1); int j0 = MathUtil.Clamp(((int)MathUtil.Min(fjp, fjq, fjr)) - exact_band, 0, nj - 1); int j1 = MathUtil.Clamp(((int)MathUtil.Max(fjp, fjq, fjr)) + exact_band + 1, 0, nj - 1); int k0 = MathUtil.Clamp(((int)MathUtil.Min(fkp, fkq, fkr)) - exact_band, 0, nk - 1); int k1 = MathUtil.Clamp(((int)MathUtil.Max(fkp, fkq, fkr)) + exact_band + 1, 0, nk - 1); // don't put into y=0 plane if (j0 == 0) { j0 = 1; } // compute distance for each tri inside this bounding box // note: this can be very conservative if the triangle is large and on diagonal to grid axes for (int k = k0; k <= k1; ++k) { for (int j = j0; j <= j1; ++j) { for (int i = i0; i <= i1; ++i) { Vector3d gx = new Vector3d((float)i * dx + origin[0], (float)j * dx + origin[1], (float)k * dx + origin[2]); float d = (float)MeshSignedDistanceGrid.point_triangle_distance(ref gx, ref va, ref vb, ref vc); // vertical checkerboard pattern (eg 'tips') if (CHECKERBOARD) { int zz = (k % 2 == 0) ? 1 : 0; if (i % 2 == zz) { continue; } } if (d < dx / 2) { if (j > 1) { supportGrid[i, j, k] = SUPPORT_TIP_TOP; supportGrid[i, j - 1, k] = SUPPORT_TIP_BASE; } else { supportGrid[i, j, k] = SUPPORT_TIP_BASE; } } } } } } if (Cancelled()) { return; } //process_version1(supportGrid, distanceField); //process_version2(supportGrid, distanceField); generate_graph(supportGrid, distanceField); //Util.WriteDebugMesh(MakeDebugGraphMesh(), "c:\\scratch\\__LAST_GRAPH_INIT.obj"); postprocess_graph(); //Util.WriteDebugMesh(MakeDebugGraphMesh(), "c:\\scratch\\__LAST_GRAPH_OPT.obj"); }
void generate_graph(DenseGrid3f supportGrid, DenseGridTrilinearImplicit distanceField) { int ni = supportGrid.ni, nj = supportGrid.nj, nk = supportGrid.nk; float dx = (float)CellSize; Vector3f origin = this.GridOrigin; // parameters for initializing cost grid float MODEL_SPACE = 0.01f; // needs small positive so that points on triangles count as inside (eg on ground plane) //float MODEL_SPACE = 2.0f*(float)CellSize; float CRAZY_DISTANCE = 99999.0f; bool UNIFORM_DISTANCE = true; float MAX_DIST = 10 * (float)CellSize; // parameters for sorting seeds Vector3i center_idx = new Vector3i(ni / 2, 0, nk / 2); // middle //Vector3i center_idx = new Vector3i(0, 0, 0); // corner bool reverse_per_layer = true; DenseGrid3f costGrid = new DenseGrid3f(supportGrid); foreach (Vector3i ijk in costGrid.Indices()) { Vector3d cell_center = new Vector3f(ijk.x * dx, ijk.y * dx, ijk.z * dx) + origin; float f = (float)distanceField.Value(ref cell_center); if (f <= MODEL_SPACE) { f = CRAZY_DISTANCE; } else if (UNIFORM_DISTANCE) { f = 1.0f; } else if (f > MAX_DIST) { f = MAX_DIST; } costGrid[ijk] = f; } // Find seeds on each layer, sort, and add to accumulated bottom-up seeds list. // This sorting has an *enormous* effect on the support generation. List <Vector3i> seeds = new List <Vector3i>(); List <Vector3i> layer_seeds = new List <Vector3i>(); for (int j = 0; j < nj; ++j) { layer_seeds.Clear(); for (int k = 0; k < nk; ++k) { for (int i = 0; i < ni; ++i) { if (supportGrid[i, j, k] == SUPPORT_TIP_BASE) { layer_seeds.Add(new Vector3i(i, j, k)); } } } layer_seeds.Sort((a, b) => { Vector3i pa = a; pa.y = 0; Vector3i pb = b; pb.y = 0; int sa = (pa - center_idx).LengthSquared, sb = (pb - center_idx).LengthSquared; return(sa.CompareTo(sb)); }); // reversing sort order is intresting? if (reverse_per_layer) { layer_seeds.Reverse(); } seeds.AddRange(layer_seeds); } HashSet <Vector3i> seed_indices = new HashSet <Vector3i>(seeds); // gives very different results... if (ProcessBottomUp == false) { seeds.Reverse(); } // for linear index a, is this a node we allow in graph? (ie graph bounds) Func <int, bool> node_filter_f = (a) => { Vector3i ai = costGrid.to_index(a); // why not y check?? return(ai.x > 0 && ai.z > 0 && ai.x != ni - 1 && ai.y != nj - 1 && ai.z != nk - 1); }; // distance from linear index a to linear index b // this defines the cost field we want to find shortest path through Func <int, int, float> node_dist_f = (a, b) => { Vector3i ai = costGrid.to_index(a), bi = costGrid.to_index(b); if (bi.y >= ai.y) // b.y should always be a.y-1 { return(float.MaxValue); } float sg = supportGrid[bi]; // don't connect to tips //if (sg == SUPPORT_TIP_BASE || sg == SUPPORT_TIP_TOP) // return float.MaxValue; if (sg == SUPPORT_TIP_TOP) { return(float.MaxValue); } if (sg < 0) { return(-999999); // if b is already used, we will terminate there, so this is a good choice } // otherwise cost is sqr-grid-distance + costGrid value (which is basically distance to surface) float c = costGrid[b]; float f = (float)(Math.Sqrt((bi - ai).LengthSquared) * CellSize); //float f = 0; return(c + f); }; // which linear-index nbrs to consider for linear index a Func <int, IEnumerable <int> > neighbour_f = (a) => { Vector3i ai = costGrid.to_index(a); return(down_neighbours(ai, costGrid)); }; // when do we terminate Func <int, bool> terminate_f = (a) => { Vector3i ai = costGrid.to_index(a); // terminate if we hit existing support path if (seed_indices.Contains(ai) == false && supportGrid[ai] < 0) { return(true); } // terminate if we hit ground plane if (ai.y == 0) { return(true); } return(false); }; DijkstraGraphDistance dijkstra = new DijkstraGraphDistance(ni * nj * nk, false, node_filter_f, node_dist_f, neighbour_f); dijkstra.TrackOrder = true; List <int> path = new List <int>(); Graph = new DGraph3(); Dictionary <Vector3i, int> CellToGraph = new Dictionary <Vector3i, int>(); TipVertices = new HashSet <int>(); TipBaseVertices = new HashSet <int>(); GroundVertices = new HashSet <int>(); // seeds are tip-base points for (int k = 0; k < seeds.Count; ++k) { // add seed point (which is a tip-base vertex) as seed for dijkstra prop int seed = costGrid.to_linear(seeds[k]); dijkstra.Reset(); dijkstra.AddSeed(seed, 0); // compute to termination (ground, existing node, etc) int base_node = dijkstra.ComputeToNode(terminate_f); if (base_node < 0) { base_node = dijkstra.GetOrder().Last(); } // extract the path path.Clear(); dijkstra.GetPathToSeed(base_node, path); int N = path.Count; // first point on path is termination point. // create vertex for it if we have not yet Vector3i basept_idx = supportGrid.to_index(path[0]); int basept_vid; if (CellToGraph.TryGetValue(basept_idx, out basept_vid) == false) { Vector3d curv = get_cell_center(basept_idx); if (basept_idx.y == 0) { curv.y = 0; } basept_vid = Graph.AppendVertex(curv); if (basept_idx.y == 0) { GroundVertices.Add(basept_vid); } CellToGraph[basept_idx] = basept_vid; } int cur_vid = basept_vid; // now walk up path and create vertices as necessary for (int i = 0; i < N; ++i) { int idx = path[i]; if (supportGrid[idx] >= 0) { supportGrid[idx] = SUPPORT_GRID_USED; } if (i > 0) { Vector3i next_idx = supportGrid.to_index(path[i]); int next_vid; if (CellToGraph.TryGetValue(next_idx, out next_vid) == false) { Vector3d nextv = get_cell_center(next_idx); next_vid = Graph.AppendVertex(nextv); CellToGraph[next_idx] = next_vid; } Graph.AppendEdge(cur_vid, next_vid); cur_vid = next_vid; } } // seed was tip-base so we should always get back there. Then we // explicitly add tip-top and edge to it. if (supportGrid[path[N - 1]] == SUPPORT_TIP_BASE) { Vector3i vec_idx = supportGrid.to_index(path[N - 1]); TipBaseVertices.Add(CellToGraph[vec_idx]); Vector3i tip_idx = vec_idx + Vector3i.AxisY; int tip_vid; if (CellToGraph.TryGetValue(tip_idx, out tip_vid) == false) { Vector3d tipv = get_cell_center(tip_idx); tip_vid = Graph.AppendVertex(tipv); CellToGraph[tip_idx] = tip_vid; Graph.AppendEdge(cur_vid, tip_vid); TipVertices.Add(tip_vid); } } } /* * Snap tips to surface */ gParallel.ForEach(TipVertices, (tip_vid) => { bool snapped = false; Vector3d v = Graph.GetVertex(tip_vid); Frame3f hitF; // try shooting ray straight up. if that hits, and point is close, we use it if (MeshQueries.RayHitPointFrame(Mesh, MeshSpatial, new Ray3d(v, Vector3d.AxisY), out hitF)) { if (v.Distance(hitF.Origin) < 2 * CellSize) { v = hitF.Origin; snapped = true; } } // if that failed, try straight down if (!snapped) { if (MeshQueries.RayHitPointFrame(Mesh, MeshSpatial, new Ray3d(v, -Vector3d.AxisY), out hitF)) { if (v.Distance(hitF.Origin) < CellSize) { v = hitF.Origin; snapped = true; } } } // if it missed, or hit pt was too far, find nearest point and try that if (!snapped) { hitF = MeshQueries.NearestPointFrame(Mesh, MeshSpatial, v); if (v.Distance(hitF.Origin) < 2 * CellSize) { v = hitF.Origin; snapped = true; } // can this ever fail? tips should always be within 2 cells... } if (snapped) { Graph.SetVertex(tip_vid, v); } }); }
public static void test_marching_cubes_topology() { AxisAlignedBox3d bounds = new AxisAlignedBox3d(1.0); int numcells = 64; double cellsize = bounds.MaxDim / numcells; Random r = new Random(31337); for (int ii = 0; ii < 100; ++ii) { DenseGrid3f grid = new DenseGrid3f(); grid.resize(numcells, numcells, numcells); grid.assign(1); for (int k = 2; k < numcells - 3; k++) { for (int j = 2; j < numcells - 3; j++) { for (int i = 2; i < numcells - 3; i++) { double d = r.NextDouble(); if (d > 0.9) { grid[i, j, k] = 0.0f; } else if (d > 0.5) { grid[i, j, k] = 1.0f; } else { grid[i, j, k] = -1.0f; } } } } var iso = new DenseGridTrilinearImplicit(grid, Vector3f.Zero, cellsize); MarchingCubes c = new MarchingCubes(); c.Implicit = iso; c.Bounds = bounds; //c.Bounds.Max += 3 * cellsize * Vector3d.One; //c.Bounds.Expand(2*cellsize); // this produces holes c.CubeSize = cellsize * 4.1; //c.CubeSize = cellsize * 2; //c.Bounds = new AxisAlignedBox3d(2.0); c.Generate(); for (float f = 2.0f; f < 8.0f; f += 0.13107f) { c.CubeSize = cellsize * 4.1; c.Generate(); c.Mesh.CheckValidity(false); MeshBoundaryLoops loops = new MeshBoundaryLoops(c.Mesh); if (loops.Count > 0) { throw new Exception("found loops!"); } } } //c.Mesh.CheckValidity(false); //TestUtil.WriteTestOutputMesh(c.Mesh, "marching_cubes_topotest.obj"); }
public static void test_marching_cubes_implicits() { DMesh3 mesh = TestUtil.LoadTestInputMesh("bunny_solid.obj"); MeshTransforms.Translate(mesh, -mesh.CachedBounds.Center); double meshCellsize = mesh.CachedBounds.MaxDim / 32; MeshSignedDistanceGrid levelSet = new MeshSignedDistanceGrid(mesh, meshCellsize); levelSet.ExactBandWidth = 3; levelSet.UseParallel = true; levelSet.ComputeMode = MeshSignedDistanceGrid.ComputeModes.NarrowBandOnly; levelSet.Compute(); var meshIso = new DenseGridTrilinearImplicit(levelSet.Grid, levelSet.GridOrigin, levelSet.CellSize); ImplicitOffset3d offsetMeshIso = new ImplicitOffset3d() { A = meshIso, Offset = 2.0 }; double r = 15.0; ImplicitSphere3d sphere1 = new ImplicitSphere3d() { Origin = Vector3d.Zero, Radius = r }; ImplicitSphere3d sphere2 = new ImplicitSphere3d() { Origin = r * Vector3d.AxisX, Radius = r }; ImplicitAxisAlignedBox3d aabox1 = new ImplicitAxisAlignedBox3d() { AABox = new AxisAlignedBox3d(r * 0.5 * Vector3d.One, r, r * 0.75, r * 0.5) }; ImplicitBox3d box1 = new ImplicitBox3d() { Box = new Box3d(new Frame3f(r * 0.5 * Vector3d.One, Vector3d.One.Normalized), new Vector3d(r, r * 0.75, r * 0.5)) }; ImplicitLine3d line1 = new ImplicitLine3d() { Segment = new Segment3d(Vector3d.Zero, r * Vector3d.One), Radius = 3.0 }; ImplicitHalfSpace3d half1 = new ImplicitHalfSpace3d() { Origin = Vector3d.Zero, Normal = Vector3d.One.Normalized }; ImplicitUnion3d union = new ImplicitUnion3d() { A = sphere1, B = line1 }; ImplicitDifference3d difference = new ImplicitDifference3d() { A = meshIso, B = aabox1 }; ImplicitIntersection3d intersect = new ImplicitIntersection3d() { A = meshIso, B = half1 }; ImplicitNaryUnion3d nunion = new ImplicitNaryUnion3d() { Children = new List <BoundedImplicitFunction3d>() { offsetMeshIso, sphere1, sphere2 } }; ImplicitNaryDifference3d ndifference = new ImplicitNaryDifference3d() { A = offsetMeshIso, BSet = new List <BoundedImplicitFunction3d>() { sphere1, sphere2 } }; ImplicitBlend3d blend = new ImplicitBlend3d() { A = sphere1, B = sphere2 }; BoundedImplicitFunction3d root = intersect; AxisAlignedBox3d bounds = root.Bounds(); int numcells = 64; MarchingCubes c = new MarchingCubes(); c.RootMode = MarchingCubes.RootfindingModes.LerpSteps; c.RootModeSteps = 5; c.Implicit = root; c.Bounds = bounds; c.CubeSize = bounds.MaxDim / numcells; c.Bounds.Expand(3 * c.CubeSize); c.Generate(); MeshNormals.QuickCompute(c.Mesh); TestUtil.WriteTestOutputMesh(c.Mesh, "marching_cubes_implicit.obj"); }
protected virtual void compute_shell_distancefield() { if (cached_is_closed == false) { compute_shell_distancefield_unsigned(); return; } double offset_distance = shell_thickness; Interval1d shell_range = new Interval1d(0, offset_distance); if (shell_direction == ShellDirections.Symmetric) { shell_range = new Interval1d(-offset_distance / 2, offset_distance / 2); } else if (shell_direction == ShellDirections.Inner) { shell_range = new Interval1d(-offset_distance, 0); offset_distance = -offset_distance; } if (cached_sdf == null || shell_thickness > cached_sdf_max_offset || grid_cell_size != cached_sdf.CellSize) { DMesh3 meshIn = MeshSource.GetDMeshUnsafe(); int exact_cells = (int)((shell_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 = shell_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 = shell_thickness; cached_sdf_bounds = meshIn.CachedBounds; } var iso = new DenseGridTrilinearImplicit(cached_sdf.Grid, cached_sdf.GridOrigin, cached_sdf.CellSize); BoundedImplicitFunction3d shell_field = (shell_direction == ShellDirections.Symmetric) ? (BoundedImplicitFunction3d) new ImplicitShell3d() { A = iso, Inside = shell_range } : (BoundedImplicitFunction3d) new ImplicitOffset3d() { A = iso, Offset = offset_distance }; //var shell_field = new ImplicitShell3d() { A = iso, Inside = shell_range }; //BoundedImplicitFunction3d shell_field = (signed_field) ? // (BoundedImplicitFunction3d)new ImplicitShell3d() { A = iso, Inside = shell_range } : // (BoundedImplicitFunction3d)new ImplicitOffset3d() { A = iso, Offset = offset_distance }; //ImplicitOffset3d offset = new ImplicitOffset3d() { A = iso, Offset = offset_distance }; MarchingCubes c = new MarchingCubes(); c.Implicit = shell_field; 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; } if (min_component_volume > 0) { MeshEditor.RemoveSmallComponents(c.Mesh, min_component_volume, min_component_volume); } if (is_invalidated()) { return; } if (shell_surface_only) { if (shell_direction == ShellDirections.Inner || shell_direction == ShellDirections.Outer) { c.Mesh.AttachMetadata("is_partial", new object()); } } ResultMesh = c.Mesh; }
protected virtual void compute_shell_distancefield_unsigned() { double offset_distance = shell_thickness * 0.5; if (cached_sdf == null || offset_distance > cached_sdf_max_offset || grid_cell_size != cached_sdf.CellSize) { DMesh3 meshIn = MeshSource.GetDMeshUnsafe(); int exact_cells = (int)((offset_distance) / grid_cell_size) + 1; MeshSignedDistanceGrid sdf = new MeshSignedDistanceGrid(meshIn, grid_cell_size) { ExactBandWidth = exact_cells, ComputeSigns = false }; sdf.CancelF = is_invalidated; sdf.Compute(); if (is_invalidated()) { return; } cached_sdf = sdf; cached_sdf_max_offset = offset_distance; cached_sdf_bounds = meshIn.CachedBounds; } var iso = new DenseGridTrilinearImplicit(cached_sdf.Grid, cached_sdf.GridOrigin, cached_sdf.CellSize); MarchingCubes c = new MarchingCubes(); c.Implicit = iso; c.IsoValue = offset_distance; 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; } if (min_component_volume > 0) { MeshEditor.RemoveSmallComponents(c.Mesh, min_component_volume, min_component_volume); } if (is_invalidated()) { return; } ResultMesh = c.Mesh; }
void generate_support(Vector3f origin, float dx, int ni, int nj, int nk, DenseGrid3f supportGrid) { supportGrid.resize(ni, nj, nk); supportGrid.assign(1); // sentinel bool CHECKERBOARD = false; // compute unsigned SDF int exact_band = 1; if (SubtractMesh && SubtractMeshOffset > 0) { int offset_band = (int)(SubtractMeshOffset / CellSize) + 1; exact_band = Math.Max(exact_band, offset_band); } sdf = new MeshSignedDistanceGrid(Mesh, CellSize) { ComputeSigns = true, ExactBandWidth = exact_band }; sdf.CancelF = this.CancelF; sdf.Compute(); if (CancelF()) { return; } var distanceField = new DenseGridTrilinearImplicit(sdf.Grid, sdf.GridOrigin, sdf.CellSize); double angle = MathUtil.Clamp(OverhangAngleDeg, 0.01, 89.99); double cos_thresh = Math.Cos(angle * MathUtil.Deg2Rad); // Compute narrow-band distances. For each triangle, we find its grid-coord-bbox, // and compute exact distances within that box. The intersection_count grid // is also filled in this computation double ddx = (double)dx; double ox = (double)origin[0], oy = (double)origin[1], oz = (double)origin[2]; Vector3d va = Vector3d.Zero, vb = Vector3d.Zero, vc = Vector3d.Zero; foreach (int tid in Mesh.TriangleIndices()) { if (tid % 100 == 0 && CancelF()) { break; } Mesh.GetTriVertices(tid, ref va, ref vb, ref vc); Vector3d normal = MathUtil.Normal(ref va, ref vb, ref vc); if (normal.Dot(-Vector3d.AxisY) < cos_thresh) { continue; } // real ijk coordinates of va/vb/vc double fip = (va[0] - ox) / ddx, fjp = (va[1] - oy) / ddx, fkp = (va[2] - oz) / ddx; double fiq = (vb[0] - ox) / ddx, fjq = (vb[1] - oy) / ddx, fkq = (vb[2] - oz) / ddx; double fir = (vc[0] - ox) / ddx, fjr = (vc[1] - oy) / ddx, fkr = (vc[2] - oz) / ddx; // clamped integer bounding box of triangle plus exact-band int extra_band = 0; int i0 = MathUtil.Clamp(((int)MathUtil.Min(fip, fiq, fir)) - extra_band, 0, ni - 1); int i1 = MathUtil.Clamp(((int)MathUtil.Max(fip, fiq, fir)) + extra_band + 1, 0, ni - 1); int j0 = MathUtil.Clamp(((int)MathUtil.Min(fjp, fjq, fjr)) - extra_band, 0, nj - 1); int j1 = MathUtil.Clamp(((int)MathUtil.Max(fjp, fjq, fjr)) + extra_band + 1, 0, nj - 1); int k0 = MathUtil.Clamp(((int)MathUtil.Min(fkp, fkq, fkr)) - extra_band, 0, nk - 1); int k1 = MathUtil.Clamp(((int)MathUtil.Max(fkp, fkq, fkr)) + extra_band + 1, 0, nk - 1); // don't put into y=0 plane //if (j0 == 0) // j0 = 1; // compute distance for each tri inside this bounding box // note: this can be very conservative if the triangle is large and on diagonal to grid axes for (int k = k0; k <= k1; ++k) { for (int j = j0; j <= j1; ++j) { for (int i = i0; i <= i1; ++i) { Vector3d gx = new Vector3d((float)i * dx + origin[0], (float)j * dx + origin[1], (float)k * dx + origin[2]); float d = (float)MeshSignedDistanceGrid.point_triangle_distance(ref gx, ref va, ref vb, ref vc); // vertical checkerboard pattern (eg 'tips') if (CHECKERBOARD) { int zz = (k % 2 == 0) ? 1 : 0; if (i % 2 == zz) { continue; } } if (d < dx / 2) { supportGrid[i, j, k] = SUPPORT_TIP_TOP; } } } } } if (CancelF()) { return; } fill_vertical_spans(supportGrid, distanceField); generate_mesh(supportGrid, distanceField); }
protected virtual void update_level_set() { double unsigned_offset = Math.Abs(offset_distance); if (cached_sdf == null || unsigned_offset > cached_sdf_max_offset || grid_cell_size != cached_sdf.CellSize) { DMesh3 meshIn = MeshSource.GetDMeshUnsafe(); int exact_cells = (int)(unsigned_offset / 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 = unsigned_offset + 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 = unsigned_offset; cached_sdf_bounds = meshIn.CachedBounds; } var iso = new DenseGridTrilinearImplicit(cached_sdf.Grid, cached_sdf.GridOrigin, cached_sdf.CellSize); MarchingCubes c = new MarchingCubes(); c.Implicit = iso; c.IsoValue = offset_distance; 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; } if (min_component_volume > 0) { MeshEditor.RemoveSmallComponents(c.Mesh, min_component_volume, min_component_volume); } if (is_invalidated()) { return; } ResultMesh = c.Mesh; }
protected virtual DMesh3 compute_blend_bounded() { bool profile = true; LocalProfiler p = null; if (profile) { p = new LocalProfiler(); p.Start("sdf"); } cache_input_sdfs_bounded(); if (is_invalidated()) { return(null); } if (profile) { p.Stop("sdf"); p.Start("mc"); } List <BoundedImplicitFunction3d> inputs = new List <BoundedImplicitFunction3d>(); foreach (var sdf in cached_bounded_sdfs) { var dist_field = new DenseGridTrilinearImplicit(sdf); var skel_field = new DistanceFieldToSkeletalField() { DistanceField = dist_field, FalloffDistance = blend_falloff }; inputs.Add(skel_field); } SkeletalRicciNaryBlend3d blend = new SkeletalRicciNaryBlend3d() { Children = inputs, BlendPower = this.blend_power }; MarchingCubesPro c = new MarchingCubesPro(); c.Implicit = blend; c.IsoValue = DistanceFieldToSkeletalField.ZeroIsocontour; c.Bounds = blend.Bounds(); c.CubeSize = mesh_cell_size; c.Bounds.Expand(3 * c.CubeSize); c.RootMode = MarchingCubesPro.RootfindingModes.LerpSteps; c.RootModeSteps = 3; c.CancelF = is_invalidated; //c.Generate(); c.GenerateContinuation(input_mesh_seeds()); if (is_invalidated()) { return(null); } if (profile) { p.Stop("mc"); p.Start("reduce"); } c.Mesh.ReverseOrientation(); 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); } if (profile) { p.Stop("reduce"); #if G3_USING_UNITY UnityEngine.Debug.Log("BLEND TIME: " + p.AllTimes()); #endif } return(c.Mesh); }
public void Initialize(MeshScalarSamplingGrid scalarGrid) { WindingGrid = new DenseGridTrilinearImplicit(scalarGrid.Grid, scalarGrid.GridOrigin, scalarGrid.CellSize); IsoValue = scalarGrid.IsoValue; }
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; }