some work-in-progress water stuff
authorPerttu Ahola <celeron55@gmail.com>
Wed, 1 Dec 2010 13:20:12 +0000 (15:20 +0200)
committerPerttu Ahola <celeron55@gmail.com>
Wed, 1 Dec 2010 13:20:12 +0000 (15:20 +0200)
Makefile
src/main.cpp
src/mapnode.h
src/test.cpp
src/voxel.cpp
src/voxel.h

index 2b4e8dda3e20441aeb3f3543e4370bf6815b4c87..102a3dcdb3f6c032c7b91b37060c7a8da7aaee5c 100644 (file)
--- 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\r
 \r
 #CXXFLAGS = -O2 -ffast-math -Wall -fomit-frame-pointer -pipe\r
-CXXFLAGS = -O2 -ffast-math -Wall -g -pipe\r
+#CXXFLAGS = -O2 -ffast-math -Wall -g -pipe\r
 #CXXFLAGS = -O1 -ffast-math -Wall -g\r
-#CXXFLAGS = -Wall -g -O0\r
+CXXFLAGS = -Wall -g -O0\r
 \r
 #CXXFLAGS = -O3 -ffast-math -Wall\r
 #CXXFLAGS = -O3 -ffast-math -Wall -g\r
index 677f03843265c127fb8fca2f8c6091b15efcb186..938eb14ef842a42af9aa02ea693b45552545b013 100644 (file)
@@ -81,6 +81,9 @@ SUGGESTION: Use same technique for sector heightmaps as what we're
             using for UnlimitedHeightmap? (getting all neighbors\r
                        when generating)\r
 \r
+TODO: Proper handling of spawning place (try to find something that\r
+      is not in the middle of an ocean (some land to stand on at\r
+         least) and save it in map config.\r
 SUGG: Set server to automatically find a good spawning place in some\r
       place where there is water and land.\r
          - Map to have a getWalkableNear(p)\r
@@ -176,6 +179,197 @@ TODO: MovingObject::move and Player::move are basically the same.
 Doing now:\r
 ======================================================================\r
 \r
+Water dynamics pseudo-code (block = MapNode):\r
+SUGG: Create separate flag table in VoxelManipulator to allow fast\r
+clearing of "modified" flags\r
+\r
+neighborCausedPressure(pos):\r
+       pressure = 0\r
+       dirs = {down, left, right, back, front, up}\r
+       for d in dirs:\r
+               pos2 = pos + d\r
+               p = block_at(pos2).pressure\r
+               if d.Y == 1 and p > min:\r
+                       p -= 1\r
+               if d.Y == -1 and p < max:\r
+                       p += 1\r
+               if p > pressure:\r
+                       pressure = p\r
+       return pressure\r
+\r
+# This should somehow update all changed pressure values\r
+# in an unknown body of water\r
+updateWaterPressure(pos):\r
+       TODO\r
+\r
+FIXME: This goes in an indefinite loop when there is an underwater\r
+chamber like this:\r
+\r
+#111######\r
+#222##22##\r
+#33333333x<- block removed from here\r
+##########\r
+\r
+#111######\r
+#222##22##\r
+#3333333x1\r
+##########\r
+\r
+#111######\r
+#222##22##\r
+#333333x11\r
+##########\r
+\r
+#111######\r
+#222##2x##\r
+#333333333\r
+##########\r
+\r
+#111######\r
+#222##x2##\r
+#333333333\r
+##########\r
+\r
+Now, consider moving to the last block not allowed.\r
+\r
+Consider it a 3D case with a depth of 2. We're now at this situation.\r
+Note the additional blocking ## in the second depth plane.\r
+\r
+z=1         z=2\r
+#111######  #111######\r
+#222##x2##  #222##22##\r
+#333333333  #33333##33\r
+##########  ##########\r
+\r
+#111######  #111######\r
+#222##22##  #222##x2##\r
+#333333333  #33333##33\r
+##########  ##########\r
+\r
+#111######  #111######\r
+#222##22##  #222##2x##\r
+#333333333  #33333##33  \r
+##########  ##########\r
+\r
+Now there is nowhere to go, without going to an already visited block,\r
+but the pressure calculated in here from neighboring blocks is >= 2,\r
+so it is not the final ending.\r
+\r
+We will back up to a state where there is somewhere to go to.\r
+It is this state:\r
+\r
+#111######  #111######\r
+#222##22##  #222##22##\r
+#333333x33  #33333##33\r
+##########  ##########\r
+\r
+Then just go on, avoiding already visited blocks:\r
+\r
+#111######  #111######\r
+#222##22##  #222##22##\r
+#33333x333  #33333##33\r
+##########  ##########\r
+\r
+#111######  #111######\r
+#222##22##  #222##22##\r
+#3333x3333  #33333##33\r
+##########  ##########\r
+\r
+#111######  #111######\r
+#222##22##  #222##22##\r
+#333x33333  #33333##33\r
+##########  ##########\r
+\r
+#111######  #111######\r
+#222##22##  #222##22##\r
+#33x333333  #33333##33\r
+##########  ##########\r
+\r
+#111######  #111######\r
+#22x##22##  #222##22##\r
+#333333333  #33333##33\r
+##########  ##########\r
+\r
+#11x######  #111######\r
+#222##22##  #222##22##\r
+#333333333  #33333##33\r
+##########  ##########\r
+\r
+"Blob". the air bubble finally got out of the water.\r
+Then return recursively to a state where there is air next to water,\r
+clear the visit flags and feed the neighbor of the water recursively\r
+to the algorithm.\r
+\r
+#11 ######  #111######\r
+#222##22##  #222##22##\r
+#333333333x #33333##33\r
+##########  ##########\r
+\r
+#11 ######  #111######\r
+#222##22##  #222##22##\r
+#33333333x3 #33333##33\r
+##########  ##########\r
+\r
+...and so on.\r
+\r
+\r
+# removed_pos: a position that has been changed from something to air\r
+flowWater(removed_pos):\r
+       dirs = {top, left, right, back, front, bottom}\r
+       selected_dir = None\r
+       for d in dirs:\r
+               b2 = removed_pos + d\r
+\r
+               # Ignore positions that don't have water\r
+               if block_at(b2) != water:\r
+                       continue\r
+\r
+               # Ignore positions that have already been checked\r
+               if block_at(b2).checked:\r
+                       continue\r
+\r
+               # If block is at top, select it always.\r
+               if d.Y == 1:\r
+                       selected_dir = d\r
+                       break\r
+\r
+               # If block is at bottom, select it if it has enough pressure.\r
+               # >= 3 needed for stability (and sanity)\r
+               if d.Y == -1:\r
+                       if block_at(b2).pressure >= 3:\r
+                               selected_dir = d\r
+                               break\r
+                       continue\r
+               \r
+               # Else block is at some side. select it if it has enough pressure.\r
+               if block_at(b2).pressure >= 2:\r
+                       selected_dir = d\r
+                       break\r
+       \r
+       # If there is nothing to do anymore, return.\r
+       if selected_dir == None\r
+               return\r
+       \r
+       b2 = removed_pos + selected_dir\r
+       \r
+       # Move block\r
+       set_block(removed_pos, block_at(b2))\r
+       set_block(b2, air_block)\r
+       \r
+       # Update pressure\r
+       updateWaterPressure(removed_pos)\r
+       \r
+       # Flow water to the newly created empty position\r
+       flowWater(b2)\r
+\r
+       # Check empty positions around and try flowing water to them\r
+       for d in dirs:\r
+               b3 = removed_pos + d\r
+               # Ignore positions that are not air\r
+               if block_at(b3) is not air:\r
+                       continue\r
+               flowWater(b3)\r
+\r
 \r
 ======================================================================\r
 \r
index 789cedb275e237885032cd35d4f2ce30e97ac057..02abe4e520dea1be3400722a6db22c0760768b50 100644 (file)
@@ -35,9 +35,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 /*
        Ignored node.
 
-       param is used for custom information in special containers,
-       like VoxelManipulator.
-
        Anything that stores MapNodes doesn't have to preserve parameters
        associated with this material.
        
@@ -67,27 +64,12 @@ enum Material
 
        MATERIAL_GRASS,
 
-       /*
-               For water, the param is water pressure. 0...127.
-               TODO: No, at least the lowest nibble is used for lighting.
-               
-               - Water will be a bit like light, but with different flow
-                 behavior.
-               - Water blocks will fall down if there is empty space below.
-               - If there is water below, the pressure of the block below is
-                 the pressure of the current block + 1, or higher.
-               - If there is any pressure in a horizontally neighboring
-                 block, a water block will try to move away from it.
-               - If there is >=2 of pressure in a block below, water will
-                 try to move upwards.
-               - NOTE: To keep large operations fast, we have to keep a
-                       cache of the water-air-surfaces, just like with light
-       */
        MATERIAL_WATER,
 
        MATERIAL_LIGHT,
 
        MATERIAL_TREE,
+       
        MATERIAL_LEAVES,
 
        MATERIAL_GRASS_FOOTSTEPS,
@@ -216,6 +198,8 @@ struct MapNode
        */
        s8 param;
 
+       u8 pressure;
+
        MapNode(const MapNode & n)
        {
                *this = n;
index 005db2d24487eea08579f2fa2cefcac196ddbab3..6b285e3a46475456b11f8c7d88315c22735a3e48 100644 (file)
@@ -157,17 +157,16 @@ struct TestVoxelManipulator
                v.print(dstream);
 
                dstream<<"*** Setting (-1,0,-1)=2 ***"<<std::endl;
-
-               //v[v3s16(-1,0,-1)] = MapNode(2);
-               v[v3s16(-1,0,-1)].d = 2;
+               
+               v.setNodeNoRef(v3s16(-1,0,-1), MapNode(2));
 
                v.print(dstream);
 
-               assert(v[v3s16(-1,0,-1)].d == 2);
+               assert(v.getNode(v3s16(-1,0,-1)).d == 2);
 
                dstream<<"*** Reading from inexistent (0,0,-1) ***"<<std::endl;
 
-               assert(v[v3s16(0,0,-1)].d == MATERIAL_IGNORE);
+               EXCEPTION_CHECK(InvalidPositionException, v.getNode(v3s16(0,0,-1)));
 
                v.print(dstream);
 
@@ -177,9 +176,51 @@ struct TestVoxelManipulator
                
                v.print(dstream);
 
-               assert(v[v3s16(-1,0,-1)].d == 2);
-               assert(v[v3s16(0,1,1)].d == MATERIAL_IGNORE);
+               assert(v.getNode(v3s16(-1,0,-1)).d == 2);
+               EXCEPTION_CHECK(InvalidPositionException, v.getNode(v3s16(0,1,1)));
+
+               /*
+                       Water stuff
+               */
+
+               v.clear();
+
+               const char *content =
+                       "#...######"
+                       "#...##..##"
+                       "#........ "
+                       "##########"
+
+                       "#...######"
+                       "#...##..##"
+                       "#........ "
+                       "##########"
+               ;
+
+               v3s16 size(10, 4, 2);
                
+               const char *p = content;
+               for(s16 z=0; z<size.Z; z++)
+               for(s16 y=size.Y-1; y>=0; y--)
+               for(s16 x=0; x<size.X; x++)
+               {
+                       MapNode n;
+                       n.pressure = size.Y - y;
+                       if(*p == '#')
+                               n.d = MATERIAL_STONE;
+                       else if(*p == '.')
+                               n.d = MATERIAL_WATER;
+                       else if(*p == ' ')
+                               n.d = MATERIAL_AIR;
+                       else
+                               assert(0);
+                       v.setNode(v3s16(x,y,z), n);
+                       p++;
+               }
+
+               v.print(dstream);
+               
+               //assert(0);
        }
 };
 
index fc3b4c428681ffa31f422acf79052e486f2dc70a..fe176a27a7d55d0998432dff54af85f3f07bd92d 100644 (file)
@@ -21,36 +21,98 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "map.h"
 
 VoxelManipulator::VoxelManipulator():
-       m_data(NULL)
+       m_data(NULL),
+       m_flags(NULL)
 {
 }
 
 VoxelManipulator::~VoxelManipulator()
 {
+       clear();
        if(m_data)
                delete[] m_data;
+       if(m_flags)
+               delete[] m_flags;
+}
+
+void VoxelManipulator::clear()
+{
+       // Reset area to volume=0
+       m_area = VoxelArea();
+       if(m_data)
+               delete[] m_data;
+       m_data = NULL;
+       if(m_flags)
+               delete[] m_flags;
+       m_flags = NULL;
+}
+
+void VoxelManipulator::print(std::ostream &o)
+{
+       v3s16 em = m_area.getExtent();
+       v3s16 of = m_area.MinEdge;
+       o<<"size: "<<em.X<<"x"<<em.Y<<"x"<<em.Z
+        <<" offset: ("<<of.X<<","<<of.Y<<","<<of.Z<<")"<<std::endl;
+       
+       for(s32 y=m_area.MaxEdge.Y; y>=m_area.MinEdge.Y; y--)
+       {
+               if(em.X >= 3 && em.Y >= 3)
+               {
+                       if     (y==m_area.MinEdge.Y+2) o<<"^     ";
+                       else if(y==m_area.MinEdge.Y+1) o<<"|     ";
+                       else if(y==m_area.MinEdge.Y+0) o<<"y x-> ";
+                       else                           o<<"      ";
+               }
+
+               for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
+               {
+                       for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
+                       {
+                               u8 f = m_flags[m_area.index(x,y,z)];
+                               char c;
+                               if(f & VOXELFLAG_NOT_LOADED)
+                                       c = 'N';
+                               else if(f & VOXELFLAG_INEXISTENT)
+                                       c = 'I';
+                               else
+                               {
+                                       c = 'X';
+                                       u8 m = m_data[m_area.index(x,y,z)].d;
+                                       if(m <= 9)
+                                               c = m + '0';
+                               }
+                               o<<c;
+                       }
+                       o<<' ';
+               }
+               o<<std::endl;
+       }
 }
 
 void VoxelManipulator::addArea(VoxelArea area)
 {
+       // Cancel if requested area has zero volume
        if(area.getExtent() == v3s16(0,0,0))
                return;
        
+       // Cancel if m_area already contains the requested area
+       if(m_area.contains(area))
+               return;
+       
        // Calculate new area
        VoxelArea new_area;
+       // New area is the requested area if m_area has zero volume
        if(m_area.getExtent() == v3s16(0,0,0))
        {
                new_area = area;
        }
+       // Else add requested area to m_area
        else
        {
                new_area = m_area;
                new_area.addArea(area);
        }
 
-       if(new_area == m_area)
-               return;
-
        s32 new_size = new_area.getVolume();
 
        /*dstream<<"adding area ";
@@ -63,11 +125,11 @@ void VoxelManipulator::addArea(VoxelArea area)
        dstream<<std::endl;*/
 
        // Allocate and clear new data
-       MapNode *new_data;
-       new_data = new MapNode[new_size];
+       MapNode *new_data = new MapNode[new_size];
+       u8 *new_flags = new u8[new_size];
        for(s32 i=0; i<new_size; i++)
        {
-               new_data[i].d = MATERIAL_IGNORE;
+               new_flags[i] = VOXELFLAG_NOT_LOADED;
        }
        
        // Copy old data
@@ -76,48 +138,31 @@ void VoxelManipulator::addArea(VoxelArea area)
        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++)
        {
-               new_data[new_area.index(z,y,x)] = m_data[m_area.index(x,y,z)];
+               // If loaded, copy data and flags
+               if((m_flags[m_area.index(x,y,z)] & VOXELFLAG_NOT_LOADED) == false)
+               {
+                       new_data[new_area.index(x,y,z)] = m_data[m_area.index(x,y,z)];
+                       new_flags[new_area.index(x,y,z)] = m_flags[m_area.index(x,y,z)];
+               }
        }
 
-       // Replace member
+       // Replace area, data and flags
+       
        m_area = new_area;
+       
        MapNode *old_data = m_data;
-       m_data = new_data;
-       delete[] old_data;
-}
+       u8 *old_flags = m_flags;
 
-void VoxelManipulator::print(std::ostream &o)
-{
-       v3s16 em = m_area.getExtent();
-       v3s16 of = m_area.MinEdge;
-       o<<"size: "<<em.X<<"x"<<em.Y<<"x"<<em.Z
-        <<" offset: ("<<of.X<<","<<of.Y<<","<<of.Z<<")"<<std::endl;
-       
-       for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
-       {
-               if(em.X >= 3 && em.Y >= 3)
-               {
-                       if(y==m_area.MinEdge.Y+0) o<<"y x-> ";
-                       if(y==m_area.MinEdge.Y+1) o<<"|     ";
-                       if(y==m_area.MinEdge.Y+2) o<<"V     ";
-               }
+       /*dstream<<"old_data="<<(int)old_data<<", new_data="<<(int)new_data
+       <<", old_flags="<<(int)m_flags<<", new_flags="<<(int)new_flags<<std::endl;*/
 
-               for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
-               {
-                       for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
-                       {
-                               u8 m = m_data[m_area.index(x,y,z)].d;
-                               char c = 'X';
-                               if(m == MATERIAL_IGNORE)
-                                       c = 'I';
-                               else if(m <= 9)
-                                       c = m + '0';
-                               o<<c;
-                       }
-                       o<<' ';
-               }
-               o<<std::endl;
-       }
+       m_data = new_data;
+       m_flags = new_flags;
+       
+       if(old_data)
+               delete[] old_data;
+       if(old_flags)
+               delete[] old_flags;
 }
 
 void VoxelManipulator::interpolate(VoxelArea area)
@@ -156,10 +201,13 @@ void VoxelManipulator::interpolate(VoxelArea area)
                {
                        v3s16 p2 = p + dirs[i];
 
-                       MapNode &n = m_data[m_area.index(p2)];
-                       if(n.d == MATERIAL_IGNORE)
+                       u8 f = m_flags[m_area.index(p2)];
+                       assert(!(f & VOXELFLAG_NOT_LOADED));
+                       if(f & VOXELFLAG_INEXISTENT)
                                continue;
 
+                       MapNode &n = m_data[m_area.index(p2)];
+
                        airness += (n.d == MATERIAL_AIR) ? 1 : -1;
                        total++;
 
@@ -182,57 +230,91 @@ void VoxelManipulator::interpolate(VoxelArea area)
        }
 }
 
-#if 0
-void VoxelManipulator::blitFromNodeContainer
-               (v3s16 p_from, v3s16 p_to, v3s16 size, NodeContainer *c)
+void VoxelManipulator::flowWater(v3s16 removed_pos)
 {
-       VoxelArea a_to(p_to, p_to+size-v3s16(1,1,1));
-       addArea(a_to);
-       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 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
+       };
+
+       v3s16 p;
+
+       // Load neighboring nodes
+       // TODO: A bigger area would be better
+       emerge(VoxelArea(removed_pos - v3s16(1,1,1), removed_pos + v3s16(1,1,1)));
+
+       s32 i;
+       for(i=0; i<6; i++)
        {
-               v3s16 p(x,y,z);
-               try{
-                       MapNode n = c->getNode(p_from + p);
-                       m_data[m_area.index(p_to + p)] = n;
-               }
-               catch(InvalidPositionException &e)
+               p = removed_pos + dirs[i];
+               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;
                }
-               
-               /*v3s16 p(x,y,z);
-               MapNode n(MATERIAL_IGNORE);
-               try{
-                       n = c->getNode(p_from + p);
+               // If block is at bottom, select it if it has enough pressure
+               if(i == 5)
+               {
+                       if(n.pressure >= 3)
+                               break;
+                       continue;
                }
-               catch(InvalidPositionException &e)
+               // Else block is at some side. Select it if it has enough pressure
+               if(n.pressure >= 2)
                {
+                       break;
                }
-               m_data[m_area.index(p_to + p)] = n;*/
        }
-}
 
-void VoxelManipulator::blitToNodeContainer
-               (v3s16 p_from, v3s16 p_to, v3s16 size, NodeContainer *c)
-{
-       for(s16 z=0; z<size.Z; z++)
-       for(s16 y=0; y<size.Y; y++)
-       for(s16 x=0; x<size.X; x++)
+       // If there is nothing to move, return
+       if(i==6)
+               return;
+       
+       // Switch nodes at p and removed_pos
+       MapNode n = m_data[m_area.index(p)];
+       u8 f = m_flags[m_area.index(p)];
+       m_data[m_area.index(p)] = m_data[m_area.index(removed_pos)];
+       m_flags[m_area.index(p)] = m_flags[m_area.index(removed_pos)];
+       m_data[m_area.index(removed_pos)] = n;
+       m_flags[m_area.index(removed_pos)] = f;
+
+       // Mark p checked
+       m_flags[m_area.index(p)] |= VOXELFLAG_CHECKED;
+       
+       // Update pressure
+       //TODO
+
+       // Flow water to the newly created empty position
+       flowWater(p);
+       
+       // 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--)
        {
-               v3s16 p(x,y,z);
-               try{
-                       MapNode &n = m_data[m_area.index(p_from + p)];
-                       if(n.d == MATERIAL_IGNORE)
-                               continue;
-                       c->setNode(p_to + p, n);
-               }
-               catch(InvalidPositionException &e)
-               {
-               }
+               p = removed_pos + dirs[i];
+               u8 f = m_flags[m_area.index(p)];
+               // Water can't move to inexistent nodes
+               if(f & VOXELFLAG_INEXISTENT)
+                       continue;
+               MapNode &n = m_data[m_area.index(p)];
+               // Water can only move to air
+               if(n.d != MATERIAL_AIR)
+                       continue;
+               flowWater(p);
        }
 }
-#endif
 
 /*
        MapVoxelManipulator
@@ -254,12 +336,18 @@ void MapVoxelManipulator::emerge(VoxelArea a)
        for(s16 x=0; x<size.X; x++)
        {
                v3s16 p(x,y,z);
+               s32 i = m_area.index(a.MinEdge + p);
+               // Don't touch nodes that have already been loaded
+               if(!(m_flags[i] & VOXELFLAG_NOT_LOADED))
+                       continue;
                try{
                        MapNode n = m_map->getNode(a.MinEdge + p);
-                       m_data[m_area.index(a.MinEdge + p)] = n;
+                       m_data[i] = n;
+                       m_flags[i] = 0;
                }
                catch(InvalidPositionException &e)
                {
+                       m_flags[i] = VOXELFLAG_INEXISTENT;
                }
        }
 }
@@ -280,9 +368,11 @@ void MapVoxelManipulator::blitBack
        {
                v3s16 p(x,y,z);
 
-               MapNode &n = m_data[m_area.index(p)];
-               if(n.d == MATERIAL_IGNORE)
+               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);
                
index 2bc591d2a292bd9c8acbcb2f2f28ae8947a7c6f8..3a4dacd4909b94d90b0a2dd51498e51b1290d37c 100644 (file)
@@ -25,7 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include <iostream>
 
 /*
-       TODO: A fast voxel manipulator class
+       A fast voxel manipulator class
 
        Not thread-safe.
 */
@@ -71,16 +71,24 @@ public:
                if(p.Y > MaxEdge.Y) MaxEdge.Y = p.Y;
                if(p.Z > MaxEdge.Z) MaxEdge.Z = p.Z;
        }
-       v3s16 getExtent()
+       v3s16 getExtent() const
        {
                return MaxEdge - MinEdge + v3s16(1,1,1);
        }
-       s32 getVolume()
+       s32 getVolume() const
        {
                v3s16 e = getExtent();
                return (s32)e.X * (s32)e.Y * (s32)e.Z;
        }
-       bool isInside(v3s16 p)
+       bool contains(VoxelArea &a) const
+       {
+               return(
+                       a.MinEdge.X >= MinEdge.X && a.MaxEdge.X <= MaxEdge.X &&
+                       a.MinEdge.Y >= MinEdge.Y && a.MaxEdge.Y <= MaxEdge.Y &&
+                       a.MinEdge.Z >= MinEdge.Z && a.MaxEdge.Z <= MaxEdge.Z
+               );
+       }
+       bool contains(v3s16 p) const
        {
                return(
                        p.X >= MinEdge.X && p.X <= MaxEdge.X &&
@@ -88,7 +96,7 @@ public:
                        p.Z >= MinEdge.Z && p.Z <= MaxEdge.Z
                );
        }
-       bool operator==(const VoxelArea &other)
+       bool operator==(const VoxelArea &other) const
        {
                return (MinEdge == other.MinEdge
                                && MaxEdge == other.MaxEdge);
@@ -97,7 +105,7 @@ public:
        /*
                Translates position from virtual coordinates to array index
        */
-       s32 index(s16 x, s16 y, s16 z)
+       s32 index(s16 x, s16 y, s16 z) const
        {
                v3s16 em = getExtent();
                v3s16 off = MinEdge;
@@ -105,12 +113,12 @@ public:
                //dstream<<" i("<<x<<","<<y<<","<<z<<")="<<i<<" ";
                return i;
        }
-       s32 index(v3s16 p)
+       s32 index(v3s16 p) const
        {
                return index(p.X, p.Y, p.Z);
        }
 
-       void print(std::ostream &o)
+       void print(std::ostream &o) const
        {
                o<<"("<<MinEdge.X
                 <<","<<MinEdge.Y
@@ -126,6 +134,13 @@ public:
        v3s16 MaxEdge;
 };
 
+// Hasn't been copied from source (emerged)
+#define VOXELFLAG_NOT_LOADED (1<<0)
+// Checked as being inexistent in source
+#define VOXELFLAG_INEXISTENT (1<<1)
+// Algorithm-dependent
+#define VOXELFLAG_CHECKED (1<<2)
+
 class VoxelManipulator : public NodeContainer
 {
 public:
@@ -141,53 +156,77 @@ public:
        }
        bool isValidPosition(v3s16 p)
        {
-               return m_area.isInside(p);
+               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)
        {
-               if(isValidPosition(p) == false)
-                       emerge(VoxelArea(p));
+               emerge(p);
 
-               MapNode &n = m_data[m_area.index(p)];
-
-               //TODO: Is this the right behaviour?
-               if(n.d == MATERIAL_IGNORE)
+               if(m_flags[m_area.index(p)] & VOXELFLAG_INEXISTENT)
+               {
+                       dstream<<"ERROR: VoxelManipulator::getNode(): "
+                                       <<"p=("<<p.X<<","<<p.Y<<","<<p.Z<<")"
+                                       <<", index="<<m_area.index(p)
+                                       <<", flags="<<(int)m_flags[m_area.index(p)]
+                                       <<" is inexistent"<<std::endl;
                        throw InvalidPositionException
-                       ("Not returning MATERIAL_IGNORE in VoxelManipulator");
+                       ("VoxelManipulator: getNode: inexistent");
+               }
 
-               return n;
+               return m_data[m_area.index(p)];
        }
-       void setNode(v3s16 p, MapNode & n)
+       void setNode(v3s16 p, MapNode &n)
        {
-               if(isValidPosition(p) == false)
-                       emerge(VoxelArea(p));
+               emerge(p);
+               
                m_data[m_area.index(p)] = n;
+               m_flags[m_area.index(p)] &= ~VOXELFLAG_INEXISTENT;
+               m_flags[m_area.index(p)] &= ~VOXELFLAG_NOT_LOADED;
        }
-       
-       MapNode & operator[](v3s16 p)
+       void setNodeNoRef(v3s16 p, MapNode n)
+       {
+               setNode(p, n);
+       }
+
+       /*void setExists(VoxelArea a)
+       {
+               emerge(a);
+               for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
+               for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
+               for(s32 x=a.MinEdge.X; x<=a.MaxEdge.X; x++)
+               {
+                       m_flags[m_area.index(x,y,z)] &= ~VOXELFLAG_INEXISTENT;
+               }
+       }*/
+
+       /*MapNode & operator[](v3s16 p)
        {
                //dstream<<"operator[] p=("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;
                if(isValidPosition(p) == false)
                        emerge(VoxelArea(p));
+               
                return m_data[m_area.index(p)];
-       }
+       }*/
 
        /*
-               Manipulation of bigger chunks
+               Control
        */
+
+       void clear();
        
        void print(std::ostream &o);
        
        void addArea(VoxelArea area);
 
+       /*
+               Algorithms
+       */
+
        void interpolate(VoxelArea area);
 
-       /*void blitFromNodeContainer
-                       (v3s16 p_from, v3s16 p_to, v3s16 size, NodeContainer *c);
-       
-       void blitToNodeContainer
-                       (v3s16 p_from, v3s16 p_to, v3s16 size, NodeContainer *c);*/
+       void flowWater(v3s16 removed_pos);
 
        /*
                Virtual functions
@@ -195,13 +234,25 @@ public:
        
        /*
                Get the contents of the requested area from somewhere.
+               Shall touch only nodes that have VOXELFLAG_NOT_LOADED
+               Shall reset VOXELFLAG_NOT_LOADED
 
-               If not found from source, add as MATERIAL_IGNORE.
+               If not found from source, add with VOXELFLAG_INEXISTENT
        */
        virtual void emerge(VoxelArea a)
        {
                //dstream<<"emerge p=("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;
                addArea(a);
+               for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
+               for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
+               for(s32 x=a.MinEdge.X; x<=a.MaxEdge.X; x++)
+               {
+                       s32 i = m_area.index(x,y,z);
+                       // Don't touch nodes that have already been loaded
+                       if(!(m_flags[i] & VOXELFLAG_NOT_LOADED))
+                               continue;
+                       m_flags[i] = VOXELFLAG_INEXISTENT;
+               }
        }
 
        /*
@@ -215,12 +266,14 @@ public:
        */
        VoxelArea m_area;
        /*
-               NULL if data size is 0
+               NULL if data size is 0 (extent (0,0,0))
                Data is stored as [z*h*w + y*h + x]
-               Special data values:
-                       MATERIAL_IGNORE: Unspecified node
        */
        MapNode *m_data;
+       /*
+               Flags of all nodes
+       */
+       u8 *m_flags;
 private:
 };