예제 #1
0
        /// <summary>
        /// count 6-nbrs of each voxel, discard if count <= minNbrs
        /// </summary>
        public void Filter(int nMinNbrs)
        {
            AxisAlignedBox3i bounds = GridBounds;

            bounds.Max -= Vector3i.One;

            for (int i = 0; i < Bits.Length; ++i)
            {
                if (Bits[i] == false)
                {
                    continue;
                }
                Vector3i idx = ToIndex(i);
                int      nc  = 0;
                for (int k = 0; k < 6 && nc <= nMinNbrs; ++k)
                {
                    Vector3i nbr = idx + gIndices.GridOffsets6[k];
                    if (bounds.Contains(nbr) == false)
                    {
                        continue;
                    }
                    if (Get(nbr))
                    {
                        nc++;
                    }
                }
                if (nc <= nMinNbrs)
                {
                    Bits[i] = false;
                }
            }
        }
예제 #2
0
    public ParticleGrid()
    {
        Particles = new Dictionary <Vector3i, PInfo>();
        Extents   = AxisAlignedBox3i.Empty;

        Uniques = new HashSet <Vector3f>();
    }
예제 #3
0
        public void Generate()
        {
            Append_mesh();

            AxisAlignedBox3i bounds = Voxels.GridBounds;

            bounds.Max -= Vector3i.One;

            int[] vertices = new int[4];

            foreach (Vector3i nz in Voxels.NonZeros())
            {
                Check_counts_or_append(6, 2);

                Box3d cube = Box3d.UnitZeroCentered;
                cube.Center = (Vector3D)nz;

                for (int fi = 0; fi < 6; ++fi)
                {
                    // checks dependent on neighbours
                    Index3i nbr = nz + gIndices.GridOffsets6[fi];
                    if (bounds.Contains(nbr))
                    {
                        if (SkipInteriorFaces && Voxels.Get(nbr))
                        {
                            continue;
                        }
                    }
                    else if (CapAtBoundary == false)
                    {
                        continue;
                    }


                    int           ni = gIndices.BoxFaceNormals[fi];
                    Vector3F      n  = (Vector3F)(Math.Sign(ni) * cube.Axis(Math.Abs(ni) - 1));
                    NewVertexInfo vi = new NewVertexInfo(Vector3D.Zero, n);
                    if (ColorSourceF != null)
                    {
                        vi.c      = ColorSourceF(nz);
                        vi.bHaveC = true;
                    }
                    for (int j = 0; j < 4; ++j)
                    {
                        vi.v        = cube.Corner(gIndices.BoxFaces[fi, j]);
                        vertices[j] = cur_mesh.AppendVertex(vi);
                    }

                    Index3i t0 = new Index3i(vertices[0], vertices[1], vertices[2], Clockwise);
                    Index3i t1 = new Index3i(vertices[0], vertices[2], vertices[3], Clockwise);
                    cur_mesh.AppendTriangle(t0);
                    cur_mesh.AppendTriangle(t1);
                }
            }
        }
예제 #4
0
        public void GenerateContinuation(IEnumerable <Vector3d> seeds)
        {
            Mesh = new DMesh3();

            int nx = (int)(Bounds.Width / CubeSize) + 1;
            int ny = (int)(Bounds.Height / CubeSize) + 1;
            int nz = (int)(Bounds.Depth / CubeSize) + 1;

            CellDimensions = new Vector3i(nx, ny, nz);
            GridBounds     = new AxisAlignedBox3i(Vector3i.Zero, CellDimensions);

            if (LastGridBounds != GridBounds)
            {
                corner_values_grid = new DenseGrid3f(nx + 1, ny + 1, nz + 1, float.MaxValue);
                edge_vertices      = new Dictionary <long, int>();
                corner_values      = new Dictionary <long, double>();
                if (ParallelCompute)
                {
                    done_cells = new DenseGrid3i(CellDimensions.x, CellDimensions.y, CellDimensions.z, 0);
                }
            }
            else
            {
                edge_vertices.Clear();
                corner_values.Clear();
                corner_values_grid.assign(float.MaxValue);
                if (ParallelCompute)
                {
                    done_cells.assign(0);
                }
            }

            if (ParallelCompute)
            {
                generate_continuation_parallel(seeds);
            }
            else
            {
                generate_continuation(seeds);
            }

            LastGridBounds = GridBounds;
        }
예제 #5
0
        /// <summary>
        /// Run MC algorithm and generate Output mesh
        /// </summary>
        public void Generate()
        {
            Mesh = new DMesh3();

            int nx = (int)(Bounds.Width / CubeSize) + 1;
            int ny = (int)(Bounds.Height / CubeSize) + 1;
            int nz = (int)(Bounds.Depth / CubeSize) + 1;

            CellDimensions = new Vector3i(nx, ny, nz);
            GridBounds     = new AxisAlignedBox3i(Vector3i.Zero, CellDimensions);

            corner_values_grid = new DenseGrid3f(nx + 1, ny + 1, nz + 1, float.MaxValue);
            edge_vertices      = new Dictionary <long, int>();
            corner_values      = new Dictionary <long, double>();

            if (ParallelCompute)
            {
                generate_parallel();
            }
            else
            {
                generate_basic();
            }
        }
        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");
            }
        }
    void update_mesh()
    {
        clear_mesh();

        AxisAlignedBox3i bounds    = grid.Extents;
        Vector3i         minCorner = bounds.Min;
        Vector3f         cornerXYZ = grid.ToXYZ(minCorner);

        Bitmap3d bmp;

        try {
            bmp = new Bitmap3d(bounds.Diagonal + Vector3i.One);
        } catch (Exception e) {
            Debug.Log("update_mesh: exception allocating grid of size " + bounds.Diagonal);
            throw e;
        }

        foreach (Vector3i idx in grid.GridIndices(MinSamples))
        {
            Vector3i bidx = idx - minCorner;
            try {
                bmp.Set(bidx, true);
            } catch (Exception e) {
                Debug.Log("bad index is " + bidx + "  grid dims " + bmp.Dimensions);
                throw e;
            }
        }

        // get rid of one-block tubes, floaters, etc.
        // todo: use a queue instead of passes? or just descend into
        //  nbrs when changing one block? one pass to compute counts and
        //  then another to remove? (yes that is a good idea...)
        bmp.Filter(2);
        bmp.Filter(2);
        bmp.Filter(2);
        bmp.Filter(2);
        bmp.Filter(2);
        bmp.Filter(2);


        VoxelSurfaceGenerator gen = new VoxelSurfaceGenerator()
        {
            Voxels = bmp, Clockwise = false,
            MaxMeshElementCount = 65000,
            ColorSourceF        = (idx) => {
                idx = idx + minCorner;
                return(grid.GetColor(idx));
            }
        };

        gen.Generate();
        List <DMesh3> meshes = gen.Meshes;

        List <fMeshGameObject> newMeshGOs = new List <fMeshGameObject>();

        foreach (DMesh3 mesh in meshes)
        {
            MeshTransforms.Scale(mesh, grid.GridStepSize);
            MeshTransforms.Translate(mesh, cornerXYZ);

            Mesh            m      = UnityUtil.DMeshToUnityMesh(mesh, false);
            fMeshGameObject meshGO = GameObjectFactory.CreateMeshGO("gridmesh", m, false, true);
            meshGO.SetMaterial(MaterialUtil.CreateStandardVertexColorMaterialF(Colorf.White));
            newMeshGOs.Add(meshGO);
        }

        CurrentMeshGOs = newMeshGOs;
    }
예제 #8
0
 /// <summary>
 /// Must provide a sample instance of the element type that we can Duplicate()
 /// to make additional copies. Should be no data in here
 /// </summary>
 public DSparseGrid3(ElemType toDuplicate)
 {
     this.exemplar = toDuplicate;
     elements      = new Dictionary <Vector3i, ElemType>();
     bounds        = AxisAlignedBox3i.Empty;
 }