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