diff --git a/VC2008/MCServer.vcproj b/VC2008/MCServer.vcproj index b1e126ad9..7bc0d9323 100644 --- a/VC2008/MCServer.vcproj +++ b/VC2008/MCServer.vcproj @@ -1744,6 +1744,10 @@ RelativePath="..\source\Generating\ComposableGenerator.h" > + + diff --git a/source/Generating/ChunkDesc.cpp b/source/Generating/ChunkDesc.cpp index ff331e6e4..5c59055f7 100644 --- a/source/Generating/ChunkDesc.cpp +++ b/source/Generating/ChunkDesc.cpp @@ -558,9 +558,11 @@ void cChunkDesc::VerifyHeightmap(void) { for (int y = cChunkDef::Height - 1; y > 0; y--) { - if (GetBlockType(x, y, z) != E_BLOCK_AIR) + BLOCKTYPE BlockType = GetBlockType(x, y, z); + if (BlockType != E_BLOCK_AIR) { - ASSERT(GetHeight(x, z) == y); + int Height = GetHeight(x, z); + ASSERT(Height == y); break; } } // for y diff --git a/source/Generating/ComposableGenerator.cpp b/source/Generating/ComposableGenerator.cpp index 178ed4537..0dd14aa50 100644 --- a/source/Generating/ComposableGenerator.cpp +++ b/source/Generating/ComposableGenerator.cpp @@ -18,6 +18,7 @@ #include "FinishGen.h" #include "Caves.h" +#include "DistortedHeightmap.h" #include "MineShafts.h" #include "Noise3DGenerator.h" #include "Ravines.h" @@ -37,7 +38,9 @@ cComposableGenerator::cComposableGenerator(cChunkGenerator & a_ChunkGenerator) : m_HeightGen(NULL), m_CompositionGen(NULL), m_Noise3DComposable(NULL), - m_NumNoise3DComposableUses(0) + m_NumNoise3DComposableUses(0), + m_DistortedHeightmap(NULL), + m_NumDistortedHeightmapUses(0) { } @@ -60,7 +63,16 @@ cComposableGenerator::~cComposableGenerator() m_StructureGens.clear(); // CompositionGen must not be freed if it is shared between HeightGenCache and CompositionGen: - if ((m_NumNoise3DComposableUses < 2) || (m_CompositionGen != m_Noise3DComposable)) + int NumUsed = 1; + if (m_CompositionGen == m_Noise3DComposable) + { + NumUsed = m_NumNoise3DComposableUses; + } + else if (m_CompositionGen == m_DistortedHeightmap) + { + NumUsed = m_NumDistortedHeightmapUses; + } + if (NumUsed == 1) { delete m_CompositionGen; } @@ -241,6 +253,17 @@ void cComposableGenerator::InitHeightGen(cIniFile & a_IniFile) float HeightAmp3 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightAmp3", 0.5); m_HeightGen = new cHeiGenClassic(Seed, HeightFreq1, HeightAmp1, HeightFreq2, HeightAmp2, HeightFreq3, HeightAmp3); } + else if (NoCaseCompare(HeightGenName, "DistortedHeightmap") == 0) + { + if (m_DistortedHeightmap == NULL) + { + m_DistortedHeightmap = new cDistortedHeightmap(Seed, *m_BiomeGen); + m_DistortedHeightmap->Initialize(a_IniFile); + } + m_HeightGen = m_DistortedHeightmap; + m_NumDistortedHeightmapUses++; + // TODO: Optimize by sharing with CompoGen + } else if (NoCaseCompare(HeightGenName, "Noise3D") == 0) { if (m_Noise3DComposable == NULL) @@ -327,6 +350,16 @@ void cComposableGenerator::InitCompositionGen(cIniFile & a_IniFile) BlockBeachBottom, BlockSea ); } + else if (NoCaseCompare(CompoGenName, "DistortedHeightmap") == 0) + { + if (m_DistortedHeightmap == NULL) + { + m_DistortedHeightmap = new cDistortedHeightmap(m_ChunkGenerator.GetSeed(), *m_BiomeGen); + m_DistortedHeightmap->Initialize(a_IniFile); + } + m_CompositionGen = m_DistortedHeightmap; + m_NumDistortedHeightmapUses++; + } else if (NoCaseCompare(CompoGenName, "nether") == 0) { m_CompositionGen = new cCompoGenNether(m_ChunkGenerator.GetSeed()); diff --git a/source/Generating/ComposableGenerator.h b/source/Generating/ComposableGenerator.h index fb8e4fdc6..ee38bdb9d 100644 --- a/source/Generating/ComposableGenerator.h +++ b/source/Generating/ComposableGenerator.h @@ -27,6 +27,9 @@ See http://forum.mc-server.org/showthread.php?tid=409 for details. // fwd: Noise3DGenerator.h class cNoise3DComposable; +// fwd: DistortedHeightmap.h +class cDistortedHeightmap; + @@ -146,8 +149,11 @@ protected: cFinishGenList m_FinishGens; // Specific generators that can be reused for different purposes - we don't want to create multiple objects for them - cNoise3DComposable * m_Noise3DComposable; - int m_NumNoise3DComposableUses; // How many times is it actually used? + cNoise3DComposable * m_Noise3DComposable; + int m_NumNoise3DComposableUses; ///< How many times is it actually used? + cDistortedHeightmap * m_DistortedHeightmap; + int m_NumDistortedHeightmapUses; ///< How many times is it actually used? + /// Reads the biome gen settings from the ini and initializes m_BiomeGen accordingly void InitBiomeGen(cIniFile & a_IniFile); diff --git a/source/Generating/DistortedHeightmap.cpp b/source/Generating/DistortedHeightmap.cpp new file mode 100644 index 000000000..b495aab22 --- /dev/null +++ b/source/Generating/DistortedHeightmap.cpp @@ -0,0 +1,341 @@ + +// 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" + + + + + +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_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_NoiseArrayX(m_NoiseArray), + m_NoiseArrayZ(m_NoiseArray + 17 * 17 * 32), + m_BiomeGen(a_BiomeGen), + m_HeightGen(new cHeiGenBiomal(a_Seed, a_BiomeGen), 64) +{ +} + + + + + +void cDistortedHeightmap::Initialize(cIniFile & a_IniFile) +{ + // 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); +} + + + + + +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; + + GenerateNoiseArray(m_NoiseArrayX, m_Noise1, m_Noise2, m_Noise3); + UpdateDistortAmps(); +} + + + + + +void cDistortedHeightmap::GenerateNoiseArray(NOISE_DATATYPE * a_NoiseArray, cNoise & a_Noise1, cNoise & a_Noise2, cNoise & a_Noise3) +{ + // Parameters: + const int INTERPOL_X = 8; + const int INTERPOL_Y = 4; + const int INTERPOL_Z = 8; + + ASSERT((NOISE_SIZE_Y % INTERPOL_Y) == 1); // Interpolation needs to set the 0-th and the last element + + int idx = 0; + for (int y = 0; y < NOISE_SIZE_Y; y += INTERPOL_Y) + { + NOISE_DATATYPE NoiseY = ((NOISE_DATATYPE)y) / m_FrequencyY; + NOISE_DATATYPE * CurFloor = &(a_NoiseArray[y * 17 * 17]); + for (int z = 0; z < 17; z += INTERPOL_Z) + { + NOISE_DATATYPE NoiseZ = ((NOISE_DATATYPE)(m_CurChunkZ * cChunkDef::Width + z)) / m_FrequencyZ; + for (int x = 0; x < 17; x += INTERPOL_X) + { + NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(m_CurChunkX * cChunkDef::Width + x)) / m_FrequencyX; + CurFloor[x + 17 * z] = + a_Noise1.CubicNoise3D(NoiseX, NoiseY, NoiseZ) * (NOISE_DATATYPE)0.5 + + a_Noise2.CubicNoise3D(NoiseX / 2, NoiseY / 2, NoiseZ / 2) + + a_Noise3.CubicNoise3D(NoiseX / 4, NoiseY / 4, NoiseZ / 4) * 2; + } + } + // Linear-interpolate this XZ floor: + ArrayLinearInterpolate2D(CurFloor, 17, 17, INTERPOL_X, INTERPOL_Z); + } // for y + + // Finish the 3D linear interpolation by interpolating between each XZ-floors on the Y axis + for (int y = 1; y < NOISE_SIZE_Y - 1; y++) + { + if ((y % INTERPOL_Y) == 0) + { + // This is the interpolation source floor, already calculated + continue; + } + int LoFloorY = (y / INTERPOL_Y) * INTERPOL_Y; + int HiFloorY = LoFloorY + INTERPOL_Y; + NOISE_DATATYPE * LoFloor = &(a_NoiseArray[LoFloorY * 17 * 17]); + NOISE_DATATYPE * HiFloor = &(a_NoiseArray[HiFloorY * 17 * 17]); + NOISE_DATATYPE * CurFloor = &(a_NoiseArray[y * 17 * 17]); + NOISE_DATATYPE Ratio = ((NOISE_DATATYPE)(y % INTERPOL_Y)) / INTERPOL_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; + } // for x + idx += 1; // Skipping one X column + } // for z + } // for y +} + + + + + +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 * z; + cChunkDef::SetHeight(a_HeightMap, x, z, m_SeaLevel - 1); + for (int y = cChunkDef::Height - 1; y > m_SeaLevel - 1; y--) + { + int HeightMapHeight = GetValue(NoiseArrayIdx + 17 * 17 * y, x, z); + if (y < HeightMapHeight) + { + cChunkDef::SetHeight(a_HeightMap, x, z, y); + break; + } + } // for y + } // for x + } // for z +} + + + + + +void cDistortedHeightmap::ComposeTerrain(cChunkDesc & a_ChunkDesc) +{ + PrepareState(a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ()); + 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 * z; + int LastAir = a_ChunkDesc.GetHeight(x, z) + 1; + bool HasHadWater = false; + for (int y = LastAir; y > 0; y--) + { + int HeightMapHeight = GetValue(NoiseArrayIdx + 17 * 17 * y, x, z); + + 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 (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 +} + + + + + +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); + cChunkDef::HeightMap Heightmap; + m_HeightGen.GenHeightMap(ChunkX, ChunkZ, Heightmap); + return cChunkDef::GetHeight(Heightmap, RelX, RelZ); +} + + + + + +int cDistortedHeightmap::GetValue(int a_NoiseArrayIdx, int a_RelX, int a_RelZ) +{ + int AmpIdx = a_RelX + 17 * a_RelZ; + NOISE_DATATYPE DistX = m_NoiseArrayX[a_NoiseArrayIdx] * m_DistortAmpX[AmpIdx]; + NOISE_DATATYPE DistZ = m_NoiseArrayZ[a_NoiseArrayIdx] * m_DistortAmpZ[AmpIdx]; + DistX += (NOISE_DATATYPE)(m_CurChunkX * cChunkDef::Width + a_RelX); + DistZ += (NOISE_DATATYPE)(m_CurChunkZ * cChunkDef::Width + a_RelZ); + return GetHeightmapAt(DistX, DistZ); +} + + + + + +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 + + // Linearly interpolate 4x4 blocks of Amps: + const int STEPZ = 4; // Must be a divisor of 16 + const int STEPX = 4; // Must be a divisor of 16 + for (int z = 0; z < 17; z += STEPZ) + { + for (int x = 0; x < 17; x += STEPX) + { + GetDistortAmpsAt(Biomes, x, z, m_DistortAmpX[x + 17 * z], m_DistortAmpZ[x + 17 * z]); + } + } + ArrayLinearInterpolate2D(m_DistortAmpX, 17, 17, STEPX, STEPZ); + ArrayLinearInterpolate2D(m_DistortAmpZ, 17, 17, STEPX, STEPZ); +} + + + + + +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; +} + + + + diff --git a/source/Generating/DistortedHeightmap.h b/source/Generating/DistortedHeightmap.h new file mode 100644 index 000000000..8c1b136b8 --- /dev/null +++ b/source/Generating/DistortedHeightmap.h @@ -0,0 +1,93 @@ + +// 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); + + void Initialize(cIniFile & a_IniFile); + +protected: + typedef cChunkDef::BiomeMap BiomeNeighbors[3][3]; + + cNoise m_Noise1; + cNoise m_Noise2; + cNoise m_Noise3; + cNoise m_Noise4; + cNoise m_Noise5; + + int m_SeaLevel; + NOISE_DATATYPE m_FrequencyX; + NOISE_DATATYPE m_FrequencyY; + NOISE_DATATYPE m_FrequencyZ; + + int m_CurChunkX; + int m_CurChunkZ; + NOISE_DATATYPE m_NoiseArray[17 * 17 * NOISE_SIZE_Y]; + NOISE_DATATYPE * m_NoiseArrayX; + NOISE_DATATYPE * m_NoiseArrayZ; + + cBiomeGen & m_BiomeGen; + cHeiGenCache m_HeightGen; // This generator provides us with base heightmap (before distortion) + + // Per-biome terrain generator parameters: + struct sGenParam + { + NOISE_DATATYPE m_DistortAmpX; + NOISE_DATATYPE m_DistortAmpZ; + } ; + static const sGenParam m_GenParam[biNumBiomes]; + + NOISE_DATATYPE m_DistortAmpX[17 * 17]; + NOISE_DATATYPE m_DistortAmpZ[17 * 17]; + + + /// 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 3D noise array using the specified noise objects + void GenerateNoiseArray(NOISE_DATATYPE * a_NoiseArray, cNoise & a_Noise1, cNoise & a_Noise2, cNoise & a_Noise3); + + /// Calculates the heightmap value (before distortion) at the specified (floating-point) coords + int GetHeightmapAt(NOISE_DATATYPE a_X, NOISE_DATATYPE a_Z); + + /// Calculates the height (after distortion) + int GetValue(int a_NoiseArrayIdx, int a_RelX, int a_RelZ); + + /// 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); + + // 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; +} ; diff --git a/source/Generating/HeiGen.cpp b/source/Generating/HeiGen.cpp index fe69ef926..24042e3ff 100644 --- a/source/Generating/HeiGen.cpp +++ b/source/Generating/HeiGen.cpp @@ -62,11 +62,13 @@ cHeiGenCache::~cHeiGenCache() 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++) { diff --git a/source/Generating/HeiGen.h b/source/Generating/HeiGen.h index a883330b3..3bdfef993 100644 --- a/source/Generating/HeiGen.h +++ b/source/Generating/HeiGen.h @@ -47,6 +47,9 @@ public: cHeiGenCache(cTerrainHeightGen * a_HeiGenToCache, int a_CacheSize); // Takes ownership of a_HeiGenToCache ~cHeiGenCache(); + // cTerrainHeightGen override: + virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override; + protected: cTerrainHeightGen * m_HeiGenToCache; @@ -67,8 +70,6 @@ protected: 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 GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override; } ;