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