Beispiel #1
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);
        }
        void make_grid(Vector3f origin, float dx,
                       int ni, int nj, int nk,
                       DenseGrid3f winding)
        {
            winding.resize(ni, nj, nk);
            winding.assign(float.MaxValue); // sentinel

            // seed MWN cache
            MeshSpatial.WindingNumber(Vector3d.Zero);

            if (DebugPrint)
            {
                System.Console.WriteLine("start");
            }

            // Ok, because the whole idea is that the surface might have holes, we are going to
            // compute MWN along known triangles and then propagate the computed region outwards
            // until any MWN iso-sign-change is surrounded.
            // To seed propagation, we compute unsigned SDF and then compute MWN for any voxels
            // containing surface (ie w/ distance smaller than cellsize)

            // compute unsigned SDF
            MeshSignedDistanceGrid 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 MWN 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)
                {
                    Vector3i 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)
                    {
                        Vector3d gx  = new Vector3d((float)ijk.x * dx + origin[0], (float)ijk.y * dx + origin[1], (float)ijk.z * dx + origin[2]);
                        winding[ijk] = (float)MeshSpatial.WindingNumber(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 MWN sign changes, that neighbour is added to front.
            // Front is initialized w/ all voxels we computed above

            AxisAlignedBox3i bounds   = winding.Bounds;

            bounds.Max -= Vector3i.One;

            // since we will be computing new MWN values as necessary, we cannot use
            // winding grid to track whether a voxel is 'new' or not.
            // So, using 3D bitmap intead - is updated at end of each pass.
            Bitmap3         bits      = new Bitmap3(new Vector3i(ni, nj, nk));
            List <Vector3i> cur_front = new List <Vector3i>();

            foreach (Vector3i ijk in winding.Indices())
            {
                if (winding[ijk] != float.MaxValue)
                {
                    cur_front.Add(ijk);
                    bits[ijk] = true;
                }
            }
            if (CancelF())
            {
                return;
            }

            // Unique set of 'new' voxels to compute in next iteration.
            HashSet <Vector3i> queue      = new HashSet <Vector3i>();
            SpinLock           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 = winding[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 = winding[nijk];
                        if (val2 == float.MaxValue)
                        {
                            Vector3d gx   = new Vector3d((float)nijk.x * dx + origin[0], (float)nijk.y * dx + origin[1], (float)nijk.z * dx + origin[2]);
                            val2          = (float)MeshSpatial.WindingNumber(gx);
                            winding[nijk] = val2;
                        }
                        if (bits[nijk] == false)
                        {
                            // this is a 'new' voxel this round.
                            // If we have a MWN-iso-crossing, add it to the front next round
                            bool crossing = (val <WindingIsoValue && val2> WindingIsoValue) ||
                                            (val > WindingIsoValue && val2 < WindingIsoValue);
                            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 winding.Indices())
                {
                    if (winding[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 MWN values
            fill_spans(ni, nj, nk, winding);

            if (DebugPrint)
            {
                System.Console.WriteLine("done sweep");
            }
        }
Beispiel #3
0
        public static void test_Winding()
        {
            Sphere3Generator_NormalizedCube gen = new Sphere3Generator_NormalizedCube()
            {
                EdgeVertices = 200
            };
            DMesh3 mesh = gen.Generate().MakeDMesh();
            //DMesh3 mesh = TestUtil.LoadTestInputMesh("bunny_solid.obj");
            DMeshAABBTree3 spatial = new DMeshAABBTree3(mesh, true);

            GC.Collect();

            double   maxdim = mesh.CachedBounds.MaxDim;
            Vector3d center = mesh.CachedBounds.Center;

            var pts        = TestUtil.RandomPoints3(100, new Random(31337), center, maxdim * 0.5);
            int num_inside = 0;

            foreach (Vector3d pt in pts)
            {
                bool   inside    = spatial.IsInside(pt);
                double distSqr   = MeshQueries.TriDistanceSqr(mesh, spatial.FindNearestTriangle(pt), pt);
                double ptDist    = pt.Length;
                double winding   = mesh.WindingNumber(pt);
                double winding_2 = spatial.WindingNumber(pt);
                if (Math.Abs(winding - winding_2) > 0.00001)
                {
                    System.Diagnostics.Debugger.Break();
                }
                bool winding_inside = Math.Abs(winding) > 0.5;
                if (inside != winding_inside)
                {
                    System.Diagnostics.Debugger.Break();
                }
                if (inside)
                {
                    num_inside++;
                }
            }
            System.Console.WriteLine("inside {0} / {1}", num_inside, pts.Length);

            // force rebuild for profiling code
            GC.Collect();

            LocalProfiler p = new LocalProfiler();

            pts = TestUtil.RandomPoints3(1000, new Random(31337), center, maxdim * 0.5);

            p.Start("full eval");
            double sum = 0;

            foreach (Vector3d pt in pts)
            {
                double winding = mesh.WindingNumber(pt);
                sum += winding;
            }
            p.StopAll();

            p.Start("tree build");
            spatial = new DMeshAABBTree3(mesh, true);
            spatial.WindingNumber(Vector3d.Zero);
            p.StopAll();

            GC.Collect();
            GC.Collect();

            p.Start("tree eval");
            sum = 0;
            foreach (Vector3d pt in pts)
            {
                double winding = spatial.WindingNumber(pt);
                sum += winding;
            }

            p.StopAll();

            System.Console.WriteLine(p.AllTimes());
        }
        public virtual bool Apply()
        {
            DMesh3 testAgainstMesh = Mesh;

            if (InsideMode == CalculationMode.RayParity)
            {
                MeshBoundaryLoops loops = new MeshBoundaryLoops(testAgainstMesh);
                if (loops.Count > 0)
                {
                    testAgainstMesh = new DMesh3(Mesh);
                    foreach (var loop in loops)
                    {
                        if (Cancelled())
                        {
                            return(false);
                        }
                        SimpleHoleFiller filler = new SimpleHoleFiller(testAgainstMesh, loop);
                        filler.Fill();
                    }
                }
            }

            DMeshAABBTree3 spatial = (Spatial != null && testAgainstMesh == Mesh) ?
                                     Spatial : new DMeshAABBTree3(testAgainstMesh, true);

            if (InsideMode == CalculationMode.AnalyticWindingNumber)
            {
                spatial.WindingNumber(Vector3d.Zero);
            }
            else if (InsideMode == CalculationMode.FastWindingNumber)
            {
                spatial.FastWindingNumber(Vector3d.Zero);
            }

            if (Cancelled())
            {
                return(false);
            }

            // ray directions
            List <Vector3d> ray_dirs = null; int NR = 0;

            if (InsideMode == CalculationMode.SimpleOcclusionTest)
            {
                ray_dirs = new List <Vector3d>();
                ray_dirs.Add(Vector3d.AxisX); ray_dirs.Add(-Vector3d.AxisX);
                ray_dirs.Add(Vector3d.AxisY); ray_dirs.Add(-Vector3d.AxisY);
                ray_dirs.Add(Vector3d.AxisZ); ray_dirs.Add(-Vector3d.AxisZ);
                NR = ray_dirs.Count;
            }

            Func <Vector3d, bool> isOccludedF = (pt) => {
                if (InsideMode == CalculationMode.RayParity)
                {
                    return(spatial.IsInside(pt));
                }
                else if (InsideMode == CalculationMode.AnalyticWindingNumber)
                {
                    return(spatial.WindingNumber(pt) > WindingIsoValue);
                }
                else if (InsideMode == CalculationMode.FastWindingNumber)
                {
                    return(spatial.FastWindingNumber(pt) > WindingIsoValue);
                }
                else
                {
                    for (int k = 0; k < NR; ++k)
                    {
                        int hit_tid = spatial.FindNearestHitTriangle(new Ray3d(pt, ray_dirs[k]));
                        if (hit_tid == DMesh3.InvalidID)
                        {
                            return(false);
                        }
                    }
                    return(true);
                }
            };

            bool cancel = false;

            BitArray vertices = null;

            if (PerVertex)
            {
                vertices = new BitArray(Mesh.MaxVertexID);

                MeshNormals normals = null;
                if (Mesh.HasVertexNormals == false)
                {
                    normals = new MeshNormals(Mesh);
                    normals.Compute();
                }

                gParallel.ForEach(Mesh.VertexIndices(), (vid) => {
                    if (cancel)
                    {
                        return;
                    }
                    if (vid % 10 == 0)
                    {
                        cancel = Cancelled();
                    }

                    Vector3d c    = Mesh.GetVertex(vid);
                    Vector3d n    = (normals == null) ? Mesh.GetVertexNormal(vid) : normals[vid];
                    c            += n * NormalOffset;
                    vertices[vid] = isOccludedF(c);
                });
            }
            if (Cancelled())
            {
                return(false);
            }

            RemovedT = new List <int>();
            SpinLock removeLock = new SpinLock();

            gParallel.ForEach(Mesh.TriangleIndices(), (tid) => {
                if (cancel)
                {
                    return;
                }
                if (tid % 10 == 0)
                {
                    cancel = Cancelled();
                }

                bool inside = false;
                if (PerVertex)
                {
                    Index3i tri = Mesh.GetTriangle(tid);
                    inside      = vertices[tri.a] || vertices[tri.b] || vertices[tri.c];
                }
                else
                {
                    Vector3d c = Mesh.GetTriCentroid(tid);
                    Vector3d n = Mesh.GetTriNormal(tid);
                    c         += n * NormalOffset;
                    inside     = isOccludedF(c);
                }

                if (inside)
                {
                    bool taken = false;
                    removeLock.Enter(ref taken);
                    RemovedT.Add(tid);
                    removeLock.Exit();
                }
            });

            if (Cancelled())
            {
                return(false);
            }

            if (RemovedT.Count > 0)
            {
                MeshEditor editor = new MeshEditor(Mesh);
                bool       bOK    = editor.RemoveTriangles(RemovedT, true);
                RemoveFailed = (bOK == false);
            }

            return(true);
        }
        public static void test_fast_winding_grid()
        {
            //Sphere3Generator_NormalizedCube gen = new Sphere3Generator_NormalizedCube() { EdgeVertices = 15, Radius = 5 };
            //CappedCylinderGenerator gen = new CappedCylinderGenerator() { };
            //TrivialBox3Generator gen = new TrivialBox3Generator() { Box = new Box3d(Vector3d.Zero, 5 * Vector3d.One) };
            //DMesh3 mesh = gen.Generate().MakeDMesh();
            //MeshTransforms.Translate(mesh, 5 * Vector3d.One);
            DMesh3 mesh = TestUtil.LoadTestInputMesh("bunny_solid.obj");
            //DMesh3 mesh = TestUtil.LoadTestInputMesh("holey_bunny.obj");
            //DMesh3 mesh = TestUtil.LoadTestInputMesh("holey_bunny_2.obj");
            //DMesh3 mesh = TestUtil.LoadTestMesh("C:\\git\\gsOrthoVRApp\\sample_files\\scan_1_raw.obj");   // use iso 0.25
            //DMesh3 mesh = TestUtil.LoadTestMesh("C:\\scratch\\irongiant.stl");
            //DMesh3 mesh = TestUtil.LoadTestMesh("C:\\Users\\rms\\Dropbox\\meshes\\cars\\beetle.obj");
            //DMesh3 mesh = TestUtil.LoadTestMesh("c:\\scratch\\PigHead_rot90.obj");
            //DMesh3 mesh = TestUtil.LoadTestMesh("C:\\Projects\\nia_files\\testscan2.obj");

            //TestUtil.WriteTestOutputMesh(mesh, "xxx.obj");

            AxisAlignedBox3d meshBounds = mesh.CachedBounds;
            int    num_cells            = 256;
            double cell_size            = meshBounds.MaxDim / num_cells;

            float winding_iso = 0.35f;

            DMeshAABBTree3 spatial = new DMeshAABBTree3(mesh, true);

            spatial.WindingNumber(Vector3d.Zero);

            DMeshAABBTreePro spatialPro = new DMeshAABBTreePro(mesh, true);

            spatialPro.FastWindingNumber(Vector3d.Zero);

            Func <Vector3d, double> exactWN     = (q) => { return(spatial.WindingNumber(q)); };
            Func <Vector3d, double> PerFacePtWN = (q) => { return(eval_point_wn(mesh, q)); };
            Func <Vector3d, double> fastWN      = (q) => { return(spatialPro.FastWindingNumber(q)); };

            //MeshScalarSamplingGrid mwnGrid = new MeshScalarSamplingGrid(mesh, cell_size, exactWN);
            //MeshScalarSamplingGrid mwnGrid = new MeshScalarSamplingGrid(mesh, cell_size, PerFacePtWN );
            MeshScalarSamplingGrid mwnGrid = new MeshScalarSamplingGrid(mesh, cell_size, fastWN);

            mwnGrid.IsoValue   = winding_iso;
            mwnGrid.DebugPrint = true;

            LocalProfiler p = new LocalProfiler();

            p.Start("grid");
            mwnGrid.Compute();
            p.Stop("grid");
            System.Console.WriteLine(p.AllTimes());


            MarchingCubes c = new MarchingCubes();

            c.Implicit = new SampledGridImplicit(mwnGrid);

            c.Bounds   = mesh.CachedBounds;
            c.CubeSize = c.Bounds.MaxDim / 128;

            //c.Bounds = mesh.CachedBounds;
            c.Bounds.Expand(c.CubeSize * 3);
            //c.CubeSize = cell_size * 0.5;
            //c.IsoValue = mwnGrid.WindingIsoValue;

            c.Generate();

            // reproject
            foreach (int vid in c.Mesh.VertexIndices())
            {
                Vector3d v = c.Mesh.GetVertex(vid);

                int tid = spatial.FindNearestTriangle(v, cell_size * MathUtil.SqrtTwo);
                if (tid != DMesh3.InvalidID)
                {
                    var query = MeshQueries.TriangleDistance(mesh, tid, v);
                    if (v.Distance(query.TriangleClosest) < cell_size * 1.5)
                    {
                        c.Mesh.SetVertex(vid, query.TriangleClosest);
                    }
                }
            }

            //MeshNormals.QuickCompute(c.Mesh);
            TestUtil.WriteTestOutputMesh(c.Mesh, "mwn_implicit.obj");
        }