some work-in-progress water stuff

This commit is contained in:
Perttu Ahola 2010-12-01 15:20:12 +02:00
parent 38353751c9
commit 5e0c284f3a
6 changed files with 517 additions and 155 deletions

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

@ -81,6 +81,9 @@ SUGGESTION: Use same technique for sector heightmaps as what we're
using for UnlimitedHeightmap? (getting all neighbors using for UnlimitedHeightmap? (getting all neighbors
when generating) 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 SUGG: Set server to automatically find a good spawning place in some
place where there is water and land. place where there is water and land.
- Map to have a getWalkableNear(p) - Map to have a getWalkableNear(p)
@ -176,6 +179,197 @@ TODO: MovingObject::move and Player::move are basically the same.
Doing now: 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)
====================================================================== ======================================================================

@ -35,9 +35,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
/* /*
Ignored node. Ignored node.
param is used for custom information in special containers,
like VoxelManipulator.
Anything that stores MapNodes doesn't have to preserve parameters Anything that stores MapNodes doesn't have to preserve parameters
associated with this material. associated with this material.
@ -67,27 +64,12 @@ enum Material
MATERIAL_GRASS, 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_WATER,
MATERIAL_LIGHT, MATERIAL_LIGHT,
MATERIAL_TREE, MATERIAL_TREE,
MATERIAL_LEAVES, MATERIAL_LEAVES,
MATERIAL_GRASS_FOOTSTEPS, MATERIAL_GRASS_FOOTSTEPS,
@ -216,6 +198,8 @@ struct MapNode
*/ */
s8 param; s8 param;
u8 pressure;
MapNode(const MapNode & n) MapNode(const MapNode & n)
{ {
*this = n; *this = n;

@ -158,16 +158,15 @@ struct TestVoxelManipulator
dstream<<"*** Setting (-1,0,-1)=2 ***"<<std::endl; dstream<<"*** Setting (-1,0,-1)=2 ***"<<std::endl;
//v[v3s16(-1,0,-1)] = MapNode(2); v.setNodeNoRef(v3s16(-1,0,-1), MapNode(2));
v[v3s16(-1,0,-1)].d = 2;
v.print(dstream); 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; 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); v.print(dstream);
@ -177,9 +176,51 @@ struct TestVoxelManipulator
v.print(dstream); v.print(dstream);
assert(v[v3s16(-1,0,-1)].d == 2); assert(v.getNode(v3s16(-1,0,-1)).d == 2);
assert(v[v3s16(0,1,1)].d == MATERIAL_IGNORE); 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);
} }
}; };

@ -21,36 +21,98 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "map.h" #include "map.h"
VoxelManipulator::VoxelManipulator(): VoxelManipulator::VoxelManipulator():
m_data(NULL) m_data(NULL),
m_flags(NULL)
{ {
} }
VoxelManipulator::~VoxelManipulator() VoxelManipulator::~VoxelManipulator()
{ {
clear();
if(m_data) if(m_data)
delete[] 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) void VoxelManipulator::addArea(VoxelArea area)
{ {
// Cancel if requested area has zero volume
if(area.getExtent() == v3s16(0,0,0)) if(area.getExtent() == v3s16(0,0,0))
return; return;
// Cancel if m_area already contains the requested area
if(m_area.contains(area))
return;
// Calculate new area // Calculate new area
VoxelArea 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)) if(m_area.getExtent() == v3s16(0,0,0))
{ {
new_area = area; new_area = area;
} }
// Else add requested area to m_area
else else
{ {
new_area = m_area; new_area = m_area;
new_area.addArea(area); new_area.addArea(area);
} }
if(new_area == m_area)
return;
s32 new_size = new_area.getVolume(); s32 new_size = new_area.getVolume();
/*dstream<<"adding area "; /*dstream<<"adding area ";
@ -63,11 +125,11 @@ void VoxelManipulator::addArea(VoxelArea area)
dstream<<std::endl;*/ dstream<<std::endl;*/
// Allocate and clear new data // Allocate and clear new data
MapNode *new_data; MapNode *new_data = new MapNode[new_size];
new_data = new MapNode[new_size]; u8 *new_flags = new u8[new_size];
for(s32 i=0; i<new_size; i++) for(s32 i=0; i<new_size; i++)
{ {
new_data[i].d = MATERIAL_IGNORE; new_flags[i] = VOXELFLAG_NOT_LOADED;
} }
// Copy old data // 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 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++) 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; m_area = new_area;
MapNode *old_data = m_data; MapNode *old_data = m_data;
u8 *old_flags = m_flags;
/*dstream<<"old_data="<<(int)old_data<<", new_data="<<(int)new_data
<<", old_flags="<<(int)m_flags<<", new_flags="<<(int)new_flags<<std::endl;*/
m_data = new_data; m_data = new_data;
delete[] old_data; m_flags = new_flags;
}
void VoxelManipulator::print(std::ostream &o) if(old_data)
{ delete[] old_data;
v3s16 em = m_area.getExtent(); if(old_flags)
v3s16 of = m_area.MinEdge; delete[] old_flags;
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 ";
}
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;
}
} }
void VoxelManipulator::interpolate(VoxelArea area) void VoxelManipulator::interpolate(VoxelArea area)
@ -156,10 +201,13 @@ void VoxelManipulator::interpolate(VoxelArea area)
{ {
v3s16 p2 = p + dirs[i]; v3s16 p2 = p + dirs[i];
MapNode &n = m_data[m_area.index(p2)]; u8 f = m_flags[m_area.index(p2)];
if(n.d == MATERIAL_IGNORE) assert(!(f & VOXELFLAG_NOT_LOADED));
if(f & VOXELFLAG_INEXISTENT)
continue; continue;
MapNode &n = m_data[m_area.index(p2)];
airness += (n.d == MATERIAL_AIR) ? 1 : -1; airness += (n.d == MATERIAL_AIR) ? 1 : -1;
total++; total++;
@ -182,57 +230,91 @@ void VoxelManipulator::interpolate(VoxelArea area)
} }
} }
#if 0 void VoxelManipulator::flowWater(v3s16 removed_pos)
void VoxelManipulator::blitFromNodeContainer
(v3s16 p_from, v3s16 p_to, v3s16 size, NodeContainer *c)
{ {
VoxelArea a_to(p_to, p_to+size-v3s16(1,1,1)); v3s16 dirs[6] = {
addArea(a_to); v3s16(0,1,0), // top
for(s16 z=0; z<size.Z; z++) v3s16(-1,0,0), // left
for(s16 y=0; y<size.Y; y++) v3s16(1,0,0), // right
for(s16 x=0; x<size.X; x++) v3s16(0,0,-1), // front
{ v3s16(0,0,1), // back
v3s16 p(x,y,z); v3s16(0,-1,0), // bottom
try{ };
MapNode n = c->getNode(p_from + p);
m_data[m_area.index(p_to + p)] = n;
}
catch(InvalidPositionException &e)
{
}
/*v3s16 p(x,y,z); v3s16 p;
MapNode n(MATERIAL_IGNORE);
try{
n = c->getNode(p_from + p);
}
catch(InvalidPositionException &e)
{
}
m_data[m_area.index(p_to + p)] = n;*/
}
}
void VoxelManipulator::blitToNodeContainer // Load neighboring nodes
(v3s16 p_from, v3s16 p_to, v3s16 size, NodeContainer *c) // TODO: A bigger area would be better
{ emerge(VoxelArea(removed_pos - v3s16(1,1,1), removed_pos + v3s16(1,1,1)));
for(s16 z=0; z<size.Z; z++)
for(s16 y=0; y<size.Y; y++) s32 i;
for(s16 x=0; x<size.X; x++) for(i=0; i<6; i++)
{ {
v3s16 p(x,y,z); p = removed_pos + dirs[i];
try{ u8 f = m_flags[m_area.index(p)];
MapNode &n = m_data[m_area.index(p_from + p)]; // Inexistent or checked nodes can't move
if(n.d == MATERIAL_IGNORE) if(f & (VOXELFLAG_INEXISTENT | VOXELFLAG_CHECKED))
continue; continue;
c->setNode(p_to + p, n); MapNode &n = m_data[m_area.index(p)];
} // Only liquid nodes can move
catch(InvalidPositionException &e) if(material_liquid(n.d) == false)
continue;
// If block is at top, select it always
if(i == 0)
{ {
break;
}
// If block is at bottom, select it if it has enough pressure
if(i == 5)
{
if(n.pressure >= 3)
break;
continue;
}
// Else block is at some side. Select it if it has enough pressure
if(n.pressure >= 2)
{
break;
} }
} }
// If there is nothing to move, return
if(i==6)
return;
// 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--)
{
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 MapVoxelManipulator
@ -254,12 +336,18 @@ void MapVoxelManipulator::emerge(VoxelArea a)
for(s16 x=0; x<size.X; x++) for(s16 x=0; x<size.X; x++)
{ {
v3s16 p(x,y,z); 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{ try{
MapNode n = m_map->getNode(a.MinEdge + p); 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) catch(InvalidPositionException &e)
{ {
m_flags[i] = VOXELFLAG_INEXISTENT;
} }
} }
} }
@ -280,10 +368,12 @@ void MapVoxelManipulator::blitBack
{ {
v3s16 p(x,y,z); v3s16 p(x,y,z);
MapNode &n = m_data[m_area.index(p)]; u8 f = m_flags[m_area.index(p)];
if(n.d == MATERIAL_IGNORE) if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
continue; continue;
MapNode &n = m_data[m_area.index(p)];
v3s16 blockpos = getNodeBlockPos(p); v3s16 blockpos = getNodeBlockPos(p);
try try

@ -25,7 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <iostream> #include <iostream>
/* /*
TODO: A fast voxel manipulator class A fast voxel manipulator class
Not thread-safe. Not thread-safe.
*/ */
@ -71,16 +71,24 @@ public:
if(p.Y > MaxEdge.Y) MaxEdge.Y = p.Y; if(p.Y > MaxEdge.Y) MaxEdge.Y = p.Y;
if(p.Z > MaxEdge.Z) MaxEdge.Z = p.Z; if(p.Z > MaxEdge.Z) MaxEdge.Z = p.Z;
} }
v3s16 getExtent() v3s16 getExtent() const
{ {
return MaxEdge - MinEdge + v3s16(1,1,1); return MaxEdge - MinEdge + v3s16(1,1,1);
} }
s32 getVolume() s32 getVolume() const
{ {
v3s16 e = getExtent(); v3s16 e = getExtent();
return (s32)e.X * (s32)e.Y * (s32)e.Z; return (s32)e.X * (s32)e.Y * (s32)e.Z;
} }
bool 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( return(
p.X >= MinEdge.X && p.X <= MaxEdge.X && p.X >= MinEdge.X && p.X <= MaxEdge.X &&
@ -88,7 +96,7 @@ public:
p.Z >= MinEdge.Z && p.Z <= MaxEdge.Z p.Z >= MinEdge.Z && p.Z <= MaxEdge.Z
); );
} }
bool operator==(const VoxelArea &other) bool operator==(const VoxelArea &other) const
{ {
return (MinEdge == other.MinEdge return (MinEdge == other.MinEdge
&& MaxEdge == other.MaxEdge); && MaxEdge == other.MaxEdge);
@ -97,7 +105,7 @@ public:
/* /*
Translates position from virtual coordinates to array index 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 em = getExtent();
v3s16 off = MinEdge; v3s16 off = MinEdge;
@ -105,12 +113,12 @@ public:
//dstream<<" i("<<x<<","<<y<<","<<z<<")="<<i<<" "; //dstream<<" i("<<x<<","<<y<<","<<z<<")="<<i<<" ";
return i; return i;
} }
s32 index(v3s16 p) s32 index(v3s16 p) const
{ {
return index(p.X, p.Y, p.Z); return index(p.X, p.Y, p.Z);
} }
void print(std::ostream &o) void print(std::ostream &o) const
{ {
o<<"("<<MinEdge.X o<<"("<<MinEdge.X
<<","<<MinEdge.Y <<","<<MinEdge.Y
@ -126,6 +134,13 @@ public:
v3s16 MaxEdge; 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 class VoxelManipulator : public NodeContainer
{ {
public: public:
@ -141,53 +156,77 @@ public:
} }
bool isValidPosition(v3s16 p) 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 // These are a bit slow and shouldn't be used internally
MapNode getNode(v3s16 p) MapNode getNode(v3s16 p)
{ {
if(isValidPosition(p) == false) emerge(p);
emerge(VoxelArea(p));
MapNode &n = m_data[m_area.index(p)]; if(m_flags[m_area.index(p)] & VOXELFLAG_INEXISTENT)
{
//TODO: Is this the right behaviour? dstream<<"ERROR: VoxelManipulator::getNode(): "
if(n.d == MATERIAL_IGNORE) <<"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 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(p);
emerge(VoxelArea(p));
m_data[m_area.index(p)] = n; m_data[m_area.index(p)] = n;
m_flags[m_area.index(p)] &= ~VOXELFLAG_INEXISTENT;
m_flags[m_area.index(p)] &= ~VOXELFLAG_NOT_LOADED;
}
void setNodeNoRef(v3s16 p, MapNode n)
{
setNode(p, n);
} }
MapNode & operator[](v3s16 p) /*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; //dstream<<"operator[] p=("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;
if(isValidPosition(p) == false) if(isValidPosition(p) == false)
emerge(VoxelArea(p)); emerge(VoxelArea(p));
return m_data[m_area.index(p)]; return m_data[m_area.index(p)];
} }*/
/* /*
Manipulation of bigger chunks Control
*/ */
void clear();
void print(std::ostream &o); void print(std::ostream &o);
void addArea(VoxelArea area); void addArea(VoxelArea area);
/*
Algorithms
*/
void interpolate(VoxelArea area); void interpolate(VoxelArea area);
/*void blitFromNodeContainer void flowWater(v3s16 removed_pos);
(v3s16 p_from, v3s16 p_to, v3s16 size, NodeContainer *c);
void blitToNodeContainer
(v3s16 p_from, v3s16 p_to, v3s16 size, NodeContainer *c);*/
/* /*
Virtual functions Virtual functions
@ -195,13 +234,25 @@ public:
/* /*
Get the contents of the requested area from somewhere. 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) virtual void emerge(VoxelArea a)
{ {
//dstream<<"emerge p=("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl; //dstream<<"emerge p=("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;
addArea(a); 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; 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] Data is stored as [z*h*w + y*h + x]
Special data values:
MATERIAL_IGNORE: Unspecified node
*/ */
MapNode *m_data; MapNode *m_data;
/*
Flags of all nodes
*/
u8 *m_flags;
private: private:
}; };