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_day_night_differs(false),
45 m_mesh_expired = false;
55 JMutexAutoLock lock(mesh_mutex);
69 bool MapBlock::isValidPositionParent(v3s16 p)
71 if(isValidPosition(p))
76 return m_parent->isValidPosition(getPosRelative() + p);
80 MapNode MapBlock::getNodeParent(v3s16 p)
82 if(isValidPosition(p) == false)
84 return m_parent->getNode(getPosRelative() + p);
89 throw InvalidPositionException();
90 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
94 void MapBlock::setNodeParent(v3s16 p, MapNode & n)
96 if(isValidPosition(p) == false)
98 m_parent->setNode(getPosRelative() + p, n);
103 throw InvalidPositionException();
104 data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X] = n;
108 MapNode MapBlock::getNodeParentNoEx(v3s16 p)
110 if(isValidPosition(p) == false)
113 return m_parent->getNode(getPosRelative() + p);
115 catch(InvalidPositionException &e)
117 return MapNode(CONTENT_IGNORE);
124 return MapNode(CONTENT_IGNORE);
126 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
131 Parameters must consist of air and !air.
132 Order doesn't matter.
134 If either of the nodes doesn't exist, light is 0.
137 daynight_ratio: 0...1000
139 n2: getNodeParent(p + face_dir)
140 face_dir: axis oriented unit vector from p to p2
142 u8 MapBlock::getFaceLight(u32 daynight_ratio, MapNode n, MapNode n2,
147 u8 l1 = n.getLightBlend(daynight_ratio);
148 u8 l2 = n2.getLightBlend(daynight_ratio);
154 // Make some nice difference to different sides
156 /*if(face_dir.X == 1 || face_dir.Z == 1 || face_dir.Y == -1)
157 light = diminish_light(diminish_light(light));
158 else if(face_dir.X == -1 || face_dir.Z == -1)
159 light = diminish_light(light);*/
161 if(face_dir.X == 1 || face_dir.X == -1 || face_dir.Y == -1)
162 light = diminish_light(diminish_light(light));
163 else if(face_dir.Z == 1 || face_dir.Z == -1)
164 light = diminish_light(light);
168 catch(InvalidPositionException &e)
176 void MapBlock::makeFastFace(TileSpec tile, u8 light, v3f p,
177 v3s16 dir, v3f scale, v3f posRelative_f,
178 core::array<FastFace> &dest)
182 // Position is at the center of the cube.
187 // If looking towards z+, this is the face that is behind
188 // the center point, facing towards z+.
189 vertex_pos[0] = v3f( BS/2,-BS/2,BS/2);
190 vertex_pos[1] = v3f(-BS/2,-BS/2,BS/2);
191 vertex_pos[2] = v3f(-BS/2, BS/2,BS/2);
192 vertex_pos[3] = v3f( BS/2, BS/2,BS/2);
194 if(dir == v3s16(0,0,1))
196 for(u16 i=0; i<4; i++)
197 vertex_pos[i].rotateXZBy(0);
199 else if(dir == v3s16(0,0,-1))
201 for(u16 i=0; i<4; i++)
202 vertex_pos[i].rotateXZBy(180);
204 else if(dir == v3s16(1,0,0))
206 for(u16 i=0; i<4; i++)
207 vertex_pos[i].rotateXZBy(-90);
209 else if(dir == v3s16(-1,0,0))
211 for(u16 i=0; i<4; i++)
212 vertex_pos[i].rotateXZBy(90);
214 else if(dir == v3s16(0,1,0))
216 for(u16 i=0; i<4; i++)
217 vertex_pos[i].rotateYZBy(-90);
219 else if(dir == v3s16(0,-1,0))
221 for(u16 i=0; i<4; i++)
222 vertex_pos[i].rotateYZBy(90);
225 for(u16 i=0; i<4; i++)
227 vertex_pos[i].X *= scale.X;
228 vertex_pos[i].Y *= scale.Y;
229 vertex_pos[i].Z *= scale.Z;
230 vertex_pos[i] += pos + posRelative_f;
234 if (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X;
235 else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
236 else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
238 v3f zerovector = v3f(0,0,0);
240 u8 li = decode_light(light);
245 if(tile.id == TILE_WATER)
250 video::SColor c = video::SColor(alpha,li,li,li);
252 face.vertices[0] = video::S3DVertex(vertex_pos[0], zerovector, c,
253 core::vector2d<f32>(0,1));
254 face.vertices[1] = video::S3DVertex(vertex_pos[1], zerovector, c,
255 core::vector2d<f32>(abs_scale,1));
256 face.vertices[2] = video::S3DVertex(vertex_pos[2], zerovector, c,
257 core::vector2d<f32>(abs_scale,0));
258 face.vertices[3] = video::S3DVertex(vertex_pos[3], zerovector, c,
259 core::vector2d<f32>(0,0));
263 //f->tile = TILE_STONE;
265 dest.push_back(face);
270 Gets node tile from any place relative to block.
271 Returns TILE_NODE if doesn't exist or should not be drawn.
273 TileSpec MapBlock::getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir)
279 spec.id = TILE_STONE;
283 spec.feature = TILEFEAT_NONE;
284 //spec.id = TILE_STONE;
285 spec.id = mn.getTile(face_dir);
288 Check temporary modifications on this node
290 core::map<v3s16, NodeMod>::Node *n;
291 n = m_temp_mods.find(p);
296 struct NodeMod mod = n->getValue();
297 if(mod.type == NODEMOD_CHANGECONTENT)
299 spec.id = content_tile(mod.param, face_dir);
301 if(mod.type == NODEMOD_CRACK)
303 spec.feature = TILEFEAT_CRACK;
304 spec.param.crack.progression = mod.param;
311 u8 MapBlock::getNodeContent(v3s16 p, MapNode mn)
314 Check temporary modifications on this node
316 core::map<v3s16, NodeMod>::Node *n;
317 n = m_temp_mods.find(p);
322 struct NodeMod mod = n->getValue();
323 if(mod.type == NODEMOD_CHANGECONTENT)
328 if(mod.type == NODEMOD_CRACK)
331 Content doesn't change.
333 face_contents works just like it should, because
334 there should not be faces between differently cracked
337 If a semi-transparent node is cracked in front an
338 another one, it really doesn't matter whether there
339 is a cracked face drawn in between or not.
349 translate_dir: unit vector with only one of x, y or z
350 face_dir: unit vector with only one of x, y or z
352 void MapBlock::updateFastFaceRow(
361 core::array<FastFace> &dest)
365 u16 continuous_tiles_count = 0;
367 MapNode n0 = getNodeParentNoEx(p);
368 MapNode n1 = getNodeParentNoEx(p + face_dir);
370 u8 light = getFaceLight(daynight_ratio, n0, n1, face_dir);
372 TileSpec tile0 = getNodeTile(n0, p, face_dir);
373 TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir);
375 for(u16 j=0; j<length; j++)
377 bool next_is_different = true;
388 p_next = p + translate_dir;
389 n0_next = getNodeParentNoEx(p_next);
390 n1_next = getNodeParentNoEx(p_next + face_dir);
391 tile0_next = getNodeTile(n0_next, p_next, face_dir);
392 tile1_next = getNodeTile(n1_next, p_next + face_dir, -face_dir);
393 light_next = getFaceLight(daynight_ratio, n0_next, n1_next, face_dir);
395 if(tile0_next == tile0
396 && tile1_next == tile1
397 && light_next == light)
399 next_is_different = false;
403 continuous_tiles_count++;
405 if(next_is_different)
408 Create a face if there should be one
410 //u8 mf = face_contents(tile0, tile1);
412 u8 content0 = getNodeContent(p, n0);
413 u8 content1 = getNodeContent(p + face_dir, n1);
414 u8 mf = face_contents(content0, content1);
418 // Floating point conversion of the position vector
419 v3f pf(p.X, p.Y, p.Z);
420 // Center point of face (kind of)
421 v3f sp = pf - ((f32)continuous_tiles_count / 2. - 0.5) * translate_dir_f;
423 if(translate_dir.X != 0){
424 scale.X = continuous_tiles_count;
426 if(translate_dir.Y != 0){
427 scale.Y = continuous_tiles_count;
429 if(translate_dir.Z != 0){
430 scale.Z = continuous_tiles_count;
435 // If node at sp (tile0) is more solid
438 makeFastFace(tile0, light,
440 posRelative_f, dest);
442 // If node at sp is less solid (mf == 2)
445 makeFastFace(tile1, light,
446 sp+face_dir_f, -face_dir, scale,
447 posRelative_f, dest);
452 continuous_tiles_count = 0;
465 This is used because CMeshBuffer::append() is very slow
469 video::SMaterial material;
470 core::array<u16> indices;
471 core::array<video::S3DVertex> vertices;
478 video::SMaterial material,
479 const video::S3DVertex* const vertices,
481 const u16* const indices,
485 PreMeshBuffer *p = NULL;
486 for(u32 i=0; i<m_prebuffers.size(); i++)
488 PreMeshBuffer &pp = m_prebuffers[i];
489 if(pp.material != material)
499 pp.material = material;
500 m_prebuffers.push_back(pp);
501 p = &m_prebuffers[m_prebuffers.size()-1];
504 u32 vertex_count = p->vertices.size();
505 for(u32 i=0; i<numIndices; i++)
507 u32 j = indices[i] + vertex_count;
510 dstream<<"FIXME: Meshbuffer ran out of indices"<<std::endl;
511 // NOTE: Fix is to just add an another MeshBuffer
513 p->indices.push_back(j);
515 for(u32 i=0; i<numVertices; i++)
517 p->vertices.push_back(vertices[i]);
521 void fillMesh(scene::SMesh *mesh)
523 /*dstream<<"Filling mesh with "<<m_prebuffers.size()
524 <<" meshbuffers"<<std::endl;*/
525 for(u32 i=0; i<m_prebuffers.size(); i++)
527 PreMeshBuffer &p = m_prebuffers[i];
529 /*dstream<<"p.vertices.size()="<<p.vertices.size()
530 <<", p.indices.size()="<<p.indices.size()
535 // This is a "Standard MeshBuffer",
536 // it's a typedeffed CMeshBuffer<video::S3DVertex>
537 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
539 buf->Material = p.material;
540 //((scene::SMeshBuffer*)buf)->Material = p.material;
542 //buf->setHardwareMappingHint(scene::EHM_STATIC);
544 mesh->addMeshBuffer(buf);
548 buf->append(p.vertices.pointer(), p.vertices.size(),
549 p.indices.pointer(), p.indices.size());
554 core::array<PreMeshBuffer> m_prebuffers;
557 void MapBlock::updateMesh(u32 daynight_ratio)
561 DEBUG: If mesh has been generated, don't generate it again
564 JMutexAutoLock meshlock(mesh_mutex);
571 //TimeTaker timer1("updateMesh()", g_device);
573 core::array<FastFace> fastfaces_new;
575 v3f posRelative_f(getPosRelative().X, getPosRelative().Y,
576 getPosRelative().Z); // floating point conversion
579 We are including the faces of the trailing edges of the block.
580 This means that when something changes, the caller must
581 also update the meshes of the blocks at the leading edges.
583 NOTE: This is the slowest part of this method.
587 Go through every y,z and get top faces in rows of x+
589 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
590 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
591 updateFastFaceRow(daynight_ratio, posRelative_f,
592 v3s16(0,y,z), MAP_BLOCKSIZE,
595 v3s16(0,1,0), //face dir
601 Go through every x,y and get right faces in rows of z+
603 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
604 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
605 updateFastFaceRow(daynight_ratio, posRelative_f,
606 v3s16(x,y,0), MAP_BLOCKSIZE,
615 Go through every y,z and get back faces in rows of x+
617 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
618 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
619 updateFastFaceRow(daynight_ratio, posRelative_f,
620 v3s16(0,y,z), MAP_BLOCKSIZE,
632 Convert FastFaces to SMesh
635 scene::SMesh *mesh_new = NULL;
637 mesh_new = new scene::SMesh();
639 if(fastfaces_new.size() > 0)
641 MeshCollector collector;
643 for(u32 i=0; i<fastfaces_new.size(); i++)
645 FastFace &f = fastfaces_new[i];
647 const u16 indices[] = {0,1,2,2,3,0};
649 if(f.tile.feature == TILEFEAT_NONE)
651 collector.append(tile_material_get(f.tile.id), f.vertices, 4,
654 else if(f.tile.feature == TILEFEAT_CRACK)
656 const char *path = tile_texture_path_get(f.tile.id);
658 u16 progression = f.tile.param.crack.progression;
660 std::string name = (std::string)path + "_cracked_"
661 + (char)('0' + progression);
663 TextureMod *mod = new CrackTextureMod(progression);
665 video::ITexture *texture = g_irrlicht->getTexture(
666 TextureSpec(name, path, mod));
668 video::SMaterial material = tile_material_get(f.tile.id);
669 material.setTexture(0, texture);
671 collector.append(material, f.vertices, 4, indices, 6);
680 collector.fillMesh(mesh_new);
682 // Use VBO for mesh (this just would set this for ever buffer)
683 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
685 /*std::cout<<"MapBlock has "<<fastfaces_new->getSize()<<" faces "
686 <<"and uses "<<mesh_new->getMeshBufferCount()
687 <<" materials (meshbuffers)"<<std::endl;*/
691 Add special graphics:
694 TODO: Optimize by using same meshbuffer for same textures
697 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
698 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
699 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
703 MapNode &n = getNodeRef(x,y,z);
705 if(n.d == CONTENT_TORCH)
707 //scene::IMeshBuffer *buf = new scene::SMeshBuffer();
708 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
709 video::SColor c(255,255,255,255);
711 video::S3DVertex vertices[4] =
713 video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c, 0,1),
714 video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c, 1,1),
715 video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, 1,0),
716 video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c, 0,0),
719 v3s16 dir = unpackDir(n.dir);
721 for(s32 i=0; i<4; i++)
723 if(dir == v3s16(1,0,0))
724 vertices[i].Pos.rotateXZBy(0);
725 if(dir == v3s16(-1,0,0))
726 vertices[i].Pos.rotateXZBy(180);
727 if(dir == v3s16(0,0,1))
728 vertices[i].Pos.rotateXZBy(90);
729 if(dir == v3s16(0,0,-1))
730 vertices[i].Pos.rotateXZBy(-90);
731 if(dir == v3s16(0,-1,0))
732 vertices[i].Pos.rotateXZBy(45);
733 if(dir == v3s16(0,1,0))
734 vertices[i].Pos.rotateXZBy(-45);
736 vertices[i].Pos += intToFloat(p + getPosRelative());
739 u16 indices[] = {0,1,2,2,3,0};
740 buf->append(vertices, 4, indices, 6);
743 buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
744 buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false);
745 buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
746 //buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
747 buf->getMaterial().MaterialType
748 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
749 if(dir == v3s16(0,-1,0))
750 buf->getMaterial().setTexture(0,
751 g_irrlicht->getTexture("../data/torch_on_floor.png"));
752 else if(dir == v3s16(0,1,0))
753 buf->getMaterial().setTexture(0,
754 g_irrlicht->getTexture("../data/torch_on_ceiling.png"));
755 // For backwards compatibility
756 else if(dir == v3s16(0,0,0))
757 buf->getMaterial().setTexture(0,
758 g_irrlicht->getTexture("../data/torch_on_floor.png"));
760 buf->getMaterial().setTexture(0,
761 g_irrlicht->getTexture("../data/torch.png"));
764 mesh_new->addMeshBuffer(buf);
770 Do some stuff to the mesh
773 mesh_new->recalculateBoundingBox();
776 Delete new mesh if it is empty
779 if(mesh_new->getMeshBufferCount() == 0)
791 //scene::SMesh *mesh_old = mesh[daynight_i];
792 //mesh[daynight_i] = mesh_new;
794 scene::SMesh *mesh_old = mesh;
796 setMeshExpired(false);
800 // Remove hardware buffers of meshbuffers of mesh
801 // NOTE: No way, this runs in a different thread and everything
802 /*u32 c = mesh_old->getMeshBufferCount();
803 for(u32 i=0; i<c; i++)
805 IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
808 /*dstream<<"mesh_old->getReferenceCount()="
809 <<mesh_old->getReferenceCount()<<std::endl;
810 u32 c = mesh_old->getMeshBufferCount();
811 for(u32 i=0; i<c; i++)
813 scene::IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
814 dstream<<"buf->getReferenceCount()="
815 <<buf->getReferenceCount()<<std::endl;
826 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
829 /*void MapBlock::updateMeshes(s32 first_i)
831 assert(first_i >= 0 && first_i <= DAYNIGHT_CACHE_COUNT);
833 for(s32 i=0; i<DAYNIGHT_CACHE_COUNT; i++)
844 Propagates sunlight down through the block.
845 Doesn't modify nodes that are not affected by sunlight.
847 Returns false if sunlight at bottom block is invalid
848 Returns true if bottom block doesn't exist.
850 If there is a block above, continues from it.
851 If there is no block above, assumes there is sunlight, unless
852 is_underground is set.
854 At the moment, all sunlighted nodes are added to light_sources.
855 TODO: This could be optimized.
857 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources)
859 // Whether the sunlight at the top of the bottom block is valid
860 bool block_below_is_valid = true;
862 v3s16 pos_relative = getPosRelative();
864 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
866 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
868 bool no_sunlight = false;
869 bool no_top_block = false;
870 // Check if node above block has sunlight
872 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
873 if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
882 catch(InvalidPositionException &e)
886 // TODO: This makes over-ground roofed places sunlighted
887 // Assume sunlight, unless is_underground==true
893 // TODO: There has to be some way to allow this behaviour
894 // As of now, it just makes everything dark.
896 //no_sunlight = true;
899 /*std::cout<<"("<<x<<","<<z<<"): "
900 <<"no_top_block="<<no_top_block
901 <<", is_underground="<<is_underground
902 <<", no_sunlight="<<no_sunlight
905 s16 y = MAP_BLOCKSIZE-1;
907 if(no_sunlight == false)
909 // Continue spreading sunlight downwards through transparent
915 MapNode &n = getNodeRef(pos);
917 if(n.sunlight_propagates())
919 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
921 light_sources.insert(pos_relative + pos, true);
929 bool sunlight_should_go_down = (y==-1);
931 // Fill rest with black (only transparent ones)
935 MapNode &n = getNodeRef(pos);
937 if(n.light_propagates())
939 n.setLight(LIGHTBANK_DAY, 0);
947 If the block below hasn't already been marked invalid:
949 Check if the node below the block has proper sunlight at top.
950 If not, the block below is invalid.
952 Ignore non-transparent nodes as they always have no light
956 if(block_below_is_valid)
958 MapNode n = getNodeParent(v3s16(x, -1, z));
959 if(n.light_propagates())
961 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN
962 && sunlight_should_go_down == false)
963 block_below_is_valid = false;
964 else if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN
965 && sunlight_should_go_down == true)
966 block_below_is_valid = false;
970 catch(InvalidPositionException &e)
972 /*std::cout<<"InvalidBlockException for bottom block node"
974 // Just no block below, no need to panic.
979 return block_below_is_valid;
982 void MapBlock::copyTo(VoxelManipulator &dst)
984 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
985 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
987 dst.copyFrom(data, data_area, v3s16(0,0,0),
988 getPosRelative(), data_size);
991 /*void getPseudoObjects(v3f origin, f32 max_d,
992 core::array<DistanceSortedObject> &dest)
997 void MapBlock::updateDayNightDiff()
1001 m_day_night_differs = false;
1005 bool differs = false;
1008 Check if any lighting value differs
1010 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1012 MapNode &n = data[i];
1013 if(n.getLight(LIGHTBANK_DAY) != n.getLight(LIGHTBANK_NIGHT))
1021 If some lighting values differ, check if the whole thing is
1022 just air. If it is, differ = false
1026 bool only_air = true;
1027 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1029 MapNode &n = data[i];
1030 if(n.d != CONTENT_AIR)
1040 // Set member variable
1041 m_day_night_differs = differs;
1048 void MapBlock::serialize(std::ostream &os, u8 version)
1050 if(!ser_ver_supported(version))
1051 throw VersionMismatchException("ERROR: MapBlock format not supported");
1055 throw SerializationError("ERROR: Not writing dummy block.");
1058 // These have no compression
1059 if(version <= 3 || version == 5 || version == 6)
1061 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1063 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
1064 SharedBuffer<u8> dest(buflen);
1066 dest[0] = is_underground;
1067 for(u32 i=0; i<nodecount; i++)
1069 u32 s = 1 + i * MapNode::serializedLength(version);
1070 data[i].serialize(&dest[s], version);
1073 os.write((char*)*dest, dest.getSize());
1075 else if(version <= 10)
1079 Compress the materials and the params separately.
1083 os.write((char*)&is_underground, 1);
1085 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1087 // Get and compress materials
1088 SharedBuffer<u8> materialdata(nodecount);
1089 for(u32 i=0; i<nodecount; i++)
1091 materialdata[i] = data[i].d;
1093 compress(materialdata, os, version);
1095 // Get and compress lights
1096 SharedBuffer<u8> lightdata(nodecount);
1097 for(u32 i=0; i<nodecount; i++)
1099 lightdata[i] = data[i].param;
1101 compress(lightdata, os, version);
1105 // Get and compress pressure
1106 SharedBuffer<u8> pressuredata(nodecount);
1107 for(u32 i=0; i<nodecount; i++)
1109 pressuredata[i] = data[i].pressure;
1111 compress(pressuredata, os, version);
1114 // All other versions (newest)
1121 if(m_day_night_differs)
1123 os.write((char*)&flags, 1);
1125 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1131 SharedBuffer<u8> databuf(nodecount*3);
1134 for(u32 i=0; i<nodecount; i++)
1136 databuf[i] = data[i].d;
1140 for(u32 i=0; i<nodecount; i++)
1142 databuf[i+nodecount] = data[i].param;
1146 for(u32 i=0; i<nodecount; i++)
1148 databuf[i+nodecount*2] = data[i].pressure;
1152 Compress data to output stream
1155 compress(databuf, os, version);
1159 void MapBlock::deSerialize(std::istream &is, u8 version)
1161 if(!ser_ver_supported(version))
1162 throw VersionMismatchException("ERROR: MapBlock format not supported");
1164 // These have no compression
1165 if(version <= 3 || version == 5 || version == 6)
1167 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1170 if(is.gcount() != 1)
1171 throw SerializationError
1172 ("MapBlock::deSerialize: no enough input data");
1173 is_underground = tmp;
1174 for(u32 i=0; i<nodecount; i++)
1176 s32 len = MapNode::serializedLength(version);
1177 SharedBuffer<u8> d(len);
1178 is.read((char*)*d, len);
1179 if(is.gcount() != len)
1180 throw SerializationError
1181 ("MapBlock::deSerialize: no enough input data");
1182 data[i].deSerialize(*d, version);
1185 else if(version <= 10)
1187 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1190 is.read((char*)&t8, 1);
1191 is_underground = t8;
1194 // Uncompress and set material data
1195 std::ostringstream os(std::ios_base::binary);
1196 decompress(is, os, version);
1197 std::string s = os.str();
1198 if(s.size() != nodecount)
1199 throw SerializationError
1200 ("MapBlock::deSerialize: invalid format");
1201 for(u32 i=0; i<s.size(); i++)
1207 // Uncompress and set param data
1208 std::ostringstream os(std::ios_base::binary);
1209 decompress(is, os, version);
1210 std::string s = os.str();
1211 if(s.size() != nodecount)
1212 throw SerializationError
1213 ("MapBlock::deSerialize: invalid format");
1214 for(u32 i=0; i<s.size(); i++)
1216 data[i].param = s[i];
1222 // Uncompress and set pressure data
1223 std::ostringstream os(std::ios_base::binary);
1224 decompress(is, os, version);
1225 std::string s = os.str();
1226 if(s.size() != nodecount)
1227 throw SerializationError
1228 ("MapBlock::deSerialize: invalid format");
1229 for(u32 i=0; i<s.size(); i++)
1231 data[i].pressure = s[i];
1235 // All other versions (newest)
1238 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1241 is.read((char*)&flags, 1);
1242 is_underground = (flags & 1) ? true : false;
1243 m_day_night_differs = (flags & 2) ? true : false;
1246 std::ostringstream os(std::ios_base::binary);
1247 decompress(is, os, version);
1248 std::string s = os.str();
1249 if(s.size() != nodecount*3)
1250 throw SerializationError
1251 ("MapBlock::deSerialize: invalid format");
1254 for(u32 i=0; i<nodecount; i++)
1259 for(u32 i=0; i<nodecount; i++)
1261 data[i].param = s[i+nodecount];
1264 for(u32 i=0; i<nodecount; i++)
1266 data[i].pressure = s[i+nodecount*2];