1
0
Fork 0

Merge remote-tracking branch 'origin-master' into c++11

This commit is contained in:
Tiger Wang 2014-11-25 23:03:33 +00:00
parent 2ae4d2a96a
commit 187bdab4fa
32 changed files with 1723 additions and 1688 deletions

View File

@ -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})

View File

@ -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,

View File

@ -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(

View File

@ -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);
@ -209,323 +212,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:
@ -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;
}

View File

@ -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

View File

@ -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);
}

View File

@ -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);

View File

@ -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;
}
else if (NoCaseCompare(CompoGenName, "debugbiomes") == 0)
{
res = new cCompoGenDebugBiomes;
}
else if (NoCaseCompare(CompoGenName, "classic") == 0)
{
res = new cCompoGenClassic;
}
else if (NoCaseCompare(CompoGenName, "DistortedHeightmap") == 0)
{
res = new cDistortedHeightmap(a_Seed, a_BiomeGen);
}
else if (NoCaseCompare(CompoGenName, "end") == 0)
{
res = new cEndGen(a_Seed);
}
else if (NoCaseCompare(CompoGenName, "nether") == 0)
{
res = new cCompoGenNether(a_Seed);
res = CreateCompoGenBiomal(a_Seed);
}
else if (NoCaseCompare(CompoGenName, "BiomalNoise3D") == 0)
{
res = new cBiomalNoise3DComposable(a_Seed, a_BiomeGen);
// 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)
{
res = std::make_shared<cCompoGenClassic>();
}
else if (NoCaseCompare(CompoGenName, "DebugBiomes") == 0)
{
res = std::make_shared<cCompoGenDebugBiomes>();
}
else if (NoCaseCompare(CompoGenName, "DistortedHeightmap") == 0)
{
// The composition that used to be provided with DistortedHeightmap is now provided by the Biomal compositor:
res = CreateCompoGenBiomal(a_Seed);
}
else if (NoCaseCompare(CompoGenName, "End") == 0)
{
res = std::make_shared<cEndGen>(a_Seed);
}
else if (NoCaseCompare(CompoGenName, "Nether") == 0)
{
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)
{

View File

@ -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 cTerrainShapeGen
{
public:
virtual ~cTerrainShapeGen() {} // Force a virtual destructor in descendants
/** 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 InitializeShapeGen(cIniFile & a_IniFile) {}
/** 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 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
/** Generates heightmap for the given chunk */
/** Retrieves the heightmap for the specified chunk. */
virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) = 0;
/** Reads parameters from the ini file, prepares generator for use. */
/** Initializes the generator, reading its parameters from the INI file. */
virtual void InitializeHeightGen(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
a_CacheOffByDefault gets set to whether the cache should be disabled by default
Implemented in HeiGen.cpp!
*/
/** 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);

View File

@ -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;
};

View File

@ -14,163 +14,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:
@ -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();
}
}

View File

@ -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;
} ;

View File

@ -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));
}

View File

@ -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;

View File

@ -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)
if (a_Shape[(x + 16 * z) * 256 + y] != 0)
{
a_ChunkDesc.SetBlockTypeMeta(x, y, z, E_BLOCK_END_STONE, 0);
}
else
{
a_ChunkDesc.SetBlockTypeMeta(x, y, z, E_BLOCK_AIR, 0);
a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_END_STONE);
}
} // for y
} // for x

View File

@ -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;
} ;

View File

@ -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);

View File

@ -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++)
@ -159,6 +149,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:
@ -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;
}

View File

@ -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,6 +23,78 @@ Interfaces to the various height generators:
/** A simple cache that stores N most recently generated chunks' heightmaps; N being settable upon creation */
class cHeiGenCache :
public cTerrainHeightGen
{
public:
cHeiGenCache(cTerrainHeightGenPtr a_HeiGenToCache, int a_CacheSize);
~cHeiGenCache();
// 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:
struct sCacheData
{
int m_ChunkX;
int m_ChunkZ;
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
sCacheData * m_CacheData; // m_CacheData[m_CacheOrder[0]] is the most recently used
// Cache statistics
int m_NumHits;
int m_NumMisses;
int m_TotalChain; // Number of cache items walked to get to a hit (only added for hits)
} ;
/** 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
{
@ -40,47 +114,6 @@ protected:
/// A simple cache that stores N most recently generated chunks' heightmaps; N being settable upon creation
class cHeiGenCache :
public cTerrainHeightGen
{
public:
cHeiGenCache(cTerrainHeightGenPtr a_HeiGenToCache, int a_CacheSize);
~cHeiGenCache();
// cTerrainHeightGen overrides:
virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override;
virtual void InitializeHeightGen(cIniFile & a_IniFile) override;
/// Retrieves height at the specified point in the cache, returns true if found, false if not found
bool GetHeightAt(int a_ChunkX, int a_ChunkZ, int a_RelX, int a_RelZ, HEIGHTTYPE & a_Height);
protected:
cTerrainHeightGenPtr m_HeiGenToCache;
struct sCacheData
{
int m_ChunkX;
int m_ChunkZ;
cChunkDef::HeightMap m_HeightMap;
} ;
// To avoid moving large amounts of data for the MRU behavior, we MRU-ize indices to an array of the actual data
int m_CacheSize;
int * m_CacheOrder; // MRU-ized order, indices into m_CacheData array
sCacheData * m_CacheData; // m_CacheData[m_CacheOrder[0]] is the most recently used
// Cache statistics
int m_NumHits;
int m_NumMisses;
int m_TotalChain; // Number of cache items walked to get to a hit (only added for hits)
} ;
class cHeiGenClassic :
public cTerrainHeightGen
{
@ -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);
} ;

View File

@ -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)
for (int x = 0; x < 5; x++)
{
AddHeight *= 4;
}
for (int z = 0; z < 5; z++)
{
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];
NOISE_DATATYPE AddHeight = (static_cast<NOISE_DATATYPE>(y * 8) - m_MidPoint) * m_HeightAmplification;
// If "underground", make the terrain smoother by forcing the vertical linear gradient into steeper slope:
if (AddHeight < 0)
{
AddHeight *= 4;
}
// If too high, cut off any terrain:
if (y > 28)
{
AddHeight = AddHeight + static_cast<NOISE_DATATYPE>(y - 28) / 4;
}
// Decide between the two density noises:
int idx = 33 * x + 33 * 5 * z + y;
Workspace[idx] = ClampedLerp(DensityNoiseA[idx], DensityNoiseB[idx], 8 * (ChoiceNoise[idx] + 0.5f)) + AddHeight + curBaseNoise;
}
}
}
LinearUpscale3DArray<NOISE_DATATYPE>(Workspace, 5, 5, 33, m_NoiseArray, 4, 4, 8);
LinearUpscale3DArray<NOISE_DATATYPE>(Workspace, 33, 5, 5, m_NoiseArray, 8, 4, 4);
}
void cNoise3DComposable::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap)
void cNoise3DComposable::GenShape(int a_ChunkX, int a_ChunkZ, cChunkDesc::Shape & a_Shape)
{
GenerateNoiseArrayIfNeeded(a_ChunkX, a_ChunkZ);
// Translate the noise array into Shape:
for (int z = 0; z < cChunkDef::Width; z++)
{
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--)
for (int y = 0; y < cChunkDef::Height; y++)
{
if (m_NoiseArray[y * 17 * 17 + z * 17 + x] <= m_AirThreshold)
{
cChunkDef::SetHeight(a_HeightMap, x, z, y);
break;
}
} // for y
} // for x
} // for z
}
void cNoise3DComposable::ComposeTerrain(cChunkDesc & a_ChunkDesc)
{
GenerateNoiseArrayIfNeeded(a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ());
a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0);
// Make basic terrain composition:
for (int z = 0; z < cChunkDef::Width; z++)
{
for (int x = 0; x < cChunkDef::Width; x++)
{
int LastAir = a_ChunkDesc.GetHeight(x, z) + 1;
bool HasHadWater = false;
for (int y = LastAir; y < m_SeaLevel; y++)
{
a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_STATIONARY_WATER);
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--)
for (int y = 0; y < cChunkDef::Height; 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++)
{
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)

View File

@ -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); }
} ;

145
src/Generating/ShapeGen.cpp Normal file
View File

@ -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;
}

View File

@ -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;

View File

@ -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);
} ;

View File

@ -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);
}

View File

@ -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);

View File

@ -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),

View File

@ -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>

View File

@ -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++;
}

View File

@ -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)
{

View File

@ -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
{
public:
cWorldLoadProgress(cWorld * a_World) :
cIsThread("cWorldLoadProgress"),
m_World(a_World)
{
Start();
}
void Stop(void)
{
m_ShouldTerminate = true;
Wait();
}
protected:
typedef cIsThread super;
cWorld * m_World;
public:
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();
}
// 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();
}
} ;
////////////////////////////////////////////////////////////////////////////////
// cWorldLightingProgress:
/// 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)
{
m_ShouldTerminate = true;
Wait();
}
protected:
cWorld & m_World;
int m_SpawnChunkX;
int m_SpawnChunkZ;
int m_PrepareDistance;
cLightingThread * m_Lighting;
virtual void Execute(void) override
/** 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;
/** Event used to signal that the thread has started. */
cEvent m_EvtStarted;
/** 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)
{
for (;;)
// Check if this was the last chunk:
m_NumPrepared += 1;
if (m_NumPrepared >= m_MaxIdx)
{
LOG("" SIZE_T_FMT " chunks remaining to light", m_Lighting->GetQueueLength()
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
);
// 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)
m_LastReportTime = Now;
m_LastReportChunkCount = m_NumPrepared;
}
}
/** Decodes the index into chunk coords. Provides the specific chunk ordering. */
void DecodeChunkCoords(int a_Idx, int & a_ChunkX, int & a_ChunkZ)
{
// 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)
{
// 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;
}

View File

@ -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 */