// EndGen.cpp // Implements the cEndGen class representing the generator for the End, both as a HeightGen and CompositionGen #include "Globals.h" #include "EndGen.h" #include "../IniFile.h" #include "../LinearUpscale.h" enum { // Interpolation cell size: INTERPOL_X = 4, INTERPOL_Y = 4, INTERPOL_Z = 4, // Size of chunk data, downscaled before interpolation: DIM_X = 16 / INTERPOL_X + 1, DIM_Y = 256 / INTERPOL_Y + 1, DIM_Z = 16 / INTERPOL_Z + 1, }; //////////////////////////////////////////////////////////////////////////////// // cEndGen: cEndGen::cEndGen(int a_Seed) : m_Seed(a_Seed), m_Perlin(m_Seed), m_MainIslandSize(200), m_IslandThickness(32), m_IslandYOffset(30), m_MainIslandFrequencyX(100), m_MainIslandFrequencyY(80), m_MainIslandFrequencyZ(100), m_MainIslandMinThreshold(0.2f), m_SmallIslandFrequencyX(50), m_SmallIslandFrequencyY(80), m_SmallIslandFrequencyZ(50), m_SmallIslandMinThreshold(-0.5f), m_LastChunkCoords(0x7fffffff, 0x7fffffff) // Use dummy coords that won't ever be used by real chunks { m_Perlin.AddOctave(1, 1); m_Perlin.AddOctave(2, 0.5); m_Perlin.AddOctave(4, 0.25); } void cEndGen::InitializeShapeGen(cIniFile & a_IniFile) { m_MainIslandSize = a_IniFile.GetValueSetI("Generator", "EndGenMainIslandSize", m_MainIslandSize); m_IslandThickness = a_IniFile.GetValueSetI("Generator", "EndGenIslandThickness", m_IslandThickness); m_IslandYOffset = a_IniFile.GetValueSetI("Generator", "EndGenIslandYOffset", m_IslandYOffset); m_MainIslandFrequencyX = static_cast(a_IniFile.GetValueSetF("Generator", "EndGenMainFrequencyX", m_MainIslandFrequencyX)); m_MainIslandFrequencyY = static_cast(a_IniFile.GetValueSetF("Generator", "EndGenMainFrequencyY", m_MainIslandFrequencyY)); m_MainIslandFrequencyZ = static_cast(a_IniFile.GetValueSetF("Generator", "EndGenMainFrequencyZ", m_MainIslandFrequencyZ)); m_MainIslandMinThreshold = static_cast(a_IniFile.GetValueSetF("Generator", "EndGenMainMinThreshold", m_MainIslandMinThreshold)); m_SmallIslandFrequencyX = static_cast(a_IniFile.GetValueSetF("Generator", "EndGenSmallFrequencyX", m_SmallIslandFrequencyX)); m_SmallIslandFrequencyY = static_cast(a_IniFile.GetValueSetF("Generator", "EndGenSmallFrequencyY", m_SmallIslandFrequencyY)); m_SmallIslandFrequencyZ = static_cast(a_IniFile.GetValueSetF("Generator", "EndGenSmallFrequencyZ", m_SmallIslandFrequencyZ)); m_SmallIslandMinThreshold = static_cast(a_IniFile.GetValueSetF("Generator", "EndGenSmallMinThreshold", m_SmallIslandMinThreshold)); } void cEndGen::PrepareState(cChunkCoords a_ChunkCoords) { if (m_LastChunkCoords == a_ChunkCoords) { return; } m_LastChunkCoords = a_ChunkCoords; GenerateNoiseArray(); } void cEndGen::GenerateNoiseArray(void) { NOISE_DATATYPE NoiseData[DIM_X * DIM_Y * DIM_Z]; // [x + DIM_X * z + DIM_X * DIM_Z * y] NOISE_DATATYPE Workspace[DIM_X * DIM_Y * DIM_Z]; // [x + DIM_X * z + DIM_X * DIM_Z * y] // Choose the frequency to use depending on the distance from spawn. auto distanceFromSpawn = cChunkDef::RelativeToAbsolute({ cChunkDef::Width / 2, 0, cChunkDef::Width / 2 }, m_LastChunkCoords).Length(); auto frequencyX = distanceFromSpawn > m_MainIslandSize * 2 ? m_SmallIslandFrequencyX : m_MainIslandFrequencyX; auto frequencyY = distanceFromSpawn > m_MainIslandSize * 2 ? m_SmallIslandFrequencyY : m_MainIslandFrequencyY; auto frequencyZ = distanceFromSpawn > m_MainIslandSize * 2 ? m_SmallIslandFrequencyZ : m_MainIslandFrequencyZ; // Generate the downscaled noise: auto StartX = static_cast(m_LastChunkCoords.m_ChunkX * cChunkDef::Width) / frequencyX; auto EndX = static_cast((m_LastChunkCoords.m_ChunkX + 1) * cChunkDef::Width) / frequencyX; auto StartZ = static_cast(m_LastChunkCoords.m_ChunkZ * cChunkDef::Width) / frequencyZ; auto EndZ = static_cast((m_LastChunkCoords.m_ChunkZ + 1) * cChunkDef::Width) / frequencyZ; auto StartY = 0.0f; auto EndY = static_cast(cChunkDef::Height) / frequencyY; m_Perlin.Generate3D(NoiseData, DIM_X, DIM_Z, DIM_Y, StartX, EndX, StartZ, EndZ, StartY, EndY, Workspace); // Add distance: for (int y = 0; y < DIM_Y; y++) { auto ValY = static_cast(2 * INTERPOL_Y * y - m_IslandThickness) / m_IslandThickness; ValY = static_cast(std::pow(ValY, 6)); for (int z = 0; z < DIM_Z; z++) { for (int x = 0; x < DIM_X; x++) { NoiseData[x + DIM_X * z + DIM_X * DIM_Z * y] += ValY; } // for x } // for z } // for y // Upscale into real chunk size: LinearUpscale3DArray(NoiseData, DIM_X, DIM_Z, DIM_Y, m_NoiseArray, INTERPOL_X, INTERPOL_Z, INTERPOL_Y); } void cEndGen::GenShape(cChunkCoords a_ChunkCoords, cChunkDesc::Shape & a_Shape) { PrepareState(a_ChunkCoords); int MaxY = std::min(static_cast(1.75 * m_IslandThickness + m_IslandYOffset), cChunkDef::Height - 1); // Choose which threshold to use depending on the distance from spawn. double chunkDistanceFromSpawn = cChunkDef::RelativeToAbsolute({ cChunkDef::Width / 2, 0, cChunkDef::Width / 2 }, a_ChunkCoords).Length(); double minThreshold = chunkDistanceFromSpawn > m_MainIslandSize * 2 ? m_SmallIslandMinThreshold : m_MainIslandMinThreshold; for (int z = 0; z < cChunkDef::Width; z++) { for (int x = 0; x < cChunkDef::Width; x++) { // Calculate the required treshold based on the distance from spawn. // This way a void can be generated between the main island and the other islands. double distanceFromSpawn = cChunkDef::RelativeToAbsolute({ x, 0, z }, a_ChunkCoords).Length(); double pow = std::pow((distanceFromSpawn - m_MainIslandSize) / m_MainIslandSize / 2, 3); double mult = 3 * ((distanceFromSpawn - m_MainIslandSize) / m_MainIslandSize); double threshold = std::min(pow - mult, minThreshold); for (int y = 0; y < m_IslandYOffset; y++) { cChunkDesc::SetShapeIsSolidAt(a_Shape, x, y, z, false); } for (int y = m_IslandYOffset; y < MaxY; y++) { cChunkDesc::SetShapeIsSolidAt(a_Shape, x, y, z, m_NoiseArray[(y - m_IslandYOffset) * 17 * 17 + z * 17 + x] <= threshold); } for (int y = MaxY; y < cChunkDef::Height; y++) { cChunkDesc::SetShapeIsSolidAt(a_Shape, x, y, z, false); } } // for x } // for z } void cEndGen::ComposeTerrain(cChunkDesc & a_ChunkDesc, const cChunkDesc::Shape & a_Shape) { 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 = 0; y < cChunkDef::Height; y++) { if (cChunkDesc::GetShapeIsSolidAt(a_Shape, x, y, z)) { a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_END_STONE); } } // for y } // for x } // for z }