3 Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
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.
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.
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.
32 bool MapBlock::isValidPositionParent(v3s16 p)
34 if(isValidPosition(p))
39 return m_parent->isValidPosition(getPosRelative() + p);
43 MapNode MapBlock::getNodeParent(v3s16 p)
45 if(isValidPosition(p) == false)
47 return m_parent->getNode(getPosRelative() + p);
52 throw InvalidPositionException();
53 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
57 void MapBlock::setNodeParent(v3s16 p, MapNode & n)
59 if(isValidPosition(p) == false)
61 m_parent->setNode(getPosRelative() + p, n);
66 throw InvalidPositionException();
67 data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X] = n;
71 FastFace * MapBlock::makeFastFace(u16 tile, u8 light, v3f p,
72 v3s16 dir, v3f scale, v3f posRelative_f)
74 FastFace *f = new FastFace;
76 // Position is at the center of the cube.
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);
88 for(u16 i=0; i<4; i++)
90 if(dir == v3s16(0,0,1))
91 vertex_pos[i].rotateXZBy(0);
92 else if(dir == v3s16(0,0,-1))
93 vertex_pos[i].rotateXZBy(180);
94 else if(dir == v3s16(1,0,0))
95 vertex_pos[i].rotateXZBy(-90);
96 else if(dir == v3s16(-1,0,0))
97 vertex_pos[i].rotateXZBy(90);
98 else if(dir == v3s16(0,1,0))
99 vertex_pos[i].rotateYZBy(-90);
100 else if(dir == v3s16(0,-1,0))
101 vertex_pos[i].rotateYZBy(90);
103 vertex_pos[i].X *= scale.X;
104 vertex_pos[i].Y *= scale.Y;
105 vertex_pos[i].Z *= scale.Z;
106 vertex_pos[i] += pos + posRelative_f;
110 if (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X;
111 else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
112 else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
114 v3f zerovector = v3f(0,0,0);
116 u8 li = decode_light(light);
121 if(tile == TILE_WATER)
126 video::SColor c = video::SColor(alpha,li,li,li);
128 f->vertices[0] = video::S3DVertex(vertex_pos[0], zerovector, c,
129 core::vector2d<f32>(0,1));
130 f->vertices[1] = video::S3DVertex(vertex_pos[1], zerovector, c,
131 core::vector2d<f32>(abs_scale,1));
132 f->vertices[2] = video::S3DVertex(vertex_pos[2], zerovector, c,
133 core::vector2d<f32>(abs_scale,0));
134 f->vertices[3] = video::S3DVertex(vertex_pos[3], zerovector, c,
135 core::vector2d<f32>(0,0));
139 //f->tile = TILE_STONE;
145 Parameters must consist of air and !air.
146 Order doesn't matter.
148 If either of the nodes doesn't exist, light is 0.
150 u8 MapBlock::getFaceLight(v3s16 p, v3s16 face_dir)
153 MapNode n = getNodeParent(p);
154 MapNode n2 = getNodeParent(p + face_dir);
156 /*if(n.solidness() < n2.solidness())
157 light = n.getLight();
159 light = n2.getLight();*/
160 if(n.getLight() > n2.getLight())
161 light = n.getLight();
163 light = n2.getLight();
165 // Make some nice difference to different sides
167 /*if(face_dir.X == 1 || face_dir.Z == 1 || face_dir.Y == -1)
168 light = diminish_light(diminish_light(light));
169 else if(face_dir.X == -1 || face_dir.Z == -1)
170 light = diminish_light(light);*/
172 if(face_dir.X == 1 || face_dir.X == -1 || face_dir.Y == -1)
173 light = diminish_light(diminish_light(light));
174 else if(face_dir.Z == 1 || face_dir.Z == -1)
175 light = diminish_light(light);
179 catch(InvalidPositionException &e)
186 Gets node tile from any place relative to block.
187 Returns CONTENT_IGNORE if doesn't exist or should not be drawn.
189 u16 MapBlock::getNodeTile(v3s16 p, v3s16 face_dir)
192 MapNode n = getNodeParent(p);
194 //return content_tile(n.d);
195 return n.getTile(face_dir);
197 catch(InvalidPositionException &e)
199 //return CONTENT_IGNORE;
204 u8 MapBlock::getNodeContent(v3s16 p)
207 MapNode n = getNodeParent(p);
211 catch(InvalidPositionException &e)
213 return CONTENT_IGNORE;
219 translate_dir: unit vector with only one of x, y or z
220 face_dir: unit vector with only one of x, y or z
222 void MapBlock::updateFastFaceRow(v3s16 startpos,
226 core::list<FastFace*> &dest)
229 Precalculate some variables
231 v3f translate_dir_f(translate_dir.X, translate_dir.Y,
232 translate_dir.Z); // floating point conversion
233 v3f face_dir_f(face_dir.X, face_dir.Y,
234 face_dir.Z); // floating point conversion
235 v3f posRelative_f(getPosRelative().X, getPosRelative().Y,
236 getPosRelative().Z); // floating point conversion
240 Get face light at starting position
242 u8 light = getFaceLight(p, face_dir);
244 u16 continuous_tiles_count = 0;
246 u8 tile0 = getNodeTile(p, face_dir);
247 u8 tile1 = getNodeTile(p + face_dir, -face_dir);
249 for(u16 j=0; j<length; j++)
251 bool next_is_different = true;
259 p_next = p + translate_dir;
260 tile0_next = getNodeTile(p_next, face_dir);
261 tile1_next = getNodeTile(p_next + face_dir, -face_dir);
262 light_next = getFaceLight(p_next, face_dir);
264 if(tile0_next == tile0
265 && tile1_next == tile1
266 && light_next == light)
268 next_is_different = false;
272 continuous_tiles_count++;
274 if(next_is_different)
277 Create a face if there should be one
279 //u8 mf = face_contents(tile0, tile1);
281 u8 content0 = getNodeContent(p);
282 u8 content1 = getNodeContent(p + face_dir);
283 u8 mf = face_contents(content0, content1);
287 // Floating point conversion of the position vector
288 v3f pf(p.X, p.Y, p.Z);
289 // Center point of face (kind of)
290 v3f sp = pf - ((f32)continuous_tiles_count / 2. - 0.5) * translate_dir_f;
292 if(translate_dir.X != 0){
293 scale.X = continuous_tiles_count;
295 if(translate_dir.Y != 0){
296 scale.Y = continuous_tiles_count;
298 if(translate_dir.Z != 0){
299 scale.Z = continuous_tiles_count;
304 // If node at sp (tile0) is more solid
307 f = makeFastFace(tile0, light,
311 // If node at sp is less solid (mf == 2)
314 f = makeFastFace(tile1, light,
315 sp+face_dir_f, -face_dir, scale,
321 continuous_tiles_count = 0;
332 This is used because CMeshBuffer::append() is very slow
336 video::SMaterial material;
337 core::array<u16> indices;
338 core::array<video::S3DVertex> vertices;
345 video::SMaterial material,
346 const video::S3DVertex* const vertices,
348 const u16* const indices,
352 PreMeshBuffer *p = NULL;
353 for(u32 i=0; i<m_prebuffers.size(); i++)
355 PreMeshBuffer &pp = m_prebuffers[i];
356 if(pp.material != material)
366 pp.material = material;
367 m_prebuffers.push_back(pp);
368 p = &m_prebuffers[m_prebuffers.size()-1];
371 u32 vertex_count = p->vertices.size();
372 for(u32 i=0; i<numIndices; i++)
374 u32 j = indices[i] + vertex_count;
377 dstream<<"FIXME: Meshbuffer ran out of indices"<<std::endl;
378 // NOTE: Fix is to just add an another MeshBuffer
380 p->indices.push_back(j);
382 for(u32 i=0; i<numVertices; i++)
384 p->vertices.push_back(vertices[i]);
388 void fillMesh(scene::SMesh *mesh)
390 /*dstream<<"Filling mesh with "<<m_prebuffers.size()
391 <<" meshbuffers"<<std::endl;*/
392 for(u32 i=0; i<m_prebuffers.size(); i++)
394 PreMeshBuffer &p = m_prebuffers[i];
396 /*dstream<<"p.vertices.size()="<<p.vertices.size()
397 <<", p.indices.size()="<<p.indices.size()
402 // This is a "Standard MeshBuffer",
403 // it's a typedeffed CMeshBuffer<video::S3DVertex>
404 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
406 buf->Material = p.material;
407 //((scene::SMeshBuffer*)buf)->Material = p.material;
409 //buf->setHardwareMappingHint(scene::EHM_STATIC);
411 mesh->addMeshBuffer(buf);
415 buf->append(p.vertices.pointer(), p.vertices.size(),
416 p.indices.pointer(), p.indices.size());
421 core::array<PreMeshBuffer> m_prebuffers;
424 void MapBlock::updateMesh()
426 /*v3s16 p = getPosRelative();
427 std::cout<<"MapBlock("<<p.X<<","<<p.Y<<","<<p.Z<<")"
428 <<"::updateMesh(): ";*/
429 //<<"::updateMesh()"<<std::endl;
432 TODO: Change this to directly generate the mesh (and get rid
436 core::list<FastFace*> *fastfaces_new = new core::list<FastFace*>;
439 We are including the faces of the trailing edges of the block.
440 This means that when something changes, the caller must
441 also update the meshes of the blocks at the leading edges.
445 Go through every y,z and get top faces in rows of x+
447 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
448 //for(s16 y=-1; y<MAP_BLOCKSIZE; y++){
449 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
450 updateFastFaceRow(v3s16(0,y,z), MAP_BLOCKSIZE,
457 Go through every x,y and get right faces in rows of z+
459 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
460 //for(s16 x=-1; x<MAP_BLOCKSIZE; x++){
461 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
462 updateFastFaceRow(v3s16(x,y,0), MAP_BLOCKSIZE,
469 Go through every y,z and get back faces in rows of x+
471 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
472 //for(s16 z=-1; z<MAP_BLOCKSIZE; z++){
473 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
474 updateFastFaceRow(v3s16(0,y,z), MAP_BLOCKSIZE,
481 scene::SMesh *mesh_new = NULL;
483 mesh_new = new scene::SMesh();
485 if(fastfaces_new->getSize() > 0)
487 MeshCollector collector;
489 core::list<FastFace*>::Iterator i = fastfaces_new->begin();
491 for(; i != fastfaces_new->end(); i++)
495 const u16 indices[] = {0,1,2,2,3,0};
497 /*collector.append(g_materials[f->material], f->vertices, 4,
499 /*collector.append(g_materials[f->tile], f->vertices, 4,
501 collector.append(g_tile_materials[f->tile], f->vertices, 4,
505 collector.fillMesh(mesh_new);
507 // Use VBO for mesh (this just would set this for ever buffer)
508 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
510 /*std::cout<<"MapBlock has "<<fastfaces_new->getSize()<<" faces "
511 <<"and uses "<<mesh_new->getMeshBufferCount()
512 <<" materials (meshbuffers)"<<std::endl;*/
516 Clear temporary FastFaces
519 core::list<FastFace*>::Iterator i;
520 i = fastfaces_new->begin();
521 for(; i != fastfaces_new->end(); i++)
525 fastfaces_new->clear();
526 delete fastfaces_new;
529 Add special graphics:
532 TODO: Optimize by using same meshbuffer for same textures
535 /*scene::ISceneManager *smgr = NULL;
536 video::IVideoDriver* driver = NULL;
539 smgr = g_device->getSceneManager();
540 driver = smgr->getVideoDriver();
543 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
544 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
545 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
549 MapNode &n = getNodeRef(x,y,z);
551 if(n.d == CONTENT_TORCH)
553 //scene::IMeshBuffer *buf = new scene::SMeshBuffer();
554 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
555 video::SColor c(255,255,255,255);
557 video::S3DVertex vertices[4] =
559 video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c, 0,1),
560 video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c, 1,1),
561 video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, 1,0),
562 video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c, 0,0),
565 v3s16 dir = unpackDir(n.dir);
567 for(s32 i=0; i<4; i++)
569 if(dir == v3s16(1,0,0))
570 vertices[i].Pos.rotateXZBy(0);
571 if(dir == v3s16(-1,0,0))
572 vertices[i].Pos.rotateXZBy(180);
573 if(dir == v3s16(0,0,1))
574 vertices[i].Pos.rotateXZBy(90);
575 if(dir == v3s16(0,0,-1))
576 vertices[i].Pos.rotateXZBy(-90);
577 if(dir == v3s16(0,-1,0))
578 vertices[i].Pos.rotateXZBy(45);
579 if(dir == v3s16(0,1,0))
580 vertices[i].Pos.rotateXZBy(-45);
582 vertices[i].Pos += intToFloat(p + getPosRelative());
585 u16 indices[] = {0,1,2,2,3,0};
586 buf->append(vertices, 4, indices, 6);
589 buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
590 buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false);
591 buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
592 //buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
593 buf->getMaterial().MaterialType
594 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
595 if(dir == v3s16(0,-1,0))
596 buf->getMaterial().setTexture(0,
597 g_texturecache.get("torch_on_floor"));
598 else if(dir == v3s16(0,1,0))
599 buf->getMaterial().setTexture(0,
600 g_texturecache.get("torch_on_ceiling"));
601 // For backwards compatibility
602 else if(dir == v3s16(0,0,0))
603 buf->getMaterial().setTexture(0,
604 g_texturecache.get("torch_on_floor"));
606 buf->getMaterial().setTexture(0, g_texturecache.get("torch"));
609 mesh_new->addMeshBuffer(buf);
615 Do some stuff to the mesh
618 mesh_new->recalculateBoundingBox();
621 Delete new mesh if it is empty
624 if(mesh_new->getMeshBufferCount() == 0)
636 scene::SMesh *mesh_old = mesh;
642 // Remove hardware buffers of meshbuffers of mesh
643 // NOTE: No way, this runs in a different thread and everything
644 /*u32 c = mesh_old->getMeshBufferCount();
645 for(u32 i=0; i<c; i++)
647 IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
656 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
660 Propagates sunlight down through the block.
661 Doesn't modify nodes that are not affected by sunlight.
663 Returns false if sunlight at bottom block is invalid
664 Returns true if bottom block doesn't exist.
666 If there is a block above, continues from it.
667 If there is no block above, assumes there is sunlight, unless
668 is_underground is set.
670 At the moment, all sunlighted nodes are added to light_sources.
671 TODO: This could be optimized.
673 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources)
675 // Whether the sunlight at the top of the bottom block is valid
676 bool block_below_is_valid = true;
678 v3s16 pos_relative = getPosRelative();
680 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
682 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
684 bool no_sunlight = false;
685 bool no_top_block = false;
686 // Check if node above block has sunlight
688 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
689 if(n.getLight() != LIGHT_SUN)
698 catch(InvalidPositionException &e)
702 // TODO: This makes over-ground roofed places sunlighted
703 // Assume sunlight, unless is_underground==true
709 // TODO: There has to be some way to allow this behaviour
710 // As of now, it just makes everything dark.
712 //no_sunlight = true;
715 /*std::cout<<"("<<x<<","<<z<<"): "
716 <<"no_top_block="<<no_top_block
717 <<", is_underground="<<is_underground
718 <<", no_sunlight="<<no_sunlight
721 s16 y = MAP_BLOCKSIZE-1;
723 if(no_sunlight == false)
725 // Continue spreading sunlight downwards through transparent
731 MapNode &n = getNodeRef(pos);
733 if(n.sunlight_propagates())
735 n.setLight(LIGHT_SUN);
737 light_sources.insert(pos_relative + pos, true);
745 bool sunlight_should_go_down = (y==-1);
747 // Fill rest with black (only transparent ones)
751 MapNode &n = getNodeRef(pos);
753 if(n.light_propagates())
763 If the block below hasn't already been marked invalid:
765 Check if the node below the block has proper sunlight at top.
766 If not, the block below is invalid.
768 Ignore non-transparent nodes as they always have no light
772 if(block_below_is_valid)
774 MapNode n = getNodeParent(v3s16(x, -1, z));
775 if(n.light_propagates())
777 if(n.getLight() == LIGHT_SUN
778 && sunlight_should_go_down == false)
779 block_below_is_valid = false;
780 else if(n.getLight() != LIGHT_SUN
781 && sunlight_should_go_down == true)
782 block_below_is_valid = false;
786 catch(InvalidPositionException &e)
788 /*std::cout<<"InvalidBlockException for bottom block node"
790 // Just no block below, no need to panic.
795 return block_below_is_valid;
798 void MapBlock::copyTo(VoxelManipulator &dst)
800 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
801 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
803 dst.copyFrom(data, data_area, v3s16(0,0,0),
804 getPosRelative(), data_size);
807 /*void getPseudoObjects(v3f origin, f32 max_d,
808 core::array<DistanceSortedObject> &dest)
816 void MapBlock::serialize(std::ostream &os, u8 version)
818 if(!ser_ver_supported(version))
819 throw VersionMismatchException("ERROR: MapBlock format not supported");
823 throw SerializationError("ERROR: Not writing dummy block.");
826 // These have no compression
827 if(version <= 3 || version == 5 || version == 6)
829 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
831 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
832 SharedBuffer<u8> dest(buflen);
834 dest[0] = is_underground;
835 for(u32 i=0; i<nodecount; i++)
837 u32 s = 1 + i * MapNode::serializedLength(version);
838 data[i].serialize(&dest[s], version);
841 os.write((char*)*dest, dest.getSize());
843 else if(version <= 10)
847 Compress the materials and the params separately.
851 os.write((char*)&is_underground, 1);
853 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
855 // Get and compress materials
856 SharedBuffer<u8> materialdata(nodecount);
857 for(u32 i=0; i<nodecount; i++)
859 materialdata[i] = data[i].d;
861 compress(materialdata, os, version);
863 // Get and compress lights
864 SharedBuffer<u8> lightdata(nodecount);
865 for(u32 i=0; i<nodecount; i++)
867 lightdata[i] = data[i].param;
869 compress(lightdata, os, version);
873 // Get and compress pressure
874 SharedBuffer<u8> pressuredata(nodecount);
875 for(u32 i=0; i<nodecount; i++)
877 pressuredata[i] = data[i].pressure;
879 compress(pressuredata, os, version);
882 // All other versions (newest)
886 os.write((char*)&is_underground, 1);
888 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
894 SharedBuffer<u8> databuf(nodecount*3);
897 for(u32 i=0; i<nodecount; i++)
899 databuf[i] = data[i].d;
903 for(u32 i=0; i<nodecount; i++)
905 databuf[i+nodecount] = data[i].param;
909 for(u32 i=0; i<nodecount; i++)
911 databuf[i+nodecount*2] = data[i].pressure;
915 Compress data to output stream
918 compress(databuf, os, version);
922 void MapBlock::deSerialize(std::istream &is, u8 version)
924 if(!ser_ver_supported(version))
925 throw VersionMismatchException("ERROR: MapBlock format not supported");
927 // These have no compression
928 if(version <= 3 || version == 5 || version == 6)
930 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
934 throw SerializationError
935 ("MapBlock::deSerialize: no enough input data");
936 is_underground = tmp;
937 for(u32 i=0; i<nodecount; i++)
939 s32 len = MapNode::serializedLength(version);
940 SharedBuffer<u8> d(len);
941 is.read((char*)*d, len);
942 if(is.gcount() != len)
943 throw SerializationError
944 ("MapBlock::deSerialize: no enough input data");
945 data[i].deSerialize(*d, version);
948 else if(version <= 10)
950 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
953 is.read((char*)&t8, 1);
957 // Uncompress and set material data
958 std::ostringstream os(std::ios_base::binary);
959 decompress(is, os, version);
960 std::string s = os.str();
961 if(s.size() != nodecount)
962 throw SerializationError
963 ("MapBlock::deSerialize: invalid format");
964 for(u32 i=0; i<s.size(); i++)
970 // Uncompress and set param data
971 std::ostringstream os(std::ios_base::binary);
972 decompress(is, os, version);
973 std::string s = os.str();
974 if(s.size() != nodecount)
975 throw SerializationError
976 ("MapBlock::deSerialize: invalid format");
977 for(u32 i=0; i<s.size(); i++)
979 data[i].param = s[i];
985 // Uncompress and set pressure data
986 std::ostringstream os(std::ios_base::binary);
987 decompress(is, os, version);
988 std::string s = os.str();
989 if(s.size() != nodecount)
990 throw SerializationError
991 ("MapBlock::deSerialize: invalid format");
992 for(u32 i=0; i<s.size(); i++)
994 data[i].pressure = s[i];
998 // All other versions (newest)
1001 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1004 is.read((char*)&t8, 1);
1005 is_underground = t8;
1008 std::ostringstream os(std::ios_base::binary);
1009 decompress(is, os, version);
1010 std::string s = os.str();
1011 if(s.size() != nodecount*3)
1012 throw SerializationError
1013 ("MapBlock::deSerialize: invalid format");
1016 for(u32 i=0; i<nodecount; i++)
1021 for(u32 i=0; i<nodecount; i++)
1023 data[i].param = s[i+nodecount];
1026 for(u32 i=0; i<nodecount; i++)
1028 data[i].pressure = s[i+nodecount*2];