summaryrefslogtreecommitdiff
path: root/src/heightmap.cpp
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.cpp
downloadminetest-4e249fb3fbf75f0359758760d88e22aa5b14533c.tar.gz
minetest-4e249fb3fbf75f0359758760d88e22aa5b14533c.tar.bz2
minetest-4e249fb3fbf75f0359758760d88e22aa5b14533c.zip
Initial files
Diffstat (limited to 'src/heightmap.cpp')
-rw-r--r--src/heightmap.cpp872
1 files changed, 872 insertions, 0 deletions
diff --git a/src/heightmap.cpp b/src/heightmap.cpp
new file mode 100644
index 000000000..a4027ee7d
--- /dev/null
+++ b/src/heightmap.cpp
@@ -0,0 +1,872 @@
+/*
+(c) 2010 Perttu Ahola <celeron55@gmail.com>
+*/
+
+#include "heightmap.h"
+
+/*
+ ValueGenerator
+*/
+
+ValueGenerator* ValueGenerator::deSerialize(std::string line)
+{
+ std::istringstream ss(line);
+ //ss.imbue(std::locale("C"));
+
+ std::string name;
+ std::getline(ss, name, ' ');
+
+ if(name == "constant")
+ {
+ f32 value;
+ ss>>value;
+
+ return new ConstantGenerator(value);
+ }
+ else if(name == "linear")
+ {
+ f32 height;
+ v2f slope;
+
+ ss>>height;
+ ss>>slope.X;
+ ss>>slope.Y;
+
+ return new LinearGenerator(height, slope);
+ }
+ else if(name == "power")
+ {
+ f32 height;
+ v2f slope;
+ f32 power;
+
+ ss>>height;
+ ss>>slope.X;
+ ss>>slope.Y;
+ ss>>power;
+
+ return new PowerGenerator(height, slope, power);
+ }
+ else
+ {
+ throw SerializationError
+ ("Invalid heightmap generator (deSerialize)");
+ }
+}
+
+/*
+ FixedHeightmap
+*/
+
+f32 FixedHeightmap::avgNeighbours(v2s16 p, s16 d)
+{
+ v2s16 dirs[4] = {
+ v2s16(1,0),
+ v2s16(0,1),
+ v2s16(-1,0),
+ v2s16(0,-1)
+ };
+ f32 sum = 0.0;
+ f32 count = 0.0;
+ for(u16 i=0; i<4; i++){
+ v2s16 p2 = p + dirs[i] * d;
+ f32 n = getGroundHeightParent(p2);
+ if(n < GROUNDHEIGHT_VALID_MINVALUE)
+ continue;
+ sum += n;
+ count += 1.0;
+ }
+ assert(count > 0.001);
+ return sum / count;
+}
+
+f32 FixedHeightmap::avgDiagNeighbours(v2s16 p, s16 d)
+{
+ v2s16 dirs[4] = {
+ v2s16(1,1),
+ v2s16(-1,-1),
+ v2s16(-1,1),
+ v2s16(1,-1)
+ };
+ f32 sum = 0.0;
+ f32 count = 0.0;
+ for(u16 i=0; i<4; i++){
+ v2s16 p2 = p + dirs[i] * d;
+ f32 n = getGroundHeightParent(p2);
+ if(n < GROUNDHEIGHT_VALID_MINVALUE)
+ continue;
+ sum += n;
+ count += 1.0;
+ }
+ assert(count > 0.001);
+ return sum / count;
+}
+
+/*
+ Adds a point to transform into a diamond pattern
+ center = Center of the diamond phase (center of a square)
+ a = Side length of the existing square (2, 4, 8, ...)
+
+ Adds the center points of the next squares to next_squares as
+ dummy "true" values.
+*/
+void FixedHeightmap::makeDiamond(
+ v2s16 center,
+ s16 a,
+ f32 randmax,
+ core::map<v2s16, bool> &next_squares)
+{
+ /*dstream<<"makeDiamond(): center="
+ <<"("<<center.X<<","<<center.Y<<")"
+ <<", a="<<a<<", randmax="<<randmax
+ <<", next_squares.size()="<<next_squares.size()
+ <<std::endl;*/
+
+ f32 n = avgDiagNeighbours(center, a/2);
+ // Add (-1.0...1.0) * randmax
+ n += ((float)rand() / (float)(RAND_MAX/2) - 1.0)*randmax;
+ bool worked = setGroundHeightParent(center, n);
+
+ if(a >= 2 && worked)
+ {
+ next_squares[center + a/2*v2s16(-1,0)] = true;
+ next_squares[center + a/2*v2s16(1,0)] = true;
+ next_squares[center + a/2*v2s16(0,-1)] = true;
+ next_squares[center + a/2*v2s16(0,1)] = true;
+ }
+}
+
+/*
+ Adds a point to transform into a square pattern
+ center = The point that is added. The center of a diamond.
+ a = Diameter of the existing diamond. (2, 4, 8, 16, ...)
+
+ Adds center points of the next diamonds to next_diamonds.
+*/
+void FixedHeightmap::makeSquare(
+ v2s16 center,
+ s16 a,
+ f32 randmax,
+ core::map<v2s16, bool> &next_diamonds)
+{
+ /*dstream<<"makeSquare(): center="
+ <<"("<<center.X<<","<<center.Y<<")"
+ <<", a="<<a<<", randmax="<<randmax
+ <<", next_diamonds.size()="<<next_diamonds.size()
+ <<std::endl;*/
+
+ f32 n = avgNeighbours(center, a/2);
+ // Add (-1.0...1.0) * randmax
+ n += ((float)rand() / (float)(RAND_MAX/2) - 1.0)*randmax;
+ bool worked = setGroundHeightParent(center, n);
+
+ if(a >= 4 && worked)
+ {
+ next_diamonds[center + a/4*v2s16(1,1)] = true;
+ next_diamonds[center + a/4*v2s16(-1,1)] = true;
+ next_diamonds[center + a/4*v2s16(-1,-1)] = true;
+ next_diamonds[center + a/4*v2s16(1,-1)] = true;
+ }
+}
+
+void FixedHeightmap::DiamondSquare(f32 randmax, f32 randfactor)
+{
+ u16 a;
+ if(W < H)
+ a = W-1;
+ else
+ a = H-1;
+
+ // Check that a is a power of two
+ if((a & (a-1)) != 0)
+ throw;
+
+ core::map<v2s16, bool> next_diamonds;
+ core::map<v2s16, bool> next_squares;
+
+ next_diamonds[v2s16(a/2, a/2)] = true;
+
+ while(a >= 2)
+ {
+ next_squares.clear();
+
+ for(core::map<v2s16, bool>::Iterator
+ i = next_diamonds.getIterator();
+ i.atEnd() == false; i++)
+ {
+ v2s16 p = i.getNode()->getKey();
+ makeDiamond(p, a, randmax, next_squares);
+ }
+
+ //print();
+
+ next_diamonds.clear();
+
+ for(core::map<v2s16, bool>::Iterator
+ i = next_squares.getIterator();
+ i.atEnd() == false; i++)
+ {
+ v2s16 p = i.getNode()->getKey();
+ makeSquare(p, a, randmax, next_diamonds);
+ }
+
+ //print();
+
+ a /= 2;
+ randmax *= randfactor;
+ }
+}
+
+void FixedHeightmap::generateContinued(f32 randmax, f32 randfactor,
+ f32 *corners)
+{
+ DSTACK(__FUNCTION_NAME);
+ /*dstream<<"FixedHeightmap("<<m_pos_on_master.X
+ <<","<<m_pos_on_master.Y
+ <<")::generateContinued()"<<std::endl;*/
+
+ // Works only with blocksize=2,4,8,16,32,64,...
+ s16 a = m_blocksize;
+
+ // Check that a is a power of two
+ if((a & (a-1)) != 0)
+ throw;
+
+ // Overwrite with GROUNDHEIGHT_NOTFOUND_SETVALUE
+ for(s16 y=0; y<=a; y++){
+ for(s16 x=0; x<=a; x++){
+ v2s16 p(x,y);
+ setGroundHeight(p, GROUNDHEIGHT_NOTFOUND_SETVALUE);
+ }
+ }
+
+ /*
+ Seed borders from master heightmap
+ NOTE: Does this actually have any effect on the output?
+ */
+ struct SeedSpec
+ {
+ v2s16 neighbour_start;
+ v2s16 heightmap_start;
+ v2s16 dir;
+ };
+
+ SeedSpec seeds[4] =
+ {
+ { // Z- edge on X-axis
+ v2s16(0, -1), // neighbour_start
+ v2s16(0, 0), // heightmap_start
+ v2s16(1, 0) // dir
+ },
+ { // Z+ edge on X-axis
+ v2s16(0, m_blocksize),
+ v2s16(0, m_blocksize),
+ v2s16(1, 0)
+ },
+ { // X- edge on Z-axis
+ v2s16(-1, 0),
+ v2s16(0, 0),
+ v2s16(0, 1)
+ },
+ { // X+ edge on Z-axis
+ v2s16(m_blocksize, 0),
+ v2s16(m_blocksize, 0),
+ v2s16(0, 1)
+ },
+ };
+
+ for(s16 i=0; i<4; i++){
+ v2s16 npos = seeds[i].neighbour_start + m_pos_on_master * m_blocksize;
+ v2s16 hpos = seeds[i].heightmap_start;
+ for(s16 s=0; s<m_blocksize+1; s++){
+ f32 h = m_master->getGroundHeight(npos, false);
+ //dstream<<"h="<<h<<std::endl;
+ if(h < GROUNDHEIGHT_VALID_MINVALUE)
+ continue;
+ //break;
+ setGroundHeight(hpos, h);
+ hpos += seeds[i].dir;
+ npos += seeds[i].dir;
+ }
+ }
+
+ /*dstream<<"borders seeded:"<<std::endl;
+ print();*/
+
+ /*
+ Fill with corners[] (if not already set)
+ */
+ v2s16 dirs[4] = {
+ v2s16(0,0),
+ v2s16(1,0),
+ v2s16(1,1),
+ v2s16(0,1),
+ };
+ for(u16 i=0; i<4; i++){
+ v2s16 npos = dirs[i] * a;
+ // Don't replace already seeded corners
+ f32 h = getGroundHeight(npos);
+ if(h > GROUNDHEIGHT_VALID_MINVALUE)
+ continue;
+ setGroundHeight(dirs[i] * a, corners[i]);
+ }
+
+ /*dstream<<"corners filled:"<<std::endl;
+ print();*/
+
+ DiamondSquare(randmax, randfactor);
+}
+
+u32 FixedHeightmap::serializedLength(u8 version, u16 blocksize)
+{
+ if(!ser_ver_supported(version))
+ throw VersionMismatchException("ERROR: FixedHeightmap format not supported");
+
+ // Any version
+ {
+ /*// [0] s32 blocksize
+ // [4] v2s16 pos_on_master
+ // [8] s32 data[W*H] (W=H=blocksize+1)
+ return 4 + 4 + (blocksize+1)*(blocksize+1)*4;*/
+
+ // [8] s32 data[W*H] (W=H=blocksize+1)
+ return (blocksize+1)*(blocksize+1)*4;
+ }
+}
+
+u32 FixedHeightmap::serializedLength(u8 version)
+{
+ return serializedLength(version, m_blocksize);
+}
+
+void FixedHeightmap::serialize(u8 *dest, u8 version)
+{
+ //dstream<<"FixedHeightmap::serialize"<<std::endl;
+
+ if(!ser_ver_supported(version))
+ throw VersionMismatchException("ERROR: FixedHeightmap format not supported");
+
+ // Any version
+ {
+ /*writeU32(&dest[0], m_blocksize);
+ writeV2S16(&dest[4], m_pos_on_master);
+ u32 nodecount = W*H;
+ for(u32 i=0; i<nodecount; i++)
+ {
+ writeS32(&dest[8+i*4], (s32)(m_data[i]*1000.0));
+ }*/
+
+ u32 nodecount = W*H;
+ for(u32 i=0; i<nodecount; i++)
+ {
+ writeS32(&dest[i*4], (s32)(m_data[i]*1000.0));
+ }
+ }
+}
+
+void FixedHeightmap::deSerialize(u8 *source, u8 version)
+{
+ /*dstream<<"FixedHeightmap::deSerialize m_blocksize="
+ <<m_blocksize<<std::endl;*/
+
+ if(!ser_ver_supported(version))
+ throw VersionMismatchException("ERROR: FixedHeightmap format not supported");
+
+ // Any version
+ {
+ u32 nodecount = (m_blocksize+1)*(m_blocksize+1);
+ for(u32 i=0; i<nodecount; i++)
+ {
+ m_data[i] = ((f32)readS32(&source[i*4]))/1000.0;
+ }
+
+ /*printf("source[0,1,2,3]=%x,%x,%x,%x\n",
+ (int)source[0]&0xff,
+ (int)source[1]&0xff,
+ (int)source[2]&0xff,
+ (int)source[3]&0xff);
+
+ dstream<<"m_data[0]="<<m_data[0]<<", "
+ <<"readS32(&source[0])="<<readS32(&source[0])
+ <<std::endl;
+ dstream<<"m_data[4*4]="<<m_data[4*4]<<", "
+ <<"readS32(&source[4*4])="<<readS32(&source[4*4])
+ <<std::endl;*/
+ }
+}
+
+
+void setcolor(f32 h, f32 rangemin, f32 rangemax)
+{
+#ifndef _WIN32
+ const char *colors[] =
+ {
+ "\x1b[40m",
+ "\x1b[44m",
+ "\x1b[46m",
+ "\x1b[42m",
+ "\x1b[43m",
+ "\x1b[41m",
+ };
+ u16 colorcount = sizeof(colors)/sizeof(colors[0]);
+ f32 scaled = (h - rangemin) / (rangemax - rangemin);
+ u8 color = scaled * colorcount;
+ if(color > colorcount-1)
+ color = colorcount-1;
+ /*printf("rangemin=%f, rangemax=%f, h=%f -> color=%i\n",
+ rangemin,
+ rangemax,
+ h,
+ color);*/
+ printf("%s", colors[color]);
+ //printf("\x1b[31;40m");
+ //printf("\x1b[44;1m");
+#endif
+}
+void resetcolor()
+{
+#ifndef _WIN32
+ printf("\x1b[0m");
+#endif
+}
+
+/*
+ UnlimitedHeightmap
+*/
+
+void UnlimitedHeightmap::print()
+{
+ s16 minx = 10000;
+ s16 miny = 10000;
+ s16 maxx = -10000;
+ s16 maxy = -10000;
+ core::map<v2s16, FixedHeightmap*>::Iterator i;
+ i = m_heightmaps.getIterator();
+ if(i.atEnd()){
+ printf("UnlimitedHeightmap::print(): empty.\n");
+ return;
+ }
+ for(; i.atEnd() == false; i++)
+ {
+ v2s16 p = i.getNode()->getValue()->getPosOnMaster();
+ if(p.X < minx) minx = p.X;
+ if(p.Y < miny) miny = p.Y;
+ if(p.X > maxx) maxx = p.X;
+ if(p.Y > maxy) maxy = p.Y;
+ }
+ minx = minx * m_blocksize;
+ miny = miny * m_blocksize;
+ maxx = (maxx+1) * m_blocksize;
+ maxy = (maxy+1) * m_blocksize;
+ printf("UnlimitedHeightmap::print(): from (%i,%i) to (%i,%i)\n",
+ minx, miny, maxx, maxy);
+
+ // Calculate range
+ f32 rangemin = 1e10;
+ f32 rangemax = -1e10;
+ for(s32 y=miny; y<=maxy; y++){
+ for(s32 x=minx; x<=maxx; x++){
+ f32 h = getGroundHeight(v2s16(x,y), false);
+ if(h < GROUNDHEIGHT_VALID_MINVALUE)
+ continue;
+ if(h < rangemin)
+ rangemin = h;
+ if(h > rangemax)
+ rangemax = h;
+ }
+ }
+
+ printf(" ");
+ for(s32 x=minx; x<=maxx; x++){
+ printf("% .3d ", x);
+ }
+ printf("\n");
+
+ for(s32 y=miny; y<=maxy; y++){
+ printf("% .3d ", y);
+ for(s32 x=minx; x<=maxx; x++){
+ f32 n = getGroundHeight(v2s16(x,y), false);
+ if(n < GROUNDHEIGHT_VALID_MINVALUE)
+ printf(" - ");
+ else
+ {
+ setcolor(n, rangemin, rangemax);
+ printf("% -5.1f", getGroundHeight(v2s16(x,y), false));
+ resetcolor();
+ }
+ }
+ printf("\n");
+ }
+}
+
+FixedHeightmap * UnlimitedHeightmap::getHeightmap(v2s16 p_from, bool generate)
+{
+ DSTACK("UnlimitedHeightmap::getHeightmap()");
+ /*
+ We want to check that all neighbours of the wanted heightmap
+ exist.
+ This is because generating the neighboring heightmaps will
+ modify the current one.
+ */
+
+ if(generate)
+ {
+ // Go through all neighbors (corners also) and the current one
+ // and generate every one of them.
+ for(s16 x=p_from.X-1; x<=p_from.X+1; x++)
+ for(s16 y=p_from.Y-1; y<=p_from.Y+1; y++)
+ {
+ v2s16 p(x,y);
+
+ // Check if exists
+ core::map<v2s16, FixedHeightmap*>::Node *n = m_heightmaps.find(p);
+ if(n != NULL)
+ continue;
+
+ // Doesn't exist
+ // Generate it
+
+ FixedHeightmap *heightmap = new FixedHeightmap(this, p, m_blocksize);
+
+ m_heightmaps.insert(p, heightmap);
+
+ f32 corners[4] = {
+ m_base_generator->getValue(p+v2s16(0,0)),
+ m_base_generator->getValue(p+v2s16(1,0)),
+ m_base_generator->getValue(p+v2s16(1,1)),
+ m_base_generator->getValue(p+v2s16(0,1)),
+ };
+
+ f32 randmax = m_randmax_generator->getValue(p);
+ f32 randfactor = m_randfactor_generator->getValue(p);
+
+ heightmap->generateContinued(randmax, randfactor, corners);
+ }
+ }
+
+ core::map<v2s16, FixedHeightmap*>::Node *n = m_heightmaps.find(p_from);
+
+ if(n != NULL)
+ {
+ return n->getValue();
+ }
+ else
+ {
+ throw InvalidPositionException
+ ("Something went really wrong in UnlimitedHeightmap::getHeightmap");
+ }
+}
+
+f32 UnlimitedHeightmap::getGroundHeight(v2s16 p, bool generate)
+{
+ v2s16 heightmappos = getNodeHeightmapPos(p);
+ v2s16 relpos = p - heightmappos*m_blocksize;
+ try{
+ FixedHeightmap * href = getHeightmap(heightmappos, generate);
+ f32 h = href->getGroundHeight(relpos);
+ if(h > GROUNDHEIGHT_VALID_MINVALUE)
+ return h;
+ }
+ catch(InvalidPositionException){}
+ /*
+ If on border or in the (0,0) corner, try to get from
+ overlapping heightmaps
+ */
+ if(relpos.X == 0){
+ try{
+ FixedHeightmap * href = getHeightmap(
+ heightmappos-v2s16(1,0), false);
+ f32 h = href->getGroundHeight(v2s16(m_blocksize, relpos.Y));
+ if(h > GROUNDHEIGHT_VALID_MINVALUE)
+ return h;
+ }
+ catch(InvalidPositionException){}
+ }
+ if(relpos.Y == 0){
+ try{
+ FixedHeightmap * href = getHeightmap(
+ heightmappos-v2s16(0,1), false);
+ f32 h = href->getGroundHeight(v2s16(relpos.X, m_blocksize));
+ if(h > GROUNDHEIGHT_VALID_MINVALUE)
+ return h;
+ }
+ catch(InvalidPositionException){}
+ }
+ if(relpos.X == 0 && relpos.Y == 0){
+ try{
+ FixedHeightmap * href = getHeightmap(
+ heightmappos-v2s16(1,1), false);
+ f32 h = href->getGroundHeight(v2s16(m_blocksize, m_blocksize));
+ if(h > GROUNDHEIGHT_VALID_MINVALUE)
+ return h;
+ }
+ catch(InvalidPositionException){}
+ }
+ return GROUNDHEIGHT_NOTFOUND_SETVALUE;
+}
+
+void UnlimitedHeightmap::setGroundHeight(v2s16 p, f32 y, bool generate)
+{
+ bool was_set = false;
+
+ v2s16 heightmappos = getNodeHeightmapPos(p);
+ v2s16 relpos = p - heightmappos*m_blocksize;
+ /*dstream<<"UnlimitedHeightmap::setGroundHeight(("
+ <<p.X<<","<<p.Y<<"), "<<y<<"): "
+ <<"heightmappos=("<<heightmappos.X<<","
+ <<heightmappos.Y<<") relpos=("
+ <<relpos.X<<","<<relpos.Y<<")"
+ <<std::endl;*/
+ try{
+ FixedHeightmap * href = getHeightmap(heightmappos, generate);
+ href->setGroundHeight(relpos, y);
+ was_set = true;
+ }catch(InvalidPositionException){}
+ // Update in neighbour heightmap if it's at border
+ if(relpos.X == 0){
+ try{
+ FixedHeightmap * href = getHeightmap(
+ heightmappos-v2s16(1,0), generate);
+ href->setGroundHeight(v2s16(m_blocksize, relpos.Y), y);
+ was_set = true;
+ }catch(InvalidPositionException){}
+ }
+ if(relpos.Y == 0){
+ try{
+ FixedHeightmap * href = getHeightmap(
+ heightmappos-v2s16(0,1), generate);
+ href->setGroundHeight(v2s16(relpos.X, m_blocksize), y);
+ was_set = true;
+ }catch(InvalidPositionException){}
+ }
+ if(relpos.X == 0 && relpos.Y == 0){
+ try{
+ FixedHeightmap * href = getHeightmap(
+ heightmappos-v2s16(1,1), generate);
+ href->setGroundHeight(v2s16(m_blocksize, m_blocksize), y);
+ was_set = true;
+ }catch(InvalidPositionException){}
+ }
+
+ if(was_set == false)
+ {
+ throw InvalidPositionException
+ ("UnlimitedHeightmap failed to set height");
+ }
+}
+
+
+void UnlimitedHeightmap::serialize(std::ostream &os, u8 version)
+{
+ //dstream<<"UnlimitedHeightmap::serialize()"<<std::endl;
+
+ if(!ser_ver_supported(version))
+ throw VersionMismatchException
+ ("ERROR: UnlimitedHeightmap format not supported");
+
+ if(version <= 7)
+ {
+ /*if(m_base_generator->getId() != VALUE_GENERATOR_ID_CONSTANT
+ || m_randmax_generator->getId() != VALUE_GENERATOR_ID_CONSTANT
+ || m_randfactor_generator->getId() != VALUE_GENERATOR_ID_CONSTANT)*/
+ if(std::string(m_base_generator->getName()) != "constant"
+ || std::string(m_randmax_generator->getName()) != "constant"
+ || std::string(m_randfactor_generator->getName()) != "constant")
+ {
+ throw SerializationError
+ ("Cannot write UnlimitedHeightmap in old version: "
+ "Generators are not ConstantGenerators.");
+ }
+
+ f32 basevalue = ((ConstantGenerator*)m_base_generator)->m_value;
+ f32 randmax = ((ConstantGenerator*)m_randmax_generator)->m_value;
+ f32 randfactor = ((ConstantGenerator*)m_randfactor_generator)->m_value;
+
+ // Write version
+ os.write((char*)&version, 1);
+
+ /*
+ [0] u16 blocksize
+ [2] s32 randmax*1000
+ [6] s32 randfactor*1000
+ [10] s32 basevalue*1000
+ [14] u32 heightmap_count
+ [18] X * (v2s16 pos + heightmap)
+ */
+ u32 heightmap_size =
+ FixedHeightmap::serializedLength(version, m_blocksize);
+ u32 heightmap_count = m_heightmaps.size();
+
+ //dstream<<"heightmap_size="<<heightmap_size<<std::endl;
+
+ u32 datasize = 2+4+4+4+4+heightmap_count*(4+heightmap_size);
+ SharedBuffer<u8> data(datasize);
+
+ writeU16(&data[0], m_blocksize);
+ writeU32(&data[2], (s32)(randmax*1000.0));
+ writeU32(&data[6], (s32)(randfactor*1000.0));
+ writeU32(&data[10], (s32)(basevalue*1000.0));
+ writeU32(&data[14], heightmap_count);
+
+ core::map<v2s16, FixedHeightmap*>::Iterator j;
+ j = m_heightmaps.getIterator();
+ u32 i=0;
+ for(; j.atEnd() == false; j++)
+ {
+ FixedHeightmap *hm = j.getNode()->getValue();
+ v2s16 pos = j.getNode()->getKey();
+ writeV2S16(&data[18+i*(4+heightmap_size)], pos);
+ hm->serialize(&data[18+i*(4+heightmap_size)+4], version);
+ i++;
+ }
+
+ os.write((char*)*data, data.getSize());
+ }
+ else
+ {
+ // Write version
+ os.write((char*)&version, 1);
+
+ u8 buf[4];
+
+ writeU16(buf, m_blocksize);
+ os.write((char*)buf, 2);
+
+ /*m_randmax_generator->serialize(os, version);
+ m_randfactor_generator->serialize(os, version);
+ m_base_generator->serialize(os, version);*/
+ m_randmax_generator->serialize(os);
+ m_randfactor_generator->serialize(os);
+ m_base_generator->serialize(os);
+
+ u32 heightmap_count = m_heightmaps.size();
+ writeU32(buf, heightmap_count);
+ os.write((char*)buf, 4);
+
+ u32 heightmap_size =
+ FixedHeightmap::serializedLength(version, m_blocksize);
+
+ SharedBuffer<u8> hmdata(heightmap_size);
+
+ core::map<v2s16, FixedHeightmap*>::Iterator j;
+ j = m_heightmaps.getIterator();
+ u32 i=0;
+ for(; j.atEnd() == false; j++)
+ {
+ v2s16 pos = j.getNode()->getKey();
+ writeV2S16(buf, pos);
+ os.write((char*)buf, 4);
+
+ FixedHeightmap *hm = j.getNode()->getValue();
+ hm->serialize(*hmdata, version);
+ os.write((char*)*hmdata, hmdata.getSize());
+
+ i++;
+ }
+ }
+}
+
+UnlimitedHeightmap * UnlimitedHeightmap::deSerialize(std::istream &is)
+{
+ u8 version;
+ is.read((char*)&version, 1);
+
+ if(!ser_ver_supported(version))
+ throw VersionMismatchException("ERROR: UnlimitedHeightmap format not supported");
+
+ if(version <= 7)
+ {
+ /*
+ [0] u16 blocksize
+ [2] s32 randmax*1000
+ [6] s32 randfactor*1000
+ [10] s32 basevalue*1000
+ [14] u32 heightmap_count
+ [18] X * (v2s16 pos + heightmap)
+ */
+ SharedBuffer<u8> data(18);
+ is.read((char*)*data, 18);
+ if(is.gcount() != 18)
+ throw SerializationError
+ ("UnlimitedHeightmap::deSerialize: no enough input data");
+ s16 blocksize = readU16(&data[0]);
+ f32 randmax = (f32)readU32(&data[2]) / 1000.0;
+ f32 randfactor = (f32)readU32(&data[6]) / 1000.0;
+ f32 basevalue = (f32)readU32(&data[10]) / 1000.0;
+ u32 heightmap_count = readU32(&data[14]);
+
+ /*dstream<<"UnlimitedHeightmap::deSerialize():"
+ <<" blocksize="<<blocksize
+ <<" heightmap_count="<<heightmap_count
+ <<std::endl;*/
+
+ u32 heightmap_size =
+ FixedHeightmap::serializedLength(version, blocksize);
+
+ //dstream<<"heightmap_size="<<heightmap_size<<std::endl;
+
+ ValueGenerator *maxgen = new ConstantGenerator(randmax);
+ ValueGenerator *factorgen = new ConstantGenerator(randfactor);
+ ValueGenerator *basegen = new ConstantGenerator(basevalue);
+
+ UnlimitedHeightmap *hm = new UnlimitedHeightmap
+ (blocksize, maxgen, factorgen, basegen);
+
+ for(u32 i=0; i<heightmap_count; i++)
+ {
+ //dstream<<"i="<<i<<std::endl;
+ SharedBuffer<u8> data(4+heightmap_size);
+ is.read((char*)*data, 4+heightmap_size);
+ if(is.gcount() != (s32)(4+heightmap_size)){
+ delete hm;
+ throw SerializationError
+ ("UnlimitedHeightmap::deSerialize: no enough input data");
+ }
+ v2s16 pos = readV2S16(&data[0]);
+ FixedHeightmap *f = new FixedHeightmap(hm, pos, blocksize);
+ f->deSerialize(&data[4], version);
+ hm->m_heightmaps.insert(pos, f);
+ }
+ return hm;
+ }
+ else
+ {
+ u8 buf[4];
+
+ is.read((char*)buf, 2);
+ s16 blocksize = readU16(buf);
+
+ ValueGenerator *maxgen = ValueGenerator::deSerialize(is);
+ ValueGenerator *factorgen = ValueGenerator::deSerialize(is);
+ ValueGenerator *basegen = ValueGenerator::deSerialize(is);
+
+ is.read((char*)buf, 4);
+ u32 heightmap_count = readU32(buf);
+
+ u32 heightmap_size =
+ FixedHeightmap::serializedLength(version, blocksize);
+
+ UnlimitedHeightmap *hm = new UnlimitedHeightmap
+ (blocksize, maxgen, factorgen, basegen);
+
+ for(u32 i=0; i<heightmap_count; i++)
+ {
+ is.read((char*)buf, 4);
+ v2s16 pos = readV2S16(buf);
+
+ SharedBuffer<u8> data(heightmap_size);
+ is.read((char*)*data, heightmap_size);
+ if(is.gcount() != (s32)(heightmap_size)){
+ delete hm;
+ throw SerializationError
+ ("UnlimitedHeightmap::deSerialize: no enough input data");
+ }
+ FixedHeightmap *f = new FixedHeightmap(hm, pos, blocksize);
+ f->deSerialize(*data, version);
+ hm->m_heightmaps.insert(pos, f);
+ }
+ return hm;
+ }
+}
+
+