summaryrefslogblamecommitdiffstats
path: root/source/cWorldGenerator.cpp
blob: cdb3951b8586066916b409dcbab413a90d112882 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11


                                                                                               


                             




                          



 




















                                                       

























                                                                       



                                 



 

                                                     


  



 




                                    


 
                                                                                                                                                                     
  
                                                                    

  



 


                                                                                  














                                                                        




































                                                                                                                             













                                                                                                                                



 











                                                                           



 
                                                              
  
                                                                                                                                 
 
                                                       





  
                                                                                                         



                                    
                                                                    


                                               
                                                    
          

                                                                           

                                                         
                                                                           
 
                                                                                   
                         
                                                                                            
                                              



                                                      



                                              

                                                                                                          







                                                                                                                                                                                    




                                                                                                                


                                             
                                                                                                             

                                          




                                                                                          
                                  



                                                                                                                         
                                          
                                                                                                 
                                          

                                     
                                  
                                                                                        
                                  

                                    


                                                                 
                                                                                     







                                                                                                    
                                  
                                                                                                          
                                  













                                                                                                             
                          
                                                                                


                                                                                          









                                                                                                           
                             
                          

                                                                         
                                  
                                                                                                      
                                  
                                                             

                            






                                                                                                                     
                                                                                                                     





  
                                                                                                                            





                                                                                                                                           
                                                          
                                                     
                                                          







































                                                                                                                                       
                                                                                                                                                                             










                                                                       



  

 
                                                                                
  
                                                   
         
                                                                                   
          




                                                                                             
                                                    
          

                                                           
                  
                                                                  
 
                                                               
                                                                                         
                                                             
                          


                                                                                           
                                  
                                                                           
                                  


                                         
  



 

#include "Globals.h"  // NOTE: MSVC stupidness requires this to be the same across all modules

#include "cWorldGenerator.h"
#include "cNoise.h"
#include "cWorld.h"
#include "cGenSettings.h"

#include "BlockID.h"
#include "Vector3i.h"





// An array describing an 8-way neighbor coords deltas
static struct 
{
	int m_X;
	int m_Z;
} g_NeighborCoords[] = 
{
	{-1, -1},
	{-1, 0},
	{-1, 1},
	{0, -1},
	{0, 1},
	{1, -1},
	{1, 0},
	{1, 1},
} ;





// You can use GLASS for these instead for debugging ore generation ;)
// Beware though, client has problems with this much glass!
const char BLOCK_STONE = E_BLOCK_STONE;
const char BLOCK_DIRT = E_BLOCK_DIRT;
const char BLOCK_GRASS = E_BLOCK_GRASS;

const int MAX_HEIGHT_COAL = 127;
const int NUM_NESTS_COAL = 40;
const int NEST_SIZE_COAL = 10;

const int MAX_HEIGHT_IRON = 70;
const int NUM_NESTS_IRON = 10;
const int NEST_SIZE_IRON = 6;

const int MAX_HEIGHT_REDSTONE = 40;
const int NUM_NESTS_REDSTONE = 10;
const int NEST_SIZE_REDSTONE = 6;

const int MAX_HEIGHT_GOLD = 35;
const int NUM_NESTS_GOLD = 6;
const int NEST_SIZE_GOLD = 6;

const int MAX_HEIGHT_DIAMOND = 16;
const int NUM_NESTS_DIAMOND = 6;
const int NEST_SIZE_DIAMOND = 5;

const int MAX_HEIGHT_LAPIS = 16;
const int NUM_NESTS_LAPIS = 6;
const int NEST_SIZE_LAPIS = 5;





cWorldGenerator::cWorldGenerator(cWorld * a_World) :
	m_World(a_World)
{
}





cWorldGenerator::~cWorldGenerator()
{
}





void cWorldGenerator::GenerateChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, BLOCKTYPE * a_BlockData, cEntityList & a_Entities, cBlockEntityList & a_BlockEntities)
{
	GenerateTerrain(a_ChunkX, a_ChunkY, a_ChunkZ, a_BlockData);
}





void cWorldGenerator::PostGenerateChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
{
	// Check the chunk just generated and all its 8-way neighbors

	// Make the chunks stay loaded in the surrounding 5x5 area:
	cChunkStay Stay(m_World);
	Stay.Add(a_ChunkX, a_ChunkY, a_ChunkZ);
	for (int x = -2; x <= 2; x++)
	{
		for (int z = -2; z <= 2; z++)
		{
			Stay.Add(a_ChunkX + x, a_ChunkY, a_ChunkZ + z);
		}  // for z
	}  // for x
	Stay.Enable();
	
	m_World->LoadChunks(Stay);
	
	CheckNeighbors(a_ChunkX, a_ChunkY, a_ChunkZ);
	for (int i = 0; i < ARRAYCOUNT(g_NeighborCoords); i++)
	{
		CheckNeighbors(a_ChunkX + g_NeighborCoords[i].m_X, a_ChunkY, a_ChunkZ + g_NeighborCoords[i].m_Z);
	}  // for i - g_NeighborCoords[]
}





void cWorldGenerator::CheckNeighbors(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
{
	if (!m_World->IsChunkValid(a_ChunkX, a_ChunkY, a_ChunkZ))
	{
		return;
	}
	
	// Check all 8-way neighbors, if they are all valid, generate foliage in this chunk:
	int NumNeighbors = 0;
	for (int i = 0; i < ARRAYCOUNT(g_NeighborCoords); i++)
	{
		if (m_World->IsChunkValid(a_ChunkX + g_NeighborCoords[i].m_X, a_ChunkY, a_ChunkZ + g_NeighborCoords[i].m_Z))
		{
			NumNeighbors++;
		}
	}  // for i - g_NeighborCoords[]
	if (NumNeighbors == 8)
	{
		GenerateFoliage(a_ChunkX, a_ChunkY, a_ChunkZ);
	}
}





static float GetNoise( float x, float y, cNoise & a_Noise )
{
	float oct1 = a_Noise.CubicNoise2D( x*cGenSettings::HeightFreq1, y*cGenSettings::HeightFreq1 )*cGenSettings::HeightAmp1;
	float oct2 = a_Noise.CubicNoise2D( x*cGenSettings::HeightFreq2, y*cGenSettings::HeightFreq2 )*cGenSettings::HeightAmp2;
	float oct3 = a_Noise.CubicNoise2D( x*cGenSettings::HeightFreq3, y*cGenSettings::HeightFreq3 )*cGenSettings::HeightAmp3;

	float height = a_Noise.CubicNoise2D( x*0.1f, y*0.1f )*2;

	float flatness = ((a_Noise.CubicNoise2D( x*0.5f, y*0.5f ) + 1.f ) * 0.5f) * 1.1f; // 0 ... 1.5
	flatness *= flatness * flatness;

	return (oct1 + oct2 + oct3) * flatness + height;
}





#define PI_2 (1.57079633f)
static float GetMarbleNoise( float x, float y, float z, cNoise & a_Noise )
{
	float oct1 = (a_Noise.CubicNoise3D( x*0.1f, y*0.1f, z*0.1f ))*4;

	oct1 = oct1 * oct1 * oct1;
	if( oct1 < 0.f ) oct1 = PI_2;
	if( oct1 > PI_2 ) oct1 = PI_2;

	return oct1;
}





unsigned int cWorldGenerator::MakeIndex(int x, int y, int z )
{
	ASSERT((x < cChunkDef::Width) && (x > -1) && (y < cChunkDef::Height) && (y > -1) && (z < cChunkDef::Width) && (z > -1));

	return cChunkDef::MakeIndexNoCheck( x, y, z );
}





void cWorldGenerator::GenerateTerrain(int a_ChunkX, int a_ChunkY, int a_ChunkZ, BLOCKTYPE * a_BlockData)
{
	const int WATER_LEVEL = 60;
	const int SAND_LEVEL = 3;
	
	memset(a_BlockData, E_BLOCK_AIR, cChunkDef::BlockDataSize);

	cNoise Noise(m_World->GetWorldSeed());
	
	for (int z = 0; z < cChunkDef::Width; z++) 
	{
		const float zz = (float)(a_ChunkZ * cChunkDef::Width + z);
		for (int x = 0; x < cChunkDef::Width; x++)
		{
			// Place bedrock on bottom layer
			a_BlockData[MakeIndex(x, 0, z)] = E_BLOCK_BEDROCK;

			const float xx = (float)(a_ChunkX * cChunkDef::Width + x);
			
			int Height = (int)(GetNoise( xx * 0.05f, zz * 0.05f, Noise ) * 16);
			const int Lower = 64;
			if ( Height + Lower > 127 )
			{
				Height = 127 - Lower;
			}
			if (Height < -63)
			{
				Height = -63;
			}
			const int Top = Lower + Height;
			const float WaveNoise = 1;  // m_Noise.CubicNoise2D( xx*0.01f, zz*0.01f ) + 0.5f;
			for( int y = 1; y < Top; ++y )
			{
				const float yy = (float)y;
				//   V prevent caves from getting too close to the surface
				if( (Top - y > (WaveNoise*2) ) && cosf(GetMarbleNoise( xx, yy*0.5f, zz, Noise )) * fabs( cosf( yy*0.2f + WaveNoise*2 )*0.75f + WaveNoise ) > 0.5f )
				{
					if( y > 4 )
					{
						a_BlockData[ MakeIndex(x, y, z) ] = E_BLOCK_AIR;
						if( z > 0 ) a_BlockData[ MakeIndex(x, y, z-1) ] = E_BLOCK_AIR;
						if( z < 15 ) a_BlockData[ MakeIndex(x, y, z+1) ] = E_BLOCK_AIR;
						if( x > 0 ) a_BlockData[ MakeIndex(x-1, y, z) ] = E_BLOCK_AIR;
						if( x < 15 ) a_BlockData[ MakeIndex(x+1, y, z) ] = E_BLOCK_AIR;
					}
					else
					{
						a_BlockData[ MakeIndex(x, y, z) ] = E_BLOCK_STATIONARY_LAVA;
					}
				}
				else if ((y < 61) && (Top - y < SAND_LEVEL ))
				{
					a_BlockData[ MakeIndex(x, y, z) ] = E_BLOCK_SAND;
				}
				else if ((y < 61) && (Top - y < 4 ))
				{
					a_BlockData[ MakeIndex(x, y, z) ] = E_BLOCK_SANDSTONE;
				}
				else if (Top - y > ((WaveNoise + 1.5f) * 1.5f)) // rock and ores between 1.5 .. 4.5 deep
				{
					{
						a_BlockData[ MakeIndex(x, y, z) ] = BLOCK_STONE;
					}
				}
				else
				{
					a_BlockData[ MakeIndex(x, y, z) ] = BLOCK_DIRT;
				}
			}  // for y
			
			if (Top + 1 >= WATER_LEVEL + SAND_LEVEL)
			{
				// Replace top dirt with grass:
				a_BlockData[MakeIndex(x, Top - 1, z)] = BLOCK_GRASS;
			
				// Generate small foliage (1-block):
				int TopY = Top - 1;
				float val1 = Noise.CubicNoise2D(xx * 0.1f,  zz * 0.1f );
				float val2 = Noise.CubicNoise2D(xx * 0.01f, zz * 0.01f );
				float val3 = Noise.CubicNoise2D(xx * 0.01f + 10, zz * 0.01f + 10 );
				float val4 = Noise.CubicNoise2D(xx * 0.05f + 20, zz * 0.05f + 20 );
				if ((val3 > 0.2f) && ((r1.randInt()%128) > 124))
				{
					a_BlockData[ MakeIndex(x, TopY + 1, z) ] = E_BLOCK_YELLOW_FLOWER;
				}
				else if ((val4 > 0.2f) && ((r1.randInt() % 128) > 124))
				{
					a_BlockData[ MakeIndex(x, TopY + 1, z) ] = E_BLOCK_RED_ROSE;
				}
				else if ((val1 + val2 + val3 + val4 > 0.2f) && ((r1.randInt() % 128) > 124))
				{
					a_BlockData[ MakeIndex(x, TopY + 1, z) ] = E_BLOCK_RED_MUSHROOM;
				}
				else if ((val1 + val2 + val3 + val4 > 0.2f) && ((r1.randInt() % 128) > 124))
				{
					a_BlockData[ MakeIndex(x, TopY + 1, z) ] = E_BLOCK_BROWN_MUSHROOM;
				}
			}  // if (Top above beach-level)
			else if (Top > WATER_LEVEL)
			{
				// This is the sandy shore, generate cacti here
				int TopY = Top - 1;
				float val1 = Noise.CubicNoise2D(xx * 0.1f,  zz * 0.1f );
				float val2 = Noise.CubicNoise2D(xx * 0.01f, zz * 0.01f );
				if ((val1 + val2 > 0.f) && ((r1.randInt() % 128) > 124))
				{
					a_BlockData[ MakeIndex(x, TopY + 1, z) ] = E_BLOCK_CACTUS;
					if ((r1.randInt() & 3) == 3)
					{
						a_BlockData[ MakeIndex(x, TopY + 2, z) ] = E_BLOCK_CACTUS;
					}
					continue;
				}
			}
			else
			{
				// Add water up to the WATER_LEVEL:
				for (int y = Top; y < WATER_LEVEL; ++y )
				{
					a_BlockData[ MakeIndex(x, y, z) ] = E_BLOCK_STATIONARY_WATER;
				}
			}  // else (Top is under waterlevel)
		}  // for x
	}  // for z
	
	// Generate ores:
	GenerateOre(E_BLOCK_COAL_ORE,     MAX_HEIGHT_COAL,     NUM_NESTS_COAL,     NEST_SIZE_COAL,     a_BlockData);
	GenerateOre(E_BLOCK_IRON_ORE,     MAX_HEIGHT_IRON,     NUM_NESTS_IRON,     NEST_SIZE_IRON,     a_BlockData);
	GenerateOre(E_BLOCK_REDSTONE_ORE, MAX_HEIGHT_REDSTONE, NUM_NESTS_REDSTONE, NEST_SIZE_REDSTONE, a_BlockData);
	GenerateOre(E_BLOCK_GOLD_ORE,     MAX_HEIGHT_GOLD,     NUM_NESTS_GOLD,     NEST_SIZE_GOLD,     a_BlockData);
	GenerateOre(E_BLOCK_DIAMOND_ORE,  MAX_HEIGHT_DIAMOND,  NUM_NESTS_DIAMOND,  NEST_SIZE_DIAMOND,  a_BlockData);
	GenerateOre(E_BLOCK_LAPIS_ORE,    MAX_HEIGHT_LAPIS,    NUM_NESTS_LAPIS,    NEST_SIZE_LAPIS,    a_BlockData);
}





void cWorldGenerator::GenerateOre(char a_OreType, int a_MaxHeight, int a_NumNests, int a_NestSize, BLOCKTYPE * a_BlockData)
{
	// This function generates several "nests" of ore, each nest consisting of number of ore blocks relatively adjacent to each other.
	// It does so by making a random XYZ walk and adding ore along the way in cuboids of different (random) sizes
	// Only stone gets replaced with ore, all other blocks stay (so the nest can actually be smaller than specified).
	for (int i = 0; i < a_NumNests; i++)
	{
		int BaseX = r1.randInt(cChunkDef::Width);
		int BaseY = r1.randInt(a_MaxHeight);
		int BaseZ = r1.randInt(cChunkDef::Width);
		sSetBlockList OreBlocks;
		size_t NestSize = (size_t)(a_NestSize + r1.randInt(a_NestSize / 4));  // The actual nest size may be up to 1/4 larger
		while (OreBlocks.size() < NestSize)
		{
			// Put a cuboid around [BaseX, BaseY, BaseZ]
			for (int x = r1.randInt(2); x >= 0; --x)
			{
				for (int y = r1.randInt(2); y >= 0; --y)
				{
					for (int z = r1.randInt(2); z >= 0; --z)
					{
						if (OreBlocks.size() < NestSize)
						{
							OreBlocks.push_back(sSetBlock(BaseX + x, BaseY + y, BaseZ + z, a_OreType, 0));
						}
					}  // for z
				}  // for y
			}  // for x
			
			// Move the base to a neighbor voxel
			switch (r1.randInt(4))
			{
				case 0: BaseX--; break;
				case 1: BaseX++; break;
			}
			switch (r1.randInt(4))
			{
				case 0: BaseY--; break;
				case 1: BaseY++; break;
			}
			switch (r1.randInt(4))
			{
				case 0: BaseZ--; break;
				case 1: BaseZ++; break;
			}
		}  // while (OreBlocks.size() < NumBlocks)
		
		// Replace stone with the queued ore blocks:
		for (sSetBlockList::iterator itr = OreBlocks.begin(); itr != OreBlocks.end(); ++itr)
		{
			if ((itr->x < 0) || (itr->y < 0) || (itr->z < 0) || (itr->x >= cChunkDef::Width) || (itr->y >= cChunkDef::Height-1) || (itr->z >= cChunkDef::Width))
			{
				continue;
			}
			int Index = MakeIndex(itr->x, itr->y, itr->z);
			if (a_BlockData[Index] == BLOCK_STONE)
			{
				a_BlockData[Index] = a_OreType;
			}
		}  // for itr - OreBlocks[]
		OreBlocks.clear();
	}  // for i
}





void cWorldGenerator::GenerateFoliage(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
{
	BLOCKTYPE BlockType[cChunkDef::NumBlocks];
	
	if (!m_World->GetChunkBlockTypes(a_ChunkX, a_ChunkY, a_ChunkZ, BlockType))
	{
		LOGWARNING("Cannot generate foliage on chunk [%d, %d]", a_ChunkX, a_ChunkZ);
		return;
	}
	
	cNoise Noise(m_World->GetWorldSeed());
	for (int z = 0; z < cChunkDef::Width; z++) 
	{
		int zz = z + a_ChunkZ * cChunkDef::Width;
		for (int x = 0; x < cChunkDef::Width; x++)
		{
			int xx = x + a_ChunkX * cChunkDef::Width;

			int TopY = m_World->GetHeight(xx, zz);
			int index = cChunkDef::MakeIndexNoCheck(x, MAX(TopY - 1, 0), z);
			if (BlockType[index] == BLOCK_GRASS)
			{
				float val1 = Noise.CubicNoise2D( xx * 0.1f, zz * 0.1f );
				float val2 = Noise.CubicNoise2D( xx * 0.01f, zz * 0.01f );
				if ((val1 + val2 > 0.2f) && ((r1.randInt() % 128) > 124))
				{
					m_World->GrowTree( xx, TopY, zz );
				}
			}  // if (Grass)
		}  // for x
	}  // for z
}