From e92238edc831a34081790e71249f1459e997974c Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Tue, 1 Feb 2011 16:17:55 +0200 Subject: This map generator is starting to look pretty good now... also, disabled loading player position from disk because map is regenerated always. --- src/map.cpp | 734 ++++++++++++++++++++++++++---------------------------------- 1 file changed, 313 insertions(+), 421 deletions(-) (limited to 'src/map.cpp') diff --git a/src/map.cpp b/src/map.cpp index 63fccc432..884482763 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -1735,8 +1735,8 @@ ServerMap::ServerMap(std::string savedir, HMParams hmp, MapParams mp): //m_chunksize = 64; //m_chunksize = 16; - //m_chunksize = 8; - m_chunksize = 4; + m_chunksize = 8; + //m_chunksize = 4; //m_chunksize = 2; /* @@ -1749,8 +1749,8 @@ ServerMap::ServerMap(std::string savedir, HMParams hmp, MapParams mp): PointAttributeList *list_baseheight = m_padb.getList("hm_baseheight"); PointAttributeList *list_randmax = m_padb.getList("hm_randmax"); PointAttributeList *list_randfactor = m_padb.getList("hm_randfactor"); - PointAttributeList *list_plants_amount = m_padb.getList("plants_amount"); - PointAttributeList *list_caves_amount = m_padb.getList("caves_amount"); + //PointAttributeList *list_plants_amount = m_padb.getList("plants_amount"); + //PointAttributeList *list_caves_amount = m_padb.getList("caves_amount"); #if 0 /* @@ -2026,119 +2026,6 @@ ServerMap::~ServerMap() } } -#if 0 -MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos) -{ - // Return if chunk already exists - MapChunk *chunk = getChunk(chunkpos); - if(chunk) - return chunk; - - /* - Add all sectors - */ - - dstream<<"generateChunkRaw(): " - <<"("< changed_blocks; - core::map lighting_invalidated_blocks; - - u32 generated_block_count = 0; - - for(s16 y=0; ygetBlockNoCreateNoEx(y2)) - continue; - - generateBlock(p, NULL, sector, changed_blocks, - lighting_invalidated_blocks); - - generated_block_count++; - } - } - } - - dstream<<"generateChunkRaw generated "< lighting_modified_blocks; - updateLighting(lighting_invalidated_blocks, lighting_modified_blocks); - }*/ - - // Add chunk meta information - chunk = new MapChunk(); - m_chunks.insert(chunkpos, chunk); - return chunk; -} - -MapChunk* ServerMap::generateChunk(v2s16 chunkpos) -{ - /* - Generate chunk and neighbors - */ - for(s16 x=-1; x<=1; x++) - for(s16 y=-1; y<=1; y++) - { - generateChunkRaw(chunkpos + v2s16(x,y)); - } - - /* - Get chunk - */ - MapChunk *chunk = getChunk(chunkpos); - assert(chunk); - // Set non-volatile - chunk->setIsVolatile(false); - // Return it - return chunk; -} -#endif - -MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos) -{ - dstream<<"WARNING: No-op "<<__FUNCTION_NAME<<" called"< &changed_blocks) { - TimeTaker timer("generateChunk()"); + /* + Don't generate if already fully generated + */ + { + MapChunk *chunk = getChunk(chunkpos); + if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY) + { + dstream<<"generateChunkRaw(): Chunk " + <<"("<setLightingExpired(false); /* - TODO: Do this better. 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. + + Actually this doesn't matter now because the + initial lighting is done here. */ block->setIsUnderground(y != y_blocks_max); } @@ -2314,15 +2236,19 @@ MapChunk* ServerMap::generateChunk(v2s16 chunkpos) ManualMapVoxelManipulator vmanip(this); // Add the area we just generated { - TimeTaker timer("generateChunk() initialEmerge"); + TimeTaker timer("generateChunkRaw() initialEmerge"); vmanip.initialEmerge(bigarea_blocks_min, bigarea_blocks_max); } - TimeTaker timer_generate("generateChunk() generate"); + TimeTaker timer_generate("generateChunkRaw() generate"); /* Generate general ground level to full area */ + + { + // 22ms @cs=8 + //TimeTaker timer1("ground level"); for(s16 x=0; x stone_surface_max_y) + stone_surface_max_y = y; + count++; if(count >= ob_size.Y) break; @@ -2468,12 +2412,29 @@ MapChunk* ServerMap::generateChunk(v2s16 chunkpos) } } + }//timer1 + { + // 24ms @cs=8 + //TimeTaker timer1("dungeons"); + /* Make dungeons */ - for(u32 jj=0; jj<2; jj++) + u32 dungeons_count = relative_volume/200000; + for(u32 jj=0; jj WATER_LEVEL) - continue; + /* + If ground level is over water level, skip. + NOTE: This leaves caves near water without water, + which looks especially crappy when the nearby water + won't start flowing either for some reason + */ + /*if(surface_y > WATER_LEVEL) + continue;*/ /* Add water on ground @@ -2861,24 +2871,44 @@ MapChunk* ServerMap::generateChunk(v2s16 chunkpos) s16 y_start = WATER_LEVEL; u8 light = LIGHT_MAX; u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y)); + MapNode *n = &vmanip.m_data[i]; + /* + Add first one to transforming liquid queue + */ + if(n->d == CONTENT_WATER || n->d == CONTENT_WATERSOURCE) + { + v3s16 p = v3s16(p2d.X, y_start, p2d.Y); + m_transforming_liquid.push_back(p); + } for(s16 y=y_start; y>=y_nodes_min; y--) { - MapNode *n = &vmanip.m_data[i]; + n = &vmanip.m_data[i]; - // Fill gaps inside water, too + // Stop when there is no water and no air if(n->d != CONTENT_AIR && n->d != CONTENT_WATERSOURCE && n->d != CONTENT_WATER) + { + /* + Add bottom one to transforming liquid queue + */ + vmanip.m_area.add_y(em, i, 1); + n = &vmanip.m_data[i]; + if(n->d == CONTENT_WATER || n->d == CONTENT_WATERSOURCE) + { + v3s16 p = v3s16(p2d.X, y, p2d.Y); + m_transforming_liquid.push_back(p); + } + break; + } n->d = CONTENT_WATERSOURCE; n->setLight(LIGHTBANK_DAY, light); - /* - Add to transforming liquid queue (in case it'd - start flowing) - */ + /*// Add to transforming liquid queue (in case it'd + // start flowing) v3s16 p = v3s16(p2d.X, y, p2d.Y); - m_transforming_liquid.push_back(p); + m_transforming_liquid.push_back(p);*/ // Next one vmanip.m_area.add_y(em, i, -1); @@ -2889,11 +2919,16 @@ MapChunk* ServerMap::generateChunk(v2s16 chunkpos) } + }//timer1 + { + // 1ms @cs=8 + //TimeTaker timer1("plant trees"); + /* Plant some trees */ { - u32 tree_max = 100; + u32 tree_max = relative_area / 100; u32 count = myrand_range(0, tree_max); for(u32 i=0; id = CONTENT_GRASS; } + }//timer1 + /* Handle lighting */ core::map light_sources; + { + // 750ms @cs=8, can't optimize more + //TimeTaker timer1("initial lighting"); + /*for(s16 x=0; x=y_nodes_min; y--) { @@ -2994,11 +3047,42 @@ MapChunk* ServerMap::generateChunk(v2s16 chunkpos) 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); - if(light != 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); @@ -3010,9 +3094,11 @@ MapChunk* ServerMap::generateChunk(v2s16 chunkpos) } } + }//timer1 + // Spread light around { - TimeTaker timer("generateChunk() spreadLight"); + TimeTaker timer("generateChunkRaw() spreadLight"); vmanip.spreadLight(LIGHTBANK_DAY, light_sources); } @@ -3025,16 +3111,16 @@ MapChunk* ServerMap::generateChunk(v2s16 chunkpos) /* Blit generated stuff to map */ - core::map modified_blocks; { - TimeTaker timer("generateChunk() blitBackAll"); - vmanip.blitBackAll(&modified_blocks); + // 70ms @cs=8 + //TimeTaker timer("generateChunkRaw() blitBackAll"); + vmanip.blitBackAll(&changed_blocks); } /* Update day/night difference cache of the MapBlocks */ { - for(core::map::Iterator i = modified_blocks.getIterator(); + for(core::map::Iterator i = changed_blocks.getIterator(); i.atEnd() == false; i++) { MapBlock *block = i.getNode()->getValue(); @@ -3044,7 +3130,7 @@ MapChunk* ServerMap::generateChunk(v2s16 chunkpos) /* - Create chunks and set them volatile + Create chunk metadata */ for(s16 x=-1; x<=1; x++) @@ -3052,13 +3138,15 @@ MapChunk* ServerMap::generateChunk(v2s16 chunkpos) { v2s16 chunkpos0 = chunkpos + v2s16(x,y); // Add chunk meta information - MapChunk *chunk = getChunk(chunkpos); + MapChunk *chunk = getChunk(chunkpos0); if(chunk == NULL) { chunk = new MapChunk(); m_chunks.insert(chunkpos0, chunk); } - chunk->setIsVolatile(true); + //chunk->setIsVolatile(true); + if(chunk->getGenLevel() > GENERATED_PARTLY) + chunk->setGenLevel(GENERATED_PARTLY); } /* @@ -3067,243 +3155,37 @@ MapChunk* ServerMap::generateChunk(v2s16 chunkpos) MapChunk *chunk = getChunk(chunkpos); assert(chunk); // Set non-volatile - chunk->setIsVolatile(false); + //chunk->setIsVolatile(false); + chunk->setGenLevel(GENERATED_FULLY); // Return it return chunk; } -#if 0 -ServerMapSector * ServerMap::generateSector(v2s16 p2d) +MapChunk* ServerMap::generateChunk(v2s16 chunkpos1, + core::map &changed_blocks) { - DSTACK("%s: p2d=(%d,%d)", - __FUNCTION_NAME, - p2d.X, p2d.Y); - - // Check that it doesn't exist already - ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d); - if(sector != NULL) - return sector; - - /* - If there is no master heightmap, throw. - */ - if(m_heightmap == NULL) - { - throw InvalidPositionException("generateSector(): no heightmap"); - } - - /* - Do not generate over-limit - */ - if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE - || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE - || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE - || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE) - throw InvalidPositionException("generateSector(): pos. over limit"); - - /* - Generate sector and heightmaps - */ - - // Number of heightmaps in sector in each direction - u16 hm_split = SECTOR_HEIGHTMAP_SPLIT; - - // Heightmap side width - s16 hm_d = MAP_BLOCKSIZE / hm_split; - - sector = new ServerMapSector(this, p2d, hm_split); - - // Sector position on map in nodes - v2s16 nodepos2d = p2d * MAP_BLOCKSIZE; - - /*dstream<<"Generating sector ("<getGroundHeight(mhm_p+v2s16(0,0)*hm_split), - m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)*hm_split), - m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)*hm_split), - m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)*hm_split), - }; - - float avgheight = (corners[0]+corners[1]+corners[2]+corners[3])/4.0; - float avgslope = 0.0; - avgslope += fabs(avgheight - corners[0]); - avgslope += fabs(avgheight - corners[1]); - avgslope += fabs(avgheight - corners[2]); - avgslope += fabs(avgheight - corners[3]); - avgslope /= 4.0; - avgslope /= MAP_BLOCKSIZE; - //dstream<<"avgslope="<getSlope(p2d+v2s16(0,0)); - pitness += -a.X; - pitness += -a.Y; - a = m_heightmap->getSlope(p2d+v2s16(0,1)); - pitness += -a.X; - pitness += a.Y; - a = m_heightmap->getSlope(p2d+v2s16(1,1)); - pitness += a.X; - pitness += a.Y; - a = m_heightmap->getSlope(p2d+v2s16(1,0)); - pitness += a.X; - pitness += -a.Y; - pitness /= 4.0; - pitness /= MAP_BLOCKSIZE; - //dstream<<"pitness="<getNearAttr(nodepos2d).getFloat();*/ - local_plants_amount = - palist->getInterpolatedFloat(nodepos2d); - } -#endif - - /* - Generate sector heightmap - */ - - // Loop through sub-heightmaps - for(s16 y=0; ygetGroundHeight(mhm_p+v2s16(0,0)), - m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)), - m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)), - m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)), - }; - - /*dstream<<"p_in_sector=("<setHeightmap(p_in_sector, hm); - - //TODO: Make these values configurable - //hm->generateContinued(0.0, 0.0, corners); - //hm->generateContinued(0.25, 0.2, corners); - //hm->generateContinued(0.5, 0.2, corners); - //hm->generateContinued(1.0, 0.2, corners); - //hm->generateContinued(2.0, 0.2, corners); - //hm->generateContinued(2.0 * avgslope, 0.5, corners); - hm->generateContinued(avgslope * MAP_BLOCKSIZE/8, 0.5, corners); - - //hm->print(); + v2s16 chunkpos0 = chunkpos1 + v2s16(x,y); + MapChunk *chunk = getChunk(chunkpos0); + // Skip if already generated + if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY) + continue; + generateChunkRaw(chunkpos0, changed_blocks); } - - /* - Generate objects - */ - core::map *objects = new core::map; - sector->setObjects(objects); + assert(chunkNonVolatile(chunkpos1)); - float area = MAP_BLOCKSIZE * MAP_BLOCKSIZE; - - /* - Plant some trees if there is not much slope - */ - { - // Avgslope is the derivative of a hill - //float t = avgslope * avgslope; - float t = avgslope; - float a = area/16 * m_params.plants_amount * local_plants_amount; - u32 tree_max; - //float something = 0.17*0.17; - float something = 0.3; - if(t > something) - tree_max = a / (t/something); - else - tree_max = a; - - u32 count = (myrand()%(tree_max+1)); - //u32 count = tree_max; - for(u32 i=0; igetGroundHeight(v2s16(x,z))+1; - if(y < WATER_LEVEL) - continue; - objects->insert(v3s16(x, y, z), - SECTOR_OBJECT_TREE_1); - } - } - /* - Plant some bushes if sector is pit-like - */ - { - // Pitness usually goes at around -0.5...0.5 - u32 bush_max = 0; - u32 a = area/16 * 3.0 * m_params.plants_amount * local_plants_amount; - if(pitness > 0) - bush_max = (pitness*a*4); - if(bush_max > a) - bush_max = a; - u32 count = (myrand()%(bush_max+1)); - for(u32 i=0; igetGroundHeight(v2s16(x,z))+1; - if(y < WATER_LEVEL) - continue; - objects->insert(v3s16(x, y, z), - SECTOR_OBJECT_BUSH_1); - } - } - /* - Add ravine (randomly) - */ - if(m_params.ravines_amount > 0.001) - { - if(myrand()%(s32)(200.0 / m_params.ravines_amount) == 0) - { - s16 s = 6; - s16 x = myrand()%(MAP_BLOCKSIZE-s*2-1)+s; - s16 z = myrand()%(MAP_BLOCKSIZE-s*2-1)+s; - /*s16 x = 8; - s16 z = 8;*/ - s16 y = sector->getGroundHeight(v2s16(x,z))+1; - objects->insert(v3s16(x, y, z), - SECTOR_OBJECT_RAVINE); - } - } - - /* - Insert to container - */ - m_sectors.insert(p2d, sector); - - return sector; + MapChunk *chunk = getChunk(chunkpos1); + return chunk; } -#endif ServerMapSector * ServerMap::createSector(v2s16 p2d) { @@ -3418,7 +3300,8 @@ ServerMapSector * ServerMap::createSector(v2s16 p2d) return sector; } -MapSector * ServerMap::emergeSector(v2s16 p2d) +MapSector * ServerMap::emergeSector(v2s16 p2d, + core::map &changed_blocks) { DSTACK("%s: p2d=(%d,%d)", __FUNCTION_NAME, @@ -3428,18 +3311,19 @@ MapSector * ServerMap::emergeSector(v2s16 p2d) Check chunk status */ v2s16 chunkpos = sector_to_chunk(p2d); - bool chunk_exists = false; + /*bool chunk_nonvolatile = false; MapChunk *chunk = getChunk(chunkpos); if(chunk && chunk->getIsVolatile() == false) - chunk_exists = true; + chunk_nonvolatile = true;*/ + bool chunk_nonvolatile = chunkNonVolatile(chunkpos); /* If chunk is not fully generated, generate chunk */ - if(chunk_exists == false) + if(chunk_nonvolatile == false) { // Generate chunk and neighbors - generateChunk(chunkpos); + generateChunk(chunkpos, changed_blocks); } /* @@ -3474,6 +3358,10 @@ MapSector * ServerMap::emergeSector(v2s16 p2d) //return generateSector(); } +/* + NOTE: This is not used for main map generation, only for blocks + that are very high or low +*/ MapBlock * ServerMap::generateBlock( v3s16 p, MapBlock *original_dummy, @@ -4418,14 +4306,9 @@ MapBlock * ServerMap::emergeBlock( */ ServerMapSector *sector; try{ - sector = (ServerMapSector*)emergeSector(p2d); + sector = (ServerMapSector*)emergeSector(p2d, changed_blocks); assert(sector->getId() == MAPSECTOR_SERVER); } - /*catch(InvalidPositionException &e) - { - dstream<<"emergeBlock: emergeSector() failed"<= 1) + { + dstream<<"initialEmerge: area: "; + block_area_nodes.print(dstream); + dstream<<" ("<