void Create3DSimplexNoiseVolumeVDB()
        {
            // Randomize the filename incase the file already exists
            System.Random randomIntGenerator = new System.Random();
            int           randomInt          = randomIntGenerator.Next();
            string        saveLocation       = Paths.voxelDatabases + "/3d-simplex-noise-" + randomInt + ".vdb";

            // The size of the volume we will generate
            int width  = 256;
            int height = 256;
            int depth  = 256;

            TerrainVolumeData data = VolumeData.CreateEmptyVolumeData <TerrainVolumeData>(new Region(0, 0, 0, width - 1, height - 1, depth - 1), saveLocation);

            // This scale factor comtrols the size of the rocks which are generated.
            float rockScale    = 32.0f;
            float invRockScale = 1.0f / rockScale;

            // Let's keep the allocation outside of the loop.
            MaterialSet materialSet = new MaterialSet();

            // Iterate over every voxel of our volume
            for (int z = 0; z < depth; z++)
            {
                for (int y = 0; y < height; y++)
                {
                    for (int x = 0; x < width; x++)
                    {
                        // Make sure we don't have anything left in here from the previous voxel
                        materialSet.weights[0] = 0;
                        materialSet.weights[1] = 0;
                        materialSet.weights[2] = 0;

                        // Simplex noise is quite high frequency. We scale the sample position to reduce this.
                        float sampleX = (float)x * invRockScale;
                        float sampleY = (float)y * invRockScale;
                        float sampleZ = (float)z * invRockScale;

                        // Get the noise value for the current position.
                        // Returned value should be in the range -1 to +1.
                        float simplexNoiseValue = SimplexNoise.Noise.Generate(sampleX, sampleY, sampleZ);

                        // Cubiquity renders anything below the threshold as empty and anythng above as solid, but
                        // in general it is easiest if empty space is completly empty and solid space is completly
                        // solid. The exception to this is the region near our surface, where a gentle transition helps
                        // obtain smooth shading. By scaling by a large number and then clamping we achieve this effect
                        // of making most voxels fully solid or fully empty except near the surface..
                        simplexNoiseValue *= 5.0f;
                        simplexNoiseValue  = Mathf.Clamp(simplexNoiseValue, -0.5f, 0.5f);

                        // Go back to the range 0.0 to 1.0;
                        simplexNoiseValue += 0.5f;

                        // And then to 0 to 255, ready to convert into a byte.
                        simplexNoiseValue *= 255;

                        // Write the final value value into the first material channel (the one with the rock texture).
                        // The value being written is usually 0 (empty) or 255 (solid) except around the transition.
                        materialSet.weights[0] = (byte)simplexNoiseValue;

                        // We can now write our computed voxel value into the volume.
                        data.SetVoxel(x, y, z, materialSet);
                    }
                }
            }

            // We need to commit this so that the changes made by the previous,line are actually written
            // to the voxel database. Otherwise they are just kept in temporary storage and will be lost.
            data.CommitChanges();

            Debug.Log("Voxel database has been saved to '" + saveLocation + "'");
        }
		public static void GeneratePlanet(TerrainVolumeData volumeData, float planetRadius, float mat1Radius, float mat2Radius, float mat3Radius)
		{
			Region volumeBounds = volumeData.enclosingRegion;
			MaterialSet materialSet = new MaterialSet();
			
			for(int z = volumeBounds.lowerCorner.z; z <= volumeBounds.upperCorner.z; z++)
			{
				for(int y = volumeBounds.lowerCorner.y; y <= volumeBounds.upperCorner.y; y++)
				{
					for(int x = volumeBounds.lowerCorner.x; x <= volumeBounds.upperCorner.x; x++)
					{
						// We are going to compute our density value based on the distance of a voxel from the center of our planet.
						// This is a function which (by definition) is zero at the center of the planet and has a smoothly increasing
						// value as we move away from the center.
						//
						// Note: For efficiency we could probably adapt this to work with squared distances (thereby eliminating
						// the square root operation), but we'd like to keep this example as intuitive as possible.
						float distFromCenter = Mathf.Sqrt(x * x + y * y + z * z);
						
						// We actually want our volume to have high values in the center and low values as we move out, because our
						// eath should be a solid sphere surrounded by empty space. If we invert the distance then this is a step in
						// the right direction. We still have zero in the center, but lower (negative) values as we move out.
						float density = -distFromCenter;
						
						// By adding the 'planetRadius' we now have a function which starts at 'planetRadius' and still decreases as it
						// moves out. The function passes through zero at a distance of 'planetRadius' and then continues do decrease
						// as it gets even further out.
						density += planetRadius;
						
						// Ideally we would like our final density value to be '255' for voxels inside the planet and '0' for voxels
						// outside the planet. At the surface there should be a transition but this should occur not too quickly and
						// not too slowly, as both of these will result in a jagged appearance to the mesh.
						//
						// We probably want the transition to occur over a few voxels, whereas it currently occurs over 255 voxels
						// because it was derived from the distance. By scaling the density field we effectivly compress the rate
						// at which it changes at the surface. We also make the center much too high and the outside very low, but
						// we will clamp these to the corect range later.
						//
						// Note: You can try commenting out or changing the value on this line to see the effect it has.
						density *= 50;
						
						// Until now we've been defining our density field as if the threshold was at zero, with positive densities
						// being solid and negative densities being empty. But actually Cubiquity operates on the range 0 to 255, and
						// uses a threashold of 127 to decide where to place the generated surface.  Therefore we shift and clamp our
						// density value and store it in a byte.
						density += 127;						
						byte densityAsByte = (byte)(Mathf.Clamp(density, 0, 255));
						
						if(distFromCenter < mat3Radius)
						{
							materialSet.weights[0] = 0;
							materialSet.weights[1] = 0;
							materialSet.weights[2] = 0;
							materialSet.weights[3] = densityAsByte;
						}
						else if(distFromCenter < mat2Radius)
						{
							materialSet.weights[0] = 0;
							materialSet.weights[1] = 0;
							materialSet.weights[2] = densityAsByte;
							materialSet.weights[3] = 0;
						}
						else if(distFromCenter < mat1Radius)
						{
							materialSet.weights[0] = 0;
							materialSet.weights[1] = densityAsByte;
							materialSet.weights[2] = 0;
							materialSet.weights[3] = 0;
						}
						else //Surface material
						{
							materialSet.weights[0] = densityAsByte;
							materialSet.weights[1] = 0;
							materialSet.weights[2] = 0;
							materialSet.weights[3] = 0;
						}
						
						volumeData.SetVoxel(x, y, z, materialSet);
						
					}
				}
			}
		}
        void Create2DSimplexNoiseVolumeVDB()
        {
            // Randomize the filename incase the file already exists
            System.Random randomIntGenerator = new System.Random();
            int           randomInt          = randomIntGenerator.Next();
            string        saveLocation       = Paths.voxelDatabases + "/2d-simplex-noise-" + randomInt + ".vdb";

            // The size of the volume we will generate
            int width  = 256;
            int height = 32;
            int depth  = 256;

            TerrainVolumeData data = VolumeData.CreateEmptyVolumeData <TerrainVolumeData>(new Region(0, 0, 0, width - 1, height - 1, depth - 1), saveLocation);

            // This scale factor comtrols the size of the rocks which are generated.
            float rockScale    = 32.0f;
            float invRockScale = 1.0f / rockScale;

            // Let's keep the allocation outside of the loop.
            MaterialSet materialSet = new MaterialSet();

            // Iterate over every voxel of our volume
            for (int z = 0; z < depth; z++)
            {
                for (int x = 0; x < width; x++)
                {
                    // Simplex noise is quite high frequency. We scale the sample position to reduce this.
                    float sampleX = (float)x * invRockScale;
                    float sampleZ = (float)z * invRockScale;

                    // Get the noise value for the current position.
                    // Returned value should be in the range -1 to +1.
                    float simplexNoiseValue = SimplexNoise.Noise.Generate(sampleX, sampleZ);

                    simplexNoiseValue += 1.0f;
                    simplexNoiseValue *= 16.0f;

                    for (int y = 0; y < height; y++)
                    {
                        // Make sure we don't have anything left in here from the previous voxel
                        materialSet.weights[0] = 0;
                        materialSet.weights[1] = 0;
                        materialSet.weights[2] = 0;

                        if (y < simplexNoiseValue)
                        {
                            // Write the final value value into the first material channel (the one with the rock texture).
                            // The value being written is usually 0 (empty) or 255 (solid) except around the transition.
                            materialSet.weights[0] = (byte)255;
                        }

                        // We can now write our computed voxel value into the volume.
                        data.SetVoxel(x, y, z, materialSet);
                    }
                }
            }

            // We need to commit this so that the changes made by the previous,line are actually written
            // to the voxel database. Otherwise they are just kept in temporary storage and will be lost.
            data.CommitChanges();

            Debug.Log("Voxel database has been saved to '" + saveLocation + "'");
        }
        public static void GeneratePlanet(TerrainVolumeData volumeData, float planetRadius, float mat1Radius, float mat2Radius, float mat3Radius)
        {
            Region      volumeBounds = volumeData.enclosingRegion;
            MaterialSet materialSet  = new MaterialSet();

            for (int z = volumeBounds.lowerCorner.z; z <= volumeBounds.upperCorner.z; z++)
            {
                for (int y = volumeBounds.lowerCorner.y; y <= volumeBounds.upperCorner.y; y++)
                {
                    for (int x = volumeBounds.lowerCorner.x; x <= volumeBounds.upperCorner.x; x++)
                    {
                        // We are going to compute our density value based on the distance of a voxel from the center of our planet.
                        // This is a function which (by definition) is zero at the center of the planet and has a smoothly increasing
                        // value as we move away from the center.
                        //
                        // Note: For efficiency we could probably adapt this to work with squared distances (thereby eliminating
                        // the square root operation), but we'd like to keep this example as intuitive as possible.
                        float distFromCenter = Mathf.Sqrt(x * x + y * y + z * z);

                        // We actually want our volume to have high values in the center and low values as we move out, because our
                        // eath should be a solid sphere surrounded by empty space. If we invert the distance then this is a step in
                        // the right direction. We still have zero in the center, but lower (negative) values as we move out.
                        float density = -distFromCenter;

                        // By adding the 'planetRadius' we now have a function which starts at 'planetRadius' and still decreases as it
                        // moves out. The function passes through zero at a distance of 'planetRadius' and then continues do decrease
                        // as it gets even further out.
                        density += planetRadius;

                        // Ideally we would like our final density value to be '255' for voxels inside the planet and '0' for voxels
                        // outside the planet. At the surface there should be a transition but this should occur not too quickly and
                        // not too slowly, as both of these will result in a jagged appearance to the mesh.
                        //
                        // We probably want the transition to occur over a few voxels, whereas it currently occurs over 255 voxels
                        // because it was derived from the distance. By scaling the density field we effectivly compress the rate
                        // at which it changes at the surface. We also make the center much too high and the outside very low, but
                        // we will clamp these to the corect range later.
                        //
                        // Note: You can try commenting out or changing the value on this line to see the effect it has.
                        density *= 50;

                        // Until now we've been defining our density field as if the threshold was at zero, with positive densities
                        // being solid and negative densities being empty. But actually Cubiquity operates on the range 0 to 255, and
                        // uses a threashold of 127 to decide where to place the generated surface.  Therefore we shift and clamp our
                        // density value and store it in a byte.
                        density += 127;
                        byte densityAsByte = (byte)(Mathf.Clamp(density, 0, 255));

                        if (distFromCenter < mat3Radius)
                        {
                            materialSet.weights[0] = 0;
                            materialSet.weights[1] = 0;
                            materialSet.weights[2] = 0;
                            materialSet.weights[3] = densityAsByte;
                        }
                        else if (distFromCenter < mat2Radius)
                        {
                            materialSet.weights[0] = 0;
                            materialSet.weights[1] = 0;
                            materialSet.weights[2] = densityAsByte;
                            materialSet.weights[3] = 0;
                        }
                        else if (distFromCenter < mat1Radius)
                        {
                            materialSet.weights[0] = 0;
                            materialSet.weights[1] = densityAsByte;
                            materialSet.weights[2] = 0;
                            materialSet.weights[3] = 0;
                        }
                        else //Surface material
                        {
                            materialSet.weights[0] = densityAsByte;
                            materialSet.weights[1] = 0;
                            materialSet.weights[2] = 0;
                            materialSet.weights[3] = 0;
                        }

                        volumeData.SetVoxel(x, y, z, materialSet);
                    }
                }
            }
        }