A third try on terrain generation. No trees yet.

This commit is contained in:
Perttu Ahola 2011-02-28 02:01:40 +02:00
parent 5707c309c9
commit c8be58a65c
13 changed files with 998 additions and 250 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 763 B

View File

@ -511,6 +511,10 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
player->setPosition(playerpos_f);
}
// Get map seed
m_map_seed = readU64(&data[2+1+6]);
dstream<<"Client: received map seed: "<<m_map_seed<<std::endl;
// Reply to server
u32 replysize = 2;
SharedBuffer<u8> reply(replysize);

View File

@ -252,6 +252,8 @@ public:
(std::wstring)L"<"+name+L"> "+message);
}
u64 getMapSeed(){ return m_map_seed; }
private:
// Virtual methods from con::PeerHandler
@ -311,6 +313,9 @@ private:
//u32 m_daynight_ratio;
Queue<std::wstring> m_chat_queue;
// The seed returned by the server in TOCLIENT_INIT is stored here
u64 m_map_seed;
};
#endif // !SERVER

View File

@ -34,6 +34,7 @@ enum ToClientCommand
[0] u16 TOSERVER_INIT
[2] u8 deployed version
[3] v3s16 player's position + v3f(0,BS/2,0) floatToInt'd
[4] u64 map seed (new as of 2011-02-27)
*/
TOCLIENT_BLOCKDATA = 0x20, //TODO: Multiple blocks

View File

@ -253,11 +253,9 @@ Doing now (most important at the top):
* not done
=== Next
* Continue making the scripting system:
* Make updateNodeMesh for a less verbose mesh update on add/removenode
* Switch to using a safe way for the self and env pointers
* Make some global environment hooks, like node placed and general
on_step()
* Make a system for pregenerating quick information for mapblocks, so
that the client can show them as cubes before they are actually sent
or even generated.
=== Fixmes
* Check the fixmes in the list above
@ -274,6 +272,11 @@ Doing now (most important at the top):
with the ones in utility.h
=== Features
* Continue making the scripting system:
* Make updateNodeMesh for a less verbose mesh update on add/removenode
* Switch to using a safe way for the self and env pointers
* Make some global environment hooks, like node placed and general
on_step()
* Map should make the appropriate MapEditEvents
* Add a global Lua spawn handler and such
* Get rid of MapBlockObjects
@ -490,6 +493,9 @@ Inventory local_inventory;
u16 g_selected_item = 0;
bool g_show_map_plot = false;
bool g_refresh_map_plot = true;
/*
Debug streams
*/
@ -606,6 +612,15 @@ public:
if(event.KeyInput.PressedDown)
{
//dstream<<"Pressed key: "<<(char)event.KeyInput.Key<<std::endl;
if(g_show_map_plot)
{
if(event.KeyInput.Key == irr::KEY_ESCAPE
|| event.KeyInput.Key == irr::KEY_KEY_M)
{
g_show_map_plot = false;
}
return true;
}
/*
Launch menus
@ -679,6 +694,16 @@ public:
<<std::endl;
debug_stacks_print();
}
// Map plot
if(event.KeyInput.Key == irr::KEY_KEY_M)
{
dstream<<"Map plot requested"<<std::endl;
g_show_map_plot = !g_show_map_plot;
if(g_show_map_plot)
g_refresh_map_plot = true;
}
}
}
@ -1110,8 +1135,8 @@ void updateViewingRange(f32 frametime_in, Client *client)
f32 wanted_frametime_change = wanted_frametime - frametime;
//dstream<<"wanted_frametime_change="<<wanted_frametime_change<<std::endl;
// If needed frametime change is very small, just return
if(fabs(wanted_frametime_change) < wanted_frametime*0.2)
// If needed frametime change is small, just return
if(fabs(wanted_frametime_change) < wanted_frametime*0.4)
{
//dstream<<"ignoring small wanted_frametime_change"<<std::endl;
return;
@ -1239,6 +1264,93 @@ void draw_hotbar(video::IVideoDriver *driver, gui::IGUIFont *font,
}
}
video::ITexture *g_map_plot_texture = NULL;
float g_map_plot_texture_scale = 2;
void updateMapPlotTexture(v2f centerpos, video::IVideoDriver* driver,
Client *client)
{
assert(driver);
assert(client);
core::dimension2d<u32> dim(640,480);
video::IImage *img = driver->createImage(video::ECF_A8R8G8B8, dim);
assert(img);
for(u32 y=0; y<dim.Height; y++)
for(u32 x=0; x<dim.Width; x++)
{
v2f pf = v2f(x, dim.Height-y) - v2f(dim.Width, dim.Height)/2;
pf *= g_map_plot_texture_scale;
pf += centerpos;
double h = base_rock_level_2d(client->getMapSeed(), pf);
video::SColor c;
//double d1 = 50;
/*s32 ux = x - centerpos.X / g_map_plot_texture_scale;
s32 uy = y - centerpos.Y / g_map_plot_texture_scale;*/
// Screen coordinates that are based on multiples of
// 1000/g_map_plot_texture_scale and never negative
u32 ux = x + (u32)(1000/g_map_plot_texture_scale) * 10;
u32 uy = y + (u32)(1000/g_map_plot_texture_scale) * 10;
// Offset to center of image
ux -= dim.Width/2;
uy -= dim.Height/2;
if(uy % (u32)(1000/g_map_plot_texture_scale) == 0
|| ux % (u32)(1000/g_map_plot_texture_scale) == 0)
c.set(255, 255, 255, 255);
else if(uy % (u32)(100/g_map_plot_texture_scale) == 0
|| ux % (u32)(100/g_map_plot_texture_scale) == 0)
c.set(255, 160, 160, 160);
else if(h < WATER_LEVEL - 0.5) // Water
c.set(255, 50, 50, 255);
else if(h < WATER_LEVEL + 2) // Sand
c.set(255, 237, 201, 175);
#if 1
else if(h < WATER_LEVEL + 10) // Green
c.set(255, 50, 150, 50);
else if(h < WATER_LEVEL + 20) // Greenish yellow
c.set(255, 110, 185, 50);
else if(h < WATER_LEVEL + 50) // Yellow
c.set(255, 220, 220, 50);
else if(h < WATER_LEVEL + 100) // White
c.set(255, 180, 180, 180);
else
c.set(255, 255, 255, 255);
#endif
/*else if(h < WATER_LEVEL + d1)
{
h -= WATER_LEVEL;
u32 a = (u32)(h / d1 * 255);
if(a > 255)
a = 255;
c.set(255, 0, a, 0);
}*/
#if 0
else
{
h -= WATER_LEVEL;
h /= 20.0;
h = 1.0 - exp(-h);
video::SColor c1(255,200,200,50);
video::SColor c2(255,0,150,0);
c = c1.getInterpolated(c2, h);
/*u32 a = (u32)(h*255);
if(a > 255)
a = 255;
a = 255-a;
c.set(255, a, a, a);*/
}
#endif
img->setPixel(x, y, c);
}
g_map_plot_texture = driver->addTexture("map_plot", img);
img->drop();
assert(g_map_plot_texture);
}
// Chat data
struct ChatLine
{
@ -2252,6 +2364,10 @@ int main(int argc, char *argv[])
*/
g_input->step(dtime);
/*
Misc. stuff
*/
/*
Player speed control
*/
@ -3020,16 +3136,6 @@ int main(int argc, char *argv[])
driver->draw3DBox(*i, video::SColor(255,0,0,0));
}
/*
Draw crosshair
*/
driver->draw2DLine(displaycenter - core::vector2d<s32>(10,0),
displaycenter + core::vector2d<s32>(10,0),
video::SColor(255,255,255,255));
driver->draw2DLine(displaycenter - core::vector2d<s32>(0,10),
displaycenter + core::vector2d<s32>(0,10),
video::SColor(255,255,255,255));
/*
Frametime log
*/
@ -3048,6 +3154,31 @@ int main(int argc, char *argv[])
}
}
/*
Draw map plot
*/
if(g_show_map_plot && g_map_plot_texture)
{
core::dimension2d<u32> drawdim(640,480);
core::rect<s32> dest(v2s32(0,0), drawdim);
dest += v2s32(
(screensize.X-drawdim.Width)/2,
(screensize.Y-drawdim.Height)/2
);
core::rect<s32> source(v2s32(0,0), g_map_plot_texture->getSize());
driver->draw2DImage(g_map_plot_texture, dest, source);
}
/*
Draw crosshair
*/
driver->draw2DLine(displaycenter - core::vector2d<s32>(10,0),
displaycenter + core::vector2d<s32>(10,0),
video::SColor(255,255,255,255));
driver->draw2DLine(displaycenter - core::vector2d<s32>(0,10),
displaycenter + core::vector2d<s32>(0,10),
video::SColor(255,255,255,255));
} // timer
//timer10.stop();
@ -3077,9 +3208,24 @@ int main(int argc, char *argv[])
drawtime = drawtimer.stop(true);
/*
Drawing ends
End of drawing
*/
/*
Refresh map plot if player has moved considerably
*/
if(g_refresh_map_plot)
{
static v3f old_player_pos = v3f(1,1,1) * 10000000;
v3f p = client.getPlayerPosition() / BS;
if(old_player_pos.getDistanceFrom(p) > 4 * g_map_plot_texture_scale)
{
updateMapPlotTexture(v2f(p.X,p.Z), driver, &client);
old_player_pos = p;
}
g_refresh_map_plot = false;
}
static s16 lastFPS = 0;
//u16 fps = driver->getFPS();
u16 fps = (1.0/dtime_avg1);

View File

@ -1422,6 +1422,9 @@ void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
while(m_transforming_liquid.size() != 0)
{
try
{
/*
Get a queued transforming liquid node
*/
@ -1680,9 +1683,12 @@ void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
}
loopcount++;
//if(loopcount >= 100000)
if(loopcount >= initial_size * 1)
if(loopcount >= initial_size * 1 || loopcount >= 1000)
break;
}catch(InvalidPositionException &e)
{
}
}
//dstream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
}
@ -1698,8 +1704,8 @@ ServerMap::ServerMap(std::string savedir):
//m_chunksize = 64;
//m_chunksize = 16; // Too slow
m_chunksize = 8; // Takes a few seconds
//m_chunksize = 4;
//m_chunksize = 8; // Takes a few seconds
m_chunksize = 4; // Too small?
//m_chunksize = 2;
// TODO: Save to and load from a file
@ -1954,31 +1960,101 @@ double tree_amount_2d(u64 seed, v2s16 p)
#define AVERAGE_MUD_AMOUNT 4
double base_rock_level_2d(u64 seed, v2s16 p)
double get_mud_amount(u64 seed, v2f p)
{
// The base ground level
double base = (double)WATER_LEVEL - (double)AVERAGE_MUD_AMOUNT
+ 25. * noise2d_perlin(
0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
(seed>>32)+654879876, 6, 0.6);
return ((float)AVERAGE_MUD_AMOUNT + 2.5 * noise2d_perlin(
0.5+p.X/200, 0.5+p.Y/200,
seed+1, 5, 0.65));
}
// -1->0, 0->1, 1->0
double contour(double v)
{
v = fabs(v);
if(v >= 1.0)
return 0.0;
return (1.0-v);
}
// -1->0, -r->1, 0->1, r->1, 1->0
double contour_flat_top(double v, double r)
{
v = fabs(v);
if(v >= 1.0)
return 0.0;
double rmax = 0.999;
if(r >= rmax)
r = rmax;
if(v <= r)
return 1.0;
v -= r;
return ((1.0-r)-v) / (1.0-r);
//return easeCurve(((1.0-r)-v) / (1.0-r));
}
double base_rock_level_2d(u64 seed, v2f p)
{
// The ground level (return value)
double h = WATER_LEVEL;
// Raises from 0 when parameter is -1...1
/*double m2 = contour_flat_top(-0.8 + 2.0 * noise2d_perlin(
0.0+(float)p.X/1500., 0.0+(float)p.Y/1500.,
(seed>>32)+34758, 5, 0.55), 0.10);*/
/*double m2 = 1.0;
if(m2 > 0.0001)
{
// HUGE mountains
double m1 = 200.0 + 300.0 * noise2d_perlin(
0.0+(float)p.X/1000., 0.0+(float)p.Y/1000.,
(seed>>32)+98525, 8, 0.5);
h += m1 * m2;
//h += 30 * m2;
}*/
// Huge mountains
double m3 = 150.0 - 800.0 * noise2d_perlin_abs(
0.5+(float)p.X/2000., 0.5+(float)p.Y/2000.,
(seed>>32)+985251, 9, 0.5);
if(m3 > h)
h = m3;
/*double tm2 = contour_flat_top(-1.0 + 3.0 * noise2d_perlin(
0.0+(float)p.X/300., 0.0+(float)p.Y/300.,
(seed>>32)+78593, 5, 0.55), 0.15);
h += 30 * tm2;*/
/*// A bit hillier one
double base2 = WATER_LEVEL - 4.0 + 40. * noise2d_perlin(
0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
(seed>>27)+90340, 6, 0.69);
if(base2 > base)
base = base2;*/
#if 1
// Higher ground level
double higher = (double)WATER_LEVEL + 25. + 45. * noise2d_perlin(
{
double a1 = 30 - 100. * noise2d_perlin_abs(
0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
seed+85039, 5, 0.69);
//higher = 30; // For debugging
seed+850342, 5, 0.63);
double d = 15;
if(a1 > d)
a1 = d + sqrt(a1-d);
if(a1 > h)
h = a1;
}
#endif
// Limit higher to at least base
if(higher < base)
higher = base;
#if 1
double base = 35. * noise2d_perlin(
0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
(seed>>32)+653876, 7, 0.55);
#else
double base = 0;
#endif
#if 1
double higher = 50. * noise2d_perlin(
0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
seed+39292, 6, 0.63);
/*double higher = 50. * noise2d_perlin_abs(
0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
seed+85039, 5, 0.63);*/
if(higher > base)
{
// Steepness factor of cliffs
double b = 1.0 + 1.0 * noise2d_perlin(
0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
@ -1991,26 +2067,183 @@ double base_rock_level_2d(u64 seed, v2s16 p)
//double b = 20;
// Offset to more low
double a_off = -0.2;
//double a_off = -0.30;
double a_off = -0.00;
// High/low selector
/*double a = 0.5 + b * (a_off + noise2d_perlin(
0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
seed-359, 6, 0.7));*/
double a = (double)0.5 + b * (a_off + noise2d_perlin(
0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
seed-359, 5, 0.60));
seed-359, 5, 0.6));
// Limit
a = rangelim(a, 0.0, 1.0);
//a = easeCurve(a);
//dstream<<"a="<<a<<std::endl;
double h = base*(1.0-a) + higher*a;
h += base*(1.0-a) + higher*a;
}
else
{
h += base;
}
#else
double h = base;
h += base;
#endif
return h;
}
double base_rock_level_2d(u64 seed, v2s16 p)
{
return base_rock_level_2d(seed, v2f((float)p.X, (float)p.Y));
}
v2f base_ground_turbulence(u64 seed, v3f p)
{
double f = 12;
double v1 = f * noise3d_perlin(
0.5+p.X/200,
0.5+p.Y/200,
0.5+p.Z/200,
seed+4045, 5, 0.7);
double v2 = f * noise3d_perlin(
0.5+p.X/200,
0.5+p.Y/200,
0.5+p.Z/200,
seed+9495, 5, 0.7);
return v2f(v1, v2);
}
bool is_carved(u64 seed, v3f p)
{
#if 1
double v1 = noise3d_perlin_abs(
0.5+p.X/200,
0.5+p.Y/200,
0.5+p.Z/200,
seed+657890854, 5, 0.7);
if(v1 > 1.5)
return true;
#endif
#if 0
double v2 = noise3d_perlin_abs(
0.5+p.X/200,
0.5+p.Y/200,
0.5+p.Z/200,
seed+657890854, 5, 0.7);
#if 0
double v3 = noise3d_perlin_abs(
0.5+p.X/200,
0.5+p.Y/200,
0.5+p.Z/200,
seed+657890854, 5, 0.7);
#else
double v3 = 1.0;
#endif
double v23 = v2*v3;
if(v23 > 0.7)
return true;
#endif
double f = 10.0;
double y_div = 1.5;
double v4 = contour(f*noise3d_perlin(
0.5+p.X/200,
0.5+p.Y/200*y_div,
0.5+p.Z/200,
seed+87592, 5, 0.7));
// Tilted 90 degrees
double v5 = contour(f*noise3d_perlin(
0.5+p.X/200,
0.5+p.Z/200,
0.5+p.Y/200*y_div,
seed+98594, 5, 0.7));
double v45 = v4*v5;
if(v45 > 2.5/f)
return true;
return false;
}
/*
if depth_guess!=NULL, it is set to a guessed value of how deep
underground the position is.
*/
bool is_base_ground(u64 seed, v3f p, double *depth_guess=NULL)
{
#if 0
// This is used for testing the output of the cave function
{
if(depth_guess)
*depth_guess = 10;
if(p.Y > 50)
return false;
return is_carved(seed, p);
}
#endif
v2f t = base_ground_turbulence(seed, p);
double surface_y_f = base_rock_level_2d(seed, v2f(p.X+t.X, p.Z+t.Y));
/*if(depth_guess)
*depth_guess = surface_y_f - p.Y;*/
if(depth_guess)
{
// Find highest surface near current
v3f dirs[4] = {
v3f(1,-1,0),
v3f(-1,-1,0),
v3f(0,-1,1),
v3f(0,-1,-1)
};
double s2 = surface_y_f;
for(u32 i=0; i<4; i++)
{
v3f dir = dirs[i];
v2f l = v2f(p.X+t.X+dir.X, p.Z+t.Y+dir.Z);
double s = base_rock_level_2d(seed, l);
if(s > s2)
s2 = s;
}
*depth_guess = s2 - p.Y;
}
/*if(depth_guess)
{
// Check a bit lower also, take highest surface
v2f t2 = base_ground_turbulence(seed, p + v3f(0,-2,0));
double s2 = base_rock_level_2d(seed, v2f(p.X+t2.X, p.Z+t2.Y));
if(s2 > surface_y_f)
*depth_guess = s2 - p.Y;
else
*depth_guess = surface_y_f - p.Y;
}*/
/*if(depth_guess)
{
// Guess surface point
v3f p2(p.X, surface_y_f, p.Z);
v2f t2 = base_ground_turbulence
double u1 =
double s1 = base_rock_level_2d(seed, v2f(p.X+v1,p.Z+v2));
}*/
bool is_ground = (p.Y <= surface_y_f);
if(is_carved(seed, p))
is_ground = false;
return is_ground;
}
#define VMANIP_FLAG_DUNGEON VOXELFLAG_CHECKED1
/*
@ -2023,6 +2256,11 @@ MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
{
DSTACK(__FUNCTION_NAME);
// Shall be not used now
//assert(0);
#if 0
/*
Don't generate if already fully generated
*/
@ -2163,7 +2401,8 @@ MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
{
// 22ms @cs=8
//TimeTaker timer1("ground level");
TimeTaker timer1("ground level");
dstream<<"Generating base ground..."<<std::endl;
for(s16 x=0; x<sectorpos_bigbase_size*MAP_BLOCKSIZE; x++)
for(s16 z=0; z<sectorpos_bigbase_size*MAP_BLOCKSIZE; z++)
@ -2172,7 +2411,79 @@ MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
v2s16 p2d = sectorpos_bigbase*MAP_BLOCKSIZE + v2s16(x,z);
/*
Skip of already generated
Skip if already generated
*/
{
v3s16 p(p2d.X, y_nodes_min, p2d.Y);
if(vmanip.m_data[vmanip.m_area.index(p)].d != CONTENT_AIR)
continue;
}
v2f p2df(p2d.X, p2d.Y);
{
// Use fast index incrementing
v3s16 em = vmanip.m_area.getExtent();
s16 min = y_nodes_min;
s16 max = y_nodes_max;
/*s16 min = -10;
s16 max = 20;*/
//float surface_y_f = base_rock_level_2d(m_seed, p2df);
u32 i = vmanip.m_area.index(v3s16(p2d.X, min, p2d.Y));
for(s16 y=min; y<=max; y++)
{
#if 1
bool is = is_base_ground(m_seed, v3f(p2df.X,y,p2df.Y));
if(is)
vmanip.m_data[i].d = CONTENT_STONE;
else
vmanip.m_data[i].d = CONTENT_AIR;
#endif
#if 0
double v = noise3d_perlin(
0.5+(float)p2d.X/200,
0.5+(float)y/200,
0.5+(float)p2d.Y/200,
m_seed+293, 6, 0.55);
if(v > 0.0)
vmanip.m_data[i].d = CONTENT_STONE;
else
vmanip.m_data[i].d = CONTENT_AIR;
#endif
#if 0
/*double v1 = 5 * noise3d_perlin(
0.5+(float)p2df.X/200,
0.5+(float)y/200,
0.5+(float)p2df.Y/200,
m_seed+293, 6, 0.55);
double v2 = 5 * noise3d_perlin(
0.5+(float)p2df.X/200,
0.5+(float)y/200,
0.5+(float)p2df.Y/200,
m_seed+293, 6, 0.55);*/
double v1 = 0;
double v2 = 0;
float surface_y_f = base_rock_level_2d(m_seed, p2df+v2f(v1,v2));
if(y <= surface_y_f)
vmanip.m_data[i].d = CONTENT_STONE;
else
vmanip.m_data[i].d = CONTENT_AIR;
#endif
vmanip.m_area.add_y(em, i, 1);
}
}
#if 0
// Node position
v2s16 p2d = sectorpos_bigbase*MAP_BLOCKSIZE + v2s16(x,z);
/*
Skip if already generated
*/
{
v3s16 p(p2d.X, y_nodes_min, p2d.Y);
@ -2214,6 +2525,7 @@ MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
vmanip.m_area.add_y(em, i, 1);
}
}
#endif
}
}//timer1
@ -2235,8 +2547,8 @@ MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
/*
Loop this part, it will make stuff look older and newer nicely
*/
//for(u32 i_age=0; i_age<1; i_age++)
for(u32 i_age=0; i_age<2; i_age++)
u32 age_count = 2;
for(u32 i_age=0; i_age<age_count; i_age++)
{ // Aging loop
{
@ -2732,9 +3044,7 @@ MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
// Randomize mud amount
s16 mud_add_amount = (s16)(2.5 + 2.0 * noise2d_perlin(
0.5+(float)p2d.X/200, 0.5+(float)p2d.Y/200,
m_seed+1, 3, 0.55));
s16 mud_add_amount = get_mud_amount(m_seed, v2f(p2d.X,p2d.Y))/age_count;
// Find ground level
s16 surface_y = find_ground_level_clever(vmanip, p2d);
@ -2777,7 +3087,7 @@ MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
}//timer1
{
// 340ms @cs=8
TimeTaker timer1("flow mud");
//TimeTaker timer1("flow mud");
/*
Flow mud away from steep edges
@ -3447,57 +3757,6 @@ MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
}
#endif
#if 0
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;
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);
// This doesn't take much time
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);
}
}
}
#endif
}//timer1
// Spread light around
@ -3533,6 +3792,7 @@ MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
}
}
#endif
/*
Create chunk metadata
@ -3581,6 +3841,9 @@ MapChunk* ServerMap::generateChunk(v2s16 chunkpos1,
<<"("<<chunkpos1.X<<","<<chunkpos1.Y<<")"
<<std::endl;
// Shall be not used now
//assert(0);
/*for(s16 x=-1; x<=1; x++)
for(s16 y=-1; y<=1; y++)*/
for(s16 x=-0; x<=0; x++)
@ -3709,13 +3972,6 @@ MapSector * ServerMap::emergeSector(v2s16 p2d,
<<p2d.X<<","<<p2d.Y<<" and chunk is already generated. "
<<std::endl;
#if 0
dstream<<"WARNING: Creating an empty sector."<<std::endl;
return createSector(p2d);
#endif
#if 1
dstream<<"WARNING: Forcing regeneration of chunk."<<std::endl;
@ -3731,7 +3987,14 @@ MapSector * ServerMap::emergeSector(v2s16 p2d,
dstream<<"ERROR: Could not get sector from anywhere."<<std::endl;
assert(0);
//assert(0);
#endif
#if 1
dstream<<"WARNING: Creating an empty sector."<<std::endl;
return createSector(p2d);
#endif
/*
@ -3765,6 +4028,7 @@ MapBlock * ServerMap::generateBlock(
v2s16 p2d(p.X, p.Z);
s16 block_y = p.Y;
v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
v3s16 p_nodes = p * MAP_BLOCKSIZE;
/*
Do not generate over-limit
@ -3798,6 +4062,107 @@ MapBlock * ServerMap::generateBlock(
s32 lowest_ground_y = 32767;
s32 highest_ground_y = -32768;
enum{
BT_GROUND,
BT_SURFACE,
BT_SKY
} block_type = BT_SURFACE;
{// ground_timer (0ms or ~100ms)
//TimeTaker ground_timer("Ground generation");
/*
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.
*/
{
/*
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;
// Handled as surface because of caves
block_type = BT_SURFACE;
}
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(block_type == BT_SURFACE)
{
/*
Generate ground precisely
*/
for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
{
@ -3805,18 +4170,112 @@ MapBlock * ServerMap::generateBlock(
//s16 surface_y = 0;
s16 surface_y = base_rock_level_2d(m_seed, p2d_nodes+v2s16(x0,z0))
/*s16 surface_y = base_rock_level_2d(m_seed, p2d_nodes+v2s16(x0,z0))
+ AVERAGE_MUD_AMOUNT;
if(surface_y < lowest_ground_y)
lowest_ground_y = surface_y;
if(surface_y > highest_ground_y)
highest_ground_y = surface_y;
highest_ground_y = surface_y;*/
s32 surface_depth = AVERAGE_MUD_AMOUNT;
v2s16 real_p2d = v2s16(x0,z0) + p2d*MAP_BLOCKSIZE;
//s32 surface_depth = AVERAGE_MUD_AMOUNT;
s16 surface_depth = get_mud_amount(m_seed, v2f(real_p2d.X,real_p2d.Y));
for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
{
#if 1
s16 real_y = block_y * MAP_BLOCKSIZE + y0;
v3s16 real_pos = v3s16(x0,y0,z0) + p_nodes;
MapNode n;
/*
Calculate lighting
NOTE: If there are some man-made structures above the
newly created block, they won't be taken into account.
*/
/*if(real_y > surface_y)
n.setLight(LIGHTBANK_DAY, LIGHT_SUN);*/
/*
Calculate material
*/
double depth_guess;
bool is_ground = is_base_ground(m_seed,
intToFloat(real_pos, 1), &depth_guess);
// Estimate the surface height
float surface_y_f = (float)real_y + depth_guess;
s16 surface_y = real_y + depth_guess;
// Get 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;
// 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)
{
n.d = water_material;
n.setLight(LIGHTBANK_DAY,
diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
/*
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;
}
// Else it's ground or dungeons (air)
else
{
// If it's surface_depth under ground, it's stone
if((float)real_y <= surface_y_f - surface_depth - 0.75)
{
n.d = CONTENT_STONE;
}
else if(surface_y_f <= WATER_LEVEL + 2.0)
{
n.d = CONTENT_SAND;
}
else
{
/*// It is mud if it is under the first ground
// level or under water
if(real_y < WATER_LEVEL || real_y <= surface_y - 1)
{
n.d = CONTENT_MUD;
}
else
{
n.d = CONTENT_GRASS;
}*/
n.d = CONTENT_MUD;
/*// If under water level, it's mud
if(real_y < WATER_LEVEL)
n.d = CONTENT_MUD;
// Only the topmost node is grass
else if(real_y <= surface_y - 1)
n.d = CONTENT_MUD;
else
n.d = CONTENT_GRASS;*/
}
}
block->setNode(v3s16(x0,y0,z0), n);
#endif
#if 0
s16 real_y = block_y * MAP_BLOCKSIZE + y0;
MapNode n;
/*
@ -3887,8 +4346,38 @@ MapBlock * ServerMap::generateBlock(
}
block->setNode(v3s16(x0,y0,z0), n);
#endif
}
}
}// BT_SURFACE
else // BT_GROUND, 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 z0=0; z0<MAP_BLOCKSIZE; z0++)
for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
{
//MapNode n = block->getNode(v3s16(x0,y0,z0));
block->setNode(v3s16(x0,y0,z0), n_fill);
}
}
}// ground_timer
/*
Calculate some helper variables
@ -3945,7 +4434,7 @@ MapBlock * ServerMap::generateBlock(
}
// Fill table
#if 1
#if 0
{
/*
Initialize orp and ors. Try to find if some neighboring
@ -4087,7 +4576,8 @@ continue_generating:
// Partly underground = cave
else if(!completely_underground)
{
do_generate_dungeons = (rand() % 100 <= (s32)(caves_amount*100));
//do_generate_dungeons = (rand() % 100 <= (s32)(caves_amount*100));
do_generate_dungeons = false;
}
// Found existing dungeon underground
else if(found_existing && completely_underground)
@ -4239,8 +4729,8 @@ continue_generating:
/*
Add coal
*/
u16 coal_amount = 30;
u16 coal_rareness = 60 / coal_amount;
u16 coal_amount = 60;
u16 coal_rareness = 120 / coal_amount;
if(coal_rareness == 0)
coal_rareness = 1;
if(myrand()%coal_rareness == 0)
@ -4271,9 +4761,8 @@ continue_generating:
/*
Add iron
*/
//TODO: change to iron_amount or whatever
u16 iron_amount = 15;
u16 iron_rareness = 60 / iron_amount;
u16 iron_amount = 40;
u16 iron_rareness = 80 / iron_amount;
if(iron_rareness == 0)
iron_rareness = 1;
if(myrand()%iron_rareness == 0)
@ -4330,8 +4819,17 @@ continue_generating:
*/
sector->insertBlock(block);
// Lighting is invalid after generation.
// Lighting is invalid after generation for surface blocks
if(block_type == BT_SURFACE)
{
block->setLightingExpired(true);
lighting_invalidated_blocks.insert(p, block);
}
// Lighting is not invalid for other blocks
else
{
block->setLightingExpired(false);
}
#if 0
/*
@ -4537,6 +5035,8 @@ MapBlock * ServerMap::emergeBlock(
{
block = generateBlock(p, block, sector, changed_blocks,
lighting_invalidated_blocks);
lighting_expired = block->getLightingExpired();
}
if(lighting_expired)
@ -4548,6 +5048,7 @@ MapBlock * ServerMap::emergeBlock(
Initially update sunlight
*/
if(lighting_expired)
{
core::map<v3s16, bool> light_sources;
bool black_air_left = false;

View File

@ -40,6 +40,15 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "voxel.h"
#include "mapchunk.h"
/*
Some exposed functions
*/
double base_rock_level_2d(u64 seed, v2f p);
/*
*/
#define MAPTYPE_BASE 0
#define MAPTYPE_SERVER 1
#define MAPTYPE_CLIENT 2
@ -531,6 +540,8 @@ public:
bool isSavingEnabled(){ return m_map_saving_enabled; }
u64 getSeed(){ return m_seed; }
private:
// Seed used for all kinds of randomness
u64 m_seed;

View File

@ -34,6 +34,7 @@ MapBlock::MapBlock(NodeContainer *parent, v3s16 pos, bool dummy):
m_pos(pos),
changed(true),
is_underground(false),
m_lighting_expired(true),
m_day_night_differs(false),
m_objects(this)
{

View File

@ -92,8 +92,7 @@ double noise3d(int x, int y, int z, int seed)
return 1.0 - (double)n/1073741824;
}
#if 0
// This is too slow
#if 1
double noise2d_gradient(double x, double y, int seed)
{
// Calculate the integer coordinates
@ -118,7 +117,7 @@ double noise2d_gradient(double x, double y, int seed)
}
#endif
#if 1
#if 0
double noise2d_gradient(double x, double y, int seed)
{
// Calculate the integer coordinates
@ -175,6 +174,21 @@ double noise2d_perlin(double x, double y, int seed,
return a;
}
double noise2d_perlin_abs(double x, double y, int seed,
int octaves, double persistence)
{
double a = 0;
double f = 1.0;
double g = 1.0;
for(int i=0; i<octaves; i++)
{
a += g * fabs(noise2d_gradient(x*f, y*f, seed+i));
f *= 2.0;
g *= persistence;
}
return a;
}
double noise3d_perlin(double x, double y, double z, int seed,
int octaves, double persistence)
{
@ -190,3 +204,18 @@ double noise3d_perlin(double x, double y, double z, int seed,
return a;
}
double noise3d_perlin_abs(double x, double y, double z, int seed,
int octaves, double persistence)
{
double a = 0;
double f = 1.0;
double g = 1.0;
for(int i=0; i<octaves; i++)
{
a += g * fabs(noise3d_gradient(x*f, y*f, z*f, seed+i));
f *= 2.0;
g *= persistence;
}
return a;
}

View File

@ -32,8 +32,14 @@ double noise3d_gradient(double x, double y, double z, int seed);
double noise2d_perlin(double x, double y, int seed,
int octaves, double persistence);
double noise2d_perlin_abs(double x, double y, int seed,
int octaves, double persistence);
double noise3d_perlin(double x, double y, double z, int seed,
int octaves, double persistence);
double noise3d_perlin_abs(double x, double y, double z, int seed,
int octaves, double persistence);
#endif

View File

@ -167,6 +167,26 @@ void * EmergeThread::Thread()
only_from_disk,
changed_blocks,
lighting_invalidated_blocks);
/*
While we're at it, generate some other blocks too
*/
try
{
map.emergeBlock(
p+v3s16(0,1,0),
only_from_disk,
changed_blocks,
lighting_invalidated_blocks);
map.emergeBlock(
p+v3s16(0,-1,0),
only_from_disk,
changed_blocks,
lighting_invalidated_blocks);
}
catch(InvalidPositionException &e)
{
}
}
// If it is a dummy, block was not found on disk
@ -209,22 +229,24 @@ void * EmergeThread::Thread()
addition to the fetched one.
*/
// Add all the "changed blocks" to modified_blocks
if(lighting_invalidated_blocks.size() > 0)
dstream<<"lighting "<<lighting_invalidated_blocks.size()
<<" blocks"<<std::endl;
// 50-100ms for single block generation
//TimeTaker timer("** EmergeThread updateLighting");
// Update lighting without locking the environment mutex,
// add modified blocks to changed blocks
map.updateLighting(lighting_invalidated_blocks, modified_blocks);
// Add all from changed_blocks to modified_blocks
for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
i.atEnd() == false; i++)
{
MapBlock *block = i.getNode()->getValue();
modified_blocks.insert(block->getPos(), block);
}
/*dstream<<"lighting "<<lighting_invalidated_blocks.size()
<<" blocks"<<std::endl;*/
//TimeTaker timer("** updateLighting");
// Update lighting without locking the environment mutex,
// add modified blocks to changed blocks
map.updateLighting(lighting_invalidated_blocks, modified_blocks);
}
// If we got no block, there should be no invalidated blocks
else
@ -551,7 +573,8 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
{
//TODO: Get value from somewhere
// Allow only one block in emerge queue
if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
//if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
if(server->m_emerge_queue.peerItemCount(peer_id) < 2)
{
//dstream<<"Adding block to emerge queue"<<std::endl;
@ -1681,10 +1704,11 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
// Now answer with a TOCLIENT_INIT
SharedBuffer<u8> reply(2+1+6);
SharedBuffer<u8> reply(2+1+6+8);
writeU16(&reply[0], TOCLIENT_INIT);
writeU8(&reply[2], deployed);
writeV3S16(&reply[3], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
writeU64(&reply[2+1+6], m_env.getServerMap().getSeed());
// Send as reliable
m_con.Send(peer_id, 0, reply, true);

View File

@ -486,7 +486,7 @@ void TextureSource::buildMainAtlas()
sourcelist.push_back("sand.png^mineral_iron.png");
// Padding to disallow texture bleeding
s32 padding = 8;
s32 padding = 16;
/*
First pass: generate almost everything

View File

@ -39,6 +39,18 @@ extern const v3s16 g_26dirs[26];
// 26th is (0,0,0)
extern const v3s16 g_27dirs[27];
inline void writeU64(u8 *data, u64 i)
{
data[0] = ((i>>56)&0xff);
data[1] = ((i>>48)&0xff);
data[2] = ((i>>40)&0xff);
data[3] = ((i>>32)&0xff);
data[4] = ((i>>24)&0xff);
data[5] = ((i>>16)&0xff);
data[6] = ((i>> 8)&0xff);
data[7] = ((i>> 0)&0xff);
}
inline void writeU32(u8 *data, u32 i)
{
data[0] = ((i>>24)&0xff);
@ -58,6 +70,14 @@ inline void writeU8(u8 *data, u8 i)
data[0] = ((i>> 0)&0xff);
}
inline u64 readU64(u8 *data)
{
return ((u64)data[0]<<56) | ((u64)data[1]<<48)
| ((u64)data[2]<<40) | ((u64)data[3]<<32)
| ((u64)data[4]<<24) | ((u64)data[5]<<16)
| ((u64)data[6]<<8) | ((u64)data[7]<<0);
}
inline u32 readU32(u8 *data)
{
return (data[0]<<24) | (data[1]<<16) | (data[2]<<8) | (data[3]<<0);