From 8f2920a090dd9000aecb04c901311ffe195ea39b Mon Sep 17 00:00:00 2001 From: "madmaxoft@gmail.com" Date: Sat, 11 May 2013 21:09:39 +0000 Subject: Converted the Noise3D generator to optimized noise and lerp git-svn-id: http://mc-server.googlecode.com/svn/trunk@1471 0a769ca7-a7f5-676a-18bf-c427514a06d6 --- source/Generating/Noise3DGenerator.cpp | 280 ++++++++++++++++++++------------- source/Generating/Noise3DGenerator.h | 12 +- 2 files changed, 183 insertions(+), 109 deletions(-) (limited to 'source') diff --git a/source/Generating/Noise3DGenerator.cpp b/source/Generating/Noise3DGenerator.cpp index 2eae7cbc8..e406127ed 100644 --- a/source/Generating/Noise3DGenerator.cpp +++ b/source/Generating/Noise3DGenerator.cpp @@ -7,6 +7,62 @@ #include "Noise3DGenerator.h" #include "../OSSupport/File.h" #include "../../iniFile/iniFile.h" +#include "../LinearInterpolation.h" + + + + + +void Debug3DNoise(NOISE_DATATYPE * a_Noise, int a_SizeX, int a_SizeY, int a_SizeZ, const AString & a_FileNameBase) +{ + const int BUF_SIZE = 512; + ASSERT(a_SizeX <= BUF_SIZE); // Just stretch it, if needed + + // Save in XY cuts: + cFile f1; + if (f1.Open(Printf("%s_XY (%d).grab", a_FileNameBase.c_str(), a_SizeX), cFile::fmWrite)) + { + for (int z = 0; z < a_SizeZ; z++) + { + for (int y = 0; y < a_SizeY; y++) + { + int idx = y * a_SizeX + z * a_SizeX * a_SizeY; + unsigned char buf[BUF_SIZE]; + for (int x = 0; x < a_SizeX; x++) + { + buf[x] = (unsigned char)(std::min(256, std::max(0, (int)(128 + 32 * a_Noise[idx++])))); + } + f1.Write(buf, a_SizeX); + } // for y + unsigned char buf[BUF_SIZE]; + memset(buf, 0, a_SizeX); + f1.Write(buf, a_SizeX); + } // for z + } // if (XY file open) + + cFile f2; + if (f2.Open(Printf("%s_XZ (%d).grab", a_FileNameBase.c_str(), a_SizeX), cFile::fmWrite)) + { + for (int y = 0; y < a_SizeY; y++) + { + for (int z = 0; z < a_SizeZ; z++) + { + int idx = y * a_SizeX + z * a_SizeX * a_SizeY; + unsigned char buf[BUF_SIZE]; + for (int x = 0; x < a_SizeX; x++) + { + buf[x] = (unsigned char)(std::min(256, std::max(0, (int)(128 + 32 * a_Noise[idx++])))); + } + f2.Write(buf, a_SizeX); + } // for z + unsigned char buf[BUF_SIZE]; + memset(buf, 0, a_SizeX); + f2.Write(buf, a_SizeX); + } // for y + } // if (XZ file open) + //*/ + +} @@ -17,10 +73,75 @@ cNoise3DGenerator::cNoise3DGenerator(cChunkGenerator & a_ChunkGenerator) : super(a_ChunkGenerator), - m_Noise1(1000), - m_Noise2(2000), - m_Noise3(3000) + m_Perlin(1000), + m_Cubic(1000) { + 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++) + { + GenerateNoiseArray(x, 5, Noise[x]); + } + + // Save in XY cuts: + cFile f1; + if (f1.Open("Test_XY.grab", cFile::fmWrite)) + { + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int y = 0; y < cChunkDef::Height; y++) + { + for (int i = 0; i < NumChunks; i++) + { + int idx = y * cChunkDef::Width + z * cChunkDef::Width * cChunkDef::Height; + unsigned char buf[cChunkDef::Width]; + for (int x = 0; x < cChunkDef::Width; x++) + { + buf[x] = (unsigned char)(std::min(256, std::max(0, (int)(128 + 32 * Noise[i][idx++])))); + } + f1.Write(buf, cChunkDef::Width); + } + } // for y + } // for z + } // if (XY file open) + + cFile f2; + if (f2.Open("Test_XZ.grab", cFile::fmWrite)) + { + for (int y = 0; y < cChunkDef::Height; y++) + { + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int i = 0; i < NumChunks; i++) + { + int idx = y * cChunkDef::Width + z * cChunkDef::Width * cChunkDef::Height; + unsigned char buf[cChunkDef::Width]; + for (int x = 0; x < cChunkDef::Width; x++) + { + buf[x] = (unsigned char)(std::min(256, std::max(0, (int)(128 + 32 * Noise[i][idx++])))); + } + f2.Write(buf, cChunkDef::Width); + } + } // for z + } // for y + } // if (XZ file open) + #endif // 0 } @@ -44,9 +165,9 @@ void cNoise3DGenerator::Initialize(cWorld * a_World, cIniFile & a_IniFile) 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_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); m_AirThreshold = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DAirThreshold", 0.5); } @@ -68,15 +189,15 @@ void cNoise3DGenerator::GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::Bi void cNoise3DGenerator::DoGenerate(int a_ChunkX, int a_ChunkZ, cChunkDesc & a_ChunkDesc) { - NOISE_DATATYPE Noise[257 * 17 * 17]; // x + 17 * z + 17 * 17 * y + NOISE_DATATYPE Noise[cChunkDef::Width * cChunkDef::Height * cChunkDef::Width]; GenerateNoiseArray(a_ChunkX, a_ChunkZ, Noise); // Output noise into chunk: - for (int y = 0; y < cChunkDef::Height; y++) + int idx = 0; + for (int z = 0; z < cChunkDef::Width; z++) { - for (int z = 0; z < cChunkDef::Width; z++) + for (int y = 0; y < cChunkDef::Height; y++) { - int idx = y * 17 * 17 + z * 17; for (int x = 0; x < cChunkDef::Width; x++) { NOISE_DATATYPE n = Noise[idx++]; @@ -102,115 +223,64 @@ void cNoise3DGenerator::DoGenerate(int a_ChunkX, int a_ChunkZ, cChunkDesc & a_Ch -void cNoise3DGenerator::GenerateNoiseArray(int a_ChunkX, int a_ChunkZ, NOISE_DATATYPE * a_Noise) +void cNoise3DGenerator::GenerateNoiseArray(int a_ChunkX, int a_ChunkZ, NOISE_DATATYPE * a_OutNoise) { - // Parameters: - const int INTERPOL_X = 8; - const int INTERPOL_Y = 4; - const int INTERPOL_Z = 8; - + NOISE_DATATYPE NoiseO[DIMX * DIMY * DIMZ]; // Output for the Perlin noise + NOISE_DATATYPE NoiseW[DIMX * DIMY * DIMZ]; // 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 StartY = 0; + NOISE_DATATYPE EndY = ((NOISE_DATATYPE)256) / m_FrequencyY; + + m_Perlin.Generate3D(NoiseO, DIMX, DIMY, DIMZ, StartX, EndX, StartY, EndY, StartZ, EndZ, NoiseW); + + // DEBUG: Debug3DNoise(NoiseO, DIMX, DIMY, DIMZ, Printf("Chunk_%d_%d_orig", a_ChunkX, a_ChunkZ)); + + // Linearly interpolate the Perlin noise into full-blown chunk dimensions: + LinearInterpolate3DArray( + NoiseO, DIMX, DIMY, DIMZ, + a_OutNoise, cChunkDef::Width, cChunkDef::Height, cChunkDef::Width + ); + + // DEBUG: Debug3DNoise(a_OutNoise, cChunkDef::Width, cChunkDef::Height, cChunkDef::Width, Printf("Chunk_%d_%d_lerp", a_ChunkX, a_ChunkZ)); + + // Modify the noise to account for the wanted elevation: + // 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 Test1 = 0; + NOISE_DATATYPE HeightS[DIMX * DIMZ]; // Output for the cubic noise heightmap ("source") + NOISE_DATATYPE Test2 = 0; + NOISE_DATATYPE Height[cChunkDef::Width * cChunkDef::Width]; // Lerp-ed heightmap [x + Width * z] + m_Cubic.Generate2D(HeightS, DIMX, DIMZ, StartX / 25, EndX / 25, StartZ / 25, EndZ / 25); + LinearInterpolate2DArray( + HeightS, DIMX, DIMZ, + Height, cChunkDef::Width, cChunkDef::Width + ); + for (int i = 0; i < ARRAYCOUNT(Height); i++) { - NOISE_DATATYPE NoiseY = ((NOISE_DATATYPE)y) / m_FrequencyY; - NOISE_DATATYPE AddHeight = (y - m_MidPoint) / 20; - AddHeight *= AddHeight * AddHeight; // * AddHeight * AddHeight; - NOISE_DATATYPE * CurFloor = &(a_Noise[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); + Height[i] = abs(Height[i]) * m_HeightAmplification + 1; } - // Finish the 3D linear interpolation by interpolating between each XZ-floors on the Y axis - for (int y = 1; y < cChunkDef::Height; y++) + // Modify noise by height data + for (int y = 0; 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 = &(a_Noise[LoFloorY * 17 * 17]); - NOISE_DATATYPE * HiFloor = &(a_Noise[HiFloorY * 17 * 17]); - NOISE_DATATYPE * CurFloor = &(a_Noise[y * 17 * 17]); - NOISE_DATATYPE Ratio = ((NOISE_DATATYPE)(y % INTERPOL_Y)) / INTERPOL_Y; - int idx = 0; + NOISE_DATATYPE AddHeight = (y - m_MidPoint) / 20; + AddHeight *= AddHeight * AddHeight; for (int z = 0; z < cChunkDef::Width; z++) { + NOISE_DATATYPE * CurRow = &(a_OutNoise[y * cChunkDef::Width + z * cChunkDef::Width * cChunkDef::Height]); for (int x = 0; x < cChunkDef::Width; x++) { - CurFloor[idx] = LoFloor[idx] + (HiFloor[idx] - LoFloor[idx]) * Ratio; - idx += 1; + CurRow[x] += AddHeight / Height[x + cChunkDef::Width * z]; } - 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 * a_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 * a_Noise[idx++])))); - } - f2.Write(buf, 16); - } // for z - } // for y - } // if (XZ file open) - */ + // DEBUG: Debug3DNoise(a_OutNoise, cChunkDef::Width, cChunkDef::Height, cChunkDef::Width, Printf("Chunk_%d_%d", a_ChunkX, a_ChunkZ); } diff --git a/source/Generating/Noise3DGenerator.h b/source/Generating/Noise3DGenerator.h index 071b19019..57cf024f8 100644 --- a/source/Generating/Noise3DGenerator.h +++ b/source/Generating/Noise3DGenerator.h @@ -29,9 +29,13 @@ public: virtual void DoGenerate(int a_ChunkX, int a_ChunkZ, cChunkDesc & a_ChunkDesc) override; protected: - cNoise m_Noise1; - cNoise m_Noise2; - cNoise m_Noise3; + // Linear interpolation dimensions: + static const int DIMX = 5; + static const int DIMY = 65; + static const int DIMZ = 5; + + cPerlinNoise m_Perlin; // The base 3D noise source for the actual composition + cCubicNoise m_Cubic; // The noise used for heightmap directing int m_SeaLevel; NOISE_DATATYPE m_HeightAmplification; @@ -41,7 +45,7 @@ protected: NOISE_DATATYPE m_FrequencyZ; NOISE_DATATYPE m_AirThreshold; - /// Generates the 3D noise array used for terrain generation + /// Generates the 3D noise array used for terrain generation; a_Noise is of ChunkData-size void GenerateNoiseArray(int a_ChunkX, int a_ChunkZ, NOISE_DATATYPE * a_Noise); /// Updates heightmap based on the chunk's contents -- cgit v1.2.3