From bc66bb2d409f13554bdcec7386766af82a343cad Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Sat, 2 Apr 2011 20:55:22 +0300 Subject: Mapgen is better now. Not a lot, but a bit! --- src/map.cpp | 1330 ++++++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 1139 insertions(+), 191 deletions(-) (limited to 'src/map.cpp') diff --git a/src/map.cpp b/src/map.cpp index aabe84067..651bece4f 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -1710,13 +1710,6 @@ ServerMap::ServerMap(std::string savedir): Map(dout_server), m_seed(0) { - - //m_chunksize = 64; - //m_chunksize = 16; // Too slow - //m_chunksize = 8; // Takes a few seconds - m_chunksize = 4; // Too small? - //m_chunksize = 2; - // TODO: Save to and load from a file m_seed = (((u64)(myrand()%0xffff)<<0) + ((u64)(myrand()%0xffff)<<16) @@ -1751,12 +1744,9 @@ ServerMap::ServerMap(std::string savedir): } else { - // Load map metadata (seed, chunksize) + // Load map metadata (seed) loadMapMeta(); - // Load chunk metadata - loadChunkMeta(); - /*// Load sector (0,0) and throw and exception on fail if(loadSectorFull(v2s16(0,0)) == false) throw LoadError("Failed to load sector (0,0)");*/ @@ -1819,16 +1809,6 @@ ServerMap::~ServerMap() dstream<::Iterator i = m_chunks.getIterator(); - for(; i.atEnd() == false; i++) - { - MapChunk *chunk = i.getNode()->getValue(); - delete chunk; - } } /* @@ -1883,6 +1863,7 @@ void make_tree(VoxelManipulator &vmanip, v3s16 p0) { MapNode treenode(CONTENT_TREE); MapNode leavesnode(CONTENT_LEAVES); + leavesnode.setLight(LIGHTBANK_DAY, LIGHT_MAX-1); vmanip.emerge(VoxelArea(p0-v3s16(2,0,2),p0+v3s16(2,7+2,2))); @@ -1962,18 +1943,18 @@ double tree_amount_2d(u64 seed, v2s16 p) double noise = noise2d_perlin( 0.5+(float)p.X/250, 0.5+(float)p.Y/250, seed+2, 5, 0.7); - double zeroval = -0.4; + double zeroval = -0.5; if(noise < zeroval) return 0; else - return 0.025 * (noise-zeroval) / (1.0-zeroval); + return 0.03 * (noise-zeroval) / (1.0-zeroval); } #define AVERAGE_MUD_AMOUNT 4.0 double get_mud_amount(u64 seed, v2f p) { - return ((float)AVERAGE_MUD_AMOUNT + 3.0 * noise2d_perlin( + return ((float)AVERAGE_MUD_AMOUNT + 2.5 * noise2d_perlin( 0.5+p.X/200, 0.5+p.Y/200, seed+1, 5, 0.65)); } @@ -2121,8 +2102,8 @@ double base_rock_level_2d(u64 seed, v2f p) { // Mountains double m4 = 1.0 - 3.0 * noise2d_perlin_abs( - 0.324+(float)p.X/2000., 0.423+(float)p.Y/2000., - (seed>>32)+65012102, 9, 0.57); + 0.324+(float)p.X/1000., 0.423+(float)p.Y/1000., + (seed>>32)+65012102, 8, 0.57); m4 *= 120; if(m4 > h) h = m4; @@ -2365,7 +2346,7 @@ bool is_carved(u64 seed, v3f p) #endif double f = 10.0; - double y_div = 1.5; + double y_div = 1.0; double v4 = contour(f*noise3d_perlin( 0.5+p.X/200, @@ -4082,6 +4063,7 @@ MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos, } #endif +#if 0 MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos, core::map &changed_blocks, bool force) @@ -4813,6 +4795,7 @@ MapChunk* ServerMap::generateChunk(v2s16 chunkpos1, MapChunk *chunk = getChunk(chunkpos1); return chunk; } +#endif ServerMapSector * ServerMap::createSector(v2s16 p2d) { @@ -4873,7 +4856,8 @@ MapSector * ServerMap::emergeSector(v2s16 p2d, DSTACK("%s: p2d=(%d,%d)", __FUNCTION_NAME, p2d.X, p2d.Y); - + +#if 0 /* Check chunk status */ @@ -4892,7 +4876,8 @@ MapSector * ServerMap::emergeSector(v2s16 p2d, // Generate chunk and neighbors generateChunk(chunkpos, changed_blocks); } - +#endif + /* Return sector if it exists now */ @@ -4923,7 +4908,7 @@ MapSector * ServerMap::emergeSector(v2s16 p2d, < &changed_blocks, - core::map &lighting_invalidated_blocks -) + bool force) { - DSTACK("%s: p=(%d,%d,%d)", - __FUNCTION_NAME, - p.X, p.Y, p.Z); - - /*dstream<<"generateBlock(): " - <<"("<isFullyGenerated()) + { + dstream<<"generateBlockRaw(): Block " + <<"("<createBlankBlockNoInsert(block_y); - } - else - { - // Remove the block so that nobody can get a half-generated one. - sector->removeBlock(block); - // Allocate the block to contain the generated data - block->unDummify(); - } + s16 y_blocks_min = blockpos0.Y-1; + s16 y_blocks_max = blockpos0.Y+1; + s16 y_nodes_min = y_blocks_min * MAP_BLOCKSIZE; + s16 y_nodes_max = y_blocks_max * MAP_BLOCKSIZE + MAP_BLOCKSIZE - 1; + v2s16 sectorpos_bigbase = sectorpos0 - v2s16(1,1); + s16 sectorpos_bigbase_size = 3; + v2s16 sectorpos_base = sectorpos0; + s16 sectorpos_base_size = 1; + s16 max_spread_amount = MAP_BLOCKSIZE; + s16 lighting_min_d = 0-max_spread_amount; + s16 lighting_max_d = sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1; - u8 water_material = CONTENT_WATERSOURCE; + /* + Create the whole area of this and the neighboring blocks + */ - s32 lowest_ground_y = 32767; - s32 highest_ground_y = -32768; + core::list blocks_created; - enum{ - BT_GROUND, - BT_SURFACE, - BT_SKY - } block_type = BT_SURFACE; + { + //TimeTaker timer("generateBlockRaw() create area"); + + for(s16 x=-1; x<=1; x++) + for(s16 z=-1; z<=1; z++) + { + v2s16 sectorpos = sectorpos0 + v2s16(x,z); + ServerMapSector *sector = createSector(sectorpos); + assert(sector); - {// ground_timer (0ms or ~100ms) - TimeTaker ground_timer("Ground generation"); + for(s16 y=blockpos0.Y-1; y<=blockpos0.Y+1; y++) + { + v3s16 blockpos(sectorpos.X, y, sectorpos.Y); + + MapBlock *block = getBlockNoCreateNoEx(blockpos); + if(block && block->isDummy() == false) + continue; + + block = createBlock(blockpos); + block->setFullyGenerated(false); + + blocks_created.push_back(blockpos); + + // Lighting won't be calculated + block->setLightingExpired(true); + // Lighting will be calculated + //block->setLightingExpired(false); + + /* + Block gets sunlight if this is true. + + This should be set to true when the top side of a block + is completely exposed to the sky. + This doesn't matter if the initial lighting is done + here. + */ + //block->setIsUnderground(y != y_blocks_max); + block->setIsUnderground(false); + } + } + } + /* - Approximate whether this block is a surface block, an air - block or a ground block. + Now we have a big empty area of (16x16x16)x27. - This shall never mark a surface block as non-surface. + Make a ManualMapVoxelManipulator that contains the whole area. */ + ManualMapVoxelManipulator vmanip(this); + // Add the area we just generated + { + //TimeTaker timer("generateBlockRaw() initialEmerge"); + vmanip.initialEmerge(blockpos0-v3s16(1,1,1), blockpos0+v3s16(1,1,1)); + } + + // Clear all flags + vmanip.clearFlag(0xff); + + // Block type of blockpos0 + BlockType center_block_type = BT_SURFACE; + + /* + Generate general ground level to newly created blocks. + Only stone is used and it is converted to other stuff later on. + */ + { + // 22ms @cs=8 + //dstream<<"Generating base ground..."<::Iterator i = blocks_created.begin(); + i != blocks_created.end(); i++) { + v3s16 blockpos = *i; + v2s16 sectorpos(blockpos.X, blockpos.Z); + /* - Estimate surface at different positions of the block, to - try to accomodate the effect of turbulence. + Approximate whether this block is a surface block, an air + block or a ground block. + + This shall never mark a surface block as non-surface. */ - v3f checklist[] = { - v3f(0,0,0), - v3f(0,1,0), - v3f(0,1,1), - v3f(0,0,1), - v3f(1,0,0), - v3f(1,1,0), - v3f(1,1,1), - v3f(1,0,1), - v3f(0.5,0.5,0.5), - }; - v3f p_nodes_f = intToFloat(p_nodes, 1); - float surface_y_max = -1000000; - float surface_y_min = 1000000; - for(u32 i=0; i surface_y_max) - surface_y_max = surface_y_f; - if(surface_y_f < surface_y_min) - surface_y_min = surface_y_f; - } + if(surface_y_f > surface_y_max) + surface_y_max = surface_y_f; + if(surface_y_f < surface_y_min) + surface_y_min = surface_y_f; + } - float block_low_y_f = p_nodes_f.Y; - float block_high_y_f = p_nodes_f.Y + MAP_BLOCKSIZE; + float block_low_y_f = p_nodes_f.Y; + float block_high_y_f = p_nodes_f.Y + MAP_BLOCKSIZE; - /*dstream<<"surface_y_max="<= surface_y_max + d_up - && block_low_y_f > WATER_LEVEL + d_up) - { - //dstream<<"BT_SKY"<= surface_y_max + d_up + && block_low_y_f > WATER_LEVEL + d_up) + { + //dstream<<"BT_SKY"<setIsUnderground(true); } - } - - if(block_type == BT_SURFACE || block_type == BT_GROUND) - { + /* - Generate ground precisely + If the block has ground, generate ground precisely. */ - for(s16 z0=0; z0=y_nodes_min; y--) + { + MapNode *n = &vmanip.m_data[i]; + if(light_propagates_content(n->d) == false) + { + light = 0; + } + else if(light != LIGHT_SUN + || sunlight_propagates_content(n->d) == false) + { + if(light > 0) + light--; + } + + n->setLight(LIGHTBANK_DAY, light); + n->setLight(LIGHTBANK_NIGHT, 0); + + if(light != 0) + { + // Insert light source + light_sources.insert(v3s16(p2d.X, y, p2d.Y), true); + } + + // Increment index by y + vmanip.m_area.add_y(em, i, -1); + } + } + } + + /* + This has to be 1 smaller than the actual area, because + neighboring nodes are checked. + */ + for(s16 x=lighting_min_d+1; + x<=lighting_max_d-1; + x++) + for(s16 z=lighting_min_d+1; + z<=lighting_max_d-1; + z++) + { + // Node position in 2d + v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z); + + /* + Apply initial sunlight + */ + { + u8 light = LIGHT_SUN; + bool add_to_sources = false; + v3s16 em = vmanip.m_area.getExtent(); + s16 y_start = y_nodes_max; + u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y)); + for(s16 y=y_start; y>=y_nodes_min; y--) + { + MapNode *n = &vmanip.m_data[i]; + + if(light_propagates_content(n->d) == false) + { + light = 0; + } + else if(light != LIGHT_SUN + || sunlight_propagates_content(n->d) == false) + { + if(light > 0) + light--; + } + + // This doesn't take much time + if(add_to_sources == false) + { + /* + Check sides. If side is not air or water, start + adding to light_sources. + */ + v3s16 dirs4[4] = { + v3s16(0,0,1), // back + v3s16(1,0,0), // right + v3s16(0,0,-1), // front + v3s16(-1,0,0), // left + }; + for(u32 di=0; di<4; di++) + { + v3s16 dirp = dirs4[di]; + u32 i2 = i; + vmanip.m_area.add_p(em, i2, dirp); + MapNode *n2 = &vmanip.m_data[i2]; + if( + n2->d != CONTENT_AIR + && n2->d != CONTENT_WATERSOURCE + && n2->d != CONTENT_WATER + ){ + add_to_sources = true; + break; + } + } + } + + n->setLight(LIGHTBANK_DAY, light); + n->setLight(LIGHTBANK_NIGHT, 0); + + // This doesn't take much time + if(light != 0 && add_to_sources) + { + // Insert light source + light_sources.insert(v3s16(p2d.X, y, p2d.Y), true); + } + + // Increment index by y + vmanip.m_area.add_y(em, i, -1); + } + } + } + + }//timer1 + + // Spread light around + { + TimeTaker timer("generateBlockRaw() spreadLight"); + vmanip.spreadLight(LIGHTBANK_DAY, light_sources); + } +#endif + + /* + Blit generated stuff to map + */ + { + vmanip.blitBackAll(&changed_blocks); + } + +#if 0 + /* + Update day/night difference cache of the MapBlocks + */ + { + for(core::map::Iterator i = changed_blocks.getIterator(); + i.atEnd() == false; i++) + { + MapBlock *block = i.getNode()->getValue(); + block->updateDayNightDiff(); + } + } +#endif + + /*for(core::map::Iterator + i = changed_blocks*/ + + // Done! + MapBlock *block = getBlockNoCreate(blockpos0); + block->setFullyGenerated(true); + + /* + TODO: Calculate lighting with the VoxelManipulator, not this way + */ + // emergeBlock reads this + block->setLightingExpired(true); + // Also set the above one, trees often will be there + { + MapBlock *block = getBlockNoCreate(blockpos0+v3s16(0,1,0)); + if(block) + block->setLightingExpired(true); + } + + return block; +} + + +/*MapBlock* ServerMap::generateBlock(v3s16 blockpos1, + core::map &changed_blocks)*/ +MapBlock * ServerMap::generateBlock( + v3s16 blockpos1, + MapBlock *original_dummy, + ServerMapSector *sector, + core::map &changed_blocks, + core::map &lighting_invalidated_blocks +) +{ + dstream<<"generateBlock(): Generating block " + <<"("<isFullyGenerated()) + continue; + generateBlockRaw(blockpos0, changed_blocks); + } + + assert(blockNonVolatile(blockpos1)); + + MapBlock *block = getBlockNoCreate(blockpos1); + + return block; +} + +#if 0 +/* + NOTE: This is not used for main map generation, only for blocks + that are very high or low. + NOTE: Now it is used mainly. Might change in the future. +*/ +MapBlock * ServerMap::generateBlock( + v3s16 p, + MapBlock *original_dummy, + ServerMapSector *sector, + core::map &changed_blocks, + core::map &lighting_invalidated_blocks +) +{ + DSTACK("%s: p=(%d,%d,%d)", + __FUNCTION_NAME, + p.X, p.Y, p.Z); + + /*dstream<<"generateBlock(): " + <<"("<createBlankBlockNoInsert(block_y); + } + else + { + // Remove the block so that nobody can get a half-generated one. + sector->removeBlock(block); + // Allocate the block to contain the generated data + block->unDummify(); + } + + u8 water_material = CONTENT_WATERSOURCE; + + s32 lowest_ground_y = 32767; + s32 highest_ground_y = -32768; + + enum{ + BT_GROUND, + BT_SURFACE, + BT_SKY + } block_type = BT_SURFACE; + + {// ground_timer (0ms or ~100ms) + TimeTaker ground_timer("Ground generation"); + + /* + Approximate whether this block is a surface block, an air + block or a ground block. + + This shall never mark a surface block as non-surface. + */ + + { + /* + Estimate surface at different positions of the block, to + try to accomodate the effect of turbulence. + */ + v3f checklist[] = { + v3f(0,0,0), + v3f(0,1,0), + v3f(0,1,1), + v3f(0,0,1), + v3f(1,0,0), + v3f(1,1,0), + v3f(1,1,1), + v3f(1,0,1), + v3f(0.5,0.5,0.5), + }; + v3f p_nodes_f = intToFloat(p_nodes, 1); + float surface_y_max = -1000000; + float surface_y_min = 1000000; + for(u32 i=0; i surface_y_max) + surface_y_max = surface_y_f; + if(surface_y_f < surface_y_min) + surface_y_min = surface_y_f; + } + + float block_low_y_f = p_nodes_f.Y; + float block_high_y_f = p_nodes_f.Y + MAP_BLOCKSIZE; + + /*dstream<<"surface_y_max="<= surface_y_max + d_up + && block_low_y_f > WATER_LEVEL + d_up) + { + //dstream<<"BT_SKY"<insertBlock(block); + if(block == NULL) + { + // Create dummy block + block = new MapBlock(this, p, true); + + // Add block to sector + sector->insertBlock(block); + } + // Done. + return block; } - // Done. - return block; } //dstream<<"Not found on disk, generating."< list = fs::GetDirListing(m_savedir+"/sectors/"); @@ -6363,7 +7307,8 @@ void ServerMap::saveMapMeta() DSTACK(__FUNCTION_NAME); dstream<<"INFO: ServerMap::saveMapMeta(): " - <<"seed="<