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,
659 collector.fillMesh(mesh_new);
661 // Use VBO for mesh (this just would set this for ever buffer)
662 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
664 /*std::cout<<"MapBlock has "<<fastfaces_new->getSize()<<" faces "
665 <<"and uses "<<mesh_new->getMeshBufferCount()
666 <<" materials (meshbuffers)"<<std::endl;*/
670 Clear temporary FastFaces
673 /*core::list<FastFace*>::Iterator i;
674 i = fastfaces_new->begin();
675 for(; i != fastfaces_new->end(); i++)
679 fastfaces_new->clear();
680 delete fastfaces_new;*/
683 Add special graphics:
686 TODO: Optimize by using same meshbuffer for same textures
689 /*scene::ISceneManager *smgr = NULL;
690 video::IVideoDriver* driver = NULL;
693 smgr = g_device->getSceneManager();
694 driver = smgr->getVideoDriver();
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_texturecache.get("torch_on_floor"));
752 else if(dir == v3s16(0,1,0))
753 buf->getMaterial().setTexture(0,
754 g_texturecache.get("torch_on_ceiling"));
755 // For backwards compatibility
756 else if(dir == v3s16(0,0,0))
757 buf->getMaterial().setTexture(0,
758 g_texturecache.get("torch_on_floor"));
760 buf->getMaterial().setTexture(0, g_texturecache.get("torch"));
763 mesh_new->addMeshBuffer(buf);
769 Do some stuff to the mesh
772 mesh_new->recalculateBoundingBox();
775 Delete new mesh if it is empty
778 if(mesh_new->getMeshBufferCount() == 0)
790 //scene::SMesh *mesh_old = mesh[daynight_i];
791 //mesh[daynight_i] = mesh_new;
793 scene::SMesh *mesh_old = mesh;
795 setMeshExpired(false);
799 // Remove hardware buffers of meshbuffers of mesh
800 // NOTE: No way, this runs in a different thread and everything
801 /*u32 c = mesh_old->getMeshBufferCount();
802 for(u32 i=0; i<c; i++)
804 IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
807 /*dstream<<"mesh_old->getReferenceCount()="
808 <<mesh_old->getReferenceCount()<<std::endl;
809 u32 c = mesh_old->getMeshBufferCount();
810 for(u32 i=0; i<c; i++)
812 scene::IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
813 dstream<<"buf->getReferenceCount()="
814 <<buf->getReferenceCount()<<std::endl;
825 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
828 /*void MapBlock::updateMeshes(s32 first_i)
830 assert(first_i >= 0 && first_i <= DAYNIGHT_CACHE_COUNT);
832 for(s32 i=0; i<DAYNIGHT_CACHE_COUNT; i++)
843 Propagates sunlight down through the block.
844 Doesn't modify nodes that are not affected by sunlight.
846 Returns false if sunlight at bottom block is invalid
847 Returns true if bottom block doesn't exist.
849 If there is a block above, continues from it.
850 If there is no block above, assumes there is sunlight, unless
851 is_underground is set.
853 At the moment, all sunlighted nodes are added to light_sources.
854 TODO: This could be optimized.
856 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources)
858 // Whether the sunlight at the top of the bottom block is valid
859 bool block_below_is_valid = true;
861 v3s16 pos_relative = getPosRelative();
863 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
865 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
867 bool no_sunlight = false;
868 bool no_top_block = false;
869 // Check if node above block has sunlight
871 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
872 if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
881 catch(InvalidPositionException &e)
885 // TODO: This makes over-ground roofed places sunlighted
886 // Assume sunlight, unless is_underground==true
892 // TODO: There has to be some way to allow this behaviour
893 // As of now, it just makes everything dark.
895 //no_sunlight = true;
898 /*std::cout<<"("<<x<<","<<z<<"): "
899 <<"no_top_block="<<no_top_block
900 <<", is_underground="<<is_underground
901 <<", no_sunlight="<<no_sunlight
904 s16 y = MAP_BLOCKSIZE-1;
906 if(no_sunlight == false)
908 // Continue spreading sunlight downwards through transparent
914 MapNode &n = getNodeRef(pos);
916 if(n.sunlight_propagates())
918 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
920 light_sources.insert(pos_relative + pos, true);
928 bool sunlight_should_go_down = (y==-1);
930 // Fill rest with black (only transparent ones)
934 MapNode &n = getNodeRef(pos);
936 if(n.light_propagates())
938 n.setLight(LIGHTBANK_DAY, 0);
946 If the block below hasn't already been marked invalid:
948 Check if the node below the block has proper sunlight at top.
949 If not, the block below is invalid.
951 Ignore non-transparent nodes as they always have no light
955 if(block_below_is_valid)
957 MapNode n = getNodeParent(v3s16(x, -1, z));
958 if(n.light_propagates())
960 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN
961 && sunlight_should_go_down == false)
962 block_below_is_valid = false;
963 else if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN
964 && sunlight_should_go_down == true)
965 block_below_is_valid = false;
969 catch(InvalidPositionException &e)
971 /*std::cout<<"InvalidBlockException for bottom block node"
973 // Just no block below, no need to panic.
978 return block_below_is_valid;
981 void MapBlock::copyTo(VoxelManipulator &dst)
983 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
984 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
986 dst.copyFrom(data, data_area, v3s16(0,0,0),
987 getPosRelative(), data_size);
990 /*void getPseudoObjects(v3f origin, f32 max_d,
991 core::array<DistanceSortedObject> &dest)
996 void MapBlock::updateDayNightDiff()
1000 m_day_night_differs = false;
1004 bool differs = false;
1007 Check if any lighting value differs
1009 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1011 MapNode &n = data[i];
1012 if(n.getLight(LIGHTBANK_DAY) != n.getLight(LIGHTBANK_NIGHT))
1020 If some lighting values differ, check if the whole thing is
1021 just air. If it is, differ = false
1025 bool only_air = true;
1026 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1028 MapNode &n = data[i];
1029 if(n.d != CONTENT_AIR)
1039 // Set member variable
1040 m_day_night_differs = differs;
1047 void MapBlock::serialize(std::ostream &os, u8 version)
1049 if(!ser_ver_supported(version))
1050 throw VersionMismatchException("ERROR: MapBlock format not supported");
1054 throw SerializationError("ERROR: Not writing dummy block.");
1057 // These have no compression
1058 if(version <= 3 || version == 5 || version == 6)
1060 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1062 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
1063 SharedBuffer<u8> dest(buflen);
1065 dest[0] = is_underground;
1066 for(u32 i=0; i<nodecount; i++)
1068 u32 s = 1 + i * MapNode::serializedLength(version);
1069 data[i].serialize(&dest[s], version);
1072 os.write((char*)*dest, dest.getSize());
1074 else if(version <= 10)
1078 Compress the materials and the params separately.
1082 os.write((char*)&is_underground, 1);
1084 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1086 // Get and compress materials
1087 SharedBuffer<u8> materialdata(nodecount);
1088 for(u32 i=0; i<nodecount; i++)
1090 materialdata[i] = data[i].d;
1092 compress(materialdata, os, version);
1094 // Get and compress lights
1095 SharedBuffer<u8> lightdata(nodecount);
1096 for(u32 i=0; i<nodecount; i++)
1098 lightdata[i] = data[i].param;
1100 compress(lightdata, os, version);
1104 // Get and compress pressure
1105 SharedBuffer<u8> pressuredata(nodecount);
1106 for(u32 i=0; i<nodecount; i++)
1108 pressuredata[i] = data[i].pressure;
1110 compress(pressuredata, os, version);
1113 // All other versions (newest)
1120 if(m_day_night_differs)
1122 os.write((char*)&flags, 1);
1124 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1130 SharedBuffer<u8> databuf(nodecount*3);
1133 for(u32 i=0; i<nodecount; i++)
1135 databuf[i] = data[i].d;
1139 for(u32 i=0; i<nodecount; i++)
1141 databuf[i+nodecount] = data[i].param;
1145 for(u32 i=0; i<nodecount; i++)
1147 databuf[i+nodecount*2] = data[i].pressure;
1151 Compress data to output stream
1154 compress(databuf, os, version);
1158 void MapBlock::deSerialize(std::istream &is, u8 version)
1160 if(!ser_ver_supported(version))
1161 throw VersionMismatchException("ERROR: MapBlock format not supported");
1163 // These have no compression
1164 if(version <= 3 || version == 5 || version == 6)
1166 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1169 if(is.gcount() != 1)
1170 throw SerializationError
1171 ("MapBlock::deSerialize: no enough input data");
1172 is_underground = tmp;
1173 for(u32 i=0; i<nodecount; i++)
1175 s32 len = MapNode::serializedLength(version);
1176 SharedBuffer<u8> d(len);
1177 is.read((char*)*d, len);
1178 if(is.gcount() != len)
1179 throw SerializationError
1180 ("MapBlock::deSerialize: no enough input data");
1181 data[i].deSerialize(*d, version);
1184 else if(version <= 10)
1186 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1189 is.read((char*)&t8, 1);
1190 is_underground = t8;
1193 // Uncompress and set material data
1194 std::ostringstream os(std::ios_base::binary);
1195 decompress(is, os, version);
1196 std::string s = os.str();
1197 if(s.size() != nodecount)
1198 throw SerializationError
1199 ("MapBlock::deSerialize: invalid format");
1200 for(u32 i=0; i<s.size(); i++)
1206 // Uncompress and set param data
1207 std::ostringstream os(std::ios_base::binary);
1208 decompress(is, os, version);
1209 std::string s = os.str();
1210 if(s.size() != nodecount)
1211 throw SerializationError
1212 ("MapBlock::deSerialize: invalid format");
1213 for(u32 i=0; i<s.size(); i++)
1215 data[i].param = s[i];
1221 // Uncompress and set pressure data
1222 std::ostringstream os(std::ios_base::binary);
1223 decompress(is, os, version);
1224 std::string s = os.str();
1225 if(s.size() != nodecount)
1226 throw SerializationError
1227 ("MapBlock::deSerialize: invalid format");
1228 for(u32 i=0; i<s.size(); i++)
1230 data[i].pressure = s[i];
1234 // All other versions (newest)
1237 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1240 is.read((char*)&flags, 1);
1241 is_underground = (flags & 1) ? true : false;
1242 m_day_night_differs = (flags & 2) ? true : false;
1245 std::ostringstream os(std::ios_base::binary);
1246 decompress(is, os, version);
1247 std::string s = os.str();
1248 if(s.size() != nodecount*3)
1249 throw SerializationError
1250 ("MapBlock::deSerialize: invalid format");
1253 for(u32 i=0; i<nodecount; i++)
1258 for(u32 i=0; i<nodecount; i++)
1260 data[i].param = s[i+nodecount];
1263 for(u32 i=0; i<nodecount; i++)
1265 data[i].pressure = s[i+nodecount*2];