summaryrefslogtreecommitdiff
path: root/src/heightmap.h
diff options
context:
space:
mode:
authorPerttu Ahola <celeron55@gmail.com>2010-11-27 01:02:21 +0200
committerPerttu Ahola <celeron55@gmail.com>2010-11-27 01:02:21 +0200
commit4e249fb3fbf75f0359758760d88e22aa5b14533c (patch)
tree323087d05efbd2ace27b316d4f017cf812a31992 /src/heightmap.h
downloadminetest-4e249fb3fbf75f0359758760d88e22aa5b14533c.tar.gz
minetest-4e249fb3fbf75f0359758760d88e22aa5b14533c.tar.bz2
minetest-4e249fb3fbf75f0359758760d88e22aa5b14533c.zip
Initial files
Diffstat (limited to 'src/heightmap.h')
-rw-r--r--src/heightmap.h556
1 files changed, 556 insertions, 0 deletions
diff --git a/src/heightmap.h b/src/heightmap.h
new file mode 100644
index 000000000..a5da092aa
--- /dev/null
+++ b/src/heightmap.h
@@ -0,0 +1,556 @@
+/*
+(c) 2010 Perttu Ahola <celeron55@gmail.com>
+*/
+
+#ifndef HEIGHTMAP_HEADER
+#define HEIGHTMAP_HEADER
+
+#include <iostream>
+#include <time.h>
+#include <sstream>
+
+#include "debug.h"
+#include "common_irrlicht.h"
+#include "exceptions.h"
+#include "utility.h"
+#include "serialization.h"
+
+#define GROUNDHEIGHT_NOTFOUND_SETVALUE (-10e6)
+#define GROUNDHEIGHT_VALID_MINVALUE ( -9e6)
+
+class Heightmappish
+{
+public:
+ virtual f32 getGroundHeight(v2s16 p, bool generate=true) = 0;
+ virtual void setGroundHeight(v2s16 p, f32 y, bool generate=true) = 0;
+
+ v2f32 getSlope(v2s16 p)
+ {
+ f32 y0 = getGroundHeight(p, false);
+
+ v2s16 dirs[] = {
+ v2s16(1,0),
+ v2s16(0,1),
+ };
+
+ v2f32 fdirs[] = {
+ v2f32(1,0),
+ v2f32(0,1),
+ };
+
+ v2f32 slopevector(0.0, 0.0);
+
+ for(u16 i=0; i<2; i++){
+ f32 y1 = 0.0;
+ f32 y2 = 0.0;
+ f32 count = 0.0;
+
+ v2s16 p1 = p - dirs[i];
+ y1 = getGroundHeight(p1, false);
+ if(y1 > GROUNDHEIGHT_VALID_MINVALUE){
+ y1 -= y0;
+ count += 1.0;
+ }
+ else
+ y1 = 0;
+
+ v2s16 p2 = p + dirs[i];
+ y2 = getGroundHeight(p2, false);
+ if(y2 > GROUNDHEIGHT_VALID_MINVALUE){
+ y2 -= y0;
+ count += 1.0;
+ }
+ else
+ y2 = 0;
+
+ if(count < 0.001)
+ return v2f32(0.0, 0.0);
+
+ /*
+ If y2 is higher than y1, slope is positive
+ */
+ f32 slope = (y2 - y1)/count;
+
+ slopevector += fdirs[i] * slope;
+ }
+
+ return slopevector;
+ }
+
+};
+
+// TODO: Get rid of this dummy wrapper
+class Heightmap : public Heightmappish /*, public ReferenceCounted*/
+{
+};
+
+class WrapperHeightmap : public Heightmap
+{
+ Heightmappish *m_target;
+public:
+
+ WrapperHeightmap(Heightmappish *target):
+ m_target(target)
+ {
+ if(target == NULL)
+ throw NullPointerException();
+ }
+
+ f32 getGroundHeight(v2s16 p, bool generate=true)
+ {
+ return m_target->getGroundHeight(p, generate);
+ }
+ void setGroundHeight(v2s16 p, f32 y, bool generate=true)
+ {
+ m_target->setGroundHeight(p, y, generate);
+ }
+};
+
+/*
+ Base class that defines a generator that gives out values at
+ positions in 2-dimensional space.
+ Can be given to UnlimitedHeightmap to feed stuff.
+
+ These are always serialized as readable text ending in "\n"
+*/
+class ValueGenerator
+{
+public:
+ ValueGenerator(){}
+ virtual ~ValueGenerator(){}
+
+ static ValueGenerator* deSerialize(std::string line);
+
+ static ValueGenerator* deSerialize(std::istream &is)
+ {
+ std::string line;
+ std::getline(is, line, '\n');
+ return deSerialize(line);
+ }
+
+ void serializeBase(std::ostream &os)
+ {
+ os<<getName()<<" ";
+ }
+
+ // Virtual methods
+ virtual const char * getName() const = 0;
+ virtual f32 getValue(v2s16 p) = 0;
+ virtual void serialize(std::ostream &os) = 0;
+};
+
+class ConstantGenerator : public ValueGenerator
+{
+public:
+ f32 m_value;
+
+ ConstantGenerator(f32 value)
+ {
+ m_value = value;
+ }
+
+ const char * getName() const
+ {
+ return "constant";
+ }
+
+ f32 getValue(v2s16 p)
+ {
+ return m_value;
+ }
+
+ void serialize(std::ostream &os)
+ {
+ serializeBase(os);
+
+ std::ostringstream ss;
+ //ss.imbue(std::locale("C"));
+
+ ss<<m_value<<"\n";
+
+ os<<ss.str();
+ }
+};
+
+class LinearGenerator : public ValueGenerator
+{
+public:
+ f32 m_height;
+ v2f m_slope;
+
+ LinearGenerator(f32 height, v2f slope)
+ {
+ m_height = height;
+ m_slope = slope;
+ }
+
+ const char * getName() const
+ {
+ return "linear";
+ }
+
+ f32 getValue(v2s16 p)
+ {
+ return m_height + m_slope.X * p.X + m_slope.Y * p.Y;
+ }
+
+ void serialize(std::ostream &os)
+ {
+ serializeBase(os);
+
+ std::ostringstream ss;
+ //ss.imbue(std::locale("C"));
+
+ ss<<m_height<<" "<<m_slope.X<<" "<<m_slope.Y<<"\n";
+
+ os<<ss.str();
+ }
+};
+
+class PowerGenerator : public ValueGenerator
+{
+public:
+ f32 m_height;
+ v2f m_slope;
+ f32 m_power;
+
+ PowerGenerator(f32 height, v2f slope, f32 power)
+ {
+ m_height = height;
+ m_slope = slope;
+ m_power = power;
+ }
+
+ const char * getName() const
+ {
+ return "power";
+ }
+
+ f32 getValue(v2s16 p)
+ {
+ return m_height
+ + m_slope.X * pow((f32)p.X, m_power)
+ + m_slope.Y * pow((f32)p.Y, m_power);
+ }
+
+ void serialize(std::ostream &os)
+ {
+ serializeBase(os);
+
+ std::ostringstream ss;
+ //ss.imbue(std::locale("C"));
+
+ ss<<m_height<<" "
+ <<m_slope.X<<" "
+ <<m_slope.Y<<" "
+ <<m_power<<"\n";
+
+ os<<ss.str();
+ }
+};
+
+class FixedHeightmap : public Heightmap
+{
+ // A meta-heightmap on which this heightmap is located
+ // (at m_pos_on_master * m_blocksize)
+ Heightmap * m_master;
+ // Position on master heightmap (in blocks)
+ v2s16 m_pos_on_master;
+ s32 m_blocksize; // This is (W-1) = (H-1)
+ // These are the actual size of the data
+ s32 W;
+ s32 H;
+ f32 *m_data;
+
+public:
+
+ FixedHeightmap(Heightmap * master,
+ v2s16 pos_on_master, s32 blocksize):
+ m_master(master),
+ m_pos_on_master(pos_on_master),
+ m_blocksize(blocksize)
+ {
+ W = m_blocksize+1;
+ H = m_blocksize+1;
+ m_data = NULL;
+ m_data = new f32[(blocksize+1)*(blocksize+1)];
+
+ for(s32 i=0; i<(blocksize+1)*(blocksize+1); i++){
+ m_data[i] = GROUNDHEIGHT_NOTFOUND_SETVALUE;
+ }
+ }
+
+ ~FixedHeightmap()
+ {
+ if(m_data)
+ delete[] m_data;
+ }
+
+ v2s16 getPosOnMaster()
+ {
+ return m_pos_on_master;
+ }
+
+ /*
+ TODO: BorderWrapper class or something to allow defining
+ borders that wrap to an another heightmap. The algorithm
+ should be allowed to edit stuff over the border and on
+ the border in that case, too.
+ This will allow non-square heightmaps, too. (probably)
+ */
+
+ void print()
+ {
+ printf("FixedHeightmap::print(): size is %ix%i\n", W, H);
+ for(s32 y=0; y<H; y++){
+ for(s32 x=0; x<W; x++){
+ /*if(getSeeded(v2s16(x,y)))
+ printf("S");*/
+ f32 n = getGroundHeight(v2s16(x,y));
+ if(n < GROUNDHEIGHT_VALID_MINVALUE)
+ printf(" - ");
+ else
+ printf("% -5.1f ", getGroundHeight(v2s16(x,y)));
+ }
+ printf("\n");
+ }
+ }
+
+ bool overborder(v2s16 p)
+ {
+ return (p.X < 0 || p.X >= W || p.Y < 0 || p.Y >= H);
+ }
+
+ bool atborder(v2s16 p)
+ {
+ if(overborder(p))
+ return false;
+ return (p.X == 0 || p.X == W-1 || p.Y == 0 || p.Y == H-1);
+ }
+
+ void setGroundHeight(v2s16 p, f32 y, bool generate=false)
+ {
+ /*dstream<<"FixedHeightmap::setGroundHeight(("
+ <<p.X<<","<<p.Y
+ <<"), "<<y<<")"<<std::endl;*/
+ if(overborder(p))
+ throw InvalidPositionException();
+ m_data[p.Y*W + p.X] = y;
+ }
+
+ // Returns true on success, false on railure.
+ bool setGroundHeightParent(v2s16 p, f32 y, bool generate=false)
+ {
+ /*// Position on master
+ v2s16 blockpos_nodes = m_pos_on_master * m_blocksize;
+ v2s16 nodepos_master = blockpos_nodes + p;
+ dstream<<"FixedHeightmap::setGroundHeightParent(("
+ <<p.X<<","<<p.Y
+ <<"), "<<y<<"): nodepos_master=("
+ <<nodepos_master.X<<","
+ <<nodepos_master.Y<<")"<<std::endl;
+ m_master->setGroundHeight(nodepos_master, y, false);*/
+
+ // Try to set on master
+ bool master_got_it = false;
+ if(overborder(p) || atborder(p))
+ {
+ try{
+ // Position on master
+ v2s16 blockpos_nodes = m_pos_on_master * m_blocksize;
+ v2s16 nodepos_master = blockpos_nodes + p;
+ m_master->setGroundHeight(nodepos_master, y, false);
+
+ master_got_it = true;
+ }
+ catch(InvalidPositionException &e)
+ {
+ }
+ }
+
+ if(overborder(p))
+ return master_got_it;
+
+ setGroundHeight(p, y);
+
+ return true;
+ }
+
+ f32 getGroundHeight(v2s16 p, bool generate=false)
+ {
+ if(overborder(p))
+ return GROUNDHEIGHT_NOTFOUND_SETVALUE;
+ return m_data[p.Y*W + p.X];
+ }
+
+ f32 getGroundHeightParent(v2s16 p)
+ {
+ /*v2s16 blockpos_nodes = m_pos_on_master * m_blocksize;
+ return m_master->getGroundHeight(blockpos_nodes + p, false);*/
+
+ if(overborder(p) == false){
+ f32 h = getGroundHeight(p);
+ if(h > GROUNDHEIGHT_VALID_MINVALUE)
+ return h;
+ }
+
+ // Position on master
+ v2s16 blockpos_nodes = m_pos_on_master * m_blocksize;
+ f32 h = m_master->getGroundHeight(blockpos_nodes + p, false);
+ return h;
+ }
+
+ f32 avgNeighbours(v2s16 p, s16 d);
+
+ f32 avgDiagNeighbours(v2s16 p, s16 d);
+
+ void makeDiamond(
+ v2s16 center,
+ s16 a,
+ f32 randmax,
+ core::map<v2s16, bool> &next_squares);
+
+ void makeSquare(
+ v2s16 center,
+ s16 a,
+ f32 randmax,
+ core::map<v2s16, bool> &next_diamonds);
+
+ void DiamondSquare(f32 randmax, f32 randfactor);
+
+ /*
+ corners: [i]=XY: [0]=00, [1]=10, [2]=11, [3]=10
+ */
+ void generateContinued(f32 randmax, f32 randfactor, f32 *corners);
+
+
+ static u32 serializedLength(u8 version, u16 blocksize);
+ u32 serializedLength(u8 version);
+ void serialize(u8 *dest, u8 version);
+ void deSerialize(u8 *source, u8 version);
+ /*static FixedHeightmap * deSerialize(u8 *source, u32 size,
+ u32 &usedsize, Heightmap *master, u8 version);*/
+};
+
+class OneChildHeightmap : public Heightmap
+{
+ s16 m_blocksize;
+
+public:
+
+ FixedHeightmap m_child;
+
+ OneChildHeightmap(s16 blocksize):
+ m_blocksize(blocksize),
+ m_child(this, v2s16(0,0), blocksize)
+ {
+ }
+
+ f32 getGroundHeight(v2s16 p, bool generate=true)
+ {
+ if(p.X < 0 || p.X > m_blocksize
+ || p.Y < 0 || p.Y > m_blocksize)
+ return GROUNDHEIGHT_NOTFOUND_SETVALUE;
+ return m_child.getGroundHeight(p);
+ }
+ void setGroundHeight(v2s16 p, f32 y, bool generate=true)
+ {
+ //dstream<<"OneChildHeightmap::setGroundHeight()"<<std::endl;
+ if(p.X < 0 || p.X > m_blocksize
+ || p.Y < 0 || p.Y > m_blocksize)
+ throw InvalidPositionException();
+ m_child.setGroundHeight(p, y);
+ }
+};
+
+
+/*
+ This is a dynamic container of an arbitrary number of heightmaps
+ at arbitrary positions.
+
+ It is able to redirect queries to the corresponding heightmaps and
+ it generates new heightmaps on-the-fly according to the relevant
+ parameters.
+
+ It doesn't have a master heightmap because it is meant to be used
+ as such itself.
+
+ Child heightmaps are spaced at m_blocksize distances, and are of
+ size (m_blocksize+1)*(m_blocksize+1)
+
+ This is used as the master heightmap of a Map object.
+*/
+class UnlimitedHeightmap: public Heightmap
+{
+private:
+
+ core::map<v2s16, FixedHeightmap*> m_heightmaps;
+ s16 m_blocksize;
+
+ ValueGenerator *m_randmax_generator;
+ ValueGenerator *m_randfactor_generator;
+ ValueGenerator *m_base_generator;
+
+public:
+
+ UnlimitedHeightmap(
+ s16 blocksize,
+ ValueGenerator *randmax_generator,
+ ValueGenerator *randfactor_generator,
+ ValueGenerator *base_generator
+ ):
+ m_blocksize(blocksize),
+ m_randmax_generator(randmax_generator),
+ m_randfactor_generator(randfactor_generator),
+ m_base_generator(base_generator)
+ {
+ assert(m_randmax_generator != NULL);
+ assert(m_randfactor_generator != NULL);
+ assert(m_base_generator != NULL);
+ }
+
+ ~UnlimitedHeightmap()
+ {
+ core::map<v2s16, FixedHeightmap*>::Iterator i;
+ i = m_heightmaps.getIterator();
+ for(; i.atEnd() == false; i++)
+ {
+ delete i.getNode()->getValue();
+ }
+
+ delete m_randmax_generator;
+ delete m_randfactor_generator;
+ delete m_base_generator;
+ }
+
+ /*void setParams(f32 randmax, f32 randfactor)
+ {
+ m_randmax = randmax;
+ m_randfactor = randfactor;
+ }*/
+
+ void print();
+
+ v2s16 getNodeHeightmapPos(v2s16 p)
+ {
+ return v2s16(
+ (p.X>=0 ? p.X : p.X-m_blocksize+1) / m_blocksize,
+ (p.Y>=0 ? p.Y : p.Y-m_blocksize+1) / m_blocksize);
+ }
+
+ // Can throw an InvalidPositionException
+ FixedHeightmap * getHeightmap(v2s16 p, bool generate=true);
+
+ f32 getGroundHeight(v2s16 p, bool generate=true);
+ void setGroundHeight(v2s16 p, f32 y, bool generate=true);
+
+ /*static UnlimitedHeightmap * deSerialize(u8 *source, u32 maxsize,
+ u32 &usedsize, u8 version);*/
+
+ //SharedBuffer<u8> serialize(u8 version);
+ void serialize(std::ostream &os, u8 version);
+ static UnlimitedHeightmap * deSerialize(std::istream &istr);
+};
+
+#endif
+