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