From af95cfc7a15788386d055a5b4a7377673d36b8bf Mon Sep 17 00:00:00 2001
From: Perttu Ahola <celeron55@gmail.com>
Date: Mon, 29 Nov 2010 10:57:05 +0200
Subject: [PATCH] =?UTF-8?q?vokselijuttu=20lis=C3=A4tty?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 minetest.vcproj |   4 +
 src/voxel.cpp   | 304 ++++++++++++++++++++++++++++++++++++++++++++++++
 src/voxel.h     | 224 +++++++++++++++++++++++++++++++++++
 3 files changed, 532 insertions(+)
 create mode 100644 src/voxel.cpp
 create mode 100644 src/voxel.h

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
+
-- 
GitLab