diff options
author | Perttu Ahola <celeron55@gmail.com> | 2011-04-02 20:55:22 +0300 |
---|---|---|
committer | Perttu Ahola <celeron55@gmail.com> | 2011-04-02 20:55:22 +0300 |
commit | bc66bb2d409f13554bdcec7386766af82a343cad (patch) | |
tree | 37376d220e421db8d567c2624fe977355fe6f832 | |
parent | 2990f5d90bd556684c07d60872a707e649bd71ee (diff) | |
download | minetest-bc66bb2d409f13554bdcec7386766af82a343cad.tar.gz minetest-bc66bb2d409f13554bdcec7386766af82a343cad.tar.bz2 minetest-bc66bb2d409f13554bdcec7386766af82a343cad.zip |
Mapgen is better now. Not a lot, but a bit!
-rw-r--r-- | data/water.png | bin | 548 -> 512 bytes | |||
-rw-r--r-- | src/main.cpp | 8 | ||||
-rw-r--r-- | src/map.cpp | 1050 | ||||
-rw-r--r-- | src/map.h | 106 | ||||
-rw-r--r-- | src/mapblock.cpp | 4 | ||||
-rw-r--r-- | src/mapblock.h | 29 | ||||
-rw-r--r-- | src/mapchunk.h | 66 | ||||
-rw-r--r-- | src/materials.cpp | 4 | ||||
-rw-r--r-- | src/server.cpp | 17 |
9 files changed, 1074 insertions, 210 deletions
diff --git a/data/water.png b/data/water.png Binary files differindex 20f74edfe..9657dbed7 100644 --- a/data/water.png +++ b/data/water.png diff --git a/src/main.cpp b/src/main.cpp index ec4060868..fb1b2d8fd 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -194,7 +194,7 @@ TODO: Copy the text of the last picked sign to inventory in creative TODO: Check what goes wrong with caching map to disk (Kray)
- Nothing?
-FIXME: Server went into some infinite PeerNotFoundException loop
+FIXME: Server sometimes goes into some infinite PeerNotFoundException loop
* Fix the problem with the server constantly saving one or a few
blocks? List the first saved block, maybe it explains.
@@ -259,7 +259,7 @@ FEATURE: Erosion simulation at map generation time - Simulate rock falling from cliffs when water has removed
enough solid rock from the bottom
-Mapgen v2:
+Mapgen v2 (not doing):
* only_from_disk might not work anymore - check and fix it.
* Make the generator to run in background and not blocking block
placement and transfer
@@ -280,7 +280,7 @@ Mapgen v4 (not doing): * Make chunks to be tiled vertically too
* MAKE IT FASTER
-Mapgen v3:
+Mapgen v3 (not doing):
* Generate trees better
- Add a "trees_added" flag to sector, or something
* How 'bout making turbulence controlled so that for a given 2d position
@@ -292,7 +292,7 @@ Mapgen v3: Mapgen v4:
* This will be the final way.
-* Generate blocks in the same way as chunks, by copying a voxelmanipulator
+* Generate blocks in the same way as chunks, by copying a VoxelManipulator
from the map that is one block larger in all directions.
Misc. stuff:
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<<DTIME<<"Server: Failed to save map to "<<m_savedir <<", exception: "<<e.what()<<std::endl; } - - /* - Free all MapChunks - */ - core::map<v2s16, MapChunk*>::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<v3s16, MapBlock*> &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, <<p2d.X<<","<<p2d.Y<<" and chunk is already generated. " <<std::endl; -#if 1 +#if 0 dstream<<"WARNING: Forcing regeneration of chunk."<<std::endl; // Generate chunk @@ -4954,6 +4939,956 @@ MapSector * ServerMap::emergeSector(v2s16 p2d, //return generateSector(); } +enum BlockType{ + BT_GROUND, + BT_SURFACE, + BT_SKY +}; + +MapBlock* ServerMap::generateBlockRaw(v3s16 blockpos0, + core::map<v3s16, MapBlock*> &changed_blocks, + bool force) +{ + DSTACK(__FUNCTION_NAME); + + /* + Don't generate if already fully generated + */ + if(force == false) + { + MapBlock *block = getBlockNoCreateNoEx(blockpos0); + if(block != NULL && block->isFullyGenerated()) + { + dstream<<"generateBlockRaw(): Block " + <<"("<<blockpos0.X<<","<<blockpos0.Y + <<","<<blockpos0.Z<<")" + <<" already generated (not forced)"<<std::endl; + return block; + } + } + + /*dstream<<"generateBlockRaw(): Generating block " + <<"("<<blockpos0.X<<","<<blockpos0.Y + <<","<<blockpos0.Z<<")" + <<std::endl;*/ + + //TimeTaker timer("generateBlockRaw()"); + + /* + Calculate some simple values + */ + + v2s16 sectorpos0(blockpos0.X, blockpos0.Z); + + /* + Fill in some variables for the code that was copied from + generateChunkRaw + */ + 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; + + /* + Create the whole area of this and the neighboring blocks + */ + + core::list<v3s16> blocks_created; + + { + //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); + + 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); + } + } + } + + /* + Now we have a big empty area of (16x16x16)x27. + + 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..."<<std::endl; + //TimeTaker timer1("ground level"); + + // Loop through created blocks + for(core::list<v3s16>::Iterator i = blocks_created.begin(); + i != blocks_created.end(); i++) + { + v3s16 blockpos = *i; + v2s16 sectorpos(blockpos.X, blockpos.Z); + + /* + 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. + */ + + BlockType block_type = BT_SURFACE; + v3s16 p_nodes = blockpos * MAP_BLOCKSIZE; + s32 lowest_ground_y = 32767; + s32 highest_ground_y = -32768; + u8 water_material = CONTENT_WATERSOURCE; + + { + /* + 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<sizeof(checklist)/sizeof(checklist[0]); i++) + { + v3f p_map_f = p_nodes_f + checklist[i]*MAP_BLOCKSIZE; + + double depth_guess; + /*bool is_ground =*/ is_base_ground(m_seed, p_map_f, &depth_guess); + + // Estimate the surface height + float surface_y_f = p_map_f.Y + depth_guess; + + 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; + + /*dstream<<"surface_y_max="<<surface_y_max + <<", surface_y_min="<<surface_y_min + <<", block_low_y_f="<<block_low_y_f + <<", block_high_y_f="<<block_high_y_f + <<std::endl;*/ + + // A fuzzyness value + // Must accomodate mud and turbulence holes + float d_down = 16; + // Must accomodate a bit less + float d_up = 5; + + if(block_high_y_f < surface_y_min - d_down) + { + //dstream<<"BT_GROUND"<<std::endl; + // A ground block + block_type = BT_GROUND; + } + else if(block_low_y_f >= surface_y_max + d_up + && block_low_y_f > WATER_LEVEL + d_up) + { + //dstream<<"BT_SKY"<<std::endl; + // A sky block + block_type = BT_SKY; + } + else + { + //dstream<<"BT_SURFACE"<<std::endl; + // A surface block + block_type = BT_SURFACE; + } + + if(/*block_type == BT_GROUND ||*/ block_type == BT_SKY) + { + lowest_ground_y = surface_y_min; + highest_ground_y = surface_y_max; + } + } + + if(blockpos == blockpos0) + center_block_type = block_type; + + if(block_type == BT_GROUND) + { + MapBlock *block = getBlockNoCreateNoEx(blockpos); + if(block) + block->setIsUnderground(true); + } + + /* + If the block has ground, generate ground precisely. + */ + + if(block_type == BT_SURFACE || block_type == BT_GROUND) + { + for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++) + for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++) + { + v2s16 real_p2d = v2s16(x0,z0) + sectorpos*MAP_BLOCKSIZE; + + v2f real_p2d_f(real_p2d.X,real_p2d.Y); + + double tfxz = get_turbulence_factor_2d(m_seed, real_p2d_f); + bool turbulence_is_used = (tfxz > 0.001); + + float surface_y_f = 0; + s16 surface_y = 0; + + float noturb_surface_y_f = base_rock_level_2d(m_seed, real_p2d_f); + s16 noturb_surface_y = noturb_surface_y_f; + + // Get some statistics of surface height + if(noturb_surface_y < lowest_ground_y) + lowest_ground_y = noturb_surface_y; + if(noturb_surface_y > highest_ground_y) + highest_ground_y = noturb_surface_y; + + // Use fast index incrementing + v3s16 em = vmanip.m_area.getExtent(); + u32 i = vmanip.m_area.index(v3s16( + real_p2d.X, + blockpos.Y*MAP_BLOCKSIZE, + real_p2d.Y)); + for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++) + { + s16 real_y = blockpos.Y * MAP_BLOCKSIZE + y0; + v3s16 real_pos = v3s16(x0,y0,z0) + p_nodes; + MapNode n; + + /* + Calculate material + */ + + bool is_ground = false; + v3f real_pos_f = intToFloat(real_pos, 1); + + bool turb_for_node = (turbulence_is_used + && real_y >= TURBULENCE_BOTTOM_CUTOFF_Y); + + bool is_cavern = false; + + if(is_carved(m_seed, real_pos_f)) + { + is_ground = false; + if(real_y < noturb_surface_y) + is_cavern = true; + } + else + { + if(turb_for_node) + { + double depth_guess; + is_ground = is_base_ground(m_seed, + real_pos_f, &depth_guess); + + // Estimate the surface height + surface_y_f = (float)real_y + depth_guess; + surface_y = real_y + depth_guess; + + // Save some statistics of surface height + if(surface_y < lowest_ground_y) + lowest_ground_y = surface_y; + if(surface_y > highest_ground_y) + highest_ground_y = surface_y; + } + else + { + surface_y = noturb_surface_y; + } + + is_ground = (real_y <= surface_y); + } + + // If node is not ground, it's air or water + if(is_ground == false) + { + // If under water level, it's water + if(real_y < WATER_LEVEL && !is_cavern) + { + n.d = water_material; + u8 dist = 16; + if(real_y >= surface_y) + dist = WATER_LEVEL-real_y+1; + n.setLight(LIGHTBANK_DAY, + diminish_light(LIGHT_SUN, dist)); + /* + Add to transforming liquid queue (in case it'd + start flowing) + */ + m_transforming_liquid.push_back(real_pos); + } + // else air + else + { + n.d = CONTENT_AIR; + + /* + Guess lighting + */ + if(real_y > surface_y + 4) + n.setLight(LIGHTBANK_DAY, LIGHT_SUN); + } + } + // Else it's ground + else + { + if(is_underground_mud(m_seed, real_pos_f)) + n.d = CONTENT_MUD; + else + n.d = CONTENT_STONE; + } + + vmanip.m_data[i] = n; + vmanip.m_area.add_y(em, i, 1); + } + } + }// BT_SURFACE + else // BT_SKY or anything else + { + MapNode n_fill; + if(block_type == BT_GROUND) + { + //n_fill.d = CONTENT_STONE; + } + else if(block_type == BT_SKY) + { + n_fill.d = CONTENT_AIR; + n_fill.setLight(LIGHTBANK_DAY, LIGHT_SUN); + } + else // fallback + { + n_fill.d = CONTENT_MESE; + } + + for(s16 x=0; x<MAP_BLOCKSIZE; x++) + for(s16 z=0; z<MAP_BLOCKSIZE; z++) + { + // Node position + v2s16 p2d = sectorpos*MAP_BLOCKSIZE + v2s16(x,z); + + { + // Use fast index incrementing + v3s16 em = vmanip.m_area.getExtent(); + s16 min = blockpos.Y*MAP_BLOCKSIZE; + s16 max = min + MAP_BLOCKSIZE-1; + u32 i = vmanip.m_area.index(v3s16(p2d.X, min, p2d.Y)); + for(s16 y=min; y<=max; y++) + { + vmanip.m_data[i] = n_fill; + vmanip.m_area.add_y(em, i, 1); + } + } + } + } + } + + }//timer1 + + /* + Convert surface ground to mud + */ + + if(center_block_type == BT_SURFACE) + { +#if 1 + for(s16 x=0; x<MAP_BLOCKSIZE; x++) + for(s16 z=0; z<MAP_BLOCKSIZE; z++) + { + // Node position + v2s16 p2d = sectorpos0*MAP_BLOCKSIZE + v2s16(x,z); + v2f real_p2d_f(p2d.X,p2d.Y); + + { + // Use fast index incrementing + v3s16 em = vmanip.m_area.getExtent(); + s16 min = blockpos0.Y*MAP_BLOCKSIZE; + // Start from one above the central block + s16 max = min + MAP_BLOCKSIZE-1+1; + u32 i = vmanip.m_area.index(v3s16(p2d.X, max, p2d.Y)); + // If stone, there won't be mud + if(vmanip.m_data[i].d == CONTENT_STONE) + continue; + // Find top of ground + bool found = false; + s16 y; + for(y=max; y>=min; y--) + { + if(vmanip.m_data[i].d == CONTENT_STONE) + { + found = true; + break; + } + vmanip.m_area.add_y(em, i, -1); + } + if(found == false) + continue; + // Set mud + s16 mud_amount = get_mud_amount(m_seed, real_p2d_f); + for(s16 j=0; j<mud_amount; j++) + { + if(vmanip.m_data[i].d != CONTENT_STONE) + { + break; + } + if(j==0 && y >= WATER_LEVEL) + vmanip.m_data[i].d = CONTENT_GRASS; + else + vmanip.m_data[i].d = CONTENT_MUD; + vmanip.m_area.add_y(em, i, -1); + } + } + } +#endif + } + + /* + Convert mud to sand + */ + if(center_block_type == BT_SURFACE) + { + for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++) + for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++) + { + // Node position in 2d + v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z); + + // Determine whether to have sand here + bool have_sand = get_have_sand_coast(m_seed, v2f(p2d.X,p2d.Y)); + + if(have_sand == false) + continue; + + // Find ground level + s16 surface_y = find_ground_level_clever(vmanip, p2d); + + if(surface_y >= WATER_LEVEL + 2) + continue; + + { + v3s16 em = vmanip.m_area.getExtent(); + s16 y_start = surface_y; + u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y)); + u32 not_sand_counter = 0; + for(s16 y=y_start; y>=y_nodes_min; y--) + { + MapNode *n = &vmanip.m_data[i]; + if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS) + { + n->d = CONTENT_SAND; + } + else + { + not_sand_counter++; + if(not_sand_counter > 3) + break; + } + + vmanip.m_area.add_y(em, i, -1); + } + } + } + } + + /* + Add some minerals + */ + + if(center_block_type == BT_SURFACE || center_block_type == BT_GROUND) + { + s16 underground_level = 1 - blockpos0.Y; + + /* + Add meseblocks + */ + for(s16 i=0; i<underground_level/4 + 1; i++) + { + if(myrand()%25 == 0) + { + v3s16 cp( + (myrand()%(MAP_BLOCKSIZE-2))+1, + (myrand()%(MAP_BLOCKSIZE-2))+1, + (myrand()%(MAP_BLOCKSIZE-2))+1 + ); + cp += blockpos0*MAP_BLOCKSIZE; + + MapNode n; + n.d = CONTENT_MESE; + + for(u16 i=0; i<27; i++) + { + if(vmanip.getNode(cp+g_27dirs[i]).d == CONTENT_STONE) + if(myrand()%8 == 0) + vmanip.setNode(cp+g_27dirs[i], n); + } + } + } + + /* + Add coal + */ + u16 coal_amount = 60; + u16 coal_rareness = 120 / coal_amount; + if(coal_rareness == 0) + coal_rareness = 1; + if(myrand()%coal_rareness == 0) + { + u16 a = myrand() % 16; + u16 amount = coal_amount * a*a*a / 1000; + for(s16 i=0; i<amount; i++) + { + v3s16 cp( + (myrand()%(MAP_BLOCKSIZE-2))+1, + (myrand()%(MAP_BLOCKSIZE-2))+1, + (myrand()%(MAP_BLOCKSIZE-2))+1 + ); + cp += blockpos0*MAP_BLOCKSIZE; + + MapNode n; + n.d = CONTENT_STONE; + n.param = MINERAL_COAL; + + for(u16 i=0; i<27; i++) + { + if(vmanip.getNode(cp+g_27dirs[i]).d == CONTENT_STONE) + if(myrand()%8 == 0) + vmanip.setNode(cp+g_27dirs[i], n); + } + } + } + + /* + Add iron + */ + u16 iron_amount = 40; + u16 iron_rareness = 80 / iron_amount; + if(iron_rareness == 0) + iron_rareness = 1; + if(myrand()%iron_rareness == 0) + { + u16 a = myrand() % 16; + u16 amount = iron_amount * a*a*a / 1000; + for(s16 i=0; i<amount; i++) + { + v3s16 cp( + (myrand()%(MAP_BLOCKSIZE-2))+1, + (myrand()%(MAP_BLOCKSIZE-2))+1, + (myrand()%(MAP_BLOCKSIZE-2))+1 + ); + cp += blockpos0*MAP_BLOCKSIZE; + + MapNode n; + n.d = CONTENT_STONE; + n.param = MINERAL_IRON; + + for(u16 i=0; i<27; i++) + { + if(vmanip.getNode(cp+g_27dirs[i]).d == CONTENT_STONE) + if(myrand()%8 == 0) + vmanip.setNode(cp+g_27dirs[i], n); + } + } + } + } + + + /* + Generate some trees + */ + if(center_block_type == BT_SURFACE) + { + // Divide area into this amount of parts + s16 div = 1; + s16 sidelen = sectorpos_base_size*MAP_BLOCKSIZE / div; + double area = sidelen * sidelen; + for(s16 x0=0; x0<div; x0++) + for(s16 z0=0; z0<div; z0++) + { + // Center position of part of division + v2s16 p2d_center( + sectorpos_base.X*MAP_BLOCKSIZE + sidelen/2 + sidelen*x0, + sectorpos_base.Y*MAP_BLOCKSIZE + sidelen/2 + sidelen*z0 + ); + // Minimum edge of part of division + v2s16 p2d_min( + sectorpos_base.X*MAP_BLOCKSIZE + sidelen*x0, + sectorpos_base.Y*MAP_BLOCKSIZE + sidelen*z0 + ); + // Maximum edge of part of division + v2s16 p2d_max( + sectorpos_base.X*MAP_BLOCKSIZE + sidelen + sidelen*x0 - 1, + sectorpos_base.Y*MAP_BLOCKSIZE + sidelen + sidelen*z0 - 1 + ); + // Amount of trees + u32 tree_count = area * tree_amount_2d(m_seed, p2d_center); + // Put trees in random places on part of division + for(u32 i=0; i<tree_count; i++) + { + s16 x = myrand_range(p2d_min.X, p2d_max.X); + s16 z = myrand_range(p2d_min.Y, p2d_max.Y); + s16 y = find_ground_level(vmanip, v2s16(x,z)); + // Don't make a tree under water level + if(y < WATER_LEVEL) + continue; + // Don't make a tree in other blocks + if(y < blockpos0.Y*MAP_BLOCKSIZE + || y >= (blockpos0.Y+1)*MAP_BLOCKSIZE) + continue; + v3s16 p(x,y,z); + /* + Trees grow only on mud and grass + */ + { + u32 i = vmanip.m_area.index(v3s16(p)); + MapNode *n = &vmanip.m_data[i]; + if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS) + continue; + } + p.Y++; + // Make a tree + make_tree(vmanip, p); + } + } + } + +#if 0 + /* + Initial lighting (sunlight) + TODO: Do the lighting this way, with the VoxelManipulator + */ + + core::map<v3s16, bool> light_sources; + + { + // 750ms @cs=8, can't optimize more + TimeTaker timer1("initial lighting"); + + /* + Go through the edges and apply sunlight to them, not caring + about neighbors + */ + + // Four edges + for(s16 i=0; i<4; i++) + // Edge length + for(s16 j=lighting_min_d; + j<=lighting_max_d; + j++) + { + s16 x; + s16 z; + // +-X + if(i == 0 || i == 1) + { + x = (i==0) ? lighting_min_d : lighting_max_d; + if(i == 0) + z = lighting_min_d; + else + z = lighting_max_d; + } + // +-Z + else + { + z = (i==0) ? lighting_min_d : lighting_max_d; + if(i == 0) + x = lighting_min_d; + else + x = lighting_max_d; + } + + // Node position in 2d + v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z); + + // Loop from top to down + { + u8 light = LIGHT_SUN; + 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--; + } + + 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<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator(); + i.atEnd() == false; i++) + { + MapBlock *block = i.getNode()->getValue(); + block->updateDayNightDiff(); + } + } +#endif + + /*for(core::map<v3s16, MapBlock*>::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<v3s16, MapBlock*> &changed_blocks)*/ +MapBlock * ServerMap::generateBlock( + v3s16 blockpos1, + MapBlock *original_dummy, + ServerMapSector *sector, + core::map<v3s16, MapBlock*> &changed_blocks, + core::map<v3s16, MapBlock*> &lighting_invalidated_blocks +) +{ + dstream<<"generateBlock(): Generating block " + <<"("<<blockpos1.X<<","<<blockpos1.Y<<","<<blockpos1.Z<<")" + <<std::endl; + + /* + The block at blockpos1 should be generated fully, so that it won't + change in the future anymore when more stuff is generated. + + Now, a block can be generated with generateBlockRaw(). + generateBlockRaw() accesses the block and all its neighbors. + Here is what generateBlockRaw() does: + - If the asked block has been marked fully generated, do nothing. + - Create the asked block and its neighbors and generate the base + ground in them (if they don't exist) + - In places where the ground level is in the asked block, convert + top layer of ground to mud or sand. Conversion is extended to + the block below as needed. + - Add trees and other objects to the asked block. Parts of them + can be located in the neighboring blocks. + - Mark the asked block as fully generated. + + This means the block and all its neighbors have to be generated to + obtain a block that won't change in the future. + */ + for(s16 x=-1; x<=1; x++) + for(s16 y=-1; y<=1; y++) + for(s16 z=-1; z<=1; z++) + { + v3s16 blockpos0 = blockpos1 + v3s16(x,y,z); + MapBlock *block = getBlockNoCreateNoEx(blockpos0); + // Skip if already generated + if(block != NULL && 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. @@ -5887,6 +6822,7 @@ continue_generating: return block; } +#endif MapBlock * ServerMap::createBlock(v3s16 p) { @@ -6019,6 +6955,7 @@ MapBlock * ServerMap::emergeBlock( bool does_not_exist = false; bool lighting_expired = false; + bool half_generated = false; MapBlock *block = sector->getBlockNoCreateNoEx(block_y); if(block == NULL) @@ -6033,6 +6970,10 @@ MapBlock * ServerMap::emergeBlock( { lighting_expired = true; } + else if(block->isFullyGenerated() == false) + { + half_generated = true; + } else { // Valid block @@ -6040,24 +6981,27 @@ MapBlock * ServerMap::emergeBlock( return block; } - /* - If block was not found on disk and not going to generate a - new one, make sure there is a dummy block in place. - */ - if(only_from_disk && (does_not_exist || lighting_expired)) + if(half_generated == false) { - //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl; - - if(block == NULL) + /* + If block was not found on disk and not going to generate a + new one, make sure there is a dummy block in place. + */ + if(only_from_disk && (does_not_exist || lighting_expired)) { - // Create dummy block - block = new MapBlock(this, p, true); + //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl; - // Add block to sector - sector->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."<<std::endl; @@ -6069,7 +7013,7 @@ MapBlock * ServerMap::emergeBlock( /* If the block doesn't exist, generate the block. */ - if(does_not_exist) + if(does_not_exist || half_generated) { block = generateBlock(p, block, sector, changed_blocks, lighting_invalidated_blocks); @@ -6212,7 +7156,7 @@ void ServerMap::save(bool only_changed) <<std::endl; saveMapMeta(); - saveChunkMeta(); + //saveChunkMeta(); u32 sector_meta_count = 0; u32 block_count = 0; @@ -6272,7 +7216,7 @@ void ServerMap::loadAll() dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl; loadMapMeta(); - loadChunkMeta(); + //loadChunkMeta(); std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/"); @@ -6363,7 +7307,8 @@ void ServerMap::saveMapMeta() DSTACK(__FUNCTION_NAME); dstream<<"INFO: ServerMap::saveMapMeta(): " - <<"seed="<<m_seed<<", chunksize="<<m_chunksize + <<"seed="<<m_seed + /*<<", chunksize="<<m_chunksize*/ <<std::endl; createDir(m_savedir); @@ -6379,7 +7324,7 @@ void ServerMap::saveMapMeta() Settings params; params.setU64("seed", m_seed); - params.setS32("chunksize", m_chunksize); + //params.setS32("chunksize", m_chunksize); params.writeLines(os); @@ -6419,13 +7364,15 @@ void ServerMap::loadMapMeta() } m_seed = params.getU64("seed"); - m_chunksize = params.getS32("chunksize"); + //m_chunksize = params.getS32("chunksize"); dstream<<"INFO: ServerMap::loadMapMeta(): " - <<"seed="<<m_seed<<", chunksize="<<m_chunksize + <<"seed="<<m_seed + /*<<", chunksize="<<m_chunksize*/ <<std::endl; } +#if 0 void ServerMap::saveChunkMeta() { DSTACK(__FUNCTION_NAME); @@ -6513,6 +7460,7 @@ void ServerMap::loadChunkMeta() m_chunks.insert(p, chunk); } } +#endif void ServerMap::saveSectorMeta(ServerMapSector *sector) { @@ -38,7 +38,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "mapsector.h" #include "constants.h" #include "voxel.h" -#include "mapchunk.h" /* Some exposed functions @@ -336,81 +335,29 @@ public: Map generation */ - // Returns the position of the chunk where the sector is in - v2s16 sector_to_chunk(v2s16 sectorpos) - { - sectorpos.X += m_chunksize / 2; - sectorpos.Y += m_chunksize / 2; - v2s16 chunkpos = getContainerPos(sectorpos, m_chunksize); - return chunkpos; - } - - // Returns the position of the (0,0) sector of the chunk - v2s16 chunk_to_sector(v2s16 chunkpos) - { - v2s16 sectorpos( - chunkpos.X * m_chunksize, - chunkpos.Y * m_chunksize - ); - sectorpos.X -= m_chunksize / 2; - sectorpos.Y -= m_chunksize / 2; - return sectorpos; - } - - /* - Get a chunk. - */ - MapChunk *getChunk(v2s16 chunkpos) - { - core::map<v2s16, MapChunk*>::Node *n; - n = m_chunks.find(chunkpos); - if(n == NULL) - return NULL; - return n->getValue(); - } - /* - True if the chunk and its neighbors are fully generated. - It means the chunk will not be touched in the future by the - generator. If false, generateChunk will make it true. + True if the block and its neighbors are fully generated. + It means the block will not be touched in the future by the + generator. If false, generateBlock will make it true. */ - bool chunkNonVolatile(v2s16 chunkpos) + bool blockNonVolatile(v3s16 blockpos) { - /*for(s16 x=-1; x<=1; x++) - for(s16 y=-1; y<=1; y++)*/ - s16 x=0; - s16 y=0; + for(s16 x=-1; x<=1; x++) + for(s16 y=-1; y<=1; y++) + for(s16 z=-1; z<=1; z++) { - v2s16 chunkpos0 = chunkpos + v2s16(x,y); - MapChunk *chunk = getChunk(chunkpos); - if(chunk == NULL) + v3s16 blockpos0 = blockpos + v3s16(x,y,z); + MapBlock *block = getBlockNoCreateNoEx(blockpos); + if(block == NULL) return false; - if(chunk->getGenLevel() != GENERATED_FULLY) + if(block->isFullyGenerated() == false) return false; } return true; } /* - Generate a chunk. - - All chunks touching this one can be altered also. - */ - MapChunk* generateChunkRaw(v2s16 chunkpos, - core::map<v3s16, MapBlock*> &changed_blocks, - bool force=false); - - /* - Generate a chunk and its neighbors so that it won't be touched - anymore. - */ - MapChunk* generateChunk(v2s16 chunkpos, - core::map<v3s16, MapBlock*> &changed_blocks); - - /* Generate a sector. - - This is mainly called by generateChunkRaw. */ //ServerMapSector * generateSector(v2s16 p); @@ -437,6 +384,27 @@ public: return emergeSector(p, changed_blocks); } + /*MapBlock * generateBlock( + v3s16 p, + MapBlock *original_dummy, + ServerMapSector *sector, + core::map<v3s16, MapBlock*> &changed_blocks, + core::map<v3s16, MapBlock*> &lighting_invalidated_blocks + );*/ + + /* + Generate a block. + + All blocks touching this one can be altered also. + */ + MapBlock* generateBlockRaw(v3s16 blockpos, + core::map<v3s16, MapBlock*> &changed_blocks, + bool force=false); + + /* + Generate a block and its neighbors so that it won't be touched + anymore. + */ MapBlock * generateBlock( v3s16 p, MapBlock *original_dummy, @@ -444,6 +412,8 @@ public: core::map<v3s16, MapBlock*> &changed_blocks, core::map<v3s16, MapBlock*> &lighting_invalidated_blocks ); + /*MapBlock* generateBlock(v3s16 blockpos, + core::map<v3s16, MapBlock*> &changed_blocks);*/ /* Get a block from somewhere. @@ -516,9 +486,6 @@ public: void saveMapMeta(); void loadMapMeta(); - void saveChunkMeta(); - void loadChunkMeta(); - // The sector mutex should be locked when calling most of these // This only saves sector-specific data such as the heightmap @@ -551,11 +518,6 @@ private: std::string m_savedir; bool m_map_saving_enabled; - - // Chunk size in MapSectors - s16 m_chunksize; - // Chunks - core::map<v2s16, MapChunk*> m_chunks; }; /* diff --git a/src/mapblock.cpp b/src/mapblock.cpp index 9594b2961..94c6ad909 100644 --- a/src/mapblock.cpp +++ b/src/mapblock.cpp @@ -36,6 +36,7 @@ MapBlock::MapBlock(NodeContainer *parent, v3s16 pos, bool dummy): is_underground(false), m_lighting_expired(true), m_day_night_differs(false), + m_not_fully_generated(false), m_objects(this) { data = NULL; @@ -1762,6 +1763,8 @@ void MapBlock::serialize(std::ostream &os, u8 version) flags |= 0x02; if(m_lighting_expired) flags |= 0x04; + if(m_not_fully_generated) + flags |= 0x08; os.write((char*)&flags, 1); u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; @@ -1884,6 +1887,7 @@ void MapBlock::deSerialize(std::istream &is, u8 version) is_underground = (flags & 0x01) ? true : false; m_day_night_differs = (flags & 0x02) ? true : false; m_lighting_expired = (flags & 0x04) ? true : false; + m_not_fully_generated = (flags & 0x08) ? true : false; // Uncompress data std::ostringstream os(std::ios_base::binary); diff --git a/src/mapblock.h b/src/mapblock.h index 02c072895..97129bd34 100644 --- a/src/mapblock.h +++ b/src/mapblock.h @@ -244,6 +244,17 @@ public: return m_lighting_expired; } + bool isFullyGenerated() + { + return !m_not_fully_generated; + } + + void setFullyGenerated(bool b) + { + setChangedFlag(); + m_not_fully_generated = !b; + } + bool isValid() { if(m_lighting_expired) @@ -655,12 +666,28 @@ private: // Whether day and night lighting differs bool m_day_night_differs; + /* + Whether everything that is mainly located on this block has + been added to the world. + + While this is false, a block can still be changed a bit when + stuff is added to the neighboring blocks that extends to this + one. + + When this is false on every one of a 3x3x3 chunk of blocks, the + central one will not be changed by the map generator in the + future. + + TODO: Save in file + */ + bool m_not_fully_generated; + MapBlockObjectList m_objects; // Object spawning stuff float m_spawn_timer; -#ifndef SERVER +#ifndef SERVER // Only on client /* Set to true if the mesh has been ordered to be updated sometime in the background. diff --git a/src/mapchunk.h b/src/mapchunk.h deleted file mode 100644 index 1819fa13e..000000000 --- a/src/mapchunk.h +++ /dev/null @@ -1,66 +0,0 @@ -/* -Minetest-c55 -Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com> - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License along -with this program; if not, write to the Free Software Foundation, Inc., -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -*/ - -#ifndef MAPCHUNK_HEADER -#define MAPCHUNK_HEADER - -/* - MapChunk contains map-generation-time metadata for an area of - some MapSectors. (something like 16x16) -*/ - -// These should fit in 8 bits, as they are saved as such. -enum{ - GENERATED_FULLY = 0, - GENERATED_PARTLY = 10, - GENERATED_NOT = 20 -}; - -class MapChunk -{ -public: - MapChunk(): - m_generation_level(GENERATED_NOT) - { - } - - /* - Generation level. Possible values: - GENERATED_FULLY = 0 = fully generated - GENERATED_PARTLY = partly generated - GENERATED_NOT = not generated - */ - u16 getGenLevel(){ return m_generation_level; } - void setGenLevel(u16 lev){ m_generation_level = lev; } - - void serialize(std::ostream &os, u8 version) - { - os.write((char*)&m_generation_level, 1); - } - void deSerialize(std::istream &is, u8 version) - { - is.read((char*)&m_generation_level, 1); - } - -private: - u8 m_generation_level; -}; - -#endif - diff --git a/src/materials.cpp b/src/materials.cpp index f56b024b2..0558a5e39 100644 --- a/src/materials.cpp +++ b/src/materials.cpp @@ -13,9 +13,9 @@ void setStoneLikeDiggingProperties(u8 material, float toughness) DiggingProperties(true, 15.0*toughness, 0)); g_material_properties[material].setDiggingProperties("WPick", - DiggingProperties(true, 1.5*toughness, 65535./30.*toughness)); + DiggingProperties(true, 1.3*toughness, 65535./30.*toughness)); g_material_properties[material].setDiggingProperties("STPick", - DiggingProperties(true, 0.7*toughness, 65535./100.*toughness)); + DiggingProperties(true, 0.65*toughness, 65535./100.*toughness)); /*g_material_properties[material].setDiggingProperties("MesePick", DiggingProperties(true, 0.0*toughness, 65535./20.*toughness));*/ diff --git a/src/server.cpp b/src/server.cpp index 61f354173..abdbd975d 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -544,11 +544,10 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, block_is_invalid = true; } - v2s16 p2d(p.X, p.Z); - ServerMap *map = (ServerMap*)(&server->m_env.getMap()); - v2s16 chunkpos = map->sector_to_chunk(p2d); - if(map->chunkNonVolatile(chunkpos) == false) + if(block->isFullyGenerated() == false) + { block_is_invalid = true; + } } /* @@ -3420,16 +3419,6 @@ Player *Server::emergePlayer(const char *name, const char *password, nodepos = v2s16(-range + (myrand()%(range*2)), -range + (myrand()%(range*2))); v2s16 sectorpos = getNodeSectorPos(nodepos); - /* - Ignore position if it is near a chunk edge. - Otherwise it would cause excessive loading time at - initial generation - */ - { - if(m_env.getServerMap().sector_to_chunk(sectorpos+v2s16(1,1)) - != m_env.getServerMap().sector_to_chunk(sectorpos+v2s16(-1,-1))) - continue; - } // Get sector (NOTE: Don't get because it's slow) //m_env.getMap().emergeSector(sectorpos); // Get ground height at point (fallbacks to heightmap function) |