public override void OnInspectorGUI() { TerrainVolumeData data = target as TerrainVolumeData; EditorGUILayout.LabelField("Full path to voxel database:", EditorStyles.boldLabel); EditorGUILayout.HelpBox(data.fullPathToVoxelDatabase, MessageType.None); }
void OnWizardCreate() { TerrainVolumeData data = VolumeDataAsset.CreateEmptyVolumeData <TerrainVolumeData>(new Region(0, 0, 0, width - 1, height - 1, depth - 1)); if (generateFloor) { // Create some ground in the terrain so it shows up in the editor. // Soil as a base (mat 1) and then a couple of layers of grass (mat 2). TerrainVolumeGenerator.GenerateFloor(data, 6, (uint)1, 8, (uint)2); } }
static void CreateTerrainVolume() { int width = 128; int height = 32; int depth = 128; TerrainVolumeData data = VolumeDataAsset.CreateEmptyVolumeData <TerrainVolumeData>(new Region(0, 0, 0, width - 1, height - 1, depth - 1)); // Create some ground in the terrain so it shows up in the editor. // Soil as a base (mat 1) and then a couple of layers of grass (mat 2). TerrainVolumeGenerator.GenerateFloor(data, 6, (uint)1, 8, (uint)2); // Now create the terrain game object from the data. GameObject terrain = TerrainVolume.CreateGameObject(data, true, true); // And select it, so the user can get straight on with editing. Selection.activeGameObject = terrain; }
/// Convinience method for creating a GameObject with a set of terrain components attached. /** * Adding a volume to a scene requires creating a GameObject and then attching the required Cubiquity components such a renderer and a * collider. This method simply automates the process and also attaches the provided volume data. * * \param data The volume data which should be attached to the construced volume. * \param addRenderer Specifies whether a renderer component should be added so that the volume is displayed. * \param addCollider Specifies whether a collider component should be added so that the volume can participate in collisions. */ public static GameObject CreateGameObject(TerrainVolumeData data, bool addRenderer, bool addCollider) { // Create our main game object representing the volume. GameObject terrainVolumeGameObject = new GameObject("Terrain Volume"); //Add the required volume component. TerrainVolume terrainVolume = terrainVolumeGameObject.GetOrAddComponent<TerrainVolume>(); // Set the provided data. terrainVolume.data = data; // Add the renderer and collider if desired. if(addRenderer) { terrainVolumeGameObject.AddComponent<TerrainVolumeRenderer>(); } if(addCollider) { terrainVolumeGameObject.AddComponent<TerrainVolumeCollider>(); } // Return the created object return terrainVolumeGameObject; }
void Start() { int planetRadius = 60; // Randomize the filename incase the file already exists System.Random randomIntGenerator = new System.Random(); int randomInt = randomIntGenerator.Next(); string saveLocation = Paths.voxelDatabases + "/planet-" + randomInt + ".vdb"; Region volumeBounds = new Region(-planetRadius, -planetRadius, -planetRadius, planetRadius, planetRadius, planetRadius); TerrainVolumeData data = VolumeData.CreateEmptyVolumeData <TerrainVolumeData>(volumeBounds, saveLocation); // The numbers below control the thickness of the various layers. TerrainVolumeGenerator.GeneratePlanet(data, planetRadius, planetRadius - 1, planetRadius - 10, planetRadius - 35); // 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(); }
/// Convinience method for creating a GameObject with a set of terrain components attached. /** * Adding a volume to a scene requires creating a GameObject and then attching the required Cubiquity components such a renderer and a * collider. This method simply automates the process and also attaches the provided volume data. * * \param data The volume data which should be attached to the construced volume. * \param addRenderer Specifies whether a renderer component should be added so that the volume is displayed. * \param addCollider Specifies whether a collider component should be added so that the volume can participate in collisions. */ public static GameObject CreateGameObject(TerrainVolumeData data, bool addRenderer, bool addCollider) { // Create our main game object representing the volume. GameObject terrainVolumeGameObject = new GameObject("Terrain Volume"); //Add the required volume component. TerrainVolume terrainVolume = terrainVolumeGameObject.GetOrAddComponent <TerrainVolume>(); // Set the provided data. terrainVolume.data = data; // Add the renderer and collider if desired. if (addRenderer) { terrainVolumeGameObject.AddComponent <TerrainVolumeRenderer>(); } if (addCollider) { terrainVolumeGameObject.AddComponent <TerrainVolumeCollider>(); } // Return the created object return(terrainVolumeGameObject); }
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 GenerateFloor(TerrainVolumeData volumeData, int lowerLayerHeight, uint lowerLayerMaterial, int upperLayerHeight, uint upperLayerMaterial) { CubiquityDLL.GenerateFloor(volumeData.volumeHandle.Value, lowerLayerHeight, lowerLayerMaterial, upperLayerHeight, upperLayerMaterial); }
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); } } } }