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 MapBlock::MapBlock(NodeContainer *parent, v3s16 pos, bool dummy):
36 is_underground(false),
37 m_mesh_expired(false),
38 m_day_night_differs(false),
48 /*for(s32 i=0; i<DAYNIGHT_CACHE_COUNT; i++)
57 JMutexAutoLock lock(mesh_mutex);
64 /*for(s32 i=0; i<DAYNIGHT_CACHE_COUNT; i++)
78 bool MapBlock::isValidPositionParent(v3s16 p)
80 if(isValidPosition(p))
85 return m_parent->isValidPosition(getPosRelative() + p);
89 MapNode MapBlock::getNodeParent(v3s16 p)
91 if(isValidPosition(p) == false)
93 return m_parent->getNode(getPosRelative() + p);
98 throw InvalidPositionException();
99 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
103 void MapBlock::setNodeParent(v3s16 p, MapNode & n)
105 if(isValidPosition(p) == false)
107 m_parent->setNode(getPosRelative() + p, n);
112 throw InvalidPositionException();
113 data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X] = n;
117 MapNode MapBlock::getNodeParentNoEx(v3s16 p)
119 if(isValidPosition(p) == false)
122 return m_parent->getNode(getPosRelative() + p);
124 catch(InvalidPositionException &e)
126 return MapNode(CONTENT_IGNORE);
133 return MapNode(CONTENT_IGNORE);
135 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
139 void MapBlock::makeFastFace(TileSpec tile, u8 light, v3f p,
140 v3s16 dir, v3f scale, v3f posRelative_f,
141 core::array<FastFace> &dest)
145 // Position is at the center of the cube.
150 // If looking towards z+, this is the face that is behind
151 // the center point, facing towards z+.
152 vertex_pos[0] = v3f( BS/2,-BS/2,BS/2);
153 vertex_pos[1] = v3f(-BS/2,-BS/2,BS/2);
154 vertex_pos[2] = v3f(-BS/2, BS/2,BS/2);
155 vertex_pos[3] = v3f( BS/2, BS/2,BS/2);
157 if(dir == v3s16(0,0,1))
159 for(u16 i=0; i<4; i++)
160 vertex_pos[i].rotateXZBy(0);
162 else if(dir == v3s16(0,0,-1))
164 for(u16 i=0; i<4; i++)
165 vertex_pos[i].rotateXZBy(180);
167 else if(dir == v3s16(1,0,0))
169 for(u16 i=0; i<4; i++)
170 vertex_pos[i].rotateXZBy(-90);
172 else if(dir == v3s16(-1,0,0))
174 for(u16 i=0; i<4; i++)
175 vertex_pos[i].rotateXZBy(90);
177 else if(dir == v3s16(0,1,0))
179 for(u16 i=0; i<4; i++)
180 vertex_pos[i].rotateYZBy(-90);
182 else if(dir == v3s16(0,-1,0))
184 for(u16 i=0; i<4; i++)
185 vertex_pos[i].rotateYZBy(90);
188 for(u16 i=0; i<4; i++)
190 vertex_pos[i].X *= scale.X;
191 vertex_pos[i].Y *= scale.Y;
192 vertex_pos[i].Z *= scale.Z;
193 vertex_pos[i] += pos + posRelative_f;
197 if (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X;
198 else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
199 else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
201 v3f zerovector = v3f(0,0,0);
203 u8 li = decode_light(light);
208 if(tile.id == TILE_WATER)
213 video::SColor c = video::SColor(alpha,li,li,li);
215 face.vertices[0] = video::S3DVertex(vertex_pos[0], zerovector, c,
216 core::vector2d<f32>(0,1));
217 face.vertices[1] = video::S3DVertex(vertex_pos[1], zerovector, c,
218 core::vector2d<f32>(abs_scale,1));
219 face.vertices[2] = video::S3DVertex(vertex_pos[2], zerovector, c,
220 core::vector2d<f32>(abs_scale,0));
221 face.vertices[3] = video::S3DVertex(vertex_pos[3], zerovector, c,
222 core::vector2d<f32>(0,0));
226 //f->tile = TILE_STONE;
228 dest.push_back(face);
233 Parameters must consist of air and !air.
234 Order doesn't matter.
236 If either of the nodes doesn't exist, light is 0.
239 daynight_ratio: 0...1000
241 n2: getNodeParent(p + face_dir)
242 face_dir: axis oriented unit vector from p to p2
244 u8 MapBlock::getFaceLight(u32 daynight_ratio, MapNode n, MapNode n2,
249 u8 l1 = n.getLightBlend(daynight_ratio);
250 u8 l2 = n2.getLightBlend(daynight_ratio);
256 // Make some nice difference to different sides
258 /*if(face_dir.X == 1 || face_dir.Z == 1 || face_dir.Y == -1)
259 light = diminish_light(diminish_light(light));
260 else if(face_dir.X == -1 || face_dir.Z == -1)
261 light = diminish_light(light);*/
263 if(face_dir.X == 1 || face_dir.X == -1 || face_dir.Y == -1)
264 light = diminish_light(diminish_light(light));
265 else if(face_dir.Z == 1 || face_dir.Z == -1)
266 light = diminish_light(light);
270 catch(InvalidPositionException &e)
277 Gets node tile from any place relative to block.
278 Returns TILE_NODE if doesn't exist or should not be drawn.
280 TileSpec MapBlock::getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir)
286 spec.id = TILE_STONE;
290 spec.feature = TILEFEAT_NONE;
291 //spec.id = TILE_STONE;
292 spec.id = mn.getTile(face_dir);
295 Check temporary modifications on this node
297 core::map<v3s16, NodeMod>::Node *n;
298 n = m_temp_mods.find(p);
303 struct NodeMod mod = n->getValue();
304 if(mod.type == NODEMOD_CHANGECONTENT)
306 spec.id = content_tile(mod.param, face_dir);
308 if(mod.type == NODEMOD_CRACK)
316 u8 MapBlock::getNodeContent(v3s16 p, MapNode mn)
319 Check temporary modifications on this node
321 core::map<v3s16, NodeMod>::Node *n;
322 n = m_temp_mods.find(p);
327 struct NodeMod mod = n->getValue();
328 if(mod.type == NODEMOD_CHANGECONTENT)
333 if(mod.type == NODEMOD_CRACK)
336 Content doesn't change.
338 face_contents works just like it should, because
339 there should not be faces between differently cracked
342 If a semi-transparent node is cracked in front an
343 another one, it really doesn't matter whether there
344 is a cracked face drawn in between or not.
354 translate_dir: unit vector with only one of x, y or z
355 face_dir: unit vector with only one of x, y or z
357 void MapBlock::updateFastFaceRow(
366 core::array<FastFace> &dest)
370 u16 continuous_tiles_count = 0;
372 MapNode n0 = getNodeParentNoEx(p);
373 MapNode n1 = getNodeParentNoEx(p + face_dir);
375 u8 light = getFaceLight(daynight_ratio, n0, n1, face_dir);
377 TileSpec tile0 = getNodeTile(n0, p, face_dir);
378 TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir);
380 for(u16 j=0; j<length; j++)
382 bool next_is_different = true;
393 p_next = p + translate_dir;
394 n0_next = getNodeParentNoEx(p_next);
395 n1_next = getNodeParentNoEx(p_next + face_dir);
396 tile0_next = getNodeTile(n0_next, p_next, face_dir);
397 tile1_next = getNodeTile(n1_next, p_next + face_dir, -face_dir);
398 light_next = getFaceLight(daynight_ratio, n0_next, n1_next, face_dir);
400 if(tile0_next == tile0
401 && tile1_next == tile1
402 && light_next == light)
404 next_is_different = false;
408 continuous_tiles_count++;
410 if(next_is_different)
413 Create a face if there should be one
415 //u8 mf = face_contents(tile0, tile1);
417 u8 content0 = getNodeContent(p, n0);
418 u8 content1 = getNodeContent(p + face_dir, n1);
419 u8 mf = face_contents(content0, content1);
423 // Floating point conversion of the position vector
424 v3f pf(p.X, p.Y, p.Z);
425 // Center point of face (kind of)
426 v3f sp = pf - ((f32)continuous_tiles_count / 2. - 0.5) * translate_dir_f;
428 if(translate_dir.X != 0){
429 scale.X = continuous_tiles_count;
431 if(translate_dir.Y != 0){
432 scale.Y = continuous_tiles_count;
434 if(translate_dir.Z != 0){
435 scale.Z = continuous_tiles_count;
440 // If node at sp (tile0) is more solid
443 makeFastFace(tile0, light,
445 posRelative_f, dest);
447 // If node at sp is less solid (mf == 2)
450 makeFastFace(tile1, light,
451 sp+face_dir_f, -face_dir, scale,
452 posRelative_f, dest);
457 continuous_tiles_count = 0;
470 This is used because CMeshBuffer::append() is very slow
474 video::SMaterial material;
475 core::array<u16> indices;
476 core::array<video::S3DVertex> vertices;
483 video::SMaterial material,
484 const video::S3DVertex* const vertices,
486 const u16* const indices,
490 PreMeshBuffer *p = NULL;
491 for(u32 i=0; i<m_prebuffers.size(); i++)
493 PreMeshBuffer &pp = m_prebuffers[i];
494 if(pp.material != material)
504 pp.material = material;
505 m_prebuffers.push_back(pp);
506 p = &m_prebuffers[m_prebuffers.size()-1];
509 u32 vertex_count = p->vertices.size();
510 for(u32 i=0; i<numIndices; i++)
512 u32 j = indices[i] + vertex_count;
515 dstream<<"FIXME: Meshbuffer ran out of indices"<<std::endl;
516 // NOTE: Fix is to just add an another MeshBuffer
518 p->indices.push_back(j);
520 for(u32 i=0; i<numVertices; i++)
522 p->vertices.push_back(vertices[i]);
526 void fillMesh(scene::SMesh *mesh)
528 /*dstream<<"Filling mesh with "<<m_prebuffers.size()
529 <<" meshbuffers"<<std::endl;*/
530 for(u32 i=0; i<m_prebuffers.size(); i++)
532 PreMeshBuffer &p = m_prebuffers[i];
534 /*dstream<<"p.vertices.size()="<<p.vertices.size()
535 <<", p.indices.size()="<<p.indices.size()
540 // This is a "Standard MeshBuffer",
541 // it's a typedeffed CMeshBuffer<video::S3DVertex>
542 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
544 buf->Material = p.material;
545 //((scene::SMeshBuffer*)buf)->Material = p.material;
547 //buf->setHardwareMappingHint(scene::EHM_STATIC);
549 mesh->addMeshBuffer(buf);
553 buf->append(p.vertices.pointer(), p.vertices.size(),
554 p.indices.pointer(), p.indices.size());
559 core::array<PreMeshBuffer> m_prebuffers;
562 void MapBlock::updateMesh(u32 daynight_ratio)
566 DEBUG: If mesh has been generated, don't generate it again
569 JMutexAutoLock meshlock(mesh_mutex);
576 //TimeTaker timer1("updateMesh()", g_device);
578 core::array<FastFace> fastfaces_new;
580 v3f posRelative_f(getPosRelative().X, getPosRelative().Y,
581 getPosRelative().Z); // floating point conversion
584 We are including the faces of the trailing edges of the block.
585 This means that when something changes, the caller must
586 also update the meshes of the blocks at the leading edges.
588 NOTE: This is the slowest part of this method.
592 Go through every y,z and get top faces in rows of x+
594 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
595 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
596 updateFastFaceRow(daynight_ratio, posRelative_f,
597 v3s16(0,y,z), MAP_BLOCKSIZE,
600 v3s16(0,1,0), //face dir
606 Go through every x,y and get right faces in rows of z+
608 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
609 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
610 updateFastFaceRow(daynight_ratio, posRelative_f,
611 v3s16(x,y,0), MAP_BLOCKSIZE,
620 Go through every y,z and get back faces in rows of x+
622 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
623 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
624 updateFastFaceRow(daynight_ratio, posRelative_f,
625 v3s16(0,y,z), MAP_BLOCKSIZE,
637 Convert FastFaces to SMesh
640 scene::SMesh *mesh_new = NULL;
642 mesh_new = new scene::SMesh();
644 if(fastfaces_new.size() > 0)
646 MeshCollector collector;
648 for(u32 i=0; i<fastfaces_new.size(); i++)
650 FastFace &f = fastfaces_new[i];
652 const u16 indices[] = {0,1,2,2,3,0};
654 if(f.tile.feature == TILEFEAT_NONE)
656 collector.append(g_tile_materials[f.tile.id], f.vertices, 4,
666 collector.fillMesh(mesh_new);
668 // Use VBO for mesh (this just would set this for ever buffer)
669 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
671 /*std::cout<<"MapBlock has "<<fastfaces_new->getSize()<<" faces "
672 <<"and uses "<<mesh_new->getMeshBufferCount()
673 <<" materials (meshbuffers)"<<std::endl;*/
677 Clear temporary FastFaces
680 /*core::list<FastFace*>::Iterator i;
681 i = fastfaces_new->begin();
682 for(; i != fastfaces_new->end(); i++)
686 fastfaces_new->clear();
687 delete fastfaces_new;*/
690 Add special graphics:
693 TODO: Optimize by using same meshbuffer for same textures
696 /*scene::ISceneManager *smgr = NULL;
697 video::IVideoDriver* driver = NULL;
700 smgr = g_device->getSceneManager();
701 driver = smgr->getVideoDriver();
704 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
705 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
706 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
710 MapNode &n = getNodeRef(x,y,z);
712 if(n.d == CONTENT_TORCH)
714 //scene::IMeshBuffer *buf = new scene::SMeshBuffer();
715 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
716 video::SColor c(255,255,255,255);
718 video::S3DVertex vertices[4] =
720 video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c, 0,1),
721 video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c, 1,1),
722 video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, 1,0),
723 video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c, 0,0),
726 v3s16 dir = unpackDir(n.dir);
728 for(s32 i=0; i<4; i++)
730 if(dir == v3s16(1,0,0))
731 vertices[i].Pos.rotateXZBy(0);
732 if(dir == v3s16(-1,0,0))
733 vertices[i].Pos.rotateXZBy(180);
734 if(dir == v3s16(0,0,1))
735 vertices[i].Pos.rotateXZBy(90);
736 if(dir == v3s16(0,0,-1))
737 vertices[i].Pos.rotateXZBy(-90);
738 if(dir == v3s16(0,-1,0))
739 vertices[i].Pos.rotateXZBy(45);
740 if(dir == v3s16(0,1,0))
741 vertices[i].Pos.rotateXZBy(-45);
743 vertices[i].Pos += intToFloat(p + getPosRelative());
746 u16 indices[] = {0,1,2,2,3,0};
747 buf->append(vertices, 4, indices, 6);
750 buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
751 buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false);
752 buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
753 //buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
754 buf->getMaterial().MaterialType
755 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
756 if(dir == v3s16(0,-1,0))
757 buf->getMaterial().setTexture(0,
758 g_texturecache.get("torch_on_floor"));
759 else if(dir == v3s16(0,1,0))
760 buf->getMaterial().setTexture(0,
761 g_texturecache.get("torch_on_ceiling"));
762 // For backwards compatibility
763 else if(dir == v3s16(0,0,0))
764 buf->getMaterial().setTexture(0,
765 g_texturecache.get("torch_on_floor"));
767 buf->getMaterial().setTexture(0, g_texturecache.get("torch"));
770 mesh_new->addMeshBuffer(buf);
776 Do some stuff to the mesh
779 mesh_new->recalculateBoundingBox();
782 Delete new mesh if it is empty
785 if(mesh_new->getMeshBufferCount() == 0)
797 //scene::SMesh *mesh_old = mesh[daynight_i];
798 //mesh[daynight_i] = mesh_new;
800 scene::SMesh *mesh_old = mesh;
802 setMeshExpired(false);
806 // Remove hardware buffers of meshbuffers of mesh
807 // NOTE: No way, this runs in a different thread and everything
808 /*u32 c = mesh_old->getMeshBufferCount();
809 for(u32 i=0; i<c; i++)
811 IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
814 /*dstream<<"mesh_old->getReferenceCount()="
815 <<mesh_old->getReferenceCount()<<std::endl;
816 u32 c = mesh_old->getMeshBufferCount();
817 for(u32 i=0; i<c; i++)
819 scene::IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
820 dstream<<"buf->getReferenceCount()="
821 <<buf->getReferenceCount()<<std::endl;
832 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
835 /*void MapBlock::updateMeshes(s32 first_i)
837 assert(first_i >= 0 && first_i <= DAYNIGHT_CACHE_COUNT);
839 for(s32 i=0; i<DAYNIGHT_CACHE_COUNT; i++)
848 Propagates sunlight down through the block.
849 Doesn't modify nodes that are not affected by sunlight.
851 Returns false if sunlight at bottom block is invalid
852 Returns true if bottom block doesn't exist.
854 If there is a block above, continues from it.
855 If there is no block above, assumes there is sunlight, unless
856 is_underground is set.
858 At the moment, all sunlighted nodes are added to light_sources.
859 TODO: This could be optimized.
861 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources)
863 // Whether the sunlight at the top of the bottom block is valid
864 bool block_below_is_valid = true;
866 v3s16 pos_relative = getPosRelative();
868 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
870 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
872 bool no_sunlight = false;
873 bool no_top_block = false;
874 // Check if node above block has sunlight
876 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
877 if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
886 catch(InvalidPositionException &e)
890 // TODO: This makes over-ground roofed places sunlighted
891 // Assume sunlight, unless is_underground==true
897 // TODO: There has to be some way to allow this behaviour
898 // As of now, it just makes everything dark.
900 //no_sunlight = true;
903 /*std::cout<<"("<<x<<","<<z<<"): "
904 <<"no_top_block="<<no_top_block
905 <<", is_underground="<<is_underground
906 <<", no_sunlight="<<no_sunlight
909 s16 y = MAP_BLOCKSIZE-1;
911 if(no_sunlight == false)
913 // Continue spreading sunlight downwards through transparent
919 MapNode &n = getNodeRef(pos);
921 if(n.sunlight_propagates())
923 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
925 light_sources.insert(pos_relative + pos, true);
933 bool sunlight_should_go_down = (y==-1);
935 // Fill rest with black (only transparent ones)
939 MapNode &n = getNodeRef(pos);
941 if(n.light_propagates())
943 n.setLight(LIGHTBANK_DAY, 0);
951 If the block below hasn't already been marked invalid:
953 Check if the node below the block has proper sunlight at top.
954 If not, the block below is invalid.
956 Ignore non-transparent nodes as they always have no light
960 if(block_below_is_valid)
962 MapNode n = getNodeParent(v3s16(x, -1, z));
963 if(n.light_propagates())
965 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN
966 && sunlight_should_go_down == false)
967 block_below_is_valid = false;
968 else if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN
969 && sunlight_should_go_down == true)
970 block_below_is_valid = false;
974 catch(InvalidPositionException &e)
976 /*std::cout<<"InvalidBlockException for bottom block node"
978 // Just no block below, no need to panic.
983 return block_below_is_valid;
986 void MapBlock::copyTo(VoxelManipulator &dst)
988 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
989 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
991 dst.copyFrom(data, data_area, v3s16(0,0,0),
992 getPosRelative(), data_size);
995 /*void getPseudoObjects(v3f origin, f32 max_d,
996 core::array<DistanceSortedObject> &dest)
1001 void MapBlock::updateDayNightDiff()
1005 m_day_night_differs = false;
1009 bool differs = false;
1012 Check if any lighting value differs
1014 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1016 MapNode &n = data[i];
1017 if(n.getLight(LIGHTBANK_DAY) != n.getLight(LIGHTBANK_NIGHT))
1025 If some lighting values differ, check if the whole thing is
1026 just air. If it is, differ = false
1030 bool only_air = true;
1031 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1033 MapNode &n = data[i];
1034 if(n.d != CONTENT_AIR)
1044 // Set member variable
1045 m_day_night_differs = differs;
1052 void MapBlock::serialize(std::ostream &os, u8 version)
1054 if(!ser_ver_supported(version))
1055 throw VersionMismatchException("ERROR: MapBlock format not supported");
1059 throw SerializationError("ERROR: Not writing dummy block.");
1062 // These have no compression
1063 if(version <= 3 || version == 5 || version == 6)
1065 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1067 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
1068 SharedBuffer<u8> dest(buflen);
1070 dest[0] = is_underground;
1071 for(u32 i=0; i<nodecount; i++)
1073 u32 s = 1 + i * MapNode::serializedLength(version);
1074 data[i].serialize(&dest[s], version);
1077 os.write((char*)*dest, dest.getSize());
1079 else if(version <= 10)
1083 Compress the materials and the params separately.
1087 os.write((char*)&is_underground, 1);
1089 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1091 // Get and compress materials
1092 SharedBuffer<u8> materialdata(nodecount);
1093 for(u32 i=0; i<nodecount; i++)
1095 materialdata[i] = data[i].d;
1097 compress(materialdata, os, version);
1099 // Get and compress lights
1100 SharedBuffer<u8> lightdata(nodecount);
1101 for(u32 i=0; i<nodecount; i++)
1103 lightdata[i] = data[i].param;
1105 compress(lightdata, os, version);
1109 // Get and compress pressure
1110 SharedBuffer<u8> pressuredata(nodecount);
1111 for(u32 i=0; i<nodecount; i++)
1113 pressuredata[i] = data[i].pressure;
1115 compress(pressuredata, os, version);
1118 // All other versions (newest)
1125 if(m_day_night_differs)
1127 os.write((char*)&flags, 1);
1129 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1135 SharedBuffer<u8> databuf(nodecount*3);
1138 for(u32 i=0; i<nodecount; i++)
1140 databuf[i] = data[i].d;
1144 for(u32 i=0; i<nodecount; i++)
1146 databuf[i+nodecount] = data[i].param;
1150 for(u32 i=0; i<nodecount; i++)
1152 databuf[i+nodecount*2] = data[i].pressure;
1156 Compress data to output stream
1159 compress(databuf, os, version);
1163 void MapBlock::deSerialize(std::istream &is, u8 version)
1165 if(!ser_ver_supported(version))
1166 throw VersionMismatchException("ERROR: MapBlock format not supported");
1168 // These have no compression
1169 if(version <= 3 || version == 5 || version == 6)
1171 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1174 if(is.gcount() != 1)
1175 throw SerializationError
1176 ("MapBlock::deSerialize: no enough input data");
1177 is_underground = tmp;
1178 for(u32 i=0; i<nodecount; i++)
1180 s32 len = MapNode::serializedLength(version);
1181 SharedBuffer<u8> d(len);
1182 is.read((char*)*d, len);
1183 if(is.gcount() != len)
1184 throw SerializationError
1185 ("MapBlock::deSerialize: no enough input data");
1186 data[i].deSerialize(*d, version);
1189 else if(version <= 10)
1191 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1194 is.read((char*)&t8, 1);
1195 is_underground = t8;
1198 // Uncompress and set material data
1199 std::ostringstream os(std::ios_base::binary);
1200 decompress(is, os, version);
1201 std::string s = os.str();
1202 if(s.size() != nodecount)
1203 throw SerializationError
1204 ("MapBlock::deSerialize: invalid format");
1205 for(u32 i=0; i<s.size(); i++)
1211 // Uncompress and set param data
1212 std::ostringstream os(std::ios_base::binary);
1213 decompress(is, os, version);
1214 std::string s = os.str();
1215 if(s.size() != nodecount)
1216 throw SerializationError
1217 ("MapBlock::deSerialize: invalid format");
1218 for(u32 i=0; i<s.size(); i++)
1220 data[i].param = s[i];
1226 // Uncompress and set pressure data
1227 std::ostringstream os(std::ios_base::binary);
1228 decompress(is, os, version);
1229 std::string s = os.str();
1230 if(s.size() != nodecount)
1231 throw SerializationError
1232 ("MapBlock::deSerialize: invalid format");
1233 for(u32 i=0; i<s.size(); i++)
1235 data[i].pressure = s[i];
1239 // All other versions (newest)
1242 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1245 is.read((char*)&flags, 1);
1246 is_underground = (flags & 1) ? true : false;
1247 m_day_night_differs = (flags & 2) ? true : false;
1250 std::ostringstream os(std::ios_base::binary);
1251 decompress(is, os, version);
1252 std::string s = os.str();
1253 if(s.size() != nodecount*3)
1254 throw SerializationError
1255 ("MapBlock::deSerialize: invalid format");
1258 for(u32 i=0; i<nodecount; i++)
1263 for(u32 i=0; i<nodecount; i++)
1265 data[i].param = s[i+nodecount];
1268 for(u32 i=0; i<nodecount; i++)
1270 data[i].pressure = s[i+nodecount*2];