summaryrefslogtreecommitdiffstats
path: root/src/Generating
diff options
context:
space:
mode:
Diffstat (limited to 'src/Generating')
-rw-r--r--src/Generating/BioGen.cpp268
-rw-r--r--src/Generating/BioGen.h2
-rw-r--r--src/Generating/CMakeLists.txt14
-rw-r--r--src/Generating/Caves.cpp10
-rw-r--r--src/Generating/Caves.h1
-rw-r--r--src/Generating/ChunkDesc.cpp65
-rw-r--r--src/Generating/ChunkDesc.h25
-rw-r--r--src/Generating/ChunkGenerator.cpp55
-rw-r--r--src/Generating/ChunkGenerator.h47
-rw-r--r--src/Generating/CompoGen.cpp350
-rw-r--r--src/Generating/CompoGen.h47
-rw-r--r--src/Generating/CompoGenBiomal.cpp586
-rw-r--r--src/Generating/CompoGenBiomal.h21
-rw-r--r--src/Generating/ComposableGenerator.cpp120
-rw-r--r--src/Generating/ComposableGenerator.h95
-rw-r--r--src/Generating/CompositedHeiGen.h49
-rw-r--r--src/Generating/DistortedHeightmap.cpp595
-rw-r--r--src/Generating/DistortedHeightmap.h61
-rw-r--r--src/Generating/DungeonRoomsFinisher.cpp52
-rw-r--r--src/Generating/DungeonRoomsFinisher.h8
-rw-r--r--src/Generating/EndGen.cpp44
-rw-r--r--src/Generating/EndGen.h10
-rw-r--r--src/Generating/FinishGen.cpp346
-rw-r--r--src/Generating/FinishGen.h88
-rw-r--r--src/Generating/GridStructGen.h2
-rw-r--r--src/Generating/HeiGen.cpp449
-rw-r--r--src/Generating/HeiGen.h100
-rw-r--r--src/Generating/IntGen.h1406
-rw-r--r--src/Generating/MineShafts.cpp5
-rw-r--r--src/Generating/MineShafts.h1
-rw-r--r--src/Generating/Noise3DGenerator.cpp678
-rw-r--r--src/Generating/Noise3DGenerator.h147
-rw-r--r--src/Generating/PieceGenerator.h2
-rw-r--r--src/Generating/ProtIntGen.h1351
-rw-r--r--src/Generating/Ravines.h1
-rw-r--r--src/Generating/ShapeGen.cpp145
-rw-r--r--src/Generating/StructGen.cpp36
-rw-r--r--src/Generating/StructGen.h35
-rw-r--r--src/Generating/Trees.cpp106
-rw-r--r--src/Generating/Trees.h8
-rw-r--r--src/Generating/TwoHeights.cpp121
-rw-r--r--src/Generating/TwoHeights.h23
-rw-r--r--src/Generating/VillageGen.cpp6
43 files changed, 5965 insertions, 1616 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/BioGen.h b/src/Generating/BioGen.h
index 5fd0844d9..13fb40c5f 100644
--- a/src/Generating/BioGen.h
+++ b/src/Generating/BioGen.h
@@ -15,7 +15,7 @@ Interfaces to the various biome generators:
#pragma once
#include "ComposableGenerator.h"
-#include "../Noise.h"
+#include "../Noise/Noise.h"
#include "../VoronoiMap.h"
diff --git a/src/Generating/CMakeLists.txt b/src/Generating/CMakeLists.txt
index cd3d5a9f3..a28510d40 100644
--- a/src/Generating/CMakeLists.txt
+++ b/src/Generating/CMakeLists.txt
@@ -10,6 +10,7 @@ SET (SRCS
ChunkDesc.cpp
ChunkGenerator.cpp
CompoGen.cpp
+ CompoGenBiomal.cpp
ComposableGenerator.cpp
DistortedHeightmap.cpp
DungeonRoomsFinisher.cpp
@@ -30,8 +31,10 @@ SET (SRCS
StructGen.cpp
TestRailsGen.cpp
Trees.cpp
+ TwoHeights.cpp
UnderwaterBaseGen.cpp
- VillageGen.cpp)
+ VillageGen.cpp
+)
SET (HDRS
BioGen.h
@@ -39,13 +42,16 @@ SET (HDRS
ChunkDesc.h
ChunkGenerator.h
CompoGen.h
+ CompoGenBiomal.h
ComposableGenerator.h
+ CompositedHeiGen.h
DistortedHeightmap.h
DungeonRoomsFinisher.h
EndGen.h
FinishGen.h
GridStructGen.h
HeiGen.h
+ IntGen.h
MineShafts.h
NetherFortGen.h
Noise3DGenerator.h
@@ -53,14 +59,18 @@ SET (HDRS
PieceGenerator.h
Prefab.h
PrefabPiecePool.h
+ ProtIntGen.h
RainbowRoadsGen.h
Ravines.h
RoughRavines.h
+ ShapeGen.cpp
StructGen.h
TestRailsGen.h
Trees.h
+ TwoHeights.h
UnderwaterBaseGen.h
- VillageGen.h)
+ VillageGen.h
+)
if(NOT MSVC)
add_library(Generating ${SRCS} ${HDRS})
diff --git a/src/Generating/Caves.cpp b/src/Generating/Caves.cpp
index fc925a150..e4735cb83 100644
--- a/src/Generating/Caves.cpp
+++ b/src/Generating/Caves.cpp
@@ -692,8 +692,14 @@ static float GetMarbleNoise( float x, float y, float z, cNoise & a_Noise)
float oct1 = (a_Noise.CubicNoise3D(x * 0.1f, y * 0.1f, z * 0.1f)) * 4;
oct1 = oct1 * oct1 * oct1;
- if (oct1 < 0.f) oct1 = PI_2;
- if (oct1 > PI_2) oct1 = PI_2;
+ if (oct1 < 0.f)
+ {
+ oct1 = PI_2;
+ }
+ if (oct1 > PI_2)
+ {
+ oct1 = PI_2;
+ }
return oct1;
}
diff --git a/src/Generating/Caves.h b/src/Generating/Caves.h
index 0e17acf9e..691ef3e62 100644
--- a/src/Generating/Caves.h
+++ b/src/Generating/Caves.h
@@ -13,7 +13,6 @@
#pragma once
#include "GridStructGen.h"
-#include "../Noise.h"
diff --git a/src/Generating/ChunkDesc.cpp b/src/Generating/ChunkDesc.cpp
index 020d3bd0f..4a5ac5a18 100644
--- a/src/Generating/ChunkDesc.cpp
+++ b/src/Generating/ChunkDesc.cpp
@@ -7,7 +7,7 @@
#include "ChunkDesc.h"
#include "../BlockArea.h"
#include "../Cuboid.h"
-#include "../Noise.h"
+#include "../Noise/Noise.h"
#include "../BlockEntities/BlockEntity.h"
@@ -152,6 +152,52 @@ int cChunkDesc::GetHeight(int a_RelX, int a_RelZ)
+void cChunkDesc::SetHeightFromShape(const Shape & a_Shape)
+{
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ for (int y = cChunkDef::Height - 1; y > 0; y--)
+ {
+ if (a_Shape[y + x * 256 + z * 16 * 256] != 0)
+ {
+ cChunkDef::SetHeight(m_HeightMap, x, z, y);
+ break;
+ }
+ } // for y
+ } // for x
+ } // for z
+}
+
+
+
+
+
+void cChunkDesc::GetShapeFromHeight(Shape & a_Shape) const
+{
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ int height = cChunkDef::GetHeight(m_HeightMap, x, z);
+ for (int y = 0; y <= height; y++)
+ {
+ a_Shape[y + x * 256 + z * 16 * 256] = 1;
+ }
+
+ for (int y = height + 1; y < cChunkDef::Height; y++)
+ {
+ a_Shape[y + x * 256 + z * 16 * 256] = 0;
+ } // for y
+ } // for x
+ } // for z
+}
+
+
+
+
+
void cChunkDesc::SetUseDefaultBiomes(bool a_bUseDefaultBiomes)
{
m_bUseDefaultBiomes = a_bUseDefaultBiomes;
@@ -366,6 +412,23 @@ HEIGHTTYPE cChunkDesc::GetMaxHeight(void) const
+HEIGHTTYPE cChunkDesc::GetMinHeight(void) const
+{
+ HEIGHTTYPE MinHeight = m_HeightMap[0];
+ for (size_t i = 1; i < ARRAYCOUNT(m_HeightMap); i++)
+ {
+ if (m_HeightMap[i] < MinHeight)
+ {
+ MinHeight = m_HeightMap[i];
+ }
+ }
+ return MinHeight;
+}
+
+
+
+
+
void cChunkDesc::FillRelCuboid(
int a_MinX, int a_MaxX,
int a_MinY, int a_MaxY,
diff --git a/src/Generating/ChunkDesc.h b/src/Generating/ChunkDesc.h
index 570132790..480106fb5 100644
--- a/src/Generating/ChunkDesc.h
+++ b/src/Generating/ChunkDesc.h
@@ -29,10 +29,17 @@ class cChunkDesc
{
public:
// tolua_end
+
+ /** The datatype used to represent the entire chunk worth of shape.
+ 0 = air
+ 1 = solid
+ Indexed as [y + 256 * x + 256 * 16 * z]. */
+ typedef Byte Shape[256 * 16 * 16];
/** Uncompressed block metas, 1 meta per byte */
typedef NIBBLETYPE BlockNibbleBytes[cChunkDef::NumBlocks];
+
cChunkDesc(int a_ChunkX, int a_ChunkZ);
~cChunkDesc();
@@ -57,10 +64,21 @@ public:
EMCSBiome GetBiome(int a_RelX, int a_RelZ);
// These operate on the heightmap, so they could get out of sync with the data
- // Use UpdateHeightmap() to re-sync
+ // Use UpdateHeightmap() to re-calculate heightmap from the block data
void SetHeight(int a_RelX, int a_RelZ, int a_Height);
int GetHeight(int a_RelX, int a_RelZ);
+ // tolua_end
+
+ /** Sets the heightmap to match the given shape data.
+ Note that this ignores overhangs; the method is mostly used by old composition generators. */
+ void SetHeightFromShape(const Shape & a_Shape);
+
+ /** Sets the shape in a_Shape to match the heightmap stored currently in m_HeightMap. */
+ void GetShapeFromHeight(Shape & a_Shape) const;
+
+ // tolua_begin
+
// Default generation:
void SetUseDefaultBiomes(bool a_bUseDefaultBiomes);
bool IsUsingDefaultBiomes(void) const;
@@ -77,8 +95,11 @@ public:
/** Reads an area from the chunk into a cBlockArea, blocktypes and blockmetas */
void ReadBlockArea(cBlockArea & a_Dest, int a_MinRelX, int a_MaxRelX, int a_MinRelY, int a_MaxRelY, int a_MinRelZ, int a_MaxRelZ);
- /** Returns the maximum height value in the heightmap */
+ /** Returns the maximum height value in the heightmap. */
HEIGHTTYPE GetMaxHeight(void) const;
+
+ /** Returns the minimum height value in the heightmap. */
+ HEIGHTTYPE GetMinHeight(void) const;
/** Fills the relative cuboid with specified block; allows cuboid out of range of this chunk */
void FillRelCuboid(
diff --git a/src/Generating/ChunkGenerator.cpp b/src/Generating/ChunkGenerator.cpp
index 92e1bb31d..854aac60d 100644
--- a/src/Generating/ChunkGenerator.cpp
+++ b/src/Generating/ChunkGenerator.cpp
@@ -6,7 +6,7 @@
#include "ChunkDesc.h"
#include "ComposableGenerator.h"
#include "Noise3DGenerator.h"
-#include "../MersenneTwister.h"
+#include "FastRandom.h"
@@ -110,29 +110,19 @@ void cChunkGenerator::Stop(void)
-void cChunkGenerator::QueueGenerateChunk(int a_ChunkX, int a_ChunkZ, bool a_ForceGenerate)
+void cChunkGenerator::QueueGenerateChunk(int a_ChunkX, int a_ChunkZ, bool a_ForceGenerate, cChunkCoordCallback * a_Callback)
{
ASSERT(m_ChunkSink->IsChunkQueued(a_ChunkX, a_ChunkZ));
{
cCSLock Lock(m_CS);
- // Check if it is already in the queue:
- for (cChunkCoordsWithBoolList::iterator itr = m_Queue.begin(); itr != m_Queue.end(); ++itr)
- {
- if ((itr->m_ChunkX == a_ChunkX) && (itr->m_ChunkZ == a_ChunkZ))
- {
- // Already in the queue, bail out
- return;
- }
- } // for itr - m_Queue[]
-
// Add to queue, issue a warning if too many:
if (m_Queue.size() >= QUEUE_WARNING_LIMIT)
{
LOGWARN("WARNING: Adding chunk [%i, %i] to generation queue; Queue is too big! (" SIZE_T_FMT ")", a_ChunkX, a_ChunkZ, m_Queue.size());
}
- m_Queue.push_back(cChunkCoordsWithBool(a_ChunkX, a_ChunkZ, a_ForceGenerate));
+ m_Queue.push_back(cQueueItem{a_ChunkX, a_ChunkZ, a_ForceGenerate, a_Callback});
}
m_Event.Set();
@@ -191,13 +181,13 @@ EMCSBiome cChunkGenerator::GetBiomeAt(int a_BlockX, int a_BlockZ)
BLOCKTYPE cChunkGenerator::GetIniBlock(cIniFile & a_IniFile, const AString & a_SectionName, const AString & a_ValueName, const AString & a_Default)
{
AString BlockType = a_IniFile.GetValueSet(a_SectionName, a_ValueName, a_Default);
- BLOCKTYPE Block = BlockStringToType(BlockType);
+ int Block = BlockStringToType(BlockType);
if (Block < 0)
{
LOGWARN("[%s].%s Could not parse block value \"%s\". Using default: \"%s\".", a_SectionName.c_str(), a_ValueName.c_str(), BlockType.c_str(), a_Default.c_str());
- return BlockStringToType(a_Default);
+ return static_cast<BLOCKTYPE>(BlockStringToType(a_Default));
}
- return Block;
+ return static_cast<BLOCKTYPE>(Block);
}
@@ -242,9 +232,9 @@ void cChunkGenerator::Execute(void)
continue;
}
- cChunkCoordsWithBool coords = m_Queue.front(); // Get next coord from queue
+ cQueueItem item = m_Queue.front(); // Get next chunk from the queue
bool SkipEnabled = (m_Queue.size() > QUEUE_SKIP_LIMIT);
- m_Queue.erase(m_Queue.begin()); // Remove coordinate from queue
+ m_Queue.erase(m_Queue.begin()); // Remove the item from the queue
Lock.Unlock(); // Unlock ASAP
m_evtRemoved.Set();
@@ -258,22 +248,35 @@ void cChunkGenerator::Execute(void)
LastReportTick = clock();
}
- if (!coords.m_ForceGenerate && m_ChunkSink->IsChunkValid(coords.m_ChunkX, coords.m_ChunkZ))
+ // Skip the chunk if it's already generated and regeneration is not forced:
+ if (!item.m_ForceGenerate && m_ChunkSink->IsChunkValid(item.m_ChunkX, item.m_ChunkZ))
{
- LOGD("Chunk [%d, %d] already generated, skipping generation", coords.m_ChunkX, coords.m_ChunkZ);
- // Already generated, ignore request
+ LOGD("Chunk [%d, %d] already generated, skipping generation", item.m_ChunkX, item.m_ChunkZ);
+ if (item.m_Callback != nullptr)
+ {
+ item.m_Callback->Call(item.m_ChunkX, item.m_ChunkZ);
+ }
continue;
}
- if (SkipEnabled && !m_ChunkSink->HasChunkAnyClients(coords.m_ChunkX, coords.m_ChunkZ))
+ // Skip the chunk if the generator is overloaded:
+ if (SkipEnabled && !m_ChunkSink->HasChunkAnyClients(item.m_ChunkX, item.m_ChunkZ))
{
- LOGWARNING("Chunk generator overloaded, skipping chunk [%d, %d]", coords.m_ChunkX, coords.m_ChunkZ);
+ LOGWARNING("Chunk generator overloaded, skipping chunk [%d, %d]", item.m_ChunkX, item.m_ChunkZ);
+ if (item.m_Callback != nullptr)
+ {
+ item.m_Callback->Call(item.m_ChunkX, item.m_ChunkZ);
+ }
continue;
}
- LOGD("Generating chunk [%d, %d]", coords.m_ChunkX, coords.m_ChunkZ);
- DoGenerate(coords.m_ChunkX, coords.m_ChunkZ);
-
+ // Generate the chunk:
+ LOGD("Generating chunk [%d, %d]", item.m_ChunkX, item.m_ChunkZ);
+ DoGenerate(item.m_ChunkX, item.m_ChunkZ);
+ if (item.m_Callback != nullptr)
+ {
+ item.m_Callback->Call(item.m_ChunkX, item.m_ChunkZ);
+ }
NumChunksGenerated++;
} // while (!bStop)
}
diff --git a/src/Generating/ChunkGenerator.h b/src/Generating/ChunkGenerator.h
index 190d9e616..4af486ae1 100644
--- a/src/Generating/ChunkGenerator.h
+++ b/src/Generating/ChunkGenerator.h
@@ -119,8 +119,12 @@ public:
bool Start(cPluginInterface & a_PluginInterface, cChunkSink & a_ChunkSink, cIniFile & a_IniFile);
void Stop(void);
- /// Queues the chunk for generation; removes duplicate requests
- void QueueGenerateChunk(int a_ChunkX, int a_ChunkZ, bool a_ForceGenerate);
+ /** Queues the chunk for generation
+ If a-ForceGenerate is set, the chunk is regenerated even if the data is already present in the chunksink.
+ a_Callback is called after the chunk is generated. If the chunk was already present, the callback is still called, even if not regenerating.
+ It is legal to set the callback to nullptr, no callback is called then.
+ If the generator becomes overloaded and skips this chunk, the callback is still called. */
+ void QueueGenerateChunk(int a_ChunkX, int a_ChunkZ, bool a_ForceGenerate, cChunkCoordCallback * a_Callback = nullptr);
/// Generates the biomes for the specified chunk (directly, not in a separate thread). Used by the world loader if biomes failed loading.
void GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap);
@@ -131,22 +135,46 @@ public:
int GetSeed(void) const { return m_Seed; }
- /// Returns the biome at the specified coords. Used by ChunkMap if an invalid chunk is queried for biome
+ /** Returns the biome at the specified coords. Used by ChunkMap if an invalid chunk is queried for biome */
EMCSBiome GetBiomeAt(int a_BlockX, int a_BlockZ);
- /// Reads a block type from the ini file; returns the blocktype on success, emits a warning and returns a_Default's representation on failure.
+ /** Reads a block type from the ini file; returns the blocktype on success, emits a warning and returns a_Default's representation on failure. */
static BLOCKTYPE GetIniBlock(cIniFile & a_IniFile, const AString & a_SectionName, const AString & a_ValueName, const AString & a_Default);
private:
+ struct cQueueItem
+ {
+ /** The chunk coords */
+ int m_ChunkX, m_ChunkZ;
+
+ /** Force the regeneration of an already existing chunk */
+ bool m_ForceGenerate;
+
+ /** Callback to call after generating.*/
+ cChunkCoordCallback * m_Callback;
+ };
+
+ typedef std::list<cQueueItem> cGenQueue;
+
+
+ /** Seed used for the generator. */
int m_Seed;
- cCriticalSection m_CS;
- cChunkCoordsWithBoolList m_Queue;
- cEvent m_Event; ///< Set when an item is added to the queue or the thread should terminate
- cEvent m_evtRemoved; ///< Set when an item is removed from the queue
+ /** CS protecting access to the queue. */
+ cCriticalSection m_CS;
+
+ /** Queue of the chunks to be generated. Protected against multithreaded access by m_CS. */
+ cGenQueue m_Queue;
+
+ /** Set when an item is added to the queue or the thread should terminate. */
+ cEvent m_Event;
+
+ /** Set when an item is removed from the queue. */
+ cEvent m_evtRemoved;
- cGenerator * m_Generator; ///< The actual generator engine used to generate chunks
+ /** The actual generator engine used to generate chunks. */
+ cGenerator * m_Generator;
/** The plugin interface that may modify the generated chunks */
cPluginInterface * m_PluginInterface;
@@ -158,6 +186,7 @@ private:
// cIsThread override:
virtual void Execute(void) override;
+ /** Generates the specified chunk and sets it into the chunksink. */
void DoGenerate(int a_ChunkX, int a_ChunkZ);
};
diff --git a/src/Generating/CompoGen.cpp b/src/Generating/CompoGen.cpp
index 29b831dfd..cb9c04fd7 100644
--- a/src/Generating/CompoGen.cpp
+++ b/src/Generating/CompoGen.cpp
@@ -21,8 +21,9 @@
////////////////////////////////////////////////////////////////////////////////
// cCompoGenSameBlock:
-void cCompoGenSameBlock::ComposeTerrain(cChunkDesc & a_ChunkDesc)
+void cCompoGenSameBlock::ComposeTerrain(cChunkDesc & a_ChunkDesc, const cChunkDesc::Shape & a_Shape)
{
+ a_ChunkDesc.SetHeightFromShape(a_Shape);
a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0);
for (int z = 0; z < cChunkDef::Width; z++)
{
@@ -63,7 +64,7 @@ void cCompoGenSameBlock::InitializeCompoGen(cIniFile & a_IniFile)
////////////////////////////////////////////////////////////////////////////////
// cCompoGenDebugBiomes:
-void cCompoGenDebugBiomes::ComposeTerrain(cChunkDesc & a_ChunkDesc)
+void cCompoGenDebugBiomes::ComposeTerrain(cChunkDesc & a_ChunkDesc, const cChunkDesc::Shape & a_Shape)
{
static BLOCKTYPE Blocks[] =
{
@@ -92,6 +93,7 @@ void cCompoGenDebugBiomes::ComposeTerrain(cChunkDesc & a_ChunkDesc)
E_BLOCK_BEDROCK,
} ;
+ a_ChunkDesc.SetHeightFromShape(a_Shape);
a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0);
for (int z = 0; z < cChunkDef::Width; z++)
@@ -131,7 +133,7 @@ cCompoGenClassic::cCompoGenClassic(void) :
-void cCompoGenClassic::ComposeTerrain(cChunkDesc & a_ChunkDesc)
+void cCompoGenClassic::ComposeTerrain(cChunkDesc & a_ChunkDesc, const cChunkDesc::Shape & a_Shape)
{
/* The classic composition means:
- 1 layer of grass, 3 of dirt and the rest stone, if the height > sealevel + beachheight
@@ -142,6 +144,7 @@ void cCompoGenClassic::ComposeTerrain(cChunkDesc & a_ChunkDesc)
*/
a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0);
+ a_ChunkDesc.SetHeightFromShape(a_Shape);
// The patterns to use for different situations, must be same length!
const BLOCKTYPE PatternGround[] = {m_BlockTop, m_BlockMiddle, m_BlockMiddle, m_BlockMiddle} ;
@@ -194,7 +197,7 @@ void cCompoGenClassic::ComposeTerrain(cChunkDesc & a_ChunkDesc)
void cCompoGenClassic::InitializeCompoGen(cIniFile & a_IniFile)
{
- m_SeaLevel = a_IniFile.GetValueSetI("Generator", "ClassicSeaLevel", m_SeaLevel);
+ m_SeaLevel = a_IniFile.GetValueSetI("Generator", "SeaLevel", m_SeaLevel);
m_BeachHeight = a_IniFile.GetValueSetI("Generator", "ClassicBeachHeight", m_BeachHeight);
m_BeachDepth = a_IniFile.GetValueSetI("Generator", "ClassicBeachDepth", m_BeachDepth);
m_BlockTop = (BLOCKTYPE)(GetIniItemSet(a_IniFile, "Generator", "ClassicBlockTop", "grass").m_ItemType);
@@ -210,323 +213,6 @@ void cCompoGenClassic::InitializeCompoGen(cIniFile & a_IniFile)
////////////////////////////////////////////////////////////////////////////////
-// cCompoGenBiomal:
-
-void cCompoGenBiomal::ComposeTerrain(cChunkDesc & a_ChunkDesc)
-{
- a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0);
-
- int ChunkX = a_ChunkDesc.GetChunkX();
- int ChunkZ = a_ChunkDesc.GetChunkZ();
-
- /*
- _X 2013_04_22:
- There's no point in generating the whole cubic noise at once, because the noise values are used in
- only about 20 % of the cases, so the speed gained by precalculating is lost by precalculating too much data
- */
-
- for (int z = 0; z < cChunkDef::Width; z++)
- {
- for (int x = 0; x < cChunkDef::Width; x++)
- {
- int Height = a_ChunkDesc.GetHeight(x, z);
- if (Height > m_SeaLevel)
- {
- switch (a_ChunkDesc.GetBiome(x, z))
- {
- case biOcean:
- case biPlains:
- case biExtremeHills:
- case biForest:
- case biTaiga:
- case biSwampland:
- case biRiver:
- case biFrozenOcean:
- case biFrozenRiver:
- case biIcePlains:
- case biIceMountains:
- case biForestHills:
- case biTaigaHills:
- case biExtremeHillsEdge:
- case biJungle:
- case biJungleHills:
- case biJungleEdge:
- case biDeepOcean:
- case biStoneBeach:
- case biColdBeach:
- case biBirchForest:
- case biBirchForestHills:
- case biRoofedForest:
- case biColdTaiga:
- case biColdTaigaHills:
- case biExtremeHillsPlus:
- case biSavanna:
- case biSavannaPlateau:
- case biSunflowerPlains:
- case biExtremeHillsM:
- case biFlowerForest:
- case biTaigaM:
- case biSwamplandM:
- case biIcePlainsSpikes:
- case biJungleM:
- case biJungleEdgeM:
- case biBirchForestM:
- case biBirchForestHillsM:
- case biRoofedForestM:
- case biColdTaigaM:
- case biExtremeHillsPlusM:
- case biSavannaM:
- case biSavannaPlateauM:
- {
- FillColumnGrass(x, z, Height, a_ChunkDesc.GetBlockTypes());
- break;
- }
-
- case biMesa:
- case biMesaPlateauF:
- case biMesaPlateau:
- case biMesaBryce:
- case biMesaPlateauFM:
- case biMesaPlateauM:
- {
- FillColumnClay(x, z, Height, a_ChunkDesc.GetBlockTypes());
- break;
- }
-
- case biMegaTaiga:
- case biMegaTaigaHills:
- case biMegaSpruceTaiga:
- case biMegaSpruceTaigaHills:
- {
- FillColumnDirt(x, z, Height, a_ChunkDesc.GetBlockTypes());
- break;
- }
-
- case biDesertHills:
- case biDesert:
- case biDesertM:
- case biBeach:
- {
- FillColumnSand(x, z, Height, a_ChunkDesc.GetBlockTypes());
- break;
- }
- case biMushroomIsland:
- case biMushroomShore:
- {
- FillColumnMycelium(x, z, Height, a_ChunkDesc.GetBlockTypes());
- break;
- }
- default:
- {
- // TODO
- ASSERT(!"CompoGenBiomal: Biome not implemented yet!");
- break;
- }
- }
- }
- else
- {
- switch (a_ChunkDesc.GetBiome(x, z))
- {
- case biDesert:
- case biBeach:
- {
- // Fill with water, sand, sandstone and stone
- FillColumnWaterSand(x, z, Height, a_ChunkDesc.GetBlockTypes());
- break;
- }
- default:
- {
- // Fill with water, sand/dirt/clay mix and stone
- if (m_Noise.CubicNoise2D(0.3f * (cChunkDef::Width * ChunkX + x), 0.3f * (cChunkDef::Width * ChunkZ + z)) < 0)
- {
- FillColumnWaterSand(x, z, Height, a_ChunkDesc.GetBlockTypes());
- }
- else
- {
- FillColumnWaterDirt(x, z, Height, a_ChunkDesc.GetBlockTypes());
- }
- break;
- }
- } // switch (biome)
- a_ChunkDesc.SetHeight(x, z, m_SeaLevel + 1);
- } // else (under water)
- a_ChunkDesc.SetBlockType(x, 0, z, E_BLOCK_BEDROCK);
- } // for x
- } // for z
-}
-
-
-
-
-
-void cCompoGenBiomal::InitializeCompoGen(cIniFile & a_IniFile)
-{
- m_SeaLevel = a_IniFile.GetValueSetI("Generator", "BiomalSeaLevel", m_SeaLevel) - 1;
-}
-
-
-
-
-
-void cCompoGenBiomal::FillColumnGrass(int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes)
-{
- BLOCKTYPE Pattern[] =
- {
- E_BLOCK_GRASS,
- E_BLOCK_DIRT,
- E_BLOCK_DIRT,
- E_BLOCK_DIRT,
- } ;
- FillColumnPattern(a_RelX, a_RelZ, a_Height, a_BlockTypes, Pattern, ARRAYCOUNT(Pattern));
-
- for (int y = a_Height - ARRAYCOUNT(Pattern); y > 0; y--)
- {
- cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, E_BLOCK_STONE);
- }
-}
-
-
-
-
-
-void cCompoGenBiomal::FillColumnClay(int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes)
-{
- BLOCKTYPE Pattern[] =
- {
- E_BLOCK_HARDENED_CLAY,
- E_BLOCK_HARDENED_CLAY,
- E_BLOCK_HARDENED_CLAY,
- E_BLOCK_HARDENED_CLAY,
- } ;
- FillColumnPattern(a_RelX, a_RelZ, a_Height, a_BlockTypes, Pattern, ARRAYCOUNT(Pattern));
-
- for (int y = a_Height - ARRAYCOUNT(Pattern); y > 0; y--)
- {
- cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, E_BLOCK_STONE);
- }
-}
-
-
-
-
-
-void cCompoGenBiomal::FillColumnDirt(int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes)
-{
- for (int y = 0; y < 4; y++)
- {
- if (a_Height - y < 0)
- {
- return;
- }
- cChunkDef::SetBlock(a_BlockTypes, a_RelX, a_Height - y, a_RelZ, E_BLOCK_DIRT);
- }
- for (int y = a_Height - 4; y > 0; y--)
- {
- cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, E_BLOCK_STONE);
- }
-}
-
-
-
-
-
-void cCompoGenBiomal::FillColumnSand(int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes)
-{
- BLOCKTYPE Pattern[] =
- {
- E_BLOCK_SAND,
- E_BLOCK_SAND,
- E_BLOCK_SAND,
- E_BLOCK_SANDSTONE,
- } ;
- FillColumnPattern(a_RelX, a_RelZ, a_Height, a_BlockTypes, Pattern, ARRAYCOUNT(Pattern));
-
- for (int y = a_Height - ARRAYCOUNT(Pattern); y > 0; y--)
- {
- cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, E_BLOCK_STONE);
- }
-}
-
-
-
-
-
-
-void cCompoGenBiomal::FillColumnMycelium (int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes)
-{
- BLOCKTYPE Pattern[] =
- {
- E_BLOCK_MYCELIUM,
- E_BLOCK_DIRT,
- E_BLOCK_DIRT,
- E_BLOCK_DIRT,
- } ;
- FillColumnPattern(a_RelX, a_RelZ, a_Height, a_BlockTypes, Pattern, ARRAYCOUNT(Pattern));
-
- for (int y = a_Height - ARRAYCOUNT(Pattern); y > 0; y--)
- {
- cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, E_BLOCK_STONE);
- }
-}
-
-
-
-
-
-void cCompoGenBiomal::FillColumnWaterSand(int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes)
-{
- FillColumnSand(a_RelX, a_RelZ, a_Height, a_BlockTypes);
- for (int y = a_Height + 1; y <= m_SeaLevel + 1; y++)
- {
- cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, E_BLOCK_STATIONARY_WATER);
- }
-}
-
-
-
-
-
-void cCompoGenBiomal::FillColumnWaterDirt(int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes)
-{
- // Dirt
- BLOCKTYPE Pattern[] =
- {
- E_BLOCK_DIRT,
- E_BLOCK_DIRT,
- E_BLOCK_DIRT,
- E_BLOCK_DIRT,
- } ;
- FillColumnPattern(a_RelX, a_RelZ, a_Height, a_BlockTypes, Pattern, ARRAYCOUNT(Pattern));
-
- for (int y = a_Height - ARRAYCOUNT(Pattern); y > 0; y--)
- {
- cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, E_BLOCK_STONE);
- }
- for (int y = a_Height + 1; y <= m_SeaLevel + 1; y++)
- {
- cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, E_BLOCK_STATIONARY_WATER);
- }
-}
-
-
-
-
-
-
-void cCompoGenBiomal::FillColumnPattern(int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes, const BLOCKTYPE * a_Pattern, int a_PatternSize)
-{
- for (int y = a_Height, idx = 0; (y >= 0) && (idx < a_PatternSize); y--, idx++)
- {
- cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, a_Pattern[idx]);
- }
-}
-
-
-
-
-
-////////////////////////////////////////////////////////////////////////////////
// cCompoGenNether:
cCompoGenNether::cCompoGenNether(int a_Seed) :
@@ -540,7 +226,7 @@ cCompoGenNether::cCompoGenNether(int a_Seed) :
-void cCompoGenNether::ComposeTerrain(cChunkDesc & a_ChunkDesc)
+void cCompoGenNether::ComposeTerrain(cChunkDesc & a_ChunkDesc, const cChunkDesc::Shape & a_Shape)
{
HEIGHTTYPE MaxHeight = a_ChunkDesc.GetMaxHeight();
@@ -604,17 +290,7 @@ void cCompoGenNether::ComposeTerrain(cChunkDesc & a_ChunkDesc)
BLOCKTYPE Block = E_BLOCK_AIR;
if (Val < m_Threshold) // Don't calculate if the block should be Netherrack or Soulsand when it's already decided that it's air.
{
- NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(BaseX + x)) / 8;
- NOISE_DATATYPE NoiseY = ((NOISE_DATATYPE)(BaseZ + z)) / 8;
- NOISE_DATATYPE CompBlock = m_Noise1.CubicNoise3D(NoiseX, (float) (y + Segment) / 2, NoiseY);
- if (CompBlock < -0.5)
- {
- Block = E_BLOCK_SOULSAND;
- }
- else
- {
- Block = E_BLOCK_NETHERRACK;
- }
+ Block = E_BLOCK_NETHERRACK;
}
a_ChunkDesc.SetBlockType(x, y + Segment, z, Block);
}
@@ -638,7 +314,7 @@ void cCompoGenNether::ComposeTerrain(cChunkDesc & a_ChunkDesc)
CeilingDisguise = -CeilingDisguise;
}
- int CeilingDisguiseHeight = Height - 2 - (int)CeilingDisguise * 3;
+ int CeilingDisguiseHeight = Height - 2 - FloorC(CeilingDisguise * 3);
for (int y = Height - 1; y > CeilingDisguiseHeight; y--)
{
@@ -696,7 +372,7 @@ cCompoGenCache::~cCompoGenCache()
-void cCompoGenCache::ComposeTerrain(cChunkDesc & a_ChunkDesc)
+void cCompoGenCache::ComposeTerrain(cChunkDesc & a_ChunkDesc, const cChunkDesc::Shape & a_Shape)
{
#ifdef _DEBUG
if (((m_NumHits + m_NumMisses) % 1024) == 10)
@@ -731,6 +407,7 @@ void cCompoGenCache::ComposeTerrain(cChunkDesc & a_ChunkDesc)
// Use the cached data:
memcpy(a_ChunkDesc.GetBlockTypes(), m_CacheData[Idx].m_BlockTypes, sizeof(a_ChunkDesc.GetBlockTypes()));
memcpy(a_ChunkDesc.GetBlockMetasUncompressed(), m_CacheData[Idx].m_BlockMetas, sizeof(a_ChunkDesc.GetBlockMetasUncompressed()));
+ memcpy(a_ChunkDesc.GetHeightMap(), m_CacheData[Idx].m_HeightMap, sizeof(a_ChunkDesc.GetHeightMap()));
m_NumHits++;
m_TotalChain += i;
@@ -739,7 +416,7 @@ void cCompoGenCache::ComposeTerrain(cChunkDesc & a_ChunkDesc)
// Not in the cache:
m_NumMisses++;
- m_Underlying->ComposeTerrain(a_ChunkDesc);
+ m_Underlying->ComposeTerrain(a_ChunkDesc, a_Shape);
// Insert it as the first item in the MRU order:
int Idx = m_CacheOrder[m_CacheSize - 1];
@@ -750,6 +427,7 @@ void cCompoGenCache::ComposeTerrain(cChunkDesc & a_ChunkDesc)
m_CacheOrder[0] = Idx;
memcpy(m_CacheData[Idx].m_BlockTypes, a_ChunkDesc.GetBlockTypes(), sizeof(a_ChunkDesc.GetBlockTypes()));
memcpy(m_CacheData[Idx].m_BlockMetas, a_ChunkDesc.GetBlockMetasUncompressed(), sizeof(a_ChunkDesc.GetBlockMetasUncompressed()));
+ memcpy(m_CacheData[Idx].m_HeightMap, a_ChunkDesc.GetHeightMap(), sizeof(a_ChunkDesc.GetHeightMap()));
m_CacheData[Idx].m_ChunkX = ChunkX;
m_CacheData[Idx].m_ChunkZ = ChunkZ;
}
diff --git a/src/Generating/CompoGen.h b/src/Generating/CompoGen.h
index b145b6ba3..3847688cd 100644
--- a/src/Generating/CompoGen.h
+++ b/src/Generating/CompoGen.h
@@ -17,7 +17,7 @@
#pragma once
#include "ComposableGenerator.h"
-#include "../Noise.h"
+#include "../Noise/Noise.h"
@@ -38,7 +38,7 @@ protected:
bool m_IsBedrocked;
// cTerrainCompositionGen overrides:
- virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override;
+ virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc, const cChunkDesc::Shape & a_Shape) override;
virtual void InitializeCompoGen(cIniFile & a_IniFile) override;
} ;
@@ -55,7 +55,7 @@ public:
protected:
// cTerrainCompositionGen overrides:
- virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override;
+ virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc, const cChunkDesc::Shape & a_Shape) override;
} ;
@@ -81,7 +81,7 @@ protected:
BLOCKTYPE m_BlockSea;
// cTerrainCompositionGen overrides:
- virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override;
+ virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc, const cChunkDesc::Shape & a_Shape) override;
virtual void InitializeCompoGen(cIniFile & a_IniFile) override;
} ;
@@ -89,40 +89,6 @@ protected:
-class cCompoGenBiomal :
- public cTerrainCompositionGen
-{
-public:
- cCompoGenBiomal(int a_Seed) :
- m_Noise(a_Seed + 1000),
- m_SeaLevel(62)
- {
- }
-
-protected:
-
- cNoise m_Noise;
- int m_SeaLevel;
-
- // cTerrainCompositionGen overrides:
- virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override;
- virtual void InitializeCompoGen(cIniFile & a_IniFile) override;
-
- void FillColumnGrass (int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes);
- void FillColumnClay (int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes);
- void FillColumnDirt (int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes);
- void FillColumnSand (int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes);
- void FillColumnMycelium (int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes);
- void FillColumnWaterSand(int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes);
- void FillColumnWaterDirt(int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes);
-
- void FillColumnPattern (int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes, const BLOCKTYPE * a_Pattern, int a_PatternSize);
-} ;
-
-
-
-
-
class cCompoGenNether :
public cTerrainCompositionGen
{
@@ -136,7 +102,7 @@ protected:
int m_Threshold;
// cTerrainCompositionGen overrides:
- virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override;
+ virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc, const cChunkDesc::Shape & a_Shape) override;
virtual void InitializeCompoGen(cIniFile & a_IniFile) override;
} ;
@@ -153,7 +119,7 @@ public:
~cCompoGenCache();
// cTerrainCompositionGen override:
- virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override;
+ virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc, const cChunkDesc::Shape & a_Shape) override;
virtual void InitializeCompoGen(cIniFile & a_IniFile) override;
protected:
@@ -166,6 +132,7 @@ protected:
int m_ChunkZ;
cChunkDef::BlockTypes m_BlockTypes;
cChunkDesc::BlockNibbleBytes m_BlockMetas; // The metas are uncompressed, 1 meta per byte
+ cChunkDef::HeightMap m_HeightMap;
} ;
// To avoid moving large amounts of data for the MRU behavior, we MRU-ize indices to an array of the actual data
diff --git a/src/Generating/CompoGenBiomal.cpp b/src/Generating/CompoGenBiomal.cpp
new file mode 100644
index 000000000..030c2baa5
--- /dev/null
+++ b/src/Generating/CompoGenBiomal.cpp
@@ -0,0 +1,586 @@
+
+// CompoGenBiomal.cpp
+
+// Implements the cCompoGenBiomal class representing the biome-aware composition generator
+
+#include "Globals.h"
+#include "ComposableGenerator.h"
+#include "../IniFile.h"
+#include "../Noise/Noise.h"
+#include "../LinearUpscale.h"
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// cPattern:
+
+/** This class is used to store a column pattern initialized at runtime,
+so that the program doesn't need to explicitly set 256 values for each pattern
+Each pattern has 256 blocks so that there's no need to check pattern bounds when assigning the
+pattern - there will always be enough pattern left, even for the whole-chunk-height columns. */
+class cPattern
+{
+public:
+ struct BlockInfo
+ {
+ BLOCKTYPE m_BlockType;
+ NIBBLETYPE m_BlockMeta;
+ };
+
+ cPattern(BlockInfo * a_TopBlocks, size_t a_Count)
+ {
+ // Copy the pattern into the top:
+ for (size_t i = 0; i < a_Count; i++)
+ {
+ m_Pattern[i] = a_TopBlocks[i];
+ }
+
+ // Fill the rest with stone:
+ static BlockInfo Stone = {E_BLOCK_STONE, 0};
+ for (int i = static_cast<int>(a_Count); i < cChunkDef::Height; i++)
+ {
+ m_Pattern[i] = Stone;
+ }
+ }
+
+ const BlockInfo * Get(void) const { return m_Pattern; }
+
+protected:
+ BlockInfo m_Pattern[cChunkDef::Height];
+} ;
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// The arrays to use for the top block pattern definitions:
+
+static cPattern::BlockInfo tbGrass[] =
+{
+ {E_BLOCK_GRASS, 0},
+ {E_BLOCK_DIRT, E_META_DIRT_NORMAL},
+ {E_BLOCK_DIRT, E_META_DIRT_NORMAL},
+ {E_BLOCK_DIRT, E_META_DIRT_NORMAL},
+} ;
+
+static cPattern::BlockInfo tbSand[] =
+{
+ { E_BLOCK_SAND, 0},
+ { E_BLOCK_SAND, 0},
+ { E_BLOCK_SAND, 0},
+ { E_BLOCK_SANDSTONE, 0},
+} ;
+
+static cPattern::BlockInfo tbDirt[] =
+{
+ {E_BLOCK_DIRT, E_META_DIRT_NORMAL},
+ {E_BLOCK_DIRT, E_META_DIRT_NORMAL},
+ {E_BLOCK_DIRT, E_META_DIRT_NORMAL},
+ {E_BLOCK_DIRT, E_META_DIRT_NORMAL},
+} ;
+
+static cPattern::BlockInfo tbPodzol[] =
+{
+ {E_BLOCK_DIRT, E_META_DIRT_PODZOL},
+ {E_BLOCK_DIRT, E_META_DIRT_NORMAL},
+ {E_BLOCK_DIRT, E_META_DIRT_NORMAL},
+ {E_BLOCK_DIRT, E_META_DIRT_NORMAL},
+} ;
+
+static cPattern::BlockInfo tbGrassLess[] =
+{
+ {E_BLOCK_DIRT, E_META_DIRT_GRASSLESS},
+ {E_BLOCK_DIRT, E_META_DIRT_NORMAL},
+ {E_BLOCK_DIRT, E_META_DIRT_NORMAL},
+ {E_BLOCK_DIRT, E_META_DIRT_NORMAL},
+} ;
+
+static cPattern::BlockInfo tbMycelium[] =
+{
+ {E_BLOCK_MYCELIUM, 0},
+ {E_BLOCK_DIRT, 0},
+ {E_BLOCK_DIRT, 0},
+ {E_BLOCK_DIRT, 0},
+} ;
+
+static cPattern::BlockInfo tbGravel[] =
+{
+ {E_BLOCK_GRAVEL, 0},
+ {E_BLOCK_GRAVEL, 0},
+ {E_BLOCK_GRAVEL, 0},
+ {E_BLOCK_STONE, 0},
+} ;
+
+static cPattern::BlockInfo tbStone[] =
+{
+ {E_BLOCK_STONE, 0},
+ {E_BLOCK_STONE, 0},
+ {E_BLOCK_STONE, 0},
+ {E_BLOCK_STONE, 0},
+} ;
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Ocean floor pattern top-block definitions:
+
+static cPattern::BlockInfo tbOFSand[] =
+{
+ {E_BLOCK_SAND, 0},
+ {E_BLOCK_SAND, 0},
+ {E_BLOCK_SAND, 0},
+ {E_BLOCK_SANDSTONE, 0}
+} ;
+
+static cPattern::BlockInfo tbOFClay[] =
+{
+ { E_BLOCK_CLAY, 0},
+ { E_BLOCK_CLAY, 0},
+ { E_BLOCK_SAND, 0},
+ { E_BLOCK_SAND, 0},
+} ;
+
+static cPattern::BlockInfo tbOFOrangeClay[] =
+{
+ { E_BLOCK_STAINED_CLAY, E_META_STAINED_GLASS_ORANGE},
+ { E_BLOCK_STAINED_CLAY, E_META_STAINED_GLASS_ORANGE},
+ { E_BLOCK_STAINED_CLAY, E_META_STAINED_GLASS_ORANGE},
+} ;
+
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Individual patterns to use:
+
+static cPattern patGrass (tbGrass, ARRAYCOUNT(tbGrass));
+static cPattern patSand (tbSand, ARRAYCOUNT(tbSand));
+static cPattern patDirt (tbDirt, ARRAYCOUNT(tbDirt));
+static cPattern patPodzol (tbPodzol, ARRAYCOUNT(tbPodzol));
+static cPattern patGrassLess(tbGrassLess, ARRAYCOUNT(tbGrassLess));
+static cPattern patMycelium (tbMycelium, ARRAYCOUNT(tbMycelium));
+static cPattern patGravel (tbGravel, ARRAYCOUNT(tbGravel));
+static cPattern patStone (tbStone, ARRAYCOUNT(tbStone));
+
+static cPattern patOFSand (tbOFSand, ARRAYCOUNT(tbOFSand));
+static cPattern patOFClay (tbOFClay, ARRAYCOUNT(tbOFClay));
+static cPattern patOFOrangeClay(tbOFOrangeClay, ARRAYCOUNT(tbOFOrangeClay));
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// cCompoGenBiomal:
+
+class cCompoGenBiomal :
+ public cTerrainCompositionGen
+{
+public:
+ cCompoGenBiomal(int a_Seed) :
+ m_SeaLevel(62),
+ m_OceanFloorSelect(a_Seed + 1),
+ m_MesaFloor(a_Seed + 2)
+ {
+ initMesaPattern(a_Seed);
+ }
+
+protected:
+ /** The block height at which water is generated instead of air. */
+ int m_SeaLevel;
+
+ /** The pattern used for mesa biomes. Initialized by seed on generator creation. */
+ cPattern::BlockInfo m_MesaPattern[2 * cChunkDef::Height];
+
+ /** Noise used for selecting between dirt and sand on the ocean floor. */
+ cNoise m_OceanFloorSelect;
+
+ /** Noise used for the floor of the clay blocks in mesa biomes. */
+ cNoise m_MesaFloor;
+
+
+ // cTerrainCompositionGen overrides:
+ virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc, const cChunkDesc::Shape & a_Shape) override
+ {
+ a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0);
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ ComposeColumn(a_ChunkDesc, x, z, &(a_Shape[x * 256 + z * 16 * 256]));
+ } // for x
+ } // for z
+ }
+
+
+
+ virtual void InitializeCompoGen(cIniFile & a_IniFile) override
+ {
+ m_SeaLevel = a_IniFile.GetValueSetI("Generator", "SeaLevel", m_SeaLevel);
+ }
+
+
+
+ /** Initializes the m_MesaPattern with a pattern based on the generator's seed. */
+ void initMesaPattern(int a_Seed)
+ {
+ // In a loop, choose whether to use one, two or three layers of stained clay, then choose a color and width for each layer
+ // Separate each group with another layer of hardened clay
+ cNoise patternNoise((unsigned)a_Seed);
+ static NIBBLETYPE allowedColors[] =
+ {
+ E_META_STAINED_CLAY_YELLOW,
+ E_META_STAINED_CLAY_YELLOW,
+ E_META_STAINED_CLAY_RED,
+ E_META_STAINED_CLAY_RED,
+ E_META_STAINED_CLAY_WHITE,
+ E_META_STAINED_CLAY_BROWN,
+ E_META_STAINED_CLAY_BROWN,
+ E_META_STAINED_CLAY_BROWN,
+ E_META_STAINED_CLAY_ORANGE,
+ E_META_STAINED_CLAY_ORANGE,
+ E_META_STAINED_CLAY_ORANGE,
+ E_META_STAINED_CLAY_ORANGE,
+ E_META_STAINED_CLAY_ORANGE,
+ E_META_STAINED_CLAY_ORANGE,
+ E_META_STAINED_CLAY_LIGHTGRAY,
+ } ;
+ static int layerSizes[] = // Adjust the chance so that thinner layers occur more commonly
+ {
+ 1, 1, 1, 1, 1, 1,
+ 2, 2, 2, 2,
+ 3, 3,
+ } ;
+ int idx = ARRAYCOUNT(m_MesaPattern) - 1;
+ while (idx >= 0)
+ {
+ // A layer group of 1 - 2 color stained clay:
+ int rnd = patternNoise.IntNoise1DInt(idx) / 7;
+ int numLayers = (rnd % 2) + 1;
+ rnd /= 2;
+ for (int lay = 0; lay < numLayers; lay++)
+ {
+ int numBlocks = layerSizes[(rnd % ARRAYCOUNT(layerSizes))];
+ NIBBLETYPE Color = allowedColors[(rnd / 4) % ARRAYCOUNT(allowedColors)];
+ if (
+ ((numBlocks == 3) && (numLayers == 2)) || // In two-layer mode disallow the 3-high layers:
+ (Color == E_META_STAINED_CLAY_WHITE)) // White stained clay can ever be only 1 block high
+ {
+ numBlocks = 1;
+ }
+ numBlocks = std::min(idx + 1, numBlocks); // Limit by idx so that we don't have to check inside the loop
+ rnd /= 32;
+ for (int block = 0; block < numBlocks; block++, idx--)
+ {
+ m_MesaPattern[idx].m_BlockMeta = Color;
+ m_MesaPattern[idx].m_BlockType = E_BLOCK_STAINED_CLAY;
+ } // for block
+ } // for lay
+
+ // A layer of hardened clay in between the layer group:
+ int numBlocks = (rnd % 4) + 1; // All heights the same probability
+ if ((numLayers == 2) && (numBlocks < 4))
+ {
+ // For two layers of stained clay, add an extra block of hardened clay:
+ numBlocks++;
+ }
+ numBlocks = std::min(idx + 1, numBlocks); // Limit by idx so that we don't have to check inside the loop
+ for (int block = 0; block < numBlocks; block++, idx--)
+ {
+ m_MesaPattern[idx].m_BlockMeta = 0;
+ m_MesaPattern[idx].m_BlockType = E_BLOCK_HARDENED_CLAY;
+ } // for block
+ } // while (idx >= 0)
+ }
+
+
+
+ /** Composes a single column in a_ChunkDesc. Chooses what to do based on the biome in that column. */
+ void ComposeColumn(cChunkDesc & a_ChunkDesc, int a_RelX, int a_RelZ, const Byte * a_ShapeColumn)
+ {
+ // Frequencies for the podzol floor selecting noise:
+ const NOISE_DATATYPE FrequencyX = 8;
+ const NOISE_DATATYPE FrequencyZ = 8;
+
+ EMCSBiome Biome = a_ChunkDesc.GetBiome(a_RelX, a_RelZ);
+ switch (Biome)
+ {
+ case biOcean:
+ case biPlains:
+ case biForest:
+ case biTaiga:
+ case biSwampland:
+ case biRiver:
+ case biFrozenOcean:
+ case biFrozenRiver:
+ case biIcePlains:
+ case biIceMountains:
+ case biForestHills:
+ case biTaigaHills:
+ case biExtremeHillsEdge:
+ case biExtremeHillsPlus:
+ case biExtremeHills:
+ case biJungle:
+ case biJungleHills:
+ case biJungleEdge:
+ case biDeepOcean:
+ case biStoneBeach:
+ case biColdBeach:
+ case biBirchForest:
+ case biBirchForestHills:
+ case biRoofedForest:
+ case biColdTaiga:
+ case biColdTaigaHills:
+ case biSavanna:
+ case biSavannaPlateau:
+ case biSunflowerPlains:
+ case biFlowerForest:
+ case biTaigaM:
+ case biSwamplandM:
+ case biIcePlainsSpikes:
+ case biJungleM:
+ case biJungleEdgeM:
+ case biBirchForestM:
+ case biBirchForestHillsM:
+ case biRoofedForestM:
+ case biColdTaigaM:
+ case biSavannaM:
+ case biSavannaPlateauM:
+ {
+ FillColumnPattern(a_ChunkDesc, a_RelX, a_RelZ, patGrass.Get(), a_ShapeColumn);
+ return;
+ }
+
+ case biMegaTaiga:
+ case biMegaTaigaHills:
+ case biMegaSpruceTaiga:
+ case biMegaSpruceTaigaHills:
+ {
+ // Select the pattern to use - podzol, grass or grassless dirt:
+ NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(a_ChunkDesc.GetChunkX() * cChunkDef::Width + a_RelX)) / FrequencyX;
+ NOISE_DATATYPE NoiseY = ((NOISE_DATATYPE)(a_ChunkDesc.GetChunkZ() * cChunkDef::Width + a_RelZ)) / FrequencyZ;
+ NOISE_DATATYPE Val = m_OceanFloorSelect.CubicNoise2D(NoiseX, NoiseY);
+ const cPattern::BlockInfo * Pattern = (Val < -0.9) ? patGrassLess.Get() : ((Val > 0) ? patPodzol.Get() : patGrass.Get());
+ FillColumnPattern(a_ChunkDesc, a_RelX, a_RelZ, Pattern, a_ShapeColumn);
+ return;
+ }
+
+ case biDesertHills:
+ case biDesert:
+ case biDesertM:
+ case biBeach:
+ {
+ FillColumnPattern(a_ChunkDesc, a_RelX, a_RelZ, patSand.Get(), a_ShapeColumn);
+ return;
+ }
+
+ case biMushroomIsland:
+ case biMushroomShore:
+ {
+ FillColumnPattern(a_ChunkDesc, a_RelX, a_RelZ, patMycelium.Get(), a_ShapeColumn);
+ return;
+ }
+
+ case biMesa:
+ case biMesaPlateauF:
+ case biMesaPlateau:
+ case biMesaBryce:
+ case biMesaPlateauFM:
+ case biMesaPlateauM:
+ {
+ // Mesa biomes need special handling, because they don't follow the usual "4 blocks from top pattern",
+ // instead, they provide a "from bottom" pattern with varying base height,
+ // usually 4 blocks below the ocean level
+ FillColumnMesa(a_ChunkDesc, a_RelX, a_RelZ, a_ShapeColumn);
+ return;
+ }
+
+ case biExtremeHillsPlusM:
+ case biExtremeHillsM:
+ {
+ // Select the pattern to use - gravel, stone or grass:
+ NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(a_ChunkDesc.GetChunkX() * cChunkDef::Width + a_RelX)) / FrequencyX;
+ NOISE_DATATYPE NoiseY = ((NOISE_DATATYPE)(a_ChunkDesc.GetChunkZ() * cChunkDef::Width + a_RelZ)) / FrequencyZ;
+ NOISE_DATATYPE Val = m_OceanFloorSelect.CubicNoise2D(NoiseX, NoiseY);
+ const cPattern::BlockInfo * Pattern = (Val < 0.0) ? patStone.Get() : patGrass.Get();
+ FillColumnPattern(a_ChunkDesc, a_RelX, a_RelZ, Pattern, a_ShapeColumn);
+ return;
+ }
+ default:
+ {
+ ASSERT(!"Unhandled biome");
+ return;
+ }
+ } // switch (Biome)
+ }
+
+
+
+ /** Fills the specified column with the specified pattern; restarts the pattern when air is reached,
+ switches to ocean floor pattern if ocean is reached. Always adds bedrock at the very bottom. */
+ void FillColumnPattern(cChunkDesc & a_ChunkDesc, int a_RelX, int a_RelZ, const cPattern::BlockInfo * a_Pattern, const Byte * a_ShapeColumn)
+ {
+ bool HasHadWater = false;
+ int PatternIdx = 0;
+ int top = std::max(m_SeaLevel, a_ChunkDesc.GetHeight(a_RelX, a_RelZ));
+ for (int y = top; y > 0; y--)
+ {
+ if (a_ShapeColumn[y] > 0)
+ {
+ // "ground" part, use the pattern:
+ a_ChunkDesc.SetBlockTypeMeta(a_RelX, y, a_RelZ, a_Pattern[PatternIdx].m_BlockType, a_Pattern[PatternIdx].m_BlockMeta);
+ PatternIdx++;
+ continue;
+ }
+
+ // "air" or "water" part:
+ // Reset the pattern index to zero, so that the pattern is repeated from the top again:
+ PatternIdx = 0;
+
+ if (y >= m_SeaLevel)
+ {
+ // "air" part, do nothing
+ continue;
+ }
+
+ a_ChunkDesc.SetBlockType(a_RelX, y, a_RelZ, E_BLOCK_STATIONARY_WATER);
+ if (HasHadWater)
+ {
+ continue;
+ }
+
+ // Select the ocean-floor pattern to use:
+ if (a_ChunkDesc.GetBiome(a_RelX, a_RelZ) == biDeepOcean)
+ {
+ a_Pattern = patGravel.Get();
+ }
+ else
+ {
+ a_Pattern = ChooseOceanFloorPattern(a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ(), a_RelX, a_RelZ);
+ }
+ HasHadWater = true;
+ } // for y
+ a_ChunkDesc.SetBlockType(a_RelX, 0, a_RelZ, E_BLOCK_BEDROCK);
+ }
+
+
+
+ /** Fills the specified column with mesa pattern, based on the column height */
+ void FillColumnMesa(cChunkDesc & a_ChunkDesc, int a_RelX, int a_RelZ, const Byte * a_ShapeColumn)
+ {
+ // Frequencies for the clay floor noise:
+ const NOISE_DATATYPE FrequencyX = 50;
+ const NOISE_DATATYPE FrequencyZ = 50;
+
+ int Top = a_ChunkDesc.GetHeight(a_RelX, a_RelZ);
+ if (Top < m_SeaLevel)
+ {
+ // The terrain is below sealevel, handle as regular ocean with red sand floor:
+ FillColumnPattern(a_ChunkDesc, a_RelX, a_RelZ, patOFOrangeClay.Get(), a_ShapeColumn);
+ return;
+ }
+
+ NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(a_ChunkDesc.GetChunkX() * cChunkDef::Width + a_RelX)) / FrequencyX;
+ NOISE_DATATYPE NoiseY = ((NOISE_DATATYPE)(a_ChunkDesc.GetChunkZ() * cChunkDef::Width + a_RelZ)) / FrequencyZ;
+ int ClayFloor = m_SeaLevel - 6 + (int)(4.f * m_MesaFloor.CubicNoise2D(NoiseX, NoiseY));
+ if (ClayFloor >= Top)
+ {
+ ClayFloor = Top - 1;
+ }
+
+ if (Top - m_SeaLevel < 5)
+ {
+ // Simple case: top is red sand, then hardened clay down to ClayFloor, then stone:
+ a_ChunkDesc.SetBlockTypeMeta(a_RelX, Top, a_RelZ, E_BLOCK_SAND, E_META_SAND_RED);
+ for (int y = Top - 1; y >= ClayFloor; y--)
+ {
+ a_ChunkDesc.SetBlockType(a_RelX, y, a_RelZ, E_BLOCK_HARDENED_CLAY);
+ }
+ for (int y = ClayFloor - 1; y > 0; y--)
+ {
+ a_ChunkDesc.SetBlockType(a_RelX, y, a_RelZ, E_BLOCK_STONE);
+ }
+ a_ChunkDesc.SetBlockType(a_RelX, 0, a_RelZ, E_BLOCK_BEDROCK);
+ return;
+ }
+
+ // Difficult case: use the mesa pattern and watch for overhangs:
+ int PatternIdx = cChunkDef::Height - (Top - ClayFloor); // We want the block at index ClayFloor to be pattern's 256th block (first stone)
+ const cPattern::BlockInfo * Pattern = m_MesaPattern;
+ bool HasHadWater = false;
+ for (int y = Top; y > 0; y--)
+ {
+ if (a_ShapeColumn[y] > 0)
+ {
+ // "ground" part, use the pattern:
+ a_ChunkDesc.SetBlockTypeMeta(a_RelX, y, a_RelZ, Pattern[PatternIdx].m_BlockType, Pattern[PatternIdx].m_BlockMeta);
+ PatternIdx++;
+ continue;
+ }
+
+ if (y >= m_SeaLevel)
+ {
+ // "air" part, do nothing
+ continue;
+ }
+
+ // "water" part, fill with water and choose new pattern for ocean floor, if not chosen already:
+ PatternIdx = 0;
+ a_ChunkDesc.SetBlockType(a_RelX, y, a_RelZ, E_BLOCK_STATIONARY_WATER);
+ if (HasHadWater)
+ {
+ continue;
+ }
+
+ // Select the ocean-floor pattern to use:
+ Pattern = ChooseOceanFloorPattern(a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ(), a_RelX, a_RelZ);
+ HasHadWater = true;
+ } // for y
+ a_ChunkDesc.SetBlockType(a_RelX, 0, a_RelZ, E_BLOCK_BEDROCK);
+ }
+
+
+
+ /** Returns the pattern to use for an ocean floor in the specified column.
+ The returned pattern is guaranteed to be 256 blocks long. */
+ const cPattern::BlockInfo * ChooseOceanFloorPattern(int a_ChunkX, int a_ChunkZ, int a_RelX, int a_RelZ)
+ {
+ // Frequencies for the ocean floor selecting noise:
+ const NOISE_DATATYPE FrequencyX = 3;
+ const NOISE_DATATYPE FrequencyZ = 3;
+
+ // Select the ocean-floor pattern to use:
+ NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(a_ChunkX * cChunkDef::Width + a_RelX)) / FrequencyX;
+ NOISE_DATATYPE NoiseY = ((NOISE_DATATYPE)(a_ChunkZ * cChunkDef::Width + a_RelZ)) / FrequencyZ;
+ NOISE_DATATYPE Val = m_OceanFloorSelect.CubicNoise2D(NoiseX, NoiseY);
+ if (Val < -0.95)
+ {
+ return patOFClay.Get();
+ }
+ else if (Val < 0)
+ {
+ return patOFSand.Get();
+ }
+ else
+ {
+ return patDirt.Get();
+ }
+ }
+} ;
+
+
+
+
+
+cTerrainCompositionGenPtr CreateCompoGenBiomal(int a_Seed)
+{
+ return std::make_shared<cCompoGenBiomal>(a_Seed);
+}
+
+
+
diff --git a/src/Generating/CompoGenBiomal.h b/src/Generating/CompoGenBiomal.h
new file mode 100644
index 000000000..a3a65d3dc
--- /dev/null
+++ b/src/Generating/CompoGenBiomal.h
@@ -0,0 +1,21 @@
+
+// CompoGenBiomal.h
+
+
+
+
+
+#pragma once
+
+#include "ComposableGenerator.h"
+
+
+
+
+
+/** Returns a new instance of the Biomal composition generator. */
+cTerrainCompositionGenPtr CreateCompoGenBiomal(int a_Seed);
+
+
+
+
diff --git a/src/Generating/ComposableGenerator.cpp b/src/Generating/ComposableGenerator.cpp
index 169821050..bda45ad92 100644
--- a/src/Generating/ComposableGenerator.cpp
+++ b/src/Generating/ComposableGenerator.cpp
@@ -17,6 +17,10 @@
#include "StructGen.h"
#include "FinishGen.h"
+#include "CompoGenBiomal.h"
+
+#include "CompositedHeiGen.h"
+
#include "Caves.h"
#include "DistortedHeightmap.h"
#include "DungeonRoomsFinisher.h"
@@ -39,7 +43,7 @@
////////////////////////////////////////////////////////////////////////////////
// cTerrainCompositionGen:
-cTerrainCompositionGenPtr cTerrainCompositionGen::CreateCompositionGen(cIniFile & a_IniFile, cBiomeGenPtr a_BiomeGen, cTerrainHeightGen & a_HeightGen, int a_Seed)
+cTerrainCompositionGenPtr cTerrainCompositionGen::CreateCompositionGen(cIniFile & a_IniFile, cBiomeGenPtr a_BiomeGen, cTerrainShapeGenPtr a_ShapeGen, int a_Seed)
{
AString CompoGenName = a_IniFile.GetValueSet("Generator", "CompositionGen", "");
if (CompoGenName.empty())
@@ -48,59 +52,52 @@ cTerrainCompositionGenPtr cTerrainCompositionGen::CreateCompositionGen(cIniFile
CompoGenName = "Biomal";
}
- cTerrainCompositionGen * res = nullptr;
- if (NoCaseCompare(CompoGenName, "sameblock") == 0)
+ // Compositor list is alpha-sorted
+ cTerrainCompositionGenPtr res;
+ if (NoCaseCompare(CompoGenName, "Biomal") == 0)
+ {
+ res = CreateCompoGenBiomal(a_Seed);
+ }
+ else if (NoCaseCompare(CompoGenName, "BiomalNoise3D") == 0)
{
- res = new cCompoGenSameBlock;
+ // The composition that used to be provided with BiomalNoise3D is now provided by the Biomal compositor:
+ res = CreateCompoGenBiomal(a_Seed);
}
- else if (NoCaseCompare(CompoGenName, "debugbiomes") == 0)
+ else if (NoCaseCompare(CompoGenName, "Classic") == 0)
{
- res = new cCompoGenDebugBiomes;
+ res = std::make_shared<cCompoGenClassic>();
}
- else if (NoCaseCompare(CompoGenName, "classic") == 0)
+ else if (NoCaseCompare(CompoGenName, "DebugBiomes") == 0)
{
- res = new cCompoGenClassic;
+ res = std::make_shared<cCompoGenDebugBiomes>();
}
else if (NoCaseCompare(CompoGenName, "DistortedHeightmap") == 0)
{
- res = new cDistortedHeightmap(a_Seed, a_BiomeGen);
+ // The composition that used to be provided with DistortedHeightmap is now provided by the Biomal compositor:
+ res = CreateCompoGenBiomal(a_Seed);
}
- else if (NoCaseCompare(CompoGenName, "end") == 0)
+ else if (NoCaseCompare(CompoGenName, "End") == 0)
{
- res = new cEndGen(a_Seed);
+ res = std::make_shared<cEndGen>(a_Seed);
}
- else if (NoCaseCompare(CompoGenName, "nether") == 0)
+ else if (NoCaseCompare(CompoGenName, "Nether") == 0)
{
- res = new cCompoGenNether(a_Seed);
+ res = std::make_shared<cCompoGenNether>(a_Seed);
}
else if (NoCaseCompare(CompoGenName, "Noise3D") == 0)
{
- res = new cNoise3DComposable(a_Seed);
+ // The composition that used to be provided with Noise3D is now provided by the Biomal compositor:
+ res = CreateCompoGenBiomal(a_Seed);
}
- else if (NoCaseCompare(CompoGenName, "biomal") == 0)
+ else if (NoCaseCompare(CompoGenName, "SameBlock") == 0)
{
- res = new cCompoGenBiomal(a_Seed);
-
- /*
- // Performance-testing:
- LOGINFO("Measuring performance of cCompoGenBiomal...");
- clock_t BeginTick = clock();
- for (int x = 0; x < 500; x++)
- {
- cChunkDesc Desc(200 + x * 8, 200 + x * 8);
- a_BiomeGen->GenBiomes(Desc.GetChunkX(), Desc.GetChunkZ(), Desc.GetBiomeMap());
- a_HeightGen->GenHeightMap(Desc.GetChunkX(), Desc.GetChunkZ(), Desc.GetHeightMap());
- res->ComposeTerrain(Desc);
- }
- clock_t Duration = clock() - BeginTick;
- LOGINFO("CompositionGen for 500 chunks took %d ticks (%.02f sec)", Duration, (double)Duration / CLOCKS_PER_SEC);
- //*/
+ res = std::make_shared<cCompoGenSameBlock>();
}
else
{
LOGWARN("Unknown CompositionGen \"%s\", using \"Biomal\" instead.", CompoGenName.c_str());
a_IniFile.SetValue("Generator", "CompositionGen", "Biomal");
- return CreateCompositionGen(a_IniFile, a_BiomeGen, a_HeightGen, a_Seed);
+ return CreateCompositionGen(a_IniFile, a_BiomeGen, a_ShapeGen, a_Seed);
}
ASSERT(res != nullptr);
@@ -120,7 +117,7 @@ cTerrainCompositionGenPtr cTerrainCompositionGen::CreateCompositionGen(cIniFile
cComposableGenerator::cComposableGenerator(cChunkGenerator & a_ChunkGenerator) :
super(a_ChunkGenerator),
m_BiomeGen(),
- m_HeightGen(),
+ m_ShapeGen(),
m_CompositionGen()
{
}
@@ -134,7 +131,7 @@ void cComposableGenerator::Initialize(cIniFile & a_IniFile)
super::Initialize(a_IniFile);
InitBiomeGen(a_IniFile);
- InitHeightGen(a_IniFile);
+ InitShapeGen(a_IniFile);
InitCompositionGen(a_IniFile);
InitFinishGens(a_IniFile);
}
@@ -162,16 +159,22 @@ void cComposableGenerator::DoGenerate(int a_ChunkX, int a_ChunkZ, cChunkDesc & a
m_BiomeGen->GenBiomes(a_ChunkX, a_ChunkZ, a_ChunkDesc.GetBiomeMap());
}
+ cChunkDesc::Shape shape;
if (a_ChunkDesc.IsUsingDefaultHeight())
{
- m_HeightGen->GenHeightMap(a_ChunkX, a_ChunkZ, a_ChunkDesc.GetHeightMap());
+ m_ShapeGen->GenShape(a_ChunkX, a_ChunkZ, shape);
+ a_ChunkDesc.SetHeightFromShape(shape);
+ }
+ else
+ {
+ // Convert the heightmap in a_ChunkDesc into shape:
+ a_ChunkDesc.GetShapeFromHeight(shape);
}
bool ShouldUpdateHeightmap = false;
if (a_ChunkDesc.IsUsingDefaultComposition())
{
- m_CompositionGen->ComposeTerrain(a_ChunkDesc);
- ShouldUpdateHeightmap = true;
+ m_CompositionGen->ComposeTerrain(a_ChunkDesc, shape);
}
if (a_ChunkDesc.IsUsingDefaultFinish())
@@ -230,13 +233,15 @@ void cComposableGenerator::InitBiomeGen(cIniFile & a_IniFile)
-void cComposableGenerator::InitHeightGen(cIniFile & a_IniFile)
+void cComposableGenerator::InitShapeGen(cIniFile & a_IniFile)
{
bool CacheOffByDefault = false;
- m_HeightGen = cTerrainHeightGen::CreateHeightGen(a_IniFile, m_BiomeGen, m_ChunkGenerator.GetSeed(), CacheOffByDefault);
+ m_ShapeGen = cTerrainShapeGen::CreateShapeGen(a_IniFile, m_BiomeGen, m_ChunkGenerator.GetSeed(), CacheOffByDefault);
+ /*
+ // TODO
// Add a cache, if requested:
- int CacheSize = a_IniFile.GetValueSetI("Generator", "HeightGenCacheSize", CacheOffByDefault ? 0 : 64);
+ int CacheSize = a_IniFile.GetValueSetI("Generator", "ShapeGenCacheSize", CacheOffByDefault ? 0 : 64);
if (CacheSize > 0)
{
if (CacheSize < 4)
@@ -249,6 +254,7 @@ void cComposableGenerator::InitHeightGen(cIniFile & a_IniFile)
LOGD("Using a cache for Heightgen of size %d.", CacheSize);
m_HeightGen = cTerrainHeightGenPtr(new cHeiGenCache(m_HeightGen, CacheSize));
}
+ */
}
@@ -257,13 +263,19 @@ void cComposableGenerator::InitHeightGen(cIniFile & a_IniFile)
void cComposableGenerator::InitCompositionGen(cIniFile & a_IniFile)
{
- m_CompositionGen = cTerrainCompositionGen::CreateCompositionGen(a_IniFile, m_BiomeGen, *m_HeightGen, m_ChunkGenerator.GetSeed());
+ m_CompositionGen = cTerrainCompositionGen::CreateCompositionGen(a_IniFile, m_BiomeGen, m_ShapeGen, m_ChunkGenerator.GetSeed());
+ // Add a cache over the composition generator:
+ // Even a cache of size 1 is useful due to the CompositedHeiGen cache after us doing re-composition on its misses
int CompoGenCacheSize = a_IniFile.GetValueSetI("Generator", "CompositionGenCacheSize", 64);
- if (CompoGenCacheSize > 1)
+ if (CompoGenCacheSize > 0)
{
- m_CompositionGen = cTerrainCompositionGenPtr(new cCompoGenCache(m_CompositionGen, 32));
+ m_CompositionGen = std::make_shared<cCompoGenCache>(m_CompositionGen, CompoGenCacheSize);
}
+
+ // Create a cache of the composited heightmaps, so that finishers may use it:
+ m_CompositedHeightCache = std::make_shared<cHeiGenMultiCache>(std::make_shared<cCompositedHeiGen>(m_ShapeGen, m_CompositionGen), 16, 24);
+ // 24 subcaches of depth 16 each = 96 KiB of RAM. Acceptable, for the amount of work this saves.
}
@@ -282,7 +294,11 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
for (AStringVector::const_iterator itr = Str.begin(); itr != Str.end(); ++itr)
{
// Finishers, alpha-sorted:
- if (NoCaseCompare(*itr, "BottomLava") == 0)
+ if (NoCaseCompare(*itr, "Animals") == 0)
+ {
+ m_FinishGens.push_back(cFinishGenPtr(new cFinishGenPassiveMobs(Seed, a_IniFile, Dimension)));
+ }
+ else if (NoCaseCompare(*itr, "BottomLava") == 0)
{
int DefaultBottomLavaLevel = (Dimension == dimNether) ? 30 : 10;
int BottomLavaLevel = a_IniFile.GetValueSetI("Generator", "BottomLavaLevel", DefaultBottomLavaLevel);
@@ -329,7 +345,7 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
int MaxSize = a_IniFile.GetValueSetI("Generator", "DungeonRoomsMaxSize", 7);
int MinSize = a_IniFile.GetValueSetI("Generator", "DungeonRoomsMinSize", 5);
AString HeightDistrib = a_IniFile.GetValueSet ("Generator", "DungeonRoomsHeightDistrib", "0, 0; 10, 10; 11, 500; 40, 500; 60, 40; 90, 1");
- m_FinishGens.push_back(cFinishGenPtr(new cDungeonRoomsFinisher(m_HeightGen, Seed, GridSize, MaxSize, MinSize, HeightDistrib)));
+ m_FinishGens.push_back(cFinishGenPtr(new cDungeonRoomsFinisher(m_ShapeGen, Seed, GridSize, MaxSize, MinSize, HeightDistrib)));
}
else if (NoCaseCompare(*itr, "Ice") == 0)
{
@@ -338,7 +354,7 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
else if (NoCaseCompare(*itr, "LavaLakes") == 0)
{
int Probability = a_IniFile.GetValueSetI("Generator", "LavaLakesProbability", 10);
- m_FinishGens.push_back(cFinishGenPtr(new cStructGenLakes(Seed * 5 + 16873, E_BLOCK_STATIONARY_LAVA, m_HeightGen, Probability)));
+ m_FinishGens.push_back(cFinishGenPtr(new cStructGenLakes(Seed * 5 + 16873, E_BLOCK_STATIONARY_LAVA, m_ShapeGen, Probability)));
}
else if (NoCaseCompare(*itr, "LavaSprings") == 0)
{
@@ -558,6 +574,10 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
GridSize, MaxOffset
)));
}
+ else if (NoCaseCompare(*itr, "SoulsandRims") == 0)
+ {
+ m_FinishGens.push_back(cFinishGenPtr(new cFinishGenSoulsandRims(Seed)));
+ }
else if (NoCaseCompare(*itr, "Snow") == 0)
{
m_FinishGens.push_back(cFinishGenPtr(new cFinishGenSnow));
@@ -576,7 +596,7 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
}
else if (NoCaseCompare(*itr, "Trees") == 0)
{
- m_FinishGens.push_back(cFinishGenPtr(new cStructGenTrees(Seed, m_BiomeGen, m_HeightGen, m_CompositionGen)));
+ m_FinishGens.push_back(cFinishGenPtr(new cStructGenTrees(Seed, m_BiomeGen, m_ShapeGen, m_CompositionGen)));
}
else if (NoCaseCompare(*itr, "UnderwaterBases") == 0)
{
@@ -584,7 +604,7 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
int MaxOffset = a_IniFile.GetValueSetI("Generator", "UnderwaterBaseMaxOffset", 128);
int MaxDepth = a_IniFile.GetValueSetI("Generator", "UnderwaterBaseMaxDepth", 7);
int MaxSize = a_IniFile.GetValueSetI("Generator", "UnderwaterBaseMaxSize", 128);
- m_FinishGens.push_back(cFinishGenPtr(new cUnderwaterBaseGen(Seed, GridSize, MaxOffset, MaxDepth, MaxSize, m_BiomeGen)));
+ m_FinishGens.push_back(std::make_shared<cUnderwaterBaseGen>(Seed, GridSize, MaxOffset, MaxDepth, MaxSize, m_BiomeGen));
}
else if (NoCaseCompare(*itr, "Villages") == 0)
{
@@ -594,12 +614,12 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
int MaxSize = a_IniFile.GetValueSetI("Generator", "VillageMaxSize", 128);
int MinDensity = a_IniFile.GetValueSetI("Generator", "VillageMinDensity", 50);
int MaxDensity = a_IniFile.GetValueSetI("Generator", "VillageMaxDensity", 80);
- m_FinishGens.push_back(cFinishGenPtr(new cVillageGen(Seed, GridSize, MaxOffset, MaxDepth, MaxSize, MinDensity, MaxDensity, m_BiomeGen, m_HeightGen)));
+ m_FinishGens.push_back(std::make_shared<cVillageGen>(Seed, GridSize, MaxOffset, MaxDepth, MaxSize, MinDensity, MaxDensity, m_BiomeGen, m_CompositedHeightCache));
}
else if (NoCaseCompare(*itr, "WaterLakes") == 0)
{
int Probability = a_IniFile.GetValueSetI("Generator", "WaterLakesProbability", 25);
- m_FinishGens.push_back(cFinishGenPtr(new cStructGenLakes(Seed * 3 + 652, E_BLOCK_STATIONARY_WATER, m_HeightGen, Probability)));
+ m_FinishGens.push_back(cFinishGenPtr(new cStructGenLakes(Seed * 3 + 652, E_BLOCK_STATIONARY_WATER, m_ShapeGen, Probability)));
}
else if (NoCaseCompare(*itr, "WaterSprings") == 0)
{
diff --git a/src/Generating/ComposableGenerator.h b/src/Generating/ComposableGenerator.h
index a091f8d53..86c30e090 100644
--- a/src/Generating/ComposableGenerator.h
+++ b/src/Generating/ComposableGenerator.h
@@ -26,20 +26,16 @@ See http://forum.mc-server.org/showthread.php?tid=409 for details.
// Forward-declare the shared pointers to subgenerator classes:
class cBiomeGen;
+class cTerrainShapeGen;
class cTerrainHeightGen;
class cTerrainCompositionGen;
class cFinishGen;
typedef SharedPtr<cBiomeGen> cBiomeGenPtr;
+typedef SharedPtr<cTerrainShapeGen> cTerrainShapeGenPtr;
typedef SharedPtr<cTerrainHeightGen> cTerrainHeightGenPtr;
typedef SharedPtr<cTerrainCompositionGen> cTerrainCompositionGenPtr;
typedef SharedPtr<cFinishGen> cFinishGenPtr;
-// fwd: Noise3DGenerator.h
-class cNoise3DComposable;
-
-// fwd: DistortedHeightmap.h
-class cDistortedHeightmap;
-
@@ -70,28 +66,54 @@ public:
-/** The interface that a terrain height generator must implement
-A terrain height generator takes chunk coords on input and outputs an array of terrain heights for that chunk.
-The output array is sequenced in the same way as the BiomeGen's biome data.
+/** The interface that a terrain shape generator must implement
+A terrain shape generator takes chunk coords on input and outputs a 3D array of "shape" for that chunk. The shape here
+represents the distinction between air and solid; there's no representation of Water since that is added by the
+composition geenrator.
+The output array is indexed [y + 256 * z + 16 * 256 * x], so that it's fast to later compose a single column of the terrain,
+which is the dominant operation following the shape generation.
The generator may request biome information from the underlying BiomeGen, it may even request information for
-other chunks than the one it's currently generating (possibly neighbors - for averaging)
+other chunks than the one it's currently generating (neighbors - for averaging)
*/
-class cTerrainHeightGen
+class cTerrainShapeGen
{
public:
- virtual ~cTerrainHeightGen() {} // Force a virtual destructor in descendants
+ virtual ~cTerrainShapeGen() {} // Force a virtual destructor in descendants
- /** Generates heightmap for the given chunk */
- virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) = 0;
+ /** Generates the shape for the given chunk */
+ virtual void GenShape(int a_ChunkX, int a_ChunkZ, cChunkDesc::Shape & a_Shape) = 0;
/** Reads parameters from the ini file, prepares generator for use. */
- virtual void InitializeHeightGen(cIniFile & a_IniFile) {}
+ virtual void InitializeShapeGen(cIniFile & a_IniFile) {}
- /** Creates the correct TerrainHeightGen descendant based on the ini file settings and the seed provided.
- a_BiomeGen is the underlying biome generator, some height generators may depend on it to generate more biomes
+ /** Creates the correct TerrainShapeGen descendant based on the ini file settings and the seed provided.
+ a_BiomeGen is the underlying biome generator, some shape generators may depend on it providing additional biomes data around the chunk
a_CacheOffByDefault gets set to whether the cache should be disabled by default
- Implemented in HeiGen.cpp!
+ Implemented in ShapeGen.cpp!
*/
+ static cTerrainShapeGenPtr CreateShapeGen(cIniFile & a_IniFile, cBiomeGenPtr a_BiomeGen, int a_Seed, bool & a_CacheOffByDefault);
+} ;
+
+
+
+
+
+/** The interface that is used to query terrain height from the shape generator.
+Usually the structure generators require only the final heightmap, and generating the whole shape only to
+consume the heightmap is wasteful, so this interface is used instead; it has a cache implemented over it so
+that data is retained. */
+class cTerrainHeightGen
+{
+public:
+ virtual ~cTerrainHeightGen() {} // Force a virtual destructor in descendants
+
+ /** Retrieves the heightmap for the specified chunk. */
+ virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) = 0;
+
+ /** Initializes the generator, reading its parameters from the INI file. */
+ virtual void InitializeHeightGen(cIniFile & a_IniFile) {}
+
+ /** Creates a cTerrainHeightGen descendant based on the INI file settings. */
static cTerrainHeightGenPtr CreateHeightGen(cIniFile & a_IniFile, cBiomeGenPtr a_BiomeGen, int a_Seed, bool & a_CacheOffByDefault);
} ;
@@ -109,16 +131,18 @@ class cTerrainCompositionGen
public:
virtual ~cTerrainCompositionGen() {} // Force a virtual destructor in descendants
- virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) = 0;
+ /** Generates the chunk's composition into a_ChunkDesc, using the terrain shape provided in a_Shape.
+ Is expected to fill a_ChunkDesc's heightmap with the data from a_Shape. */
+ virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc, const cChunkDesc::Shape & a_Shape) = 0;
/** Reads parameters from the ini file, prepares generator for use. */
virtual void InitializeCompoGen(cIniFile & a_IniFile) {}
/** Creates the correct TerrainCompositionGen descendant based on the ini file settings and the seed provided.
- a_BiomeGen is the underlying biome generator, some composition generators may depend on it to generate more biomes
- a_HeightGen is the underlying height generator, some composition generators may depend on it providing additional values
+ a_BiomeGen is the underlying biome generator, some composition generators may depend on it providing additional biomes around the chunk
+ a_ShapeGen is the underlying shape generator, some composition generators may depend on it providing additional shape around the chunk
*/
- static cTerrainCompositionGenPtr CreateCompositionGen(cIniFile & a_IniFile, cBiomeGenPtr a_BiomeGen, cTerrainHeightGen & a_HeightGen, int a_Seed);
+ static cTerrainCompositionGenPtr CreateCompositionGen(cIniFile & a_IniFile, cBiomeGenPtr a_BiomeGen, cTerrainShapeGenPtr a_ShapeGen, int a_Seed);
} ;
@@ -128,7 +152,7 @@ public:
/** The interface that a finisher must implement
Finisher implements changes to the chunk after the rough terrain has been generated.
Examples of finishers are trees, snow, ore, lilypads and others.
-Note that a worldgenerator may contain multiple finishers.
+Note that a worldgenerator may contain multiple finishers, chained one after another.
Also note that previously we used to distinguish between a structuregen and a finisher; this distinction is
no longer relevant, all structure generators are considered finishers now (#398)
*/
@@ -154,23 +178,34 @@ class cComposableGenerator :
public:
cComposableGenerator(cChunkGenerator & a_ChunkGenerator);
+ // cChunkGenerator::cGenerator overrides:
virtual void Initialize(cIniFile & a_IniFile) override;
virtual void GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override;
virtual void DoGenerate(int a_ChunkX, int a_ChunkZ, cChunkDesc & a_ChunkDesc) override;
protected:
- // The generation composition:
- cBiomeGenPtr m_BiomeGen;
- cTerrainHeightGenPtr m_HeightGen;
+ // The generator's composition:
+ /** The biome generator. */
+ cBiomeGenPtr m_BiomeGen;
+
+ /** The terrain shape generator. */
+ cTerrainShapeGenPtr m_ShapeGen;
+
+ /** The terrain composition generator. */
cTerrainCompositionGenPtr m_CompositionGen;
- cFinishGenList m_FinishGens;
+
+ /** The cache for the heights of the composited terrain. */
+ cTerrainHeightGenPtr m_CompositedHeightCache;
+
+ /** The finisher generators, in the order in which they are applied. */
+ cFinishGenList m_FinishGens;
- /** Reads the biome gen settings from the ini and initializes m_BiomeGen accordingly */
+ /** Reads the BiomeGen settings from the ini and initializes m_BiomeGen accordingly */
void InitBiomeGen(cIniFile & a_IniFile);
- /** Reads the HeightGen settings from the ini and initializes m_HeightGen accordingly */
- void InitHeightGen(cIniFile & a_IniFile);
+ /** Reads the ShapeGen settings from the ini and initializes m_ShapeGen accordingly */
+ void InitShapeGen(cIniFile & a_IniFile);
/** Reads the CompositionGen settings from the ini and initializes m_CompositionGen accordingly */
void InitCompositionGen(cIniFile & a_IniFile);
diff --git a/src/Generating/CompositedHeiGen.h b/src/Generating/CompositedHeiGen.h
new file mode 100644
index 000000000..fa33a7861
--- /dev/null
+++ b/src/Generating/CompositedHeiGen.h
@@ -0,0 +1,49 @@
+
+// CompositedHeiGen.h
+
+// Declares the cCompositedHeiGen class representing a cTerrainHeightGen descendant that calculates heightmap of the composited terrain
+// This is used to further cache heightmaps for chunks already generated for finishers that require only heightmap information
+
+
+
+
+
+#pragma once
+
+#include "ComposableGenerator.h"
+
+
+
+
+
+class cCompositedHeiGen:
+ public cTerrainHeightGen
+{
+public:
+ cCompositedHeiGen(cTerrainShapeGenPtr a_ShapeGen, cTerrainCompositionGenPtr a_CompositionGen):
+ m_ShapeGen(a_ShapeGen),
+ m_CompositionGen(a_CompositionGen)
+ {
+ }
+
+
+
+ // cTerrainheightGen overrides:
+ virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override
+ {
+ cChunkDesc::Shape shape;
+ m_ShapeGen->GenShape(a_ChunkX, a_ChunkZ, shape);
+ cChunkDesc desc(a_ChunkX, a_ChunkZ);
+ desc.SetHeightFromShape(shape);
+ m_CompositionGen->ComposeTerrain(desc, shape);
+ memcpy(a_HeightMap, desc.GetHeightMap(), sizeof(a_HeightMap));
+ }
+
+protected:
+ cTerrainShapeGenPtr m_ShapeGen;
+ cTerrainCompositionGenPtr m_CompositionGen;
+};
+
+
+
+
diff --git a/src/Generating/DistortedHeightmap.cpp b/src/Generating/DistortedHeightmap.cpp
index aac1d2bf3..37a51c18e 100644
--- a/src/Generating/DistortedHeightmap.cpp
+++ b/src/Generating/DistortedHeightmap.cpp
@@ -15,163 +15,6 @@
////////////////////////////////////////////////////////////////////////////////
-// cPattern:
-
-/// This class is used to store a column pattern initialized at runtime,
-/// so that the program doesn't need to explicitly set 256 values for each pattern
-/// Each pattern has 256 blocks so that there's no need to check pattern bounds when assigning the
-/// pattern - there will always be enough pattern left, even for the whole chunk height
-class cPattern
-{
-public:
- cPattern(cDistortedHeightmap::sBlockInfo * a_TopBlocks, size_t a_Count)
- {
- // Copy the pattern into the top:
- for (size_t i = 0; i < a_Count; i++)
- {
- m_Pattern[i] = a_TopBlocks[i];
- }
-
- // Fill the rest with stone:
- static cDistortedHeightmap::sBlockInfo Stone = {E_BLOCK_STONE, 0};
- for (size_t i = a_Count; i < cChunkDef::Height; i++)
- {
- m_Pattern[i] = Stone;
- }
- }
-
- const cDistortedHeightmap::sBlockInfo * Get(void) const { return m_Pattern; }
-
-protected:
- cDistortedHeightmap::sBlockInfo m_Pattern[cChunkDef::Height];
-} ;
-
-
-
-
-
-////////////////////////////////////////////////////////////////////////////////
-// The arrays to use for the top block pattern definitions:
-
-static cDistortedHeightmap::sBlockInfo tbGrass[] =
-{
- {E_BLOCK_GRASS, 0},
- {E_BLOCK_DIRT, E_META_DIRT_NORMAL},
- {E_BLOCK_DIRT, E_META_DIRT_NORMAL},
- {E_BLOCK_DIRT, E_META_DIRT_NORMAL},
-} ;
-
-static cDistortedHeightmap::sBlockInfo tbSand[] =
-{
- { E_BLOCK_SAND, 0},
- { E_BLOCK_SAND, 0},
- { E_BLOCK_SAND, 0},
- { E_BLOCK_SANDSTONE, 0},
-} ;
-
-static cDistortedHeightmap::sBlockInfo tbDirt[] =
-{
- {E_BLOCK_DIRT, E_META_DIRT_NORMAL},
- {E_BLOCK_DIRT, E_META_DIRT_NORMAL},
- {E_BLOCK_DIRT, E_META_DIRT_NORMAL},
- {E_BLOCK_DIRT, E_META_DIRT_NORMAL},
-} ;
-
-static cDistortedHeightmap::sBlockInfo tbPodzol[] =
-{
- {E_BLOCK_DIRT, E_META_DIRT_PODZOL},
- {E_BLOCK_DIRT, E_META_DIRT_NORMAL},
- {E_BLOCK_DIRT, E_META_DIRT_NORMAL},
- {E_BLOCK_DIRT, E_META_DIRT_NORMAL},
-} ;
-
-static cDistortedHeightmap::sBlockInfo tbGrassLess[] =
-{
- {E_BLOCK_DIRT, E_META_DIRT_GRASSLESS},
- {E_BLOCK_DIRT, E_META_DIRT_NORMAL},
- {E_BLOCK_DIRT, E_META_DIRT_NORMAL},
- {E_BLOCK_DIRT, E_META_DIRT_NORMAL},
-} ;
-
-static cDistortedHeightmap::sBlockInfo tbMycelium[] =
-{
- {E_BLOCK_MYCELIUM, 0},
- {E_BLOCK_DIRT, 0},
- {E_BLOCK_DIRT, 0},
- {E_BLOCK_DIRT, 0},
-} ;
-
-static cDistortedHeightmap::sBlockInfo tbGravel[] =
-{
- {E_BLOCK_GRAVEL, 0},
- {E_BLOCK_GRAVEL, 0},
- {E_BLOCK_GRAVEL, 0},
- {E_BLOCK_STONE, 0},
-} ;
-
-static cDistortedHeightmap::sBlockInfo tbStone[] =
-{
- {E_BLOCK_STONE, 0},
- {E_BLOCK_STONE, 0},
- {E_BLOCK_STONE, 0},
- {E_BLOCK_STONE, 0},
-} ;
-
-
-
-////////////////////////////////////////////////////////////////////////////////
-// Ocean floor pattern top-block definitions:
-
-static cDistortedHeightmap::sBlockInfo tbOFSand[] =
-{
- {E_BLOCK_SAND, 0},
- {E_BLOCK_SAND, 0},
- {E_BLOCK_SAND, 0},
- {E_BLOCK_SANDSTONE, 0}
-} ;
-
-static cDistortedHeightmap::sBlockInfo tbOFClay[] =
-{
- { E_BLOCK_CLAY, 0},
- { E_BLOCK_CLAY, 0},
- { E_BLOCK_SAND, 0},
- { E_BLOCK_SAND, 0},
-} ;
-
-static cDistortedHeightmap::sBlockInfo tbOFRedSand[] =
-{
- { E_BLOCK_SAND, E_META_SAND_RED},
- { E_BLOCK_SAND, E_META_SAND_RED},
- { E_BLOCK_SAND, E_META_SAND_RED},
- { E_BLOCK_SANDSTONE, 0},
-} ;
-
-
-
-
-
-
-////////////////////////////////////////////////////////////////////////////////
-// Individual patterns to use:
-
-static cPattern patGrass (tbGrass, ARRAYCOUNT(tbGrass));
-static cPattern patSand (tbSand, ARRAYCOUNT(tbSand));
-static cPattern patDirt (tbDirt, ARRAYCOUNT(tbDirt));
-static cPattern patPodzol (tbPodzol, ARRAYCOUNT(tbPodzol));
-static cPattern patGrassLess(tbGrassLess, ARRAYCOUNT(tbGrassLess));
-static cPattern patMycelium (tbMycelium, ARRAYCOUNT(tbMycelium));
-static cPattern patGravel (tbGravel, ARRAYCOUNT(tbGravel));
-static cPattern patStone (tbStone, ARRAYCOUNT(tbStone));
-
-static cPattern patOFSand (tbOFSand, ARRAYCOUNT(tbOFSand));
-static cPattern patOFClay (tbOFClay, ARRAYCOUNT(tbOFClay));
-static cPattern patOFRedSand(tbOFRedSand, ARRAYCOUNT(tbOFRedSand));
-
-
-
-
-
-////////////////////////////////////////////////////////////////////////////////
// cDistortedHeightmap:
/** This table assigns a relative maximum overhang size in each direction to biomes.
@@ -227,17 +70,17 @@ 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:
+ // Release 1.7 biome variants:
/* biSunflowerPlains */ { 1.0f, 1.0f}, // 129
/* biDesertM */ { 1.0f, 1.0f}, // 130
/* biExtremeHillsM */ {16.0f, 16.0f}, // 131
@@ -246,22 +89,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
@@ -279,8 +122,8 @@ const cDistortedHeightmap::sGenParam cDistortedHeightmap::m_GenParam[256] =
cDistortedHeightmap::cDistortedHeightmap(int a_Seed, cBiomeGenPtr a_BiomeGen) :
m_NoiseDistortX(a_Seed + 1000),
m_NoiseDistortZ(a_Seed + 2000),
- m_OceanFloorSelect(a_Seed + 3000),
- m_MesaFloor(a_Seed + 4000),
+ m_CurChunkX(0x7fffffff), // Set impossible coords for the chunk so that it's always considered stale
+ m_CurChunkZ(0x7fffffff),
m_BiomeGen(a_BiomeGen),
m_UnderlyingHeiGen(new cHeiGenBiomal(a_Seed, a_BiomeGen)),
m_HeightGen(m_UnderlyingHeiGen, 64),
@@ -293,8 +136,6 @@ cDistortedHeightmap::cDistortedHeightmap(int a_Seed, cBiomeGenPtr a_BiomeGen) :
m_NoiseDistortZ.AddOctave((NOISE_DATATYPE)1, (NOISE_DATATYPE)0.5);
m_NoiseDistortZ.AddOctave((NOISE_DATATYPE)0.5, (NOISE_DATATYPE)1);
m_NoiseDistortZ.AddOctave((NOISE_DATATYPE)0.25, (NOISE_DATATYPE)2);
-
- InitMesaPattern(a_Seed);
}
@@ -309,7 +150,7 @@ void cDistortedHeightmap::Initialize(cIniFile & a_IniFile)
}
// Read the params from the INI file:
- m_SeaLevel = a_IniFile.GetValueSetI("Generator", "DistortedHeightmapSeaLevel", 62);
+ m_SeaLevel = a_IniFile.GetValueSetI("Generator", "SeaLevel", 62);
m_FrequencyX = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "DistortedHeightmapFrequencyX", 10);
m_FrequencyY = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "DistortedHeightmapFrequencyY", 10);
m_FrequencyZ = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "DistortedHeightmapFrequencyZ", 10);
@@ -321,89 +162,6 @@ void cDistortedHeightmap::Initialize(cIniFile & a_IniFile)
-void cDistortedHeightmap::InitMesaPattern(int a_Seed)
-{
- // Stone in the bottom half of the pattern:
- for (int i = cChunkDef::Height; i < 2 * cChunkDef::Height; i++)
- {
- m_MesaPattern[i].BlockMeta = 0;
- m_MesaPattern[i].BlockType = E_BLOCK_STONE;
- }
-
- // Stained and hardened clay in the top half of the pattern
- // In a loop, choose whether to use one or two layers of stained clay, then choose a color and width for each layer
- // Separate each group with another layer of hardened clay
- cNoise PatternNoise((unsigned)a_Seed);
- static NIBBLETYPE AllowedColors[] =
- {
- E_META_STAINED_CLAY_YELLOW,
- E_META_STAINED_CLAY_YELLOW,
- E_META_STAINED_CLAY_RED,
- E_META_STAINED_CLAY_RED,
- E_META_STAINED_CLAY_WHITE,
- E_META_STAINED_CLAY_BROWN,
- E_META_STAINED_CLAY_BROWN,
- E_META_STAINED_CLAY_BROWN,
- E_META_STAINED_CLAY_ORANGE,
- E_META_STAINED_CLAY_ORANGE,
- E_META_STAINED_CLAY_ORANGE,
- E_META_STAINED_CLAY_ORANGE,
- E_META_STAINED_CLAY_ORANGE,
- E_META_STAINED_CLAY_ORANGE,
- E_META_STAINED_CLAY_LIGHTGRAY,
- } ;
- static int LayerSizes[] = // Adjust the chance so that thinner layers occur more commonly
- {
- 1, 1, 1, 1, 1, 1,
- 2, 2, 2, 2,
- 3, 3,
- } ;
- int Idx = cChunkDef::Height - 1;
- while (Idx >= 0)
- {
- // A layer group of 1 - 2 color stained clay:
- int Random = PatternNoise.IntNoise1DInt(Idx) / 7;
- int NumLayers = (Random % 2) + 1;
- Random /= 2;
- for (int Lay = 0; Lay < NumLayers; Lay++)
- {
- int NumBlocks = LayerSizes[(Random % ARRAYCOUNT(LayerSizes))];
- NIBBLETYPE Color = AllowedColors[(Random / 4) % ARRAYCOUNT(AllowedColors)];
- if (
- ((NumBlocks == 3) && (NumLayers == 2)) || // In two-layer mode disallow the 3-high layers:
- (Color == E_META_STAINED_CLAY_WHITE)) // White stained clay can ever be only 1 block high
- {
- NumBlocks = 1;
- }
- NumBlocks = std::min(Idx + 1, NumBlocks); // Limit by Idx so that we don't have to check inside the loop
- Random /= 32;
- for (int Block = 0; Block < NumBlocks; Block++, Idx--)
- {
- m_MesaPattern[Idx].BlockMeta = Color;
- m_MesaPattern[Idx].BlockType = E_BLOCK_STAINED_CLAY;
- } // for Block
- } // for Lay
-
- // A layer of hardened clay in between the layer group:
- int NumBlocks = (Random % 4) + 1; // All heights the same probability
- if ((NumLayers == 2) && (NumBlocks < 4))
- {
- // For two layers of stained clay, add an extra block of hardened clay:
- NumBlocks++;
- }
- NumBlocks = std::min(Idx + 1, NumBlocks); // Limit by Idx so that we don't have to check inside the loop
- for (int Block = 0; Block < NumBlocks; Block++, Idx--)
- {
- m_MesaPattern[Idx].BlockMeta = 0;
- m_MesaPattern[Idx].BlockType = E_BLOCK_HARDENED_CLAY;
- } // for Block
- } // while (Idx >= 0)
-}
-
-
-
-
-
void cDistortedHeightmap::PrepareState(int a_ChunkX, int a_ChunkZ)
{
if ((m_CurChunkX == a_ChunkX) && (m_CurChunkZ == a_ChunkZ))
@@ -474,23 +232,17 @@ void cDistortedHeightmap::GenerateHeightArray(void)
-void cDistortedHeightmap::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap)
+void cDistortedHeightmap::GenShape(int a_ChunkX, int a_ChunkZ, cChunkDesc::Shape & a_Shape)
{
PrepareState(a_ChunkX, a_ChunkZ);
for (int z = 0; z < cChunkDef::Width; z++)
{
for (int x = 0; x < cChunkDef::Width; x++)
{
- int NoiseArrayIdx = x + 17 * 257 * z;
- cChunkDef::SetHeight(a_HeightMap, x, z, m_SeaLevel - 1);
- for (int y = cChunkDef::Height - 1; y > m_SeaLevel - 1; y--)
+ int idx = x + 17 * 257 * z;
+ for (int y = 0; y < cChunkDef::Height; y++)
{
- int HeightMapHeight = (int)m_DistortedHeightmap[NoiseArrayIdx + 17 * y];
- if (y < HeightMapHeight)
- {
- cChunkDef::SetHeight(a_HeightMap, x, z, y);
- break;
- }
+ a_Shape[y + x * 256 + z * 16 * 256] = (y < m_DistortedHeightmap[idx + y * 17]) ? 1 : 0;
} // for y
} // for x
} // for z
@@ -500,36 +252,7 @@ void cDistortedHeightmap::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::He
-void cDistortedHeightmap::InitializeHeightGen(cIniFile & a_IniFile)
-{
- Initialize(a_IniFile);
-}
-
-
-
-
-
-void cDistortedHeightmap::ComposeTerrain(cChunkDesc & a_ChunkDesc)
-{
- // Prepare the internal state for generating this chunk:
- PrepareState(a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ());
-
- // Compose:
- a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0);
- for (int z = 0; z < cChunkDef::Width; z++)
- {
- for (int x = 0; x < cChunkDef::Width; x++)
- {
- ComposeColumn(a_ChunkDesc, x, z);
- } // for x
- } // for z
-}
-
-
-
-
-
-void cDistortedHeightmap::InitializeCompoGen(cIniFile & a_IniFile)
+void cDistortedHeightmap::InitializeShapeGen(cIniFile & a_IniFile)
{
Initialize(a_IniFile);
}
@@ -654,275 +377,3 @@ void cDistortedHeightmap::GetDistortAmpsAt(BiomeNeighbors & a_Neighbors, int a_R
-void cDistortedHeightmap::ComposeColumn(cChunkDesc & a_ChunkDesc, int a_RelX, int a_RelZ)
-{
- // Frequencies for the podzol floor selecting noise:
- const NOISE_DATATYPE FrequencyX = 8;
- const NOISE_DATATYPE FrequencyZ = 8;
-
- EMCSBiome Biome = a_ChunkDesc.GetBiome(a_RelX, a_RelZ);
- switch (Biome)
- {
- case biOcean:
- case biPlains:
- case biForest:
- case biTaiga:
- case biSwampland:
- case biRiver:
- case biFrozenOcean:
- case biFrozenRiver:
- case biIcePlains:
- case biIceMountains:
- case biForestHills:
- case biTaigaHills:
- case biExtremeHillsEdge:
- case biExtremeHillsPlus:
- case biExtremeHills:
- case biJungle:
- case biJungleHills:
- case biJungleEdge:
- case biDeepOcean:
- case biStoneBeach:
- case biColdBeach:
- case biBirchForest:
- case biBirchForestHills:
- case biRoofedForest:
- case biColdTaiga:
- case biColdTaigaHills:
- case biSavanna:
- case biSavannaPlateau:
- case biSunflowerPlains:
- case biFlowerForest:
- case biTaigaM:
- case biSwamplandM:
- case biIcePlainsSpikes:
- case biJungleM:
- case biJungleEdgeM:
- case biBirchForestM:
- case biBirchForestHillsM:
- case biRoofedForestM:
- case biColdTaigaM:
- case biSavannaM:
- case biSavannaPlateauM:
- {
- FillColumnPattern(a_ChunkDesc, a_RelX, a_RelZ, patGrass.Get());
- return;
- }
-
- case biMegaTaiga:
- case biMegaTaigaHills:
- case biMegaSpruceTaiga:
- case biMegaSpruceTaigaHills:
- {
- // Select the pattern to use - podzol, grass or grassless dirt:
- NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(m_CurChunkX * cChunkDef::Width + a_RelX)) / FrequencyX;
- NOISE_DATATYPE NoiseY = ((NOISE_DATATYPE)(m_CurChunkZ * cChunkDef::Width + a_RelZ)) / FrequencyZ;
- NOISE_DATATYPE Val = m_OceanFloorSelect.CubicNoise2D(NoiseX, NoiseY);
- const sBlockInfo * Pattern = (Val < -0.9) ? patGrassLess.Get() : ((Val > 0) ? patPodzol.Get() : patGrass.Get());
- FillColumnPattern(a_ChunkDesc, a_RelX, a_RelZ, Pattern);
- return;
- }
-
- case biDesertHills:
- case biDesert:
- case biDesertM:
- case biBeach:
- {
- FillColumnPattern(a_ChunkDesc, a_RelX, a_RelZ, patSand.Get());
- return;
- }
-
- case biMushroomIsland:
- case biMushroomShore:
- {
- FillColumnPattern(a_ChunkDesc, a_RelX, a_RelZ, patMycelium.Get());
- return;
- }
-
- case biMesa:
- case biMesaPlateauF:
- case biMesaPlateau:
- case biMesaBryce:
- case biMesaPlateauFM:
- case biMesaPlateauM:
- {
- // Mesa biomes need special handling, because they don't follow the usual "4 blocks from top pattern",
- // instead, they provide a "from bottom" pattern with varying base height,
- // usually 4 blocks below the ocean level
- FillColumnMesa(a_ChunkDesc, a_RelX, a_RelZ);
- return;
- }
-
- case biExtremeHillsPlusM:
- case biExtremeHillsM:
- {
- // Select the pattern to use - gravel, stone or grass:
- NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(m_CurChunkX * cChunkDef::Width + a_RelX)) / FrequencyX;
- NOISE_DATATYPE NoiseY = ((NOISE_DATATYPE)(m_CurChunkZ * cChunkDef::Width + a_RelZ)) / FrequencyZ;
- NOISE_DATATYPE Val = m_OceanFloorSelect.CubicNoise2D(NoiseX, NoiseY);
- const sBlockInfo * Pattern = (Val < 0.0) ? patStone.Get() : patGrass.Get();
- FillColumnPattern(a_ChunkDesc, a_RelX, a_RelZ, Pattern);
- return;
- }
- default:
- {
- ASSERT(!"Unhandled biome");
- return;
- }
- } // switch (Biome)
-}
-
-
-
-
-
-void cDistortedHeightmap::FillColumnPattern(cChunkDesc & a_ChunkDesc, int a_RelX, int a_RelZ, const sBlockInfo * a_Pattern)
-{
- int NoiseArrayIdx = a_RelX + 17 * 257 * a_RelZ;
- bool HasHadWater = false;
- int PatternIdx = 0;
- for (int y = a_ChunkDesc.GetHeight(a_RelX, a_RelZ); y > 0; y--)
- {
- int HeightMapHeight = (int)m_DistortedHeightmap[NoiseArrayIdx + 17 * y];
-
- if (y < HeightMapHeight)
- {
- // "ground" part, use the pattern:
- a_ChunkDesc.SetBlockTypeMeta(a_RelX, y, a_RelZ, a_Pattern[PatternIdx].BlockType, a_Pattern[PatternIdx].BlockMeta);
- PatternIdx++;
- continue;
- }
-
- // "air" or "water" part:
- // Reset the pattern index to zero, so that the pattern is repeated from the top again:
- PatternIdx = 0;
-
- if (y >= m_SeaLevel)
- {
- // "air" part, do nothing
- continue;
- }
-
- a_ChunkDesc.SetBlockType(a_RelX, y, a_RelZ, E_BLOCK_STATIONARY_WATER);
- if (HasHadWater)
- {
- continue;
- }
-
- // Select the ocean-floor pattern to use:
- a_Pattern = a_ChunkDesc.GetBiome(a_RelX, a_RelZ) == biDeepOcean ? patGravel.Get() : ChooseOceanFloorPattern(a_RelX, a_RelZ);
- HasHadWater = true;
- } // for y
- a_ChunkDesc.SetBlockType(a_RelX, 0, a_RelZ, E_BLOCK_BEDROCK);
-}
-
-
-
-
-
-void cDistortedHeightmap::FillColumnMesa(cChunkDesc & a_ChunkDesc, int a_RelX, int a_RelZ)
-{
- // Frequencies for the clay floor noise:
- const NOISE_DATATYPE FrequencyX = 50;
- const NOISE_DATATYPE FrequencyZ = 50;
-
- int Top = a_ChunkDesc.GetHeight(a_RelX, a_RelZ);
- if (Top < m_SeaLevel)
- {
- // The terrain is below sealevel, handle as regular ocean:
- FillColumnPattern(a_ChunkDesc, a_RelX, a_RelZ, patOFRedSand.Get());
- return;
- }
-
- NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(m_CurChunkX * cChunkDef::Width + a_RelX)) / FrequencyX;
- NOISE_DATATYPE NoiseY = ((NOISE_DATATYPE)(m_CurChunkZ * cChunkDef::Width + a_RelZ)) / FrequencyZ;
- int ClayFloor = m_SeaLevel - 6 + (int)(4.f * m_MesaFloor.CubicNoise2D(NoiseX, NoiseY));
- if (ClayFloor >= Top)
- {
- ClayFloor = Top - 1;
- }
-
- if (Top - m_SeaLevel < 5)
- {
- // Simple case: top is red sand, then hardened clay down to ClayFloor, then stone:
- a_ChunkDesc.SetBlockTypeMeta(a_RelX, Top, a_RelZ, E_BLOCK_SAND, E_META_SAND_RED);
- for (int y = Top - 1; y >= ClayFloor; y--)
- {
- a_ChunkDesc.SetBlockType(a_RelX, y, a_RelZ, E_BLOCK_HARDENED_CLAY);
- }
- for (int y = ClayFloor - 1; y > 0; y--)
- {
- a_ChunkDesc.SetBlockType(a_RelX, y, a_RelZ, E_BLOCK_STONE);
- }
- a_ChunkDesc.SetBlockType(a_RelX, 0, a_RelZ, E_BLOCK_BEDROCK);
- return;
- }
-
- // Difficult case: use the mesa pattern and watch for overhangs:
- int NoiseArrayIdx = a_RelX + 17 * 257 * a_RelZ;
- int PatternIdx = cChunkDef::Height - (Top - ClayFloor); // We want the block at index ClayFloor to be pattern's 256th block (first stone)
- const sBlockInfo * Pattern = m_MesaPattern;
- bool HasHadWater = false;
- for (int y = Top; y > 0; y--)
- {
- int HeightMapHeight = (int)m_DistortedHeightmap[NoiseArrayIdx + 17 * y];
- if (y < HeightMapHeight)
- {
- // "ground" part, use the pattern:
- a_ChunkDesc.SetBlockTypeMeta(a_RelX, y, a_RelZ, Pattern[PatternIdx].BlockType, Pattern[PatternIdx].BlockMeta);
- PatternIdx++;
- continue;
- }
-
- if (y >= m_SeaLevel)
- {
- // "air" part, do nothing
- continue;
- }
-
- // "water" part, fill with water and choose new pattern for ocean floor, if not chosen already:
- PatternIdx = 0;
- a_ChunkDesc.SetBlockType(a_RelX, y, a_RelZ, E_BLOCK_STATIONARY_WATER);
- if (HasHadWater)
- {
- continue;
- }
-
- // Select the ocean-floor pattern to use:
- Pattern = ChooseOceanFloorPattern(a_RelX, a_RelZ);
- HasHadWater = true;
- } // for y
- a_ChunkDesc.SetBlockType(a_RelX, 0, a_RelZ, E_BLOCK_BEDROCK);
-}
-
-
-
-
-
-const cDistortedHeightmap::sBlockInfo * cDistortedHeightmap::ChooseOceanFloorPattern(int a_RelX, int a_RelZ)
-{
- // Frequencies for the ocean floor selecting noise:
- const NOISE_DATATYPE FrequencyX = 3;
- const NOISE_DATATYPE FrequencyZ = 3;
-
- // Select the ocean-floor pattern to use:
- NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(m_CurChunkX * cChunkDef::Width + a_RelX)) / FrequencyX;
- NOISE_DATATYPE NoiseY = ((NOISE_DATATYPE)(m_CurChunkZ * cChunkDef::Width + a_RelZ)) / FrequencyZ;
- NOISE_DATATYPE Val = m_OceanFloorSelect.CubicNoise2D(NoiseX, NoiseY);
- if (Val < -0.95)
- {
- return patOFClay.Get();
- }
- else if (Val < 0)
- {
- return patOFSand.Get();
- }
- else
- {
- return patDirt.Get();
- }
-}
-
-
-
-
diff --git a/src/Generating/DistortedHeightmap.h b/src/Generating/DistortedHeightmap.h
index d073f29e4..79fc35542 100644
--- a/src/Generating/DistortedHeightmap.h
+++ b/src/Generating/DistortedHeightmap.h
@@ -11,7 +11,6 @@
#include "ComposableGenerator.h"
#include "HeiGen.h"
-#include "../Noise.h"
@@ -24,17 +23,9 @@
class cDistortedHeightmap :
- public cTerrainHeightGen,
- public cTerrainCompositionGen
+ public cTerrainShapeGen
{
public:
- /// Structure used for storing block patterns for columns
- struct sBlockInfo
- {
- BLOCKTYPE BlockType;
- NIBBLETYPE BlockMeta;
- } ;
-
cDistortedHeightmap(int a_Seed, cBiomeGenPtr a_BiomeGen);
protected:
@@ -52,8 +43,6 @@ protected:
cPerlinNoise m_NoiseDistortX;
cPerlinNoise m_NoiseDistortZ;
- cNoise m_OceanFloorSelect; ///< Used for selecting between dirt and sand on the ocean floor
- cNoise m_MesaFloor; ///< Used for the floor of the clay blocks in mesa biomes
int m_SeaLevel;
NOISE_DATATYPE m_FrequencyX;
@@ -71,9 +60,9 @@ protected:
cTerrainHeightGenPtr m_UnderlyingHeiGen;
/** Cache for m_UnderlyingHeiGen. */
- cHeiGenCache m_HeightGen;
+ cHeiGenCache m_HeightGen;
- /// Heightmap for the current chunk, before distortion (from m_HeightGen). Used for optimization.
+ /** Heightmap for the current chunk, before distortion (from m_HeightGen). Used for optimization. */
cChunkDef::HeightMap m_CurChunkHeights;
// Per-biome terrain generator parameters:
@@ -88,54 +77,30 @@ protected:
NOISE_DATATYPE m_DistortAmpX[DIM_X * DIM_Z];
NOISE_DATATYPE m_DistortAmpZ[DIM_X * DIM_Z];
- /// True if Initialize() has been called. Used to initialize-once even with multiple init entrypoints (HeiGen / CompoGen)
+ /** True if Initialize() has been called. Used to initialize-once even with multiple init entrypoints (HeiGen / CompoGen). */
bool m_IsInitialized;
- /// The vertical pattern to be used for mesa biomes. Seed-dependant.
- /// One Height of pattern and one Height of stone to avoid checking pattern dimensions
- sBlockInfo m_MesaPattern[2 * cChunkDef::Height];
-
- /// Initializes m_MesaPattern with a reasonable pattern of stained clay / hardened clay, based on the seed
- void InitMesaPattern(int a_Seed);
-
- /// Unless the LastChunk coords are equal to coords given, prepares the internal state (noise arrays, heightmap)
+ /** Unless the LastChunk coords are equal to coords given, prepares the internal state (noise arrays, heightmap). */
void PrepareState(int a_ChunkX, int a_ChunkZ);
- /// Generates the m_DistortedHeightmap array for the current chunk
+ /** Generates the m_DistortedHeightmap array for the current chunk. */
void GenerateHeightArray(void);
- /// Calculates the heightmap value (before distortion) at the specified (floating-point) coords
+ /** Calculates the heightmap value (before distortion) at the specified (floating-point) coords. */
int GetHeightmapAt(NOISE_DATATYPE a_X, NOISE_DATATYPE a_Z);
- /// Updates m_DistortAmpX/Z[] based on m_CurChunkX and m_CurChunkZ
+ /** Updates m_DistortAmpX/Z[] based on m_CurChunkX and m_CurChunkZ. */
void UpdateDistortAmps(void);
- /// Calculates the X and Z distortion amplitudes based on the neighbors' biomes
+ /** Calculates the X and Z distortion amplitudes based on the neighbors' biomes. */
void GetDistortAmpsAt(BiomeNeighbors & a_Neighbors, int a_RelX, int a_RelZ, NOISE_DATATYPE & a_DistortAmpX, NOISE_DATATYPE & a_DistortAmpZ);
- /// Reads the settings from the ini file. Skips reading if already initialized
+ /** Reads the settings from the ini file. Skips reading if already initialized. */
void Initialize(cIniFile & a_IniFile);
- /// Composes a single column in a_ChunkDesc. Chooses what to do based on the biome in that column
- void ComposeColumn(cChunkDesc & a_ChunkDesc, int a_RelX, int a_RelZ);
- /// Fills the specified column with the specified pattern; restarts the pattern when air is reached,
- /// switches to ocean floor pattern if ocean is reached. Always adds bedrock at the very bottom.
- void FillColumnPattern(cChunkDesc & a_ChunkDesc, int a_RelX, int a_RelZ, const sBlockInfo * a_Pattern);
-
- /// Fills the specified column with mesa pattern, based on the column height
- void FillColumnMesa(cChunkDesc & a_ChunkDesc, int a_RelX, int a_RelZ);
-
- /// Returns the pattern to use for an ocean floor in the specified column
- const sBlockInfo * ChooseOceanFloorPattern(int a_RelX, int a_RelZ);
-
-
- // cTerrainHeightGen overrides:
- virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override;
- virtual void InitializeHeightGen(cIniFile & a_IniFile) override;
-
- // cTerrainCompositionGen overrides:
- virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override;
- virtual void InitializeCompoGen(cIniFile & a_IniFile) override;
+ // cTerrainShapeGen overrides:
+ virtual void GenShape(int a_ChunkX, int a_ChunkZ, cChunkDesc::Shape & a_Shape) override;
+ virtual void InitializeShapeGen(cIniFile & a_IniFile) override;
} ;
diff --git a/src/Generating/DungeonRoomsFinisher.cpp b/src/Generating/DungeonRoomsFinisher.cpp
index 3f328868d..c4bf8839e 100644
--- a/src/Generating/DungeonRoomsFinisher.cpp
+++ b/src/Generating/DungeonRoomsFinisher.cpp
@@ -7,6 +7,7 @@
#include "DungeonRoomsFinisher.h"
#include "../FastRandom.h"
#include "../BlockEntities/ChestEntity.h"
+#include "../BlockEntities/MobSpawnerEntity.h"
@@ -57,6 +58,22 @@ public:
int SecondChestPos = (FirstChestPos + 2 + (rnd % (NumPositions - 3))) % NumPositions;
m_Chest1 = DecodeChestCoords(FirstChestPos, SizeX, SizeZ);
m_Chest2 = DecodeChestCoords(SecondChestPos, SizeX, SizeZ);
+
+ // Choose what the mobspawner will spawn.
+ // 25% chance for a spider, 25% for a skeleton and 50% chance to get a zombie spawer.
+ int MobType = (a_Noise.IntNoise3DInt(a_OriginX, m_FloorHeight, a_OriginZ) / 7) % 100;
+ if (MobType <= 25)
+ {
+ m_MonsterType = mtSkeleton;
+ }
+ else if (MobType <= 50)
+ {
+ m_MonsterType = mtSpider;
+ }
+ else
+ {
+ m_MonsterType = mtZombie;
+ }
}
protected:
@@ -76,9 +93,12 @@ protected:
/** The (absolute) coords of the second chest. The Y coord represents the chest's Meta value (facing). */
Vector3i m_Chest2;
+ /** The monster type for the mobspawner entity. */
+ eMonsterType m_MonsterType;
- /** Decodes the position index along the room walls into a proper 2D position for a chest. */
+ /** Decodes the position index along the room walls into a proper 2D position for a chest.
+ The Y coord of the returned vector specifies the chest's meta value*/
Vector3i DecodeChestCoords(int a_PosIdx, int a_SizeX, int a_SizeZ)
{
if (a_PosIdx < a_SizeX)
@@ -111,8 +131,8 @@ protected:
{
int BlockX = a_ChunkDesc.GetChunkX() * cChunkDef::Width;
int BlockZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width;
- int RelStartX = Clamp(a_StartX - BlockX, 0, cChunkDef::Width - 1);
- int RelStartZ = Clamp(a_StartZ - BlockZ, 0, cChunkDef::Width - 1);
+ int RelStartX = Clamp(a_StartX - BlockX, 0, cChunkDef::Width);
+ int RelStartZ = Clamp(a_StartZ - BlockZ, 0, cChunkDef::Width);
int RelEndX = Clamp(a_EndX - BlockX, 0, cChunkDef::Width);
int RelEndZ = Clamp(a_EndZ - BlockZ, 0, cChunkDef::Width);
for (int y = a_StartY; y < a_EndY; y++)
@@ -245,7 +265,9 @@ protected:
)
{
a_ChunkDesc.SetBlockTypeMeta(CenterX, b, CenterZ, E_BLOCK_MOB_SPAWNER, 0);
- // TODO: Set the spawned mob
+ cMobSpawnerEntity * MobSpawner = static_cast<cMobSpawnerEntity *>(a_ChunkDesc.GetBlockEntity(CenterX, b, CenterZ));
+ ASSERT((MobSpawner != nullptr) && (MobSpawner->GetBlockType() == E_BLOCK_MOB_SPAWNER));
+ MobSpawner->SetEntity(m_MonsterType);
}
}
} ;
@@ -258,9 +280,9 @@ protected:
////////////////////////////////////////////////////////////////////////////////
// cDungeonRoomsFinisher:
-cDungeonRoomsFinisher::cDungeonRoomsFinisher(cTerrainHeightGenPtr a_HeightGen, int a_Seed, int a_GridSize, int a_MaxSize, int a_MinSize, const AString & a_HeightDistrib) :
+cDungeonRoomsFinisher::cDungeonRoomsFinisher(cTerrainShapeGenPtr a_ShapeGen, int a_Seed, int a_GridSize, int a_MaxSize, int a_MinSize, const AString & a_HeightDistrib) :
super(a_Seed + 100, a_GridSize, a_GridSize, a_GridSize, a_GridSize, a_MaxSize, a_MaxSize, 1024),
- m_HeightGen(a_HeightGen),
+ m_ShapeGen(a_ShapeGen),
m_MaxHalfSize((a_MaxSize + 1) / 2),
m_MinHalfSize((a_MinSize + 1) / 2),
m_HeightProbability(cChunkDef::Height)
@@ -293,13 +315,21 @@ cDungeonRoomsFinisher::cStructurePtr cDungeonRoomsFinisher::CreateStructure(int
int ChunkX, ChunkZ;
int RelX = a_OriginX, RelY = 0, RelZ = a_OriginZ;
cChunkDef::AbsoluteToRelative(RelX, RelY, RelZ, ChunkX, ChunkZ);
- cChunkDef::HeightMap HeightMap;
- m_HeightGen->GenHeightMap(ChunkX, ChunkZ, HeightMap);
- int Height = cChunkDef::GetHeight(HeightMap, RelX, RelZ); // Max room height at {a_OriginX, a_OriginZ}
- Height = Clamp(m_HeightProbability.MapValue(rnd % m_HeightProbability.GetSum()), 10, Height - 5);
+ cChunkDesc::Shape shape;
+ m_ShapeGen->GenShape(ChunkX, ChunkZ, shape);
+ int height = 0;
+ int idx = RelX * 256 + RelZ * 16 * 256;
+ for (int y = 6; y < cChunkDef::Height; y++)
+ {
+ if (shape[idx + y] != 0)
+ {
+ continue;
+ }
+ height = Clamp(m_HeightProbability.MapValue(rnd % m_HeightProbability.GetSum()), 10, y - 5);
+ }
// Create the dungeon room descriptor:
- return cStructurePtr(new cDungeonRoom(a_GridX, a_GridZ, a_OriginX, a_OriginZ, HalfSizeX, HalfSizeZ, Height, m_Noise));
+ return cStructurePtr(new cDungeonRoom(a_GridX, a_GridZ, a_OriginX, a_OriginZ, HalfSizeX, HalfSizeZ, height, m_Noise));
}
diff --git a/src/Generating/DungeonRoomsFinisher.h b/src/Generating/DungeonRoomsFinisher.h
index 09dd0448a..e5828f989 100644
--- a/src/Generating/DungeonRoomsFinisher.h
+++ b/src/Generating/DungeonRoomsFinisher.h
@@ -23,15 +23,15 @@ class cDungeonRoomsFinisher :
public:
/** Creates a new dungeon room finisher.
- a_HeightGen is the underlying height generator, so that the rooms can always be placed under the terrain.
+ a_ShapeGen is the underlying terrain shape generator, so that the rooms can always be placed under the terrain.
a_MaxSize and a_MinSize are the maximum and minimum sizes of the room's internal (air) area, in blocks across.
a_HeightDistrib is the string defining the height distribution for the rooms (cProbabDistrib format). */
- cDungeonRoomsFinisher(cTerrainHeightGenPtr a_HeightGen, int a_Seed, int a_GridSize, int a_MaxSize, int a_MinSize, const AString & a_HeightDistrib);
+ cDungeonRoomsFinisher(cTerrainShapeGenPtr a_ShapeGen, int a_Seed, int a_GridSize, int a_MaxSize, int a_MinSize, const AString & a_HeightDistrib);
protected:
- /** The height gen that is used for limiting the rooms' Y coords */
- cTerrainHeightGenPtr m_HeightGen;
+ /** The shape gen that is used for limiting the rooms' Y coords */
+ cTerrainShapeGenPtr m_ShapeGen;
/** Maximum half-size (from center to wall) of the dungeon room's inner (air) area. Default is 3 (vanilla). */
int m_MaxHalfSize;
diff --git a/src/Generating/EndGen.cpp b/src/Generating/EndGen.cpp
index 0111d2fa3..89d6117bb 100644
--- a/src/Generating/EndGen.cpp
+++ b/src/Generating/EndGen.cpp
@@ -147,13 +147,14 @@ bool cEndGen::IsChunkOutsideRange(int a_ChunkX, int a_ChunkZ)
-void cEndGen::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap)
+void cEndGen::GenShape(int a_ChunkX, int a_ChunkZ, cChunkDesc::Shape & a_Shape)
{
+ // If the chunk is outside out range, fill the shape with zeroes:
if (IsChunkOutsideRange(a_ChunkX, a_ChunkZ))
{
- for (size_t i = 0; i < ARRAYCOUNT(a_HeightMap); i++)
+ for (size_t i = 0; i < ARRAYCOUNT(a_Shape); i++)
{
- a_HeightMap[i] = 0;
+ a_Shape[i] = 0;
}
return;
}
@@ -165,15 +166,14 @@ void cEndGen::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_
{
for (int x = 0; x < cChunkDef::Width; x++)
{
- cChunkDef::SetHeight(a_HeightMap, x, z, MaxY);
- for (int y = MaxY; y > 0; y--)
+ for (int y = 0; y < MaxY; y++)
{
- if (m_NoiseArray[y * 17 * 17 + z * 17 + x] <= 0)
- {
- cChunkDef::SetHeight(a_HeightMap, x, z, y);
- break;
- }
- } // for y
+ a_Shape[(x + 16 * z) * 256 + y] = (m_NoiseArray[y * 17 * 17 + z * 17 + z] > 0) ? 1 : 0;
+ }
+ for (int y = MaxY; y < cChunkDef::Height; y++)
+ {
+ a_Shape[(x + 16 * z) * 256 + y] = 0;
+ }
} // for x
} // for z
}
@@ -182,30 +182,18 @@ void cEndGen::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_
-void cEndGen::ComposeTerrain(cChunkDesc & a_ChunkDesc)
+void cEndGen::ComposeTerrain(cChunkDesc & a_ChunkDesc, const cChunkDesc::Shape & a_Shape)
{
- if (IsChunkOutsideRange(a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ()))
- {
- a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0);
- return;
- }
-
- PrepareState(a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ());
-
- int MaxY = std::min((int)(1.75 * m_IslandSizeY + 1), cChunkDef::Height - 1);
+ a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0);
for (int z = 0; z < cChunkDef::Width; z++)
{
for (int x = 0; x < cChunkDef::Width; x++)
{
- for (int y = MaxY; y > 0; y--)
+ for (int y = 0; y < cChunkDef::Height; y++)
{
- if (m_NoiseArray[y * 17 * 17 + z * 17 + x] <= 0)
- {
- a_ChunkDesc.SetBlockTypeMeta(x, y, z, E_BLOCK_END_STONE, 0);
- }
- else
+ if (a_Shape[(x + 16 * z) * 256 + y] != 0)
{
- a_ChunkDesc.SetBlockTypeMeta(x, y, z, E_BLOCK_AIR, 0);
+ a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_END_STONE);
}
} // for y
} // for x
diff --git a/src/Generating/EndGen.h b/src/Generating/EndGen.h
index 322061810..f9e3f6e53 100644
--- a/src/Generating/EndGen.h
+++ b/src/Generating/EndGen.h
@@ -10,14 +10,14 @@
#pragma once
#include "ComposableGenerator.h"
-#include "../Noise.h"
+#include "../Noise/Noise.h"
class cEndGen :
- public cTerrainHeightGen,
+ public cTerrainShapeGen,
public cTerrainCompositionGen
{
public:
@@ -59,10 +59,10 @@ protected:
/// Returns true if the chunk is outside of the island's dimensions
bool IsChunkOutsideRange(int a_ChunkX, int a_ChunkZ);
- // cTerrainHeightGen overrides:
- virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override;
+ // cTerrainShapeGen overrides:
+ virtual void GenShape(int a_ChunkX, int a_ChunkZ, cChunkDesc::Shape & a_Shape) override;
// cTerrainCompositionGen overrides:
- virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override;
+ virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc, const cChunkDesc::Shape & a_Shape) override;
virtual void InitializeCompoGen(cIniFile & a_IniFile) override;
} ;
diff --git a/src/Generating/FinishGen.cpp b/src/Generating/FinishGen.cpp
index 0564789dc..e10cb00f8 100644
--- a/src/Generating/FinishGen.cpp
+++ b/src/Generating/FinishGen.cpp
@@ -10,7 +10,6 @@
#include "Globals.h"
#include "FinishGen.h"
-#include "../Noise.h"
#include "../BlockID.h"
#include "../Simulator/FluidSimulator.h" // for cFluidSimulator::CanWashAway()
#include "../Simulator/FireSimulator.h"
@@ -27,6 +26,8 @@
#define DEF_OVERWORLD_LAVA_SPRINGS "0, 0; 10, 5; 11, 45; 48, 2; 64, 1; 255, 0"
#define DEF_END_WATER_SPRINGS "0, 1; 255, 1"
#define DEF_END_LAVA_SPRINGS "0, 1; 255, 1"
+#define DEF_ANIMAL_SPAWN_PERCENT 10
+#define DEF_NO_ANIMALS 0
@@ -66,7 +67,7 @@ void cFinishGenNetherClumpFoliage::GenFinish(cChunkDesc & a_ChunkDesc)
{
continue;
}
-
+
// Choose what block to use.
NOISE_DATATYPE BlockType = m_Noise.IntNoise3D((int) ChunkX, y, (int) ChunkZ);
if (BlockType < -0.7)
@@ -196,10 +197,15 @@ void cFinishGenTallGrass::GenFinish(cChunkDesc & a_ChunkDesc)
{
continue;
}
-
+
// Get the top block + 1. This is the place where the grass would finaly be placed:
int y = a_ChunkDesc.GetHeight(x, z) + 1;
+ if (y >= 255)
+ {
+ continue;
+ }
+
// Check if long grass can be placed:
if (
(a_ChunkDesc.GetBlockType(x, y, z) != E_BLOCK_AIR) ||
@@ -277,7 +283,7 @@ bool cFinishGenSprinkleFoliage::TryAddSugarcane(cChunkDesc & a_ChunkDesc, int a_
{
return false;
}
-
+
// All conditions met, place a sugarcane here:
a_ChunkDesc.SetBlockType(a_RelX, a_RelY + 1, a_RelZ, E_BLOCK_SUGARCANE);
return true;
@@ -290,7 +296,7 @@ bool cFinishGenSprinkleFoliage::TryAddSugarcane(cChunkDesc & a_ChunkDesc, int a_
void cFinishGenSprinkleFoliage::GenFinish(cChunkDesc & a_ChunkDesc)
{
// Generate small foliage (1-block):
-
+
// TODO: Update heightmap with 1-block-tall foliage
for (int z = 0; z < cChunkDef::Width; z++)
{
@@ -315,7 +321,7 @@ void cFinishGenSprinkleFoliage::GenFinish(cChunkDesc & a_ChunkDesc)
// WEIRD, since we're using heightmap, so there should NOT be anything above it
continue;
}
-
+
const float xx = (float)BlockX;
float val1 = m_Noise.CubicNoise2D(xx * 0.1f, zz * 0.1f);
float val2 = m_Noise.CubicNoise2D(xx * 0.01f, zz * 0.01f);
@@ -355,7 +361,7 @@ void cFinishGenSprinkleFoliage::GenFinish(cChunkDesc & a_ChunkDesc)
}
break;
} // case E_BLOCK_GRASS
-
+
case E_BLOCK_SAND:
{
int y = Top + 1;
@@ -366,7 +372,8 @@ void cFinishGenSprinkleFoliage::GenFinish(cChunkDesc & a_ChunkDesc)
(a_ChunkDesc.GetBlockType(x + 1, y, z) == E_BLOCK_AIR) &&
(a_ChunkDesc.GetBlockType(x - 1, y, z) == E_BLOCK_AIR) &&
(a_ChunkDesc.GetBlockType(x, y, z + 1) == E_BLOCK_AIR) &&
- (a_ChunkDesc.GetBlockType(x, y, z - 1) == E_BLOCK_AIR)
+ (a_ChunkDesc.GetBlockType(x, y, z - 1) == E_BLOCK_AIR) &&
+ IsDesertVariant(a_ChunkDesc.GetBiome(x, z))
)
{
a_ChunkDesc.SetBlockType(x, ++Top, z, E_BLOCK_CACTUS);
@@ -387,6 +394,72 @@ void cFinishGenSprinkleFoliage::GenFinish(cChunkDesc & a_ChunkDesc)
+bool cFinishGenSprinkleFoliage::IsDesertVariant(EMCSBiome a_Biome)
+{
+ return
+ (
+ (a_Biome == biDesertHills) ||
+ (a_Biome == biDesert) ||
+ (a_Biome == biDesertM)
+ );
+}
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// cFinishGenSoulsandRims
+
+void cFinishGenSoulsandRims::GenFinish(cChunkDesc & a_ChunkDesc)
+{
+ int ChunkX = a_ChunkDesc.GetChunkX() * cChunkDef::Width;
+ int ChunkZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width;
+ HEIGHTTYPE MaxHeight = a_ChunkDesc.GetMaxHeight();
+
+ for (int x = 0; x < 16; x++)
+ {
+ int xx = ChunkX + x;
+ for (int z = 0; z < 16; z++)
+ {
+ int zz = ChunkZ + z;
+
+ // Place soulsand rims when netherrack gets thin
+ for (int y = 2; y < MaxHeight - 2; y++)
+ {
+ // The current block is air. Let's bail ut.
+ BLOCKTYPE Block = a_ChunkDesc.GetBlockType(x, y, z);
+ if (Block == E_BLOCK_AIR)
+ {
+ continue;
+ }
+
+ if (
+ ((a_ChunkDesc.GetBlockType(x, y + 1, z) != E_BLOCK_AIR) &&
+ ( a_ChunkDesc.GetBlockType(x, y + 2, z) != E_BLOCK_AIR)) ||
+ ((a_ChunkDesc.GetBlockType(x, y - 1, z) != E_BLOCK_AIR) &&
+ ( a_ChunkDesc.GetBlockType(x, y - 2, z) != E_BLOCK_AIR))
+ )
+ {
+ continue;
+ }
+
+ NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(xx)) / 32;
+ NOISE_DATATYPE NoiseY = ((NOISE_DATATYPE)(zz)) / 32;
+ NOISE_DATATYPE CompBlock = m_Noise.CubicNoise3D(NoiseX, (float) (y) / 4, NoiseY);
+ if (CompBlock < 0)
+ {
+ a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_SOULSAND);
+ }
+ }
+ }
+ }
+}
+
+
+
+
+
////////////////////////////////////////////////////////////////////////////////
// cFinishGenSnow:
@@ -407,13 +480,18 @@ void cFinishGenSnow::GenFinish(cChunkDesc & a_ChunkDesc)
case biFrozenOcean:
{
int Height = a_ChunkDesc.GetHeight(x, z);
- if (cBlockInfo::IsSnowable(a_ChunkDesc.GetBlockType(x, Height, z)))
+ if (cBlockInfo::IsSnowable(a_ChunkDesc.GetBlockType(x, Height, z)) && (Height < cChunkDef::Height - 1))
{
a_ChunkDesc.SetBlockType(x, Height + 1, z, E_BLOCK_SNOW);
a_ChunkDesc.SetHeight(x, z, Height + 1);
}
break;
}
+ default:
+ {
+ // There's no snow in the other biomes.
+ break;
+ }
}
}
} // for z
@@ -454,6 +532,11 @@ void cFinishGenIce::GenFinish(cChunkDesc & a_ChunkDesc)
}
break;
}
+ default:
+ {
+ // No icy water in other biomes.
+ break;
+ }
}
}
} // for z
@@ -502,7 +585,7 @@ void cFinishGenSingleTopBlock::GenFinish(cChunkDesc & a_ChunkDesc)
}
int Height = a_ChunkDesc.GetHeight(x, z);
- if (Height >= cChunkDef::Height)
+ if (Height >= cChunkDef::Height - 1)
{
// Too high up
continue;
@@ -702,7 +785,7 @@ void cFinishGenPreSimulator::StationarizeFluid(
} // for y
} // for x
} // for z
-
+
// Turn fluid at the chunk edges into non-stationary fluid:
for (int y = 0; y < cChunkDef::Height; y++)
{
@@ -794,12 +877,12 @@ void cFinishGenFluidSprings::GenFinish(cChunkDesc & a_ChunkDesc)
// Not in this chunk
return;
}
-
+
// Get the height at which to try:
int Height = m_Noise.IntNoise3DInt(128 * a_ChunkDesc.GetChunkX(), 1024, 256 * a_ChunkDesc.GetChunkZ()) / 11;
Height %= m_HeightDistribution.GetSum();
Height = m_HeightDistribution.MapValue(Height);
-
+
// Try adding the spring at the height, if unsuccessful, move lower:
for (int y = Height; y > 1; y--)
{
@@ -837,7 +920,7 @@ bool cFinishGenFluidSprings::TryPlaceSpring(cChunkDesc & a_ChunkDesc, int x, int
{
return false;
}
-
+
static const struct
{
int x, y, z;
@@ -868,7 +951,7 @@ bool cFinishGenFluidSprings::TryPlaceSpring(cChunkDesc & a_ChunkDesc, int x, int
{
return false;
}
-
+
// Has exactly one air neighbor, place a spring:
a_ChunkDesc.SetBlockTypeMeta(x, y, z, m_Fluid, 0);
return true;
@@ -877,3 +960,236 @@ bool cFinishGenFluidSprings::TryPlaceSpring(cChunkDesc & a_ChunkDesc, int x, int
+
+////////////////////////////////////////////////////////////////////////////////
+// cFinishGenPassiveMobs:
+
+cFinishGenPassiveMobs::cFinishGenPassiveMobs(int a_Seed, cIniFile & a_IniFile, eDimension a_Dimension) :
+ m_Noise(a_Seed)
+{
+ AString SectionName = "Animals";
+ int DefaultAnimalSpawnChunkPercentage = DEF_ANIMAL_SPAWN_PERCENT;
+ switch (a_Dimension)
+ {
+ case dimOverworld:
+ {
+ DefaultAnimalSpawnChunkPercentage = DEF_ANIMAL_SPAWN_PERCENT;
+ break;
+ }
+ case dimNether:
+ case dimEnd: // No nether or end animals (currently)
+ {
+ DefaultAnimalSpawnChunkPercentage = DEF_NO_ANIMALS;
+ break;
+ }
+ default:
+ {
+ ASSERT(!"Unhandled world dimension");
+ DefaultAnimalSpawnChunkPercentage = DEF_NO_ANIMALS;
+ break;
+ }
+ } // switch (dimension)
+ m_AnimalProbability = a_IniFile.GetValueSetI(SectionName, "AnimalSpawnChunkPercentage", DefaultAnimalSpawnChunkPercentage);
+ if ((m_AnimalProbability < 0) || (m_AnimalProbability > 100))
+ {
+ LOGWARNING("[Animals]: AnimalSpawnChunkPercentage is invalid, using the default of \"%d\".", DefaultAnimalSpawnChunkPercentage);
+ m_AnimalProbability = DefaultAnimalSpawnChunkPercentage;
+ }
+}
+
+
+
+
+
+void cFinishGenPassiveMobs::GenFinish(cChunkDesc & a_ChunkDesc)
+{
+ int chunkX = a_ChunkDesc.GetChunkX();
+ int chunkZ = a_ChunkDesc.GetChunkZ();
+ int ChanceRnd = (m_Noise.IntNoise2DInt(chunkX, chunkZ) / 7) % 100;
+ if (ChanceRnd > m_AnimalProbability)
+ {
+ return;
+ }
+
+ eMonsterType RandomMob = GetRandomMob(a_ChunkDesc);
+ if (RandomMob == mtInvalidType)
+ {
+ // No mobs here. Don't send an error, because if the biome was a desert it would return mtInvalidType as well.
+ return;
+ }
+
+ // Try spawning a pack center 10 times, should get roughly the same probability
+ for (int Tries = 0; Tries < 10; Tries++)
+ {
+ int PackCenterX = (m_Noise.IntNoise2DInt(chunkX + chunkZ, Tries) / 7) % cChunkDef::Width;
+ int PackCenterZ = (m_Noise.IntNoise2DInt(chunkX, chunkZ + Tries) / 7) % cChunkDef::Width;
+ if (TrySpawnAnimals(a_ChunkDesc, PackCenterX, a_ChunkDesc.GetHeight(PackCenterX, PackCenterZ), PackCenterZ, RandomMob))
+ {
+ for (int i = 0; i < 3; i++)
+ {
+ int OffsetX = (m_Noise.IntNoise2DInt(chunkX + chunkZ + i, Tries) / 7) % cChunkDef::Width;
+ int OffsetZ = (m_Noise.IntNoise2DInt(chunkX, chunkZ + Tries + i) / 7) % cChunkDef::Width;
+ TrySpawnAnimals(a_ChunkDesc, OffsetX, a_ChunkDesc.GetHeight(OffsetX, OffsetZ), OffsetZ, RandomMob);
+ }
+ return;
+
+ } // if pack center spawn successful
+ } // for tries
+}
+
+
+
+
+
+bool cFinishGenPassiveMobs::TrySpawnAnimals(cChunkDesc & a_ChunkDesc, int a_RelX, int a_RelY, int a_RelZ, eMonsterType AnimalToSpawn)
+{
+ if ((a_RelY >= cChunkDef::Height - 1) || (a_RelY <= 0))
+ {
+ return false;
+ }
+
+ BLOCKTYPE BlockAtHead = a_ChunkDesc.GetBlockType(a_RelX, a_RelY + 1, a_RelZ);
+ BLOCKTYPE BlockAtFeet = a_ChunkDesc.GetBlockType(a_RelX, a_RelY, a_RelZ);
+ BLOCKTYPE BlockUnderFeet = a_ChunkDesc.GetBlockType(a_RelX, a_RelY - 1, a_RelZ);
+
+ // Check block below (opaque, grass, water), and above (air)
+ if ((AnimalToSpawn == mtSquid) && (BlockAtFeet != E_BLOCK_WATER))
+ {
+ return false;
+ }
+ if (
+ (AnimalToSpawn != mtSquid) &&
+ (BlockAtHead != E_BLOCK_AIR) &&
+ (BlockAtFeet != E_BLOCK_AIR) &&
+ (!cBlockInfo::IsTransparent(BlockUnderFeet))
+ )
+ {
+ return false;
+ }
+ if (
+ (BlockUnderFeet != E_BLOCK_GRASS) &&
+ ((AnimalToSpawn == mtSheep) || (AnimalToSpawn == mtChicken) || (AnimalToSpawn == mtPig))
+ )
+ {
+ return false;
+ }
+ if ((AnimalToSpawn == mtMooshroom) && (BlockUnderFeet != E_BLOCK_MYCELIUM))
+ {
+ return false;
+ }
+
+ double AnimalX = static_cast<double>(a_ChunkDesc.GetChunkX() * cChunkDef::Width + a_RelX + 0.5);
+ double AnimalY = a_RelY;
+ double AnimalZ = static_cast<double>(a_ChunkDesc.GetChunkZ() * cChunkDef::Width + a_RelZ + 0.5);
+
+ cMonster * NewMob = cMonster::NewMonsterFromType(AnimalToSpawn);
+ NewMob->SetPosition(AnimalX, AnimalY, AnimalZ);
+ a_ChunkDesc.GetEntities().push_back(NewMob);
+ LOGD("Spawning %s #%i at {%.02f, %.02f, %.02f}", NewMob->GetClass(), NewMob->GetUniqueID(), AnimalX, AnimalY, AnimalZ);
+
+ return true;
+}
+
+
+
+
+
+eMonsterType cFinishGenPassiveMobs::GetRandomMob(cChunkDesc & a_ChunkDesc)
+{
+
+ std::set<eMonsterType> ListOfSpawnables;
+ int chunkX = a_ChunkDesc.GetChunkX();
+ int chunkZ = a_ChunkDesc.GetChunkZ();
+ int x = (m_Noise.IntNoise2DInt(chunkX, chunkZ + 10) / 7) % cChunkDef::Width;
+ int z = (m_Noise.IntNoise2DInt(chunkX + chunkZ, chunkZ) / 7) % cChunkDef::Width;
+
+ // Check biomes first to get a list of animals
+ switch (a_ChunkDesc.GetBiome(x, z))
+ {
+ // No animals in deserts or non-overworld dimensions
+ case biNether:
+ case biEnd:
+ case biDesertHills:
+ case biDesert:
+ case biDesertM:
+ {
+ return mtInvalidType;
+ }
+
+ // Mooshroom only - no other mobs on mushroom islands
+ case biMushroomIsland:
+ case biMushroomShore:
+ {
+ return mtMooshroom;
+ }
+
+ // Add squid in ocean biomes
+ case biOcean:
+ case biFrozenOcean:
+ case biFrozenRiver:
+ case biRiver:
+ case biDeepOcean:
+ {
+ ListOfSpawnables.insert(mtSquid);
+ break;
+ }
+
+ // Add ocelots in jungle biomes
+ case biJungle:
+ case biJungleHills:
+ case biJungleEdge:
+ case biJungleM:
+ case biJungleEdgeM:
+ {
+ ListOfSpawnables.insert(mtOcelot);
+ break;
+ }
+
+ // Add horses in plains-like biomes
+ case biPlains:
+ case biSunflowerPlains:
+ case biSavanna:
+ case biSavannaPlateau:
+ case biSavannaM:
+ case biSavannaPlateauM:
+ {
+ ListOfSpawnables.insert(mtHorse);
+ break;
+ }
+
+ // Add wolves in forest and spruce forests
+ case biForest:
+ case biTaiga:
+ case biMegaTaiga:
+ case biColdTaiga:
+ case biColdTaigaM:
+ {
+ ListOfSpawnables.insert(mtWolf);
+ break;
+ }
+ // Nothing special about this biome
+ default:
+ {
+ break;
+ }
+ }
+ ListOfSpawnables.insert(mtChicken);
+ ListOfSpawnables.insert(mtCow);
+ ListOfSpawnables.insert(mtPig);
+ ListOfSpawnables.insert(mtSheep);
+
+ if (ListOfSpawnables.empty())
+ {
+ return mtInvalidType;
+ }
+
+ int RandMob = (m_Noise.IntNoise2DInt(chunkX - chunkZ + 2, chunkX + 5) / 7) % ListOfSpawnables.size();
+ auto MobIter = ListOfSpawnables.begin();
+ std::advance(MobIter, RandMob);
+
+ return *MobIter;
+}
+
+
+
+
diff --git a/src/Generating/FinishGen.h b/src/Generating/FinishGen.h
index 4a08d70c8..ae6dee590 100644
--- a/src/Generating/FinishGen.h
+++ b/src/Generating/FinishGen.h
@@ -16,8 +16,9 @@
#include "ComposableGenerator.h"
-#include "../Noise.h"
+#include "../Noise/Noise.h"
#include "../ProbabDistrib.h"
+#include "../Mobs/Monster.h"
@@ -117,19 +118,41 @@ protected:
+class cFinishGenSoulsandRims :
+ public cFinishGen
+{
+public:
+ cFinishGenSoulsandRims(int a_Seed) :
+ m_Noise(a_Seed)
+ {
+ }
+
+protected:
+ cNoise m_Noise;
+
+ virtual void GenFinish(cChunkDesc & a_ChunkDesc) override;
+} ;
+
+
+
+
+
class cFinishGenSprinkleFoliage :
public cFinishGen
{
public:
cFinishGenSprinkleFoliage(int a_Seed) : m_Noise(a_Seed), m_Seed(a_Seed) {}
-
+
protected:
cNoise m_Noise;
int m_Seed;
-
+
/// Tries to place sugarcane at the coords specified, returns true if successful
bool TryAddSugarcane(cChunkDesc & a_ChunkDesc, int a_RelX, int a_RelY, int a_RelZ);
-
+
+ // Returns true is the specified biome is a desert or its variant
+ static bool IsDesertVariant(EMCSBiome a_biome);
+
// cFinishGen override:
virtual void GenFinish(cChunkDesc & a_ChunkDesc) override;
} ;
@@ -167,31 +190,31 @@ public:
{
m_IsAllowedBelow[idx] = false;
}
-
+
// Load the allowed blocks into m_IsAllowedBelow
for (BlockList::iterator itr = a_AllowedBelow.begin(); itr != a_AllowedBelow.end(); ++itr)
{
m_IsAllowedBelow[*itr] = true;
}
-
+
// Initialize all the biome types.
for (size_t idx = 0; idx < ARRAYCOUNT(m_IsBiomeAllowed); ++idx)
{
m_IsBiomeAllowed[idx] = false;
}
-
+
// Load the allowed biomes into m_IsBiomeAllowed
for (BiomeList::iterator itr = a_Biomes.begin(); itr != a_Biomes.end(); ++itr)
{
m_IsBiomeAllowed[*itr] = true;
}
}
-
+
protected:
cNoise m_Noise;
BLOCKTYPE m_BlockType;
int m_Amount; ///< Relative amount of blocks to try adding. 1 = one block per 256 biome columns.
-
+
int GetNumToGen(const cChunkDef::BiomeMap & a_BiomeMap);
// Returns true if the given biome is a biome that is allowed.
@@ -206,7 +229,7 @@ protected:
return m_IsAllowedBelow[a_BlockBelow];
}
-
+
// cFinishGen override:
virtual void GenFinish(cChunkDesc & a_ChunkDesc) override;
} ;
@@ -223,11 +246,11 @@ public:
m_Level(a_Level)
{
}
-
+
int GetLevel(void) const { return m_Level; }
protected:
int m_Level;
-
+
// cFinishGen override:
virtual void GenFinish(cChunkDesc & a_ChunkDesc) override;
} ;
@@ -241,7 +264,7 @@ class cFinishGenPreSimulator :
{
public:
cFinishGenPreSimulator(bool a_PreSimulateFallingBlocks, bool a_PreSimulateWater, bool a_PreSimulateLava);
-
+
protected:
bool m_PreSimulateFallingBlocks;
@@ -253,7 +276,7 @@ protected:
cChunkDef::BlockTypes & a_BlockTypes, // Block types to read and change
cChunkDef::HeightMap & a_HeightMap // Height map to update by the current data
);
-
+
/** For each fluid block:
- if all surroundings are of the same fluid, makes it stationary; otherwise makes it flowing (excl. top)
- all fluid on the chunk's edge is made flowing
@@ -278,7 +301,7 @@ class cFinishGenFluidSprings :
{
public:
cFinishGenFluidSprings(int a_Seed, BLOCKTYPE a_Fluid, cIniFile & a_IniFile, eDimension a_Dimension);
-
+
protected:
cNoise m_Noise;
@@ -289,10 +312,43 @@ protected:
// cFinishGen override:
virtual void GenFinish(cChunkDesc & a_ChunkDesc) override;
- /// Tries to place a spring at the specified coords, checks neighbors. Returns true if successful
+ /** Tries to place a spring at the specified coords, checks neighbors. Returns true if successful. */
bool TryPlaceSpring(cChunkDesc & a_ChunkDesc, int x, int y, int z);
} ;
+
+/** This class populates generated chunks with packs of biome-dependant animals
+Animals: cows, sheep, pigs, mooshrooms, squid, horses, wolves, ocelots */
+class cFinishGenPassiveMobs :
+ public cFinishGen
+{
+public:
+
+ cFinishGenPassiveMobs(int a_Seed, cIniFile & a_IniFile, eDimension a_Dimension);
+
+protected:
+
+ /** The noise used as the source of randomness */
+ cNoise m_Noise;
+
+ /** Chance, [0..100], that an animal pack will be generated in a chunk */
+ int m_AnimalProbability;
+
+
+ // cFinishGen override:
+ virtual void GenFinish(cChunkDesc & a_ChunkDesc) override;
+
+ /** Returns false if an animal cannot spawn at given coords, else adds it to the chunk's entity list and returns true */
+ bool TrySpawnAnimals(cChunkDesc & a_ChunkDesc, int x, int y, int z, eMonsterType AnimalToSpawn);
+
+ /** Picks a random animal from biome-dependant list for a random position in the chunk.
+ Returns the chosen mob type, or mtInvalid if no mob chosen. */
+ eMonsterType GetRandomMob(cChunkDesc & a_ChunkDesc);
+} ;
+
+
+
+
diff --git a/src/Generating/GridStructGen.h b/src/Generating/GridStructGen.h
index 03131fce9..b92fb2e9d 100644
--- a/src/Generating/GridStructGen.h
+++ b/src/Generating/GridStructGen.h
@@ -10,7 +10,7 @@
#pragma once
#include "ComposableGenerator.h"
-#include "../Noise.h"
+#include "../Noise/Noise.h"
diff --git a/src/Generating/HeiGen.cpp b/src/Generating/HeiGen.cpp
index a0b8770f5..61d087c17 100644
--- a/src/Generating/HeiGen.cpp
+++ b/src/Generating/HeiGen.cpp
@@ -15,82 +15,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:
@@ -208,15 +132,6 @@ void cHeiGenCache::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap
-void cHeiGenCache::InitializeHeightGen(cIniFile & a_IniFile)
-{
- m_HeiGenToCache->InitializeHeightGen(a_IniFile);
-}
-
-
-
-
-
bool cHeiGenCache::GetHeightAt(int a_ChunkX, int a_ChunkZ, int a_RelX, int a_RelZ, HEIGHTTYPE & a_Height)
{
for (int i = 0; i < m_CacheSize; i++)
@@ -235,6 +150,51 @@ bool cHeiGenCache::GetHeightAt(int a_ChunkX, int a_ChunkZ, int a_RelX, int a_Rel
////////////////////////////////////////////////////////////////////////////////
+// cHeiGenMultiCache:
+
+cHeiGenMultiCache::cHeiGenMultiCache(cTerrainHeightGenPtr a_HeiGenToCache, size_t a_SubCacheSize, size_t a_NumSubCaches):
+ m_NumSubCaches(a_NumSubCaches)
+{
+ // Create the individual sub-caches:
+ m_SubCaches.reserve(a_NumSubCaches);
+ for (size_t i = 0; i < a_NumSubCaches; i++)
+ {
+ m_SubCaches.push_back(std::make_shared<cHeiGenCache>(a_HeiGenToCache, a_SubCacheSize));
+ }
+}
+
+
+
+
+
+void cHeiGenMultiCache::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap)
+{
+ // Get the subcache responsible for this chunk:
+ const size_t cacheIdx = ((size_t)a_ChunkX + m_CoeffZ * (size_t)a_ChunkZ) % m_NumSubCaches;
+
+ // Ask the subcache:
+ m_SubCaches[cacheIdx]->GenHeightMap(a_ChunkX, a_ChunkZ, a_HeightMap);
+}
+
+
+
+
+
+bool cHeiGenMultiCache::GetHeightAt(int a_ChunkX, int a_ChunkZ, int a_RelX, int a_RelZ, HEIGHTTYPE & a_Height)
+{
+ // Get the subcache responsible for this chunk:
+ const size_t cacheIdx = ((size_t)a_ChunkX + m_CoeffZ * (size_t)a_ChunkZ) % m_NumSubCaches;
+
+ // Ask the subcache:
+ return m_SubCaches[cacheIdx]->GetHeightAt(a_ChunkX, a_ChunkZ, a_RelX, a_RelZ, a_Height);
+}
+
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
// cHeiGenClassic:
cHeiGenClassic::cHeiGenClassic(int a_Seed) :
@@ -430,15 +390,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 +408,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 +571,294 @@ 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;
+ cTerrainHeightGenPtr res;
+ if (NoCaseCompare(HeightGenName, "Flat") == 0)
+ {
+ res = std::make_shared<cHeiGenFlat>();
+ a_CacheOffByDefault = true; // We're generating faster than a cache would retrieve data
+ }
+ else if (NoCaseCompare(HeightGenName, "classic") == 0)
+ {
+ res = std::make_shared<cHeiGenClassic>(a_Seed);
+ }
+ else if (NoCaseCompare(HeightGenName, "DistortedHeightmap") == 0)
+ {
+ // Not a heightmap-based generator, but it used to be accessible via HeightGen, so we need to skip making the default out of it
+ // Return an empty pointer, the caller will create the proper generator:
+ return cTerrainHeightGenPtr();
+ }
+ else if (NoCaseCompare(HeightGenName, "End") == 0)
+ {
+ // Not a heightmap-based generator, but it used to be accessible via HeightGen, so we need to skip making the default out of it
+ // Return an empty pointer, the caller will create the proper generator:
+ return cTerrainHeightGenPtr();
+ }
+ else if (NoCaseCompare(HeightGenName, "MinMax") == 0)
+ {
+ res = std::make_shared<cHeiGenMinMax>(a_Seed, a_BiomeGen);
+ }
+ else if (NoCaseCompare(HeightGenName, "Mountains") == 0)
+ {
+ res = std::make_shared<cHeiGenMountains>(a_Seed);
+ }
+ else if (NoCaseCompare(HeightGenName, "BiomalNoise3D") == 0)
+ {
+ // Not a heightmap-based generator, but it used to be accessible via HeightGen, so we need to skip making the default out of it
+ // Return an empty pointer, the caller will create the proper generator:
+ return cTerrainHeightGenPtr();
+ }
+ else if (NoCaseCompare(HeightGenName, "Noise3D") == 0)
+ {
+ // Not a heightmap-based generator, but it used to be accessible via HeightGen, so we need to skip making the default out of it
+ // Return an empty pointer, the caller will create the proper generator:
+ return cTerrainHeightGenPtr();
+ }
+ else if (NoCaseCompare(HeightGenName, "Biomal") == 0)
+ {
+ res = std::make_shared<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.SetValue("Generator", "HeightGen", "Biomal");
+ return CreateHeightGen(a_IniFile, a_BiomeGen, a_Seed, a_CacheOffByDefault);
+ }
+
+ // Read the settings:
+ res->InitializeHeightGen(a_IniFile);
+
+ return res;
+}
+
+
+
+
+
diff --git a/src/Generating/HeiGen.h b/src/Generating/HeiGen.h
index 6ae5ba362..62bb227c6 100644
--- a/src/Generating/HeiGen.h
+++ b/src/Generating/HeiGen.h
@@ -2,10 +2,12 @@
// HeiGen.h
/*
-Interfaces to the various height generators:
+Interfaces to the various height-based terrain shape generators:
- cHeiGenFlat
- cHeiGenClassic
- cHeiGenBiomal
+
+Also implements the heightmap cache
*/
@@ -15,32 +17,13 @@ Interfaces to the various height generators:
#pragma once
#include "ComposableGenerator.h"
-#include "../Noise.h"
-
-
-
-
-
-class cHeiGenFlat :
- public cTerrainHeightGen
-{
-public:
- cHeiGenFlat(void) : m_Height(5) {}
-
-protected:
-
- int m_Height;
-
- // cTerrainHeightGen overrides:
- virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override;
- virtual void InitializeHeightGen(cIniFile & a_IniFile) override;
-} ;
+#include "../Noise/Noise.h"
-/// A simple cache that stores N most recently generated chunks' heightmaps; N being settable upon creation
+/** A simple cache that stores N most recently generated chunks' heightmaps; N being settable upon creation */
class cHeiGenCache :
public cTerrainHeightGen
{
@@ -50,15 +33,11 @@ public:
// cTerrainHeightGen overrides:
virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override;
- virtual void InitializeHeightGen(cIniFile & a_IniFile) override;
- /// Retrieves height at the specified point in the cache, returns true if found, false if not found
+ /** Retrieves height at the specified point in the cache, returns true if found, false if not found */
bool GetHeightAt(int a_ChunkX, int a_ChunkZ, int a_RelX, int a_RelZ, HEIGHTTYPE & a_Height);
protected:
-
- cTerrainHeightGenPtr m_HeiGenToCache;
-
struct sCacheData
{
int m_ChunkX;
@@ -66,6 +45,9 @@ protected:
cChunkDef::HeightMap m_HeightMap;
} ;
+ /** The terrain height generator that is being cached. */
+ cTerrainHeightGenPtr m_HeiGenToCache;
+
// To avoid moving large amounts of data for the MRU behavior, we MRU-ize indices to an array of the actual data
int m_CacheSize;
int * m_CacheOrder; // MRU-ized order, indices into m_CacheData array
@@ -81,6 +63,57 @@ protected:
+/** Caches heightmaps in multiple underlying caches to improve the distribution and lower the chain length. */
+class cHeiGenMultiCache:
+ public cTerrainHeightGen
+{
+public:
+ cHeiGenMultiCache(cTerrainHeightGenPtr a_HeightGenToCache, size_t a_SubCacheSize, size_t a_NumSubCaches);
+
+ // cTerrainHeightGen overrides:
+ virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override;
+
+ /** Retrieves height at the specified point in the cache, returns true if found, false if not found */
+ bool GetHeightAt(int a_ChunkX, int a_ChunkZ, int a_RelX, int a_RelZ, HEIGHTTYPE & a_Height);
+
+protected:
+ typedef SharedPtr<cHeiGenCache> cHeiGenCachePtr;
+ typedef std::vector<cHeiGenCachePtr> cHeiGenCachePtrs;
+
+
+ /** The coefficient used to turn Z coords into index (x + Coeff * z). */
+ static const size_t m_CoeffZ = 5;
+
+ /** Number of sub-caches, pulled out of m_SubCaches.size() for performance reasons. */
+ size_t m_NumSubCaches;
+
+ /** The individual sub-caches. */
+ cHeiGenCachePtrs m_SubCaches;
+};
+
+
+
+
+
+class cHeiGenFlat :
+ public cTerrainHeightGen
+{
+public:
+ cHeiGenFlat(void) : m_Height(5) {}
+
+protected:
+
+ int m_Height;
+
+ // cTerrainHeightGen overrides:
+ virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override;
+ virtual void InitializeHeightGen(cIniFile & a_IniFile) override;
+} ;
+
+
+
+
+
class cHeiGenClassic :
public cTerrainHeightGen
{
@@ -137,7 +170,11 @@ public:
m_BiomeGen(a_BiomeGen)
{
}
-
+
+ // cTerrainHeightGen overrides:
+ virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override;
+ virtual void InitializeHeightGen(cIniFile & a_IniFile) override;
+
protected:
typedef cChunkDef::BiomeMap BiomeNeighbors[3][3];
@@ -154,11 +191,8 @@ protected:
float m_BaseHeight;
} ;
static const sGenParam m_GenParam[256];
-
- // cTerrainHeightGen overrides:
- virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override;
- virtual void InitializeHeightGen(cIniFile & a_IniFile) override;
-
+
+
NOISE_DATATYPE GetHeightAt(int a_RelX, int a_RelZ, int a_ChunkX, int a_ChunkZ, const BiomeNeighbors & a_BiomeNeighbors);
} ;
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/MineShafts.cpp b/src/Generating/MineShafts.cpp
index 55b3b64dd..65588ce4b 100644
--- a/src/Generating/MineShafts.cpp
+++ b/src/Generating/MineShafts.cpp
@@ -20,6 +20,7 @@ in a depth-first processing. Each of the descendants will branch randomly, if no
#include "MineShafts.h"
#include "../Cuboid.h"
#include "../BlockEntities/ChestEntity.h"
+#include "../BlockEntities/MobSpawnerEntity.h"
@@ -875,7 +876,9 @@ void cMineShaftCorridor::PlaceSpawner(cChunkDesc & a_ChunkDesc)
)
{
a_ChunkDesc.SetBlockTypeMeta(SpawnerRelX, m_BoundingBox.p1.y + 1, SpawnerRelZ, E_BLOCK_MOB_SPAWNER, 0);
- // TODO: The spawner needs its accompanying cMobSpawnerEntity, when implemented
+ cMobSpawnerEntity * MobSpawner = static_cast<cMobSpawnerEntity *>(a_ChunkDesc.GetBlockEntity(SpawnerRelX, m_BoundingBox.p1.y + 1, SpawnerRelZ));
+ ASSERT((MobSpawner != nullptr) && (MobSpawner->GetBlockType() == E_BLOCK_MOB_SPAWNER));
+ MobSpawner->SetEntity(mtCaveSpider);
}
}
diff --git a/src/Generating/MineShafts.h b/src/Generating/MineShafts.h
index 2850db571..efb11cfee 100644
--- a/src/Generating/MineShafts.h
+++ b/src/Generating/MineShafts.h
@@ -10,7 +10,6 @@
#pragma once
#include "GridStructGen.h"
-#include "../Noise.h"
diff --git a/src/Generating/Noise3DGenerator.cpp b/src/Generating/Noise3DGenerator.cpp
index 5a4cb44cf..b43a1a6de 100644
--- a/src/Generating/Noise3DGenerator.cpp
+++ b/src/Generating/Noise3DGenerator.cpp
@@ -61,80 +61,110 @@ public:
-////////////////////////////////////////////////////////////////////////////////
-// cNoise3DGenerator:
-
-cNoise3DGenerator::cNoise3DGenerator(cChunkGenerator & a_ChunkGenerator) :
- super(a_ChunkGenerator),
- m_Perlin(1000),
- m_Cubic(1000)
+#if 0
+// Perform speed test of the cInterpolNoise class
+static class cInterpolNoiseSpeedTest
{
- m_Perlin.AddOctave(1, (NOISE_DATATYPE)0.5);
- m_Perlin.AddOctave((NOISE_DATATYPE)0.5, 1);
- m_Perlin.AddOctave((NOISE_DATATYPE)0.5, 2);
-
- #if 0
- // DEBUG: Test the noise generation:
- // NOTE: In order to be able to run MCS with this code, you need to increase the default thread stack size
- // In MSVC, it is done in Project Settings -> Configuration Properties -> Linker -> System, set Stack reserve size to at least 64M
- m_SeaLevel = 62;
- m_HeightAmplification = 0;
- m_MidPoint = 75;
- m_FrequencyX = 4;
- m_FrequencyY = 4;
- m_FrequencyZ = 4;
- m_AirThreshold = 0.5;
-
- const int NumChunks = 4;
- NOISE_DATATYPE Noise[NumChunks][cChunkDef::Width * cChunkDef::Width * cChunkDef::Height];
- for (int x = 0; x < NumChunks; x++)
+public:
+ cInterpolNoiseSpeedTest(void)
{
- GenerateNoiseArray(x, 5, Noise[x]);
+ TestSpeed2D();
+ TestSpeed3D();
+ printf("InterpolNoise speed comparison finished.\n");
}
- // Save in XY cuts:
- cFile f1;
- if (f1.Open("Test_XY.grab", cFile::fmWrite))
+
+ /** Compare the speed of the 3D InterpolNoise vs 3D CubicNoise. */
+ void TestSpeed3D(void)
{
- for (int z = 0; z < cChunkDef::Width; z++)
+ printf("Evaluating 3D noise performance...\n");
+ static const int SIZE_X = 128;
+ static const int SIZE_Y = 128;
+ static const int SIZE_Z = 128;
+ static const NOISE_DATATYPE MUL = 80;
+ std::unique_ptr<NOISE_DATATYPE[]> arr(new NOISE_DATATYPE[SIZE_X * SIZE_Y * SIZE_Z]);
+ cTimer timer;
+
+ // Test the cInterpolNoise:
+ cInterpolNoise<Interp5Deg> interpNoise(1);
+ long long start = timer.GetNowTime();
+ for (int i = 0; i < 30; i++)
{
- for (int y = 0; y < cChunkDef::Height; y++)
- {
- for (int i = 0; i < NumChunks; i++)
- {
- int idx = y * cChunkDef::Width + z * cChunkDef::Width * cChunkDef::Height;
- unsigned char buf[cChunkDef::Width];
- for (int x = 0; x < cChunkDef::Width; x++)
- {
- buf[x] = (unsigned char)(std::min(256, std::max(0, (int)(128 + 32 * Noise[i][idx++]))));
- }
- f1.Write(buf, cChunkDef::Width);
- }
- } // for y
- } // for z
- } // if (XY file open)
+ interpNoise.Generate3D(arr.get(), SIZE_X, SIZE_Y, SIZE_Z, MUL * i, MUL * i + MUL, 0, MUL, 0, MUL);
+ }
+ long long end = timer.GetNowTime();
+ printf("InterpolNoise took %.02f sec\n", static_cast<float>(end - start) / 1000);
+
+ // Test the cCubicNoise:
+ cCubicNoise cubicNoise(1);
+ start = timer.GetNowTime();
+ for (int i = 0; i < 30; i++)
+ {
+ cubicNoise.Generate3D(arr.get(), SIZE_X, SIZE_Y, SIZE_Z, MUL * i, MUL * i + MUL, 0, MUL, 0, MUL);
+ }
+ end = timer.GetNowTime();
+ printf("CubicNoise took %.02f sec\n", static_cast<float>(end - start) / 1000);
+ printf("3D noise performance comparison finished.\n");
+ }
+
- cFile f2;
- if (f2.Open("Test_XZ.grab", cFile::fmWrite))
+ /** Compare the speed of the 2D InterpolNoise vs 2D CubicNoise. */
+ void TestSpeed2D(void)
{
- for (int y = 0; y < cChunkDef::Height; y++)
+ printf("Evaluating 2D noise performance...\n");
+ static const int SIZE_X = 128;
+ static const int SIZE_Y = 128;
+ static const NOISE_DATATYPE MUL = 80;
+ std::unique_ptr<NOISE_DATATYPE[]> arr(new NOISE_DATATYPE[SIZE_X * SIZE_Y]);
+ cTimer timer;
+
+ // Test the cInterpolNoise:
+ cInterpolNoise<Interp5Deg> interpNoise(1);
+ long long start = timer.GetNowTime();
+ for (int i = 0; i < 500; i++)
{
- for (int z = 0; z < cChunkDef::Width; z++)
- {
- for (int i = 0; i < NumChunks; i++)
- {
- int idx = y * cChunkDef::Width + z * cChunkDef::Width * cChunkDef::Height;
- unsigned char buf[cChunkDef::Width];
- for (int x = 0; x < cChunkDef::Width; x++)
- {
- buf[x] = (unsigned char)(std::min(256, std::max(0, (int)(128 + 32 * Noise[i][idx++]))));
- }
- f2.Write(buf, cChunkDef::Width);
- }
- } // for z
- } // for y
- } // if (XZ file open)
- #endif // 0
+ interpNoise.Generate2D(arr.get(), SIZE_X, SIZE_Y, MUL * i, MUL * i + MUL, 0, MUL);
+ }
+ long long end = timer.GetNowTime();
+ printf("InterpolNoise took %.02f sec\n", static_cast<float>(end - start) / 1000);
+
+ // Test the cCubicNoise:
+ cCubicNoise cubicNoise(1);
+ start = timer.GetNowTime();
+ for (int i = 0; i < 500; i++)
+ {
+ cubicNoise.Generate2D(arr.get(), SIZE_X, SIZE_Y, MUL * i, MUL * i + MUL, 0, MUL);
+ }
+ end = timer.GetNowTime();
+ printf("CubicNoise took %.02f sec\n", static_cast<float>(end - start) / 1000);
+ printf("2D noise performance comparison finished.\n");
+ }
+} g_InterpolNoiseSpeedTest;
+#endif
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// cNoise3DGenerator:
+
+cNoise3DGenerator::cNoise3DGenerator(cChunkGenerator & a_ChunkGenerator) :
+ super(a_ChunkGenerator),
+ m_Perlin(1000),
+ m_Cubic(1000)
+{
+ m_Perlin.AddOctave(1, 1);
+ m_Perlin.AddOctave(2, 0.5);
+ m_Perlin.AddOctave(4, 0.25);
+ m_Perlin.AddOctave(8, 0.125);
+ m_Perlin.AddOctave(16, 0.0625);
+
+ m_Cubic.AddOctave(1, 1);
+ m_Cubic.AddOctave(2, 0.5);
+ m_Cubic.AddOctave(4, 0.25);
+ m_Cubic.AddOctave(8, 0.125);
+ m_Cubic.AddOctave(16, 0.0625);
}
@@ -153,9 +183,9 @@ cNoise3DGenerator::~cNoise3DGenerator()
void cNoise3DGenerator::Initialize(cIniFile & a_IniFile)
{
// Params:
- m_SeaLevel = a_IniFile.GetValueSetI("Generator", "Noise3DSeaLevel", 62);
- m_HeightAmplification = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DHeightAmplification", 0);
- m_MidPoint = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DMidPoint", 75);
+ m_SeaLevel = a_IniFile.GetValueSetI("Generator", "SeaLevel", 62);
+ m_HeightAmplification = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DHeightAmplification", 0.1);
+ m_MidPoint = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DMidPoint", 68);
m_FrequencyX = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyX", 8);
m_FrequencyY = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyY", 8);
m_FrequencyZ = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyZ", 8);
@@ -220,10 +250,10 @@ void cNoise3DGenerator::GenerateNoiseArray(int a_ChunkX, int a_ChunkZ, NOISE_DAT
NOISE_DATATYPE NoiseW[DIM_X * DIM_Y * DIM_Z]; // Workspace that the noise calculation can use and trash
// Our noise array has different layout, XZY, instead of regular chunk's XYZ, that's why the coords are "renamed"
- NOISE_DATATYPE StartX = ((NOISE_DATATYPE)(a_ChunkX * cChunkDef::Width)) / m_FrequencyX;
- NOISE_DATATYPE EndX = ((NOISE_DATATYPE)((a_ChunkX + 1) * cChunkDef::Width) - 1) / m_FrequencyX;
- NOISE_DATATYPE StartZ = ((NOISE_DATATYPE)(a_ChunkZ * cChunkDef::Width)) / m_FrequencyZ;
- NOISE_DATATYPE EndZ = ((NOISE_DATATYPE)((a_ChunkZ + 1) * cChunkDef::Width) - 1) / m_FrequencyZ;
+ NOISE_DATATYPE StartX = ((NOISE_DATATYPE)(a_ChunkX * cChunkDef::Width)) / m_FrequencyX;
+ NOISE_DATATYPE EndX = ((NOISE_DATATYPE)((a_ChunkX + 1) * cChunkDef::Width)) / m_FrequencyX;
+ NOISE_DATATYPE StartZ = ((NOISE_DATATYPE)(a_ChunkZ * cChunkDef::Width)) / m_FrequencyZ;
+ NOISE_DATATYPE EndZ = ((NOISE_DATATYPE)((a_ChunkZ + 1) * cChunkDef::Width)) / m_FrequencyZ;
NOISE_DATATYPE StartY = 0;
NOISE_DATATYPE EndY = ((NOISE_DATATYPE)256) / m_FrequencyY;
@@ -233,23 +263,23 @@ void cNoise3DGenerator::GenerateNoiseArray(int a_ChunkX, int a_ChunkZ, NOISE_DAT
// Precalculate a "height" array:
NOISE_DATATYPE Height[DIM_X * DIM_Z]; // Output for the cubic noise heightmap ("source")
- m_Cubic.Generate2D(Height, DIM_X, DIM_Z, StartX / 25, EndX / 25, StartZ / 25, EndZ / 25);
+ m_Cubic.Generate2D(Height, DIM_X, DIM_Z, StartX / 5, EndX / 5, StartZ / 5, EndZ / 5);
for (size_t i = 0; i < ARRAYCOUNT(Height); i++)
{
- Height[i] = std::abs(Height[i]) * m_HeightAmplification + 1;
+ Height[i] = Height[i] * m_HeightAmplification;
}
// Modify the noise by height data:
for (int y = 0; y < DIM_Y; y++)
{
- NOISE_DATATYPE AddHeight = (y * UPSCALE_Y - m_MidPoint) / 20;
- AddHeight *= AddHeight * AddHeight;
+ NOISE_DATATYPE AddHeight = (y * UPSCALE_Y - m_MidPoint) / 30;
+ // AddHeight *= AddHeight * AddHeight;
for (int z = 0; z < DIM_Z; z++)
{
NOISE_DATATYPE * CurRow = &(NoiseO[y * DIM_X + z * DIM_X * DIM_Y]);
for (int x = 0; x < DIM_X; x++)
{
- CurRow[x] += AddHeight / Height[x + DIM_X * z];
+ CurRow[x] += AddHeight + Height[x + DIM_X * z];
}
}
}
@@ -346,9 +376,10 @@ void cNoise3DGenerator::ComposeTerrain(cChunkDesc & a_ChunkDesc)
// cNoise3DComposable:
cNoise3DComposable::cNoise3DComposable(int a_Seed) :
- m_Noise1(a_Seed + 1000),
- m_Noise2(a_Seed + 2000),
- m_Noise3(a_Seed + 3000)
+ m_ChoiceNoise(a_Seed),
+ m_DensityNoiseA(a_Seed + 1),
+ m_DensityNoiseB(a_Seed + 2),
+ m_BaseNoise(a_Seed + 3)
{
}
@@ -359,13 +390,50 @@ cNoise3DComposable::cNoise3DComposable(int a_Seed) :
void cNoise3DComposable::Initialize(cIniFile & a_IniFile)
{
// Params:
- m_SeaLevel = a_IniFile.GetValueSetI("Generator", "Noise3DSeaLevel", 62);
- m_HeightAmplification = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DHeightAmplification", 0);
+ // The defaults generate extreme hills terrain
+ m_HeightAmplification = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DHeightAmplification", 0.045);
m_MidPoint = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DMidPoint", 75);
- m_FrequencyX = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyX", 10);
- m_FrequencyY = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyY", 10);
- m_FrequencyZ = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyZ", 10);
- m_AirThreshold = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DAirThreshold", 0.5);
+ m_FrequencyX = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyX", 40);
+ m_FrequencyY = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyY", 40);
+ m_FrequencyZ = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyZ", 40);
+ m_BaseFrequencyX = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DBaseFrequencyX", 40);
+ m_BaseFrequencyZ = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DBaseFrequencyZ", 40);
+ m_ChoiceFrequencyX = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DChoiceFrequencyX", 40);
+ m_ChoiceFrequencyY = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DChoiceFrequencyY", 80);
+ m_ChoiceFrequencyZ = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DChoiceFrequencyZ", 40);
+ m_AirThreshold = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DAirThreshold", 0);
+ int NumChoiceOctaves = a_IniFile.GetValueSetI("Generator", "Noise3DNumChoiceOctaves", 4);
+ int NumDensityOctaves = a_IniFile.GetValueSetI("Generator", "Noise3DNumDensityOctaves", 6);
+ int NumBaseOctaves = a_IniFile.GetValueSetI("Generator", "Noise3DNumBaseOctaves", 6);
+ NOISE_DATATYPE BaseNoiseAmplitude = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DBaseAmplitude", 1);
+
+ // Add octaves for the choice noise:
+ NOISE_DATATYPE wavlen = 1, ampl = 0.5;
+ for (int i = 0; i < NumChoiceOctaves; i++)
+ {
+ m_ChoiceNoise.AddOctave(wavlen, ampl);
+ wavlen = wavlen * 2;
+ ampl = ampl / 2;
+ }
+
+ // Add octaves for the density noises:
+ wavlen = 1, ampl = 1;
+ for (int i = 0; i < NumDensityOctaves; i++)
+ {
+ m_DensityNoiseA.AddOctave(wavlen, ampl);
+ m_DensityNoiseB.AddOctave(wavlen, ampl);
+ wavlen = wavlen * 2;
+ ampl = ampl / 2;
+ }
+
+ // Add octaves for the base noise:
+ wavlen = 1, ampl = BaseNoiseAmplitude;
+ for (int i = 0; i < NumBaseOctaves; i++)
+ {
+ m_BaseNoise.AddOctave(wavlen, ampl);
+ wavlen = wavlen * 2;
+ ampl = ampl / 2;
+ }
}
@@ -376,142 +444,266 @@ void cNoise3DComposable::GenerateNoiseArrayIfNeeded(int a_ChunkX, int a_ChunkZ)
{
if ((a_ChunkX == m_LastChunkX) && (a_ChunkZ == m_LastChunkZ))
{
- // The noise for this chunk is already generated in m_Noise
+ // The noise for this chunk is already generated in m_NoiseArray
return;
}
m_LastChunkX = a_ChunkX;
m_LastChunkZ = a_ChunkZ;
- // Upscaling parameters:
- const int UPSCALE_X = 8;
- const int UPSCALE_Y = 4;
- const int UPSCALE_Z = 8;
-
- // Precalculate a "height" array:
- NOISE_DATATYPE Height[17 * 17]; // x + 17 * z
- for (int z = 0; z < 17; z += UPSCALE_Z)
+ // Generate all the noises:
+ NOISE_DATATYPE ChoiceNoise[5 * 5 * 33];
+ NOISE_DATATYPE Workspace[5 * 5 * 33];
+ NOISE_DATATYPE DensityNoiseA[5 * 5 * 33];
+ NOISE_DATATYPE DensityNoiseB[5 * 5 * 33];
+ NOISE_DATATYPE BaseNoise[5 * 5];
+ NOISE_DATATYPE BlockX = static_cast<NOISE_DATATYPE>(a_ChunkX * cChunkDef::Width);
+ NOISE_DATATYPE BlockZ = static_cast<NOISE_DATATYPE>(a_ChunkZ * cChunkDef::Width);
+ // Note that we have to swap the X and Y coords, because noise generator uses [x + SizeX * y + SizeX * SizeY * z] ordering and we want "BlockY" to be "x":
+ m_ChoiceNoise.Generate3D (ChoiceNoise, 33, 5, 5, 0, 257 / m_ChoiceFrequencyY, BlockX / m_ChoiceFrequencyX, (BlockX + 17) / m_ChoiceFrequencyX, BlockZ / m_ChoiceFrequencyZ, (BlockZ + 17) / m_ChoiceFrequencyZ, Workspace);
+ m_DensityNoiseA.Generate3D(DensityNoiseA, 33, 5, 5, 0, 257 / m_FrequencyY, BlockX / m_FrequencyX, (BlockX + 17) / m_FrequencyX, BlockZ / m_FrequencyZ, (BlockZ + 17) / m_FrequencyZ, Workspace);
+ m_DensityNoiseB.Generate3D(DensityNoiseB, 33, 5, 5, 0, 257 / m_FrequencyY, BlockX / m_FrequencyX, (BlockX + 17) / m_FrequencyX, BlockZ / m_FrequencyZ, (BlockZ + 17) / m_FrequencyZ, Workspace);
+ m_BaseNoise.Generate2D (BaseNoise, 5, 5, BlockX / m_BaseFrequencyX, (BlockX + 17) / m_BaseFrequencyX, BlockZ / m_FrequencyZ, (BlockZ + 17) / m_FrequencyZ, Workspace);
+
+ // Calculate the final noise based on the partial noises:
+ for (int z = 0; z < 5; z++)
{
- NOISE_DATATYPE NoiseZ = ((NOISE_DATATYPE)(a_ChunkZ * cChunkDef::Width + z)) / m_FrequencyZ;
- for (int x = 0; x < 17; x += UPSCALE_X)
+ for (int x = 0; x < 5; x++)
{
- NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(a_ChunkX * cChunkDef::Width + x)) / m_FrequencyX;
- NOISE_DATATYPE val = std::abs(m_Noise1.CubicNoise2D(NoiseX / 5, NoiseZ / 5)) * m_HeightAmplification + 1;
- Height[x + 17 * z] = val * val * val;
+ NOISE_DATATYPE curBaseNoise = BaseNoise[x + 5 * z];
+ for (int y = 0; y < 33; y++)
+ {
+ NOISE_DATATYPE AddHeight = (static_cast<NOISE_DATATYPE>(y * 8) - m_MidPoint) * m_HeightAmplification;
+
+ // If "underground", make the terrain smoother by forcing the vertical linear gradient into steeper slope:
+ if (AddHeight < 0)
+ {
+ AddHeight *= 4;
+ }
+
+ // If too high, cut off any terrain:
+ if (y > 28)
+ {
+ AddHeight = AddHeight + static_cast<NOISE_DATATYPE>(y - 28) / 4;
+ }
+
+ // Decide between the two density noises:
+ int idx = 33 * x + 33 * 5 * z + y;
+ Workspace[idx] = ClampedLerp(DensityNoiseA[idx], DensityNoiseB[idx], 8 * (ChoiceNoise[idx] + 0.5f)) + AddHeight + curBaseNoise;
+ }
}
}
+ LinearUpscale3DArray<NOISE_DATATYPE>(Workspace, 33, 5, 5, m_NoiseArray, 8, 4, 4);
+}
+
+
+
+
- for (int y = 0; y < 257; y += UPSCALE_Y)
+void cNoise3DComposable::GenShape(int a_ChunkX, int a_ChunkZ, cChunkDesc::Shape & a_Shape)
+{
+ GenerateNoiseArrayIfNeeded(a_ChunkX, a_ChunkZ);
+
+ // Translate the noise array into Shape:
+ for (int z = 0; z < cChunkDef::Width; z++)
{
- NOISE_DATATYPE NoiseY = ((NOISE_DATATYPE)y) / m_FrequencyY;
- NOISE_DATATYPE AddHeight = (y - m_MidPoint) / 20;
- AddHeight *= AddHeight * AddHeight;
- NOISE_DATATYPE * CurFloor = &(m_NoiseArray[y * 17 * 17]);
- for (int z = 0; z < 17; z += UPSCALE_Z)
+ for (int x = 0; x < cChunkDef::Width; x++)
{
- NOISE_DATATYPE NoiseZ = ((NOISE_DATATYPE)(a_ChunkZ * cChunkDef::Width + z)) / m_FrequencyZ;
- for (int x = 0; x < 17; x += UPSCALE_X)
+ for (int y = 0; y < cChunkDef::Height; y++)
{
- NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(a_ChunkX * cChunkDef::Width + x)) / m_FrequencyX;
- CurFloor[x + 17 * z] = (
- m_Noise1.CubicNoise3D(NoiseX, NoiseY, NoiseZ) * (NOISE_DATATYPE)0.5 +
- m_Noise2.CubicNoise3D(NoiseX / 2, NoiseY / 2, NoiseZ / 2) +
- m_Noise3.CubicNoise3D(NoiseX / 4, NoiseY / 4, NoiseZ / 4) * 2 +
- AddHeight / Height[x + 17 * z]
- );
+ a_Shape[y + x * 256 + z * 256 * 16] = (m_NoiseArray[y + 257 * x + 257 * 17 * z] > m_AirThreshold) ? 0 : 1;
}
- }
- // Linear-interpolate this XZ floor:
- LinearUpscale2DArrayInPlace<17, 17, UPSCALE_X, UPSCALE_Z>(CurFloor);
- }
+ } // for x
+ } // for z
+}
- // Finish the 3D linear interpolation by interpolating between each XZ-floors on the Y axis
- for (int y = 1; y < cChunkDef::Height; y++)
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// cBiomalNoise3DComposable:
+
+cBiomalNoise3DComposable::cBiomalNoise3DComposable(int a_Seed, cBiomeGenPtr a_BiomeGen) :
+ m_ChoiceNoise(a_Seed),
+ m_DensityNoiseA(a_Seed + 1),
+ m_DensityNoiseB(a_Seed + 2),
+ m_BaseNoise(a_Seed + 3),
+ m_BiomeGen(a_BiomeGen),
+ m_LastChunkX(0x7fffffff), // Set impossible coords for the chunk so that it's always considered stale
+ m_LastChunkZ(0x7fffffff)
+{
+ // Generate the weight distribution for summing up neighboring biomes:
+ m_WeightSum = 0;
+ for (int z = 0; z <= AVERAGING_SIZE * 2; z++)
{
- if ((y % UPSCALE_Y) == 0)
- {
- // This is the interpolation source floor, already calculated
- continue;
- }
- int LoFloorY = (y / UPSCALE_Y) * UPSCALE_Y;
- int HiFloorY = LoFloorY + UPSCALE_Y;
- NOISE_DATATYPE * LoFloor = &(m_NoiseArray[LoFloorY * 17 * 17]);
- NOISE_DATATYPE * HiFloor = &(m_NoiseArray[HiFloorY * 17 * 17]);
- NOISE_DATATYPE * CurFloor = &(m_NoiseArray[y * 17 * 17]);
- NOISE_DATATYPE Ratio = ((NOISE_DATATYPE)(y % UPSCALE_Y)) / UPSCALE_Y;
- int idx = 0;
- for (int z = 0; z < cChunkDef::Width; z++)
+ for (int x = 0; x <= AVERAGING_SIZE * 2; x++)
{
- for (int x = 0; x < cChunkDef::Width; x++)
- {
- CurFloor[idx] = LoFloor[idx] + (HiFloor[idx] - LoFloor[idx]) * Ratio;
- idx += 1;
- }
- idx += 1; // Skipping one X column
+ m_Weight[z][x] = static_cast<NOISE_DATATYPE>((AVERAGING_SIZE - std::abs(AVERAGING_SIZE - x)) + (AVERAGING_SIZE - std::abs(AVERAGING_SIZE - z)));
+ m_WeightSum += m_Weight[z][x];
}
}
+}
+
+
+
+
- // The noise array is now fully interpolated
- /*
- // DEBUG: Output two images of the array, sliced by XY and XZ:
- cFile f1;
- if (f1.Open(Printf("Chunk_%d_%d_XY.raw", a_ChunkX, a_ChunkZ), cFile::fmWrite))
+void cBiomalNoise3DComposable::Initialize(cIniFile & a_IniFile)
+{
+ // Params:
+ // The defaults generate extreme hills terrain
+ m_SeaLevel = a_IniFile.GetValueSetI("Generator", "SeaLevel", 62);
+ m_FrequencyX = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "BiomalNoise3DFrequencyX", 40);
+ m_FrequencyY = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "BiomalNoise3DFrequencyY", 40);
+ m_FrequencyZ = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "BiomalNoise3DFrequencyZ", 40);
+ m_BaseFrequencyX = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "BiomalNoise3DBaseFrequencyX", 40);
+ m_BaseFrequencyZ = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "BiomalNoise3DBaseFrequencyZ", 40);
+ m_ChoiceFrequencyX = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "BiomalNoise3DChoiceFrequencyX", 40);
+ m_ChoiceFrequencyY = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "BiomalNoise3DChoiceFrequencyY", 80);
+ m_ChoiceFrequencyZ = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "BiomalNoise3DChoiceFrequencyZ", 40);
+ m_AirThreshold = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "BiomalNoise3DAirThreshold", 0);
+ int NumChoiceOctaves = a_IniFile.GetValueSetI("Generator", "BiomalNoise3DNumChoiceOctaves", 4);
+ int NumDensityOctaves = a_IniFile.GetValueSetI("Generator", "BiomalNoise3DNumDensityOctaves", 6);
+ int NumBaseOctaves = a_IniFile.GetValueSetI("Generator", "BiomalNoise3DNumBaseOctaves", 6);
+ NOISE_DATATYPE BaseNoiseAmplitude = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "BiomalNoise3DBaseAmplitude", 1);
+
+ // Add octaves for the choice noise:
+ NOISE_DATATYPE wavlen = 1, ampl = 0.5;
+ for (int i = 0; i < NumChoiceOctaves; i++)
{
- for (int z = 0; z < cChunkDef::Width; z++)
- {
- for (int y = 0; y < cChunkDef::Height; y++)
- {
- int idx = y * 17 * 17 + z * 17;
- unsigned char buf[16];
- for (int x = 0; x < cChunkDef::Width; x++)
- {
- buf[x] = (unsigned char)(std::min(256, std::max(0, (int)(128 + 128 * m_Noise[idx++]))));
- }
- f1.Write(buf, 16);
- } // for y
- } // for z
- } // if (XY file open)
+ m_ChoiceNoise.AddOctave(wavlen, ampl);
+ wavlen = wavlen * 2;
+ ampl = ampl / 2;
+ }
- cFile f2;
- if (f2.Open(Printf("Chunk_%d_%d_XZ.raw", a_ChunkX, a_ChunkZ), cFile::fmWrite))
+ // Add octaves for the density noises:
+ wavlen = 1, ampl = 1;
+ for (int i = 0; i < NumDensityOctaves; i++)
{
- for (int y = 0; y < cChunkDef::Height; y++)
+ m_DensityNoiseA.AddOctave(wavlen, ampl);
+ m_DensityNoiseB.AddOctave(wavlen, ampl);
+ wavlen = wavlen * 2;
+ ampl = ampl / 2;
+ }
+
+ // Add octaves for the base noise:
+ wavlen = 1, ampl = BaseNoiseAmplitude;
+ for (int i = 0; i < NumBaseOctaves; i++)
+ {
+ m_BaseNoise.AddOctave(wavlen, ampl);
+ wavlen = wavlen * 2;
+ ampl = ampl / 2;
+ }
+}
+
+
+
+
+
+void cBiomalNoise3DComposable::GenerateNoiseArrayIfNeeded(int a_ChunkX, int a_ChunkZ)
+{
+ if ((a_ChunkX == m_LastChunkX) && (a_ChunkZ == m_LastChunkZ))
+ {
+ // The noise for this chunk is already generated in m_NoiseArray
+ return;
+ }
+ m_LastChunkX = a_ChunkX;
+ m_LastChunkZ = a_ChunkZ;
+
+ // Calculate the parameters for the biomes:
+ ChunkParam MidPoint;
+ ChunkParam HeightAmp;
+ CalcBiomeParamArrays(a_ChunkX, a_ChunkZ, HeightAmp, MidPoint);
+
+ // Generate all the noises:
+ NOISE_DATATYPE ChoiceNoise[5 * 5 * 33];
+ NOISE_DATATYPE Workspace[5 * 5 * 33];
+ NOISE_DATATYPE DensityNoiseA[5 * 5 * 33];
+ NOISE_DATATYPE DensityNoiseB[5 * 5 * 33];
+ NOISE_DATATYPE BaseNoise[5 * 5];
+ NOISE_DATATYPE BlockX = static_cast<NOISE_DATATYPE>(a_ChunkX * cChunkDef::Width);
+ NOISE_DATATYPE BlockZ = static_cast<NOISE_DATATYPE>(a_ChunkZ * cChunkDef::Width);
+ // Note that we have to swap the X and Y coords, because noise generator uses [x + SizeX * y + SizeX * SizeY * z] ordering and we want "BlockY" to be "x":
+ m_ChoiceNoise.Generate3D (ChoiceNoise, 33, 5, 5, 0, 257 / m_ChoiceFrequencyY, BlockX / m_ChoiceFrequencyX, (BlockX + 17) / m_ChoiceFrequencyX, BlockZ / m_ChoiceFrequencyZ, (BlockZ + 17) / m_ChoiceFrequencyZ, Workspace);
+ m_DensityNoiseA.Generate3D(DensityNoiseA, 33, 5, 5, 0, 257 / m_FrequencyY, BlockX / m_FrequencyX, (BlockX + 17) / m_FrequencyX, BlockZ / m_FrequencyZ, (BlockZ + 17) / m_FrequencyZ, Workspace);
+ m_DensityNoiseB.Generate3D(DensityNoiseB, 33, 5, 5, 0, 257 / m_FrequencyY, BlockX / m_FrequencyX, (BlockX + 17) / m_FrequencyX, BlockZ / m_FrequencyZ, (BlockZ + 17) / m_FrequencyZ, Workspace);
+ m_BaseNoise.Generate2D (BaseNoise, 5, 5, BlockX / m_BaseFrequencyX, (BlockX + 17) / m_BaseFrequencyX, BlockZ / m_FrequencyZ, (BlockZ + 17) / m_FrequencyZ, Workspace);
+
+ // Calculate the final noise based on the partial noises:
+ for (int z = 0; z < 5; z++)
+ {
+ for (int x = 0; x < 5; x++)
{
- for (int z = 0; z < cChunkDef::Width; z++)
+ NOISE_DATATYPE curMidPoint = MidPoint[x + 5 * z];
+ NOISE_DATATYPE curHeightAmp = HeightAmp[x + 5 * z];
+ NOISE_DATATYPE curBaseNoise = BaseNoise[x + 5 * z];
+ for (int y = 0; y < 33; y++)
{
- int idx = y * 17 * 17 + z * 17;
- unsigned char buf[16];
- for (int x = 0; x < cChunkDef::Width; x++)
+ NOISE_DATATYPE AddHeight = (static_cast<NOISE_DATATYPE>(y * 8) - curMidPoint) * curHeightAmp;
+
+ // If "underground", make the terrain smoother by forcing the vertical linear gradient into steeper slope:
+ if (AddHeight < 0)
{
- buf[x] = (unsigned char)(std::min(256, std::max(0, (int)(128 + 128 * m_Noise[idx++]))));
+ AddHeight *= 4;
}
- f2.Write(buf, 16);
- } // for z
- } // for y
- } // if (XZ file open)
- */
+ // If too high, cut off any terrain:
+ if (y > 28)
+ {
+ AddHeight = AddHeight + static_cast<NOISE_DATATYPE>(y - 28) / 4;
+ }
+
+ // Decide between the two density noises:
+ int idx = 33 * x + y + 33 * 5 * z;
+ Workspace[idx] = ClampedLerp(DensityNoiseA[idx], DensityNoiseB[idx], 8 * (ChoiceNoise[idx] + 0.5f)) + AddHeight + curBaseNoise;
+ }
+ }
+ }
+ LinearUpscale3DArray<NOISE_DATATYPE>(Workspace, 33, 5, 5, m_NoiseArray, 8, 4, 4);
}
-void cNoise3DComposable::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap)
+void cBiomalNoise3DComposable::CalcBiomeParamArrays(int a_ChunkX, int a_ChunkZ, ChunkParam & a_HeightAmp, ChunkParam & a_MidPoint)
{
- GenerateNoiseArrayIfNeeded(a_ChunkX, a_ChunkZ);
+ // Generate the 3*3 chunks of biomes around this chunk:
+ 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[x + 3 * z]);
+ }
+ }
- for (int z = 0; z < cChunkDef::Width; z++)
+ // Sum up the biome values:
+ for (int z = 0; z < 5; z++)
{
- for (int x = 0; x < cChunkDef::Width; x++)
+ for (int x = 0; x < 5; x++)
{
- cChunkDef::SetHeight(a_HeightMap, x, z, m_SeaLevel);
- for (int y = cChunkDef::Height - 1; y > m_SeaLevel; y--)
+ NOISE_DATATYPE totalHeightAmp = 0;
+ NOISE_DATATYPE totalMidPoint = 0;
+ // Add up the biomes around this point:
+ for (int relz = 0; relz <= AVERAGING_SIZE * 2; ++relz)
{
- if (m_NoiseArray[y * 17 * 17 + z * 17 + x] <= m_AirThreshold)
+ int colz = 16 + z * 4 + relz - AVERAGING_SIZE; // Biome Z coord relative to the neighborBiomes start
+ int neicellz = colz / 16; // Chunk Z coord relative to the neighborBiomes start
+ int neirelz = colz % 16; // Biome Z coord relative to cz in neighborBiomes
+ for (int relx = 0; relx <= AVERAGING_SIZE * 2; ++relx)
{
- cChunkDef::SetHeight(a_HeightMap, x, z, y);
- break;
- }
- } // for y
+ int colx = 16 + x * 4 + relx - AVERAGING_SIZE; // Biome X coord relative to the neighborBiomes start
+ int neicellx = colx / 16; // Chunk X coord relative to the neighborBiomes start
+ int neirelx = colx % 16; // Biome X coord relative to cz in neighborBiomes
+ EMCSBiome biome = cChunkDef::GetBiome(neighborBiomes[neicellx + neicellz * 3], neirelx, neirelz);
+ NOISE_DATATYPE heightAmp, midPoint;
+ GetBiomeParams(biome, heightAmp, midPoint);
+ totalHeightAmp += heightAmp * m_Weight[relz][relx];
+ totalMidPoint += midPoint * m_Weight[relz][relx];
+ } // for relx
+ } // for relz
+ a_HeightAmp[x + 5 * z] = totalHeightAmp / m_WeightSum;
+ a_MidPoint[x + 5 * z] = totalMidPoint / m_WeightSum;
} // for x
} // for z
}
@@ -520,52 +712,97 @@ void cNoise3DComposable::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::Hei
-void cNoise3DComposable::ComposeTerrain(cChunkDesc & a_ChunkDesc)
+void cBiomalNoise3DComposable::GetBiomeParams(EMCSBiome a_Biome, NOISE_DATATYPE & a_HeightAmp, NOISE_DATATYPE & a_MidPoint)
{
- GenerateNoiseArrayIfNeeded(a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ());
+ switch (a_Biome)
+ {
+ case biBeach: a_HeightAmp = 0.2f; a_MidPoint = 60; break;
+ case biBirchForest: a_HeightAmp = 0.1f; a_MidPoint = 64; break;
+ case biBirchForestHills: a_HeightAmp = 0.075f; a_MidPoint = 68; break;
+ case biBirchForestHillsM: a_HeightAmp = 0.075f; a_MidPoint = 68; break;
+ case biBirchForestM: a_HeightAmp = 0.1f; a_MidPoint = 64; break;
+ case biColdBeach: a_HeightAmp = 0.3f; a_MidPoint = 62; break;
+ case biColdTaiga: a_HeightAmp = 0.1f; a_MidPoint = 64; break;
+ case biColdTaigaM: a_HeightAmp = 0.1f; a_MidPoint = 64; break;
+ case biColdTaigaHills: a_HeightAmp = 0.075f; a_MidPoint = 68; break;
+ case biDesertHills: a_HeightAmp = 0.075f; a_MidPoint = 68; break;
+ case biDeepOcean: a_HeightAmp = 0.17f; a_MidPoint = 35; break;
+ case biDesert: a_HeightAmp = 0.29f; a_MidPoint = 62; break;
+ case biDesertM: a_HeightAmp = 0.29f; a_MidPoint = 62; break;
+ case biEnd: a_HeightAmp = 0.15f; a_MidPoint = 64; break;
+ case biExtremeHills: a_HeightAmp = 0.045f; a_MidPoint = 75; break;
+ case biExtremeHillsEdge: a_HeightAmp = 0.1f; a_MidPoint = 70; break;
+ case biExtremeHillsM: a_HeightAmp = 0.045f; a_MidPoint = 75; break;
+ case biExtremeHillsPlus: a_HeightAmp = 0.04f; a_MidPoint = 80; break;
+ case biExtremeHillsPlusM: a_HeightAmp = 0.04f; a_MidPoint = 80; break;
+ case biFlowerForest: a_HeightAmp = 0.1f; a_MidPoint = 64; break;
+ case biForest: a_HeightAmp = 0.1f; a_MidPoint = 64; break;
+ case biForestHills: a_HeightAmp = 0.075f; a_MidPoint = 68; break;
+ case biFrozenRiver: a_HeightAmp = 0.4f; a_MidPoint = 54; break;
+ case biFrozenOcean: a_HeightAmp = 0.12f; a_MidPoint = 45; break;
+ case biIceMountains: a_HeightAmp = 0.075f; a_MidPoint = 68; break;
+ case biIcePlains: a_HeightAmp = 0.3f; a_MidPoint = 62; break;
+ case biIcePlainsSpikes: a_HeightAmp = 0.3f; a_MidPoint = 62; break;
+ case biJungle: a_HeightAmp = 0.1f; a_MidPoint = 63; break;
+ case biJungleEdge: a_HeightAmp = 0.15f; a_MidPoint = 62; break;
+ case biJungleEdgeM: a_HeightAmp = 0.15f; a_MidPoint = 62; break;
+ case biJungleHills: a_HeightAmp = 0.075f; a_MidPoint = 68; break;
+ case biJungleM: a_HeightAmp = 0.1f; a_MidPoint = 63; break;
+ case biMegaSpruceTaiga: a_HeightAmp = 0.09f; a_MidPoint = 64; break;
+ case biMegaSpruceTaigaHills: a_HeightAmp = 0.075f; a_MidPoint = 68; break;
+ case biMegaTaiga: a_HeightAmp = 0.1f; a_MidPoint = 64; break;
+ case biMegaTaigaHills: a_HeightAmp = 0.075f; a_MidPoint = 68; break;
+ case biMesa: a_HeightAmp = 0.09f; a_MidPoint = 61; break;
+ case biMesaBryce: a_HeightAmp = 0.15f; a_MidPoint = 61; break;
+ case biMesaPlateau: a_HeightAmp = 0.25f; a_MidPoint = 86; break;
+ case biMesaPlateauF: a_HeightAmp = 0.25f; a_MidPoint = 96; break;
+ case biMesaPlateauFM: a_HeightAmp = 0.25f; a_MidPoint = 96; break;
+ case biMesaPlateauM: a_HeightAmp = 0.25f; a_MidPoint = 86; break;
+ case biMushroomShore: a_HeightAmp = 0.075f; a_MidPoint = 60; break;
+ case biMushroomIsland: a_HeightAmp = 0.06f; a_MidPoint = 80; break;
+ case biNether: a_HeightAmp = 0.01f; a_MidPoint = 64; break;
+ case biOcean: a_HeightAmp = 0.12f; a_MidPoint = 45; break;
+ case biPlains: a_HeightAmp = 0.3f; a_MidPoint = 62; break;
+ case biRiver: a_HeightAmp = 0.4f; a_MidPoint = 54; break;
+ case biRoofedForest: a_HeightAmp = 0.1f; a_MidPoint = 64; break;
+ case biRoofedForestM: a_HeightAmp = 0.1f; a_MidPoint = 64; break;
+ case biSavanna: a_HeightAmp = 0.3f; a_MidPoint = 62; break;
+ case biSavannaM: a_HeightAmp = 0.3f; a_MidPoint = 62; break;
+ case biSavannaPlateau: a_HeightAmp = 0.3f; a_MidPoint = 85; break;
+ case biSavannaPlateauM: a_HeightAmp = 0.012f; a_MidPoint = 105; break;
+ case biStoneBeach: a_HeightAmp = 0.075f; a_MidPoint = 60; break;
+ case biSunflowerPlains: a_HeightAmp = 0.3f; a_MidPoint = 62; break;
+ case biSwampland: a_HeightAmp = 0.25f; a_MidPoint = 59; break;
+ case biSwamplandM: a_HeightAmp = 0.11f; a_MidPoint = 59; break;
+ case biTaiga: a_HeightAmp = 0.1f; a_MidPoint = 64; break;
+ case biTaigaM: a_HeightAmp = 0.1f; a_MidPoint = 70; break;
+ case biTaigaHills: a_HeightAmp = 0.075f; a_MidPoint = 68; break;
+ default:
+ {
+ // Make a crazy terrain so that it stands out
+ a_HeightAmp = 0.001f;
+ a_MidPoint = 90;
+ break;
+ }
+ }
+}
- a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0);
- // Make basic terrain composition:
+
+
+void cBiomalNoise3DComposable::GenShape(int a_ChunkX, int a_ChunkZ, cChunkDesc::Shape & a_Shape)
+{
+ GenerateNoiseArrayIfNeeded(a_ChunkX, a_ChunkZ);
+
+ // Translate the noise array into Shape:
for (int z = 0; z < cChunkDef::Width; z++)
{
for (int x = 0; x < cChunkDef::Width; x++)
{
- int LastAir = a_ChunkDesc.GetHeight(x, z) + 1;
- bool HasHadWater = false;
- for (int y = LastAir; y < m_SeaLevel; y++)
+ for (int y = 0; y < cChunkDef::Height; y++)
{
- a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_STATIONARY_WATER);
+ a_Shape[y + x * 256 + z * 256 * 16] = (m_NoiseArray[y + 257 * x + 257 * 17 * z] > m_AirThreshold) ? 0 : 1;
}
- for (int y = LastAir - 1; y > 0; y--)
- {
- if (m_NoiseArray[x + 17 * z + 17 * 17 * y] > m_AirThreshold)
- {
- // "air" part
- LastAir = y;
- if (y < m_SeaLevel)
- {
- a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_STATIONARY_WATER);
- HasHadWater = true;
- }
- continue;
- }
- // "ground" part:
- if (LastAir - y > 4)
- {
- a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_STONE);
- continue;
- }
- if (HasHadWater)
- {
- a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_SAND);
- }
- else
- {
- a_ChunkDesc.SetBlockType(x, y, z, (LastAir == y + 1) ? E_BLOCK_GRASS : E_BLOCK_DIRT);
- }
- } // for y
- a_ChunkDesc.SetBlockType(x, 0, z, E_BLOCK_BEDROCK);
} // for x
} // for z
}
@@ -573,3 +810,4 @@ void cNoise3DComposable::ComposeTerrain(cChunkDesc & a_ChunkDesc)
+
diff --git a/src/Generating/Noise3DGenerator.h b/src/Generating/Noise3DGenerator.h
index 42f61a854..35b1e4c94 100644
--- a/src/Generating/Noise3DGenerator.h
+++ b/src/Generating/Noise3DGenerator.h
@@ -1,7 +1,11 @@
// Noise3DGenerator.h
-// Generates terrain using 3D noise, rather than composing. Is a test.
+// Declares cNoise3DGenerator and cNoise3DComposable classes, representing 3D-noise-based generators.
+// They generate terrain shape by combining a lerp of two 3D noises with a vertical linear gradient
+// cNoise3DGenerator is obsolete and unmaintained.
+// cNoise3DComposable is used to test parameter combinations for single-biome worlds.
+
@@ -9,7 +13,8 @@
#pragma once
#include "ComposableGenerator.h"
-#include "../Noise.h"
+#include "../Noise/Noise.h"
+#include "../Noise/InterpolNoise.h"
@@ -30,17 +35,20 @@ public:
protected:
// Linear interpolation step sizes, must be divisors of cChunkDef::Width and cChunkDef::Height, respectively:
- static const int UPSCALE_X = 8;
- static const int UPSCALE_Y = 4;
- static const int UPSCALE_Z = 8;
+ static const int UPSCALE_X = 4;
+ static const int UPSCALE_Y = 8;
+ static const int UPSCALE_Z = 4;
// Linear interpolation buffer dimensions, calculated from the step sizes:
static const int DIM_X = 1 + cChunkDef::Width / UPSCALE_X;
static const int DIM_Y = 1 + cChunkDef::Height / UPSCALE_Y;
static const int DIM_Z = 1 + cChunkDef::Width / UPSCALE_Z;
- cPerlinNoise m_Perlin; // The base 3D noise source for the actual composition
- cCubicNoise m_Cubic; // The noise used for heightmap directing
+ /** The base 3D noise source for the actual composition */
+ cOctavedNoise<cInterp5DegNoise> m_Perlin;
+
+ /** The noise used for heightmap directing. */
+ cOctavedNoise<cInterp5DegNoise> m_Cubic;
int m_SeaLevel;
NOISE_DATATYPE m_HeightAmplification;
@@ -65,8 +73,7 @@ protected:
class cNoise3DComposable :
- public cTerrainHeightGen,
- public cTerrainCompositionGen
+ public cTerrainShapeGen
{
public:
cNoise3DComposable(int a_Seed);
@@ -74,31 +81,135 @@ public:
void Initialize(cIniFile & a_IniFile);
protected:
- cNoise m_Noise1;
- cNoise m_Noise2;
- cNoise m_Noise3;
+ /** The 3D noise that is used to choose between density noise A and B. */
+ cOctavedNoise<cInterpolNoise<Interp5Deg>> m_ChoiceNoise;
+
+ /** Density 3D noise, variant A. */
+ cOctavedNoise<cInterpolNoise<Interp5Deg>> m_DensityNoiseA;
+
+ /** Density 3D noise, variant B. */
+ cOctavedNoise<cInterpolNoise<Interp5Deg>> m_DensityNoiseB;
+
+ /** Heightmap-like noise used to provide variance for low-amplitude biomes. */
+ cOctavedNoise<cInterpolNoise<Interp5Deg>> m_BaseNoise;
- int m_SeaLevel;
+ /** The main parameter of the generator, specifies the slope of the vertical linear gradient.
+ A higher value means a steeper slope and a smaller total amplitude of the generated terrain. */
NOISE_DATATYPE m_HeightAmplification;
- NOISE_DATATYPE m_MidPoint; // Where the vertical "center" of the noise should be
+
+ /** Where the vertical "center" of the noise should be, as block height. */
+ NOISE_DATATYPE m_MidPoint;
+
+ // Frequency of the 3D noise's first octave:
NOISE_DATATYPE m_FrequencyX;
NOISE_DATATYPE m_FrequencyY;
NOISE_DATATYPE m_FrequencyZ;
+
+ // Frequency of the base terrain noise:
+ NOISE_DATATYPE m_BaseFrequencyX;
+ NOISE_DATATYPE m_BaseFrequencyZ;
+
+ // Frequency of the choice noise:
+ NOISE_DATATYPE m_ChoiceFrequencyX;
+ NOISE_DATATYPE m_ChoiceFrequencyY;
+ NOISE_DATATYPE m_ChoiceFrequencyZ;
+
+ // Threshold for when the values are considered air:
NOISE_DATATYPE m_AirThreshold;
+ // Cache for the last calculated chunk (reused between heightmap and composition queries):
int m_LastChunkX;
int m_LastChunkZ;
NOISE_DATATYPE m_NoiseArray[17 * 17 * 257]; // x + 17 * z + 17 * 17 * y
- /// Generates the 3D noise array used for terrain generation, unless the LastChunk coords are equal to coords given
+ /** Generates the 3D noise array used for terrain generation (m_NoiseArray), unless the LastChunk coords are equal to coords given */
void GenerateNoiseArrayIfNeeded(int a_ChunkX, int a_ChunkZ);
// cTerrainHeightGen overrides:
- virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override;
+ virtual void GenShape(int a_ChunkX, int a_ChunkZ, cChunkDesc::Shape & a_Shape) override;
+ virtual void InitializeShapeGen(cIniFile & a_IniFile) override { Initialize(a_IniFile); }
+} ;
+
+
+
+
+
+class cBiomalNoise3DComposable :
+ public cTerrainShapeGen
+{
+public:
+ cBiomalNoise3DComposable(int a_Seed, cBiomeGenPtr a_BiomeGen);
+
+ void Initialize(cIniFile & a_IniFile);
+
+protected:
+ /** Number of columns around the pixel to query for biomes for averaging. Must be less than or equal to 16. */
+ static const int AVERAGING_SIZE = 9;
+
+ /** Type used for a single parameter across the entire (downscaled) chunk. */
+ typedef NOISE_DATATYPE ChunkParam[5 * 5];
- // cTerrainCompositionGen overrides:
- virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override;
+
+ /** The noise that is used to choose between density noise A and B. */
+ cOctavedNoise<cInterpolNoise<Interp5Deg>> m_ChoiceNoise;
+
+ /** Density 3D noise, variant A. */
+ cOctavedNoise<cInterpolNoise<Interp5Deg>> m_DensityNoiseA;
+
+ /** Density 3D noise, variant B. */
+ cOctavedNoise<cInterpolNoise<Interp5Deg>> m_DensityNoiseB;
+
+ /** Heightmap-like noise used to provide variance for low-amplitude biomes. */
+ cOctavedNoise<cInterpolNoise<Interp5Deg>> m_BaseNoise;
+
+ /** The underlying biome generator. */
+ cBiomeGenPtr m_BiomeGen;
+
+ /** Block height of the sealevel, used for composing the terrain. */
+ int m_SeaLevel;
+
+ // Frequency of the 3D noise's first octave:
+ NOISE_DATATYPE m_FrequencyX;
+ NOISE_DATATYPE m_FrequencyY;
+ NOISE_DATATYPE m_FrequencyZ;
+
+ // Frequency of the base terrain noise:
+ NOISE_DATATYPE m_BaseFrequencyX;
+ NOISE_DATATYPE m_BaseFrequencyZ;
+
+ // Frequency of the choice noise:
+ NOISE_DATATYPE m_ChoiceFrequencyX;
+ NOISE_DATATYPE m_ChoiceFrequencyY;
+ NOISE_DATATYPE m_ChoiceFrequencyZ;
+
+ // Threshold for when the values are considered air:
+ NOISE_DATATYPE m_AirThreshold;
+
+ // Cache for the last calculated chunk (reused between heightmap and composition queries):
+ int m_LastChunkX;
+ int m_LastChunkZ;
+ NOISE_DATATYPE m_NoiseArray[17 * 17 * 257]; // 257 * x + y + 257 * 17 * z
+
+ /** Weights for summing up neighboring biomes. */
+ NOISE_DATATYPE m_Weight[AVERAGING_SIZE * 2 + 1][AVERAGING_SIZE * 2 + 1];
+
+ /** The sum of m_Weight[]. */
+ NOISE_DATATYPE m_WeightSum;
+
+
+ /** Generates the 3D noise array used for terrain generation (m_NoiseArray), unless the LastChunk coords are equal to coords given */
+ void GenerateNoiseArrayIfNeeded(int a_ChunkX, int a_ChunkZ);
+
+ /** Calculates the biome-related parameters for the chunk. */
+ void CalcBiomeParamArrays(int a_ChunkX, int a_ChunkZ, ChunkParam & a_HeightAmp, ChunkParam & a_MidPoint);
+
+ /** Returns the parameters for the specified biome. */
+ void GetBiomeParams(EMCSBiome a_Biome, NOISE_DATATYPE & a_HeightAmp, NOISE_DATATYPE & a_MidPoint);
+
+ // cTerrainShapeGen overrides:
+ virtual void GenShape(int a_ChunkX, int a_ChunkZ, cChunkDesc::Shape & a_Shape) override;
+ virtual void InitializeShapeGen(cIniFile & a_IniFile) override { Initialize(a_IniFile); }
} ;
diff --git a/src/Generating/PieceGenerator.h b/src/Generating/PieceGenerator.h
index f06029280..43ffed7a2 100644
--- a/src/Generating/PieceGenerator.h
+++ b/src/Generating/PieceGenerator.h
@@ -20,7 +20,7 @@ Each uses a slightly different approach to generating:
#include "../Defines.h"
#include "../Cuboid.h"
-#include "../Noise.h"
+#include "../Noise/Noise.h"
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;
+};
+
+
+
+
diff --git a/src/Generating/Ravines.h b/src/Generating/Ravines.h
index 3e41c5ce6..b11037433 100644
--- a/src/Generating/Ravines.h
+++ b/src/Generating/Ravines.h
@@ -10,7 +10,6 @@
#pragma once
#include "GridStructGen.h"
-#include "../Noise.h"
diff --git a/src/Generating/ShapeGen.cpp b/src/Generating/ShapeGen.cpp
new file mode 100644
index 000000000..45a9c3b93
--- /dev/null
+++ b/src/Generating/ShapeGen.cpp
@@ -0,0 +1,145 @@
+
+// ShapeGen.cpp
+
+// Implements the function to create a cTerrainShapeGen descendant based on INI file settings
+
+#include "Globals.h"
+#include "HeiGen.h"
+#include "../IniFile.h"
+#include "DistortedHeightmap.h"
+#include "EndGen.h"
+#include "Noise3DGenerator.h"
+#include "TwoHeights.h"
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// cTerrainHeightToShapeGen:
+
+/** Converts old-style height-generators into new-style shape-generators. */
+class cTerrainHeightToShapeGen:
+ public cTerrainShapeGen
+{
+public:
+ cTerrainHeightToShapeGen(cTerrainHeightGenPtr a_HeightGen):
+ m_HeightGen(a_HeightGen)
+ {
+ }
+
+
+ // cTerrainShapeGen overrides:
+ virtual void GenShape(int a_ChunkX, int a_ChunkZ, cChunkDesc::Shape & a_Shape) override
+ {
+ // Generate the heightmap:
+ cChunkDef::HeightMap heightMap;
+ m_HeightGen->GenHeightMap(a_ChunkX, a_ChunkZ, heightMap);
+
+ // Convert from heightmap to shape:
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ HEIGHTTYPE height = cChunkDef::GetHeight(heightMap, x, z) + 1;
+ Byte * shapeColumn = &(a_Shape[(x + 16 * z) * 256]);
+ for (int y = 0; y < height; y++)
+ {
+ shapeColumn[y] = 1;
+ }
+ for (int y = height; y < cChunkDef::Height; y++)
+ {
+ shapeColumn[y] = 0;
+ }
+ } // for x
+ } // for z
+ }
+
+
+ virtual void InitializeShapeGen(cIniFile & a_IniFile) override
+ {
+ m_HeightGen->InitializeHeightGen(a_IniFile);
+ }
+
+protected:
+ /** The height generator being converted. */
+ cTerrainHeightGenPtr m_HeightGen;
+};
+
+typedef SharedPtr<cTerrainHeightToShapeGen> cTerrainHeightToShapeGenPtr;
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// cTerrainShapeGen:
+
+cTerrainShapeGenPtr cTerrainShapeGen::CreateShapeGen(cIniFile & a_IniFile, cBiomeGenPtr a_BiomeGen, int a_Seed, bool & a_CacheOffByDefault)
+{
+ AString shapeGenName = a_IniFile.GetValueSet("Generator", "ShapeGen", "");
+ if (shapeGenName.empty())
+ {
+ LOGWARN("[Generator] ShapeGen value not set in world.ini, using \"BiomalNoise3D\".");
+ shapeGenName = "BiomalNoise3D";
+ }
+
+ // If the shapegen is HeightMap, redirect to older HeightMap-based generators:
+ if (NoCaseCompare(shapeGenName, "HeightMap") == 0)
+ {
+ cTerrainHeightGenPtr heightGen = cTerrainHeightGen::CreateHeightGen(a_IniFile, a_BiomeGen, a_Seed, a_CacheOffByDefault);
+ if (heightGen != nullptr)
+ {
+ return std::make_shared<cTerrainHeightToShapeGen>(heightGen);
+ }
+
+ // The height gen was not recognized; several heightgens were promoted to shape gens, so let's try them instead:
+ shapeGenName = a_IniFile.GetValue("Generator", "HeightGen", "");
+ if (shapeGenName.empty())
+ {
+ LOGWARNING("[Generator] ShapeGen set to HeightMap, but HeightGen not set. Reverting to \"BiomalNoise3D\".");
+ shapeGenName = "BiomalNoise3D";
+ a_IniFile.SetValue("Generator", "ShapeGen", shapeGenName);
+ }
+ }
+
+ // Choose the shape generator based on the name:
+ a_CacheOffByDefault = false;
+ cTerrainShapeGenPtr res;
+ if (NoCaseCompare(shapeGenName, "DistortedHeightmap") == 0)
+ {
+ res = std::make_shared<cDistortedHeightmap>(a_Seed, a_BiomeGen);
+ }
+ else if (NoCaseCompare(shapeGenName, "End") == 0)
+ {
+ res = std::make_shared<cEndGen>(a_Seed);
+ }
+ else if (NoCaseCompare(shapeGenName, "BiomalNoise3D") == 0)
+ {
+ res = std::make_shared<cBiomalNoise3DComposable>(a_Seed, a_BiomeGen);
+ }
+ else if (NoCaseCompare(shapeGenName, "Noise3D") == 0)
+ {
+ res = std::make_shared<cNoise3DComposable>(a_Seed);
+ }
+ else if (NoCaseCompare(shapeGenName, "TwoHeights") == 0)
+ {
+ res = CreateShapeGenTwoHeights(a_Seed, a_BiomeGen);
+ }
+ else
+ {
+ // No match found, force-set the default and retry
+ LOGWARN("Unknown ShapeGen \"%s\", using \"BiomalNoise3D\" instead.", shapeGenName.c_str());
+ a_IniFile.SetValue("Generator", "ShapeGen", "BiomalNoise3D");
+ return CreateShapeGen(a_IniFile, a_BiomeGen, a_Seed, a_CacheOffByDefault);
+ }
+
+ // Read the settings:
+ res->InitializeShapeGen(a_IniFile);
+
+ return res;
+}
+
+
+
+
diff --git a/src/Generating/StructGen.cpp b/src/Generating/StructGen.cpp
index bdefcd8c1..2f685c808 100644
--- a/src/Generating/StructGen.cpp
+++ b/src/Generating/StructGen.cpp
@@ -37,10 +37,13 @@ void cStructGenTrees::GenFinish(cChunkDesc & a_ChunkDesc)
Dest = &WorkerDesc;
WorkerDesc.SetChunkCoords(BaseX, BaseZ);
+ // TODO: This may cause a lot of wasted calculations, instead of pulling data out of a single (cChunkDesc) cache
+
+ cChunkDesc::Shape workerShape;
m_BiomeGen->GenBiomes (BaseX, BaseZ, WorkerDesc.GetBiomeMap());
- m_HeightGen->GenHeightMap (BaseX, BaseZ, WorkerDesc.GetHeightMap());
- m_CompositionGen->ComposeTerrain(WorkerDesc);
- // TODO: Free the entity lists
+ m_ShapeGen->GenShape (BaseX, BaseZ, workerShape);
+ WorkerDesc.SetHeightFromShape (workerShape);
+ m_CompositionGen->ComposeTerrain(WorkerDesc, workerShape);
}
else
{
@@ -97,7 +100,7 @@ void cStructGenTrees::GenerateSingleTree(
int Height = a_ChunkDesc.GetHeight(x, z);
- if ((Height <= 0) || (Height > 240))
+ if ((Height <= 0) || (Height >= 230))
{
return;
}
@@ -125,6 +128,11 @@ void cStructGenTrees::GenerateSingleTree(
// Outside the chunk
continue;
}
+ if (itr->y >= cChunkDef::Height)
+ {
+ // Above the chunk, cut off (this shouldn't happen too often, we're limiting trees to y < 230)
+ continue;
+ }
BLOCKTYPE Block = a_ChunkDesc.GetBlockType(itr->x, itr->y, itr->z);
switch (Block)
@@ -159,7 +167,7 @@ void cStructGenTrees::ApplyTreeImage(
// Put the generated image into a_BlockTypes, push things outside this chunk into a_Blocks
for (sSetBlockVector::const_iterator itr = a_Image.begin(), end = a_Image.end(); itr != end; ++itr)
{
- if ((itr->ChunkX == a_ChunkX) && (itr->ChunkZ == a_ChunkZ))
+ if ((itr->ChunkX == a_ChunkX) && (itr->ChunkZ == a_ChunkZ) && (itr->y < cChunkDef::Height))
{
// Inside this chunk, integrate into a_ChunkDesc:
switch (a_ChunkDesc.GetBlockType(itr->x, itr->y, itr->z))
@@ -390,7 +398,7 @@ void cStructGenLakes::GenFinish(cChunkDesc & a_ChunkDesc)
}
cBlockArea Lake;
- CreateLakeImage(ChunkX + x, ChunkZ + z, Lake);
+ CreateLakeImage(ChunkX + x, ChunkZ + z, a_ChunkDesc.GetMinHeight(), Lake);
int OfsX = Lake.GetOriginX() + x * cChunkDef::Width;
int OfsZ = Lake.GetOriginZ() + z * cChunkDef::Width;
@@ -404,25 +412,13 @@ void cStructGenLakes::GenFinish(cChunkDesc & a_ChunkDesc)
-void cStructGenLakes::CreateLakeImage(int a_ChunkX, int a_ChunkZ, cBlockArea & a_Lake)
+void cStructGenLakes::CreateLakeImage(int a_ChunkX, int a_ChunkZ, int a_MaxLakeHeight, cBlockArea & a_Lake)
{
a_Lake.Create(16, 8, 16);
a_Lake.Fill(cBlockArea::baTypes, E_BLOCK_SPONGE); // Sponge is the NOP blocktype for lake merging strategy
- // Find the minimum height in this chunk:
- cChunkDef::HeightMap HeightMap;
- m_HeiGen->GenHeightMap(a_ChunkX, a_ChunkZ, HeightMap);
- HEIGHTTYPE MinHeight = HeightMap[0];
- for (size_t i = 1; i < ARRAYCOUNT(HeightMap); i++)
- {
- if (HeightMap[i] < MinHeight)
- {
- MinHeight = HeightMap[i];
- }
- }
-
// Make a random position in the chunk by using a random 16 block XZ offset and random height up to chunk's max height minus 6
- MinHeight = std::max(MinHeight - 6, 2);
+ int MinHeight = std::max(a_MaxLakeHeight - 6, 2);
int Rnd = m_Noise.IntNoise3DInt(a_ChunkX, 128, a_ChunkZ) / 11;
// Random offset [-8 .. 8], with higher probability around 0; add up four three-bit-wide randoms [0 .. 28], divide and subtract to get range
int OffsetX = 4 * ((Rnd & 0x07) + ((Rnd & 0x38) >> 3) + ((Rnd & 0x1c0) >> 6) + ((Rnd & 0xe00) >> 9)) / 7 - 8;
diff --git a/src/Generating/StructGen.h b/src/Generating/StructGen.h
index 906fdd722..796abf0f5 100644
--- a/src/Generating/StructGen.h
+++ b/src/Generating/StructGen.h
@@ -14,7 +14,7 @@
#pragma once
#include "ComposableGenerator.h"
-#include "../Noise.h"
+#include "../Noise/Noise.h"
@@ -24,11 +24,11 @@ class cStructGenTrees :
public cFinishGen
{
public:
- cStructGenTrees(int a_Seed, cBiomeGenPtr a_BiomeGen, cTerrainHeightGenPtr a_HeightGen, cTerrainCompositionGenPtr a_CompositionGen) :
+ cStructGenTrees(int a_Seed, cBiomeGenPtr a_BiomeGen, cTerrainShapeGenPtr a_ShapeGen, cTerrainCompositionGenPtr a_CompositionGen) :
m_Seed(a_Seed),
m_Noise(a_Seed),
m_BiomeGen(a_BiomeGen),
- m_HeightGen(a_HeightGen),
+ m_ShapeGen(a_ShapeGen),
m_CompositionGen(a_CompositionGen)
{}
@@ -37,12 +37,12 @@ protected:
int m_Seed;
cNoise m_Noise;
cBiomeGenPtr m_BiomeGen;
- cTerrainHeightGenPtr m_HeightGen;
+ cTerrainShapeGenPtr m_ShapeGen;
cTerrainCompositionGenPtr m_CompositionGen;
/** Generates and applies an image of a single tree.
- Parts of the tree inside the chunk are applied to a_BlockX.
- Parts of the tree outside the chunk are stored in a_OutsideX
+ Parts of the tree inside the chunk are applied to a_ChunkDesc.
+ Parts of the tree outside the chunk are stored in a_OutsideXYZ
*/
void GenerateSingleTree(
int a_ChunkX, int a_ChunkZ, int a_Seq,
@@ -51,7 +51,7 @@ protected:
sSetBlockVector & a_OutsideOther
) ;
- /// Applies an image into chunk blockdata; all blocks outside the chunk will be appended to a_Overflow
+ /** Applies an image into chunk blockdata; all blocks outside the chunk will be appended to a_Overflow. */
void ApplyTreeImage(
int a_ChunkX, int a_ChunkZ,
cChunkDesc & a_ChunkDesc,
@@ -124,27 +124,30 @@ class cStructGenLakes :
public cFinishGen
{
public:
- cStructGenLakes(int a_Seed, BLOCKTYPE a_Fluid, cTerrainHeightGenPtr a_HeiGen, int a_Probability) :
+ cStructGenLakes(int a_Seed, BLOCKTYPE a_Fluid, cTerrainShapeGenPtr a_ShapeGen, int a_Probability) :
m_Noise(a_Seed),
m_Seed(a_Seed),
m_Fluid(a_Fluid),
- m_HeiGen(a_HeiGen),
+ m_ShapeGen(a_ShapeGen),
m_Probability(a_Probability)
{
}
protected:
- cNoise m_Noise;
- int m_Seed;
- BLOCKTYPE m_Fluid;
- cTerrainHeightGenPtr m_HeiGen;
- int m_Probability; ///< Chance, 0 .. 100, of a chunk having the lake
+ cNoise m_Noise;
+ int m_Seed;
+ BLOCKTYPE m_Fluid;
+ cTerrainShapeGenPtr m_ShapeGen;
+
+ /** Chance, [0 .. 100], of a chunk having the lake. */
+ int m_Probability;
+
// cFinishGen override:
virtual void GenFinish(cChunkDesc & a_ChunkDesc) override;
- /// Creates a lake image for the specified chunk into a_Lake
- void CreateLakeImage(int a_ChunkX, int a_ChunkZ, cBlockArea & a_Lake);
+ /** Creates a lake image for the specified chunk into a_Lake. */
+ void CreateLakeImage(int a_ChunkX, int a_ChunkZ, int a_MaxLakeHeight, cBlockArea & a_Lake);
} ;
diff --git a/src/Generating/Trees.cpp b/src/Generating/Trees.cpp
index 7fd6d6f07..be8b0cd6b 100644
--- a/src/Generating/Trees.cpp
+++ b/src/Generating/Trees.cpp
@@ -61,7 +61,7 @@ static const sCoords BigO3[] =
static const sCoords BigO4[] = // Part of Big Jungle tree
{
- /* -4 */ {-2, -4}, {-1, -4}, {0, -4}, {1, -4}, {2, -4},
+ /* -4 */ {-2, -4}, {-1, -4}, {0, -4}, {1, -4}, {2, -4},
/* -3 */ {-3, -3}, {-2, -3}, {-1, -3}, {0, -3}, {1, -3}, {2, -3}, {3, -3},
/* -2 */ {-4, -2}, {-3, -2}, {-2, -2}, {-1, -2}, {0, -2}, {1, -2}, {2, -2}, {3, -2}, {4, -2},
/* -1 */ {-4, -1}, {-3, -1}, {-2, -1}, {-1, -1}, {0, -1}, {1, -1}, {2, -1}, {3, -1}, {4, -1},
@@ -361,7 +361,109 @@ void GetSmallAppleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a
void GetLargeAppleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks)
{
- // TODO
+ int Height = 7 + a_Noise.IntNoise3DInt(a_BlockX, a_BlockY, a_BlockZ) % 4;
+
+ // Array with possible directions for a branch to go to.
+ const Vector3d AvailableDirections[] =
+ {
+ { -1, 0, 0 }, { 0, 0, -1 },
+ { -1, 0, 1 }, { -1, 0, -1 },
+ { 1, 0, 1 }, { 1, 0, -1 },
+ { 1, 0, 0 }, { 0, 0, 1 },
+
+ { -0.5, 0, 0 }, { 0, 0, -0.5 },
+ { -0.5, 0, 0.5 }, { -0.5, 0, -0.5 },
+ { 0.5, 0, 0.5 }, { 0.5, 0, -0.5 },
+ { 0.5, 0, 0 }, { 0, 0, 0.5 },
+
+ { -1, 0.5, 0 }, { 0, 0.5, -1 },
+ { -1, 0.5, 1 }, { -1, 0.5, -1 },
+ { 1, 0.5, 1 }, { 1, 0.5, -1 },
+ { 1, 0.5, 0 }, { 0, 0.5, 1 },
+
+ { -0.5, 0.5, 0 }, { 0, 0.5, -0.5 },
+ { -0.5, 0.5, 0.5 }, { -0.5, 0.5, -0.5 },
+ { 0.5, 0.5, 0.5 }, { 0.5, 0.5, -0.5 },
+ { 0.5, 0.5, 0 }, { 0, 0.5, 0.5 },
+
+ };
+
+ // Create branches
+ for (int i = 4; i < Height; i++)
+ {
+ // Get a direction for the trunk to go to.
+ Vector3d BranchStartDirection = AvailableDirections[a_Noise.IntNoise3DInt(a_BlockX, a_BlockY + i, a_BlockZ) % ARRAYCOUNT(AvailableDirections)];
+ Vector3d BranchDirection = AvailableDirections[a_Noise.IntNoise3DInt(a_BlockX, a_BlockY / i, a_BlockZ) % ARRAYCOUNT(AvailableDirections)] / 3;
+
+ int BranchLength = 2 + a_Noise.IntNoise3DInt(a_BlockX * a_Seq, a_BlockY * a_Seq, a_BlockZ * a_Seq) % 3;
+ GetLargeAppleTreeBranch(a_BlockX, a_BlockY + i, a_BlockZ, BranchLength, BranchStartDirection, BranchDirection, a_BlockY + Height, a_Noise, a_LogBlocks);
+ }
+
+ // Place leaves around each log block
+ for (auto itr : a_LogBlocks)
+ {
+ // Get the log's X and Z coordinates
+ int X = itr.ChunkX * 16 + itr.x;
+ int Z = itr.ChunkZ * 16 + itr.z;
+
+ a_OtherBlocks.push_back(sSetBlock(X, itr.y - 2, Z, E_BLOCK_LEAVES, E_META_LEAVES_APPLE));
+ PushCoordBlocks(X, itr.y - 2, Z, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_APPLE);
+ for (int y = -1; y <= 1; y++)
+ {
+ PushCoordBlocks (X, itr.y + y, Z, a_OtherBlocks, BigO2, ARRAYCOUNT(BigO2), E_BLOCK_LEAVES, E_META_LEAVES_APPLE);
+ }
+ PushCoordBlocks(X, itr.y + 2, Z, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_APPLE);
+ a_OtherBlocks.push_back(sSetBlock(X, itr.y + 2, Z, E_BLOCK_LEAVES, E_META_LEAVES_APPLE));
+ }
+
+ // Trunk:
+ for (int i = 0; i < Height; i++)
+ {
+ a_LogBlocks.push_back(sSetBlock(a_BlockX, a_BlockY + i, a_BlockZ, E_BLOCK_LOG, E_META_LOG_APPLE));
+ }
+}
+
+
+
+
+
+void GetLargeAppleTreeBranch(int a_BlockX, int a_BlockY, int a_BlockZ, int a_BranchLength, Vector3d a_StartDirection, Vector3d a_Direction, int a_TreeHeight, cNoise & a_Noise, sSetBlockVector & a_LogBlocks)
+{
+ Vector3d CurrentPos = Vector3d(a_BlockX, a_BlockY, a_BlockZ);
+ Vector3d Direction = a_StartDirection;
+ for (int i = 0; i < a_BranchLength; i++)
+ {
+ CurrentPos += Direction;
+ if (CurrentPos.y >= a_TreeHeight)
+ {
+ return;
+ }
+ Direction -= a_Direction;
+ Direction.clamp(-1.0, 1.0);
+ a_LogBlocks.push_back(sSetBlock(FloorC(CurrentPos.x), FloorC(CurrentPos.y), FloorC(CurrentPos.z), E_BLOCK_LOG, GetLogMetaFromDirection(E_META_LOG_APPLE, Direction)));
+ }
+}
+
+
+
+
+
+NIBBLETYPE GetLogMetaFromDirection(NIBBLETYPE a_BlockMeta, Vector3d a_Direction)
+{
+ a_Direction.abs();
+
+ if ((a_Direction.y > a_Direction.x) && (a_Direction.y > a_Direction.z))
+ {
+ return a_BlockMeta;
+ }
+ else if (a_Direction.x > a_Direction.z)
+ {
+ return a_BlockMeta + 4;
+ }
+ else
+ {
+ return a_BlockMeta + 8;
+ }
}
diff --git a/src/Generating/Trees.h b/src/Generating/Trees.h
index c9eb7de80..a2c8274f5 100644
--- a/src/Generating/Trees.h
+++ b/src/Generating/Trees.h
@@ -18,7 +18,7 @@ logs can overwrite others(leaves), but others shouldn't overwrite logs. This is
#pragma once
#include "../ChunkDef.h"
-#include "../Noise.h"
+#include "../Noise/Noise.h"
@@ -62,6 +62,12 @@ void GetSmallAppleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a
/// Generates an image of a large (branching) apple tree
void GetLargeAppleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks);
+/// Generates a branch for a large apple tree
+void GetLargeAppleTreeBranch(int a_BlockX, int a_BlockY, int a_BlockZ, int a_BranchLength, Vector3d a_StartDirection, Vector3d a_Direction, int a_TreeHeight, cNoise & a_Noise, sSetBlockVector & a_LogBlocks);
+
+/// Returns the meta for a log from the given direction
+NIBBLETYPE GetLogMetaFromDirection(NIBBLETYPE a_BlockMeta, Vector3d a_Direction);
+
/// Generates an image of a random birch tree
void GetBirchTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks);
diff --git a/src/Generating/TwoHeights.cpp b/src/Generating/TwoHeights.cpp
new file mode 100644
index 000000000..e75c301de
--- /dev/null
+++ b/src/Generating/TwoHeights.cpp
@@ -0,0 +1,121 @@
+
+// TwoHeights.cpp
+
+// Implements the cTwoHeights class representing the terrain shape generator using two switched heightmaps
+
+#include "Globals.h"
+#include "TwoHeights.h"
+#include "../Noise/InterpolNoise.h"
+#include "HeiGen.h"
+#include "../LinearUpscale.h"
+#include "../IniFile.h"
+
+
+
+
+
+class cTwoHeights:
+ public cTerrainShapeGen
+{
+ typedef cTerrainShapeGen super;
+public:
+ cTwoHeights(int a_Seed, cBiomeGenPtr a_BiomeGen):
+ m_Seed(a_Seed),
+ m_Choice(a_Seed),
+ m_HeightA(a_Seed + 1, a_BiomeGen),
+ m_HeightB(a_Seed + 2, a_BiomeGen)
+ {
+ }
+
+
+ // cTerrainShapeGen override:
+ virtual void GenShape(int a_ChunkX, int a_ChunkZ, cChunkDesc::Shape & a_Shape) override
+ {
+ // Generate the two heightmaps:
+ cChunkDef::HeightMap heightsA;
+ cChunkDef::HeightMap heightsB;
+ m_HeightA.GenHeightMap(a_ChunkX, a_ChunkZ, heightsA);
+ m_HeightB.GenHeightMap(a_ChunkX, a_ChunkZ, heightsB);
+
+ // Generate the choice noise:
+ NOISE_DATATYPE smallChoice[33 * 5 * 5];
+ NOISE_DATATYPE workspace[33 * 5 * 5];
+ NOISE_DATATYPE startX = 0;
+ NOISE_DATATYPE endX = 256 * m_FrequencyY;
+ NOISE_DATATYPE startY = a_ChunkX * cChunkDef::Width * m_FrequencyX;
+ NOISE_DATATYPE endY = (a_ChunkX * cChunkDef::Width + cChunkDef::Width + 1) * m_FrequencyX;
+ NOISE_DATATYPE startZ = a_ChunkZ * cChunkDef::Width * m_FrequencyZ;
+ NOISE_DATATYPE endZ = (a_ChunkZ * cChunkDef::Width + cChunkDef::Width + 1) * m_FrequencyZ;
+ m_Choice.Generate3D(smallChoice, 33, 5, 5, startX, endX, startY, endY, startZ, endZ, workspace);
+ NOISE_DATATYPE choice[257 * 17 * 17];
+ LinearUpscale3DArray(smallChoice, 33, 5, 5, choice, 8, 4, 4);
+
+ // Generate the shape:
+ int idxShape = 0;
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ int idxChoice = 257 * 17 * z + 257 * x;
+ NOISE_DATATYPE heightA = static_cast<NOISE_DATATYPE>(cChunkDef::GetHeight(heightsA, x, z));
+ NOISE_DATATYPE heightB = static_cast<NOISE_DATATYPE>(cChunkDef::GetHeight(heightsB, x, z));
+ for (int y = 0; y < cChunkDef::Height; y++)
+ {
+ int height = static_cast<int>(ClampedLerp(heightA, heightB, choice[idxChoice++]));
+ a_Shape[idxShape++] = (y < height) ? 1 : 0;
+ }
+ } // for x
+ } // for z
+ }
+
+
+ virtual void InitializeShapeGen(cIniFile & a_IniFile)
+ {
+ m_FrequencyX = static_cast<NOISE_DATATYPE>(a_IniFile.GetValueSetF("Generator", "TwoHeightsFrequencyX", 40));
+ m_FrequencyY = static_cast<NOISE_DATATYPE>(a_IniFile.GetValueSetF("Generator", "TwoHeightsFrequencyY", 40));
+ m_FrequencyZ = static_cast<NOISE_DATATYPE>(a_IniFile.GetValueSetF("Generator", "TwoHeightsFrequencyZ", 40));
+
+ // Initialize the two underlying height generators from an empty INI file:
+ cIniFile empty;
+ m_HeightA.InitializeHeightGen(empty);
+ m_HeightB.InitializeHeightGen(empty);
+
+ // Add the choice octaves:
+ NOISE_DATATYPE freq = 0.001f;
+ NOISE_DATATYPE ampl = 1;
+ for (int i = 0; i < 4; i++)
+ {
+ m_Choice.AddOctave(freq, ampl);
+ freq = freq * 2;
+ ampl = ampl / 2;
+ }
+ }
+
+protected:
+ int m_Seed;
+
+ /** The noise used to decide between the two heightmaps. */
+ cOctavedNoise<cInterpolNoise<Interp5Deg>> m_Choice;
+
+ /** The first height generator. */
+ cHeiGenBiomal m_HeightA;
+
+ /** The second height generator. */
+ cHeiGenBiomal m_HeightB;
+
+ /** The base frequencies for m_Choice in each of the world axis directions. */
+ NOISE_DATATYPE m_FrequencyX, m_FrequencyY, m_FrequencyZ;
+};
+
+
+
+
+
+cTerrainShapeGenPtr CreateShapeGenTwoHeights(int a_Seed, cBiomeGenPtr a_BiomeGen)
+{
+ return std::make_shared<cTwoHeights>(a_Seed, a_BiomeGen);
+}
+
+
+
+
diff --git a/src/Generating/TwoHeights.h b/src/Generating/TwoHeights.h
new file mode 100644
index 000000000..353598011
--- /dev/null
+++ b/src/Generating/TwoHeights.h
@@ -0,0 +1,23 @@
+
+// TwoHeights.h
+
+// Declares the function to create a new instance of the cTwoHeights terrain shape generator
+
+
+
+
+
+#pragma once
+
+#include "ComposableGenerator.h"
+
+
+
+
+/** Creates and returns a new instance of the cTwoHeights terrain shape generator.
+The instance must be Initialize()-d before it is used. */
+extern cTerrainShapeGenPtr CreateShapeGenTwoHeights(int a_Seed, cBiomeGenPtr a_BiomeGen);
+
+
+
+
diff --git a/src/Generating/VillageGen.cpp b/src/Generating/VillageGen.cpp
index a9b359b6a..f7d9a8316 100644
--- a/src/Generating/VillageGen.cpp
+++ b/src/Generating/VillageGen.cpp
@@ -18,8 +18,8 @@
/*
How village generating works:
-By descending from a cGridStructGen, a semi-random grid is generated. A village may be generated for each of
-the grid's cells. Each cell checks the biomes in an entire chunk around it, only generating a village if all
+By descending from a cGridStructGen, a semi-random (jitter) grid is generated. A village may be generated for each
+of the grid's cells. Each cell checks the biomes in an entire chunk around it, only generating a village if all
biomes are village-friendly. If yes, the entire village structure is built for that cell. If not, the cell
is left village-less.
@@ -125,7 +125,7 @@ public:
m_Noise(a_Seed),
m_MaxSize(a_MaxSize),
m_Density(a_Density),
- m_Borders(a_OriginX - a_MaxSize, 0, a_OriginZ - a_MaxSize, a_OriginX + a_MaxSize, 255, a_OriginZ + a_MaxSize),
+ m_Borders(a_OriginX - a_MaxSize, 0, a_OriginZ - a_MaxSize, a_OriginX + a_MaxSize, cChunkDef::Height - 1, a_OriginZ + a_MaxSize),
m_Prefabs(a_Prefabs),
m_HeightGen(a_HeightGen),
m_RoadBlock(a_RoadBlock),