bool CrearVoxel(float px, float py, float pz, string pnombre) { //Creacion del voxel la db TerrainVolumeData volumeData = VolumeData.CreateEmptyVolumeData<TerrainVolumeData>(new Region(0, 0, 0, Ancho - 1, Altura - 1, Profundidad - 1), @DirectorioLocal + NombreMapa+"/"+pnombre + ".vdb"); MaterialSet materialSet = new MaterialSet(); double DifAltura = Altura - Altura * 0.9; for (int z = 0; z < Profundidad; z++) { for (int y = 0; y < (int)DifAltura; y++) { for (int x = 0; x < Ancho; x++) { materialSet.weights[0] = (byte)255; volumeData.SetVoxel(x, y, z, materialSet); } } } volumeData.CommitChanges(); //asignacion del prefab GameObject VoxelTemporal = Instantiate(VoxelPrefab) as GameObject; VoxelTemporal.name = pnombre; VoxelTemporal.transform.GetChild(0).GetComponent<TerrainVolume>().data=volumeData; VoxelTemporal.transform.GetChild(1).Translate(Ancho/2, (float)DifAltura, Profundidad/2); VoxelTemporal.transform.position = new Vector3(px,py,pz); MapaVoxels.Add(pnombre, VoxelTemporal); VoxelActual = VoxelTemporal; ClaveActual = pnombre; return false; }
private void CreateAVolumeDB() { System.Random randomIntGenerator = new System.Random(); int randomInt = randomIntGenerator.Next(); string saveLocation = Paths.voxelDatabases + "/Matt-test" + randomInt + ".vdb"; var volumeBounds = new Region(Vector3i.zero, size); ColoredCubesVolumeData data = VolumeData.CreateEmptyVolumeData <ColoredCubesVolumeData>(volumeBounds, null); // saveLocation); var coloredCubeVolume = GetComponent <ColoredCubesVolume>(); coloredCubeVolume.data = data; float invRockScale = 1f / noiseScale; MaterialSet materialSet = new MaterialSet(); // It's best to create these outside of the loop. QuantizedColor red = new QuantizedColor(255, 0, 0, 255); QuantizedColor blue = new QuantizedColor(122, 122, 255, 255); QuantizedColor gray = new QuantizedColor(127, 127, 127, 255); QuantizedColor white = new QuantizedColor(255, 255, 255, 255); // Iterate over every voxel of our volume for (int z = 0; z < size.x; z++) { for (int y = 0; y < size.y; y++) { for (int x = 0; x < size.z; x++) { // 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; // range -1 to +1 float simplexNoiseValue = SimplexNoise.Noise.Generate(sampleX, sampleY, sampleZ); simplexNoiseValue -= y / size.y * .75f; // mul by 5 and clamp? //simplexNoiseValue *= 5f; //simplexNoiseValue = Mathf.Clamp(simplexNoiseValue, -.5f, .5f); //simplexNoiseValue += .5f; //simplexNoiseValue *= 255; if (simplexNoiseValue > 0f) { data.SetVoxel(x, y, z, blue); } } } } data.CommitChanges(); Debug.Log("Voxel db saved to: " + saveLocation); }
/// Sets the material weights of the specified position. /** * \param x The 'x' position of the voxel to set. * \param y The 'y' position of the voxel to set. * \param z The 'z' position of the voxel to set. * \param materialSet The material weights the voxel should be set to. */ public void SetVoxel(int x, int y, int z, MaterialSet materialSet) { // The initialization can fail (bad filename, database locked, etc), so the volume handle could still be null. if(volumeHandle.HasValue) { if(x >= enclosingRegion.lowerCorner.x && y >= enclosingRegion.lowerCorner.y && z >= enclosingRegion.lowerCorner.z && x <= enclosingRegion.upperCorner.x && y <= enclosingRegion.upperCorner.y && z <= enclosingRegion.upperCorner.z) { CubiquityDLL.SetVoxel(volumeHandle.Value, x, y, z, materialSet); } } }
/// Sets the material weights of the specified position. /** * \param x The 'x' position of the voxel to set. * \param y The 'y' position of the voxel to set. * \param z The 'z' position of the voxel to set. * \param materialSet The material weights the voxel should be set to. */ public void SetVoxel(int x, int y, int z, MaterialSet materialSet) { // The initialization can fail (bad filename, database locked, etc), so the volume handle could still be null. if (volumeHandle.HasValue) { if (x >= enclosingRegion.lowerCorner.x && y >= enclosingRegion.lowerCorner.y && z >= enclosingRegion.lowerCorner.z && x <= enclosingRegion.upperCorner.x && y <= enclosingRegion.upperCorner.y && z <= enclosingRegion.upperCorner.z) { CubiquityDLL.SetVoxel(volumeHandle.Value, x, y, z, materialSet); } } }
// Use this for initialization void Start () { // Dimensions of our volume. int width = 64; int height = 64; int depth = 64; TerrainVolumeData volumeData = VolumeData.CreateEmptyVolumeData<TerrainVolumeData>(new Region(0, 0, 0, width - 1, height - 1, depth - 1)); float noiseScale = 32.0f; float invNoiseScale = 1.0f / noiseScale; // Let's keep the allocation outside of the loop. MaterialSet materialSet = new MaterialSet(); // Iterate over each voxel and assign a value to it for (int z = 0; z < depth; z++) { for (int y = 0; y < height; y++) { 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 * invNoiseScale; float sampleY = (float)y * invNoiseScale; float sampleZ = (float)z * invNoiseScale; // 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 material weights need to be in the range 0 - 255. simplexNoiseValue += 1.0f; // Now it's 0.0 to 2.0 simplexNoiseValue *= 127.5f; // Now it's 0.0 to 255.0 materialSet.weights[0] = (byte)simplexNoiseValue; // We can now write our computed voxel value into the volume. volumeData.SetVoxel(x, y, z, materialSet); } } } //Add the required volume component. TerrainVolume terrainVolume = gameObject.AddComponent<TerrainVolume>(); // Set the provided data. terrainVolume.data = volumeData; // Add the renderer gameObject.AddComponent<TerrainVolumeRenderer>(); }
/// Gets the material weights of the specified position. /** * \param x The 'x' position of the voxel to get. * \param y The 'y' position of the voxel to get. * \param z The 'z' position of the voxel to get. * \return The material weights of the voxel. */ public MaterialSet GetVoxel(int x, int y, int z) { // The initialization can fail (bad filename, database locked, etc), so the volume handle could still be null. MaterialSet materialSet; if (volumeHandle.HasValue) { CubiquityDLL.GetVoxelMC(volumeHandle.Value, x, y, z, out materialSet); } else { materialSet = new MaterialSet(); } return(materialSet); }
TerrainVolumeData crearVolumenData(string direccion) { TerrainVolumeData volumeData = VolumeData.CreateEmptyVolumeData<TerrainVolumeData>(new Region(0, 0, 0, width - 1, height - 1, depth - 1),@"C:\CS-UCSP\Evolcraft\"+ direccion+".vdb"); numObjects++; //Si no recibe un path, crea solo un temporal. Caso contrario, evalua si es un path relativo o no... MaterialSet materialSet = new MaterialSet(); for (int z = 0; z < depth; z++) { for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { materialSet.weights[0] = (byte)255; volumeData.SetVoxel(x, y, z, materialSet); } } } volumeData.CommitChanges (); return volumeData; }
void DestroyVoxels(int xPos, int yPos, int zPos, int range) { // Initialise outside the loop, but we'll use it later. int rangeSquared = range * range; MaterialSet emptyMaterialSet = new MaterialSet(); // Iterage over every voxel in a cubic region defined by the received position (the center) and // the range. It is quite possible that this will be hundreds or even thousands of voxels. for(int z = zPos - range; z < zPos + range; z++) { for(int y = yPos - range; y < yPos + range; y++) { for(int x = xPos - range; x < xPos + range; x++) { // Compute the distance from the current voxel to the center of our explosion. int xDistance = x - xPos; int yDistance = y - yPos; int zDistance = z - zPos; // Working with squared distances avoids costly square root operations. int distSquared = xDistance * xDistance + yDistance * yDistance + zDistance * zDistance; // We're iterating over a cubic region, but we want our explosion to be spherical. Therefore // we only further consider voxels which are within the required range of our explosion center. // The corners of the cubic region we are iterating over will fail the following test. if(distSquared < rangeSquared) { terrainVolume.data.SetVoxel(x, y, z, emptyMaterialSet); } } } } range += 2; TerrainVolumeEditor.BlurTerrainVolume(terrainVolume, new Region(xPos - range, yPos - range, zPos - range, xPos + range, yPos + range, zPos + range)); //TerrainVolumeEditor.BlurTerrainVolume(terrainVolume, new Region(xPos - range, yPos - range, zPos - range, xPos + range, yPos + range, zPos + range)); //TerrainVolumeEditor.BlurTerrainVolume(terrainVolume, new Region(xPos - range, yPos - range, zPos - range, xPos + range, yPos + range, zPos + range)); }
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 + "'"); }
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 + "'"); }
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 + "'"); }
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); } } } }
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); } } } }
// Use this for initialization void Start() { // The size of the volume we will generate int width = 128; int height = 32; int depth = 128; // FIXME - Where should we delete this? /// [DoxygenSnippet-CreateEmptyTerrainVolumeData] //Create an empty TerrainVolumeData with dimensions width * height * depth TerrainVolumeData data = VolumeData.CreateEmptyVolumeData<TerrainVolumeData>(new Region(0, 0, 0, width-1, height-1, depth-1)); /// [DoxygenSnippet-CreateEmptyTerrainVolumeData] TerrainVolume volume = GetComponent<TerrainVolume>(); TerrainVolumeRenderer volumeRenderer = GetComponent<TerrainVolumeRenderer>(); volume.data = data; // This example looks better if we adjust the scaling factors on the textures. volumeRenderer.material.SetTextureScale("_Tex0", new Vector2(0.062f, 0.062f)); volumeRenderer.material.SetTextureScale("_Tex1", new Vector2(0.125f, 0.125f)); volumeRenderer.material.SetTextureScale("_Tex2", new Vector2(0.125f, 0.125f)); // At this point our volume is set up and ready to use. The remaining code is responsible // for iterating over all the voxels and filling them according to our noise functions. // 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 = height-1; y > 0; 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); // We want to fade off the noise towards the top of the volume (so that the rocks don't go // up to the sky) adn add extra material near the bottom of the volume (to create a floor). // This altitude value is initially in the range from 0 to +1. float altitude = (float)(y + 1) / (float)height; // Map the altitude to the range -1.0 to +1.0... altitude = (altitude * 2.0f) - 1.0f; // Subtract the altitude from the noise. This adds // material near the ground and subtracts it higher up. simplexNoiseValue -= altitude; // After combining our noise value and our altitude we now have values between -2.0 and 2.0. // 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 third 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[2] = (byte)simplexNoiseValue; // Lastly we write soil or grass voxels into the volume to create a level floor between the rocks. // This means we want to set the sum of the materials to 255 if the voxel is below the floor height. // We don't want to interfere with the rocks on the transition between the material so we work out // how much extra we have to add to get to 255 and then add that to either soil or grass. byte excess = (byte)(255 - materialSet.weights[2]); if(y < 11) { // Add to soil material channel. materialSet.weights[1] = excess; } else if(y < 12) { // Add to grass material channel. materialSet.weights[0] = excess; } // We can now write our computed voxel value into the volume. data.SetVoxel(x, y, z, materialSet); } } } }