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