commit before some radicallish changes to water behavior

This commit is contained in:
Perttu Ahola 2010-12-11 18:11:03 +02:00
parent 5e0c284f3a
commit 2a0d1a059e
15 changed files with 1436 additions and 182 deletions

@ -13,9 +13,9 @@ JTHREADPATH = ../jthread/jthread-1.2.1
CPPFLAGS = -I$(IRRLICHTPATH)/include -I/usr/X11R6/include -I$(JTHREADPATH)/src CPPFLAGS = -I$(IRRLICHTPATH)/include -I/usr/X11R6/include -I$(JTHREADPATH)/src
#CXXFLAGS = -O2 -ffast-math -Wall -fomit-frame-pointer -pipe #CXXFLAGS = -O2 -ffast-math -Wall -fomit-frame-pointer -pipe
#CXXFLAGS = -O2 -ffast-math -Wall -g -pipe CXXFLAGS = -O2 -ffast-math -Wall -g -pipe
#CXXFLAGS = -O1 -ffast-math -Wall -g #CXXFLAGS = -O1 -ffast-math -Wall -g
CXXFLAGS = -Wall -g -O0 #CXXFLAGS = -Wall -g -O0
#CXXFLAGS = -O3 -ffast-math -Wall #CXXFLAGS = -O3 -ffast-math -Wall
#CXXFLAGS = -O3 -ffast-math -Wall -g #CXXFLAGS = -O3 -ffast-math -Wall -g

@ -116,6 +116,14 @@ public:
{} {}
}; };
class ProcessingLimitException : public BaseException
{
public:
ProcessingLimitException(const char *s):
BaseException(s)
{}
};
/* /*
Some "old-style" interrupts: Some "old-style" interrupts:
*/ */

@ -173,9 +173,12 @@ TODO: Remove LazyMeshUpdater. It is not used as supposed.
FIXME: Rats somehow go underground sometimes (you can see it in water) FIXME: Rats somehow go underground sometimes (you can see it in water)
- Does their position get saved to a border value or something? - Does their position get saved to a border value or something?
TODO: MovingObject::move and Player::move are basically the same. SUGG: MovingObject::move and Player::move are basically the same.
combine them. combine them.
TODO: Transfer sign texts as metadata of block and not as data of
object
Doing now: Doing now:
====================================================================== ======================================================================

@ -47,10 +47,10 @@ MapBlockPointerCache::~MapBlockPointerCache()
{ {
m_map->m_blockcachelock.cacheRemoved(); m_map->m_blockcachelock.cacheRemoved();
dstream<<"MapBlockPointerCache:" /*dstream<<"MapBlockPointerCache:"
<<" from_cache_count="<<m_from_cache_count <<" from_cache_count="<<m_from_cache_count
<<" from_map_count="<<m_from_map_count <<" from_map_count="<<m_from_map_count
<<std::endl; <<std::endl;*/
} }
MapBlock * MapBlockPointerCache::getBlockNoCreate(v3s16 p) MapBlock * MapBlockPointerCache::getBlockNoCreate(v3s16 p)
@ -1050,6 +1050,8 @@ void Map::removeNodeAndUpdate(v3s16 p,
// Node will be replaced with this // Node will be replaced with this
u8 replace_material = MATERIAL_AIR; u8 replace_material = MATERIAL_AIR;
// NOTE: Water is now managed elsewhere
#if 0
{ {
/* /*
Find out with what material the node will be replaced. Find out with what material the node will be replaced.
@ -1107,6 +1109,8 @@ void Map::removeNodeAndUpdate(v3s16 p,
} }
} }
#endif
/* /*
If there is a node at top and it doesn't have sunlight, If there is a node at top and it doesn't have sunlight,
there will be no sunlight going down. there will be no sunlight going down.
@ -3143,3 +3147,144 @@ void ClientMap::PrintInfo(std::ostream &out)
} }
/*
MapVoxelManipulator
*/
MapVoxelManipulator::MapVoxelManipulator(Map *map)
{
m_map = map;
}
MapVoxelManipulator::~MapVoxelManipulator()
{
dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
<<std::endl;
}
void MapVoxelManipulator::emerge(VoxelArea a)
{
TimeTaker timer1("emerge", g_device, &emerge_time);
// Units of these are MapBlocks
v3s16 p_min = getNodeBlockPos(a.MinEdge);
v3s16 p_max = getNodeBlockPos(a.MaxEdge);
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", g_device, &emerge_load_time);
dstream<<"Loading block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
<<std::endl;
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);
}
//dstream<<"emerge done"<<std::endl;
}
/*
TODO: Add an option to only update eg. water and air nodes.
This will make it interfere less with important stuff if
run on background.
*/
void MapVoxelManipulator::blitBack
(core::map<v3s16, MapBlock*> & modified_blocks)
{
TimeTaker timer1("blitBack", g_device);
/*
Initialize block cache
*/
v3s16 blockpos_last;
MapBlock *block = NULL;
bool block_checked_in_modified = false;
for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
{
v3s16 p(x,y,z);
u8 f = m_flags[m_area.index(p)];
if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
continue;
MapNode &n = m_data[m_area.index(p)];
v3s16 blockpos = getNodeBlockPos(p);
try
{
// Get block
if(block == NULL || blockpos != blockpos_last){
block = m_map->getBlockNoCreate(blockpos);
blockpos_last = blockpos;
block_checked_in_modified = false;
}
// Calculate relative position in block
v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
// Don't continue if nothing has changed here
if(block->getNode(relpos) == n)
continue;
//m_map->setNode(m_area.MinEdge + p, n);
block->setNode(relpos, n);
/*
Make sure block is in modified_blocks
*/
if(block_checked_in_modified == false)
{
modified_blocks[blockpos] = block;
block_checked_in_modified = true;
}
}
catch(InvalidPositionException &e)
{
}
}
}
//END

@ -40,6 +40,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "mapblock.h" #include "mapblock.h"
#include "mapsector.h" #include "mapsector.h"
#include "constants.h" #include "constants.h"
#include "voxel.h"
class Map; class Map;
@ -49,6 +50,7 @@ class Map;
NOTE: This doesn't really make anything more efficient NOTE: This doesn't really make anything more efficient
NOTE: Use VoxelManipulator, if possible NOTE: Use VoxelManipulator, if possible
TODO: Get rid of this? TODO: Get rid of this?
NOTE: CONFIRMED: THIS CACHE DOESN'T MAKE ANYTHING ANY FASTER
*/ */
class MapBlockPointerCache : public NodeContainer class MapBlockPointerCache : public NodeContainer
{ {
@ -121,7 +123,7 @@ public:
void cacheCreated() void cacheCreated()
{ {
dstream<<"cacheCreated() begin"<<std::endl; //dstream<<"cacheCreated() begin"<<std::endl;
JMutexAutoLock waitcachelock(m_waitcache_mutex); JMutexAutoLock waitcachelock(m_waitcache_mutex);
JMutexAutoLock countlock(m_count_mutex); JMutexAutoLock countlock(m_count_mutex);
@ -131,12 +133,12 @@ public:
m_count++; m_count++;
dstream<<"cacheCreated() end"<<std::endl; //dstream<<"cacheCreated() end"<<std::endl;
} }
void cacheRemoved() void cacheRemoved()
{ {
dstream<<"cacheRemoved() begin"<<std::endl; //dstream<<"cacheRemoved() begin"<<std::endl;
JMutexAutoLock countlock(m_count_mutex); JMutexAutoLock countlock(m_count_mutex);
assert(m_count > 0); assert(m_count > 0);
@ -147,7 +149,7 @@ public:
if(m_count == 0) if(m_count == 0)
m_cache_mutex.Unlock(); m_cache_mutex.Unlock();
dstream<<"cacheRemoved() end"<<std::endl; //dstream<<"cacheRemoved() end"<<std::endl;
} }
/* /*
@ -589,5 +591,29 @@ private:
JMutex mesh_mutex; JMutex mesh_mutex;
}; };
class MapVoxelManipulator : public VoxelManipulator
{
public:
MapVoxelManipulator(Map *map);
virtual ~MapVoxelManipulator();
virtual void clear()
{
VoxelManipulator::clear();
m_loaded_blocks.clear();
}
virtual void emerge(VoxelArea a);
void blitBack(core::map<v3s16, MapBlock*> & modified_blocks);
private:
Map *m_map;
// bool is dummy value
// SUGG: How 'bout an another VoxelManipulator for storing the
// information about which block is loaded?
core::map<v3s16, bool> m_loaded_blocks;
};
#endif #endif

@ -696,6 +696,15 @@ bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources)
return block_below_is_valid; return block_below_is_valid;
} }
void MapBlock::copyTo(VoxelManipulator &dst)
{
v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
dst.copyFrom(data, data_area, v3s16(0,0,0),
getPosRelative(), data_size);
}
/* /*
Serialization Serialization
*/ */
@ -755,6 +764,17 @@ void MapBlock::serialize(std::ostream &os, u8 version)
paramdata[i] = data[i].param; paramdata[i] = data[i].param;
} }
compress(paramdata, os, version); compress(paramdata, os, version);
if(version >= 10)
{
// Get and compress pressure
SharedBuffer<u8> pressuredata(nodecount);
for(u32 i=0; i<nodecount; i++)
{
pressuredata[i] = data[i].pressure;
}
compress(pressuredata, os, version);
}
} }
} }
@ -819,6 +839,21 @@ void MapBlock::deSerialize(std::istream &is, u8 version)
data[i].param = s[i]; data[i].param = s[i];
} }
} }
if(version >= 10)
{
// Uncompress and set pressure data
std::ostringstream os(std::ios_base::binary);
decompress(is, os, version);
std::string s = os.str();
if(s.size() != nodecount)
throw SerializationError
("MapBlock::deSerialize: invalid format");
for(u32 i=0; i<s.size(); i++)
{
data[i].pressure = s[i];
}
}
} }
} }

@ -30,6 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "serialization.h" #include "serialization.h"
#include "constants.h" #include "constants.h"
#include "mapblockobject.h" #include "mapblockobject.h"
#include "voxel.h"
#define MAP_BLOCKSIZE 16 #define MAP_BLOCKSIZE 16
@ -69,31 +70,6 @@ public:
class MapBlock : public NodeContainer class MapBlock : public NodeContainer
{ {
private:
NodeContainer *m_parent;
// Position in blocks on parent
v3s16 m_pos;
/*
If NULL, block is a dummy block.
Dummy blocks are used for caching not-found-on-disk blocks.
*/
MapNode * data;
/*
- On the client, this is used for checking whether to
recalculate the face cache. (Is it anymore?)
- On the server, this is used for telling whether the
block has been changed from the one on disk.
*/
bool changed;
/*
Used for some initial lighting stuff.
At least /has been/ used. 8)
*/
bool is_underground;
MapBlockObjectList m_objects;
public: public:
/* /*
@ -333,10 +309,12 @@ public:
bool propagateSunlight(core::map<v3s16, bool> & light_sources); bool propagateSunlight(core::map<v3s16, bool> & light_sources);
// Doesn't write version by itself // Copies data to VoxelManipulator to getPosRelative()
void serialize(std::ostream &os, u8 version); void copyTo(VoxelManipulator &dst);
void deSerialize(std::istream &is, u8 version); /*
Object stuff
*/
void serializeObjects(std::ostream &os, u8 version) void serializeObjects(std::ostream &os, u8 version)
{ {
@ -403,6 +381,15 @@ public:
return m_objects.getCount(); return m_objects.getCount();
} }
/*
Serialization
*/
// Doesn't write version by itself
void serialize(std::ostream &os, u8 version);
void deSerialize(std::istream &is, u8 version);
private: private:
/* /*
@ -420,6 +407,31 @@ private:
{ {
return getNodeRef(p.X, p.Y, p.Z); return getNodeRef(p.X, p.Y, p.Z);
} }
NodeContainer *m_parent;
// Position in blocks on parent
v3s16 m_pos;
/*
If NULL, block is a dummy block.
Dummy blocks are used for caching not-found-on-disk blocks.
*/
MapNode * data;
/*
- On the client, this is used for checking whether to
recalculate the face cache. (Is it anymore?)
- On the server, this is used for telling whether the
block has been changed from the one on disk.
*/
bool changed;
/*
Used for some initial lighting stuff.
At least /has been/ used. 8)
*/
bool is_underground;
MapBlockObjectList m_objects;
}; };
inline bool blockpos_over_limit(v3s16 p) inline bool blockpos_over_limit(v3s16 p)

@ -205,10 +205,11 @@ struct MapNode
*this = n; *this = n;
} }
MapNode(u8 data=MATERIAL_AIR, u8 a_param=0) MapNode(u8 data=MATERIAL_AIR, u8 a_param=0, u8 a_pressure=0)
{ {
d = data; d = data;
param = a_param; param = a_param;
pressure = a_pressure;
} }
bool operator==(const MapNode &other) bool operator==(const MapNode &other)
@ -261,6 +262,11 @@ struct MapNode
param = a_light; param = a_light;
} }
/*
These serialization functions are used when informing client
of a single node add
*/
static u32 serializedLength(u8 version) static u32 serializedLength(u8 version)
{ {
if(!ser_ver_supported(version)) if(!ser_ver_supported(version))
@ -268,8 +274,10 @@ struct MapNode
if(version == 0) if(version == 0)
return 1; return 1;
else else if(version <= 9)
return 2; return 2;
else
return 3;
} }
void serialize(u8 *dest, u8 version) void serialize(u8 *dest, u8 version)
{ {
@ -280,10 +288,16 @@ struct MapNode
{ {
dest[0] = d; dest[0] = d;
} }
else if(version <= 9)
{
dest[0] = d;
dest[1] = param;
}
else else
{ {
dest[0] = d; dest[0] = d;
dest[1] = param; dest[1] = param;
dest[2] = pressure;
} }
} }
void deSerialize(u8 *source, u8 version) void deSerialize(u8 *source, u8 version)
@ -304,10 +318,16 @@ struct MapNode
else else
param = source[1]; param = source[1];
} }
else if(version <= 9)
{
d = source[0];
param = source[1];
}
else else
{ {
d = source[0]; d = source[0];
param = source[1]; param = source[1];
pressure = source[2];
} }
} }
}; };

@ -46,11 +46,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
7: block compression switched on again 7: block compression switched on again
8: (dev) server-initiated block transfers and all kinds of stuff 8: (dev) server-initiated block transfers and all kinds of stuff
9: (dev) block objects 9: (dev) block objects
10: (dev) water pressure
*/ */
// This represents an uninitialized or invalid format // This represents an uninitialized or invalid format
#define SER_FMT_VER_INVALID 255 #define SER_FMT_VER_INVALID 255
// Highest supported serialization version // Highest supported serialization version
#define SER_FMT_VER_HIGHEST 9 #define SER_FMT_VER_HIGHEST 10
// Lowest supported serialization version // Lowest supported serialization version
#define SER_FMT_VER_LOWEST 2 #define SER_FMT_VER_LOWEST 2

@ -29,6 +29,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "jmutexautolock.h" #include "jmutexautolock.h"
#include "main.h" #include "main.h"
#include "constants.h" #include "constants.h"
#include "voxel.h"
void * ServerThread::Thread() void * ServerThread::Thread()
{ {
@ -991,6 +992,76 @@ void Server::AsyncRunStep()
Do background stuff Do background stuff
*/ */
/*
Flow water
*/
{
static float counter = 0.0;
counter += dtime;
if(counter >= 1.0)
{
counter = 0.0;
core::map<v3s16, MapBlock*> modified_blocks;
{
JMutexAutoLock lock(m_env_mutex);
MapVoxelManipulator v(&m_env.getMap());
/*try{
v.flowWater(m_flow_active_nodes, 0, false, 20);
//v.flowWater(p_under, 0, true, 100);
}
catch(ProcessingLimitException &e)
{
dstream<<"Processing limit reached"<<std::endl;
}*/
v.flowWater(m_flow_active_nodes, 0, false, 20);
v.blitBack(modified_blocks);
ServerMap &map = ((ServerMap&)m_env.getMap());
// Update lighting
core::map<v3s16, MapBlock*> lighting_modified_blocks;
map.updateLighting(modified_blocks, lighting_modified_blocks);
// Add blocks modified by lighting to modified_blocks
for(core::map<v3s16, MapBlock*>::Iterator
i = lighting_modified_blocks.getIterator();
i.atEnd() == false; i++)
{
MapBlock *block = i.getNode()->getValue();
modified_blocks.insert(block->getPos(), block);
}
}
/*
Set the modified blocks unsent for all the clients
*/
JMutexAutoLock lock2(m_con_mutex);
for(core::map<u16, RemoteClient*>::Iterator
i = m_clients.getIterator();
i.atEnd() == false; i++)
{
RemoteClient *client = i.getNode()->getValue();
if(modified_blocks.size() > 0)
{
// Remove block from sent history
client->SetBlocksNotSent(modified_blocks);
}
}
}
}
// Periodically print some info // Periodically print some info
{ {
static float counter = 0.0; static float counter = 0.0;
@ -1459,6 +1530,31 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
*/ */
m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks); m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
/*
Update water
*/
// Update water pressure around modification
// This also adds it to m_flow_active_nodes if appropriate
MapVoxelManipulator v(&m_env.getMap());
VoxelArea area(p_under-v3s16(1,1,1), p_under+v3s16(1,1,1));
try
{
v.updateAreaWaterPressure(area, m_flow_active_nodes);
}
catch(ProcessingLimitException &e)
{
dstream<<"Processing limit reached"<<std::endl;
}
v.blitBack(modified_blocks);
// Add the node to m_flow_active_nodes.
//m_flow_active_nodes[p_under] = 1;
} // button == 0 } // button == 0
/* /*
Right button places blocks and stuff Right button places blocks and stuff

@ -469,6 +469,9 @@ private:
BlockEmergeQueue m_emerge_queue; BlockEmergeQueue m_emerge_queue;
// Nodes that are destinations of flowing liquid at the moment
core::map<v3s16, u8> m_flow_active_nodes;
friend class EmergeThread; friend class EmergeThread;
friend class RemoteClient; friend class RemoteClient;
}; };

@ -148,10 +148,45 @@ struct TestVoxelManipulator
{ {
void Run() void Run()
{ {
/*
VoxelArea
*/
VoxelArea a(v3s16(-1,-1,-1), v3s16(1,1,1)); VoxelArea a(v3s16(-1,-1,-1), v3s16(1,1,1));
assert(a.index(0,0,0) == 1*3*3 + 1*3 + 1); assert(a.index(0,0,0) == 1*3*3 + 1*3 + 1);
assert(a.index(-1,-1,-1) == 0); assert(a.index(-1,-1,-1) == 0);
VoxelArea c(v3s16(-2,-2,-2), v3s16(2,2,2));
// An area that is 1 bigger in x+ and z-
VoxelArea d(v3s16(-2,-2,-3), v3s16(3,2,2));
core::list<VoxelArea> aa;
d.diff(c, aa);
// Correct results
core::array<VoxelArea> results;
results.push_back(VoxelArea(v3s16(-2,-2,-3),v3s16(3,2,-3)));
results.push_back(VoxelArea(v3s16(3,-2,-2),v3s16(3,2,2)));
assert(aa.size() == results.size());
dstream<<"Result of diff:"<<std::endl;
for(core::list<VoxelArea>::Iterator
i = aa.begin(); i != aa.end(); i++)
{
i->print(dstream);
dstream<<std::endl;
s32 j = results.linear_search(*i);
assert(j != -1);
results.erase(j, 1);
}
/*
VoxelManipulator
*/
VoxelManipulator v; VoxelManipulator v;
v.print(dstream); v.print(dstream);
@ -188,16 +223,17 @@ struct TestVoxelManipulator
const char *content = const char *content =
"#...###### " "#...###### "
"#...##..## " "#...##..## "
"#........ " "#........ .."
"##########" "############"
"#...###### " "#...###### "
"#...##..## " "#...##..## "
"#........ " "#........# "
"##########" "############"
; ;
v3s16 size(10, 4, 2); v3s16 size(12, 4, 2);
VoxelArea area(v3s16(0,0,0), size-v3s16(1,1,1));
const char *p = content; const char *p = content;
for(s16 z=0; z<size.Z; z++) for(s16 z=0; z<size.Z; z++)
@ -205,7 +241,7 @@ struct TestVoxelManipulator
for(s16 x=0; x<size.X; x++) for(s16 x=0; x<size.X; x++)
{ {
MapNode n; MapNode n;
n.pressure = size.Y - y; //n.pressure = size.Y - y;
if(*p == '#') if(*p == '#')
n.d = MATERIAL_STONE; n.d = MATERIAL_STONE;
else if(*p == '.') else if(*p == '.')
@ -218,7 +254,24 @@ struct TestVoxelManipulator
p++; p++;
} }
v.print(dstream); v.print(dstream, VOXELPRINT_WATERPRESSURE);
core::map<v3s16, u8> active_nodes;
v.updateAreaWaterPressure(area, active_nodes);
v.print(dstream, VOXELPRINT_WATERPRESSURE);
s16 highest_y = -32768;
assert(v.getWaterPressure(v3s16(7, 1, 1), highest_y, 0) == -1);
assert(highest_y == 3);
active_nodes.clear();
active_nodes[v3s16(9,1,0)] = 1;
//v.flowWater(active_nodes, 0, false);
v.flowWater(active_nodes, 0, true);
dstream<<"Final result of flowWater:"<<std::endl;
v.print(dstream, VOXELPRINT_WATERPRESSURE);
//assert(0); //assert(0);
} }

@ -394,12 +394,18 @@ private:
class TimeTaker class TimeTaker
{ {
public: public:
TimeTaker(const char *name, IrrlichtDevice *dev) TimeTaker(const char *name, IrrlichtDevice *dev, u32 *result=NULL)
{ {
m_name = name; m_name = name;
m_dev = dev; m_dev = dev;
m_time1 = m_dev->getTimer()->getRealTime(); m_result = result;
m_running = true; m_running = true;
if(dev == NULL)
{
m_time1 = 0;
return;
}
m_time1 = m_dev->getTimer()->getRealTime();
} }
~TimeTaker() ~TimeTaker()
{ {
@ -409,10 +415,24 @@ public:
{ {
if(m_running) if(m_running)
{ {
if(m_dev == NULL)
{
/*if(quiet == false)
std::cout<<"Couldn't measure time for "<<m_name
<<": dev==NULL"<<std::endl;*/
return 0;
}
u32 time2 = m_dev->getTimer()->getRealTime(); u32 time2 = m_dev->getTimer()->getRealTime();
u32 dtime = time2 - m_time1; u32 dtime = time2 - m_time1;
if(m_result != NULL)
{
(*m_result) += dtime;
}
else
{
if(quiet == false) if(quiet == false)
std::cout<<m_name<<" took "<<dtime<<"ms"<<std::endl; std::cout<<m_name<<" took "<<dtime<<"ms"<<std::endl;
}
m_running = false; m_running = false;
return dtime; return dtime;
} }
@ -423,6 +443,7 @@ private:
IrrlichtDevice *m_dev; IrrlichtDevice *m_dev;
u32 m_time1; u32 m_time1;
bool m_running; bool m_running;
u32 *m_result;
}; };
// Calculates the borders of a "d-radius" cube // Calculates the borders of a "d-radius" cube

@ -20,6 +20,23 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "voxel.h" #include "voxel.h"
#include "map.h" #include "map.h"
// For TimeTaker
#include "main.h"
#include "utility.h"
/*
Debug stuff
*/
u32 addarea_time = 0;
u32 emerge_time = 0;
u32 emerge_load_time = 0;
u32 clearflag_time = 0;
//u32 getwaterpressure_time = 0;
//u32 spreadwaterpressure_time = 0;
u32 updateareawaterpressure_time = 0;
u32 flowwater_pre_time = 0;
VoxelManipulator::VoxelManipulator(): VoxelManipulator::VoxelManipulator():
m_data(NULL), m_data(NULL),
m_flags(NULL) m_flags(NULL)
@ -47,7 +64,7 @@ void VoxelManipulator::clear()
m_flags = NULL; m_flags = NULL;
} }
void VoxelManipulator::print(std::ostream &o) void VoxelManipulator::print(std::ostream &o, VoxelPrintMode mode)
{ {
v3s16 em = m_area.getExtent(); v3s16 em = m_area.getExtent();
v3s16 of = m_area.MinEdge; v3s16 of = m_area.MinEdge;
@ -78,9 +95,30 @@ void VoxelManipulator::print(std::ostream &o)
{ {
c = 'X'; c = 'X';
u8 m = m_data[m_area.index(x,y,z)].d; u8 m = m_data[m_area.index(x,y,z)].d;
u8 pr = m_data[m_area.index(x,y,z)].pressure;
if(mode == VOXELPRINT_MATERIAL)
{
if(m <= 9) if(m <= 9)
c = m + '0'; c = m + '0';
} }
else if(mode == VOXELPRINT_WATERPRESSURE)
{
if(m == MATERIAL_WATER)
{
c = 'w';
if(pr <= 9)
c = pr + '0';
}
else if(m == MATERIAL_AIR)
{
c = ' ';
}
else
{
c = '#';
}
}
}
o<<c; o<<c;
} }
o<<' '; o<<' ';
@ -99,6 +137,8 @@ void VoxelManipulator::addArea(VoxelArea area)
if(m_area.contains(area)) if(m_area.contains(area))
return; return;
TimeTaker timer("addArea", g_device, &addarea_time);
// Calculate new area // Calculate new area
VoxelArea new_area; VoxelArea new_area;
// New area is the requested area if m_area has zero volume // New area is the requested area if m_area has zero volume
@ -163,6 +203,21 @@ void VoxelManipulator::addArea(VoxelArea area)
delete[] old_data; delete[] old_data;
if(old_flags) if(old_flags)
delete[] old_flags; delete[] old_flags;
//dstream<<"addArea done"<<std::endl;
}
void VoxelManipulator::copyFrom(MapNode *src, VoxelArea src_area,
v3s16 from_pos, v3s16 to_pos, v3s16 size)
{
for(s16 z=0; z<size.Z; z++)
for(s16 y=0; y<size.Y; y++)
{
s32 i_src = src_area.index(from_pos.X, from_pos.Y+y, from_pos.Z+z);
s32 i_local = m_area.index(to_pos.X, to_pos.Y+y, to_pos.Z+z);
memcpy(&m_data[i_local], &src[i_src], size.X*sizeof(MapNode));
memset(&m_flags[i_local], 0, size.X);
}
} }
void VoxelManipulator::interpolate(VoxelArea area) void VoxelManipulator::interpolate(VoxelArea area)
@ -230,7 +285,318 @@ void VoxelManipulator::interpolate(VoxelArea area)
} }
} }
void VoxelManipulator::flowWater(v3s16 removed_pos)
void VoxelManipulator::clearFlag(u8 flags)
{
// 0-1ms on moderate area
TimeTaker timer("clearFlag", g_device, &clearflag_time);
v3s16 s = m_area.getExtent();
/*dstream<<"clearFlag clearing area of size "
<<""<<s.X<<"x"<<s.Y<<"x"<<s.Z<<""
<<std::endl;*/
//s32 count = 0;
/*for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
{
u8 f = m_flags[m_area.index(x,y,z)];
m_flags[m_area.index(x,y,z)] &= ~flags;
if(m_flags[m_area.index(x,y,z)] != f)
count++;
}*/
s32 volume = m_area.getVolume();
for(s32 i=0; i<volume; i++)
{
m_flags[i] &= ~flags;
}
/*s32 volume = m_area.getVolume();
for(s32 i=0; i<volume; i++)
{
u8 f = m_flags[i];
m_flags[i] &= ~flags;
if(m_flags[i] != f)
count++;
}
dstream<<"clearFlag changed "<<count<<" flags out of "
<<volume<<" nodes"<<std::endl;*/
}
int VoxelManipulator::getWaterPressure(v3s16 p, s16 &highest_y, int recur_count)
{
m_flags[m_area.index(p)] |= VOXELFLAG_CHECKED2;
if(p.Y > highest_y)
highest_y = p.Y;
recur_count++;
if(recur_count > 30)
throw ProcessingLimitException
("getWaterPressure recur_count limit reached");
v3s16 dirs[6] = {
v3s16(0,1,0), // top
v3s16(-1,0,0), // left
v3s16(1,0,0), // right
v3s16(0,0,-1), // front
v3s16(0,0,1), // back
v3s16(0,-1,0), // bottom
};
// Load neighboring nodes
// TODO: A bigger area would be better
emerge(VoxelArea(p - v3s16(1,1,1), p + v3s16(1,1,1)));
s32 i;
for(i=0; i<6; i++)
{
v3s16 p2 = p + dirs[i];
u8 f = m_flags[m_area.index(p2)];
// Ignore inexistent or checked nodes
if(f & (VOXELFLAG_INEXISTENT | VOXELFLAG_CHECKED2))
continue;
MapNode &n = m_data[m_area.index(p2)];
// Ignore non-liquid nodes
if(material_liquid(n.d) == false)
continue;
int pr;
// If at surface
/*if(n.pressure == 1)
{
pr = 1;
}
// Otherwise recurse more
else*/
{
pr = getWaterPressure(p2, highest_y, recur_count);
if(pr == -1)
continue;
}
// If block is at top, pressure here is one higher
if(i == 0)
{
if(pr < 255)
pr++;
}
// If block is at bottom, pressure here is one lower
else if(i == 5)
{
if(pr > 1)
pr--;
}
// Node is on the pressure route
m_flags[m_area.index(p)] |= VOXELFLAG_CHECKED4;
// Got pressure
return pr;
}
// Nothing useful found
return -1;
}
void VoxelManipulator::spreadWaterPressure(v3s16 p, int pr,
VoxelArea request_area,
core::map<v3s16, u8> &active_nodes,
int recur_count)
{
recur_count++;
if(recur_count > 10000)
throw ProcessingLimitException
("spreadWaterPressure recur_count limit reached");
m_flags[m_area.index(p)] |= VOXELFLAG_CHECKED3;
m_data[m_area.index(p)].pressure = pr;
v3s16 dirs[6] = {
v3s16(0,1,0), // top
v3s16(-1,0,0), // left
v3s16(1,0,0), // right
v3s16(0,0,-1), // front
v3s16(0,0,1), // back
v3s16(0,-1,0), // bottom
};
// Load neighboring nodes
emerge(VoxelArea(p - v3s16(1,1,1), p + v3s16(1,1,1)));
s32 i;
for(i=0; i<6; i++)
{
v3s16 p2 = p + dirs[i];
u8 f = m_flags[m_area.index(p2)];
// Ignore inexistent and checked nodes
if(f & (VOXELFLAG_INEXISTENT | VOXELFLAG_CHECKED3))
continue;
MapNode &n = m_data[m_area.index(p2)];
/*
If material is air:
add to active_nodes if there is flow-causing pressure.
NOTE: Do not remove anything from there. We cannot know
here if some other neighbor of it causes flow.
*/
if(n.d == MATERIAL_AIR)
{
bool pressure_causes_flow = false;
// If block is at top
if(i == 0)
{
if(pr >= 3)
pressure_causes_flow = true;
}
// If block is at bottom
else if(i == 5)
{
pressure_causes_flow = true;
}
// If block is at side
else
{
if(pr >= 2)
pressure_causes_flow = true;
}
if(pressure_causes_flow)
{
active_nodes[p2] = 1;
}
continue;
}
// Ignore non-liquid nodes
if(material_liquid(n.d) == false)
continue;
int pr2 = pr;
// If block is at top, pressure there is lower
if(i == 0)
{
if(pr2 > 0)
pr2--;
}
// If block is at bottom, pressure there is higher
else if(i == 5)
{
if(pr2 < 255)
pr2++;
}
// Ignore if correct pressure is already set and is not on
// request_area
if(n.pressure == pr2 && request_area.contains(p2) == false)
continue;
spreadWaterPressure(p2, pr2, request_area, active_nodes, recur_count);
}
}
void VoxelManipulator::updateAreaWaterPressure(VoxelArea a,
core::map<v3s16, u8> &active_nodes,
bool checked3_is_clear)
{
TimeTaker timer("updateAreaWaterPressure", g_device,
&updateareawaterpressure_time);
emerge(a);
bool checked2_clear = false;
if(checked3_is_clear == false)
{
//clearFlag(VOXELFLAG_CHECKED3);
clearFlag(VOXELFLAG_CHECKED3 | VOXELFLAG_CHECKED2);
checked2_clear = true;
}
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++)
{
v3s16 p(x,y,z);
u8 f = m_flags[m_area.index(p)];
// Ignore inexistent or checked nodes
if(f & (VOXELFLAG_INEXISTENT | VOXELFLAG_CHECKED3))
continue;
MapNode &n = m_data[m_area.index(p)];
// Ignore non-liquid nodes
if(material_liquid(n.d) == false)
continue;
if(checked2_clear == false)
{
clearFlag(VOXELFLAG_CHECKED2);
checked2_clear = true;
}
checked2_clear = false;
s16 highest_y = -32768;
int recur_count = 0;
int pr = -1;
try
{
// 0-1ms @ recur_count <= 100
//TimeTaker timer("getWaterPressure", g_device);
pr = getWaterPressure(p, highest_y, recur_count);
}
catch(ProcessingLimitException &e)
{
//dstream<<"getWaterPressure ProcessingLimitException"<<std::endl;
}
if(pr == -1)
{
assert(highest_y != -32768);
pr = highest_y - p.Y + 1;
if(pr > 255)
pr = 255;
/*dstream<<"WARNING: Pressure at ("
<<p.X<<","<<p.Y<<","<<p.Z<<")"
<<" = "<<pr
//<<" and highest_y == -32768"
<<std::endl;
assert(highest_y != -32768);
continue;*/
}
try
{
// 0ms
//TimeTaker timer("spreadWaterPressure", g_device);
spreadWaterPressure(p, pr, a, active_nodes, 0);
}
catch(ProcessingLimitException &e)
{
//dstream<<"getWaterPressure ProcessingLimitException"<<std::endl;
}
}
}
bool VoxelManipulator::flowWater(v3s16 removed_pos,
core::map<v3s16, u8> &active_nodes,
int recursion_depth, bool debugprint,
int *counter, int counterlimit)
{ {
v3s16 dirs[6] = { v3s16 dirs[6] = {
v3s16(0,1,0), // top v3s16(0,1,0), // top
@ -241,16 +607,43 @@ void VoxelManipulator::flowWater(v3s16 removed_pos)
v3s16(0,-1,0), // bottom v3s16(0,-1,0), // bottom
}; };
recursion_depth++;
v3s16 p; v3s16 p;
// Randomize horizontal order
static s32 cs = 0;
if(cs < 3)
cs++;
else
cs = 0;
s16 s1 = (cs & 1) ? 1 : -1;
s16 s2 = (cs & 2) ? 1 : -1;
//dstream<<"s1="<<s1<<", s2="<<s2<<std::endl;
{
TimeTaker timer1("flowWater pre", g_device, &flowwater_pre_time);
// Load neighboring nodes // Load neighboring nodes
// TODO: A bigger area would be better
emerge(VoxelArea(removed_pos - v3s16(1,1,1), removed_pos + v3s16(1,1,1))); emerge(VoxelArea(removed_pos - v3s16(1,1,1), removed_pos + v3s16(1,1,1)));
// Ignore incorrect removed_pos
{
u8 f = m_flags[m_area.index(removed_pos)];
// Ignore inexistent or checked node
if(f & (VOXELFLAG_INEXISTENT | VOXELFLAG_CHECKED))
return false;
MapNode &n = m_data[m_area.index(removed_pos)];
// Water can move only to air
if(n.d != MATERIAL_AIR)
return false;
}
s32 i; s32 i;
for(i=0; i<6; i++) for(i=0; i<6; i++)
{ {
p = removed_pos + dirs[i]; p = removed_pos + v3s16(s1*dirs[i].X, dirs[i].Y, s2*dirs[i].Z);
u8 f = m_flags[m_area.index(p)]; u8 f = m_flags[m_area.index(p)];
// Inexistent or checked nodes can't move // Inexistent or checked nodes can't move
if(f & (VOXELFLAG_INEXISTENT | VOXELFLAG_CHECKED)) if(f & (VOXELFLAG_INEXISTENT | VOXELFLAG_CHECKED))
@ -280,30 +673,66 @@ void VoxelManipulator::flowWater(v3s16 removed_pos)
// If there is nothing to move, return // If there is nothing to move, return
if(i==6) if(i==6)
return; return false;
// Switch nodes at p and removed_pos // Switch nodes at p and removed_pos
MapNode n = m_data[m_area.index(p)]; u8 m = m_data[m_area.index(p)].d;
u8 f = m_flags[m_area.index(p)]; u8 f = m_flags[m_area.index(p)];
m_data[m_area.index(p)] = m_data[m_area.index(removed_pos)]; m_data[m_area.index(p)].d = m_data[m_area.index(removed_pos)].d;
m_flags[m_area.index(p)] = m_flags[m_area.index(removed_pos)]; m_flags[m_area.index(p)] = m_flags[m_area.index(removed_pos)];
m_data[m_area.index(removed_pos)] = n; m_data[m_area.index(removed_pos)].d = m;
m_flags[m_area.index(removed_pos)] = f; m_flags[m_area.index(removed_pos)] = f;
// Mark p checked // Mark removed_pos checked
m_flags[m_area.index(p)] |= VOXELFLAG_CHECKED; m_flags[m_area.index(removed_pos)] |= VOXELFLAG_CHECKED;
// If block was dropped from surface, increase pressure
if(i == 0 && m_data[m_area.index(removed_pos)].pressure == 1)
{
m_data[m_area.index(removed_pos)].pressure = 2;
}
/*if(debugprint)
{
dstream<<"VoxelManipulator::flowWater(): Moved bubble:"<<std::endl;
print(dstream, VOXELPRINT_WATERPRESSURE);
}*/
// Update pressure // Update pressure
//TODO VoxelArea a;
a.addPoint(p - v3s16(1,1,1));
a.addPoint(p + v3s16(1,1,1));
a.addPoint(removed_pos - v3s16(1,1,1));
a.addPoint(removed_pos + v3s16(1,1,1));
updateAreaWaterPressure(a, active_nodes);
/*if(debugprint)
{
dstream<<"VoxelManipulator::flowWater(): Pressure updated:"<<std::endl;
print(dstream, VOXELPRINT_WATERPRESSURE);
//std::cin.get();
}*/
if(debugprint)
{
dstream<<"VoxelManipulator::flowWater(): step done:"<<std::endl;
print(dstream, VOXELPRINT_WATERPRESSURE);
//std::cin.get();
}
}//timer1
// Flow water to the newly created empty position // Flow water to the newly created empty position
flowWater(p); flowWater(p, active_nodes, recursion_depth,
debugprint, counter, counterlimit);
find_again:
// Try flowing water to empty positions around removed_pos. // Try flowing water to empty positions around removed_pos.
// They are checked in reverse order compared to the previous loop. // They are checked in reverse order compared to the previous loop.
for(i=5; i>=0; i--) for(s32 i=5; i>=0; i--)
{ {
p = removed_pos + dirs[i]; //v3s16 p = removed_pos + dirs[i];
p = removed_pos + v3s16(s1*dirs[i].X, dirs[i].Y, s2*dirs[i].Z);
u8 f = m_flags[m_area.index(p)]; u8 f = m_flags[m_area.index(p)];
// Water can't move to inexistent nodes // Water can't move to inexistent nodes
if(f & VOXELFLAG_INEXISTENT) if(f & VOXELFLAG_INEXISTENT)
@ -312,102 +741,291 @@ void VoxelManipulator::flowWater(v3s16 removed_pos)
// Water can only move to air // Water can only move to air
if(n.d != MATERIAL_AIR) if(n.d != MATERIAL_AIR)
continue; continue;
flowWater(p);
}
}
/* // Flow water to node
MapVoxelManipulator bool moved =
*/ flowWater(p, active_nodes, recursion_depth,
debugprint, counter, counterlimit);
MapVoxelManipulator::MapVoxelManipulator(Map *map) if(moved)
{ {
m_map = map; // Search again from all neighbors
} goto find_again;
void MapVoxelManipulator::emerge(VoxelArea a)
{
v3s16 size = a.getExtent();
addArea(a);
for(s16 z=0; z<size.Z; z++)
for(s16 y=0; y<size.Y; y++)
for(s16 x=0; x<size.X; x++)
{
v3s16 p(x,y,z);
s32 i = m_area.index(a.MinEdge + p);
// Don't touch nodes that have already been loaded
if(!(m_flags[i] & VOXELFLAG_NOT_LOADED))
continue;
try{
MapNode n = m_map->getNode(a.MinEdge + p);
m_data[i] = n;
m_flags[i] = 0;
}
catch(InvalidPositionException &e)
{
m_flags[i] = VOXELFLAG_INEXISTENT;
}
} }
} }
void MapVoxelManipulator::blitBack if(counter != NULL)
(core::map<v3s16, MapBlock*> & modified_blocks)
{ {
/* (*counter)++;
Initialize block cache if((*counter) % 10 == 0)
*/ dstream<<"flowWater(): moved "<<(*counter)<<" nodes"
v3s16 blockpos_last; <<std::endl;
MapBlock *block = NULL;
bool block_checked_in_modified = false;
for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++) if(counterlimit != -1 && (*counter) > counterlimit)
for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
{ {
v3s16 p(x,y,z); dstream<<"Counter limit reached; returning"<<std::endl;
throw ProcessingLimitException("flowWater counterlimit reached");
}
}
return true;
}
bool VoxelManipulator::flowWater(v3s16 removed_pos,
core::map<v3s16, u8> &active_nodes,
int recursion_depth, bool debugprint,
int *counter, int counterlimit)
{
v3s16 dirs[6] = {
v3s16(0,1,0), // top
v3s16(-1,0,0), // left
v3s16(1,0,0), // right
v3s16(0,0,-1), // front
v3s16(0,0,1), // back
v3s16(0,-1,0), // bottom
};
recursion_depth++;
v3s16 p;
// Randomize horizontal order
static s32 cs = 0;
if(cs < 3)
cs++;
else
cs = 0;
s16 s1 = (cs & 1) ? 1 : -1;
s16 s2 = (cs & 2) ? 1 : -1;
//dstream<<"s1="<<s1<<", s2="<<s2<<std::endl;
{
TimeTaker timer1("flowWater pre", g_device, &flowwater_pre_time);
// Load neighboring nodes
emerge(VoxelArea(removed_pos - v3s16(1,1,1), removed_pos + v3s16(1,1,1)));
// Ignore incorrect removed_pos
{
u8 f = m_flags[m_area.index(removed_pos)];
// Ignore inexistent or checked node
if(f & (VOXELFLAG_INEXISTENT | VOXELFLAG_CHECKED))
return false;
MapNode &n = m_data[m_area.index(removed_pos)];
// Water can move only to air
if(n.d != MATERIAL_AIR)
return false;
}
s32 i;
for(i=0; i<6; i++)
{
p = removed_pos + v3s16(s1*dirs[i].X, dirs[i].Y, s2*dirs[i].Z);
u8 f = m_flags[m_area.index(p)]; u8 f = m_flags[m_area.index(p)];
if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT)) // Inexistent or checked nodes can't move
if(f & (VOXELFLAG_INEXISTENT | VOXELFLAG_CHECKED))
continue;
MapNode &n = m_data[m_area.index(p)];
// Only liquid nodes can move
if(material_liquid(n.d) == false)
continue;
// If block is at top, select it always
if(i == 0)
{
break;
}
// If block is at bottom, select it if it has enough pressure
if(i == 5)
{
if(n.pressure >= 3)
break;
continue;
}
// Else block is at some side. Select it if it has enough pressure
if(n.pressure >= 2)
{
break;
}
}
// If there is nothing to move, return
if(i==6)
return false;
// Switch nodes at p and removed_pos
u8 m = m_data[m_area.index(p)].d;
u8 f = m_flags[m_area.index(p)];
m_data[m_area.index(p)].d = m_data[m_area.index(removed_pos)].d;
m_flags[m_area.index(p)] = m_flags[m_area.index(removed_pos)];
m_data[m_area.index(removed_pos)].d = m;
m_flags[m_area.index(removed_pos)] = f;
// Mark removed_pos checked
m_flags[m_area.index(removed_pos)] |= VOXELFLAG_CHECKED;
// If block was dropped from surface, increase pressure
if(i == 0 && m_data[m_area.index(removed_pos)].pressure == 1)
{
m_data[m_area.index(removed_pos)].pressure = 2;
}
/*if(debugprint)
{
dstream<<"VoxelManipulator::flowWater(): Moved bubble:"<<std::endl;
print(dstream, VOXELPRINT_WATERPRESSURE);
}*/
// Update pressure
VoxelArea a;
a.addPoint(p - v3s16(1,1,1));
a.addPoint(p + v3s16(1,1,1));
a.addPoint(removed_pos - v3s16(1,1,1));
a.addPoint(removed_pos + v3s16(1,1,1));
updateAreaWaterPressure(a, active_nodes);
/*if(debugprint)
{
dstream<<"VoxelManipulator::flowWater(): Pressure updated:"<<std::endl;
print(dstream, VOXELPRINT_WATERPRESSURE);
//std::cin.get();
}*/
if(debugprint)
{
dstream<<"VoxelManipulator::flowWater(): step done:"<<std::endl;
print(dstream, VOXELPRINT_WATERPRESSURE);
//std::cin.get();
}
}//timer1
// Flow water to the newly created empty position
flowWater(p, active_nodes, recursion_depth,
debugprint, counter, counterlimit);
find_again:
// Try flowing water to empty positions around removed_pos.
// They are checked in reverse order compared to the previous loop.
for(s32 i=5; i>=0; i--)
{
//v3s16 p = removed_pos + dirs[i];
p = removed_pos + v3s16(s1*dirs[i].X, dirs[i].Y, s2*dirs[i].Z);
u8 f = m_flags[m_area.index(p)];
// Water can't move to inexistent nodes
if(f & VOXELFLAG_INEXISTENT)
continue;
MapNode &n = m_data[m_area.index(p)];
// Water can only move to air
if(n.d != MATERIAL_AIR)
continue; continue;
MapNode &n = m_data[m_area.index(p)]; // Flow water to node
bool moved =
flowWater(p, active_nodes, recursion_depth,
debugprint, counter, counterlimit);
v3s16 blockpos = getNodeBlockPos(p); if(moved)
{
// Search again from all neighbors
goto find_again;
}
}
if(counter != NULL)
{
(*counter)++;
if((*counter) % 10 == 0)
dstream<<"flowWater(): moved "<<(*counter)<<" nodes"
<<std::endl;
if(counterlimit != -1 && (*counter) > counterlimit)
{
dstream<<"Counter limit reached; returning"<<std::endl;
throw ProcessingLimitException("flowWater counterlimit reached");
}
}
return true;
}
void VoxelManipulator::flowWater(
core::map<v3s16, u8> &active_nodes,
int recursion_depth, bool debugprint,
int counterlimit)
{
addarea_time = 0;
emerge_time = 0;
emerge_load_time = 0;
clearflag_time = 0;
updateareawaterpressure_time = 0;
flowwater_pre_time = 0;
TimeTaker timer1("flowWater (active_nodes)", g_device);
dstream<<"active_nodes.size() = "<<active_nodes.size()<<std::endl;
int counter = 0;
try try
{ {
// Get block
if(block == NULL || blockpos != blockpos_last){
block = m_map->getBlockNoCreate(blockpos);
blockpos_last = blockpos;
block_checked_in_modified = false;
}
// Calculate relative position in block // Flow water to active nodes
v3s16 relpos = p - blockpos * MAP_BLOCKSIZE; for(;;)
// Don't continue if nothing has changed here
if(block->getNode(relpos) == n)
continue;
//m_map->setNode(m_area.MinEdge + p, n);
block->setNode(relpos, n);
/*
Make sure block is in modified_blocks
*/
if(block_checked_in_modified == false)
{ {
modified_blocks[blockpos] = block; // Clear check flags
block_checked_in_modified = true; clearFlag(VOXELFLAG_CHECKED);
}
} if(active_nodes.size() == 0)
catch(InvalidPositionException &e) break;
dstream<<"Selecting a new active_node"<<std::endl;
#if 0
// Take first one
core::map<v3s16, u8>::Node
*n = active_nodes.getIterator().getNode();
#endif
#if 1
// Take random one
s32 k = (s32)rand() % (s32)active_nodes.size();
//s32 k = 0;
core::map<v3s16, u8>::Iterator
i = active_nodes.getIterator().getNode();
for(s32 j=0; j<k; j++)
{ {
i++;
} }
core::map<v3s16, u8>::Node *n = i.getNode();
#endif
v3s16 p = n->getKey();
active_nodes.remove(p);
flowWater(p, active_nodes, recursion_depth,
debugprint, &counter, counterlimit);
} }
} }
catch(ProcessingLimitException &e)
{
//dstream<<"getWaterPressure ProcessingLimitException"<<std::endl;
}
v3s16 e = m_area.getExtent();
s32 v = m_area.getVolume();
dstream<<"flowWater (active): moved "<<counter<<" nodes, "
<<"area ended up as "
<<e.X<<"x"<<e.Y<<"x"<<e.Z<<" = "<<v
<<std::endl;
dstream<<"addarea_time: "<<addarea_time
<<", emerge_time: "<<emerge_time
<<", emerge_load_time: "<<emerge_load_time
<<", clearflag_time: "<<clearflag_time
<<", flowwater_pre_time: "<<flowwater_pre_time
<<", updateareawaterpressure_time: "<<updateareawaterpressure_time
<<std::endl;
}
//END //END

@ -21,8 +21,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define VOXEL_HEADER #define VOXEL_HEADER
#include "common_irrlicht.h" #include "common_irrlicht.h"
#include "mapblock.h"
#include <iostream> #include <iostream>
#include "debug.h"
#include "mapnode.h"
/* /*
A fast voxel manipulator class A fast voxel manipulator class
@ -30,6 +31,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
Not thread-safe. Not thread-safe.
*/ */
/*
Debug stuff
*/
extern u32 emerge_time;
extern u32 emerge_load_time;
/* /*
This class resembles aabbox3d<s16> a lot, but has inclusive This class resembles aabbox3d<s16> a lot, but has inclusive
edges for saner handling of integer sizes edges for saner handling of integer sizes
@ -53,8 +60,18 @@ public:
MaxEdge(p) MaxEdge(p)
{ {
} }
/*
Modifying methods
*/
void addArea(VoxelArea &a) void addArea(VoxelArea &a)
{ {
if(getExtent() == v3s16(0,0,0))
{
*this = a;
return;
}
if(a.MinEdge.X < MinEdge.X) MinEdge.X = a.MinEdge.X; if(a.MinEdge.X < MinEdge.X) MinEdge.X = a.MinEdge.X;
if(a.MinEdge.Y < MinEdge.Y) MinEdge.Y = a.MinEdge.Y; if(a.MinEdge.Y < MinEdge.Y) MinEdge.Y = a.MinEdge.Y;
if(a.MinEdge.Z < MinEdge.Z) MinEdge.Z = a.MinEdge.Z; if(a.MinEdge.Z < MinEdge.Z) MinEdge.Z = a.MinEdge.Z;
@ -64,6 +81,12 @@ public:
} }
void addPoint(v3s16 p) void addPoint(v3s16 p)
{ {
if(getExtent() == v3s16(0,0,0))
{
MinEdge = p;
MaxEdge = p;
return;
}
if(p.X < MinEdge.X) MinEdge.X = p.X; if(p.X < MinEdge.X) MinEdge.X = p.X;
if(p.Y < MinEdge.Y) MinEdge.Y = p.Y; if(p.Y < MinEdge.Y) MinEdge.Y = p.Y;
if(p.Z < MinEdge.Z) MinEdge.Z = p.Z; if(p.Z < MinEdge.Z) MinEdge.Z = p.Z;
@ -71,6 +94,30 @@ public:
if(p.Y > MaxEdge.Y) MaxEdge.Y = p.Y; if(p.Y > MaxEdge.Y) MaxEdge.Y = p.Y;
if(p.Z > MaxEdge.Z) MaxEdge.Z = p.Z; if(p.Z > MaxEdge.Z) MaxEdge.Z = p.Z;
} }
// Pad with d nodes
void pad(v3s16 d)
{
MinEdge -= d;
MaxEdge += d;
}
/*void operator+=(v3s16 off)
{
MinEdge += off;
MaxEdge += off;
}
void operator-=(v3s16 off)
{
MinEdge -= off;
MaxEdge -= off;
}*/
/*
const methods
*/
v3s16 getExtent() const v3s16 getExtent() const
{ {
return MaxEdge - MinEdge + v3s16(1,1,1); return MaxEdge - MinEdge + v3s16(1,1,1);
@ -80,8 +127,13 @@ public:
v3s16 e = getExtent(); v3s16 e = getExtent();
return (s32)e.X * (s32)e.Y * (s32)e.Z; return (s32)e.X * (s32)e.Y * (s32)e.Z;
} }
bool contains(VoxelArea &a) const bool contains(const VoxelArea &a) const
{ {
// No area contains an empty area
// NOTE: Algorithms depend on this, so do not change.
if(a.getExtent() == v3s16(0,0,0))
return false;
return( return(
a.MinEdge.X >= MinEdge.X && a.MaxEdge.X <= MaxEdge.X && a.MinEdge.X >= MinEdge.X && a.MaxEdge.X <= MaxEdge.X &&
a.MinEdge.Y >= MinEdge.Y && a.MaxEdge.Y <= MaxEdge.Y && a.MinEdge.Y >= MinEdge.Y && a.MaxEdge.Y <= MaxEdge.Y &&
@ -102,6 +154,95 @@ public:
&& MaxEdge == other.MaxEdge); && MaxEdge == other.MaxEdge);
} }
VoxelArea operator+(v3s16 off) const
{
return VoxelArea(MinEdge+off, MaxEdge+off);
}
VoxelArea operator-(v3s16 off) const
{
return VoxelArea(MinEdge-off, MaxEdge-off);
}
/*
Returns 0-6 non-overlapping areas that can be added to
a to make up this area.
a: area inside *this
*/
void diff(const VoxelArea &a, core::list<VoxelArea> &result)
{
/*
This can result in a maximum of 6 areas
*/
// If a is an empty area, return the current area as a whole
if(a.getExtent() == v3s16(0,0,0))
{
VoxelArea b = *this;
if(b.getVolume() != 0)
result.push_back(b);
return;
}
assert(contains(a));
// Take back area, XY inclusive
{
v3s16 min(MinEdge.X, MinEdge.Y, a.MaxEdge.Z+1);
v3s16 max(MaxEdge.X, MaxEdge.Y, MaxEdge.Z);
VoxelArea b(min, max);
if(b.getVolume() != 0)
result.push_back(b);
}
// Take front area, XY inclusive
{
v3s16 min(MinEdge.X, MinEdge.Y, MinEdge.Z);
v3s16 max(MaxEdge.X, MaxEdge.Y, a.MinEdge.Z-1);
VoxelArea b(min, max);
if(b.getVolume() != 0)
result.push_back(b);
}
// Take top area, X inclusive
{
v3s16 min(MinEdge.X, a.MaxEdge.Y+1, a.MinEdge.Z);
v3s16 max(MaxEdge.X, MaxEdge.Y, a.MaxEdge.Z);
VoxelArea b(min, max);
if(b.getVolume() != 0)
result.push_back(b);
}
// Take bottom area, X inclusive
{
v3s16 min(MinEdge.X, MinEdge.Y, a.MinEdge.Z);
v3s16 max(MaxEdge.X, a.MinEdge.Y-1, a.MaxEdge.Z);
VoxelArea b(min, max);
if(b.getVolume() != 0)
result.push_back(b);
}
// Take left area, non-inclusive
{
v3s16 min(MinEdge.X, a.MinEdge.Y, a.MinEdge.Z);
v3s16 max(a.MinEdge.X-1, a.MaxEdge.Y, a.MaxEdge.Z);
VoxelArea b(min, max);
if(b.getVolume() != 0)
result.push_back(b);
}
// Take right area, non-inclusive
{
v3s16 min(a.MaxEdge.X+1, a.MinEdge.Y, a.MinEdge.Z);
v3s16 max(MaxEdge.X, a.MaxEdge.Y, a.MaxEdge.Z);
VoxelArea b(min, max);
if(b.getVolume() != 0)
result.push_back(b);
}
}
/* /*
Translates position from virtual coordinates to array index Translates position from virtual coordinates to array index
*/ */
@ -120,13 +261,15 @@ public:
void print(std::ostream &o) const void print(std::ostream &o) const
{ {
v3s16 e = getExtent();
o<<"("<<MinEdge.X o<<"("<<MinEdge.X
<<","<<MinEdge.Y <<","<<MinEdge.Y
<<","<<MinEdge.Z <<","<<MinEdge.Z
<<")("<<MaxEdge.X <<")("<<MaxEdge.X
<<","<<MaxEdge.Y <<","<<MaxEdge.Y
<<","<<MaxEdge.Z <<","<<MaxEdge.Z
<<")"; <<")"
<<"="<<e.X<<"x"<<e.Y<<"x"<<e.Z<<"="<<getVolume();
} }
// Edges are inclusive // Edges are inclusive
@ -139,18 +282,35 @@ public:
// Checked as being inexistent in source // Checked as being inexistent in source
#define VOXELFLAG_INEXISTENT (1<<1) #define VOXELFLAG_INEXISTENT (1<<1)
// Algorithm-dependent // Algorithm-dependent
// flowWater: "visited"
#define VOXELFLAG_CHECKED (1<<2) #define VOXELFLAG_CHECKED (1<<2)
// Algorithm-dependent
// getWaterPressure: "visited"
#define VOXELFLAG_CHECKED2 (1<<3)
// Algorithm-dependent
// spreadWaterPressure: "visited"
#define VOXELFLAG_CHECKED3 (1<<4)
// Algorithm-dependent
// water: "pressure check route node"
#define VOXELFLAG_CHECKED4 (1<<5)
class VoxelManipulator : public NodeContainer enum VoxelPrintMode
{
VOXELPRINT_NOTHING,
VOXELPRINT_MATERIAL,
VOXELPRINT_WATERPRESSURE,
};
class VoxelManipulator /*: public NodeContainer*/
{ {
public: public:
VoxelManipulator(); VoxelManipulator();
~VoxelManipulator(); virtual ~VoxelManipulator();
/* /*
Virtuals from NodeContainer Virtuals from NodeContainer
*/ */
virtual u16 nodeContainerId() const /*virtual u16 nodeContainerId() const
{ {
return NODECONTAINER_ID_VOXELMANIPULATOR; return NODECONTAINER_ID_VOXELMANIPULATOR;
} }
@ -158,7 +318,7 @@ 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
MapNode getNode(v3s16 p) MapNode getNode(v3s16 p)
{ {
@ -166,7 +326,7 @@ public:
if(m_flags[m_area.index(p)] & VOXELFLAG_INEXISTENT) if(m_flags[m_area.index(p)] & VOXELFLAG_INEXISTENT)
{ {
dstream<<"ERROR: VoxelManipulator::getNode(): " dstream<<"EXCEPT: VoxelManipulator::getNode(): "
<<"p=("<<p.X<<","<<p.Y<<","<<p.Z<<")" <<"p=("<<p.X<<","<<p.Y<<","<<p.Z<<")"
<<", index="<<m_area.index(p) <<", index="<<m_area.index(p)
<<", flags="<<(int)m_flags[m_area.index(p)] <<", flags="<<(int)m_flags[m_area.index(p)]
@ -214,19 +374,80 @@ public:
Control Control
*/ */
void clear(); virtual void clear();
void print(std::ostream &o); void print(std::ostream &o, VoxelPrintMode mode=VOXELPRINT_MATERIAL);
void addArea(VoxelArea area); void addArea(VoxelArea area);
/*
Copy data and set flags to 0
dst_area.getExtent() <= src_area.getExtent()
*/
void copyFrom(MapNode *src, VoxelArea src_area,
v3s16 from_pos, v3s16 to_pos, v3s16 size);
/* /*
Algorithms Algorithms
*/ */
void interpolate(VoxelArea area); void interpolate(VoxelArea area);
void flowWater(v3s16 removed_pos); void clearFlag(u8 flag);
// VOXELFLAG_CHECKED2s must usually be cleared before calling
// -1: dead end, 0-255: pressure
// highest_y: Highest found water y is stored here.
// Must be initialized to -32768
int getWaterPressure(v3s16 p, s16 &highest_y, int recur_count);
/*
VOXELFLAG_CHECKED3s must usually be cleared before calling.
active_nodes: surface-touching air nodes with flow-causing
pressure. set-like dummy map container.
Spreads pressure pr at node p to request_area or as far as
there is invalid pressure.
*/
void spreadWaterPressure(v3s16 p, int pr,
VoxelArea request_area,
core::map<v3s16, u8> &active_nodes,
int recur_count);
/*
VOXELFLAG_CHECKED3s must usually be cleared before calling.
*/
void updateAreaWaterPressure(VoxelArea a,
core::map<v3s16, u8> &active_nodes,
bool checked3_is_clear=false);
/*
Returns true if moved something
*/
bool flowWater(v3s16 removed_pos,
core::map<v3s16, u8> &active_nodes,
int recursion_depth=0,
bool debugprint=false, int *counter=NULL,
int counterlimit=-1
);
/*
To flow some water, call this with the target node in
active_nodes
TODO: Make the active_nodes map to contain some vectors
that are properly sorted according to water flow order.
The current order makes water flow strangely if the
first one is always taken.
No, active_nodes should preserve the order stuff is
added to it, in addition to adhering the water flow
order.
*/
void flowWater(core::map<v3s16, u8> &active_nodes,
int recursion_depth=0,
bool debugprint=false,
int counterlimit=-1
);
/* /*
Virtual functions Virtual functions
@ -265,32 +486,24 @@ public:
MaxEdge is 1 higher than maximum allowed position MaxEdge is 1 higher than maximum allowed position
*/ */
VoxelArea m_area; VoxelArea m_area;
/* /*
NULL if data size is 0 (extent (0,0,0)) NULL if data size is 0 (extent (0,0,0))
Data is stored as [z*h*w + y*h + x] Data is stored as [z*h*w + y*h + x]
*/ */
MapNode *m_data; MapNode *m_data;
/* /*
Flags of all nodes Flags of all nodes
*/ */
u8 *m_flags; u8 *m_flags;
//TODO: Use these or remove them
//TODO: Would these make any speed improvement?
//bool m_pressure_route_valid;
//v3s16 m_pressure_route_surface;
private: private:
}; };
class Map;
class MapVoxelManipulator : public VoxelManipulator
{
public:
MapVoxelManipulator(Map *map);
virtual void emerge(VoxelArea a);
void blitBack(core::map<v3s16, MapBlock*> & modified_blocks);
private:
Map *m_map;
};
#endif #endif