/* (c) 2010 Perttu Ahola */ #ifndef HEIGHTMAP_HEADER #define HEIGHTMAP_HEADER #include #include #include #include "common_irrlicht.h" #include "exceptions.h" #define GROUNDHEIGHT_NOTFOUND_SETVALUE (-10e6) #define GROUNDHEIGHT_VALID_MINVALUE ( -9e6) extern bool g_heightmap_debugprint; #define HEIGHTMAP_DEBUGPRINT g_heightmap_debugprint class Heightmappish { public: virtual f32 getGroundHeight(v2s16 p, bool generate=true) { printf("Heightmappish::getGroundHeight() stub called\n"); assert(0); return 0.0; } virtual void setGroundHeight(v2s16 p, f32 y, bool generate=true) { printf("Heightmappish::setGroundHeight() stub called\n"); assert(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; } }; 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); } }; class DummyHeightmap : public Heightmap { public: f32 m_value; DummyHeightmap(f32 value=0.0) { m_value = value; } f32 getGroundHeight(v2s16 p, bool generate=true) { return m_value; } void setGroundHeight(v2s16 p, f32 y, bool generate=true) { std::cout<<"DummyHeightmap::getGroundHeight()"<= 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) { /*std::cout<<"FixedHeightmap::setGroundHeight((" <setGroundHeight(nodepos_master, y, 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); } catch(InvalidPositionException &e) { } } if(overborder(p)) return; setGroundHeight(p, y); } 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); /* Adds a point to transform into a diamond pattern a = 2, 4, 8, 16, ... */ void makeDiamond(v2s16 center, s16 a, f32 randmax); /* Adds points to transform into a diamond pattern a = 2, 4, 8, 16, ... */ void makeDiamonds(s16 a, f32 randmax); /* Adds a point to transform into a square pattern a = 2, 4, 8, 16, ... */ void makeSquare(v2s16 center, s16 a, f32 randmax); /* Adds points to transform into a square pattern a = 2, 4, 8, 16, ... */ void makeSquares(s16 a, f32 randmax); 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); }; 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) { //std::cout<<"OneChildHeightmap::setGroundHeight()"< m_blocksize || p.Y < 0 || p.Y > m_blocksize) throw InvalidPositionException(); m_child.setGroundHeight(p, y); } }; /* TODO 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) TODO: Dynamic unloading and loading of stuff to/from disk This is used as the master heightmap of a Map object. */ class UnlimitedHeightmap: public Heightmap { private: core::map m_heightmaps; s16 m_blocksize; f32 m_randmax; f32 m_randfactor; f32 m_basevalue; public: UnlimitedHeightmap(s16 blocksize, f32 randmax, f32 randfactor, f32 basevalue=0.0): m_blocksize(blocksize), m_randmax(randmax), m_randfactor(randfactor), m_basevalue(basevalue) { } ~UnlimitedHeightmap() { core::map::Iterator i; i = m_heightmaps.getIterator(); for(; i.atEnd() == false; i++) { delete i.getNode()->getValue(); } } 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); } FixedHeightmap * getHeightmap(v2s16 p, bool generate=true); f32 getGroundHeight(v2s16 p, bool generate=true); void setGroundHeight(v2s16 p, f32 y, bool generate=true); }; #endif