diff --git a/source/Generating/ComposableGenerator.cpp b/source/Generating/ComposableGenerator.cpp index 1612d038b..178ed4537 100644 --- a/source/Generating/ComposableGenerator.cpp +++ b/source/Generating/ComposableGenerator.cpp @@ -19,6 +19,7 @@ #include "Caves.h" #include "MineShafts.h" +#include "Noise3DGenerator.h" #include "Ravines.h" @@ -34,7 +35,9 @@ cComposableGenerator::cComposableGenerator(cChunkGenerator & a_ChunkGenerator) : super(a_ChunkGenerator), m_BiomeGen(NULL), m_HeightGen(NULL), - m_CompositionGen(NULL) + m_CompositionGen(NULL), + m_Noise3DComposable(NULL), + m_NumNoise3DComposableUses(0) { } @@ -55,7 +58,13 @@ cComposableGenerator::~cComposableGenerator() delete *itr; } m_StructureGens.clear(); - delete m_CompositionGen; + + // CompositionGen must not be freed if it is shared between HeightGenCache and CompositionGen: + if ((m_NumNoise3DComposableUses < 2) || (m_CompositionGen != m_Noise3DComposable)) + { + delete m_CompositionGen; + } + m_CompositionGen = NULL; delete m_HeightGen; m_HeightGen = NULL; @@ -232,6 +241,16 @@ void cComposableGenerator::InitHeightGen(cIniFile & a_IniFile) float HeightAmp3 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightAmp3", 0.5); m_HeightGen = new cHeiGenClassic(Seed, HeightFreq1, HeightAmp1, HeightFreq2, HeightAmp2, HeightFreq3, HeightAmp3); } + else if (NoCaseCompare(HeightGenName, "Noise3D") == 0) + { + if (m_Noise3DComposable == NULL) + { + m_Noise3DComposable = new cNoise3DComposable(Seed); + m_Noise3DComposable->Initialize(a_IniFile); + } + m_HeightGen = m_Noise3DComposable; + m_NumNoise3DComposableUses++; + } else // "biomal" or { if (NoCaseCompare(HeightGenName, "biomal") != 0) @@ -312,6 +331,16 @@ void cComposableGenerator::InitCompositionGen(cIniFile & a_IniFile) { m_CompositionGen = new cCompoGenNether(m_ChunkGenerator.GetSeed()); } + else if (NoCaseCompare(CompoGenName, "Noise3D") == 0) + { + if (m_Noise3DComposable == NULL) + { + m_Noise3DComposable = new cNoise3DComposable(m_ChunkGenerator.GetSeed()); + m_Noise3DComposable->Initialize(a_IniFile); + } + m_CompositionGen = m_Noise3DComposable; + m_NumNoise3DComposableUses++; + } else { if (NoCaseCompare(CompoGenName, "biomal") != 0) diff --git a/source/Generating/ComposableGenerator.h b/source/Generating/ComposableGenerator.h index fca2c26c6..fb8e4fdc6 100644 --- a/source/Generating/ComposableGenerator.h +++ b/source/Generating/ComposableGenerator.h @@ -24,6 +24,13 @@ See http://forum.mc-server.org/showthread.php?tid=409 for details. +// fwd: Noise3DGenerator.h +class cNoise3DComposable; + + + + + /** The interface that a biome generator must implement A biome generator takes chunk coords on input and outputs an array of biome indices for that chunk on output. The output array is sequenced in the same way as the MapChunk packet's biome data. @@ -137,6 +144,10 @@ protected: cTerrainCompositionGen * m_CompositionGen; cStructureGenList m_StructureGens; cFinishGenList m_FinishGens; + + // Specific generators that can be reused for different purposes - we don't want to create multiple objects for them + cNoise3DComposable * m_Noise3DComposable; + int m_NumNoise3DComposableUses; // How many times is it actually used? /// Reads the biome gen settings from the ini and initializes m_BiomeGen accordingly void InitBiomeGen(cIniFile & a_IniFile); diff --git a/source/Generating/Noise3DGenerator.cpp b/source/Generating/Noise3DGenerator.cpp index 5605b1cbd..d8ba5f690 100644 --- a/source/Generating/Noise3DGenerator.cpp +++ b/source/Generating/Noise3DGenerator.cpp @@ -12,6 +12,9 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cNoise3DGenerator: + cNoise3DGenerator::cNoise3DGenerator(cChunkGenerator & a_ChunkGenerator) : super(a_ChunkGenerator), m_Noise1(1000), @@ -286,3 +289,229 @@ 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) +{ +} + + + + + +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); + 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); +} + + + + + +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 + return; + } + m_LastChunkX = a_ChunkX; + m_LastChunkZ = a_ChunkZ; + + // Parameters: + const int INTERPOL_X = 8; + const int INTERPOL_Y = 4; + const int INTERPOL_Z = 8; + + // Precalculate a "height" array: + NOISE_DATATYPE Height[17 * 17]; // x + 17 * z + for (int z = 0; z < 17; z += INTERPOL_Z) + { + NOISE_DATATYPE NoiseZ = ((NOISE_DATATYPE)(a_ChunkZ * cChunkDef::Width + z)) / m_FrequencyZ; + for (int x = 0; x < 17; x += INTERPOL_X) + { + NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(a_ChunkX * cChunkDef::Width + x)) / m_FrequencyX; + NOISE_DATATYPE val = abs(m_Noise1.CubicNoise2D(NoiseX / 5, NoiseZ / 5)) * m_HeightAmplification + 1; + Height[x + 17 * z] = val * val * val; + } + } + + int idx = 0; + for (int y = 0; y < 257; y += INTERPOL_Y) + { + 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 += INTERPOL_Z) + { + NOISE_DATATYPE NoiseZ = ((NOISE_DATATYPE)(a_ChunkZ * cChunkDef::Width + z)) / m_FrequencyZ; + for (int x = 0; x < 17; x += INTERPOL_X) + { + 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]; + } + } + // Linear-interpolate this XZ floor: + ArrayLinearInterpolate2D(CurFloor, 17, 17, INTERPOL_X, INTERPOL_Z); + } + + // Finish the 3D linear interpolation by interpolating between each XZ-floors on the Y axis + for (int y = 1; y < cChunkDef::Height; y++) + { + if ((y % INTERPOL_Y) == 0) + { + // This is the interpolation source floor, already calculated + continue; + } + int LoFloorY = (y / INTERPOL_Y) * INTERPOL_Y; + int HiFloorY = LoFloorY + INTERPOL_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 % INTERPOL_Y)) / INTERPOL_Y; + int idx = 0; + for (int z = 0; z < cChunkDef::Width; z++) + { + 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 + } + } + + // 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)) + { + 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) + + cFile f2; + if (f2.Open(Printf("Chunk_%d_%d_XZ.raw", a_ChunkX, a_ChunkZ), cFile::fmWrite)) + { + for (int y = 0; y < cChunkDef::Height; y++) + { + for (int z = 0; z < cChunkDef::Width; z++) + { + 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++])))); + } + f2.Write(buf, 16); + } // for z + } // for y + } // if (XZ file open) + */ +} + + + + + +void cNoise3DComposable::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) +{ + GenerateNoiseArrayIfNeeded(a_ChunkX, a_ChunkZ); + + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int x = 0; x < cChunkDef::Width; x++) + { + cChunkDef::SetHeight(a_HeightMap, x, z, m_SeaLevel); + for (int y = cChunkDef::Height - 1; y > m_SeaLevel; y--) + { + if (m_NoiseArray[y * 17 * 17 + z * 17 + x] <= m_AirThreshold) + { + cChunkDef::SetHeight(a_HeightMap, x, z, y); + break; + } + } // for y + } // for x + } // for z +} + + + + + +void cNoise3DComposable::ComposeTerrain(cChunkDesc & a_ChunkDesc) +{ + GenerateNoiseArrayIfNeeded(a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ()); + + // Make basic terrain composition: + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int x = 0; x < cChunkDef::Width; x++) + { + int LastAir = a_ChunkDesc.GetHeight(x, z) + 1; + bool HasHadWater = false; + for (int y = LastAir - 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 +} + + + + diff --git a/source/Generating/Noise3DGenerator.h b/source/Generating/Noise3DGenerator.h index 44f4f3fce..071b19019 100644 --- a/source/Generating/Noise3DGenerator.h +++ b/source/Generating/Noise3DGenerator.h @@ -8,8 +8,7 @@ #pragma once -#include "ChunkGenerator.h" -#include "ChunkDesc.h" +#include "ComposableGenerator.h" #include "../Noise.h" @@ -55,3 +54,44 @@ protected: + +class cNoise3DComposable : + public cTerrainHeightGen, + public cTerrainCompositionGen +{ +public: + cNoise3DComposable(int a_Seed); + + void Initialize(cIniFile & a_IniFile); + +protected: + cNoise m_Noise1; + cNoise m_Noise2; + cNoise m_Noise3; + + int m_SeaLevel; + NOISE_DATATYPE m_HeightAmplification; + NOISE_DATATYPE m_MidPoint; // Where the vertical "center" of the noise should be + NOISE_DATATYPE m_FrequencyX; + NOISE_DATATYPE m_FrequencyY; + NOISE_DATATYPE m_FrequencyZ; + NOISE_DATATYPE m_AirThreshold; + + 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 + void GenerateNoiseArrayIfNeeded(int a_ChunkX, int a_ChunkZ); + + // cTerrainHeightGen overrides: + virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override; + + // cTerrainCompositionGen overrides: + virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override; +} ; + + + +