aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPerttu Ahola <celeron55@gmail.com>2010-12-11 18:11:03 +0200
committerPerttu Ahola <celeron55@gmail.com>2010-12-11 18:11:03 +0200
commit2a0d1a059e556afaeb7f5b72205b26447e23286f (patch)
tree2e30313c2c40b59239a062fdb69d66e9abf153c4
parent5e0c284f3a8debdd9ebb080f80e36dceb7bc4ce2 (diff)
downloadminetest-2a0d1a059e556afaeb7f5b72205b26447e23286f.tar.gz
minetest-2a0d1a059e556afaeb7f5b72205b26447e23286f.tar.bz2
minetest-2a0d1a059e556afaeb7f5b72205b26447e23286f.zip
commit before some radicallish changes to water behavior
-rw-r--r--Makefile4
-rw-r--r--src/exceptions.h8
-rw-r--r--src/main.cpp5
-rw-r--r--src/map.cpp149
-rw-r--r--src/map.h34
-rw-r--r--src/mapblock.cpp35
-rw-r--r--src/mapblock.h68
-rw-r--r--src/mapnode.h24
-rw-r--r--src/serialization.h3
-rw-r--r--src/server.cpp96
-rw-r--r--src/server.h3
-rw-r--r--src/test.cpp77
-rw-r--r--src/utility.h29
-rw-r--r--src/voxel.cpp788
-rw-r--r--src/voxel.h267
15 files changed, 1422 insertions, 168 deletions
diff --git a/Makefile b/Makefile
index 102a3dcdb..2b4e8dda3 100644
--- a/Makefile
+++ b/Makefile
@@ -13,9 +13,9 @@ JTHREADPATH = ../jthread/jthread-1.2.1
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 -g -pipe
+CXXFLAGS = -O2 -ffast-math -Wall -g -pipe
#CXXFLAGS = -O1 -ffast-math -Wall -g
-CXXFLAGS = -Wall -g -O0
+#CXXFLAGS = -Wall -g -O0
#CXXFLAGS = -O3 -ffast-math -Wall
#CXXFLAGS = -O3 -ffast-math -Wall -g
diff --git a/src/exceptions.h b/src/exceptions.h
index 80bdbeb36..0f95bd07a 100644
--- a/src/exceptions.h
+++ b/src/exceptions.h
@@ -116,6 +116,14 @@ public:
{}
};
+class ProcessingLimitException : public BaseException
+{
+public:
+ ProcessingLimitException(const char *s):
+ BaseException(s)
+ {}
+};
+
/*
Some "old-style" interrupts:
*/
diff --git a/src/main.cpp b/src/main.cpp
index 938eb14ef..73ef37951 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -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)
- 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.
+TODO: Transfer sign texts as metadata of block and not as data of
+ object
+
Doing now:
======================================================================
diff --git a/src/map.cpp b/src/map.cpp
index 050299bc9..88cb0f3f7 100644
--- a/src/map.cpp
+++ b/src/map.cpp
@@ -47,10 +47,10 @@ MapBlockPointerCache::~MapBlockPointerCache()
{
m_map->m_blockcachelock.cacheRemoved();
- dstream<<"MapBlockPointerCache:"
+ /*dstream<<"MapBlockPointerCache:"
<<" from_cache_count="<<m_from_cache_count
<<" from_map_count="<<m_from_map_count
- <<std::endl;
+ <<std::endl;*/
}
MapBlock * MapBlockPointerCache::getBlockNoCreate(v3s16 p)
@@ -1050,6 +1050,8 @@ void Map::removeNodeAndUpdate(v3s16 p,
// Node will be replaced with this
u8 replace_material = MATERIAL_AIR;
+ // NOTE: Water is now managed elsewhere
+#if 0
{
/*
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,
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
diff --git a/src/map.h b/src/map.h
index 7f791ffad..62d1f8aee 100644
--- a/src/map.h
+++ b/src/map.h
@@ -40,6 +40,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "mapblock.h"
#include "mapsector.h"
#include "constants.h"
+#include "voxel.h"
class Map;
@@ -49,6 +50,7 @@ class Map;
NOTE: This doesn't really make anything more efficient
NOTE: Use VoxelManipulator, if possible
TODO: Get rid of this?
+ NOTE: CONFIRMED: THIS CACHE DOESN'T MAKE ANYTHING ANY FASTER
*/
class MapBlockPointerCache : public NodeContainer
{
@@ -121,7 +123,7 @@ public:
void cacheCreated()
{
- dstream<<"cacheCreated() begin"<<std::endl;
+ //dstream<<"cacheCreated() begin"<<std::endl;
JMutexAutoLock waitcachelock(m_waitcache_mutex);
JMutexAutoLock countlock(m_count_mutex);
@@ -131,12 +133,12 @@ public:
m_count++;
- dstream<<"cacheCreated() end"<<std::endl;
+ //dstream<<"cacheCreated() end"<<std::endl;
}
void cacheRemoved()
{
- dstream<<"cacheRemoved() begin"<<std::endl;
+ //dstream<<"cacheRemoved() begin"<<std::endl;
JMutexAutoLock countlock(m_count_mutex);
assert(m_count > 0);
@@ -147,7 +149,7 @@ public:
if(m_count == 0)
m_cache_mutex.Unlock();
- dstream<<"cacheRemoved() end"<<std::endl;
+ //dstream<<"cacheRemoved() end"<<std::endl;
}
/*
@@ -589,5 +591,29 @@ private:
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
diff --git a/src/mapblock.cpp b/src/mapblock.cpp
index a4da657d1..d2c323291 100644
--- a/src/mapblock.cpp
+++ b/src/mapblock.cpp
@@ -696,6 +696,15 @@ bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources)
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
*/
@@ -755,6 +764,17 @@ void MapBlock::serialize(std::ostream &os, u8 version)
paramdata[i] = data[i].param;
}
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];
}
}
+
+ 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];
+ }
+ }
}
}
diff --git a/src/mapblock.h b/src/mapblock.h
index 2c04343de..c18bbb2b4 100644
--- a/src/mapblock.h
+++ b/src/mapblock.h
@@ -30,6 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "serialization.h"
#include "constants.h"
#include "mapblockobject.h"
+#include "voxel.h"
#define MAP_BLOCKSIZE 16
@@ -69,31 +70,6 @@ public:
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:
/*
@@ -333,10 +309,12 @@ public:
bool propagateSunlight(core::map<v3s16, bool> & light_sources);
- // Doesn't write version by itself
- void serialize(std::ostream &os, u8 version);
+ // Copies data to VoxelManipulator to getPosRelative()
+ void copyTo(VoxelManipulator &dst);
- void deSerialize(std::istream &is, u8 version);
+ /*
+ Object stuff
+ */
void serializeObjects(std::ostream &os, u8 version)
{
@@ -403,6 +381,15 @@ public:
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:
/*
@@ -420,6 +407,31 @@ private:
{
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)
diff --git a/src/mapnode.h b/src/mapnode.h
index 02abe4e52..7502c42d7 100644
--- a/src/mapnode.h
+++ b/src/mapnode.h
@@ -205,10 +205,11 @@ struct MapNode
*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;
param = a_param;
+ pressure = a_pressure;
}
bool operator==(const MapNode &other)
@@ -261,6 +262,11 @@ struct MapNode
param = a_light;
}
+ /*
+ These serialization functions are used when informing client
+ of a single node add
+ */
+
static u32 serializedLength(u8 version)
{
if(!ser_ver_supported(version))
@@ -268,8 +274,10 @@ struct MapNode
if(version == 0)
return 1;
- else
+ else if(version <= 9)
return 2;
+ else
+ return 3;
}
void serialize(u8 *dest, u8 version)
{
@@ -280,10 +288,16 @@ struct MapNode
{
dest[0] = d;
}
+ else if(version <= 9)
+ {
+ dest[0] = d;
+ dest[1] = param;
+ }
else
{
dest[0] = d;
dest[1] = param;
+ dest[2] = pressure;
}
}
void deSerialize(u8 *source, u8 version)
@@ -304,10 +318,16 @@ struct MapNode
else
param = source[1];
}
+ else if(version <= 9)
+ {
+ d = source[0];
+ param = source[1];
+ }
else
{
d = source[0];
param = source[1];
+ pressure = source[2];
}
}
};
diff --git a/src/serialization.h b/src/serialization.h
index 6e18497cf..8d3c5fee2 100644
--- a/src/serialization.h
+++ b/src/serialization.h
@@ -46,11 +46,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
7: block compression switched on again
8: (dev) server-initiated block transfers and all kinds of stuff
9: (dev) block objects
+ 10: (dev) water pressure
*/
// This represents an uninitialized or invalid format
#define SER_FMT_VER_INVALID 255
// Highest supported serialization version
-#define SER_FMT_VER_HIGHEST 9
+#define SER_FMT_VER_HIGHEST 10
// Lowest supported serialization version
#define SER_FMT_VER_LOWEST 2
diff --git a/src/server.cpp b/src/server.cpp
index f8248acb4..8bcfe5216 100644
--- a/src/server.cpp
+++ b/src/server.cpp
@@ -29,6 +29,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "jmutexautolock.h"
#include "main.h"
#include "constants.h"
+#include "voxel.h"
void * ServerThread::Thread()
{
@@ -990,6 +991,76 @@ void Server::AsyncRunStep()
/*
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
{
@@ -1458,6 +1529,31 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
(this takes some time so it is done after the quick stuff)
*/
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
/*
diff --git a/src/server.h b/src/server.h
index a48f88805..82e9136b5 100644
--- a/src/server.h
+++ b/src/server.h
@@ -469,6 +469,9 @@ private:
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 RemoteClient;
};
diff --git a/src/test.cpp b/src/test.cpp
index 6b285e3a4..ebefb8e32 100644
--- a/src/test.cpp
+++ b/src/test.cpp
@@ -148,10 +148,45 @@ struct TestVoxelManipulator
{
void Run()
{
+ /*
+ VoxelArea
+ */
+
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(-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;
v.print(dstream);
@@ -186,18 +221,19 @@ struct TestVoxelManipulator
v.clear();
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;
for(s16 z=0; z<size.Z; z++)
@@ -205,7 +241,7 @@ struct TestVoxelManipulator
for(s16 x=0; x<size.X; x++)
{
MapNode n;
- n.pressure = size.Y - y;
+ //n.pressure = size.Y - y;
if(*p == '#')
n.d = MATERIAL_STONE;
else if(*p == '.')
@@ -218,7 +254,24 @@ struct TestVoxelManipulator
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);
}
diff --git a/src/utility.h b/src/utility.h
index 93fd9d4a7..c9b13546c 100644
--- a/src/utility.h
+++ b/src/utility.h
@@ -394,12 +394,18 @@ private:
class TimeTaker
{
public:
- TimeTaker(const char *name, IrrlichtDevice *dev)
+ TimeTaker(const char *name, IrrlichtDevice *dev, u32 *result=NULL)
{
m_name = name;
m_dev = dev;
- m_time1 = m_dev->getTimer()->getRealTime();
+ m_result = result;
m_running = true;
+ if(dev == NULL)
+ {
+ m_time1 = 0;
+ return;
+ }
+ m_time1 = m_dev->getTimer()->getRealTime();
}
~TimeTaker()
{
@@ -409,10 +415,24 @@ public:
{
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 dtime = time2 - m_time1;
- if(quiet == false)
- std::cout<<m_name<<" took "<<dtime<<"ms"<<std::endl;
+ if(m_result != NULL)
+ {
+ (*m_result) += dtime;
+ }
+ else
+ {
+ if(quiet == false)
+ std::cout<<m_name<<" took "<<dtime<<"ms"<<std::endl;
+ }
m_running = false;
return dtime;
}
@@ -423,6 +443,7 @@ private:
IrrlichtDevice *m_dev;
u32 m_time1;
bool m_running;
+ u32 *m_result;
};
// Calculates the borders of a "d-radius" cube
diff --git a/src/voxel.cpp b/src/voxel.cpp
index fe176a27a..15624feed 100644
--- a/src/voxel.cpp
+++ b/src/voxel.cpp
@@ -20,6 +20,23 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "voxel.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():
m_data(NULL),
m_flags(NULL)
@@ -47,7 +64,7 @@ void VoxelManipulator::clear()
m_flags = NULL;
}
-void VoxelManipulator::print(std::ostream &o)
+void VoxelManipulator::print(std::ostream &o, VoxelPrintMode mode)
{
v3s16 em = m_area.getExtent();
v3s16 of = m_area.MinEdge;
@@ -78,8 +95,29 @@ void VoxelManipulator::print(std::ostream &o)
{
c = 'X';
u8 m = m_data[m_area.index(x,y,z)].d;
- if(m <= 9)
- c = m + '0';
+ u8 pr = m_data[m_area.index(x,y,z)].pressure;
+ if(mode == VOXELPRINT_MATERIAL)
+ {
+ if(m <= 9)
+ 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;
}
@@ -99,6 +137,8 @@ void VoxelManipulator::addArea(VoxelArea area)
if(m_area.contains(area))
return;
+ TimeTaker timer("addArea", g_device, &addarea_time);
+
// Calculate new area
VoxelArea new_area;
// New area is the requested area if m_area has zero volume
@@ -163,6 +203,21 @@ void VoxelManipulator::addArea(VoxelArea area)
delete[] old_data;
if(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)
@@ -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(0,1,0), // top
@@ -241,16 +607,43 @@ void VoxelManipulator::flowWater(v3s16 removed_pos)
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
- // TODO: A bigger area would be better
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 + 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)];
// Inexistent or checked nodes can't move
if(f & (VOXELFLAG_INEXISTENT | VOXELFLAG_CHECKED))
@@ -280,30 +673,66 @@ void VoxelManipulator::flowWater(v3s16 removed_pos)
// If there is nothing to move, return
if(i==6)
- return;
-
+ return false;
+
// 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)];
- 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_data[m_area.index(removed_pos)] = n;
+ m_data[m_area.index(removed_pos)].d = m;
m_flags[m_area.index(removed_pos)] = f;
- // Mark p checked
- m_flags[m_area.index(p)] |= VOXELFLAG_CHECKED;
+ // 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
- //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
- flowWater(p);
+ 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(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)];
// Water can't move to inexistent nodes
if(f & VOXELFLAG_INEXISTENT)
@@ -312,102 +741,291 @@ void VoxelManipulator::flowWater(v3s16 removed_pos)
// Water can only move to air
if(n.d != MATERIAL_AIR)
continue;
- flowWater(p);
+
+ // Flow water to node
+ bool moved =
+ flowWater(p, active_nodes, recursion_depth,
+ debugprint, counter, counterlimit);
+
+ if(moved)
+ {
+ // Search again from all neighbors
+ goto find_again;
+ }
}
-}
-/*
- MapVoxelManipulator
-*/
+ if(counter != NULL)
+ {
+ (*counter)++;
+ if((*counter) % 10 == 0)
+ dstream<<"flowWater(): moved "<<(*counter)<<" nodes"
+ <<std::endl;
-MapVoxelManipulator::MapVoxelManipulator(Map *map)
-{
- m_map = map;
+ if(counterlimit != -1 && (*counter) > counterlimit)
+ {
+ dstream<<"Counter limit reached; returning"<<std::endl;
+ throw ProcessingLimitException("flowWater counterlimit reached");
+ }
+ }
+
+ return true;
}
-
-void MapVoxelManipulator::emerge(VoxelArea a)
+bool VoxelManipulator::flowWater(v3s16 removed_pos,
+ core::map<v3s16, u8> &active_nodes,
+ int recursion_depth, bool debugprint,
+ int *counter, int counterlimit)
{
- v3s16 size = a.getExtent();
+ 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
+ };
- addArea(a);
+ 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;
- 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))
+ 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)];
+ // 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;
- try{
- MapNode n = m_map->getNode(a.MinEdge + p);
- m_data[i] = n;
- m_flags[i] = 0;
}
- catch(InvalidPositionException &e)
+ // Else block is at some side. Select it if it has enough pressure
+ if(n.pressure >= 2)
{
- m_flags[i] = VOXELFLAG_INEXISTENT;
+ break;
}
}
-}
-void MapVoxelManipulator::blitBack
- (core::map<v3s16, MapBlock*> & modified_blocks)
-{
- /*
- Initialize block cache
- */
- v3s16 blockpos_last;
- MapBlock *block = NULL;
- bool block_checked_in_modified = false;
+ // If there is nothing to move, return
+ if(i==6)
+ return 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++)
+ // 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)
{
- v3s16 p(x,y,z);
+ 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)];
- if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
+ // 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;
- v3s16 blockpos = getNodeBlockPos(p);
+ // Flow water to node
+ bool moved =
+ flowWater(p, active_nodes, recursion_depth,
+ debugprint, counter, counterlimit);
- try
+ if(moved)
{
- // 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;
+ // Search again from all neighbors
+ goto find_again;
+ }
+ }
- // Don't continue if nothing has changed here
- if(block->getNode(relpos) == n)
- continue;
+ if(counter != NULL)
+ {
+ (*counter)++;
+ if((*counter) % 10 == 0)
+ dstream<<"flowWater(): moved "<<(*counter)<<" nodes"
+ <<std::endl;
- //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;
- }
+ if(counterlimit != -1 && (*counter) > counterlimit)
+ {
+ dstream<<"Counter limit reached; returning"<<std::endl;
+ throw ProcessingLimitException("flowWater counterlimit reached");
}
- catch(InvalidPositionException &e)
+ }
+
+ 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
+ {
+
+ // Flow water to active nodes
+ for(;;)
+ {
+ // Clear check flags
+ clearFlag(VOXELFLAG_CHECKED);
+
+ if(active_nodes.size() == 0)
+ 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
diff --git a/src/voxel.h b/src/voxel.h
index 3a4dacd49..74c0a00e5 100644
--- a/src/voxel.h
+++ b/src/voxel.h
@@ -21,8 +21,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define VOXEL_HEADER
#include "common_irrlicht.h"
-#include "mapblock.h"
#include <iostream>
+#include "debug.h"
+#include "mapnode.h"
/*
A fast voxel manipulator class
@@ -31,6 +32,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
*/
/*
+ Debug stuff
+*/
+extern u32 emerge_time;
+extern u32 emerge_load_time;
+
+/*
This class resembles aabbox3d<s16> a lot, but has inclusive
edges for saner handling of integer sizes
*/
@@ -53,8 +60,18 @@ public:
MaxEdge(p)
{
}
+
+ /*
+ Modifying methods
+ */
+
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.Y < MinEdge.Y) MinEdge.Y = a.MinEdge.Y;
if(a.MinEdge.Z < MinEdge.Z) MinEdge.Z = a.MinEdge.Z;
@@ -64,6 +81,12 @@ public:
}
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.Y < MinEdge.Y) MinEdge.Y = p.Y;
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.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
{
return MaxEdge - MinEdge + v3s16(1,1,1);
@@ -80,8 +127,13 @@ public:
v3s16 e = getExtent();
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(
a.MinEdge.X >= MinEdge.X && a.MaxEdge.X <= MaxEdge.X &&
a.MinEdge.Y >= MinEdge.Y && a.MaxEdge.Y <= MaxEdge.Y &&
@@ -101,6 +153,95 @@ public:
return (MinEdge == other.MinEdge
&& 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
@@ -120,13 +261,15 @@ public:
void print(std::ostream &o) const
{
+ v3s16 e = getExtent();
o<<"("<<MinEdge.X
<<","<<MinEdge.Y
<<","<<MinEdge.Z
<<")("<<MaxEdge.X
<<","<<MaxEdge.Y
<<","<<MaxEdge.Z
- <<")";
+ <<")"
+ <<"="<<e.X<<"x"<<e.Y<<"x"<<e.Z<<"="<<getVolume();
}
// Edges are inclusive
@@ -139,18 +282,35 @@ public:
// Checked as being inexistent in source
#define VOXELFLAG_INEXISTENT (1<<1)
// Algorithm-dependent
+// flowWater: "visited"
#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:
VoxelManipulator();
- ~VoxelManipulator();
+ virtual ~VoxelManipulator();
/*
Virtuals from NodeContainer
*/
- virtual u16 nodeContainerId() const
+ /*virtual u16 nodeContainerId() const
{
return NODECONTAINER_ID_VOXELMANIPULATOR;
}
@@ -158,7 +318,7 @@ public:
{
emerge(p);
return !(m_flags[m_area.index(p)] & VOXELFLAG_INEXISTENT);
- }
+ }*/
// These are a bit slow and shouldn't be used internally
MapNode getNode(v3s16 p)
{
@@ -166,7 +326,7 @@ public:
if(m_flags[m_area.index(p)] & VOXELFLAG_INEXISTENT)
{
- dstream<<"ERROR: VoxelManipulator::getNode(): "
+ dstream<<"EXCEPT: VoxelManipulator::getNode(): "
<<"p=("<<p.X<<","<<p.Y<<","<<p.Z<<")"
<<", index="<<m_area.index(p)
<<", flags="<<(int)m_flags[m_area.index(p)]
@@ -214,19 +374,80 @@ public:
Control
*/
- void clear();
-
- void print(std::ostream &o);
+ virtual void clear();
+
+ void print(std::ostream &o, VoxelPrintMode mode=VOXELPRINT_MATERIAL);
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
*/
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
@@ -265,32 +486,24 @@ public:
MaxEdge is 1 higher than maximum allowed position
*/
VoxelArea m_area;
+
/*
NULL if data size is 0 (extent (0,0,0))
Data is stored as [z*h*w + y*h + x]
*/
MapNode *m_data;
+
/*
Flags of all nodes
*/
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:
};
-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