1
0
Fork 0

Generator: Shape initial refactoring.

The code compiles, but several structure generators are broken, crash on start.
This commit is contained in:
Mattes D 2014-11-12 21:24:26 +01:00
parent b525eee8e0
commit 5fb2526e07
23 changed files with 1383 additions and 1373 deletions

View File

@ -10,6 +10,7 @@ SET (SRCS
ChunkDesc.cpp
ChunkGenerator.cpp
CompoGen.cpp
CompoGenBiomal.cpp
ComposableGenerator.cpp
DistortedHeightmap.cpp
DungeonRoomsFinisher.cpp
@ -39,6 +40,7 @@ SET (HDRS
ChunkDesc.h
ChunkGenerator.h
CompoGen.h
CompoGenBiomal.h
ComposableGenerator.h
DistortedHeightmap.h
DungeonRoomsFinisher.h
@ -58,6 +60,7 @@ SET (HDRS
RainbowRoadsGen.h
Ravines.h
RoughRavines.h
ShapeGen.cpp
StructGen.h
TestRailsGen.h
Trees.h

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 * z + 256 * 16 * x]. */
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} ;
@ -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)
@ -739,7 +425,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];

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:

View File

@ -0,0 +1,799 @@
// CompoGenBiomal.cpp
// Implements the cCompoGenBiomal class representing the biome-aware composition generator
#include "Globals.h"
#include "ComposableGenerator.h"
#include "../IniFile.h"
#include "../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) - 1;
}
/** 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;
for (int y = a_ChunkDesc.GetHeight(a_RelX, a_RelZ); 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();
}
}
#if 0
/** Fills a single column with grass-based terrain (grass or water, dirt, stone). */
void FillColumnGrass(int a_RelX, int a_RelZ, const Byte * a_ShapeColumn, cChunkDesc & a_ChunkDesc)
{
static const PatternItem pattern[] =
{
{ E_BLOCK_GRASS, 0},
{ E_BLOCK_DIRT, 0},
{ E_BLOCK_DIRT, 0},
{ E_BLOCK_DIRT, 0},
} ;
FillColumnPattern(a_RelX, a_RelZ, a_ShapeColumn, a_ChunkDesc, pattern, ARRAYCOUNT(pattern));
}
/** Fills a single column with grass-based terrain (grass or water, dirt, stone). */
void FillColumnStone(int a_RelX, int a_RelZ, const Byte * a_ShapeColumn, cChunkDesc & a_ChunkDesc)
{
static const PatternItem pattern[] =
{
{ E_BLOCK_STONE, 0},
} ;
FillColumnPattern(a_RelX, a_RelZ, a_ShapeColumn, a_ChunkDesc, pattern, ARRAYCOUNT(pattern));
}
/** Fills a single column with Mesa-like terrain (variations of clay). */
void FillColumnMesa(int a_RelX, int a_RelZ, const Byte * a_ShapeColumn, cChunkDesc & a_ChunkDesc)
{
// Fill with grass and dirt on the very top of mesa plateaus:
size_t curIdx = 0;
for (int y = 255; y > m_MesaDirtLevel; y--)
{
if (a_ShapeColumn[y] > 0)
{
a_ChunkDesc.SetBlockType(a_RelX, y, a_RelZ, (curIdx > 0) ? E_BLOCK_DIRT : E_BLOCK_GRASS);
curIdx += 1;
}
else
{
curIdx = 0;
}
} // for y
// Fill with clays from the DirtLevel down to SandLevel:
for (int y = m_MesaDirtLevel; y > m_MesaSandLevel; y--)
{
if (a_ShapeColumn[y] > 0)
{
a_ChunkDesc.SetBlockTypeMeta(a_RelX, y, a_RelZ, m_MesaPattern[y].m_BlockType, m_MesaPattern[y].m_BlockMeta);
}
else
{
curIdx = 0;
}
} // for y
// If currently air, switch to red sand pattern:
static const PatternItem redSandPattern[] =
{
{ E_BLOCK_SAND, E_META_SAND_RED},
{ E_BLOCK_STAINED_CLAY, E_META_STAINED_CLAY_ORANGE},
{ E_BLOCK_STAINED_CLAY, E_META_STAINED_CLAY_ORANGE},
{ E_BLOCK_STAINED_CLAY, E_META_STAINED_CLAY_ORANGE},
};
Pattern pattern;
size_t patternSize;
if (curIdx == 0)
{
pattern = redSandPattern;
patternSize = ARRAYCOUNT(redSandPattern);
}
else
{
pattern = m_MesaPattern + m_MesaSandLevel;
patternSize = static_cast<size_t>(m_MesaSandLevel);
}
// Fill with current pattern (MesaPattern or RedSand) until sealevel:
for (int y = m_MesaSandLevel; y > m_SeaLevel; y--)
{
if (a_ShapeColumn[y] > 0)
{
if (curIdx >= patternSize)
{
a_ChunkDesc.SetBlockTypeMeta(a_RelX, y, a_RelZ, E_BLOCK_STAINED_CLAY, E_META_STAINED_CLAY_ORANGE);
}
else
{
a_ChunkDesc.SetBlockTypeMeta(a_RelX, y, a_RelZ, pattern[curIdx].m_BlockType, pattern[curIdx].m_BlockMeta);
}
curIdx += 1;
}
else
{
// Air resets the pattern to red sand:
curIdx = 0;
pattern = redSandPattern;
patternSize = ARRAYCOUNT(redSandPattern);
}
} // for y
// If there is an ocean, fill it with water and then redsand:
int y = m_SeaLevel;
for (; y > 0; y--)
{
if ((a_ShapeColumn[y] == 0) || (curIdx >= ARRAYCOUNT(redSandPattern)))
{
// water pocket or out of red sand pattern, use stone from now on
break;
}
a_ChunkDesc.SetBlockTypeMeta(a_RelX, y, a_RelZ, E_BLOCK_STAINED_CLAY, E_META_STAINED_CLAY_ORANGE);
curIdx = curIdx + 1;
} // for y
// The rest should be filled with stone:
for (; y > 0; y--)
{
if (a_ShapeColumn[y] > 0)
{
a_ChunkDesc.SetBlockType(a_RelX, y, a_RelZ, E_BLOCK_STONE);
}
} // for y
}
/** Fills a single column with megataiga-based terrain (grass or podzol on top). */
void FillColumnMegaTaiga(int a_RelX, int a_RelZ, const Byte * a_ShapeColumn, cChunkDesc & a_ChunkDesc)
{
// TODO
}
/** Fills a single column with sand-based terrain (such as desert or beach). */
void FillColumnSand(int a_RelX, int a_RelZ, const Byte * a_ShapeColumn, cChunkDesc & a_ChunkDesc)
{
static const PatternItem pattern[] =
{
{ E_BLOCK_SAND, 0},
{ E_BLOCK_SAND, 0},
{ E_BLOCK_SAND, 0},
{ E_BLOCK_SANDSTONE, 0},
} ;
FillColumnPattern(a_RelX, a_RelZ, a_ShapeColumn, a_ChunkDesc, pattern, ARRAYCOUNT(pattern));
}
void FillColumnMycelium(int a_RelX, int a_RelZ, const Byte * a_ShapeColumn, cChunkDesc & a_ChunkDesc)
{
static const PatternItem pattern[] =
{
{ E_BLOCK_MYCELIUM, 0},
{ E_BLOCK_DIRT, 0},
{ E_BLOCK_DIRT, 0},
{ E_BLOCK_DIRT, 0},
} ;
FillColumnPattern(a_RelX, a_RelZ, a_ShapeColumn, a_ChunkDesc, pattern, ARRAYCOUNT(pattern));
}
/** Fills the column with the specified pattern, repeating it if there's an air pocket in between. */
void FillColumnPattern(int a_RelX, int a_RelZ, const Byte * a_ShapeColumn, cChunkDesc & a_ChunkDesc, Pattern a_Pattern, size_t a_PatternSize)
{
// Fill with pattern until sealevel:
size_t curIdx = 0;
for (int y = 255; y > m_SeaLevel; y--)
{
if (a_ShapeColumn[y] > 0)
{
// Continue with the pattern:
if (curIdx >= a_PatternSize)
{
a_ChunkDesc.SetBlockType(a_RelX, y, a_RelZ, E_BLOCK_STONE);
}
else
{
a_ChunkDesc.SetBlockTypeMeta(a_RelX, y, a_RelZ, a_Pattern[curIdx].m_BlockType, a_Pattern[curIdx].m_BlockMeta);
}
curIdx += 1;
}
else
{
// Air pocket, restart the pattern:
curIdx = 0;
}
} // for y
// From sealevel downward use the ocean floor pattern:
FillOceanFloor(a_RelX, a_RelZ, a_ShapeColumn, a_ChunkDesc, a_Pattern, a_PatternSize, curIdx);
}
/** Fills the blocks from sealevel down to bottom with ocean-floor pattern.
a_PatternStartOffset specifies the offset at which to start the pattern, in case there was air just above. */
void FillOceanFloor(int a_RelX, int a_RelZ, const Byte * a_ShapeColumn, cChunkDesc & a_ChunkDesc, Pattern a_Pattern, size_t a_PatternSize, size_t a_PatternStartOffset)
{
for (int y = m_SeaLevel; y > 0; y--)
{
if (a_ShapeColumn[y] > 0)
{
// TODO
}
} // for y
}
#endif
} ;
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,8 @@
#include "StructGen.h"
#include "FinishGen.h"
#include "CompoGenBiomal.h"
#include "Caves.h"
#include "DistortedHeightmap.h"
#include "DungeonRoomsFinisher.h"
@ -39,7 +41,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 +50,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 +115,7 @@ cTerrainCompositionGenPtr cTerrainCompositionGen::CreateCompositionGen(cIniFile
cComposableGenerator::cComposableGenerator(cChunkGenerator & a_ChunkGenerator) :
super(a_ChunkGenerator),
m_BiomeGen(),
m_HeightGen(),
m_ShapeGen(),
m_CompositionGen()
{
}
@ -138,7 +129,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,15 +157,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);
m_CompositionGen->ComposeTerrain(a_ChunkDesc, shape);
ShouldUpdateHeightmap = true;
}
@ -234,13 +232,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 +253,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,7 +262,7 @@ 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());
int CompoGenCacheSize = a_IniFile.GetValueSetI("Generator", "CompositionGenCacheSize", 64);
if (CompoGenCacheSize > 1)
@ -333,7 +334,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 +343,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 +581,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 +589,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 +599,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

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

@ -24,17 +24,9 @@
class cDistortedHeightmap :
public cTerrainHeightGen,
public cTerrainCompositionGen
public cTerrainShapeGen
{
public:
/// Structure used for storing block patterns for columns
struct sBlockInfo
{
BLOCKTYPE BlockType;
NIBBLETYPE BlockMeta;
} ;
cDistortedHeightmap(int a_Seed, cBiomeGenPtr a_BiomeGen);
protected:
@ -52,8 +44,6 @@ protected:
cPerlinNoise m_NoiseDistortX;
cPerlinNoise m_NoiseDistortZ;
cNoise m_OceanFloorSelect; ///< Used for selecting between dirt and sand on the ocean floor
cNoise m_MesaFloor; ///< Used for the floor of the clay blocks in mesa biomes
int m_SeaLevel;
NOISE_DATATYPE m_FrequencyX;
@ -71,9 +61,9 @@ protected:
cTerrainHeightGenPtr m_UnderlyingHeiGen;
/** Cache for m_UnderlyingHeiGen. */
cHeiGenCache m_HeightGen;
cHeiGenCache m_HeightGen;
/// Heightmap for the current chunk, before distortion (from m_HeightGen). Used for optimization.
/** Heightmap for the current chunk, before distortion (from m_HeightGen). Used for optimization. */
cChunkDef::HeightMap m_CurChunkHeights;
// Per-biome terrain generator parameters:
@ -88,54 +78,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

@ -258,9 +258,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,10 +293,14 @@ cDungeonRoomsFinisher::cStructurePtr cDungeonRoomsFinisher::CreateStructure(int
int ChunkX, ChunkZ;
int RelX = a_OriginX, RelY = 0, RelZ = a_OriginZ;
cChunkDef::AbsoluteToRelative(RelX, RelY, RelZ, ChunkX, ChunkZ);
/*
// TODO
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);
*/
int Height = 62;
// Create the dungeon room descriptor:
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

@ -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++)
@ -750,43 +740,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 +803,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,46 @@ 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)
} ;
class cHeiGenFlat :
public cTerrainHeightGen
{
@ -40,47 +82,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
{

View File

@ -458,110 +458,53 @@ 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;
}
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
}
@ -671,21 +614,23 @@ 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)
@ -693,12 +638,12 @@ void cBiomalNoise3DComposable::GenerateNoiseArrayIfNeeded(int a_ChunkX, int a_Ch
AddHeight *= 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];
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);
}
@ -778,78 +723,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
}
@ -857,3 +743,4 @@ void cBiomalNoise3DComposable::ComposeTerrain(cChunkDesc & a_ChunkDesc)

View File

@ -69,8 +69,7 @@ protected:
class cNoise3DComposable :
public cTerrainHeightGen,
public cTerrainCompositionGen
public cTerrainShapeGen
{
public:
cNoise3DComposable(int a_Seed);
@ -127,12 +126,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); }
} ;
@ -140,8 +135,7 @@ protected:
class cBiomalNoise3DComposable :
public cTerrainHeightGen,
public cTerrainCompositionGen
public cTerrainShapeGen
{
public:
cBiomalNoise3DComposable(int a_Seed, cBiomeGenPtr a_BiomeGen);
@ -194,7 +188,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];
@ -212,13 +206,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); }
} ;

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

@ -0,0 +1,140 @@
// 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"
////////////////////////////////////////////////////////////////////////////////
// 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
{
// 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,12 @@ 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);
m_CompositionGen->ComposeTerrain(WorkerDesc, workerShape);
}
else
{
@ -390,7 +392,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 +406,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

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