Re-implemented tree-growing. May produce artefacts on old-world / new-world boundaries.

git-svn-id: http://mc-server.googlecode.com/svn/trunk@293 0a769ca7-a7f5-676a-18bf-c427514a06d6
master
madmaxoft@gmail.com 2012-02-18 19:18:16 +00:00
parent 499745c1c7
commit 76a92a21d1
11 changed files with 212 additions and 118 deletions

View File

@ -248,6 +248,16 @@ void cChunk::SetAllData(const char * a_BlockData, cEntityList & a_Entities, cBlo
/// Copies m_BlockData into a_Blocks, only the block types
void cChunk::GetBlocks(char * a_Blocks)
{
memcpy(a_Blocks, m_BlockData, cChunk::c_NumBlocks);
}
/// Returns true if there is a block entity at the coords specified
bool cChunk::HasBlockEntityAt(int a_BlockX, int a_BlockY, int a_BlockZ)
{

View File

@ -73,6 +73,9 @@ public:
class cChunk
{
public:
static const int c_NumBlocks = 16 * 128 * 16;
static const int c_BlockDataSize = c_NumBlocks * 2 + (c_NumBlocks/2); // 2.5 * numblocks
cChunk(int a_X, int a_Y, int a_Z, cWorld* a_World);
~cChunk();
@ -98,6 +101,9 @@ public:
/// Sets all chunk data
void SetAllData(const char * a_BlockData, cEntityList & a_Entities, cBlockEntityList & a_BlockEntities);
/// Copies m_BlockData into a_Blocks, only the block types
void GetBlocks(char a_Blocks[cChunk::c_NumBlocks]);
/// Returns true if there is a block entity at the coords specified
bool HasBlockEntityAt(int a_BlockX, int a_BlockY, int a_BlockZ);
@ -183,9 +189,6 @@ public:
m_IsSaving = false;
}
static const int c_NumBlocks = 16*128*16;
static const int c_BlockDataSize = c_NumBlocks * 2 + (c_NumBlocks/2); // 2.5 * numblocks
private:
bool m_IsValid; // True if the chunk is loaded / generated

View File

@ -182,6 +182,7 @@ void cChunkGenerator::DoGenerate(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
cBlockEntityList BlockEntities;
m_pWorldGenerator->GenerateChunk(a_ChunkX, a_ChunkY, a_ChunkZ, BlockData, Entities, BlockEntities);
m_World->ChunkDataGenerated(a_ChunkX, a_ChunkY, a_ChunkZ, BlockData, Entities, BlockEntities);
m_pWorldGenerator->PostGenerateChunk(a_ChunkX, a_ChunkY, a_ChunkZ);
}

View File

@ -271,6 +271,22 @@ void cChunkMap::GetChunkData(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cChunkDat
bool cChunkMap::GetChunkBlocks(int a_ChunkX, int a_ChunkY, int a_ChunkZ, char * a_Blocks)
{
cCSLock Lock(m_CSLayers);
cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkY, a_ChunkZ);
if ((Chunk == NULL) || !Chunk->IsValid())
{
return false;
}
Chunk->GetBlocks(a_Blocks);
return true;
}
bool cChunkMap::IsChunkValid(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
{
cCSLock Lock(m_CSLayers);

View File

@ -42,6 +42,7 @@ public:
void ChunkDataLoaded (int a_ChunkX, int a_ChunkY, int a_ChunkZ, const char * a_BlockData, cEntityList & a_Entities, cBlockEntityList & a_BlockEntities);
void ChunkDataGenerated (int a_ChunkX, int a_ChunkY, int a_ChunkZ, const char * a_BlockData, cEntityList & a_Entities, cBlockEntityList & a_BlockEntities);
void GetChunkData (int a_ChunkX, int a_ChunkY, int a_ChunkZ, cChunkDataCallback * a_Callback);
bool GetChunkBlocks (int a_ChunkX, int a_ChunkY, int a_ChunkZ, char * a_Blocks);
bool IsChunkValid (int a_ChunkX, int a_ChunkY, int a_ChunkZ);
bool HasChunkAnyClients (int a_ChunkX, int a_ChunkY, int a_ChunkZ);
void SpreadChunkLighting(int a_ChunkX, int a_ChunkY, int a_ChunkZ);

View File

@ -431,7 +431,9 @@ void cWorld::InitializeSpawn()
int ChunkX = 0, ChunkY = 0, ChunkZ = 0;
BlockToChunk( (int)m_SpawnX, (int)m_SpawnY, (int)m_SpawnZ, ChunkX, ChunkY, ChunkZ );
int ViewDist = 20; // Always prepare an area 20 chunks across, no matter what the actual cClientHandle::VIEWDISTANCE is
// DEBUG:
// int ViewDist = 20; // Always prepare an area 20 chunks across, no matter what the actual cClientHandle::VIEWDISTANCE is
int ViewDist = 5; // Always prepare an area 20 chunks across, no matter what the actual cClientHandle::VIEWDISTANCE is
LOG("Preparing spawn area in world \"%s\"", m_WorldName.c_str());
for (int x = 0; x < ViewDist; x++)
@ -744,32 +746,42 @@ void cWorld::GrowTree( int a_X, int a_Y, int a_Z )
int trunk = r1.randInt() % (7 - 5 + 1) + 5;
for (int i = 0; i < trunk; i++)
{
if( GetBlock( a_X, a_Y + i, a_Z ) == E_BLOCK_AIR )
FastSetBlock( a_X, a_Y + i, a_Z, E_BLOCK_LOG, 0 );
FastSetBlock( a_X, a_Y + i, a_Z, E_BLOCK_LOG, 0 );
}
// build tree
for (int j = 0; j < trunk; j++) {
for (int j = 0; j < trunk; j++)
{
int radius = trunk - j;
if (radius < 4) {
if (radius > 2) {
if (radius < 4)
{
if (radius > 2)
{
radius = 2;
}
for (int i = a_X - radius; i <= a_X + radius; i++) {
for (int k = a_Z-radius; k <= a_Z + radius; k++) {
for (int i = a_X - radius; i <= a_X + radius; i++)
{
for (int k = a_Z-radius; k <= a_Z + radius; k++)
{
// small chance to be missing a block to add a little random
if (k != a_Z || i != a_X && (r1.randInt() % 100 + 1) > 20) {
if (k != a_Z || i != a_X && (r1.randInt() % 100 + 1) > 20)
{
if( GetBlock( i, a_Y + j, k ) == E_BLOCK_AIR )
{
FastSetBlock(i, a_Y+j, k, E_BLOCK_LEAVES, 0 );
}
}
else {
else
{
//if( m_BlockType[ MakeIndex(i, TopY+j, k) ] == E_BLOCK_AIR )
// m_BlockType[ MakeIndex(i, TopY+j, k) ] = E_BLOCK_LEAVES;
}
}
}
if( GetBlock( a_X, a_Y+j, a_Z ) == E_BLOCK_AIR )
if (GetBlock( a_X, a_Y+j, a_Z ) == E_BLOCK_AIR )
{
FastSetBlock( a_X, a_Y+j, a_Z, E_BLOCK_LOG, 0 );
}
}
}
@ -1069,6 +1081,15 @@ void cWorld::GetChunkData(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cChunkDataCa
bool cWorld::GetChunkBlocks(int a_ChunkX, int a_ChunkY, int a_ChunkZ, char * a_Blocks)
{
return m_ChunkMap->GetChunkBlocks(a_ChunkX, a_ChunkY, a_ChunkZ, a_Blocks);
}
bool cWorld::IsChunkValid(int a_ChunkX, int a_ChunkY, int a_ChunkZ) const
{
return m_ChunkMap->IsChunkValid(a_ChunkX, a_ChunkY, a_ChunkZ);

View File

@ -76,6 +76,7 @@ public:
void ChunkDataLoaded (int a_ChunkX, int a_ChunkY, int a_ChunkZ, const char * a_BlockData, cEntityList & a_Entities, cBlockEntityList & a_BlockEntities);
void ChunkDataGenerated(int a_ChunkX, int a_ChunkY, int a_ChunkZ, const char * a_BlockData, cEntityList & a_Entities, cBlockEntityList & a_BlockEntities);
void GetChunkData (int a_ChunkX, int a_ChunkY, int a_ChunkZ, cChunkDataCallback * a_Callback);
bool GetChunkBlocks (int a_ChunkX, int a_ChunkY, int a_ChunkZ, char * a_Blocks);
bool IsChunkValid (int a_ChunkX, int a_ChunkY, int a_ChunkZ) const;
bool HasChunkAnyClients(int a_ChunkX, int a_ChunkY, int a_ChunkZ) const;
void UnloadUnusedChunks(void);

View File

@ -14,6 +14,27 @@
// An array describing an 8-way neighbor coords deltas
static struct
{
int m_X;
int m_Z;
} g_NeighborCoords[] =
{
{-1, -1},
{-1, 0},
{-1, 1},
{0, -1},
{0, 1},
{1, -1},
{1, 0},
{1, 1},
} ;
cWorldGenerator::cWorldGenerator(cWorld * a_World) :
m_World(a_World)
{
@ -40,6 +61,46 @@ void cWorldGenerator::GenerateChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, ch
void cWorldGenerator::PostGenerateChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
{
// Check the chunk just generated and all its 8-way neighbors
CheckNeighbors(a_ChunkX, a_ChunkY, a_ChunkZ);
for (int i = 0; i < ARRAYCOUNT(g_NeighborCoords); i++)
{
CheckNeighbors(a_ChunkX + g_NeighborCoords[i].m_X, a_ChunkY, a_ChunkZ + g_NeighborCoords[i].m_Z);
} // for i - g_NeighborCoords[]
}
void cWorldGenerator::CheckNeighbors(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
{
if (!m_World->IsChunkValid(a_ChunkX, a_ChunkY, a_ChunkZ))
{
return;
}
// Check all 8-way neighbors, if they are all valid, generate foliage in this chunk:
int NumNeighbors = 0;
for (int i = 0; i < ARRAYCOUNT(g_NeighborCoords); i++)
{
if (m_World->IsChunkValid(a_ChunkX + g_NeighborCoords[i].m_X, a_ChunkY, a_ChunkZ + g_NeighborCoords[i].m_Z))
{
NumNeighbors++;
}
} // for i - g_NeighborCoords[]
if (NumNeighbors == 8)
{
GenerateFoliage(a_ChunkX, a_ChunkY, a_ChunkZ);
}
}
static float GetNoise( float x, float y, cNoise & a_Noise )
{
float oct1 = a_Noise.CubicNoise2D( x*cGenSettings::HeightFreq1, y*cGenSettings::HeightFreq1 )*cGenSettings::HeightAmp1;
@ -187,7 +248,8 @@ void cWorldGenerator::GenerateTerrain(int a_ChunkX, int a_ChunkY, int a_ChunkZ,
{
a_BlockData[ MakeIndex(x, y, z) ] = E_BLOCK_DIRT;
}
}
} // for y
if (Top + 1 >= WATER_LEVEL + SAND_LEVEL)
{
// Replace top dirt with grass:
@ -201,111 +263,84 @@ void cWorldGenerator::GenerateTerrain(int a_ChunkX, int a_ChunkY, int a_ChunkZ,
a_BlockData[ MakeIndex(x, y, z) ] = E_BLOCK_STATIONARY_WATER;
}
}
}
}
}
void cWorldGenerator::GenerateFoliage( cChunkPtr & a_Chunk )
{
const ENUM_BLOCK_ID GrassID = E_BLOCK_GRASS;
const ENUM_BLOCK_ID DirtID = E_BLOCK_DIRT;
const ENUM_BLOCK_ID SandID = E_BLOCK_SAND;
const ENUM_BLOCK_ID SandStoneID = E_BLOCK_SANDSTONE;
const ENUM_BLOCK_ID CaveID = E_BLOCK_AIR;
int PosX = a_Chunk->GetPosX();
int PosZ = a_Chunk->GetPosZ();
cWorld* World = a_Chunk->GetWorld();
cNoise m_Noise( World->GetWorldSeed() );
char* BlockType = a_Chunk->pGetType();
for(int z = 0; z < 16; z++) for(int x = 0; x < 16; x++)
{
// Find top most Y
int TopY = -1;
for(int y = 127; y > 0; y--)
{
int index = MakeIndex( x, y, z );
if( BlockType[index] != E_BLOCK_AIR )
// Generate small foliage (1-block):
int index = MakeIndex(x, Top - 1, z);
int TopY = Top - 1;
float val1 = Noise.CubicNoise2D(xx * 0.1f, zz * 0.1f );
float val2 = Noise.CubicNoise2D(xx * 0.01f, zz * 0.01f );
if( a_BlockData[index] == E_BLOCK_SAND )
{
TopY = y;
break;
}
}
if( TopY > 0 )
{
// Change top dirt into grass and prevent sand from floating over caves
int index = MakeIndex( x, TopY, z );
int index1 = MakeIndex( x, TopY-1, z );
int index2 = MakeIndex( x, TopY-2, z );
int index3 = MakeIndex( x, TopY-3, z );
int index4 = MakeIndex( x, TopY-4, z );
int index5 = MakeIndex( x, TopY-5, z );
if( BlockType[index] == SandID ) {
if( BlockType[index1] == CaveID ) {
BlockType[ index ] = (char)SandStoneID;
} else if( BlockType[index2] == CaveID ) {
BlockType[ index1 ] = (char)SandStoneID;
} else if( BlockType[index3] == CaveID ) {
BlockType[ index2 ] = (char)SandStoneID;
} else if( BlockType[index4] == CaveID ) {
BlockType[ index3 ] = (char)SandStoneID;
} else if( BlockType[index5] == CaveID ) {
BlockType[ index4 ] = (char)SandStoneID;
}
}
if( BlockType[index] == DirtID )
{
BlockType[ index ] = (char)GrassID;
}
// Plant sum trees
{
int xx = x + PosX*16;
int zz = z + PosZ*16;
float val1 = m_Noise.CubicNoise2D( xx*0.1f, zz*0.1f );
float val2 = m_Noise.CubicNoise2D( xx*0.01f, zz*0.01f );
if( BlockType[index] == SandID )
if ((val1 + val2 > 0.f) && ((r1.randInt() % 128) > 124))
{
if( (val1 + val2 > 0.f) && (r1.randInt()%128) > 124 && BlockType[index] == E_BLOCK_SAND )
a_BlockData[ MakeIndex(x, TopY + 1, z) ] = E_BLOCK_CACTUS;
if ((r1.randInt() & 3) == 3)
{
BlockType[ MakeIndex(x, TopY+1, z) ] = E_BLOCK_CACTUS;
if( (r1.randInt() & 3) == 3 )
{
BlockType[ MakeIndex(x, TopY+2, z) ] = E_BLOCK_CACTUS;
}
continue;
a_BlockData[ MakeIndex(x, TopY + 2, z) ] = E_BLOCK_CACTUS;
}
}
else if( BlockType[index] == GrassID )
{
float val3 = m_Noise.CubicNoise2D( xx*0.01f+10, zz*0.01f+10 );
float val4 = m_Noise.CubicNoise2D( xx*0.05f+20, zz*0.05f+20 );
if( val1 + val2 > 0.2f && (r1.randInt()%128) > 124 )
World->GrowTree( xx, TopY, zz );
else if( val3 > 0.2f && (r1.randInt()%128) > 124 )
BlockType[ MakeIndex(x, TopY+1, z) ] = E_BLOCK_YELLOW_FLOWER;
else if( val4 > 0.2f && (r1.randInt()%128) > 124 )
BlockType[ MakeIndex(x, TopY+1, z) ] = E_BLOCK_RED_ROSE;
else if( val1+val2+val3+val4 > 0.2f && (r1.randInt()%128) > 124 )
BlockType[ MakeIndex(x, TopY+1, z) ] = E_BLOCK_RED_MUSHROOM;
else if( val1+val2+val3+val4 > 0.2f && (r1.randInt()%128) > 124 )
BlockType[ MakeIndex(x, TopY+1, z) ] = E_BLOCK_BROWN_MUSHROOM;
continue;
}
}
}
}
else if( a_BlockData[index] == E_BLOCK_GRASS )
{
float val3 = Noise.CubicNoise2D(xx * 0.01f + 10, zz * 0.01f + 10 );
float val4 = Noise.CubicNoise2D(xx * 0.05f + 20, zz * 0.05f + 20 );
if( val3 > 0.2f && (r1.randInt()%128) > 124 )
{
a_BlockData[ MakeIndex(x, TopY+1, z) ] = E_BLOCK_YELLOW_FLOWER;
}
else if( val4 > 0.2f && (r1.randInt()%128) > 124 )
{
a_BlockData[ MakeIndex(x, TopY+1, z) ] = E_BLOCK_RED_ROSE;
}
else if( val1+val2+val3+val4 > 0.2f && (r1.randInt()%128) > 124 )
{
a_BlockData[ MakeIndex(x, TopY+1, z) ] = E_BLOCK_RED_MUSHROOM;
}
else if( val1+val2+val3+val4 > 0.2f && (r1.randInt()%128) > 124 )
{
a_BlockData[ MakeIndex(x, TopY+1, z) ] = E_BLOCK_BROWN_MUSHROOM;
}
}
} // for x
} // for z
}
void cWorldGenerator::GenerateFoliage(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
{
char BlockType[cChunk::c_NumBlocks];
if (!m_World->GetChunkBlocks(a_ChunkX, a_ChunkY, a_ChunkZ, BlockType))
{
LOGWARNING("Cannot generate foliage on chunk [%d, %d]", a_ChunkX, a_ChunkZ);
return;
}
cNoise Noise(m_World->GetWorldSeed());
for (int z = 0; z < 16; z++)
{
int zz = z + a_ChunkZ * 16;
for (int x = 0; x < 16; x++)
{
int xx = x + a_ChunkX * 16;
int TopY = m_World->GetHeight(xx, zz);
int index = MakeIndex(x, TopY - 1, z);
if (BlockType[index] == E_BLOCK_GRASS )
{
float val1 = Noise.CubicNoise2D( xx * 0.1f, zz * 0.1f );
float val2 = Noise.CubicNoise2D( xx * 0.01f, zz * 0.01f );
if ((val1 + val2 > 0.2f) && ((r1.randInt() % 128) > 124))
{
m_World->GrowTree( xx, TopY, zz );
}
} // if (Grass)
} // for x
} // for z
}

View File

@ -19,6 +19,8 @@ public:
~cWorldGenerator();
virtual void GenerateChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, char * a_BlockData, cEntityList & a_Entities, cBlockEntityList & a_BlockEntities);
virtual void PostGenerateChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ); // Called when the chunk has been already generated and set valid
protected:
@ -31,8 +33,10 @@ protected:
virtual void GenerateTerrain(int a_ChunkX, int a_ChunkY, int a_ChunkZ, char * a_BlockData);
virtual void GenerateFoliage(cChunkPtr & a_Chunk );
virtual void GenerateFoliage(int a_ChunkX, int a_ChunkY, int a_ChunkZ);
/// Checks if the chunk has all 8 neighbors valid, if so, foliage is generated on that chunk
void CheckNeighbors(int a_ChunkX, int a_ChunkY, int a_ChunkZ);
};

View File

@ -25,9 +25,11 @@ void cWorldGenerator_Test::GenerateTerrain(int a_ChunkX, int a_ChunkY, int a_Chu
void cWorldGenerator_Test::GenerateFoliage( cChunkPtr & a_Chunk )
void cWorldGenerator_Test::GenerateFoliage(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
{
(void)a_Chunk;
(void)a_ChunkX;
(void)a_ChunkY;
(void)a_ChunkZ;
}

View File

@ -17,7 +17,7 @@ public:
protected:
virtual void GenerateTerrain(int a_ChunkX, int a_ChunkY, int a_ChunkZ, char * a_BlockData) override;
virtual void GenerateFoliage(cChunkPtr & a_Chunk ) override;
virtual void GenerateFoliage(int a_ChunkX, int a_ChunkY, int a_ChunkZ) override;
};