From aed9e809a19bffdf911f75dd9d718efb8decd2c1 Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Sat, 25 Jun 2011 16:32:09 +0300 Subject: [PATCH] mapgen stuff --- src/defaultsettings.cpp | 1 + src/environment.cpp | 2 + src/map.cpp | 537 +++++++++++++++++++++++++--------------- src/mapblock_mesh.cpp | 4 +- src/noise.h | 39 +++ src/server.cpp | 2 +- src/utility.h | 5 + 7 files changed, 393 insertions(+), 197 deletions(-) diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 289be0a..74d3232 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -92,5 +92,6 @@ void set_default_settings() g_settings.setDefault("server_unload_unused_sectors_timeout", "60"); g_settings.setDefault("server_map_save_interval", "60"); g_settings.setDefault("full_block_send_enable_min_time_from_building", "2.0"); + //g_settings.setDefault("dungeon_rarity", "0.025"); } diff --git a/src/environment.cpp b/src/environment.cpp index 9bbba08..cd25534 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -619,6 +619,7 @@ void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime) { v3s16 p = p0 + block->getPosRelative(); MapNode n = block->getNodeNoEx(p0); +#if 1 // Test something: // Convert all mud under proper day lighting to grass if(n.d == CONTENT_MUD) @@ -634,6 +635,7 @@ void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime) } } } +#endif } } diff --git a/src/map.cpp b/src/map.cpp index fe6b7a7..cb0967c 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -2169,6 +2169,7 @@ void make_randomstone(VoxelManipulator &vmanip, v3s16 p0) } #endif +#if 0 void make_largestone(VoxelManipulator &vmanip, v3s16 p0) { MapNode stonenode(CONTENT_STONE); @@ -2250,6 +2251,7 @@ void make_largestone(VoxelManipulator &vmanip, v3s16 p0) vmanip.m_data[vi] = stonenode; } } +#endif /* Dungeon making routines @@ -2375,39 +2377,79 @@ void make_hole1(VoxelManipulator &vmanip, v3s16 place) void make_door1(VoxelManipulator &vmanip, v3s16 doorplace, v3s16 doordir) { make_hole1(vmanip, doorplace); + // Place torch (for testing) + //vmanip.m_data[vmanip.m_area.index(doorplace)] = MapNode(CONTENT_TORCH); } -v3s16 rand_ortho_dir() +v3s16 rand_ortho_dir(PseudoRandom &random) { - if(myrand()%2==0) - return myrand()%2 ? v3s16(-1,0,0) : v3s16(1,0,0); + if(random.next()%2==0) + return random.next()%2 ? v3s16(-1,0,0) : v3s16(1,0,0); else - return myrand()%2 ? v3s16(0,0,-1) : v3s16(0,0,1); + return random.next()%2 ? v3s16(0,0,-1) : v3s16(0,0,1); +} + +v3s16 turn_xz(v3s16 olddir, int t) +{ + v3s16 dir; + if(t == 0) + { + // Turn right + dir.X = olddir.Z; + dir.Z = -olddir.X; + dir.Y = olddir.Y; + } + else + { + // Turn left + dir.X = -olddir.Z; + dir.Z = olddir.X; + dir.Y = olddir.Y; + } + return dir; +} + +v3s16 random_turn(PseudoRandom &random, v3s16 olddir) +{ + int turn = random.range(0,2); + v3s16 dir; + if(turn == 0) + { + // Go straight + dir = olddir; + } + else if(turn == 1) + // Turn right + dir = turn_xz(olddir, 0); + else + // Turn left + dir = turn_xz(olddir, 1); + return dir; } void make_corridor(VoxelManipulator &vmanip, v3s16 doorplace, v3s16 doordir, - v3s16 &result_place, v3s16 &result_dir) + v3s16 &result_place, v3s16 &result_dir, PseudoRandom &random) { make_hole1(vmanip, doorplace); v3s16 p0 = doorplace; v3s16 dir = doordir; u32 length; - if(myrand()%2) - length = myrand_range(1,13); + if(random.next()%2) + length = random.range(1,13); else - length = myrand_range(1,6); - u32 partlength = myrand_range(1,length); + length = random.range(1,6); + length = random.range(1,13); + u32 partlength = random.range(1,length); u32 partcount = 0; s16 make_stairs = 0; - if(myrand()%2 == 0 && partlength >= 3) - make_stairs = myrand()%2 ? 1 : -1; + if(random.next()%2 == 0 && partlength >= 3) + make_stairs = random.next()%2 ? 1 : -1; for(u32 i=0; i= partlength) { partcount = 0; - v3s16 newdir = rand_ortho_dir(); - partlength = myrand_range(1,7); - make_stairs = 0; - if(myrand()%2 == 0 && partlength >= 3) - make_stairs = myrand()%2 ? 1 : -1; + dir = random_turn(random, dir); + + partlength = random.range(1,length); - if(make_stairs != 0) - { - if(newdir.X == 0 && dir.X != 0) - dir = newdir; - if(newdir.Z == 0 && dir.Z != 0) - dir = newdir; - } - else - { - dir = newdir; - } - dir.Y = make_stairs; + make_stairs = 0; + if(random.next()%2 == 0 && partlength >= 3) + make_stairs = random.next()%2 ? 1 : -1; } } - //p0.Y -= make_stairs; - dir.Y = 0; result_place = p0; result_dir = dir; } @@ -2466,16 +2513,17 @@ class RoomWalker { public: - RoomWalker(VoxelManipulator &vmanip_, v3s16 pos): + RoomWalker(VoxelManipulator &vmanip_, v3s16 pos, PseudoRandom &random): vmanip(vmanip_), - m_pos(pos) + m_pos(pos), + m_random(random) { randomizeDir(); } void randomizeDir() { - m_dir = rand_ortho_dir(); + m_dir = rand_ortho_dir(m_random); } void setPos(v3s16 pos) @@ -2561,13 +2609,13 @@ public: v3s16 roomplace; // X east, Z north, Y up if(doordir == v3s16(1,0,0)) // X+ - roomplace = doorplace + v3s16(0,-1,-roomsize.Z/2+myrand_range(-roomsize.Z/2,roomsize.Z/2)); + roomplace = doorplace + v3s16(0,-1,-roomsize.Z/2+m_random.range(-roomsize.Z/2+1,roomsize.Z/2-1)); if(doordir == v3s16(-1,0,0)) // X- - roomplace = doorplace + v3s16(-roomsize.X+1,-1,-roomsize.Z/2+myrand_range(-roomsize.Z/2,roomsize.Z/2)); + roomplace = doorplace + v3s16(-roomsize.X+1,-1,-roomsize.Z/2+m_random.range(-roomsize.Z/2+1,roomsize.Z/2-1)); if(doordir == v3s16(0,0,1)) // Z+ - roomplace = doorplace + v3s16(-roomsize.X/2+myrand_range(-roomsize.X/2,roomsize.X/2),-1,0); + roomplace = doorplace + v3s16(-roomsize.X/2+m_random.range(-roomsize.X/2+1,roomsize.X/2-1),-1,0); if(doordir == v3s16(0,0,-1)) // Z- - roomplace = doorplace + v3s16(-roomsize.X/2+myrand_range(-roomsize.X/2,roomsize.X/2),-1,-roomsize.Z+1); + roomplace = doorplace + v3s16(-roomsize.X/2+m_random.range(-roomsize.X/2+1,roomsize.X/2-1),-1,-roomsize.Z+1); // Check fit bool fits = true; @@ -2604,58 +2652,133 @@ public: private: VoxelManipulator &vmanip; v3s16 m_pos; - v3s16 m_dir; + v3s16 m_dir; + PseudoRandom &m_random; }; -void make_dungeon1(VoxelManipulator &vmanip) +void make_dungeon1(VoxelManipulator &vmanip, PseudoRandom &random) { v3s16 areasize = vmanip.m_area.getExtent(); v3s16 roomsize; v3s16 roomplace; - roomsize = v3s16(myrand_range(4,8),myrand_range(4,6),myrand_range(4,8)); - roomplace = vmanip.m_area.MinEdge + v3s16( - myrand_range(0,areasize.X-roomsize.X-1), - myrand_range(0,areasize.Y-roomsize.Y-1), - myrand_range(0,areasize.Z-roomsize.Z-1)); + /* + Find place for first room + */ + bool fits = false; + for(u32 i=0; i<100; i++) + { + roomsize = v3s16(random.range(4,8),random.range(4,6),random.range(4,8)); + roomplace = vmanip.m_area.MinEdge + v3s16( + random.range(0,areasize.X-roomsize.X-1), + random.range(0,areasize.Y-roomsize.Y-1), + random.range(0,areasize.Z-roomsize.Z-1)); + /* + Check that we're not putting the room to an unknown place, + otherwise it might end up floating in the air + */ + fits = true; + for(s16 z=1; zm_static_objects.insert(0, s_obj); delete obj; } - if(myrand() % 300 == 0) + if(myrand() % 1000 == 0) { v3f pos_f = intToFloat(p+block->getPosRelative(), BS); pos_f.Y -= BS*0.4; @@ -3078,18 +3202,18 @@ void makeBlock(BlockMakeData *data) s16 approx_ground_depth = approx_groundlevel - (node_min.Y+MAP_BLOCKSIZE/2); - /*s16 minimum_groundlevel = (s16)get_sector_minimum_ground_level( + s16 minimum_groundlevel = (s16)get_sector_minimum_ground_level( data->seed, v2s16(blockpos.X, blockpos.Z)); - // Minimum amount of ground above the central block - s16 minimum_ground_depth = minimum_groundlevel - node_max.Y;*/ - + // Minimum amount of ground above the top of the central block + s16 minimum_ground_depth = minimum_groundlevel - node_max.Y; + s16 maximum_groundlevel = (s16)get_sector_maximum_ground_level( data->seed, v2s16(blockpos.X, blockpos.Z), 1); - // Minimum amount of ground above the central block + // Maximum amount of ground above the bottom of the central block s16 maximum_ground_depth = maximum_groundlevel - node_min.Y; /* - Special case for high air or water + Special case for high air or water: Just fill with air and water. */ if(maximum_ground_depth < -20) { @@ -3121,6 +3245,18 @@ void makeBlock(BlockMakeData *data) // We're done return; } + + /* + If block is deep underground, this is set to true and ground + density noise is not generated, for speed optimization. + */ + bool all_is_ground_except_caves = (minimum_ground_depth > 16); + + /* + Create a block-specific seed + */ + u32 blockseed = (data->seed%0x100000000) + full_node_min.Z*38134234 + + full_node_min.Y*42123 + full_node_min.X*23; /* Make some 3D noise @@ -3145,8 +3281,7 @@ void makeBlock(BlockMakeData *data) noisebuf_cave.create(get_cave_noise1_params(data->seed), minpos_f.X, minpos_f.Y, minpos_f.Z, maxpos_f.X, maxpos_f.Y, maxpos_f.Z, - 4, 4, 4); - //3.5, 3.5, 3.5); + 4, 3, 4); noisebuf_cave.multiply(get_cave_noise2_params(data->seed)); @@ -3158,16 +3293,17 @@ void makeBlock(BlockMakeData *data) v3f sl = v3f(4.0, 4.0, 4.0); /* - Density + Density noise */ - //noisebuf_ground.create(data->seed+983240, 6, 0.60, false, - noisebuf_ground.create(get_ground_noise1_params(data->seed), - minpos_f.X, minpos_f.Y, minpos_f.Z, - maxpos_f.X, maxpos_f.Y, maxpos_f.Z, - sl.X, sl.Y, sl.Z); + if(all_is_ground_except_caves == false) + //noisebuf_ground.create(data->seed+983240, 6, 0.60, false, + noisebuf_ground.create(get_ground_noise1_params(data->seed), + minpos_f.X, minpos_f.Y, minpos_f.Z, + maxpos_f.X, maxpos_f.Y, maxpos_f.Z, + sl.X, sl.Y, sl.Z); /* - Content + Ground property noise */ sl = v3f(2.5, 2.5, 2.5); noisebuf_ground_crumbleness.create( @@ -3200,42 +3336,22 @@ void makeBlock(BlockMakeData *data) // Only modify places that have no content if(vmanip.m_data[i].d == CONTENT_IGNORE) { - if(noisebuf_cave.get(x,y,z) > CAVE_NOISE_THRESHOLD) + // First priority: make air and water. + // This avoids caves inside water. + if(all_is_ground_except_caves == false + && val_is_ground(noisebuf_ground.get(x,y,z), + v3s16(x,y,z), data->seed) == false) + { + if(y <= WATER_LEVEL) + vmanip.m_data[i] = MapNode(CONTENT_WATERSOURCE); + else + vmanip.m_data[i] = MapNode(CONTENT_AIR); + } + else if(noisebuf_cave.get(x,y,z) > CAVE_NOISE_THRESHOLD) vmanip.m_data[i] = MapNode(CONTENT_AIR); - else if(val_is_ground(noisebuf_ground.get(x,y,z), - v3s16(x,y,z), data->seed)) - vmanip.m_data[i] = MapNode(CONTENT_STONE); - else if(y <= WATER_LEVEL) - vmanip.m_data[i] = MapNode(CONTENT_WATERSOURCE); else - vmanip.m_data[i] = MapNode(CONTENT_AIR); - } - - /*if(noisebuf_cave.get(x,y,z) > CAVE_NOISE_THRESHOLD) - { - // Only modify places that have no content - if(vmanip.m_data[i].d == CONTENT_IGNORE) - vmanip.m_data[i] = MapNode(CONTENT_AIR); - } - else if(is_ground(noisebuf_ground.get(x,y,z), y)) - { - // Only modify places that have no content - if(vmanip.m_data[i].d == CONTENT_IGNORE) vmanip.m_data[i] = MapNode(CONTENT_STONE); } - else if(y <= WATER_LEVEL) - { - // Only modify places that have air or no content - if(vmanip.m_data[i].d == CONTENT_IGNORE - || vmanip.m_data[i].d == CONTENT_AIR) - vmanip.m_data[i] = MapNode(CONTENT_WATERSOURCE); - } - else - { - // Only modify places that have no content - if(vmanip.m_data[i].d == CONTENT_IGNORE) - vmanip.m_data[i] = MapNode(CONTENT_AIR); - }*/ data->vmanip.m_area.add_y(em, i, 1); } @@ -3247,22 +3363,24 @@ void makeBlock(BlockMakeData *data) */ { + PseudoRandom mineralrandom(blockseed); + /* Add meseblocks */ for(s16 i=0; iseed)+1.0)/2.0) + < dungeon_rarity + && node_min.Y < approx_groundlevel) { // Dungeon generator doesn't modify places which have this set data->vmanip.clearFlag(VMANIP_FLAG_DUNGEON_INSIDE @@ -3433,7 +3556,7 @@ void makeBlock(BlockMakeData *data) // Use fast index incrementing v3s16 em = vmanip.m_area.getExtent(); u32 i = vmanip.m_area.index(v3s16(p2d.X, full_node_max.Y, p2d.Y)); - for(s16 y=node_max.Y; y>=full_node_min.Y; y--) + for(s16 y=full_node_max.Y; y>=full_node_min.Y; y--) { if(vmanip.m_data[i].d == CONTENT_AIR) vmanip.m_flags[i] |= VMANIP_FLAG_DUNGEON_PRESERVE; @@ -3444,18 +3567,11 @@ void makeBlock(BlockMakeData *data) } } - /*s16 x = myrand_range(node_min.X, node_max.X); - s16 z = myrand_range(node_min.Z, node_max.Z); - s16 y = myrand_range(node_min.Y, node_max.Y);*/ - // Add it - make_dungeon1(data->vmanip); - - // Take different seed for every dungeon for not blending their - // mossyness together - //u32 mossyseed = z*38134234+y*42123+x*23; - u32 mossyseed = full_node_min.Z*38134234 - +full_node_min.Y*42123+full_node_min.X*23; + PseudoRandom random(blockseed+2); + // Add it + make_dungeon1(data->vmanip, random); + // Convert some cobble to mossy cobble for(s16 x=full_node_min.X; x<=full_node_max.X; x++) for(s16 z=full_node_min.Z; z<=full_node_max.Z; z++) @@ -3466,20 +3582,27 @@ void makeBlock(BlockMakeData *data) // Use fast index incrementing v3s16 em = vmanip.m_area.getExtent(); u32 i = vmanip.m_area.index(v3s16(p2d.X, full_node_max.Y, p2d.Y)); - for(s16 y=node_max.Y; y>=full_node_min.Y; y--) + for(s16 y=full_node_max.Y; y>=full_node_min.Y; y--) { - // No mossy in dry places - // (noisebuf not used because it's smaller in size) - if(noise3d_param(get_ground_wetness_params(data->seed), x,y,z) - < -0.6) - continue; - double d = noise3d_perlin((float)x/4., - (float)y/4.,(float)z/4., - mossyseed, 2, 0.9); - if(d < 0.0) - continue; + // (noisebuf not used because it doesn't contain the + // full area) + double wetness = noise3d_param( + get_ground_wetness_params(data->seed), x,y,z); + double d = noise3d_perlin((float)x/2.5, + (float)y/2.5,(float)z/2.5, + blockseed, 2, 1.4); if(vmanip.m_data[i].d == CONTENT_COBBLE) - vmanip.m_data[i].d = CONTENT_MOSSYCOBBLE; + { + if(d < wetness/3.0) + { + vmanip.m_data[i].d = CONTENT_MOSSYCOBBLE; + } + } + /*else if(vmanip.m_flags[i] & VMANIP_FLAG_DUNGEON_INSIDE) + { + if(wetness > 1.2) + vmanip.m_data[i].d = CONTENT_MUD; + }*/ data->vmanip.m_area.add_y(em, i, -1); } } @@ -3533,7 +3656,8 @@ void makeBlock(BlockMakeData *data) If close to ground level */ - if(abs(approx_ground_depth) < 20) + //if(abs(approx_ground_depth) < 30) + if(minimum_ground_depth < 5 && maximum_ground_depth > -5) { /* Add grass and mud @@ -3547,14 +3671,14 @@ void makeBlock(BlockMakeData *data) { bool possibly_have_sand = get_have_sand(data->seed, p2d); bool have_sand = false; - u32 mud_count = 0; + u32 current_depth = 0; bool air_detected = false; bool water_detected = false; // Use fast index incrementing s16 start_y = node_max.Y+2; v3s16 em = vmanip.m_area.getExtent(); u32 i = vmanip.m_area.index(v3s16(p2d.X, start_y, p2d.Y)); - for(s16 y=start_y; y>=node_min.Y-2; y--) + for(s16 y=start_y; y>=node_min.Y-3; y--) { if(vmanip.m_data[i].d == CONTENT_WATERSOURCE) water_detected = true; @@ -3568,24 +3692,37 @@ void makeBlock(BlockMakeData *data) || vmanip.m_data[i].d == CONTENT_GRAVEL ) && (air_detected || water_detected)) { - if(mud_count == 0 && y <= WATER_LEVEL+2 + if(current_depth == 0 && y <= WATER_LEVEL+2 && possibly_have_sand) have_sand = true; - - if(have_sand) + + if(current_depth < 4) { - vmanip.m_data[i] = MapNode(CONTENT_SAND); + if(have_sand) + { + vmanip.m_data[i] = MapNode(CONTENT_SAND); + } + #if 1 + else if(current_depth==0 && !water_detected + && y >= WATER_LEVEL && air_detected) + vmanip.m_data[i] = MapNode(CONTENT_GRASS); + #endif + else + vmanip.m_data[i] = MapNode(CONTENT_MUD); } - else if(mud_count==0 && !water_detected && y >= WATER_LEVEL) - vmanip.m_data[i] = MapNode(CONTENT_GRASS); else - vmanip.m_data[i] = MapNode(CONTENT_MUD); + { + if(vmanip.m_data[i].d == CONTENT_MUD + || vmanip.m_data[i].d == CONTENT_GRASS) + vmanip.m_data[i] = MapNode(CONTENT_STONE); + } - mud_count++; - if(mud_count >= 4) + current_depth++; + + if(current_depth >= 8) break; } - else if(mud_count != 0) + else if(current_depth != 0) break; data->vmanip.m_area.add_y(em, i, -1); @@ -3599,11 +3736,12 @@ void makeBlock(BlockMakeData *data) // Amount of trees u32 tree_count = block_area_nodes * tree_amount_2d(data->seed, p2d_center); + PseudoRandom treerandom(blockseed); // Put trees in random places on part of division for(u32 i=0; ivmanip, v2s16(x,z)); s16 y = find_ground_level_from_noise(data->seed, v2s16(x,z), 4); // Don't make a tree under water level @@ -3800,15 +3938,17 @@ MapBlock* ServerMap::finishBlockMake(BlockMakeData *data, /* Blit generated stuff to map + NOTE: blitBackAll adds nearly everything to changed_blocks */ { // 70ms @cs=8 //TimeTaker timer("finishBlockMake() blitBackAll"); data->vmanip.blitBackAll(&changed_blocks); } - - //dstream<<"changed_blocks.size()="< 32768/10) + { + //dstream<<"WARNING: PseudoRandom::range: max > 32767"< max) + { + assert(0); + return max; + } + return (next()%(max-min+1))+min; + } +private: + int m_next; +}; + double easeCurve(double t); // Return value: -1 ... 1 diff --git a/src/server.cpp b/src/server.cpp index 3036aef..49eb157 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -1720,7 +1720,7 @@ void Server::AsyncRunStep() // Don't send too many at a time count++; - if(count >= 2 && m_unsent_map_edit_queue.size() < 50) + if(count >= 1 && m_unsent_map_edit_queue.size() < 100) break; } } diff --git a/src/utility.h b/src/utility.h index 534aea4..497f79f 100644 --- a/src/utility.h +++ b/src/utility.h @@ -1741,6 +1741,11 @@ void mysrand(unsigned seed); inline int myrand_range(int min, int max) { + if(max-min > MYRAND_MAX) + { + dstream<<"WARNING: myrand_range: max-min > MYRAND_MAX"< max) { assert(0);