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)
309 u8 MapBlock::getNodeContent(v3s16 p, MapNode mn)
312 Check temporary modifications on this node
314 core::map<v3s16, NodeMod>::Node *n;
315 n = m_temp_mods.find(p);
320 struct NodeMod mod = n->getValue();
321 if(mod.type == NODEMOD_CHANGECONTENT)
326 if(mod.type == NODEMOD_CRACK)
329 Content doesn't change.
331 face_contents works just like it should, because
332 there should not be faces between differently cracked
335 If a semi-transparent node is cracked in front an
336 another one, it really doesn't matter whether there
337 is a cracked face drawn in between or not.
347 translate_dir: unit vector with only one of x, y or z
348 face_dir: unit vector with only one of x, y or z
350 void MapBlock::updateFastFaceRow(
359 core::array<FastFace> &dest)
363 u16 continuous_tiles_count = 0;
365 MapNode n0 = getNodeParentNoEx(p);
366 MapNode n1 = getNodeParentNoEx(p + face_dir);
368 u8 light = getFaceLight(daynight_ratio, n0, n1, face_dir);
370 TileSpec tile0 = getNodeTile(n0, p, face_dir);
371 TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir);
373 for(u16 j=0; j<length; j++)
375 bool next_is_different = true;
386 p_next = p + translate_dir;
387 n0_next = getNodeParentNoEx(p_next);
388 n1_next = getNodeParentNoEx(p_next + face_dir);
389 tile0_next = getNodeTile(n0_next, p_next, face_dir);
390 tile1_next = getNodeTile(n1_next, p_next + face_dir, -face_dir);
391 light_next = getFaceLight(daynight_ratio, n0_next, n1_next, face_dir);
393 if(tile0_next == tile0
394 && tile1_next == tile1
395 && light_next == light)
397 next_is_different = false;
401 continuous_tiles_count++;
403 if(next_is_different)
406 Create a face if there should be one
408 //u8 mf = face_contents(tile0, tile1);
410 u8 content0 = getNodeContent(p, n0);
411 u8 content1 = getNodeContent(p + face_dir, n1);
412 u8 mf = face_contents(content0, content1);
416 // Floating point conversion of the position vector
417 v3f pf(p.X, p.Y, p.Z);
418 // Center point of face (kind of)
419 v3f sp = pf - ((f32)continuous_tiles_count / 2. - 0.5) * translate_dir_f;
421 if(translate_dir.X != 0){
422 scale.X = continuous_tiles_count;
424 if(translate_dir.Y != 0){
425 scale.Y = continuous_tiles_count;
427 if(translate_dir.Z != 0){
428 scale.Z = continuous_tiles_count;
433 // If node at sp (tile0) is more solid
436 makeFastFace(tile0, light,
438 posRelative_f, dest);
440 // If node at sp is less solid (mf == 2)
443 makeFastFace(tile1, light,
444 sp+face_dir_f, -face_dir, scale,
445 posRelative_f, dest);
450 continuous_tiles_count = 0;
463 This is used because CMeshBuffer::append() is very slow
467 video::SMaterial material;
468 core::array<u16> indices;
469 core::array<video::S3DVertex> vertices;
476 video::SMaterial material,
477 const video::S3DVertex* const vertices,
479 const u16* const indices,
483 PreMeshBuffer *p = NULL;
484 for(u32 i=0; i<m_prebuffers.size(); i++)
486 PreMeshBuffer &pp = m_prebuffers[i];
487 if(pp.material != material)
497 pp.material = material;
498 m_prebuffers.push_back(pp);
499 p = &m_prebuffers[m_prebuffers.size()-1];
502 u32 vertex_count = p->vertices.size();
503 for(u32 i=0; i<numIndices; i++)
505 u32 j = indices[i] + vertex_count;
508 dstream<<"FIXME: Meshbuffer ran out of indices"<<std::endl;
509 // NOTE: Fix is to just add an another MeshBuffer
511 p->indices.push_back(j);
513 for(u32 i=0; i<numVertices; i++)
515 p->vertices.push_back(vertices[i]);
519 void fillMesh(scene::SMesh *mesh)
521 /*dstream<<"Filling mesh with "<<m_prebuffers.size()
522 <<" meshbuffers"<<std::endl;*/
523 for(u32 i=0; i<m_prebuffers.size(); i++)
525 PreMeshBuffer &p = m_prebuffers[i];
527 /*dstream<<"p.vertices.size()="<<p.vertices.size()
528 <<", p.indices.size()="<<p.indices.size()
533 // This is a "Standard MeshBuffer",
534 // it's a typedeffed CMeshBuffer<video::S3DVertex>
535 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
537 buf->Material = p.material;
538 //((scene::SMeshBuffer*)buf)->Material = p.material;
540 //buf->setHardwareMappingHint(scene::EHM_STATIC);
542 mesh->addMeshBuffer(buf);
546 buf->append(p.vertices.pointer(), p.vertices.size(),
547 p.indices.pointer(), p.indices.size());
552 core::array<PreMeshBuffer> m_prebuffers;
555 void MapBlock::updateMesh(u32 daynight_ratio)
559 DEBUG: If mesh has been generated, don't generate it again
562 JMutexAutoLock meshlock(mesh_mutex);
569 //TimeTaker timer1("updateMesh()", g_device);
571 core::array<FastFace> fastfaces_new;
573 v3f posRelative_f(getPosRelative().X, getPosRelative().Y,
574 getPosRelative().Z); // floating point conversion
577 We are including the faces of the trailing edges of the block.
578 This means that when something changes, the caller must
579 also update the meshes of the blocks at the leading edges.
581 NOTE: This is the slowest part of this method.
585 Go through every y,z and get top faces in rows of x+
587 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
588 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
589 updateFastFaceRow(daynight_ratio, posRelative_f,
590 v3s16(0,y,z), MAP_BLOCKSIZE,
593 v3s16(0,1,0), //face dir
599 Go through every x,y and get right faces in rows of z+
601 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
602 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
603 updateFastFaceRow(daynight_ratio, posRelative_f,
604 v3s16(x,y,0), MAP_BLOCKSIZE,
613 Go through every y,z and get back faces in rows of x+
615 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
616 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
617 updateFastFaceRow(daynight_ratio, posRelative_f,
618 v3s16(0,y,z), MAP_BLOCKSIZE,
630 Convert FastFaces to SMesh
633 scene::SMesh *mesh_new = NULL;
635 mesh_new = new scene::SMesh();
637 if(fastfaces_new.size() > 0)
639 MeshCollector collector;
641 for(u32 i=0; i<fastfaces_new.size(); i++)
643 FastFace &f = fastfaces_new[i];
645 const u16 indices[] = {0,1,2,2,3,0};
647 if(f.tile.feature == TILEFEAT_NONE)
649 /*collector.append(g_tile_materials[f.tile.id], f.vertices, 4,
651 collector.append(tile_material_get(f.tile.id), f.vertices, 4,
661 collector.fillMesh(mesh_new);
663 // Use VBO for mesh (this just would set this for ever buffer)
664 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
666 /*std::cout<<"MapBlock has "<<fastfaces_new->getSize()<<" faces "
667 <<"and uses "<<mesh_new->getMeshBufferCount()
668 <<" materials (meshbuffers)"<<std::endl;*/
672 Clear temporary FastFaces
675 /*core::list<FastFace*>::Iterator i;
676 i = fastfaces_new->begin();
677 for(; i != fastfaces_new->end(); i++)
681 fastfaces_new->clear();
682 delete fastfaces_new;*/
685 Add special graphics:
688 TODO: Optimize by using same meshbuffer for same textures
691 /*scene::ISceneManager *smgr = NULL;
692 video::IVideoDriver* driver = NULL;
695 smgr = g_device->getSceneManager();
696 driver = smgr->getVideoDriver();
699 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
700 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
701 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
705 MapNode &n = getNodeRef(x,y,z);
707 if(n.d == CONTENT_TORCH)
709 //scene::IMeshBuffer *buf = new scene::SMeshBuffer();
710 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
711 video::SColor c(255,255,255,255);
713 video::S3DVertex vertices[4] =
715 video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c, 0,1),
716 video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c, 1,1),
717 video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, 1,0),
718 video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c, 0,0),
721 v3s16 dir = unpackDir(n.dir);
723 for(s32 i=0; i<4; i++)
725 if(dir == v3s16(1,0,0))
726 vertices[i].Pos.rotateXZBy(0);
727 if(dir == v3s16(-1,0,0))
728 vertices[i].Pos.rotateXZBy(180);
729 if(dir == v3s16(0,0,1))
730 vertices[i].Pos.rotateXZBy(90);
731 if(dir == v3s16(0,0,-1))
732 vertices[i].Pos.rotateXZBy(-90);
733 if(dir == v3s16(0,-1,0))
734 vertices[i].Pos.rotateXZBy(45);
735 if(dir == v3s16(0,1,0))
736 vertices[i].Pos.rotateXZBy(-45);
738 vertices[i].Pos += intToFloat(p + getPosRelative());
741 u16 indices[] = {0,1,2,2,3,0};
742 buf->append(vertices, 4, indices, 6);
745 buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
746 buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false);
747 buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
748 //buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
749 buf->getMaterial().MaterialType
750 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
751 if(dir == v3s16(0,-1,0))
752 buf->getMaterial().setTexture(0,
753 g_irrlicht->getTexture("../data/torch_on_floor.png"));
754 //g_texturecache.get("torch_on_floor"));
755 else if(dir == v3s16(0,1,0))
756 buf->getMaterial().setTexture(0,
757 g_irrlicht->getTexture("../data/torch_on_ceiling.png"));
758 //g_texturecache.get("torch_on_ceiling"));
759 // For backwards compatibility
760 else if(dir == v3s16(0,0,0))
761 buf->getMaterial().setTexture(0,
762 g_irrlicht->getTexture("../data/torch_on_floor.png"));
763 //g_texturecache.get("torch_on_floor"));
765 buf->getMaterial().setTexture(0,
766 g_irrlicht->getTexture("../data/torch.png"));
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++)
850 Propagates sunlight down through the block.
851 Doesn't modify nodes that are not affected by sunlight.
853 Returns false if sunlight at bottom block is invalid
854 Returns true if bottom block doesn't exist.
856 If there is a block above, continues from it.
857 If there is no block above, assumes there is sunlight, unless
858 is_underground is set.
860 At the moment, all sunlighted nodes are added to light_sources.
861 TODO: This could be optimized.
863 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources)
865 // Whether the sunlight at the top of the bottom block is valid
866 bool block_below_is_valid = true;
868 v3s16 pos_relative = getPosRelative();
870 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
872 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
874 bool no_sunlight = false;
875 bool no_top_block = false;
876 // Check if node above block has sunlight
878 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
879 if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
888 catch(InvalidPositionException &e)
892 // TODO: This makes over-ground roofed places sunlighted
893 // Assume sunlight, unless is_underground==true
899 // TODO: There has to be some way to allow this behaviour
900 // As of now, it just makes everything dark.
902 //no_sunlight = true;
905 /*std::cout<<"("<<x<<","<<z<<"): "
906 <<"no_top_block="<<no_top_block
907 <<", is_underground="<<is_underground
908 <<", no_sunlight="<<no_sunlight
911 s16 y = MAP_BLOCKSIZE-1;
913 if(no_sunlight == false)
915 // Continue spreading sunlight downwards through transparent
921 MapNode &n = getNodeRef(pos);
923 if(n.sunlight_propagates())
925 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
927 light_sources.insert(pos_relative + pos, true);
935 bool sunlight_should_go_down = (y==-1);
937 // Fill rest with black (only transparent ones)
941 MapNode &n = getNodeRef(pos);
943 if(n.light_propagates())
945 n.setLight(LIGHTBANK_DAY, 0);
953 If the block below hasn't already been marked invalid:
955 Check if the node below the block has proper sunlight at top.
956 If not, the block below is invalid.
958 Ignore non-transparent nodes as they always have no light
962 if(block_below_is_valid)
964 MapNode n = getNodeParent(v3s16(x, -1, z));
965 if(n.light_propagates())
967 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN
968 && sunlight_should_go_down == false)
969 block_below_is_valid = false;
970 else if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN
971 && sunlight_should_go_down == true)
972 block_below_is_valid = false;
976 catch(InvalidPositionException &e)
978 /*std::cout<<"InvalidBlockException for bottom block node"
980 // Just no block below, no need to panic.
985 return block_below_is_valid;
988 void MapBlock::copyTo(VoxelManipulator &dst)
990 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
991 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
993 dst.copyFrom(data, data_area, v3s16(0,0,0),
994 getPosRelative(), data_size);
997 /*void getPseudoObjects(v3f origin, f32 max_d,
998 core::array<DistanceSortedObject> &dest)
1003 void MapBlock::updateDayNightDiff()
1007 m_day_night_differs = false;
1011 bool differs = false;
1014 Check if any lighting value differs
1016 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1018 MapNode &n = data[i];
1019 if(n.getLight(LIGHTBANK_DAY) != n.getLight(LIGHTBANK_NIGHT))
1027 If some lighting values differ, check if the whole thing is
1028 just air. If it is, differ = false
1032 bool only_air = true;
1033 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1035 MapNode &n = data[i];
1036 if(n.d != CONTENT_AIR)
1046 // Set member variable
1047 m_day_night_differs = differs;
1054 void MapBlock::serialize(std::ostream &os, u8 version)
1056 if(!ser_ver_supported(version))
1057 throw VersionMismatchException("ERROR: MapBlock format not supported");
1061 throw SerializationError("ERROR: Not writing dummy block.");
1064 // These have no compression
1065 if(version <= 3 || version == 5 || version == 6)
1067 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1069 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
1070 SharedBuffer<u8> dest(buflen);
1072 dest[0] = is_underground;
1073 for(u32 i=0; i<nodecount; i++)
1075 u32 s = 1 + i * MapNode::serializedLength(version);
1076 data[i].serialize(&dest[s], version);
1079 os.write((char*)*dest, dest.getSize());
1081 else if(version <= 10)
1085 Compress the materials and the params separately.
1089 os.write((char*)&is_underground, 1);
1091 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1093 // Get and compress materials
1094 SharedBuffer<u8> materialdata(nodecount);
1095 for(u32 i=0; i<nodecount; i++)
1097 materialdata[i] = data[i].d;
1099 compress(materialdata, os, version);
1101 // Get and compress lights
1102 SharedBuffer<u8> lightdata(nodecount);
1103 for(u32 i=0; i<nodecount; i++)
1105 lightdata[i] = data[i].param;
1107 compress(lightdata, os, version);
1111 // Get and compress pressure
1112 SharedBuffer<u8> pressuredata(nodecount);
1113 for(u32 i=0; i<nodecount; i++)
1115 pressuredata[i] = data[i].pressure;
1117 compress(pressuredata, os, version);
1120 // All other versions (newest)
1127 if(m_day_night_differs)
1129 os.write((char*)&flags, 1);
1131 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1137 SharedBuffer<u8> databuf(nodecount*3);
1140 for(u32 i=0; i<nodecount; i++)
1142 databuf[i] = data[i].d;
1146 for(u32 i=0; i<nodecount; i++)
1148 databuf[i+nodecount] = data[i].param;
1152 for(u32 i=0; i<nodecount; i++)
1154 databuf[i+nodecount*2] = data[i].pressure;
1158 Compress data to output stream
1161 compress(databuf, os, version);
1165 void MapBlock::deSerialize(std::istream &is, u8 version)
1167 if(!ser_ver_supported(version))
1168 throw VersionMismatchException("ERROR: MapBlock format not supported");
1170 // These have no compression
1171 if(version <= 3 || version == 5 || version == 6)
1173 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1176 if(is.gcount() != 1)
1177 throw SerializationError
1178 ("MapBlock::deSerialize: no enough input data");
1179 is_underground = tmp;
1180 for(u32 i=0; i<nodecount; i++)
1182 s32 len = MapNode::serializedLength(version);
1183 SharedBuffer<u8> d(len);
1184 is.read((char*)*d, len);
1185 if(is.gcount() != len)
1186 throw SerializationError
1187 ("MapBlock::deSerialize: no enough input data");
1188 data[i].deSerialize(*d, version);
1191 else if(version <= 10)
1193 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1196 is.read((char*)&t8, 1);
1197 is_underground = t8;
1200 // Uncompress and set material data
1201 std::ostringstream os(std::ios_base::binary);
1202 decompress(is, os, version);
1203 std::string s = os.str();
1204 if(s.size() != nodecount)
1205 throw SerializationError
1206 ("MapBlock::deSerialize: invalid format");
1207 for(u32 i=0; i<s.size(); i++)
1213 // Uncompress and set param data
1214 std::ostringstream os(std::ios_base::binary);
1215 decompress(is, os, version);
1216 std::string s = os.str();
1217 if(s.size() != nodecount)
1218 throw SerializationError
1219 ("MapBlock::deSerialize: invalid format");
1220 for(u32 i=0; i<s.size(); i++)
1222 data[i].param = s[i];
1228 // Uncompress and set pressure data
1229 std::ostringstream os(std::ios_base::binary);
1230 decompress(is, os, version);
1231 std::string s = os.str();
1232 if(s.size() != nodecount)
1233 throw SerializationError
1234 ("MapBlock::deSerialize: invalid format");
1235 for(u32 i=0; i<s.size(); i++)
1237 data[i].pressure = s[i];
1241 // All other versions (newest)
1244 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1247 is.read((char*)&flags, 1);
1248 is_underground = (flags & 1) ? true : false;
1249 m_day_night_differs = (flags & 2) ? true : false;
1252 std::ostringstream os(std::ios_base::binary);
1253 decompress(is, os, version);
1254 std::string s = os.str();
1255 if(s.size() != nodecount*3)
1256 throw SerializationError
1257 ("MapBlock::deSerialize: invalid format");
1260 for(u32 i=0; i<nodecount; i++)
1265 for(u32 i=0; i<nodecount; i++)
1267 data[i].param = s[i+nodecount];
1270 for(u32 i=0; i<nodecount; i++)
1272 data[i].pressure = s[i+nodecount*2];