summaryrefslogblamecommitdiffstats
path: root/source/Generating/StructGen.cpp
blob: 6c5bf64915abf0d2fd3d6ff6045a700cb20b17ee (plain) (tree)
1
2
3
4
5
6
7
8




                      
                       
                  
                         








                                                                                                                       
                              

                              

                               

                             

                                   

                                 

                               

                             


                                  

                                
                              

                              







                                 












                                                                                 








                                                                                         


                                                                                                                       
                                                             
 



                                              



                                    
                                           

                                            
                                                   
                        
                                          


                                                 
                                                   
                                                                        
                                


                                                                                                          



                                                              
                                                    

                         
                                                                                      
 
                                                                  

                                                          
                                                                                                      

                         

                                                                     
                                                                                                   

                                                                    
                                                                                                  









                                                                        
                                                                                     
                                 
                                                                       












                                              
                                 

                                        

 

                                                                                                      
        
                                                 






                                               
                                                                    
                                                                                                        



                       



                                                                                             
                                           




                                                                                                  
         
                                                                           
                 
                                            


                                 
                                                                                   













                                                                                           

                                                                                        







                                     
                                 



                                        
                                                                                                  
                                                                                                           


                                                                           

                                                                                 
                         







                                                                          

                                                             
                                                                                                                             

                                              
                                



                                                 


                                                                                                                        















                                                                                             
                                                            
                 











                                                                  
                                                                  
                                                                  










                                                                                                                       

                      
                                                                
 










                                                                                                                                       
























































































                                                                                                                                                                                       
 


















































































































                                                                                                                                                    

// StructGen.h

#include "Globals.h"
#include "StructGen.h"
#include "../BlockID.h"
#include "Trees.h"
#include "../BlockArea.h"





///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cStructGenOreNests configuration:

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

const int MAX_HEIGHT_IRON = 64;
const int NUM_NESTS_IRON = 14;
const int NEST_SIZE_IRON = 6;

const int MAX_HEIGHT_REDSTONE = 16;
const int NUM_NESTS_REDSTONE = 4;
const int NEST_SIZE_REDSTONE = 6;

const int MAX_HEIGHT_GOLD = 32;
const int NUM_NESTS_GOLD = 2;
const int NEST_SIZE_GOLD = 6;

const int MAX_HEIGHT_DIAMOND = 15;
const int NUM_NESTS_DIAMOND = 1;
const int NEST_SIZE_DIAMOND = 4;

const int MAX_HEIGHT_LAPIS = 30;
const int NUM_NESTS_LAPIS = 2;
const int NEST_SIZE_LAPIS = 5;

const int MAX_HEIGHT_DIRT = 127;
const int NUM_NESTS_DIRT = 20;
const int NEST_SIZE_DIRT = 32;

const int MAX_HEIGHT_GRAVEL = 70;
const int NUM_NESTS_GRAVEL = 15;
const int NEST_SIZE_GRAVEL = 32;





template <typename T> T Clamp(T a_Value, T a_Min, T a_Max)
{
	return (a_Value < a_Min) ? a_Min : ((a_Value > a_Max) ? a_Max : a_Value);
}





static bool SortTreeBlocks(const sSetBlock & a_First, const sSetBlock & a_Second)
{
	return (a_First.BlockType == E_BLOCK_LOG) && (a_Second.BlockType != E_BLOCK_LOG);
}





///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cStructGenTrees:

void cStructGenTrees::GenStructures(cChunkDesc & a_ChunkDesc)
{
	int ChunkX = a_ChunkDesc.GetChunkX();
	int ChunkZ = a_ChunkDesc.GetChunkZ();
	
	cChunkDesc WorkerDesc(ChunkX, ChunkZ);
	
	// Generate trees:
	for (int x = 0; x <= 2; x++)
	{
		int BaseX = ChunkX + x - 1;
		for (int z = 0; z <= 2; z++)
		{
			int BaseZ = ChunkZ + z - 1;
			
			cChunkDesc * Dest;

			if ((x != 1) || (z != 1))
			{
				Dest = &WorkerDesc;
				WorkerDesc.SetChunkCoords(BaseX, BaseZ);
				
				m_BiomeGen->GenBiomes           (BaseX, BaseZ, WorkerDesc.GetBiomeMap());
				m_HeightGen->GenHeightMap       (BaseX, BaseZ, WorkerDesc.GetHeightMap());
				m_CompositionGen->ComposeTerrain(WorkerDesc);
				// TODO: Free the entity lists
			}
			else
			{
				Dest = &a_ChunkDesc;
			}

			int NumTrees = GetNumTrees(BaseX, BaseZ, Dest->GetBiomeMap());

			sSetBlockVector OutsideLogs, OutsideOther;
			for (int i = 0; i < NumTrees; i++)
			{
				GenerateSingleTree(BaseX, BaseZ, i, *Dest, OutsideLogs, OutsideOther);
			}

			sSetBlockVector IgnoredOverflow;
			IgnoredOverflow.reserve(OutsideOther.size());
			ApplyTreeImage(ChunkX, ChunkZ, a_ChunkDesc, OutsideOther, IgnoredOverflow);
			IgnoredOverflow.clear();
			IgnoredOverflow.reserve(OutsideLogs.size());
			ApplyTreeImage(ChunkX, ChunkZ, a_ChunkDesc, OutsideLogs, IgnoredOverflow);
		}  // for z
	}  // for x
	
	// Update the heightmap:
	for (int x = 0; x < cChunkDef::Width; x++)
	{
		for (int z = 0; z < cChunkDef::Width; z++)
		{
			for (int y = cChunkDef::Height - 1; y >= 0; y--)
			{
				if (a_ChunkDesc.GetBlockType(x, y, z) != E_BLOCK_AIR)
				{
					a_ChunkDesc.SetHeight(x, z, y);
					break;
				}
			}  // for y
		}  // for z
	}  // for x
}





void cStructGenTrees::GenerateSingleTree(
	int a_ChunkX, int a_ChunkZ, int a_Seq,
	cChunkDesc & a_ChunkDesc,
	sSetBlockVector & a_OutsideLogs,
	sSetBlockVector & a_OutsideOther
)
{
	int x = (m_Noise.IntNoise3DInt(a_ChunkX + a_ChunkZ, a_ChunkZ, a_Seq) / 19) % cChunkDef::Width;
	int z = (m_Noise.IntNoise3DInt(a_ChunkX - a_ChunkZ, a_Seq, a_ChunkZ) / 19) % cChunkDef::Width;
	
	int Height = a_ChunkDesc.GetHeight(x, z);
	
	if ((Height <= 0) || (Height > 240))
	{
		return;
	}

	// Check the block underneath the tree:
	BLOCKTYPE TopBlock = a_ChunkDesc.GetBlockType(x, Height, z);
	if ((TopBlock != E_BLOCK_DIRT) && (TopBlock != E_BLOCK_GRASS) && (TopBlock != E_BLOCK_FARMLAND))
	{
		return;
	}
	
	sSetBlockVector TreeLogs, TreeOther;
	GetTreeImageByBiome(
		a_ChunkX * cChunkDef::Width + x, Height + 1, a_ChunkZ * cChunkDef::Width + z,
		m_Noise, a_Seq, 
		a_ChunkDesc.GetBiome(x, z),
		TreeLogs, TreeOther
	);

	// Check if the generated image fits the terrain. Only the logs are checked:
	for (sSetBlockVector::const_iterator itr = TreeLogs.begin(); itr != TreeLogs.end(); ++itr)
	{
		if ((itr->ChunkX != a_ChunkX) || (itr->ChunkZ != a_ChunkZ))
		{
			// Outside the chunk
			continue;
		}

		BLOCKTYPE Block = a_ChunkDesc.GetBlockType(itr->x, itr->y, itr->z);
		switch (Block)
		{
			CASE_TREE_ALLOWED_BLOCKS:
			{
				break;
			}
			default:
			{
				// There's something in the way, abort this tree altogether
				return;
			}
		}
	}
	
	ApplyTreeImage(a_ChunkX, a_ChunkZ, a_ChunkDesc, TreeOther, a_OutsideOther);
	ApplyTreeImage(a_ChunkX, a_ChunkZ, a_ChunkDesc, TreeLogs,  a_OutsideLogs);	
}





void cStructGenTrees::ApplyTreeImage(
	int a_ChunkX, int a_ChunkZ,
	cChunkDesc & a_ChunkDesc,
	const sSetBlockVector & a_Image,
	sSetBlockVector & a_Overflow
)
{
	// Put the generated image into a_BlockTypes, push things outside this chunk into a_Blocks
	for (sSetBlockVector::const_iterator itr = a_Image.begin(), end = a_Image.end(); itr != end; ++itr)
	{
		if ((itr->ChunkX == a_ChunkX) && (itr->ChunkZ == a_ChunkZ))
		{
			// Inside this chunk, integrate into a_ChunkDesc:
			switch (a_ChunkDesc.GetBlockType(itr->x, itr->y, itr->z))
			{
				case E_BLOCK_LEAVES:
				{
					if (itr->BlockType != E_BLOCK_LOG)
					{
						break;
					}
					// fallthrough:
				}
				CASE_TREE_OVERWRITTEN_BLOCKS:
				{
					a_ChunkDesc.SetBlockTypeMeta(itr->x, itr->y, itr->z, itr->BlockType, itr->BlockMeta);
					break;
				}
				
			}  // switch (GetBlock())
			continue;
		}
		
		// Outside the chunk, push into a_Overflow.
		// Don't check if already present there, by separating logs and others we don't need the checks anymore:
		a_Overflow.push_back(*itr);
	}
}





int cStructGenTrees::GetNumTrees(
	int a_ChunkX, int a_ChunkZ,
	const cChunkDef::BiomeMap & a_Biomes
)
{
	int NumTrees = 0;
	for (int x = 0; x < cChunkDef::Width; x++) for (int z = 0; z < cChunkDef::Width; z++)
	{
		int Add = 0;
		switch (cChunkDef::GetBiome(a_Biomes, x, z))
		{
			case biPlains:           Add =   1; break;
			case biExtremeHills:     Add =   3; break;
			case biForest:           Add =  30; break;
			case biTaiga:            Add =  30; break;
			case biSwampland:        Add =   8; break;
			case biIcePlains:        Add =   1; break;
			case biIceMountains:     Add =   1; break;
			case biMushroomIsland:   Add =   3; break;
			case biMushroomShore:    Add =   3; break;
			case biForestHills:      Add =  20; break;
			case biTaigaHills:       Add =  20; break;
			case biExtremeHillsEdge: Add =   5; break;
			case biJungle:           Add = 120; break;
			case biJungleHills:      Add =  90; break;
		}
		NumTrees += Add;
	}
	return NumTrees / 1024;
}





///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cStructGenOreNests:

void cStructGenOreNests::GenStructures(cChunkDesc & a_ChunkDesc)
{
	int ChunkX = a_ChunkDesc.GetChunkX();
	int ChunkZ = a_ChunkDesc.GetChunkZ();
	cChunkDef::BlockTypes & BlockTypes = a_ChunkDesc.GetBlockTypes();
	GenerateOre(ChunkX, ChunkZ, E_BLOCK_COAL_ORE,     MAX_HEIGHT_COAL,     NUM_NESTS_COAL,     NEST_SIZE_COAL,     BlockTypes, 1);
	GenerateOre(ChunkX, ChunkZ, E_BLOCK_IRON_ORE,     MAX_HEIGHT_IRON,     NUM_NESTS_IRON,     NEST_SIZE_IRON,     BlockTypes, 2);
	GenerateOre(ChunkX, ChunkZ, E_BLOCK_REDSTONE_ORE, MAX_HEIGHT_REDSTONE, NUM_NESTS_REDSTONE, NEST_SIZE_REDSTONE, BlockTypes, 3);
	GenerateOre(ChunkX, ChunkZ, E_BLOCK_GOLD_ORE,     MAX_HEIGHT_GOLD,     NUM_NESTS_GOLD,     NEST_SIZE_GOLD,     BlockTypes, 4);
	GenerateOre(ChunkX, ChunkZ, E_BLOCK_DIAMOND_ORE,  MAX_HEIGHT_DIAMOND,  NUM_NESTS_DIAMOND,  NEST_SIZE_DIAMOND,  BlockTypes, 5);
	GenerateOre(ChunkX, ChunkZ, E_BLOCK_LAPIS_ORE,    MAX_HEIGHT_LAPIS,    NUM_NESTS_LAPIS,    NEST_SIZE_LAPIS,    BlockTypes, 6);
	GenerateOre(ChunkX, ChunkZ, E_BLOCK_DIRT,         MAX_HEIGHT_DIRT,     NUM_NESTS_DIRT,     NEST_SIZE_DIRT,     BlockTypes, 10);
	GenerateOre(ChunkX, ChunkZ, E_BLOCK_GRAVEL,       MAX_HEIGHT_GRAVEL,   NUM_NESTS_GRAVEL,   NEST_SIZE_GRAVEL,   BlockTypes, 11);
}





void cStructGenOreNests::GenerateOre(int a_ChunkX, int a_ChunkZ, BLOCKTYPE a_OreType, int a_MaxHeight, int a_NumNests, int a_NestSize, cChunkDef::BlockTypes & a_BlockTypes, int a_Seq)
{
	// 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 rnd = m_Noise.IntNoise3DInt(a_ChunkX + i, a_Seq, a_ChunkZ + 64 * i) / 8;
		int BaseX = rnd % cChunkDef::Width;
		rnd /= cChunkDef::Width;
		int BaseZ = rnd % cChunkDef::Width;
		rnd /= cChunkDef::Width;
		int BaseY = rnd % a_MaxHeight;
		rnd /= a_MaxHeight;
		int NestSize = a_NestSize + (rnd % (a_NestSize / 4));  // The actual nest size may be up to 1/4 larger
		int Num = 0;
		while (Num < NestSize)
		{
			// Put a cuboid around [BaseX, BaseY, BaseZ]
			int rnd = m_Noise.IntNoise3DInt(a_ChunkX + 64 * i, 2 * a_Seq + Num, a_ChunkZ + 32 * i) / 8;
			int xsize = rnd % 2;
			int ysize = (rnd / 4) % 2;
			int zsize = (rnd / 16) % 2;
			rnd >>= 8;
			for (int x = xsize; x >= 0; --x)
			{
				int BlockX = BaseX + x;
				if ((BlockX < 0) || (BlockX >= cChunkDef::Width))
				{
					Num++;  // So that the cycle finishes even if the base coords wander away from the chunk
					continue;
				}
				for (int y = ysize; y >= 0; --y)
				{
					int BlockY = BaseY + y;
					if ((BlockY < 0) || (BlockY >= cChunkDef::Height))
					{
						Num++;  // So that the cycle finishes even if the base coords wander away from the chunk
						continue;
					}
					for (int z = zsize; z >= 0; --z)
					{
						int BlockZ = BaseZ + z;
						if ((BlockZ < 0) || (BlockZ >= cChunkDef::Width))
						{
							Num++;  // So that the cycle finishes even if the base coords wander away from the chunk
							continue;
						}

						int Index = cChunkDef::MakeIndexNoCheck(BlockX, BlockY, BlockZ);
						if (a_BlockTypes[Index] == E_BLOCK_STONE)
						{
							a_BlockTypes[Index] = a_OreType;
						}
						Num++;
					}  // for z
				}  // for y
			}  // for x
			
			// Move the base to a neighbor voxel
			switch (rnd % 4)
			{
				case 0: BaseX--; break;
				case 1: BaseX++; break;
			}
			switch ((rnd >> 3) % 4)
			{
				case 0: BaseY--; break;
				case 1: BaseY++; break;
			}
			switch ((rnd >> 6) % 4)
			{
				case 0: BaseZ--; break;
				case 1: BaseZ++; break;
			}
		}  // while (Num < NumBlocks)
	}  // for i - NumNests
}






///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cStructGenLakes:

void cStructGenLakes::GenStructures(cChunkDesc & a_ChunkDesc)
{
	int ChunkX = a_ChunkDesc.GetChunkX();
	int ChunkZ = a_ChunkDesc.GetChunkZ();
	
	for (int z = -1; z < 2; z++) for (int x = -1; x < 2; x++)
	{
		cBlockArea Lake;
		CreateLakeImage(ChunkX + x, ChunkZ + z, Lake);
		
		int OfsX = Lake.GetOriginX() + x * cChunkDef::Width;
		int OfsZ = Lake.GetOriginZ() + z * cChunkDef::Width;
		
		// Merge the lake into the current data
		a_ChunkDesc.WriteBlockArea(Lake, OfsX, Lake.GetOriginY(), OfsZ, cBlockArea::msLake);
	}  // for x, z - neighbor chunks
}





void cStructGenLakes::CreateLakeImage(int a_ChunkX, int a_ChunkZ, cBlockArea & a_Lake)
{
	a_Lake.Create(16, 8, 16);
	a_Lake.Fill(cBlockArea::baTypes, E_BLOCK_SPONGE);  // Sponge is the NOP blocktype for lake merging strategy
	
	// Find the maximum height in this chunk:
	cChunkDef::HeightMap HeightMap;
	m_HeiGen.GenHeightMap(a_ChunkX, a_ChunkZ, HeightMap);
	HEIGHTTYPE MaxHeight = HeightMap[0];
	for (int i = 1; i < ARRAYCOUNT(HeightMap); i++)
	{
		if (HeightMap[i] > MaxHeight)
		{
			MaxHeight = HeightMap[i];
		}
	}
	
	// Make a random position in the chunk by using a random 16 block XZ offset and random height up to chunk's max height
	int Rnd = m_Noise.IntNoise3DInt(a_ChunkX, 128, a_ChunkZ) / 11;
	// Random offset [-8 .. 8], with higher probability around 0; add up four three-bit-wide randoms [0 .. 28], divide and subtract to get range
	int OffsetX = 4 * ((Rnd & 0x07) + ((Rnd & 0x38) >> 3) + ((Rnd & 0x1c0) >> 6) + ((Rnd & 0xe00) >> 9)) / 7 - 8;
	Rnd >>= 12;
	// Random offset [-8 .. 8], with higher probability around 0; add up four three-bit-wide randoms [0 .. 28], divide and subtract to get range
	int OffsetZ = 4 * ((Rnd & 0x07) + ((Rnd & 0x38) >> 3) + ((Rnd & 0x1c0) >> 6) + ((Rnd & 0xe00) >> 9)) / 7 - 8;
	Rnd = m_Noise.IntNoise3DInt(a_ChunkX, 512, a_ChunkZ) / 13;
	// Random height [0 .. MaxHeight] with preference to center heights
	int HeightY = (((Rnd & 0x1ff) % (MaxHeight + 1)) + (((Rnd >> 9) & 0x1ff) % (MaxHeight + 1))) / 2;
	a_Lake.SetOrigin(OffsetX, HeightY, OffsetZ);
	
	// Hollow out a few bubbles inside the blockarea:
	int NumBubbles = 4 + ((Rnd >> 18) & 0x03);  // 4 .. 7 bubbles
	BLOCKTYPE * BlockTypes = a_Lake.GetBlockTypes();
	for (int i = 0; i < NumBubbles; i++)
	{
		int Rnd = m_Noise.IntNoise3DInt(a_ChunkX, i, a_ChunkZ) / 13;
		const int BubbleR = 2 + (Rnd & 0x03);  // 2 .. 5
		const int Range = 16 - 2 * BubbleR;
		const int BubbleX = BubbleR + (Rnd % Range);
		Rnd >>= 4;
		const int BubbleY = 4 + (Rnd & 0x01);  // 4 .. 5
		Rnd >>= 1;
		const int BubbleZ = BubbleR + (Rnd % Range);
		Rnd >>= 4;
		const int HalfR = BubbleR / 2;  // 1 .. 2
		const int RSquared = BubbleR * BubbleR;
		for (int y = -HalfR; y <= HalfR; y++)
		{
			// BubbleY + y is in the [0, 7] bounds
			int DistY = 4 * y * y / 3;
			int IdxY = (BubbleY + y) * 16 * 16;
			for (int z = -BubbleR; z <= BubbleR; z++)
			{
				int DistYZ = DistY + z * z;
				if (DistYZ >= RSquared)
				{
					continue;
				}
				int IdxYZ = BubbleX + IdxY + (BubbleZ + z) * 16;
				for (int x = -BubbleR; x <= BubbleR; x++) 
				{
					if (x * x + DistYZ < RSquared)
					{
						BlockTypes[x + IdxYZ] = E_BLOCK_AIR;
					}
				}  // for x
			}  // for z
		}  // for y
	}  // for i - bubbles

	// Turn air in the bottom half into liquid:	
	for (int y = 0; y < 4; y++)
	{
		for (int z = 0; z < 16; z++) for (int x = 0; x < 16; x++) 
		{
			if (BlockTypes[x + z * 16 + y * 16 * 16] == E_BLOCK_AIR)
			{
				BlockTypes[x + z * 16 + y * 16 * 16] = m_Fluid;
			}
		}  // for z, x
	}  // for y
	
	// TODO: Turn sponge next to lava into stone
	
	// a_Lake.SaveToSchematicFile(Printf("Lake_%d_%d.schematic", a_ChunkX, a_ChunkZ));
}