diff --git a/data/sand.png b/data/sand.png index 89149add2..15101a7eb 100644 Binary files a/data/sand.png and b/data/sand.png differ diff --git a/data/water.png b/data/water.png index 6b693f196..a5347f61d 100644 Binary files a/data/water.png and b/data/water.png differ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d027c7cd9..1c5e5f6e1 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -39,6 +39,7 @@ configure_file( ) set(common_SRCS + noise.cpp mineral.cpp porting.cpp materials.cpp diff --git a/src/common_irrlicht.h b/src/common_irrlicht.h index 9f28687d9..88e74878b 100644 --- a/src/common_irrlicht.h +++ b/src/common_irrlicht.h @@ -32,5 +32,7 @@ typedef core::vector2d v2s32; typedef core::vector2d v2u32; typedef core::vector2d v2f32; +typedef unsigned long long u64; + #endif diff --git a/src/main.cpp b/src/main.cpp index 57ebfce1a..a97d1f45d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -315,43 +315,45 @@ FEATURE: The map could be generated procedually: - Simulate rock falling from cliffs when water has removed enough solid rock from the bottom -Doing now: ----------- +Doing now (most important at the top): +-------------------------------------- # maybe done * not done +* Perlin noise stuff sucks in heightmap generation, fix it +* Create perlin noise functions and use them to get natural randomness + in everything. No need for attributes or fractal terrain. +* Do something about AttributeDatabase/List being too slow + - Remove it +* Save chunk metadata on disk * Remove all kinds of systems that are made redundant by the new map generator - Sector heightmaps? At least they should be made redundant. - Sector objects -* Do something about AttributeDatabase/List being too slow -* Save chunk metadata on disk -* Change water side textures so that buggy water doesn't look bad +* Fix the strange mineral occurences + - Do they appear anymore? * Make server find the spawning place from the real map data, not from the heightmap -* only_from_disk doesn't work that well anymore + - But the changing borders of chunk have to be avoided, because + there is time to generate only one chunk. +* 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 -* Fix the strange mineral occurences -* When the map is generated and a place is found for the player, the - first chunk is actually still volatile and will have stuff still - changed after spawning, which creates a lot of glitches. - - This is partly fixed by now allowing only 2-sector deeep - modification of volatile chunks. But it should still be fixed? - - How about checking that the neighbors are fully generated too and - generate them when the middle piece is needed - - This is very slow - - How about just enabling changed_blocks properly - - This is probably a good idea - - The server has to make sure the spawn point is not at the - changing borders of a chunk * Add some kind of erosion and other stuff that now is possible * Make client to fetch stuff asynchronously - Needs method SyncProcessData * What is the problem with the server constantly saving one or a few blocks? List the first saved block, maybe it explains. + - Does it still do this? * Water doesn't start flowing after map generation like it should -* Better water generation + - Are there still problems? +* Better water generation (spread it to underwater caverns) +* When generating a chunk and the neighboring chunk doesn't have mud + and stuff yet and the ground is fairly flat, the mud will flow to + the other chunk making nasty straight walls when the other chunk + is generated. Fix it. +* Save map seed to a metafile (with version information) + - Remove master heightmap ====================================================================== diff --git a/src/map.cpp b/src/map.cpp index 3a3169200..117435565 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -26,6 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "voxel.h" #include "porting.h" #include "mineral.h" +#include "noise.h" /* Map @@ -1731,7 +1732,8 @@ void Map::transformLiquids(core::map & modified_blocks) ServerMap::ServerMap(std::string savedir, HMParams hmp, MapParams mp): Map(dout_server), - m_heightmap(NULL) + m_heightmap(NULL), + m_seed(0) { //m_chunksize = 64; @@ -1739,6 +1741,12 @@ ServerMap::ServerMap(std::string savedir, HMParams hmp, MapParams mp): m_chunksize = 8; //m_chunksize = 4; //m_chunksize = 2; + + // TODO: Save to and load from a file + m_seed = (((u64)myrand()<<0)%0x7fff) + + (((u64)myrand()<<16)%0x7fff) + + (((u64)myrand()<<32)%0x7fff) + + (((u64)myrand()<<48)%0x7fff); /* Experimental and debug stuff @@ -1979,6 +1987,8 @@ ServerMap::ServerMap(std::string savedir, HMParams hmp, MapParams mp): dstream<=y_nodes_min; y--) + { + MapNode &n = vmanip.m_data[i]; + if(content_walkable(n.d) + && n.d != CONTENT_TREE + && n.d != CONTENT_LEAVES) + break; + + vmanip.m_area.add_y(em, i, -1); + } + if(y >= y_nodes_min) + return y; + else + return y_nodes_min; +} + void make_tree(VoxelManipulator &vmanip, v3s16 p0) { MapNode treenode(CONTENT_TREE); @@ -2123,6 +2156,49 @@ void make_tree(VoxelManipulator &vmanip, v3s16 p0) } } +/* + Noise functions. Make sure seed is mangled differently in each one. +*/ + +// Amount of trees per area in nodes +double tree_amount_2d(u64 seed, v2s16 p) +{ + double noise = noise2d_perlin( + 0.5+(float)p.X/500, 0.5+(float)p.Y/500, + seed+2, 4, 0.55); + double zeroval = -0.3; + if(noise < zeroval) + return 0; + else + return 0.05 * (noise-zeroval) / (0.5-zeroval); +} + +double base_rock_level_2d(u64 seed, v2s16 p) +{ + return -4. + 25. * noise2d_perlin( + 0.5+(float)p.X/500., 0.5+(float)p.Y/500., + seed, 6, 0.6); +} + +double highlands_level_2d(u64 seed, v2s16 p) +{ + double a = noise2d_perlin( + 0.5+(float)p.X/1000., 0.5+(float)p.Y/1000., + seed-359, 6, 0.55); + if(a > 0.2) + { + return 35. + 10. * noise2d_perlin( + 0.5+(float)p.X/500., 0.5+(float)p.Y/500., + seed+85039, 6, 0.55); + } + else + return -100000; +} + +/* + This is the main map generation method +*/ + MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos, core::map &changed_blocks) { @@ -2185,8 +2261,8 @@ MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos, ); // Relative values to control amount of stuff in one chunk - u32 relative_area = (u32)sectorpos_base_size*MAP_BLOCKSIZE - *(u32)sectorpos_base_size*MAP_BLOCKSIZE; + /*u32 relative_area = (u32)sectorpos_base_size*MAP_BLOCKSIZE + *(u32)sectorpos_base_size*MAP_BLOCKSIZE;*/ u32 relative_volume = (u32)sectorpos_base_size*MAP_BLOCKSIZE *(u32)sectorpos_base_size*MAP_BLOCKSIZE *(u32)h_blocks*MAP_BLOCKSIZE; @@ -2234,12 +2310,6 @@ MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos, } } - /* - Clear all light emitted - */ - - core::map unlight_from; - /* Now we have a big empty area. @@ -2281,22 +2351,17 @@ MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos, // Ground height at this point float surface_y_f = 0.0; - /* - A hack to get the ground height from the sector. - Do this better. - */ + + // Use perlin noise for ground height + surface_y_f = base_rock_level_2d(m_seed, p2d); + + // Experimental stuff { - v2s16 sectorpos = getContainerPos(p2d, MAP_BLOCKSIZE); - v2s16 sector_relpos = p2d - sectorpos*MAP_BLOCKSIZE; - MapSector *sector = getSectorNoGenerate(sectorpos); - assert(sector); - float h = sector->getGroundHeight(sector_relpos); - if(h > GROUNDHEIGHT_VALID_MINVALUE) - surface_y_f = h; - else - dstream<<"WARNING: "<<__FUNCTION_NAME - <<": sector->getGroundHeight returned bad height"< surface_y_f) + surface_y_f = a; } + // Convert to integer s16 surface_y = (s16)surface_y_f; @@ -2327,6 +2392,7 @@ MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos, stone_obstacle_amount = myrand_range(0, myrand_range(20, 150)); else stone_obstacle_amount = myrand_range(0, myrand_range(20, 50)); + //u32 stone_obstacle_amount = // myrand_range(0, myrand_range(20, myrand_range(80,150))); @@ -2364,6 +2430,11 @@ MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos, myrand_range(0, maxheight_randomized), myrand_range(5, stone_obstacle_max_size) ); + + // Don't make stupid small rectable bumps + if(ob_size.Y < 5) + continue; + /*v2s16 ob_place( myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1), myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1) @@ -2477,11 +2548,13 @@ MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos, /* Make dungeons */ - u32 dungeons_count = relative_volume / 200000; + u32 dungeons_count = relative_volume / 400000; u32 bruises_count = relative_volume * stone_surface_max_y / 200000 / 50; + /*u32 dungeons_count = 0; + u32 bruises_count = 0;*/ for(u32 jj=0; jj= mud_add_amount) break; + MapNode &n = vmanip.m_data[i]; + n.d = CONTENT_MUD; + mudcount++; + vmanip.m_area.add_y(em, i, 1); } } @@ -2828,7 +2913,7 @@ MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos, */ // Iterate a few times - for(s16 k=0; k<4; k++) + for(s16 k=0; k<3; k++) { /*for(s16 x=0-max_spread_amount+1; @@ -2865,7 +2950,7 @@ MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos, u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y)); s16 y=y_nodes_max; - for(;;) + for(;; y--) { MapNode *n = NULL; // Find mud @@ -2890,6 +2975,20 @@ MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos, if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS) continue;*/ + /* + Don't flow it if the stuff under it is not mud + */ + { + u32 i2 = i; + vmanip.m_area.add_y(em, i2, -1); + // Cancel if out of area + if(vmanip.m_area.contains(i2) == false) + continue; + MapNode *n2 = &vmanip.m_data[i2]; + if(n2->d != CONTENT_MUD && n2->d != CONTENT_GRASS) + continue; + } + // Make it exactly mud n->d = CONTENT_MUD; @@ -2904,61 +3003,58 @@ MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos, }; // Theck that upper is air or doesn't exist. - // Only drop mud if upper doesn't contain anything that - // would keep the mud in place. + // Cancel dropping if upper keeps it in place u32 i3 = i; vmanip.m_area.add_y(em, i3, 1); - if(vmanip.m_area.contains(i3) == false - || content_walkable(vmanip.m_data[i3].d) == false) + if(vmanip.m_area.contains(i3) == true + && content_walkable(vmanip.m_data[i3].d) == true) { + continue; + } - // Drop mud on side - - for(u32 di=0; di<4; di++) - { - v3s16 dirp = dirs4[di]; - u32 i2 = i; - // Move to side - vmanip.m_area.add_p(em, i2, dirp); - // Fail if out of area - if(vmanip.m_area.contains(i2) == false) - continue; - // Check that side is air - MapNode *n2 = &vmanip.m_data[i2]; - if(content_walkable(n2->d)) - continue; - // Check that under side is air + // Drop mud on side + + for(u32 di=0; di<4; di++) + { + v3s16 dirp = dirs4[di]; + u32 i2 = i; + // Move to side + vmanip.m_area.add_p(em, i2, dirp); + // Fail if out of area + if(vmanip.m_area.contains(i2) == false) + continue; + // Check that side is air + MapNode *n2 = &vmanip.m_data[i2]; + if(content_walkable(n2->d)) + continue; + // Check that under side is air + vmanip.m_area.add_y(em, i2, -1); + // Fail if out of area + if(vmanip.m_area.contains(i2) == false) + continue; + n2 = &vmanip.m_data[i2]; + if(content_walkable(n2->d)) + continue; + // Loop further down until not air + do{ vmanip.m_area.add_y(em, i2, -1); // Fail if out of area if(vmanip.m_area.contains(i2) == false) continue; n2 = &vmanip.m_data[i2]; - if(content_walkable(n2->d)) - continue; - // Loop further down until not air - do{ - vmanip.m_area.add_y(em, i2, -1); - // Fail if out of area - if(vmanip.m_area.contains(i2) == false) - continue; - n2 = &vmanip.m_data[i2]; - }while(content_walkable(n2->d) == false); - // Loop one up so that we're in air - vmanip.m_area.add_y(em, i2, 1); - n2 = &vmanip.m_data[i2]; + }while(content_walkable(n2->d) == false); + // Loop one up so that we're in air + vmanip.m_area.add_y(em, i2, 1); + n2 = &vmanip.m_data[i2]; - // Move mud to new place - *n2 = *n; - // Set old place to be air - *n = MapNode(CONTENT_AIR); + // Move mud to new place + *n2 = *n; + // Set old place to be air + *n = MapNode(CONTENT_AIR); - // Done - break; - } + // Done + break; } - - // Continue from next y - y--; } } @@ -3054,6 +3150,67 @@ MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos, } // Aging loop + {//TimeTaker timer1("convert mud to sand"); + + /* + Convert mud to sand + */ + + //s16 mud_add_amount = myrand_range(2, 4); + //s16 mud_add_amount = 0; + + /*for(s16 x=0; x 0.0); + + 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); + } + } + + } + + }//timer1 { // 1ms @cs=8 //TimeTaker timer1("plant trees"); @@ -3062,9 +3219,56 @@ MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos, Plant some trees */ { - u32 tree_max = relative_area / 60; - - u32 count = myrand_range(0, tree_max); + // Divide area into parts + s16 div = 8; + s16 sidelen = sectorpos_base_size*MAP_BLOCKSIZE / div; + double area = sidelen * sidelen; + for(s16 x0=0; x0d != CONTENT_MUD && n->d != CONTENT_GRASS) + continue; + } + p.Y++; + // Make a tree + make_tree(vmanip, p); + } + } + /*u32 tree_max = relative_area / 60; + //u32 count = myrand_range(0, tree_max); for(u32 i=0; i +Copyright (C) 2010-2011 celeron55, Perttu Ahola 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 diff --git a/src/materials.cpp b/src/materials.cpp index bc39619fc..7f1ba18bd 100644 --- a/src/materials.cpp +++ b/src/materials.cpp @@ -57,6 +57,9 @@ void initializeMaterialProperties() g_material_properties[CONTENT_WOOD].setDiggingProperties("", DiggingProperties(true, 1.0, 0)); + g_material_properties[CONTENT_SAND].setDiggingProperties("", + DiggingProperties(true, 0.5, 0)); + /* Add MesePick to everything */