2f7ecc4dc01c336c4fac87436645192af1dbd53f
[oweals/minetest.git] / src / heightmap.cpp
1 /*
2 Minetest-c55
3 Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 /*
21 (c) 2010 Perttu Ahola <celeron55@gmail.com>
22 */
23
24 #include "heightmap.h"
25
26 /*
27         ValueGenerator
28 */
29
30 ValueGenerator* ValueGenerator::deSerialize(std::string line)
31 {
32         std::istringstream ss(line);
33         //ss.imbue(std::locale("C"));
34         
35         std::string name;
36         std::getline(ss, name, ' ');
37
38         if(name == "constant")
39         {
40                 f32 value;
41                 ss>>value;
42                 
43                 return new ConstantGenerator(value);
44         }
45         else if(name == "linear")
46         {
47                 f32 height;
48                 v2f slope;
49
50                 ss>>height;
51                 ss>>slope.X;
52                 ss>>slope.Y;
53
54                 return new LinearGenerator(height, slope);
55         }
56         else if(name == "power")
57         {
58                 f32 height;
59                 v2f slope;
60                 f32 power;
61
62                 ss>>height;
63                 ss>>slope.X;
64                 ss>>slope.Y;
65                 ss>>power;
66
67                 return new PowerGenerator(height, slope, power);
68         }
69         else
70         {
71                 throw SerializationError
72                 ("Invalid heightmap generator (deSerialize)");
73         }
74 }
75
76 /*
77         FixedHeightmap
78 */
79
80 f32 FixedHeightmap::avgNeighbours(v2s16 p, s16 d)
81 {
82         v2s16 dirs[4] = {
83                 v2s16(1,0),
84                 v2s16(0,1),
85                 v2s16(-1,0),
86                 v2s16(0,-1)
87         };
88         f32 sum = 0.0;
89         f32 count = 0.0;
90         for(u16 i=0; i<4; i++){
91                 v2s16 p2 = p + dirs[i] * d;
92                 f32 n = getGroundHeightParent(p2);
93                 if(n < GROUNDHEIGHT_VALID_MINVALUE)
94                         continue;
95                 sum += n;
96                 count += 1.0;
97         }
98         assert(count > 0.001);
99         return sum / count;
100 }
101
102 f32 FixedHeightmap::avgDiagNeighbours(v2s16 p, s16 d)
103 {
104         v2s16 dirs[4] = {
105                 v2s16(1,1),
106                 v2s16(-1,-1),
107                 v2s16(-1,1),
108                 v2s16(1,-1)
109         };
110         f32 sum = 0.0;
111         f32 count = 0.0;
112         for(u16 i=0; i<4; i++){
113                 v2s16 p2 = p + dirs[i] * d;
114                 f32 n = getGroundHeightParent(p2);
115                 if(n < GROUNDHEIGHT_VALID_MINVALUE)
116                         continue;
117                 sum += n;
118                 count += 1.0;
119         }
120         assert(count > 0.001);
121         return sum / count;
122 }
123
124 /*
125         Adds a point to transform into a diamond pattern
126         center = Center of the diamond phase (center of a square)
127         a = Side length of the existing square (2, 4, 8, ...)
128
129         Adds the center points of the next squares to next_squares as
130         dummy "true" values.
131 */
132 void FixedHeightmap::makeDiamond(
133                 v2s16 center,
134                 s16 a,
135                 f32 randmax,
136                 core::map<v2s16, bool> &next_squares)
137 {
138         /*dstream<<"makeDiamond(): center="
139                         <<"("<<center.X<<","<<center.Y<<")"
140                         <<", a="<<a<<", randmax="<<randmax
141                         <<", next_squares.size()="<<next_squares.size()
142                         <<std::endl;*/
143
144         f32 n = avgDiagNeighbours(center, a/2);
145         // Add (-1.0...1.0) * randmax
146         n += ((float)myrand() / (float)(MYRAND_MAX/2) - 1.0)*randmax;
147         bool worked = setGroundHeightParent(center, n);
148         
149         if(a >= 2 && worked)
150         {
151                 next_squares[center + a/2*v2s16(-1,0)] = true;
152                 next_squares[center + a/2*v2s16(1,0)] = true;
153                 next_squares[center + a/2*v2s16(0,-1)] = true;
154                 next_squares[center + a/2*v2s16(0,1)] = true;
155         }
156 }
157
158 /*
159         Adds a point to transform into a square pattern
160         center = The point that is added. The center of a diamond.
161         a = Diameter of the existing diamond. (2, 4, 8, 16, ...)
162
163         Adds center points of the next diamonds to next_diamonds.
164 */
165 void FixedHeightmap::makeSquare(
166                 v2s16 center,
167                 s16 a,
168                 f32 randmax,
169                 core::map<v2s16, bool> &next_diamonds)
170 {
171         /*dstream<<"makeSquare(): center="
172                         <<"("<<center.X<<","<<center.Y<<")"
173                         <<", a="<<a<<", randmax="<<randmax
174                         <<", next_diamonds.size()="<<next_diamonds.size()
175                         <<std::endl;*/
176
177         f32 n = avgNeighbours(center, a/2);
178         // Add (-1.0...1.0) * randmax
179         n += ((float)myrand() / (float)(MYRAND_MAX/2) - 1.0)*randmax;
180         bool worked = setGroundHeightParent(center, n);
181         
182         if(a >= 4 && worked)
183         {
184                 next_diamonds[center + a/4*v2s16(1,1)] = true;
185                 next_diamonds[center + a/4*v2s16(-1,1)] = true;
186                 next_diamonds[center + a/4*v2s16(-1,-1)] = true;
187                 next_diamonds[center + a/4*v2s16(1,-1)] = true;
188         }
189 }
190
191 void FixedHeightmap::DiamondSquare(f32 randmax, f32 randfactor)
192 {
193         u16 a;
194         if(W < H)
195                 a = W-1;
196         else
197                 a = H-1;
198         
199         // Check that a is a power of two
200         if((a & (a-1)) != 0)
201                 throw;
202         
203         core::map<v2s16, bool> next_diamonds;
204         core::map<v2s16, bool> next_squares;
205
206         next_diamonds[v2s16(a/2, a/2)] = true;
207         
208         while(a >= 2)
209         {
210                 next_squares.clear();
211                 
212                 for(core::map<v2s16, bool>::Iterator
213                                 i = next_diamonds.getIterator();
214                                 i.atEnd() == false; i++)
215                 {
216                         v2s16 p = i.getNode()->getKey();
217                         makeDiamond(p, a, randmax, next_squares);
218                 }
219
220                 //print();
221                 
222                 next_diamonds.clear();
223                 
224                 for(core::map<v2s16, bool>::Iterator
225                                 i = next_squares.getIterator();
226                                 i.atEnd() == false; i++)
227                 {
228                         v2s16 p = i.getNode()->getKey();
229                         makeSquare(p, a, randmax, next_diamonds);
230                 }
231
232                 //print();
233                 
234                 a /= 2;
235                 randmax *= randfactor;
236         }
237 }
238
239 void FixedHeightmap::generateContinued(f32 randmax, f32 randfactor,
240                 f32 *corners)
241 {
242         DSTACK(__FUNCTION_NAME);
243         /*dstream<<"FixedHeightmap("<<m_pos_on_master.X
244                         <<","<<m_pos_on_master.Y
245                         <<")::generateContinued()"<<std::endl;*/
246
247         // Works only with blocksize=2,4,8,16,32,64,...
248         s16 a = m_blocksize;
249         
250         // Check that a is a power of two
251         assert((a & (a-1)) == 0);
252         
253         // Overwrite with GROUNDHEIGHT_NOTFOUND_SETVALUE
254         for(s16 y=0; y<=a; y++){
255                 for(s16 x=0; x<=a; x++){
256                         v2s16 p(x,y);
257                         setGroundHeight(p, GROUNDHEIGHT_NOTFOUND_SETVALUE);
258                 }
259         }
260
261         /*
262                 Seed borders from master heightmap
263                 NOTE: Does this actually have any effect on the output?
264         */
265         /*struct SeedSpec
266         {
267                 v2s16 neighbour_start;
268                 v2s16 heightmap_start;
269                 v2s16 dir;
270         };
271
272         SeedSpec seeds[4] =
273         {
274                 { // Z- edge on X-axis
275                         v2s16(0, -1), // neighbour_start
276                         v2s16(0, 0), // heightmap_start
277                         v2s16(1, 0) // dir
278                 },
279                 { // Z+ edge on X-axis
280                         v2s16(0, m_blocksize),
281                         v2s16(0, m_blocksize),
282                         v2s16(1, 0)
283                 },
284                 { // X- edge on Z-axis
285                         v2s16(-1, 0),
286                         v2s16(0, 0),
287                         v2s16(0, 1)
288                 },
289                 { // X+ edge on Z-axis
290                         v2s16(m_blocksize, 0),
291                         v2s16(m_blocksize, 0),
292                         v2s16(0, 1)
293                 },
294         };
295
296         for(s16 i=0; i<4; i++){
297                 v2s16 npos = seeds[i].neighbour_start + m_pos_on_master * m_blocksize;
298                 v2s16 hpos = seeds[i].heightmap_start;
299                 for(s16 s=0; s<m_blocksize+1; s++){
300                         f32 h = m_master->getGroundHeight(npos, false);
301                         //dstream<<"h="<<h<<std::endl;
302                         if(h < GROUNDHEIGHT_VALID_MINVALUE)
303                                 continue;
304                                 //break;
305                         setGroundHeight(hpos, h);
306                         hpos += seeds[i].dir;
307                         npos += seeds[i].dir;
308                 }
309         }*/
310         
311         /*dstream<<"borders seeded:"<<std::endl;
312         print();*/
313
314         /*
315                 Fill with corners[] (if not already set)
316         */
317         v2s16 dirs[4] = {
318                 v2s16(0,0),
319                 v2s16(1,0),
320                 v2s16(1,1),
321                 v2s16(0,1),
322         };
323         for(u16 i=0; i<4; i++){
324                 v2s16 npos = dirs[i] * a;
325                 // Don't replace already seeded corners
326                 f32 h = getGroundHeight(npos);
327                 if(h > GROUNDHEIGHT_VALID_MINVALUE)
328                         continue;
329                 setGroundHeight(dirs[i] * a, corners[i]);
330         }
331         
332         /*dstream<<"corners filled:"<<std::endl;
333         print();*/
334
335         DiamondSquare(randmax, randfactor);
336 }
337
338 u32 FixedHeightmap::serializedLength(u8 version, u16 blocksize)
339 {
340         if(!ser_ver_supported(version))
341                 throw VersionMismatchException("ERROR: FixedHeightmap format not supported");
342         
343         // Any version
344         {
345                 /*// [0] s32 blocksize
346                 // [4] v2s16 pos_on_master
347                 // [8] s32 data[W*H] (W=H=blocksize+1)
348                 return 4 + 4 + (blocksize+1)*(blocksize+1)*4;*/
349
350                 // [8] s32 data[W*H] (W=H=blocksize+1)
351                 return (blocksize+1)*(blocksize+1)*4;
352         }
353 }
354
355 u32 FixedHeightmap::serializedLength(u8 version)
356 {
357         return serializedLength(version, m_blocksize);
358 }
359
360 void FixedHeightmap::serialize(u8 *dest, u8 version)
361 {
362         //dstream<<"FixedHeightmap::serialize"<<std::endl;
363
364         if(!ser_ver_supported(version))
365                 throw VersionMismatchException("ERROR: FixedHeightmap format not supported");
366         
367         // Any version
368         {
369                 /*writeU32(&dest[0], m_blocksize);
370                 writeV2S16(&dest[4], m_pos_on_master);
371                 u32 nodecount = W*H;
372                 for(u32 i=0; i<nodecount; i++)
373                 {
374                         writeS32(&dest[8+i*4], (s32)(m_data[i]*1000.0));
375                 }*/
376
377                 u32 nodecount = W*H;
378                 for(u32 i=0; i<nodecount; i++)
379                 {
380                         writeS32(&dest[i*4], (s32)(m_data[i]*1000.0));
381                 }
382         }
383 }
384
385 void FixedHeightmap::deSerialize(u8 *source, u8 version)
386 {
387         /*dstream<<"FixedHeightmap::deSerialize m_blocksize="
388                         <<m_blocksize<<std::endl;*/
389
390         if(!ser_ver_supported(version))
391                 throw VersionMismatchException("ERROR: FixedHeightmap format not supported");
392         
393         // Any version
394         {
395                 u32 nodecount = (m_blocksize+1)*(m_blocksize+1);
396                 for(u32 i=0; i<nodecount; i++)
397                 {
398                         m_data[i] = ((f32)readS32(&source[i*4]))/1000.0;
399                 }
400
401                 /*printf("source[0,1,2,3]=%x,%x,%x,%x\n",
402                                 (int)source[0]&0xff,
403                                 (int)source[1]&0xff,
404                                 (int)source[2]&0xff,
405                                 (int)source[3]&0xff);
406                                 
407                 dstream<<"m_data[0]="<<m_data[0]<<", "
408                                 <<"readS32(&source[0])="<<readS32(&source[0])
409                                 <<std::endl;
410                 dstream<<"m_data[4*4]="<<m_data[4*4]<<", "
411                                 <<"readS32(&source[4*4])="<<readS32(&source[4*4])
412                                 <<std::endl;*/
413         }
414 }
415
416
417 void setcolor(f32 h, f32 rangemin, f32 rangemax)
418 {
419 #ifndef _WIN32
420         const char *colors[] =
421         {
422                 "\x1b[40m",
423                 "\x1b[44m",
424                 "\x1b[46m",
425                 "\x1b[42m",
426                 "\x1b[43m",
427                 "\x1b[41m",
428         };
429         u16 colorcount = sizeof(colors)/sizeof(colors[0]);
430         f32 scaled = (h - rangemin) / (rangemax - rangemin);
431         u8 color = scaled * colorcount;
432         if(color > colorcount-1)
433                 color = colorcount-1;
434         /*printf("rangemin=%f, rangemax=%f, h=%f -> color=%i\n",
435                 rangemin,
436                 rangemax,
437                 h,
438                 color);*/
439         printf("%s", colors[color]);
440         //printf("\x1b[31;40m");
441         //printf("\x1b[44;1m");
442 #endif
443 }
444 void resetcolor()
445 {
446 #ifndef _WIN32
447         printf("\x1b[0m");
448 #endif
449 }
450
451 /*
452         UnlimitedHeightmap
453 */
454
455 void UnlimitedHeightmap::print()
456 {
457         s16 minx =  10000;
458         s16 miny =  10000;
459         s16 maxx = -10000;
460         s16 maxy = -10000;
461         core::map<v2s16, FixedHeightmap*>::Iterator i;
462         i = m_heightmaps.getIterator();
463         if(i.atEnd()){
464                 printf("UnlimitedHeightmap::print(): empty.\n");
465                 return;
466         }
467         for(; i.atEnd() == false; i++)
468         {
469                 v2s16 p = i.getNode()->getValue()->getPosOnMaster();
470                 if(p.X < minx) minx = p.X;
471                 if(p.Y < miny) miny = p.Y;
472                 if(p.X > maxx) maxx = p.X;
473                 if(p.Y > maxy) maxy = p.Y;
474         }
475         minx = minx * m_blocksize;
476         miny = miny * m_blocksize;
477         maxx = (maxx+1) * m_blocksize;
478         maxy = (maxy+1) * m_blocksize;
479         printf("UnlimitedHeightmap::print(): from (%i,%i) to (%i,%i)\n",
480                         minx, miny, maxx, maxy);
481         
482         // Calculate range
483         f32 rangemin = 1e10;
484         f32 rangemax = -1e10;
485         for(s32 y=miny; y<=maxy; y++){
486                 for(s32 x=minx; x<=maxx; x++){
487                         f32 h = getGroundHeight(v2s16(x,y), false);
488                         if(h < GROUNDHEIGHT_VALID_MINVALUE)
489                                 continue;
490                         if(h < rangemin)
491                                 rangemin = h;
492                         if(h > rangemax)
493                                 rangemax = h;
494                 }
495         }
496
497         printf("     ");
498         for(s32 x=minx; x<=maxx; x++){
499                 printf("% .3d ", x);
500         }
501         printf("\n");
502         
503         for(s32 y=miny; y<=maxy; y++){
504                 printf("% .3d ", y);
505                 for(s32 x=minx; x<=maxx; x++){
506                         f32 n = getGroundHeight(v2s16(x,y), false);
507                         if(n < GROUNDHEIGHT_VALID_MINVALUE)
508                                 printf("  -   ");
509                         else
510                         {
511                                 setcolor(n, rangemin, rangemax);
512                                 printf("% -5.1f", getGroundHeight(v2s16(x,y), false));
513                                 resetcolor();
514                         }
515                 }
516                 printf("\n");
517         }
518 }
519         
520 FixedHeightmap * UnlimitedHeightmap::getHeightmap(v2s16 p_from, bool generate)
521 {
522         DSTACK("UnlimitedHeightmap::getHeightmap()");
523         /*
524                 We want to check that all neighbours of the wanted heightmap
525                 exist.
526                 This is because generating the neighboring heightmaps will
527                 modify the current one.
528         */
529         
530         if(generate)
531         {
532                 // Go through all neighbors (corners also) and the current one
533                 // and generate every one of them.
534                 for(s16 x=p_from.X-1; x<=p_from.X+1; x++)
535                 for(s16 y=p_from.Y-1; y<=p_from.Y+1; y++)
536                 {
537                         v2s16 p(x,y);
538
539                         // Check if exists
540                         core::map<v2s16, FixedHeightmap*>::Node *n = m_heightmaps.find(p);
541                         if(n != NULL)
542                                 continue;
543                         
544                         // Doesn't exist
545                         // Generate it
546
547                         FixedHeightmap *heightmap = new FixedHeightmap(this, p, m_blocksize);
548
549                         m_heightmaps.insert(p, heightmap);
550
551                         f32 corners[4];
552                         
553                         if(m_palist)
554                         {
555                                 //TODO: palist must be taken into account in generateContinued.
556                                 // It is almost useless in here.
557                                 s32 div = 2 * 16;
558                                 Settings *attr = m_palist->getNearAttr(p / div);
559                                 assert(attr);
560                                 corners[0] = attr->getFloat("baseheight");
561                                 corners[1] = attr->getFloat("baseheight");
562                                 corners[2] = attr->getFloat("baseheight");
563                                 corners[3] = attr->getFloat("baseheight");
564                         }
565                         else
566                         {
567                                 corners[0] = m_base_generator->getValue(p+v2s16(0,0));
568                                 corners[1] = m_base_generator->getValue(p+v2s16(1,0));
569                                 corners[2] = m_base_generator->getValue(p+v2s16(1,1));
570                                 corners[3] = m_base_generator->getValue(p+v2s16(0,1));
571                         }
572
573                         f32 randmax = m_randmax_generator->getValue(p);
574                         f32 randfactor = m_randfactor_generator->getValue(p);
575
576                         heightmap->generateContinued(randmax, randfactor, corners);
577                 }
578         }
579
580         core::map<v2s16, FixedHeightmap*>::Node *n = m_heightmaps.find(p_from);
581
582         if(n != NULL)
583         {
584                 return n->getValue();
585         }
586         else
587         {
588                 throw InvalidPositionException
589                 ("Something went really wrong in UnlimitedHeightmap::getHeightmap");
590         }
591 }
592
593 f32 UnlimitedHeightmap::getGroundHeight(v2s16 p, bool generate)
594 {
595         v2s16 heightmappos = getNodeHeightmapPos(p);
596         v2s16 relpos = p - heightmappos*m_blocksize;
597         try{
598                 FixedHeightmap * href = getHeightmap(heightmappos, generate);
599                 f32 h = href->getGroundHeight(relpos);
600                 if(h > GROUNDHEIGHT_VALID_MINVALUE)
601                         return h;
602         }
603         catch(InvalidPositionException){}
604         /*
605                 If on border or in the (0,0) corner, try to get from
606                 overlapping heightmaps
607         */
608         if(relpos.X == 0){
609                 try{
610                         FixedHeightmap * href = getHeightmap(
611                                         heightmappos-v2s16(1,0), false);
612                         f32 h = href->getGroundHeight(v2s16(m_blocksize, relpos.Y));
613                         if(h > GROUNDHEIGHT_VALID_MINVALUE)
614                                 return h;
615                 }
616                 catch(InvalidPositionException){}
617         }
618         if(relpos.Y == 0){
619                 try{
620                         FixedHeightmap * href = getHeightmap(
621                                         heightmappos-v2s16(0,1), false);
622                         f32 h = href->getGroundHeight(v2s16(relpos.X, m_blocksize));
623                         if(h > GROUNDHEIGHT_VALID_MINVALUE)
624                                 return h;
625                 }
626                 catch(InvalidPositionException){}
627         }
628         if(relpos.X == 0 && relpos.Y == 0){
629                 try{
630                         FixedHeightmap * href = getHeightmap(
631                                         heightmappos-v2s16(1,1), false);
632                         f32 h = href->getGroundHeight(v2s16(m_blocksize, m_blocksize));
633                         if(h > GROUNDHEIGHT_VALID_MINVALUE)
634                                 return h;
635                 }
636                 catch(InvalidPositionException){}
637         }
638         return GROUNDHEIGHT_NOTFOUND_SETVALUE;
639 }
640
641 void UnlimitedHeightmap::setGroundHeight(v2s16 p, f32 y, bool generate)
642 {
643         bool was_set = false;
644
645         v2s16 heightmappos = getNodeHeightmapPos(p);
646         v2s16 relpos = p - heightmappos*m_blocksize;
647         /*dstream<<"UnlimitedHeightmap::setGroundHeight(("
648                         <<p.X<<","<<p.Y<<"), "<<y<<"): "
649                         <<"heightmappos=("<<heightmappos.X<<","
650                         <<heightmappos.Y<<") relpos=("
651                         <<relpos.X<<","<<relpos.Y<<")"
652                         <<std::endl;*/
653         try{
654                 FixedHeightmap * href = getHeightmap(heightmappos, generate);
655                 href->setGroundHeight(relpos, y);
656                 was_set = true;
657         }catch(InvalidPositionException){}
658         // Update in neighbour heightmap if it's at border
659         if(relpos.X == 0){
660                 try{
661                         FixedHeightmap * href = getHeightmap(
662                                         heightmappos-v2s16(1,0), generate);
663                         href->setGroundHeight(v2s16(m_blocksize, relpos.Y), y);
664                         was_set = true;
665                 }catch(InvalidPositionException){}
666         }
667         if(relpos.Y == 0){
668                 try{
669                         FixedHeightmap * href = getHeightmap(
670                                         heightmappos-v2s16(0,1), generate);
671                         href->setGroundHeight(v2s16(relpos.X, m_blocksize), y);
672                         was_set = true;
673                 }catch(InvalidPositionException){}
674         }
675         if(relpos.X == 0 && relpos.Y == 0){
676                 try{
677                         FixedHeightmap * href = getHeightmap(
678                                         heightmappos-v2s16(1,1), generate);
679                         href->setGroundHeight(v2s16(m_blocksize, m_blocksize), y);
680                         was_set = true;
681                 }catch(InvalidPositionException){}
682         }
683
684         if(was_set == false)
685         {
686                 throw InvalidPositionException
687                                 ("UnlimitedHeightmap failed to set height");
688         }
689 }
690
691
692 void UnlimitedHeightmap::serialize(std::ostream &os, u8 version)
693 {
694         //dstream<<"UnlimitedHeightmap::serialize()"<<std::endl;
695
696         if(!ser_ver_supported(version))
697                 throw VersionMismatchException
698                 ("ERROR: UnlimitedHeightmap format not supported");
699         
700         if(version <= 7)
701         {
702                 /*if(m_base_generator->getId() != VALUE_GENERATOR_ID_CONSTANT
703                 || m_randmax_generator->getId() != VALUE_GENERATOR_ID_CONSTANT
704                 || m_randfactor_generator->getId() != VALUE_GENERATOR_ID_CONSTANT)*/
705                 if(std::string(m_base_generator->getName()) != "constant"
706                 || std::string(m_randmax_generator->getName()) != "constant"
707                 || std::string(m_randfactor_generator->getName()) != "constant")
708                 {
709                         throw SerializationError
710                         ("Cannot write UnlimitedHeightmap in old version: "
711                         "Generators are not ConstantGenerators.");
712                 }
713
714                 f32 basevalue = ((ConstantGenerator*)m_base_generator)->m_value;
715                 f32 randmax = ((ConstantGenerator*)m_randmax_generator)->m_value;
716                 f32 randfactor = ((ConstantGenerator*)m_randfactor_generator)->m_value;
717
718                 // Write version
719                 os.write((char*)&version, 1);
720                 
721                 /*
722                         [0] u16 blocksize
723                         [2] s32 randmax*1000
724                         [6] s32 randfactor*1000
725                         [10] s32 basevalue*1000
726                         [14] u32 heightmap_count
727                         [18] X * (v2s16 pos + heightmap)
728                 */
729                 u32 heightmap_size =
730                                 FixedHeightmap::serializedLength(version, m_blocksize);
731                 u32 heightmap_count = m_heightmaps.size();
732
733                 //dstream<<"heightmap_size="<<heightmap_size<<std::endl;
734
735                 u32 datasize = 2+4+4+4+4+heightmap_count*(4+heightmap_size);
736                 SharedBuffer<u8> data(datasize);
737                 
738                 writeU16(&data[0], m_blocksize);
739                 writeU32(&data[2], (s32)(randmax*1000.0));
740                 writeU32(&data[6], (s32)(randfactor*1000.0));
741                 writeU32(&data[10], (s32)(basevalue*1000.0));
742                 writeU32(&data[14], heightmap_count);
743
744                 core::map<v2s16, FixedHeightmap*>::Iterator j;
745                 j = m_heightmaps.getIterator();
746                 u32 i=0;
747                 for(; j.atEnd() == false; j++)
748                 {
749                         FixedHeightmap *hm = j.getNode()->getValue();
750                         v2s16 pos = j.getNode()->getKey();
751                         writeV2S16(&data[18+i*(4+heightmap_size)], pos);
752                         hm->serialize(&data[18+i*(4+heightmap_size)+4], version);
753                         i++;
754                 }
755
756                 os.write((char*)*data, data.getSize());
757         }
758         else
759         {
760                 // Write version
761                 os.write((char*)&version, 1);
762                 
763                 u8 buf[4];
764                 
765                 writeU16(buf, m_blocksize);
766                 os.write((char*)buf, 2);
767
768                 /*m_randmax_generator->serialize(os, version);
769                 m_randfactor_generator->serialize(os, version);
770                 m_base_generator->serialize(os, version);*/
771                 m_randmax_generator->serialize(os);
772                 m_randfactor_generator->serialize(os);
773                 m_base_generator->serialize(os);
774
775                 u32 heightmap_count = m_heightmaps.size();
776                 writeU32(buf, heightmap_count);
777                 os.write((char*)buf, 4);
778
779                 u32 heightmap_size =
780                                 FixedHeightmap::serializedLength(version, m_blocksize);
781
782                 SharedBuffer<u8> hmdata(heightmap_size);
783
784                 core::map<v2s16, FixedHeightmap*>::Iterator j;
785                 j = m_heightmaps.getIterator();
786                 u32 i=0;
787                 for(; j.atEnd() == false; j++)
788                 {
789                         v2s16 pos = j.getNode()->getKey();
790                         writeV2S16(buf, pos);
791                         os.write((char*)buf, 4);
792
793                         FixedHeightmap *hm = j.getNode()->getValue();
794                         hm->serialize(*hmdata, version);
795                         os.write((char*)*hmdata, hmdata.getSize());
796
797                         i++;
798                 }
799         }
800 }
801
802 UnlimitedHeightmap * UnlimitedHeightmap::deSerialize(std::istream &is)
803 {
804         u8 version;
805         is.read((char*)&version, 1);
806         
807         if(!ser_ver_supported(version))
808                 throw VersionMismatchException("ERROR: UnlimitedHeightmap format not supported");
809         
810         if(version <= 7)
811         {
812                 /*
813                         [0] u16 blocksize
814                         [2] s32 randmax*1000
815                         [6] s32 randfactor*1000
816                         [10] s32 basevalue*1000
817                         [14] u32 heightmap_count
818                         [18] X * (v2s16 pos + heightmap)
819                 */
820                 SharedBuffer<u8> data(18);
821                 is.read((char*)*data, 18);
822                 if(is.gcount() != 18)
823                         throw SerializationError
824                                         ("UnlimitedHeightmap::deSerialize: no enough input data");
825                 s16 blocksize = readU16(&data[0]);
826                 f32 randmax = (f32)readU32(&data[2]) / 1000.0;
827                 f32 randfactor = (f32)readU32(&data[6]) / 1000.0;
828                 f32 basevalue = (f32)readU32(&data[10]) / 1000.0;
829                 u32 heightmap_count = readU32(&data[14]);
830
831                 /*dstream<<"UnlimitedHeightmap::deSerialize():"
832                                 <<" blocksize="<<blocksize
833                                 <<" heightmap_count="<<heightmap_count
834                                 <<std::endl;*/
835
836                 u32 heightmap_size =
837                                 FixedHeightmap::serializedLength(version, blocksize);
838
839                 //dstream<<"heightmap_size="<<heightmap_size<<std::endl;
840
841                 ValueGenerator *maxgen = new ConstantGenerator(randmax);
842                 ValueGenerator *factorgen = new ConstantGenerator(randfactor);
843                 ValueGenerator *basegen = new ConstantGenerator(basevalue);
844
845                 UnlimitedHeightmap *hm = new UnlimitedHeightmap
846                                 (blocksize, maxgen, factorgen, basegen, NULL);
847
848                 for(u32 i=0; i<heightmap_count; i++)
849                 {
850                         //dstream<<"i="<<i<<std::endl;
851                         SharedBuffer<u8> data(4+heightmap_size);
852                         is.read((char*)*data, 4+heightmap_size);
853                         if(is.gcount() != (s32)(4+heightmap_size)){
854                                 delete hm;
855                                 throw SerializationError
856                                                 ("UnlimitedHeightmap::deSerialize: no enough input data");
857                         }
858                         v2s16 pos = readV2S16(&data[0]);
859                         FixedHeightmap *f = new FixedHeightmap(hm, pos, blocksize);
860                         f->deSerialize(&data[4], version);
861                         hm->m_heightmaps.insert(pos, f);
862                 }
863                 return hm;
864         }
865         else
866         {
867                 u8 buf[4];
868                 
869                 is.read((char*)buf, 2);
870                 s16 blocksize = readU16(buf);
871
872                 ValueGenerator *maxgen = ValueGenerator::deSerialize(is);
873                 ValueGenerator *factorgen = ValueGenerator::deSerialize(is);
874                 ValueGenerator *basegen = ValueGenerator::deSerialize(is);
875
876                 is.read((char*)buf, 4);
877                 u32 heightmap_count = readU32(buf);
878
879                 u32 heightmap_size =
880                                 FixedHeightmap::serializedLength(version, blocksize);
881
882                 UnlimitedHeightmap *hm = new UnlimitedHeightmap
883                                 (blocksize, maxgen, factorgen, basegen, NULL);
884
885                 for(u32 i=0; i<heightmap_count; i++)
886                 {
887                         is.read((char*)buf, 4);
888                         v2s16 pos = readV2S16(buf);
889
890                         SharedBuffer<u8> data(heightmap_size);
891                         is.read((char*)*data, heightmap_size);
892                         if(is.gcount() != (s32)(heightmap_size)){
893                                 delete hm;
894                                 throw SerializationError
895                                                 ("UnlimitedHeightmap::deSerialize: no enough input data");
896                         }
897                         FixedHeightmap *f = new FixedHeightmap(hm, pos, blocksize);
898                         f->deSerialize(*data, version);
899                         hm->m_heightmaps.insert(pos, f);
900                 }
901                 return hm;
902         }
903 }
904
905