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_day_night_differs(false),
44 m_spawn_timer = -10000;
47 m_mesh_expired = false;
50 m_temp_mods_mutex.Init();
58 JMutexAutoLock lock(mesh_mutex);
72 bool MapBlock::isValidPositionParent(v3s16 p)
74 if(isValidPosition(p))
79 return m_parent->isValidPosition(getPosRelative() + p);
83 MapNode MapBlock::getNodeParent(v3s16 p)
85 if(isValidPosition(p) == false)
87 return m_parent->getNode(getPosRelative() + p);
92 throw InvalidPositionException();
93 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
97 void MapBlock::setNodeParent(v3s16 p, MapNode & n)
99 if(isValidPosition(p) == false)
101 m_parent->setNode(getPosRelative() + p, n);
106 throw InvalidPositionException();
107 data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X] = n;
111 MapNode MapBlock::getNodeParentNoEx(v3s16 p)
113 if(isValidPosition(p) == false)
116 return m_parent->getNode(getPosRelative() + p);
118 catch(InvalidPositionException &e)
120 return MapNode(CONTENT_IGNORE);
127 return MapNode(CONTENT_IGNORE);
129 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
134 Parameters must consist of air and !air.
135 Order doesn't matter.
137 If either of the nodes doesn't exist, light is 0.
140 daynight_ratio: 0...1000
142 n2: getNodeParent(p + face_dir)
143 face_dir: axis oriented unit vector from p to p2
145 returns encoded light value.
147 u8 MapBlock::getFaceLight(u32 daynight_ratio, MapNode n, MapNode n2,
153 if(n.d == CONTENT_WATER)
160 if(n2.d == CONTENT_WATER)
171 u8 l1 = n.getLightBlend(daynight_ratio);
172 u8 l2 = n2.getLightBlend(daynight_ratio);
178 // Make some nice difference to different sides
180 /*if(face_dir.X == 1 || face_dir.Z == 1 || face_dir.Y == -1)
181 light = diminish_light(diminish_light(light));
182 else if(face_dir.X == -1 || face_dir.Z == -1)
183 light = diminish_light(light);*/
185 if(face_dir.X == 1 || face_dir.X == -1 || face_dir.Y == -1)
186 light = diminish_light(diminish_light(light));
187 else if(face_dir.Z == 1 || face_dir.Z == -1)
188 light = diminish_light(light);
192 catch(InvalidPositionException &e)
200 void MapBlock::makeFastFace(TileSpec tile, u8 light, v3f p,
201 v3s16 dir, v3f scale, v3f posRelative_f,
202 core::array<FastFace> &dest)
206 // Position is at the center of the cube.
211 // If looking towards z+, this is the face that is behind
212 // the center point, facing towards z+.
213 vertex_pos[0] = v3f(-BS/2,-BS/2,BS/2);
214 vertex_pos[1] = v3f( BS/2,-BS/2,BS/2);
215 vertex_pos[2] = v3f( BS/2, BS/2,BS/2);
216 vertex_pos[3] = v3f(-BS/2, BS/2,BS/2);
218 if(dir == v3s16(0,0,1))
220 for(u16 i=0; i<4; i++)
221 vertex_pos[i].rotateXZBy(0);
223 else if(dir == v3s16(0,0,-1))
225 for(u16 i=0; i<4; i++)
226 vertex_pos[i].rotateXZBy(180);
228 else if(dir == v3s16(1,0,0))
230 for(u16 i=0; i<4; i++)
231 vertex_pos[i].rotateXZBy(-90);
233 else if(dir == v3s16(-1,0,0))
235 for(u16 i=0; i<4; i++)
236 vertex_pos[i].rotateXZBy(90);
238 else if(dir == v3s16(0,1,0))
240 for(u16 i=0; i<4; i++)
241 vertex_pos[i].rotateYZBy(-90);
243 else if(dir == v3s16(0,-1,0))
245 for(u16 i=0; i<4; i++)
246 vertex_pos[i].rotateYZBy(90);
249 for(u16 i=0; i<4; i++)
251 vertex_pos[i].X *= scale.X;
252 vertex_pos[i].Y *= scale.Y;
253 vertex_pos[i].Z *= scale.Z;
254 vertex_pos[i] += pos + posRelative_f;
258 if (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X;
259 else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
260 else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
262 v3f zerovector = v3f(0,0,0);
264 //u8 li = decode_light(light);
266 //u8 li = 255; //DEBUG
268 u8 alpha = tile.alpha;
270 if(tile.id == TILE_WATER)
271 alpha = WATER_ALPHA;*/
273 video::SColor c = video::SColor(alpha,li,li,li);
275 face.vertices[0] = video::S3DVertex(vertex_pos[0], zerovector, c,
276 core::vector2d<f32>(abs_scale,1));
277 face.vertices[1] = video::S3DVertex(vertex_pos[1], zerovector, c,
278 core::vector2d<f32>(0,1));
279 face.vertices[2] = video::S3DVertex(vertex_pos[2], zerovector, c,
280 core::vector2d<f32>(0,0));
281 face.vertices[3] = video::S3DVertex(vertex_pos[3], zerovector, c,
282 core::vector2d<f32>(abs_scale,0));
284 /*float x0 = (float)tile.tx/256.0;
285 float y0 = (float)tile.ty/256.0;
286 float w = ((float)tile.tw + 1.0)/256.0;
287 float h = ((float)tile.th + 1.0)/256.0;*/
289 float x0 = tile.texture.pos.X;
290 float y0 = tile.texture.pos.Y;
291 float w = tile.texture.size.X;
292 float h = tile.texture.size.Y;
294 face.vertices[0] = video::S3DVertex(vertex_pos[0], zerovector, c,
295 core::vector2d<f32>(x0+w*abs_scale, y0+h));
296 face.vertices[1] = video::S3DVertex(vertex_pos[1], zerovector, c,
297 core::vector2d<f32>(x0, y0+h));
298 face.vertices[2] = video::S3DVertex(vertex_pos[2], zerovector, c,
299 core::vector2d<f32>(x0, y0));
300 face.vertices[3] = video::S3DVertex(vertex_pos[3], zerovector, c,
301 core::vector2d<f32>(x0+w*abs_scale, y0));
305 //f->tile = TILE_STONE;
307 dest.push_back(face);
312 Gets node tile from any place relative to block.
313 Returns TILE_NODE if doesn't exist or should not be drawn.
315 TileSpec MapBlock::getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir,
316 NodeModMap &temp_mods)
319 spec = mn.getTile(face_dir);
322 Check temporary modifications on this node
324 /*core::map<v3s16, NodeMod>::Node *n;
325 n = m_temp_mods.find(p);
329 struct NodeMod mod = n->getValue();*/
331 if(temp_mods.get(p, &mod))
333 if(mod.type == NODEMOD_CHANGECONTENT)
335 MapNode mn2(mod.param);
336 spec = mn2.getTile(face_dir);
338 if(mod.type == NODEMOD_CRACK)
341 Get texture id, translate it to name, append stuff to
345 // Get original texture name
346 u32 orig_id = spec.texture.id;
347 std::string orig_name = g_texturesource->getTextureName(orig_id);
349 // Create new texture name
350 std::ostringstream os;
351 os<<orig_name<<"^[crack"<<mod.param;
354 u32 new_id = g_texturesource->getTextureId(os.str());
356 /*dstream<<"MapBlock::getNodeTile(): Switching from "
357 <<orig_name<<" to "<<os.str()<<" ("
358 <<orig_id<<" to "<<new_id<<")"<<std::endl;*/
360 spec.texture = g_texturesource->getTexture(new_id);
367 u8 MapBlock::getNodeContent(v3s16 p, MapNode mn, NodeModMap &temp_mods)
370 Check temporary modifications on this node
372 /*core::map<v3s16, NodeMod>::Node *n;
373 n = m_temp_mods.find(p);
377 struct NodeMod mod = n->getValue();*/
379 if(temp_mods.get(p, &mod))
381 if(mod.type == NODEMOD_CHANGECONTENT)
386 if(mod.type == NODEMOD_CRACK)
389 Content doesn't change.
391 face_contents works just like it should, because
392 there should not be faces between differently cracked
395 If a semi-transparent node is cracked in front an
396 another one, it really doesn't matter whether there
397 is a cracked face drawn in between or not.
407 translate_dir: unit vector with only one of x, y or z
408 face_dir: unit vector with only one of x, y or z
410 void MapBlock::updateFastFaceRow(
419 core::array<FastFace> &dest,
420 NodeModMap &temp_mods)
424 u16 continuous_tiles_count = 0;
426 MapNode n0 = getNodeParentNoEx(p);
427 MapNode n1 = getNodeParentNoEx(p + face_dir);
429 u8 light = getFaceLight(daynight_ratio, n0, n1, face_dir);
431 TileSpec tile0 = getNodeTile(n0, p, face_dir, temp_mods);
432 TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir, temp_mods);
434 for(u16 j=0; j<length; j++)
436 bool next_is_different = true;
445 // If at last position, there is nothing to compare to and
446 // the face must be drawn anyway
449 p_next = p + translate_dir;
450 n0_next = getNodeParentNoEx(p_next);
451 n1_next = getNodeParentNoEx(p_next + face_dir);
452 tile0_next = getNodeTile(n0_next, p_next, face_dir, temp_mods);
453 tile1_next = getNodeTile(n1_next,p_next+face_dir,-face_dir, temp_mods);
454 light_next = getFaceLight(daynight_ratio, n0_next, n1_next, face_dir);
456 if(tile0_next == tile0
457 && tile1_next == tile1
458 && light_next == light)
460 next_is_different = false;
464 continuous_tiles_count++;
466 // This is set to true if the texture doesn't allow more tiling
467 bool end_of_texture = false;
469 If there is no texture, it can be tiled infinitely.
470 If tiled==0, it means the texture can be tiled infinitely.
471 Otherwise check tiled agains continuous_tiles_count.
473 This check has to be made for both tiles, because this is
474 a bit hackish and we know which one we're using only when
475 the decision to make the faces is made.
477 if(tile0.texture.atlas != NULL && tile0.texture.tiled != 0)
479 if(tile0.texture.tiled <= continuous_tiles_count)
480 end_of_texture = true;
482 if(tile1.texture.atlas != NULL && tile1.texture.tiled != 0)
484 if(tile1.texture.tiled <= continuous_tiles_count)
485 end_of_texture = true;
488 //end_of_texture = true; //DEBUG
490 if(next_is_different || end_of_texture)
493 Create a face if there should be one
495 //u8 mf = face_contents(tile0, tile1);
497 u8 content0 = getNodeContent(p, n0, temp_mods);
498 u8 content1 = getNodeContent(p + face_dir, n1, temp_mods);
499 u8 mf = face_contents(content0, content1);
503 // Floating point conversion of the position vector
504 v3f pf(p.X, p.Y, p.Z);
505 // Center point of face (kind of)
506 v3f sp = pf - ((f32)continuous_tiles_count / 2. - 0.5) * translate_dir_f;
508 if(translate_dir.X != 0){
509 scale.X = continuous_tiles_count;
511 if(translate_dir.Y != 0){
512 scale.Y = continuous_tiles_count;
514 if(translate_dir.Z != 0){
515 scale.Z = continuous_tiles_count;
520 // If node at sp (tile0) is more solid
523 makeFastFace(tile0, decode_light(light),
525 posRelative_f, dest);
527 // If node at sp is less solid (mf == 2)
530 makeFastFace(tile1, decode_light(light),
531 sp+face_dir_f, -face_dir, scale,
532 posRelative_f, dest);
537 continuous_tiles_count = 0;
550 This is used because CMeshBuffer::append() is very slow
554 video::SMaterial material;
555 core::array<u16> indices;
556 core::array<video::S3DVertex> vertices;
563 video::SMaterial material,
564 const video::S3DVertex* const vertices,
566 const u16* const indices,
570 PreMeshBuffer *p = NULL;
571 for(u32 i=0; i<m_prebuffers.size(); i++)
573 PreMeshBuffer &pp = m_prebuffers[i];
574 if(pp.material != material)
584 pp.material = material;
585 m_prebuffers.push_back(pp);
586 p = &m_prebuffers[m_prebuffers.size()-1];
589 u32 vertex_count = p->vertices.size();
590 for(u32 i=0; i<numIndices; i++)
592 u32 j = indices[i] + vertex_count;
595 dstream<<"FIXME: Meshbuffer ran out of indices"<<std::endl;
596 // NOTE: Fix is to just add an another MeshBuffer
598 p->indices.push_back(j);
600 for(u32 i=0; i<numVertices; i++)
602 p->vertices.push_back(vertices[i]);
606 void fillMesh(scene::SMesh *mesh)
608 /*dstream<<"Filling mesh with "<<m_prebuffers.size()
609 <<" meshbuffers"<<std::endl;*/
610 for(u32 i=0; i<m_prebuffers.size(); i++)
612 PreMeshBuffer &p = m_prebuffers[i];
614 /*dstream<<"p.vertices.size()="<<p.vertices.size()
615 <<", p.indices.size()="<<p.indices.size()
620 // This is a "Standard MeshBuffer",
621 // it's a typedeffed CMeshBuffer<video::S3DVertex>
622 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
624 buf->Material = p.material;
625 //((scene::SMeshBuffer*)buf)->Material = p.material;
627 //buf->setHardwareMappingHint(scene::EHM_STATIC);
629 mesh->addMeshBuffer(buf);
633 buf->append(p.vertices.pointer(), p.vertices.size(),
634 p.indices.pointer(), p.indices.size());
639 core::array<PreMeshBuffer> m_prebuffers;
642 void MapBlock::updateMesh(u32 daynight_ratio)
646 DEBUG: If mesh has been generated, don't generate it again
649 JMutexAutoLock meshlock(mesh_mutex);
655 // 4-21ms for MAP_BLOCKSIZE=16
656 // 24-155ms for MAP_BLOCKSIZE=32
657 //TimeTaker timer1("updateMesh()");
659 core::array<FastFace> fastfaces_new;
661 v3f posRelative_f(getPosRelative().X, getPosRelative().Y,
662 getPosRelative().Z); // floating point conversion
665 Avoid interlocks by copying m_temp_mods
667 NodeModMap temp_mods;
669 JMutexAutoLock lock(m_temp_mods_mutex);
670 m_temp_mods.copy(temp_mods);
676 bool new_style_water = g_settings.getBool("new_style_water");
677 bool new_style_leaves = g_settings.getBool("new_style_leaves");
679 float node_water_level = 1.0;
681 node_water_level = 0.9;
684 We are including the faces of the trailing edges of the block.
685 This means that when something changes, the caller must
686 also update the meshes of the blocks at the leading edges.
688 NOTE: This is the slowest part of this method.
692 // 4-23ms for MAP_BLOCKSIZE=16
693 //TimeTaker timer2("updateMesh() collect");
696 Go through every y,z and get top faces in rows of x+
698 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
699 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
700 updateFastFaceRow(daynight_ratio, posRelative_f,
701 v3s16(0,y,z), MAP_BLOCKSIZE,
704 v3s16(0,1,0), //face dir
711 Go through every x,y and get right faces in rows of z+
713 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
714 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
715 updateFastFaceRow(daynight_ratio, posRelative_f,
716 v3s16(x,y,0), MAP_BLOCKSIZE,
726 Go through every y,z and get back faces in rows of x+
728 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
729 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
730 updateFastFaceRow(daynight_ratio, posRelative_f,
731 v3s16(0,y,z), MAP_BLOCKSIZE,
745 Convert FastFaces to SMesh
748 MeshCollector collector;
750 if(fastfaces_new.size() > 0)
752 // avg 0ms (100ms spikes when loading textures the first time)
753 //TimeTaker timer2("updateMesh() mesh building");
755 video::SMaterial material;
756 material.Lighting = false;
757 material.BackfaceCulling = false;
758 material.setFlag(video::EMF_BILINEAR_FILTER, false);
759 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
760 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_SIMPLE);
761 material.setFlag(video::EMF_FOG_ENABLE, true);
763 for(u32 i=0; i<fastfaces_new.size(); i++)
765 FastFace &f = fastfaces_new[i];
767 const u16 indices[] = {0,1,2,2,3,0};
769 //video::ITexture *texture = g_irrlicht->getTexture(f.tile.spec);
770 video::ITexture *texture = f.tile.texture.atlas;
774 material.setTexture(0, texture);
776 f.tile.applyMaterialOptions(material);
778 collector.append(material, f.vertices, 4, indices, 6);
783 Add special graphics:
789 //TimeTaker timer2("updateMesh() adding special stuff");
791 // Flowing water material
792 video::SMaterial material_water1;
793 material_water1.setFlag(video::EMF_LIGHTING, false);
794 material_water1.setFlag(video::EMF_BACK_FACE_CULLING, false);
795 material_water1.setFlag(video::EMF_BILINEAR_FILTER, false);
796 material_water1.setFlag(video::EMF_FOG_ENABLE, true);
797 material_water1.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
799 //material_water1.setTexture(0, g_irrlicht->getTexture("water.png"));
800 AtlasPointer pa_water1 = g_texturesource->getTexture(
801 g_texturesource->getTextureId("water.png"));
802 material_water1.setTexture(0, pa_water1.atlas);
804 // New-style leaves material
805 video::SMaterial material_leaves1;
806 material_leaves1.setFlag(video::EMF_LIGHTING, false);
807 //material_leaves1.setFlag(video::EMF_BACK_FACE_CULLING, false);
808 material_leaves1.setFlag(video::EMF_BILINEAR_FILTER, false);
809 material_leaves1.setFlag(video::EMF_FOG_ENABLE, true);
810 material_leaves1.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
812 //material_leaves1.setTexture(0, g_irrlicht->getTexture("leaves.png"));
813 AtlasPointer pa_leaves1 = g_texturesource->getTexture(
814 g_texturesource->getTextureId("leaves.png"));
815 material_leaves1.setTexture(0, pa_leaves1.atlas);
817 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
818 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
819 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
823 MapNode &n = getNodeRef(x,y,z);
828 if(n.d == CONTENT_TORCH)
830 video::SColor c(255,255,255,255);
832 video::S3DVertex vertices[4] =
834 video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c, 0,1),
835 video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c, 1,1),
836 video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, 1,0),
837 video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c, 0,0),
840 v3s16 dir = unpackDir(n.dir);
842 for(s32 i=0; i<4; i++)
844 if(dir == v3s16(1,0,0))
845 vertices[i].Pos.rotateXZBy(0);
846 if(dir == v3s16(-1,0,0))
847 vertices[i].Pos.rotateXZBy(180);
848 if(dir == v3s16(0,0,1))
849 vertices[i].Pos.rotateXZBy(90);
850 if(dir == v3s16(0,0,-1))
851 vertices[i].Pos.rotateXZBy(-90);
852 if(dir == v3s16(0,-1,0))
853 vertices[i].Pos.rotateXZBy(45);
854 if(dir == v3s16(0,1,0))
855 vertices[i].Pos.rotateXZBy(-45);
857 vertices[i].Pos += intToFloat(p + getPosRelative());
861 video::SMaterial material;
862 material.setFlag(video::EMF_LIGHTING, false);
863 material.setFlag(video::EMF_BACK_FACE_CULLING, false);
864 material.setFlag(video::EMF_BILINEAR_FILTER, false);
865 //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
866 material.MaterialType
867 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
869 if(dir == v3s16(0,-1,0))
870 material.setTexture(0,
871 g_texturesource->getTextureRaw("torch_on_floor.png"));
872 else if(dir == v3s16(0,1,0))
873 material.setTexture(0,
874 g_texturesource->getTextureRaw("torch_on_ceiling.png"));
875 // For backwards compatibility
876 else if(dir == v3s16(0,0,0))
877 material.setTexture(0,
878 g_texturesource->getTextureRaw("torch_on_floor.png"));
880 material.setTexture(0,
881 g_texturesource->getTextureRaw("torch.png"));
883 u16 indices[] = {0,1,2,2,3,0};
884 // Add to mesh collector
885 collector.append(material, vertices, 4, indices, 6);
888 Add flowing water to mesh
890 else if(n.d == CONTENT_WATER)
892 bool top_is_water = false;
894 MapNode n = getNodeParent(v3s16(x,y+1,z));
895 if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
897 }catch(InvalidPositionException &e){}
899 u8 l = decode_light(n.getLightBlend(daynight_ratio));
900 video::SColor c(WATER_ALPHA,l,l,l);
902 // Neighbor water levels (key = relative position)
903 // Includes current node
904 core::map<v3s16, f32> neighbor_levels;
905 core::map<v3s16, u8> neighbor_contents;
906 core::map<v3s16, u8> neighbor_flags;
907 const u8 neighborflag_top_is_water = 0x01;
908 v3s16 neighbor_dirs[9] = {
919 for(u32 i=0; i<9; i++)
921 u8 content = CONTENT_AIR;
922 float level = -0.5 * BS;
926 v3s16 p2 = p + neighbor_dirs[i];
927 MapNode n2 = getNodeParent(p2);
931 if(n2.d == CONTENT_WATERSOURCE)
932 level = (-0.5+node_water_level) * BS;
933 else if(n2.d == CONTENT_WATER)
934 level = (-0.5 + ((float)n2.param2 + 0.5) / 8.0
935 * node_water_level) * BS;
937 // Check node above neighbor.
938 // NOTE: This doesn't get executed if neighbor
941 n2 = getNodeParent(p2);
942 if(n2.d == CONTENT_WATERSOURCE || n2.d == CONTENT_WATER)
943 flags |= neighborflag_top_is_water;
945 catch(InvalidPositionException &e){}
947 neighbor_levels.insert(neighbor_dirs[i], level);
948 neighbor_contents.insert(neighbor_dirs[i], content);
949 neighbor_flags.insert(neighbor_dirs[i], flags);
952 //float water_level = (-0.5 + ((float)n.param2 + 0.5) / 8.0) * BS;
953 //float water_level = neighbor_levels[v3s16(0,0,0)];
955 // Corner heights (average between four waters)
956 f32 corner_levels[4];
958 v3s16 halfdirs[4] = {
964 for(u32 i=0; i<4; i++)
966 v3s16 cornerdir = halfdirs[i];
967 float cornerlevel = 0;
969 for(u32 j=0; j<4; j++)
971 v3s16 neighbordir = cornerdir - halfdirs[j];
972 u8 content = neighbor_contents[neighbordir];
973 // Special case for source nodes
974 if(content == CONTENT_WATERSOURCE)
976 cornerlevel = (-0.5+node_water_level)*BS;
980 else if(content == CONTENT_WATER)
982 cornerlevel += neighbor_levels[neighbordir];
985 else if(content == CONTENT_AIR)
987 cornerlevel += -0.5*BS;
992 cornerlevel /= valid_count;
993 corner_levels[i] = cornerlevel;
1000 v3s16 side_dirs[4] = {
1006 s16 side_corners[4][2] = {
1012 for(u32 i=0; i<4; i++)
1014 v3s16 dir = side_dirs[i];
1017 If our topside is water and neighbor's topside
1018 is water, don't draw side face
1021 neighbor_flags[dir] & neighborflag_top_is_water)
1024 u8 neighbor_content = neighbor_contents[dir];
1026 // Don't draw face if neighbor is not air or water
1027 if(neighbor_content != CONTENT_AIR
1028 && neighbor_content != CONTENT_WATER)
1031 bool neighbor_is_water = (neighbor_content == CONTENT_WATER);
1033 // Don't draw any faces if neighbor is water and top is water
1034 if(neighbor_is_water == true && top_is_water == false)
1037 video::S3DVertex vertices[4] =
1039 /*video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1),
1040 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1),
1041 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1042 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1043 video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
1044 pa_water1.x0(), pa_water1.y1()),
1045 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
1046 pa_water1.x1(), pa_water1.y1()),
1047 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1048 pa_water1.x1(), pa_water1.y0()),
1049 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1050 pa_water1.x0(), pa_water1.y0()),
1054 If our topside is water, set upper border of face
1055 at upper border of node
1059 vertices[2].Pos.Y = 0.5*BS;
1060 vertices[3].Pos.Y = 0.5*BS;
1063 Otherwise upper position of face is corner levels
1067 vertices[2].Pos.Y = corner_levels[side_corners[i][0]];
1068 vertices[3].Pos.Y = corner_levels[side_corners[i][1]];
1072 If neighbor is water, lower border of face is corner
1075 if(neighbor_is_water)
1077 vertices[0].Pos.Y = corner_levels[side_corners[i][1]];
1078 vertices[1].Pos.Y = corner_levels[side_corners[i][0]];
1081 If neighbor is not water, lower border of face is
1082 lower border of node
1086 vertices[0].Pos.Y = -0.5*BS;
1087 vertices[1].Pos.Y = -0.5*BS;
1090 for(s32 j=0; j<4; j++)
1092 if(dir == v3s16(0,0,1))
1093 vertices[j].Pos.rotateXZBy(0);
1094 if(dir == v3s16(0,0,-1))
1095 vertices[j].Pos.rotateXZBy(180);
1096 if(dir == v3s16(-1,0,0))
1097 vertices[j].Pos.rotateXZBy(90);
1098 if(dir == v3s16(1,0,-0))
1099 vertices[j].Pos.rotateXZBy(-90);
1101 vertices[j].Pos += intToFloat(p + getPosRelative());
1104 u16 indices[] = {0,1,2,2,3,0};
1105 // Add to mesh collector
1106 collector.append(material_water1, vertices, 4, indices, 6);
1110 Generate top side, if appropriate
1113 if(top_is_water == false)
1115 video::S3DVertex vertices[4] =
1117 /*video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
1118 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
1119 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1120 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1121 video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
1122 pa_water1.x0(), pa_water1.y1()),
1123 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
1124 pa_water1.x1(), pa_water1.y1()),
1125 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1126 pa_water1.x1(), pa_water1.y0()),
1127 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1128 pa_water1.x0(), pa_water1.y0()),
1131 for(s32 i=0; i<4; i++)
1133 //vertices[i].Pos.Y += water_level;
1134 //vertices[i].Pos.Y += neighbor_levels[v3s16(0,0,0)];
1135 vertices[i].Pos.Y += corner_levels[i];
1136 vertices[i].Pos += intToFloat(p + getPosRelative());
1139 u16 indices[] = {0,1,2,2,3,0};
1140 // Add to mesh collector
1141 collector.append(material_water1, vertices, 4, indices, 6);
1145 Add water sources to mesh if using new style
1147 else if(n.d == CONTENT_WATERSOURCE && new_style_water)
1149 //bool top_is_water = false;
1150 bool top_is_air = false;
1152 MapNode n = getNodeParent(v3s16(x,y+1,z));
1153 /*if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
1154 top_is_water = true;*/
1155 if(n.d == CONTENT_AIR)
1157 }catch(InvalidPositionException &e){}
1159 /*if(top_is_water == true)
1161 if(top_is_air == false)
1164 u8 l = decode_light(n.getLightBlend(daynight_ratio));
1165 video::SColor c(WATER_ALPHA,l,l,l);
1167 video::S3DVertex vertices[4] =
1169 /*video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
1170 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
1171 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1172 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1173 video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
1174 pa_water1.x0(), pa_water1.y1()),
1175 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
1176 pa_water1.x1(), pa_water1.y1()),
1177 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1178 pa_water1.x1(), pa_water1.y0()),
1179 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1180 pa_water1.x0(), pa_water1.y0()),
1183 for(s32 i=0; i<4; i++)
1185 vertices[i].Pos.Y += (-0.5+node_water_level)*BS;
1186 vertices[i].Pos += intToFloat(p + getPosRelative());
1189 u16 indices[] = {0,1,2,2,3,0};
1190 // Add to mesh collector
1191 collector.append(material_water1, vertices, 4, indices, 6);
1194 Add leaves if using new style
1196 else if(n.d == CONTENT_LEAVES && new_style_leaves)
1198 u8 l = decode_light(n.getLightBlend(daynight_ratio));
1199 video::SColor c(255,l,l,l);
1201 for(u32 j=0; j<6; j++)
1203 video::S3DVertex vertices[4] =
1205 /*video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, 0,1),
1206 video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, 1,1),
1207 video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c, 1,0),
1208 video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c, 0,0),*/
1209 video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c,
1210 pa_leaves1.x0(), pa_leaves1.y1()),
1211 video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c,
1212 pa_leaves1.x1(), pa_leaves1.y1()),
1213 video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c,
1214 pa_leaves1.x1(), pa_leaves1.y0()),
1215 video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c,
1216 pa_leaves1.x0(), pa_leaves1.y0()),
1221 for(u16 i=0; i<4; i++)
1222 vertices[i].Pos.rotateXZBy(0);
1226 for(u16 i=0; i<4; i++)
1227 vertices[i].Pos.rotateXZBy(180);
1231 for(u16 i=0; i<4; i++)
1232 vertices[i].Pos.rotateXZBy(-90);
1236 for(u16 i=0; i<4; i++)
1237 vertices[i].Pos.rotateXZBy(90);
1241 for(u16 i=0; i<4; i++)
1242 vertices[i].Pos.rotateYZBy(-90);
1246 for(u16 i=0; i<4; i++)
1247 vertices[i].Pos.rotateYZBy(90);
1250 for(u16 i=0; i<4; i++)
1252 vertices[i].Pos += intToFloat(p + getPosRelative());
1255 u16 indices[] = {0,1,2,2,3,0};
1256 // Add to mesh collector
1257 collector.append(material_leaves1, vertices, 4, indices, 6);
1263 Add stuff from collector to mesh
1266 scene::SMesh *mesh_new = NULL;
1267 mesh_new = new scene::SMesh();
1269 collector.fillMesh(mesh_new);
1272 Do some stuff to the mesh
1275 mesh_new->recalculateBoundingBox();
1278 Delete new mesh if it is empty
1281 if(mesh_new->getMeshBufferCount() == 0)
1290 // Usually 1-700 faces and 1-7 materials
1291 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
1292 <<"and uses "<<mesh_new->getMeshBufferCount()
1293 <<" materials (meshbuffers)"<<std::endl;
1296 // Use VBO for mesh (this just would set this for ever buffer)
1297 // This will lead to infinite memory usage because or irrlicht.
1298 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
1301 NOTE: If that is enabled, some kind of a queue to the main
1302 thread should be made which would call irrlicht to delete
1303 the hardware buffer and then delete the mesh
1313 //scene::SMesh *mesh_old = mesh[daynight_i];
1314 //mesh[daynight_i] = mesh_new;
1316 scene::SMesh *mesh_old = mesh;
1318 setMeshExpired(false);
1320 if(mesh_old != NULL)
1322 // Remove hardware buffers of meshbuffers of mesh
1323 // NOTE: No way, this runs in a different thread and everything
1324 /*u32 c = mesh_old->getMeshBufferCount();
1325 for(u32 i=0; i<c; i++)
1327 IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1330 /*dstream<<"mesh_old->getReferenceCount()="
1331 <<mesh_old->getReferenceCount()<<std::endl;
1332 u32 c = mesh_old->getMeshBufferCount();
1333 for(u32 i=0; i<c; i++)
1335 scene::IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1336 dstream<<"buf->getReferenceCount()="
1337 <<buf->getReferenceCount()<<std::endl;
1346 mesh_mutex.Unlock();
1348 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1351 /*void MapBlock::updateMeshes(s32 first_i)
1353 assert(first_i >= 0 && first_i <= DAYNIGHT_CACHE_COUNT);
1354 updateMesh(first_i);
1355 for(s32 i=0; i<DAYNIGHT_CACHE_COUNT; i++)
1366 Propagates sunlight down through the block.
1367 Doesn't modify nodes that are not affected by sunlight.
1369 Returns false if sunlight at bottom block is invalid
1370 Returns true if bottom block doesn't exist.
1372 If there is a block above, continues from it.
1373 If there is no block above, assumes there is sunlight, unless
1374 is_underground is set or highest node is water.
1376 At the moment, all sunlighted nodes are added to light_sources.
1377 - SUGG: This could be optimized
1379 Turns sunglighted mud into grass.
1381 if remove_light==true, sets non-sunlighted nodes black.
1383 if black_air_left!=NULL, it is set to true if non-sunlighted
1384 air is left in block.
1386 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources,
1387 bool remove_light, bool *black_air_left,
1390 // Whether the sunlight at the top of the bottom block is valid
1391 bool block_below_is_valid = true;
1393 v3s16 pos_relative = getPosRelative();
1395 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
1397 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
1400 bool no_sunlight = false;
1401 bool no_top_block = false;
1402 // Check if node above block has sunlight
1404 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1405 if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1410 catch(InvalidPositionException &e)
1412 no_top_block = true;
1414 // NOTE: This makes over-ground roofed places sunlighted
1415 // Assume sunlight, unless is_underground==true
1422 MapNode n = getNode(v3s16(x, MAP_BLOCKSIZE-1, z));
1423 if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
1428 // NOTE: As of now, this just would make everything dark.
1430 //no_sunlight = true;
1433 #if 0 // Doesn't work; nothing gets light.
1434 bool no_sunlight = true;
1435 bool no_top_block = false;
1436 // Check if node above block has sunlight
1438 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1439 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
1441 no_sunlight = false;
1444 catch(InvalidPositionException &e)
1446 no_top_block = true;
1450 /*std::cout<<"("<<x<<","<<z<<"): "
1451 <<"no_top_block="<<no_top_block
1452 <<", is_underground="<<is_underground
1453 <<", no_sunlight="<<no_sunlight
1456 s16 y = MAP_BLOCKSIZE-1;
1458 // This makes difference to diminishing in water.
1459 bool stopped_to_solid_object = false;
1461 u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
1466 MapNode &n = getNodeRef(pos);
1468 if(current_light == 0)
1472 else if(current_light == LIGHT_SUN && n.sunlight_propagates())
1474 // Do nothing: Sunlight is continued
1476 else if(n.light_propagates() == false)
1480 bool upper_is_air = false;
1483 if(getNodeParent(pos+v3s16(0,1,0)).d == CONTENT_AIR)
1484 upper_is_air = true;
1486 catch(InvalidPositionException &e)
1489 // Turn mud into grass
1490 if(upper_is_air && n.d == CONTENT_MUD
1491 && current_light == LIGHT_SUN)
1493 n.d = CONTENT_GRASS;
1497 // A solid object is on the way.
1498 stopped_to_solid_object = true;
1506 current_light = diminish_light(current_light);
1509 u8 old_light = n.getLight(LIGHTBANK_DAY);
1511 if(current_light > old_light || remove_light)
1513 n.setLight(LIGHTBANK_DAY, current_light);
1516 if(diminish_light(current_light) != 0)
1518 light_sources.insert(pos_relative + pos, true);
1521 if(current_light == 0 && stopped_to_solid_object)
1525 *black_air_left = true;
1530 // Whether or not the block below should see LIGHT_SUN
1531 bool sunlight_should_go_down = (current_light == LIGHT_SUN);
1534 If the block below hasn't already been marked invalid:
1536 Check if the node below the block has proper sunlight at top.
1537 If not, the block below is invalid.
1539 Ignore non-transparent nodes as they always have no light
1543 if(block_below_is_valid)
1545 MapNode n = getNodeParent(v3s16(x, -1, z));
1546 if(n.light_propagates())
1548 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN
1549 && sunlight_should_go_down == false)
1550 block_below_is_valid = false;
1551 else if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN
1552 && sunlight_should_go_down == true)
1553 block_below_is_valid = false;
1557 catch(InvalidPositionException &e)
1559 /*std::cout<<"InvalidBlockException for bottom block node"
1561 // Just no block below, no need to panic.
1566 return block_below_is_valid;
1569 void MapBlock::copyTo(VoxelManipulator &dst)
1571 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
1572 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
1574 // Copy from data to VoxelManipulator
1575 dst.copyFrom(data, data_area, v3s16(0,0,0),
1576 getPosRelative(), data_size);
1579 void MapBlock::copyFrom(VoxelManipulator &dst)
1581 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
1582 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
1584 // Copy from VoxelManipulator to data
1585 dst.copyTo(data, data_area, v3s16(0,0,0),
1586 getPosRelative(), data_size);
1589 void MapBlock::stepObjects(float dtime, bool server, u32 daynight_ratio)
1594 m_objects.step(dtime, server, daynight_ratio);
1597 Spawn some objects at random.
1599 Use dayNightDiffed() to approximate being near ground level
1601 if(m_spawn_timer < -999)
1605 if(dayNightDiffed() == true && getObjectCount() == 0)
1607 m_spawn_timer -= dtime;
1608 if(m_spawn_timer <= 0.0)
1610 m_spawn_timer += myrand() % 300;
1613 (myrand()%(MAP_BLOCKSIZE-1))+0,
1614 (myrand()%(MAP_BLOCKSIZE-1))+0
1617 s16 y = getGroundLevel(p2d);
1621 v3s16 p(p2d.X, y+1, p2d.Y);
1623 if(getNode(p).d == CONTENT_AIR
1624 && getNode(p).getLightBlend(daynight_ratio) <= 11)
1626 RatObject *obj = new RatObject(NULL, -1, intToFloat(p));
1637 void MapBlock::updateDayNightDiff()
1641 m_day_night_differs = false;
1645 bool differs = false;
1648 Check if any lighting value differs
1650 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1652 MapNode &n = data[i];
1653 if(n.getLight(LIGHTBANK_DAY) != n.getLight(LIGHTBANK_NIGHT))
1661 If some lighting values differ, check if the whole thing is
1662 just air. If it is, differ = false
1666 bool only_air = true;
1667 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1669 MapNode &n = data[i];
1670 if(n.d != CONTENT_AIR)
1680 // Set member variable
1681 m_day_night_differs = differs;
1684 s16 MapBlock::getGroundLevel(v2s16 p2d)
1690 s16 y = MAP_BLOCKSIZE-1;
1693 //if(is_ground_content(getNodeRef(p2d.X, y, p2d.Y).d))
1694 if(content_features(getNodeRef(p2d.X, y, p2d.Y).d).walkable)
1696 if(y == MAP_BLOCKSIZE-1)
1704 catch(InvalidPositionException &e)
1714 void MapBlock::serialize(std::ostream &os, u8 version)
1716 if(!ser_ver_supported(version))
1717 throw VersionMismatchException("ERROR: MapBlock format not supported");
1721 throw SerializationError("ERROR: Not writing dummy block.");
1724 // These have no compression
1725 if(version <= 3 || version == 5 || version == 6)
1727 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1729 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
1730 SharedBuffer<u8> dest(buflen);
1732 dest[0] = is_underground;
1733 for(u32 i=0; i<nodecount; i++)
1735 u32 s = 1 + i * MapNode::serializedLength(version);
1736 data[i].serialize(&dest[s], version);
1739 os.write((char*)*dest, dest.getSize());
1741 else if(version <= 10)
1745 Compress the materials and the params separately.
1749 os.write((char*)&is_underground, 1);
1751 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1753 // Get and compress materials
1754 SharedBuffer<u8> materialdata(nodecount);
1755 for(u32 i=0; i<nodecount; i++)
1757 materialdata[i] = data[i].d;
1759 compress(materialdata, os, version);
1761 // Get and compress lights
1762 SharedBuffer<u8> lightdata(nodecount);
1763 for(u32 i=0; i<nodecount; i++)
1765 lightdata[i] = data[i].param;
1767 compress(lightdata, os, version);
1771 // Get and compress param2
1772 SharedBuffer<u8> param2data(nodecount);
1773 for(u32 i=0; i<nodecount; i++)
1775 param2data[i] = data[i].param2;
1777 compress(param2data, os, version);
1780 // All other versions (newest)
1787 if(m_day_night_differs)
1789 if(m_lighting_expired)
1791 os.write((char*)&flags, 1);
1793 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1799 SharedBuffer<u8> databuf(nodecount*3);
1802 for(u32 i=0; i<nodecount; i++)
1804 databuf[i] = data[i].d;
1808 for(u32 i=0; i<nodecount; i++)
1810 databuf[i+nodecount] = data[i].param;
1814 for(u32 i=0; i<nodecount; i++)
1816 databuf[i+nodecount*2] = data[i].param2;
1820 Compress data to output stream
1823 compress(databuf, os, version);
1827 void MapBlock::deSerialize(std::istream &is, u8 version)
1829 if(!ser_ver_supported(version))
1830 throw VersionMismatchException("ERROR: MapBlock format not supported");
1832 // These have no compression
1833 if(version <= 3 || version == 5 || version == 6)
1835 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1838 if(is.gcount() != 1)
1839 throw SerializationError
1840 ("MapBlock::deSerialize: no enough input data");
1841 is_underground = tmp;
1842 for(u32 i=0; i<nodecount; i++)
1844 s32 len = MapNode::serializedLength(version);
1845 SharedBuffer<u8> d(len);
1846 is.read((char*)*d, len);
1847 if(is.gcount() != len)
1848 throw SerializationError
1849 ("MapBlock::deSerialize: no enough input data");
1850 data[i].deSerialize(*d, version);
1853 else if(version <= 10)
1855 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1858 is.read((char*)&t8, 1);
1859 is_underground = t8;
1862 // Uncompress and set material data
1863 std::ostringstream os(std::ios_base::binary);
1864 decompress(is, os, version);
1865 std::string s = os.str();
1866 if(s.size() != nodecount)
1867 throw SerializationError
1868 ("MapBlock::deSerialize: invalid format");
1869 for(u32 i=0; i<s.size(); i++)
1875 // Uncompress and set param data
1876 std::ostringstream os(std::ios_base::binary);
1877 decompress(is, os, version);
1878 std::string s = os.str();
1879 if(s.size() != nodecount)
1880 throw SerializationError
1881 ("MapBlock::deSerialize: invalid format");
1882 for(u32 i=0; i<s.size(); i++)
1884 data[i].param = s[i];
1890 // Uncompress and set param2 data
1891 std::ostringstream os(std::ios_base::binary);
1892 decompress(is, os, version);
1893 std::string s = os.str();
1894 if(s.size() != nodecount)
1895 throw SerializationError
1896 ("MapBlock::deSerialize: invalid format");
1897 for(u32 i=0; i<s.size(); i++)
1899 data[i].param2 = s[i];
1903 // All other versions (newest)
1906 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1909 is.read((char*)&flags, 1);
1910 is_underground = (flags & 1) ? true : false;
1911 m_day_night_differs = (flags & 2) ? true : false;
1912 m_lighting_expired = (flags & 3) ? true : false;
1915 std::ostringstream os(std::ios_base::binary);
1916 decompress(is, os, version);
1917 std::string s = os.str();
1918 if(s.size() != nodecount*3)
1919 throw SerializationError
1920 ("MapBlock::deSerialize: invalid format");
1923 for(u32 i=0; i<nodecount; i++)
1928 for(u32 i=0; i<nodecount; i++)
1930 data[i].param = s[i+nodecount];
1933 for(u32 i=0; i<nodecount; i++)
1935 data[i].param2 = s[i+nodecount*2];
1940 Translate nodes as specified in the translate_to fields of
1943 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1945 MapNode &n = data[i];
1947 MapNode *translate_to = content_features(n.d).translate_to;
1950 dstream<<"MapBlock: WARNING: Translating node "<<n.d<<" to "
1951 <<translate_to->d<<std::endl;