0f2eba8568bd6d2d5aad466635e4006c148aec67
[oweals/minetest.git] / src / mapblock.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 #include "mapblock.h"
21 #include "map.h"
22 // For g_materials
23 #include "main.h"
24 #include "light.h"
25 #include <sstream>
26
27
28 /*
29         MapBlock
30 */
31
32 bool MapBlock::isValidPositionParent(v3s16 p)
33 {
34         if(isValidPosition(p))
35         {
36                 return true;
37         }
38         else{
39                 return m_parent->isValidPosition(getPosRelative() + p);
40         }
41 }
42
43 MapNode MapBlock::getNodeParent(v3s16 p)
44 {
45         if(isValidPosition(p) == false)
46         {
47                 return m_parent->getNode(getPosRelative() + p);
48         }
49         else
50         {
51                 if(data == NULL)
52                         throw InvalidPositionException();
53                 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
54         }
55 }
56
57 void MapBlock::setNodeParent(v3s16 p, MapNode & n)
58 {
59         if(isValidPosition(p) == false)
60         {
61                 m_parent->setNode(getPosRelative() + p, n);
62         }
63         else
64         {
65                 if(data == NULL)
66                         throw InvalidPositionException();
67                 data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X] = n;
68         }
69 }
70
71 FastFace * MapBlock::makeFastFace(u8 material, u8 light, v3f p,
72                 v3f dir, v3f scale, v3f posRelative_f)
73 {
74         FastFace *f = new FastFace;
75         
76         // Position is at the center of the cube.
77         v3f pos = p * BS;
78         posRelative_f *= BS;
79
80         v3f vertex_pos[4];
81         // If looking towards z+, this is the face that is behind
82         // the center point, facing towards z+.
83         vertex_pos[0] = v3f( BS/2,-BS/2,BS/2);
84         vertex_pos[1] = v3f(-BS/2,-BS/2,BS/2);
85         vertex_pos[2] = v3f(-BS/2, BS/2,BS/2);
86         vertex_pos[3] = v3f( BS/2, BS/2,BS/2);
87         
88         /*
89                 TODO: Rotate it the right way (one side comes upside down)
90         */
91         core::CMatrix4<f32> m;
92         m.buildRotateFromTo(v3f(0,0,1), dir);
93         
94         for(u16 i=0; i<4; i++){
95                 m.rotateVect(vertex_pos[i]);
96                 vertex_pos[i].X *= scale.X;
97                 vertex_pos[i].Y *= scale.Y;
98                 vertex_pos[i].Z *= scale.Z;
99                 vertex_pos[i] += pos + posRelative_f;
100         }
101
102         f32 abs_scale = 1.;
103         if     (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X;
104         else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
105         else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
106
107         v3f zerovector = v3f(0,0,0);
108         
109         u8 li = decode_light(light);
110         //u8 li = 150;
111
112         u8 alpha = 255;
113
114         if(material == MATERIAL_WATER || material == MATERIAL_OCEAN)
115         {
116                 alpha = 128;
117         }
118
119         video::SColor c = video::SColor(alpha,li,li,li);
120
121         /*f->vertices[0] = video::S3DVertex(vertex_pos[0], zerovector, c,
122                         core::vector2d<f32>(0,1));
123         f->vertices[1] = video::S3DVertex(vertex_pos[1], zerovector, c,
124                         core::vector2d<f32>(abs_scale,1));
125         f->vertices[2] = video::S3DVertex(vertex_pos[2], zerovector, c,
126                         core::vector2d<f32>(abs_scale,0));
127         f->vertices[3] = video::S3DVertex(vertex_pos[3], zerovector, c,
128                         core::vector2d<f32>(0,0));*/
129         f->vertices[0] = video::S3DVertex(vertex_pos[0], zerovector, c,
130                         core::vector2d<f32>(0,1));
131         f->vertices[1] = video::S3DVertex(vertex_pos[1], zerovector, c,
132                         core::vector2d<f32>(abs_scale,1));
133         f->vertices[2] = video::S3DVertex(vertex_pos[2], zerovector, c,
134                         core::vector2d<f32>(abs_scale,0));
135         f->vertices[3] = video::S3DVertex(vertex_pos[3], zerovector, c,
136                         core::vector2d<f32>(0,0));
137
138         f->material = material;
139
140         return f;
141 }
142         
143 /*
144         Parameters must consist of air and !air.
145         Order doesn't matter.
146
147         If either of the nodes doesn't exist, light is 0.
148 */
149 u8 MapBlock::getFaceLight(v3s16 p, v3s16 face_dir)
150 {
151         try{
152                 MapNode n = getNodeParent(p);
153                 MapNode n2 = getNodeParent(p + face_dir);
154                 u8 light;
155                 if(n.solidness() < n2.solidness())
156                         light = n.getLight();
157                 else
158                         light = n2.getLight();
159
160                 // Make some nice difference to different sides
161                 if(face_dir.X == 1 || face_dir.Z == 1 || face_dir.Y == -1)
162                         light = diminish_light(diminish_light(light));
163                 else if(face_dir.X == -1 || face_dir.Z == -1)
164                         light = diminish_light(light);
165
166                 return light;
167         }
168         catch(InvalidPositionException &e)
169         {
170                 return 0;
171         }
172 }
173
174 /*
175         Gets node material from any place relative to block.
176         Returns MATERIAL_IGNORE if doesn't exist or should not be drawn.
177 */
178 u8 MapBlock::getNodeMaterial(v3s16 p)
179 {
180         try{
181                 MapNode n = getNodeParent(p);
182                 
183                 return content_cube_material(n.d);
184         }
185         catch(InvalidPositionException &e)
186         {
187                 return MATERIAL_IGNORE;
188         }
189 }
190
191 /*
192         startpos:
193         translate_dir: unit vector with only one of x, y or z
194         face_dir: unit vector with only one of x, y or z
195 */
196 void MapBlock::updateFastFaceRow(v3s16 startpos,
197                 u16 length,
198                 v3s16 translate_dir,
199                 v3s16 face_dir,
200                 core::list<FastFace*> &dest)
201 {
202         /*
203                 Precalculate some variables
204         */
205         v3f translate_dir_f(translate_dir.X, translate_dir.Y,
206                         translate_dir.Z); // floating point conversion
207         v3f face_dir_f(face_dir.X, face_dir.Y,
208                         face_dir.Z); // floating point conversion
209         v3f posRelative_f(getPosRelative().X, getPosRelative().Y,
210                         getPosRelative().Z); // floating point conversion
211
212         v3s16 p = startpos;
213         /*
214                 The light in the air lights the surface is taken from
215                 the node that is air.
216         */
217         u8 light = getFaceLight(p, face_dir);
218         
219         u16 continuous_materials_count = 0;
220         
221         u8 material0 = getNodeMaterial(p);
222         u8 material1 = getNodeMaterial(p + face_dir);
223                 
224         for(u16 j=0; j<length; j++)
225         {
226                 bool next_is_different = true;
227                 
228                 v3s16 p_next;
229                 u8 material0_next = 0;
230                 u8 material1_next = 0;
231                 u8 light_next = 0;
232
233                 if(j != length - 1){
234                         p_next = p + translate_dir;
235                         material0_next = getNodeMaterial(p_next);
236                         material1_next = getNodeMaterial(p_next + face_dir);
237                         light_next = getFaceLight(p_next, face_dir);
238
239                         if(material0_next == material0
240                                         && material1_next == material1
241                                         && light_next == light)
242                         {
243                                 next_is_different = false;
244                         }
245                 }
246
247                 continuous_materials_count++;
248                 
249                 if(next_is_different)
250                 {
251                         /*
252                                 Create a face if there should be one
253                         */
254                         u8 mf = face_materials(material0, material1);
255                         
256                         if(mf != 0)
257                         {
258                                 // Floating point conversion of the position vector
259                                 v3f pf(p.X, p.Y, p.Z);
260                                 // Center point of face (kind of)
261                                 v3f sp = pf - ((f32)continuous_materials_count / 2. - 0.5) * translate_dir_f;
262                                 v3f scale(1,1,1);
263                                 if(translate_dir.X != 0){
264                                         scale.X = continuous_materials_count;
265                                 }
266                                 if(translate_dir.Y != 0){
267                                         scale.Y = continuous_materials_count;
268                                 }
269                                 if(translate_dir.Z != 0){
270                                         scale.Z = continuous_materials_count;
271                                 }
272                                 
273                                 FastFace *f;
274
275                                 // If node at sp (material0) is more solid
276                                 if(mf == 1)
277                                 {
278                                         f = makeFastFace(material0, light,
279                                                         sp, face_dir_f, scale,
280                                                         posRelative_f);
281                                 }
282                                 // If node at sp is less solid (mf == 2)
283                                 else
284                                 {
285                                         f = makeFastFace(material1, light,
286                                                         sp+face_dir_f, -1*face_dir_f, scale,
287                                                         posRelative_f);
288                                 }
289                                 dest.push_back(f);
290                         }
291
292                         continuous_materials_count = 0;
293                         material0 = material0_next;
294                         material1 = material1_next;
295                         light = light_next;
296                 }
297                 
298                 p = p_next;
299         }
300 }
301
302 /*
303         This is used because CMeshBuffer::append() is very slow
304 */
305 struct PreMeshBuffer
306 {
307         video::SMaterial material;
308         core::array<u16> indices;
309         core::array<video::S3DVertex> vertices;
310 };
311
312 class MeshCollector
313 {
314 public:
315         void append(
316                         video::SMaterial material,
317                         const video::S3DVertex* const vertices,
318                         u32 numVertices,
319                         const u16* const indices,
320                         u32 numIndices
321                 )
322         {
323                 PreMeshBuffer *p = NULL;
324                 for(u32 i=0; i<m_prebuffers.size(); i++)
325                 {
326                         PreMeshBuffer &pp = m_prebuffers[i];
327                         if(pp.material != material)
328                                 continue;
329
330                         p = &pp;
331                         break;
332                 }
333
334                 if(p == NULL)
335                 {
336                         PreMeshBuffer pp;
337                         pp.material = material;
338                         m_prebuffers.push_back(pp);
339                         p = &m_prebuffers[m_prebuffers.size()-1];
340                 }
341
342                 u32 vertex_count = p->vertices.size();
343                 for(u32 i=0; i<numIndices; i++)
344                 {
345                         u32 j = indices[i] + vertex_count;
346                         if(j > 65535)
347                         {
348                                 dstream<<"FIXME: Meshbuffer ran out of indices"<<std::endl;
349                                 // NOTE: Fix is to just add an another MeshBuffer
350                         }
351                         p->indices.push_back(j);
352                 }
353                 for(u32 i=0; i<numVertices; i++)
354                 {
355                         p->vertices.push_back(vertices[i]);
356                 }
357         }
358
359         void fillMesh(scene::SMesh *mesh)
360         {
361                 /*dstream<<"Filling mesh with "<<m_prebuffers.size()
362                                 <<" meshbuffers"<<std::endl;*/
363                 for(u32 i=0; i<m_prebuffers.size(); i++)
364                 {
365                         PreMeshBuffer &p = m_prebuffers[i];
366
367                         /*dstream<<"p.vertices.size()="<<p.vertices.size()
368                                         <<", p.indices.size()="<<p.indices.size()
369                                         <<std::endl;*/
370                         
371                         // Create meshbuffer
372                         
373                         // This is a "Standard MeshBuffer",
374                         // it's a typedeffed CMeshBuffer<video::S3DVertex>
375                         scene::SMeshBuffer *buf = new scene::SMeshBuffer();
376                         // Set material
377                         buf->Material = p.material;
378                         //((scene::SMeshBuffer*)buf)->Material = p.material;
379                         // Use VBO
380                         //buf->setHardwareMappingHint(scene::EHM_STATIC);
381                         // Add to mesh
382                         mesh->addMeshBuffer(buf);
383                         // Mesh grabbed it
384                         buf->drop();
385
386                         buf->append(p.vertices.pointer(), p.vertices.size(),
387                                         p.indices.pointer(), p.indices.size());
388                 }
389         }
390
391 private:
392         core::array<PreMeshBuffer> m_prebuffers;
393 };
394
395 void MapBlock::updateMesh()
396 {
397         /*v3s16 p = getPosRelative();
398         std::cout<<"MapBlock("<<p.X<<","<<p.Y<<","<<p.Z<<")"
399                         <<"::updateMesh(): ";*/
400                         //<<"::updateMesh()"<<std::endl;
401         
402         /*
403                 TODO: Change this to directly generate the mesh (and get rid
404                       of FastFaces)
405         */
406
407         core::list<FastFace*> *fastfaces_new = new core::list<FastFace*>;
408         
409         /*
410                 We are including the faces of the trailing edges of the block.
411                 This means that when something changes, the caller must
412                 also update the meshes of the blocks at the leading edges.
413         */
414
415         /*
416                 Go through every y,z and get top faces in rows of x+
417         */
418         for(s16 y=0; y<MAP_BLOCKSIZE; y++){
419         //for(s16 y=-1; y<MAP_BLOCKSIZE; y++){
420                 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
421                         updateFastFaceRow(v3s16(0,y,z), MAP_BLOCKSIZE,
422                                         v3s16(1,0,0),
423                                         v3s16(0,1,0),
424                                         *fastfaces_new);
425                 }
426         }
427         /*
428                 Go through every x,y and get right faces in rows of z+
429         */
430         for(s16 x=0; x<MAP_BLOCKSIZE; x++){
431         //for(s16 x=-1; x<MAP_BLOCKSIZE; x++){
432                 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
433                         updateFastFaceRow(v3s16(x,y,0), MAP_BLOCKSIZE,
434                                         v3s16(0,0,1),
435                                         v3s16(1,0,0),
436                                         *fastfaces_new);
437                 }
438         }
439         /*
440                 Go through every y,z and get back faces in rows of x+
441         */
442         for(s16 z=0; z<MAP_BLOCKSIZE; z++){
443         //for(s16 z=-1; z<MAP_BLOCKSIZE; z++){
444                 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
445                         updateFastFaceRow(v3s16(0,y,z), MAP_BLOCKSIZE,
446                                         v3s16(1,0,0),
447                                         v3s16(0,0,1),
448                                         *fastfaces_new);
449                 }
450         }
451
452         scene::SMesh *mesh_new = NULL;
453         
454         if(fastfaces_new->getSize() > 0)
455         {
456                 MeshCollector collector;
457
458                 core::list<FastFace*>::Iterator i = fastfaces_new->begin();
459
460                 for(; i != fastfaces_new->end(); i++)
461                 {
462                         FastFace *f = *i;
463
464                         const u16 indices[] = {0,1,2,2,3,0};
465
466                         collector.append(g_materials[f->material], f->vertices, 4,
467                                         indices, 6);
468                 }
469
470                 mesh_new = new scene::SMesh();
471                 
472                 collector.fillMesh(mesh_new);
473
474                 // Use VBO for mesh (this just would set this for ever buffer)
475                 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
476                 
477                 /*std::cout<<"MapBlock has "<<fastfaces_new->getSize()<<" faces "
478                                 <<"and uses "<<mesh_new->getMeshBufferCount()
479                                 <<" materials (meshbuffers)"<<std::endl;*/
480         }
481         
482         /*
483                 Clear temporary FastFaces
484         */
485
486         core::list<FastFace*>::Iterator i;
487         i = fastfaces_new->begin();
488         for(; i != fastfaces_new->end(); i++)
489         {
490                 delete *i;
491         }
492         fastfaces_new->clear();
493         delete fastfaces_new;
494
495         /*
496                 Add special graphics:
497                 - torches
498         */
499
500         for(s16 z=0; z<MAP_BLOCKSIZE; z++)
501         for(s16 y=0; y<MAP_BLOCKSIZE; y++)
502         for(s16 x=0; x<MAP_BLOCKSIZE; x++)
503         {
504                 v3s16 p(x,y,z);
505         }
506
507         /*
508                 Replace the mesh
509         */
510
511         mesh_mutex.Lock();
512
513         scene::SMesh *mesh_old = mesh;
514
515         mesh = mesh_new;
516         
517         if(mesh_old != NULL)
518         {
519                 // Remove hardware buffers of meshbuffers of mesh
520                 // NOTE: No way, this runs in a different thread and everything
521                 /*u32 c = mesh_old->getMeshBufferCount();
522                 for(u32 i=0; i<c; i++)
523                 {
524                         IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
525                 }*/
526                 // Drop the mesh
527                 mesh_old->drop();
528                 //delete mesh_old;
529         }
530
531         mesh_mutex.Unlock();
532         
533         //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
534 }
535
536 /*
537         Propagates sunlight down through the block.
538         Doesn't modify nodes that are not affected by sunlight.
539         
540         Returns false if sunlight at bottom block is invalid
541         Returns true if bottom block doesn't exist.
542
543         If there is a block above, continues from it.
544         If there is no block above, assumes there is sunlight, unless
545         is_underground is set.
546
547         At the moment, all sunlighted nodes are added to light_sources.
548         TODO: This could be optimized.
549 */
550 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources)
551 {
552         // Whether the sunlight at the top of the bottom block is valid
553         bool block_below_is_valid = true;
554         
555         v3s16 pos_relative = getPosRelative();
556         
557         for(s16 x=0; x<MAP_BLOCKSIZE; x++)
558         {
559                 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
560                 {
561                         bool no_sunlight = false;
562                         bool no_top_block = false;
563                         // Check if node above block has sunlight
564                         try{
565                                 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
566                                 if(n.getLight() != LIGHT_SUN)
567                                 {
568                                         /*if(is_underground)
569                                         {
570                                                 no_sunlight = true;
571                                         }*/
572                                         no_sunlight = true;
573                                 }
574                         }
575                         catch(InvalidPositionException &e)
576                         {
577                                 no_top_block = true;
578                                 
579                                 // TODO: This makes over-ground roofed places sunlighted
580                                 // Assume sunlight, unless is_underground==true
581                                 if(is_underground)
582                                 {
583                                         no_sunlight = true;
584                                 }
585                                 
586                                 // TODO: There has to be some way to allow this behaviour
587                                 // As of now, it just makes everything dark.
588                                 // No sunlight here
589                                 //no_sunlight = true;
590                         }
591
592                         /*std::cout<<"("<<x<<","<<z<<"): "
593                                         <<"no_top_block="<<no_top_block
594                                         <<", is_underground="<<is_underground
595                                         <<", no_sunlight="<<no_sunlight
596                                         <<std::endl;*/
597                 
598                         s16 y = MAP_BLOCKSIZE-1;
599                         
600                         if(no_sunlight == false)
601                         {
602                                 // Continue spreading sunlight downwards through transparent
603                                 // nodes
604                                 for(; y >= 0; y--)
605                                 {
606                                         v3s16 pos(x, y, z);
607                                         
608                                         MapNode &n = getNodeRef(pos);
609
610                                         if(n.sunlight_propagates())
611                                         {
612                                                 n.setLight(LIGHT_SUN);
613
614                                                 light_sources.insert(pos_relative + pos, true);
615                                         }
616                                         else{
617                                                 break;
618                                         }
619                                 }
620                         }
621
622                         bool sunlight_should_go_down = (y==-1);
623
624                         // Fill rest with black (only transparent ones)
625                         for(; y >= 0; y--){
626                                 v3s16 pos(x, y, z);
627                                 
628                                 MapNode &n = getNodeRef(pos);
629
630                                 if(n.light_propagates())
631                                 {
632                                         n.setLight(0);
633                                 }
634                                 else{
635                                         break;
636                                 }
637                         }
638
639                         /*
640                                 If the block below hasn't already been marked invalid:
641
642                                 Check if the node below the block has proper sunlight at top.
643                                 If not, the block below is invalid.
644                                 
645                                 Ignore non-transparent nodes as they always have no light
646                         */
647                         try
648                         {
649                         if(block_below_is_valid)
650                         {
651                                 MapNode n = getNodeParent(v3s16(x, -1, z));
652                                 if(n.light_propagates())
653                                 {
654                                         if(n.getLight() == LIGHT_SUN
655                                                         && sunlight_should_go_down == false)
656                                                 block_below_is_valid = false;
657                                         else if(n.getLight() != LIGHT_SUN
658                                                         && sunlight_should_go_down == true)
659                                                 block_below_is_valid = false;
660                                 }
661                         }//if
662                         }//try
663                         catch(InvalidPositionException &e)
664                         {
665                                 /*std::cout<<"InvalidBlockException for bottom block node"
666                                                 <<std::endl;*/
667                                 // Just no block below, no need to panic.
668                         }
669                 }
670         }
671
672         return block_below_is_valid;
673 }
674
675 void MapBlock::copyTo(VoxelManipulator &dst)
676 {
677         v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
678         VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
679         
680         dst.copyFrom(data, data_area, v3s16(0,0,0),
681                         getPosRelative(), data_size);
682 }
683
684 /*
685         Serialization
686 */
687
688 void MapBlock::serialize(std::ostream &os, u8 version)
689 {
690         if(!ser_ver_supported(version))
691                 throw VersionMismatchException("ERROR: MapBlock format not supported");
692         
693         if(data == NULL)
694         {
695                 throw SerializationError("ERROR: Not writing dummy block.");
696         }
697         
698         // These have no compression
699         if(version <= 3 || version == 5 || version == 6)
700         {
701                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
702                 
703                 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
704                 SharedBuffer<u8> dest(buflen);
705
706                 dest[0] = is_underground;
707                 for(u32 i=0; i<nodecount; i++)
708                 {
709                         u32 s = 1 + i * MapNode::serializedLength(version);
710                         data[i].serialize(&dest[s], version);
711                 }
712                 
713                 os.write((char*)*dest, dest.getSize());
714         }
715         // All otherversions
716         else
717         {
718                 /*
719                         With compression.
720                         Compress the materials and the params separately.
721                 */
722                 
723                 // First byte
724                 os.write((char*)&is_underground, 1);
725
726                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
727
728                 // Get and compress materials
729                 SharedBuffer<u8> materialdata(nodecount);
730                 for(u32 i=0; i<nodecount; i++)
731                 {
732                         materialdata[i] = data[i].d;
733                 }
734                 compress(materialdata, os, version);
735
736                 // Get and compress params
737                 SharedBuffer<u8> paramdata(nodecount);
738                 for(u32 i=0; i<nodecount; i++)
739                 {
740                         paramdata[i] = data[i].param;
741                 }
742                 compress(paramdata, os, version);
743                 
744                 if(version >= 10)
745                 {
746                         // Get and compress pressure
747                         SharedBuffer<u8> pressuredata(nodecount);
748                         for(u32 i=0; i<nodecount; i++)
749                         {
750                                 pressuredata[i] = data[i].pressure;
751                         }
752                         compress(pressuredata, os, version);
753                 }
754         }
755 }
756
757 void MapBlock::deSerialize(std::istream &is, u8 version)
758 {
759         if(!ser_ver_supported(version))
760                 throw VersionMismatchException("ERROR: MapBlock format not supported");
761
762         // These have no compression
763         if(version <= 3 || version == 5 || version == 6)
764         {
765                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
766                 char tmp;
767                 is.read(&tmp, 1);
768                 if(is.gcount() != 1)
769                         throw SerializationError
770                                         ("MapBlock::deSerialize: no enough input data");
771                 is_underground = tmp;
772                 for(u32 i=0; i<nodecount; i++)
773                 {
774                         s32 len = MapNode::serializedLength(version);
775                         SharedBuffer<u8> d(len);
776                         is.read((char*)*d, len);
777                         if(is.gcount() != len)
778                                 throw SerializationError
779                                                 ("MapBlock::deSerialize: no enough input data");
780                         data[i].deSerialize(*d, version);
781                 }
782         }
783         // All other versions
784         else
785         {
786                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
787
788                 u8 t8;
789                 is.read((char*)&t8, 1);
790                 is_underground = t8;
791
792                 {
793                         // Uncompress and set material data
794                         std::ostringstream os(std::ios_base::binary);
795                         decompress(is, os, version);
796                         std::string s = os.str();
797                         if(s.size() != nodecount)
798                                 throw SerializationError
799                                                 ("MapBlock::deSerialize: invalid format");
800                         for(u32 i=0; i<s.size(); i++)
801                         {
802                                 data[i].d = s[i];
803                         }
804                 }
805                 {
806                         // Uncompress and set param data
807                         std::ostringstream os(std::ios_base::binary);
808                         decompress(is, os, version);
809                         std::string s = os.str();
810                         if(s.size() != nodecount)
811                                 throw SerializationError
812                                                 ("MapBlock::deSerialize: invalid format");
813                         for(u32 i=0; i<s.size(); i++)
814                         {
815                                 data[i].param = s[i];
816                         }
817                 }
818         
819                 if(version >= 10)
820                 {
821                         // Uncompress and set pressure data
822                         std::ostringstream os(std::ios_base::binary);
823                         decompress(is, os, version);
824                         std::string s = os.str();
825                         if(s.size() != nodecount)
826                                 throw SerializationError
827                                                 ("MapBlock::deSerialize: invalid format");
828                         for(u32 i=0; i<s.size(); i++)
829                         {
830                                 data[i].pressure = s[i];
831                         }
832                 }
833         }
834 }
835
836
837 //END