summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTiger Wang <ziwei.tiger@hotmail.co.uk>2014-11-26 00:03:33 +0100
committerTiger Wang <ziwei.tiger@hotmail.co.uk>2014-11-26 00:03:33 +0100
commit187bdab4fa90fbfa5b1979ea529dc9c0deac89d9 (patch)
treece15b54701666b6f5ecd9563df8b7103d88c29b0
parentRemoved unnecessary #includes (diff)
downloadcuberite-187bdab4fa90fbfa5b1979ea529dc9c0deac89d9.tar
cuberite-187bdab4fa90fbfa5b1979ea529dc9c0deac89d9.tar.gz
cuberite-187bdab4fa90fbfa5b1979ea529dc9c0deac89d9.tar.bz2
cuberite-187bdab4fa90fbfa5b1979ea529dc9c0deac89d9.tar.lz
cuberite-187bdab4fa90fbfa5b1979ea529dc9c0deac89d9.tar.xz
cuberite-187bdab4fa90fbfa5b1979ea529dc9c0deac89d9.tar.zst
cuberite-187bdab4fa90fbfa5b1979ea529dc9c0deac89d9.zip
-rw-r--r--src/Generating/CMakeLists.txt12
-rw-r--r--src/Generating/ChunkDesc.cpp63
-rw-r--r--src/Generating/ChunkDesc.h25
-rw-r--r--src/Generating/CompoGen.cpp336
-rw-r--r--src/Generating/CompoGen.h45
-rw-r--r--src/Generating/CompoGenBiomal.cpp586
-rw-r--r--src/Generating/CompoGenBiomal.h21
-rw-r--r--src/Generating/ComposableGenerator.cpp112
-rw-r--r--src/Generating/ComposableGenerator.h95
-rw-r--r--src/Generating/CompositedHeiGen.h49
-rw-r--r--src/Generating/DistortedHeightmap.cpp565
-rw-r--r--src/Generating/DistortedHeightmap.h60
-rw-r--r--src/Generating/DungeonRoomsFinisher.cpp25
-rw-r--r--src/Generating/DungeonRoomsFinisher.h8
-rw-r--r--src/Generating/EndGen.cpp44
-rw-r--r--src/Generating/EndGen.h8
-rw-r--r--src/Generating/FinishGen.cpp2
-rw-r--r--src/Generating/HeiGen.cpp92
-rw-r--r--src/Generating/HeiGen.h98
-rw-r--r--src/Generating/Noise3DGenerator.cpp407
-rw-r--r--src/Generating/Noise3DGenerator.h51
-rw-r--r--src/Generating/ShapeGen.cpp145
-rw-r--r--src/Generating/StructGen.cpp36
-rw-r--r--src/Generating/StructGen.h33
-rw-r--r--src/Generating/TwoHeights.cpp121
-rw-r--r--src/Generating/TwoHeights.h23
-rw-r--r--src/Generating/VillageGen.cpp6
-rw-r--r--src/Globals.h4
-rw-r--r--src/Mobs/Monster.cpp4
-rw-r--r--src/Mobs/Monster.h8
-rw-r--r--src/World.cpp255
-rw-r--r--src/World.h2
32 files changed, 1688 insertions, 1653 deletions
diff --git a/src/Generating/CMakeLists.txt b/src/Generating/CMakeLists.txt
index 1a26bd0d5..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,7 +42,9 @@ SET (HDRS
ChunkDesc.h
ChunkGenerator.h
CompoGen.h
+ CompoGenBiomal.h
ComposableGenerator.h
+ CompositedHeiGen.h
DistortedHeightmap.h
DungeonRoomsFinisher.h
EndGen.h
@@ -58,11 +63,14 @@ SET (HDRS
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/ChunkDesc.cpp b/src/Generating/ChunkDesc.cpp
index 042b688b7..4a5ac5a18 100644
--- a/src/Generating/ChunkDesc.cpp
+++ b/src/Generating/ChunkDesc.cpp
@@ -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/CompoGen.cpp b/src/Generating/CompoGen.cpp
index 29b831dfd..23cc64d78 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();
@@ -696,7 +382,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 +417,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 +426,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 +437,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 c65a07fe8..3847688cd 100644
--- a/src/Generating/CompoGen.h
+++ b/src/Generating/CompoGen.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..995851c95
--- /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 (size_t i = 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 5f46574c7..4192dfa72 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,63 +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 = new cCompoGenSameBlock;
+ res = CreateCompoGenBiomal(a_Seed);
}
- else if (NoCaseCompare(CompoGenName, "debugbiomes") == 0)
+ else if (NoCaseCompare(CompoGenName, "BiomalNoise3D") == 0)
{
- res = new cCompoGenDebugBiomes;
+ // The composition that used to be provided with BiomalNoise3D is now provided by the Biomal compositor:
+ res = CreateCompoGenBiomal(a_Seed);
}
- else if (NoCaseCompare(CompoGenName, "classic") == 0)
+ else if (NoCaseCompare(CompoGenName, "Classic") == 0)
{
- res = new cCompoGenClassic;
+ res = std::make_shared<cCompoGenClassic>();
}
- else if (NoCaseCompare(CompoGenName, "DistortedHeightmap") == 0)
+ else if (NoCaseCompare(CompoGenName, "DebugBiomes") == 0)
{
- res = new cDistortedHeightmap(a_Seed, a_BiomeGen);
+ res = std::make_shared<cCompoGenDebugBiomes>();
}
- else if (NoCaseCompare(CompoGenName, "end") == 0)
+ else if (NoCaseCompare(CompoGenName, "DistortedHeightmap") == 0)
{
- res = new cEndGen(a_Seed);
+ // The composition that used to be provided with DistortedHeightmap is now provided by the Biomal compositor:
+ res = CreateCompoGenBiomal(a_Seed);
}
- else if (NoCaseCompare(CompoGenName, "nether") == 0)
+ else if (NoCaseCompare(CompoGenName, "End") == 0)
{
- res = new cCompoGenNether(a_Seed);
+ res = std::make_shared<cEndGen>(a_Seed);
}
- else if (NoCaseCompare(CompoGenName, "BiomalNoise3D") == 0)
+ else if (NoCaseCompare(CompoGenName, "Nether") == 0)
{
- res = new cBiomalNoise3DComposable(a_Seed, a_BiomeGen);
+ 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);
@@ -124,7 +117,7 @@ cTerrainCompositionGenPtr cTerrainCompositionGen::CreateCompositionGen(cIniFile
cComposableGenerator::cComposableGenerator(cChunkGenerator & a_ChunkGenerator) :
super(a_ChunkGenerator),
m_BiomeGen(),
- m_HeightGen(),
+ m_ShapeGen(),
m_CompositionGen()
{
}
@@ -138,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);
}
@@ -166,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())
@@ -234,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)
@@ -253,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));
}
+ */
}
@@ -261,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.
}
@@ -333,7 +341,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)
{
@@ -342,7 +350,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)
{
@@ -580,7 +588,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)
{
@@ -588,7 +596,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)
{
@@ -598,12 +606,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 d5bc6ab55..e1ed9b450 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.
@@ -237,7 +80,7 @@ const cDistortedHeightmap::sGenParam cDistortedHeightmap::m_GenParam[256] =
{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
@@ -279,8 +122,6 @@ 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_BiomeGen(a_BiomeGen),
m_UnderlyingHeiGen(new cHeiGenBiomal(a_Seed, a_BiomeGen)),
m_HeightGen(m_UnderlyingHeiGen, 64),
@@ -293,8 +134,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 +148,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 +160,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 +230,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 +250,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 +375,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 955e57a1e..79fc35542 100644
--- a/src/Generating/DistortedHeightmap.h
+++ b/src/Generating/DistortedHeightmap.h
@@ -23,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:
@@ -51,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;
@@ -70,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:
@@ -87,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..7ab22c2c5 100644
--- a/src/Generating/DungeonRoomsFinisher.cpp
+++ b/src/Generating/DungeonRoomsFinisher.cpp
@@ -78,7 +78,8 @@ protected:
- /** 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)
@@ -258,9 +259,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 +294,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 c2278c1e1..f9e3f6e53 100644
--- a/src/Generating/EndGen.h
+++ b/src/Generating/EndGen.h
@@ -17,7 +17,7 @@
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 42ecdf8a8..2ff3e7f67 100644
--- a/src/Generating/FinishGen.cpp
+++ b/src/Generating/FinishGen.cpp
@@ -411,7 +411,7 @@ 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);
diff --git a/src/Generating/HeiGen.cpp b/src/Generating/HeiGen.cpp
index 1d9f1e3aa..61d087c17 100644
--- a/src/Generating/HeiGen.cpp
+++ b/src/Generating/HeiGen.cpp
@@ -15,7 +15,6 @@
-
////////////////////////////////////////////////////////////////////////////////
// cHeiGenFlat:
@@ -133,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++)
@@ -160,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) :
@@ -750,43 +785,51 @@ cTerrainHeightGenPtr cTerrainHeightGen::CreateHeightGen(cIniFile & a_IniFile, cB
}
a_CacheOffByDefault = false;
- cTerrainHeightGen * res = nullptr;
- if (NoCaseCompare(HeightGenName, "flat") == 0)
+ cTerrainHeightGenPtr res;
+ if (NoCaseCompare(HeightGenName, "Flat") == 0)
{
- res = new cHeiGenFlat;
+ 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 = new cHeiGenClassic(a_Seed);
+ res = std::make_shared<cHeiGenClassic>(a_Seed);
}
else if (NoCaseCompare(HeightGenName, "DistortedHeightmap") == 0)
{
- res = new cDistortedHeightmap(a_Seed, a_BiomeGen);
+ // 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)
{
- res = new cEndGen(a_Seed);
+ // 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 = new cHeiGenMinMax(a_Seed, a_BiomeGen);
+ res = std::make_shared<cHeiGenMinMax>(a_Seed, a_BiomeGen);
}
else if (NoCaseCompare(HeightGenName, "Mountains") == 0)
{
- res = new cHeiGenMountains(a_Seed);
+ res = std::make_shared<cHeiGenMountains>(a_Seed);
}
else if (NoCaseCompare(HeightGenName, "BiomalNoise3D") == 0)
{
- res = new cBiomalNoise3DComposable(a_Seed, a_BiomeGen);
+ // 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)
{
- res = new cNoise3DComposable(a_Seed);
+ // 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)
+ else if (NoCaseCompare(HeightGenName, "Biomal") == 0)
{
- res = new cHeiGenBiomal(a_Seed, a_BiomeGen);
+ res = std::make_shared<cHeiGenBiomal>(a_Seed, a_BiomeGen);
/*
// Performance-testing:
@@ -805,15 +848,14 @@ cTerrainHeightGenPtr cTerrainHeightGen::CreateHeightGen(cIniFile & a_IniFile, cB
{
// 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);
+
+ return res;
}
diff --git a/src/Generating/HeiGen.h b/src/Generating/HeiGen.h
index b3c9483fb..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
*/
@@ -21,26 +23,7 @@ Interfaces to the various height generators:
-class cHeiGenFlat :
- public cTerrainHeightGen
-{
-public:
- cHeiGenFlat(void) : m_Height(5) {}
-
-protected:
-
- int m_Height;
-
- // cTerrainHeightGen overrides:
- virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override;
- virtual void InitializeHeightGen(cIniFile & a_IniFile) override;
-} ;
-
-
-
-
-
-/// A simple cache that stores N most recently generated chunks' heightmaps; N being settable upon creation
+/** 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/Noise3DGenerator.cpp b/src/Generating/Noise3DGenerator.cpp
index c8f87ec8e..5cf46b873 100644
--- a/src/Generating/Noise3DGenerator.cpp
+++ b/src/Generating/Noise3DGenerator.cpp
@@ -165,69 +165,6 @@ cNoise3DGenerator::cNoise3DGenerator(cChunkGenerator & a_ChunkGenerator) :
m_Cubic.AddOctave(4, 0.25);
m_Cubic.AddOctave(8, 0.125);
m_Cubic.AddOctave(16, 0.0625);
-
- #if 0
- // DEBUG: Test the noise generation:
- // NOTE: In order to be able to run MCS with this code, you need to increase the default thread stack size
- // In MSVC, it is done in Project Settings -> Configuration Properties -> Linker -> System, set Stack reserve size to at least 64M
- m_SeaLevel = 62;
- m_HeightAmplification = 0;
- m_MidPoint = 75;
- m_FrequencyX = 4;
- m_FrequencyY = 4;
- m_FrequencyZ = 4;
- m_AirThreshold = 0.5;
-
- const int NumChunks = 4;
- NOISE_DATATYPE Noise[NumChunks][cChunkDef::Width * cChunkDef::Width * cChunkDef::Height];
- for (int x = 0; x < NumChunks; x++)
- {
- GenerateNoiseArray(x, 5, Noise[x]);
- }
-
- // Save in XY cuts:
- cFile f1;
- if (f1.Open("Test_XY.grab", cFile::fmWrite))
- {
- for (int z = 0; z < cChunkDef::Width; z++)
- {
- for (int y = 0; y < cChunkDef::Height; y++)
- {
- for (int i = 0; i < NumChunks; i++)
- {
- int idx = y * cChunkDef::Width + z * cChunkDef::Width * cChunkDef::Height;
- unsigned char buf[cChunkDef::Width];
- for (int x = 0; x < cChunkDef::Width; x++)
- {
- buf[x] = (unsigned char)(std::min(256, std::max(0, (int)(128 + 32 * Noise[i][idx++]))));
- }
- f1.Write(buf, cChunkDef::Width);
- }
- } // for y
- } // for z
- } // if (XY file open)
-
- cFile f2;
- if (f2.Open("Test_XZ.grab", cFile::fmWrite))
- {
- for (int y = 0; y < cChunkDef::Height; y++)
- {
- for (int z = 0; z < cChunkDef::Width; z++)
- {
- for (int i = 0; i < NumChunks; i++)
- {
- int idx = y * cChunkDef::Width + z * cChunkDef::Width * cChunkDef::Height;
- unsigned char buf[cChunkDef::Width];
- for (int x = 0; x < cChunkDef::Width; x++)
- {
- buf[x] = (unsigned char)(std::min(256, std::max(0, (int)(128 + 32 * Noise[i][idx++]))));
- }
- f2.Write(buf, cChunkDef::Width);
- }
- } // for z
- } // for y
- } // if (XZ file open)
- #endif // 0
}
@@ -246,7 +183,7 @@ cNoise3DGenerator::~cNoise3DGenerator()
void cNoise3DGenerator::Initialize(cIniFile & a_IniFile)
{
// Params:
- m_SeaLevel = a_IniFile.GetValueSetI("Generator", "Noise3DSeaLevel", 62);
+ 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);
@@ -454,7 +391,6 @@ void cNoise3DComposable::Initialize(cIniFile & a_IniFile)
{
// Params:
// The defaults generate extreme hills terrain
- m_SeaLevel = a_IniFile.GetValueSetI("Generator", "Noise3DSeaLevel", 62);
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", 40);
@@ -522,110 +458,60 @@ void cNoise3DComposable::GenerateNoiseArrayIfNeeded(int a_ChunkX, int a_ChunkZ)
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 coords, because noise generator uses [x + SizeX * y + SizeX * SizeY * z] ordering and we want "BlockY" to be "z":
- m_ChoiceNoise.Generate3D (ChoiceNoise, 5, 5, 33, BlockX / m_ChoiceFrequencyX, (BlockX + 17) / m_ChoiceFrequencyX, BlockZ / m_ChoiceFrequencyZ, (BlockZ + 17) / m_ChoiceFrequencyZ, 0, 257 / m_ChoiceFrequencyY, Workspace);
- m_DensityNoiseA.Generate3D(DensityNoiseA, 5, 5, 33, BlockX / m_FrequencyX, (BlockX + 17) / m_FrequencyX, BlockZ / m_FrequencyZ, (BlockZ + 17) / m_FrequencyZ, 0, 257 / m_FrequencyY, Workspace);
- m_DensityNoiseB.Generate3D(DensityNoiseB, 5, 5, 33, BlockX / m_FrequencyX, (BlockX + 17) / m_FrequencyX, BlockZ / m_FrequencyZ, (BlockZ + 17) / m_FrequencyZ, 0, 257 / m_FrequencyY, Workspace);
+ // 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 y = 0; y < 33; y++)
+ for (int z = 0; z < 5; z++)
{
- 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;
- }
-
- for (int z = 0; z < 5; z++)
+ for (int x = 0; x < 5; x++)
{
- for (int x = 0; x < 5; x++)
+ NOISE_DATATYPE curBaseNoise = BaseNoise[x + 5 * z];
+ for (int y = 0; y < 33; y++)
{
- int idx = x + 5 * z + 5 * 5 * y;
- Workspace[idx] = ClampedLerp(DensityNoiseA[idx], DensityNoiseB[idx], 8 * (ChoiceNoise[idx] + 0.5f)) + AddHeight + BaseNoise[x + 5 * z];
- }
- }
- }
- LinearUpscale3DArray<NOISE_DATATYPE>(Workspace, 5, 5, 33, m_NoiseArray, 4, 4, 8);
-}
+ 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;
+ }
-
-
-
-void cNoise3DComposable::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap)
-{
- GenerateNoiseArrayIfNeeded(a_ChunkX, a_ChunkZ);
-
- for (int z = 0; z < cChunkDef::Width; z++)
- {
- for (int x = 0; x < cChunkDef::Width; x++)
- {
- cChunkDef::SetHeight(a_HeightMap, x, z, m_SeaLevel);
- for (int y = cChunkDef::Height - 1; y > m_SeaLevel; y--)
- {
- if (m_NoiseArray[y * 17 * 17 + z * 17 + x] <= m_AirThreshold)
+ // If too high, cut off any terrain:
+ if (y > 28)
{
- cChunkDef::SetHeight(a_HeightMap, x, z, y);
- break;
+ AddHeight = AddHeight + static_cast<NOISE_DATATYPE>(y - 28) / 4;
}
- } // for y
- } // for x
- } // for z
+
+ // 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);
}
-void cNoise3DComposable::ComposeTerrain(cChunkDesc & a_ChunkDesc)
+void cNoise3DComposable::GenShape(int a_ChunkX, int a_ChunkZ, cChunkDesc::Shape & a_Shape)
{
- GenerateNoiseArrayIfNeeded(a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ());
-
- a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0);
+ GenerateNoiseArrayIfNeeded(a_ChunkX, a_ChunkZ);
- // Make basic terrain composition:
+ // 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
}
@@ -650,7 +536,7 @@ cBiomalNoise3DComposable::cBiomalNoise3DComposable(int a_Seed, cBiomeGenPtr a_Bi
{
for (int x = 0; x <= AVERAGING_SIZE * 2; x++)
{
- m_Weight[z][x] = static_cast<NOISE_DATATYPE>((5 - std::abs(5 - x)) + (5 - std::abs(5 - z)));
+ 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];
}
}
@@ -664,7 +550,7 @@ void cBiomalNoise3DComposable::Initialize(cIniFile & a_IniFile)
{
// Params:
// The defaults generate extreme hills terrain
- m_SeaLevel = a_IniFile.GetValueSetI("Generator", "BiomalNoise3DSeaLevel", 62);
+ 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);
@@ -735,34 +621,42 @@ void cBiomalNoise3DComposable::GenerateNoiseArrayIfNeeded(int a_ChunkX, int a_Ch
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 coords, because noise generator uses [x + SizeX * y + SizeX * SizeY * z] ordering and we want "BlockY" to be "z":
- m_ChoiceNoise.Generate3D (ChoiceNoise, 5, 5, 33, BlockX / m_ChoiceFrequencyX, (BlockX + 17) / m_ChoiceFrequencyX, BlockZ / m_ChoiceFrequencyZ, (BlockZ + 17) / m_ChoiceFrequencyZ, 0, 257 / m_ChoiceFrequencyY, Workspace);
- m_DensityNoiseA.Generate3D(DensityNoiseA, 5, 5, 33, BlockX / m_FrequencyX, (BlockX + 17) / m_FrequencyX, BlockZ / m_FrequencyZ, (BlockZ + 17) / m_FrequencyZ, 0, 257 / m_FrequencyY, Workspace);
- m_DensityNoiseB.Generate3D(DensityNoiseB, 5, 5, 33, BlockX / m_FrequencyX, (BlockX + 17) / m_FrequencyX, BlockZ / m_FrequencyZ, (BlockZ + 17) / m_FrequencyZ, 0, 257 / m_FrequencyY, Workspace);
+ // 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 y = 0; y < 33; y++)
+ for (int z = 0; z < 5; z++)
{
- NOISE_DATATYPE BlockHeight = static_cast<NOISE_DATATYPE>(y * 8);
- for (int z = 0; z < 5; z++)
+ for (int x = 0; x < 5; x++)
{
- for (int x = 0; x < 5; x++)
+ 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++)
{
- NOISE_DATATYPE AddHeight = (BlockHeight - MidPoint[x + 5 * z]) * HeightAmp[x + 5 * z];
+ 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)
{
AddHeight *= 4;
}
+ // If too high, cut off any terrain:
+ if (y > 28)
+ {
+ AddHeight = AddHeight + static_cast<NOISE_DATATYPE>(y - 28) / 4;
+ }
- int idx = x + 5 * z + 5 * 5 * y;
- Workspace[idx] = ClampedLerp(DensityNoiseA[idx], DensityNoiseB[idx], 8 * (ChoiceNoise[idx] + 0.5f)) + AddHeight + BaseNoise[x + 5 * z];
+ // 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, 5, 5, 33, m_NoiseArray, 4, 4, 8);
+ LinearUpscale3DArray<NOISE_DATATYPE>(Workspace, 33, 5, 5, m_NoiseArray, 8, 4, 4);
}
@@ -820,72 +714,67 @@ void cBiomalNoise3DComposable::GetBiomeParams(EMCSBiome a_Biome, NOISE_DATATYPE
{
switch (a_Biome)
{
- case biBeach: a_HeightAmp = 0.3f; a_MidPoint = 62; 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 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 biEnd: a_HeightAmp = 0.15f; a_MidPoint = 64; break;
- case biExtremeHills: a_HeightAmp = 0.045f; a_MidPoint = 75; break;
- case biExtremeHillsPlus: 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 = 53; break;
- case biFrozenOcean: a_HeightAmp = 0.17f; a_MidPoint = 47; 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 biJungleHills: a_HeightAmp = 0.075f; a_MidPoint = 68; break;
- case biJungleM: a_HeightAmp = 0.1f; a_MidPoint = 63; break;
- case biMegaSpruceTaigaHills: a_HeightAmp = 0.075f; a_MidPoint = 68; break;
- case biMegaTaigaHills: a_HeightAmp = 0.075f; a_MidPoint = 68; break;
- case biMushroomShore: a_HeightAmp = 0.15f; a_MidPoint = 15; break;
- case biOcean: a_HeightAmp = 0.3f; a_MidPoint = 62; break;
- case biPlains: a_HeightAmp = 0.3f; a_MidPoint = 62; break;
- case biRiver: a_HeightAmp = 0.4f; a_MidPoint = 53; break;
- case biSwampland: a_HeightAmp = 0.25f; a_MidPoint = 59; break;
- case biSwamplandM: a_HeightAmp = 0.11f; a_MidPoint = 59; break;
- case biTaigaHills: a_HeightAmp = 0.075f; a_MidPoint = 68; break;
-
- /*
- // Still missing:
- case biColdTaiga: a_HeightAmp = 0.15f; a_MidPoint = 30; break;
- case biColdTaigaHills: a_HeightAmp = 0.15f; a_MidPoint = 31; break;
- case biColdTaigaM: a_HeightAmp = 0.15f; a_MidPoint = 70; break;
- case biDesertM: a_HeightAmp = 0.15f; a_MidPoint = 70; break;
- case biExtremeHillsEdge: a_HeightAmp = 0.15f; a_MidPoint = 20; break;
- case biExtremeHillsM: a_HeightAmp = 0.15f; a_MidPoint = 70; break;
- case biExtremeHillsPlusM: a_HeightAmp = 0.15f; a_MidPoint = 70; break;
- case biJungleEdge: a_HeightAmp = 0.15f; a_MidPoint = 23; break;
- case biJungleEdgeM: a_HeightAmp = 0.15f; a_MidPoint = 70; break;
- case biMegaSpruceTaiga: a_HeightAmp = 0.15f; a_MidPoint = 70; break;
- case biMegaTaiga: a_HeightAmp = 0.15f; a_MidPoint = 32; break;
- case biMesa: a_HeightAmp = 0.15f; a_MidPoint = 37; break;
- case biMesaBryce: a_HeightAmp = 0.15f; a_MidPoint = 70; break;
- case biMesaPlateau: a_HeightAmp = 0.15f; a_MidPoint = 39; break;
- case biMesaPlateauF: a_HeightAmp = 0.15f; a_MidPoint = 38; break;
- case biMesaPlateauFM: a_HeightAmp = 0.15f; a_MidPoint = 70; break;
- case biMesaPlateauM: a_HeightAmp = 0.15f; a_MidPoint = 70; break;
- case biMushroomIsland: a_HeightAmp = 0.15f; a_MidPoint = 14; break;
- case biNether: a_HeightAmp = 0.15f; a_MidPoint = 68; break;
- case biRoofedForest: a_HeightAmp = 0.15f; a_MidPoint = 29; break;
- case biRoofedForestM: a_HeightAmp = 0.15f; a_MidPoint = 70; break;
- case biSavanna: a_HeightAmp = 0.15f; a_MidPoint = 35; break;
- case biSavannaM: a_HeightAmp = 0.15f; a_MidPoint = 70; break;
- case biSavannaPlateau: a_HeightAmp = 0.15f; a_MidPoint = 36; break;
- case biSavannaPlateauM: a_HeightAmp = 0.15f; a_MidPoint = 70; break;
- case biStoneBeach: a_HeightAmp = 0.15f; a_MidPoint = 25; break;
- case biSunflowerPlains: a_HeightAmp = 0.15f; a_MidPoint = 70; break;
- case biTaiga: a_HeightAmp = 0.15f; a_MidPoint = 65; break;
- case biTaigaM: a_HeightAmp = 0.15f; a_MidPoint = 70; break;
- */
-
+ 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 = 57; 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 = 57; 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
@@ -899,78 +788,19 @@ void cBiomalNoise3DComposable::GetBiomeParams(EMCSBiome a_Biome, NOISE_DATATYPE
-
-void cBiomalNoise3DComposable::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap)
+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++)
{
- cChunkDef::SetHeight(a_HeightMap, x, z, m_SeaLevel);
- for (int y = cChunkDef::Height - 1; y > m_SeaLevel; y--)
- {
- if (m_NoiseArray[y * 17 * 17 + z * 17 + x] <= m_AirThreshold)
- {
- cChunkDef::SetHeight(a_HeightMap, x, z, y);
- break;
- }
- } // for y
- } // for x
- } // for z
-}
-
-
-
-
-
-void cBiomalNoise3DComposable::ComposeTerrain(cChunkDesc & a_ChunkDesc)
-{
- GenerateNoiseArrayIfNeeded(a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ());
-
- a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0);
-
- // Make basic terrain composition:
- for (int z = 0; z < cChunkDef::Width; z++)
- {
- for (int x = 0; x < cChunkDef::Width; x++)
- {
- int LastAir = a_ChunkDesc.GetHeight(x, z) + 1;
- bool HasHadWater = false;
- for (int y = LastAir; y < m_SeaLevel; y++)
+ 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
}
@@ -978,3 +808,4 @@ void cBiomalNoise3DComposable::ComposeTerrain(cChunkDesc & a_ChunkDesc)
+
diff --git a/src/Generating/Noise3DGenerator.h b/src/Generating/Noise3DGenerator.h
index 07767ba84..35b1e4c94 100644
--- a/src/Generating/Noise3DGenerator.h
+++ b/src/Generating/Noise3DGenerator.h
@@ -73,8 +73,7 @@ protected:
class cNoise3DComposable :
- public cTerrainHeightGen,
- public cTerrainCompositionGen
+ public cTerrainShapeGen
{
public:
cNoise3DComposable(int a_Seed);
@@ -82,21 +81,18 @@ public:
void Initialize(cIniFile & a_IniFile);
protected:
- /** The noise that is used to choose between density noise A and B. */
- cPerlinNoise m_ChoiceNoise;
+ /** The 3D noise that is used to choose between density noise A and B. */
+ cOctavedNoise<cInterpolNoise<Interp5Deg>> m_ChoiceNoise;
/** Density 3D noise, variant A. */
- cPerlinNoise m_DensityNoiseA;
+ cOctavedNoise<cInterpolNoise<Interp5Deg>> m_DensityNoiseA;
/** Density 3D noise, variant B. */
- cPerlinNoise m_DensityNoiseB;
+ cOctavedNoise<cInterpolNoise<Interp5Deg>> m_DensityNoiseB;
/** Heightmap-like noise used to provide variance for low-amplitude biomes. */
- cPerlinNoise m_BaseNoise;
+ cOctavedNoise<cInterpolNoise<Interp5Deg>> m_BaseNoise;
- /** Block height of the sealevel, used for composing the terrain. */
- 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;
@@ -131,12 +127,8 @@ protected:
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 InitializeHeightGen(cIniFile & a_IniFile) override { Initialize(a_IniFile); }
-
- // cTerrainCompositionGen overrides:
- virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override;
- virtual void InitializeCompoGen(cIniFile & a_IniFile) override { Initialize(a_IniFile); }
+ virtual void GenShape(int a_ChunkX, int a_ChunkZ, cChunkDesc::Shape & a_Shape) override;
+ virtual void InitializeShapeGen(cIniFile & a_IniFile) override { Initialize(a_IniFile); }
} ;
@@ -144,8 +136,7 @@ protected:
class cBiomalNoise3DComposable :
- public cTerrainHeightGen,
- public cTerrainCompositionGen
+ public cTerrainShapeGen
{
public:
cBiomalNoise3DComposable(int a_Seed, cBiomeGenPtr a_BiomeGen);
@@ -153,24 +144,24 @@ public:
void Initialize(cIniFile & a_IniFile);
protected:
- /** Number of columns around the pixel to query for biomes for averaging. */
- static const int AVERAGING_SIZE = 5;
+ /** 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];
/** The noise that is used to choose between density noise A and B. */
- cPerlinNoise m_ChoiceNoise;
+ cOctavedNoise<cInterpolNoise<Interp5Deg>> m_ChoiceNoise;
/** Density 3D noise, variant A. */
- cPerlinNoise m_DensityNoiseA;
+ cOctavedNoise<cInterpolNoise<Interp5Deg>> m_DensityNoiseA;
/** Density 3D noise, variant B. */
- cPerlinNoise m_DensityNoiseB;
+ cOctavedNoise<cInterpolNoise<Interp5Deg>> m_DensityNoiseB;
/** Heightmap-like noise used to provide variance for low-amplitude biomes. */
- cPerlinNoise m_BaseNoise;
+ cOctavedNoise<cInterpolNoise<Interp5Deg>> m_BaseNoise;
/** The underlying biome generator. */
cBiomeGenPtr m_BiomeGen;
@@ -198,7 +189,7 @@ protected:
// 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
+ 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];
@@ -216,13 +207,9 @@ protected:
/** Returns the parameters for the specified biome. */
void GetBiomeParams(EMCSBiome a_Biome, NOISE_DATATYPE & a_HeightAmp, NOISE_DATATYPE & a_MidPoint);
- // cTerrainHeightGen overrides:
- virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override;
- virtual void InitializeHeightGen(cIniFile & a_IniFile) override { Initialize(a_IniFile); }
-
- // cTerrainCompositionGen overrides:
- virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override;
- virtual void InitializeCompoGen(cIniFile & a_IniFile) override { Initialize(a_IniFile); }
+ // 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/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 d3b0b5544..796abf0f5 100644
--- a/src/Generating/StructGen.h
+++ b/src/Generating/StructGen.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/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),
diff --git a/src/Globals.h b/src/Globals.h
index a64be8d54..fcadc3269 100644
--- a/src/Globals.h
+++ b/src/Globals.h
@@ -184,9 +184,9 @@ template class SizeChecker<UInt16, 2>;
// OS-dependent stuff:
#ifdef _WIN32
- #define WIN32_LEAN_AND_MEAN
- #define _WIN32_WINNT_WS03 // We want to target Windows XP with Service Pack 2 & Windows Server 2003 with Service Pack 1 and higher
+ #define WIN32_LEAN_AND_MEAN
+ #define _WIN32_WINNT _WIN32_WINNT_WS03 // We want to target Windows XP with Service Pack 2 & Windows Server 2003 with Service Pack 1 and higher
#include <Windows.h>
#include <winsock2.h>
diff --git a/src/Mobs/Monster.cpp b/src/Mobs/Monster.cpp
index bcdc404c2..4f8afa0de 100644
--- a/src/Mobs/Monster.cpp
+++ b/src/Mobs/Monster.cpp
@@ -159,7 +159,7 @@ void cMonster::TickPathFinding()
BLOCKTYPE BlockAtYP = m_World->GetBlock(gCrossCoords[i].x + PosX, PosY + 1, gCrossCoords[i].z + PosZ);
BLOCKTYPE BlockAtYPP = m_World->GetBlock(gCrossCoords[i].x + PosX, PosY + 2, gCrossCoords[i].z + PosZ);
int LowestY = FindFirstNonAirBlockPosition(gCrossCoords[i].x + PosX, gCrossCoords[i].z + PosZ);
- BLOCKTYPE BlockAtLowestY = m_World->GetBlock(gCrossCoords[i].x + PosX, LowestY, gCrossCoords[i].z + PosZ);
+ BLOCKTYPE BlockAtLowestY = (LowestY >= cChunkDef::Height) ? E_BLOCK_AIR : m_World->GetBlock(gCrossCoords[i].x + PosX, LowestY, gCrossCoords[i].z + PosZ);
if (
(!cBlockInfo::IsSolid(BlockAtY)) &&
@@ -452,7 +452,7 @@ int cMonster::FindFirstNonAirBlockPosition(double a_PosX, double a_PosZ)
}
else
{
- while (cBlockInfo::IsSolid(m_World->GetBlock((int)floor(a_PosX), PosY, (int)floor(a_PosZ))) && (PosY < cChunkDef::Height))
+ while ((PosY < cChunkDef::Height) && cBlockInfo::IsSolid(m_World->GetBlock((int)floor(a_PosX), PosY, (int)floor(a_PosZ))))
{
PosY++;
}
diff --git a/src/Mobs/Monster.h b/src/Mobs/Monster.h
index f5ae2cb4d..e5dcb0309 100644
--- a/src/Mobs/Monster.h
+++ b/src/Mobs/Monster.h
@@ -169,10 +169,12 @@ protected:
/** Stores if mobile is currently moving towards the ultimate, final destination */
bool m_bMovingToDestination;
- /** Finds the first non-air block position (not the highest, as cWorld::GetHeight does)
- If current Y is nonsolid, goes down to try to find a solid block, then returns that + 1
- If current Y is solid, goes up to find first nonsolid block, and returns that */
+ /** Finds the lowest non-air block position (not the highest, as cWorld::GetHeight does)
+ If current Y is nonsolid, goes down to try to find a solid block, then returns that + 1
+ If current Y is solid, goes up to find first nonsolid block, and returns that.
+ If no suitable position is found, returns cChunkDef::Height. */
int FindFirstNonAirBlockPosition(double a_PosX, double a_PosZ);
+
/** Returns if a monster can actually reach a given height by jumping or walking */
inline bool IsNextYPosReachable(int a_PosY)
{
diff --git a/src/World.cpp b/src/World.cpp
index 09fb90a12..1602de4bb 100644
--- a/src/World.cpp
+++ b/src/World.cpp
@@ -72,99 +72,130 @@ const int TIME_SPAWN_DIVISOR = 148;
////////////////////////////////////////////////////////////////////////////////
-// cWorldLoadProgress:
+// cSpawnPrepare:
-/// A simple thread that displays the progress of world loading / saving in cWorld::InitializeSpawn()
-class cWorldLoadProgress :
- public cIsThread
+/** Generates and lights the spawn area of the world. Runs as a separate thread. */
+class cSpawnPrepare:
+ public cIsThread,
+ public cChunkCoordCallback
{
+ typedef cIsThread super;
+
public:
- cWorldLoadProgress(cWorld * a_World) :
- cIsThread("cWorldLoadProgress"),
- m_World(a_World)
- {
+ cSpawnPrepare(cWorld & a_World, int a_SpawnChunkX, int a_SpawnChunkZ, int a_PrepareDistance):
+ super("SpawnPrepare"),
+ m_World(a_World),
+ m_SpawnChunkX(a_SpawnChunkX),
+ m_SpawnChunkZ(a_SpawnChunkZ),
+ m_PrepareDistance(a_PrepareDistance),
+ m_MaxIdx(a_PrepareDistance * a_PrepareDistance),
+ m_NumPrepared(0),
+ m_LastReportChunkCount(0)
+ {
+ // Start the thread:
Start();
+
+ // Wait for start confirmation, so that the thread can be waited-upon after the constructor returns:
+ m_EvtStarted.Wait();
}
-
- void Stop(void)
- {
- m_ShouldTerminate = true;
- Wait();
- }
-
-protected:
- cWorld * m_World;
-
+
+ // cIsThread override:
virtual void Execute(void) override
{
- for (;;)
+ // Confirm thread start:
+ m_EvtStarted.Set();
+
+ // Queue the initial chunks:
+ m_MaxIdx = m_PrepareDistance * m_PrepareDistance;
+ int maxQueue = std::min(m_MaxIdx - 1, 100); // Number of chunks to queue at once
+ m_NextIdx = maxQueue;
+ m_LastReportTime = std::chrono::steady_clock::now();
+ for (int i = 0; i < maxQueue; i++)
{
- LOG("" SIZE_T_FMT " chunks to load, %d chunks to generate",
- m_World->GetStorage().GetLoadQueueLength(),
- m_World->GetGenerator().GetQueueLength()
- );
-
- // Wait for 2 sec, but be "reasonably wakeable" when the thread is to finish
- for (int i = 0; i < 20; i++)
- {
- std::this_thread::sleep_for(std::chrono::milliseconds(100));
- if (m_ShouldTerminate)
- {
- return;
- }
- }
- } // for (-ever)
+ int chunkX, chunkZ;
+ DecodeChunkCoords(i, chunkX, chunkZ);
+ m_World.GetLightingThread().QueueChunk(chunkX, chunkZ, this);
+ } // for i
+
+ // Wait for the lighting thread to prepare everything. Event is set in the Call() callback:
+ m_EvtFinished.Wait();
}
-
-} ;
+protected:
+ cWorld & m_World;
+ int m_SpawnChunkX;
+ int m_SpawnChunkZ;
+ int m_PrepareDistance;
+ /** The index of the next chunk to be queued in the lighting thread. */
+ int m_NextIdx;
+ /** The maximum index of the prepared chunks. Queueing stops when m_NextIdx reaches this number. */
+ int m_MaxIdx;
+ /** Total number of chunks already finished preparing. Preparation finishes when this number reaches m_MaxIdx. */
+ int m_NumPrepared;
-////////////////////////////////////////////////////////////////////////////////
-// cWorldLightingProgress:
+ /** Event used to signal that the thread has started. */
+ cEvent m_EvtStarted;
-/// A simple thread that displays the progress of world lighting in cWorld::InitializeSpawn()
-class cWorldLightingProgress :
- public cIsThread
-{
-public:
- cWorldLightingProgress(cLightingThread * a_Lighting) :
- cIsThread("cWorldLightingProgress"),
- m_Lighting(a_Lighting)
- {
- Start();
- }
-
- void Stop(void)
+ /** Event used to signal that the preparation is finished. */
+ cEvent m_EvtFinished;
+
+ /** The timestamp of the last progress report emitted. */
+ std::chrono::steady_clock::time_point m_LastReportTime;
+
+ /** Number of chunks prepared when the last progress report was emitted. */
+ int m_LastReportChunkCount;
+
+ // cChunkCoordCallback override:
+ virtual void Call(int a_ChunkX, int a_ChunkZ)
{
- m_ShouldTerminate = true;
- Wait();
+ // Check if this was the last chunk:
+ m_NumPrepared += 1;
+ if (m_NumPrepared >= m_MaxIdx)
+ {
+ m_EvtFinished.Set();
+ }
+
+ // Queue another chunk, if appropriate:
+ if (m_NextIdx < m_MaxIdx)
+ {
+ int chunkX, chunkZ;
+ DecodeChunkCoords(m_NextIdx, chunkX, chunkZ);
+ m_World.GetLightingThread().QueueChunk(chunkX, chunkZ, this);
+ m_NextIdx += 1;
+ }
+
+ // Report progress every 1 second:
+ auto Now = std::chrono::steady_clock::now();
+ if (Now - m_LastReportTime > std::chrono::seconds(1))
+ {
+ float PercentDone = static_cast<float>(m_NumPrepared * 100) / m_MaxIdx;
+ float ChunkSpeed = static_cast<float>((m_NumPrepared - m_LastReportChunkCount) * 1000) / std::chrono::duration_cast<std::chrono::milliseconds>(Now - m_LastReportTime).count();
+ LOG("Preparing spawn (%s): %.02f%% (%d/%d; %.02f chunks/s)",
+ m_World.GetName().c_str(), PercentDone, m_NumPrepared, m_MaxIdx, ChunkSpeed
+ );
+ m_LastReportTime = Now;
+ m_LastReportChunkCount = m_NumPrepared;
+ }
}
-
-protected:
- cLightingThread * m_Lighting;
-
- virtual void Execute(void) override
+
+ /** Decodes the index into chunk coords. Provides the specific chunk ordering. */
+ void DecodeChunkCoords(int a_Idx, int & a_ChunkX, int & a_ChunkZ)
{
- for (;;)
+ // A zigzag pattern from the top to bottom, each row alternating between forward-x and backward-x:
+ int z = a_Idx / m_PrepareDistance;
+ int x = a_Idx % m_PrepareDistance;
+ if ((z & 1) == 0)
{
- LOG("" SIZE_T_FMT " chunks remaining to light", m_Lighting->GetQueueLength()
- );
-
- // Wait for 2 sec, but be "reasonably wakeable" when the thread is to finish
- for (int i = 0; i < 20; i++)
- {
- std::this_thread::sleep_for(std::chrono::milliseconds(100));
- if (m_ShouldTerminate)
- {
- return;
- }
- }
- } // for (-ever)
+ // Reverse every second row:
+ x = m_PrepareDistance - 1 - x;
+ }
+ a_ChunkZ = m_SpawnChunkZ + z - m_PrepareDistance / 2;
+ a_ChunkX = m_SpawnChunkX + x - m_PrepareDistance / 2;
}
};
@@ -206,7 +237,7 @@ void cWorld::cTickThread::Execute(void)
while (!m_ShouldTerminate)
{
auto NowTime = std::chrono::steady_clock::now();
- m_World.Tick(std::chrono::duration_cast<std::chrono::milliseconds>(NowTime - LastTime).count(), std::chrono::duration_cast<std::chrono::duration<int>>(TickTime).count());
+ m_World.Tick(static_cast<float>(std::chrono::duration_cast<std::chrono::milliseconds>(NowTime - LastTime).count()), std::chrono::duration_cast<std::chrono::duration<int>>(TickTime).count());
TickTime = std::chrono::steady_clock::now() - NowTime;
if (TickTime < msPerTick)
@@ -426,53 +457,9 @@ void cWorld::InitializeSpawn(void)
IniFile.ReadFile(m_IniFileName);
int ViewDist = IniFile.GetValueSetI("SpawnPosition", "PregenerateDistance", DefaultViewDist);
IniFile.WriteFile(m_IniFileName);
-
- LOG("Preparing spawn area in world \"%s\", %d x %d chunks, total %d chunks...", m_WorldName.c_str(), ViewDist, ViewDist, ViewDist * ViewDist);
- for (int x = 0; x < ViewDist; x++)
- {
- for (int z = 0; z < ViewDist; z++)
- {
- m_ChunkMap->TouchChunk(x + ChunkX-(ViewDist - 1) / 2, z + ChunkZ-(ViewDist - 1) / 2); // Queue the chunk in the generator / loader
- }
- }
-
- {
- // Display progress during this process:
- cWorldLoadProgress Progress(this);
-
- // Wait for the loader to finish loading
- m_Storage.WaitForLoadQueueEmpty();
-
- // Wait for the generator to finish generating
- m_Generator.WaitForQueueEmpty();
- // Wait for the loader to finish saving
- m_Storage.WaitForSaveQueueEmpty();
-
- Progress.Stop();
- }
-
- // Light all chunks that have been newly generated:
- LOG("Lighting spawn area in world \"%s\"...", m_WorldName.c_str());
-
- for (int x = 0; x < ViewDist; x++)
- {
- int ChX = x + ChunkX-(ViewDist - 1) / 2;
- for (int z = 0; z < ViewDist; z++)
- {
- int ChZ = z + ChunkZ-(ViewDist - 1) / 2;
- if (!m_ChunkMap->IsChunkLighted(ChX, ChZ))
- {
- m_Lighting.QueueChunk(ChX, ChZ); // Queue the chunk in the lighting thread
- }
- } // for z
- } // for x
-
- {
- cWorldLightingProgress Progress(&m_Lighting);
- m_Lighting.WaitForQueueEmpty();
- Progress.Stop();
- }
+ cSpawnPrepare prep(*this, ChunkX, ChunkZ, ViewDist);
+ prep.Wait();
#ifdef TEST_LINEBLOCKTRACER
// DEBUG: Test out the cLineBlockTracer class by tracing a few lines:
@@ -735,28 +722,32 @@ void cWorld::InitialiseGeneratorDefaults(cIniFile & a_IniFile)
{
case dimEnd:
{
- a_IniFile.GetValueSet("Generator", "BiomeGen", "Constant");
- a_IniFile.GetValueSet("Generator", "ConstantBiome", "End");
- a_IniFile.GetValueSet("Generator", "HeightGen", "Biomal");
+ a_IniFile.GetValueSet("Generator", "Generator", "Composable");
+ a_IniFile.GetValueSet("Generator", "BiomeGen", "Constant");
+ a_IniFile.GetValueSet("Generator", "ConstantBiome", "End");
+ a_IniFile.GetValueSet("Generator", "ShapeGen", "End");
a_IniFile.GetValueSet("Generator", "CompositionGen", "End");
break;
}
case dimOverworld:
{
- a_IniFile.GetValueSet("Generator", "BiomeGen", "MultiStepMap");
- a_IniFile.GetValueSet("Generator", "HeightGen", "DistortedHeightmap");
- a_IniFile.GetValueSet("Generator", "CompositionGen", "DistortedHeightmap");
- a_IniFile.GetValueSet("Generator", "Finishers", "Ravines, WormNestCaves, WaterLakes, WaterSprings, LavaLakes, LavaSprings, OreNests, Mineshafts, Trees, SprinkleFoliage, Ice, Snow, Lilypads, BottomLava, DeadBushes, PreSimulator");
+ a_IniFile.GetValueSet("Generator", "Generator", "Composable");
+ a_IniFile.GetValueSet("Generator", "BiomeGen", "Grown");
+ a_IniFile.GetValueSet("Generator", "ShapeGen", "BiomalNoise3D");
+ a_IniFile.GetValueSet("Generator", "CompositionGen", "Biomal");
+ a_IniFile.GetValueSet("Generator", "Finishers", "Ravines, WormNestCaves, WaterLakes, WaterSprings, LavaLakes, LavaSprings, OreNests, Mineshafts, Trees, Villages, SprinkleFoliage, Ice, Snow, Lilypads, BottomLava, DeadBushes, PreSimulator");
break;
}
case dimNether:
{
- a_IniFile.GetValueSet("Generator", "BiomeGen", "Constant");
- a_IniFile.GetValueSet("Generator", "ConstantBiome", "Nether");
- a_IniFile.GetValueSet("Generator", "HeightGen", "Flat");
- a_IniFile.GetValueSet("Generator", "FlatHeight", "128");
- a_IniFile.GetValueSet("Generator", "CompositionGen", "Nether");
- a_IniFile.GetValueSet("Generator", "Finishers", "WormNestCaves, BottomLava, LavaSprings, NetherClumpFoliage, NetherForts, PreSimulator");
+ a_IniFile.GetValueSet("Generator", "Generator", "Composable");
+ a_IniFile.GetValueSet("Generator", "BiomeGen", "Constant");
+ a_IniFile.GetValueSet("Generator", "ConstantBiome", "Nether");
+ a_IniFile.GetValueSet("Generator", "ShapeGen", "HeightMap");
+ a_IniFile.GetValueSet("Generator", "HeightGen", "Flat");
+ a_IniFile.GetValueSet("Generator", "FlatHeight", "128");
+ a_IniFile.GetValueSet("Generator", "CompositionGen", "Nether");
+ a_IniFile.GetValueSet("Generator", "Finishers", "WormNestCaves, BottomLava, LavaSprings, NetherClumpFoliage, NetherForts, PreSimulator");
a_IniFile.GetValueSet("Generator", "BottomLavaHeight", "30");
break;
}
diff --git a/src/World.h b/src/World.h
index 9e91064df..c5fdaeff5 100644
--- a/src/World.h
+++ b/src/World.h
@@ -696,6 +696,8 @@ public:
inline size_t GetStorageLoadQueueLength(void) { return m_Storage.GetLoadQueueLength(); } // tolua_export
inline size_t GetStorageSaveQueueLength(void) { return m_Storage.GetSaveQueueLength(); } // tolua_export
+ cLightingThread & GetLightingThread(void) { return m_Lighting; }
+
void InitializeSpawn(void);
/** Starts threads that belong to this world */