diff options
author | Mattes D <github@xoft.cz> | 2014-11-04 15:45:08 +0100 |
---|---|---|
committer | Mattes D <github@xoft.cz> | 2014-11-04 15:45:08 +0100 |
commit | 6201b3602c2f82fd9de68404a9e742c9cf3c6554 (patch) | |
tree | e086038a40a07eba6be7a10a18f9cc95527fc729 /src/Generating | |
parent | Moved the chrono include into Globals. (diff) | |
parent | WebAdmin: Fixed login template destination address. (diff) | |
download | cuberite-6201b3602c2f82fd9de68404a9e742c9cf3c6554.tar cuberite-6201b3602c2f82fd9de68404a9e742c9cf3c6554.tar.gz cuberite-6201b3602c2f82fd9de68404a9e742c9cf3c6554.tar.bz2 cuberite-6201b3602c2f82fd9de68404a9e742c9cf3c6554.tar.lz cuberite-6201b3602c2f82fd9de68404a9e742c9cf3c6554.tar.xz cuberite-6201b3602c2f82fd9de68404a9e742c9cf3c6554.tar.zst cuberite-6201b3602c2f82fd9de68404a9e742c9cf3c6554.zip |
Diffstat (limited to '')
-rw-r--r-- | src/Generating/BioGen.cpp | 268 | ||||
-rw-r--r-- | src/Generating/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/Generating/DistortedHeightmap.cpp | 28 | ||||
-rw-r--r-- | src/Generating/FinishGen.cpp | 10 | ||||
-rw-r--r-- | src/Generating/HeiGen.cpp | 383 | ||||
-rw-r--r-- | src/Generating/IntGen.h | 1406 | ||||
-rw-r--r-- | src/Generating/ProtIntGen.h | 1351 |
7 files changed, 3345 insertions, 103 deletions
diff --git a/src/Generating/BioGen.cpp b/src/Generating/BioGen.cpp index 203faff56..2a4dbe794 100644 --- a/src/Generating/BioGen.cpp +++ b/src/Generating/BioGen.cpp @@ -5,6 +5,10 @@ #include "Globals.h" #include "BioGen.h" +#include <chrono> +#include <iostream> +#include "IntGen.h" +#include "ProtIntGen.h" #include "../IniFile.h" #include "../LinearUpscale.h" @@ -917,6 +921,214 @@ void cBioGenTwoLevel::InitializeBiomeGen(cIniFile & a_IniFile) //////////////////////////////////////////////////////////////////////////////// +// cBioGenGrown: + +class cBioGenGrown: + public cBiomeGen +{ +public: + cBioGenGrown(int a_Seed) + { + auto FinalRivers = + std::make_shared<cIntGenSmooth<8>> (a_Seed + 1, + std::make_shared<cIntGenZoom <10>> (a_Seed + 2, + std::make_shared<cIntGenRiver <7>> (a_Seed + 3, + std::make_shared<cIntGenZoom <9>> (a_Seed + 4, + std::make_shared<cIntGenSmooth<6>> (a_Seed + 5, + std::make_shared<cIntGenZoom <8>> (a_Seed + 8, + std::make_shared<cIntGenSmooth<6>> (a_Seed + 5, + std::make_shared<cIntGenZoom <8>> (a_Seed + 9, + std::make_shared<cIntGenSmooth<6>> (a_Seed + 5, + std::make_shared<cIntGenZoom <8>> (a_Seed + 10, + std::make_shared<cIntGenSmooth<6>> (a_Seed + 5, + std::make_shared<cIntGenSmooth<8>> (a_Seed + 6, + std::make_shared<cIntGenZoom <10>> (a_Seed + 11, + std::make_shared<cIntGenChoice<2, 7>>(a_Seed + 12 + )))))))))))))); + + auto alteration = + std::make_shared<cIntGenZoom <8>>(a_Seed, + std::make_shared<cIntGenLandOcean<6>>(a_Seed, 20 + )); + + auto alteration2 = + std::make_shared<cIntGenZoom <8>>(a_Seed + 1, + std::make_shared<cIntGenZoom <6>>(a_Seed + 2, + std::make_shared<cIntGenZoom <5>>(a_Seed + 1, + std::make_shared<cIntGenZoom <4>>(a_Seed + 2, + std::make_shared<cIntGenLandOcean<4>>(a_Seed + 1, 10 + ))))); + + auto FinalBiomes = + std::make_shared<cIntGenSmooth <8>> (a_Seed + 1, + std::make_shared<cIntGenZoom <10>>(a_Seed + 15, + std::make_shared<cIntGenSmooth <7>> (a_Seed + 1, + std::make_shared<cIntGenZoom <9>> (a_Seed + 16, + std::make_shared<cIntGenBeaches <6>> ( + std::make_shared<cIntGenZoom <8>> (a_Seed + 1, + std::make_shared<cIntGenAddIslands <6>> (a_Seed + 2004, 10, + std::make_shared<cIntGenAddToOcean <6>> (a_Seed + 10, 500, biDeepOcean, + std::make_shared<cIntGenReplaceRandomly<8>> (a_Seed + 1, biPlains, biSunflowerPlains, 20, + std::make_shared<cIntGenMBiomes <8>> (a_Seed + 5, alteration2, + std::make_shared<cIntGenAlternateBiomes<8>> (a_Seed + 1, alteration, + std::make_shared<cIntGenBiomeEdges <8>> (a_Seed + 3, + std::make_shared<cIntGenZoom <10>>(a_Seed + 2, + std::make_shared<cIntGenZoom <7>> (a_Seed + 4, + std::make_shared<cIntGenReplaceRandomly<5>> (a_Seed + 99, biIcePlains, biIcePlainsSpikes, 50, + std::make_shared<cIntGenZoom <5>> (a_Seed + 8, + std::make_shared<cIntGenAddToOcean <4>> (a_Seed + 10, 300, biDeepOcean, + std::make_shared<cIntGenAddToOcean <6>> (a_Seed + 9, 8, biMushroomIsland, + std::make_shared<cIntGenBiomes <8>> (a_Seed + 3000, + std::make_shared<cIntGenAddIslands <8>> (a_Seed + 2000, 200, + std::make_shared<cIntGenZoom <8>> (a_Seed + 5, + std::make_shared<cIntGenRareBiomeGroups<6>> (a_Seed + 5, 50, + std::make_shared<cIntGenBiomeGroupEdges<6>> ( + std::make_shared<cIntGenAddIslands <8>> (a_Seed + 2000, 200, + std::make_shared<cIntGenZoom <8>> (a_Seed + 7, + std::make_shared<cIntGenSetRandomly <6>> (a_Seed + 8, 50, bgOcean, + std::make_shared<cIntGenReplaceRandomly<6>> (a_Seed + 101, bgIce, bgTemperate, 150, + std::make_shared<cIntGenAddIslands <6>> (a_Seed + 2000, 200, + std::make_shared<cIntGenSetRandomly <6>> (a_Seed + 9, 50, bgOcean, + std::make_shared<cIntGenZoom <6>> (a_Seed + 10, + std::make_shared<cIntGenLandOcean <5>> (a_Seed + 100, 30 + ))))))))))))))))))))))))))))))); + + m_Gen = + std::make_shared<cIntGenSmooth <16>>(a_Seed, + std::make_shared<cIntGenZoom <18>>(a_Seed, + std::make_shared<cIntGenSmooth <11>>(a_Seed, + std::make_shared<cIntGenZoom <13>>(a_Seed, + std::make_shared<cIntGenMixRivers<8>> ( + FinalBiomes, FinalRivers + ))))); + } + + virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_Biomes) override + { + cIntGen<16, 16>::Values vals; + m_Gen->GetInts(a_ChunkX * cChunkDef::Width, a_ChunkZ * cChunkDef::Width, vals); + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int x = 0; x < cChunkDef::Width; x++) + { + cChunkDef::SetBiome(a_Biomes, x, z, (EMCSBiome)vals[x + cChunkDef::Width * z]); + } + } + } + +protected: + std::shared_ptr<cIntGen<16, 16>> m_Gen; +}; + + + + + +//////////////////////////////////////////////////////////////////////////////// +// cBioGenGrown: + +class cBioGenProtGrown: + public cBiomeGen +{ +public: + cBioGenProtGrown(int a_Seed) + { + auto FinalRivers = + std::make_shared<cProtIntGenSmooth>(a_Seed + 1, + std::make_shared<cProtIntGenZoom >(a_Seed + 2, + std::make_shared<cProtIntGenRiver >(a_Seed + 3, + std::make_shared<cProtIntGenZoom >(a_Seed + 4, + std::make_shared<cProtIntGenSmooth>(a_Seed + 5, + std::make_shared<cProtIntGenZoom >(a_Seed + 8, + std::make_shared<cProtIntGenSmooth>(a_Seed + 5, + std::make_shared<cProtIntGenZoom >(a_Seed + 9, + std::make_shared<cProtIntGenSmooth>(a_Seed + 5, + std::make_shared<cProtIntGenZoom >(a_Seed + 10, + std::make_shared<cProtIntGenSmooth>(a_Seed + 5, + std::make_shared<cProtIntGenSmooth>(a_Seed + 6, + std::make_shared<cProtIntGenZoom >(a_Seed + 11, + std::make_shared<cProtIntGenChoice>(a_Seed + 12, 2 + )))))))))))))); + + auto alteration = + std::make_shared<cProtIntGenZoom >(a_Seed, + std::make_shared<cProtIntGenLandOcean>(a_Seed, 20 + )); + + auto alteration2 = + std::make_shared<cProtIntGenZoom >(a_Seed + 1, + std::make_shared<cProtIntGenZoom >(a_Seed + 2, + std::make_shared<cProtIntGenZoom >(a_Seed + 1, + std::make_shared<cProtIntGenZoom >(a_Seed + 2, + std::make_shared<cProtIntGenLandOcean>(a_Seed + 1, 10 + ))))); + + auto FinalBiomes = + std::make_shared<cProtIntGenSmooth >(a_Seed + 1, + std::make_shared<cProtIntGenZoom >(a_Seed + 15, + std::make_shared<cProtIntGenSmooth >(a_Seed + 1, + std::make_shared<cProtIntGenZoom >(a_Seed + 16, + std::make_shared<cProtIntGenBeaches >( + std::make_shared<cProtIntGenZoom >(a_Seed + 1, + std::make_shared<cProtIntGenAddIslands >(a_Seed + 2004, 10, + std::make_shared<cProtIntGenAddToOcean >(a_Seed + 10, 500, biDeepOcean, + std::make_shared<cProtIntGenReplaceRandomly>(a_Seed + 1, biPlains, biSunflowerPlains, 20, + std::make_shared<cProtIntGenMBiomes >(a_Seed + 5, alteration2, + std::make_shared<cProtIntGenAlternateBiomes>(a_Seed + 1, alteration, + std::make_shared<cProtIntGenBiomeEdges >(a_Seed + 3, + std::make_shared<cProtIntGenZoom >(a_Seed + 2, + std::make_shared<cProtIntGenZoom >(a_Seed + 4, + std::make_shared<cProtIntGenReplaceRandomly>(a_Seed + 99, biIcePlains, biIcePlainsSpikes, 50, + std::make_shared<cProtIntGenZoom >(a_Seed + 8, + std::make_shared<cProtIntGenAddToOcean >(a_Seed + 10, 300, biDeepOcean, + std::make_shared<cProtIntGenAddToOcean >(a_Seed + 9, 8, biMushroomIsland, + std::make_shared<cProtIntGenBiomes >(a_Seed + 3000, + std::make_shared<cProtIntGenAddIslands >(a_Seed + 2000, 200, + std::make_shared<cProtIntGenZoom >(a_Seed + 5, + std::make_shared<cProtIntGenRareBiomeGroups>(a_Seed + 5, 50, + std::make_shared<cProtIntGenBiomeGroupEdges>( + std::make_shared<cProtIntGenAddIslands >(a_Seed + 2000, 200, + std::make_shared<cProtIntGenZoom >(a_Seed + 7, + std::make_shared<cProtIntGenSetRandomly >(a_Seed + 8, 50, bgOcean, + std::make_shared<cProtIntGenReplaceRandomly>(a_Seed + 101, bgIce, bgTemperate, 150, + std::make_shared<cProtIntGenAddIslands >(a_Seed + 2000, 200, + std::make_shared<cProtIntGenSetRandomly >(a_Seed + 9, 50, bgOcean, + std::make_shared<cProtIntGenZoom >(a_Seed + 10, + std::make_shared<cProtIntGenLandOcean >(a_Seed + 100, 30 + ))))))))))))))))))))))))))))))); + + m_Gen = + std::make_shared<cProtIntGenSmooth >(a_Seed, + std::make_shared<cProtIntGenZoom >(a_Seed, + std::make_shared<cProtIntGenSmooth >(a_Seed, + std::make_shared<cProtIntGenZoom >(a_Seed, + std::make_shared<cProtIntGenMixRivers>( + FinalBiomes, FinalRivers + ))))); + } + + virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_Biomes) override + { + int vals[16 * 16]; + m_Gen->GetInts(a_ChunkX * cChunkDef::Width, a_ChunkZ * cChunkDef::Width, 16, 16, vals); + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int x = 0; x < cChunkDef::Width; x++) + { + cChunkDef::SetBiome(a_Biomes, x, z, (EMCSBiome)vals[x + cChunkDef::Width * z]); + } + } + } + +protected: + std::shared_ptr<cProtIntGen> m_Gen; +}; + + + + + +//////////////////////////////////////////////////////////////////////////////// // cBiomeGen: cBiomeGenPtr cBiomeGen::CreateBiomeGen(cIniFile & a_IniFile, int a_Seed, bool & a_CacheOffByDefault) @@ -952,6 +1164,14 @@ cBiomeGenPtr cBiomeGen::CreateBiomeGen(cIniFile & a_IniFile, int a_Seed, bool & { res = new cBioGenTwoLevel(a_Seed); } + else if (NoCaseCompare(BiomeGenName, "grown") == 0) + { + res = new cBioGenGrown(a_Seed); + } + else if (NoCaseCompare(BiomeGenName, "grownprot") == 0) + { + res = new cBioGenProtGrown(a_Seed); + } else { if (NoCaseCompare(BiomeGenName, "multistepmap") != 0) @@ -981,3 +1201,51 @@ cBiomeGenPtr cBiomeGen::CreateBiomeGen(cIniFile & a_IniFile, int a_Seed, bool & + +//////////////////////////////////////////////////////////////////////////////// +// Performance tests: + +// Change to 1 to enable the perf test: +#if 0 + +class cBioGenPerfTest +{ +public: + cBioGenPerfTest() + { + std::cout << "BioGen performance tests commencing, please wait..." << std::endl; + TestGen("MultiStepMap", std::make_unique<cBioGenMultiStepMap>(1).get()); + TestGen("Grown", std::make_unique<cBioGenGrown>(1).get()); + TestGen("GrownProt", std::make_unique<cBioGenProtGrown>(1).get()); + std::cout << "BioGen performance tests complete." << std::endl; + } + +protected: + void TestGen(const AString && a_GenName, cBiomeGen * a_BioGen) + { + // Initialize the default settings for the generator: + cIniFile iniFile; + a_BioGen->InitializeBiomeGen(iniFile); + + // Generate the biomes: + auto start = std::chrono::system_clock::now(); + for (int z = 0; z < 100; z++) + { + for (int x = 0; x < 100; x++) + { + cChunkDef::BiomeMap biomes; + a_BioGen->GenBiomes(x, z, biomes); + } // for x + } // for z + auto dur = std::chrono::system_clock::now() - start; + double milliseconds = static_cast<double>((std::chrono::duration_cast<std::chrono::milliseconds>(dur)).count()); + + std::cout << a_GenName << ": " << 1000.0 * 100.0 * 100.0 / milliseconds << " chunks per second" << std::endl; + } +} g_BioGenPerfTest; + +#endif + + + + diff --git a/src/Generating/CMakeLists.txt b/src/Generating/CMakeLists.txt index cd3d5a9f3..1a26bd0d5 100644 --- a/src/Generating/CMakeLists.txt +++ b/src/Generating/CMakeLists.txt @@ -46,6 +46,7 @@ SET (HDRS FinishGen.h GridStructGen.h HeiGen.h + IntGen.h MineShafts.h NetherFortGen.h Noise3DGenerator.h @@ -53,6 +54,7 @@ SET (HDRS PieceGenerator.h Prefab.h PrefabPiecePool.h + ProtIntGen.h RainbowRoadsGen.h Ravines.h RoughRavines.h diff --git a/src/Generating/DistortedHeightmap.cpp b/src/Generating/DistortedHeightmap.cpp index aac1d2bf3..d5bc6ab55 100644 --- a/src/Generating/DistortedHeightmap.cpp +++ b/src/Generating/DistortedHeightmap.cpp @@ -227,15 +227,15 @@ const cDistortedHeightmap::sGenParam cDistortedHeightmap::m_GenParam[256] = /* biMesaPlateau */ { 2.0f, 2.0f}, // 39 // biomes 40 .. 128 are unused, 89 empty placeholders here: - {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, // 40 .. 49 - {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, // 50 .. 59 - {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, // 60 .. 69 - {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, // 70 .. 79 - {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, // 80 .. 89 - {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, // 90 .. 99 - {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, // 100 .. 109 - {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, // 110 .. 119 - {}, {}, {}, {}, {}, {}, {}, {}, {}, // 120 .. 128 + {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, // 40 .. 49 + {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, // 50 .. 59 + {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, // 60 .. 69 + {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, // 70 .. 79 + {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, // 80 .. 89 + {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, // 90 .. 99 + {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, // 100 .. 109 + {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, // 110 .. 119 + {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, // 120 .. 128 // Release 1.7 /* biome variants: /* biSunflowerPlains */ { 1.0f, 1.0f}, // 129 @@ -246,22 +246,22 @@ const cDistortedHeightmap::sGenParam cDistortedHeightmap::m_GenParam[256] = /* biSwamplandM */ { 0.0f, 0.0f}, // 134 // Biomes 135 .. 139 unused, 5 empty placeholders here: - {}, {}, {}, {}, {}, // 135 .. 139 + {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, // 135 .. 139 /* biIcePlainsSpikes */ { 1.0f, 1.0f}, // 140 // Biomes 141 .. 148 unused, 8 empty placeholders here: - {}, {}, {}, {}, {}, {}, {}, {}, // 141 .. 148 + {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, // 141 .. 148 /* biJungleM */ { 4.0f, 4.0f}, // 149 - {}, // 150 + {0.0f, 0.0f}, // 150 /* biJungleEdgeM */ { 3.0f, 3.0f}, // 151 - {}, {}, {}, // 152 .. 154 + {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, // 152 .. 154 /* biBirchForestM */ { 3.0f, 3.0f}, // 155 /* biBirchForestHillsM */ { 5.0f, 5.0f}, // 156 /* biRoofedForestM */ { 2.0f, 2.0f}, // 157 /* biColdTaigaM */ { 1.0f, 1.0f}, // 158 - {}, // 159 + {0.0f, 0.0f}, // 159 /* biMegaSpruceTaiga */ { 3.0f, 3.0f}, // 160 /* biMegaSpruceTaigaHills */ { 3.0f, 3.0f}, // 161 /* biExtremeHillsPlusM */ {32.0f, 32.0f}, // 162 diff --git a/src/Generating/FinishGen.cpp b/src/Generating/FinishGen.cpp index 0564789dc..18f8ee2bc 100644 --- a/src/Generating/FinishGen.cpp +++ b/src/Generating/FinishGen.cpp @@ -414,6 +414,11 @@ void cFinishGenSnow::GenFinish(cChunkDesc & a_ChunkDesc) } break; } + default: + { + // There's no snow in the other biomes. + break; + } } } } // for z @@ -454,6 +459,11 @@ void cFinishGenIce::GenFinish(cChunkDesc & a_ChunkDesc) } break; } + default: + { + // No icy water in other biomes. + break; + } } } } // for z diff --git a/src/Generating/HeiGen.cpp b/src/Generating/HeiGen.cpp index a0b8770f5..28a5698e4 100644 --- a/src/Generating/HeiGen.cpp +++ b/src/Generating/HeiGen.cpp @@ -17,81 +17,6 @@ //////////////////////////////////////////////////////////////////////////////// -// cTerrainHeightGen: - -cTerrainHeightGenPtr cTerrainHeightGen::CreateHeightGen(cIniFile & a_IniFile, cBiomeGenPtr a_BiomeGen, int a_Seed, bool & a_CacheOffByDefault) -{ - AString HeightGenName = a_IniFile.GetValueSet("Generator", "HeightGen", ""); - if (HeightGenName.empty()) - { - LOGWARN("[Generator] HeightGen value not set in world.ini, using \"Biomal\"."); - HeightGenName = "Biomal"; - } - - a_CacheOffByDefault = false; - cTerrainHeightGen * res = nullptr; - if (NoCaseCompare(HeightGenName, "flat") == 0) - { - res = new cHeiGenFlat; - a_CacheOffByDefault = true; // We're generating faster than a cache would retrieve data - } - else if (NoCaseCompare(HeightGenName, "classic") == 0) - { - res = new cHeiGenClassic(a_Seed); - } - else if (NoCaseCompare(HeightGenName, "DistortedHeightmap") == 0) - { - res = new cDistortedHeightmap(a_Seed, a_BiomeGen); - } - else if (NoCaseCompare(HeightGenName, "End") == 0) - { - res = new cEndGen(a_Seed); - } - else if (NoCaseCompare(HeightGenName, "Mountains") == 0) - { - res = new cHeiGenMountains(a_Seed); - } - else if (NoCaseCompare(HeightGenName, "Noise3D") == 0) - { - res = new cNoise3DComposable(a_Seed); - } - else if (NoCaseCompare(HeightGenName, "biomal") == 0) - { - res = new cHeiGenBiomal(a_Seed, a_BiomeGen); - - /* - // Performance-testing: - LOGINFO("Measuring performance of cHeiGenBiomal..."); - clock_t BeginTick = clock(); - for (int x = 0; x < 500; x++) - { - cChunkDef::HeightMap Heights; - res->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); - //*/ - } - else - { - // No match found, force-set the default and retry - LOGWARN("Unknown HeightGen \"%s\", using \"Biomal\" instead.", HeightGenName.c_str()); - a_IniFile.DeleteValue("Generator", "HeightGen"); - a_IniFile.SetValue("Generator", "HeightGen", "Biomal"); - return CreateHeightGen(a_IniFile, a_BiomeGen, a_Seed, a_CacheOffByDefault); - } - - // Read the settings: - res->InitializeHeightGen(a_IniFile); - - return cTerrainHeightGenPtr(res); -} - - - - - -//////////////////////////////////////////////////////////////////////////////// // cHeiGenFlat: void cHeiGenFlat::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) @@ -430,15 +355,15 @@ const cHeiGenBiomal::sGenParam cHeiGenBiomal::m_GenParam[256] = /* biMesaPlateau */ { 0.1f, 1.0f, 0.05f, 1.5f, 0.01f, 4.0f, 80}, // biomes 40 .. 128 are unused, 89 empty placeholders here: - {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, // 40 .. 49 - {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, // 50 .. 59 - {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, // 60 .. 69 - {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, // 70 .. 79 - {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, // 80 .. 89 - {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, // 90 .. 99 - {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, // 100 .. 109 - {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, // 110 .. 119 - {}, {}, {}, {}, {}, {}, {}, {}, {}, // 120 .. 128 + {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, // 40 .. 49 + {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, // 50 .. 59 + {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, // 60 .. 69 + {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, // 70 .. 79 + {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, // 80 .. 89 + {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, // 90 .. 99 + {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, // 100 .. 109 + {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, // 110 .. 119 + {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, // 120 .. 128 /* biSunflowerPlains */ { 0.1f, 2.0f, 0.05f, 12.0f, 0.01f, 10.0f, 40}, // 129 /* biDesertM */ { 0.1f, 2.0f, 0.05f, 12.0f, 0.01f, 10.0f, 40}, // 130 @@ -448,22 +373,22 @@ const cHeiGenBiomal::sGenParam cHeiGenBiomal::m_GenParam[256] = /* biSwamplandM */ { 1.0f, 3.0f, 1.10f, 7.0f, 0.01f, 0.01f, 60}, // 134 // Biomes 135 .. 139 unused, 5 empty placeholders here: - {}, {}, {}, {}, {}, // 135 .. 139 + {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, // 135 .. 139 /* biIcePlainsSpikes */ { 0.1f, 2.0f, 0.05f, 12.0f, 0.01f, 10.0f, 40}, // 140 // Biomes 141 .. 148 unused, 8 empty placeholders here: - {}, {}, {}, {}, {}, {}, {}, {}, // 141 .. 148 + {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, // 141 .. 148 /* biJungleM */ { 0.1f, 3.0f, 0.05f, 6.0f, 0.01f, 6.0f, 70}, // 149 - {}, // 150 + {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, // 150 /* biJungleEdgeM */ { 0.1f, 3.0f, 0.05f, 6.0f, 0.01f, 6.0f, 70}, // 151 - {}, {}, {}, // 152 .. 154 + {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, // 152 .. 154 /* biBirchForestM */ { 0.1f, 1.0f, 0.05f, 2.0f, 0.01f, 4.0f, 70}, // 155 /* biBirchForestHillsM */ { 0.2f, 2.0f, 0.05f, 10.0f, 0.01f, 8.0f, 80}, // 156 /* biRoofedForestM */ { 0.1f, 1.0f, 0.05f, 2.0f, 0.01f, 4.0f, 70}, // 157 /* biColdTaigaM */ { 0.1f, 1.0f, 0.05f, 2.0f, 0.01f, 4.0f, 70}, // 158 - {}, // 159 + {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, // 159 /* biMegaSpruceTaiga */ { 0.1f, 1.0f, 0.05f, 2.0f, 0.01f, 4.0f, 70}, // 160 /* biMegaSpruceTaigaHills */ { 0.2f, 2.0f, 0.05f, 10.0f, 0.01f, 8.0f, 80}, // 161 /* biExtremeHillsPlusM */ { 0.2f, 4.0f, 0.05f, 20.0f, 0.01f, 16.0f, 120}, // 162 @@ -611,3 +536,283 @@ NOISE_DATATYPE cHeiGenBiomal::GetHeightAt(int a_RelX, int a_RelZ, int a_ChunkX, +//////////////////////////////////////////////////////////////////////////////// +// cHeiGenMinMax: + +class cHeiGenMinMax: + public cTerrainHeightGen +{ + typedef cTerrainHeightGen super; + + /** Size of the averaging process, in columns (for each direction). Must be less than 16. */ + static const int AVERAGING_SIZE = 4; + +public: + cHeiGenMinMax(int a_Seed, cBiomeGenPtr a_BiomeGen): + m_Noise(a_Seed), + m_BiomeGen(a_BiomeGen), + m_TotalWeight(0) + { + // Initialize the weights: + for (int z = 0; z <= AVERAGING_SIZE * 2; z++) + { + for (int x = 0; x <= AVERAGING_SIZE * 2; x++) + { + m_Weights[z][x] = 1 + 2 * AVERAGING_SIZE - std::abs(x - AVERAGING_SIZE) - std::abs(z - AVERAGING_SIZE); + m_TotalWeight += m_Weights[z][x]; + } + } + + // Initialize the Perlin generator: + m_Perlin.AddOctave(0.04f, 0.2f); + m_Perlin.AddOctave(0.02f, 0.1f); + m_Perlin.AddOctave(0.01f, 0.05f); + } + + + virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) + { + // Generate the biomes for the 3*3 neighbors: + cChunkDef::BiomeMap neighborBiomes[3][3]; + for (int z = 0; z < 3; z++) for (int x = 0; x < 3; x++) + { + m_BiomeGen->GenBiomes(a_ChunkX + x - 1, a_ChunkZ + z - 1, neighborBiomes[z][x]); + } + + // Get the min and max heights based on the biomes: + double minHeight[cChunkDef::Width * cChunkDef::Width]; + double maxHeight[cChunkDef::Width * cChunkDef::Width]; + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int x = 0; x < cChunkDef::Width; x++) + { + // For each column, sum the min and max values of the neighborhood around it: + double min = 0, max = 0; + for (int relz = 0; relz <= AVERAGING_SIZE * 2; relz++) + { + int bz = z + 16 + relz - AVERAGING_SIZE; // Biome Z coord relative to the neighborBiomes start + int cz = bz / 16; // Chunk Z coord relative to the neighborBiomes start + bz = bz % 16; // Biome Z coord relative to cz in neighborBiomes + for (int relx = 0; relx <= AVERAGING_SIZE * 2; relx++) + { + int bx = x + 16 + relx - AVERAGING_SIZE; // Biome X coord relative to the neighborBiomes start + int cx = bx / 16; // Chunk X coord relative to the neighborBiomes start + bx = bx % 16; // Biome X coord relative to cz in neighborBiomes + + // Get the biome's min and max heights: + double bmin, bmax; + getBiomeMinMax(cChunkDef::GetBiome(neighborBiomes[cz][cx], bx, bz), bmin, bmax); + + // Add them to the total, with the weight depending on their relative position to the column: + min += bmin * m_Weights[relz][relx]; + max += bmax * m_Weights[relz][relx]; + } // for relx + } // for relz + minHeight[x + z * cChunkDef::Width] = min / m_TotalWeight; + maxHeight[x + z * cChunkDef::Width] = max / m_TotalWeight; + } // for x + } // for z + + // Generate the base noise: + NOISE_DATATYPE noise[cChunkDef::Width * cChunkDef::Width]; + NOISE_DATATYPE workspace[cChunkDef::Width * cChunkDef::Width]; + NOISE_DATATYPE startX = static_cast<float>(a_ChunkX * cChunkDef::Width); + NOISE_DATATYPE endX = startX + cChunkDef::Width - 1; + NOISE_DATATYPE startZ = static_cast<float>(a_ChunkZ * cChunkDef::Width); + NOISE_DATATYPE endZ = startZ + cChunkDef::Width - 1; + m_Perlin.Generate2D(noise, 16, 16, startX, endX, startZ, endZ, workspace); + + // Make the height by ranging the noise between min and max: + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int x = 0; x < cChunkDef::Width; x++) + { + double min = minHeight[x + z * cChunkDef::Width]; + double max = maxHeight[x + z * cChunkDef::Width]; + double h = (max + min) / 2 + noise[x + z * cChunkDef::Width] * (max - min); + cChunkDef::SetHeight(a_HeightMap, x, z, static_cast<HEIGHTTYPE>(h)); + } + } + } + + + virtual void InitializeHeightGen(cIniFile & a_IniFile) + { + // No settings available + } + +protected: + cNoise m_Noise; + + cPerlinNoise m_Perlin; + + /** The biome generator to query for the underlying biomes. */ + cBiomeGenPtr m_BiomeGen; + + /** Weights applied to each of the min / max values in the neighborhood of the currently evaluated column. */ + double m_Weights[AVERAGING_SIZE * 2 + 1][AVERAGING_SIZE * 2 + 1]; + + /** Sum of all the m_Weights items. */ + double m_TotalWeight; + + + /** Returns the minimum and maximum heights for the given biome. */ + void getBiomeMinMax(EMCSBiome a_Biome, double & a_Min, double & a_Max) + { + switch (a_Biome) + { + case biBeach: a_Min = 61; a_Max = 64; break; + case biBirchForest: a_Min = 63; a_Max = 75; break; + case biBirchForestHills: a_Min = 63; a_Max = 90; break; + case biBirchForestHillsM: a_Min = 63; a_Max = 90; break; + case biBirchForestM: a_Min = 63; a_Max = 75; break; + case biColdBeach: a_Min = 61; a_Max = 64; break; + case biColdTaiga: a_Min = 63; a_Max = 75; break; + case biColdTaigaHills: a_Min = 63; a_Max = 90; break; + case biColdTaigaM: a_Min = 63; a_Max = 75; break; + case biDeepOcean: a_Min = 30; a_Max = 60; break; + case biDesert: a_Min = 63; a_Max = 70; break; + case biDesertHills: a_Min = 63; a_Max = 85; break; + case biDesertM: a_Min = 63; a_Max = 70; break; + case biEnd: a_Min = 10; a_Max = 100; break; + case biExtremeHills: a_Min = 60; a_Max = 120; break; + case biExtremeHillsEdge: a_Min = 63; a_Max = 100; break; + case biExtremeHillsM: a_Min = 60; a_Max = 120; break; + case biExtremeHillsPlus: a_Min = 60; a_Max = 140; break; + case biExtremeHillsPlusM: a_Min = 60; a_Max = 140; break; + case biFlowerForest: a_Min = 63; a_Max = 75; break; + case biForest: a_Min = 63; a_Max = 75; break; + case biForestHills: a_Min = 63; a_Max = 90; break; + case biFrozenOcean: a_Min = 45; a_Max = 64; break; + case biFrozenRiver: a_Min = 60; a_Max = 62; break; + case biIceMountains: a_Min = 63; a_Max = 90; break; + case biIcePlains: a_Min = 63; a_Max = 70; break; + case biIcePlainsSpikes: a_Min = 60; a_Max = 70; break; + case biJungle: a_Min = 60; a_Max = 80; break; + case biJungleEdge: a_Min = 62; a_Max = 75; break; + case biJungleEdgeM: a_Min = 62; a_Max = 75; break; + case biJungleHills: a_Min = 60; a_Max = 90; break; + case biJungleM: a_Min = 60; a_Max = 75; break; + case biMegaSpruceTaiga: a_Min = 63; a_Max = 75; break; + case biMegaSpruceTaigaHills: a_Min = 63; a_Max = 90; break; + case biMegaTaiga: a_Min = 63; a_Max = 75; break; + case biMegaTaigaHills: a_Min = 63; a_Max = 90; break; + case biMesa: a_Min = 63; a_Max = 90; break; + case biMesaBryce: a_Min = 60; a_Max = 67; break; + case biMesaPlateau: a_Min = 75; a_Max = 85; break; + case biMesaPlateauF: a_Min = 80; a_Max = 90; break; + case biMesaPlateauFM: a_Min = 80; a_Max = 90; break; + case biMesaPlateauM: a_Min = 75; a_Max = 85; break; + case biMushroomIsland: a_Min = 63; a_Max = 90; break; + case biMushroomShore: a_Min = 60; a_Max = 75; break; + case biNether: a_Min = 10; a_Max = 100; break; + case biOcean: a_Min = 45; a_Max = 64; break; + case biPlains: a_Min = 63; a_Max = 70; break; + case biRiver: a_Min = 60; a_Max = 62; break; + case biRoofedForest: a_Min = 63; a_Max = 75; break; + case biRoofedForestM: a_Min = 63; a_Max = 75; break; + case biSavanna: a_Min = 63; a_Max = 75; break; + case biSavannaM: a_Min = 63; a_Max = 80; break; + case biSavannaPlateau: a_Min = 75; a_Max = 100; break; + case biSavannaPlateauM: a_Min = 80; a_Max = 160; break; + case biStoneBeach: a_Min = 60; a_Max = 64; break; + case biSunflowerPlains: a_Min = 63; a_Max = 70; break; + case biSwampland: a_Min = 60; a_Max = 67; break; + case biSwamplandM: a_Min = 61; a_Max = 67; break; + case biTaiga: a_Min = 63; a_Max = 75; break; + case biTaigaHills: a_Min = 63; a_Max = 90; break; + case biTaigaM: a_Min = 63; a_Max = 80; break; + default: + { + ASSERT(!"Unknown biome"); + a_Min = 10; + a_Max = 10; + break; + } + } + } +}; + + + + + +//////////////////////////////////////////////////////////////////////////////// +// cTerrainHeightGen: + +cTerrainHeightGenPtr cTerrainHeightGen::CreateHeightGen(cIniFile & a_IniFile, cBiomeGenPtr a_BiomeGen, int a_Seed, bool & a_CacheOffByDefault) +{ + AString HeightGenName = a_IniFile.GetValueSet("Generator", "HeightGen", ""); + if (HeightGenName.empty()) + { + LOGWARN("[Generator] HeightGen value not set in world.ini, using \"Biomal\"."); + HeightGenName = "Biomal"; + } + + a_CacheOffByDefault = false; + cTerrainHeightGen * res = nullptr; + if (NoCaseCompare(HeightGenName, "flat") == 0) + { + res = new cHeiGenFlat; + a_CacheOffByDefault = true; // We're generating faster than a cache would retrieve data + } + else if (NoCaseCompare(HeightGenName, "classic") == 0) + { + res = new cHeiGenClassic(a_Seed); + } + else if (NoCaseCompare(HeightGenName, "DistortedHeightmap") == 0) + { + res = new cDistortedHeightmap(a_Seed, a_BiomeGen); + } + else if (NoCaseCompare(HeightGenName, "End") == 0) + { + res = new cEndGen(a_Seed); + } + else if (NoCaseCompare(HeightGenName, "MinMax") == 0) + { + res = new cHeiGenMinMax(a_Seed, a_BiomeGen); + } + else if (NoCaseCompare(HeightGenName, "Mountains") == 0) + { + res = new cHeiGenMountains(a_Seed); + } + else if (NoCaseCompare(HeightGenName, "Noise3D") == 0) + { + res = new cNoise3DComposable(a_Seed); + } + else if (NoCaseCompare(HeightGenName, "biomal") == 0) + { + res = new cHeiGenBiomal(a_Seed, a_BiomeGen); + + /* + // Performance-testing: + LOGINFO("Measuring performance of cHeiGenBiomal..."); + clock_t BeginTick = clock(); + for (int x = 0; x < 500; x++) + { + cChunkDef::HeightMap Heights; + res->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); + //*/ + } + else + { + // No match found, force-set the default and retry + LOGWARN("Unknown HeightGen \"%s\", using \"Biomal\" instead.", HeightGenName.c_str()); + a_IniFile.DeleteValue("Generator", "HeightGen"); + a_IniFile.SetValue("Generator", "HeightGen", "Biomal"); + return CreateHeightGen(a_IniFile, a_BiomeGen, a_Seed, a_CacheOffByDefault); + } + + // Read the settings: + res->InitializeHeightGen(a_IniFile); + + return cTerrainHeightGenPtr(res); +} + + + + + diff --git a/src/Generating/IntGen.h b/src/Generating/IntGen.h new file mode 100644 index 000000000..b25e378c0 --- /dev/null +++ b/src/Generating/IntGen.h @@ -0,0 +1,1406 @@ + +// IntGen.h + +// Declares the cIntGen class and descendants for generating and filtering various 2D arrays of ints + +/* +The integers generated may be interpreted in several ways: +- land/see designators + - 0 = ocean + - >0 = land +- biome group + - 0 = ocean + - 1 = desert biomes + - 2 = temperate biomes + - 3 = mountains (hills and forests) + - 4 = ice biomes +- biome group with "bgfRare" flag (for generating rare biomes for the group) +- biome IDs +The interpretation depends on the generator used and on the position in the chain. + +The generators can be chained together - one produces data that another one consumes. +Some of such chain connections require changing the data dimensions between the two, which is handled automatically +by using templates. +*/ + + + + + +#pragma once + +#include "../BiomeDef.h" + + + + + +/** Constants representing the biome group designators. */ +const int bgOcean = 0; +const int bgDesert = 1; +const int bgTemperate = 2; +const int bgMountains = 3; +const int bgIce = 4; +const int bgLandOceanMax = 4; // Maximum biome group value generated in the landOcean generator +const int bgfRare = 1024; // Flag added to values to generate rare biomes for the group + + + + + +/** Interface that all the generator classes provide. */ +template <int SizeX, int SizeZ = SizeX> +class cIntGen +{ +public: + /** Force a virtual destructor in all descendants. + Descendants contain virtual functions and are referred to via pointer-to-base, so they need a virtual destructor. */ + virtual ~cIntGen() {} + + /** Holds the array of values generated by this class (descendant). */ + typedef int Values[SizeX * SizeZ]; + + /** Generates the array of templated size into a_Values, based on given min coords. */ + virtual void GetInts(int a_MinX, int a_MinZ, Values & a_Values) = 0; +}; + + + + + +/** Provides additional cNoise member and its helper functions. */ +template <int SizeX, int SizeZ = SizeX> +class cIntGenWithNoise : + public cIntGen<SizeX, SizeZ> +{ + typedef cIntGen<SizeX, SizeZ> super; + +public: + cIntGenWithNoise(int a_Seed) : + m_Noise(a_Seed) + { + } + +protected: + cNoise m_Noise; + + /** Chooses one of a_Val1 or a_Val2, based on m_Noise and the coordinates for querying the noise. */ + int ChooseRandomOne(int a_RndX, int a_RndZ, int a_Val1, int a_Val2) + { + int rnd = m_Noise.IntNoise2DInt(a_RndX, a_RndZ) / 7; + return ((rnd & 1) == 0) ? a_Val1 : a_Val2; + } + + /** Chooses one of a_ValN, based on m_Noise and the coordinates for querying the noise. */ + int ChooseRandomOne(int a_RndX, int a_RndZ, int a_Val1, int a_Val2, int a_Val3, int a_Val4) + { + int rnd = m_Noise.IntNoise2DInt(a_RndX, a_RndZ) / 7; + switch (rnd % 4) + { + case 0: return a_Val1; + case 1: return a_Val2; + case 2: return a_Val3; + default: return a_Val4; + } + } +}; + + + + + + +/** Generates a 2D array of random integers in the specified range [0 .. Range). */ +template <int Range, int SizeX, int SizeZ = SizeX> +class cIntGenChoice : + public cIntGenWithNoise<SizeX, SizeZ> +{ + typedef cIntGenWithNoise<SizeX, SizeZ> super; + +public: + cIntGenChoice(int a_Seed) : + super(a_Seed) + { + } + + + virtual void GetInts(int a_MinX, int a_MinZ, typename super::Values & a_Values) override + { + for (int z = 0; z < SizeZ; z++) + { + int BaseZ = a_MinZ + z; + for (int x = 0; x < SizeX; x++) + { + a_Values[x + SizeX * z] = (super::m_Noise.IntNoise2DInt(a_MinX + x, BaseZ) / 7) % Range; + } + } // for z + } +}; + + + + + + +/** Decides between the ocean and landmass biomes. +Has a threshold (in percent) of how much land, the larger the threshold, the more land. +Generates 0 for ocean, biome group ID for landmass. */ +template <int SizeX, int SizeZ = SizeX> +class cIntGenLandOcean : + public cIntGenWithNoise<SizeX, SizeZ> +{ + typedef cIntGenWithNoise<SizeX, SizeZ> super; + +public: + cIntGenLandOcean(int a_Seed, int a_Threshold) : + super(a_Seed), + m_Threshold(a_Threshold) + { + } + + + virtual void GetInts(int a_MinX, int a_MinZ, typename super::Values & a_Values) override + { + for (int z = 0; z < SizeZ; z++) + { + int BaseZ = a_MinZ + z; + for (int x = 0; x < SizeX; x++) + { + int rnd = (super::m_Noise.IntNoise2DInt(a_MinX + x, BaseZ) / 7); + a_Values[x + SizeX * z] = ((rnd % 100) < m_Threshold) ? ((rnd / 101) % bgLandOceanMax + 1) : 0; + } + } + + // If the centerpoint of the world is within the area, set it to bgTemperate, always: + if ((a_MinX <= 0) && (a_MinZ <= 0) && (a_MinX + SizeX > 0) && (a_MinZ + SizeZ > 0)) + { + a_Values[-a_MinX - a_MinZ * SizeX] = bgTemperate; + } + } + +protected: + int m_Threshold; +}; + + + + + +/** Zooms the underlying value array to twice the size. Uses random-neighbor for the pixels in-between. +This means that the zoome out image is randomly distorted. Applying zoom several times provides all +the distortion that the generators need. */ +template <int SizeX, int SizeZ = SizeX> +class cIntGenZoom : + public cIntGenWithNoise<SizeX, SizeZ> +{ + typedef cIntGenWithNoise<SizeX, SizeZ> super; + +protected: + static const int m_LowerSizeX = (SizeX / 2) + 2; + static const int m_LowerSizeZ = (SizeZ / 2) + 2; + +public: + typedef std::shared_ptr<cIntGen<m_LowerSizeX, m_LowerSizeZ>> Underlying; + + + cIntGenZoom(int a_Seed, Underlying a_UnderlyingGen) : + super(a_Seed), + m_UnderlyingGen(a_UnderlyingGen) + { + } + + + virtual void GetInts(int a_MinX, int a_MinZ, typename super::Values & a_Values) override + { + // Generate the underlying data with half the resolution: + int lowerMinX = a_MinX >> 1; + int lowerMinZ = a_MinZ >> 1; + int lowerData[m_LowerSizeX * m_LowerSizeZ]; + m_UnderlyingGen->GetInts(lowerMinX, lowerMinZ, lowerData); + const int lowStepX = (m_LowerSizeX - 1) * 2; + const int lowStepZ = (m_LowerSizeZ - 1) * 2; + int cache[lowStepX * lowStepZ]; + + // Discreet-interpolate the values into twice the size: + for (int z = 0; z < m_LowerSizeZ - 1; ++z) + { + int idx = (z * 2) * lowStepX; + int PrevZ0 = lowerData[z * m_LowerSizeX]; + int PrevZ1 = lowerData[(z + 1) * m_LowerSizeX]; + + for (int x = 0; x < m_LowerSizeX - 1; ++x) + { + int ValX1Z0 = lowerData[x + 1 + z * m_LowerSizeX]; + int ValX1Z1 = lowerData[x + 1 + (z + 1) * m_LowerSizeX]; + int RndX = (x + lowerMinX) * 2; + int RndZ = (z + lowerMinZ) * 2; + cache[idx] = PrevZ0; + cache[idx + lowStepX] = super::ChooseRandomOne(RndX, RndZ + 1, PrevZ0, PrevZ1); + cache[idx + 1] = super::ChooseRandomOne(RndX, RndZ - 1, PrevZ0, ValX1Z0); + cache[idx + 1 + lowStepX] = super::ChooseRandomOne(RndX, RndZ, PrevZ0, ValX1Z0, PrevZ1, ValX1Z1); + idx += 2; + PrevZ0 = ValX1Z0; + PrevZ1 = ValX1Z1; + } + } + + // Copy from Cache into a_Values; take into account the even/odd offsets in a_Min: + for (int z = 0; z < SizeZ; ++z) + { + memcpy(a_Values + z * SizeX, cache + (z + (a_MinZ & 1)) * lowStepX + (a_MinX & 1), SizeX * sizeof(int)); + } + } + +protected: + Underlying m_UnderlyingGen; +}; + + + + + +/** Smoothes out some artifacts generated by the zooming - mostly single-pixel values. +Compares each pixel to its neighbors and if the neighbors are equal, changes the pixel to their value. */ +template <int SizeX, int SizeZ = SizeX> +class cIntGenSmooth : + public cIntGenWithNoise<SizeX, SizeZ> +{ + typedef cIntGenWithNoise<SizeX, SizeZ> super; + static const int m_LowerSizeX = SizeX + 2; + static const int m_LowerSizeZ = SizeZ + 2; + +public: + typedef std::shared_ptr<cIntGen<m_LowerSizeX, m_LowerSizeZ>> Underlying; + + + cIntGenSmooth(int a_Seed, Underlying a_Underlying) : + super(a_Seed), + m_Underlying(a_Underlying) + { + } + + + virtual void GetInts(int a_MinX, int a_MinZ, typename super::Values & a_Values) override + { + // Generate the underlying values: + int lowerData[m_LowerSizeX * m_LowerSizeZ]; + m_Underlying->GetInts(a_MinX - 1, a_MinZ - 1, lowerData); + + // Smooth - for each square check if the surroundings are the same, if so, expand them diagonally. + // Also get rid of single-pixel irregularities (A-B-A): + for (int z = 0; z < SizeZ; z++) + { + int NoiseZ = a_MinZ + z; + for (int x = 0; x < SizeX; x++) + { + int val = lowerData[x + 1 + (z + 1) * m_LowerSizeX]; + int above = lowerData[x + 1 + z * m_LowerSizeX]; + int below = lowerData[x + 1 + (z + 2) * m_LowerSizeX]; + int left = lowerData[x + (z + 1) * m_LowerSizeX]; + int right = lowerData[x + 2 + (z + 1) * m_LowerSizeX]; + + if ((left == right) && (above == below)) + { + if (((super::m_Noise.IntNoise2DInt(a_MinX + x, NoiseZ) / 7) % 2) == 0) + { + val = left; + } + else + { + val = above; + } + } + else + { + if (left == right) + { + val = left; + } + + if (above == below) + { + val = above; + } + } + + a_Values[x + z * SizeX] = val; + } + } + } + +protected: + Underlying m_Underlying; +}; + + + + + +/** Converts land biomes at the edge of an ocean into the respective beach biome. */ +template <int SizeX, int SizeZ = SizeX> +class cIntGenBeaches : + public cIntGen<SizeX, SizeZ> +{ + typedef cIntGen<SizeX, SizeZ> super; + static const int m_UnderlyingSizeX = SizeX + 2; + static const int m_UnderlyingSizeZ = SizeZ + 2; + +public: + typedef std::shared_ptr<cIntGen<m_UnderlyingSizeX, m_UnderlyingSizeZ>> Underlying; + + + cIntGenBeaches(Underlying a_Underlying) : + m_Underlying(a_Underlying) + { + } + + + virtual void GetInts(int a_MinX, int a_MinZ, typename super::Values & a_Values) override + { + // Map for biome -> its beach: + static const int ToBeach[] = + { + /* biOcean */ biOcean, + /* biPlains */ biBeach, + /* biDesert */ biBeach, + /* biExtremeHills */ biStoneBeach, + /* biForest */ biBeach, + /* biTaiga */ biColdBeach, + /* biSwampland */ biSwampland, + /* biRiver */ biRiver, + /* biNether */ biNether, + /* biEnd */ biEnd, + /* biFrozenOcean */ biColdBeach, + /* biFrozenRiver */ biColdBeach, + /* biIcePlains */ biColdBeach, + /* biIceMountains */ biColdBeach, + /* biMushroomIsland */ biMushroomShore, + /* biMushroomShore */ biMushroomShore, + /* biBeach */ biBeach, + /* biDesertHills */ biBeach, + /* biForestHills */ biBeach, + /* biTaigaHills */ biColdBeach, + /* biExtremeHillsEdge */ biStoneBeach, + /* biJungle */ biBeach, + /* biJungleHills */ biBeach, + /* biJungleEdge */ biBeach, + /* biDeepOcean */ biOcean, + /* biStoneBeach */ biStoneBeach, + /* biColdBeach */ biColdBeach, + /* biBirchForest */ biBeach, + /* biBirchForestHills */ biBeach, + /* biRoofedForest */ biBeach, + /* biColdTaiga */ biColdBeach, + /* biColdTaigaHills */ biColdBeach, + /* biMegaTaiga */ biStoneBeach, + /* biMegaTaigaHills */ biStoneBeach, + /* biExtremeHillsPlus */ biStoneBeach, + /* biSavanna */ biBeach, + /* biSavannaPlateau */ biBeach, + /* biMesa */ biMesa, + /* biMesaPlateauF */ biMesa, + /* biMesaPlateau */ biMesa, + }; + + // Generate the underlying values: + int lowerValues[m_UnderlyingSizeX * m_UnderlyingSizeZ]; + m_Underlying->GetInts(a_MinX - 1, a_MinZ - 1, lowerValues); + + // Add beaches between ocean and biomes: + for (int z = 0; z < SizeZ; z++) + { + for (int x = 0; x < SizeX; x++) + { + int val = lowerValues[x + 1 + (z + 1) * m_UnderlyingSizeX]; + int above = lowerValues[x + 1 + z * m_UnderlyingSizeX]; + int below = lowerValues[x + 1 + (z + 2) * m_UnderlyingSizeX]; + int left = lowerValues[x + (z + 1) * m_UnderlyingSizeX]; + int right = lowerValues[x + 2 + (z + 1) * m_UnderlyingSizeX]; + if (!IsBiomeOcean(val)) + { + if (IsBiomeOcean(above) || IsBiomeOcean(below) || IsBiomeOcean(left) || IsBiomeOcean(right)) + { + // First convert the value to a regular biome (drop the M flag), then modulo by our biome count: + val = ToBeach[(val % 128) % ARRAYCOUNT(ToBeach)]; + } + } + a_Values[x + z * SizeX] = val; + } + } + } + +protected: + Underlying m_Underlying; +}; + + + + + +/** Generates the underlying numbers and then randomly changes some ocean group pixels into random land +biome group pixels, based on the predefined chance. */ +template <int SizeX, int SizeZ = SizeX> +class cIntGenAddIslands : + public cIntGenWithNoise<SizeX, SizeZ> +{ + typedef cIntGenWithNoise<SizeX, SizeZ> super; + +public: + typedef std::shared_ptr<cIntGen<SizeX, SizeZ>> Underlying; + + + cIntGenAddIslands(int a_Seed, int a_Chance, Underlying a_Underlying) : + super(a_Seed), + m_Chance(a_Chance), + m_Underlying(a_Underlying) + { + } + + + virtual void GetInts(int a_MinX, int a_MinZ, typename super::Values & a_Values) override + { + m_Underlying->GetInts(a_MinX, a_MinZ, a_Values); + for (int z = 0; z < SizeZ; z++) + { + for (int x = 0; x < SizeX; x++) + { + if (a_Values[x + z * SizeX] == bgOcean) + { + int rnd = super::m_Noise.IntNoise2DInt(a_MinX + x, a_MinZ + z) / 7; + if (rnd % 1000 < m_Chance) + { + a_Values[x + z * SizeX] = (rnd / 1003) % bgLandOceanMax; + } + } + } // for x + } // for z + } + +protected: + /** Chance, in permille, of an island being generated in ocean. */ + int m_Chance; + + Underlying m_Underlying; +}; + + + + + +/** A filter that adds an edge biome group between two biome groups that need an edge between them. */ +template <int SizeX, int SizeZ = SizeX> +class cIntGenBiomeGroupEdges : + public cIntGen<SizeX, SizeZ> +{ + typedef cIntGen<SizeX, SizeZ> super; + + static const int m_UnderlyingSizeX = SizeX + 2; + static const int m_UnderlyingSizeZ = SizeZ + 2; + +public: + + typedef std::shared_ptr<cIntGen<m_UnderlyingSizeX, m_UnderlyingSizeZ>> Underlying; + + cIntGenBiomeGroupEdges(Underlying a_Underlying) : + m_Underlying(a_Underlying) + { + } + + + virtual void GetInts(int a_MinX, int a_MinZ, typename super::Values & a_Values) + { + // Generate the underlying biome groups: + int lowerValues[m_UnderlyingSizeX * m_UnderlyingSizeZ]; + m_Underlying->GetInts(a_MinX, a_MinZ, lowerValues); + + // Change the biomes on incompatible edges into an edge biome: + for (int z = 0; z < SizeZ; z++) + { + for (int x = 0; x < SizeX; x++) + { + int val = lowerValues[x + 1 + (z + 1) * m_UnderlyingSizeX]; + int above = lowerValues[x + 1 + z * m_UnderlyingSizeX]; + int below = lowerValues[x + 1 + (z + 2) * m_UnderlyingSizeX]; + int left = lowerValues[x + (z + 1) * m_UnderlyingSizeX]; + int right = lowerValues[x + 2 + (z + 1) * m_UnderlyingSizeX]; + switch (val) + { + // Desert should neighbor only oceans, desert and temperates; change to temperate when another: + case bgDesert: + { + if ( + !isDesertCompatible(above) || + !isDesertCompatible(below) || + !isDesertCompatible(left) || + !isDesertCompatible(right) + ) + { + val = bgTemperate; + } + break; + } // case bgDesert + + // Ice should not neighbor deserts; change to temperate: + case bgIce: + { + if ( + (above == bgDesert) || + (below == bgDesert) || + (left == bgDesert) || + (right == bgDesert) + ) + { + val = bgTemperate; + } + break; + } // case bgIce + } + a_Values[x + z * SizeX] = val; + } // for x + } // for z + } + +protected: + Underlying m_Underlying; + + + inline bool isDesertCompatible(int a_BiomeGroup) + { + switch (a_BiomeGroup) + { + case bgOcean: + case bgDesert: + case bgTemperate: + { + return true; + } + default: + { + return false; + } + } + } +}; + + + + + +/** Turns biome group indices into real biomes. +For each pixel, takes its biome group and chooses a random biome from that group; replaces the value with +that biome. */ +template <int SizeX, int SizeZ = SizeX> +class cIntGenBiomes : + public cIntGenWithNoise<SizeX, SizeZ> +{ + typedef cIntGenWithNoise<SizeX, SizeZ> super; + +public: + typedef std::shared_ptr<cIntGen<SizeX, SizeZ>> Underlying; + + + cIntGenBiomes(int a_Seed, Underlying a_Underlying) : + super(a_Seed), + m_Underlying(a_Underlying) + { + } + + + virtual void GetInts(int a_MinX, int a_MinZ, typename super::Values & a_Values) override + { + // Define the per-biome-group biomes: + static const int oceanBiomes[] = + { + biOcean, // biDeepOcean, + }; + + // Same as oceanBiomes, there are no rare oceanic biomes (mushroom islands are handled separately) + static const int rareOceanBiomes[] = + { + biOcean, + }; + + static const int desertBiomes[] = + { + biDesert, biDesert, biDesert, biDesert, biDesert, biDesert, biSavanna, biSavanna, biPlains, + }; + + static const int rareDesertBiomes[] = + { + biMesaPlateau, biMesaPlateauF, + }; + + static const int temperateBiomes[] = + { + biForest, biForest, biRoofedForest, biExtremeHills, biPlains, biBirchForest, biSwampland, + }; + + static const int rareTemperateBiomes[] = + { + biJungle, // Jungle is not strictly temperate, but let's piggyback it here + }; + + static const int mountainBiomes[] = + { + biExtremeHills, biForest, biTaiga, biPlains, + }; + + static const int rareMountainBiomes[] = + { + biMegaTaiga, + }; + + static const int iceBiomes[] = + { + biIcePlains, biIcePlains, biIcePlains, biIcePlains, biColdTaiga, + }; + + // Same as iceBiomes, there's no rare ice biome + static const int rareIceBiomes[] = + { + biIcePlains, biIcePlains, biIcePlains, biIcePlains, biColdTaiga, + }; + + static const cBiomesInGroups biomesInGroups[] = + { + /* bgOcean */ { static_cast<int>(ARRAYCOUNT(oceanBiomes)), oceanBiomes}, + /* bgDesert */ { static_cast<int>(ARRAYCOUNT(desertBiomes)), desertBiomes}, + /* bgTemperate */ { static_cast<int>(ARRAYCOUNT(temperateBiomes)), temperateBiomes}, + /* bgMountains */ { static_cast<int>(ARRAYCOUNT(mountainBiomes)), mountainBiomes}, + /* bgIce */ { static_cast<int>(ARRAYCOUNT(iceBiomes)), iceBiomes}, + }; + + static const cBiomesInGroups rareBiomesInGroups[] = + { + /* bgOcean */ { static_cast<int>(ARRAYCOUNT(rareOceanBiomes)), rareOceanBiomes}, + /* bgDesert */ { static_cast<int>(ARRAYCOUNT(rareDesertBiomes)), rareDesertBiomes}, + /* bgTemperate */ { static_cast<int>(ARRAYCOUNT(rareTemperateBiomes)), rareTemperateBiomes}, + /* bgMountains */ { static_cast<int>(ARRAYCOUNT(rareMountainBiomes)), rareMountainBiomes}, + /* bgIce */ { static_cast<int>(ARRAYCOUNT(rareIceBiomes)), rareIceBiomes}, + }; + + // Generate the underlying values, representing biome groups: + m_Underlying->GetInts(a_MinX, a_MinZ, a_Values); + + // Overwrite each biome group with a random biome from that group: + for (int z = 0; z < SizeZ; z++) + { + int IdxZ = z * SizeX; + for (int x = 0; x < SizeX; x++) + { + int val = a_Values[x + IdxZ]; + const cBiomesInGroups & Biomes = (val > bgfRare) ? + rareBiomesInGroups[(val & (bgfRare - 1)) % ARRAYCOUNT(rareBiomesInGroups)] : + biomesInGroups[val % ARRAYCOUNT(biomesInGroups)]; + int rnd = (super::m_Noise.IntNoise2DInt(x + a_MinX, z + a_MinZ) / 7); + a_Values[x + IdxZ] = Biomes.Biomes[rnd % Biomes.Count]; + } + } + } + +protected: + + struct cBiomesInGroups + { + const int Count; + const int * Biomes; + }; + + + /** The underlying int generator */ + Underlying m_Underlying; +}; + + + + + +/** Randomly replaces pixels of one value to another value, using the given chance. */ +template <int SizeX, int SizeZ = SizeX> +class cIntGenReplaceRandomly : + public cIntGenWithNoise<SizeX, SizeZ> +{ + typedef cIntGenWithNoise<SizeX, SizeZ> super; + +public: + typedef std::shared_ptr<cIntGen<SizeX, SizeZ>> Underlying; + + + cIntGenReplaceRandomly(int a_From, int a_To, int a_Chance, int a_Seed, Underlying a_Underlying) : + super(a_Seed), + m_From(a_From), + m_To(a_To), + m_Chance(a_Chance), + m_Underlying(a_Underlying) + { + } + + + virtual void GetInts(int a_MinX, int a_MinZ, typename super::Values & a_Values) override + { + // Generate the underlying values: + m_Underlying->GetInts(a_MinX, a_MinZ, a_Values); + + // Replace some of the values: + for (int z = 0; z < SizeZ; z++) + { + int idxZ = z * SizeX; + for (int x = 0; x < SizeX; x++) + { + int idx = x + idxZ; + if (a_Values[idx] == m_From) + { + int rnd = super::m_Noise.IntNoise2DInt(x + a_MinX, z + a_MinZ) / 7; + if (rnd % 1000 < m_Chance) + { + a_Values[idx] = m_To; + } + } + } + } // for z + } + + +protected: + /** The original value to be replaced. */ + int m_From; + + /** The destination value to which to replace. */ + int m_To; + + /** Chance, in permille, of replacing the value. */ + int m_Chance; + + Underlying m_Underlying; +}; + + + + + +/** Mixer that joins together finalized biomes and rivers. +It first checks for oceans, if there is an ocean in the Biomes, it keeps the ocean. +If there's no ocean, it checks Rivers for a river, if there is a river, it uses the Biomes to select either +regular river or frozen river, based on the biome. */ +template <int SizeX, int SizeZ = SizeX> +class cIntGenMixRivers: + public cIntGen<SizeX, SizeZ> +{ + typedef cIntGen<SizeX, SizeZ> super; + +public: + typedef std::shared_ptr<cIntGen<SizeX, SizeZ>> Underlying; + + + cIntGenMixRivers(Underlying a_Biomes, Underlying a_Rivers): + m_Biomes(a_Biomes), + m_Rivers(a_Rivers) + { + } + + + virtual void GetInts(int a_MinX, int a_MinZ, typename super::Values & a_Values) override + { + // Generate the underlying data: + m_Biomes->GetInts(a_MinX, a_MinZ, a_Values); + typename super::Values riverData; + m_Rivers->GetInts(a_MinX, a_MinZ, riverData); + + // Mix the values: + for (int z = 0; z < SizeZ; z++) + { + int idxZ = z * SizeX; + for (int x = 0; x < SizeX; x++) + { + int idx = x + idxZ; + if (IsBiomeOcean(a_Values[idx])) + { + // Oceans are kept without any changes + continue; + } + if (riverData[idx] != biRiver) + { + // There's no river, keep the current value + continue; + } + + // There's a river, change the output to a river or a frozen river, based on the original biome: + if (IsBiomeVeryCold((EMCSBiome)a_Values[idx])) + { + a_Values[idx] = biFrozenRiver; + } + else + { + a_Values[idx] = biRiver; + } + } // for x + } // for z + } + +protected: + Underlying m_Biomes; + Underlying m_Rivers; +}; + + + + + +/** Generates a river based on the underlying data. +This is basically an edge detector over the underlying data. The rivers are the edges where the underlying data +changes from one pixel to its neighbor. */ +template <int SizeX, int SizeZ = SizeX> +class cIntGenRiver: + public cIntGenWithNoise<SizeX, SizeZ> +{ + typedef cIntGenWithNoise<SizeX, SizeZ> super; + static const int UnderlyingSizeX = SizeX + 2; + static const int UnderlyingSizeZ = SizeZ + 2; + +public: + typedef std::shared_ptr<cIntGen<UnderlyingSizeX, UnderlyingSizeZ>> Underlying; + + + cIntGenRiver(int a_Seed, Underlying a_Underlying): + super(a_Seed), + m_Underlying(a_Underlying) + { + } + + + virtual void GetInts(int a_MinX, int a_MinZ, typename super::Values & a_Values) override + { + // Generate the underlying data: + int Cache[UnderlyingSizeX * UnderlyingSizeZ]; + m_Underlying->GetInts(a_MinX - 1, a_MinZ - 1, Cache); + + // Detect the edges: + for (int z = 0; z < SizeZ; z++) + { + for (int x = 0; x < SizeX; x++) + { + int Above = Cache[x + 1 + z * UnderlyingSizeX]; + int Below = Cache[x + 1 + (z + 2) * UnderlyingSizeX]; + int Left = Cache[x + (z + 1) * UnderlyingSizeX]; + int Right = Cache[x + 2 + (z + 1) * UnderlyingSizeX]; + int val = Cache[x + 1 + (z + 1) * UnderlyingSizeX]; + + if ((val == Above) && (val == Below) && (val == Left) && (val == Right)) + { + val = 0; + } + else + { + val = biRiver; + } + a_Values[x + z * SizeX] = val; + } // for x + } // for z + } + +protected: + Underlying m_Underlying; +}; + + + + + +/** Turns some of the oceans into the specified biome. Used for mushroom and deep ocean. +The biome is only placed if at least 3 of its neighbors are ocean and only with the specified chance. */ +template <int SizeX, int SizeZ = SizeX> +class cIntGenAddToOcean: + public cIntGenWithNoise<SizeX, SizeZ> +{ + typedef cIntGenWithNoise<SizeX, SizeZ> super; + static const int UnderlyingSizeX = SizeX + 2; + static const int UnderlyingSizeZ = SizeZ + 2; + +public: + typedef std::shared_ptr<cIntGen<UnderlyingSizeX, UnderlyingSizeZ>> Underlying; + + + cIntGenAddToOcean(int a_Seed, int a_Chance, int a_ToValue, Underlying a_Underlying): + super(a_Seed), + m_Chance(a_Chance), + m_ToValue(a_ToValue), + m_Underlying(a_Underlying) + { + } + + + virtual void GetInts(int a_MinX, int a_MinZ, typename super::Values & a_Values) override + { + // Generate the underlying data: + int Cache[UnderlyingSizeX * UnderlyingSizeZ]; + m_Underlying->GetInts(a_MinX - 1, a_MinZ - 1, Cache); + + // Add the mushroom islands: + for (int z = 0; z < SizeZ; z++) + { + for (int x = 0; x < SizeX; x++) + { + int val = Cache[x + 1 + (z + 1) * UnderlyingSizeX]; + if (!IsBiomeOcean(val)) + { + a_Values[x + z * SizeX] = val; + continue; + } + + // Count the ocean neighbors: + int Above = Cache[x + 1 + z * UnderlyingSizeX]; + int Below = Cache[x + 1 + (z + 2) * UnderlyingSizeX]; + int Left = Cache[x + (z + 1) * UnderlyingSizeX]; + int Right = Cache[x + 2 + (z + 1) * UnderlyingSizeX]; + int NumOceanNeighbors = 0; + if (IsBiomeOcean(Above)) + { + NumOceanNeighbors += 1; + } + if (IsBiomeOcean(Below)) + { + NumOceanNeighbors += 1; + } + if (IsBiomeOcean(Left)) + { + NumOceanNeighbors += 1; + } + if (IsBiomeOcean(Right)) + { + NumOceanNeighbors += 1; + } + + // If at least 3 ocean neighbors and the chance is right, change: + if ( + (NumOceanNeighbors >= 3) && + ((super::m_Noise.IntNoise2DInt(x + a_MinX, z + a_MinZ) / 7) % 1000 < m_Chance) + ) + { + a_Values[x + z * SizeX] = m_ToValue; + } + else + { + a_Values[x + z * SizeX] = val; + } + } // for x + } // for z + } + +protected: + /** Chance, in permille, of changing the biome. */ + int m_Chance; + + /** The value to change the ocean into. */ + int m_ToValue; + + Underlying m_Underlying; +}; + + + + + +/** Changes random pixels of the underlying data to the specified value. */ +template <int SizeX, int SizeZ = SizeX> +class cIntGenSetRandomly : + public cIntGenWithNoise<SizeX, SizeZ> +{ + typedef cIntGenWithNoise<SizeX, SizeZ> super; + +public: + typedef std::shared_ptr<cIntGen<SizeX, SizeZ>> Underlying; + + cIntGenSetRandomly(int a_Seed, int a_Chance, int a_ToValue, Underlying a_Underlying) : + super(a_Seed), + m_Chance(a_Chance), + m_ToValue(a_ToValue), + m_Underlying(a_Underlying) + { + } + + + virtual void GetInts(int a_MinX, int a_MinZ, typename super::Values & a_Values) override + { + // Generate the underlying data: + m_Underlying->GetInts(a_MinX, a_MinZ, a_Values); + + // Change random pixels to bgOcean: + for (int z = 0; z < SizeZ; z++) + { + for (int x = 0; x < SizeX; x++) + { + int rnd = super::m_Noise.IntNoise2DInt(x + a_MinX, z + a_MinZ) / 7; + if (rnd % 1000 < m_Chance) + { + a_Values[x + z * SizeX] = m_ToValue; + } + } + } + } + +protected: + /** Chance, in permille, of changing each pixel. */ + int m_Chance; + + /** The value to which to set the pixel. */ + int m_ToValue; + + Underlying m_Underlying; +}; + + + + + + +/** Adds a "rare" flag to random biome groups, based on the given chance. */ +template <int SizeX, int SizeZ = SizeX> +class cIntGenRareBiomeGroups: + public cIntGenWithNoise<SizeX, SizeZ> +{ + typedef cIntGenWithNoise<SizeX, SizeZ> super; + +public: + typedef std::shared_ptr<cIntGen<SizeX, SizeZ>> Underlying; + + + cIntGenRareBiomeGroups(int a_Seed, int a_Chance, Underlying a_Underlying): + super(a_Seed), + m_Chance(a_Chance), + m_Underlying(a_Underlying) + { + } + + + virtual void GetInts(int a_MinX, int a_MinZ, typename super::Values & a_Values) override + { + // Generate the underlying data: + m_Underlying->GetInts(a_MinX, a_MinZ, a_Values); + + // Change some of the biome groups into rare biome groups: + for (int z = 0; z < SizeZ; z++) + { + for (int x = 0; x < SizeX; x++) + { + int rnd = super::m_Noise.IntNoise2DInt(x + a_MinX, z + a_MinZ) / 7; + if (rnd % 1000 < m_Chance) + { + int idx = x + SizeX * z; + a_Values[idx] = a_Values[idx] | bgfRare; + } + } + } + } + +protected: + /** Chance, in permille, of changing each pixel into the rare biome group. */ + int m_Chance; + + /** The underlying generator. */ + Underlying m_Underlying; +}; + + + + + +/** Changes biomes in the parent data into an alternate versions (usually "hill" variants), in such places +that have their alterations set. */ +template <int SizeX, int SizeZ = SizeX> +class cIntGenAlternateBiomes: + public cIntGenWithNoise<SizeX, SizeZ> +{ + typedef cIntGenWithNoise<SizeX, SizeZ> super; + +public: + typedef std::shared_ptr<cIntGen<SizeX, SizeZ>> Underlying; + + + cIntGenAlternateBiomes(int a_Seed, Underlying a_Alterations, Underlying a_BaseBiomes): + super(a_Seed), + m_Alterations(a_Alterations), + m_BaseBiomes(a_BaseBiomes) + { + } + + + virtual void GetInts(int a_MinX, int a_MinZ, typename super::Values & a_Values) override + { + // Generate the base biomes and the alterations: + m_BaseBiomes->GetInts(a_MinX, a_MinZ, a_Values); + typename super::Values alterations; + m_Alterations->GetInts(a_MinX, a_MinZ, alterations); + + // Change the biomes into their alternate versions: + for (int idx = 0; idx < SizeX * SizeZ; ++idx) + { + if (alterations[idx] == 0) + { + // No change + continue; + } + + // Change to alternate biomes: + int val = a_Values[idx]; + switch (val) + { + case biBirchForest: val = biBirchForestHills; break; + case biDesert: val = biDesertHills; break; + case biExtremeHills: val = biExtremeHillsPlus; break; + case biForest: val = biForestHills; break; + case biIcePlains: val = biIceMountains; break; + case biJungle: val = biJungleHills; break; + case biMegaTaiga: val = biMegaTaigaHills; break; + case biMesaPlateau: val = biMesa; break; + case biMesaPlateauF: val = biMesa; break; + case biMesaPlateauM: val = biMesa; break; + case biMesaPlateauFM: val = biMesa; break; + case biPlains: val = biForest; break; + case biRoofedForest: val = biPlains; break; + case biSavanna: val = biSavannaPlateau; break; + case biTaiga: val = biTaigaHills; break; + } + a_Values[idx] = val; + } // for idx - a_Values[] + } + +protected: + Underlying m_Alterations; + Underlying m_BaseBiomes; +}; + + + + + +/** Adds an edge between two specifically incompatible biomes, such as mesa and forest. */ +template <int SizeX, int SizeZ = SizeX> +class cIntGenBiomeEdges: + public cIntGenWithNoise<SizeX, SizeZ> +{ + typedef cIntGenWithNoise<SizeX, SizeZ> super; + static const int m_LowerSizeX = SizeX + 2; + static const int m_LowerSizeZ = SizeZ + 2; + +public: + typedef std::shared_ptr<cIntGen<m_LowerSizeX, m_LowerSizeZ>> Underlying; + + + cIntGenBiomeEdges(int a_Seed, Underlying a_Underlying): + super(a_Seed), + m_Underlying(a_Underlying) + { + } + + + virtual void GetInts(int a_MinX, int a_MinZ, typename super::Values & a_Values) override + { + // Generate the underlying biomes: + typename Underlying::element_type::Values lowerValues; + m_Underlying->GetInts(a_MinX - 1, a_MinZ - 1, lowerValues); + + // Convert incompatible edges into neutral biomes: + for (int z = 0; z < SizeZ; z++) + { + for (int x = 0; x < SizeX; x++) + { + int biome = lowerValues[x + 1 + (z + 1) * m_LowerSizeX]; + int above = lowerValues[x + 1 + z * m_LowerSizeX]; + int below = lowerValues[x + 1 + (z + 2) * m_LowerSizeX]; + int left = lowerValues[x + (z + 1) * m_LowerSizeX]; + int right = lowerValues[x + 2 + (z + 1) * m_LowerSizeX]; + + switch (biome) + { + case biDesert: + case biDesertM: + case biDesertHills: + { + if ( + IsBiomeVeryCold(static_cast<EMCSBiome>(above)) || + IsBiomeVeryCold(static_cast<EMCSBiome>(below)) || + IsBiomeVeryCold(static_cast<EMCSBiome>(left)) || + IsBiomeVeryCold(static_cast<EMCSBiome>(right)) + ) + { + biome = biPlains; + } + break; + } // case biDesert + + case biMesaPlateau: + case biMesaPlateauF: + case biMesaPlateauFM: + case biMesaPlateauM: + { + if ( + !isMesaCompatible(above) || + !isMesaCompatible(below) || + !isMesaCompatible(left) || + !isMesaCompatible(right) + ) + { + biome = biDesert; + } + break; + } // Mesa biomes + + case biJungle: + case biJungleM: + { + if ( + !isJungleCompatible(above) || + !isJungleCompatible(below) || + !isJungleCompatible(left) || + !isJungleCompatible(right) + ) + { + biome = (biome == biJungle) ? biJungleEdge : biJungleEdgeM; + } + break; + } // Jungle biomes + + case biSwampland: + case biSwamplandM: + { + if ( + IsBiomeNoDownfall(static_cast<EMCSBiome>(above)) || + IsBiomeNoDownfall(static_cast<EMCSBiome>(below)) || + IsBiomeNoDownfall(static_cast<EMCSBiome>(left)) || + IsBiomeNoDownfall(static_cast<EMCSBiome>(right)) + ) + { + biome = biPlains; + } + break; + } // Swampland biomes + } // switch (biome) + + a_Values[x + z * SizeX] = biome; + } // for x + } // for z + } + + +protected: + Underlying m_Underlying; + + + bool isMesaCompatible(int a_Biome) + { + switch (a_Biome) + { + case biDesert: + case biMesa: + case biMesaBryce: + case biMesaPlateau: + case biMesaPlateauF: + case biMesaPlateauFM: + case biMesaPlateauM: + case biOcean: + case biDeepOcean: + { + return true; + } + default: + { + return false; + } + } + } + + + bool isJungleCompatible(int a_Biome) + { + switch (a_Biome) + { + case biJungle: + case biJungleM: + case biJungleEdge: + case biJungleEdgeM: + case biJungleHills: + { + return true; + } + default: + { + return false; + } + } + } +}; + + + + + +/** Changes biomes in the parent data into their alternate versions ("M" variants), in such places that +have their alterations set. */ +template <int SizeX, int SizeZ = SizeX> +class cIntGenMBiomes: + public cIntGenWithNoise<SizeX, SizeZ> +{ + typedef cIntGenWithNoise<SizeX, SizeZ> super; + +public: + typedef std::shared_ptr<cIntGen<SizeX, SizeZ>> Underlying; + + + cIntGenMBiomes(int a_Seed, Underlying a_Alteration, Underlying a_Underlying): + super(a_Seed), + m_Underlying(a_Underlying), + m_Alteration(a_Alteration) + { + } + + + virtual void GetInts(int a_MinX, int a_MinZ, typename super::Values & a_Values) override + { + // Generate the underlying biomes and the alterations: + m_Underlying->GetInts(a_MinX, a_MinZ, a_Values); + typename super::Values alterations; + m_Alteration->GetInts(a_MinX, a_MinZ, alterations); + + // Wherever alterations are nonzero, change into alternate biome, if available: + for (int idx = 0; idx < SizeX * SizeZ; ++idx) + { + if (alterations[idx] == 0) + { + continue; + } + + // Ice spikes biome was removed from here, because it was generated way too often + switch (a_Values[idx]) + { + case biPlains: a_Values[idx] = biSunflowerPlains; break; + case biDesert: a_Values[idx] = biDesertM; break; + case biExtremeHills: a_Values[idx] = biExtremeHillsM; break; + case biForest: a_Values[idx] = biFlowerForest; break; + case biTaiga: a_Values[idx] = biTaigaM; break; + case biSwampland: a_Values[idx] = biSwamplandM; break; + case biJungle: a_Values[idx] = biJungleM; break; + case biJungleEdge: a_Values[idx] = biJungleEdgeM; break; + case biBirchForest: a_Values[idx] = biBirchForestM; break; + case biBirchForestHills: a_Values[idx] = biBirchForestHillsM; break; + case biRoofedForest: a_Values[idx] = biRoofedForestM; break; + case biColdTaiga: a_Values[idx] = biColdTaigaM; break; + case biMegaSpruceTaiga: a_Values[idx] = biMegaSpruceTaiga; break; + case biMegaSpruceTaigaHills: a_Values[idx] = biMegaSpruceTaigaHills; break; + case biExtremeHillsPlus: a_Values[idx] = biExtremeHillsPlusM; break; + case biSavanna: a_Values[idx] = biSavannaM; break; + case biSavannaPlateau: a_Values[idx] = biSavannaPlateauM; break; + case biMesa: a_Values[idx] = biMesaBryce; break; + case biMesaPlateauF: a_Values[idx] = biMesaPlateauFM; break; + case biMesaPlateau: a_Values[idx] = biMesaBryce; break; + } + } // for idx - a_Values[] / alterations[] + } + +protected: + Underlying m_Underlying; + Underlying m_Alteration; +}; + + + + diff --git a/src/Generating/ProtIntGen.h b/src/Generating/ProtIntGen.h new file mode 100644 index 000000000..73ed27096 --- /dev/null +++ b/src/Generating/ProtIntGen.h @@ -0,0 +1,1351 @@ + +// ProtIntGen.h + +// Declares the prototyping integer generators - cProtIntGen class and its descendants + +/* +These classes generate 2D arrays of integers that have various interpretations. The main purpose of these +classes is to provide fast prototyping for cIntGen classes - unlike cIntGen classes, these are not +template-based and so they take care of the underlying sizes automatically. This makes them easier to chain +and re-chain, since the size parameters don't need to be adjusted after each such case. Their performance is, +however, slightly worse, which is why we use cIntGen classes in the final generator. + +Because there is no SizeX / SizeZ template param, the generators would have to either alloc memory for each +underlying generator's values, or use a maximum-size buffer. We chose the latter, to avoid memory allocation +overhead; this however means that there's (an arbitrary) limit to the size of the generated data. +*/ + + + + + +#pragma once + +// We need the biome group constants defined there: +#include "IntGen.h" + + + + + +/** Interface that all the generator classes provide. */ +class cProtIntGen +{ +protected: + /** Maximum size of the generated area. + Adjust the constant if you need larger areas, these are just so that we can use fixed-size buffers. */ + static const int m_BufferSize = 900; + +public: + + /** Type of the generic interface used for storing links to the underlying generators. */ + typedef std::shared_ptr<cProtIntGen> Underlying; + + + /** Force a virtual destructor in all descendants. + Descendants contain virtual functions and are referred to via pointer-to-base, so they need a virtual destructor. */ + virtual ~cProtIntGen() {} + + /** Generates the array of specified size into a_Values, based on given min coords. */ + virtual void GetInts(int a_MinX, int a_MinZ, int a_SizeX, int a_SizeZ, int * a_Values) = 0; +}; + + + + + +/** Provides additional cNoise member and its helper functions. */ +class cProtIntGenWithNoise : + public cProtIntGen +{ + typedef cProtIntGen super; + +public: + cProtIntGenWithNoise(int a_Seed) : + m_Noise(a_Seed) + { + } + +protected: + cNoise m_Noise; + + /** Chooses one of a_Val1 or a_Val2, based on m_Noise and the coordinates for querying the noise. */ + int chooseRandomOne(int a_RndX, int a_RndZ, int a_Val1, int a_Val2) + { + int rnd = m_Noise.IntNoise2DInt(a_RndX, a_RndZ) / 7; + return ((rnd & 1) == 0) ? a_Val1 : a_Val2; + } + + /** Chooses one of a_ValN, based on m_Noise and the coordinates for querying the noise. */ + int chooseRandomOne(int a_RndX, int a_RndZ, int a_Val1, int a_Val2, int a_Val3, int a_Val4) + { + int rnd = m_Noise.IntNoise2DInt(a_RndX, a_RndZ) / 7; + switch (rnd % 4) + { + case 0: return a_Val1; + case 1: return a_Val2; + case 2: return a_Val3; + default: return a_Val4; + } + } +}; + + + + + + +/** Generates a 2D array of random integers in the specified range [0 .. Range). */ +class cProtIntGenChoice : + public cProtIntGenWithNoise +{ + typedef cProtIntGenWithNoise super; + +public: + cProtIntGenChoice(int a_Seed, int a_Range) : + super(a_Seed), + m_Range(a_Range) + { + } + + + virtual void GetInts(int a_MinX, int a_MinZ, int a_SizeX, int a_SizeZ, int * a_Values) override + { + for (int z = 0; z < a_SizeZ; z++) + { + int BaseZ = a_MinZ + z; + for (int x = 0; x < a_SizeX; x++) + { + a_Values[x + a_SizeX * z] = (super::m_Noise.IntNoise2DInt(a_MinX + x, BaseZ) / 7) % m_Range; + } + } // for z + } + +protected: + int m_Range; +}; + + + + + + +/** Decides between the ocean and landmass biomes. +Has a threshold (in percent) of how much land, the larger the threshold, the more land. +Generates 0 for ocean, biome group ID for landmass. */ +class cProtIntGenLandOcean : + public cProtIntGenWithNoise +{ + typedef cProtIntGenWithNoise super; + +public: + cProtIntGenLandOcean(int a_Seed, int a_Threshold) : + super(a_Seed), + m_Threshold(a_Threshold) + { + } + + + virtual void GetInts(int a_MinX, int a_MinZ, int a_SizeX, int a_SizeZ, int * a_Values) override + { + for (int z = 0; z < a_SizeZ; z++) + { + int BaseZ = a_MinZ + z; + for (int x = 0; x < a_SizeX; x++) + { + int rnd = (super::m_Noise.IntNoise2DInt(a_MinX + x, BaseZ) / 7); + a_Values[x + a_SizeX * z] = ((rnd % 100) < m_Threshold) ? ((rnd / 101) % bgLandOceanMax + 1) : 0; + } + } + + // If the centerpoint of the world is within the area, set it to bgTemperate, always: + if ((a_MinX <= 0) && (a_MinZ <= 0) && (a_MinX + a_SizeX > 0) && (a_MinZ + a_SizeZ > 0)) + { + a_Values[-a_MinX - a_MinZ * a_SizeX] = bgTemperate; + } + } + +protected: + int m_Threshold; +}; + + + + + +/** Zooms the underlying value array to twice the size. Uses random-neighbor for the pixels in-between. +This means that the zoome out image is randomly distorted. Applying zoom several times provides all +the distortion that the generators need. */ +class cProtIntGenZoom : + public cProtIntGenWithNoise +{ + typedef cProtIntGenWithNoise super; + +public: + cProtIntGenZoom(int a_Seed, Underlying a_UnderlyingGen) : + super(a_Seed), + m_UnderlyingGen(a_UnderlyingGen) + { + } + + + virtual void GetInts(int a_MinX, int a_MinZ, int a_SizeX, int a_SizeZ, int * a_Values) override + { + // Get the coords for the lower generator: + int lowerMinX = a_MinX >> 1; + int lowerMinZ = a_MinZ >> 1; + int lowerSizeX = a_SizeX / 2 + 2; + int lowerSizeZ = a_SizeZ / 2 + 2; + ASSERT(lowerSizeX * lowerSizeZ <= m_BufferSize); + ASSERT(lowerSizeX > 0); + ASSERT(lowerSizeZ > 0); + + // Generate the underlying data with half the resolution: + int lowerData[m_BufferSize]; + m_UnderlyingGen->GetInts(lowerMinX, lowerMinZ, lowerSizeX, lowerSizeZ, lowerData); + const int lowStepX = (lowerSizeX - 1) * 2; + int cache[m_BufferSize]; + + // Discreet-interpolate the values into twice the size: + for (int z = 0; z < lowerSizeZ - 1; ++z) + { + int idx = (z * 2) * lowStepX; + int PrevZ0 = lowerData[z * lowerSizeX]; + int PrevZ1 = lowerData[(z + 1) * lowerSizeX]; + + for (int x = 0; x < lowerSizeX - 1; ++x) + { + int ValX1Z0 = lowerData[x + 1 + z * lowerSizeX]; + int ValX1Z1 = lowerData[x + 1 + (z + 1) * lowerSizeX]; + int RndX = (x + lowerMinX) * 2; + int RndZ = (z + lowerMinZ) * 2; + cache[idx] = PrevZ0; + cache[idx + lowStepX] = super::chooseRandomOne(RndX, RndZ + 1, PrevZ0, PrevZ1); + cache[idx + 1] = super::chooseRandomOne(RndX, RndZ - 1, PrevZ0, ValX1Z0); + cache[idx + 1 + lowStepX] = super::chooseRandomOne(RndX, RndZ, PrevZ0, ValX1Z0, PrevZ1, ValX1Z1); + idx += 2; + PrevZ0 = ValX1Z0; + PrevZ1 = ValX1Z1; + } + } + + // Copy from Cache into a_Values; take into account the even/odd offsets in a_Min: + for (int z = 0; z < a_SizeZ; ++z) + { + memcpy(a_Values + z * a_SizeX, cache + (z + (a_MinZ & 1)) * lowStepX + (a_MinX & 1), a_SizeX * sizeof(int)); + } + } + +protected: + Underlying m_UnderlyingGen; +}; + + + + + +/** Smoothes out some artifacts generated by the zooming - mostly single-pixel values. +Compares each pixel to its neighbors and if the neighbors are equal, changes the pixel to their value. */ +class cProtIntGenSmooth : + public cProtIntGenWithNoise +{ + typedef cProtIntGenWithNoise super; + +public: + cProtIntGenSmooth(int a_Seed, Underlying a_Underlying) : + super(a_Seed), + m_Underlying(a_Underlying) + { + } + + + virtual void GetInts(int a_MinX, int a_MinZ, int a_SizeX, int a_SizeZ, int * a_Values) override + { + // Generate the underlying values: + int lowerSizeX = a_SizeX + 2; + int lowerSizeZ = a_SizeZ + 2; + ASSERT(lowerSizeX * lowerSizeZ <= m_BufferSize); + int lowerData[m_BufferSize]; + m_Underlying->GetInts(a_MinX - 1, a_MinZ - 1, lowerSizeX, lowerSizeZ, lowerData); + + // Smooth - for each square check if the surroundings are the same, if so, expand them diagonally. + // Also get rid of single-pixel irregularities (A-B-A): + for (int z = 0; z < a_SizeZ; z++) + { + int NoiseZ = a_MinZ + z; + for (int x = 0; x < a_SizeX; x++) + { + int val = lowerData[x + 1 + (z + 1) * lowerSizeX]; + int above = lowerData[x + 1 + z * lowerSizeX]; + int below = lowerData[x + 1 + (z + 2) * lowerSizeX]; + int left = lowerData[x + (z + 1) * lowerSizeX]; + int right = lowerData[x + 2 + (z + 1) * lowerSizeX]; + + if ((left == right) && (above == below)) + { + if (((super::m_Noise.IntNoise2DInt(a_MinX + x, NoiseZ) / 7) % 2) == 0) + { + val = left; + } + else + { + val = above; + } + } + else + { + if (left == right) + { + val = left; + } + + if (above == below) + { + val = above; + } + } + + a_Values[x + z * a_SizeX] = val; + } + } + } + +protected: + Underlying m_Underlying; +}; + + + + + +/** Converts land biomes at the edge of an ocean into the respective beach biome. */ +class cProtIntGenBeaches : + public cProtIntGen +{ + typedef cProtIntGen super; + +public: + cProtIntGenBeaches(Underlying a_Underlying) : + m_Underlying(a_Underlying) + { + } + + + virtual void GetInts(int a_MinX, int a_MinZ, int a_SizeX, int a_SizeZ, int * a_Values) override + { + // Map for biome -> its beach: + static const int ToBeach[] = + { + /* biOcean */ biOcean, + /* biPlains */ biBeach, + /* biDesert */ biBeach, + /* biExtremeHills */ biStoneBeach, + /* biForest */ biBeach, + /* biTaiga */ biColdBeach, + /* biSwampland */ biSwampland, + /* biRiver */ biRiver, + /* biNether */ biNether, + /* biEnd */ biEnd, + /* biFrozenOcean */ biColdBeach, + /* biFrozenRiver */ biColdBeach, + /* biIcePlains */ biColdBeach, + /* biIceMountains */ biColdBeach, + /* biMushroomIsland */ biMushroomShore, + /* biMushroomShore */ biMushroomShore, + /* biBeach */ biBeach, + /* biDesertHills */ biBeach, + /* biForestHills */ biBeach, + /* biTaigaHills */ biColdBeach, + /* biExtremeHillsEdge */ biStoneBeach, + /* biJungle */ biBeach, + /* biJungleHills */ biBeach, + /* biJungleEdge */ biBeach, + /* biDeepOcean */ biOcean, + /* biStoneBeach */ biStoneBeach, + /* biColdBeach */ biColdBeach, + /* biBirchForest */ biBeach, + /* biBirchForestHills */ biBeach, + /* biRoofedForest */ biBeach, + /* biColdTaiga */ biColdBeach, + /* biColdTaigaHills */ biColdBeach, + /* biMegaTaiga */ biStoneBeach, + /* biMegaTaigaHills */ biStoneBeach, + /* biExtremeHillsPlus */ biStoneBeach, + /* biSavanna */ biBeach, + /* biSavannaPlateau */ biBeach, + /* biMesa */ biMesa, + /* biMesaPlateauF */ biMesa, + /* biMesaPlateau */ biMesa, + }; + + // Generate the underlying values: + int lowerSizeX = a_SizeX + 2; + int lowerSizeZ = a_SizeZ + 2; + ASSERT(lowerSizeX * lowerSizeZ <= m_BufferSize); + int lowerValues[m_BufferSize]; + m_Underlying->GetInts(a_MinX - 1, a_MinZ - 1, lowerSizeX, lowerSizeZ, lowerValues); + + // Add beaches between ocean and biomes: + for (int z = 0; z < a_SizeZ; z++) + { + for (int x = 0; x < a_SizeX; x++) + { + int val = lowerValues[x + 1 + (z + 1) * lowerSizeX]; + int above = lowerValues[x + 1 + z * lowerSizeX]; + int below = lowerValues[x + 1 + (z + 2) * lowerSizeX]; + int left = lowerValues[x + (z + 1) * lowerSizeX]; + int right = lowerValues[x + 2 + (z + 1) * lowerSizeX]; + if (!IsBiomeOcean(val)) + { + if (IsBiomeOcean(above) || IsBiomeOcean(below) || IsBiomeOcean(left) || IsBiomeOcean(right)) + { + // First convert the value to a regular biome (drop the M flag), then modulo by our biome count: + val = ToBeach[(val % 128) % ARRAYCOUNT(ToBeach)]; + } + } + a_Values[x + z * a_SizeX] = val; + } + } + } + +protected: + Underlying m_Underlying; +}; + + + + + +/** Generates the underlying numbers and then randomly changes some ocean group pixels into random land +biome group pixels, based on the predefined chance. */ +class cProtIntGenAddIslands : + public cProtIntGenWithNoise +{ + typedef cProtIntGenWithNoise super; + +public: + typedef std::shared_ptr<cProtIntGen> Underlying; + + + cProtIntGenAddIslands(int a_Seed, int a_Chance, Underlying a_Underlying) : + super(a_Seed), + m_Chance(a_Chance), + m_Underlying(a_Underlying) + { + } + + + virtual void GetInts(int a_MinX, int a_MinZ, int a_SizeX, int a_SizeZ, int * a_Values) override + { + m_Underlying->GetInts(a_MinX, a_MinZ, a_SizeX, a_SizeZ, a_Values); + for (int z = 0; z < a_SizeZ; z++) + { + for (int x = 0; x < a_SizeX; x++) + { + if (a_Values[x + z * a_SizeX] == bgOcean) + { + int rnd = super::m_Noise.IntNoise2DInt(a_MinX + x, a_MinZ + z) / 7; + if (rnd % 1000 < m_Chance) + { + a_Values[x + z * a_SizeX] = (rnd / 1003) % bgLandOceanMax; + } + } + } + } + } + +protected: + /** Chance of each ocean pixel being converted, in permille. */ + int m_Chance; + + Underlying m_Underlying; +}; + + + + + +/** A filter that adds an edge biome group between two biome groups that need an edge between them. */ +class cProtIntGenBiomeGroupEdges : + public cProtIntGen +{ + typedef cProtIntGen super; + +public: + cProtIntGenBiomeGroupEdges(Underlying a_Underlying) : + m_Underlying(a_Underlying) + { + } + + + virtual void GetInts(int a_MinX, int a_MinZ, int a_SizeX, int a_SizeZ, int * a_Values) + { + // Generate the underlying biome groups: + int lowerSizeX = a_SizeX + 2; + int lowerSizeZ = a_SizeZ + 2; + ASSERT(lowerSizeX * lowerSizeZ <= m_BufferSize); + int lowerValues[m_BufferSize]; + m_Underlying->GetInts(a_MinX, a_MinZ, lowerSizeX, lowerSizeZ, lowerValues); + + // Change the biomes on incompatible edges into an edge biome: + for (int z = 0; z < a_SizeZ; z++) + { + for (int x = 0; x < a_SizeX; x++) + { + int val = lowerValues[x + 1 + (z + 1) * lowerSizeX]; + int Above = lowerValues[x + 1 + z * lowerSizeX]; + int Below = lowerValues[x + 1 + (z + 2) * lowerSizeX]; + int Left = lowerValues[x + (z + 1) * lowerSizeX]; + int Right = lowerValues[x + 2 + (z + 1) * lowerSizeX]; + switch (val) + { + // Desert should neighbor only oceans, desert and temperates; change to temperate when another: + case bgDesert: + { + if ( + !isDesertCompatible(Above) || + !isDesertCompatible(Below) || + !isDesertCompatible(Left) || + !isDesertCompatible(Right) + ) + { + val = bgTemperate; + } + break; + } // case bgDesert + + // Ice should not neighbor deserts; change to temperate: + case bgIce: + { + if ( + (Above == bgDesert) || + (Below == bgDesert) || + (Left == bgDesert) || + (Right == bgDesert) + ) + { + val = bgTemperate; + } + break; + } // case bgIce + } + a_Values[x + z * a_SizeX] = val; + } // for x + } // for z + } + +protected: + Underlying m_Underlying; + + + inline bool isDesertCompatible(int a_BiomeGroup) + { + switch (a_BiomeGroup) + { + case bgOcean: + case bgDesert: + case bgTemperate: + { + return true; + } + default: + { + return false; + } + } + } +}; + + + + + +/** Turns biome group indices into real biomes. +For each pixel, takes its biome group and chooses a random biome from that group; replaces the value with +that biome. */ +class cProtIntGenBiomes : + public cProtIntGenWithNoise +{ + typedef cProtIntGenWithNoise super; + +public: + cProtIntGenBiomes(int a_Seed, Underlying a_Underlying) : + super(a_Seed), + m_Underlying(a_Underlying) + { + } + + + virtual void GetInts(int a_MinX, int a_MinZ, int a_SizeX, int a_SizeZ, int * a_Values) override + { + // Define the per-biome-group biomes: + static const int oceanBiomes[] = + { + biOcean, // biDeepOcean, + }; + + // Same as oceanBiomes, there are no rare oceanic biomes (mushroom islands are handled separately) + static const int rareOceanBiomes[] = + { + biOcean, + }; + + static const int desertBiomes[] = + { + biDesert, biDesert, biDesert, biDesert, biDesert, biDesert, biSavanna, biSavanna, biPlains, + }; + + static const int rareDesertBiomes[] = + { + biMesaPlateau, biMesaPlateauF, + }; + + static const int temperateBiomes[] = + { + biForest, biForest, biRoofedForest, biExtremeHills, biPlains, biBirchForest, biSwampland, + }; + + static const int rareTemperateBiomes[] = + { + biJungle, // Jungle is not strictly temperate, but let's piggyback it here + }; + + static const int mountainBiomes[] = + { + biExtremeHills, biForest, biTaiga, biPlains, + }; + + static const int rareMountainBiomes[] = + { + biMegaTaiga, + }; + + static const int iceBiomes[] = + { + biIcePlains, biIcePlains, biIcePlains, biIcePlains, biColdTaiga, + }; + + // Same as iceBiomes, there's no rare ice biome + static const int rareIceBiomes[] = + { + biIcePlains, biIcePlains, biIcePlains, biIcePlains, biColdTaiga, + }; + + static const cBiomesInGroups biomesInGroups[] = + { + /* bgOcean */ { static_cast<int>(ARRAYCOUNT(oceanBiomes)), oceanBiomes}, + /* bgDesert */ { static_cast<int>(ARRAYCOUNT(desertBiomes)), desertBiomes}, + /* bgTemperate */ { static_cast<int>(ARRAYCOUNT(temperateBiomes)), temperateBiomes}, + /* bgMountains */ { static_cast<int>(ARRAYCOUNT(mountainBiomes)), mountainBiomes}, + /* bgIce */ { static_cast<int>(ARRAYCOUNT(iceBiomes)), iceBiomes}, + }; + + static const cBiomesInGroups rareBiomesInGroups[] = + { + /* bgOcean */ { static_cast<int>(ARRAYCOUNT(rareOceanBiomes)), rareOceanBiomes}, + /* bgDesert */ { static_cast<int>(ARRAYCOUNT(rareDesertBiomes)), rareDesertBiomes}, + /* bgTemperate */ { static_cast<int>(ARRAYCOUNT(rareTemperateBiomes)), rareTemperateBiomes}, + /* bgMountains */ { static_cast<int>(ARRAYCOUNT(rareMountainBiomes)), rareMountainBiomes}, + /* bgIce */ { static_cast<int>(ARRAYCOUNT(rareIceBiomes)), rareIceBiomes}, + }; + + // Generate the underlying values, representing biome groups: + m_Underlying->GetInts(a_MinX, a_MinZ, a_SizeX, a_SizeZ, a_Values); + + // Overwrite each biome group with a random biome from that group: + // Take care of the bgfRare flag + for (int z = 0; z < a_SizeZ; z++) + { + int IdxZ = z * a_SizeX; + for (int x = 0; x < a_SizeX; x++) + { + int val = a_Values[x + IdxZ]; + const cBiomesInGroups & Biomes = (val > bgfRare) ? + rareBiomesInGroups[(val & (bgfRare - 1)) % ARRAYCOUNT(rareBiomesInGroups)] : + biomesInGroups[val % ARRAYCOUNT(biomesInGroups)]; + int rnd = (super::m_Noise.IntNoise2DInt(x + a_MinX, z + a_MinZ) / 7); + a_Values[x + IdxZ] = Biomes.Biomes[rnd % Biomes.Count]; + } + } + } + +protected: + + struct cBiomesInGroups + { + const int Count; + const int * Biomes; + }; + + + /** The underlying int generator */ + Underlying m_Underlying; +}; + + + + + +/** Randomly replaces pixels of one value to another value, using the given chance. */ +class cProtIntGenReplaceRandomly : + public cProtIntGenWithNoise +{ + typedef cProtIntGenWithNoise super; + +public: + typedef std::shared_ptr<cProtIntGen> Underlying; + + + cProtIntGenReplaceRandomly(int a_Seed, int a_From, int a_To, int a_Chance, Underlying a_Underlying) : + super(a_Seed), + m_From(a_From), + m_To(a_To), + m_Chance(a_Chance), + m_Underlying(a_Underlying) + { + } + + + virtual void GetInts(int a_MinX, int a_MinZ, int a_SizeX, int a_SizeZ, int * a_Values) override + { + // Generate the underlying values: + m_Underlying->GetInts(a_MinX, a_MinZ, a_SizeX, a_SizeZ, a_Values); + + // Replace some of the values: + for (int z = 0; z < a_SizeZ; z++) + { + int idxZ = z * a_SizeX; + for (int x = 0; x < a_SizeX; x++) + { + int idx = x + idxZ; + if (a_Values[idx] == m_From) + { + int rnd = super::m_Noise.IntNoise2DInt(x + a_MinX, z + a_MinZ) / 7; + if (rnd % 1000 < m_Chance) + { + a_Values[idx] = m_To; + } + } + } + } // for z + } + + +protected: + /** The original value to be replaced. */ + int m_From; + + /** The destination value to which to replace. */ + int m_To; + + /** Chance, in permille, of replacing the value. */ + int m_Chance; + + Underlying m_Underlying; +}; + + + + + +/** Mixer that joins together finalized biomes and rivers. +It first checks for oceans, if there is an ocean in the Biomes, it keeps the ocean. +If there's no ocean, it checks Rivers for a river, if there is a river, it uses the Biomes to select either +regular river or frozen river, based on the biome. */ +class cProtIntGenMixRivers: + public cProtIntGen +{ + typedef cProtIntGen super; + +public: + cProtIntGenMixRivers(Underlying a_Biomes, Underlying a_Rivers): + m_Biomes(a_Biomes), + m_Rivers(a_Rivers) + { + } + + + virtual void GetInts(int a_MinX, int a_MinZ, int a_SizeX, int a_SizeZ, int * a_Values) override + { + // Generate the underlying data: + ASSERT(a_SizeX * a_SizeZ <= m_BufferSize); + m_Biomes->GetInts(a_MinX, a_MinZ, a_SizeX, a_SizeZ, a_Values); + int riverData[m_BufferSize]; + m_Rivers->GetInts(a_MinX, a_MinZ, a_SizeX, a_SizeZ, riverData); + + // Mix the values: + for (int z = 0; z < a_SizeZ; z++) + { + int idxZ = z * a_SizeX; + for (int x = 0; x < a_SizeX; x++) + { + int idx = x + idxZ; + if (IsBiomeOcean(a_Values[idx])) + { + // Oceans are kept without any changes + continue; + } + if (riverData[idx] != biRiver) + { + // There's no river, keep the current value + continue; + } + + // There's a river, change the output to a river or a frozen river, based on the original biome: + if (IsBiomeVeryCold((EMCSBiome)a_Values[idx])) + { + a_Values[idx] = biFrozenRiver; + } + else + { + a_Values[idx] = biRiver; + } + } // for x + } // for z + } + +protected: + Underlying m_Biomes; + Underlying m_Rivers; +}; + + + + + +/** Generates a river based on the underlying data. +This is basically an edge detector over the underlying data. The rivers are the edges where the underlying +data changes from one pixel to its neighbor. */ +class cProtIntGenRiver: + public cProtIntGenWithNoise +{ + typedef cProtIntGenWithNoise super; + +public: + cProtIntGenRiver(int a_Seed, Underlying a_Underlying): + super(a_Seed), + m_Underlying(a_Underlying) + { + } + + + virtual void GetInts(int a_MinX, int a_MinZ, int a_SizeX, int a_SizeZ, int * a_Values) override + { + // Generate the underlying data: + int lowerSizeX = a_SizeX + 2; + int lowerSizeZ = a_SizeZ + 2; + ASSERT(lowerSizeX * lowerSizeZ <= m_BufferSize); + int lowerValues[m_BufferSize]; + m_Underlying->GetInts(a_MinX - 1, a_MinZ - 1, lowerSizeX, lowerSizeZ, lowerValues); + + // Detect the edges: + for (int z = 0; z < a_SizeZ; z++) + { + for (int x = 0; x < a_SizeX; x++) + { + int Above = lowerValues[x + 1 + z * lowerSizeX]; + int Below = lowerValues[x + 1 + (z + 2) * lowerSizeX]; + int Left = lowerValues[x + (z + 1) * lowerSizeX]; + int Right = lowerValues[x + 2 + (z + 1) * lowerSizeX]; + int val = lowerValues[x + 1 + (z + 1) * lowerSizeX]; + + if ((val == Above) && (val == Below) && (val == Left) && (val == Right)) + { + val = 0; + } + else + { + val = biRiver; + } + a_Values[x + z * a_SizeX] = val; + } // for x + } // for z + } + +protected: + Underlying m_Underlying; +}; + + + + + +/** Turns some of the oceans into the specified biome. Used for mushroom and deep ocean. +The biome is only placed if at least 3 of its neighbors are ocean and only with the specified chance. */ +class cProtIntGenAddToOcean: + public cProtIntGenWithNoise +{ + typedef cProtIntGenWithNoise super; + +public: + cProtIntGenAddToOcean(int a_Seed, int a_Chance, int a_ToValue, Underlying a_Underlying): + super(a_Seed), + m_Chance(a_Chance), + m_ToValue(a_ToValue), + m_Underlying(a_Underlying) + { + } + + + virtual void GetInts(int a_MinX, int a_MinZ, int a_SizeX, int a_SizeZ, int * a_Values) override + { + // Generate the underlying data: + int lowerSizeX = a_SizeX + 2; + int lowerSizeZ = a_SizeZ + 2; + ASSERT(lowerSizeX * lowerSizeZ <= m_BufferSize); + int lowerValues[m_BufferSize]; + m_Underlying->GetInts(a_MinX - 1, a_MinZ - 1, lowerSizeX, lowerSizeZ, lowerValues); + + // Add the mushroom islands: + for (int z = 0; z < a_SizeZ; z++) + { + for (int x = 0; x < a_SizeX; x++) + { + int val = lowerValues[x + 1 + (z + 1) * lowerSizeX]; + if (!IsBiomeOcean(val)) + { + a_Values[x + z * a_SizeX] = val; + continue; + } + + // Count the ocean neighbors: + int Above = lowerValues[x + 1 + z * lowerSizeX]; + int Below = lowerValues[x + 1 + (z + 2) * lowerSizeX]; + int Left = lowerValues[x + (z + 1) * lowerSizeX]; + int Right = lowerValues[x + 2 + (z + 1) * lowerSizeX]; + int NumOceanNeighbors = 0; + if (IsBiomeOcean(Above)) + { + NumOceanNeighbors += 1; + } + if (IsBiomeOcean(Below)) + { + NumOceanNeighbors += 1; + } + if (IsBiomeOcean(Left)) + { + NumOceanNeighbors += 1; + } + if (IsBiomeOcean(Right)) + { + NumOceanNeighbors += 1; + } + + // If at least 3 ocean neighbors and the chance is right, change: + if ( + (NumOceanNeighbors >= 3) && + ((super::m_Noise.IntNoise2DInt(x + a_MinX, z + a_MinZ) / 7) % 1000 < m_Chance) + ) + { + a_Values[x + z * a_SizeX] = m_ToValue; + } + else + { + a_Values[x + z * a_SizeX] = val; + } + } // for x + } // for z + } + +protected: + /** Chance, in permille, of changing the biome. */ + int m_Chance; + + /** The value to change the ocean into. */ + int m_ToValue; + + Underlying m_Underlying; +}; + + + + + +/** Changes random pixels of the underlying data to the specified value. */ +class cProtIntGenSetRandomly : + public cProtIntGenWithNoise +{ + typedef cProtIntGenWithNoise super; + +public: + cProtIntGenSetRandomly(int a_Seed, int a_Chance, int a_ToValue, Underlying a_Underlying) : + super(a_Seed), + m_Chance(a_Chance), + m_ToValue(a_ToValue), + m_Underlying(a_Underlying) + { + } + + + virtual void GetInts(int a_MinX, int a_MinZ, int a_SizeX, int a_SizeZ, int * a_Values) override + { + // Generate the underlying data: + m_Underlying->GetInts(a_MinX, a_MinZ, a_SizeX, a_SizeZ, a_Values); + + // Change random pixels to bgOcean: + for (int z = 0; z < a_SizeZ; z++) + { + for (int x = 0; x < a_SizeX; x++) + { + int rnd = super::m_Noise.IntNoise2DInt(x + a_MinX, z + a_MinZ) / 7; + if (rnd % 1000 < m_Chance) + { + a_Values[x + z * a_SizeX] = m_ToValue; + } + } + } + } + +protected: + /** Chance, in permille, of changing each pixel. */ + int m_Chance; + + /** The value to which to set the pixel. */ + int m_ToValue; + + Underlying m_Underlying; +}; + + + + + + +/** Adds a "rare" flag to random biome groups, based on the given chance. */ +class cProtIntGenRareBiomeGroups: + public cProtIntGenWithNoise +{ + typedef cProtIntGenWithNoise super; + +public: + cProtIntGenRareBiomeGroups(int a_Seed, int a_Chance, Underlying a_Underlying): + super(a_Seed), + m_Chance(a_Chance), + m_Underlying(a_Underlying) + { + } + + + virtual void GetInts(int a_MinX, int a_MinZ, int a_SizeX, int a_SizeZ, int * a_Values) override + { + // Generate the underlying data: + m_Underlying->GetInts(a_MinX, a_MinZ, a_SizeX, a_SizeZ, a_Values); + + // Change some of the biome groups into rare biome groups: + for (int z = 0; z < a_SizeZ; z++) + { + for (int x = 0; x < a_SizeX; x++) + { + int rnd = super::m_Noise.IntNoise2DInt(x + a_MinX, z + a_MinZ) / 7; + if (rnd % 1000 < m_Chance) + { + int idx = x + a_SizeX * z; + a_Values[idx] = a_Values[idx] | bgfRare; + } + } + } + } + +protected: + /** Chance, in permille, of changing each pixel into the rare biome group. */ + int m_Chance; + + /** The underlying generator. */ + Underlying m_Underlying; +}; + + + + + +/** Changes biomes in the parent data into an alternate versions (usually "hill" variants), in such places +that have their alterations set. */ +class cProtIntGenAlternateBiomes: + public cProtIntGenWithNoise +{ + typedef cProtIntGenWithNoise super; + +public: + cProtIntGenAlternateBiomes(int a_Seed, Underlying a_Alterations, Underlying a_BaseBiomes): + super(a_Seed), + m_Alterations(a_Alterations), + m_BaseBiomes(a_BaseBiomes) + { + } + + + virtual void GetInts(int a_MinX, int a_MinZ, int a_SizeX, int a_SizeZ, int * a_Values) override + { + // Generate the base biomes and the alterations: + m_BaseBiomes->GetInts(a_MinX, a_MinZ, a_SizeX, a_SizeZ, a_Values); + int alterations[m_BufferSize]; + m_Alterations->GetInts(a_MinX, a_MinZ, a_SizeX, a_SizeZ, alterations); + + // Change the biomes into their alternate versions: + int len = a_SizeX * a_SizeZ; + for (int idx = 0; idx < len; ++idx) + { + if (alterations[idx] == 0) + { + // No change + continue; + } + + // Change to alternate biomes: + int val = a_Values[idx]; + switch (val) + { + case biBirchForest: val = biBirchForestHills; break; + case biDesert: val = biDesertHills; break; + case biExtremeHills: val = biExtremeHillsPlus; break; + case biForest: val = biForestHills; break; + case biIcePlains: val = biIceMountains; break; + case biJungle: val = biJungleHills; break; + case biMegaTaiga: val = biMegaTaigaHills; break; + case biMesaPlateau: val = biMesa; break; + case biMesaPlateauF: val = biMesa; break; + case biMesaPlateauM: val = biMesa; break; + case biMesaPlateauFM: val = biMesa; break; + case biPlains: val = biForest; break; + case biRoofedForest: val = biPlains; break; + case biSavanna: val = biSavannaPlateau; break; + case biTaiga: val = biTaigaHills; break; + } + a_Values[idx] = val; + } // for idx - a_Values[] + } + +protected: + Underlying m_Alterations; + Underlying m_BaseBiomes; +}; + + + + + +/** Adds an edge between two specifically incompatible biomes, such as mesa and forest. */ +class cProtIntGenBiomeEdges: + public cProtIntGenWithNoise +{ + typedef cProtIntGenWithNoise super; + +public: + cProtIntGenBiomeEdges(int a_Seed, Underlying a_Underlying): + super(a_Seed), + m_Underlying(a_Underlying) + { + } + + + virtual void GetInts(int a_MinX, int a_MinZ, int a_SizeX, int a_SizeZ, int * a_Values) override + { + // Generate the underlying biomes: + int lowerSizeX = a_SizeX + 2; + int lowerSizeZ = a_SizeZ + 2; + ASSERT(lowerSizeX * lowerSizeZ <= m_BufferSize); + int lowerValues[m_BufferSize]; + m_Underlying->GetInts(a_MinX - 1, a_MinZ - 1, lowerSizeX, lowerSizeZ, lowerValues); + + // Convert incompatible edges into neutral biomes: + for (int z = 0; z < a_SizeZ; z++) + { + for (int x = 0; x < a_SizeX; x++) + { + int biome = lowerValues[x + 1 + (z + 1) * lowerSizeX]; + int above = lowerValues[x + 1 + z * lowerSizeX]; + int below = lowerValues[x + 1 + (z + 2) * lowerSizeX]; + int left = lowerValues[x + (z + 1) * lowerSizeX]; + int right = lowerValues[x + 2 + (z + 1) * lowerSizeX]; + + switch (biome) + { + case biDesert: + case biDesertM: + case biDesertHills: + { + if ( + IsBiomeVeryCold(static_cast<EMCSBiome>(above)) || + IsBiomeVeryCold(static_cast<EMCSBiome>(below)) || + IsBiomeVeryCold(static_cast<EMCSBiome>(left)) || + IsBiomeVeryCold(static_cast<EMCSBiome>(right)) + ) + { + biome = biPlains; + } + break; + } // case biDesert + + case biMesaPlateau: + case biMesaPlateauF: + case biMesaPlateauFM: + case biMesaPlateauM: + { + if ( + !isMesaCompatible(above) || + !isMesaCompatible(below) || + !isMesaCompatible(left) || + !isMesaCompatible(right) + ) + { + biome = biDesert; + } + break; + } // Mesa biomes + + case biJungle: + case biJungleM: + { + if ( + !isJungleCompatible(above) || + !isJungleCompatible(below) || + !isJungleCompatible(left) || + !isJungleCompatible(right) + ) + { + biome = (biome == biJungle) ? biJungleEdge : biJungleEdgeM; + } + break; + } // Jungle biomes + + case biSwampland: + case biSwamplandM: + { + if ( + IsBiomeNoDownfall(static_cast<EMCSBiome>(above)) || + IsBiomeNoDownfall(static_cast<EMCSBiome>(below)) || + IsBiomeNoDownfall(static_cast<EMCSBiome>(left)) || + IsBiomeNoDownfall(static_cast<EMCSBiome>(right)) + ) + { + biome = biPlains; + } + break; + } // Swampland biomes + } // switch (biome) + + a_Values[x + z * a_SizeX] = biome; + } // for x + } // for z + } + + +protected: + Underlying m_Underlying; + + + bool isMesaCompatible(int a_Biome) + { + switch (a_Biome) + { + case biDesert: + case biMesa: + case biMesaBryce: + case biMesaPlateau: + case biMesaPlateauF: + case biMesaPlateauFM: + case biMesaPlateauM: + case biOcean: + case biDeepOcean: + { + return true; + } + default: + { + return false; + } + } + } + + + bool isJungleCompatible(int a_Biome) + { + switch (a_Biome) + { + case biJungle: + case biJungleM: + case biJungleEdge: + case biJungleEdgeM: + case biJungleHills: + { + return true; + } + default: + { + return false; + } + } + } +}; + + + + + +/** Changes biomes in the parent data into their alternate versions ("M" variants), in such places that +have their alterations set. */ +class cProtIntGenMBiomes: + public cProtIntGenWithNoise +{ + typedef cProtIntGenWithNoise super; + +public: + cProtIntGenMBiomes(int a_Seed, Underlying a_Alteration, Underlying a_Underlying): + super(a_Seed), + m_Underlying(a_Underlying), + m_Alteration(a_Alteration) + { + } + + + virtual void GetInts(int a_MinX, int a_MinZ, int a_SizeX, int a_SizeZ, int * a_Values) override + { + // Generate the underlying biomes and the alterations: + m_Underlying->GetInts(a_MinX, a_MinZ, a_SizeX, a_SizeZ, a_Values); + int alterations[m_BufferSize]; + m_Alteration->GetInts(a_MinX, a_MinZ, a_SizeX, a_SizeZ, alterations); + + // Wherever alterations are nonzero, change into alternate biome, if available: + int len = a_SizeX * a_SizeZ; + for (int idx = 0; idx < len; ++idx) + { + if (alterations[idx] == 0) + { + continue; + } + + // Ice spikes biome was removed from here, because it was generated way too often + switch (a_Values[idx]) + { + case biPlains: a_Values[idx] = biSunflowerPlains; break; + case biDesert: a_Values[idx] = biDesertM; break; + case biExtremeHills: a_Values[idx] = biExtremeHillsM; break; + case biForest: a_Values[idx] = biFlowerForest; break; + case biTaiga: a_Values[idx] = biTaigaM; break; + case biSwampland: a_Values[idx] = biSwamplandM; break; + case biJungle: a_Values[idx] = biJungleM; break; + case biJungleEdge: a_Values[idx] = biJungleEdgeM; break; + case biBirchForest: a_Values[idx] = biBirchForestM; break; + case biBirchForestHills: a_Values[idx] = biBirchForestHillsM; break; + case biRoofedForest: a_Values[idx] = biRoofedForestM; break; + case biColdTaiga: a_Values[idx] = biColdTaigaM; break; + case biMegaSpruceTaiga: a_Values[idx] = biMegaSpruceTaiga; break; + case biMegaSpruceTaigaHills: a_Values[idx] = biMegaSpruceTaigaHills; break; + case biExtremeHillsPlus: a_Values[idx] = biExtremeHillsPlusM; break; + case biSavanna: a_Values[idx] = biSavannaM; break; + case biSavannaPlateau: a_Values[idx] = biSavannaPlateauM; break; + case biMesa: a_Values[idx] = biMesaBryce; break; + case biMesaPlateauF: a_Values[idx] = biMesaPlateauFM; break; + case biMesaPlateau: a_Values[idx] = biMesaBryce; break; + } + } // for idx - a_Values[] / alterations[] + } + +protected: + Underlying m_Underlying; + Underlying m_Alteration; +}; + + + + |