aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorPerttu Ahola <celeron55@gmail.com>2010-12-01 15:20:12 +0200
committerPerttu Ahola <celeron55@gmail.com>2010-12-01 15:20:12 +0200
commit5e0c284f3a8debdd9ebb080f80e36dceb7bc4ce2 (patch)
treed9c4f61f491b57ad7b84df0925186e2183da140b /src
parent38353751c9f4e03fb6a0b855e6d8b5691af71dc0 (diff)
downloadminetest-5e0c284f3a8debdd9ebb080f80e36dceb7bc4ce2.tar.gz
minetest-5e0c284f3a8debdd9ebb080f80e36dceb7bc4ce2.tar.bz2
minetest-5e0c284f3a8debdd9ebb080f80e36dceb7bc4ce2.zip
some work-in-progress water stuff
Diffstat (limited to 'src')
-rw-r--r--src/main.cpp194
-rw-r--r--src/mapnode.h22
-rw-r--r--src/test.cpp55
-rw-r--r--src/voxel.cpp264
-rw-r--r--src/voxel.h119
5 files changed, 508 insertions, 146 deletions
diff --git a/src/main.cpp b/src/main.cpp
index 677f03843..938eb14ef 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -81,6 +81,9 @@ SUGGESTION: Use same technique for sector heightmaps as what we're
using for UnlimitedHeightmap? (getting all neighbors
when generating)
+TODO: Proper handling of spawning place (try to find something that
+ is not in the middle of an ocean (some land to stand on at
+ least) and save it in map config.
SUGG: Set server to automatically find a good spawning place in some
place where there is water and land.
- Map to have a getWalkableNear(p)
@@ -176,6 +179,197 @@ TODO: MovingObject::move and Player::move are basically the same.
Doing now:
======================================================================
+Water dynamics pseudo-code (block = MapNode):
+SUGG: Create separate flag table in VoxelManipulator to allow fast
+clearing of "modified" flags
+
+neighborCausedPressure(pos):
+ pressure = 0
+ dirs = {down, left, right, back, front, up}
+ for d in dirs:
+ pos2 = pos + d
+ p = block_at(pos2).pressure
+ if d.Y == 1 and p > min:
+ p -= 1
+ if d.Y == -1 and p < max:
+ p += 1
+ if p > pressure:
+ pressure = p
+ return pressure
+
+# This should somehow update all changed pressure values
+# in an unknown body of water
+updateWaterPressure(pos):
+ TODO
+
+FIXME: This goes in an indefinite loop when there is an underwater
+chamber like this:
+
+#111######
+#222##22##
+#33333333x<- block removed from here
+##########
+
+#111######
+#222##22##
+#3333333x1
+##########
+
+#111######
+#222##22##
+#333333x11
+##########
+
+#111######
+#222##2x##
+#333333333
+##########
+
+#111######
+#222##x2##
+#333333333
+##########
+
+Now, consider moving to the last block not allowed.
+
+Consider it a 3D case with a depth of 2. We're now at this situation.
+Note the additional blocking ## in the second depth plane.
+
+z=1 z=2
+#111###### #111######
+#222##x2## #222##22##
+#333333333 #33333##33
+########## ##########
+
+#111###### #111######
+#222##22## #222##x2##
+#333333333 #33333##33
+########## ##########
+
+#111###### #111######
+#222##22## #222##2x##
+#333333333 #33333##33
+########## ##########
+
+Now there is nowhere to go, without going to an already visited block,
+but the pressure calculated in here from neighboring blocks is >= 2,
+so it is not the final ending.
+
+We will back up to a state where there is somewhere to go to.
+It is this state:
+
+#111###### #111######
+#222##22## #222##22##
+#333333x33 #33333##33
+########## ##########
+
+Then just go on, avoiding already visited blocks:
+
+#111###### #111######
+#222##22## #222##22##
+#33333x333 #33333##33
+########## ##########
+
+#111###### #111######
+#222##22## #222##22##
+#3333x3333 #33333##33
+########## ##########
+
+#111###### #111######
+#222##22## #222##22##
+#333x33333 #33333##33
+########## ##########
+
+#111###### #111######
+#222##22## #222##22##
+#33x333333 #33333##33
+########## ##########
+
+#111###### #111######
+#22x##22## #222##22##
+#333333333 #33333##33
+########## ##########
+
+#11x###### #111######
+#222##22## #222##22##
+#333333333 #33333##33
+########## ##########
+
+"Blob". the air bubble finally got out of the water.
+Then return recursively to a state where there is air next to water,
+clear the visit flags and feed the neighbor of the water recursively
+to the algorithm.
+
+#11 ###### #111######
+#222##22## #222##22##
+#333333333x #33333##33
+########## ##########
+
+#11 ###### #111######
+#222##22## #222##22##
+#33333333x3 #33333##33
+########## ##########
+
+...and so on.
+
+
+# removed_pos: a position that has been changed from something to air
+flowWater(removed_pos):
+ dirs = {top, left, right, back, front, bottom}
+ selected_dir = None
+ for d in dirs:
+ b2 = removed_pos + d
+
+ # Ignore positions that don't have water
+ if block_at(b2) != water:
+ continue
+
+ # Ignore positions that have already been checked
+ if block_at(b2).checked:
+ continue
+
+ # If block is at top, select it always.
+ if d.Y == 1:
+ selected_dir = d
+ break
+
+ # If block is at bottom, select it if it has enough pressure.
+ # >= 3 needed for stability (and sanity)
+ if d.Y == -1:
+ if block_at(b2).pressure >= 3:
+ selected_dir = d
+ break
+ continue
+
+ # Else block is at some side. select it if it has enough pressure.
+ if block_at(b2).pressure >= 2:
+ selected_dir = d
+ break
+
+ # If there is nothing to do anymore, return.
+ if selected_dir == None
+ return
+
+ b2 = removed_pos + selected_dir
+
+ # Move block
+ set_block(removed_pos, block_at(b2))
+ set_block(b2, air_block)
+
+ # Update pressure
+ updateWaterPressure(removed_pos)
+
+ # Flow water to the newly created empty position
+ flowWater(b2)
+
+ # Check empty positions around and try flowing water to them
+ for d in dirs:
+ b3 = removed_pos + d
+ # Ignore positions that are not air
+ if block_at(b3) is not air:
+ continue
+ flowWater(b3)
+
======================================================================
diff --git a/src/mapnode.h b/src/mapnode.h
index 789cedb27..02abe4e52 100644
--- a/src/mapnode.h
+++ b/src/mapnode.h
@@ -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;
diff --git a/src/test.cpp b/src/test.cpp
index 005db2d24..6b285e3a4 100644
--- a/src/test.cpp
+++ b/src/test.cpp
@@ -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);
}
};
diff --git a/src/voxel.cpp b/src/voxel.cpp
index fc3b4c428..fe176a27a 100644
--- a/src/voxel.cpp
+++ b/src/voxel.cpp
@@ -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);
diff --git a/src/voxel.h b/src/voxel.h
index 2bc591d2a..3a4dacd49 100644
--- a/src/voxel.h
+++ b/src/voxel.h
@@ -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:
};