diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/exceptions.h | 8 | ||||
-rw-r--r-- | src/main.cpp | 5 | ||||
-rw-r--r-- | src/map.cpp | 149 | ||||
-rw-r--r-- | src/map.h | 34 | ||||
-rw-r--r-- | src/mapblock.cpp | 35 | ||||
-rw-r--r-- | src/mapblock.h | 68 | ||||
-rw-r--r-- | src/mapnode.h | 24 | ||||
-rw-r--r-- | src/serialization.h | 3 | ||||
-rw-r--r-- | src/server.cpp | 96 | ||||
-rw-r--r-- | src/server.h | 3 | ||||
-rw-r--r-- | src/test.cpp | 77 | ||||
-rw-r--r-- | src/utility.h | 29 | ||||
-rw-r--r-- | src/voxel.cpp | 788 | ||||
-rw-r--r-- | src/voxel.h | 267 |
14 files changed, 1420 insertions, 166 deletions
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 @@ -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 |