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.
22 // For g_settings and g_irrlicht
32 MapBlock::MapBlock(NodeContainer *parent, v3s16 pos, bool dummy):
36 is_underground(false),
37 m_lighting_expired(true),
38 m_day_night_differs(false),
39 m_not_fully_generated(false),
46 m_spawn_timer = -10000;
49 m_mesh_expired = false;
52 m_temp_mods_mutex.Init();
60 JMutexAutoLock lock(mesh_mutex);
74 bool MapBlock::isValidPositionParent(v3s16 p)
76 if(isValidPosition(p))
81 return m_parent->isValidPosition(getPosRelative() + p);
85 MapNode MapBlock::getNodeParent(v3s16 p)
87 if(isValidPosition(p) == false)
89 return m_parent->getNode(getPosRelative() + p);
94 throw InvalidPositionException();
95 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
99 void MapBlock::setNodeParent(v3s16 p, MapNode & n)
101 if(isValidPosition(p) == false)
103 m_parent->setNode(getPosRelative() + p, n);
108 throw InvalidPositionException();
109 data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X] = n;
113 MapNode MapBlock::getNodeParentNoEx(v3s16 p)
115 if(isValidPosition(p) == false)
118 return m_parent->getNode(getPosRelative() + p);
120 catch(InvalidPositionException &e)
122 return MapNode(CONTENT_IGNORE);
129 return MapNode(CONTENT_IGNORE);
131 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
136 Parameters must consist of air and !air.
137 Order doesn't matter.
139 If either of the nodes doesn't exist, light is 0.
142 daynight_ratio: 0...1000
144 n2: getNodeParent(p + face_dir)
145 face_dir: axis oriented unit vector from p to p2
147 returns encoded light value.
149 u8 MapBlock::getFaceLight(u32 daynight_ratio, MapNode n, MapNode n2,
154 u8 l1 = n.getLightBlend(daynight_ratio);
155 u8 l2 = n2.getLightBlend(daynight_ratio);
161 // Make some nice difference to different sides
163 // This makes light come from a corner
164 /*if(face_dir.X == 1 || face_dir.Z == 1 || face_dir.Y == -1)
165 light = diminish_light(diminish_light(light));
166 else if(face_dir.X == -1 || face_dir.Z == -1)
167 light = diminish_light(light);*/
169 // All neighboring faces have different shade (like in minecraft)
170 if(face_dir.X == 1 || face_dir.X == -1 || face_dir.Y == -1)
171 light = diminish_light(diminish_light(light));
172 else if(face_dir.Z == 1 || face_dir.Z == -1)
173 light = diminish_light(light);
177 catch(InvalidPositionException &e)
185 void MapBlock::makeFastFace(TileSpec tile, u8 light, v3f p,
186 v3s16 dir, v3f scale, v3f posRelative_f,
187 core::array<FastFace> &dest)
191 // Position is at the center of the cube.
196 // If looking towards z+, this is the face that is behind
197 // the center point, facing towards z+.
198 vertex_pos[0] = v3f(-BS/2,-BS/2,BS/2);
199 vertex_pos[1] = v3f( BS/2,-BS/2,BS/2);
200 vertex_pos[2] = v3f( BS/2, BS/2,BS/2);
201 vertex_pos[3] = v3f(-BS/2, BS/2,BS/2);
203 if(dir == v3s16(0,0,1))
205 for(u16 i=0; i<4; i++)
206 vertex_pos[i].rotateXZBy(0);
208 else if(dir == v3s16(0,0,-1))
210 for(u16 i=0; i<4; i++)
211 vertex_pos[i].rotateXZBy(180);
213 else if(dir == v3s16(1,0,0))
215 for(u16 i=0; i<4; i++)
216 vertex_pos[i].rotateXZBy(-90);
218 else if(dir == v3s16(-1,0,0))
220 for(u16 i=0; i<4; i++)
221 vertex_pos[i].rotateXZBy(90);
223 else if(dir == v3s16(0,1,0))
225 for(u16 i=0; i<4; i++)
226 vertex_pos[i].rotateYZBy(-90);
228 else if(dir == v3s16(0,-1,0))
230 for(u16 i=0; i<4; i++)
231 vertex_pos[i].rotateYZBy(90);
234 for(u16 i=0; i<4; i++)
236 vertex_pos[i].X *= scale.X;
237 vertex_pos[i].Y *= scale.Y;
238 vertex_pos[i].Z *= scale.Z;
239 vertex_pos[i] += pos + posRelative_f;
243 if (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X;
244 else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
245 else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
247 v3f zerovector = v3f(0,0,0);
249 //u8 li = decode_light(light);
251 //u8 li = 255; //DEBUG
253 u8 alpha = tile.alpha;
255 if(tile.id == TILE_WATER)
256 alpha = WATER_ALPHA;*/
258 video::SColor c = video::SColor(alpha,li,li,li);
260 float x0 = tile.texture.pos.X;
261 float y0 = tile.texture.pos.Y;
262 float w = tile.texture.size.X;
263 float h = tile.texture.size.Y;
265 face.vertices[0] = video::S3DVertex(vertex_pos[0], v3f(0,1,0), c,
266 core::vector2d<f32>(x0+w*abs_scale, y0+h));
267 face.vertices[1] = video::S3DVertex(vertex_pos[1], v3f(0,1,0), c,
268 core::vector2d<f32>(x0, y0+h));
269 face.vertices[2] = video::S3DVertex(vertex_pos[2], v3f(0,1,0), c,
270 core::vector2d<f32>(x0, y0));
271 face.vertices[3] = video::S3DVertex(vertex_pos[3], v3f(0,1,0), c,
272 core::vector2d<f32>(x0+w*abs_scale, y0));
276 //f->tile = TILE_STONE;
278 dest.push_back(face);
283 Gets node tile from any place relative to block.
284 Returns TILE_NODE if doesn't exist or should not be drawn.
286 TileSpec MapBlock::getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir,
287 NodeModMap &temp_mods)
290 spec = mn.getTile(face_dir);
293 Check temporary modifications on this node
295 /*core::map<v3s16, NodeMod>::Node *n;
296 n = m_temp_mods.find(p);
300 struct NodeMod mod = n->getValue();*/
302 if(temp_mods.get(p, &mod))
304 if(mod.type == NODEMOD_CHANGECONTENT)
306 MapNode mn2(mod.param);
307 spec = mn2.getTile(face_dir);
309 if(mod.type == NODEMOD_CRACK)
312 Get texture id, translate it to name, append stuff to
316 // Get original texture name
317 u32 orig_id = spec.texture.id;
318 std::string orig_name = g_texturesource->getTextureName(orig_id);
320 // Create new texture name
321 std::ostringstream os;
322 os<<orig_name<<"^[crack"<<mod.param;
325 u32 new_id = g_texturesource->getTextureId(os.str());
327 /*dstream<<"MapBlock::getNodeTile(): Switching from "
328 <<orig_name<<" to "<<os.str()<<" ("
329 <<orig_id<<" to "<<new_id<<")"<<std::endl;*/
331 spec.texture = g_texturesource->getTexture(new_id);
338 u8 MapBlock::getNodeContent(v3s16 p, MapNode mn, NodeModMap &temp_mods)
341 Check temporary modifications on this node
343 /*core::map<v3s16, NodeMod>::Node *n;
344 n = m_temp_mods.find(p);
348 struct NodeMod mod = n->getValue();*/
350 if(temp_mods.get(p, &mod))
352 if(mod.type == NODEMOD_CHANGECONTENT)
357 if(mod.type == NODEMOD_CRACK)
360 Content doesn't change.
362 face_contents works just like it should, because
363 there should not be faces between differently cracked
366 If a semi-transparent node is cracked in front an
367 another one, it really doesn't matter whether there
368 is a cracked face drawn in between or not.
378 translate_dir: unit vector with only one of x, y or z
379 face_dir: unit vector with only one of x, y or z
381 void MapBlock::updateFastFaceRow(
390 core::array<FastFace> &dest,
391 NodeModMap &temp_mods)
395 u16 continuous_tiles_count = 0;
397 MapNode n0 = getNodeParentNoEx(p);
398 MapNode n1 = getNodeParentNoEx(p + face_dir);
400 u8 light = getFaceLight(daynight_ratio, n0, n1, face_dir);
402 TileSpec tile0 = getNodeTile(n0, p, face_dir, temp_mods);
403 TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir, temp_mods);
405 for(u16 j=0; j<length; j++)
407 bool next_is_different = true;
416 // If at last position, there is nothing to compare to and
417 // the face must be drawn anyway
420 p_next = p + translate_dir;
421 n0_next = getNodeParentNoEx(p_next);
422 n1_next = getNodeParentNoEx(p_next + face_dir);
423 tile0_next = getNodeTile(n0_next, p_next, face_dir, temp_mods);
424 tile1_next = getNodeTile(n1_next,p_next+face_dir,-face_dir, temp_mods);
425 light_next = getFaceLight(daynight_ratio, n0_next, n1_next, face_dir);
427 if(tile0_next == tile0
428 && tile1_next == tile1
429 && light_next == light)
431 next_is_different = false;
435 continuous_tiles_count++;
437 // This is set to true if the texture doesn't allow more tiling
438 bool end_of_texture = false;
440 If there is no texture, it can be tiled infinitely.
441 If tiled==0, it means the texture can be tiled infinitely.
442 Otherwise check tiled agains continuous_tiles_count.
444 This check has to be made for both tiles, because this is
445 a bit hackish and we know which one we're using only when
446 the decision to make the faces is made.
448 if(tile0.texture.atlas != NULL && tile0.texture.tiled != 0)
450 if(tile0.texture.tiled <= continuous_tiles_count)
451 end_of_texture = true;
453 if(tile1.texture.atlas != NULL && tile1.texture.tiled != 0)
455 if(tile1.texture.tiled <= continuous_tiles_count)
456 end_of_texture = true;
459 //end_of_texture = true; //DEBUG
461 if(next_is_different || end_of_texture)
464 Create a face if there should be one
466 //u8 mf = face_contents(tile0, tile1);
468 u8 content0 = getNodeContent(p, n0, temp_mods);
469 u8 content1 = getNodeContent(p + face_dir, n1, temp_mods);
470 u8 mf = face_contents(content0, content1);
474 // Floating point conversion of the position vector
475 v3f pf(p.X, p.Y, p.Z);
476 // Center point of face (kind of)
477 v3f sp = pf - ((f32)continuous_tiles_count / 2. - 0.5) * translate_dir_f;
479 if(translate_dir.X != 0){
480 scale.X = continuous_tiles_count;
482 if(translate_dir.Y != 0){
483 scale.Y = continuous_tiles_count;
485 if(translate_dir.Z != 0){
486 scale.Z = continuous_tiles_count;
491 // If node at sp (tile0) is more solid
494 makeFastFace(tile0, decode_light(light),
496 posRelative_f, dest);
498 // If node at sp is less solid (mf == 2)
501 makeFastFace(tile1, decode_light(light),
502 sp+face_dir_f, -face_dir, scale,
503 posRelative_f, dest);
508 continuous_tiles_count = 0;
521 This is used because CMeshBuffer::append() is very slow
525 video::SMaterial material;
526 core::array<u16> indices;
527 core::array<video::S3DVertex> vertices;
534 video::SMaterial material,
535 const video::S3DVertex* const vertices,
537 const u16* const indices,
541 PreMeshBuffer *p = NULL;
542 for(u32 i=0; i<m_prebuffers.size(); i++)
544 PreMeshBuffer &pp = m_prebuffers[i];
545 if(pp.material != material)
555 pp.material = material;
556 m_prebuffers.push_back(pp);
557 p = &m_prebuffers[m_prebuffers.size()-1];
560 u32 vertex_count = p->vertices.size();
561 for(u32 i=0; i<numIndices; i++)
563 u32 j = indices[i] + vertex_count;
566 dstream<<"FIXME: Meshbuffer ran out of indices"<<std::endl;
567 // NOTE: Fix is to just add an another MeshBuffer
569 p->indices.push_back(j);
571 for(u32 i=0; i<numVertices; i++)
573 p->vertices.push_back(vertices[i]);
577 void fillMesh(scene::SMesh *mesh)
579 /*dstream<<"Filling mesh with "<<m_prebuffers.size()
580 <<" meshbuffers"<<std::endl;*/
581 for(u32 i=0; i<m_prebuffers.size(); i++)
583 PreMeshBuffer &p = m_prebuffers[i];
585 /*dstream<<"p.vertices.size()="<<p.vertices.size()
586 <<", p.indices.size()="<<p.indices.size()
591 // This is a "Standard MeshBuffer",
592 // it's a typedeffed CMeshBuffer<video::S3DVertex>
593 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
595 buf->Material = p.material;
596 //((scene::SMeshBuffer*)buf)->Material = p.material;
598 //buf->setHardwareMappingHint(scene::EHM_STATIC);
600 mesh->addMeshBuffer(buf);
604 buf->append(p.vertices.pointer(), p.vertices.size(),
605 p.indices.pointer(), p.indices.size());
610 core::array<PreMeshBuffer> m_prebuffers;
613 void MapBlock::updateMesh(u32 daynight_ratio)
617 DEBUG: If mesh has been generated, don't generate it again
620 JMutexAutoLock meshlock(mesh_mutex);
626 // 4-21ms for MAP_BLOCKSIZE=16
627 // 24-155ms for MAP_BLOCKSIZE=32
628 //TimeTaker timer1("updateMesh()");
630 core::array<FastFace> fastfaces_new;
632 v3f posRelative_f(getPosRelative().X, getPosRelative().Y,
633 getPosRelative().Z); // floating point conversion
636 Avoid interlocks by copying m_temp_mods
638 NodeModMap temp_mods;
640 JMutexAutoLock lock(m_temp_mods_mutex);
641 m_temp_mods.copy(temp_mods);
647 bool new_style_water = g_settings.getBool("new_style_water");
648 bool new_style_leaves = g_settings.getBool("new_style_leaves");
650 float node_water_level = 1.0;
652 node_water_level = 0.85;
655 We are including the faces of the trailing edges of the block.
656 This means that when something changes, the caller must
657 also update the meshes of the blocks at the leading edges.
659 NOTE: This is the slowest part of this method.
663 // 4-23ms for MAP_BLOCKSIZE=16
664 //TimeTaker timer2("updateMesh() collect");
667 Go through every y,z and get top faces in rows of x+
669 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
670 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
671 updateFastFaceRow(daynight_ratio, posRelative_f,
672 v3s16(0,y,z), MAP_BLOCKSIZE,
675 v3s16(0,1,0), //face dir
682 Go through every x,y and get right faces in rows of z+
684 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
685 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
686 updateFastFaceRow(daynight_ratio, posRelative_f,
687 v3s16(x,y,0), MAP_BLOCKSIZE,
697 Go through every y,z and get back faces in rows of x+
699 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
700 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
701 updateFastFaceRow(daynight_ratio, posRelative_f,
702 v3s16(0,y,z), MAP_BLOCKSIZE,
716 Convert FastFaces to SMesh
719 MeshCollector collector;
721 if(fastfaces_new.size() > 0)
723 // avg 0ms (100ms spikes when loading textures the first time)
724 //TimeTaker timer2("updateMesh() mesh building");
726 video::SMaterial material;
727 material.setFlag(video::EMF_LIGHTING, false);
728 material.setFlag(video::EMF_BILINEAR_FILTER, false);
729 material.setFlag(video::EMF_FOG_ENABLE, true);
730 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
731 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_SIMPLE);
733 for(u32 i=0; i<fastfaces_new.size(); i++)
735 FastFace &f = fastfaces_new[i];
737 const u16 indices[] = {0,1,2,2,3,0};
739 //video::ITexture *texture = g_irrlicht->getTexture(f.tile.spec);
740 video::ITexture *texture = f.tile.texture.atlas;
744 material.setTexture(0, texture);
746 f.tile.applyMaterialOptions(material);
748 collector.append(material, f.vertices, 4, indices, 6);
753 Add special graphics:
759 //TimeTaker timer2("updateMesh() adding special stuff");
761 // Flowing water material
762 video::SMaterial material_water1;
763 material_water1.setFlag(video::EMF_LIGHTING, false);
764 //material_water1.setFlag(video::EMF_BACK_FACE_CULLING, false);
765 material_water1.setFlag(video::EMF_BILINEAR_FILTER, false);
766 material_water1.setFlag(video::EMF_FOG_ENABLE, true);
767 material_water1.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
769 //material_water1.setTexture(0, g_irrlicht->getTexture("water.png"));
770 AtlasPointer pa_water1 = g_texturesource->getTexture(
771 g_texturesource->getTextureId("water.png"));
772 material_water1.setTexture(0, pa_water1.atlas);
774 // New-style leaves material
775 video::SMaterial material_leaves1;
776 material_leaves1.setFlag(video::EMF_LIGHTING, false);
777 //material_leaves1.setFlag(video::EMF_BACK_FACE_CULLING, false);
778 material_leaves1.setFlag(video::EMF_BILINEAR_FILTER, false);
779 material_leaves1.setFlag(video::EMF_FOG_ENABLE, true);
780 material_leaves1.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
782 //material_leaves1.setTexture(0, g_irrlicht->getTexture("leaves.png"));
783 AtlasPointer pa_leaves1 = g_texturesource->getTexture(
784 g_texturesource->getTextureId("leaves.png"));
785 material_leaves1.setTexture(0, pa_leaves1.atlas);
787 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
788 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
789 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
793 MapNode &n = getNodeRef(x,y,z);
798 if(n.d == CONTENT_TORCH)
800 video::SColor c(255,255,255,255);
802 video::S3DVertex vertices[4] =
804 video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c, 0,1),
805 video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c, 1,1),
806 video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, 1,0),
807 video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c, 0,0),
810 v3s16 dir = unpackDir(n.dir);
812 for(s32 i=0; i<4; i++)
814 if(dir == v3s16(1,0,0))
815 vertices[i].Pos.rotateXZBy(0);
816 if(dir == v3s16(-1,0,0))
817 vertices[i].Pos.rotateXZBy(180);
818 if(dir == v3s16(0,0,1))
819 vertices[i].Pos.rotateXZBy(90);
820 if(dir == v3s16(0,0,-1))
821 vertices[i].Pos.rotateXZBy(-90);
822 if(dir == v3s16(0,-1,0))
823 vertices[i].Pos.rotateXZBy(45);
824 if(dir == v3s16(0,1,0))
825 vertices[i].Pos.rotateXZBy(-45);
827 vertices[i].Pos += intToFloat(p + getPosRelative(), BS);
831 video::SMaterial material;
832 material.setFlag(video::EMF_LIGHTING, false);
833 material.setFlag(video::EMF_BACK_FACE_CULLING, false);
834 material.setFlag(video::EMF_BILINEAR_FILTER, false);
835 //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
836 material.MaterialType
837 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
839 if(dir == v3s16(0,-1,0))
840 material.setTexture(0,
841 g_texturesource->getTextureRaw("torch_on_floor.png"));
842 else if(dir == v3s16(0,1,0))
843 material.setTexture(0,
844 g_texturesource->getTextureRaw("torch_on_ceiling.png"));
845 // For backwards compatibility
846 else if(dir == v3s16(0,0,0))
847 material.setTexture(0,
848 g_texturesource->getTextureRaw("torch_on_floor.png"));
850 material.setTexture(0,
851 g_texturesource->getTextureRaw("torch.png"));
853 u16 indices[] = {0,1,2,2,3,0};
854 // Add to mesh collector
855 collector.append(material, vertices, 4, indices, 6);
858 Add flowing water to mesh
860 else if(n.d == CONTENT_WATER)
862 bool top_is_water = false;
864 MapNode n = getNodeParent(v3s16(x,y+1,z));
865 if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
867 }catch(InvalidPositionException &e){}
869 u8 l = decode_light(n.getLightBlend(daynight_ratio));
870 video::SColor c(WATER_ALPHA,l,l,l);
872 // Neighbor water levels (key = relative position)
873 // Includes current node
874 core::map<v3s16, f32> neighbor_levels;
875 core::map<v3s16, u8> neighbor_contents;
876 core::map<v3s16, u8> neighbor_flags;
877 const u8 neighborflag_top_is_water = 0x01;
878 v3s16 neighbor_dirs[9] = {
889 for(u32 i=0; i<9; i++)
891 u8 content = CONTENT_AIR;
892 float level = -0.5 * BS;
896 v3s16 p2 = p + neighbor_dirs[i];
897 MapNode n2 = getNodeParent(p2);
901 if(n2.d == CONTENT_WATERSOURCE)
902 level = (-0.5+node_water_level) * BS;
903 else if(n2.d == CONTENT_WATER)
904 level = (-0.5 + ((float)n2.param2 + 0.5) / 8.0
905 * node_water_level) * BS;
907 // Check node above neighbor.
908 // NOTE: This doesn't get executed if neighbor
911 n2 = getNodeParent(p2);
912 if(n2.d == CONTENT_WATERSOURCE || n2.d == CONTENT_WATER)
913 flags |= neighborflag_top_is_water;
915 catch(InvalidPositionException &e){}
917 neighbor_levels.insert(neighbor_dirs[i], level);
918 neighbor_contents.insert(neighbor_dirs[i], content);
919 neighbor_flags.insert(neighbor_dirs[i], flags);
922 //float water_level = (-0.5 + ((float)n.param2 + 0.5) / 8.0) * BS;
923 //float water_level = neighbor_levels[v3s16(0,0,0)];
925 // Corner heights (average between four waters)
926 f32 corner_levels[4];
928 v3s16 halfdirs[4] = {
934 for(u32 i=0; i<4; i++)
936 v3s16 cornerdir = halfdirs[i];
937 float cornerlevel = 0;
939 for(u32 j=0; j<4; j++)
941 v3s16 neighbordir = cornerdir - halfdirs[j];
942 u8 content = neighbor_contents[neighbordir];
943 // Special case for source nodes
944 if(content == CONTENT_WATERSOURCE)
946 cornerlevel = (-0.5+node_water_level)*BS;
950 else if(content == CONTENT_WATER)
952 cornerlevel += neighbor_levels[neighbordir];
955 else if(content == CONTENT_AIR)
957 cornerlevel += -0.5*BS;
962 cornerlevel /= valid_count;
963 corner_levels[i] = cornerlevel;
970 v3s16 side_dirs[4] = {
976 s16 side_corners[4][2] = {
982 for(u32 i=0; i<4; i++)
984 v3s16 dir = side_dirs[i];
987 If our topside is water and neighbor's topside
988 is water, don't draw side face
991 neighbor_flags[dir] & neighborflag_top_is_water)
994 u8 neighbor_content = neighbor_contents[dir];
996 // Don't draw face if neighbor is not air or water
997 if(neighbor_content != CONTENT_AIR
998 && neighbor_content != CONTENT_WATER)
1001 bool neighbor_is_water = (neighbor_content == CONTENT_WATER);
1003 // Don't draw any faces if neighbor is water and top is water
1004 if(neighbor_is_water == true && top_is_water == false)
1007 video::S3DVertex vertices[4] =
1009 /*video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1),
1010 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1),
1011 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1012 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1013 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1014 pa_water1.x0(), pa_water1.y1()),
1015 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1016 pa_water1.x1(), pa_water1.y1()),
1017 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1018 pa_water1.x1(), pa_water1.y0()),
1019 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1020 pa_water1.x0(), pa_water1.y0()),
1024 If our topside is water, set upper border of face
1025 at upper border of node
1029 vertices[2].Pos.Y = 0.5*BS;
1030 vertices[3].Pos.Y = 0.5*BS;
1033 Otherwise upper position of face is corner levels
1037 vertices[2].Pos.Y = corner_levels[side_corners[i][0]];
1038 vertices[3].Pos.Y = corner_levels[side_corners[i][1]];
1042 If neighbor is water, lower border of face is corner
1045 if(neighbor_is_water)
1047 vertices[0].Pos.Y = corner_levels[side_corners[i][1]];
1048 vertices[1].Pos.Y = corner_levels[side_corners[i][0]];
1051 If neighbor is not water, lower border of face is
1052 lower border of node
1056 vertices[0].Pos.Y = -0.5*BS;
1057 vertices[1].Pos.Y = -0.5*BS;
1060 for(s32 j=0; j<4; j++)
1062 if(dir == v3s16(0,0,1))
1063 vertices[j].Pos.rotateXZBy(0);
1064 if(dir == v3s16(0,0,-1))
1065 vertices[j].Pos.rotateXZBy(180);
1066 if(dir == v3s16(-1,0,0))
1067 vertices[j].Pos.rotateXZBy(90);
1068 if(dir == v3s16(1,0,-0))
1069 vertices[j].Pos.rotateXZBy(-90);
1071 vertices[j].Pos += intToFloat(p + getPosRelative(), BS);
1074 u16 indices[] = {0,1,2,2,3,0};
1075 // Add to mesh collector
1076 collector.append(material_water1, vertices, 4, indices, 6);
1080 Generate top side, if appropriate
1083 if(top_is_water == false)
1085 video::S3DVertex vertices[4] =
1087 /*video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
1088 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
1089 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1090 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1091 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1092 pa_water1.x0(), pa_water1.y1()),
1093 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1094 pa_water1.x1(), pa_water1.y1()),
1095 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
1096 pa_water1.x1(), pa_water1.y0()),
1097 video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
1098 pa_water1.x0(), pa_water1.y0()),
1101 // This fixes a strange bug
1102 s32 corner_resolve[4] = {3,2,1,0};
1104 for(s32 i=0; i<4; i++)
1106 //vertices[i].Pos.Y += water_level;
1107 //vertices[i].Pos.Y += neighbor_levels[v3s16(0,0,0)];
1108 s32 j = corner_resolve[i];
1109 vertices[i].Pos.Y += corner_levels[j];
1110 vertices[i].Pos += intToFloat(p + getPosRelative(), BS);
1113 u16 indices[] = {0,1,2,2,3,0};
1114 // Add to mesh collector
1115 collector.append(material_water1, vertices, 4, indices, 6);
1119 Add water sources to mesh if using new style
1121 else if(n.d == CONTENT_WATERSOURCE && new_style_water)
1123 //bool top_is_water = false;
1124 bool top_is_air = false;
1126 MapNode n = getNodeParent(v3s16(x,y+1,z));
1127 /*if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
1128 top_is_water = true;*/
1129 if(n.d == CONTENT_AIR)
1131 }catch(InvalidPositionException &e){}
1133 /*if(top_is_water == true)
1135 if(top_is_air == false)
1138 u8 l = decode_light(n.getLightBlend(daynight_ratio));
1139 video::SColor c(WATER_ALPHA,l,l,l);
1141 video::S3DVertex vertices[4] =
1143 /*video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
1144 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
1145 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1146 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1147 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1148 pa_water1.x0(), pa_water1.y1()),
1149 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1150 pa_water1.x1(), pa_water1.y1()),
1151 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
1152 pa_water1.x1(), pa_water1.y0()),
1153 video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
1154 pa_water1.x0(), pa_water1.y0()),
1157 for(s32 i=0; i<4; i++)
1159 vertices[i].Pos.Y += (-0.5+node_water_level)*BS;
1160 vertices[i].Pos += intToFloat(p + getPosRelative(), BS);
1163 u16 indices[] = {0,1,2,2,3,0};
1164 // Add to mesh collector
1165 collector.append(material_water1, vertices, 4, indices, 6);
1168 Add leaves if using new style
1170 else if(n.d == CONTENT_LEAVES && new_style_leaves)
1172 /*u8 l = decode_light(n.getLightBlend(daynight_ratio));*/
1173 u8 l = decode_light(undiminish_light(n.getLightBlend(daynight_ratio)));
1174 video::SColor c(255,l,l,l);
1176 for(u32 j=0; j<6; j++)
1178 video::S3DVertex vertices[4] =
1180 /*video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, 0,1),
1181 video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, 1,1),
1182 video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c, 1,0),
1183 video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c, 0,0),*/
1184 video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c,
1185 pa_leaves1.x0(), pa_leaves1.y1()),
1186 video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c,
1187 pa_leaves1.x1(), pa_leaves1.y1()),
1188 video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c,
1189 pa_leaves1.x1(), pa_leaves1.y0()),
1190 video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c,
1191 pa_leaves1.x0(), pa_leaves1.y0()),
1196 for(u16 i=0; i<4; i++)
1197 vertices[i].Pos.rotateXZBy(0);
1201 for(u16 i=0; i<4; i++)
1202 vertices[i].Pos.rotateXZBy(180);
1206 for(u16 i=0; i<4; i++)
1207 vertices[i].Pos.rotateXZBy(-90);
1211 for(u16 i=0; i<4; i++)
1212 vertices[i].Pos.rotateXZBy(90);
1216 for(u16 i=0; i<4; i++)
1217 vertices[i].Pos.rotateYZBy(-90);
1221 for(u16 i=0; i<4; i++)
1222 vertices[i].Pos.rotateYZBy(90);
1225 for(u16 i=0; i<4; i++)
1227 vertices[i].Pos += intToFloat(p + getPosRelative(), BS);
1230 u16 indices[] = {0,1,2,2,3,0};
1231 // Add to mesh collector
1232 collector.append(material_leaves1, vertices, 4, indices, 6);
1238 Add stuff from collector to mesh
1241 scene::SMesh *mesh_new = NULL;
1242 mesh_new = new scene::SMesh();
1244 collector.fillMesh(mesh_new);
1247 Do some stuff to the mesh
1250 mesh_new->recalculateBoundingBox();
1253 Delete new mesh if it is empty
1256 if(mesh_new->getMeshBufferCount() == 0)
1265 // Usually 1-700 faces and 1-7 materials
1266 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
1267 <<"and uses "<<mesh_new->getMeshBufferCount()
1268 <<" materials (meshbuffers)"<<std::endl;
1271 // Use VBO for mesh (this just would set this for ever buffer)
1272 // This will lead to infinite memory usage because or irrlicht.
1273 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
1276 NOTE: If that is enabled, some kind of a queue to the main
1277 thread should be made which would call irrlicht to delete
1278 the hardware buffer and then delete the mesh
1288 //scene::SMesh *mesh_old = mesh[daynight_i];
1289 //mesh[daynight_i] = mesh_new;
1291 scene::SMesh *mesh_old = mesh;
1293 setMeshExpired(false);
1295 if(mesh_old != NULL)
1297 // Remove hardware buffers of meshbuffers of mesh
1298 // NOTE: No way, this runs in a different thread and everything
1299 /*u32 c = mesh_old->getMeshBufferCount();
1300 for(u32 i=0; i<c; i++)
1302 IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1305 /*dstream<<"mesh_old->getReferenceCount()="
1306 <<mesh_old->getReferenceCount()<<std::endl;
1307 u32 c = mesh_old->getMeshBufferCount();
1308 for(u32 i=0; i<c; i++)
1310 scene::IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1311 dstream<<"buf->getReferenceCount()="
1312 <<buf->getReferenceCount()<<std::endl;
1321 mesh_mutex.Unlock();
1323 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1326 /*void MapBlock::updateMeshes(s32 first_i)
1328 assert(first_i >= 0 && first_i <= DAYNIGHT_CACHE_COUNT);
1329 updateMesh(first_i);
1330 for(s32 i=0; i<DAYNIGHT_CACHE_COUNT; i++)
1341 Propagates sunlight down through the block.
1342 Doesn't modify nodes that are not affected by sunlight.
1344 Returns false if sunlight at bottom block is invalid
1345 Returns true if bottom block doesn't exist.
1347 If there is a block above, continues from it.
1348 If there is no block above, assumes there is sunlight, unless
1349 is_underground is set or highest node is water.
1351 At the moment, all sunlighted nodes are added to light_sources.
1352 - SUGG: This could be optimized
1354 Turns sunglighted mud into grass.
1356 if remove_light==true, sets non-sunlighted nodes black.
1358 if black_air_left!=NULL, it is set to true if non-sunlighted
1359 air is left in block.
1361 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources,
1362 bool remove_light, bool *black_air_left,
1365 // Whether the sunlight at the top of the bottom block is valid
1366 bool block_below_is_valid = true;
1368 v3s16 pos_relative = getPosRelative();
1370 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
1372 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
1375 bool no_sunlight = false;
1376 bool no_top_block = false;
1377 // Check if node above block has sunlight
1379 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1380 if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1385 catch(InvalidPositionException &e)
1387 no_top_block = true;
1389 // NOTE: This makes over-ground roofed places sunlighted
1390 // Assume sunlight, unless is_underground==true
1397 MapNode n = getNode(v3s16(x, MAP_BLOCKSIZE-1, z));
1398 if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
1403 // NOTE: As of now, this just would make everything dark.
1405 //no_sunlight = true;
1408 #if 0 // Doesn't work; nothing gets light.
1409 bool no_sunlight = true;
1410 bool no_top_block = false;
1411 // Check if node above block has sunlight
1413 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1414 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
1416 no_sunlight = false;
1419 catch(InvalidPositionException &e)
1421 no_top_block = true;
1425 /*std::cout<<"("<<x<<","<<z<<"): "
1426 <<"no_top_block="<<no_top_block
1427 <<", is_underground="<<is_underground
1428 <<", no_sunlight="<<no_sunlight
1431 s16 y = MAP_BLOCKSIZE-1;
1433 // This makes difference to diminishing in water.
1434 bool stopped_to_solid_object = false;
1436 u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
1441 MapNode &n = getNodeRef(pos);
1443 if(current_light == 0)
1447 else if(current_light == LIGHT_SUN && n.sunlight_propagates())
1449 // Do nothing: Sunlight is continued
1451 else if(n.light_propagates() == false)
1455 bool upper_is_air = false;
1458 if(getNodeParent(pos+v3s16(0,1,0)).d == CONTENT_AIR)
1459 upper_is_air = true;
1461 catch(InvalidPositionException &e)
1464 // Turn mud into grass
1465 if(upper_is_air && n.d == CONTENT_MUD
1466 && current_light == LIGHT_SUN)
1468 n.d = CONTENT_GRASS;
1472 // A solid object is on the way.
1473 stopped_to_solid_object = true;
1481 current_light = diminish_light(current_light);
1484 u8 old_light = n.getLight(LIGHTBANK_DAY);
1486 if(current_light > old_light || remove_light)
1488 n.setLight(LIGHTBANK_DAY, current_light);
1491 if(diminish_light(current_light) != 0)
1493 light_sources.insert(pos_relative + pos, true);
1496 if(current_light == 0 && stopped_to_solid_object)
1500 *black_air_left = true;
1505 // Whether or not the block below should see LIGHT_SUN
1506 bool sunlight_should_go_down = (current_light == LIGHT_SUN);
1509 If the block below hasn't already been marked invalid:
1511 Check if the node below the block has proper sunlight at top.
1512 If not, the block below is invalid.
1514 Ignore non-transparent nodes as they always have no light
1518 if(block_below_is_valid)
1520 MapNode n = getNodeParent(v3s16(x, -1, z));
1521 if(n.light_propagates())
1523 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN
1524 && sunlight_should_go_down == false)
1525 block_below_is_valid = false;
1526 else if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN
1527 && sunlight_should_go_down == true)
1528 block_below_is_valid = false;
1532 catch(InvalidPositionException &e)
1534 /*std::cout<<"InvalidBlockException for bottom block node"
1536 // Just no block below, no need to panic.
1541 return block_below_is_valid;
1544 void MapBlock::copyTo(VoxelManipulator &dst)
1546 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
1547 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
1549 // Copy from data to VoxelManipulator
1550 dst.copyFrom(data, data_area, v3s16(0,0,0),
1551 getPosRelative(), data_size);
1554 void MapBlock::copyFrom(VoxelManipulator &dst)
1556 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
1557 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
1559 // Copy from VoxelManipulator to data
1560 dst.copyTo(data, data_area, v3s16(0,0,0),
1561 getPosRelative(), data_size);
1564 void MapBlock::stepObjects(float dtime, bool server, u32 daynight_ratio)
1569 m_objects.step(dtime, server, daynight_ratio);
1572 Spawn some objects at random.
1574 Use dayNightDiffed() to approximate being near ground level
1576 if(m_spawn_timer < -999)
1580 if(dayNightDiffed() == true && getObjectCount() == 0)
1582 m_spawn_timer -= dtime;
1583 if(m_spawn_timer <= 0.0)
1585 m_spawn_timer += myrand() % 300;
1588 (myrand()%(MAP_BLOCKSIZE-1))+0,
1589 (myrand()%(MAP_BLOCKSIZE-1))+0
1592 s16 y = getGroundLevel(p2d);
1596 v3s16 p(p2d.X, y+1, p2d.Y);
1598 if(getNode(p).d == CONTENT_AIR
1599 && getNode(p).getLightBlend(daynight_ratio) <= 11)
1601 RatObject *obj = new RatObject(NULL, -1, intToFloat(p, BS));
1612 void MapBlock::updateDayNightDiff()
1616 m_day_night_differs = false;
1620 bool differs = false;
1623 Check if any lighting value differs
1625 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1627 MapNode &n = data[i];
1628 if(n.getLight(LIGHTBANK_DAY) != n.getLight(LIGHTBANK_NIGHT))
1636 If some lighting values differ, check if the whole thing is
1637 just air. If it is, differ = false
1641 bool only_air = true;
1642 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1644 MapNode &n = data[i];
1645 if(n.d != CONTENT_AIR)
1655 // Set member variable
1656 m_day_night_differs = differs;
1659 s16 MapBlock::getGroundLevel(v2s16 p2d)
1665 s16 y = MAP_BLOCKSIZE-1;
1668 //if(is_ground_content(getNodeRef(p2d.X, y, p2d.Y).d))
1669 if(content_features(getNodeRef(p2d.X, y, p2d.Y).d).walkable)
1671 if(y == MAP_BLOCKSIZE-1)
1679 catch(InvalidPositionException &e)
1689 void MapBlock::serialize(std::ostream &os, u8 version)
1691 if(!ser_ver_supported(version))
1692 throw VersionMismatchException("ERROR: MapBlock format not supported");
1696 throw SerializationError("ERROR: Not writing dummy block.");
1699 // These have no compression
1700 if(version <= 3 || version == 5 || version == 6)
1702 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1704 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
1705 SharedBuffer<u8> dest(buflen);
1707 dest[0] = is_underground;
1708 for(u32 i=0; i<nodecount; i++)
1710 u32 s = 1 + i * MapNode::serializedLength(version);
1711 data[i].serialize(&dest[s], version);
1714 os.write((char*)*dest, dest.getSize());
1716 else if(version <= 10)
1720 Compress the materials and the params separately.
1724 os.write((char*)&is_underground, 1);
1726 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1728 // Get and compress materials
1729 SharedBuffer<u8> materialdata(nodecount);
1730 for(u32 i=0; i<nodecount; i++)
1732 materialdata[i] = data[i].d;
1734 compress(materialdata, os, version);
1736 // Get and compress lights
1737 SharedBuffer<u8> lightdata(nodecount);
1738 for(u32 i=0; i<nodecount; i++)
1740 lightdata[i] = data[i].param;
1742 compress(lightdata, os, version);
1746 // Get and compress param2
1747 SharedBuffer<u8> param2data(nodecount);
1748 for(u32 i=0; i<nodecount; i++)
1750 param2data[i] = data[i].param2;
1752 compress(param2data, os, version);
1755 // All other versions (newest)
1762 if(m_day_night_differs)
1764 if(m_lighting_expired)
1766 if(m_not_fully_generated)
1768 os.write((char*)&flags, 1);
1770 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1776 SharedBuffer<u8> databuf(nodecount*3);
1779 for(u32 i=0; i<nodecount; i++)
1781 databuf[i] = data[i].d;
1785 for(u32 i=0; i<nodecount; i++)
1787 databuf[i+nodecount] = data[i].param;
1791 for(u32 i=0; i<nodecount; i++)
1793 databuf[i+nodecount*2] = data[i].param2;
1797 Compress data to output stream
1800 compress(databuf, os, version);
1804 void MapBlock::deSerialize(std::istream &is, u8 version)
1806 if(!ser_ver_supported(version))
1807 throw VersionMismatchException("ERROR: MapBlock format not supported");
1809 // These have no compression
1810 if(version <= 3 || version == 5 || version == 6)
1812 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1815 if(is.gcount() != 1)
1816 throw SerializationError
1817 ("MapBlock::deSerialize: no enough input data");
1818 is_underground = tmp;
1819 for(u32 i=0; i<nodecount; i++)
1821 s32 len = MapNode::serializedLength(version);
1822 SharedBuffer<u8> d(len);
1823 is.read((char*)*d, len);
1824 if(is.gcount() != len)
1825 throw SerializationError
1826 ("MapBlock::deSerialize: no enough input data");
1827 data[i].deSerialize(*d, version);
1830 else if(version <= 10)
1832 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1835 is.read((char*)&t8, 1);
1836 is_underground = t8;
1839 // Uncompress and set material data
1840 std::ostringstream os(std::ios_base::binary);
1841 decompress(is, os, version);
1842 std::string s = os.str();
1843 if(s.size() != nodecount)
1844 throw SerializationError
1845 ("MapBlock::deSerialize: invalid format");
1846 for(u32 i=0; i<s.size(); i++)
1852 // Uncompress and set param data
1853 std::ostringstream os(std::ios_base::binary);
1854 decompress(is, os, version);
1855 std::string s = os.str();
1856 if(s.size() != nodecount)
1857 throw SerializationError
1858 ("MapBlock::deSerialize: invalid format");
1859 for(u32 i=0; i<s.size(); i++)
1861 data[i].param = s[i];
1867 // Uncompress and set param2 data
1868 std::ostringstream os(std::ios_base::binary);
1869 decompress(is, os, version);
1870 std::string s = os.str();
1871 if(s.size() != nodecount)
1872 throw SerializationError
1873 ("MapBlock::deSerialize: invalid format");
1874 for(u32 i=0; i<s.size(); i++)
1876 data[i].param2 = s[i];
1880 // All other versions (newest)
1883 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1886 is.read((char*)&flags, 1);
1887 is_underground = (flags & 0x01) ? true : false;
1888 m_day_night_differs = (flags & 0x02) ? true : false;
1889 m_lighting_expired = (flags & 0x04) ? true : false;
1890 m_not_fully_generated = (flags & 0x08) ? true : false;
1893 std::ostringstream os(std::ios_base::binary);
1894 decompress(is, os, version);
1895 std::string s = os.str();
1896 if(s.size() != nodecount*3)
1897 throw SerializationError
1898 ("MapBlock::deSerialize: invalid format");
1901 for(u32 i=0; i<nodecount; i++)
1906 for(u32 i=0; i<nodecount; i++)
1908 data[i].param = s[i+nodecount];
1911 for(u32 i=0; i<nodecount; i++)
1913 data[i].param2 = s[i+nodecount*2];
1918 Translate nodes as specified in the translate_to fields of
1921 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1923 MapNode &n = data[i];
1925 MapNode *translate_to = content_features(n.d).translate_to;
1928 dstream<<"MapBlock: WARNING: Translating node "<<n.d<<" to "
1929 <<translate_to->d<<std::endl;