Example #1
0
    void GenerateMap()
    {
        map = new int[width, height, depth];

        switch (fillType)
        {
        case FillType.Random:
            RandomFillMap();
            break;

        case FillType.Pink:
            PerlinFillMap();
            break;

        case FillType.Ridge:
            RidgeFillMap();
            break;

        case FillType.Billow:
            BillowFillMap();
            break;
        }

        for (int i = 0; i < smoothIterations; i++)
        {
            SmoothMap();
        }

        //TODO not working
        RemoveWallsFromMap();

        float[,,] floatMap = new float[width, height, depth];
        for (int x = 0; x < width - 1; x++)
        {
            for (int y = 0; y < height - 1; y++)
            {
                for (int z = 0; z < depth - 1; z++)
                {
                    floatMap[x, y, z] = (float)map[x, y, z];
                }
            }
        }

        MarchingCubes.SetModeToCubes();
        Mesh mesh = MarchingCubes.CreateMesh(floatMap);

        //Move all vertices so that origin is centered
        Vector3[] vertices = mesh.vertices;

/*		for(int i = 0; i < vertices.Count(); i++) {
 *                      vertices[i] = vertices[i] + (-mesh.bounds.center);
 *              }*/

        mesh.name = "world";
        myMesh    = mesh;
        GetComponent <MeshFilter>().mesh = mesh;
        mesh.RecalculateNormals();

        GetComponent <MeshCollider>().sharedMesh = mesh;
    }
Example #2
0
        public Mesh CreateMesh()
        {
            //float startTime = Time.realtimeSinceStartup;

            Mesh mesh = MarchingCubes.CreateMesh(GridData);

            if (mesh == null)
            {
                return(null);
            }

            int size = mesh.vertices.Length;

            if (GridNormals != null)
            {
                Vector3[] normals = new Vector3[size];
                Vector3[] verts   = mesh.vertices;

                //Each verts in the mesh generated is its position in the voxel array
                //and you can use this to find what the normal at this position.
                //The verts are not at whole numbers though so you need to use trilinear interpolation
                //to find the normal for that position

                for (int i = 0; i < size; i++)
                {
                    normals[i] = TriLinearInterpNormal(verts[i]);
                }

                mesh.normals = normals;
            }
            else
            {
                mesh.RecalculateNormals();
            }

            Color[]   control     = new Color[size];
            Vector3[] meshNormals = mesh.normals;

            for (int i = 0; i < size; i++)
            {
                //This creates a control map used to texture the mesh based on the slope
                //of the vert. Its very basic and if you modify how this works yoou will
                //you will probably need to modify the shader as well.
                float dpUp = Vector3.Dot(meshNormals[i], Vector3.up);

                float R = Mathf.Pow(Mathf.Abs(dpUp), 2.0f);
                float G = Mathf.Max(0.0f, dpUp);                 // (Mathf.Max(0.0f, dpUp) < 0.8f) ? 0.0f : 1.0f;

                //Whats left end up being the rock face on the vertical areas

                control[i] = new Color(R, G, 0, 0);
            }

            //May as well store in colors
            mesh.colors = control;

            return(mesh);

            //Debug.Log("Create mesh time = " + (Time.realtimeSinceStartup-startTime).ToString() );
        }
Example #3
0
    private void MeshThread(MapData mapData, Action <MeshData> callback)
    {
        MeshData mesh = MarchingCubes.CreateMesh(mapData);

        lock (meshThreadInfoQueue)
        {
            meshThreadInfoQueue.Enqueue(new MapThreadInfo <MeshData>(callback, mesh));
        }
    }
Example #4
0
    public void Build()
    {
        marchingCubes.CreateMesh(field);

        vertBuffer.Clear();
        for (int i = 0; i < marchingCubes.mb.vertices.Count; i++)
        {
            TVector3 vert = marchingCubes.mb.vertices[i];
            vertBuffer.Add(new Vector3(vert.X, vert.Y, vert.Z));
        }

        m.Clear();
        m.SetVertices(vertBuffer);
        m.SetTriangles(marchingCubes.mb.triangles, 0);
        m.RecalculateNormals();

        mf.sharedMesh = m;
        mc.sharedMesh = m;
    }
Example #5
0
    public void UpdateMesh()
    {
        if (isDirty)
        {
            Mesh mesh = MarchingCubes.CreateMesh(voxels);

            MarchingCubes.SetTarget(target);
            //The diffuse shader wants uvs so just fill with a empty array, there not actually used
            mesh.uv = new Vector2[mesh.vertices.Length];
            mesh.RecalculateNormals();
            DestroyImmediate(m_mesh.GetComponent <MeshFilter>().sharedMesh, true);
            m_mesh.GetComponent <MeshFilter>().mesh = mesh;
            m_mesh.transform.localPosition          = transform.position + new Vector3(-32 / 2, -32 / 2, -32 / 2);
            isDirty = false;
        }
    }
Example #6
0
    /// <summary>
    /// Creates the surface objects.
    /// </summary>
    /// <param name='voxels'>
    /// Voxels, i.e. the scalar field used to compute the surface.
    /// </param>
    /// <param name='threshold'>
    /// The threshold on which the isosurface is based.
    /// </param>
    /// <param name='delta'>
    /// Delta parameter from the grid, basically the size of each cell.
    /// </param>
    /// <param name='origin'>
    /// Origin of the grid.
    /// </param>
    /// <param name='colors'>
    /// Colors. Kept from previous implementation, but doesn't do anything here. I'm only
    /// keeping it because I'm not sure what it was used for. --- Alexandre
    /// </param>
    /// <param name='tag'>
    /// Tag for the objects to be created.
    /// </param>
    /// <param name='electro'>
    /// True if this is an electrostatic field isosurface.
    /// </param>
    public static void CreateSurfaceObjects(float[,,] voxels, float threshold, Vector3 delta, Vector3 origin,
                                            Color[] colors, string tag = "SurfaceManager", bool electro = false)
    {
        ELECTRO = electro;
        Debug.Log(ELECTRO.ToString());
        if (ELECTRO)
        {
            ReadDX readDX = UI.GUIMoleculeController.readdx;
            origin = readDX.GetOrigin();
            delta  = readDX.GetDelta();
        }

        InitGenMesh(voxels, threshold, delta, origin, tag);
        SetDims();

        float    bMCTime = Time.realtimeSinceStartup;
        MeshData mData   = MarchingCubes.CreateMesh(VOXELS, 0, XDIM, 0, YDIM, 0, ZDIM);

        Debug.Log("Entire surface contains " + mData.vertices.Length.ToString() + " vertices.");
        float elapsed = 10f * (Time.realtimeSinceStartup - bMCTime);

        Debug.Log("GenerateMesh::MarchingCubes time: " + elapsed.ToString());
        OffsetVertices(mData);

        float         bSmooth       = Time.realtimeSinceStartup;
        AdjacencySets adjacencySets = new AdjacencySets(mData.triangles.Length);

        adjacencySets.AddAllTriangles(mData.triangles);
        SmoothFilter.AdjSetsSmoother(mData, adjacencySets);
        elapsed = Time.realtimeSinceStartup - bSmooth;
        Debug.Log("Smoothing time: " + elapsed.ToString());

        ProperlyCalculateNormals(mData);

        // Necessary for electrostatic fields isosurfaces
        Debug.Log(threshold.ToString());
        if (threshold < 0)
        {
            FlipTriangles(mData);
        }

        Splitting   splitting = new Splitting();
        List <Mesh> meshes    = splitting.Split(mData);

        CreateSurfaceObjects(meshes);
    }
    void Update()
    {
        var offset = noiseMove * Time.time;
        var dx     = Vector3.right * (noiseScale / division);
        var dy     = Vector3.up * (noiseScale / division);
        var dz     = Vector3.forward * (noiseScale / division);

        for (var iz = 0; iz < division; iz++)
        {
            for (var iy = 0; iy < division; iy++)
            {
                for (var ix = 0; ix < division; ix++)
                {
                    var pos = dx * ix + dy * iy + dz * iz + offset;
                    voxels [ix, iy, iz] = Perlin.Fbm(pos, octave) * noise + 1.0f * iz / division;
                }
            }
        }

        for (var iz = 1; iz < division - 1; iz++)
        {
            for (var iy = 1; iy < division - 1; iy++)
            {
                for (var ix = 1; ix < division - 1; ix++)
                {
                    var gx = voxels[ix + 1, iy, iz] - voxels[ix - 1, iy, iz];
                    var gy = voxels[ix, iy + 1, iz] - voxels[ix, iy - 1, iz];
                    var gz = voxels[ix, iy, iz + 1] - voxels[ix, iy, iz - 1];
                    gradient[ix, iy, iz] = new Vector3(gx, gy, gz);
                }
            }
        }

        var oldMesh = meshFilter.sharedMesh;

        MarchingCubes.SetTarget(target);

        var mesh = MarchingCubes.CreateMesh(voxels, gradient);

        meshFilter.sharedMesh = mesh;

        if (oldMesh != null)
        {
            Destroy(oldMesh);
        }
    }
Example #8
0
    void Start()
    {
        currentTarget = target;
        m_perlin      = new PerlinNoise(2);

        //Target is the value that represents the surface of mesh
        //For example the perlin noise has a range of -1 to 1 so the mid point is were we want the surface to cut through
        //The target value does not have to be the mid point it can be any value with in the range
        MarchingCubes.SetTarget(target);

        //Winding order of triangles use 2,1,0 or 0,1,2
        MarchingCubes.SetWindingOrder(0, 1, 2);

        //Set the mode used to create the mesh
        //Cubes is faster and creates less verts, tetrahedrons is slower and creates more verts but better represents the mesh surface
        //MarchingCubes.SetModeToCubes();
        MarchingCubes.SetModeToCubes();

        //The size of voxel array. Be carefull not to make it to large as a mesh in unity can only be made up of 65000 verts
        int width  = 32;
        int height = 32;
        int length = 32;

        voxels = new float[width, height, length];

        //Fill voxels with values. Im using perlin noise but any method to create voxels will work
        CalcVoxels(width, height, length);

        Mesh mesh = MarchingCubes.CreateMesh(voxels);

        //The diffuse shader wants uvs so just fill with a empty array, there not actually used
        mesh.uv = new Vector2[mesh.vertices.Length];
        mesh.RecalculateNormals();

        m_mesh = new GameObject("Mesh");
        m_mesh.AddComponent <MeshFilter>();
        m_mesh.AddComponent <MeshRenderer>();
        m_mesh.GetComponent <Renderer>().material = m_material;
        m_mesh.GetComponent <MeshFilter>().mesh   = mesh;
        //Center mesh
        m_mesh.transform.localPosition = new Vector3(-32 / 2, -32 / 2, -32 / 2);
    }
Example #9
0
    void Update()
    {
        time += Time.deltaTime;

        center1 = new Vector3(Mathf.Cos(Time.time * 3.14f) * 16 + 16, 32, Mathf.Sin(Time.time * 3.14f) * 16 + 16);

        if (time > 1 / 30f)
        {
            time         -= 1 / 30f;
            currentTarget = target;
            MarchingCubes.SetTarget(target);
            CalcVoxels(32, 32, 32);
            Mesh mesh = MarchingCubes.CreateMesh(voxels);

            //The diffuse shader wants uvs so just fill with a empty array, there not actually used
            mesh.uv = new Vector2[mesh.vertices.Length];
            mesh.RecalculateNormals();
            DestroyImmediate(m_mesh.GetComponent <MeshFilter>().sharedMesh, true);
            m_mesh.GetComponent <MeshFilter>().mesh = mesh;
        }
    }
Example #10
0
    public void Build()
    {
        float[,,] voxels = new float[size + 1, size + 1, size + 1];
        int   height;
        float hFactor;

        for (int z = 0; z < size + 1; z++)
        {
            for (int y = 0; y < size + 1; y++)
            {
                height  = yi + y;
                hFactor = (float)(maxHeight - 2 * height) / (float)maxHeight;
                for (int x = 0; x < size + 1; x++)
                {
                    voxels[x, y, z] = -hFactor + m_perlin.FractalNoise3D((float)(xi + x), (float)(yi + y), (float)(zi + z), 3, freq, 1.0f);
                }
            }
        }

        mesh = MarchingCubes.CreateMesh(voxels);

        //UV MAPPING
        Vector3[] vertices = mesh.vertices;
        Vector2[] uvs      = new Vector2[mesh.vertices.Length];
        for (int i = 0; i < uvs.Length; i++)
        {
            uvs[i] = new Vector2(vertices[i].x, vertices[i].z);
        }
        mesh.uv = uvs;
        //NORMALS
        mesh.RecalculateNormals();
        //TANGENTS
        TangentSolver.Solve(mesh);
        mesh.Optimize();
        AssetDatabase.CreateAsset(mesh, "Assets/Resources/meshes/mesh" + xi + yi + zi + ".asset");
        //GAMEOBJECT SETUP
        m_mesh.GetComponent <MeshFilter>().mesh = mesh;
        m_mesh.transform.localPosition          = gPos;
        m_mesh.AddComponent <MeshCollider>();
    }
Example #11
0
    //creates the mesh from the voxel data and assigns it to the mesh filter and mesh collider
    public override void Render()
    {
        //if(Time.time<10)
        //{
        MeshBuilder mb = MarchingCubes.CreateMesh(voxVals, voxSubs);

        //build all the skirts using marching squares with the edge values
        if (scale != 1)
        {
            for (int i = 0; i < 6; i++)
            {
                float[,] slice = new float[chunkSize + 1, chunkSize + 1];
                Sub[,] subs    = new Sub[chunkSize, chunkSize];
                for (int x = 0; x < chunkSize + 1; x++)
                {
                    for (int y = 0; y < chunkSize + 1; y++)
                    {
                        switch (i)
                        {
                        case 0:
                            slice[x, y] = voxVals[x, y, 0];

                            if (x < chunkSize && y < chunkSize)
                            {
                                subs[x, y] = voxSubs[x, y, 0];
                            }
                            break;

                        case 1:
                            slice[x, y] = voxVals[chunkSize - x, y, chunkSize];
                            if (x < chunkSize && y < chunkSize)
                            {
                                subs[x, y] = voxSubs[chunkSize - 1 - x, y, chunkSize - 1];
                            }
                            break;

                        case 2:
                            slice[x, y] = voxVals[0, y, chunkSize - x];
                            if (x < chunkSize && y < chunkSize)
                            {
                                subs[x, y] = voxSubs[0, y, chunkSize - 1 - x];
                            }
                            break;

                        case 3:
                            slice[x, y] = voxVals[chunkSize, y, x];
                            if (x < chunkSize && y < chunkSize)
                            {
                                subs[x, y] = voxSubs[chunkSize - 1, y, x];
                            }

                            break;

                        case 4:
                            slice[x, y] = voxVals[x, chunkSize, y];
                            if (x < chunkSize && y < chunkSize)
                            {
                                subs[x, y] = voxSubs[x, chunkSize - 1, y];
                            }
                            break;

                        case 5:
                            slice[x, y] = voxVals[x, 0, chunkSize - y];
                            if (x < chunkSize && y < chunkSize)
                            {
                                subs[x, y] = voxSubs[x, 0, chunkSize - 1 - y];
                            }
                            break;

                        default:
                            break;
                        }
                    }
                }

                switch (i)
                {
                case 0:
                    mb.addMesh(MarchingSquares.buildMesh(slice, subs), Vector3.zero, Quaternion.identity);
                    break;

                case 1:
                    mb.addMesh(MarchingSquares.buildMesh(slice, subs), new Vector3(chunkSize, 0, chunkSize), Quaternion.Euler(0, 180, 0));
                    break;

                case 2:
                    mb.addMesh(MarchingSquares.buildMesh(slice, subs), new Vector3(0, 0, chunkSize), Quaternion.Euler(0, 90, 0));
                    break;

                case 3:
                    mb.addMesh(MarchingSquares.buildMesh(slice, subs), new Vector3(chunkSize, 0, 0), Quaternion.Euler(0, -90, 0));
                    break;

                case 4:
                    mb.addMesh(MarchingSquares.buildMesh(slice, subs), new Vector3(0, chunkSize, 0), Quaternion.Euler(90, 0, 0));
                    break;

                case 5:
                    mb.addMesh(MarchingSquares.buildMesh(slice, subs), new Vector3(0, 0, chunkSize), Quaternion.Euler(-90, 0, 0));
                    break;

                default:
                    break;
                }
            }
        }



        Mesh mesh = mb.getMesh();

        //Mesh mesh = MarchingCubes.CreateMesh(voxVals, voxType);
        //Mesh mesh = MarchingCubes2.CreateMesh(voxVals);
        //mesh.RecalculateNormals();//not sure what this does at the moment
        filter.mesh = mesh;

        //only add colliders for level 0 terrain objects (scale 1)
        if (scale == 1)
        {
            //coll = gameObject.AddComponent<MeshCollider>();
            coll.sharedMesh = mesh;
        }
        //transform.position.localScale = new Vector3(scale, scale, scale);
        //}
    }
Example #12
0
        //args0 - filepath
        //args1 - resolution
        //args2 - xaxis
        //args3 - yaxis
        //args4 - zaxis
        //args5 - isoLevel
        public static void Main(string[] args)
        {
            DebugLog.resetLog();
            DebugLog.setDebug(true);
            int   resolution;
            float isoLevel;

            #region cmd args handling
            if (args.Length > 6 || args.Length < 1)
            {
                Console.Out.WriteLine(numArgs);
                Console.In.ReadLine();
                return;
            }
            if (args.Length == 1 && (args[0].ToLower() == "--help" || args[0].ToLower() == "help" || args[0].ToLower() == "?" || args[0].ToLower() == "h"))
            {
                Console.Out.WriteLine(help);
                Console.In.ReadLine();
                return;
            }

            if (!File.Exists(args[0]))
            {
                DebugLog.logConsole("Could not find file. Are you sure you are passing in the right string?");
                throw new Exception("Could not find file. Are you sure you are passing in the right string?");
            }
            try
            {
                resolution = Int32.Parse(args[1]);
            }
            catch
            {
                DebugLog.logConsole("Resolution not a number");
                throw new Exception("Resolution not a number");
            }
            try
            {
                isoLevel = float.Parse(args[5]);
            }
            catch
            {
                DebugLog.logConsole("IsoLevel is not a float");
                throw new Exception("IsoLevel is not a float");
            }
            if (resolution < 1)
            {
                DebugLog.logConsole("Resolution cannot be less than 1");
                throw new Exception("Resolution cannot be less than 1");
            }
            #endregion
            SingularMesh mcAlg       = new SingularMesh();
            string       dir         = Directory.GetParent(Directory.GetParent(Directory.GetParent(args[0]).FullName).FullName).Name;
            List <float> minMaxArray = generateMinMaxArray(new string[] { args[2], args[3], args[4] }, dir);


            VoxelArray array = mcAlg.createPointCloud(args[0], resolution, args[2], args[3], args[4], minMaxArray.ToArray());
            //VoxelArray.WriteFloatArray(mcAlg.outputPath + ".FARA", array);
            MarchingCubes test = new MarchingCubes();
            test.SetTarget(isoLevel);
            IM intermediate = test.CreateMesh(array.toFloatArray());
            if (intermediate.verts.Count > 64999)
            {
                IM[] divs = intermediate.divideIntoSmall();
                for (int i = 0; i < divs.Length; i++)
                {
                    divs[i].WriteIntermediateToFile(mcAlg.outputPath + "_" + i + ".IMF");
                }
            }
            else
            {
                intermediate.WriteIntermediateToFile(mcAlg.outputPath + ".IMF");
            }
        }
    public void CreateMesh(Material mat)
    {
        //float startTime = Time.realtimeSinceStartup;

        Mesh mesh = MarchingCubes.CreateMesh(m_voxels, 2, 2);

        if (mesh == null)
        {
            return;
        }

        int size = mesh.vertices.Length;

        if (m_normals != null)
        {
            Vector3[] normals = new Vector3[size];
            Vector3[] verts   = mesh.vertices;

            //Each verts in the mesh generated is its position in the voxel array
            //and i use this to find what the normal at this position.
            //The verts are not at whole numbers so i use trilinear interpolation
            //to find the normal for that position

            for (int i = 0; i < size; i++)
            {
                normals[i] = TriLinearInterpNormal(verts[i]);
            }

            mesh.normals = normals;
        }
        else
        {
            mesh.RecalculateNormals();
        }

        Color[]   control     = new Color[size];
        Vector3[] meshNormals = mesh.normals;

        for (int i = 0; i < size; i++)
        {
            //This creates a control map used to texture the mesh based on the slope
            //of the vert.
            float dpUp = Vector3.Dot(meshNormals[i], Vector3.up);

            //Red channel is the sand on flat areas
            float R = (Mathf.Max(0.0f, dpUp) < 0.8f) ? 0.0f : 1.0f;
            //Green channel is the gravel on the sloped areas
            float G = Mathf.Pow(Mathf.Abs(dpUp), 2.0f);

            //Whats left end up being the rock face on the vertical areas

            control[i] = new Color(R, G, 0, 0);
        }

        //May as well store in colors
        mesh.colors = control;

        m_mesh = new GameObject("Voxel Mesh " + m_pos.x.ToString() + " " + m_pos.y.ToString() + " " + m_pos.z.ToString());
        m_mesh.AddComponent <MeshFilter>();
        m_mesh.AddComponent <MeshRenderer>();
        m_mesh.renderer.material = mat;
        m_mesh.GetComponent <MeshFilter>().mesh = mesh;
        m_mesh.transform.localPosition          = m_pos;

        MeshCollider collider = m_mesh.AddComponent <MeshCollider>();

        collider.sharedMesh = mesh;

        //Debug.Log("Create mesh time = " + (Time.realtimeSinceStartup-startTime).ToString() );
    }
Example #14
0
        public void createMesh(ConcurrentStack <string> stack, int resolution, string[] axis, float[] minMaxArray, float isoLevel)
        {
            DebugLog.logConsole("Mesh creation thread started");
            totalMeshes = stack.Count;
            while (!stack.IsEmpty)
            {
                //If we fail popping, we may be attempting to access
                //at the same time as someone else. This means the stack
                //might have emptied since we last checked so we check again
                if (!stack.TryPop(out string file))
                {
                    continue;
                }
                //If we got this far, then we have a file
                DebugLog.logConsole(file + " has been selected");
                SingularMesh alg   = alg = new SingularMesh();
                VoxelArray   array = null;
                try
                {
                    array = alg.createPointCloud(file, resolution, axis[0], axis[1], axis[2], minMaxArray);
                }
                catch (Exception) { DebugLog.logConsole("Threading failed at array creation for " + file); }
                MarchingCubes test = new MarchingCubes();
                test.SetTarget(isoLevel);
                IM intermediate = null;
                try
                {
                    intermediate = test.CreateMesh(array.toFloatArray());
                }
                catch (Exception) { DebugLog.logConsole("Threading failed at intermediate creation for " + file); }
                try
                {
                    if (intermediate.verts.Count > 64999)
                    {
                        DebugLog.logConsole("Dividing " + file);
                        IM[] divs = intermediate.divideIntoSmall();
                        for (int i = 0; i < divs.Length; i++)
                        {
                            divs[i].WriteIntermediateToFile(alg.outputPath + "_" + i + ".IMF");
                        }
                    }
                    else
                    {
                        intermediate.WriteIntermediateToFile(alg.outputPath + ".IMF");
                    }
                    DebugLog.logConsole(alg.outputPath + ".IMF");
                }
                catch (Exception e) { DebugLog.logConsole(e.Message + "\n" + e.StackTrace + "\n" + file + "\n--------------------------------------------"); }

                array        = null;
                alg          = null;
                intermediate = null;

                try
                {
                    double perc = ((double)(totalMeshes - stack.Count) / (double)totalMeshes) * 100;
                    PercentageClass.UpdatePercentage("Creating Mesh", perc);
                    DebugLog.logConsole("Mesh Creation: " + perc.ToString("#.##") + "% total");
                }
                catch (Exception) { DebugLog.logConsole("Threading failed at percentage writing for " + file); }
            }
        }
Example #15
0
    // Use this for initialization
    void Awake()
    {
        //Random.seed = 2;

        //Target is the value that represents the surface of mesh
        //For example the perlin noise has a range of -1 to 1 so the mid point is were we want the surface to cut through
        //The target value does not have to be the mid point it can be any value with in the range
        MarchingCubes.SetTarget(-0.8825f);

        //Winding order of triangles use 2,1,0 or 0,1,2
        MarchingCubes.SetWindingOrder(2, 1, 0);

        //Set the mode used to create the mesh
        //Cubes is faster and creates less verts, tetrahedrons is slower and creates more verts but better represents the mesh surface
        MarchingCubes.SetModeToCubes();
        //MarchingCubes.SetModeToTetrahedrons();

        //the index of the closest voronoi seed
        int[, ,] voxelVoronoi = new int[_width + 1, _height + 1, _length + 1];
        //smoothmin distances
        float[, ,] voxelSmoothMin = new float[_width + 1, _height + 1, _length + 1];
        //final values
        float[, ,] voxels = new float[_width + 1, _height + 1, _length + 1];
        int x, y, z;

        float start = Time.realtimeSinceStartup;

        Vector3[] seeds = new Vector3[numSeeds];

        for (int i = 0; i < seeds.Length; i++)
        {
            seeds[i] = new Vector3(Random.value * (_width + 1), Random.value * (_height + 1), Random.value * (_length + 1));
        }

        //populate the voronoi/smoothmin arrays
        for (x = 0; x < _width + 1; x++)
        {
            for (y = 0; y < _height + 1; y++)
            {
                for (z = 0; z < _length + 1; z++)
                {
                    Vector3 testPoint = new Vector3(x, y, z);

                    int   closestSeedIndex = 0;
                    float distance         = distanceOfMirrors(testPoint, seeds[0]);

                    for (int i = 1; i < seeds.Length; i++)
                    {
                        float queryDistance = distanceOfMirrors(testPoint, seeds[i]);
                        if (queryDistance < distance)
                        {
                            distance         = queryDistance;
                            closestSeedIndex = i;
                        }
                    }

                    voxelVoronoi[x, y, z] = closestSeedIndex;

                    voxelSmoothMin[x, y, z] = sminDistance(testPoint, seeds, k);
                }
            }
        }
        int yesCount = 0;
        int noCount  = 0;

        //Create a mesh for each seed
        for (int i = 0; i < seeds.Length; i++)
        {
            //Fill voxels with values. Im using perlin noise but any method to create voxels will work
            for (x = 0; x < _width + 1; x++)
            {
                for (y = 0; y < _height + 1; y++)
                {
                    for (z = 0; z < _length + 1; z++)
                    {
                        if (voxelVoronoi[x, y, z] != i) //we are not in the correct voronoi cell
                        {
                            voxels[x, y, z] = 1;        //outside
                            noCount++;
                        }
                        else
                        {
                            Vector3 queryPoint = new Vector3(x, y, z);
                            float   distance   = distanceOfMirrors(queryPoint, seeds[i]);
                            distance        = voxelSmoothMin[x, y, z] / distance;
                            voxels[x, y, z] = 1 - (distance * 2); //transform from 0..1 to 1..-1
                            yesCount++;
                        }
                    }
                }
            }

            Mesh mesh = MarchingCubes.CreateMesh(voxels, _scale);

            //The diffuse shader wants uvs so just fill with a empty array, there not actually used
            mesh.RecalculateNormals();

            m_mesh = Instantiate(meshPrefab);
            m_mesh.GetComponent <MeshFilter>().mesh = mesh;

            //Center mesh
            //m_mesh.transform.localPosition = scale * new Vector3(-width / 2, -height / 2, -length / 2);

            //meshcolliders need to be added as components to properly initialize
            //so I can't put it in the prefab
            m_mesh.AddComponent <MeshCollider>();

            ProceduralDuplication.AddToDuplicate(m_mesh);
        }
        Debug.Log("Time take = " + (Time.realtimeSinceStartup - start) * 1000.0f);
    }