From 4b95f7c69a1b3a3aac8855d5e026238ed0f61fa7 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Mon, 10 Nov 2014 17:00:14 +0100 Subject: Added BiomalNoise3D shape generator. --- src/Generating/ComposableGenerator.cpp | 4 + src/Generating/HeiGen.cpp | 4 + src/Generating/Noise3DGenerator.cpp | 304 +++++++++++++++++++++++++++++---- src/Generating/Noise3DGenerator.h | 86 ++++++++++ 4 files changed, 369 insertions(+), 29 deletions(-) diff --git a/src/Generating/ComposableGenerator.cpp b/src/Generating/ComposableGenerator.cpp index 169821050..5f46574c7 100644 --- a/src/Generating/ComposableGenerator.cpp +++ b/src/Generating/ComposableGenerator.cpp @@ -73,6 +73,10 @@ cTerrainCompositionGenPtr cTerrainCompositionGen::CreateCompositionGen(cIniFile { res = new cCompoGenNether(a_Seed); } + else if (NoCaseCompare(CompoGenName, "BiomalNoise3D") == 0) + { + res = new cBiomalNoise3DComposable(a_Seed, a_BiomeGen); + } else if (NoCaseCompare(CompoGenName, "Noise3D") == 0) { res = new cNoise3DComposable(a_Seed); diff --git a/src/Generating/HeiGen.cpp b/src/Generating/HeiGen.cpp index 28a5698e4..1d9f1e3aa 100644 --- a/src/Generating/HeiGen.cpp +++ b/src/Generating/HeiGen.cpp @@ -776,6 +776,10 @@ cTerrainHeightGenPtr cTerrainHeightGen::CreateHeightGen(cIniFile & a_IniFile, cB { res = new cHeiGenMountains(a_Seed); } + else if (NoCaseCompare(HeightGenName, "BiomalNoise3D") == 0) + { + res = new cBiomalNoise3DComposable(a_Seed, a_BiomeGen); + } else if (NoCaseCompare(HeightGenName, "Noise3D") == 0) { res = new cNoise3DComposable(a_Seed); diff --git a/src/Generating/Noise3DGenerator.cpp b/src/Generating/Noise3DGenerator.cpp index 885a9bab3..3f50ea0e9 100644 --- a/src/Generating/Noise3DGenerator.cpp +++ b/src/Generating/Noise3DGenerator.cpp @@ -444,7 +444,7 @@ 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; @@ -485,52 +485,298 @@ void cNoise3DComposable::GenerateNoiseArrayIfNeeded(int a_ChunkX, int a_ChunkZ) } } LinearUpscale3DArray(Workspace, 5, 5, 33, m_NoiseArray, 4, 4, 8); +} - #if 0 - // DEBUG: Output two images of m_NoiseArray, sliced by XY and XZ, into grayscale files, to be inspected by Grabber: - cFile f1; - if (f1.Open(Printf("Chunk_%d_%d_XY.raw", a_ChunkX, a_ChunkZ), cFile::fmWrite)) + + + + +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 z = 0; z < cChunkDef::Width; z++) + for (int x = 0; x < cChunkDef::Width; x++) { - for (int y = 0; y < cChunkDef::Height; y++) + cChunkDef::SetHeight(a_HeightMap, x, z, m_SeaLevel); + for (int y = cChunkDef::Height - 1; y > m_SeaLevel; y--) { - int idx = y * 17 * 17 + z * 17; - unsigned char buf[16]; - for (int x = 0; x < cChunkDef::Width; x++) + if (m_NoiseArray[y * 17 * 17 + z * 17 + x] <= m_AirThreshold) { - buf[x] = (unsigned char)(std::min(256, std::max(0, (int)(128 + 32 * m_NoiseArray[idx++])))); + cChunkDef::SetHeight(a_HeightMap, x, z, y); + break; } - f1.Write(buf, 16); } // for y - } // for z - } // if (XY file open) + } // for x + } // for z +} - cFile f2; - if (f2.Open(Printf("Chunk_%d_%d_XZ.raw", a_ChunkX, a_ChunkZ), cFile::fmWrite)) + + + + +void cNoise3DComposable::ComposeTerrain(cChunkDesc & a_ChunkDesc) +{ + GenerateNoiseArrayIfNeeded(a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ()); + + a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0); + + // Make basic terrain composition: + for (int z = 0; z < cChunkDef::Width; z++) { - for (int y = 0; y < cChunkDef::Height; y++) + for (int x = 0; x < cChunkDef::Width; x++) { - for (int z = 0; z < cChunkDef::Width; z++) + int LastAir = a_ChunkDesc.GetHeight(x, z) + 1; + bool HasHadWater = false; + for (int y = LastAir; y < m_SeaLevel; y++) { - int idx = y * 17 * 17 + z * 17; - unsigned char buf[16]; - for (int x = 0; x < cChunkDef::Width; x++) + a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_STATIONARY_WATER); + } + for (int y = LastAir - 1; y > 0; y--) + { + if (m_NoiseArray[x + 17 * z + 17 * 17 * y] > m_AirThreshold) { - buf[x] = (unsigned char)(std::min(256, std::max(0, (int)(128 + 32 * m_NoiseArray[idx++])))); + // "air" part + LastAir = y; + if (y < m_SeaLevel) + { + a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_STATIONARY_WATER); + HasHadWater = true; + } + continue; } - f2.Write(buf, 16); - } // for z - } // for y - } // if (XZ file open) - #endif + // "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 } -void cNoise3DComposable::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) +//////////////////////////////////////////////////////////////////////////////// +// 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) +{ + // Generate the weight distribution for summing up neighboring biomes: + m_WeightSum = 0; + for (int z = 0; z <= AVERAGING_SIZE * 2; z++) + { + for (int x = 0; x <= AVERAGING_SIZE * 2; x++) + { + m_Weight[z][x] = static_cast((5 - std::abs(5 - x)) + (5 - std::abs(5 - z))); + m_WeightSum += m_Weight[z][x]; + } + } +} + + + + + +void cBiomalNoise3DComposable::Initialize(cIniFile & a_IniFile) +{ + // Params: + // The defaults generate extreme hills terrain + m_SeaLevel = a_IniFile.GetValueSetI("Generator", "BiomalNoise3DSeaLevel", 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++) + { + 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; + } +} + + + + + +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(a_ChunkX * cChunkDef::Width); + NOISE_DATATYPE BlockZ = static_cast(a_ChunkZ * cChunkDef::Width); + // Note that we have to swap the coords, because noise generator uses [x + SizeX * y + SizeX * SizeY * z] ordering and we want "BlockY" to be "z": + m_ChoiceNoise.Generate3D (ChoiceNoise, 5, 5, 33, BlockX / m_ChoiceFrequencyX, (BlockX + 17) / m_ChoiceFrequencyX, BlockZ / m_ChoiceFrequencyZ, (BlockZ + 17) / m_ChoiceFrequencyZ, 0, 257 / m_ChoiceFrequencyY, Workspace); + m_DensityNoiseA.Generate3D(DensityNoiseA, 5, 5, 33, BlockX / m_FrequencyX, (BlockX + 17) / m_FrequencyX, BlockZ / m_FrequencyZ, (BlockZ + 17) / m_FrequencyZ, 0, 257 / m_FrequencyY, Workspace); + m_DensityNoiseB.Generate3D(DensityNoiseB, 5, 5, 33, BlockX / m_FrequencyX, (BlockX + 17) / m_FrequencyX, BlockZ / m_FrequencyZ, (BlockZ + 17) / m_FrequencyZ, 0, 257 / m_FrequencyY, Workspace); + m_BaseNoise.Generate2D (BaseNoise, 5, 5, BlockX / m_BaseFrequencyX, (BlockX + 17) / m_BaseFrequencyX, BlockZ / m_FrequencyZ, (BlockZ + 17) / m_FrequencyZ, Workspace); + + // Calculate the final noise based on the partial noises: + for (int y = 0; y < 33; y++) + { + NOISE_DATATYPE BlockHeight = static_cast(y * 8); + for (int z = 0; z < 5; z++) + { + for (int x = 0; x < 5; x++) + { + NOISE_DATATYPE AddHeight = (BlockHeight - MidPoint[x + 5 * z]) * HeightAmp[x + 5 * z]; + + // If "underground", make the terrain smoother by forcing the vertical linear gradient into steeper slope: + if (AddHeight < 0) + { + AddHeight *= 4; + } + + int idx = x + 5 * z + 5 * 5 * y; + Workspace[idx] = ClampedLerp(DensityNoiseA[idx], DensityNoiseB[idx], 8 * (ChoiceNoise[idx] + 0.5f)) + AddHeight + BaseNoise[x + 5 * z]; + } + } + } + LinearUpscale3DArray(Workspace, 5, 5, 33, m_NoiseArray, 4, 4, 8); +} + + + + + +void cBiomalNoise3DComposable::CalcBiomeParamArrays(int a_ChunkX, int a_ChunkZ, ChunkParam & a_HeightAmp, ChunkParam & a_MidPoint) +{ + // 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]); + } + } + + // Sum up the biome values: + for (int z = 0; z < 5; z++) + { + for (int x = 0; x < 5; x++) + { + NOISE_DATATYPE totalHeightAmp = 0; + NOISE_DATATYPE totalMidPoint = 0; + // Add up the biomes around this point: + for (int relz = 0; relz <= AVERAGING_SIZE * 2; ++relz) + { + 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) + { + 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 +} + + + + + +void cBiomalNoise3DComposable::GetBiomeParams(EMCSBiome a_Biome, NOISE_DATATYPE & a_HeightAmp, NOISE_DATATYPE & a_MidPoint) +{ + switch (a_Biome) + { + case biDesert: a_HeightAmp = 0.29f; a_MidPoint = 62; break; // Needs verification + case biExtremeHills: a_HeightAmp = 0.045f; a_MidPoint = 75; break; + case biPlains: a_HeightAmp = 0.3f; a_MidPoint = 62; break; // Needs verification + case biSwampland: a_HeightAmp = 0.25f; a_MidPoint = 59; break; + case biSwamplandM: a_HeightAmp = 0.11f; a_MidPoint = 59; break; + + default: + { + // Make a crazy terrain so that it stands out + a_HeightAmp = 0.001f; + a_MidPoint = 90; + break; + } + } +} + + + + + +void cBiomalNoise3DComposable::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) { GenerateNoiseArrayIfNeeded(a_ChunkX, a_ChunkZ); @@ -555,7 +801,7 @@ void cNoise3DComposable::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::Hei -void cNoise3DComposable::ComposeTerrain(cChunkDesc & a_ChunkDesc) +void cBiomalNoise3DComposable::ComposeTerrain(cChunkDesc & a_ChunkDesc) { GenerateNoiseArrayIfNeeded(a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ()); diff --git a/src/Generating/Noise3DGenerator.h b/src/Generating/Noise3DGenerator.h index 207aeab97..ba541fbcc 100644 --- a/src/Generating/Noise3DGenerator.h +++ b/src/Generating/Noise3DGenerator.h @@ -138,3 +138,89 @@ protected: + +class cBiomalNoise3DComposable : + public cTerrainHeightGen, + public cTerrainCompositionGen +{ +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. */ + static const int AVERAGING_SIZE = 5; + + /** Type used for a single parameter across the entire (downscaled) chunk. */ + typedef NOISE_DATATYPE ChunkParam[5 * 5]; + + + /** The noise that is used to choose between density noise A and B. */ + cPerlinNoise m_ChoiceNoise; + + /** Density 3D noise, variant A. */ + cPerlinNoise m_DensityNoiseA; + + /** Density 3D noise, variant B. */ + cPerlinNoise m_DensityNoiseB; + + /** Heightmap-like noise used to provide variance for low-amplitude biomes. */ + cPerlinNoise 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]; // x + 17 * z + 17 * 17 * y + + /** 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); + + // cTerrainHeightGen overrides: + virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override; + virtual void InitializeHeightGen(cIniFile & a_IniFile) override { Initialize(a_IniFile); } + + // cTerrainCompositionGen overrides: + virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override; + virtual void InitializeCompoGen(cIniFile & a_IniFile) override { Initialize(a_IniFile); } +} ; + + + + -- cgit v1.2.3