Moved source to src

This commit is contained in:
Alexander Harkness
2013-11-24 14:19:41 +00:00
parent 1480d6d64d
commit 675b4aa878
469 changed files with 0 additions and 0 deletions
+707
View File
@@ -0,0 +1,707 @@
// BioGen.cpp
// Implements the various biome generators
#include "Globals.h"
#include "BioGen.h"
#include "../../iniFile/iniFile.h"
#include "../LinearUpscale.h"
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cBioGenConstant:
void cBioGenConstant::GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap)
{
for (int i = 0; i < ARRAYCOUNT(a_BiomeMap); i++)
{
a_BiomeMap[i] = m_Biome;
}
}
void cBioGenConstant::InitializeBiomeGen(cIniFile & a_IniFile)
{
AString Biome = a_IniFile.GetValueSet("Generator", "ConstantBiome", "Plains");
m_Biome = StringToBiome(Biome);
if (m_Biome == -1)
{
LOGWARN("[Generator]::ConstantBiome value \"%s\" not recognized, using \"Plains\".", Biome.c_str());
m_Biome = biPlains;
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cBioGenCache:
cBioGenCache::cBioGenCache(cBiomeGen * a_BioGenToCache, int a_CacheSize) :
m_BioGenToCache(a_BioGenToCache),
m_CacheSize(a_CacheSize),
m_CacheOrder(new int[a_CacheSize]),
m_CacheData(new sCacheData[a_CacheSize]),
m_NumHits(0),
m_NumMisses(0),
m_TotalChain(0)
{
for (int i = 0; i < m_CacheSize; i++)
{
m_CacheOrder[i] = i;
m_CacheData[i].m_ChunkX = 0x7fffffff;
m_CacheData[i].m_ChunkZ = 0x7fffffff;
}
}
cBioGenCache::~cBioGenCache()
{
delete[] m_CacheData;
delete[] m_CacheOrder;
}
void cBioGenCache::GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap)
{
if (((m_NumHits + m_NumMisses) % 1024) == 10)
{
LOGD("BioGenCache: %d hits, %d misses, saved %.2f %%", m_NumHits, m_NumMisses, 100.0 * m_NumHits / (m_NumHits + m_NumMisses));
LOGD("BioGenCache: Avg cache chain length: %.2f", (float)m_TotalChain / m_NumHits);
}
for (int i = 0; i < m_CacheSize; i++)
{
if (
(m_CacheData[m_CacheOrder[i]].m_ChunkX != a_ChunkX) ||
(m_CacheData[m_CacheOrder[i]].m_ChunkZ != a_ChunkZ)
)
{
continue;
}
// Found it in the cache
int Idx = m_CacheOrder[i];
// Move to front:
for (int j = i; j > 0; j--)
{
m_CacheOrder[j] = m_CacheOrder[j - 1];
}
m_CacheOrder[0] = Idx;
// Use the cached data:
memcpy(a_BiomeMap, m_CacheData[Idx].m_BiomeMap, sizeof(a_BiomeMap));
m_NumHits++;
m_TotalChain += i;
return;
} // for i - cache
// Not in the cache:
m_NumMisses++;
m_BioGenToCache->GenBiomes(a_ChunkX, a_ChunkZ, a_BiomeMap);
// Insert it as the first item in the MRU order:
int Idx = m_CacheOrder[m_CacheSize - 1];
for (int i = m_CacheSize - 1; i > 0; i--)
{
m_CacheOrder[i] = m_CacheOrder[i - 1];
} // for i - m_CacheOrder[]
m_CacheOrder[0] = Idx;
memcpy(m_CacheData[Idx].m_BiomeMap, a_BiomeMap, sizeof(a_BiomeMap));
m_CacheData[Idx].m_ChunkX = a_ChunkX;
m_CacheData[Idx].m_ChunkZ = a_ChunkZ;
}
void cBioGenCache::InitializeBiomeGen(cIniFile & a_IniFile)
{
super::InitializeBiomeGen(a_IniFile);
m_BioGenToCache->InitializeBiomeGen(a_IniFile);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cBiomeGenList:
void cBiomeGenList::InitializeBiomes(const AString & a_Biomes)
{
AStringVector Split = StringSplit(a_Biomes, ",");
// Convert each string in the list into biome:
for (AStringVector::const_iterator itr = Split.begin(); itr != Split.end(); ++itr)
{
AStringVector Split2 = StringSplit(*itr, ":");
if (Split2.size() < 1)
{
continue;
}
int Count = 1;
if (Split2.size() >= 2)
{
Count = atol(Split2[1].c_str());
if (Count <= 0)
{
LOGWARNING("Cannot decode biome count: \"%s\"; using 1.", Split2[1].c_str());
Count = 1;
}
}
EMCSBiome Biome = StringToBiome(Split2[0]);
if (Biome != -1)
{
for (int i = 0; i < Count; i++)
{
m_Biomes.push_back(Biome);
}
}
else
{
LOGWARNING("Cannot decode biome name: \"%s\"; skipping", Split2[0].c_str());
}
} // for itr - Split[]
if (!m_Biomes.empty())
{
m_BiomesCount = (int)m_Biomes.size();
return;
}
// There were no biomes, add default biomes:
static EMCSBiome Biomes[] =
{
biOcean,
biPlains,
biDesert,
biExtremeHills,
biForest,
biTaiga,
biSwampland,
biRiver,
biFrozenOcean,
biFrozenRiver,
biIcePlains,
biIceMountains,
biMushroomIsland,
biMushroomShore,
biBeach,
biDesertHills,
biForestHills,
biTaigaHills,
biExtremeHillsEdge,
biJungle,
biJungleHills,
} ;
m_Biomes.reserve(ARRAYCOUNT(Biomes));
for (int i = 0; i < ARRAYCOUNT(Biomes); i++)
{
m_Biomes.push_back(Biomes[i]);
}
m_BiomesCount = (int)m_Biomes.size();
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cBioGenCheckerboard:
void cBioGenCheckerboard::GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap)
{
for (int z = 0; z < cChunkDef::Width; z++)
{
int Base = cChunkDef::Width * a_ChunkZ + z;
for (int x = 0; x < cChunkDef::Width; x++)
{
int Add = cChunkDef::Width * a_ChunkX + x;
a_BiomeMap[x + cChunkDef::Width * z] = m_Biomes[(Base / m_BiomeSize + Add / m_BiomeSize) % m_BiomesCount];
}
}
}
void cBioGenCheckerboard::InitializeBiomeGen(cIniFile & a_IniFile)
{
super::InitializeBiomeGen(a_IniFile);
AString Biomes = a_IniFile.GetValueSet ("Generator", "CheckerBoardBiomes", "");
m_BiomeSize = a_IniFile.GetValueSetI("Generator", "CheckerboardBiomeSize", 64);
m_BiomeSize = (m_BiomeSize < 8) ? 8 : m_BiomeSize;
InitializeBiomes(Biomes);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cBioGenVoronoi :
void cBioGenVoronoi::GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap)
{
int BaseZ = cChunkDef::Width * a_ChunkZ;
int BaseX = cChunkDef::Width * a_ChunkX;
for (int z = 0; z < cChunkDef::Width; z++)
{
int AbsoluteZ = BaseZ + z;
for (int x = 0; x < cChunkDef::Width; x++)
{
cChunkDef::SetBiome(a_BiomeMap, x, z, VoronoiBiome(BaseX + x, AbsoluteZ));
} // for x
} // for z
}
void cBioGenVoronoi::InitializeBiomeGen(cIniFile & a_IniFile)
{
super::InitializeBiomeGen(a_IniFile);
m_CellSize = a_IniFile.GetValueSetI("Generator", "VoronoiCellSize", 64);
AString Biomes = a_IniFile.GetValueSet ("Generator", "VoronoiBiomes", "");
InitializeBiomes(Biomes);
}
EMCSBiome cBioGenVoronoi::VoronoiBiome(int a_BlockX, int a_BlockZ)
{
int CellX = a_BlockX / m_CellSize;
int CellZ = a_BlockZ / m_CellSize;
// Note that Noise values need to be divided by 8 to gain a uniform modulo-2^n distribution
// Get 5x5 neighboring cell seeds, compare distance to each. Return the biome in the minumim-distance cell
int MinDist = m_CellSize * m_CellSize * 16; // There has to be a cell closer than this
EMCSBiome res = biPlains; // Will be overriden
for (int x = CellX - 2; x <= CellX + 2; x++)
{
int BaseX = x * m_CellSize;
for (int z = CellZ - 2; z < CellZ + 2; z++)
{
int OffsetX = (m_Noise.IntNoise3DInt(x, 16 * x + 32 * z, z) / 8) % m_CellSize;
int OffsetZ = (m_Noise.IntNoise3DInt(x, 32 * x - 16 * z, z) / 8) % m_CellSize;
int SeedX = BaseX + OffsetX;
int SeedZ = z * m_CellSize + OffsetZ;
int Dist = (SeedX - a_BlockX) * (SeedX - a_BlockX) + (SeedZ - a_BlockZ) * (SeedZ - a_BlockZ);
if (Dist < MinDist)
{
MinDist = Dist;
res = m_Biomes[(m_Noise.IntNoise3DInt(x, x - z + 1000, z) / 8) % m_BiomesCount];
}
} // for z
} // for x
return res;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cBioGenDistortedVoronoi:
void cBioGenDistortedVoronoi::GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap)
{
int BaseZ = cChunkDef::Width * a_ChunkZ;
int BaseX = cChunkDef::Width * a_ChunkX;
// Distortions for linear interpolation:
int DistortX[cChunkDef::Width + 1][cChunkDef::Width + 1];
int DistortZ[cChunkDef::Width + 1][cChunkDef::Width + 1];
for (int x = 0; x <= 4; x++) for (int z = 0; z <= 4; z++)
{
Distort(BaseX + x * 4, BaseZ + z * 4, DistortX[4 * x][4 * z], DistortZ[4 * x][4 * z]);
}
LinearUpscale2DArrayInPlace(&DistortX[0][0], cChunkDef::Width + 1, cChunkDef::Width + 1, 4, 4);
LinearUpscale2DArrayInPlace(&DistortZ[0][0], cChunkDef::Width + 1, cChunkDef::Width + 1, 4, 4);
for (int z = 0; z < cChunkDef::Width; z++)
{
int AbsoluteZ = BaseZ + z;
for (int x = 0; x < cChunkDef::Width; x++)
{
// Distort(BaseX + x, AbsoluteZ, DistX, DistZ);
cChunkDef::SetBiome(a_BiomeMap, x, z, VoronoiBiome(DistortX[x][z], DistortZ[x][z]));
} // for x
} // for z
}
void cBioGenDistortedVoronoi::InitializeBiomeGen(cIniFile & a_IniFile)
{
// Do NOT call super::InitializeBiomeGen(), as it would try to read Voronoi params instead of DistortedVoronoi params
m_CellSize = a_IniFile.GetValueSetI("Generator", "DistortedVoronoiCellSize", 96);
AString Biomes = a_IniFile.GetValueSet ("Generator", "DistortedVoronoiBiomes", "");
InitializeBiomes(Biomes);
}
void cBioGenDistortedVoronoi::Distort(int a_BlockX, int a_BlockZ, int & a_DistortedX, int & a_DistortedZ)
{
double NoiseX = m_Noise.CubicNoise3D((float)a_BlockX / m_CellSize, (float)a_BlockZ / m_CellSize, 1000);
NoiseX += 0.5 * m_Noise.CubicNoise3D(2 * (float)a_BlockX / m_CellSize, 2 * (float)a_BlockZ / m_CellSize, 2000);
NoiseX += 0.08 * m_Noise.CubicNoise3D(16 * (float)a_BlockX / m_CellSize, 16 * (float)a_BlockZ / m_CellSize, 3000);
double NoiseZ = m_Noise.CubicNoise3D((float)a_BlockX / m_CellSize, (float)a_BlockZ / m_CellSize, 4000);
NoiseZ += 0.5 * m_Noise.CubicNoise3D(2 * (float)a_BlockX / m_CellSize, 2 * (float)a_BlockZ / m_CellSize, 5000);
NoiseZ += 0.08 * m_Noise.CubicNoise3D(16 * (float)a_BlockX / m_CellSize, 16 * (float)a_BlockZ / m_CellSize, 6000);
a_DistortedX = a_BlockX + (int)(m_CellSize * 0.5 * NoiseX);
a_DistortedZ = a_BlockZ + (int)(m_CellSize * 0.5 * NoiseZ);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cBioGenMultiStepMap :
cBioGenMultiStepMap::cBioGenMultiStepMap(int a_Seed) :
m_Noise1(a_Seed + 1000),
m_Noise2(a_Seed + 2000),
m_Noise3(a_Seed + 3000),
m_Noise4(a_Seed + 4000),
m_Noise5(a_Seed + 5000),
m_Noise6(a_Seed + 6000),
m_Seed(a_Seed),
m_OceanCellSize(384),
m_MushroomIslandSize(64),
m_RiverCellSize(384),
m_RiverWidthThreshold(0.125),
m_LandBiomesSize(1024)
{
}
void cBioGenMultiStepMap::InitializeBiomeGen(cIniFile & a_IniFile)
{
m_OceanCellSize = a_IniFile.GetValueSetI("Generator", "MultiStepMapOceanCellSize", m_OceanCellSize);
m_MushroomIslandSize = a_IniFile.GetValueSetI("Generator", "MultiStepMapMushroomIslandSize", m_MushroomIslandSize);
m_RiverCellSize = a_IniFile.GetValueSetI("Generator", "MultiStepMapRiverCellSize", m_RiverCellSize);
m_RiverWidthThreshold = a_IniFile.GetValueSetF("Generator", "MultiStepMapRiverWidth", m_RiverWidthThreshold);
m_LandBiomesSize = (float)a_IniFile.GetValueSetI("Generator", "MultiStepMapLandBiomeSize", (int)m_LandBiomesSize);
}
void cBioGenMultiStepMap::GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap)
{
DecideOceanLandMushroom(a_ChunkX, a_ChunkZ, a_BiomeMap);
AddRivers(a_ChunkX, a_ChunkZ, a_BiomeMap);
ApplyTemperatureHumidity(a_ChunkX, a_ChunkZ, a_BiomeMap);
}
void cBioGenMultiStepMap::DecideOceanLandMushroom(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap)
{
// Distorted Voronoi over 3 biomes, with mushroom having only a special occurence.
// Prepare a distortion lookup table, by distorting a 5x5 area and using that as 1:4 zoom (linear interpolate):
int BaseZ = cChunkDef::Width * a_ChunkZ;
int BaseX = cChunkDef::Width * a_ChunkX;
int DistortX[cChunkDef::Width + 1][cChunkDef::Width + 1];
int DistortZ[cChunkDef::Width + 1][cChunkDef::Width + 1];
int DistortSize = m_OceanCellSize / 2;
for (int x = 0; x <= 4; x++) for (int z = 0; z <= 4; z++)
{
Distort(BaseX + x * 4, BaseZ + z * 4, DistortX[4 * x][4 * z], DistortZ[4 * x][4 * z], DistortSize);
}
LinearUpscale2DArrayInPlace(&DistortX[0][0], cChunkDef::Width + 1, cChunkDef::Width + 1, 4, 4);
LinearUpscale2DArrayInPlace(&DistortZ[0][0], cChunkDef::Width + 1, cChunkDef::Width + 1, 4, 4);
// Prepare a 9x9 area of neighboring cell seeds
// (assuming that 7x7 cell area is larger than a chunk being generated)
const int NEIGHBORHOOD_SIZE = 4; // How many seeds in each direction to check
int CellX = BaseX / m_OceanCellSize;
int CellZ = BaseZ / m_OceanCellSize;
int SeedX[2 * NEIGHBORHOOD_SIZE + 1][2 * NEIGHBORHOOD_SIZE + 1];
int SeedZ[2 * NEIGHBORHOOD_SIZE + 1][2 * NEIGHBORHOOD_SIZE + 1];
EMCSBiome SeedV[2 * NEIGHBORHOOD_SIZE + 1][2 * NEIGHBORHOOD_SIZE + 1];
for (int xc = 0; xc < 2 * NEIGHBORHOOD_SIZE + 1; xc++)
{
int RealCellX = xc + CellX - NEIGHBORHOOD_SIZE;
int CellBlockX = RealCellX * m_OceanCellSize;
for (int zc = 0; zc < 2 * NEIGHBORHOOD_SIZE + 1; zc++)
{
int RealCellZ = zc + CellZ - NEIGHBORHOOD_SIZE;
int CellBlockZ = RealCellZ * m_OceanCellSize;
int OffsetX = (m_Noise2.IntNoise3DInt(RealCellX, 16 * RealCellX + 32 * RealCellZ, RealCellZ) / 8) % m_OceanCellSize;
int OffsetZ = (m_Noise4.IntNoise3DInt(RealCellX, 32 * RealCellX - 16 * RealCellZ, RealCellZ) / 8) % m_OceanCellSize;
SeedX[xc][zc] = CellBlockX + OffsetX;
SeedZ[xc][zc] = CellBlockZ + OffsetZ;
SeedV[xc][zc] = (((m_Noise6.IntNoise3DInt(RealCellX, RealCellX - RealCellZ + 1000, RealCellZ) / 11) % 256) > 90) ? biOcean : ((EMCSBiome)(-1));
} // for z
} // for x
for (int xc = 1; xc < 2 * NEIGHBORHOOD_SIZE; xc++) for (int zc = 1; zc < 2 * NEIGHBORHOOD_SIZE; zc++)
{
if (
(SeedV[xc ][zc] == biOcean) &&
(SeedV[xc - 1][zc] == biOcean) &&
(SeedV[xc + 1][zc] == biOcean) &&
(SeedV[xc ][zc - 1] == biOcean) &&
(SeedV[xc ][zc + 1] == biOcean) &&
(SeedV[xc - 1][zc - 1] == biOcean) &&
(SeedV[xc + 1][zc - 1] == biOcean) &&
(SeedV[xc - 1][zc + 1] == biOcean) &&
(SeedV[xc + 1][zc + 1] == biOcean)
)
{
SeedV[xc][zc] = biMushroomIsland;
}
}
// For each column find the nearest distorted cell and use its value as the biome:
int MushroomOceanThreshold = m_OceanCellSize * m_OceanCellSize * m_MushroomIslandSize / 1024;
int MushroomShoreThreshold = m_OceanCellSize * m_OceanCellSize * m_MushroomIslandSize / 2048;
for (int z = 0; z < cChunkDef::Width; z++)
{
for (int x = 0; x < cChunkDef::Width; x++)
{
int AbsoluteZ = DistortZ[x][z];
int AbsoluteX = DistortX[x][z];
int MinDist = m_OceanCellSize * m_OceanCellSize * 16; // There has to be a cell closer than this
EMCSBiome Biome = biPlains;
// Find the nearest cell seed:
for (int xs = 1; xs < 2 * NEIGHBORHOOD_SIZE; xs++) for (int zs = 1; zs < 2 * NEIGHBORHOOD_SIZE; zs++)
{
int Dist = (SeedX[xs][zs] - AbsoluteX) * (SeedX[xs][zs] - AbsoluteX) + (SeedZ[xs][zs] - AbsoluteZ) * (SeedZ[xs][zs] - AbsoluteZ);
if (Dist >= MinDist)
{
continue;
}
MinDist = Dist;
Biome = SeedV[xs][zs];
// Shrink mushroom biome and add a shore:
if (Biome == biMushroomIsland)
{
if (Dist > MushroomOceanThreshold)
{
Biome = biOcean;
}
else if (Dist > MushroomShoreThreshold)
{
Biome = biMushroomShore;
}
}
} // for zs, xs
cChunkDef::SetBiome(a_BiomeMap, x, z, Biome);
} // for x
} // for z
}
void cBioGenMultiStepMap::AddRivers(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap)
{
for (int z = 0; z < cChunkDef::Width; z++)
{
float NoiseCoordZ = (float)(a_ChunkZ * cChunkDef::Width + z) / m_RiverCellSize;
for (int x = 0; x < cChunkDef::Width; x++)
{
if (cChunkDef::GetBiome(a_BiomeMap, x, z) != -1)
{
// Biome already set, skip this column
continue;
}
float NoiseCoordX = (float)(a_ChunkX * cChunkDef::Width + x) / m_RiverCellSize;
double Noise = m_Noise1.CubicNoise2D( NoiseCoordX, NoiseCoordZ);
Noise += 0.5 * m_Noise3.CubicNoise2D(2 * NoiseCoordX, 2 * NoiseCoordZ);
Noise += 0.1 * m_Noise5.CubicNoise2D(8 * NoiseCoordX, 8 * NoiseCoordZ);
if ((Noise > 0) && (Noise < m_RiverWidthThreshold))
{
cChunkDef::SetBiome(a_BiomeMap, x, z, biRiver);
}
} // for x
} // for z
}
void cBioGenMultiStepMap::ApplyTemperatureHumidity(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap)
{
IntMap TemperatureMap;
IntMap HumidityMap;
BuildTemperatureHumidityMaps(a_ChunkX, a_ChunkZ, TemperatureMap, HumidityMap);
FreezeWaterBiomes(a_BiomeMap, TemperatureMap);
DecideLandBiomes(a_BiomeMap, TemperatureMap, HumidityMap);
}
void cBioGenMultiStepMap::Distort(int a_BlockX, int a_BlockZ, int & a_DistortedX, int & a_DistortedZ, int a_CellSize)
{
double NoiseX = m_Noise3.CubicNoise2D( (float)a_BlockX / a_CellSize, (float)a_BlockZ / a_CellSize);
NoiseX += 0.5 * m_Noise2.CubicNoise2D(2 * (float)a_BlockX / a_CellSize, 2 * (float)a_BlockZ / a_CellSize);
NoiseX += 0.1 * m_Noise1.CubicNoise2D(16 * (float)a_BlockX / a_CellSize, 16 * (float)a_BlockZ / a_CellSize);
double NoiseZ = m_Noise6.CubicNoise2D( (float)a_BlockX / a_CellSize, (float)a_BlockZ / a_CellSize);
NoiseZ += 0.5 * m_Noise5.CubicNoise2D(2 * (float)a_BlockX / a_CellSize, 2 * (float)a_BlockZ / a_CellSize);
NoiseZ += 0.1 * m_Noise4.CubicNoise2D(16 * (float)a_BlockX / a_CellSize, 16 * (float)a_BlockZ / a_CellSize);
a_DistortedX = a_BlockX + (int)(a_CellSize * 0.5 * NoiseX);
a_DistortedZ = a_BlockZ + (int)(a_CellSize * 0.5 * NoiseZ);
}
void cBioGenMultiStepMap::BuildTemperatureHumidityMaps(int a_ChunkX, int a_ChunkZ, IntMap & a_TemperatureMap, IntMap & a_HumidityMap)
{
// Linear interpolation over 8x8 blocks; use double for better precision:
DblMap TemperatureMap;
DblMap HumidityMap;
for (int z = 0; z < 17; z += 8)
{
float NoiseCoordZ = (float)(a_ChunkZ * cChunkDef::Width + z) / m_LandBiomesSize;
for (int x = 0; x < 17; x += 8)
{
float NoiseCoordX = (float)(a_ChunkX * cChunkDef::Width + x) / m_LandBiomesSize;
double NoiseT = m_Noise1.CubicNoise2D( NoiseCoordX, NoiseCoordZ);
NoiseT += 0.5 * m_Noise2.CubicNoise2D(2 * NoiseCoordX, 2 * NoiseCoordZ);
NoiseT += 0.1 * m_Noise3.CubicNoise2D(8 * NoiseCoordX, 8 * NoiseCoordZ);
TemperatureMap[x + 17 * z] = NoiseT;
double NoiseH = m_Noise4.CubicNoise2D( NoiseCoordX, NoiseCoordZ);
NoiseH += 0.5 * m_Noise5.CubicNoise2D(2 * NoiseCoordX, 2 * NoiseCoordZ);
NoiseH += 0.1 * m_Noise6.CubicNoise2D(8 * NoiseCoordX, 8 * NoiseCoordZ);
HumidityMap[x + 17 * z] = NoiseH;
} // for x
} // for z
LinearUpscale2DArrayInPlace(TemperatureMap, 17, 17, 8, 8);
LinearUpscale2DArrayInPlace(HumidityMap, 17, 17, 8, 8);
// Re-map into integral values in [0 .. 255] range:
for (int idx = 0; idx < ARRAYCOUNT(a_TemperatureMap); idx++)
{
a_TemperatureMap[idx] = std::max(0, std::min(255, (int)(128 + TemperatureMap[idx] * 128)));
a_HumidityMap[idx] = std::max(0, std::min(255, (int)(128 + HumidityMap[idx] * 128)));
}
}
void cBioGenMultiStepMap::DecideLandBiomes(cChunkDef::BiomeMap & a_BiomeMap, const IntMap & a_TemperatureMap, const IntMap & a_HumidityMap)
{
static const EMCSBiome BiomeMap[] =
{
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
/* 0 */ biTundra, biTundra, biTundra, biTundra, biPlains, biPlains, biPlains, biPlains, biPlains, biPlains, biDesert, biDesert, biDesert, biDesert, biDesert, biDesert,
/* 1 */ biTundra, biTundra, biTundra, biTundra, biPlains, biPlains, biPlains, biPlains, biPlains, biPlains, biDesert, biDesert, biDesert, biDesert, biDesert, biDesert,
/* 2 */ biTundra, biTundra, biTundra, biTundra, biPlains, biExtremeHills, biPlains, biPlains, biPlains, biPlains, biDesert, biDesert, biDesertHills, biDesertHills, biDesert, biDesert,
/* 3 */ biTundra, biTundra, biTundra, biTundra, biExtremeHills, biExtremeHills, biPlains, biPlains, biPlains, biPlains, biDesert, biDesert, biDesertHills, biDesertHills, biDesert, biDesert,
/* 4 */ biTundra, biTundra, biIceMountains, biIceMountains, biExtremeHills, biExtremeHills, biPlains, biPlains, biPlains, biPlains, biForestHills, biForestHills, biExtremeHills, biExtremeHills, biDesertHills, biDesert,
/* 5 */ biTundra, biTundra, biIceMountains, biIceMountains, biExtremeHills, biExtremeHills, biPlains, biPlains, biPlains, biPlains, biForestHills, biForestHills, biExtremeHills, biExtremeHills, biDesertHills, biDesert,
/* 6 */ biTundra, biTundra, biIceMountains, biIceMountains, biForestHills, biForestHills, biForest, biForest, biForest, biForest, biForest, biForestHills, biExtremeHills, biExtremeHills, biPlains, biPlains,
/* 7 */ biTundra, biTundra, biIceMountains, biIceMountains, biForestHills, biForestHills, biForest, biForest, biForest, biForest, biForest, biForestHills, biExtremeHills, biExtremeHills, biPlains, biPlains,
/* 8 */ biTundra, biTundra, biTaiga, biTaiga, biForest, biForest, biForest, biForest, biForest, biForest, biForest, biForestHills, biExtremeHills, biExtremeHills, biPlains, biPlains,
/* 9 */ biTundra, biTundra, biTaiga, biTaiga, biForest, biForest, biForest, biForest, biForest, biForest, biForest, biForestHills, biExtremeHills, biExtremeHills, biPlains, biPlains,
/* 10 */ biTaiga, biTaiga, biTaiga, biIceMountains, biForestHills, biForestHills, biForest, biForest, biForest, biForest, biJungle, biJungle, biSwampland, biSwampland, biSwampland, biSwampland,
/* 11 */ biTaiga, biTaiga, biIceMountains, biIceMountains, biExtremeHills, biForestHills, biForest, biForest, biForest, biForest, biJungle, biJungle, biSwampland, biSwampland, biSwampland, biSwampland,
/* 12 */ biTaiga, biTaiga, biIceMountains, biIceMountains, biExtremeHills, biJungleHills, biJungle, biJungle, biJungle, biJungle, biJungle, biJungle, biSwampland, biSwampland, biSwampland, biSwampland,
/* 13 */ biTaiga, biTaiga, biTaiga, biIceMountains, biJungleHills, biJungleHills, biJungle, biJungle, biJungle, biJungle, biJungle, biJungle, biSwampland, biSwampland, biSwampland, biSwampland,
/* 14 */ biTaiga, biTaiga, biTaiga, biTaiga, biJungle, biJungle, biJungle, biJungle, biJungle, biJungle, biJungle, biJungle, biSwampland, biSwampland, biSwampland, biSwampland,
/* 15 */ biTaiga, biTaiga, biTaiga, biTaiga, biJungle, biJungle, biJungle, biJungle, biJungle, biJungle, biJungle, biJungle, biSwampland, biSwampland, biSwampland, biSwampland,
} ;
for (int z = 0; z < cChunkDef::Width; z++)
{
int idxZ = 17 * z;
for (int x = 0; x < cChunkDef::Width; x++)
{
if (cChunkDef::GetBiome(a_BiomeMap, x, z) != -1)
{
// Already set before
continue;
}
int idx = idxZ + x;
int Temperature = a_TemperatureMap[idx] / 16; // -> [0..15] range
int Humidity = a_HumidityMap[idx] / 16; // -> [0..15] range
cChunkDef::SetBiome(a_BiomeMap, x, z, BiomeMap[Temperature + 16 * Humidity]);
} // for x
} // for z
}
void cBioGenMultiStepMap::FreezeWaterBiomes(cChunkDef::BiomeMap & a_BiomeMap, const IntMap & a_TemperatureMap)
{
int idx = 0;
for (int z = 0; z < cChunkDef::Width; z++)
{
for (int x = 0; x < cChunkDef::Width; x++, idx++)
{
if (a_TemperatureMap[idx] > 4 * 16)
{
// Not frozen
continue;
}
switch (cChunkDef::GetBiome(a_BiomeMap, x, z))
{
case biRiver: cChunkDef::SetBiome(a_BiomeMap, x, z, biFrozenRiver); break;
case biOcean: cChunkDef::SetBiome(a_BiomeMap, x, z, biFrozenOcean); break;
}
} // for x
idx += 1;
} // for z
}
+230
View File
@@ -0,0 +1,230 @@
// BioGen.h
/*
Interfaces to the various biome generators:
- cBioGenConstant
- cBioGenCheckerboard
- cBioGenDistortedVoronoi
*/
#pragma once
#include "ComposableGenerator.h"
#include "../Noise.h"
class cBioGenConstant :
public cBiomeGen
{
public:
cBioGenConstant(void) : m_Biome(biPlains) {}
protected:
EMCSBiome m_Biome;
// cBiomeGen overrides:
virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override;
virtual void InitializeBiomeGen(cIniFile & a_IniFile) override;
} ;
/// A simple cache that stores N most recently generated chunks' biomes; N being settable upon creation
class cBioGenCache :
public cBiomeGen
{
typedef cBiomeGen super;
public:
cBioGenCache(cBiomeGen * a_BioGenToCache, int a_CacheSize); // Doesn't take ownership of a_BioGenToCache
~cBioGenCache();
protected:
cBiomeGen * m_BioGenToCache;
struct sCacheData
{
int m_ChunkX;
int m_ChunkZ;
cChunkDef::BiomeMap m_BiomeMap;
} ;
// To avoid moving large amounts of data for the MRU behavior, we MRU-ize indices to an array of the actual data
int m_CacheSize;
int * m_CacheOrder; // MRU-ized order, indices into m_CacheData array
sCacheData * m_CacheData; // m_CacheData[m_CacheOrder[0]] is the most recently used
// Cache statistics
int m_NumHits;
int m_NumMisses;
int m_TotalChain; // Number of cache items walked to get to a hit (only added for hits)
virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override;
virtual void InitializeBiomeGen(cIniFile & a_IniFile) override;
} ;
/// Base class for generators that use a list of available biomes. This class takes care of the list.
class cBiomeGenList :
public cBiomeGen
{
typedef cBiomeGen super;
protected:
// List of biomes that the generator is allowed to generate:
typedef std::vector<EMCSBiome> EMCSBiomes;
EMCSBiomes m_Biomes;
int m_BiomesCount; // Pulled out of m_Biomes for faster access
/// Parses the INI file setting string into m_Biomes.
void InitializeBiomes(const AString & a_Biomes);
} ;
class cBioGenCheckerboard :
public cBiomeGenList
{
typedef cBiomeGenList super;
protected:
int m_BiomeSize;
// cBiomeGen overrides:
virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override;
virtual void InitializeBiomeGen(cIniFile & a_IniFile) override;
} ;
class cBioGenVoronoi :
public cBiomeGenList
{
typedef cBiomeGenList super;
public:
cBioGenVoronoi(int a_Seed) :
m_Noise(a_Seed)
{
}
protected:
int m_CellSize;
cNoise m_Noise;
// cBiomeGen overrides:
virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override;
virtual void InitializeBiomeGen(cIniFile & a_IniFile) override;
EMCSBiome VoronoiBiome(int a_BlockX, int a_BlockZ);
} ;
class cBioGenDistortedVoronoi :
public cBioGenVoronoi
{
typedef cBioGenVoronoi super;
public:
cBioGenDistortedVoronoi(int a_Seed) :
cBioGenVoronoi(a_Seed)
{
}
protected:
// cBiomeGen overrides:
virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override;
virtual void InitializeBiomeGen(cIniFile & a_IniFile) override;
/// Distorts the coords using a Perlin-like noise
void Distort(int a_BlockX, int a_BlockZ, int & a_DistortedX, int & a_DistortedZ);
} ;
class cBioGenMultiStepMap :
public cBiomeGen
{
typedef cBiomeGen super;
public:
cBioGenMultiStepMap(int a_Seed);
protected:
// Noises used for composing the perlin-noise:
cNoise m_Noise1;
cNoise m_Noise2;
cNoise m_Noise3;
cNoise m_Noise4;
cNoise m_Noise5;
cNoise m_Noise6;
int m_Seed;
int m_OceanCellSize;
int m_MushroomIslandSize;
int m_RiverCellSize;
double m_RiverWidthThreshold;
float m_LandBiomesSize;
typedef int IntMap[17 * 17]; // x + 17 * z, expected trimmed into [0..255] range
typedef double DblMap[17 * 17]; // x + 17 * z, expected trimmed into [0..1] range
// cBiomeGen overrides:
virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override;
virtual void InitializeBiomeGen(cIniFile & a_IniFile) override;
/** Step 1: Decides between ocean, land and mushroom, using a DistVoronoi with special conditions and post-processing for mushroom islands
Sets biomes to biOcean, -1 (i.e. land), biMushroomIsland or biMushroomShore
*/
void DecideOceanLandMushroom(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap);
/** Step 2: Add rivers to the land
Flips some "-1" biomes into biRiver
*/
void AddRivers(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap);
/** Step 3: Decide land biomes using a temperature / humidity map; freeze ocean / river in low temperatures.
Flips all remaining "-1" biomes into land biomes. Also flips some biOcean and biRiver into biFrozenOcean, biFrozenRiver, based on temp map.
*/
void ApplyTemperatureHumidity(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap);
/// Distorts the coords using a Perlin-like noise, with a specified cell-size
void Distort(int a_BlockX, int a_BlockZ, int & a_DistortedX, int & a_DistortedZ, int a_CellSize);
/// Builds two Perlin-noise maps, one for temperature, the other for humidity. Trims both into [0..255] range
void BuildTemperatureHumidityMaps(int a_ChunkX, int a_ChunkZ, IntMap & a_TemperatureMap, IntMap & a_HumidityMap);
/// Flips all remaining "-1" biomes into land biomes using the two maps
void DecideLandBiomes(cChunkDef::BiomeMap & a_BiomeMap, const IntMap & a_TemperatureMap, const IntMap & a_HumidityMap);
/// Flips biOcean and biRiver into biFrozenOcean and biFrozenRiver if the temperature is too low
void FreezeWaterBiomes(cChunkDef::BiomeMap & a_BiomeMap, const IntMap & a_TemperatureMap);
} ;
+970
View File
@@ -0,0 +1,970 @@
// Caves.cpp
// Implements the various cave structure generators:
// - cStructGenWormNestCaves
// - cStructGenDualRidgeCaves
// - cStructGenMarbleCaves
// - cStructGenNetherCaves
/*
WormNestCave generator:
Caves are generated in "nests" - groups of tunnels generated from a single point.
For each chunk, all the nests that could intersect it are generated.
For each nest, first the schematic structure is generated (tunnel from ... to ..., branch, tunnel2 from ... to ...)
Then each tunnel is randomized by inserting points in between its ends.
Finally each tunnel is smoothed and Bresenham-3D-ed so that it is a collection of spheres with their centers next to each other.
When the tunnels are ready, they are simply carved into the chunk, one by one.
To optimize, each tunnel keeps track of its bounding box, so that it can be skipped for chunks that don't intersect it.
MarbleCaves generator:
For each voxel a 3D noise function is evaluated, if the value crosses a boundary, the voxel is dug out, otherwise it is kept.
Problem with this is the amount of CPU work needed for each chunk.
Also the overall shape of the generated holes is unsatisfactory - there are whole "sheets" of holes in the ground.
DualRidgeCaves generator:
Instead of evaluating a single noise function, two different noise functions are multiplied. This produces
regular tunnels instead of sheets. However due to the sheer amount of CPU work needed, the noise functions need to be
reduced in complexity in order for this generator to be useful, so the caves' shapes are "bubbly" at best.
*/
#include "Globals.h"
#include "Caves.h"
/// How many nests in each direction are generated for a given chunk. Must be an even number
#define NEIGHBORHOOD_SIZE 8
const int MIN_RADIUS = 3;
const int MAX_RADIUS = 8;
struct cCaveDefPoint
{
int m_BlockX;
int m_BlockY;
int m_BlockZ;
int m_Radius;
cCaveDefPoint(int a_BlockX, int a_BlockY, int a_BlockZ, int a_Radius) :
m_BlockX(a_BlockX),
m_BlockY(a_BlockY),
m_BlockZ(a_BlockZ),
m_Radius(a_Radius)
{
}
} ;
typedef std::vector<cCaveDefPoint> cCaveDefPoints;
/// A single non-branching tunnel of a WormNestCave
class cCaveTunnel
{
// The bounding box, including the radii around defpoints:
int m_MinBlockX, m_MaxBlockX;
int m_MinBlockY, m_MaxBlockY;
int m_MinBlockZ, m_MaxBlockZ;
/// Generates the shaping defpoints for the ravine, based on the ravine block coords and noise
void Randomize(cNoise & a_Noise);
/// Refines (adds and smooths) defpoints from a_Src into a_Dst; returns false if no refinement possible (segments too short)
bool RefineDefPoints(const cCaveDefPoints & a_Src, cCaveDefPoints & a_Dst);
/// Does rounds of smoothing, two passes of RefineDefPoints(), as long as they return true
void Smooth(void);
/// Linearly interpolates the points so that the maximum distance between two neighbors is max 1 block
void FinishLinear(void);
/// Calculates the bounding box of the points present
void CalcBoundingBox(void);
public:
cCaveDefPoints m_Points;
cCaveTunnel(
int a_BlockStartX, int a_BlockStartY, int a_BlockStartZ, int a_StartRadius,
int a_BlockEndX, int a_BlockEndY, int a_BlockEndZ, int a_EndRadius,
cNoise & a_Noise
);
/// Carves the tunnel into the chunk specified
void ProcessChunk(
int a_ChunkX, int a_ChunkZ,
cChunkDef::BlockTypes & a_BlockTypes,
cChunkDef::HeightMap & a_HeightMap
);
#ifdef _DEBUG
AString ExportAsSVG(int a_Color, int a_OffsetX, int a_OffsetZ) const;
#endif // _DEBUG
} ;
typedef std::vector<cCaveTunnel *> cCaveTunnels;
/// A collection of connected tunnels, possibly branching.
class cStructGenWormNestCaves::cCaveSystem
{
public:
// The generating block position; is read directly in cStructGenWormNestCaves::GetCavesForChunk()
int m_BlockX;
int m_BlockZ;
cCaveSystem(int a_BlockX, int a_BlockZ, int a_MaxOffset, int a_Size, cNoise & a_Noise);
~cCaveSystem();
/// Carves the cave system into the chunk specified
void ProcessChunk(
int a_ChunkX, int a_ChunkZ,
cChunkDef::BlockTypes & a_BlockTypes,
cChunkDef::HeightMap & a_HeightMap
);
#ifdef _DEBUG
AString ExportAsSVG(int a_Color, int a_OffsetX, int a_OffsetZ) const;
#endif // _DEBUG
protected:
int m_Size;
cCaveTunnels m_Tunnels;
void Clear(void);
/// Generates a_Segment successive tunnels, with possible branches. Generates the same output for the same [x, y, z, a_Segments]
void GenerateTunnelsFromPoint(
int a_OriginX, int a_OriginY, int a_OriginZ,
cNoise & a_Noise, int a_Segments
);
/// Returns a radius based on the location provided.
int GetRadius(cNoise & a_Noise, int a_OriginX, int a_OriginY, int a_OriginZ);
} ;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cCaveTunnel:
cCaveTunnel::cCaveTunnel(
int a_BlockStartX, int a_BlockStartY, int a_BlockStartZ, int a_StartRadius,
int a_BlockEndX, int a_BlockEndY, int a_BlockEndZ, int a_EndRadius,
cNoise & a_Noise
)
{
m_Points.push_back(cCaveDefPoint(a_BlockStartX, a_BlockStartY, a_BlockStartZ, a_StartRadius));
m_Points.push_back(cCaveDefPoint(a_BlockEndX, a_BlockEndY, a_BlockEndZ, a_EndRadius));
if ((a_BlockStartY <= 0) && (a_BlockEndY <= 0))
{
// Don't bother detailing this cave, it's under the world anyway
return;
}
Randomize(a_Noise);
Smooth();
// We know that the linear finishing won't affect the bounding box, so let's calculate it now, as we have less data:
CalcBoundingBox();
FinishLinear();
}
void cCaveTunnel::Randomize(cNoise & a_Noise)
{
// Repeat 4 times:
for (int i = 0; i < 4; i++)
{
// For each already present point, insert a point in between it and its predecessor, shifted randomly.
int PrevX = m_Points.front().m_BlockX;
int PrevY = m_Points.front().m_BlockY;
int PrevZ = m_Points.front().m_BlockZ;
int PrevR = m_Points.front().m_Radius;
cCaveDefPoints Pts;
Pts.reserve(m_Points.size() * 2 + 1);
Pts.push_back(m_Points.front());
for (cCaveDefPoints::const_iterator itr = m_Points.begin() + 1, end = m_Points.end(); itr != end; ++itr)
{
int Random = a_Noise.IntNoise3DInt(PrevX, PrevY, PrevZ + i) / 11;
int len = (PrevX - itr->m_BlockX) * (PrevX - itr->m_BlockX);
len += (PrevY - itr->m_BlockY) * (PrevY - itr->m_BlockY);
len += (PrevZ - itr->m_BlockZ) * (PrevZ - itr->m_BlockZ);
len = 3 * (int)sqrt((double)len) / 4;
int Rad = std::min(MAX_RADIUS, std::max(MIN_RADIUS, (PrevR + itr->m_Radius) / 2 + (Random % 3) - 1));
Random /= 4;
int x = (itr->m_BlockX + PrevX) / 2 + (Random % (len + 1) - len / 2);
Random /= 256;
int y = (itr->m_BlockY + PrevY) / 2 + (Random % (len / 2 + 1) - len / 4);
Random /= 256;
int z = (itr->m_BlockZ + PrevZ) / 2 + (Random % (len + 1) - len / 2);
Pts.push_back(cCaveDefPoint(x, y, z, Rad));
Pts.push_back(*itr);
PrevX = itr->m_BlockX;
PrevY = itr->m_BlockY;
PrevZ = itr->m_BlockZ;
PrevR = itr->m_Radius;
}
std::swap(Pts, m_Points);
}
}
bool cCaveTunnel::RefineDefPoints(const cCaveDefPoints & a_Src, cCaveDefPoints & a_Dst)
{
// Smoothing: for each line segment, add points on its 1/4 lengths
bool res = false;
int Num = a_Src.size() - 2; // this many intermediary points
a_Dst.clear();
a_Dst.reserve(Num * 2 + 2);
cCaveDefPoints::const_iterator itr = a_Src.begin() + 1;
a_Dst.push_back(a_Src.front());
int PrevX = a_Src.front().m_BlockX;
int PrevY = a_Src.front().m_BlockY;
int PrevZ = a_Src.front().m_BlockZ;
int PrevR = a_Src.front().m_Radius;
for (int i = 0; i <= Num; ++i, ++itr)
{
int dx = itr->m_BlockX - PrevX;
int dy = itr->m_BlockY - PrevY;
int dz = itr->m_BlockZ - PrevZ;
if (abs(dx) + abs(dz) + abs(dy) < 6)
{
// Too short a segment to smooth-subdivide into quarters
PrevX = itr->m_BlockX;
PrevY = itr->m_BlockY;
PrevZ = itr->m_BlockZ;
PrevR = itr->m_Radius;
continue;
}
int dr = itr->m_Radius - PrevR;
int Rad1 = std::max(PrevR + 1 * dr / 4, 1);
int Rad2 = std::max(PrevR + 3 * dr / 4, 1);
a_Dst.push_back(cCaveDefPoint(PrevX + 1 * dx / 4, PrevY + 1 * dy / 4, PrevZ + 1 * dz / 4, Rad1));
a_Dst.push_back(cCaveDefPoint(PrevX + 3 * dx / 4, PrevY + 3 * dy / 4, PrevZ + 3 * dz / 4, Rad2));
PrevX = itr->m_BlockX;
PrevY = itr->m_BlockY;
PrevZ = itr->m_BlockZ;
PrevR = itr->m_Radius;
res = true;
}
a_Dst.push_back(a_Src.back());
return res && (a_Src.size() < a_Dst.size());
}
void cCaveTunnel::Smooth(void)
{
cCaveDefPoints Pts;
while (true)
{
if (!RefineDefPoints(m_Points, Pts))
{
std::swap(Pts, m_Points);
return;
}
if (!RefineDefPoints(Pts, m_Points))
{
return;
}
}
}
void cCaveTunnel::FinishLinear(void)
{
// For each segment, use Bresenham's 3D line algorithm to draw a "line" of defpoints
cCaveDefPoints Pts;
std::swap(Pts, m_Points);
m_Points.reserve(Pts.size() * 3);
int PrevX = Pts.front().m_BlockX;
int PrevY = Pts.front().m_BlockY;
int PrevZ = Pts.front().m_BlockZ;
for (cCaveDefPoints::const_iterator itr = Pts.begin() + 1, end = Pts.end(); itr != end; ++itr)
{
int x1 = itr->m_BlockX;
int y1 = itr->m_BlockY;
int z1 = itr->m_BlockZ;
int dx = abs(x1 - PrevX);
int dy = abs(y1 - PrevY);
int dz = abs(z1 - PrevZ);
int sx = (PrevX < x1) ? 1 : -1;
int sy = (PrevY < y1) ? 1 : -1;
int sz = (PrevZ < z1) ? 1 : -1;
int err = dx - dz;
int R = itr->m_Radius;
if (dx >= std::max(dy, dz)) // x dominant
{
int yd = dy - dx / 2;
int zd = dz - dx / 2;
while (true)
{
m_Points.push_back(cCaveDefPoint(PrevX, PrevY, PrevZ, R));
if (PrevX == x1)
{
break;
}
if (yd >= 0) // move along y
{
PrevY += sy;
yd -= dx;
}
if (zd >= 0) // move along z
{
PrevZ += sz;
zd -= dx;
}
// move along x
PrevX += sx;
yd += dy;
zd += dz;
}
}
else if (dy >= std::max(dx, dz)) // y dominant
{
int xd = dx - dy / 2;
int zd = dz - dy / 2;
while (true)
{
m_Points.push_back(cCaveDefPoint(PrevX, PrevY, PrevZ, R));
if (PrevY == y1)
{
break;
}
if (xd >= 0) // move along x
{
PrevX += sx;
xd -= dy;
}
if (zd >= 0) // move along z
{
PrevZ += sz;
zd -= dy;
}
// move along y
PrevY += sy;
xd += dx;
zd += dz;
}
}
else
{
// z dominant
ASSERT(dz >= std::max(dx, dy));
int xd = dx - dz / 2;
int yd = dy - dz / 2;
while (true)
{
m_Points.push_back(cCaveDefPoint(PrevX, PrevY, PrevZ, R));
if (PrevZ == z1)
{
break;
}
if (xd >= 0) // move along x
{
PrevX += sx;
xd -= dz;
}
if (yd >= 0) // move along y
{
PrevY += sy;
yd -= dz;
}
// move along z
PrevZ += sz;
xd += dx;
yd += dy;
}
} // if (which dimension is dominant)
} // for itr
}
void cCaveTunnel::CalcBoundingBox(void)
{
m_MinBlockX = m_MaxBlockX = m_Points.front().m_BlockX;
m_MinBlockY = m_MaxBlockY = m_Points.front().m_BlockY;
m_MinBlockZ = m_MaxBlockZ = m_Points.front().m_BlockZ;
for (cCaveDefPoints::const_iterator itr = m_Points.begin() + 1, end = m_Points.end(); itr != end; ++itr)
{
m_MinBlockX = std::min(m_MinBlockX, itr->m_BlockX - itr->m_Radius);
m_MaxBlockX = std::max(m_MaxBlockX, itr->m_BlockX + itr->m_Radius);
m_MinBlockY = std::min(m_MinBlockY, itr->m_BlockY - itr->m_Radius);
m_MaxBlockY = std::max(m_MaxBlockY, itr->m_BlockY + itr->m_Radius);
m_MinBlockZ = std::min(m_MinBlockZ, itr->m_BlockZ - itr->m_Radius);
m_MaxBlockZ = std::max(m_MaxBlockZ, itr->m_BlockZ + itr->m_Radius);
} // for itr - m_Points[]
}
void cCaveTunnel::ProcessChunk(
int a_ChunkX, int a_ChunkZ,
cChunkDef::BlockTypes & a_BlockTypes,
cChunkDef::HeightMap & a_HeightMap
)
{
int BaseX = a_ChunkX * cChunkDef::Width;
int BaseZ = a_ChunkZ * cChunkDef::Width;
if (
(BaseX > m_MaxBlockX) || (BaseX + cChunkDef::Width < m_MinBlockX) ||
(BaseX > m_MaxBlockX) || (BaseX + cChunkDef::Width < m_MinBlockX)
)
{
// Tunnel does not intersect the chunk at all, bail out
return;
}
int BlockStartX = a_ChunkX * cChunkDef::Width;
int BlockStartZ = a_ChunkZ * cChunkDef::Width;
int BlockEndX = BlockStartX + cChunkDef::Width;
int BlockEndZ = BlockStartZ + cChunkDef::Width;
for (cCaveDefPoints::const_iterator itr = m_Points.begin(), end = m_Points.end(); itr != end; ++itr)
{
if (
(itr->m_BlockX + itr->m_Radius < BlockStartX) ||
(itr->m_BlockX - itr->m_Radius > BlockEndX) ||
(itr->m_BlockZ + itr->m_Radius < BlockStartZ) ||
(itr->m_BlockZ - itr->m_Radius > BlockEndZ)
)
{
// Cannot intersect, bail out early
continue;
}
// Carve out a sphere around the xyz point, m_Radius in diameter; skip 3/7 off the top and bottom:
int DifX = itr->m_BlockX - BlockStartX; // substitution for faster calc
int DifY = itr->m_BlockY;
int DifZ = itr->m_BlockZ - BlockStartZ; // substitution for faster calc
int Bottom = std::max(itr->m_BlockY - 3 * itr->m_Radius / 7, 1);
int Top = std::min(itr->m_BlockY + 3 * itr->m_Radius / 7, (int)(cChunkDef::Height));
int SqRad = itr->m_Radius * itr->m_Radius;
for (int z = 0; z < cChunkDef::Width; z++) for (int x = 0; x < cChunkDef::Width; x++)
{
for (int y = Bottom; y <= Top; y++)
{
int SqDist = (DifX - x) * (DifX - x) + (DifY - y) * (DifY - y) + (DifZ - z) * (DifZ - z);
if (4 * SqDist <= SqRad)
{
switch (cChunkDef::GetBlock(a_BlockTypes, x, y, z))
{
// Only carve out these specific block types
case E_BLOCK_DIRT:
case E_BLOCK_GRASS:
case E_BLOCK_STONE:
case E_BLOCK_COBBLESTONE:
case E_BLOCK_GRAVEL:
case E_BLOCK_SAND:
case E_BLOCK_SANDSTONE:
case E_BLOCK_NETHERRACK:
case E_BLOCK_COAL_ORE:
case E_BLOCK_IRON_ORE:
case E_BLOCK_GOLD_ORE:
case E_BLOCK_DIAMOND_ORE:
case E_BLOCK_REDSTONE_ORE:
case E_BLOCK_REDSTONE_ORE_GLOWING:
{
cChunkDef::SetBlock(a_BlockTypes, x, y, z, E_BLOCK_AIR);
break;
}
default: break;
}
}
} // for y
} // for x, z
} // for itr - m_Points[]
/*
#ifdef _DEBUG
// For debugging purposes, outline the shape of the cave using glowstone, *after* carving the entire cave:
for (cCaveDefPoints::const_iterator itr = m_Points.begin(), end = m_Points.end(); itr != end; ++itr)
{
int DifX = itr->m_BlockX - BlockStartX; // substitution for faster calc
int DifZ = itr->m_BlockZ - BlockStartZ; // substitution for faster calc
if (
(DifX >= 0) && (DifX < cChunkDef::Width) &&
(itr->m_BlockY > 0) && (itr->m_BlockY < cChunkDef::Height) &&
(DifZ >= 0) && (DifZ < cChunkDef::Width)
)
{
cChunkDef::SetBlock(a_BlockTypes, DifX, itr->m_BlockY, DifZ, E_BLOCK_GLOWSTONE);
}
} // for itr - m_Points[]
#endif // _DEBUG
//*/
}
#ifdef _DEBUG
AString cCaveTunnel::ExportAsSVG(int a_Color, int a_OffsetX, int a_OffsetZ) const
{
AString SVG;
SVG.reserve(m_Points.size() * 20 + 200);
AppendPrintf(SVG, "<path style=\"fill:none;stroke:#%06x;stroke-width:1px;\"\nd=\"", a_Color);
char Prefix = 'M'; // The first point needs "M" prefix, all the others need "L"
for (cCaveDefPoints::const_iterator itr = m_Points.begin(); itr != m_Points.end(); ++itr)
{
AppendPrintf(SVG, "%c %d,%d ", Prefix, a_OffsetX + itr->m_BlockX, a_OffsetZ + itr->m_BlockZ);
Prefix = 'L';
}
SVG.append("\"/>\n");
return SVG;
}
#endif // _DEBUG
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cStructGenWormNestCaves::cCaveSystem:
cStructGenWormNestCaves::cCaveSystem::cCaveSystem(int a_BlockX, int a_BlockZ, int a_MaxOffset, int a_Size, cNoise & a_Noise) :
m_BlockX(a_BlockX),
m_BlockZ(a_BlockZ),
m_Size(a_Size)
{
int Num = 1 + a_Noise.IntNoise2DInt(a_BlockX, a_BlockZ) % 3;
for (int i = 0; i < Num; i++)
{
int OriginX = a_BlockX + (a_Noise.IntNoise3DInt(13 * a_BlockX, 17 * a_BlockZ, 11 * i) / 19) % a_MaxOffset;
int OriginZ = a_BlockZ + (a_Noise.IntNoise3DInt(17 * a_BlockX, 13 * a_BlockZ, 11 * i) / 23) % a_MaxOffset;
int OriginY = 20 + (a_Noise.IntNoise3DInt(19 * a_BlockX, 13 * a_BlockZ, 11 * i) / 17) % 20;
// Generate three branches from the origin point:
// The tunnels generated depend on X, Y, Z and Branches,
// for the same set of numbers it generates the same offsets!
// That's why we add a +1 to X in the third line
GenerateTunnelsFromPoint(OriginX, OriginY, OriginZ, a_Noise, 3);
GenerateTunnelsFromPoint(OriginX, OriginY, OriginZ, a_Noise, 2);
GenerateTunnelsFromPoint(OriginX + 1, OriginY, OriginZ, a_Noise, 3);
}
}
cStructGenWormNestCaves::cCaveSystem::~cCaveSystem()
{
Clear();
}
void cStructGenWormNestCaves::cCaveSystem::ProcessChunk(
int a_ChunkX, int a_ChunkZ,
cChunkDef::BlockTypes & a_BlockTypes,
cChunkDef::HeightMap & a_HeightMap
)
{
for (cCaveTunnels::const_iterator itr = m_Tunnels.begin(), end = m_Tunnels.end(); itr != end; ++itr)
{
(*itr)->ProcessChunk(a_ChunkX, a_ChunkZ, a_BlockTypes, a_HeightMap);
} // for itr - m_Tunnels[]
}
#ifdef _DEBUG
AString cStructGenWormNestCaves::cCaveSystem::ExportAsSVG(int a_Color, int a_OffsetX, int a_OffsetZ) const
{
AString SVG;
SVG.reserve(512 * 1024);
for (cCaveTunnels::const_iterator itr = m_Tunnels.begin(), end = m_Tunnels.end(); itr != end; ++itr)
{
SVG.append((*itr)->ExportAsSVG(a_Color, a_OffsetX, a_OffsetZ));
} // for itr - m_Tunnels[]
// Base point highlight:
AppendPrintf(SVG, "<path style=\"fill:none;stroke:#ff0000;stroke-width:1px;\"\nd=\"M %d,%d L %d,%d\"/>\n",
a_OffsetX + m_BlockX - 5, a_OffsetZ + m_BlockZ, a_OffsetX + m_BlockX + 5, a_OffsetZ + m_BlockZ
);
AppendPrintf(SVG, "<path style=\"fill:none;stroke:#ff0000;stroke-width:1px;\"\nd=\"M %d,%d L %d,%d\"/>\n",
a_OffsetX + m_BlockX, a_OffsetZ + m_BlockZ - 5, a_OffsetX + m_BlockX, a_OffsetZ + m_BlockZ + 5
);
// A gray line from the base point to the first point of the ravine, for identification:
AppendPrintf(SVG, "<path style=\"fill:none;stroke:#cfcfcf;stroke-width:1px;\"\nd=\"M %d,%d L %d,%d\"/>\n",
a_OffsetX + m_BlockX, a_OffsetZ + m_BlockZ,
a_OffsetX + m_Tunnels.front()->m_Points.front().m_BlockX,
a_OffsetZ + m_Tunnels.front()->m_Points.front().m_BlockZ
);
// Offset guides:
if (a_OffsetX > 0)
{
AppendPrintf(SVG, "<path style=\"fill:none;stroke:#0000ff;stroke-width:1px;\"\nd=\"M %d,0 L %d,1024\"/>\n",
a_OffsetX, a_OffsetX
);
}
if (a_OffsetZ > 0)
{
AppendPrintf(SVG, "<path style=\"fill:none;stroke:#0000ff;stroke-width:1px;\"\nd=\"M 0,%d L 1024,%d\"/>\n",
a_OffsetZ, a_OffsetZ
);
}
return SVG;
}
#endif // _DEBUG
void cStructGenWormNestCaves::cCaveSystem::Clear(void)
{
for (cCaveTunnels::const_iterator itr = m_Tunnels.begin(), end = m_Tunnels.end(); itr != end; ++itr)
{
delete *itr;
}
m_Tunnels.clear();
}
void cStructGenWormNestCaves::cCaveSystem::GenerateTunnelsFromPoint(
int a_OriginX, int a_OriginY, int a_OriginZ,
cNoise & a_Noise, int a_NumSegments
)
{
int DoubleSize = m_Size * 2;
int Radius = GetRadius(a_Noise, a_OriginX + a_OriginY, a_OriginY + a_OriginZ, a_OriginZ + a_OriginX);
for (int i = a_NumSegments - 1; i >= 0; --i)
{
int EndX = a_OriginX + (((a_Noise.IntNoise3DInt(a_OriginX, a_OriginY, a_OriginZ + 11 * a_NumSegments) / 7) % DoubleSize) - m_Size) / 2;
int EndY = a_OriginY + (((a_Noise.IntNoise3DInt(a_OriginY, 13 * a_NumSegments, a_OriginZ + a_OriginX) / 7) % DoubleSize) - m_Size) / 4;
int EndZ = a_OriginZ + (((a_Noise.IntNoise3DInt(a_OriginZ + 17 * a_NumSegments, a_OriginX, a_OriginY) / 7) % DoubleSize) - m_Size) / 2;
int EndR = GetRadius(a_Noise, a_OriginX + 7 * i, a_OriginY + 11 * i, a_OriginZ + a_OriginX);
m_Tunnels.push_back(new cCaveTunnel(a_OriginX, a_OriginY, a_OriginZ, Radius, EndX, EndY, EndZ, EndR, a_Noise));
GenerateTunnelsFromPoint(EndX, EndY, EndZ, a_Noise, i);
a_OriginX = EndX;
a_OriginY = EndY;
a_OriginZ = EndZ;
Radius = EndR;
} // for i - a_NumSegments
}
int cStructGenWormNestCaves::cCaveSystem::GetRadius(cNoise & a_Noise, int a_OriginX, int a_OriginY, int a_OriginZ)
{
// Instead of a flat distribution noise function, we need to shape it, so that most caves are smallish and only a few select are large
int rnd = a_Noise.IntNoise3DInt(a_OriginX, a_OriginY, a_OriginZ) / 11;
/*
// Not good enough:
// The algorithm of choice: emulate gauss-distribution noise by adding 3 flat noises, then fold it in half using absolute value.
// To save on processing, use one random value and extract 3 bytes to be separately added as the gaussian noise
int sum = (rnd & 0xff) + ((rnd >> 8) & 0xff) + ((rnd >> 16) & 0xff);
// sum is now a gaussian-distribution noise within [0 .. 767], with center at 384.
// We want mapping 384 -> 3, 0 -> 19, 768 -> 19, so divide by 24 to get [0 .. 31] with center at 16, then use abs() to fold around the center
int res = 3 + abs((sum / 24) - 16);
*/
// Algorithm of choice: random value in the range of zero to random value - heavily towards zero
int res = MIN_RADIUS + (rnd >> 8) % ((rnd % (MAX_RADIUS - MIN_RADIUS)) + 1);
return res;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cStructGenWormNestCaves:
cStructGenWormNestCaves::~cStructGenWormNestCaves()
{
ClearCache();
}
void cStructGenWormNestCaves::ClearCache(void)
{
for (cCaveSystems::const_iterator itr = m_Cache.begin(), end = m_Cache.end(); itr != end; ++itr)
{
delete *itr;
} // for itr - m_Cache[]
m_Cache.clear();
}
void cStructGenWormNestCaves::GenStructures(cChunkDesc & a_ChunkDesc)
{
int ChunkX = a_ChunkDesc.GetChunkX();
int ChunkZ = a_ChunkDesc.GetChunkZ();
cCaveSystems Caves;
GetCavesForChunk(ChunkX, ChunkZ, Caves);
for (cCaveSystems::const_iterator itr = Caves.begin(); itr != Caves.end(); ++itr)
{
(*itr)->ProcessChunk(ChunkX, ChunkZ, a_ChunkDesc.GetBlockTypes(), a_ChunkDesc.GetHeightMap());
} // for itr - Caves[]
}
void cStructGenWormNestCaves::GetCavesForChunk(int a_ChunkX, int a_ChunkZ, cStructGenWormNestCaves::cCaveSystems & a_Caves)
{
int BaseX = a_ChunkX * cChunkDef::Width / m_Grid;
int BaseZ = a_ChunkZ * cChunkDef::Width / m_Grid;
if (BaseX < 0)
{
--BaseX;
}
if (BaseZ < 0)
{
--BaseZ;
}
BaseX -= NEIGHBORHOOD_SIZE / 2;
BaseZ -= NEIGHBORHOOD_SIZE / 2;
// Walk the cache, move each cave system that we want into a_Caves:
int StartX = BaseX * m_Grid;
int EndX = (BaseX + NEIGHBORHOOD_SIZE + 1) * m_Grid;
int StartZ = BaseZ * m_Grid;
int EndZ = (BaseZ + NEIGHBORHOOD_SIZE + 1) * m_Grid;
for (cCaveSystems::iterator itr = m_Cache.begin(), end = m_Cache.end(); itr != end;)
{
if (
((*itr)->m_BlockX >= StartX) && ((*itr)->m_BlockX < EndX) &&
((*itr)->m_BlockZ >= StartZ) && ((*itr)->m_BlockZ < EndZ)
)
{
// want
a_Caves.push_back(*itr);
itr = m_Cache.erase(itr);
}
else
{
// don't want
++itr;
}
} // for itr - m_Cache[]
for (int x = 0; x < NEIGHBORHOOD_SIZE; x++)
{
int RealX = (BaseX + x) * m_Grid;
for (int z = 0; z < NEIGHBORHOOD_SIZE; z++)
{
int RealZ = (BaseZ + z) * m_Grid;
bool Found = false;
for (cCaveSystems::const_iterator itr = a_Caves.begin(), end = a_Caves.end(); itr != end; ++itr)
{
if (((*itr)->m_BlockX == RealX) && ((*itr)->m_BlockZ == RealZ))
{
Found = true;
break;
}
}
if (!Found)
{
a_Caves.push_back(new cCaveSystem(RealX, RealZ, m_MaxOffset, m_Size, m_Noise));
}
}
}
// Copy a_Caves into m_Cache to the beginning:
cCaveSystems CavesCopy(a_Caves);
m_Cache.splice(m_Cache.begin(), CavesCopy, CavesCopy.begin(), CavesCopy.end());
// Trim the cache if it's too long:
if (m_Cache.size() > 100)
{
cCaveSystems::iterator itr = m_Cache.begin();
std::advance(itr, 100);
for (cCaveSystems::iterator end = m_Cache.end(); itr != end; ++itr)
{
delete *itr;
}
itr = m_Cache.begin();
std::advance(itr, 100);
m_Cache.erase(itr, m_Cache.end());
}
/*
// Uncomment this block for debugging the caves' shapes in 2D using an SVG export
#ifdef _DEBUG
AString SVG;
SVG.append("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"1024\" height = \"1024\">\n");
SVG.reserve(2 * 1024 * 1024);
for (cCaveSystems::const_iterator itr = a_Caves.begin(), end = a_Caves.end(); itr != end; ++itr)
{
int Color = 0x10 * abs((*itr)->m_BlockX / m_Grid);
Color |= 0x1000 * abs((*itr)->m_BlockZ / m_Grid);
SVG.append((*itr)->ExportAsSVG(Color, 512, 512));
}
SVG.append("</svg>\n");
AString fnam;
Printf(fnam, "wnc\\%03d_%03d.svg", a_ChunkX, a_ChunkZ);
cFile File(fnam, cFile::fmWrite);
File.Write(SVG.c_str(), SVG.size());
#endif // _DEBUG
//*/
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cStructGenMarbleCaves:
static float GetMarbleNoise( float x, float y, float z, cNoise & a_Noise )
{
static const float PI_2 = 1.57079633f;
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;
}
void cStructGenMarbleCaves::GenStructures(cChunkDesc & a_ChunkDesc)
{
cNoise Noise(m_Seed);
for (int z = 0; z < cChunkDef::Width; z++)
{
const float zz = (float)(a_ChunkDesc.GetChunkZ() * cChunkDef::Width + z);
for (int x = 0; x < cChunkDef::Width; x++)
{
const float xx = (float)(a_ChunkDesc.GetChunkX() * cChunkDef::Width + x);
int Top = a_ChunkDesc.GetHeight(x, z);
for (int y = 1; y < Top; ++y )
{
if (a_ChunkDesc.GetBlockType(x, y, z) != E_BLOCK_STONE)
{
continue;
}
const float yy = (float)y;
const float WaveNoise = 1;
if (cosf(GetMarbleNoise(xx, yy * 0.5f, zz, Noise)) * fabs(cosf(yy * 0.2f + WaveNoise * 2) * 0.75f + WaveNoise) > 0.0005f)
{
a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_AIR);
}
} // for y
} // for x
} // for z
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cStructGenDualRidgeCaves:
void cStructGenDualRidgeCaves::GenStructures(cChunkDesc & a_ChunkDesc)
{
for (int z = 0; z < cChunkDef::Width; z++)
{
const float zz = (float)(a_ChunkDesc.GetChunkZ() * cChunkDef::Width + z) / 10;
for (int x = 0; x < cChunkDef::Width; x++)
{
const float xx = (float)(a_ChunkDesc.GetChunkX() * cChunkDef::Width + x) / 10;
int Top = a_ChunkDesc.GetHeight(x, z);
for (int y = 1; y <= Top; ++y)
{
const float yy = (float)y / 10;
const float WaveNoise = 1;
float n1 = m_Noise1.CubicNoise3D(xx, yy, zz);
float n2 = m_Noise2.CubicNoise3D(xx, yy, zz);
float n3 = m_Noise1.CubicNoise3D(xx * 4, yy * 4, zz * 4) / 4;
float n4 = m_Noise2.CubicNoise3D(xx * 4, yy * 4, zz * 4) / 4;
if ((abs(n1 + n3) * abs(n2 + n4)) > m_Threshold)
{
a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_AIR);
}
} // for y
} // for x
} // for z
}
+102
View File
@@ -0,0 +1,102 @@
// Caves.h
// Interfaces to the various cave structure generators:
// - cStructGenWormNestCaves
// - cStructGenMarbleCaves
// - cStructGenNetherCaves
#pragma once
#include "ComposableGenerator.h"
#include "../Noise.h"
class cStructGenMarbleCaves :
public cStructureGen
{
public:
cStructGenMarbleCaves(int a_Seed) : m_Seed(a_Seed) {}
protected:
int m_Seed;
// cStructureGen override:
virtual void GenStructures(cChunkDesc & a_ChunkDesc) override;
} ;
class cStructGenDualRidgeCaves :
public cStructureGen
{
public:
cStructGenDualRidgeCaves(int a_Seed, float a_Threshold) :
m_Noise1(a_Seed),
m_Noise2(2 * a_Seed + 19999),
m_Seed(a_Seed),
m_Threshold(a_Threshold)
{
}
protected:
cNoise m_Noise1;
cNoise m_Noise2;
int m_Seed;
float m_Threshold;
// cStructureGen override:
virtual void GenStructures(cChunkDesc & a_ChunkDesc) override;
} ;
class cStructGenWormNestCaves :
public cStructureGen
{
public:
cStructGenWormNestCaves(int a_Seed, int a_Size = 64, int a_Grid = 96, int a_MaxOffset = 128) :
m_Noise(a_Seed),
m_Size(a_Size),
m_Grid(a_Grid),
m_MaxOffset(a_MaxOffset)
{
}
~cStructGenWormNestCaves();
protected:
class cCaveSystem; // fwd: Caves.cpp
typedef std::list<cCaveSystem *> cCaveSystems;
cNoise m_Noise;
int m_Size; // relative size of the cave systems' caves. Average number of blocks of each initial tunnel
int m_MaxOffset; // maximum offset of the cave nest origin from the grid cell the nest belongs to
int m_Grid; // average spacing of the nests
cCaveSystems m_Cache;
/// Clears everything from the cache
void ClearCache(void);
/// Returns all caves that *may* intersect the given chunk. All the caves are valid until the next call to this function.
void GetCavesForChunk(int a_ChunkX, int a_ChunkZ, cCaveSystems & a_Caves);
// cStructGen override:
virtual void GenStructures(cChunkDesc & a_ChunkDesc) override;
} ;
+605
View File
@@ -0,0 +1,605 @@
// ChunkDesc.cpp
// Implements the cChunkDesc class representing the chunk description used while generating a chunk. This class is also exported to Lua for HOOK_CHUNK_GENERATING.
#include "Globals.h"
#include "ChunkDesc.h"
#include "../BlockArea.h"
#include "../Cuboid.h"
#include "../Noise.h"
#include "../BlockEntities/BlockEntity.h"
cChunkDesc::cChunkDesc(int a_ChunkX, int a_ChunkZ) :
m_ChunkX(a_ChunkX),
m_ChunkZ(a_ChunkZ),
m_bUseDefaultBiomes(true),
m_bUseDefaultHeight(true),
m_bUseDefaultComposition(true),
m_bUseDefaultStructures(true),
m_bUseDefaultFinish(true)
{
m_BlockArea.Create(cChunkDef::Width, cChunkDef::Height, cChunkDef::Width);
/*
memset(m_BlockTypes, 0, sizeof(cChunkDef::BlockTypes));
memset(m_BlockMeta, 0, sizeof(cChunkDef::BlockNibbles));
*/
memset(m_BiomeMap, 0, sizeof(cChunkDef::BiomeMap));
memset(m_HeightMap, 0, sizeof(cChunkDef::HeightMap));
}
cChunkDesc::~cChunkDesc()
{
// Nothing needed yet
}
void cChunkDesc::SetChunkCoords(int a_ChunkX, int a_ChunkZ)
{
m_ChunkX = a_ChunkX;
m_ChunkZ = a_ChunkZ;
}
void cChunkDesc::FillBlocks(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
{
m_BlockArea.Fill(cBlockArea::baTypes | cBlockArea::baMetas, a_BlockType, a_BlockMeta);
}
void cChunkDesc::SetBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
{
m_BlockArea.SetRelBlockTypeMeta(a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta);
}
void cChunkDesc::GetBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta)
{
m_BlockArea.GetRelBlockTypeMeta(a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta);
}
void cChunkDesc::SetBlockType(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType)
{
cChunkDef::SetBlock(m_BlockArea.GetBlockTypes(), a_RelX, a_RelY, a_RelZ, a_BlockType);
}
BLOCKTYPE cChunkDesc::GetBlockType(int a_RelX, int a_RelY, int a_RelZ)
{
return cChunkDef::GetBlock(m_BlockArea.GetBlockTypes(), a_RelX, a_RelY, a_RelZ);
}
NIBBLETYPE cChunkDesc::GetBlockMeta(int a_RelX, int a_RelY, int a_RelZ)
{
return m_BlockArea.GetRelBlockMeta(a_RelX, a_RelY, a_RelZ);
}
void cChunkDesc::SetBlockMeta(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_BlockMeta)
{
m_BlockArea.SetRelBlockMeta(a_RelX, a_RelY, a_RelZ, a_BlockMeta);
}
void cChunkDesc::SetBiome(int a_RelX, int a_RelZ, int a_BiomeID)
{
cChunkDef::SetBiome(m_BiomeMap, a_RelX, a_RelZ, (EMCSBiome)a_BiomeID);
}
EMCSBiome cChunkDesc::GetBiome(int a_RelX, int a_RelZ)
{
return cChunkDef::GetBiome(m_BiomeMap, a_RelX, a_RelZ);
}
void cChunkDesc::SetHeight(int a_RelX, int a_RelZ, int a_Height)
{
cChunkDef::SetHeight(m_HeightMap, a_RelX, a_RelZ, a_Height);
}
int cChunkDesc::GetHeight(int a_RelX, int a_RelZ)
{
return cChunkDef::GetHeight(m_HeightMap, a_RelX, a_RelZ);
}
void cChunkDesc::SetUseDefaultBiomes(bool a_bUseDefaultBiomes)
{
m_bUseDefaultBiomes = a_bUseDefaultBiomes;
}
bool cChunkDesc::IsUsingDefaultBiomes(void) const
{
return m_bUseDefaultBiomes;
}
void cChunkDesc::SetUseDefaultHeight(bool a_bUseDefaultHeight)
{
m_bUseDefaultHeight = a_bUseDefaultHeight;
}
bool cChunkDesc::IsUsingDefaultHeight(void) const
{
return m_bUseDefaultHeight;
}
void cChunkDesc::SetUseDefaultComposition(bool a_bUseDefaultComposition)
{
m_bUseDefaultComposition = a_bUseDefaultComposition;
}
bool cChunkDesc::IsUsingDefaultComposition(void) const
{
return m_bUseDefaultComposition;
}
void cChunkDesc::SetUseDefaultStructures(bool a_bUseDefaultStructures)
{
m_bUseDefaultStructures = a_bUseDefaultStructures;
}
bool cChunkDesc::IsUsingDefaultStructures(void) const
{
return m_bUseDefaultStructures;
}
void cChunkDesc::SetUseDefaultFinish(bool a_bUseDefaultFinish)
{
m_bUseDefaultFinish = a_bUseDefaultFinish;
}
bool cChunkDesc::IsUsingDefaultFinish(void) const
{
return m_bUseDefaultFinish;
}
void cChunkDesc::WriteBlockArea(const cBlockArea & a_BlockArea, int a_RelX, int a_RelY, int a_RelZ, cBlockArea::eMergeStrategy a_MergeStrategy)
{
m_BlockArea.Merge(a_BlockArea, a_RelX, a_RelY, a_RelZ, a_MergeStrategy);
}
void cChunkDesc::ReadBlockArea(cBlockArea & a_Dest, int a_MinRelX, int a_MaxRelX, int a_MinRelY, int a_MaxRelY, int a_MinRelZ, int a_MaxRelZ)
{
// Normalize the coords:
if (a_MinRelX > a_MaxRelX)
{
std::swap(a_MinRelX, a_MaxRelX);
}
if (a_MinRelY > a_MaxRelY)
{
std::swap(a_MinRelY, a_MaxRelY);
}
if (a_MinRelZ > a_MaxRelZ)
{
std::swap(a_MinRelZ, a_MaxRelZ);
}
// Include the Max coords:
a_MaxRelX += 1;
a_MaxRelY += 1;
a_MaxRelZ += 1;
// Check coords validity:
if (a_MinRelX < 0)
{
LOGWARNING("%s: MinRelX less than zero, adjusting to zero", __FUNCTION__);
a_MinRelX = 0;
}
else if (a_MinRelX >= cChunkDef::Width)
{
LOGWARNING("%s: MinRelX more than chunk width, adjusting to chunk width", __FUNCTION__);
a_MinRelX = cChunkDef::Width - 1;
}
if (a_MaxRelX < 0)
{
LOGWARNING("%s: MaxRelX less than zero, adjusting to zero", __FUNCTION__);
a_MaxRelX = 0;
}
else if (a_MinRelX >= cChunkDef::Width)
{
LOGWARNING("%s: MaxRelX more than chunk width, adjusting to chunk width", __FUNCTION__);
a_MaxRelX = cChunkDef::Width - 1;
}
if (a_MinRelY < 0)
{
LOGWARNING("%s: MinRelY less than zero, adjusting to zero", __FUNCTION__);
a_MinRelY = 0;
}
else if (a_MinRelY >= cChunkDef::Height)
{
LOGWARNING("%s: MinRelY more than chunk height, adjusting to chunk height", __FUNCTION__);
a_MinRelY = cChunkDef::Height - 1;
}
if (a_MaxRelY < 0)
{
LOGWARNING("%s: MaxRelY less than zero, adjusting to zero", __FUNCTION__);
a_MaxRelY = 0;
}
else if (a_MinRelY >= cChunkDef::Height)
{
LOGWARNING("%s: MaxRelY more than chunk height, adjusting to chunk height", __FUNCTION__);
a_MaxRelY = cChunkDef::Height - 1;
}
if (a_MinRelZ < 0)
{
LOGWARNING("%s: MinRelZ less than zero, adjusting to zero", __FUNCTION__);
a_MinRelZ = 0;
}
else if (a_MinRelZ >= cChunkDef::Width)
{
LOGWARNING("%s: MinRelZ more than chunk width, adjusting to chunk width", __FUNCTION__);
a_MinRelZ = cChunkDef::Width - 1;
}
if (a_MaxRelZ < 0)
{
LOGWARNING("%s: MaxRelZ less than zero, adjusting to zero", __FUNCTION__);
a_MaxRelZ = 0;
}
else if (a_MinRelZ >= cChunkDef::Width)
{
LOGWARNING("%s: MaxRelZ more than chunk width, adjusting to chunk width", __FUNCTION__);
a_MaxRelZ = cChunkDef::Width - 1;
}
// Prepare the block area:
int SizeX = a_MaxRelX - a_MinRelX;
int SizeY = a_MaxRelY - a_MinRelY;
int SizeZ = a_MaxRelZ - a_MinRelZ;
a_Dest.Clear();
a_Dest.m_OriginX = m_ChunkX * cChunkDef::Width + a_MinRelX;
a_Dest.m_OriginY = a_MinRelY;
a_Dest.m_OriginZ = m_ChunkZ * cChunkDef::Width + a_MinRelZ;
a_Dest.SetSize(SizeX, SizeY, SizeZ, cBlockArea::baTypes | cBlockArea::baMetas);
for (int y = 0; y < SizeY; y++)
{
int CDY = a_MinRelY + y;
for (int z = 0; z < SizeZ; z++)
{
int CDZ = a_MinRelZ + z;
for (int x = 0; x < SizeX; x++)
{
int CDX = a_MinRelX + x;
BLOCKTYPE BlockType;
NIBBLETYPE BlockMeta;
GetBlockTypeMeta(CDX, CDY, CDZ, BlockType, BlockMeta);
a_Dest.SetRelBlockTypeMeta(x, y, z, BlockType, BlockMeta);
} // for x
} // for z
} // for y
}
HEIGHTTYPE cChunkDesc::GetMaxHeight(void) const
{
HEIGHTTYPE MaxHeight = m_HeightMap[0];
for (int i = 1; i < ARRAYCOUNT(m_HeightMap); i++)
{
if (m_HeightMap[i] > MaxHeight)
{
MaxHeight = m_HeightMap[i];
}
}
return MaxHeight;
}
void cChunkDesc::FillRelCuboid(
int a_MinX, int a_MaxX,
int a_MinY, int a_MaxY,
int a_MinZ, int a_MaxZ,
BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta
)
{
int MinX = std::max(a_MinX, 0);
int MinY = std::max(a_MinY, 0);
int MinZ = std::max(a_MinZ, 0);
int MaxX = std::min(a_MaxX, cChunkDef::Width - 1);
int MaxY = std::min(a_MaxY, cChunkDef::Height - 1);
int MaxZ = std::min(a_MaxZ, cChunkDef::Width - 1);
for (int y = MinY; y <= MaxY; y++)
{
for (int z = MinZ; z <= MaxZ; z++)
{
for (int x = MinX; x <= MaxX; x++)
{
SetBlockTypeMeta(x, y, z, a_BlockType, a_BlockMeta);
}
} // for z
} // for y
}
void cChunkDesc::ReplaceRelCuboid(
int a_MinX, int a_MaxX,
int a_MinY, int a_MaxY,
int a_MinZ, int a_MaxZ,
BLOCKTYPE a_SrcType, NIBBLETYPE a_SrcMeta,
BLOCKTYPE a_DstType, NIBBLETYPE a_DstMeta
)
{
int MinX = std::max(a_MinX, 0);
int MinY = std::max(a_MinY, 0);
int MinZ = std::max(a_MinZ, 0);
int MaxX = std::min(a_MaxX, cChunkDef::Width - 1);
int MaxY = std::min(a_MaxY, cChunkDef::Height - 1);
int MaxZ = std::min(a_MaxZ, cChunkDef::Width - 1);
for (int y = MinY; y <= MaxY; y++)
{
for (int z = MinZ; z <= MaxZ; z++)
{
for (int x = MinX; x <= MaxX; x++)
{
BLOCKTYPE BlockType;
NIBBLETYPE BlockMeta;
GetBlockTypeMeta(x, y, z, BlockType, BlockMeta);
if ((BlockType == a_SrcType) && (BlockMeta == a_SrcMeta))
{
SetBlockTypeMeta(x, y, z, a_DstType, a_DstMeta);
}
}
} // for z
} // for y
}
void cChunkDesc::FloorRelCuboid(
int a_MinX, int a_MaxX,
int a_MinY, int a_MaxY,
int a_MinZ, int a_MaxZ,
BLOCKTYPE a_DstType, NIBBLETYPE a_DstMeta
)
{
int MinX = std::max(a_MinX, 0);
int MinY = std::max(a_MinY, 0);
int MinZ = std::max(a_MinZ, 0);
int MaxX = std::min(a_MaxX, cChunkDef::Width - 1);
int MaxY = std::min(a_MaxY, cChunkDef::Height - 1);
int MaxZ = std::min(a_MaxZ, cChunkDef::Width - 1);
for (int y = MinY; y <= MaxY; y++)
{
for (int z = MinZ; z <= MaxZ; z++)
{
for (int x = MinX; x <= MaxX; x++)
{
switch (GetBlockType(x, y, z))
{
case E_BLOCK_AIR:
case E_BLOCK_WATER:
case E_BLOCK_STATIONARY_WATER:
{
SetBlockTypeMeta(x, y, z, a_DstType, a_DstMeta);
break;
}
} // switch (GetBlockType)
} // for x
} // for z
} // for y
}
void cChunkDesc::RandomFillRelCuboid(
int a_MinX, int a_MaxX,
int a_MinY, int a_MaxY,
int a_MinZ, int a_MaxZ,
BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta,
int a_RandomSeed, int a_ChanceOutOf10k
)
{
cNoise Noise(a_RandomSeed);
int MinX = std::max(a_MinX, 0);
int MinY = std::max(a_MinY, 0);
int MinZ = std::max(a_MinZ, 0);
int MaxX = std::min(a_MaxX, cChunkDef::Width - 1);
int MaxY = std::min(a_MaxY, cChunkDef::Height - 1);
int MaxZ = std::min(a_MaxZ, cChunkDef::Width - 1);
for (int y = MinY; y <= MaxY; y++)
{
for (int z = MinZ; z <= MaxZ; z++)
{
for (int x = MinX; x <= MaxX; x++)
{
int rnd = (Noise.IntNoise3DInt(x, y, z) / 7) % 10000;
if (rnd <= a_ChanceOutOf10k)
{
SetBlockTypeMeta(x, y, z, a_BlockType, a_BlockMeta);
}
}
} // for z
} // for y
}
cBlockEntity * cChunkDesc::GetBlockEntity(int a_RelX, int a_RelY, int a_RelZ)
{
int AbsX = a_RelX + m_ChunkX * cChunkDef::Width;
int AbsZ = a_RelZ + m_ChunkZ * cChunkDef::Width;
for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), end = m_BlockEntities.end(); itr != end; ++itr)
{
if (((*itr)->GetPosX() == AbsX) && ((*itr)->GetPosY() == a_RelY) && ((*itr)->GetPosZ() == AbsZ))
{
// Already in the list:
if ((*itr)->GetBlockType() != GetBlockType(a_RelX, a_RelY, a_RelZ))
{
// Wrong type, the block type has been overwritten. Erase and create new:
m_BlockEntities.erase(itr);
break;
}
// Correct type, already present. Return it:
return *itr;
}
} // for itr - m_BlockEntities[]
// The block entity is not created yet, try to create it and add to list:
cBlockEntity * be = cBlockEntity::CreateByBlockType(GetBlockType(a_RelX, a_RelY, a_RelZ), GetBlockMeta(a_RelX, a_RelY, a_RelZ), AbsX, a_RelY, AbsZ);
if (be == NULL)
{
// No block entity for this block type
return NULL;
}
m_BlockEntities.push_back(be);
return be;
}
void cChunkDesc::CompressBlockMetas(cChunkDef::BlockNibbles & a_DestMetas)
{
const NIBBLETYPE * AreaMetas = m_BlockArea.GetBlockMetas();
for (int i = 0; i < ARRAYCOUNT(a_DestMetas); i++)
{
a_DestMetas[i] = AreaMetas[2 * i] | (AreaMetas[2 * i + 1] << 4);
}
}
#ifdef _DEBUG
void cChunkDesc::VerifyHeightmap(void)
{
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--)
{
BLOCKTYPE BlockType = GetBlockType(x, y, z);
if (BlockType != E_BLOCK_AIR)
{
int Height = GetHeight(x, z);
ASSERT(Height == y);
break;
}
} // for y
} // for z
} // for x
}
#endif // _DEBUG
+217
View File
@@ -0,0 +1,217 @@
// ChunkDesc.h
// Declares the cChunkDesc class representing the chunk description used while generating a chunk. This class is also exported to Lua for HOOK_CHUNK_GENERATING.
#pragma once
#include "../BlockArea.h"
#include "../ChunkDef.h"
#include "../Cuboid.h"
// fwd: ../BlockArea.h
class cBlockArea;
// tolua_begin
class cChunkDesc
{
public:
// tolua_end
/// Uncompressed block metas, 1 meta per byte
typedef NIBBLETYPE BlockNibbleBytes[cChunkDef::NumBlocks];
cChunkDesc(int a_ChunkX, int a_ChunkZ);
~cChunkDesc();
void SetChunkCoords(int a_ChunkX, int a_ChunkZ);
// tolua_begin
int GetChunkX(void) const { return m_ChunkX; }
int GetChunkZ(void) const { return m_ChunkZ; }
void FillBlocks(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
void SetBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
void GetBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta);
void SetBlockType(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType);
BLOCKTYPE GetBlockType(int a_RelX, int a_RelY, int a_RelZ);
void SetBlockMeta(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_BlockMeta);
NIBBLETYPE GetBlockMeta(int a_RelX, int a_RelY, int a_RelZ);
void SetBiome(int a_RelX, int a_RelZ, int a_BiomeID);
EMCSBiome GetBiome(int a_RelX, int a_RelZ);
void SetHeight(int a_RelX, int a_RelZ, int a_Height);
int GetHeight(int a_RelX, int a_RelZ);
// Default generation:
void SetUseDefaultBiomes(bool a_bUseDefaultBiomes);
bool IsUsingDefaultBiomes(void) const;
void SetUseDefaultHeight(bool a_bUseDefaultHeight);
bool IsUsingDefaultHeight(void) const;
void SetUseDefaultComposition(bool a_bUseDefaultComposition);
bool IsUsingDefaultComposition(void) const;
void SetUseDefaultStructures(bool a_bUseDefaultStructures);
bool IsUsingDefaultStructures(void) const;
void SetUseDefaultFinish(bool a_bUseDefaultFinish);
bool IsUsingDefaultFinish(void) const;
/// Writes the block area into the chunk, with its origin set at the specified relative coords. Area's data overwrite everything in the chunk.
void WriteBlockArea(const cBlockArea & a_BlockArea, int a_RelX, int a_RelY, int a_RelZ, cBlockArea::eMergeStrategy a_MergeStrategy = cBlockArea::msOverwrite);
/// Reads an area from the chunk into a cBlockArea, blocktypes and blockmetas
void ReadBlockArea(cBlockArea & a_Dest, int a_MinRelX, int a_MaxRelX, int a_MinRelY, int a_MaxRelY, int a_MinRelZ, int a_MaxRelZ);
/// Returns the maximum height value in the heightmap
HEIGHTTYPE GetMaxHeight(void) const;
/// Fills the relative cuboid with specified block; allows cuboid out of range of this chunk
void FillRelCuboid(
int a_MinX, int a_MaxX,
int a_MinY, int a_MaxY,
int a_MinZ, int a_MaxZ,
BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta
);
/// Fills the relative cuboid with specified block; allows cuboid out of range of this chunk
void FillRelCuboid(const cCuboid & a_RelCuboid, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
{
FillRelCuboid(
a_RelCuboid.p1.x, a_RelCuboid.p2.x,
a_RelCuboid.p1.y, a_RelCuboid.p2.y,
a_RelCuboid.p1.z, a_RelCuboid.p2.z,
a_BlockType, a_BlockMeta
);
}
/// Replaces the specified src blocks in the cuboid by the dst blocks; allows cuboid out of range of this chunk
void ReplaceRelCuboid(
int a_MinX, int a_MaxX,
int a_MinY, int a_MaxY,
int a_MinZ, int a_MaxZ,
BLOCKTYPE a_SrcType, NIBBLETYPE a_SrcMeta,
BLOCKTYPE a_DstType, NIBBLETYPE a_DstMeta
);
/// Replaces the specified src blocks in the cuboid by the dst blocks; allows cuboid out of range of this chunk
void ReplaceRelCuboid(
const cCuboid & a_RelCuboid,
BLOCKTYPE a_SrcType, NIBBLETYPE a_SrcMeta,
BLOCKTYPE a_DstType, NIBBLETYPE a_DstMeta
)
{
ReplaceRelCuboid(
a_RelCuboid.p1.x, a_RelCuboid.p2.x,
a_RelCuboid.p1.y, a_RelCuboid.p2.y,
a_RelCuboid.p1.z, a_RelCuboid.p2.z,
a_SrcType, a_SrcMeta,
a_DstType, a_DstMeta
);
}
/// Replaces the blocks in the cuboid by the dst blocks if they are considered non-floor (air, water); allows cuboid out of range of this chunk
void FloorRelCuboid(
int a_MinX, int a_MaxX,
int a_MinY, int a_MaxY,
int a_MinZ, int a_MaxZ,
BLOCKTYPE a_DstType, NIBBLETYPE a_DstMeta
);
/// Replaces the blocks in the cuboid by the dst blocks if they are considered non-floor (air, water); allows cuboid out of range of this chunk
void FloorRelCuboid(
const cCuboid & a_RelCuboid,
BLOCKTYPE a_DstType, NIBBLETYPE a_DstMeta
)
{
FloorRelCuboid(
a_RelCuboid.p1.x, a_RelCuboid.p2.x,
a_RelCuboid.p1.y, a_RelCuboid.p2.y,
a_RelCuboid.p1.z, a_RelCuboid.p2.z,
a_DstType, a_DstMeta
);
}
/// Fills the relative cuboid with specified block with a random chance; allows cuboid out of range of this chunk
void RandomFillRelCuboid(
int a_MinX, int a_MaxX,
int a_MinY, int a_MaxY,
int a_MinZ, int a_MaxZ,
BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta,
int a_RandomSeed, int a_ChanceOutOf10k
);
/// Fills the relative cuboid with specified block with a random chance; allows cuboid out of range of this chunk
void RandomFillRelCuboid(
const cCuboid & a_RelCuboid, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta,
int a_RandomSeed, int a_ChanceOutOf10k
)
{
RandomFillRelCuboid(
a_RelCuboid.p1.x, a_RelCuboid.p2.x,
a_RelCuboid.p1.y, a_RelCuboid.p2.y,
a_RelCuboid.p1.z, a_RelCuboid.p2.z,
a_BlockType, a_BlockMeta,
a_RandomSeed, a_ChanceOutOf10k
);
}
/// Returns the block entity at the specified coords.
/// If there is no block entity at those coords, tries to create one, based on the block type
/// If the blocktype doesn't support a block entity, returns NULL.
cBlockEntity * GetBlockEntity(int a_RelX, int a_RelY, int a_RelZ);
// tolua_end
// Accessors used by cChunkGenerator::Generator descendants:
inline cChunkDef::BiomeMap & GetBiomeMap (void) { return m_BiomeMap; }
inline cChunkDef::BlockTypes & GetBlockTypes (void) { return *((cChunkDef::BlockTypes *)m_BlockArea.GetBlockTypes()); }
// CANNOT, different compression!
// inline cChunkDef::BlockNibbles & GetBlockMetas (void) { return *((cChunkDef::BlockNibbles *)m_BlockArea.GetBlockMetas()); }
inline BlockNibbleBytes & GetBlockMetasUncompressed(void) { return *((BlockNibbleBytes *)m_BlockArea.GetBlockMetas()); }
inline cChunkDef::HeightMap & GetHeightMap (void) { return m_HeightMap; }
inline cEntityList & GetEntities (void) { return m_Entities; }
inline cBlockEntityList & GetBlockEntities (void) { return m_BlockEntities; }
/// Compresses the metas from the BlockArea format (1 meta per byte) into regular format (2 metas per byte)
void CompressBlockMetas(cChunkDef::BlockNibbles & a_DestMetas);
#ifdef _DEBUG
/// Verifies that the heightmap corresponds to blocktype contents; if not, asserts on that column
void VerifyHeightmap(void);
#endif // _DEBUG
private:
int m_ChunkX;
int m_ChunkZ;
cChunkDef::BiomeMap m_BiomeMap;
cBlockArea m_BlockArea;
cChunkDef::HeightMap m_HeightMap;
cEntityList m_Entities; // Individual entities are NOT owned by this object!
cBlockEntityList m_BlockEntities; // Individual block entities are NOT owned by this object!
bool m_bUseDefaultBiomes;
bool m_bUseDefaultHeight;
bool m_bUseDefaultComposition;
bool m_bUseDefaultStructures;
bool m_bUseDefaultFinish;
} ; // tolua_export
+329
View File
@@ -0,0 +1,329 @@
#include "Globals.h"
#include "ChunkGenerator.h"
#include "../World.h"
#include "../../iniFile/iniFile.h"
#include "../Root.h"
#include "../PluginManager.h"
#include "ChunkDesc.h"
#include "ComposableGenerator.h"
#include "Noise3DGenerator.h"
/// If the generation queue size exceeds this number, a warning will be output
const int QUEUE_WARNING_LIMIT = 1000;
/// If the generation queue size exceeds this number, chunks with no clients will be skipped
const int QUEUE_SKIP_LIMIT = 500;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cChunkGenerator:
cChunkGenerator::cChunkGenerator(void) :
super("cChunkGenerator"),
m_World(NULL),
m_Generator(NULL)
{
}
cChunkGenerator::~cChunkGenerator()
{
Stop();
}
bool cChunkGenerator::Start(cWorld * a_World, cIniFile & a_IniFile)
{
MTRand rnd;
m_World = a_World;
m_Seed = a_IniFile.GetValueSetI("Seed", "Seed", rnd.randInt());
AString GeneratorName = a_IniFile.GetValueSet("Generator", "Generator", "Composable");
if (NoCaseCompare(GeneratorName, "Noise3D") == 0)
{
m_Generator = new cNoise3DGenerator(*this);
}
else
{
if (NoCaseCompare(GeneratorName, "composable") != 0)
{
LOGWARN("[Generator]::Generator value \"%s\" not recognized, using \"Composable\".", GeneratorName.c_str());
}
m_Generator = new cComposableGenerator(*this);
}
if (m_Generator == NULL)
{
LOGERROR("Generator could not start, aborting the server");
return false;
}
m_Generator->Initialize(a_World, a_IniFile);
return super::Start();
}
void cChunkGenerator::Stop(void)
{
m_ShouldTerminate = true;
m_Event.Set();
m_evtRemoved.Set(); // Wake up anybody waiting for empty queue
Wait();
delete m_Generator;
m_Generator = NULL;
}
void cChunkGenerator::QueueGenerateChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
{
{
cCSLock Lock(m_CS);
// Check if it is already in the queue:
for (cChunkCoordsList::iterator itr = m_Queue.begin(); itr != m_Queue.end(); ++itr)
{
if ((itr->m_ChunkX == a_ChunkX) && (itr->m_ChunkY == a_ChunkY) && (itr->m_ChunkZ == a_ChunkZ))
{
// Already in the queue, bail out
return;
}
} // for itr - m_Queue[]
// Add to queue, issue a warning if too many:
if (m_Queue.size() >= QUEUE_WARNING_LIMIT)
{
LOGWARN("WARNING: Adding chunk [%i, %i] to generation queue; Queue is too big! (%i)", a_ChunkX, a_ChunkZ, m_Queue.size());
}
m_Queue.push_back(cChunkCoords(a_ChunkX, a_ChunkY, a_ChunkZ));
}
m_Event.Set();
}
void cChunkGenerator::GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap)
{
if (m_Generator != NULL)
{
m_Generator->GenerateBiomes(a_ChunkX, a_ChunkZ, a_BiomeMap);
}
}
void cChunkGenerator::WaitForQueueEmpty(void)
{
cCSLock Lock(m_CS);
while (!m_ShouldTerminate && !m_Queue.empty())
{
cCSUnlock Unlock(Lock);
m_evtRemoved.Wait();
}
}
int cChunkGenerator::GetQueueLength(void)
{
cCSLock Lock(m_CS);
return (int)m_Queue.size();
}
EMCSBiome cChunkGenerator::GetBiomeAt(int a_BlockX, int a_BlockZ)
{
ASSERT(m_Generator != NULL);
return m_Generator->GetBiomeAt(a_BlockX, a_BlockZ);
}
BLOCKTYPE cChunkGenerator::GetIniBlock(cIniFile & a_IniFile, const AString & a_SectionName, const AString & a_ValueName, const AString & a_Default)
{
AString BlockType = a_IniFile.GetValueSet(a_SectionName, a_ValueName, a_Default);
BLOCKTYPE Block = BlockStringToType(BlockType);
if (Block < 0)
{
LOGWARN("[&s].%s Could not parse block value \"%s\". Using default: \"%s\".", a_SectionName.c_str(), a_ValueName.c_str(), BlockType.c_str(),a_Default.c_str());
return BlockStringToType(a_Default);
}
return Block;
}
void cChunkGenerator::Execute(void)
{
// To be able to display performance information, the generator counts the chunks generated.
// When the queue gets empty, the count is reset, so that waiting for the queue is not counted into the total time.
int NumChunksGenerated = 0; // Number of chunks generated since the queue was last empty
clock_t GenerationStart = clock(); // Clock tick when the queue started to fill
clock_t LastReportTick = clock(); // Clock tick of the last report made (so that performance isn't reported too often)
while (!m_ShouldTerminate)
{
cCSLock Lock(m_CS);
while (m_Queue.size() == 0)
{
if ((NumChunksGenerated > 16) && (clock() - LastReportTick > CLOCKS_PER_SEC))
{
LOG("Chunk generator performance: %.2f ch/s (%d ch total)",
(double)NumChunksGenerated * CLOCKS_PER_SEC/ (clock() - GenerationStart),
NumChunksGenerated
);
}
cCSUnlock Unlock(Lock);
m_Event.Wait();
if (m_ShouldTerminate)
{
return;
}
NumChunksGenerated = 0;
GenerationStart = clock();
LastReportTick = clock();
}
cChunkCoords coords = m_Queue.front(); // Get next coord from queue
m_Queue.erase( m_Queue.begin() ); // Remove coordinate from queue
bool SkipEnabled = (m_Queue.size() > QUEUE_SKIP_LIMIT);
Lock.Unlock(); // Unlock ASAP
m_evtRemoved.Set();
// Display perf info once in a while:
if ((NumChunksGenerated > 16) && (clock() - LastReportTick > 2 * CLOCKS_PER_SEC))
{
LOG("Chunk generator performance: %.2f ch/s (%d ch total)",
(double)NumChunksGenerated * CLOCKS_PER_SEC / (clock() - GenerationStart),
NumChunksGenerated
);
LastReportTick = clock();
}
// Hack for regenerating chunks: if Y != 0, the chunk is considered invalid, even if it has its data set
if ((coords.m_ChunkY == 0) && m_World->IsChunkValid(coords.m_ChunkX, coords.m_ChunkZ))
{
LOGD("Chunk [%d, %d] already generated, skipping generation", coords.m_ChunkX, coords.m_ChunkZ);
// Already generated, ignore request
continue;
}
if (SkipEnabled && !m_World->HasChunkAnyClients(coords.m_ChunkX, coords.m_ChunkZ))
{
LOGWARNING("Chunk generator overloaded, skipping chunk [%d, %d]", coords.m_ChunkX, coords.m_ChunkZ);
continue;
}
LOGD("Generating chunk [%d, %d, %d]", coords.m_ChunkX, coords.m_ChunkY, coords.m_ChunkZ);
DoGenerate(coords.m_ChunkX, coords.m_ChunkY, coords.m_ChunkZ);
// Save the chunk right after generating, so that we don't have to generate it again on next run
m_World->GetStorage().QueueSaveChunk(coords.m_ChunkX, coords.m_ChunkY, coords.m_ChunkZ);
NumChunksGenerated++;
} // while (!bStop)
}
void cChunkGenerator::DoGenerate(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
{
cChunkDesc ChunkDesc(a_ChunkX, a_ChunkZ);
cRoot::Get()->GetPluginManager()->CallHookChunkGenerating(m_World, a_ChunkX, a_ChunkZ, &ChunkDesc);
m_Generator->DoGenerate(a_ChunkX, a_ChunkZ, ChunkDesc);
cRoot::Get()->GetPluginManager()->CallHookChunkGenerated(m_World, a_ChunkX, a_ChunkZ, &ChunkDesc);
#ifdef _DEBUG
// Verify that the generator has produced valid data:
ChunkDesc.VerifyHeightmap();
#endif
cChunkDef::BlockNibbles BlockMetas;
ChunkDesc.CompressBlockMetas(BlockMetas);
m_World->SetChunkData(
a_ChunkX, a_ChunkZ,
ChunkDesc.GetBlockTypes(), BlockMetas,
NULL, NULL, // We don't have lighting, chunk will be lighted when needed
&ChunkDesc.GetHeightMap(), &ChunkDesc.GetBiomeMap(),
ChunkDesc.GetEntities(), ChunkDesc.GetBlockEntities(),
true
);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cChunkGenerator::cGenerator:
cChunkGenerator::cGenerator::cGenerator(cChunkGenerator & a_ChunkGenerator) :
m_ChunkGenerator(a_ChunkGenerator)
{
}
void cChunkGenerator::cGenerator::Initialize(cWorld * a_World, cIniFile & a_IniFile)
{
m_World = a_World;
UNUSED(a_IniFile);
}
EMCSBiome cChunkGenerator::cGenerator::GetBiomeAt(int a_BlockX, int a_BlockZ)
{
cChunkDef::BiomeMap Biomes;
int Y = 0;
int ChunkX, ChunkZ;
cWorld::AbsoluteToRelative(a_BlockX, Y, a_BlockZ, ChunkX, Y, ChunkZ);
GenerateBiomes(ChunkX, ChunkZ, Biomes);
return cChunkDef::GetBiome(Biomes, a_BlockX, a_BlockZ);
}
+113
View File
@@ -0,0 +1,113 @@
// ChunkGenerator.h
// Interfaces to the cChunkGenerator class representing the thread that generates chunks
/*
The object takes requests for generating chunks and processes them in a separate thread one by one.
The requests are not added to the queue if there is already a request with the same coords
Before generating, the thread checks if the chunk hasn't been already generated.
It is theoretically possible to have multiple generator threads by having multiple instances of this object,
but then it MAY happen that the chunk is generated twice.
If the generator queue is overloaded, the generator skips chunks with no clients in them
*/
#pragma once
#include "../OSSupport/IsThread.h"
#include "../ChunkDef.h"
// fwd:
class cWorld;
class cIniFile;
class cChunkDesc;
class cChunkGenerator :
cIsThread
{
typedef cIsThread super;
public:
/// The interface that a class has to implement to become a generator
class cGenerator
{
public:
cGenerator(cChunkGenerator & a_ChunkGenerator);
virtual ~cGenerator() {} ; // Force a virtual destructor
/// Called to initialize the generator on server startup.
virtual void Initialize(cWorld * a_World, cIniFile & a_IniFile);
/// Generates the biomes for the specified chunk (directly, not in a separate thread). Used by the world loader if biomes failed loading.
virtual void GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) = 0;
/// Returns the biome at the specified coords. Used by ChunkMap if an invalid chunk is queried for biome. Default implementation uses GenerateBiomes().
virtual EMCSBiome GetBiomeAt(int a_BlockX, int a_BlockZ);
/// Called in a separate thread to do the actual chunk generation. Generator should generate into a_ChunkDesc.
virtual void DoGenerate(int a_ChunkX, int a_ChunkZ, cChunkDesc & a_ChunkDesc) = 0;
protected:
cChunkGenerator & m_ChunkGenerator;
cWorld * m_World;
} ;
cChunkGenerator (void);
~cChunkGenerator();
bool Start(cWorld * a_World, cIniFile & a_IniFile);
void Stop(void);
/// Queues the chunk for generation; removes duplicate requests
void QueueGenerateChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ);
/// Generates the biomes for the specified chunk (directly, not in a separate thread). Used by the world loader if biomes failed loading.
void GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap);
void WaitForQueueEmpty(void);
int GetQueueLength(void);
int GetSeed(void) const { return m_Seed; }
/// Returns the biome at the specified coords. Used by ChunkMap if an invalid chunk is queried for biome
EMCSBiome GetBiomeAt(int a_BlockX, int a_BlockZ);
/// Reads a block type from the ini file; returns the blocktype on success, emits a warning and returns a_Default's representation on failure.
static BLOCKTYPE GetIniBlock(cIniFile & a_IniFile, const AString & a_SectionName, const AString & a_ValueName, const AString & a_Default);
private:
cWorld * m_World;
int m_Seed;
cCriticalSection m_CS;
cChunkCoordsList m_Queue;
cEvent m_Event; ///< Set when an item is added to the queue or the thread should terminate
cEvent m_evtRemoved; ///< Set when an item is removed from the queue
cGenerator * m_Generator; ///< The actual generator engine used to generate chunks
// cIsThread override:
virtual void Execute(void) override;
void DoGenerate(int a_ChunkX, int a_ChunkY, int a_ChunkZ);
};
+634
View File
@@ -0,0 +1,634 @@
// CompoGen.cpp
/* Implements the various terrain composition generators:
- cCompoGenSameBlock
- cCompoGenDebugBiomes
- cCompoGenClassic
*/
#include "Globals.h"
#include "CompoGen.h"
#include "../BlockID.h"
#include "../Item.h"
#include "../LinearUpscale.h"
#include "../../iniFile/iniFile.h"
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cCompoGenSameBlock:
void cCompoGenSameBlock::ComposeTerrain(cChunkDesc & a_ChunkDesc)
{
a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0);
for (int z = 0; z < cChunkDef::Width; z++)
{
for (int x = 0; x < cChunkDef::Width; x++)
{
int Start;
if (m_IsBedrocked)
{
a_ChunkDesc.SetBlockType(x, 0, z, E_BLOCK_BEDROCK);
Start = 1;
}
else
{
Start = 0;
}
for (int y = a_ChunkDesc.GetHeight(x, z); y >= Start; y--)
{
a_ChunkDesc.SetBlockType(x, y, z, m_BlockType);
} // for y
} // for z
} // for x
}
void cCompoGenSameBlock::InitializeCompoGen(cIniFile & a_IniFile)
{
m_BlockType = (BLOCKTYPE)(GetIniItemSet(a_IniFile, "Generator", "SameBlockType", "stone").m_ItemType);
m_IsBedrocked = (a_IniFile.GetValueSetI("Generator", "SameBlockBedrocked", 1) != 0);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cCompoGenDebugBiomes:
void cCompoGenDebugBiomes::ComposeTerrain(cChunkDesc & a_ChunkDesc)
{
static BLOCKTYPE Blocks[] =
{
E_BLOCK_STONE,
E_BLOCK_COBBLESTONE,
E_BLOCK_LOG,
E_BLOCK_PLANKS,
E_BLOCK_SANDSTONE,
E_BLOCK_WOOL,
E_BLOCK_COAL_ORE,
E_BLOCK_IRON_ORE,
E_BLOCK_GOLD_ORE,
E_BLOCK_DIAMOND_ORE,
E_BLOCK_LAPIS_ORE,
E_BLOCK_REDSTONE_ORE,
E_BLOCK_IRON_BLOCK,
E_BLOCK_GOLD_BLOCK,
E_BLOCK_DIAMOND_BLOCK,
E_BLOCK_LAPIS_BLOCK,
E_BLOCK_BRICK,
E_BLOCK_MOSSY_COBBLESTONE,
E_BLOCK_OBSIDIAN,
E_BLOCK_NETHERRACK,
E_BLOCK_SOULSAND,
E_BLOCK_NETHER_BRICK,
E_BLOCK_BEDROCK,
} ;
a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0);
for (int z = 0; z < cChunkDef::Width; z++)
{
for (int x = 0; x < cChunkDef::Width; x++)
{
BLOCKTYPE BlockType = Blocks[a_ChunkDesc.GetBiome(x, z)];
for (int y = a_ChunkDesc.GetHeight(x, z); y >= 0; y--)
{
a_ChunkDesc.SetBlockType(x, y, z, BlockType);
} // for y
} // for z
} // for x
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cCompoGenClassic:
cCompoGenClassic::cCompoGenClassic(void) :
m_SeaLevel(60),
m_BeachHeight(2),
m_BeachDepth(4),
m_BlockTop(E_BLOCK_GRASS),
m_BlockMiddle(E_BLOCK_DIRT),
m_BlockBottom(E_BLOCK_STONE),
m_BlockBeach(E_BLOCK_SAND),
m_BlockBeachBottom(E_BLOCK_SANDSTONE),
m_BlockSea(E_BLOCK_STATIONARY_WATER)
{
}
void cCompoGenClassic::ComposeTerrain(cChunkDesc & a_ChunkDesc)
{
/* The classic composition means:
- 1 layer of grass, 3 of dirt and the rest stone, if the height > sealevel + beachheight
- 3 sand and a 1 sandstone, rest stone if between sealevel and sealevel + beachheight
- water from waterlevel to height, then 3 sand, 1 sandstone, the rest stone, if water depth < beachdepth
- water from waterlevel, then 3 dirt, the rest stone otherwise
- bedrock at the bottom
*/
a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0);
// The patterns to use for different situations, must be same length!
const BLOCKTYPE PatternGround[] = {m_BlockTop, m_BlockMiddle, m_BlockMiddle, m_BlockMiddle} ;
const BLOCKTYPE PatternBeach[] = {m_BlockBeach, m_BlockBeach, m_BlockBeach, m_BlockBeachBottom} ;
const BLOCKTYPE PatternOcean[] = {m_BlockMiddle, m_BlockMiddle, m_BlockMiddle, m_BlockBottom} ;
static int PatternLength = ARRAYCOUNT(PatternGround);
ASSERT(ARRAYCOUNT(PatternGround) == ARRAYCOUNT(PatternBeach));
ASSERT(ARRAYCOUNT(PatternGround) == ARRAYCOUNT(PatternOcean));
for (int z = 0; z < cChunkDef::Width; z++)
{
for (int x = 0; x < cChunkDef::Width; x++)
{
int Height = a_ChunkDesc.GetHeight(x, z);
const BLOCKTYPE * Pattern;
if (Height > m_SeaLevel + m_BeachHeight)
{
Pattern = PatternGround;
}
else if (Height > m_SeaLevel - m_BeachDepth)
{
Pattern = PatternBeach;
}
else
{
Pattern = PatternOcean;
}
// Fill water from sealevel down to height (if any):
for (int y = m_SeaLevel; y >= Height; --y)
{
a_ChunkDesc.SetBlockType(x, y, z, m_BlockSea);
}
// Fill from height till the bottom:
for (int y = Height; y >= 1; y--)
{
a_ChunkDesc.SetBlockType(x, y, z, (Height - y < PatternLength) ? Pattern[Height - y] : m_BlockBottom);
}
// The last layer is always bedrock:
a_ChunkDesc.SetBlockType(x, 0, z, E_BLOCK_BEDROCK);
} // for x
} // for z
}
void cCompoGenClassic::InitializeCompoGen(cIniFile & a_IniFile)
{
m_SeaLevel = a_IniFile.GetValueSetI("Generator", "ClassicSeaLevel", m_SeaLevel);
m_BeachHeight = a_IniFile.GetValueSetI("Generator", "ClassicBeachHeight", m_BeachHeight);
m_BeachDepth = a_IniFile.GetValueSetI("Generator", "ClassicBeachDepth", m_BeachDepth);
m_BlockTop = (BLOCKTYPE)(GetIniItemSet(a_IniFile, "Generator", "ClassicBlockTop", "grass").m_ItemType);
m_BlockMiddle = (BLOCKTYPE)(GetIniItemSet(a_IniFile, "Generator", "ClassicBlockMiddle", "dirt").m_ItemType);
m_BlockBottom = (BLOCKTYPE)(GetIniItemSet(a_IniFile, "Generator", "ClassicBlockBottom", "stone").m_ItemType);
m_BlockBeach = (BLOCKTYPE)(GetIniItemSet(a_IniFile, "Generator", "ClassicBlockBeach", "sand").m_ItemType);
m_BlockBeachBottom = (BLOCKTYPE)(GetIniItemSet(a_IniFile, "Generator", "ClassicBlockBeachBottom", "sandstone").m_ItemType);
m_BlockSea = (BLOCKTYPE)(GetIniItemSet(a_IniFile, "Generator", "ClassicBlockSea", "stationarywater").m_ItemType);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cCompoGenBiomal:
void cCompoGenBiomal::ComposeTerrain(cChunkDesc & a_ChunkDesc)
{
a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0);
int ChunkX = a_ChunkDesc.GetChunkX();
int ChunkZ = a_ChunkDesc.GetChunkZ();
/*
_X 2013_04_22:
There's no point in generating the whole cubic noise at once, because the noise values are used in
only about 20 % of the cases, so the speed gained by precalculating is lost by precalculating too much data
*/
for (int z = 0; z < cChunkDef::Width; z++)
{
for (int x = 0; x < cChunkDef::Width; x++)
{
int Height = a_ChunkDesc.GetHeight(x, z);
if (Height > m_SeaLevel)
{
switch (a_ChunkDesc.GetBiome(x, z))
{
case biOcean:
case biPlains:
case biExtremeHills:
case biForest:
case biTaiga:
case biSwampland:
case biRiver:
case biFrozenOcean:
case biFrozenRiver:
case biIcePlains:
case biIceMountains:
case biForestHills:
case biTaigaHills:
case biExtremeHillsEdge:
case biJungle:
case biJungleHills:
{
FillColumnGrass(x, z, Height, a_ChunkDesc.GetBlockTypes());
break;
}
case biDesertHills:
case biDesert:
case biBeach:
{
FillColumnSand(x, z, Height, a_ChunkDesc.GetBlockTypes());
break;
}
case biMushroomIsland:
case biMushroomShore:
{
FillColumnMycelium(x, z, Height, a_ChunkDesc.GetBlockTypes());
break;
}
default:
{
// TODO
ASSERT(!"CompoGenBiomal: Biome not implemented yet!");
break;
}
}
}
else
{
switch (a_ChunkDesc.GetBiome(x, z))
{
case biDesert:
case biBeach:
{
// Fill with water, sand, sandstone and stone
FillColumnWaterSand(x, z, Height, a_ChunkDesc.GetBlockTypes());
break;
}
default:
{
// Fill with water, sand/dirt/clay mix and stone
if (m_Noise.CubicNoise2D(0.3f * (cChunkDef::Width * ChunkX + x), 0.3f * (cChunkDef::Width * ChunkZ + z)) < 0)
{
FillColumnWaterSand(x, z, Height, a_ChunkDesc.GetBlockTypes());
}
else
{
FillColumnWaterDirt(x, z, Height, a_ChunkDesc.GetBlockTypes());
}
break;
}
} // switch (biome)
a_ChunkDesc.SetHeight(x, z, m_SeaLevel + 1);
} // else (under water)
a_ChunkDesc.SetBlockType(x, 0, z, E_BLOCK_BEDROCK);
} // for x
} // for z
}
void cCompoGenBiomal::InitializeCompoGen(cIniFile & a_IniFile)
{
m_SeaLevel = a_IniFile.GetValueSetI("Generator", "BiomalSeaLevel", m_SeaLevel) - 1;
}
void cCompoGenBiomal::FillColumnGrass(int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes)
{
BLOCKTYPE Pattern[] =
{
E_BLOCK_GRASS,
E_BLOCK_DIRT,
E_BLOCK_DIRT,
E_BLOCK_DIRT,
} ;
FillColumnPattern(a_RelX, a_RelZ, a_Height, a_BlockTypes, Pattern, ARRAYCOUNT(Pattern));
for (int y = a_Height - ARRAYCOUNT(Pattern); y > 0; y--)
{
cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, E_BLOCK_STONE);
}
}
void cCompoGenBiomal::FillColumnSand(int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes)
{
BLOCKTYPE Pattern[] =
{
E_BLOCK_SAND,
E_BLOCK_SAND,
E_BLOCK_SAND,
E_BLOCK_SANDSTONE,
} ;
FillColumnPattern(a_RelX, a_RelZ, a_Height, a_BlockTypes, Pattern, ARRAYCOUNT(Pattern));
for (int y = a_Height - ARRAYCOUNT(Pattern); y > 0; y--)
{
cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, E_BLOCK_STONE);
}
}
void cCompoGenBiomal::FillColumnMycelium (int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes)
{
BLOCKTYPE Pattern[] =
{
E_BLOCK_MYCELIUM,
E_BLOCK_DIRT,
E_BLOCK_DIRT,
E_BLOCK_DIRT,
} ;
FillColumnPattern(a_RelX, a_RelZ, a_Height, a_BlockTypes, Pattern, ARRAYCOUNT(Pattern));
for (int y = a_Height - ARRAYCOUNT(Pattern); y > 0; y--)
{
cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, E_BLOCK_STONE);
}
}
void cCompoGenBiomal::FillColumnWaterSand(int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes)
{
FillColumnSand(a_RelX, a_RelZ, a_Height, a_BlockTypes);
for (int y = a_Height + 1; y <= m_SeaLevel + 1; y++)
{
cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, E_BLOCK_STATIONARY_WATER);
}
}
void cCompoGenBiomal::FillColumnWaterDirt(int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes)
{
// Dirt
BLOCKTYPE Pattern[] =
{
E_BLOCK_DIRT,
E_BLOCK_DIRT,
E_BLOCK_DIRT,
E_BLOCK_DIRT,
} ;
FillColumnPattern(a_RelX, a_RelZ, a_Height, a_BlockTypes, Pattern, ARRAYCOUNT(Pattern));
for (int y = a_Height - ARRAYCOUNT(Pattern); y > 0; y--)
{
cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, E_BLOCK_STONE);
}
for (int y = a_Height + 1; y <= m_SeaLevel + 1; y++)
{
cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, E_BLOCK_STATIONARY_WATER);
}
}
void cCompoGenBiomal::FillColumnPattern(int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes, const BLOCKTYPE * a_Pattern, int a_PatternSize)
{
for (int y = a_Height, idx = 0; (y >= 0) && (idx < a_PatternSize); y--, idx++)
{
cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, a_Pattern[idx]);
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cCompoGenNether:
cCompoGenNether::cCompoGenNether(int a_Seed) :
m_Noise1(a_Seed + 10),
m_Noise2(a_Seed * a_Seed * 10 + a_Seed * 1000 + 6000),
m_Threshold(0)
{
}
void cCompoGenNether::ComposeTerrain(cChunkDesc & a_ChunkDesc)
{
HEIGHTTYPE MaxHeight = a_ChunkDesc.GetMaxHeight();
const int SEGMENT_HEIGHT = 8;
const int INTERPOL_X = 16; // Must be a divisor of 16
const int INTERPOL_Z = 16; // Must be a divisor of 16
// Interpolate the chunk in 16 * SEGMENT_HEIGHT * 16 "segments", each SEGMENT_HEIGHT blocks high and each linearly interpolated separately.
// Have two buffers, one for the lowest floor and one for the highest floor, so that Y-interpolation can be done between them
// Then swap the buffers and use the previously-top one as the current-bottom, without recalculating it.
int FloorBuf1[17 * 17];
int FloorBuf2[17 * 17];
int * FloorHi = FloorBuf1;
int * FloorLo = FloorBuf2;
int BaseX = a_ChunkDesc.GetChunkX() * cChunkDef::Width;
int BaseZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width;
// Interpolate the lowest floor:
for (int z = 0; z <= 16 / INTERPOL_Z; z++) for (int x = 0; x <= 16 / INTERPOL_X; x++)
{
FloorLo[INTERPOL_X * x + 17 * INTERPOL_Z * z] =
m_Noise1.IntNoise3DInt(BaseX + INTERPOL_X * x, 0, BaseZ + INTERPOL_Z * z) *
m_Noise2.IntNoise3DInt(BaseX + INTERPOL_X * x, 0, BaseZ + INTERPOL_Z * z) /
256;
} // for x, z - FloorLo[]
LinearUpscale2DArrayInPlace(FloorLo, 17, 17, INTERPOL_X, INTERPOL_Z);
// Interpolate segments:
for (int Segment = 0; Segment < MaxHeight; Segment += SEGMENT_HEIGHT)
{
// First update the high floor:
for (int z = 0; z <= 16 / INTERPOL_Z; z++) for (int x = 0; x <= 16 / INTERPOL_X; x++)
{
FloorHi[INTERPOL_X * x + 17 * INTERPOL_Z * z] =
m_Noise1.IntNoise3DInt(BaseX + INTERPOL_X * x, Segment + SEGMENT_HEIGHT, BaseZ + INTERPOL_Z * z) *
m_Noise2.IntNoise3DInt(BaseX + INTERPOL_Z * x, Segment + SEGMENT_HEIGHT, BaseZ + INTERPOL_Z * z) /
256;
} // for x, z - FloorLo[]
LinearUpscale2DArrayInPlace(FloorHi, 17, 17, INTERPOL_X, INTERPOL_Z);
// Interpolate between FloorLo and FloorHi:
for (int z = 0; z < 16; z++) for (int x = 0; x < 16; x++)
{
int Lo = FloorLo[x + 17 * z] / 256;
int Hi = FloorHi[x + 17 * z] / 256;
for (int y = 0; y < SEGMENT_HEIGHT; y++)
{
int Val = Lo + (Hi - Lo) * y / SEGMENT_HEIGHT;
a_ChunkDesc.SetBlockType(x, y + Segment, z, (Val < m_Threshold) ? E_BLOCK_NETHERRACK : E_BLOCK_AIR);
}
}
// Swap the floors:
std::swap(FloorLo, FloorHi);
}
// Bedrock at the bottom and at the top:
for (int z = 0; z < 16; z++) for (int x = 0; x < 16; x++)
{
a_ChunkDesc.SetBlockType(x, 0, z, E_BLOCK_BEDROCK);
a_ChunkDesc.SetBlockType(x, a_ChunkDesc.GetHeight(x, z), z, E_BLOCK_BEDROCK);
}
}
void cCompoGenNether::InitializeCompoGen(cIniFile & a_IniFile)
{
m_Threshold = a_IniFile.GetValueSetI("Generator", "NetherThreshold", m_Threshold);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cCompoGenCache:
cCompoGenCache::cCompoGenCache(cTerrainCompositionGen & a_Underlying, int a_CacheSize) :
m_Underlying(a_Underlying),
m_CacheSize(a_CacheSize),
m_CacheOrder(new int[a_CacheSize]),
m_CacheData(new sCacheData[a_CacheSize]),
m_NumHits(0),
m_NumMisses(0),
m_TotalChain(0)
{
for (int i = 0; i < m_CacheSize; i++)
{
m_CacheOrder[i] = i;
m_CacheData[i].m_ChunkX = 0x7fffffff;
m_CacheData[i].m_ChunkZ = 0x7fffffff;
}
}
cCompoGenCache::~cCompoGenCache()
{
delete[] m_CacheData;
delete[] m_CacheOrder;
}
void cCompoGenCache::ComposeTerrain(cChunkDesc & a_ChunkDesc)
{
#ifdef _DEBUG
if (((m_NumHits + m_NumMisses) % 1024) == 10)
{
LOGD("CompoGenCache: %d hits, %d misses, saved %.2f %%", m_NumHits, m_NumMisses, 100.0 * m_NumHits / (m_NumHits + m_NumMisses));
LOGD("CompoGenCache: Avg cache chain length: %.2f", (float)m_TotalChain / m_NumHits);
}
#endif // _DEBUG
int ChunkX = a_ChunkDesc.GetChunkX();
int ChunkZ = a_ChunkDesc.GetChunkZ();
for (int i = 0; i < m_CacheSize; i++)
{
if (
(m_CacheData[m_CacheOrder[i]].m_ChunkX != ChunkX) ||
(m_CacheData[m_CacheOrder[i]].m_ChunkZ != ChunkZ)
)
{
continue;
}
// Found it in the cache
int Idx = m_CacheOrder[i];
// Move to front:
for (int j = i; j > 0; j--)
{
m_CacheOrder[j] = m_CacheOrder[j - 1];
}
m_CacheOrder[0] = Idx;
// Use the cached data:
memcpy(a_ChunkDesc.GetBlockTypes(), m_CacheData[Idx].m_BlockTypes, sizeof(a_ChunkDesc.GetBlockTypes()));
memcpy(a_ChunkDesc.GetBlockMetasUncompressed(), m_CacheData[Idx].m_BlockMetas, sizeof(a_ChunkDesc.GetBlockMetasUncompressed()));
m_NumHits++;
m_TotalChain += i;
return;
} // for i - cache
// Not in the cache:
m_NumMisses++;
m_Underlying.ComposeTerrain(a_ChunkDesc);
// Insert it as the first item in the MRU order:
int Idx = m_CacheOrder[m_CacheSize - 1];
for (int i = m_CacheSize - 1; i > 0; i--)
{
m_CacheOrder[i] = m_CacheOrder[i - 1];
} // for i - m_CacheOrder[]
m_CacheOrder[0] = Idx;
memcpy(m_CacheData[Idx].m_BlockTypes, a_ChunkDesc.GetBlockTypes(), sizeof(a_ChunkDesc.GetBlockTypes()));
memcpy(m_CacheData[Idx].m_BlockMetas, a_ChunkDesc.GetBlockMetasUncompressed(), sizeof(a_ChunkDesc.GetBlockMetasUncompressed()));
m_CacheData[Idx].m_ChunkX = ChunkX;
m_CacheData[Idx].m_ChunkZ = ChunkZ;
}
void cCompoGenCache::InitializeCompoGen(cIniFile & a_IniFile)
{
m_Underlying.InitializeCompoGen(a_IniFile);
}
+182
View File
@@ -0,0 +1,182 @@
// CompoGen.h
/* Interfaces to the various terrain composition generators:
- cCompoGenSameBlock
- cCompoGenDebugBiomes
- cCompoGenClassic
- cCompoGenBiomal
- cCompoGenNether
- cCompoGenCache
*/
#pragma once
#include "ComposableGenerator.h"
#include "../Noise.h"
class cCompoGenSameBlock :
public cTerrainCompositionGen
{
public:
cCompoGenSameBlock(void) :
m_BlockType(E_BLOCK_STONE),
m_IsBedrocked(true)
{}
protected:
BLOCKTYPE m_BlockType;
bool m_IsBedrocked;
// cTerrainCompositionGen overrides:
virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override;
virtual void InitializeCompoGen(cIniFile & a_IniFile) override;
} ;
class cCompoGenDebugBiomes :
public cTerrainCompositionGen
{
public:
cCompoGenDebugBiomes(void) {}
protected:
// cTerrainCompositionGen overrides:
virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override;
} ;
class cCompoGenClassic :
public cTerrainCompositionGen
{
public:
cCompoGenClassic(void);
protected:
int m_SeaLevel;
int m_BeachHeight;
int m_BeachDepth;
BLOCKTYPE m_BlockTop;
BLOCKTYPE m_BlockMiddle;
BLOCKTYPE m_BlockBottom;
BLOCKTYPE m_BlockBeach;
BLOCKTYPE m_BlockBeachBottom;
BLOCKTYPE m_BlockSea;
// cTerrainCompositionGen overrides:
virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override;
virtual void InitializeCompoGen(cIniFile & a_IniFile) override;
} ;
class cCompoGenBiomal :
public cTerrainCompositionGen
{
public:
cCompoGenBiomal(int a_Seed) :
m_Noise(a_Seed + 1000),
m_SeaLevel(62)
{
}
protected:
cNoise m_Noise;
int m_SeaLevel;
// cTerrainCompositionGen overrides:
virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override;
virtual void InitializeCompoGen(cIniFile & a_IniFile) override;
void FillColumnGrass (int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes);
void FillColumnSand (int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes);
void FillColumnMycelium (int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes);
void FillColumnWaterSand(int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes);
void FillColumnWaterDirt(int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes);
void FillColumnPattern (int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes, const BLOCKTYPE * a_Pattern, int a_PatternSize);
} ;
class cCompoGenNether :
public cTerrainCompositionGen
{
public:
cCompoGenNether(int a_Seed);
protected:
cNoise m_Noise1;
cNoise m_Noise2;
int m_Threshold;
// cTerrainCompositionGen overrides:
virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override;
virtual void InitializeCompoGen(cIniFile & a_IniFile) override;
} ;
/// Caches most-recently-used chunk composition of another composition generator. Caches only the types and metas
class cCompoGenCache :
public cTerrainCompositionGen
{
public:
cCompoGenCache(cTerrainCompositionGen & a_Underlying, int a_CacheSize); // Doesn't take ownership of a_Underlying
~cCompoGenCache();
// cTerrainCompositionGen override:
virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override;
virtual void InitializeCompoGen(cIniFile & a_IniFile) override;
protected:
cTerrainCompositionGen & m_Underlying;
struct sCacheData
{
int m_ChunkX;
int m_ChunkZ;
cChunkDef::BlockTypes m_BlockTypes;
cChunkDesc::BlockNibbleBytes m_BlockMetas; // The metas are uncompressed, 1 meta per byte
} ;
// To avoid moving large amounts of data for the MRU behavior, we MRU-ize indices to an array of the actual data
int m_CacheSize;
int * m_CacheOrder; // MRU-ized order, indices into m_CacheData array
sCacheData * m_CacheData; // m_CacheData[m_CacheOrder[0]] is the most recently used
// Cache statistics
int m_NumHits;
int m_NumMisses;
int m_TotalChain; // Number of cache items walked to get to a hit (only added for hits)
} ;
+501
View File
@@ -0,0 +1,501 @@
// ComposableGenerator.cpp
// Implements the cComposableGenerator class representing the chunk generator that takes the composition approach to generating chunks
#include "Globals.h"
#include "ComposableGenerator.h"
#include "../World.h"
#include "../../iniFile/iniFile.h"
#include "../Root.h"
// Individual composed algorithms:
#include "BioGen.h"
#include "HeiGen.h"
#include "CompoGen.h"
#include "StructGen.h"
#include "FinishGen.h"
#include "Caves.h"
#include "DistortedHeightmap.h"
#include "EndGen.h"
#include "MineShafts.h"
#include "Noise3DGenerator.h"
#include "Ravines.h"
cComposableGenerator::cComposableGenerator(cChunkGenerator & a_ChunkGenerator) :
super(a_ChunkGenerator),
m_BiomeGen(NULL),
m_HeightGen(NULL),
m_CompositionGen(NULL),
m_UnderlyingBiomeGen(NULL),
m_UnderlyingHeightGen(NULL),
m_UnderlyingCompositionGen(NULL)
{
}
cComposableGenerator::~cComposableGenerator()
{
// Delete the generating composition:
for (cFinishGenList::const_iterator itr = m_FinishGens.begin(); itr != m_FinishGens.end(); ++itr)
{
delete *itr;
}
m_FinishGens.clear();
for (cStructureGenList::const_iterator itr = m_StructureGens.begin(); itr != m_StructureGens.end(); ++itr)
{
delete *itr;
}
m_StructureGens.clear();
delete m_CompositionGen;
m_CompositionGen = NULL;
delete m_HeightGen;
m_HeightGen = NULL;
delete m_BiomeGen;
m_BiomeGen = NULL;
delete m_UnderlyingCompositionGen;
m_UnderlyingCompositionGen = NULL;
delete m_UnderlyingHeightGen;
m_UnderlyingHeightGen = NULL;
delete m_UnderlyingBiomeGen;
m_UnderlyingBiomeGen = NULL;
}
void cComposableGenerator::Initialize(cWorld * a_World, cIniFile & a_IniFile)
{
super::Initialize(a_World, a_IniFile);
InitBiomeGen(a_IniFile);
InitHeightGen(a_IniFile);
InitCompositionGen(a_IniFile);
InitStructureGens(a_IniFile);
InitFinishGens(a_IniFile);
}
void cComposableGenerator::GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap)
{
if (m_BiomeGen != NULL) // Quick fix for generator deinitializing before the world storage finishes loading
{
m_BiomeGen->GenBiomes(a_ChunkX, a_ChunkZ, a_BiomeMap);
}
}
void cComposableGenerator::DoGenerate(int a_ChunkX, int a_ChunkZ, cChunkDesc & a_ChunkDesc)
{
if (a_ChunkDesc.IsUsingDefaultBiomes())
{
m_BiomeGen->GenBiomes(a_ChunkX, a_ChunkZ, a_ChunkDesc.GetBiomeMap());
}
if (a_ChunkDesc.IsUsingDefaultHeight())
{
m_HeightGen->GenHeightMap(a_ChunkX, a_ChunkZ, a_ChunkDesc.GetHeightMap());
}
if (a_ChunkDesc.IsUsingDefaultComposition())
{
m_CompositionGen->ComposeTerrain(a_ChunkDesc);
}
if (a_ChunkDesc.IsUsingDefaultStructures())
{
for (cStructureGenList::iterator itr = m_StructureGens.begin(); itr != m_StructureGens.end(); ++itr)
{
(*itr)->GenStructures(a_ChunkDesc);
} // for itr - m_StructureGens[]
}
if (a_ChunkDesc.IsUsingDefaultFinish())
{
for (cFinishGenList::iterator itr = m_FinishGens.begin(); itr != m_FinishGens.end(); ++itr)
{
(*itr)->GenFinish(a_ChunkDesc);
} // for itr - m_FinishGens[]
}
}
void cComposableGenerator::InitBiomeGen(cIniFile & a_IniFile)
{
AString BiomeGenName = a_IniFile.GetValueSet("Generator", "BiomeGen", "");
if (BiomeGenName.empty())
{
LOGWARN("[Generator] BiomeGen value not set in world.ini, using \"MultiStepMap\".");
BiomeGenName = "MultiStepMap";
}
int Seed = m_ChunkGenerator.GetSeed();
bool CacheOffByDefault = false;
if (NoCaseCompare(BiomeGenName, "constant") == 0)
{
m_BiomeGen = new cBioGenConstant;
CacheOffByDefault = true; // we're generating faster than a cache would retrieve data :)
}
else if (NoCaseCompare(BiomeGenName, "checkerboard") == 0)
{
m_BiomeGen = new cBioGenCheckerboard;
CacheOffByDefault = true; // we're (probably) generating faster than a cache would retrieve data
}
else if (NoCaseCompare(BiomeGenName, "voronoi") == 0)
{
m_BiomeGen = new cBioGenVoronoi(Seed);
}
else if (NoCaseCompare(BiomeGenName, "distortedvoronoi") == 0)
{
m_BiomeGen = new cBioGenDistortedVoronoi(Seed);
}
else
{
if (NoCaseCompare(BiomeGenName, "multistepmap") != 0)
{
LOGWARNING("Unknown BiomeGen \"%s\", using \"MultiStepMap\" instead.", BiomeGenName.c_str());
}
m_BiomeGen = new cBioGenMultiStepMap(Seed);
/*
// Performance-testing:
LOGINFO("Measuring performance of cBioGenMultiStepMap...");
clock_t BeginTick = clock();
for (int x = 0; x < 5000; x++)
{
cChunkDef::BiomeMap Biomes;
m_BiomeGen->GenBiomes(x * 5, x * 5, Biomes);
}
clock_t Duration = clock() - BeginTick;
LOGINFO("cBioGenMultiStepMap for 5000 chunks took %d ticks (%.02f sec)", Duration, (double)Duration / CLOCKS_PER_SEC);
//*/
}
// Add a cache, if requested:
int CacheSize = a_IniFile.GetValueSetI("Generator", "BiomeGenCacheSize", CacheOffByDefault ? 0 : 64);
if (CacheSize > 0)
{
if (CacheSize < 4)
{
LOGWARNING("Biomegen cache size set too low, would hurt performance instead of helping. Increasing from %d to %d",
CacheSize, 4
);
CacheSize = 4;
}
LOGD("Using a cache for biomegen of size %d.", CacheSize);
m_UnderlyingBiomeGen = m_BiomeGen;
m_BiomeGen = new cBioGenCache(m_UnderlyingBiomeGen, CacheSize);
}
m_BiomeGen->InitializeBiomeGen(a_IniFile);
}
void cComposableGenerator::InitHeightGen(cIniFile & a_IniFile)
{
AString HeightGenName = a_IniFile.GetValueSet("Generator", "HeightGen", "");
if (HeightGenName.empty())
{
LOGWARN("[Generator] HeightGen value not set in world.ini, using \"Biomal\".");
HeightGenName = "Biomal";
}
int Seed = m_ChunkGenerator.GetSeed();
bool CacheOffByDefault = false;
if (NoCaseCompare(HeightGenName, "flat") == 0)
{
m_HeightGen = new cHeiGenFlat;
CacheOffByDefault = true; // We're generating faster than a cache would retrieve data
}
else if (NoCaseCompare(HeightGenName, "classic") == 0)
{
m_HeightGen = new cHeiGenClassic(Seed);
}
else if (NoCaseCompare(HeightGenName, "DistortedHeightmap") == 0)
{
m_HeightGen = new cDistortedHeightmap(Seed, *m_BiomeGen);
}
else if (NoCaseCompare(HeightGenName, "End") == 0)
{
m_HeightGen = new cEndGen(Seed);
}
else if (NoCaseCompare(HeightGenName, "Noise3D") == 0)
{
m_HeightGen = new cNoise3DComposable(Seed);
}
else // "biomal" or <not found>
{
if (NoCaseCompare(HeightGenName, "biomal") != 0)
{
LOGWARN("Unknown HeightGen \"%s\", using \"Biomal\" instead.", HeightGenName.c_str());
}
m_HeightGen = new cHeiGenBiomal(Seed, *m_BiomeGen);
/*
// Performance-testing:
LOGINFO("Measuring performance of cHeiGenBiomal...");
clock_t BeginTick = clock();
for (int x = 0; x < 500; x++)
{
cChunkDef::HeightMap Heights;
m_HeightGen->GenHeightMap(x * 5, x * 5, Heights);
}
clock_t Duration = clock() - BeginTick;
LOGINFO("HeightGen for 500 chunks took %d ticks (%.02f sec)", Duration, (double)Duration / CLOCKS_PER_SEC);
//*/
}
// Read the settings:
m_HeightGen->InitializeHeightGen(a_IniFile);
// Add a cache, if requested:
int CacheSize = a_IniFile.GetValueSetI("Generator", "HeightGenCacheSize", CacheOffByDefault ? 0 : 64);
if (CacheSize > 0)
{
if (CacheSize < 4)
{
LOGWARNING("Heightgen cache size set too low, would hurt performance instead of helping. Increasing from %d to %d",
CacheSize, 4
);
CacheSize = 4;
}
LOGD("Using a cache for Heightgen of size %d.", CacheSize);
m_UnderlyingHeightGen = m_HeightGen;
m_HeightGen = new cHeiGenCache(*m_UnderlyingHeightGen, CacheSize);
}
}
void cComposableGenerator::InitCompositionGen(cIniFile & a_IniFile)
{
int Seed = m_ChunkGenerator.GetSeed();
AString CompoGenName = a_IniFile.GetValueSet("Generator", "CompositionGen", "");
if (CompoGenName.empty())
{
LOGWARN("[Generator] CompositionGen value not set in world.ini, using \"Biomal\".");
CompoGenName = "Biomal";
}
if (NoCaseCompare(CompoGenName, "sameblock") == 0)
{
m_CompositionGen = new cCompoGenSameBlock;
}
else if (NoCaseCompare(CompoGenName, "debugbiomes") == 0)
{
m_CompositionGen = new cCompoGenDebugBiomes;
}
else if (NoCaseCompare(CompoGenName, "classic") == 0)
{
m_CompositionGen = new cCompoGenClassic;
}
else if (NoCaseCompare(CompoGenName, "DistortedHeightmap") == 0)
{
m_CompositionGen = new cDistortedHeightmap(Seed, *m_BiomeGen);
}
else if (NoCaseCompare(CompoGenName, "end") == 0)
{
m_CompositionGen = new cEndGen(Seed);
}
else if (NoCaseCompare(CompoGenName, "nether") == 0)
{
m_CompositionGen = new cCompoGenNether(Seed);
}
else if (NoCaseCompare(CompoGenName, "Noise3D") == 0)
{
m_CompositionGen = new cNoise3DComposable(m_ChunkGenerator.GetSeed());
}
else
{
if (NoCaseCompare(CompoGenName, "biomal") != 0)
{
LOGWARN("Unknown CompositionGen \"%s\", using \"biomal\" instead.", CompoGenName.c_str());
}
m_CompositionGen = new cCompoGenBiomal(Seed);
/*
// Performance-testing:
LOGINFO("Measuring performance of cCompoGenBiomal...");
clock_t BeginTick = clock();
for (int x = 0; x < 500; x++)
{
cChunkDesc Desc(200 + x * 8, 200 + x * 8);
m_BiomeGen->GenBiomes(Desc.GetChunkX(), Desc.GetChunkZ(), Desc.GetBiomeMap());
m_HeightGen->GenHeightMap(Desc.GetChunkX(), Desc.GetChunkZ(), Desc.GetHeightMap());
m_CompositionGen->ComposeTerrain(Desc);
}
clock_t Duration = clock() - BeginTick;
LOGINFO("CompositionGen for 500 chunks took %d ticks (%.02f sec)", Duration, (double)Duration / CLOCKS_PER_SEC);
//*/
}
// Read the settings from the ini file:
m_CompositionGen->InitializeCompoGen(a_IniFile);
int CompoGenCacheSize = a_IniFile.GetValueSetI("Generator", "CompositionGenCacheSize", 64);
if (CompoGenCacheSize > 1)
{
m_UnderlyingCompositionGen = m_CompositionGen;
m_CompositionGen = new cCompoGenCache(*m_UnderlyingCompositionGen, 32);
}
}
void cComposableGenerator::InitStructureGens(cIniFile & a_IniFile)
{
AString Structures = a_IniFile.GetValueSet("Generator", "Structures", "Ravines, WormNestCaves, WaterLakes, LavaLakes, OreNests, Trees");
int Seed = m_ChunkGenerator.GetSeed();
AStringVector Str = StringSplitAndTrim(Structures, ",");
for (AStringVector::const_iterator itr = Str.begin(); itr != Str.end(); ++itr)
{
if (NoCaseCompare(*itr, "DualRidgeCaves") == 0)
{
float Threshold = (float)a_IniFile.GetValueSetF("Generator", "DualRidgeCavesThreshold", 0.3);
m_StructureGens.push_back(new cStructGenDualRidgeCaves(Seed, Threshold));
}
else if (NoCaseCompare(*itr, "DirectOverhangs") == 0)
{
m_StructureGens.push_back(new cStructGenDirectOverhangs(Seed));
}
else if (NoCaseCompare(*itr, "DistortedMembraneOverhangs") == 0)
{
m_StructureGens.push_back(new cStructGenDistortedMembraneOverhangs(Seed));
}
else if (NoCaseCompare(*itr, "LavaLakes") == 0)
{
int Probability = a_IniFile.GetValueSetI("Generator", "LavaLakesProbability", 10);
m_StructureGens.push_back(new cStructGenLakes(Seed * 5 + 16873, E_BLOCK_STATIONARY_LAVA, *m_HeightGen, Probability));
}
else if (NoCaseCompare(*itr, "MarbleCaves") == 0)
{
m_StructureGens.push_back(new cStructGenMarbleCaves(Seed));
}
else if (NoCaseCompare(*itr, "MineShafts") == 0)
{
int GridSize = a_IniFile.GetValueSetI("Generator", "MineShaftsGridSize", 512);
int MaxSystemSize = a_IniFile.GetValueSetI("Generator", "MineShaftsMaxSystemSize", 160);
int ChanceCorridor = a_IniFile.GetValueSetI("Generator", "MineShaftsChanceCorridor", 600);
int ChanceCrossing = a_IniFile.GetValueSetI("Generator", "MineShaftsChanceCrossing", 200);
int ChanceStaircase = a_IniFile.GetValueSetI("Generator", "MineShaftsChanceStaircase", 200);
m_StructureGens.push_back(new cStructGenMineShafts(
Seed, GridSize, MaxSystemSize,
ChanceCorridor, ChanceCrossing, ChanceStaircase
));
}
else if (NoCaseCompare(*itr, "OreNests") == 0)
{
m_StructureGens.push_back(new cStructGenOreNests(Seed));
}
else if (NoCaseCompare(*itr, "Ravines") == 0)
{
m_StructureGens.push_back(new cStructGenRavines(Seed, 128));
}
else if (NoCaseCompare(*itr, "Trees") == 0)
{
m_StructureGens.push_back(new cStructGenTrees(Seed, m_BiomeGen, m_HeightGen, m_CompositionGen));
}
else if (NoCaseCompare(*itr, "WaterLakes") == 0)
{
int Probability = a_IniFile.GetValueSetI("Generator", "WaterLakesProbability", 25);
m_StructureGens.push_back(new cStructGenLakes(Seed * 3 + 652, E_BLOCK_STATIONARY_WATER, *m_HeightGen, Probability));
}
else if (NoCaseCompare(*itr, "WormNestCaves") == 0)
{
m_StructureGens.push_back(new cStructGenWormNestCaves(Seed));
}
else
{
LOGWARNING("Unknown structure generator: \"%s\". Ignoring.", itr->c_str());
}
} // for itr - Str[]
}
void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
{
int Seed = m_ChunkGenerator.GetSeed();
AString Structures = a_IniFile.GetValueSet("Generator", "Finishers", "SprinkleFoliage,Ice,Snow,Lilypads,BottomLava,DeadBushes,PreSimulator");
AStringVector Str = StringSplitAndTrim(Structures, ",");
for (AStringVector::const_iterator itr = Str.begin(); itr != Str.end(); ++itr)
{
// Finishers, alpha-sorted:
if (NoCaseCompare(*itr, "BottomLava") == 0)
{
int DefaultBottomLavaLevel = (m_World->GetDimension() == dimNether) ? 30 : 10;
int BottomLavaLevel = a_IniFile.GetValueSetI("Generator", "BottomLavaLevel", DefaultBottomLavaLevel);
m_FinishGens.push_back(new cFinishGenBottomLava(BottomLavaLevel));
}
else if (NoCaseCompare(*itr, "DeadBushes") == 0)
{
m_FinishGens.push_back(new cFinishGenSingleBiomeSingleTopBlock(Seed, E_BLOCK_DEAD_BUSH, biDesert, 2, E_BLOCK_SAND, E_BLOCK_SAND));
}
else if (NoCaseCompare(*itr, "Ice") == 0)
{
m_FinishGens.push_back(new cFinishGenIce);
}
else if (NoCaseCompare(*itr, "LavaSprings") == 0)
{
m_FinishGens.push_back(new cFinishGenFluidSprings(Seed, E_BLOCK_LAVA, a_IniFile, *m_World));
}
else if (NoCaseCompare(*itr, "Lilypads") == 0)
{
m_FinishGens.push_back(new cFinishGenSingleBiomeSingleTopBlock(Seed, E_BLOCK_LILY_PAD, biSwampland, 4, E_BLOCK_WATER, E_BLOCK_STATIONARY_WATER));
}
else if (NoCaseCompare(*itr, "PreSimulator") == 0)
{
m_FinishGens.push_back(new cFinishGenPreSimulator);
}
else if (NoCaseCompare(*itr, "Snow") == 0)
{
m_FinishGens.push_back(new cFinishGenSnow);
}
else if (NoCaseCompare(*itr, "SprinkleFoliage") == 0)
{
m_FinishGens.push_back(new cFinishGenSprinkleFoliage(Seed));
}
else if (NoCaseCompare(*itr, "WaterSprings") == 0)
{
m_FinishGens.push_back(new cFinishGenFluidSprings(Seed, E_BLOCK_WATER, a_IniFile, *m_World));
}
} // for itr - Str[]
}
+181
View File
@@ -0,0 +1,181 @@
// ComposableGenerator.h
// Declares the cComposableGenerator class representing the chunk generator that takes the composition approach to generating chunks
/*
Generating works by composing several algorithms:
Biome, TerrainHeight, TerrainComposition, Ores, Structures and SmallFoliage
Each algorithm may be chosen from a pool of available algorithms in the same class and combined with others,
based on user's preferences in the world.ini.
See http://forum.mc-server.org/showthread.php?tid=409 for details.
*/
#pragma once
#include "ChunkGenerator.h"
#include "ChunkDesc.h"
// fwd: Noise3DGenerator.h
class cNoise3DComposable;
// fwd: DistortedHeightmap.h
class cDistortedHeightmap;
/** The interface that a biome generator must implement
A biome generator takes chunk coords on input and outputs an array of biome indices for that chunk on output.
The output array is sequenced in the same way as the MapChunk packet's biome data.
*/
class cBiomeGen
{
public:
virtual ~cBiomeGen() {} // Force a virtual destructor in descendants
/// Generates biomes for the given chunk
virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) = 0;
/// Reads parameters from the ini file, prepares generator for use.
virtual void InitializeBiomeGen(cIniFile & a_IniFile) {}
} ;
/** The interface that a terrain height generator must implement
A terrain height generator takes chunk coords on input and outputs an array of terrain heights for that chunk.
The output array is sequenced in the same way as the BiomeGen's biome data.
The generator may request biome information from the underlying BiomeGen, it may even request information for
other chunks than the one it's currently generating (possibly neighbors - for averaging)
*/
class cTerrainHeightGen
{
public:
virtual ~cTerrainHeightGen() {} // Force a virtual destructor in descendants
/// Generates heightmap for the given chunk
virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) = 0;
/// Reads parameters from the ini file, prepares generator for use.
virtual void InitializeHeightGen(cIniFile & a_IniFile) {}
} ;
/** The interface that a terrain composition generator must implement
Terrain composition takes chunk coords on input and outputs the blockdata for that entire chunk, along with
the list of entities. It is supposed to make use of the underlying TerrainHeightGen and BiomeGen for that purpose,
but it may request information for other chunks than the one it's currently generating from them.
*/
class cTerrainCompositionGen
{
public:
virtual ~cTerrainCompositionGen() {} // Force a virtual destructor in descendants
virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) = 0;
/// Reads parameters from the ini file, prepares generator for use.
virtual void InitializeCompoGen(cIniFile & a_IniFile) {}
} ;
/** The interface that a structure generator must implement
Structures are generated after the terrain composition took place. It should modify the blocktype data to account
for whatever structures the generator is generating.
Note that ores are considered structures too, at least from the interface point of view.
Also note that a worldgenerator may contain multiple structure generators, one for each type of structure
*/
class cStructureGen
{
public:
virtual ~cStructureGen() {} // Force a virtual destructor in descendants
virtual void GenStructures(cChunkDesc & a_ChunkDesc) = 0;
} ;
typedef std::list<cStructureGen *> cStructureGenList;
/** The interface that a finisher must implement
Finisher implements small additions after all structures have been generated.
*/
class cFinishGen
{
public:
virtual ~cFinishGen() {} // Force a virtual destructor in descendants
virtual void GenFinish(cChunkDesc & a_ChunkDesc) = 0;
} ;
typedef std::list<cFinishGen *> cFinishGenList;
class cComposableGenerator :
public cChunkGenerator::cGenerator
{
typedef cChunkGenerator::cGenerator super;
public:
cComposableGenerator(cChunkGenerator & a_ChunkGenerator);
virtual ~cComposableGenerator();
virtual void Initialize(cWorld * a_World, cIniFile & a_IniFile) override;
virtual void GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override;
virtual void DoGenerate(int a_ChunkX, int a_ChunkZ, cChunkDesc & a_ChunkDesc) override;
protected:
// The generation composition:
cBiomeGen * m_BiomeGen;
cTerrainHeightGen * m_HeightGen;
cTerrainCompositionGen * m_CompositionGen;
cStructureGenList m_StructureGens;
cFinishGenList m_FinishGens;
// Generators underlying the caches:
cBiomeGen * m_UnderlyingBiomeGen;
cTerrainHeightGen * m_UnderlyingHeightGen;
cTerrainCompositionGen * m_UnderlyingCompositionGen;
/// Reads the biome gen settings from the ini and initializes m_BiomeGen accordingly
void InitBiomeGen(cIniFile & a_IniFile);
/// Reads the HeightGen settings from the ini and initializes m_HeightGen accordingly
void InitHeightGen(cIniFile & a_IniFile);
/// Reads the CompositionGen settings from the ini and initializes m_CompositionGen accordingly
void InitCompositionGen(cIniFile & a_IniFile);
/// Reads the structures to generate from the ini and initializes m_StructureGens accordingly
void InitStructureGens(cIniFile & a_IniFile);
/// Reads the finishers from the ini and initializes m_FinishGens accordingly
void InitFinishGens(cIniFile & a_IniFile);
} ;
+444
View File
@@ -0,0 +1,444 @@
// DistortedHeightmap.cpp
// Implements the cDistortedHeightmap class representing the height and composition generator capable of overhangs
#include "Globals.h"
#include "DistortedHeightmap.h"
#include "../OSSupport/File.h"
#include "../../iniFile/iniFile.h"
#include "../LinearUpscale.h"
/** This table assigns a relative maximum overhang size in each direction to biomes.
Both numbers indicate a number which will multiply the noise value for each coord;
this means that you can have different-sized overhangs in each direction.
Usually you'd want to keep both numbers the same.
The numbers are "relative", not absolute maximum; overhangs of a slightly larger size are possible
due to the way that noise is calculated.
*/
const cDistortedHeightmap::sGenParam cDistortedHeightmap::m_GenParam[biNumBiomes] =
{
/* Biome | AmpX | AmpZ */
/* biOcean */ { 1.5f, 1.5f},
/* biPlains */ { 0.5f, 0.5f},
/* biDesert */ { 0.5f, 0.5f},
/* biExtremeHills */ {16.0f, 16.0f},
/* biForest */ { 3.0f, 3.0f},
/* biTaiga */ { 1.5f, 1.5f},
/* biSwampland */ { 0.0f, 0.0f},
/* biRiver */ { 0.0f, 0.0f},
/* biNether */ { 0.0f, 0.0f}, // Unused, but must be here due to indexing
/* biSky */ { 0.0f, 0.0f}, // Unused, but must be here due to indexing
/* biFrozenOcean */ { 0.0f, 0.0f},
/* biFrozenRiver */ { 0.0f, 0.0f},
/* biIcePlains */ { 0.0f, 0.0f},
/* biIceMountains */ { 8.0f, 8.0f},
/* biMushroomIsland */ { 4.0f, 4.0f},
/* biMushroomShore */ { 0.0f, 0.0f},
/* biBeach */ { 0.0f, 0.0f},
/* biDesertHills */ { 5.0f, 5.0f},
/* biForestHills */ { 6.0f, 6.0f},
/* biTaigaHills */ { 8.0f, 8.0f},
/* biExtremeHillsEdge */ { 7.0f, 7.0f},
/* biJungle */ { 0.0f, 0.0f},
/* biJungleHills */ { 8.0f, 8.0f},
} ;
cDistortedHeightmap::cDistortedHeightmap(int a_Seed, cBiomeGen & a_BiomeGen) :
m_NoiseDistortX(a_Seed + 1000),
m_NoiseDistortZ(a_Seed + 2000),
m_OceanFloorSelect(a_Seed + 3000),
m_BiomeGen(a_BiomeGen),
m_UnderlyingHeiGen(a_Seed, a_BiomeGen),
m_HeightGen(m_UnderlyingHeiGen, 64)
{
m_NoiseDistortX.AddOctave((NOISE_DATATYPE)1, (NOISE_DATATYPE)0.5);
m_NoiseDistortX.AddOctave((NOISE_DATATYPE)0.5, (NOISE_DATATYPE)1);
m_NoiseDistortX.AddOctave((NOISE_DATATYPE)0.25, (NOISE_DATATYPE)2);
m_NoiseDistortZ.AddOctave((NOISE_DATATYPE)1, (NOISE_DATATYPE)0.5);
m_NoiseDistortZ.AddOctave((NOISE_DATATYPE)0.5, (NOISE_DATATYPE)1);
m_NoiseDistortZ.AddOctave((NOISE_DATATYPE)0.25, (NOISE_DATATYPE)2);
}
void cDistortedHeightmap::Initialize(cIniFile & a_IniFile)
{
if (m_IsInitialized)
{
return;
}
// Read the params from the INI file:
m_SeaLevel = a_IniFile.GetValueSetI("Generator", "DistortedHeightmapSeaLevel", 62);
m_FrequencyX = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "DistortedHeightmapFrequencyX", 10);
m_FrequencyY = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "DistortedHeightmapFrequencyY", 10);
m_FrequencyZ = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "DistortedHeightmapFrequencyZ", 10);
m_IsInitialized = true;
}
void cDistortedHeightmap::PrepareState(int a_ChunkX, int a_ChunkZ)
{
if ((m_CurChunkX == a_ChunkX) && (m_CurChunkZ == a_ChunkZ))
{
return;
}
m_CurChunkX = a_ChunkX;
m_CurChunkZ = a_ChunkZ;
m_HeightGen.GenHeightMap(a_ChunkX, a_ChunkZ, m_CurChunkHeights);
UpdateDistortAmps();
GenerateHeightArray();
}
void cDistortedHeightmap::GenerateHeightArray(void)
{
// Generate distortion noise:
NOISE_DATATYPE DistortNoiseX[DIM_X * DIM_Y * DIM_Z];
NOISE_DATATYPE DistortNoiseZ[DIM_X * DIM_Y * DIM_Z];
NOISE_DATATYPE Workspace[DIM_X * DIM_Y * DIM_Z];
NOISE_DATATYPE StartX = ((NOISE_DATATYPE)(m_CurChunkX * cChunkDef::Width)) / m_FrequencyX;
NOISE_DATATYPE EndX = ((NOISE_DATATYPE)((m_CurChunkX + 1) * cChunkDef::Width - 1)) / m_FrequencyX;
NOISE_DATATYPE StartY = 0;
NOISE_DATATYPE EndY = ((NOISE_DATATYPE)(257)) / m_FrequencyY;
NOISE_DATATYPE StartZ = ((NOISE_DATATYPE)(m_CurChunkZ * cChunkDef::Width)) / m_FrequencyZ;
NOISE_DATATYPE EndZ = ((NOISE_DATATYPE)((m_CurChunkZ + 1) * cChunkDef::Width - 1)) / m_FrequencyZ;
m_NoiseDistortX.Generate3D(DistortNoiseX, DIM_X, DIM_Y, DIM_Z, StartX, EndX, StartY, EndY, StartZ, EndZ, Workspace);
m_NoiseDistortZ.Generate3D(DistortNoiseZ, DIM_X, DIM_Y, DIM_Z, StartX, EndX, StartY, EndY, StartZ, EndZ, Workspace);
// The distorted heightmap, before linear upscaling
NOISE_DATATYPE DistHei[DIM_X * DIM_Y * DIM_Z];
// Distort the heightmap using the distortion:
for (int z = 0; z < DIM_Z; z++)
{
int AmpIdx = z * DIM_X;
for (int y = 0; y < DIM_Y; y++)
{
int NoiseArrayIdx = z * DIM_X * DIM_Y + y * DIM_X;
for (int x = 0; x < DIM_X; x++)
{
NOISE_DATATYPE DistX = DistortNoiseX[NoiseArrayIdx + x] * m_DistortAmpX[AmpIdx + x];
NOISE_DATATYPE DistZ = DistortNoiseZ[NoiseArrayIdx + x] * m_DistortAmpZ[AmpIdx + x];
DistX += (NOISE_DATATYPE)(m_CurChunkX * cChunkDef::Width + x * INTERPOL_X);
DistZ += (NOISE_DATATYPE)(m_CurChunkZ * cChunkDef::Width + z * INTERPOL_Z);
// Adding 0.5 helps alleviate the interpolation artifacts
DistHei[NoiseArrayIdx + x] = (NOISE_DATATYPE)GetHeightmapAt(DistX, DistZ) + (NOISE_DATATYPE)0.5;
}
}
}
// Upscale the distorted heightmap into full dimensions:
LinearUpscale3DArray(
DistHei, DIM_X, DIM_Y, DIM_Z,
m_DistortedHeightmap, INTERPOL_X, INTERPOL_Y, INTERPOL_Z
);
// DEBUG: Debug3DNoise(m_DistortedHeightmap, 17, 257, 17, Printf("DistortedHeightmap_%d_%d", m_CurChunkX, m_CurChunkZ));
}
void cDistortedHeightmap::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap)
{
PrepareState(a_ChunkX, a_ChunkZ);
for (int z = 0; z < cChunkDef::Width; z++)
{
for (int x = 0; x < cChunkDef::Width; x++)
{
int NoiseArrayIdx = x + 17 * 257 * z;
cChunkDef::SetHeight(a_HeightMap, x, z, m_SeaLevel - 1);
for (int y = cChunkDef::Height - 1; y > m_SeaLevel - 1; y--)
{
int HeightMapHeight = (int)m_DistortedHeightmap[NoiseArrayIdx + 17 * y];
if (y < HeightMapHeight)
{
cChunkDef::SetHeight(a_HeightMap, x, z, y);
break;
}
} // for y
} // for x
} // for z
}
void cDistortedHeightmap::InitializeHeightGen(cIniFile & a_IniFile)
{
Initialize(a_IniFile);
}
void cDistortedHeightmap::ComposeTerrain(cChunkDesc & a_ChunkDesc)
{
// Frequencies for the ocean floor selecting noise:
NOISE_DATATYPE FrequencyX = 3;
NOISE_DATATYPE FrequencyZ = 3;
// Prepare the internal state for generating this chunk:
PrepareState(a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ());
// Compose:
a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0);
for (int z = 0; z < cChunkDef::Width; z++)
{
for (int x = 0; x < cChunkDef::Width; x++)
{
int NoiseArrayIdx = x + 17 * 257 * z;
int LastAir = a_ChunkDesc.GetHeight(x, z) + 1;
bool HasHadWater = false;
for (int y = LastAir - 1; y > 0; y--)
{
int HeightMapHeight = (int)m_DistortedHeightmap[NoiseArrayIdx + 17 * y];
if (y >= HeightMapHeight)
{
// "air" part
LastAir = y;
if (y < m_SeaLevel)
{
a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_STATIONARY_WATER);
HasHadWater = true;
}
continue;
}
// "ground" part:
if (y < LastAir - 4)
{
a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_STONE);
continue;
}
if (HasHadWater)
{
// Decide between clay, sand and dirt
NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(m_CurChunkX * cChunkDef::Width + x)) / FrequencyX;
NOISE_DATATYPE NoiseY = ((NOISE_DATATYPE)(m_CurChunkZ * cChunkDef::Width + z)) / FrequencyZ;
NOISE_DATATYPE Val = m_OceanFloorSelect.CubicNoise2D(NoiseX, NoiseY);
if (Val < -0.95)
{
// Clay:
switch (LastAir - y)
{
case 0:
case 1:
{
a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_CLAY);
break;
}
case 2:
case 3:
{
a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_SAND);
break;
}
case 4:
{
a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_SANDSTONE);
break;
}
} // switch (floor depth)
}
else if (Val < 0)
{
a_ChunkDesc.SetBlockType(x, y, z, (y < LastAir - 3) ? E_BLOCK_SANDSTONE : E_BLOCK_SAND);
}
else
{
a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_DIRT);
}
}
else
{
switch (a_ChunkDesc.GetBiome(x, z))
{
case biOcean:
case biPlains:
case biExtremeHills:
case biForest:
case biTaiga:
case biSwampland:
case biRiver:
case biFrozenOcean:
case biFrozenRiver:
case biIcePlains:
case biIceMountains:
case biForestHills:
case biTaigaHills:
case biExtremeHillsEdge:
case biJungle:
case biJungleHills:
{
a_ChunkDesc.SetBlockType(x, y, z, (y == LastAir - 1) ? E_BLOCK_GRASS : E_BLOCK_DIRT);
break;
}
case biDesertHills:
case biDesert:
case biBeach:
{
a_ChunkDesc.SetBlockType(x, y, z, (y < LastAir - 3) ? E_BLOCK_SANDSTONE : E_BLOCK_SAND);
break;
}
case biMushroomIsland:
case biMushroomShore:
{
a_ChunkDesc.SetBlockType(x, y, z, (y == LastAir - 1) ? E_BLOCK_MYCELIUM : E_BLOCK_DIRT);
break;
}
}
}
} // for y
a_ChunkDesc.SetBlockType(x, 0, z, E_BLOCK_BEDROCK);
} // for x
} // for z
}
void cDistortedHeightmap::InitializeCompoGen(cIniFile & a_IniFile)
{
Initialize(a_IniFile);
}
int cDistortedHeightmap::GetHeightmapAt(NOISE_DATATYPE a_X, NOISE_DATATYPE a_Z)
{
int ChunkX = (int)floor(a_X / (NOISE_DATATYPE)16);
int ChunkZ = (int)floor(a_Z / (NOISE_DATATYPE)16);
int RelX = (int)(a_X - (NOISE_DATATYPE)ChunkX * cChunkDef::Width);
int RelZ = (int)(a_Z - (NOISE_DATATYPE)ChunkZ * cChunkDef::Width);
// If we're withing the same chunk, return the pre-cached heightmap:
if ((ChunkX == m_CurChunkX) && (ChunkZ == m_CurChunkZ))
{
return cChunkDef::GetHeight(m_CurChunkHeights, RelX, RelZ);
}
// Ask the cache:
HEIGHTTYPE res = 0;
if (m_HeightGen.GetHeightAt(ChunkX, ChunkZ, RelX, RelZ, res))
{
// The height was in the cache
return res;
}
// The height is not in the cache, generate full heightmap and get it there:
cChunkDef::HeightMap Heightmap;
m_HeightGen.GenHeightMap(ChunkX, ChunkZ, Heightmap);
return cChunkDef::GetHeight(Heightmap, RelX, RelZ);
}
void cDistortedHeightmap::UpdateDistortAmps(void)
{
BiomeNeighbors Biomes;
for (int z = -1; z <= 1; z++)
{
for (int x = -1; x <= 1; x++)
{
m_BiomeGen.GenBiomes(m_CurChunkX + x, m_CurChunkZ + z, Biomes[x + 1][z + 1]);
} // for x
} // for z
for (int z = 0; z < DIM_Z; z++)
{
for (int x = 0; x < DIM_Z; x++)
{
GetDistortAmpsAt(Biomes, x * INTERPOL_X, z * INTERPOL_Z, m_DistortAmpX[x + DIM_X * z], m_DistortAmpZ[x + DIM_X * z]);
}
}
}
void cDistortedHeightmap::GetDistortAmpsAt(BiomeNeighbors & a_Neighbors, int a_RelX, int a_RelZ, NOISE_DATATYPE & a_DistortAmpX, NOISE_DATATYPE & a_DistortAmpZ)
{
// Sum up how many biomes of each type there are in the neighborhood:
int BiomeCounts[biNumBiomes];
memset(BiomeCounts, 0, sizeof(BiomeCounts));
int Sum = 0;
for (int z = -8; z <= 8; z++)
{
int FinalZ = a_RelZ + z + cChunkDef::Width;
int IdxZ = FinalZ / cChunkDef::Width;
int ModZ = FinalZ % cChunkDef::Width;
int WeightZ = 9 - abs(z);
for (int x = -8; x <= 8; x++)
{
int FinalX = a_RelX + x + cChunkDef::Width;
int IdxX = FinalX / cChunkDef::Width;
int ModX = FinalX % cChunkDef::Width;
EMCSBiome Biome = cChunkDef::GetBiome(a_Neighbors[IdxX][IdxZ], ModX, ModZ);
if ((Biome < 0) || (Biome >= ARRAYCOUNT(BiomeCounts)))
{
continue;
}
int WeightX = 9 - abs(x);
BiomeCounts[Biome] += WeightX + WeightZ;
Sum += WeightX + WeightZ;
} // for x
} // for z
if (Sum <= 0)
{
// No known biome around? Weird. Return a bogus value:
ASSERT(!"cHeiGenBiomal: Biome sum failed, no known biome around");
a_DistortAmpX = 16;
a_DistortAmpZ = 16;
}
// For each biome type that has a nonzero count, calc its amps and add it:
NOISE_DATATYPE AmpX = 0;
NOISE_DATATYPE AmpZ = 0;
for (int i = 0; i < ARRAYCOUNT(BiomeCounts); i++)
{
AmpX += BiomeCounts[i] * m_GenParam[i].m_DistortAmpX;
AmpZ += BiomeCounts[i] * m_GenParam[i].m_DistortAmpZ;
}
a_DistortAmpX = AmpX / Sum;
a_DistortAmpZ = AmpZ / Sum;
}
+108
View File
@@ -0,0 +1,108 @@
// DistortedHeightmap.h
// Declares the cDistortedHeightmap class representing the height and composition generator capable of overhangs
#pragma once
#include "ComposableGenerator.h"
#include "HeiGen.h"
#include "../Noise.h"
#define NOISE_SIZE_Y (257 + 32)
class cDistortedHeightmap :
public cTerrainHeightGen,
public cTerrainCompositionGen
{
public:
cDistortedHeightmap(int a_Seed, cBiomeGen & a_BiomeGen);
protected:
typedef cChunkDef::BiomeMap BiomeNeighbors[3][3];
// Linear upscaling step sizes, must be divisors of cChunkDef::Width and cChunkDef::Height, respectively:
static const int INTERPOL_X = 8;
static const int INTERPOL_Y = 4;
static const int INTERPOL_Z = 8;
// Linear upscaling buffer dimensions, calculated from the step sizes:
static const int DIM_X = 1 + (17 / INTERPOL_X);
static const int DIM_Y = 1 + (257 / INTERPOL_Y);
static const int DIM_Z = 1 + (17 / INTERPOL_Z);
cPerlinNoise m_NoiseDistortX;
cPerlinNoise m_NoiseDistortZ;
cNoise m_OceanFloorSelect; ///< Used for selecting between dirt and sand on the ocean floor
int m_SeaLevel;
NOISE_DATATYPE m_FrequencyX;
NOISE_DATATYPE m_FrequencyY;
NOISE_DATATYPE m_FrequencyZ;
int m_CurChunkX;
int m_CurChunkZ;
NOISE_DATATYPE m_DistortedHeightmap[17 * 257 * 17];
cBiomeGen & m_BiomeGen;
cHeiGenBiomal m_UnderlyingHeiGen; // This generator provides us with base heightmap (before distortion)
cHeiGenCache m_HeightGen; // Cache above m_UnderlyingHeiGen
/// Heightmap for the current chunk, before distortion (from m_HeightGen). Used for optimization.
cChunkDef::HeightMap m_CurChunkHeights;
// Per-biome terrain generator parameters:
struct sGenParam
{
NOISE_DATATYPE m_DistortAmpX;
NOISE_DATATYPE m_DistortAmpZ;
} ;
static const sGenParam m_GenParam[biNumBiomes];
// Distortion amplitudes for each direction, before linear upscaling
NOISE_DATATYPE m_DistortAmpX[DIM_X * DIM_Z];
NOISE_DATATYPE m_DistortAmpZ[DIM_X * DIM_Z];
/// True if Initialize() has been called. Used to initialize-once even with multiple init entrypoints (HeiGen / CompoGen)
bool m_IsInitialized;
/// Unless the LastChunk coords are equal to coords given, prepares the internal state (noise arrays, heightmap)
void PrepareState(int a_ChunkX, int a_ChunkZ);
/// Generates the m_DistortedHeightmap array for the current chunk
void GenerateHeightArray(void);
/// Calculates the heightmap value (before distortion) at the specified (floating-point) coords
int GetHeightmapAt(NOISE_DATATYPE a_X, NOISE_DATATYPE a_Z);
/// Updates m_DistortAmpX/Z[] based on m_CurChunkX and m_CurChunkZ
void UpdateDistortAmps(void);
/// Calculates the X and Z distortion amplitudes based on the neighbors' biomes
void GetDistortAmpsAt(BiomeNeighbors & a_Neighbors, int a_RelX, int a_RelZ, NOISE_DATATYPE & a_DistortAmpX, NOISE_DATATYPE & a_DistortAmpZ);
/// Reads the settings from the ini file. Skips reading if already initialized
void Initialize(cIniFile & a_IniFile);
// cTerrainHeightGen overrides:
virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override;
virtual void InitializeHeightGen(cIniFile & a_IniFile) override;
// cTerrainCompositionGen overrides:
virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override;
virtual void InitializeCompoGen(cIniFile & a_IniFile) override;
} ;
+217
View File
@@ -0,0 +1,217 @@
// EndGen.cpp
// Implements the cEndGen class representing the generator for the End, both as a HeightGen and CompositionGen
#include "Globals.h"
#include "EndGen.h"
#include "../../iniFile/iniFile.h"
#include "../LinearUpscale.h"
enum
{
// Interpolation cell size:
INTERPOL_X = 4,
INTERPOL_Y = 4,
INTERPOL_Z = 4,
// Size of chunk data, downscaled before interpolation:
DIM_X = 16 / INTERPOL_X + 1,
DIM_Y = 256 / INTERPOL_Y + 1,
DIM_Z = 16 / INTERPOL_Z + 1,
} ;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cEndGen:
cEndGen::cEndGen(int a_Seed) :
m_Seed(a_Seed),
m_IslandSizeX(256),
m_IslandSizeY(96),
m_IslandSizeZ(256),
m_FrequencyX(80),
m_FrequencyY(80),
m_FrequencyZ(80)
{
m_Perlin.AddOctave(1, 1);
m_Perlin.AddOctave(2, 0.5);
m_Perlin.AddOctave(4, 0.25);
}
void cEndGen::Initialize(cIniFile & a_IniFile)
{
m_IslandSizeX = a_IniFile.GetValueSetI("Generator", "EndGenIslandSizeX", m_IslandSizeX);
m_IslandSizeY = a_IniFile.GetValueSetI("Generator", "EndGenIslandSizeY", m_IslandSizeY);
m_IslandSizeZ = a_IniFile.GetValueSetI("Generator", "EndGenIslandSizeZ", m_IslandSizeZ);
m_FrequencyX = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "EndGenFrequencyX", m_FrequencyX);
m_FrequencyY = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "EndGenFrequencyY", m_FrequencyY);
m_FrequencyZ = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "EndGenFrequencyZ", m_FrequencyZ);
// Recalculate the min and max chunk coords of the island
m_MaxChunkX = (m_IslandSizeX + cChunkDef::Width - 1) / cChunkDef::Width;
m_MinChunkX = -m_MaxChunkX;
m_MaxChunkZ = (m_IslandSizeZ + cChunkDef::Width - 1) / cChunkDef::Width;
m_MinChunkZ = -m_MaxChunkZ;
}
/// Unless the LastChunk coords are equal to coords given, prepares the internal state (noise array)
void cEndGen::PrepareState(int a_ChunkX, int a_ChunkZ)
{
ASSERT(!IsChunkOutsideRange(a_ChunkX, a_ChunkZ)); // Should be filtered before calling this function
if ((m_LastChunkX == a_ChunkX) && (m_LastChunkZ == a_ChunkZ))
{
return;
}
m_LastChunkX = a_ChunkX;
m_LastChunkZ = a_ChunkZ;
GenerateNoiseArray();
}
/// Generates the m_NoiseArray array for the current chunk
void cEndGen::GenerateNoiseArray(void)
{
NOISE_DATATYPE NoiseData[DIM_X * DIM_Y * DIM_Z]; // [x + DIM_X * z + DIM_X * DIM_Z * y]
NOISE_DATATYPE Workspace[DIM_X * DIM_Y * DIM_Z]; // [x + DIM_X * z + DIM_X * DIM_Z * y]
// Generate the downscaled noise:
NOISE_DATATYPE StartX = ((NOISE_DATATYPE)(m_LastChunkX * cChunkDef::Width)) / m_FrequencyX;
NOISE_DATATYPE EndX = ((NOISE_DATATYPE)((m_LastChunkX + 1) * cChunkDef::Width)) / m_FrequencyX;
NOISE_DATATYPE StartZ = ((NOISE_DATATYPE)(m_LastChunkZ * cChunkDef::Width)) / m_FrequencyZ;
NOISE_DATATYPE EndZ = ((NOISE_DATATYPE)((m_LastChunkZ + 1) * cChunkDef::Width)) / m_FrequencyZ;
NOISE_DATATYPE StartY = 0;
NOISE_DATATYPE EndY = ((NOISE_DATATYPE)257) / m_FrequencyY;
m_Perlin.Generate3D(NoiseData, DIM_X, DIM_Z, DIM_Y, StartX, EndX, StartZ, EndZ, StartY, EndY, Workspace);
// Add distance:
int idx = 0;
for (int y = 0; y < DIM_Y; y++)
{
NOISE_DATATYPE ValY = (NOISE_DATATYPE)(2 * INTERPOL_Y * y - m_IslandSizeY) / m_IslandSizeY;
ValY = ValY * ValY;
for (int z = 0; z < DIM_Z; z++)
{
NOISE_DATATYPE ValZ = (NOISE_DATATYPE)(m_LastChunkZ * cChunkDef::Width + (z * cChunkDef::Width / (DIM_Z - 1))) / m_IslandSizeZ;
ValZ = ValZ * ValZ;
for (int x = 0; x < DIM_X; x++)
{
// NOISE_DATATYPE ValX = StartX + (EndX - StartX) * x / (DIM_X - 1);
NOISE_DATATYPE ValX = (NOISE_DATATYPE)(m_LastChunkX * cChunkDef::Width + (x * cChunkDef::Width / (DIM_X - 1))) / m_IslandSizeX;
ValX = ValX * ValX;
NoiseData[idx++] += ValX + ValZ + ValY;
} // for x
} // for z
} // for y
// Upscale into real chunk size:
LinearUpscale3DArray(NoiseData, DIM_X, DIM_Z, DIM_Y, m_NoiseArray, INTERPOL_X, INTERPOL_Z, INTERPOL_Y);
}
/// Returns true if the chunk is outside of the island's dimensions
bool cEndGen::IsChunkOutsideRange(int a_ChunkX, int a_ChunkZ)
{
return (
(a_ChunkX < m_MinChunkX) || (a_ChunkX > m_MaxChunkX) ||
(a_ChunkZ < m_MinChunkZ) || (a_ChunkZ > m_MaxChunkZ)
);
}
void cEndGen::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap)
{
if (IsChunkOutsideRange(a_ChunkX, a_ChunkZ))
{
for (int i = 0; i < ARRAYCOUNT(a_HeightMap); i++)
{
a_HeightMap[i] = 0;
}
return;
}
PrepareState(a_ChunkX, a_ChunkZ);
int MaxY = std::min((int)(1.75 * m_IslandSizeY + 1), cChunkDef::Height - 1);
for (int z = 0; z < cChunkDef::Width; z++)
{
for (int x = 0; x < cChunkDef::Width; x++)
{
cChunkDef::SetHeight(a_HeightMap, x, z, MaxY);
for (int y = MaxY; y > 0; y--)
{
if (m_NoiseArray[y * 17 * 17 + z * 17 + x] <= 0)
{
cChunkDef::SetHeight(a_HeightMap, x, z, y);
break;
}
} // for y
} // for x
} // for z
}
void cEndGen::ComposeTerrain(cChunkDesc & a_ChunkDesc)
{
if (IsChunkOutsideRange(a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ()))
{
a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0);
return;
}
PrepareState(a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ());
int MaxY = std::min((int)(1.75 * m_IslandSizeY + 1), cChunkDef::Height - 1);
for (int z = 0; z < cChunkDef::Width; z++)
{
for (int x = 0; x < cChunkDef::Width; x++)
{
for (int y = MaxY; y > 0; y--)
{
if (m_NoiseArray[y * 17 * 17 + z * 17 + x] <= 0)
{
a_ChunkDesc.SetBlockTypeMeta(x, y, z, E_BLOCK_END_STONE, 0);
}
else
{
a_ChunkDesc.SetBlockTypeMeta(x, y, z, E_BLOCK_AIR, 0);
}
} // for y
} // for x
} // for z
}
+69
View File
@@ -0,0 +1,69 @@
// EndGen.h
// Declares the cEndGen class representing the generator for the End, both as a HeightGen and CompositionGen
#pragma once
#include "ComposableGenerator.h"
#include "../Noise.h"
class cEndGen :
public cTerrainHeightGen,
public cTerrainCompositionGen
{
public:
cEndGen(int a_Seed);
void Initialize(cIniFile & a_IniFile);
protected:
/// Seed for the noise
int m_Seed;
/// The Perlin noise used for generating
cPerlinNoise m_Perlin;
// XYZ size of the "island", in blocks:
int m_IslandSizeX;
int m_IslandSizeY;
int m_IslandSizeZ;
// XYZ Frequencies of the noise functions:
NOISE_DATATYPE m_FrequencyX;
NOISE_DATATYPE m_FrequencyY;
NOISE_DATATYPE m_FrequencyZ;
// Minimum and maximum chunk coords for chunks inside the island area. Chunks outside won't get calculated at all
int m_MinChunkX, m_MaxChunkX;
int m_MinChunkZ, m_MaxChunkZ;
// Noise array for the last chunk (in the noise range)
int m_LastChunkX;
int m_LastChunkZ;
NOISE_DATATYPE m_NoiseArray[17 * 17 * 257]; // x + 17 * z + 17 * 17 * y
/// Unless the LastChunk coords are equal to coords given, prepares the internal state (noise array)
void PrepareState(int a_ChunkX, int a_ChunkZ);
/// Generates the m_NoiseArray array for the current chunk
void GenerateNoiseArray(void);
/// Returns true if the chunk is outside of the island's dimensions
bool IsChunkOutsideRange(int a_ChunkX, int a_ChunkZ);
// cTerrainHeightGen overrides:
virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override;
// cTerrainCompositionGen overrides:
virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override;
} ;
+664
View File
@@ -0,0 +1,664 @@
// FinishGen.cpp
/* Implements the various finishing generators:
- cFinishGenSnow
- cFinishGenIce
- cFinishGenSprinkleFoliage
*/
#include "Globals.h"
#include "FinishGen.h"
#include "../Noise.h"
#include "../BlockID.h"
#include "../Simulator/FluidSimulator.h" // for cFluidSimulator::CanWashAway()
#include "../World.h"
#define DEF_NETHER_WATER_SPRINGS "0, 1; 255, 1"
#define DEF_NETHER_LAVA_SPRINGS "0, 0; 30, 0; 31, 50; 120, 50; 127, 0"
#define DEF_OVERWORLD_WATER_SPRINGS "0, 0; 10, 10; 11, 75; 16, 83; 20, 83; 24, 78; 32, 62; 40, 40; 44, 15; 48, 7; 56, 2; 64, 1; 255, 0"
#define DEF_OVERWORLD_LAVA_SPRINGS "0, 0; 10, 5; 11, 45; 48, 2; 64, 1; 255, 0"
#define DEF_END_WATER_SPRINGS "0, 1; 255, 1"
#define DEF_END_LAVA_SPRINGS "0, 1; 255, 1"
static inline bool IsWater(BLOCKTYPE a_BlockType)
{
return (a_BlockType == E_BLOCK_STATIONARY_WATER) || (a_BlockType == E_BLOCK_WATER);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cFinishGenSprinkleFoliage:
bool cFinishGenSprinkleFoliage::TryAddSugarcane(cChunkDesc & a_ChunkDesc, int a_RelX, int a_RelY, int a_RelZ)
{
// We'll be doing comparison to neighbors, so require the coords to be 1 block away from the chunk edges:
if (
(a_RelX < 1) || (a_RelX >= cChunkDef::Width - 1) ||
(a_RelY < 1) || (a_RelY >= cChunkDef::Height - 2) ||
(a_RelZ < 1) || (a_RelZ >= cChunkDef::Width - 1)
)
{
return false;
}
// Only allow dirt, grass or sand below sugarcane:
switch (a_ChunkDesc.GetBlockType(a_RelX, a_RelY, a_RelZ))
{
case E_BLOCK_DIRT:
case E_BLOCK_GRASS:
case E_BLOCK_SAND:
{
break;
}
default:
{
return false;
}
}
// Water is required next to the block below the sugarcane:
if (
!IsWater(a_ChunkDesc.GetBlockType(a_RelX - 1, a_RelY, a_RelZ)) &&
!IsWater(a_ChunkDesc.GetBlockType(a_RelX + 1, a_RelY, a_RelZ)) &&
!IsWater(a_ChunkDesc.GetBlockType(a_RelX , a_RelY, a_RelZ - 1)) &&
!IsWater(a_ChunkDesc.GetBlockType(a_RelX , a_RelY, a_RelZ + 1))
)
{
return false;
}
// All conditions met, place a sugarcane here:
a_ChunkDesc.SetBlockType(a_RelX, a_RelY + 1, a_RelZ, E_BLOCK_SUGARCANE);
return true;
}
void cFinishGenSprinkleFoliage::GenFinish(cChunkDesc & a_ChunkDesc)
{
// Generate small foliage (1-block):
// TODO: Update heightmap with 1-block-tall foliage
for (int z = 0; z < cChunkDef::Width; z++)
{
int BlockZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width + z;
const float zz = (float)BlockZ;
for (int x = 0; x < cChunkDef::Width; x++)
{
int BlockX = a_ChunkDesc.GetChunkX() * cChunkDef::Width + x;
if (((m_Noise.IntNoise2DInt(BlockX, BlockZ) / 8) % 128) < 124)
{
continue;
}
int Top = a_ChunkDesc.GetHeight(x, z);
if (Top > 250)
{
// Nothing grows above Y=250
continue;
}
if (a_ChunkDesc.GetBlockType(x, Top + 1, z) != E_BLOCK_AIR)
{
// Space already taken by something else, don't grow here
// WEIRD, since we're using heightmap, so there should NOT be anything above it
continue;
}
const float xx = (float)BlockX;
float val1 = m_Noise.CubicNoise2D(xx * 0.1f, zz * 0.1f );
float val2 = m_Noise.CubicNoise2D(xx * 0.01f, zz * 0.01f );
switch (a_ChunkDesc.GetBlockType(x, Top, z))
{
case E_BLOCK_GRASS:
{
float val3 = m_Noise.CubicNoise2D(xx * 0.01f + 10, zz * 0.01f + 10 );
float val4 = m_Noise.CubicNoise2D(xx * 0.05f + 20, zz * 0.05f + 20 );
if (val1 + val2 > 0.2f)
{
a_ChunkDesc.SetBlockType(x, ++Top, z, E_BLOCK_YELLOW_FLOWER);
}
else if (val2 + val3 > 0.2f)
{
a_ChunkDesc.SetBlockType(x, ++Top, z, E_BLOCK_RED_ROSE);
}
else if (val3 + val4 > 0.2f)
{
a_ChunkDesc.SetBlockType(x, ++Top, z, E_BLOCK_RED_MUSHROOM);
}
else if (val1 + val4 > 0.2f)
{
a_ChunkDesc.SetBlockType(x, ++Top, z, E_BLOCK_BROWN_MUSHROOM);
}
else if (val1 + val2 + val3 + val4 < -0.1)
{
a_ChunkDesc.SetBlockTypeMeta(x, ++Top, z, E_BLOCK_TALL_GRASS, E_META_TALL_GRASS_GRASS);
}
else if (TryAddSugarcane(a_ChunkDesc, x, Top, z))
{
++Top;
}
else if ((val1 > 0.5) && (val2 < -0.5))
{
a_ChunkDesc.SetBlockTypeMeta(x, ++Top, z, E_BLOCK_PUMPKIN, (int)(val3 * 8) % 4);
}
break;
} // case E_BLOCK_GRASS
case E_BLOCK_SAND:
{
int y = Top + 1;
if (
(x > 0) && (x < cChunkDef::Width - 1) &&
(z > 0) && (z < cChunkDef::Width - 1) &&
(val1 + val2 > 0.5f) &&
(a_ChunkDesc.GetBlockType(x + 1, y, z) == E_BLOCK_AIR) &&
(a_ChunkDesc.GetBlockType(x - 1, y, z) == E_BLOCK_AIR) &&
(a_ChunkDesc.GetBlockType(x, y, z + 1) == E_BLOCK_AIR) &&
(a_ChunkDesc.GetBlockType(x, y, z - 1) == E_BLOCK_AIR)
)
{
a_ChunkDesc.SetBlockType(x, ++Top, z, E_BLOCK_CACTUS);
}
else if (TryAddSugarcane(a_ChunkDesc, x, Top, z))
{
++Top;
}
break;
}
} // switch (TopBlock)
a_ChunkDesc.SetHeight(x, z, Top);
} // for y
} // for z
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cFinishGenSnow:
void cFinishGenSnow::GenFinish(cChunkDesc & a_ChunkDesc)
{
// Add a snow block in snowy biomes onto blocks that can be snowed over
for (int z = 0; z < cChunkDef::Width; z++)
{
for (int x = 0; x < cChunkDef::Width; x++)
{
switch (a_ChunkDesc.GetBiome(x, z))
{
case biIcePlains:
case biIceMountains:
case biTaiga:
case biTaigaHills:
case biFrozenRiver:
case biFrozenOcean:
{
int Height = a_ChunkDesc.GetHeight(x, z);
if (g_BlockIsSnowable[a_ChunkDesc.GetBlockType(x, Height, z)])
{
a_ChunkDesc.SetBlockType(x, Height + 1, z, E_BLOCK_SNOW);
a_ChunkDesc.SetHeight(x, z, Height + 1);
}
break;
}
}
}
} // for z
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cFinishGenIce:
void cFinishGenIce::GenFinish(cChunkDesc & a_ChunkDesc)
{
// Turn surface water into ice in icy biomes
for (int z = 0; z < cChunkDef::Width; z++)
{
for (int x = 0; x < cChunkDef::Width; x++)
{
switch (a_ChunkDesc.GetBiome(x, z))
{
case biIcePlains:
case biIceMountains:
case biTaiga:
case biTaigaHills:
case biFrozenRiver:
case biFrozenOcean:
{
int Height = a_ChunkDesc.GetHeight(x, z);
switch (a_ChunkDesc.GetBlockType(x, Height, z))
{
case E_BLOCK_WATER:
case E_BLOCK_STATIONARY_WATER:
{
a_ChunkDesc.SetBlockType(x, Height, z, E_BLOCK_ICE);
break;
}
}
break;
}
}
}
} // for z
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cFinishGenLilypads:
int cFinishGenSingleBiomeSingleTopBlock::GetNumToGen(const cChunkDef::BiomeMap & a_BiomeMap)
{
int res = 0;
for (int i = 0; i < ARRAYCOUNT(a_BiomeMap); i++)
{
if (a_BiomeMap[i] == m_Biome)
{
res++;
}
} // for i - a_BiomeMap[]
return m_Amount * res / 256;
}
void cFinishGenSingleBiomeSingleTopBlock::GenFinish(cChunkDesc & a_ChunkDesc)
{
// Add Lilypads on top of water surface in Swampland
int NumToGen = GetNumToGen(a_ChunkDesc.GetBiomeMap());
int ChunkX = a_ChunkDesc.GetChunkX();
int ChunkZ = a_ChunkDesc.GetChunkZ();
for (int i = 0; i < NumToGen; i++)
{
int x = (m_Noise.IntNoise3DInt(ChunkX + ChunkZ, ChunkZ, i) / 13) % cChunkDef::Width;
int z = (m_Noise.IntNoise3DInt(ChunkX - ChunkZ, i, ChunkZ) / 11) % cChunkDef::Width;
// Place the block at {x, z} if possible:
if (a_ChunkDesc.GetBiome(x, z) != m_Biome)
{
// Incorrect biome
continue;
}
int Height = a_ChunkDesc.GetHeight(x, z);
if (Height >= cChunkDef::Height)
{
// Too high up
continue;
}
if (a_ChunkDesc.GetBlockType(x, Height + 1, z) != E_BLOCK_AIR)
{
// Not an empty block
continue;
}
BLOCKTYPE BlockBelow = a_ChunkDesc.GetBlockType(x, Height, z);
if ((BlockBelow == m_AllowedBelow1) || (BlockBelow == m_AllowedBelow2))
{
a_ChunkDesc.SetBlockType(x, Height + 1, z, m_BlockType);
a_ChunkDesc.SetHeight(x, z, Height + 1);
}
} // for i
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cFinishGenBottomLava:
void cFinishGenBottomLava::GenFinish(cChunkDesc & a_ChunkDesc)
{
cChunkDef::BlockTypes & BlockTypes = a_ChunkDesc.GetBlockTypes();
for (int y = m_Level; y > 0; y--)
{
for (int z = 0; z < cChunkDef::Width; z++) for (int x = 0; x < cChunkDef::Width; x++)
{
int Index = cChunkDef::MakeIndexNoCheck(x, y, z);
if (BlockTypes[Index] == E_BLOCK_AIR)
{
BlockTypes[Index] = E_BLOCK_STATIONARY_LAVA;
}
} // for x, for z
} // for y
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cFinishGenPreSimulator:
cFinishGenPreSimulator::cFinishGenPreSimulator(void)
{
// Nothing needed yet
}
void cFinishGenPreSimulator::GenFinish(cChunkDesc & a_ChunkDesc)
{
CollapseSandGravel(a_ChunkDesc.GetBlockTypes(), a_ChunkDesc.GetHeightMap());
StationarizeFluid(a_ChunkDesc.GetBlockTypes(), a_ChunkDesc.GetHeightMap(), E_BLOCK_WATER, E_BLOCK_STATIONARY_WATER);
StationarizeFluid(a_ChunkDesc.GetBlockTypes(), a_ChunkDesc.GetHeightMap(), E_BLOCK_LAVA, E_BLOCK_STATIONARY_LAVA);
// TODO: other operations
}
void cFinishGenPreSimulator::CollapseSandGravel(
cChunkDef::BlockTypes & a_BlockTypes, // Block types to read and change
cChunkDef::HeightMap & a_HeightMap // Height map to update by the current data
)
{
for (int z = 0; z < cChunkDef::Width; z++)
{
for (int x = 0; x < cChunkDef::Width; x++)
{
int LastY = -1;
int HeightY = 0;
for (int y = 0; y < cChunkDef::Height; y++)
{
BLOCKTYPE Block = cChunkDef::GetBlock(a_BlockTypes, x, y, z);
switch (Block)
{
default:
{
// Set the last block onto which stuff can fall to this height:
LastY = y;
HeightY = y;
break;
}
case E_BLOCK_AIR:
{
// Do nothing
break;
}
case E_BLOCK_FIRE:
case E_BLOCK_WATER:
case E_BLOCK_STATIONARY_WATER:
case E_BLOCK_LAVA:
case E_BLOCK_STATIONARY_LAVA:
{
// Do nothing, only remember this height as potentially highest
HeightY = y;
break;
}
case E_BLOCK_SAND:
case E_BLOCK_GRAVEL:
{
if (LastY < y - 1)
{
cChunkDef::SetBlock(a_BlockTypes, x, LastY + 1, z, Block);
cChunkDef::SetBlock(a_BlockTypes, x, y, z, E_BLOCK_AIR);
}
LastY++;
if (LastY > HeightY)
{
HeightY = LastY;
}
break;
}
} // switch (GetBlock)
} // for y
cChunkDef::SetHeight(a_HeightMap, x, z, HeightY);
} // for x
} // for z
}
void cFinishGenPreSimulator::StationarizeFluid(
cChunkDef::BlockTypes & a_BlockTypes, // Block types to read and change
cChunkDef::HeightMap & a_HeightMap, // Height map to read
BLOCKTYPE a_Fluid,
BLOCKTYPE a_StationaryFluid
)
{
// Turn fluid in the middle to stationary, unless it has air or washable block next to it:
for (int z = 1; z < cChunkDef::Width - 1; z++)
{
for (int x = 1; x < cChunkDef::Width - 1; x++)
{
for (int y = cChunkDef::GetHeight(a_HeightMap, x, z); y >= 0; y--)
{
BLOCKTYPE Block = cChunkDef::GetBlock(a_BlockTypes, x, y, z);
if ((Block != a_Fluid) && (Block != a_StationaryFluid))
{
continue;
}
static const struct
{
int x, y, z;
} Coords[] =
{
{1, 0, 0},
{-1, 0, 0},
{0, 0, 1},
{0, 0, -1},
{0, -1, 0}
} ;
BLOCKTYPE BlockToSet = a_StationaryFluid; // By default, don't simulate this block
for (int i = 0; i < ARRAYCOUNT(Coords); i++)
{
if ((y == 0) && (Coords[i].y < 0))
{
continue;
}
BLOCKTYPE Neighbor = cChunkDef::GetBlock(a_BlockTypes, x + Coords[i].x, y + Coords[i].y, z + Coords[i].z);
if ((Neighbor == E_BLOCK_AIR) || cFluidSimulator::CanWashAway(Neighbor))
{
// There is an air / washable neighbor, simulate this block
BlockToSet = a_Fluid;
break;
}
} // for i - Coords[]
cChunkDef::SetBlock(a_BlockTypes, x, y, z, BlockToSet);
} // for y
} // for x
} // for z
// Turn fluid at the chunk edges into non-stationary fluid:
for (int y = 0; y < cChunkDef::Height; y++)
{
for (int i = 0; i < cChunkDef::Width; i++) // i stands for both x and z here
{
if (cChunkDef::GetBlock(a_BlockTypes, 0, y, i) == a_StationaryFluid)
{
cChunkDef::SetBlock(a_BlockTypes, 0, y, i, a_Fluid);
}
if (cChunkDef::GetBlock(a_BlockTypes, i, y, 0) == a_StationaryFluid)
{
cChunkDef::SetBlock(a_BlockTypes, i, y, 0, a_Fluid);
}
if (cChunkDef::GetBlock(a_BlockTypes, cChunkDef::Width - 1, y, i) == a_StationaryFluid)
{
cChunkDef::SetBlock(a_BlockTypes, cChunkDef::Width - 1, y, i, a_Fluid);
}
if (cChunkDef::GetBlock(a_BlockTypes, i, y, cChunkDef::Width - 1) == a_StationaryFluid)
{
cChunkDef::SetBlock(a_BlockTypes, i, y, cChunkDef::Width - 1, a_Fluid);
}
}
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cFinishGenFluidSprings:
cFinishGenFluidSprings::cFinishGenFluidSprings(int a_Seed, BLOCKTYPE a_Fluid, cIniFile & a_IniFile, const cWorld & a_World) :
m_Noise(a_Seed + a_Fluid * 100), // Need to take fluid into account, otherwise water and lava springs generate next to each other
m_HeightDistribution(255),
m_Fluid(a_Fluid)
{
bool IsWater = (a_Fluid == E_BLOCK_WATER);
AString SectionName = IsWater ? "WaterSprings" : "LavaSprings";
AString DefaultHeightDistribution;
int DefaultChance;
switch (a_World.GetDimension())
{
case dimNether:
{
DefaultHeightDistribution = IsWater ? DEF_NETHER_WATER_SPRINGS : DEF_NETHER_LAVA_SPRINGS;
DefaultChance = IsWater ? 0 : 15;
break;
}
case dimOverworld:
{
DefaultHeightDistribution = IsWater ? DEF_OVERWORLD_WATER_SPRINGS : DEF_OVERWORLD_LAVA_SPRINGS;
DefaultChance = IsWater ? 24 : 9;
break;
}
case dimEnd:
{
DefaultHeightDistribution = IsWater ? DEF_END_WATER_SPRINGS : DEF_END_LAVA_SPRINGS;
DefaultChance = 0;
break;
}
default:
{
ASSERT(!"Unhandled world dimension");
break;
}
} // switch (dimension)
AString HeightDistribution = a_IniFile.GetValueSet(SectionName, "HeightDistribution", DefaultHeightDistribution);
if (!m_HeightDistribution.SetDefString(HeightDistribution) || (m_HeightDistribution.GetSum() <= 0))
{
LOGWARNING("[%sSprings]: HeightDistribution is invalid, using the default of \"%s\".",
(a_Fluid == E_BLOCK_WATER) ? "Water" : "Lava",
DefaultHeightDistribution.c_str()
);
m_HeightDistribution.SetDefString(DefaultHeightDistribution);
}
m_Chance = a_IniFile.GetValueSetI(SectionName, "Chance", DefaultChance);
}
void cFinishGenFluidSprings::GenFinish(cChunkDesc & a_ChunkDesc)
{
int ChanceRnd = (m_Noise.IntNoise3DInt(128 * a_ChunkDesc.GetChunkX(), 512, 256 * a_ChunkDesc.GetChunkZ()) / 13) % 100;
if (ChanceRnd > m_Chance)
{
// Not in this chunk
return;
}
// Get the height at which to try:
int Height = m_Noise.IntNoise3DInt(128 * a_ChunkDesc.GetChunkX(), 1024, 256 * a_ChunkDesc.GetChunkZ()) / 11;
Height %= m_HeightDistribution.GetSum();
Height = m_HeightDistribution.MapValue(Height);
// Try adding the spring at the height, if unsuccessful, move lower:
for (int y = Height; y > 1; y--)
{
// TODO: randomize the order in which the coords are being checked
for (int z = 1; z < cChunkDef::Width - 1; z++)
{
for (int x = 1; x < cChunkDef::Width - 1; x++)
{
switch (a_ChunkDesc.GetBlockType(x, y, z))
{
case E_BLOCK_NETHERRACK:
case E_BLOCK_STONE:
{
if (TryPlaceSpring(a_ChunkDesc, x, y, z))
{
// Succeeded, bail out
return;
}
}
} // switch (BlockType)
} // for x
} // for y
} // for y
}
bool cFinishGenFluidSprings::TryPlaceSpring(cChunkDesc & a_ChunkDesc, int x, int y, int z)
{
// In order to place a spring, it needs exactly one of the XZ neighbors or a below neighbor to be air
// Also, its neighbor on top of it must be non-air
if (a_ChunkDesc.GetBlockType(x, y + 1, z) == E_BLOCK_AIR)
{
return false;
}
static const struct
{
int x, y, z;
} Coords[] =
{
{-1, 0, 0},
{ 1, 0, 0},
{ 0, -1, 0},
{ 0, 0, -1},
{ 0, 0, 1},
} ;
int NumAirNeighbors = 0;
for (int i = 0; i < ARRAYCOUNT(Coords); i++)
{
switch (a_ChunkDesc.GetBlockType(x + Coords[i].x, y + Coords[i].y, z + Coords[i].z))
{
case E_BLOCK_AIR:
{
NumAirNeighbors += 1;
if (NumAirNeighbors > 1)
{
return false;
}
}
}
}
if (NumAirNeighbors == 0)
{
return false;
}
// Has exactly one air neighbor, place a spring:
a_ChunkDesc.SetBlockTypeMeta(x, y, z, m_Fluid, 0);
return true;
}
+185
View File
@@ -0,0 +1,185 @@
// FinishGen.h
/* Interfaces to the various finishing generators:
- cFinishGenSnow
- cFinishGenIce
- cFinishGenSprinkleFoliage
- cFinishGenLilypads
- cFinishGenBottomLava
- cFinishGenPreSimulator
- cFinishGenDeadBushes
*/
#include "ComposableGenerator.h"
#include "../Noise.h"
#include "../ProbabDistrib.h"
class cFinishGenSnow :
public cFinishGen
{
protected:
// cFinishGen override:
virtual void GenFinish(cChunkDesc & a_ChunkDesc) override;
} ;
class cFinishGenIce :
public cFinishGen
{
protected:
// cFinishGen override:
virtual void GenFinish(cChunkDesc & a_ChunkDesc) override;
} ;
class cFinishGenSprinkleFoliage :
public cFinishGen
{
public:
cFinishGenSprinkleFoliage(int a_Seed) : m_Noise(a_Seed), m_Seed(a_Seed) {}
protected:
cNoise m_Noise;
int m_Seed;
/// Tries to place sugarcane at the coords specified, returns true if successful
bool TryAddSugarcane(cChunkDesc & a_ChunkDesc, int a_RelX, int a_RelY, int a_RelZ);
// cFinishGen override:
virtual void GenFinish(cChunkDesc & a_ChunkDesc) override;
} ;
/** This class adds a single top block in random positions in the specified biome on top of specified allowed blocks.
Used for:
- Lilypads finisher
- DeadBushes finisher
*/
class cFinishGenSingleBiomeSingleTopBlock :
public cFinishGen
{
public:
cFinishGenSingleBiomeSingleTopBlock(
int a_Seed, BLOCKTYPE a_BlockType, EMCSBiome a_Biome, int a_Amount,
BLOCKTYPE a_AllowedBelow1, BLOCKTYPE a_AllowedBelow2
) :
m_Noise(a_Seed),
m_BlockType(a_BlockType),
m_Biome(a_Biome),
m_Amount(a_Amount),
m_AllowedBelow1(a_AllowedBelow1),
m_AllowedBelow2(a_AllowedBelow2)
{
}
protected:
cNoise m_Noise;
BLOCKTYPE m_BlockType;
EMCSBiome m_Biome;
int m_Amount; ///< Relative amount of blocks to try adding. 1 = one block per 256 biome columns.
BLOCKTYPE m_AllowedBelow1; ///< First of the two blocktypes that are allowed below m_BlockType
BLOCKTYPE m_AllowedBelow2; ///< Second of the two blocktypes that are allowed below m_BlockType
int GetNumToGen(const cChunkDef::BiomeMap & a_BiomeMap);
// cFinishGen override:
virtual void GenFinish(cChunkDesc & a_ChunkDesc) override;
} ;
class cFinishGenBottomLava :
public cFinishGen
{
public:
cFinishGenBottomLava(int a_Level) :
m_Level(a_Level)
{
}
protected:
int m_Level;
// cFinishGen override:
virtual void GenFinish(cChunkDesc & a_ChunkDesc) override;
} ;
class cFinishGenPreSimulator :
public cFinishGen
{
public:
cFinishGenPreSimulator(void);
protected:
// Drops hanging sand and gravel down to the ground, recalculates heightmap
void CollapseSandGravel(
cChunkDef::BlockTypes & a_BlockTypes, // Block types to read and change
cChunkDef::HeightMap & a_HeightMap // Height map to update by the current data
);
/** For each fluid block:
- if all surroundings are of the same fluid, makes it stationary; otherwise makes it flowing (excl. top)
- all fluid on the chunk's edge is made flowing
*/
void StationarizeFluid(
cChunkDef::BlockTypes & a_BlockTypes, // Block types to read and change
cChunkDef::HeightMap & a_HeightMap, // Height map to read
BLOCKTYPE a_Fluid,
BLOCKTYPE a_StationaryFluid
);
// cFinishGen override:
virtual void GenFinish(cChunkDesc & a_ChunkDesc) override;
} ;
class cFinishGenFluidSprings :
public cFinishGen
{
public:
cFinishGenFluidSprings(int a_Seed, BLOCKTYPE a_Fluid, cIniFile & a_IniFile, const cWorld & a_World);
protected:
cNoise m_Noise;
cProbabDistrib m_HeightDistribution;
BLOCKTYPE m_Fluid;
int m_Chance; ///< Chance, [0..100], that a spring will be generated in a chunk
// cFinishGen override:
virtual void GenFinish(cChunkDesc & a_ChunkDesc) override;
/// Tries to place a spring at the specified coords, checks neighbors. Returns true if successful
bool TryPlaceSpring(cChunkDesc & a_ChunkDesc, int x, int y, int z);
} ;
+390
View File
@@ -0,0 +1,390 @@
// HeiGen.cpp
// Implements the various terrain height generators
#include "Globals.h"
#include "HeiGen.h"
#include "../LinearUpscale.h"
#include "../../iniFile/iniFile.h"
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cHeiGenFlat:
void cHeiGenFlat::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap)
{
for (int i = 0; i < ARRAYCOUNT(a_HeightMap); i++)
{
a_HeightMap[i] = m_Height;
}
}
void cHeiGenFlat::InitializeHeightGen(cIniFile & a_IniFile)
{
m_Height = a_IniFile.GetValueSetI("Generator", "FlatHeight", m_Height);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cHeiGenCache:
cHeiGenCache::cHeiGenCache(cTerrainHeightGen & a_HeiGenToCache, int a_CacheSize) :
m_HeiGenToCache(a_HeiGenToCache),
m_CacheSize(a_CacheSize),
m_CacheOrder(new int[a_CacheSize]),
m_CacheData(new sCacheData[a_CacheSize]),
m_NumHits(0),
m_NumMisses(0),
m_TotalChain(0)
{
for (int i = 0; i < m_CacheSize; i++)
{
m_CacheOrder[i] = i;
m_CacheData[i].m_ChunkX = 0x7fffffff;
m_CacheData[i].m_ChunkZ = 0x7fffffff;
}
}
cHeiGenCache::~cHeiGenCache()
{
delete[] m_CacheData;
delete[] m_CacheOrder;
}
void cHeiGenCache::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap)
{
/*
if (((m_NumHits + m_NumMisses) % 1024) == 10)
{
LOGD("HeiGenCache: %d hits, %d misses, saved %.2f %%", m_NumHits, m_NumMisses, 100.0 * m_NumHits / (m_NumHits + m_NumMisses));
LOGD("HeiGenCache: Avg cache chain length: %.2f", (float)m_TotalChain / m_NumHits);
}
//*/
for (int i = 0; i < m_CacheSize; i++)
{
if (
(m_CacheData[m_CacheOrder[i]].m_ChunkX != a_ChunkX) ||
(m_CacheData[m_CacheOrder[i]].m_ChunkZ != a_ChunkZ)
)
{
continue;
}
// Found it in the cache
int Idx = m_CacheOrder[i];
// Move to front:
for (int j = i; j > 0; j--)
{
m_CacheOrder[j] = m_CacheOrder[j - 1];
}
m_CacheOrder[0] = Idx;
// Use the cached data:
memcpy(a_HeightMap, m_CacheData[Idx].m_HeightMap, sizeof(a_HeightMap));
m_NumHits++;
m_TotalChain += i;
return;
} // for i - cache
// Not in the cache:
m_NumMisses++;
m_HeiGenToCache.GenHeightMap(a_ChunkX, a_ChunkZ, a_HeightMap);
// Insert it as the first item in the MRU order:
int Idx = m_CacheOrder[m_CacheSize - 1];
for (int i = m_CacheSize - 1; i > 0; i--)
{
m_CacheOrder[i] = m_CacheOrder[i - 1];
} // for i - m_CacheOrder[]
m_CacheOrder[0] = Idx;
memcpy(m_CacheData[Idx].m_HeightMap, a_HeightMap, sizeof(a_HeightMap));
m_CacheData[Idx].m_ChunkX = a_ChunkX;
m_CacheData[Idx].m_ChunkZ = a_ChunkZ;
}
void cHeiGenCache::InitializeHeightGen(cIniFile & a_IniFile)
{
m_HeiGenToCache.InitializeHeightGen(a_IniFile);
}
bool cHeiGenCache::GetHeightAt(int a_ChunkX, int a_ChunkZ, int a_RelX, int a_RelZ, HEIGHTTYPE & a_Height)
{
for (int i = 0; i < m_CacheSize; i++)
{
if ((m_CacheData[i].m_ChunkX == a_ChunkX) && (m_CacheData[i].m_ChunkZ == a_ChunkZ))
{
a_Height = cChunkDef::GetHeight(m_CacheData[i].m_HeightMap, a_RelX, a_RelZ);
return true;
}
} // for i - m_CacheData[]
return false;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cHeiGenClassic:
cHeiGenClassic::cHeiGenClassic(int a_Seed) :
m_Seed(a_Seed),
m_Noise(a_Seed)
{
}
float cHeiGenClassic::GetNoise(float x, float y)
{
float oct1 = m_Noise.CubicNoise2D(x * m_HeightFreq1, y * m_HeightFreq1) * m_HeightAmp1;
float oct2 = m_Noise.CubicNoise2D(x * m_HeightFreq2, y * m_HeightFreq2) * m_HeightAmp2;
float oct3 = m_Noise.CubicNoise2D(x * m_HeightFreq3, y * m_HeightFreq3) * m_HeightAmp3;
float height = m_Noise.CubicNoise2D(x * 0.1f, y * 0.1f ) * 2;
float flatness = ((m_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;
}
void cHeiGenClassic::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap)
{
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++)
{
const float xx = (float)(a_ChunkX * cChunkDef::Width + x);
int hei = 64 + (int)(GetNoise(xx * 0.05f, zz * 0.05f) * 16);
if (hei < 10)
{
hei = 10;
}
if (hei > 250)
{
hei = 250;
}
cChunkDef::SetHeight(a_HeightMap, x , z, hei);
} // for x
} // for z
}
void cHeiGenClassic::InitializeHeightGen(cIniFile & a_IniFile)
{
m_HeightFreq1 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightFreq1", 0.1);
m_HeightFreq2 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightFreq2", 1.0);
m_HeightFreq3 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightFreq3", 2.0);
m_HeightAmp1 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightAmp1", 1.0);
m_HeightAmp2 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightAmp2", 0.5);
m_HeightAmp3 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightAmp3", 0.5);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cHeiGenBiomal:
const cHeiGenBiomal::sGenParam cHeiGenBiomal::m_GenParam[biNumBiomes] =
{
/* Fast-changing | Middle-changing | Slow-changing |*/
/* Biome | Freq1 | Amp1 | Freq2 | Amp2 | Freq3 | Amp3 | BaseHeight */
/* biOcean */ { 0.1f, 2.0f, 0.05f, 12.0f, 0.01f, 10.0f, 40},
/* biPlains */ { 0.1f, 1.0f, 0.05f, 1.5f, 0.01f, 4.0f, 68},
/* biDesert */ { 0.1f, 1.0f, 0.05f, 1.5f, 0.01f, 4.0f, 68},
/* biExtremeHills */ { 0.2f, 4.0f, 0.05f, 20.0f, 0.01f, 16.0f, 100},
/* biForest */ { 0.1f, 1.0f, 0.05f, 2.0f, 0.01f, 4.0f, 70},
/* biTaiga */ { 0.1f, 1.0f, 0.05f, 2.0f, 0.01f, 4.0f, 70},
/* biSwampland */ { 0.1f, 1.1f, 0.05f, 1.5f, 0.02f, 2.5f, 61.5},
/* biRiver */ { 0.2f, 0.1f, 0.05f, 0.1f, 0.01f, 0.1f, 56},
/* biNether */ { 0.1f, 0.0f, 0.01f, 0.0f, 0.01f, 0.0f, 0}, // Unused, but must be here due to indexing
/* biSky */ { 0.1f, 0.0f, 0.01f, 0.0f, 0.01f, 0.0f, 0}, // Unused, but must be here due to indexing
/* biFrozenOcean */ { 0.1f, 2.0f, 0.05f, 12.0f, 0.01f, 10.0f, 40},
/* biFrozenRiver */ { 0.2f, 0.1f, 0.05f, 0.1f, 0.01f, 0.1f, 56},
/* biIcePlains */ { 0.1f, 1.0f, 0.05f, 1.5f, 0.01f, 4.0f, 68},
/* biIceMountains */ { 0.2f, 2.0f, 0.05f, 10.0f, 0.01f, 8.0f, 80},
/* biMushroomIsland */ { 0.1f, 2.0f, 0.05f, 8.0f, 0.01f, 6.0f, 80},
/* biMushroomShore */ { 0.1f, 1.0f, 0.05f, 2.0f, 0.01f, 4.0f, 64},
/* biBeach */ { 0.1f, 0.5f, 0.05f, 1.0f, 0.01f, 1.0f, 64},
/* biDesertHills */ { 0.2f, 2.0f, 0.05f, 5.0f, 0.01f, 4.0f, 75},
/* biForestHills */ { 0.2f, 2.0f, 0.05f, 12.0f, 0.01f, 10.0f, 80},
/* biTaigaHills */ { 0.2f, 2.0f, 0.05f, 12.0f, 0.01f, 10.0f, 80},
/* biExtremeHillsEdge */ { 0.2f, 3.0f, 0.05f, 16.0f, 0.01f, 12.0f, 80},
/* biJungle */ { 0.1f, 3.0f, 0.05f, 6.0f, 0.01f, 6.0f, 70},
/* biJungleHills */ { 0.2f, 3.0f, 0.05f, 12.0f, 0.01f, 10.0f, 80},
} ;
void cHeiGenBiomal::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap)
{
// Generate a 3x3 chunk area of biomes around this chunk:
BiomeNeighbors Biomes;
for (int z = -1; z <= 1; z++)
{
for (int x = -1; x <= 1; x++)
{
m_BiomeGen.GenBiomes(a_ChunkX + x, a_ChunkZ + z, Biomes[x + 1][z + 1]);
} // for x
} // for z
/*
_X 2013_04_22:
There's no point in precalculating the entire perlin noise arrays, too many values are calculated uselessly,
resulting in speed DEcrease.
*/
//*
// Linearly interpolate 4x4 blocks of heightmap:
// Must be done on a floating point datatype, else the results are ugly!
const int STEPZ = 4; // Must be a divisor of 16
const int STEPX = 4; // Must be a divisor of 16
NOISE_DATATYPE Height[17 * 17];
for (int z = 0; z < 17; z += STEPZ)
{
for (int x = 0; x < 17; x += STEPX)
{
Height[x + 17 * z] = GetHeightAt(x, z, a_ChunkX, a_ChunkZ, Biomes);
}
}
LinearUpscale2DArrayInPlace(Height, 17, 17, STEPX, STEPZ);
// Copy into the heightmap
for (int z = 0; z < cChunkDef::Width; z++)
{
for (int x = 0; x < cChunkDef::Width; x++)
{
cChunkDef::SetHeight(a_HeightMap, x, z, (int)Height[x + 17 * z]);
}
}
//*/
/*
// For each height, go through neighboring biomes and add up their idea of height:
for (int z = 0; z < cChunkDef::Width; z++)
{
for (int x = 0; x < cChunkDef::Width; x++)
{
cChunkDef::SetHeight(a_HeightMap, x, z, GetHeightAt(x, z, a_ChunkX, a_ChunkZ, Biomes));
} // for x
}
//*/
}
void cHeiGenBiomal::InitializeHeightGen(cIniFile & a_IniFile)
{
// No user-settable params
}
NOISE_DATATYPE cHeiGenBiomal::GetHeightAt(int a_RelX, int a_RelZ, int a_ChunkX, int a_ChunkZ, const cHeiGenBiomal::BiomeNeighbors & a_BiomeNeighbors)
{
// Sum up how many biomes of each type there are in the neighborhood:
int BiomeCounts[biNumBiomes];
memset(BiomeCounts, 0, sizeof(BiomeCounts));
int Sum = 0;
for (int z = -8; z <= 8; z++)
{
int FinalZ = a_RelZ + z + cChunkDef::Width;
int IdxZ = FinalZ / cChunkDef::Width;
int ModZ = FinalZ % cChunkDef::Width;
int WeightZ = 9 - abs(z);
for (int x = -8; x <= 8; x++)
{
int FinalX = a_RelX + x + cChunkDef::Width;
int IdxX = FinalX / cChunkDef::Width;
int ModX = FinalX % cChunkDef::Width;
EMCSBiome Biome = cChunkDef::GetBiome(a_BiomeNeighbors[IdxX][IdxZ], ModX, ModZ);
if ((Biome < 0) || (Biome >= ARRAYCOUNT(BiomeCounts)))
{
continue;
}
int WeightX = 9 - abs(x);
BiomeCounts[Biome] += WeightX + WeightZ;
Sum += WeightX + WeightZ;
} // for x
} // for z
// For each biome type that has a nonzero count, calc its height and add it:
if (Sum > 0)
{
NOISE_DATATYPE Height = 0;
int BlockX = a_ChunkX * cChunkDef::Width + a_RelX;
int BlockZ = a_ChunkZ * cChunkDef::Width + a_RelZ;
for (int i = 0; i < ARRAYCOUNT(BiomeCounts); i++)
{
if (BiomeCounts[i] == 0)
{
continue;
}
NOISE_DATATYPE oct1 = m_Noise.CubicNoise2D(BlockX * m_GenParam[i].m_HeightFreq1, BlockZ * m_GenParam[i].m_HeightFreq1) * m_GenParam[i].m_HeightAmp1;
NOISE_DATATYPE oct2 = m_Noise.CubicNoise2D(BlockX * m_GenParam[i].m_HeightFreq2, BlockZ * m_GenParam[i].m_HeightFreq2) * m_GenParam[i].m_HeightAmp2;
NOISE_DATATYPE oct3 = m_Noise.CubicNoise2D(BlockX * m_GenParam[i].m_HeightFreq3, BlockZ * m_GenParam[i].m_HeightFreq3) * m_GenParam[i].m_HeightAmp3;
Height += BiomeCounts[i] * (m_GenParam[i].m_BaseHeight + oct1 + oct2 + oct3);
}
NOISE_DATATYPE res = Height / Sum;
return std::min((NOISE_DATATYPE)250, std::max(res, (NOISE_DATATYPE)5));
}
// No known biome around? Weird. Return a bogus value:
ASSERT(!"cHeiGenBiomal: Biome sum failed, no known biome around");
return 5;
}
+145
View File
@@ -0,0 +1,145 @@
// HeiGen.h
/*
Interfaces to the various height generators:
- cHeiGenFlat
- cHeiGenClassic
- cHeiGenBiomal
*/
#pragma once
#include "ComposableGenerator.h"
#include "../Noise.h"
class cHeiGenFlat :
public cTerrainHeightGen
{
public:
cHeiGenFlat(void) : m_Height(5) {}
protected:
int m_Height;
// cTerrainHeightGen overrides:
virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override;
virtual void InitializeHeightGen(cIniFile & a_IniFile) override;
} ;
/// A simple cache that stores N most recently generated chunks' heightmaps; N being settable upon creation
class cHeiGenCache :
public cTerrainHeightGen
{
public:
cHeiGenCache(cTerrainHeightGen & a_HeiGenToCache, int a_CacheSize); // Doesn't take ownership of a_HeiGenToCache
~cHeiGenCache();
// cTerrainHeightGen overrides:
virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override;
virtual void InitializeHeightGen(cIniFile & a_IniFile) override;
/// Retrieves height at the specified point in the cache, returns true if found, false if not found
bool GetHeightAt(int a_ChunkX, int a_ChunkZ, int a_RelX, int a_RelZ, HEIGHTTYPE & a_Height);
protected:
cTerrainHeightGen & m_HeiGenToCache;
struct sCacheData
{
int m_ChunkX;
int m_ChunkZ;
cChunkDef::HeightMap m_HeightMap;
} ;
// To avoid moving large amounts of data for the MRU behavior, we MRU-ize indices to an array of the actual data
int m_CacheSize;
int * m_CacheOrder; // MRU-ized order, indices into m_CacheData array
sCacheData * m_CacheData; // m_CacheData[m_CacheOrder[0]] is the most recently used
// Cache statistics
int m_NumHits;
int m_NumMisses;
int m_TotalChain; // Number of cache items walked to get to a hit (only added for hits)
} ;
class cHeiGenClassic :
public cTerrainHeightGen
{
public:
cHeiGenClassic(int a_Seed);
protected:
int m_Seed;
cNoise m_Noise;
float m_HeightFreq1, m_HeightAmp1;
float m_HeightFreq2, m_HeightAmp2;
float m_HeightFreq3, m_HeightAmp3;
float GetNoise(float x, float y);
// cTerrainHeightGen overrides:
virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override;
virtual void InitializeHeightGen(cIniFile & a_IniFile) override;
} ;
class cHeiGenBiomal :
public cTerrainHeightGen
{
public:
cHeiGenBiomal(int a_Seed, cBiomeGen & a_BiomeGen) :
m_Noise(a_Seed),
m_BiomeGen(a_BiomeGen)
{
}
protected:
typedef cChunkDef::BiomeMap BiomeNeighbors[3][3];
cNoise m_Noise;
cBiomeGen & m_BiomeGen;
// Per-biome terrain generator parameters:
struct sGenParam
{
float m_HeightFreq1, m_HeightAmp1;
float m_HeightFreq2, m_HeightAmp2;
float m_HeightFreq3, m_HeightAmp3;
float m_BaseHeight;
} ;
static const sGenParam m_GenParam[biNumBiomes];
// cTerrainHeightGen overrides:
virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override;
virtual void InitializeHeightGen(cIniFile & a_IniFile) override;
NOISE_DATATYPE GetHeightAt(int a_RelX, int a_RelZ, int a_ChunkX, int a_ChunkZ, const BiomeNeighbors & a_BiomeNeighbors);
} ;
File diff suppressed because it is too large Load Diff
+61
View File
@@ -0,0 +1,61 @@
// MineShafts.h
// Declares the cStructGenMineShafts class representing the structure generator for abandoned mineshafts
#pragma once
#include "ComposableGenerator.h"
#include "../Noise.h"
class cStructGenMineShafts :
public cStructureGen
{
public:
cStructGenMineShafts(
int a_Seed, int a_GridSize, int a_MaxSystemSize,
int a_ChanceCorridor, int a_ChanceCrossing, int a_ChanceStaircase
);
virtual ~cStructGenMineShafts();
protected:
friend class cMineShaft;
friend class cMineShaftDirtRoom;
friend class cMineShaftCorridor;
friend class cMineShaftCrossing;
friend class cMineShaftStaircase;
class cMineShaftSystem; // fwd: MineShafts.cpp
typedef std::list<cMineShaftSystem *> cMineShaftSystems;
cNoise m_Noise;
int m_GridSize; ///< Average spacing of the systems
int m_MaxSystemSize; ///< Maximum blcok size of a mineshaft system
int m_ProbLevelCorridor; ///< Probability level of a branch object being the corridor
int m_ProbLevelCrossing; ///< Probability level of a branch object being the crossing, minus Corridor
int m_ProbLevelStaircase; ///< Probability level of a branch object being the staircase, minus Crossing
cMineShaftSystems m_Cache; ///< Cache of the most recently used systems. MoveToFront used.
/// Clears everything from the cache
void ClearCache(void);
/** Returns all systems that *may* intersect the given chunk.
All the systems are valid until the next call to this function (which may delete some of the pointers).
*/
void GetMineShaftSystemsForChunk(int a_ChunkX, int a_ChunkZ, cMineShaftSystems & a_MineShaftSystems);
// cStructureGen overrides:
virtual void GenStructures(cChunkDesc & a_ChunkDesc) override;
} ;
+581
View File
@@ -0,0 +1,581 @@
// Nosie3DGenerator.cpp
// Generates terrain using 3D noise, rather than composing. Is a test.
#include "Globals.h"
#include "Noise3DGenerator.h"
#include "../OSSupport/File.h"
#include "../../iniFile/iniFile.h"
#include "../LinearInterpolation.h"
#include "../LinearUpscale.h"
/*
// Perform an automatic test of upscaling upon program start (use breakpoints to debug):
class Test
{
public:
Test(void)
{
DoTest1();
DoTest2();
}
void DoTest1(void)
{
float In[3 * 3 * 3];
for (int i = 0; i < ARRAYCOUNT(In); i++)
{
In[i] = (float)(i % 5);
}
Debug3DNoise(In, 3, 3, 3, "Upscale3D in");
float Out[17 * 33 * 35];
LinearUpscale3DArray(In, 3, 3, 3, Out, 8, 16, 17);
Debug3DNoise(Out, 17, 33, 35, "Upscale3D test");
}
void DoTest2(void)
{
float In[3 * 3];
for (int i = 0; i < ARRAYCOUNT(In); i++)
{
In[i] = (float)(i % 5);
}
Debug2DNoise(In, 3, 3, "Upscale2D in");
float Out[17 * 33];
LinearUpscale2DArray(In, 3, 3, Out, 8, 16);
Debug2DNoise(Out, 17, 33, "Upscale2D test");
}
} gTest;
//*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cNoise3DGenerator:
cNoise3DGenerator::cNoise3DGenerator(cChunkGenerator & a_ChunkGenerator) :
super(a_ChunkGenerator),
m_Perlin(1000),
m_Cubic(1000)
{
m_Perlin.AddOctave(1, (NOISE_DATATYPE)0.5);
m_Perlin.AddOctave((NOISE_DATATYPE)0.5, 1);
m_Perlin.AddOctave((NOISE_DATATYPE)0.5, 2);
#if 0
// DEBUG: Test the noise generation:
// NOTE: In order to be able to run MCS with this code, you need to increase the default thread stack size
// In MSVC, it is done in Project Settings -> Configuration Properties -> Linker -> System, set Stack reserve size to at least 64M
m_SeaLevel = 62;
m_HeightAmplification = 0;
m_MidPoint = 75;
m_FrequencyX = 4;
m_FrequencyY = 4;
m_FrequencyZ = 4;
m_AirThreshold = 0.5;
const int NumChunks = 4;
NOISE_DATATYPE Noise[NumChunks][cChunkDef::Width * cChunkDef::Width * cChunkDef::Height];
for (int x = 0; x < NumChunks; x++)
{
GenerateNoiseArray(x, 5, Noise[x]);
}
// Save in XY cuts:
cFile f1;
if (f1.Open("Test_XY.grab", cFile::fmWrite))
{
for (int z = 0; z < cChunkDef::Width; z++)
{
for (int y = 0; y < cChunkDef::Height; y++)
{
for (int i = 0; i < NumChunks; i++)
{
int idx = y * cChunkDef::Width + z * cChunkDef::Width * cChunkDef::Height;
unsigned char buf[cChunkDef::Width];
for (int x = 0; x < cChunkDef::Width; x++)
{
buf[x] = (unsigned char)(std::min(256, std::max(0, (int)(128 + 32 * Noise[i][idx++]))));
}
f1.Write(buf, cChunkDef::Width);
}
} // for y
} // for z
} // if (XY file open)
cFile f2;
if (f2.Open("Test_XZ.grab", cFile::fmWrite))
{
for (int y = 0; y < cChunkDef::Height; y++)
{
for (int z = 0; z < cChunkDef::Width; z++)
{
for (int i = 0; i < NumChunks; i++)
{
int idx = y * cChunkDef::Width + z * cChunkDef::Width * cChunkDef::Height;
unsigned char buf[cChunkDef::Width];
for (int x = 0; x < cChunkDef::Width; x++)
{
buf[x] = (unsigned char)(std::min(256, std::max(0, (int)(128 + 32 * Noise[i][idx++]))));
}
f2.Write(buf, cChunkDef::Width);
}
} // for z
} // for y
} // if (XZ file open)
#endif // 0
}
cNoise3DGenerator::~cNoise3DGenerator()
{
// Nothing needed yet
}
void cNoise3DGenerator::Initialize(cWorld * a_World, cIniFile & a_IniFile)
{
m_World = a_World;
// Params:
m_SeaLevel = a_IniFile.GetValueSetI("Generator", "Noise3DSeaLevel", 62);
m_HeightAmplification = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DHeightAmplification", 0);
m_MidPoint = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DMidPoint", 75);
m_FrequencyX = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyX", 8);
m_FrequencyY = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyY", 8);
m_FrequencyZ = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyZ", 8);
m_AirThreshold = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DAirThreshold", 0.5);
}
void cNoise3DGenerator::GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap)
{
for (int i = 0; i < ARRAYCOUNT(a_BiomeMap); i++)
{
a_BiomeMap[i] = biExtremeHills;
}
}
void cNoise3DGenerator::DoGenerate(int a_ChunkX, int a_ChunkZ, cChunkDesc & a_ChunkDesc)
{
NOISE_DATATYPE Noise[17 * 257 * 17];
GenerateNoiseArray(a_ChunkX, a_ChunkZ, Noise);
// Output noise into chunk:
for (int z = 0; z < cChunkDef::Width; z++)
{
for (int y = 0; y < cChunkDef::Height; y++)
{
int idx = z * 17 * 257 + y * 17;
for (int x = 0; x < cChunkDef::Width; x++)
{
NOISE_DATATYPE n = Noise[idx++];
BLOCKTYPE BlockType;
if (n > m_AirThreshold)
{
BlockType = (y > m_SeaLevel) ? E_BLOCK_AIR : E_BLOCK_STATIONARY_WATER;
}
else
{
BlockType = E_BLOCK_STONE;
}
a_ChunkDesc.SetBlockType(x, y, z, BlockType);
}
}
}
UpdateHeightmap(a_ChunkDesc);
ComposeTerrain (a_ChunkDesc);
}
void cNoise3DGenerator::GenerateNoiseArray(int a_ChunkX, int a_ChunkZ, NOISE_DATATYPE * a_OutNoise)
{
NOISE_DATATYPE NoiseO[DIM_X * DIM_Y * DIM_Z]; // Output for the Perlin noise
NOISE_DATATYPE NoiseW[DIM_X * DIM_Y * DIM_Z]; // Workspace that the noise calculation can use and trash
// Our noise array has different layout, XZY, instead of regular chunk's XYZ, that's why the coords are "renamed"
NOISE_DATATYPE StartX = ((NOISE_DATATYPE)(a_ChunkX * cChunkDef::Width)) / m_FrequencyX;
NOISE_DATATYPE EndX = ((NOISE_DATATYPE)((a_ChunkX + 1) * cChunkDef::Width) - 1) / m_FrequencyX;
NOISE_DATATYPE StartZ = ((NOISE_DATATYPE)(a_ChunkZ * cChunkDef::Width)) / m_FrequencyZ;
NOISE_DATATYPE EndZ = ((NOISE_DATATYPE)((a_ChunkZ + 1) * cChunkDef::Width) - 1) / m_FrequencyZ;
NOISE_DATATYPE StartY = 0;
NOISE_DATATYPE EndY = ((NOISE_DATATYPE)256) / m_FrequencyY;
m_Perlin.Generate3D(NoiseO, DIM_X, DIM_Y, DIM_Z, StartX, EndX, StartY, EndY, StartZ, EndZ, NoiseW);
// DEBUG: Debug3DNoise(NoiseO, DIM_X, DIM_Y, DIM_Z, Printf("Chunk_%d_%d_orig", a_ChunkX, a_ChunkZ));
// Precalculate a "height" array:
NOISE_DATATYPE Height[DIM_X * DIM_Z]; // Output for the cubic noise heightmap ("source")
m_Cubic.Generate2D(Height, DIM_X, DIM_Z, StartX / 25, EndX / 25, StartZ / 25, EndZ / 25);
for (int i = 0; i < ARRAYCOUNT(Height); i++)
{
Height[i] = abs(Height[i]) * m_HeightAmplification + 1;
}
// Modify the noise by height data:
for (int y = 0; y < DIM_Y; y++)
{
NOISE_DATATYPE AddHeight = (y * UPSCALE_Y - m_MidPoint) / 20;
AddHeight *= AddHeight * AddHeight;
for (int z = 0; z < DIM_Z; z++)
{
NOISE_DATATYPE * CurRow = &(NoiseO[y * DIM_X + z * DIM_X * DIM_Y]);
for (int x = 0; x < DIM_X; x++)
{
CurRow[x] += AddHeight / Height[x + DIM_X * z];
}
}
}
// DEBUG: Debug3DNoise(NoiseO, DIM_X, DIM_Y, DIM_Z, Printf("Chunk_%d_%d_hei", a_ChunkX, a_ChunkZ));
// Upscale the Perlin noise into full-blown chunk dimensions:
LinearUpscale3DArray(
NoiseO, DIM_X, DIM_Y, DIM_Z,
a_OutNoise, UPSCALE_X, UPSCALE_Y, UPSCALE_Z
);
// DEBUG: Debug3DNoise(a_OutNoise, 17, 257, 17, Printf("Chunk_%d_%d_lerp", a_ChunkX, a_ChunkZ));
}
void cNoise3DGenerator::UpdateHeightmap(cChunkDesc & a_ChunkDesc)
{
for (int z = 0; z < cChunkDef::Width; z++)
{
for (int x = 0; x < cChunkDef::Width; x++)
{
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 x
} // for z
}
void cNoise3DGenerator::ComposeTerrain(cChunkDesc & a_ChunkDesc)
{
// Make basic terrain composition:
for (int z = 0; z < cChunkDef::Width; z++)
{
for (int x = 0; x < cChunkDef::Width; x++)
{
int LastAir = a_ChunkDesc.GetHeight(x, z) + 1;
bool HasHadWater = false;
for (int y = LastAir - 1; y > 0; y--)
{
switch (a_ChunkDesc.GetBlockType(x, y, z))
{
case E_BLOCK_AIR:
{
LastAir = y;
break;
}
case E_BLOCK_STONE:
{
if (LastAir - y > 3)
{
break;
}
if (HasHadWater)
{
a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_SAND);
}
else
{
a_ChunkDesc.SetBlockType(x, y, z, (LastAir == y + 1) ? E_BLOCK_GRASS : E_BLOCK_DIRT);
}
break;
}
case E_BLOCK_STATIONARY_WATER:
{
LastAir = y;
HasHadWater = true;
break;
}
} // switch (GetBlockType())
} // for y
a_ChunkDesc.SetBlockType(x, 0, z, E_BLOCK_BEDROCK);
} // for x
} // for z
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cNoise3DComposable:
cNoise3DComposable::cNoise3DComposable(int a_Seed) :
m_Noise1(a_Seed + 1000),
m_Noise2(a_Seed + 2000),
m_Noise3(a_Seed + 3000)
{
}
void cNoise3DComposable::Initialize(cIniFile & a_IniFile)
{
// Params:
m_SeaLevel = a_IniFile.GetValueSetI("Generator", "Noise3DSeaLevel", 62);
m_HeightAmplification = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DHeightAmplification", 0);
m_MidPoint = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DMidPoint", 75);
m_FrequencyX = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyX", 10);
m_FrequencyY = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyY", 10);
m_FrequencyZ = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyZ", 10);
m_AirThreshold = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DAirThreshold", 0.5);
}
void cNoise3DComposable::GenerateNoiseArrayIfNeeded(int a_ChunkX, int a_ChunkZ)
{
if ((a_ChunkX == m_LastChunkX) && (a_ChunkZ == m_LastChunkZ))
{
// The noise for this chunk is already generated in m_Noise
return;
}
m_LastChunkX = a_ChunkX;
m_LastChunkZ = a_ChunkZ;
// Upscaling parameters:
const int UPSCALE_X = 8;
const int UPSCALE_Y = 4;
const int UPSCALE_Z = 8;
const int DIM_X = 1 + cChunkDef::Width / UPSCALE_X;
const int DIM_Y = 1 + cChunkDef::Height / UPSCALE_Y;
const int DIM_Z = 1 + cChunkDef::Width / UPSCALE_Z;
// Precalculate a "height" array:
NOISE_DATATYPE Height[17 * 17]; // x + 17 * z
for (int z = 0; z < 17; z += UPSCALE_Z)
{
NOISE_DATATYPE NoiseZ = ((NOISE_DATATYPE)(a_ChunkZ * cChunkDef::Width + z)) / m_FrequencyZ;
for (int x = 0; x < 17; x += UPSCALE_X)
{
NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(a_ChunkX * cChunkDef::Width + x)) / m_FrequencyX;
NOISE_DATATYPE val = abs(m_Noise1.CubicNoise2D(NoiseX / 5, NoiseZ / 5)) * m_HeightAmplification + 1;
Height[x + 17 * z] = val * val * val;
}
}
int idx = 0;
for (int y = 0; y < 257; y += UPSCALE_Y)
{
NOISE_DATATYPE NoiseY = ((NOISE_DATATYPE)y) / m_FrequencyY;
NOISE_DATATYPE AddHeight = (y - m_MidPoint) / 20;
AddHeight *= AddHeight * AddHeight;
NOISE_DATATYPE * CurFloor = &(m_NoiseArray[y * 17 * 17]);
for (int z = 0; z < 17; z += UPSCALE_Z)
{
NOISE_DATATYPE NoiseZ = ((NOISE_DATATYPE)(a_ChunkZ * cChunkDef::Width + z)) / m_FrequencyZ;
for (int x = 0; x < 17; x += UPSCALE_X)
{
NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(a_ChunkX * cChunkDef::Width + x)) / m_FrequencyX;
CurFloor[x + 17 * z] =
m_Noise1.CubicNoise3D(NoiseX, NoiseY, NoiseZ) * (NOISE_DATATYPE)0.5 +
m_Noise2.CubicNoise3D(NoiseX / 2, NoiseY / 2, NoiseZ / 2) +
m_Noise3.CubicNoise3D(NoiseX / 4, NoiseY / 4, NoiseZ / 4) * 2 +
AddHeight / Height[x + 17 * z];
}
}
// Linear-interpolate this XZ floor:
LinearUpscale2DArrayInPlace(CurFloor, 17, 17, UPSCALE_X, UPSCALE_Z);
}
// Finish the 3D linear interpolation by interpolating between each XZ-floors on the Y axis
for (int y = 1; y < cChunkDef::Height; y++)
{
if ((y % UPSCALE_Y) == 0)
{
// This is the interpolation source floor, already calculated
continue;
}
int LoFloorY = (y / UPSCALE_Y) * UPSCALE_Y;
int HiFloorY = LoFloorY + UPSCALE_Y;
NOISE_DATATYPE * LoFloor = &(m_NoiseArray[LoFloorY * 17 * 17]);
NOISE_DATATYPE * HiFloor = &(m_NoiseArray[HiFloorY * 17 * 17]);
NOISE_DATATYPE * CurFloor = &(m_NoiseArray[y * 17 * 17]);
NOISE_DATATYPE Ratio = ((NOISE_DATATYPE)(y % UPSCALE_Y)) / UPSCALE_Y;
int idx = 0;
for (int z = 0; z < cChunkDef::Width; z++)
{
for (int x = 0; x < cChunkDef::Width; x++)
{
CurFloor[idx] = LoFloor[idx] + (HiFloor[idx] - LoFloor[idx]) * Ratio;
idx += 1;
}
idx += 1; // Skipping one X column
}
}
// The noise array is now fully interpolated
/*
// DEBUG: Output two images of the array, sliced by XY and XZ:
cFile f1;
if (f1.Open(Printf("Chunk_%d_%d_XY.raw", a_ChunkX, a_ChunkZ), cFile::fmWrite))
{
for (int z = 0; z < cChunkDef::Width; z++)
{
for (int y = 0; y < cChunkDef::Height; y++)
{
int idx = y * 17 * 17 + z * 17;
unsigned char buf[16];
for (int x = 0; x < cChunkDef::Width; x++)
{
buf[x] = (unsigned char)(std::min(256, std::max(0, (int)(128 + 128 * m_Noise[idx++]))));
}
f1.Write(buf, 16);
} // for y
} // for z
} // if (XY file open)
cFile f2;
if (f2.Open(Printf("Chunk_%d_%d_XZ.raw", a_ChunkX, a_ChunkZ), cFile::fmWrite))
{
for (int y = 0; y < cChunkDef::Height; y++)
{
for (int z = 0; z < cChunkDef::Width; z++)
{
int idx = y * 17 * 17 + z * 17;
unsigned char buf[16];
for (int x = 0; x < cChunkDef::Width; x++)
{
buf[x] = (unsigned char)(std::min(256, std::max(0, (int)(128 + 128 * m_Noise[idx++]))));
}
f2.Write(buf, 16);
} // for z
} // for y
} // if (XZ file open)
*/
}
void cNoise3DComposable::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap)
{
GenerateNoiseArrayIfNeeded(a_ChunkX, a_ChunkZ);
for (int z = 0; z < cChunkDef::Width; z++)
{
for (int x = 0; x < cChunkDef::Width; x++)
{
cChunkDef::SetHeight(a_HeightMap, x, z, m_SeaLevel);
for (int y = cChunkDef::Height - 1; y > m_SeaLevel; y--)
{
if (m_NoiseArray[y * 17 * 17 + z * 17 + x] <= m_AirThreshold)
{
cChunkDef::SetHeight(a_HeightMap, x, z, y);
break;
}
} // for y
} // for x
} // for z
}
void cNoise3DComposable::ComposeTerrain(cChunkDesc & a_ChunkDesc)
{
GenerateNoiseArrayIfNeeded(a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ());
a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0);
// Make basic terrain composition:
for (int z = 0; z < cChunkDef::Width; z++)
{
for (int x = 0; x < cChunkDef::Width; x++)
{
int LastAir = a_ChunkDesc.GetHeight(x, z) + 1;
bool HasHadWater = false;
for (int y = LastAir; y < m_SeaLevel; y++)
{
a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_STATIONARY_WATER);
}
for (int y = LastAir - 1; y > 0; y--)
{
if (m_NoiseArray[x + 17 * z + 17 * 17 * y] > m_AirThreshold)
{
// "air" part
LastAir = y;
if (y < m_SeaLevel)
{
a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_STATIONARY_WATER);
HasHadWater = true;
}
continue;
}
// "ground" part:
if (LastAir - y > 4)
{
a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_STONE);
continue;
}
if (HasHadWater)
{
a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_SAND);
}
else
{
a_ChunkDesc.SetBlockType(x, y, z, (LastAir == y + 1) ? E_BLOCK_GRASS : E_BLOCK_DIRT);
}
} // for y
a_ChunkDesc.SetBlockType(x, 0, z, E_BLOCK_BEDROCK);
} // for x
} // for z
}
+106
View File
@@ -0,0 +1,106 @@
// Noise3DGenerator.h
// Generates terrain using 3D noise, rather than composing. Is a test.
#pragma once
#include "ComposableGenerator.h"
#include "../Noise.h"
class cNoise3DGenerator :
public cChunkGenerator::cGenerator
{
typedef cChunkGenerator::cGenerator super;
public:
cNoise3DGenerator(cChunkGenerator & a_ChunkGenerator);
virtual ~cNoise3DGenerator();
virtual void Initialize(cWorld * a_World, cIniFile & a_IniFile) override;
virtual void GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override;
virtual void DoGenerate(int a_ChunkX, int a_ChunkZ, cChunkDesc & a_ChunkDesc) override;
protected:
// Linear interpolation step sizes, must be divisors of cChunkDef::Width and cChunkDef::Height, respectively:
static const int UPSCALE_X = 8;
static const int UPSCALE_Y = 4;
static const int UPSCALE_Z = 8;
// Linear interpolation buffer dimensions, calculated from the step sizes:
static const int DIM_X = 1 + cChunkDef::Width / UPSCALE_X;
static const int DIM_Y = 1 + cChunkDef::Height / UPSCALE_Y;
static const int DIM_Z = 1 + cChunkDef::Width / UPSCALE_Z;
cPerlinNoise m_Perlin; // The base 3D noise source for the actual composition
cCubicNoise m_Cubic; // The noise used for heightmap directing
int m_SeaLevel;
NOISE_DATATYPE m_HeightAmplification;
NOISE_DATATYPE m_MidPoint; // Where the vertical "center" of the noise should be
NOISE_DATATYPE m_FrequencyX;
NOISE_DATATYPE m_FrequencyY;
NOISE_DATATYPE m_FrequencyZ;
NOISE_DATATYPE m_AirThreshold;
/// Generates the 3D noise array used for terrain generation; a_Noise is of ChunkData-size
void GenerateNoiseArray(int a_ChunkX, int a_ChunkZ, NOISE_DATATYPE * a_Noise);
/// Updates heightmap based on the chunk's contents
void UpdateHeightmap(cChunkDesc & a_ChunkDesc);
/// Composes terrain - adds dirt, grass and sand
void ComposeTerrain(cChunkDesc & a_ChunkDesc);
} ;
class cNoise3DComposable :
public cTerrainHeightGen,
public cTerrainCompositionGen
{
public:
cNoise3DComposable(int a_Seed);
void Initialize(cIniFile & a_IniFile);
protected:
cNoise m_Noise1;
cNoise m_Noise2;
cNoise m_Noise3;
int m_SeaLevel;
NOISE_DATATYPE m_HeightAmplification;
NOISE_DATATYPE m_MidPoint; // Where the vertical "center" of the noise should be
NOISE_DATATYPE m_FrequencyX;
NOISE_DATATYPE m_FrequencyY;
NOISE_DATATYPE m_FrequencyZ;
NOISE_DATATYPE m_AirThreshold;
int m_LastChunkX;
int m_LastChunkZ;
NOISE_DATATYPE m_NoiseArray[17 * 17 * 257]; // x + 17 * z + 17 * 17 * y
/// Generates the 3D noise array used for terrain generation, unless the LastChunk coords are equal to coords given
void GenerateNoiseArrayIfNeeded(int a_ChunkX, int a_ChunkZ);
// cTerrainHeightGen overrides:
virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override;
// cTerrainCompositionGen overrides:
virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override;
} ;
+531
View File
@@ -0,0 +1,531 @@
// Ravines.cpp
// Implements the cStructGenRavines class representing the ravine structure generator
#include "Globals.h"
#include "Ravines.h"
/// How many ravines in each direction are generated for a given chunk. Must be an even number
static const int NEIGHBORHOOD_SIZE = 8;
static const int NUM_RAVINE_POINTS = 4;
struct cRavDefPoint
{
int m_BlockX;
int m_BlockZ;
int m_Radius;
int m_Top;
int m_Bottom;
cRavDefPoint(int a_BlockX, int a_BlockZ, int a_Radius, int a_Top, int a_Bottom) :
m_BlockX(a_BlockX),
m_BlockZ(a_BlockZ),
m_Radius(a_Radius),
m_Top (a_Top),
m_Bottom(a_Bottom)
{
}
} ;
typedef std::vector<cRavDefPoint> cRavDefPoints;
class cStructGenRavines::cRavine
{
cRavDefPoints m_Points;
/// Generates the shaping defpoints for the ravine, based on the ravine block coords and noise
void GenerateBaseDefPoints(int a_BlockX, int a_BlockZ, int a_Size, cNoise & a_Noise);
/// Refines (adds and smooths) defpoints from a_Src into a_Dst
void RefineDefPoints(const cRavDefPoints & a_Src, cRavDefPoints & a_Dst);
/// Does one round of smoothing, two passes of RefineDefPoints()
void Smooth(void);
/// Linearly interpolates the points so that the maximum distance between two neighbors is max 1 block
void FinishLinear(void);
public:
// Coords for which the ravine was generated (not necessarily the center)
int m_BlockX;
int m_BlockZ;
cRavine(int a_BlockX, int a_BlockZ, int a_Size, cNoise & a_Noise);
/// Carves the ravine into the chunk specified
void ProcessChunk(
int a_ChunkX, int a_ChunkZ,
cChunkDef::BlockTypes & a_BlockTypes,
cChunkDef::HeightMap & a_HeightMap
);
#ifdef _DEBUG
/// Exports itself as a SVG line definition
AString ExportAsSVG(int a_Color, int a_OffsetX = 0, int a_OffsetZ = 0) const;
#endif // _DEBUG
} ;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cStructGenRavines:
cStructGenRavines::cStructGenRavines(int a_Seed, int a_Size) :
m_Noise(a_Seed),
m_Size(a_Size)
{
}
cStructGenRavines::~cStructGenRavines()
{
ClearCache();
}
void cStructGenRavines::ClearCache(void)
{
for (cRavines::const_iterator itr = m_Cache.begin(), end = m_Cache.end(); itr != end; ++itr)
{
delete *itr;
} // for itr - m_Cache[]
m_Cache.clear();
}
void cStructGenRavines::GenStructures(cChunkDesc & a_ChunkDesc)
{
int ChunkX = a_ChunkDesc.GetChunkX();
int ChunkZ = a_ChunkDesc.GetChunkZ();
cRavines Ravines;
GetRavinesForChunk(ChunkX, ChunkZ, Ravines);
for (cRavines::const_iterator itr = Ravines.begin(), end = Ravines.end(); itr != end; ++itr)
{
(*itr)->ProcessChunk(ChunkX, ChunkZ, a_ChunkDesc.GetBlockTypes(), a_ChunkDesc.GetHeightMap());
} // for itr - Ravines[]
}
void cStructGenRavines::GetRavinesForChunk(int a_ChunkX, int a_ChunkZ, cStructGenRavines::cRavines & a_Ravines)
{
int BaseX = a_ChunkX * cChunkDef::Width / m_Size;
int BaseZ = a_ChunkZ * cChunkDef::Width / m_Size;
if (BaseX < 0)
{
--BaseX;
}
if (BaseZ < 0)
{
--BaseZ;
}
BaseX -= 4;
BaseZ -= 4;
// Walk the cache, move each ravine that we want into a_Ravines:
int StartX = BaseX * m_Size;
int EndX = (BaseX + NEIGHBORHOOD_SIZE + 1) * m_Size;
int StartZ = BaseZ * m_Size;
int EndZ = (BaseZ + NEIGHBORHOOD_SIZE + 1) * m_Size;
for (cRavines::iterator itr = m_Cache.begin(), end = m_Cache.end(); itr != end;)
{
if (
((*itr)->m_BlockX >= StartX) && ((*itr)->m_BlockX < EndX) &&
((*itr)->m_BlockZ >= StartZ) && ((*itr)->m_BlockZ < EndZ)
)
{
// want
a_Ravines.push_back(*itr);
itr = m_Cache.erase(itr);
}
else
{
// don't want
++itr;
}
} // for itr - m_Cache[]
for (int x = 0; x < NEIGHBORHOOD_SIZE; x++)
{
int RealX = (BaseX + x) * m_Size;
for (int z = 0; z < NEIGHBORHOOD_SIZE; z++)
{
int RealZ = (BaseZ + z) * m_Size;
bool Found = false;
for (cRavines::const_iterator itr = a_Ravines.begin(), end = a_Ravines.end(); itr != end; ++itr)
{
if (((*itr)->m_BlockX == RealX) && ((*itr)->m_BlockZ == RealZ))
{
Found = true;
break;
}
}
if (!Found)
{
a_Ravines.push_back(new cRavine(RealX, RealZ, m_Size, m_Noise));
}
}
}
// Copy a_Ravines into m_Cache to the beginning:
cRavines RavinesCopy(a_Ravines);
m_Cache.splice(m_Cache.begin(), RavinesCopy, RavinesCopy.begin(), RavinesCopy.end());
// Trim the cache if it's too long:
if (m_Cache.size() > 100)
{
cRavines::iterator itr = m_Cache.begin();
std::advance(itr, 100);
for (cRavines::iterator end = m_Cache.end(); itr != end; ++itr)
{
delete *itr;
}
itr = m_Cache.begin();
std::advance(itr, 100);
m_Cache.erase(itr, m_Cache.end());
}
/*
#ifdef _DEBUG
// DEBUG: Export as SVG into a file specific for the chunk, for visual verification:
AString SVG;
SVG.append("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"1024\" height = \"1024\">\n");
for (cRavines::const_iterator itr = a_Ravines.begin(), end = a_Ravines.end(); itr != end; ++itr)
{
SVG.append((*itr)->ExportAsSVG(0, 512, 512));
}
SVG.append("</svg>\n");
AString fnam;
Printf(fnam, "ravines\\%03d_%03d.svg", a_ChunkX, a_ChunkZ);
cFile File(fnam, cFile::fmWrite);
File.Write(SVG.c_str(), SVG.size());
#endif // _DEBUG
//*/
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cStructGenRavines::cRavine
cStructGenRavines::cRavine::cRavine(int a_BlockX, int a_BlockZ, int a_Size, cNoise & a_Noise) :
m_BlockX(a_BlockX),
m_BlockZ(a_BlockZ)
{
// Calculate the ravine shape-defining points:
GenerateBaseDefPoints(a_BlockX, a_BlockZ, a_Size, a_Noise);
// Smooth the ravine. A two passes are needed:
Smooth();
Smooth();
// Linearly interpolate the neighbors so that they're close enough together:
FinishLinear();
}
void cStructGenRavines::cRavine::GenerateBaseDefPoints(int a_BlockX, int a_BlockZ, int a_Size, cNoise & a_Noise)
{
// Modify the size slightly to have different-sized ravines (1/2 to 1/1 of a_Size):
a_Size = (512 + ((a_Noise.IntNoise3DInt(19 * a_BlockX, 11 * a_BlockZ, a_BlockX + a_BlockZ) / 17) % 512)) * a_Size / 1024;
// The complete offset of the ravine from its cellpoint, up to 2 * a_Size in each direction
int OffsetX = (((a_Noise.IntNoise3DInt(50 * a_BlockX, 30 * a_BlockZ, 0) / 9) % (2 * a_Size)) + ((a_Noise.IntNoise3DInt(30 * a_BlockX, 50 * m_BlockZ, 1000) / 7) % (2 * a_Size)) - 2 * a_Size) / 2;
int OffsetZ = (((a_Noise.IntNoise3DInt(50 * a_BlockX, 30 * a_BlockZ, 2000) / 7) % (2 * a_Size)) + ((a_Noise.IntNoise3DInt(30 * a_BlockX, 50 * m_BlockZ, 3000) / 9) % (2 * a_Size)) - 2 * a_Size) / 2;
int CenterX = a_BlockX + OffsetX;
int CenterZ = a_BlockZ + OffsetZ;
// Get the base angle in which the ravine "axis" goes:
float Angle = (float)(((float)((a_Noise.IntNoise3DInt(20 * a_BlockX, 70 * a_BlockZ, 6000) / 9) % 16384)) / 16384.0 * 3.141592653);
float xc = sin(Angle);
float zc = cos(Angle);
// Calculate the definition points and radii:
int MaxRadius = (int)(sqrt(12.0 + ((a_Noise.IntNoise2DInt(61 * a_BlockX, 97 * a_BlockZ) / 13) % a_Size) / 16));
int Top = 32 + ((a_Noise.IntNoise2DInt(13 * a_BlockX, 17 * a_BlockZ) / 23) % 32);
int Bottom = 5 + ((a_Noise.IntNoise2DInt(17 * a_BlockX, 29 * a_BlockZ) / 13) % 32);
int Mid = (Top + Bottom) / 2;
int PointX = CenterX - (int)(xc * a_Size / 2);
int PointZ = CenterZ - (int)(zc * a_Size / 2);
m_Points.push_back(cRavDefPoint(PointX, PointZ, 0, (Mid + Top) / 2, (Mid + Bottom) / 2));
for (int i = 1; i < NUM_RAVINE_POINTS - 1; i++)
{
int LineX = CenterX + (int)(xc * a_Size * (i - NUM_RAVINE_POINTS / 2) / NUM_RAVINE_POINTS);
int LineZ = CenterZ + (int)(zc * a_Size * (i - NUM_RAVINE_POINTS / 2) / NUM_RAVINE_POINTS);
// Amplitude is the amount of blocks that this point is away from the ravine "axis"
int Amplitude = (a_Noise.IntNoise3DInt(70 * a_BlockX, 20 * a_BlockZ + 31 * i, 10000 * i) / 9) % a_Size;
Amplitude = Amplitude / 4 - a_Size / 8; // Amplitude is in interval [-a_Size / 4, a_Size / 4]
int PointX = LineX + (int)(zc * Amplitude);
int PointZ = LineZ - (int)(xc * Amplitude);
int Radius = MaxRadius - abs(i - NUM_RAVINE_POINTS / 2); // TODO: better radius function
int ThisTop = Top + ((a_Noise.IntNoise3DInt(7 * a_BlockX, 19 * a_BlockZ, i * 31) / 13) % 8) - 4;
int ThisBottom = Bottom + ((a_Noise.IntNoise3DInt(19 * a_BlockX, 7 * a_BlockZ, i * 31) / 13) % 8) - 4;
m_Points.push_back(cRavDefPoint(PointX, PointZ, Radius, ThisTop, ThisBottom));
} // for i - m_Points[]
PointX = CenterX + (int)(xc * a_Size / 2);
PointZ = CenterZ + (int)(zc * a_Size / 2);
m_Points.push_back(cRavDefPoint(PointX, PointZ, 0, Mid, Mid));
}
void cStructGenRavines::cRavine::RefineDefPoints(const cRavDefPoints & a_Src, cRavDefPoints & a_Dst)
{
// Smoothing: for each line segment, add points on its 1/4 lengths
int Num = a_Src.size() - 2; // this many intermediary points
a_Dst.clear();
a_Dst.reserve(Num * 2 + 2);
cRavDefPoints::const_iterator itr = a_Src.begin() + 1;
a_Dst.push_back(a_Src.front());
int PrevX = a_Src.front().m_BlockX;
int PrevZ = a_Src.front().m_BlockZ;
int PrevR = a_Src.front().m_Radius;
int PrevT = a_Src.front().m_Top;
int PrevB = a_Src.front().m_Bottom;
for (int i = 0; i <= Num; ++i, ++itr)
{
int dx = itr->m_BlockX - PrevX;
int dz = itr->m_BlockZ - PrevZ;
if (abs(dx) + abs(dz) < 4)
{
// Too short a segment to smooth-subdivide into quarters
continue;
}
int dr = itr->m_Radius - PrevR;
int dt = itr->m_Top - PrevT;
int db = itr->m_Bottom - PrevB;
int Rad1 = std::max(PrevR + 1 * dr / 4, 1);
int Rad2 = std::max(PrevR + 3 * dr / 4, 1);
a_Dst.push_back(cRavDefPoint(PrevX + 1 * dx / 4, PrevZ + 1 * dz / 4, Rad1, PrevT + 1 * dt / 4, PrevB + 1 * db / 4));
a_Dst.push_back(cRavDefPoint(PrevX + 3 * dx / 4, PrevZ + 3 * dz / 4, Rad2, PrevT + 3 * dt / 4, PrevB + 3 * db / 4));
PrevX = itr->m_BlockX;
PrevZ = itr->m_BlockZ;
PrevR = itr->m_Radius;
PrevT = itr->m_Top;
PrevB = itr->m_Bottom;
}
a_Dst.push_back(a_Src.back());
}
void cStructGenRavines::cRavine::Smooth(void)
{
cRavDefPoints Pts;
RefineDefPoints(m_Points, Pts); // Refine m_Points -> Pts
RefineDefPoints(Pts, m_Points); // Refine Pts -> m_Points
}
void cStructGenRavines::cRavine::FinishLinear(void)
{
// For each segment, use Bresenham's line algorithm to draw a "line" of defpoints
// _X 2012_07_20: I tried modifying this algorithm to produce "thick" lines (only one coord change per point)
// But the results were about the same as the original, so I disposed of it again - no need to use twice the count of points
cRavDefPoints Pts;
std::swap(Pts, m_Points);
m_Points.reserve(Pts.size() * 3);
int PrevX = Pts.front().m_BlockX;
int PrevZ = Pts.front().m_BlockZ;
for (cRavDefPoints::const_iterator itr = Pts.begin() + 1, end = Pts.end(); itr != end; ++itr)
{
int x1 = itr->m_BlockX;
int z1 = itr->m_BlockZ;
int dx = abs(x1 - PrevX);
int dz = abs(z1 - PrevZ);
int sx = (PrevX < x1) ? 1 : -1;
int sz = (PrevZ < z1) ? 1 : -1;
int err = dx - dz;
int R = itr->m_Radius;
int T = itr->m_Top;
int B = itr->m_Bottom;
while (true)
{
m_Points.push_back(cRavDefPoint(PrevX, PrevZ, R, T, B));
if ((PrevX == x1) && (PrevZ == z1))
{
break;
}
int e2 = 2 * err;
if (e2 > -dz)
{
err -= dz;
PrevX += sx;
}
if (e2 < dx)
{
err += dx;
PrevZ += sz;
}
} // while (true)
} // for itr
}
#ifdef _DEBUG
AString cStructGenRavines::cRavine::ExportAsSVG(int a_Color, int a_OffsetX, int a_OffsetZ) const
{
AString SVG;
AppendPrintf(SVG, "<path style=\"fill:none;stroke:#%06x;stroke-width:1px;\"\nd=\"", a_Color);
char Prefix = 'M'; // The first point needs "M" prefix, all the others need "L"
for (cRavDefPoints::const_iterator itr = m_Points.begin(); itr != m_Points.end(); ++itr)
{
AppendPrintf(SVG, "%c %d,%d ", Prefix, a_OffsetX + itr->m_BlockX, a_OffsetZ + itr->m_BlockZ);
Prefix = 'L';
}
SVG.append("\"/>\n");
// Base point highlight:
AppendPrintf(SVG, "<path style=\"fill:none;stroke:#ff0000;stroke-width:1px;\"\nd=\"M %d,%d L %d,%d\"/>\n",
a_OffsetX + m_BlockX - 5, a_OffsetZ + m_BlockZ, a_OffsetX + m_BlockX + 5, a_OffsetZ + m_BlockZ
);
AppendPrintf(SVG, "<path style=\"fill:none;stroke:#ff0000;stroke-width:1px;\"\nd=\"M %d,%d L %d,%d\"/>\n",
a_OffsetX + m_BlockX, a_OffsetZ + m_BlockZ - 5, a_OffsetX + m_BlockX, a_OffsetZ + m_BlockZ + 5
);
// A gray line from the base point to the first point of the ravine, for identification:
AppendPrintf(SVG, "<path style=\"fill:none;stroke:#cfcfcf;stroke-width:1px;\"\nd=\"M %d,%d L %d,%d\"/>\n",
a_OffsetX + m_BlockX, a_OffsetZ + m_BlockZ, a_OffsetX + m_Points.front().m_BlockX, a_OffsetZ + m_Points.front().m_BlockZ
);
// Offset guides:
if (a_OffsetX > 0)
{
AppendPrintf(SVG, "<path style=\"fill:none;stroke:#0000ff;stroke-width:1px;\"\nd=\"M %d,0 L %d,1024\"/>\n",
a_OffsetX, a_OffsetX
);
}
if (a_OffsetZ > 0)
{
AppendPrintf(SVG, "<path style=\"fill:none;stroke:#0000ff;stroke-width:1px;\"\nd=\"M 0,%d L 1024,%d\"/>\n",
a_OffsetZ, a_OffsetZ
);
}
return SVG;
}
#endif // _DEBUG
void cStructGenRavines::cRavine::ProcessChunk(
int a_ChunkX, int a_ChunkZ,
cChunkDef::BlockTypes & a_BlockTypes,
cChunkDef::HeightMap & a_HeightMap
)
{
int BlockStartX = a_ChunkX * cChunkDef::Width;
int BlockStartZ = a_ChunkZ * cChunkDef::Width;
int BlockEndX = BlockStartX + cChunkDef::Width;
int BlockEndZ = BlockStartZ + cChunkDef::Width;
for (cRavDefPoints::const_iterator itr = m_Points.begin(), end = m_Points.end(); itr != end; ++itr)
{
if (
(itr->m_BlockX + itr->m_Radius < BlockStartX) ||
(itr->m_BlockX - itr->m_Radius > BlockEndX) ||
(itr->m_BlockZ + itr->m_Radius < BlockStartZ) ||
(itr->m_BlockZ - itr->m_Radius > BlockEndZ)
)
{
// Cannot intersect, bail out early
continue;
}
// Carve out a cylinder around the xz point, m_Radius in diameter, from Bottom to Top:
int RadiusSq = itr->m_Radius * itr->m_Radius; // instead of doing sqrt for each distance, we do sqr of the radius
int DifX = BlockStartX - itr->m_BlockX; // substitution for faster calc
int DifZ = BlockStartZ - itr->m_BlockZ; // substitution for faster calc
for (int x = 0; x < cChunkDef::Width; x++) for (int z = 0; z < cChunkDef::Width; z++)
{
#ifdef _DEBUG
// DEBUG: Make the ravine shapepoints visible on a single layer (so that we can see with Minutor what's going on)
if ((DifX + x == 0) && (DifZ + z == 0))
{
cChunkDef::SetBlock(a_BlockTypes, x, 4, z, E_BLOCK_LAPIS_ORE);
}
#endif // _DEBUG
int DistSq = (DifX + x) * (DifX + x) + (DifZ + z) * (DifZ + z);
if (DistSq <= RadiusSq)
{
int Top = std::min(itr->m_Top, (int)(cChunkDef::Height)); // Stupid gcc needs int cast
for (int y = std::max(itr->m_Bottom, 1); y <= Top; y++)
{
switch (cChunkDef::GetBlock(a_BlockTypes, x, y, z))
{
// Only carve out these specific block types
case E_BLOCK_DIRT:
case E_BLOCK_GRASS:
case E_BLOCK_STONE:
case E_BLOCK_COBBLESTONE:
case E_BLOCK_GRAVEL:
case E_BLOCK_SAND:
case E_BLOCK_SANDSTONE:
case E_BLOCK_NETHERRACK:
case E_BLOCK_COAL_ORE:
case E_BLOCK_IRON_ORE:
case E_BLOCK_GOLD_ORE:
case E_BLOCK_DIAMOND_ORE:
case E_BLOCK_REDSTONE_ORE:
case E_BLOCK_REDSTONE_ORE_GLOWING:
{
cChunkDef::SetBlock(a_BlockTypes, x, y, z, E_BLOCK_AIR);
break;
}
default: break;
}
}
}
} // for x, z - a_BlockTypes
} // for itr - m_Points[]
}
+46
View File
@@ -0,0 +1,46 @@
// Ravines.h
// Interfaces to the cStructGenRavines class representing the ravine structure generator
#pragma once
#include "ComposableGenerator.h"
#include "../Noise.h"
class cStructGenRavines :
public cStructureGen
{
public:
cStructGenRavines(int a_Seed, int a_Size);
~cStructGenRavines();
protected:
class cRavine; // fwd: Ravines.cpp
typedef std::list<cRavine *> cRavines;
cNoise m_Noise;
int m_Size; // Max size, in blocks, of the ravines generated
cRavines m_Cache;
/// Clears everything from the cache
void ClearCache(void);
/// Returns all ravines that *may* intersect the given chunk. All the ravines are valid until the next call to this function.
void GetRavinesForChunk(int a_ChunkX, int a_ChunkZ, cRavines & a_Ravines);
// cStructureGen override:
virtual void GenStructures(cChunkDesc & a_ChunkDesc) override;
} ;
+675
View File
@@ -0,0 +1,675 @@
// StructGen.h
#include "Globals.h"
#include "StructGen.h"
#include "../BlockID.h"
#include "Trees.h"
#include "../BlockArea.h"
#include "../LinearUpscale.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++)
{
if (((m_Noise.IntNoise2DInt(ChunkX + x, ChunkZ + z) / 17) % 100) > m_Probability)
{
continue;
}
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 minimum height in this chunk:
cChunkDef::HeightMap HeightMap;
m_HeiGen.GenHeightMap(a_ChunkX, a_ChunkZ, HeightMap);
HEIGHTTYPE MinHeight = HeightMap[0];
for (int i = 1; i < ARRAYCOUNT(HeightMap); i++)
{
if (HeightMap[i] < MinHeight)
{
MinHeight = 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 minus 6
MinHeight = std::max(MinHeight - 6, 2);
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 [1 .. MinHeight] with preference to center heights
int HeightY = 1 + (((Rnd & 0x1ff) % MinHeight) + (((Rnd >> 9) & 0x1ff) % MinHeight)) / 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));
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cStructGenDirectOverhangs:
cStructGenDirectOverhangs::cStructGenDirectOverhangs(int a_Seed) :
m_Noise1(a_Seed),
m_Noise2(a_Seed + 1000)
{
}
void cStructGenDirectOverhangs::GenStructures(cChunkDesc & a_ChunkDesc)
{
// If there is no column of the wanted biome, bail out:
if (!HasWantedBiome(a_ChunkDesc))
{
return;
}
HEIGHTTYPE MaxHeight = a_ChunkDesc.GetMaxHeight();
const int SEGMENT_HEIGHT = 8;
const int INTERPOL_X = 16; // Must be a divisor of 16
const int INTERPOL_Z = 16; // Must be a divisor of 16
// Interpolate the chunk in 16 * SEGMENT_HEIGHT * 16 "segments", each SEGMENT_HEIGHT blocks high and each linearly interpolated separately.
// Have two buffers, one for the lowest floor and one for the highest floor, so that Y-interpolation can be done between them
// Then swap the buffers and use the previously-top one as the current-bottom, without recalculating it.
int FloorBuf1[17 * 17];
int FloorBuf2[17 * 17];
int * FloorHi = FloorBuf1;
int * FloorLo = FloorBuf2;
int BaseX = a_ChunkDesc.GetChunkX() * cChunkDef::Width;
int BaseZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width;
int BaseY = 63;
// Interpolate the lowest floor:
for (int z = 0; z <= 16 / INTERPOL_Z; z++) for (int x = 0; x <= 16 / INTERPOL_X; x++)
{
FloorLo[INTERPOL_X * x + 17 * INTERPOL_Z * z] =
m_Noise1.IntNoise3DInt(BaseX + INTERPOL_X * x, BaseY, BaseZ + INTERPOL_Z * z) *
m_Noise2.IntNoise3DInt(BaseX + INTERPOL_X * x, BaseY, BaseZ + INTERPOL_Z * z) /
256;
} // for x, z - FloorLo[]
LinearUpscale2DArrayInPlace(FloorLo, 17, 17, INTERPOL_X, INTERPOL_Z);
// Interpolate segments:
for (int Segment = BaseY; Segment < MaxHeight; Segment += SEGMENT_HEIGHT)
{
// First update the high floor:
for (int z = 0; z <= 16 / INTERPOL_Z; z++) for (int x = 0; x <= 16 / INTERPOL_X; x++)
{
FloorHi[INTERPOL_X * x + 17 * INTERPOL_Z * z] =
m_Noise1.IntNoise3DInt(BaseX + INTERPOL_X * x, Segment + SEGMENT_HEIGHT, BaseZ + INTERPOL_Z * z) *
m_Noise2.IntNoise3DInt(BaseX + INTERPOL_Z * x, Segment + SEGMENT_HEIGHT, BaseZ + INTERPOL_Z * z) /
256;
} // for x, z - FloorLo[]
LinearUpscale2DArrayInPlace(FloorHi, 17, 17, INTERPOL_X, INTERPOL_Z);
// Interpolate between FloorLo and FloorHi:
for (int z = 0; z < 16; z++) for (int x = 0; x < 16; x++)
{
switch (a_ChunkDesc.GetBiome(x, z))
{
case biExtremeHills:
case biExtremeHillsEdge:
{
int Lo = FloorLo[x + 17 * z] / 256;
int Hi = FloorHi[x + 17 * z] / 256;
for (int y = 0; y < SEGMENT_HEIGHT; y++)
{
int Val = Lo + (Hi - Lo) * y / SEGMENT_HEIGHT;
if (Val < 0)
{
a_ChunkDesc.SetBlockType(x, y + Segment, z, E_BLOCK_AIR);
}
} // for y
break;
}
} // switch (biome)
} // for z, x
// Swap the floors:
std::swap(FloorLo, FloorHi);
}
}
bool cStructGenDirectOverhangs::HasWantedBiome(cChunkDesc & a_ChunkDesc) const
{
cChunkDef::BiomeMap & Biomes = a_ChunkDesc.GetBiomeMap();
for (int i = 0; i < ARRAYCOUNT(Biomes); i++)
{
switch (Biomes[i])
{
case biExtremeHills:
case biExtremeHillsEdge:
{
return true;
}
}
} // for i
return false;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cStructGenDistortedMembraneOverhangs:
cStructGenDistortedMembraneOverhangs::cStructGenDistortedMembraneOverhangs(int a_Seed) :
m_NoiseX(a_Seed + 1000),
m_NoiseY(a_Seed + 2000),
m_NoiseZ(a_Seed + 3000),
m_NoiseH(a_Seed + 4000)
{
}
void cStructGenDistortedMembraneOverhangs::GenStructures(cChunkDesc & a_ChunkDesc)
{
const NOISE_DATATYPE Frequency = (NOISE_DATATYPE)16;
const NOISE_DATATYPE Amount = (NOISE_DATATYPE)1;
for (int y = 50; y < 128; y++)
{
NOISE_DATATYPE NoiseY = (NOISE_DATATYPE)y / 32;
// TODO: proper water level - where to get?
BLOCKTYPE ReplacementBlock = (y > 62) ? E_BLOCK_AIR : E_BLOCK_STATIONARY_WATER;
for (int z = 0; z < cChunkDef::Width; z++)
{
NOISE_DATATYPE NoiseZ = ((NOISE_DATATYPE)(a_ChunkDesc.GetChunkZ() * cChunkDef::Width + z)) / Frequency;
for (int x = 0; x < cChunkDef::Width; x++)
{
NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(a_ChunkDesc.GetChunkX() * cChunkDef::Width + x)) / Frequency;
NOISE_DATATYPE DistortX = m_NoiseX.CubicNoise3D(NoiseX, NoiseY, NoiseZ) * Amount;
NOISE_DATATYPE DistortY = m_NoiseY.CubicNoise3D(NoiseX, NoiseY, NoiseZ) * Amount;
NOISE_DATATYPE DistortZ = m_NoiseZ.CubicNoise3D(NoiseX, NoiseY, NoiseZ) * Amount;
int MembraneHeight = 96 - (int)((DistortY + m_NoiseH.CubicNoise2D(NoiseX + DistortX, NoiseZ + DistortZ)) * 30);
if (MembraneHeight < y)
{
a_ChunkDesc.SetBlockType(x, y, z, ReplacementBlock);
}
} // for y
} // for x
} // for z
}
+165
View File
@@ -0,0 +1,165 @@
// StructGen.h
/* Interfaces to the various structure generators:
- cStructGenTrees
- cStructGenMarbleCaves
- cStructGenOres
*/
#pragma once
#include "ComposableGenerator.h"
#include "../Noise.h"
class cStructGenTrees :
public cStructureGen
{
public:
cStructGenTrees(int a_Seed, cBiomeGen * a_BiomeGen, cTerrainHeightGen * a_HeightGen, cTerrainCompositionGen * a_CompositionGen) :
m_Seed(a_Seed),
m_Noise(a_Seed),
m_BiomeGen(a_BiomeGen),
m_HeightGen(a_HeightGen),
m_CompositionGen(a_CompositionGen)
{}
protected:
int m_Seed;
cNoise m_Noise;
cBiomeGen * m_BiomeGen;
cTerrainHeightGen * m_HeightGen;
cTerrainCompositionGen * m_CompositionGen;
/** Generates and applies an image of a single tree.
Parts of the tree inside the chunk are applied to a_BlockX.
Parts of the tree outside the chunk are stored in a_OutsideX
*/
void GenerateSingleTree(
int a_ChunkX, int a_ChunkZ, int a_Seq,
cChunkDesc & a_ChunkDesc,
sSetBlockVector & a_OutsideLogs,
sSetBlockVector & a_OutsideOther
) ;
/// Applies an image into chunk blockdata; all blocks outside the chunk will be appended to a_Overflow
void ApplyTreeImage(
int a_ChunkX, int a_ChunkZ,
cChunkDesc & a_ChunkDesc,
const sSetBlockVector & a_Image,
sSetBlockVector & a_Overflow
);
int GetNumTrees(
int a_ChunkX, int a_ChunkZ,
const cChunkDef::BiomeMap & a_Biomes
);
// cStructureGen override:
virtual void GenStructures(cChunkDesc & a_ChunkDesc) override;
} ;
class cStructGenOreNests :
public cStructureGen
{
public:
cStructGenOreNests(int a_Seed) : m_Noise(a_Seed), m_Seed(a_Seed) {}
protected:
cNoise m_Noise;
int m_Seed;
// cStructureGen override:
virtual void GenStructures(cChunkDesc & a_ChunkDesc) override;
void 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);
} ;
class cStructGenLakes :
public cStructureGen
{
public:
cStructGenLakes(int a_Seed, BLOCKTYPE a_Fluid, cTerrainHeightGen & a_HeiGen, int a_Probability) :
m_Noise(a_Seed),
m_Seed(a_Seed),
m_Fluid(a_Fluid),
m_HeiGen(a_HeiGen),
m_Probability(a_Probability)
{
}
protected:
cNoise m_Noise;
int m_Seed;
BLOCKTYPE m_Fluid;
cTerrainHeightGen & m_HeiGen;
int m_Probability; ///< Chance, 0 .. 100, of a chunk having the lake
// cStructureGen override:
virtual void GenStructures(cChunkDesc & a_ChunkDesc) override;
/// Creates a lake image for the specified chunk into a_Lake
void CreateLakeImage(int a_ChunkX, int a_ChunkZ, cBlockArea & a_Lake);
} ;
class cStructGenDirectOverhangs :
public cStructureGen
{
public:
cStructGenDirectOverhangs(int a_Seed);
protected:
cNoise m_Noise1;
cNoise m_Noise2;
// cStructureGen override:
virtual void GenStructures(cChunkDesc & a_ChunkDesc) override;
bool HasWantedBiome(cChunkDesc & a_ChunkDesc) const;
} ;
class cStructGenDistortedMembraneOverhangs :
public cStructureGen
{
public:
cStructGenDistortedMembraneOverhangs(int a_Seed);
protected:
cNoise m_NoiseX;
cNoise m_NoiseY;
cNoise m_NoiseZ;
cNoise m_NoiseH;
// cStructureGen override:
virtual void GenStructures(cChunkDesc & a_ChunkDesc) override;
} ;
+684
View File
@@ -0,0 +1,684 @@
// Trees.cpp
// Implements helper functions used for generating trees
#include "Globals.h"
#include "Trees.h"
#include "../BlockID.h"
// DEBUG:
int gTotalLargeJungleTrees = 0;
int gOversizeLargeJungleTrees = 0;
typedef struct
{
int x, z;
} sCoords;
typedef struct
{
int x, z;
NIBBLETYPE Meta;
} sMetaCoords;
static const sCoords Corners[] =
{
{-1, -1},
{-1, 1},
{1, -1},
{1, 1},
} ;
// BigO = a big ring of blocks, used for generating horz slices of treetops, the number indicates the radius
static const sCoords BigO1[] =
{
{0, -1},
{-1, 0}, {1, 0},
{0, 1},
} ;
static const sCoords BigO2[] =
{
{-1, -2}, {0, -2}, {1, -2},
{-2, -1}, {-1, -1}, {0, -1}, {1, -1}, {2, -1},
{-2, 0}, {-1, 0}, {1, 0}, {2, 0},
{-2, 1}, {-1, 1}, {0, 1}, {1, 1}, {2, 1},
{-1, 2}, {0, 2}, {1, 2},
} ;
static const sCoords BigO3[] =
{
{-2, -3}, {-1, -3}, {0, -3}, {1, -3}, {2, -3},
{-3, -2}, {-2, -2}, {-1, -2}, {0, -2}, {1, -2}, {2, -2}, {3, -2},
{-3, -1}, {-2, -1}, {-1, -1}, {0, -1}, {1, -1}, {2, -1}, {3, -1},
{-3, 0}, {-2, 0}, {-1, 0}, {1, 0}, {2, 0}, {3, 0},
{-3, 1}, {-2, 1}, {-1, 1}, {0, 1}, {1, 1}, {2, 1}, {3, 1},
{-3, 2}, {-2, 2}, {-1, 2}, {0, 2}, {1, 2}, {2, 2}, {3, 2},
{-2, 3}, {-1, 3}, {0, 3}, {1, 3}, {2, 3},
} ;
static const sCoords BigO4[] = // Part of Big Jungle tree
{
{-2, -4}, {-1, -4}, {0, -4}, {1, -4}, {2, -4},
{-3, -3}, {-2, -3}, {-1, -3}, {0, -3}, {1, -3}, {2, -3}, {3, -3},
{-4, -2}, {-3, -2}, {-2, -2}, {-1, -2}, {0, -2}, {1, -2}, {2, -2}, {3, -2}, {4, -2},
{-4, -1}, {-3, -1}, {-2, -1}, {-1, -1}, {0, -1}, {1, -1}, {2, -1}, {3, -1}, {4, -1},
{-4, 0}, {-3, 0}, {-2, 0}, {-1, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0},
{-4, 1}, {-3, 1}, {-2, 1}, {-1, 1}, {0, 1}, {1, 1}, {2, 1}, {3, 1}, {4, 1},
{-4, 2}, {-3, 2}, {-2, 2}, {-1, 2}, {0, 2}, {1, 2}, {2, 2}, {3, 2}, {4, 2},
{-3, 3}, {-2, 3}, {-1, 3}, {0, 3}, {1, 3}, {2, 3}, {3, 3},
{-2, 4}, {-1, 4}, {0, 4}, {1, 4}, {2, 4},
} ;
typedef struct
{
const sCoords * Coords;
size_t Count;
} sCoordsArr;
static const sCoordsArr BigOs[] =
{
{BigO1, ARRAYCOUNT(BigO1)},
{BigO2, ARRAYCOUNT(BigO2)},
{BigO3, ARRAYCOUNT(BigO3)},
{BigO4, ARRAYCOUNT(BigO4)},
} ;
/// Pushes a specified layer of blocks of the same type around (x, h, z) into a_Blocks
inline void PushCoordBlocks(int a_BlockX, int a_Height, int a_BlockZ, sSetBlockVector & a_Blocks, const sCoords * a_Coords, size_t a_NumCoords, BLOCKTYPE a_BlockType, NIBBLETYPE a_Meta)
{
for (size_t i = 0; i < a_NumCoords; i++)
{
a_Blocks.push_back(sSetBlock(a_BlockX + a_Coords[i].x, a_Height, a_BlockZ + a_Coords[i].z, a_BlockType, a_Meta));
}
}
inline void PushCornerBlocks(int a_BlockX, int a_Height, int a_BlockZ, int a_Seq, cNoise & a_Noise, int a_Chance, sSetBlockVector & a_Blocks, int a_CornersDist, BLOCKTYPE a_BlockType, NIBBLETYPE a_Meta)
{
for (size_t i = 0; i < ARRAYCOUNT(Corners); i++)
{
int x = a_BlockX + Corners[i].x;
int z = a_BlockZ + Corners[i].z;
if (a_Noise.IntNoise3DInt(x + 64 * a_Seq, a_Height, z + 64 * a_Seq) <= a_Chance)
{
a_Blocks.push_back(sSetBlock(x, a_Height, z, a_BlockType, a_Meta));
}
} // for i - Corners[]
}
inline void PushSomeColumns(int a_BlockX, int a_Height, int a_BlockZ, int a_ColumnHeight, int a_Seq, cNoise & a_Noise, int a_Chance, sSetBlockVector & a_Blocks, const sMetaCoords * a_Coords, size_t a_NumCoords, BLOCKTYPE a_BlockType)
{
for (size_t i = 0; i < a_NumCoords; i++)
{
int x = a_BlockX + a_Coords[i].x;
int z = a_BlockZ + a_Coords[i].z;
if (a_Noise.IntNoise3DInt(x + 64 * a_Seq, a_Height + i, z + 64 * a_Seq) <= a_Chance)
{
for (int j = 0; j < a_ColumnHeight; j++)
{
a_Blocks.push_back(sSetBlock(x, a_Height - j, z, a_BlockType, a_Coords[i].Meta));
}
}
} // for i - a_Coords[]
}
void GetTreeImageByBiome(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, EMCSBiome a_Biome, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks)
{
switch (a_Biome)
{
case biPlains:
case biExtremeHills:
case biExtremeHillsEdge:
case biForest:
case biMushroomIsland:
case biMushroomShore:
case biForestHills:
{
// Apple or birch trees:
if (a_Noise.IntNoise3DInt(a_BlockX, a_BlockY + 16 * a_Seq, a_BlockZ + 16 * a_Seq) < 0x5fffffff)
{
GetAppleTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks);
}
else
{
GetBirchTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks);
}
break;
}
case biTaiga:
case biIcePlains:
case biIceMountains:
case biTaigaHills:
{
// Conifers
GetConiferTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks);
break;
}
case biSwampland:
{
// Swamp trees:
GetSwampTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks);
break;
}
case biJungle:
case biJungleHills:
{
// Apple bushes, large jungle trees, small jungle trees
if (a_Noise.IntNoise3DInt(a_BlockX, a_BlockY + 16 * a_Seq, a_BlockZ + 16 * a_Seq) < 0x6fffffff)
{
GetAppleBushImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks);
}
else
{
GetJungleTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks);
}
}
}
}
void GetAppleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks)
{
if (a_Noise.IntNoise3DInt(a_BlockX + 32 * a_Seq, a_BlockY + 32 * a_Seq, a_BlockZ) < 0x60000000)
{
GetSmallAppleTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks);
}
else
{
GetLargeAppleTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks);
}
}
void GetSmallAppleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks)
{
/* Small apple tree has:
- a top plus (no log)
- optional BigO1 + random corners (log)
- 2 layers of BigO2 + random corners (log)
- 1 to 3 blocks of trunk
*/
int Random = a_Noise.IntNoise3DInt(a_BlockX + 64 * a_Seq, a_BlockY, a_BlockZ) >> 3;
int Heights[] = {1, 2, 2, 3} ;
int Height = 1 + Heights[Random & 3];
Random >>= 2;
// Pre-alloc so that we don't realloc too often later:
a_LogBlocks.reserve(Height + 5);
a_OtherBlocks.reserve(ARRAYCOUNT(BigO2) * 2 + ARRAYCOUNT(BigO1) + ARRAYCOUNT(Corners) * 3 + 3 + 5);
// Trunk:
for (int i = 0; i < Height; i++)
{
a_LogBlocks.push_back(sSetBlock(a_BlockX, a_BlockY + i, a_BlockZ, E_BLOCK_LOG, E_META_LOG_APPLE));
}
int Hei = a_BlockY + Height;
// 2 BigO2 + corners layers:
for (int i = 0; i < 2; i++)
{
PushCoordBlocks (a_BlockX, Hei, a_BlockZ, a_OtherBlocks, BigO2, ARRAYCOUNT(BigO2), E_BLOCK_LEAVES, E_META_LEAVES_APPLE);
PushCornerBlocks(a_BlockX, Hei, a_BlockZ, a_Seq, a_Noise, 0x5000000 - i * 0x10000000, a_OtherBlocks, 2, E_BLOCK_LEAVES, E_META_LEAVES_APPLE);
a_LogBlocks.push_back(sSetBlock(a_BlockX, Hei, a_BlockZ, E_BLOCK_LOG, E_META_LOG_APPLE));
Hei++;
} // for i - 2*
// Optional BigO1 + corners layer:
if ((Random & 1) == 0)
{
PushCoordBlocks (a_BlockX, Hei, a_BlockZ, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_APPLE);
PushCornerBlocks(a_BlockX, Hei, a_BlockZ, a_Seq, a_Noise, 0x6000000, a_OtherBlocks, 1, E_BLOCK_LEAVES, E_META_LEAVES_APPLE);
a_LogBlocks.push_back(sSetBlock(a_BlockX, Hei, a_BlockZ, E_BLOCK_LOG, E_META_LOG_APPLE));
Hei++;
}
// Top plus:
PushCoordBlocks(a_BlockX, Hei, a_BlockZ, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_APPLE);
a_OtherBlocks.push_back(sSetBlock(a_BlockX, Hei, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_APPLE));
}
void GetLargeAppleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks)
{
// TODO
}
void GetBirchTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks)
{
int Height = 5 + (a_Noise.IntNoise3DInt(a_BlockX + 64 * a_Seq, a_BlockY, a_BlockZ) % 3);
// Prealloc, so that we don't realloc too often later:
a_LogBlocks.reserve(Height);
a_OtherBlocks.reserve(80);
// The entire trunk, out of logs:
for (int i = Height - 1; i >= 0; --i)
{
a_LogBlocks.push_back(sSetBlock(a_BlockX, a_BlockY + i, a_BlockZ, E_BLOCK_LOG, E_META_LOG_BIRCH));
}
int h = a_BlockY + Height;
// Top layer - just the Plus:
PushCoordBlocks(a_BlockX, h, a_BlockZ, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_BIRCH);
a_OtherBlocks.push_back(sSetBlock(a_BlockX, h, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_BIRCH)); // There's no log at this layer
h--;
// Second layer - log, Plus and maybe Corners:
PushCoordBlocks (a_BlockX, h, a_BlockZ, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_BIRCH);
PushCornerBlocks(a_BlockX, h, a_BlockZ, a_Seq, a_Noise, 0x5fffffff, a_OtherBlocks, 1, E_BLOCK_LEAVES, E_META_LEAVES_BIRCH);
h--;
// Third and fourth layers - BigO2 and maybe 2*Corners:
for (int Row = 0; Row < 2; Row++)
{
PushCoordBlocks (a_BlockX, h, a_BlockZ, a_OtherBlocks, BigO2, ARRAYCOUNT(BigO2), E_BLOCK_LEAVES, E_META_LEAVES_BIRCH);
PushCornerBlocks(a_BlockX, h, a_BlockZ, a_Seq, a_Noise, 0x3fffffff + Row * 0x10000000, a_OtherBlocks, 2, E_BLOCK_LEAVES, E_META_LEAVES_BIRCH);
h--;
} // for Row - 2*
}
void GetConiferTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks)
{
// Half chance for a spruce, half for a pine:
if (a_Noise.IntNoise3DInt(a_BlockX + 64 * a_Seq, a_BlockY, a_BlockZ + 32 * a_Seq) < 0x40000000)
{
GetSpruceTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks);
}
else
{
GetPineTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks);
}
}
void GetSpruceTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks)
{
// Spruces have a top section with layer sizes of (0, 1, 0) or only (1, 0),
// then 1 - 3 sections of ascending sizes (1, 2) [most often], (1, 3) or (1, 2, 3)
// and an optional bottom section of size 1, followed by 1 - 3 clear trunk blocks
// We'll use bits from this number as partial random numbers; but the noise function has mod8 irregularities
// (each of the mod8 remainders has a very different chance of occurrence) - that's why we divide by 8
int MyRandom = a_Noise.IntNoise3DInt(a_BlockX + 32 * a_Seq, a_BlockY + 32 * a_Seq, a_BlockZ) / 8;
static const int sHeights[] = {1, 2, 2, 3};
int Height = sHeights[MyRandom & 3];
MyRandom >>= 2;
// Prealloc, so that we don't realloc too often later:
a_LogBlocks.reserve(Height);
a_OtherBlocks.reserve(180);
// Clear trunk blocks:
for (int i = 0; i < Height; i++)
{
a_LogBlocks.push_back(sSetBlock(a_BlockX, a_BlockY + i, a_BlockZ, E_BLOCK_LOG, E_META_LOG_CONIFER));
}
Height += a_BlockY;
// Optional size-1 bottom leaves layer:
if ((MyRandom & 1) == 0)
{
PushCoordBlocks(a_BlockX, Height, a_BlockZ, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_CONIFER);
a_OtherBlocks.push_back(sSetBlock(a_BlockX, Height, a_BlockZ, E_BLOCK_LOG, E_META_LOG_CONIFER));
Height++;
}
MyRandom >>= 1;
// 1 to 3 sections of leaves layers:
static const int sNumSections[] = {1, 2, 2, 3};
int NumSections = sNumSections[MyRandom & 3];
MyRandom >>= 2;
for (int i = 0; i < NumSections; i++)
{
switch (MyRandom & 3) // SectionType; (1, 2) twice as often as the other two
{
case 0:
case 1:
{
PushCoordBlocks(a_BlockX, Height, a_BlockZ, a_OtherBlocks, BigO2, ARRAYCOUNT(BigO2), E_BLOCK_LEAVES, E_META_LEAVES_CONIFER);
PushCoordBlocks(a_BlockX, Height + 1, a_BlockZ, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_CONIFER);
a_LogBlocks.push_back(sSetBlock(a_BlockX, Height, a_BlockZ, E_BLOCK_LOG, E_META_LOG_CONIFER));
a_LogBlocks.push_back(sSetBlock(a_BlockX, Height + 1, a_BlockZ, E_BLOCK_LOG, E_META_LOG_CONIFER));
Height += 2;
break;
}
case 2:
{
PushCoordBlocks(a_BlockX, Height, a_BlockZ, a_OtherBlocks, BigO3, ARRAYCOUNT(BigO3), E_BLOCK_LEAVES, E_META_LEAVES_CONIFER);
PushCoordBlocks(a_BlockX, Height + 1, a_BlockZ, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_CONIFER);
a_LogBlocks.push_back(sSetBlock(a_BlockX, Height, a_BlockZ, E_BLOCK_LOG, E_META_LOG_CONIFER));
a_LogBlocks.push_back(sSetBlock(a_BlockX, Height + 1, a_BlockZ, E_BLOCK_LOG, E_META_LOG_CONIFER));
Height += 2;
break;
}
case 3:
{
PushCoordBlocks(a_BlockX, Height, a_BlockZ, a_OtherBlocks, BigO3, ARRAYCOUNT(BigO3), E_BLOCK_LEAVES, E_META_LEAVES_CONIFER);
PushCoordBlocks(a_BlockX, Height + 1, a_BlockZ, a_OtherBlocks, BigO2, ARRAYCOUNT(BigO2), E_BLOCK_LEAVES, E_META_LEAVES_CONIFER);
PushCoordBlocks(a_BlockX, Height + 2, a_BlockZ, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_CONIFER);
a_LogBlocks.push_back(sSetBlock(a_BlockX, Height, a_BlockZ, E_BLOCK_LOG, E_META_LOG_CONIFER));
a_LogBlocks.push_back(sSetBlock(a_BlockX, Height + 1, a_BlockZ, E_BLOCK_LOG, E_META_LOG_CONIFER));
a_LogBlocks.push_back(sSetBlock(a_BlockX, Height + 2, a_BlockZ, E_BLOCK_LOG, E_META_LOG_CONIFER));
Height += 3;
break;
}
} // switch (SectionType)
MyRandom >>= 2;
} // for i - Sections
if ((MyRandom & 1) == 0)
{
// (0, 1, 0) top:
a_LogBlocks.push_back (sSetBlock(a_BlockX, Height, a_BlockZ, E_BLOCK_LOG, E_META_LOG_CONIFER));
PushCoordBlocks (a_BlockX, Height + 1, a_BlockZ, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_CONIFER);
a_OtherBlocks.push_back(sSetBlock(a_BlockX, Height + 1, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_CONIFER));
a_OtherBlocks.push_back(sSetBlock(a_BlockX, Height + 2, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_CONIFER));
}
else
{
// (1, 0) top:
a_OtherBlocks.push_back(sSetBlock(a_BlockX, Height, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_CONIFER));
PushCoordBlocks (a_BlockX, Height + 1, a_BlockZ, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_CONIFER);
a_OtherBlocks.push_back(sSetBlock(a_BlockX, Height + 1, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_CONIFER));
}
}
void GetPineTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks)
{
// Tall, little leaves on top. The top leaves are arranged in a shape of two cones joined by their bases.
// There can be one or two layers representing the cone bases (SameSizeMax)
int MyRandom = a_Noise.IntNoise3DInt(a_BlockX + 32 * a_Seq, a_BlockY, a_BlockZ + 32 * a_Seq) / 8;
int TrunkHeight = 8 + (MyRandom % 3);
int SameSizeMax = ((MyRandom & 8) == 0) ? 1 : 0;
MyRandom >>= 3;
int NumLeavesLayers = 2 + (MyRandom % 3); // Number of layers that have leaves in them
if (NumLeavesLayers == 2)
{
SameSizeMax = 0;
}
// Pre-allocate the vector:
a_LogBlocks.reserve(TrunkHeight);
a_OtherBlocks.reserve(NumLeavesLayers * 25);
// The entire trunk, out of logs:
for (int i = TrunkHeight; i >= 0; --i)
{
a_LogBlocks.push_back(sSetBlock(a_BlockX, a_BlockY + i, a_BlockZ, E_BLOCK_LOG, E_META_LOG_CONIFER));
}
int h = a_BlockY + TrunkHeight + 2;
// Top layer - just a single leaves block:
a_OtherBlocks.push_back(sSetBlock(a_BlockX, h, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_CONIFER));
h--;
// One more layer is above the trunk, push the central leaves:
a_OtherBlocks.push_back(sSetBlock(a_BlockX, h, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_CONIFER));
// Layers expanding in size, then collapsing again:
// LOGD("Generating %d layers of pine leaves, SameSizeMax = %d", NumLeavesLayers, SameSizeMax);
for (int i = 0; i < NumLeavesLayers; ++i)
{
int LayerSize = std::min(i, NumLeavesLayers - i + SameSizeMax - 1);
// LOGD("LayerSize %d: %d", i, LayerSize);
if (LayerSize < 0)
{
break;
}
ASSERT(LayerSize < ARRAYCOUNT(BigOs));
PushCoordBlocks(a_BlockX, h, a_BlockZ, a_OtherBlocks, BigOs[LayerSize].Coords, BigOs[LayerSize].Count, E_BLOCK_LEAVES, E_META_LEAVES_CONIFER);
h--;
}
}
void GetSwampTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks)
{
// Vines are around the BigO3, but not in the corners; need proper meta for direction
static const sMetaCoords Vines[] =
{
{-2, -4, 1}, {-1, -4, 1}, {0, -4, 1}, {1, -4, 1}, {2, -4, 1}, // North face
{-2, 4, 4}, {-1, 4, 4}, {0, 4, 4}, {1, 4, 4}, {2, 4, 4}, // South face
{4, -2, 2}, {4, -1, 2}, {4, 0, 2}, {4, 1, 2}, {4, 2, 2}, // East face
{-4, -2, 8}, {-4, -1, 8}, {-4, 0, 8}, {-4, 1, 8}, {-4, 2, 8}, // West face
} ;
int Height = 3 + (a_Noise.IntNoise3DInt(a_BlockX + 32 * a_Seq, a_BlockY, a_BlockZ + 32 * a_Seq) / 8) % 3;
a_LogBlocks.reserve(Height);
a_OtherBlocks.reserve(2 * ARRAYCOUNT(BigO2) + 2 * ARRAYCOUNT(BigO3) + Height * ARRAYCOUNT(Vines) + 20);
for (int i = 0; i < Height; i++)
{
a_LogBlocks.push_back(sSetBlock(a_BlockX, a_BlockY + i, a_BlockZ, E_BLOCK_LOG, E_META_LOG_APPLE));
}
int hei = a_BlockY + Height - 2;
// Put vines around the lowermost leaves layer:
PushSomeColumns(a_BlockX, hei, a_BlockZ, Height, a_Seq, a_Noise, 0x3fffffff, a_OtherBlocks, Vines, ARRAYCOUNT(Vines), E_BLOCK_VINES);
// The lower two leaves layers are BigO3 with log in the middle and possibly corners:
for (int i = 0; i < 2; i++)
{
PushCoordBlocks(a_BlockX, hei, a_BlockZ, a_OtherBlocks, BigO3, ARRAYCOUNT(BigO3), E_BLOCK_LEAVES, E_META_LEAVES_APPLE);
PushCornerBlocks(a_BlockX, hei, a_BlockZ, a_Seq, a_Noise, 0x5fffffff, a_OtherBlocks, 3, E_BLOCK_LEAVES, E_META_LEAVES_APPLE);
hei++;
} // for i - 2*
// The upper two leaves layers are BigO2 with leaves in the middle and possibly corners:
for (int i = 0; i < 2; i++)
{
PushCoordBlocks(a_BlockX, hei, a_BlockZ, a_OtherBlocks, BigO2, ARRAYCOUNT(BigO2), E_BLOCK_LEAVES, E_META_LEAVES_APPLE);
PushCornerBlocks(a_BlockX, hei, a_BlockZ, a_Seq, a_Noise, 0x5fffffff, a_OtherBlocks, 3, E_BLOCK_LEAVES, E_META_LEAVES_APPLE);
a_OtherBlocks.push_back(sSetBlock(a_BlockX, hei, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_APPLE));
hei++;
} // for i - 2*
}
void GetAppleBushImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks)
{
a_OtherBlocks.reserve(3 + ARRAYCOUNT(BigO2) + ARRAYCOUNT(BigO1));
int hei = a_BlockY;
a_LogBlocks.push_back(sSetBlock(a_BlockX, hei, a_BlockZ, E_BLOCK_LOG, E_META_LOG_JUNGLE));
PushCoordBlocks(a_BlockX, hei, a_BlockZ, a_OtherBlocks, BigO2, ARRAYCOUNT(BigO2), E_BLOCK_LEAVES, E_META_LEAVES_APPLE);
hei++;
a_OtherBlocks.push_back(sSetBlock(a_BlockX, hei, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_APPLE));
PushCoordBlocks(a_BlockX, hei, a_BlockZ, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_APPLE);
hei++;
a_OtherBlocks.push_back(sSetBlock(a_BlockX, hei, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_APPLE));
}
void GetJungleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks)
{
if (a_Noise.IntNoise3DInt(a_BlockX + 32 * a_Seq, a_BlockY + 32 * a_Seq, a_BlockZ) < 0x60000000)
{
GetSmallJungleTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks);
}
else
{
GetLargeJungleTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks);
}
}
void GetLargeJungleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks)
{
// TODO: Generate proper jungle trees with branches
// Vines are around the BigO4, but not in the corners; need proper meta for direction
static const sMetaCoords Vines[] =
{
{-2, -5, 1}, {-1, -5, 1}, {0, -5, 1}, {1, -5, 1}, {2, -5, 1}, // North face
{-2, 5, 4}, {-1, 5, 4}, {0, 5, 4}, {1, 5, 4}, {2, 5, 4}, // South face
{5, -2, 2}, {5, -1, 2}, {5, 0, 2}, {5, 1, 2}, {5, 2, 2}, // East face
{-5, -2, 8}, {-5, -1, 8}, {-5, 0, 8}, {-5, 1, 8}, {-5, 2, 8}, // West face
// TODO: vines around the trunk, proper metas and height
} ;
int Height = 24 + (a_Noise.IntNoise3DInt(a_BlockX + 32 * a_Seq, a_BlockY, a_BlockZ + 32 * a_Seq) / 11) % 24;
a_LogBlocks.reserve(Height * 4);
a_OtherBlocks.reserve(2 * ARRAYCOUNT(BigO4) + ARRAYCOUNT(BigO3) + Height * ARRAYCOUNT(Vines) + 50);
for (int i = 0; i < Height; i++)
{
a_LogBlocks.push_back(sSetBlock(a_BlockX, a_BlockY + i, a_BlockZ, E_BLOCK_LOG, E_META_LOG_JUNGLE));
a_LogBlocks.push_back(sSetBlock(a_BlockX + 1, a_BlockY + i, a_BlockZ, E_BLOCK_LOG, E_META_LOG_JUNGLE));
a_LogBlocks.push_back(sSetBlock(a_BlockX, a_BlockY + i, a_BlockZ + 1, E_BLOCK_LOG, E_META_LOG_JUNGLE));
a_LogBlocks.push_back(sSetBlock(a_BlockX + 1, a_BlockY + i, a_BlockZ + 1, E_BLOCK_LOG, E_META_LOG_JUNGLE));
}
int hei = a_BlockY + Height - 2;
// Put vines around the lowermost leaves layer:
PushSomeColumns(a_BlockX, hei, a_BlockZ, Height, a_Seq, a_Noise, 0x3fffffff, a_OtherBlocks, Vines, ARRAYCOUNT(Vines), E_BLOCK_VINES);
// The lower two leaves layers are BigO4 with log in the middle and possibly corners:
for (int i = 0; i < 2; i++)
{
PushCoordBlocks(a_BlockX, hei, a_BlockZ, a_OtherBlocks, BigO4, ARRAYCOUNT(BigO4), E_BLOCK_LEAVES, E_META_LEAVES_JUNGLE);
PushCornerBlocks(a_BlockX, hei, a_BlockZ, a_Seq, a_Noise, 0x5fffffff, a_OtherBlocks, 3, E_BLOCK_LEAVES, E_META_LEAVES_JUNGLE);
hei++;
} // for i - 2*
// The top leaves layer is a BigO3 with leaves in the middle and possibly corners:
PushCoordBlocks(a_BlockX, hei, a_BlockZ, a_OtherBlocks, BigO3, ARRAYCOUNT(BigO3), E_BLOCK_LEAVES, E_META_LEAVES_JUNGLE);
PushCornerBlocks(a_BlockX, hei, a_BlockZ, a_Seq, a_Noise, 0x5fffffff, a_OtherBlocks, 3, E_BLOCK_LEAVES, E_META_LEAVES_JUNGLE);
a_OtherBlocks.push_back(sSetBlock(a_BlockX, hei, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_JUNGLE));
}
void GetSmallJungleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks)
{
// Vines are around the BigO3, but not in the corners; need proper meta for direction
static const sMetaCoords Vines[] =
{
{-2, -4, 1}, {-1, -4, 1}, {0, -4, 1}, {1, -4, 1}, {2, -4, 1}, // North face
{-2, 4, 4}, {-1, 4, 4}, {0, 4, 4}, {1, 4, 4}, {2, 4, 4}, // South face
{4, -2, 2}, {4, -1, 2}, {4, 0, 2}, {4, 1, 2}, {4, 2, 2}, // East face
{-4, -2, 8}, {-4, -1, 8}, {-4, 0, 8}, {-4, 1, 8}, // West face
// TODO: proper metas and height: {0, 1, 1}, {0, -1, 4}, {-1, 0, 2}, {1, 1, 8}, // Around the tunk
} ;
int Height = 7 + (a_Noise.IntNoise3DInt(a_BlockX + 5 * a_Seq, a_BlockY, a_BlockZ + 5 * a_Seq) / 5) % 3;
a_LogBlocks.reserve(Height);
a_OtherBlocks.reserve(
2 * ARRAYCOUNT(BigO3) + // O3 layer, 2x
2 * ARRAYCOUNT(BigO2) + // O2 layer, 2x
ARRAYCOUNT(BigO1) + 1 + // Plus on the top
Height * ARRAYCOUNT(Vines) + // Vines
50 // some safety
);
for (int i = 0; i < Height; i++)
{
a_LogBlocks.push_back(sSetBlock(a_BlockX, a_BlockY + i, a_BlockZ, E_BLOCK_LOG, E_META_LOG_JUNGLE));
}
int hei = a_BlockY + Height - 3;
// Put vines around the lowermost leaves layer:
PushSomeColumns(a_BlockX, hei, a_BlockZ, Height, a_Seq, a_Noise, 0x3fffffff, a_OtherBlocks, Vines, ARRAYCOUNT(Vines), E_BLOCK_VINES);
// The lower two leaves layers are BigO3 with log in the middle and possibly corners:
for (int i = 0; i < 2; i++)
{
PushCoordBlocks(a_BlockX, hei, a_BlockZ, a_OtherBlocks, BigO3, ARRAYCOUNT(BigO3), E_BLOCK_LEAVES, E_META_LEAVES_JUNGLE);
PushCornerBlocks(a_BlockX, hei, a_BlockZ, a_Seq, a_Noise, 0x5fffffff, a_OtherBlocks, 3, E_BLOCK_LEAVES, E_META_LEAVES_JUNGLE);
hei++;
} // for i - 2*
// Two layers of BigO2 leaves, possibly with corners:
for (int i = 0; i < 1; i++)
{
PushCoordBlocks(a_BlockX, hei, a_BlockZ, a_OtherBlocks, BigO2, ARRAYCOUNT(BigO2), E_BLOCK_LEAVES, E_META_LEAVES_JUNGLE);
PushCornerBlocks(a_BlockX, hei, a_BlockZ, a_Seq, a_Noise, 0x5fffffff, a_OtherBlocks, 2, E_BLOCK_LEAVES, E_META_LEAVES_JUNGLE);
hei++;
} // for i - 2*
// Top plus, all leaves:
PushCoordBlocks(a_BlockX, hei, a_BlockZ, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_JUNGLE);
a_OtherBlocks.push_back(sSetBlock(a_BlockX, hei, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_JUNGLE));
}
+93
View File
@@ -0,0 +1,93 @@
// Trees.h
// Interfaces to helper functions used for generating trees
/*
Note that all of these functions must generate the same tree image for the same input (x, y, z, seq)
- cStructGenTrees depends on this
To generate a random image for the (x, y, z) coords, pass an arbitrary value as (seq).
Each function returns two arrays of blocks, "logs" and "other". The point is that logs are of higher priority,
logs can overwrite others(leaves), but others shouldn't overwrite logs. This is an optimization for the generator.
*/
#pragma once
#include "../ChunkDef.h"
#include "../Noise.h"
// Blocks that don't block tree growth:
#define CASE_TREE_ALLOWED_BLOCKS \
case E_BLOCK_AIR: \
case E_BLOCK_LEAVES: \
case E_BLOCK_SNOW: \
case E_BLOCK_TALL_GRASS: \
case E_BLOCK_DEAD_BUSH: \
case E_BLOCK_SAPLING: \
case E_BLOCK_VINES
// Blocks that a tree may overwrite when growing:
#define CASE_TREE_OVERWRITTEN_BLOCKS \
case E_BLOCK_AIR: \
/* case E_BLOCK_LEAVES: LEAVES are a special case, they can be overwritten only by log. Handled in cChunkMap::ReplaceTreeBlocks(). */ \
case E_BLOCK_SNOW: \
case E_BLOCK_TALL_GRASS: \
case E_BLOCK_DEAD_BUSH: \
case E_BLOCK_SAPLING: \
case E_BLOCK_VINES
/// Generates an image of a tree at the specified coords (lowest trunk block) in the specified biome
void GetTreeImageByBiome(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, EMCSBiome a_Biome, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks);
/// Generates an image of a random apple tree
void GetAppleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks);
/// Generates an image of a small (nonbranching) apple tree
void GetSmallAppleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks);
/// Generates an image of a large (branching) apple tree
void GetLargeAppleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks);
/// Generates an image of a random birch tree
void GetBirchTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks);
/// Generates an image of a random conifer tree
void GetConiferTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks);
/// Generates an image of a random spruce (short conifer, two layers of leaves)
void GetSpruceTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks);
/// Generates an image of a random pine (tall conifer, little leaves at top)
void GetPineTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks);
/// Generates an image of a random swampland tree
void GetSwampTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks);
/// Generates an image of a random apple bush (for jungles)
void GetAppleBushImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks);
/// Generates an image of a random jungle tree
void GetJungleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks);
/// Generates an image of a large jungle tree (2x2 trunk)
void GetLargeJungleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks);
/// Generates an image of a small jungle tree (1x1 trunk)
void GetSmallJungleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks);