namrog84
One of the things I wished I had when I started playing around with VF was some a little better of an idea of how to write and handle some basic traditional terrain generation with ore veins.

Inspired by a Minecraft like block worlds. 

My objectives of this prototype:
1. Make a basic procedural terrain generator extension for VF 
2. Learn how to make a traditionally looking block world that is composed of smaller blocks. 
3. Make the generator approximate top level grass and 'underground' materials
4. Make rudimentary 'ore deposits' underground



VoxelStudio_2018-01-23_17-31-22.png 

Each generated 'block' is a VF 4x4 tiny cube



// For building an extension
// though this could be refactored easily for a IVoxelLayer impl

extern "C" __declspec(dllexport) int __cdecl v1_getVoxels(
  char* object,
  VoxelFarm::API::CellId cell,
  double cellOrigin[3],
  double cellSize,
  double voxelSize,
  int blockSize,
  VoxelFarm::API::VoxelType* changeFlags,
  VoxelFarm::API::MaterialId* materials,
  VoxelFarm::API::Vector* vectors,
  bool& empty,
  void* threadSafeData)
{
  double MinHeight = 100000;
  double minimumGround = 120000;
  double MaxHeight = 123000;
  //I don't care about data above/below are min/max heights.
  if (cellOrigin[1] + cellSize < MinHeight ||
    cellOrigin[1] > MaxHeight)
  {
    return VoxelFarm::API::RESULT_OK;
  }

  OreWave& wave = objects[object];
  
  auto oreVein = ((OreVeinStruct*)threadSafeData);
  // just contains a double[][]

  int level, xx, yy, zz;
  unpackCellId(cell, level, xx, yy, zz); // can be found in mapindex.h

  int delta = 1;
  // LOD0 == 2, LOD1 == 3, LOD2+ is 4
  if (level == 2)
  {
    delta = 4;
  }
  else if (level == 3)
  {
    delta = 2;
  }
  else {
    delta = 1;
  }
  // pre generate the 'height map' for the terrain
  for (int z = 0; z < blockSize; z += delta)
  {
    for (int x = 0; x < blockSize; x += delta)
    {
      double worldX = cellOrigin[0] + voxelSize*x;
      double worldZ = cellOrigin[2] + voxelSize*z;

      double noise = PerlinNoise.octaveNoise0_1(worldX * 0.0005, worldZ * 0.0005, 4); // (x, y, octaves)

      double height = minimumGround + (MaxHeight - minimumGround) * noise;
      // forcing to up to 4x4 sections for LOD0, 2x2 for LOD1, and 1x1 for above
      for (int dx = 0; dx < delta; dx++)
      {
        for (int dz = 0; dz < delta; dz++)
        {
          oreVein->heightmap[x + dx][z + dz] = height;
        }
      }
    }
  }
  // using just generated data, lets modify the voxel data
  int dz = 0, dx = 0, dy = 0;
  int voxelIndex;
  for (int z = 0; z < blockSize; z += 1)
  for (int x = 0; x < blockSize; x += 1)
  {
    for (int y = 0; y < blockSize; y += delta)
    {
      double worldY = cellOrigin[1] + voxelSize * y;
      
      if (worldY <= oreVein->heightmap[x][z])
      {
        //if we are here, we are not above ground air. 
        // default material is dirt (underground)
        int material = 1;
        // if we are within some number of blocks of surface, lets go for some grass for now. 
        if (worldY >= oreVein->heightmap[x][z] - voxelSize * 16)
          material = 2;
        empty = false;
        
        for (int dy = 0; dy < delta; dy++)
        {
          voxelIndex = (z + 0) * blockSize * blockSize + (x + 0) * blockSize + (y + dy);
          materials[voxelIndex] = material;
          changeFlags[voxelIndex] |= VoxelFarm::API::VOXEL_HAS_MATERIAL;
        }
      }
    }
  }

  //populate some ore clusters

  // NOTE: This isn't a great way to do this, this is just a very basic and bad example that is barely functional.
  int num_ore_clusters = 5;
  int ore_size = 12;
  int material = 3; // put gold/iron material in material id slot = 3.

  // only bother showing to the user if its nearby (LOD0 == 2)
  if (level == 2)
  {
    for (int i = 0; i < num_ore_clusters; i++)
    {
      // use values for 'seeds' into perlin noise
      double worldZ = cellOrigin[2] + voxelSize * ((i*num_ore_clusters) / blockSize);
      double worldX = cellOrigin[0] + voxelSize * ((i*num_ore_clusters) / blockSize);
      double worldY = cellOrigin[1] + voxelSize * ((i*num_ore_clusters) / blockSize);
            
      int initial_z = PerlinNoise.noise0_1(worldZ)*blockSize;
      int initial_x = PerlinNoise.noise0_1(worldX)*blockSize;
      int initial_y = PerlinNoise.noise0_1(worldY)*blockSize;

      // are we underground at the core here?
      if (cellOrigin[1] + voxelSize * initial_y < oreVein->heightmap[initial_x][initial_z])
      {
        // we allow ore to 'pop' out of the ground.
        // just plopping down a cube shaped ore vein!
        for (int dz = -(ore_size / 2); dz < ore_size / 2; dz++)
        for (int dx = -(ore_size / 2); dx < ore_size / 2; dx++)
        for (int dy = -(ore_size / 2); dy < ore_size / 2; dy++)
        {
          // don't go outside our particular chunk (0-40)
          int place_z = clamp(initial_z + dz, 0, 40); 
          int place_x = clamp(initial_x + dx, 0, 40);
          int place_y = clamp(initial_y + dy, 0, 40);
          voxelIndex = (place_z)* blockSize * blockSize + (place_x)* blockSize + (place_y);
          materials[voxelIndex] = material;
          changeFlags[voxelIndex] |= VoxelFarm::API::VOXEL_HAS_MATERIAL;
        }

      }
    }
  }
  return VoxelFarm::API::RESULT_OK;
}


I didn't include code for 'smooth' non cube terrain here, though there are other posts that better demonstrate how to refactor into smooth terrain version. (average the corners for the vectors)


This is not an idealized example, though I hope to make a better one in the future, hopefully it gives a few people some rough ideas on getting started who wanted to achieve something similar in a random prototype.
1 0
ashkan.saeedi.1989

ery nice and many thanks. I would like to put sometime and do such prototype and you saved me a lot of time and quesswork

 

The extension sample in the docs is good as well and showed lots of the stuff, including smooth surfaces.  

 

I guess we have an example autogenerated world in samples as well but don't remember clearly. I guess the assumption is most voxelfarm users will use voxel studio which is not true of every project of course and i agree that any project which is sufficiently advanced will need a voxel layer code at one point or another. 

0 0
Zimnel
Would this work in a non block project? Adapted of course.
0 0
namrog84
Yeah, no reason it wouldn't work in a non block.  The only thing that'd need to be really adjusted is the introduction of calculating the vectors. 

When I have some more time, I still have plans to clean it up and make a non block version, just been busy with other things. 
1 0
Kovak
Thank you so much for sharing this. It has definitely been a big area that many of us have wondered about!
0 0