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 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"); }
public static BoundedImplicitFunction3d MeshToImplicitF(DMesh3 mesh, int num_cells, double offset) { double meshCellsize = mesh.CachedBounds.MaxDim / num_cells; MeshSignedDistanceGrid levelSet = new MeshSignedDistanceGrid(mesh, meshCellsize); levelSet.ExactBandWidth = (int)(offset / meshCellsize) + 1; levelSet.Compute(); return(new DenseGridTrilinearImplicit(levelSet.Grid, levelSet.GridOrigin, levelSet.CellSize)); }
public static void test_voxel_surface() { //DMesh3 mesh = TestUtil.LoadTestInputMesh("bunny_solid.obj"); DMesh3 mesh = TestUtil.LoadTestInputMesh("holey_bunny_2.obj"); DMeshAABBTree3 spatial = new DMeshAABBTree3(mesh, autoBuild: true); AxisAlignedBox3d bounds = mesh.CachedBounds; int numcells = 64; double cellsize = bounds.MaxDim / numcells; MeshSignedDistanceGrid levelSet = new MeshSignedDistanceGrid(mesh, cellsize); levelSet.UseParallel = true; levelSet.Compute(); Bitmap3 bmp = new Bitmap3(levelSet.Dimensions); foreach (Vector3i idx in bmp.Indices()) { float f = levelSet[idx.x, idx.y, idx.z]; bmp.Set(idx, (f < 0) ? true : false); } //AxisAlignedBox3d bounds = mesh.CachedBounds; //int numcells = 32; //double cellsize = bounds.MaxDim / numcells; //ShiftGridIndexer3 indexer = new ShiftGridIndexer3(bounds.Min-2*cellsize, cellsize); //Bitmap3 bmp = new Bitmap3(new Vector3i(numcells, numcells, numcells)); //foreach (Vector3i idx in bmp.Indices()) { // Vector3d v = indexer.FromGrid(idx); // bmp.Set(idx, spatial.IsInside(v)); //} //spatial.WindingNumber(Vector3d.Zero); //Bitmap3 bmp = new Bitmap3(new Vector3i(numcells+3, numcells+3, numcells+3)); //gParallel.ForEach(bmp.Indices(), (idx) => { // Vector3d v = indexer.FromGrid(idx); // bmp.SafeSet(idx, spatial.WindingNumber(v) > 0.8); //}); VoxelSurfaceGenerator voxGen = new VoxelSurfaceGenerator(); voxGen.Voxels = bmp; voxGen.ColorSourceF = (idx) => { return(new Colorf((float)idx.x, (float)idx.y, (float)idx.z) * (1.0f / numcells)); }; voxGen.Generate(); DMesh3 voxMesh = voxGen.Meshes[0]; Util.WriteDebugMesh(voxMesh, "c:\\scratch\\temp.obj"); TestUtil.WriteTestOutputMesh(voxMesh, "voxel_surf.obj", true, true); }
void cache_input_sdfs_bounded() { if (cached_bounded_sdfs == null) { cached_bounded_sdfs = new MeshSignedDistanceGrid[mesh_sources.Count]; cached_bounded_sdf_maxdist = new double[mesh_sources.Count]; } cache_bvtrees(false); double falloff_distance = blend_falloff; gParallel.ForEach(Interval1i.Range(mesh_sources.Count), (k) => { if (falloff_distance > cached_bounded_sdf_maxdist[k]) { cached_bounded_sdfs[k] = null; } // [TODO] we could expand via flood-fill here instead of throwing away all previously computed! if (cached_bounded_sdfs[k] != null) { return; } if (is_invalidated()) { return; } int exact_cells = (int)(falloff_distance / grid_cell_size) + 2; DMesh3 source_mesh = mesh_sources[k].GetDMeshUnsafe(); DMeshAABBTree3 use_spatial = GenerateClosedMeshOp.MeshSDFShouldUseSpatial( cached_bvtrees[k], exact_cells, grid_cell_size, source_edge_stats[k].z); MeshSignedDistanceGrid sdf = new MeshSignedDistanceGrid(source_mesh, grid_cell_size, use_spatial) { ExactBandWidth = exact_cells }; if (use_spatial != null) { sdf.NarrowBandMaxDistance = falloff_distance + grid_cell_size; sdf.ComputeMode = MeshSignedDistanceGrid.ComputeModes.NarrowBand_SpatialFloodFill; } sdf.CancelF = is_invalidated; sdf.Compute(); if (is_invalidated()) { return; } cached_bounded_sdfs[k] = sdf; cached_bounded_sdf_maxdist[k] = falloff_distance; }); }
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); }
public static void test_levelset_basic() { //DMesh3 mesh = TestUtil.MakeCappedCylinder(false); //MeshTransforms.Scale(mesh, 1, 3, 1); DMesh3 mesh = TestUtil.LoadTestMesh(Program.TEST_FILES_PATH + "bunny_open_base.obj"); AxisAlignedBox3d bounds = mesh.CachedBounds; float cellSize = (float)bounds.MaxDim / 32.0f; MeshSignedDistanceGrid levelSet = new MeshSignedDistanceGrid(mesh, cellSize); levelSet.Compute(); Vector3i dims = levelSet.Dimensions; int midx = dims.x / 2; int midy = dims.y / 2; int midz = dims.z / 2; //for ( int xi = 0; xi < dims.x; ++xi ) { // System.Console.Write(levelSet[xi, yi, zi] + " "); //} for (int yi = 0; yi < dims.y; ++yi) { System.Console.Write(levelSet[midx, yi, midz] + " "); } System.Console.WriteLine(); DMesh3 tmp = new DMesh3(); MeshEditor editor = new MeshEditor(tmp); for (int x = 0; x < dims.x; ++x) { for (int y = 0; y < dims.y; ++y) { for (int z = 0; z < dims.z; ++z) { if (levelSet[x, y, z] < 0) { Vector3f c = levelSet.CellCenter(x, y, z); editor.AppendBox(new Frame3f(c), cellSize); } } } } TestUtil.WriteTestOutputMesh(tmp, "LevelSetInterior.obj"); TestUtil.WriteTestOutputMesh(mesh, "LevelSetInterior_src.obj"); }
public void SetSources(List <DMeshSourceOp> sources) { if (mesh_sources != null) { throw new Exception("todo: handle changing sources!"); } mesh_sources = new List <DMeshSourceOp>(sources); foreach (var source in mesh_sources) { source.OperatorModified += on_input_modified; } cached_sdfs = new MeshSignedDistanceGrid[sources.Count]; cached_isos = new BoundedImplicitFunction3d[sources.Count]; invalidate(); }
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"); }
public void SetSources(List <DMeshSourceOp> sources) { if (mesh_sources != null) { throw new Exception("MeshVoxelBlendOp.SetSources: handle changing sources!"); } //if (sources.Count != 2) // throw new Exception("MeshVoxelBlendOp.SetSources: only two sources supported!"); mesh_sources = new List <DMeshSourceOp>(sources); foreach (var source in mesh_sources) { source.OperatorModified += on_input_modified; } cached_sdfs = new MeshSignedDistanceGrid[sources.Count]; cached_isos = new BoundedImplicitFunction3d[sources.Count]; cached_bvtrees = new DMeshAABBTreePro[sources.Count]; invalidate(); }
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); }
public static Bitmap3 createVoxelizedRepresentation(String objPath) { DMesh3 mesh = StandardMeshReader.ReadMesh(objPath); int num_cells = 128; double cell_size = mesh.CachedBounds.MaxDim / num_cells; MeshSignedDistanceGrid sdf = new MeshSignedDistanceGrid(mesh, cell_size); sdf.Compute(); //** voxels**// Bitmap3 bmp = new Bitmap3(sdf.Dimensions); Console.WriteLine(bmp.Dimensions.x + " " + bmp.Dimensions.y + " " + bmp.Dimensions.z); foreach (Vector3i idx in bmp.Indices()) { float f = sdf[idx.x, idx.y, idx.z]; bmp.Set(idx, (f < 0) ? true : false); //for bunny only removes bottom if (idx.y < 8) { bmp.Set(idx, false); } if (test) //take only one line from top { if (idx.z != 50 || idx.x != 60) { bmp.Set(idx, false); } else { bmp.Set(idx, true); Console.WriteLine(bmp.Get(idx)); } } } return(bmp); }
/// <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"); }
void make_grid(Vector3f origin, float dx, int ni, int nj, int nk, DenseGrid3f scalars) { scalars.resize(ni, nj, nk); scalars.assign(float.MaxValue); // sentinel if (DebugPrint) { System.Console.WriteLine("start"); } // Ok, because the whole idea is that the surface might have holes, we are going to // compute values along known triangles and then propagate the computed region outwards // until any iso-sign-change is surrounded. // To seed propagation, we compute unsigned SDF and then compute values for any voxels // containing surface (ie w/ distance smaller than cellsize) // compute unsigned SDF var sdf = new MeshSignedDistanceGrid(Mesh, CellSize) { ComputeSigns = false }; sdf.CancelF = this.CancelF; sdf.Compute(); if (CancelF()) { return; } DenseGrid3f distances = sdf.Grid; if (WantMeshSDFGrid) { mesh_sdf = sdf; } if (DebugPrint) { System.Console.WriteLine("done initial sdf"); } // compute values at surface voxels double ox = (double)origin[0], oy = (double)origin[1], oz = (double)origin[2]; gParallel.ForEach(gIndices.Grid3IndicesYZ(nj, nk), (jk) => { if (CancelF()) { return; } for (int i = 0; i < ni; ++i) { var ijk = new Vector3i(i, jk.y, jk.z); float dist = distances[ijk]; // this could be tighter? but I don't think it matters... if (dist < CellSize) { var gx = new Vector3d((float)ijk.x * dx + origin[0], (float)ijk.y * dx + origin[1], (float)ijk.z * dx + origin[2]); scalars[ijk] = (float)ScalarF(gx); } } }); if (CancelF()) { return; } if (DebugPrint) { System.Console.WriteLine("done narrow-band"); } // Now propagate outwards from computed voxels. // Current procedure is to check 26-neighbours around each 'front' voxel, // and if there are any sign changes, that neighbour is added to front. // Front is initialized w/ all voxels we computed above AxisAlignedBox3i bounds = scalars.Bounds; bounds.Max -= Vector3i.One; // since we will be computing new values as necessary, we cannot use // grid to track whether a voxel is 'new' or not. // So, using 3D bitmap intead - is updated at end of each pass. var bits = new Bitmap3(new Vector3i(ni, nj, nk)); var cur_front = new List <Vector3i>(); foreach (Vector3i ijk in scalars.Indices()) { if (scalars[ijk] != float.MaxValue) { cur_front.Add(ijk); bits[ijk] = true; } } if (CancelF()) { return; } // Unique set of 'new' voxels to compute in next iteration. var queue = new HashSet <Vector3i>(); var queue_lock = new SpinLock(); while (true) { if (CancelF()) { return; } // can process front voxels in parallel bool abort = false; int iter_count = 0; gParallel.ForEach(cur_front, (ijk) => { Interlocked.Increment(ref iter_count); if (iter_count % 100 == 0) { abort = CancelF(); } if (abort) { return; } float val = scalars[ijk]; // check 26-neighbours to see if we have a crossing in any direction for (int k = 0; k < 26; ++k) { Vector3i nijk = ijk + gIndices.GridOffsets26[k]; if (bounds.Contains(nijk) == false) { continue; } float val2 = scalars[nijk]; if (val2 == float.MaxValue) { var gx = new Vector3d((float)nijk.x * dx + origin[0], (float)nijk.y * dx + origin[1], (float)nijk.z * dx + origin[2]); val2 = (float)ScalarF(gx); scalars[nijk] = val2; } if (bits[nijk] == false) { // this is a 'new' voxel this round. // If we have an iso-crossing, add it to the front next round bool crossing = (val <IsoValue && val2> IsoValue) || (val > IsoValue && val2 < IsoValue); if (crossing) { bool taken = false; queue_lock.Enter(ref taken); queue.Add(nijk); queue_lock.Exit(); } } } }); if (DebugPrint) { System.Console.WriteLine("front has {0} voxels", queue.Count); } if (queue.Count == 0) { break; } // update known-voxels list and create front for next iteration foreach (Vector3i idx in queue) { bits[idx] = true; } cur_front.Clear(); cur_front.AddRange(queue); queue.Clear(); } if (DebugPrint) { System.Console.WriteLine("done front-prop"); } if (DebugPrint) { int filled = 0; foreach (Vector3i ijk in scalars.Indices()) { if (scalars[ijk] != float.MaxValue) { filled++; } } System.Console.WriteLine("filled: {0} / {1} - {2}%", filled, ni * nj * nk, (double)filled / (double)(ni * nj * nk) * 100.0); } if (CancelF()) { return; } // fill in the rest of the grid by propagating know values fill_spans(ni, nj, nk, scalars); if (DebugPrint) { System.Console.WriteLine("done sweep"); } }
public override Schematic WriteSchematic() { DMesh3 mesh = StandardMeshReader.ReadMesh(_path); AxisAlignedBox3d bounds = mesh.CachedBounds; DMeshAABBTree3 spatial = new DMeshAABBTree3(mesh, autoBuild: true); double cellsize = mesh.CachedBounds.MaxDim / _gridSize; ShiftGridIndexer3 indexer = new ShiftGridIndexer3(bounds.Min, cellsize); MeshSignedDistanceGrid sdf = new MeshSignedDistanceGrid(mesh, cellsize); sdf.Compute(); Bitmap3 bmp = new Bitmap3(sdf.Dimensions); Schematic schematic = new Schematic() { Blocks = new HashSet <Block>(), Width = (ushort)bmp.Dimensions.x, Height = (ushort)bmp.Dimensions.y, Length = (ushort)bmp.Dimensions.z }; LoadedSchematic.WidthSchematic = schematic.Width; LoadedSchematic.HeightSchematic = schematic.Height; LoadedSchematic.LengthSchematic = schematic.Length; if (_winding_number != 0) { spatial.WindingNumber(Vector3d.Zero); // seed cache outside of parallel eval using (ProgressBar progressbar = new ProgressBar()) { List <Vector3i> list = bmp.Indices().ToList(); int count = 0; gParallel.ForEach(bmp.Indices(), (idx) => { Vector3d v = indexer.FromGrid(idx); bmp.SafeSet(idx, spatial.WindingNumber(v) > _winding_number); count++; progressbar.Report(count / (float)list.Count); }); } if (!_excavate) { foreach (Vector3i idx in bmp.Indices()) { if (bmp.Get(idx)) { schematic.Blocks.Add(new Block((ushort)idx.x, (ushort)idx.y, (ushort)idx.z, Color.White.ColorToUInt())); } } } } else { using (ProgressBar progressbar = new ProgressBar()) { int count = bmp.Indices().Count(); List <Vector3i> list = bmp.Indices().ToList(); for (int i = 0; i < count; i++) { Vector3i idx = list[i]; float f = sdf[idx.x, idx.y, idx.z]; bool isInside = f < 0; bmp.Set(idx, (f < 0)); if (!_excavate && isInside) { schematic.Blocks.Add(new Block((ushort)idx.x, (ushort)idx.y, (ushort)idx.z, Color.White.ColorToUInt())); } progressbar.Report((i / (float)count)); } } } if (_excavate) { foreach (Vector3i idx in bmp.Indices()) { if (bmp.Get(idx) && IsBlockConnectedToAir(bmp, idx)) { schematic.Blocks.Add(new Block((ushort)idx.x, (ushort)idx.y, (ushort)idx.z, Color.White.ColorToUInt())); } } } return(schematic); }
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 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"); }
public static void test_marching_cubes_demos() { // generateMeshF() meshes the input implicit function at // the given cell resolution, and writes out the resulting mesh Action <BoundedImplicitFunction3d, int, string> generateMeshF = (root, numcells, path) => { MarchingCubes c = new MarchingCubes(); c.Implicit = root; c.RootMode = MarchingCubes.RootfindingModes.LerpSteps; // cube-edge convergence method c.RootModeSteps = 5; // number of iterations c.Bounds = root.Bounds(); c.CubeSize = c.Bounds.MaxDim / numcells; c.Bounds.Expand(3 * c.CubeSize); // leave a buffer of cells c.Generate(); MeshNormals.QuickCompute(c.Mesh); // generate normals StandardMeshWriter.WriteMesh(path, c.Mesh, WriteOptions.Defaults); // write mesh }; // meshToImplicitF() generates a narrow-band distance-field and // returns it as an implicit surface, that can be combined with other implicits Func <DMesh3, int, double, BoundedImplicitFunction3d> meshToImplicitF = (meshIn, numcells, max_offset) => { double meshCellsize = meshIn.CachedBounds.MaxDim / numcells; MeshSignedDistanceGrid levelSet = new MeshSignedDistanceGrid(meshIn, meshCellsize); levelSet.ExactBandWidth = (int)(max_offset / meshCellsize) + 1; levelSet.Compute(); return(new DenseGridTrilinearImplicit(levelSet.Grid, levelSet.GridOrigin, levelSet.CellSize)); }; // meshToBlendImplicitF() computes the full distance-field grid for the input // mesh. The bounds are expanded quite a bit to allow for blending, // probably more than necessary in most cases Func <DMesh3, int, BoundedImplicitFunction3d> meshToBlendImplicitF = (meshIn, numcells) => { double meshCellsize = meshIn.CachedBounds.MaxDim / numcells; MeshSignedDistanceGrid levelSet = new MeshSignedDistanceGrid(meshIn, meshCellsize); levelSet.ExpandBounds = meshIn.CachedBounds.Diagonal * 0.25; // need some values outside mesh levelSet.ComputeMode = MeshSignedDistanceGrid.ComputeModes.FullGrid; levelSet.Compute(); return(new DenseGridTrilinearImplicit(levelSet.Grid, levelSet.GridOrigin, levelSet.CellSize)); }; // generate union/difference/intersection of sphere and cube ImplicitSphere3d sphere = new ImplicitSphere3d() { Origin = Vector3d.Zero, Radius = 1.0 }; ImplicitBox3d box = new ImplicitBox3d() { Box = new Box3d(new Frame3f(Vector3f.AxisX), 0.5 * Vector3d.One) }; generateMeshF(new ImplicitUnion3d() { A = sphere, B = box }, 128, "c:\\demo\\union.obj"); generateMeshF(new ImplicitDifference3d() { A = sphere, B = box }, 128, "c:\\demo\\difference.obj"); generateMeshF(new ImplicitIntersection3d() { A = sphere, B = box }, 128, "c:\\demo\\intersection.obj"); // generate bunny offset surfaces //double offset = 0.2f; //DMesh3 mesh = TestUtil.LoadTestInputMesh("bunny_solid.obj"); //MeshTransforms.Scale(mesh, 3.0 / mesh.CachedBounds.MaxDim); //BoundedImplicitFunction3d meshImplicit = meshToImplicitF(mesh, 64, offset); //generateMeshF(meshImplicit, 128, "c:\\demo\\mesh.obj"); //generateMeshF(new ImplicitOffset3d() { A = meshImplicit, Offset = offset }, 128, "c:\\demo\\mesh_outset.obj"); //generateMeshF(new ImplicitOffset3d() { A = meshImplicit, Offset = -offset }, 128, "c:\\demo\\mesh_inset.obj"); // compare offset of sharp and smooth union //var smooth_union = new ImplicitSmoothDifference3d() { A = sphere, B = box }; //generateMeshF(smooth_union, 128, "c:\\demo\\smooth_union.obj"); //generateMeshF(new ImplicitOffset3d() { A = smooth_union, Offset = 0.2 }, 128, "c:\\demo\\smooth_union_offset.obj"); //var union = new ImplicitUnion3d() { A = sphere, B = box }; //generateMeshF(new ImplicitOffset3d() { A = union, Offset = offset }, 128, "c:\\demo\\union_offset.obj"); // blending //ImplicitSphere3d sphere1 = new ImplicitSphere3d() { // Origin = Vector3d.Zero, Radius = 1.0 //}; //ImplicitSphere3d sphere2 = new ImplicitSphere3d() { // Origin = 1.5 * Vector3d.AxisX, Radius = 1.0 //}; //generateMeshF(new ImplicitBlend3d() { A = sphere1, B = sphere2, Blend = 1.0 }, 128, "c:\\demo\\blend_1.obj"); //generateMeshF(new ImplicitBlend3d() { A = sphere1, B = sphere2, Blend = 4.0 }, 128, "c:\\demo\\blend_4.obj"); //generateMeshF(new ImplicitBlend3d() { A = sphere1, B = sphere2, Blend = 16.0 }, 128, "c:\\demo\\blend_16.obj"); //generateMeshF(new ImplicitBlend3d() { A = sphere1, B = sphere2, Blend = 64.0 }, 128, "c:\\demo\\blend_64.obj"); //sphere1.Radius = sphere2.Radius = 2.0f; //sphere2.Origin = 1.5 * sphere1.Radius * Vector3d.AxisX; //generateMeshF(new ImplicitBlend3d() { A = sphere1, B = sphere2, Blend = 1.0 }, 128, "c:\\demo\\blend_2x_1.obj"); //generateMeshF(new ImplicitBlend3d() { A = sphere1, B = sphere2, Blend = 4.0 }, 128, "c:\\demo\\blend_2x_4.obj"); //generateMeshF(new ImplicitBlend3d() { A = sphere1, B = sphere2, Blend = 16.0 }, 128, "c:\\demo\\blend_2x_16.obj"); //generateMeshF(new ImplicitBlend3d() { A = sphere1, B = sphere2, Blend = 64.0 }, 128, "c:\\demo\\blend_2x_64.obj"); // mesh blending //DMesh3 mesh1 = TestUtil.LoadTestInputMesh("bunny_solid.obj"); //MeshTransforms.Scale(mesh1, 3.0 / mesh1.CachedBounds.MaxDim); //DMesh3 mesh2 = new DMesh3(mesh1); //MeshTransforms.Rotate(mesh2, mesh2.CachedBounds.Center, Quaternionf.AxisAngleD(Vector3f.OneNormalized, 45.0f)); //var meshImplicit1 = meshToImplicitF(mesh1, 64, 0); //var meshImplicit2 = meshToImplicitF(mesh2, 64, 0); //generateMeshF(new ImplicitBlend3d() { A = meshImplicit1, B = meshImplicit2, Blend = 0.0 }, 256, "c:\\demo\\blend_mesh_union.obj"); //generateMeshF(new ImplicitBlend3d() { A = meshImplicit1, B = meshImplicit2, Blend = 10.0 }, 256, "c:\\demo\\blend_mesh_bad.obj"); //var meshFullImplicit1 = meshToBlendImplicitF(mesh1, 64); //var meshFullImplicit2 = meshToBlendImplicitF(mesh2, 64); //generateMeshF(new ImplicitBlend3d() { A = meshFullImplicit1, B = meshFullImplicit2, Blend = 0.0 }, 256, "c:\\demo\\blend_mesh_union.obj"); //generateMeshF(new ImplicitBlend3d() { A = meshFullImplicit1, B = meshFullImplicit2, Blend = 1.0 }, 256, "c:\\demo\\blend_mesh_1.obj"); //generateMeshF(new ImplicitBlend3d() { A = meshFullImplicit1, B = meshFullImplicit2, Blend = 10.0 }, 256, "c:\\demo\\blend_mesh_10.obj"); //generateMeshF(new ImplicitBlend3d() { A = meshFullImplicit1, B = meshFullImplicit2, Blend = 50.0 }, 256, "c:\\demo\\blend_mesh_100.obj"); //DMesh3 mesh = TestUtil.LoadTestInputMesh("bunny_solid.obj"); //MeshTransforms.Scale(mesh, 3.0 / mesh.CachedBounds.MaxDim); //MeshTransforms.Translate(mesh, -mesh.CachedBounds.Center); //Reducer r = new Reducer(mesh); //r.ReduceToTriangleCount(100); //double radius = 0.1; //List<BoundedImplicitFunction3d> Lines = new List<BoundedImplicitFunction3d>(); //foreach (Index4i edge_info in mesh.Edges()) { // var segment = new Segment3d(mesh.GetVertex(edge_info.a), mesh.GetVertex(edge_info.b)); // Lines.Add(new ImplicitLine3d() { Segment = segment, Radius = radius }); //} //ImplicitNaryUnion3d unionN = new ImplicitNaryUnion3d() { Children = Lines }; //generateMeshF(unionN, 128, "c:\\demo\\mesh_edges.obj"); //radius = 0.05; //List<BoundedImplicitFunction3d> Elements = new List<BoundedImplicitFunction3d>(); //foreach (int eid in mesh.EdgeIndices()) { // var segment = new Segment3d(mesh.GetEdgePoint(eid, 0), mesh.GetEdgePoint(eid, 1)); // Elements.Add(new ImplicitLine3d() { Segment = segment, Radius = radius }); //} //foreach (Vector3d v in mesh.Vertices()) // Elements.Add(new ImplicitSphere3d() { Origin = v, Radius = 2 * radius }); //generateMeshF(new ImplicitNaryUnion3d() { Children = Elements }, 256, "c:\\demo\\mesh_edges_and_vertices.obj"); //double lattice_radius = 0.05; //double lattice_spacing = 0.4; //double shell_thickness = 0.05; //int mesh_resolution = 64; // set to 256 for image quality //var shellMeshImplicit = meshToImplicitF(mesh, 128, shell_thickness); //double max_dim = mesh.CachedBounds.MaxDim; //AxisAlignedBox3d bounds = new AxisAlignedBox3d(mesh.CachedBounds.Center, max_dim / 2); //bounds.Expand(2 * lattice_spacing); //AxisAlignedBox2d element = new AxisAlignedBox2d(lattice_spacing); //AxisAlignedBox2d bounds_xy = new AxisAlignedBox2d(bounds.Min.xy, bounds.Max.xy); //AxisAlignedBox2d bounds_xz = new AxisAlignedBox2d(bounds.Min.xz, bounds.Max.xz); //AxisAlignedBox2d bounds_yz = new AxisAlignedBox2d(bounds.Min.yz, bounds.Max.yz); //List<BoundedImplicitFunction3d> Tiling = new List<BoundedImplicitFunction3d>(); //foreach (Vector2d uv in TilingUtil.BoundedRegularTiling2(element, bounds_xy, 0)) { // Segment3d seg = new Segment3d(new Vector3d(uv.x, uv.y, bounds.Min.z), new Vector3d(uv.x, uv.y, bounds.Max.z)); // Tiling.Add(new ImplicitLine3d() { Segment = seg, Radius = lattice_radius }); //} //foreach (Vector2d uv in TilingUtil.BoundedRegularTiling2(element, bounds_xz, 0)) { // Segment3d seg = new Segment3d(new Vector3d(uv.x, bounds.Min.y, uv.y), new Vector3d(uv.x, bounds.Max.y, uv.y)); // Tiling.Add(new ImplicitLine3d() { Segment = seg, Radius = lattice_radius }); //} //foreach (Vector2d uv in TilingUtil.BoundedRegularTiling2(element, bounds_yz, 0)) { // Segment3d seg = new Segment3d(new Vector3d(bounds.Min.x, uv.x, uv.y), new Vector3d(bounds.Max.x, uv.x, uv.y)); // Tiling.Add(new ImplicitLine3d() { Segment = seg, Radius = lattice_radius }); //} //ImplicitNaryUnion3d lattice = new ImplicitNaryUnion3d() { Children = Tiling }; //generateMeshF(lattice, 128, "c:\\demo\\lattice.obj"); //ImplicitIntersection3d lattice_clipped = new ImplicitIntersection3d() { A = lattice, B = shellMeshImplicit }; //generateMeshF(lattice_clipped, mesh_resolution, "c:\\demo\\lattice_clipped.obj"); //var shell = new ImplicitDifference3d() { // A = shellMeshImplicit, B = new ImplicitOffset3d() { A = shellMeshImplicit, Offset = -shell_thickness } //}; //var shell_cut = new ImplicitDifference3d() { // A = shell, B = new ImplicitAxisAlignedBox3d() { AABox = new AxisAlignedBox3d(Vector3d.Zero, max_dim / 2, 0.4, max_dim / 2) } //}; //generateMeshF(new ImplicitUnion3d() { A = lattice_clipped, B = shell_cut }, mesh_resolution, "c:\\demo\\lattice_result.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 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; }