diff options
-rw-r--r-- | minetest.vcproj | 4 | ||||
-rw-r--r-- | src/voxel.cpp | 304 | ||||
-rw-r--r-- | src/voxel.h | 224 |
3 files changed, 532 insertions, 0 deletions
diff --git a/minetest.vcproj b/minetest.vcproj index c60d9a1fa..0401e2a04 100644 --- a/minetest.vcproj +++ b/minetest.vcproj @@ -247,6 +247,10 @@ RelativePath=".\src\utility.cpp"
>
</File>
+ <File
+ RelativePath=".\src\voxel.cpp"
+ >
+ </File>
</Filter>
<Filter
Name="Header Files"
diff --git a/src/voxel.cpp b/src/voxel.cpp new file mode 100644 index 000000000..98c9817c5 --- /dev/null +++ b/src/voxel.cpp @@ -0,0 +1,304 @@ +#include "voxel.h" +#include "map.h" + +VoxelManipulator::VoxelManipulator(): + m_data(NULL) +{ +} + +VoxelManipulator::~VoxelManipulator() +{ + if(m_data) + delete[] m_data; +} + +void VoxelManipulator::addArea(VoxelArea area) +{ + if(area.getExtent() == v3s16(0,0,0)) + return; + + // Calculate new area + VoxelArea new_area; + if(m_area.getExtent() == v3s16(0,0,0)) + { + new_area = 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 "; + area.print(dstream); + dstream<<", old area "; + m_area.print(dstream); + dstream<<", new area "; + new_area.print(dstream); + dstream<<", new_size="<<new_size; + dstream<<std::endl;*/ + + // Allocate and clear new data + MapNode *new_data; + new_data = new MapNode[new_size]; + for(s32 i=0; i<new_size; i++) + { + new_data[i].d = MATERIAL_IGNORE; + } + + // Copy old data + + 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++) + { + new_data[new_area.index(z,y,x)] = m_data[m_area.index(x,y,z)]; + } + + // Replace member + m_area = new_area; + MapNode *old_data = m_data; + m_data = new_data; + delete[] old_data; +} + +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 "; + } + + 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) +{ + VoxelArea emerge_area = area; + emerge_area.MinEdge -= v3s16(1,1,1); + emerge_area.MaxEdge += v3s16(1,1,1); + emerge(emerge_area); + + SharedBuffer<u8> buf(area.getVolume()); + + for(s32 z=area.MinEdge.Z; z<=area.MaxEdge.Z; z++) + for(s32 y=area.MinEdge.Y; y<=area.MaxEdge.Y; y++) + for(s32 x=area.MinEdge.X; x<=area.MaxEdge.X; x++) + { + v3s16 p(x,y,z); + + v3s16 dirs[] = { + v3s16(1,1,0), + v3s16(1,0,1), + v3s16(1,-1,0), + v3s16(1,0,-1), + v3s16(-1,1,0), + v3s16(-1,0,1), + v3s16(-1,-1,0), + v3s16(-1,0,-1), + }; + //const v3s16 *dirs = g_26dirs; + + s16 total = 0; + s16 airness = 0; + u8 m = MATERIAL_IGNORE; + + for(s16 i=0; i<8; i++) + //for(s16 i=0; i<26; i++) + { + v3s16 p2 = p + dirs[i]; + + MapNode &n = m_data[m_area.index(p2)]; + if(n.d == MATERIAL_IGNORE) + continue; + + airness += (n.d == MATERIAL_AIR) ? 1 : -1; + total++; + + if(m == MATERIAL_IGNORE && n.d != MATERIAL_AIR) + m = n.d; + } + + // 1 if air, 0 if not + buf[area.index(p)] = airness > -total/2 ? MATERIAL_AIR : m; + //buf[area.index(p)] = airness > -total ? MATERIAL_AIR : m; + //buf[area.index(p)] = airness >= -7 ? MATERIAL_AIR : m; + } + + for(s32 z=area.MinEdge.Z; z<=area.MaxEdge.Z; z++) + for(s32 y=area.MinEdge.Y; y<=area.MaxEdge.Y; y++) + for(s32 x=area.MinEdge.X; x<=area.MaxEdge.X; x++) + { + v3s16 p(x,y,z); + m_data[m_area.index(p)].d = buf[area.index(p)]; + } +} + +#if 0 +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)); + 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 p(x,y,z); + 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); + 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 + (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++) + { + 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) + { + } + } +} +#endif + +/* + MapVoxelManipulator +*/ + +MapVoxelManipulator::MapVoxelManipulator(Map *map) +{ + m_map = map; +} + +void MapVoxelManipulator::emerge(VoxelArea a) +{ + v3s16 size = a.getExtent(); + + addArea(a); + + 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); + try{ + MapNode n = m_map->getNode(a.MinEdge + p); + m_data[m_area.index(a.MinEdge + p)] = n; + } + catch(InvalidPositionException &e) + { + } + } +} + +void MapVoxelManipulator::blitBack + (core::map<v3s16, MapBlock*> & modified_blocks) +{ + /* + 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); + + MapNode &n = m_data[m_area.index(p)]; + if(n.d == MATERIAL_IGNORE) + continue; + + v3s16 blockpos = getNodeBlockPos(p); + + try + { + // Get block + if(block == NULL || blockpos != blockpos_last){ + block = m_map->getBlockNoCreate(blockpos); + blockpos_last = blockpos; + block_checked_in_modified = false; + } + + // Calculate relative position in block + v3s16 relpos = p - blockpos * MAP_BLOCKSIZE; + + // Don't continue if nothing has changed here + if(block->getNode(relpos) == n) + continue; + + //m_map->setNode(m_area.MinEdge + p, n); + block->setNode(relpos, n); + + /* + Make sure block is in modified_blocks + */ + if(block_checked_in_modified == false) + { + modified_blocks[blockpos] = block; + block_checked_in_modified = true; + } + } + catch(InvalidPositionException &e) + { + } + } +} + +//END diff --git a/src/voxel.h b/src/voxel.h new file mode 100644 index 000000000..eee9177f3 --- /dev/null +++ b/src/voxel.h @@ -0,0 +1,224 @@ +#ifndef VOXEL_HEADER +#define VOXEL_HEADER + +#include "common_irrlicht.h" +#include "mapblock.h" +#include <iostream> + +/* + TODO: A fast voxel manipulator class + + Not thread-safe. +*/ + +/* + This class resembles aabbox3d<s16> a lot, but has inclusive + edges for saner handling of integer sizes +*/ +class VoxelArea +{ +public: + // Starts as zero sized + VoxelArea(): + MinEdge(1,1,1), + MaxEdge(0,0,0) + { + } + VoxelArea(v3s16 min_edge, v3s16 max_edge): + MinEdge(min_edge), + MaxEdge(max_edge) + { + } + VoxelArea(v3s16 p): + MinEdge(p), + MaxEdge(p) + { + } + void addArea(VoxelArea &a) + { + 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; + if(a.MaxEdge.X > MaxEdge.X) MaxEdge.X = a.MaxEdge.X; + if(a.MaxEdge.Y > MaxEdge.Y) MaxEdge.Y = a.MaxEdge.Y; + if(a.MaxEdge.Z > MaxEdge.Z) MaxEdge.Z = a.MaxEdge.Z; + } + void addPoint(v3s16 p) + { + 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; + if(p.X > MaxEdge.X) MaxEdge.X = p.X; + if(p.Y > MaxEdge.Y) MaxEdge.Y = p.Y; + if(p.Z > MaxEdge.Z) MaxEdge.Z = p.Z; + } + v3s16 getExtent() + { + return MaxEdge - MinEdge + v3s16(1,1,1); + } + s32 getVolume() + { + v3s16 e = getExtent(); + return (s32)e.X * (s32)e.Y * (s32)e.Z; + } + bool isInside(v3s16 p) + { + return( + p.X >= MinEdge.X && p.X <= MaxEdge.X && + p.Y >= MinEdge.Y && p.Y <= MaxEdge.Y && + p.Z >= MinEdge.Z && p.Z <= MaxEdge.Z + ); + } + bool operator==(const VoxelArea &other) + { + return (MinEdge == other.MinEdge + && MaxEdge == other.MaxEdge); + } + + /* + Translates position from virtual coordinates to array index + */ + s32 index(s16 x, s16 y, s16 z) + { + v3s16 em = getExtent(); + v3s16 off = MinEdge; + s32 i = (s32)(z-off.Z)*em.Y*em.X + (y-off.Y)*em.X + (x-off.X); + //dstream<<" i("<<x<<","<<y<<","<<z<<")="<<i<<" "; + return i; + } + s32 index(v3s16 p) + { + return index(p.X, p.Y, p.Z); + } + + void print(std::ostream &o) + { + o<<"("<<MinEdge.X + <<","<<MinEdge.Y + <<","<<MinEdge.Z + <<")("<<MaxEdge.X + <<","<<MaxEdge.Y + <<","<<MaxEdge.Z + <<")"; + } + + // Edges are inclusive + v3s16 MinEdge; + v3s16 MaxEdge; +}; + +class VoxelManipulator : public NodeContainer +{ +public: + VoxelManipulator(); + ~VoxelManipulator(); + + /* + Virtuals from NodeContainer + */ + virtual u16 nodeContainerId() const + { + return NODECONTAINER_ID_VOXELMANIPULATOR; + } + bool isValidPosition(v3s16 p) + { + return m_area.isInside(p); + } + // These are a bit slow and shouldn't be used internally + MapNode getNode(v3s16 p) + { + if(isValidPosition(p) == false) + emerge(VoxelArea(p)); + + MapNode &n = m_data[m_area.index(p)]; + + //TODO: Is this the right behaviour? + if(n.d == MATERIAL_IGNORE) + throw InvalidPositionException + ("Not returning MATERIAL_IGNORE in VoxelManipulator"); + + return n; + } + void setNode(v3s16 p, MapNode & n) + { + if(isValidPosition(p) == false) + emerge(VoxelArea(p)); + m_data[m_area.index(p)] = n; + } + + 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 + */ + + void print(std::ostream &o); + + void addArea(VoxelArea area); + + 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);*/ + + /* + Virtual functions + */ + + /* + Get the contents of the requested area from somewhere. + + If not found from source, add as MATERIAL_IGNORE. + */ + virtual void emerge(VoxelArea a) + { + //dstream<<"emerge p=("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl; + addArea(a); + } + + /* + Member variables + */ + + /* + The area that is stored in m_data. + addInternalBox should not be used if getExtent() == v3s16(0,0,0) + MaxEdge is 1 higher than maximum allowed position + */ + VoxelArea m_area; + /* + NULL if data size is 0 + Data is stored as [z*h*w + y*h + x] + Special data values: + MATERIAL_IGNORE: Unspecified node + */ + MapNode *m_data; +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 + |