예제 #1
0
        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);
            });
        }
예제 #2
0
        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");
        }
예제 #3
0
        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));
        }
예제 #4
0
        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);
        }
예제 #5
0
        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;
            });
        }
예제 #6
0
        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);
        }
예제 #7
0
        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");
        }
예제 #8
0
        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();
        }
예제 #9
0
        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);
        }
예제 #10
0
        /// <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);
        }
예제 #12
0
        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");
        }
예제 #13
0
        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();
        }
예제 #14
0
        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);
        }
예제 #15
0
        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");
            }
        }
예제 #18
0
        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);
        }
예제 #19
0
        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);
        }
예제 #20
0
        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");
        }
예제 #21
0
        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");
        }
예제 #22
0
        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");
        }
예제 #23
0
        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;
        }
예제 #24
0
        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;
        }
예제 #25
0
        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);
        }
예제 #26
0
        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;
        }
예제 #27
0
        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;
        }