Moved source to src
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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)
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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[]
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
} ;
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
} ;
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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
@@ -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;
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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[]
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user