Faster lighting at map generation time

This commit is contained in:
Perttu Ahola 2011-01-24 16:36:58 +02:00
parent 87554408ca
commit 3909e712a0
8 changed files with 605 additions and 33 deletions

@ -47,6 +47,7 @@ void set_default_settings()
g_settings.setDefault("objectdata_interval", "0.2"); g_settings.setDefault("objectdata_interval", "0.2");
g_settings.setDefault("active_object_range", "2"); g_settings.setDefault("active_object_range", "2");
g_settings.setDefault("max_simultaneous_block_sends_per_client", "1"); g_settings.setDefault("max_simultaneous_block_sends_per_client", "1");
//g_settings.setDefault("max_simultaneous_block_sends_per_client", "2");
g_settings.setDefault("max_simultaneous_block_sends_server_total", "4"); g_settings.setDefault("max_simultaneous_block_sends_server_total", "4");
g_settings.setDefault("max_block_send_distance", "6"); g_settings.setDefault("max_block_send_distance", "6");
g_settings.setDefault("max_block_generate_distance", "6"); g_settings.setDefault("max_block_generate_distance", "6");

@ -259,7 +259,8 @@ TODO: Remove HMParams
TODO: Flowing water to actually contain flow direction information TODO: Flowing water to actually contain flow direction information
TODO: Faster lighting using VoxelManipulator TODO: Remove duplicate lighting implementation from Map (leave
VoxelManipulator)
Doing now: Doing now:
---------- ----------

@ -622,10 +622,13 @@ void Map::updateLighting(enum LightBank bank,
core::map<v3s16, MapBlock*> & modified_blocks) core::map<v3s16, MapBlock*> & modified_blocks)
{ {
/*m_dout<<DTIME<<"Map::updateLighting(): " /*m_dout<<DTIME<<"Map::updateLighting(): "
<<a_blocks.getSize()<<" blocks... ";*/ <<a_blocks.size()<<" blocks."<<std::endl;*/
//TimeTaker timer("updateLighting");
// For debugging // For debugging
bool debug=false; bool debug=true;
u32 count_was = modified_blocks.size(); u32 count_was = modified_blocks.size();
core::map<v3s16, bool> light_sources; core::map<v3s16, bool> light_sources;
@ -721,8 +724,9 @@ void Map::updateLighting(enum LightBank bank,
} }
} }
#if 0
{ {
//TimeTaker timer("unspreadLight"); TimeTaker timer("unspreadLight");
unspreadLight(bank, unlight_from, light_sources, modified_blocks); unspreadLight(bank, unlight_from, light_sources, modified_blocks);
} }
@ -741,7 +745,7 @@ void Map::updateLighting(enum LightBank bank,
// - Find out why it works // - Find out why it works
{ {
//TimeTaker timer("spreadLight"); TimeTaker timer("spreadLight");
spreadLight(bank, light_sources, modified_blocks); spreadLight(bank, light_sources, modified_blocks);
} }
@ -751,6 +755,36 @@ void Map::updateLighting(enum LightBank bank,
count_was = modified_blocks.size(); count_was = modified_blocks.size();
dstream<<"spreadLight modified "<<diff<<std::endl; dstream<<"spreadLight modified "<<diff<<std::endl;
} }
#endif
{
//MapVoxelManipulator vmanip(this);
ManualMapVoxelManipulator vmanip(this);
core::map<v3s16, MapBlock*>::Iterator i;
i = a_blocks.getIterator();
for(; i.atEnd() == false; i++)
{
MapBlock *block = i.getNode()->getValue();
v3s16 p = block->getPos();
vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
}
{
//TimeTaker timer("unSpreadLight");
vmanip.unspreadLight(bank, unlight_from, light_sources);
}
{
//TimeTaker timer("spreadLight");
vmanip.spreadLight(bank, light_sources);
}
{
//TimeTaker timer("blitBack");
vmanip.blitBack(modified_blocks);
}
/*dstream<<"emerge_time="<<emerge_time<<std::endl;
emerge_time = 0;*/
}
//m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl; //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
} }
@ -2220,7 +2254,8 @@ MapBlock * ServerMap::emergeBlock(
} }
//dstream<<"Not found on disk, generating."<<std::endl; //dstream<<"Not found on disk, generating."<<std::endl;
//TimeTaker("emergeBlock()", g_irrlicht); // 0ms
//TimeTaker("emergeBlock() generate");
/* /*
Do not generate over-limit Do not generate over-limit
@ -4138,7 +4173,10 @@ void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
#endif #endif
#if 0 #if 0
void MapVoxelManipulator::emerge(VoxelArea a) /*
NOTE: This is slow
*/
void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
{ {
TimeTaker timer1("emerge", &emerge_time); TimeTaker timer1("emerge", &emerge_time);
@ -4186,6 +4224,9 @@ void MapVoxelManipulator::blitBack
//TimeTaker timer1("blitBack"); //TimeTaker timer1("blitBack");
/*dstream<<"blitBack(): m_loaded_blocks.size()="
<<m_loaded_blocks.size()<<std::endl;*/
/* /*
Initialize block cache Initialize block cache
*/ */
@ -4241,4 +4282,90 @@ void MapVoxelManipulator::blitBack
} }
} }
ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
MapVoxelManipulator(map)
{
}
ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
{
}
void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
{
// Just create the area to avoid segfaults
VoxelManipulator::emerge(a, caller_id);
/*
Just create the area to avoid segfaults
*/
/*addArea(a);
for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
for(s32 x=a.MinEdge.X; x<=a.MaxEdge.X; x++)
{
s32 i = m_area.index(x,y,z);
// Don't touch nodes that have already been loaded
if(!(m_flags[i] & VOXELFLAG_NOT_LOADED))
continue;
m_flags[i] = VOXELFLAG_INEXISTENT;
}*/
}
void ManualMapVoxelManipulator::initialEmerge(
v3s16 blockpos_min, v3s16 blockpos_max)
{
TimeTaker timer1("emerge", &emerge_time);
// Units of these are MapBlocks
v3s16 p_min = blockpos_min;
v3s16 p_max = blockpos_max;
VoxelArea block_area_nodes
(p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
addArea(block_area_nodes);
for(s32 z=p_min.Z; z<=p_max.Z; z++)
for(s32 y=p_min.Y; y<=p_max.Y; y++)
for(s32 x=p_min.X; x<=p_max.X; x++)
{
v3s16 p(x,y,z);
core::map<v3s16, bool>::Node *n;
n = m_loaded_blocks.find(p);
if(n != NULL)
continue;
bool block_data_inexistent = false;
try
{
TimeTaker timer1("emerge load", &emerge_load_time);
MapBlock *block = m_map->getBlockNoCreate(p);
if(block->isDummy())
block_data_inexistent = true;
else
block->copyTo(*this);
}
catch(InvalidPositionException &e)
{
block_data_inexistent = true;
}
if(block_data_inexistent)
{
VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
// Fill with VOXELFLAG_INEXISTENT
for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
{
s32 i = m_area.index(a.MinEdge.X,y,z);
memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
}
}
m_loaded_blocks.insert(p, true);
}
}
//END //END

@ -523,7 +523,7 @@ public:
void blitBack(core::map<v3s16, MapBlock*> & modified_blocks); void blitBack(core::map<v3s16, MapBlock*> & modified_blocks);
private: protected:
Map *m_map; Map *m_map;
/* /*
NOTE: This might be used or not NOTE: This might be used or not
@ -534,5 +534,18 @@ private:
core::map<v3s16, bool> m_loaded_blocks; core::map<v3s16, bool> m_loaded_blocks;
}; };
class ManualMapVoxelManipulator : public MapVoxelManipulator
{
public:
ManualMapVoxelManipulator(Map *map);
virtual ~ManualMapVoxelManipulator();
virtual void emerge(VoxelArea a, s32 caller_id=-1);
void initialEmerge(v3s16 blockpos_min, v3s16 blockpos_max);
protected:
};
#endif #endif

@ -412,8 +412,9 @@ void LocalPlayer::applyControl(float dtime)
} }
else else
{ {
speed += move_direction; // "Turbo button"
superspeed = true; /*speed += move_direction;
superspeed = true;*/
} }
} }

@ -168,7 +168,7 @@ void * EmergeThread::Thread()
changed_blocks, changed_blocks,
lighting_invalidated_blocks); lighting_invalidated_blocks);
#if 0 #if 1
/* /*
EXPERIMENTAL: Create a few other blocks too EXPERIMENTAL: Create a few other blocks too
*/ */
@ -184,6 +184,19 @@ void * EmergeThread::Thread()
only_from_disk, only_from_disk,
changed_blocks, changed_blocks,
lighting_invalidated_blocks); lighting_invalidated_blocks);
#if 0
map.emergeBlock(
p + v3s16(0,2,0),
only_from_disk,
changed_blocks,
lighting_invalidated_blocks);
map.emergeBlock(
p + v3s16(0,-2,0),
only_from_disk,
changed_blocks,
lighting_invalidated_blocks);
#endif
#endif #endif
} }
@ -216,23 +229,6 @@ void * EmergeThread::Thread()
dout_server<<std::endl; dout_server<<std::endl;
} }
#if 0
/*
Update water pressure
*/
m_server->UpdateBlockWaterPressure(block, modified_blocks);
for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
i.atEnd() == false; i++)
{
MapBlock *block = i.getNode()->getValue();
m_server->UpdateBlockWaterPressure(block, modified_blocks);
//v3s16 p = i.getNode()->getKey();
//m_server->UpdateBlockWaterPressure(p, modified_blocks);
}
#endif
/* /*
Collect a list of blocks that have been modified in Collect a list of blocks that have been modified in
addition to the fetched one. addition to the fetched one.
@ -249,7 +245,7 @@ void * EmergeThread::Thread()
/*dstream<<"lighting "<<lighting_invalidated_blocks.size() /*dstream<<"lighting "<<lighting_invalidated_blocks.size()
<<" blocks"<<std::endl;*/ <<" blocks"<<std::endl;*/
//TimeTaker timer("** updateLighting", g_device); //TimeTaker timer("** updateLighting");
// Update lighting without locking the environment mutex, // Update lighting without locking the environment mutex,
// add modified blocks to changed blocks // add modified blocks to changed blocks
@ -498,6 +494,7 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
|| p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE) || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
continue; continue;
// If this is true, inexistent block will be made from scratch
bool generate = d <= d_max_gen; bool generate = d <= d_max_gen;
if(haxmode) if(haxmode)
@ -513,6 +510,35 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
generate = false; generate = false;
} }
/*
If block is far away, don't generate it unless it is
near ground level
*/
if(d > 4)
{
v2s16 p2d(p.X, p.Z);
MapSector *sector = NULL;
try
{
sector = server->m_env.getMap().getSectorNoGenerate(p2d);
}
catch(InvalidPositionException &e)
{
}
if(sector != NULL)
{
// Get center ground height in nodes
f32 gh = sector->getGroundHeight(
v2s16(MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2));
// Block center y in nodes
f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
// If differs a lot, don't generate
if(fabs(gh - y) > MAP_BLOCKSIZE*2)
generate = false;
}
}
/* /*
Don't draw if not in sight Don't draw if not in sight
*/ */

@ -264,6 +264,395 @@ void VoxelManipulator::clearFlag(u8 flags)
<<volume<<" nodes"<<std::endl;*/ <<volume<<" nodes"<<std::endl;*/
} }
void VoxelManipulator::unspreadLight(enum LightBank bank, v3s16 p, u8 oldlight,
core::map<v3s16, bool> & light_sources)
{
v3s16 dirs[6] = {
v3s16(0,0,1), // back
v3s16(0,1,0), // top
v3s16(1,0,0), // right
v3s16(0,0,-1), // front
v3s16(0,-1,0), // bottom
v3s16(-1,0,0), // left
};
emerge(VoxelArea(p - v3s16(1,1,1), p + v3s16(1,1,1)));
// Loop through 6 neighbors
for(u16 i=0; i<6; i++)
{
// Get the position of the neighbor node
v3s16 n2pos = p + dirs[i];
u32 n2i = m_area.index(n2pos);
if(m_flags[n2i] & VOXELFLAG_INEXISTENT)
continue;
MapNode &n2 = m_data[n2i];
/*
If the neighbor is dimmer than what was specified
as oldlight (the light of the previous node)
*/
if(n2.getLight(bank) < oldlight)
{
/*
And the neighbor is transparent and it has some light
*/
if(n2.light_propagates() && n2.getLight(bank) != 0)
{
/*
Set light to 0 and add to queue
*/
u8 current_light = n2.getLight(bank);
n2.setLight(bank, 0);
unspreadLight(bank, n2pos, current_light, light_sources);
/*
Remove from light_sources if it is there
NOTE: This doesn't happen nearly at all
*/
/*if(light_sources.find(n2pos))
{
std::cout<<"Removed from light_sources"<<std::endl;
light_sources.remove(n2pos);
}*/
}
}
else{
light_sources.insert(n2pos, true);
}
}
}
#if 1
/*
Goes recursively through the neighbours of the node.
Alters only transparent nodes.
If the lighting of the neighbour is lower than the lighting of
the node was (before changing it to 0 at the step before), the
lighting of the neighbour is set to 0 and then the same stuff
repeats for the neighbour.
The ending nodes of the routine are stored in light_sources.
This is useful when a light is removed. In such case, this
routine can be called for the light node and then again for
light_sources to re-light the area without the removed light.
values of from_nodes are lighting values.
*/
void VoxelManipulator::unspreadLight(enum LightBank bank,
core::map<v3s16, u8> & from_nodes,
core::map<v3s16, bool> & light_sources)
{
if(from_nodes.size() == 0)
return;
core::map<v3s16, u8>::Iterator j;
j = from_nodes.getIterator();
for(; j.atEnd() == false; j++)
{
v3s16 pos = j.getNode()->getKey();
//MapNode &n = m_data[m_area.index(pos)];
u8 oldlight = j.getNode()->getValue();
unspreadLight(bank, pos, oldlight, light_sources);
}
}
#endif
#if 0
/*
Goes recursively through the neighbours of the node.
Alters only transparent nodes.
If the lighting of the neighbour is lower than the lighting of
the node was (before changing it to 0 at the step before), the
lighting of the neighbour is set to 0 and then the same stuff
repeats for the neighbour.
The ending nodes of the routine are stored in light_sources.
This is useful when a light is removed. In such case, this
routine can be called for the light node and then again for
light_sources to re-light the area without the removed light.
values of from_nodes are lighting values.
*/
void VoxelManipulator::unspreadLight(enum LightBank bank,
core::map<v3s16, u8> & from_nodes,
core::map<v3s16, bool> & light_sources)
{
v3s16 dirs[6] = {
v3s16(0,0,1), // back
v3s16(0,1,0), // top
v3s16(1,0,0), // right
v3s16(0,0,-1), // front
v3s16(0,-1,0), // bottom
v3s16(-1,0,0), // left
};
if(from_nodes.size() == 0)
return;
core::map<v3s16, u8> unlighted_nodes;
core::map<v3s16, u8>::Iterator j;
j = from_nodes.getIterator();
for(; j.atEnd() == false; j++)
{
v3s16 pos = j.getNode()->getKey();
emerge(VoxelArea(pos - v3s16(1,1,1), pos + v3s16(1,1,1)));
//MapNode &n = m_data[m_area.index(pos)];
u8 oldlight = j.getNode()->getValue();
// Loop through 6 neighbors
for(u16 i=0; i<6; i++)
{
// Get the position of the neighbor node
v3s16 n2pos = pos + dirs[i];
u32 n2i = m_area.index(n2pos);
if(m_flags[n2i] & VOXELFLAG_INEXISTENT)
continue;
MapNode &n2 = m_data[n2i];
/*
If the neighbor is dimmer than what was specified
as oldlight (the light of the previous node)
*/
if(n2.getLight(bank) < oldlight)
{
/*
And the neighbor is transparent and it has some light
*/
if(n2.light_propagates() && n2.getLight(bank) != 0)
{
/*
Set light to 0 and add to queue
*/
u8 current_light = n2.getLight(bank);
n2.setLight(bank, 0);
unlighted_nodes.insert(n2pos, current_light);
/*
Remove from light_sources if it is there
NOTE: This doesn't happen nearly at all
*/
/*if(light_sources.find(n2pos))
{
std::cout<<"Removed from light_sources"<<std::endl;
light_sources.remove(n2pos);
}*/
}
}
else{
light_sources.insert(n2pos, true);
}
}
}
/*dstream<<"unspreadLight(): Changed block "
<<blockchangecount<<" times"
<<" for "<<from_nodes.size()<<" nodes"
<<std::endl;*/
if(unlighted_nodes.size() > 0)
unspreadLight(bank, unlighted_nodes, light_sources);
}
#endif
void VoxelManipulator::spreadLight(enum LightBank bank, v3s16 p)
{
const v3s16 dirs[6] = {
v3s16(0,0,1), // back
v3s16(0,1,0), // top
v3s16(1,0,0), // right
v3s16(0,0,-1), // front
v3s16(0,-1,0), // bottom
v3s16(-1,0,0), // left
};
emerge(VoxelArea(p - v3s16(1,1,1), p + v3s16(1,1,1)));
u32 i = m_area.index(p);
if(m_flags[i] & VOXELFLAG_INEXISTENT)
return;
MapNode &n = m_data[i];
u8 oldlight = n.getLight(bank);
u8 newlight = diminish_light(oldlight);
// Loop through 6 neighbors
for(u16 i=0; i<6; i++)
{
// Get the position of the neighbor node
v3s16 n2pos = p + dirs[i];
u32 n2i = m_area.index(n2pos);
if(m_flags[n2i] & VOXELFLAG_INEXISTENT)
continue;
MapNode &n2 = m_data[n2i];
/*
If the neighbor is brighter than the current node,
add to list (it will light up this node on its turn)
*/
if(n2.getLight(bank) > undiminish_light(oldlight))
{
spreadLight(bank, n2pos);
}
/*
If the neighbor is dimmer than how much light this node
would spread on it, add to list
*/
if(n2.getLight(bank) < newlight)
{
if(n2.light_propagates())
{
n2.setLight(bank, newlight);
spreadLight(bank, n2pos);
}
}
}
}
#if 1
/*
Lights neighbors of from_nodes, collects all them and then
goes on recursively.
*/
void VoxelManipulator::spreadLight(enum LightBank bank,
core::map<v3s16, bool> & from_nodes)
{
if(from_nodes.size() == 0)
return;
core::map<v3s16, bool> lighted_nodes;
core::map<v3s16, bool>::Iterator j;
j = from_nodes.getIterator();
for(; j.atEnd() == false; j++)
{
v3s16 pos = j.getNode()->getKey();
spreadLight(bank, pos);
}
}
#endif
#if 0
/*
Lights neighbors of from_nodes, collects all them and then
goes on recursively.
*/
void VoxelManipulator::spreadLight(enum LightBank bank,
core::map<v3s16, bool> & from_nodes)
{
const v3s16 dirs[6] = {
v3s16(0,0,1), // back
v3s16(0,1,0), // top
v3s16(1,0,0), // right
v3s16(0,0,-1), // front
v3s16(0,-1,0), // bottom
v3s16(-1,0,0), // left
};
if(from_nodes.size() == 0)
return;
core::map<v3s16, bool> lighted_nodes;
core::map<v3s16, bool>::Iterator j;
j = from_nodes.getIterator();
for(; j.atEnd() == false; j++)
{
v3s16 pos = j.getNode()->getKey();
emerge(VoxelArea(pos - v3s16(1,1,1), pos + v3s16(1,1,1)));
u32 i = m_area.index(pos);
if(m_flags[i] & VOXELFLAG_INEXISTENT)
continue;
MapNode &n = m_data[i];
u8 oldlight = n.getLight(bank);
u8 newlight = diminish_light(oldlight);
// Loop through 6 neighbors
for(u16 i=0; i<6; i++)
{
// Get the position of the neighbor node
v3s16 n2pos = pos + dirs[i];
try
{
u32 n2i = m_area.index(n2pos);
if(m_flags[n2i] & VOXELFLAG_INEXISTENT)
continue;
MapNode &n2 = m_data[n2i];
/*
If the neighbor is brighter than the current node,
add to list (it will light up this node on its turn)
*/
if(n2.getLight(bank) > undiminish_light(oldlight))
{
lighted_nodes.insert(n2pos, true);
}
/*
If the neighbor is dimmer than how much light this node
would spread on it, add to list
*/
if(n2.getLight(bank) < newlight)
{
if(n2.light_propagates())
{
n2.setLight(bank, newlight);
lighted_nodes.insert(n2pos, true);
}
}
}
catch(InvalidPositionException &e)
{
continue;
}
}
}
/*dstream<<"spreadLight(): Changed block "
<<blockchangecount<<" times"
<<" for "<<from_nodes.size()<<" nodes"
<<std::endl;*/
if(lighted_nodes.size() > 0)
spreadLight(bank, lighted_nodes);
}
#endif
#if 0 #if 0
int VoxelManipulator::getWaterPressure(v3s16 p, s16 &highest_y, int recur_count) int VoxelManipulator::getWaterPressure(v3s16 p, s16 &highest_y, int recur_count)
{ {

@ -323,7 +323,11 @@ public:
emerge(p); emerge(p);
return !(m_flags[m_area.index(p)] & VOXELFLAG_INEXISTENT); return !(m_flags[m_area.index(p)] & VOXELFLAG_INEXISTENT);
}*/ }*/
// These are a bit slow and shouldn't be used internally
/*
These are a bit slow and shouldn't be used internally.
Use m_data[m_area.index(p)] instead.
*/
MapNode getNode(v3s16 p) MapNode getNode(v3s16 p)
{ {
emerge(p); emerge(p);
@ -397,6 +401,16 @@ public:
void clearFlag(u8 flag); void clearFlag(u8 flag);
void unspreadLight(enum LightBank bank, v3s16 p, u8 oldlight,
core::map<v3s16, bool> & light_sources);
void unspreadLight(enum LightBank bank,
core::map<v3s16, u8> & from_nodes,
core::map<v3s16, bool> & light_sources);
void spreadLight(enum LightBank bank, v3s16 p);
void spreadLight(enum LightBank bank,
core::map<v3s16, bool> & from_nodes);
#if 0 #if 0
// VOXELFLAG_CHECKED2s must usually be cleared before calling // VOXELFLAG_CHECKED2s must usually be cleared before calling
// -1: dead end, 0-255: pressure // -1: dead end, 0-255: pressure