- Simulate rock falling from cliffs when water has removed\r
enough solid rock from the bottom\r
\r
-Doing now:\r
-----------\r
+Doing now (most important at the top):\r
+--------------------------------------\r
# maybe done\r
* not done\r
\r
+* Perlin noise stuff sucks in heightmap generation, fix it\r
+* Create perlin noise functions and use them to get natural randomness\r
+ in everything. No need for attributes or fractal terrain.\r
+* Do something about AttributeDatabase/List being too slow\r
+ - Remove it\r
+* Save chunk metadata on disk\r
* Remove all kinds of systems that are made redundant by the new map\r
generator\r
- Sector heightmaps? At least they should be made redundant.\r
- Sector objects\r
-* Do something about AttributeDatabase/List being too slow\r
-* Save chunk metadata on disk\r
-* Change water side textures so that buggy water doesn't look bad\r
+* Fix the strange mineral occurences\r
+ - Do they appear anymore?\r
* Make server find the spawning place from the real map data, not from\r
the heightmap\r
-* only_from_disk doesn't work that well anymore\r
+ - But the changing borders of chunk have to be avoided, because\r
+ there is time to generate only one chunk.\r
+* only_from_disk might not work anymore - check and fix it.\r
* Make the generator to run in background and not blocking block\r
placement and transfer\r
-* Fix the strange mineral occurences\r
-* When the map is generated and a place is found for the player, the\r
- first chunk is actually still volatile and will have stuff still\r
- changed after spawning, which creates a lot of glitches.\r
- - This is partly fixed by now allowing only 2-sector deeep\r
- modification of volatile chunks. But it should still be fixed?\r
- - How about checking that the neighbors are fully generated too and\r
- generate them when the middle piece is needed\r
- - This is very slow\r
- - How about just enabling changed_blocks properly\r
- - This is probably a good idea\r
- - The server has to make sure the spawn point is not at the\r
- changing borders of a chunk\r
* Add some kind of erosion and other stuff that now is possible\r
* Make client to fetch stuff asynchronously\r
- Needs method SyncProcessData\r
* What is the problem with the server constantly saving one or a few\r
blocks? List the first saved block, maybe it explains.\r
+ - Does it still do this?\r
* Water doesn't start flowing after map generation like it should\r
-* Better water generation\r
+ - Are there still problems?\r
+* Better water generation (spread it to underwater caverns)\r
+* When generating a chunk and the neighboring chunk doesn't have mud\r
+ and stuff yet and the ground is fairly flat, the mud will flow to\r
+ the other chunk making nasty straight walls when the other chunk\r
+ is generated. Fix it.\r
+* Save map seed to a metafile (with version information)\r
+ - Remove master heightmap\r
\r
======================================================================\r
\r
#include "voxel.h"
#include "porting.h"
#include "mineral.h"
+#include "noise.h"
/*
Map
ServerMap::ServerMap(std::string savedir, HMParams hmp, MapParams mp):
Map(dout_server),
- m_heightmap(NULL)
+ m_heightmap(NULL),
+ m_seed(0)
{
//m_chunksize = 64;
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
dstream<<DTIME<<"Initializing new map."<<std::endl;
// Create master heightmap
+ // NOTE: Yes, that is a magic number down there. It specifies
+ // the size of the internal FixedHeightmaps.
m_heightmap = new UnlimitedHeightmap
(32, &m_padb);
}
/*
- Some helper functions
+ Some helper functions for the map generator
*/
s16 find_ground_level(VoxelManipulator &vmanip, v2s16 p2d)
return y_nodes_min;
}
+s16 find_ground_level_clever(VoxelManipulator &vmanip, v2s16 p2d)
+{
+ v3s16 em = vmanip.m_area.getExtent();
+ s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
+ s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
+ u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
+ s16 y;
+ for(y=y_nodes_max; y>=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);
}
}
+/*
+ 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<v3s16, MapBlock*> &changed_blocks)
{
);
// 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;
}
}
- /*
- Clear all light emitted
- */
-
- core::map<v3s16, u8> unlight_from;
-
/*
Now we have a big empty area.
// 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"<<std::endl;
+ float a = highlands_level_2d(m_seed, p2d);
+ if(a > surface_y_f)
+ surface_y_f = a;
}
+
// Convert to integer
s16 surface_y = (s16)surface_y_f;
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)));
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)
/*
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<dungeons_count+bruises_count; jj++)
{
- s16 min_tunnel_diameter = 3;
+ s16 min_tunnel_diameter = 2;
s16 max_tunnel_diameter = 6;
u16 tunnel_routepoints = 15;
s16 route_y_min = 0;
//s16 route_y_max = ar.Y-1;
s16 route_y_max = -of.Y + stone_surface_max_y + max_tunnel_diameter/2;
+ if(bruise_surface == false)
+ {
+ // Don't go through surface too often
+ route_y_max -= myrand_range(0, max_tunnel_diameter);
+ }
route_y_max = rangelim(route_y_max, 0, ar.Y-1);
if(bruise_surface)
Add mud to the central chunk
*/
- s16 mud_add_amount = myrand_range(1, 5);
+ //s16 mud_add_amount = myrand_range(2, 4);
+ //s16 mud_add_amount = 0;
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);
+ // Randomize mud amount
+ s16 mud_add_amount = (s16)(3.5 + 2. * noise2d_perlin(
+ 0.5+(float)p2d.X/200, 0.5+(float)p2d.Y/200,
+ m_seed+1, 3, 0.55));
+
// Find ground level
s16 surface_y = find_ground_level(vmanip, p2d);
u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
for(s16 y=y_start; y<=y_nodes_max; y++)
{
- MapNode &n = vmanip.m_data[i];
- n.d = CONTENT_MUD;
- mudcount++;
if(mudcount >= mud_add_amount)
break;
+ MapNode &n = vmanip.m_data[i];
+ n.d = CONTENT_MUD;
+ mudcount++;
+
vmanip.m_area.add_y(em, i, 1);
}
}
*/
// 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;
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
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;
};
// 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--;
}
}
} // 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<sectorpos_base_size*MAP_BLOCKSIZE; x++)
+ for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
+ for(s16 x=0-max_spread_amount+1;
+ x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
+ x++)
+ for(s16 z=0-max_spread_amount+1;
+ z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
+ z++)
+ {
+ // Node position in 2d
+ v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
+
+ // Determine whether to have sand here
+ double sandnoise = noise2d_perlin(
+ 0.5+(float)p2d.X/500, 0.5+(float)p2d.Y/500,
+ m_seed+59420, 3, 0.50);
+
+ bool have_sand = (sandnoise > 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");
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; 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;
+ 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);
+ }
+ }
+ /*u32 tree_max = relative_area / 60;
+ //u32 count = myrand_range(0, tree_max);
for(u32 i=0; i<count; i++)
{
s16 x = myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1);
v3s16 p(x,y+1,z);
// Make a tree
make_tree(vmanip, p);
- }
+ }*/
}
}//timer1