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
344 // Get original texture name
345 u32 orig_id = spec.texture.id;
346 std::string orig_name = g_texturesource->getTextureName(orig_id);
347 // Create new texture name
348 std::ostringstream os;
349 os<<orig_name<<"^[crack"<<mod.param;
350 //os<<orig_name<<"^[progressbar0.5";
353 u32 new_id = g_texturesource->getTextureId(os.str());
355 dstream<<"MapBlock::getNodeTile(): Switching from "
356 <<orig_name<<" to "<<os.str()<<" ("
357 <<orig_id<<" to "<<new_id<<")"<<std::endl;
359 spec.texture = g_texturesource->getTexture(new_id);
366 u8 MapBlock::getNodeContent(v3s16 p, MapNode mn, NodeModMap &temp_mods)
369 Check temporary modifications on this node
371 /*core::map<v3s16, NodeMod>::Node *n;
372 n = m_temp_mods.find(p);
376 struct NodeMod mod = n->getValue();*/
378 if(temp_mods.get(p, &mod))
380 if(mod.type == NODEMOD_CHANGECONTENT)
385 if(mod.type == NODEMOD_CRACK)
388 Content doesn't change.
390 face_contents works just like it should, because
391 there should not be faces between differently cracked
394 If a semi-transparent node is cracked in front an
395 another one, it really doesn't matter whether there
396 is a cracked face drawn in between or not.
406 translate_dir: unit vector with only one of x, y or z
407 face_dir: unit vector with only one of x, y or z
409 void MapBlock::updateFastFaceRow(
418 core::array<FastFace> &dest,
419 NodeModMap &temp_mods)
423 u16 continuous_tiles_count = 0;
425 MapNode n0 = getNodeParentNoEx(p);
426 MapNode n1 = getNodeParentNoEx(p + face_dir);
428 u8 light = getFaceLight(daynight_ratio, n0, n1, face_dir);
430 TileSpec tile0 = getNodeTile(n0, p, face_dir, temp_mods);
431 TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir, temp_mods);
433 for(u16 j=0; j<length; j++)
435 bool next_is_different = true;
444 // If at last position, there is nothing to compare to and
445 // the face must be drawn anyway
448 p_next = p + translate_dir;
449 n0_next = getNodeParentNoEx(p_next);
450 n1_next = getNodeParentNoEx(p_next + face_dir);
451 tile0_next = getNodeTile(n0_next, p_next, face_dir, temp_mods);
452 tile1_next = getNodeTile(n1_next,p_next+face_dir,-face_dir, temp_mods);
453 light_next = getFaceLight(daynight_ratio, n0_next, n1_next, face_dir);
455 if(tile0_next == tile0
456 && tile1_next == tile1
457 && light_next == light)
459 next_is_different = false;
463 continuous_tiles_count++;
465 // This is set to true if the texture doesn't allow more tiling
466 bool end_of_texture = false;
468 If there is no texture, it can be tiled infinitely.
469 If tiled==0, it means the texture can be tiled infinitely.
470 Otherwise check tiled agains continuous_tiles_count.
472 This check has to be made for both tiles, because this is
473 a bit hackish and we know which one we're using only when
474 the decision to make the faces is made.
476 if(tile0.texture.atlas != NULL && tile0.texture.tiled != 0)
478 if(tile0.texture.tiled <= continuous_tiles_count)
479 end_of_texture = true;
481 if(tile1.texture.atlas != NULL && tile1.texture.tiled != 0)
483 if(tile1.texture.tiled <= continuous_tiles_count)
484 end_of_texture = true;
487 //end_of_texture = true; //DEBUG
489 if(next_is_different || end_of_texture)
492 Create a face if there should be one
494 //u8 mf = face_contents(tile0, tile1);
496 u8 content0 = getNodeContent(p, n0, temp_mods);
497 u8 content1 = getNodeContent(p + face_dir, n1, temp_mods);
498 u8 mf = face_contents(content0, content1);
502 // Floating point conversion of the position vector
503 v3f pf(p.X, p.Y, p.Z);
504 // Center point of face (kind of)
505 v3f sp = pf - ((f32)continuous_tiles_count / 2. - 0.5) * translate_dir_f;
507 if(translate_dir.X != 0){
508 scale.X = continuous_tiles_count;
510 if(translate_dir.Y != 0){
511 scale.Y = continuous_tiles_count;
513 if(translate_dir.Z != 0){
514 scale.Z = continuous_tiles_count;
519 // If node at sp (tile0) is more solid
522 makeFastFace(tile0, decode_light(light),
524 posRelative_f, dest);
526 // If node at sp is less solid (mf == 2)
529 makeFastFace(tile1, decode_light(light),
530 sp+face_dir_f, -face_dir, scale,
531 posRelative_f, dest);
536 continuous_tiles_count = 0;
549 This is used because CMeshBuffer::append() is very slow
553 video::SMaterial material;
554 core::array<u16> indices;
555 core::array<video::S3DVertex> vertices;
562 video::SMaterial material,
563 const video::S3DVertex* const vertices,
565 const u16* const indices,
569 PreMeshBuffer *p = NULL;
570 for(u32 i=0; i<m_prebuffers.size(); i++)
572 PreMeshBuffer &pp = m_prebuffers[i];
573 if(pp.material != material)
583 pp.material = material;
584 m_prebuffers.push_back(pp);
585 p = &m_prebuffers[m_prebuffers.size()-1];
588 u32 vertex_count = p->vertices.size();
589 for(u32 i=0; i<numIndices; i++)
591 u32 j = indices[i] + vertex_count;
594 dstream<<"FIXME: Meshbuffer ran out of indices"<<std::endl;
595 // NOTE: Fix is to just add an another MeshBuffer
597 p->indices.push_back(j);
599 for(u32 i=0; i<numVertices; i++)
601 p->vertices.push_back(vertices[i]);
605 void fillMesh(scene::SMesh *mesh)
607 /*dstream<<"Filling mesh with "<<m_prebuffers.size()
608 <<" meshbuffers"<<std::endl;*/
609 for(u32 i=0; i<m_prebuffers.size(); i++)
611 PreMeshBuffer &p = m_prebuffers[i];
613 /*dstream<<"p.vertices.size()="<<p.vertices.size()
614 <<", p.indices.size()="<<p.indices.size()
619 // This is a "Standard MeshBuffer",
620 // it's a typedeffed CMeshBuffer<video::S3DVertex>
621 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
623 buf->Material = p.material;
624 //((scene::SMeshBuffer*)buf)->Material = p.material;
626 //buf->setHardwareMappingHint(scene::EHM_STATIC);
628 mesh->addMeshBuffer(buf);
632 buf->append(p.vertices.pointer(), p.vertices.size(),
633 p.indices.pointer(), p.indices.size());
638 core::array<PreMeshBuffer> m_prebuffers;
641 void MapBlock::updateMesh(u32 daynight_ratio)
645 DEBUG: If mesh has been generated, don't generate it again
648 JMutexAutoLock meshlock(mesh_mutex);
654 // 4-21ms for MAP_BLOCKSIZE=16
655 // 24-155ms for MAP_BLOCKSIZE=32
656 //TimeTaker timer1("updateMesh()");
658 core::array<FastFace> fastfaces_new;
660 v3f posRelative_f(getPosRelative().X, getPosRelative().Y,
661 getPosRelative().Z); // floating point conversion
664 Avoid interlocks by copying m_temp_mods
666 NodeModMap temp_mods;
668 JMutexAutoLock lock(m_temp_mods_mutex);
669 m_temp_mods.copy(temp_mods);
675 bool new_style_water = g_settings.getBool("new_style_water");
676 bool new_style_leaves = g_settings.getBool("new_style_leaves");
678 float node_water_level = 1.0;
680 node_water_level = 0.9;
683 We are including the faces of the trailing edges of the block.
684 This means that when something changes, the caller must
685 also update the meshes of the blocks at the leading edges.
687 NOTE: This is the slowest part of this method.
691 // 4-23ms for MAP_BLOCKSIZE=16
692 //TimeTaker timer2("updateMesh() collect");
695 Go through every y,z and get top faces in rows of x+
697 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
698 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
699 updateFastFaceRow(daynight_ratio, posRelative_f,
700 v3s16(0,y,z), MAP_BLOCKSIZE,
703 v3s16(0,1,0), //face dir
710 Go through every x,y and get right faces in rows of z+
712 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
713 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
714 updateFastFaceRow(daynight_ratio, posRelative_f,
715 v3s16(x,y,0), MAP_BLOCKSIZE,
725 Go through every y,z and get back faces in rows of x+
727 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
728 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
729 updateFastFaceRow(daynight_ratio, posRelative_f,
730 v3s16(0,y,z), MAP_BLOCKSIZE,
744 Convert FastFaces to SMesh
747 MeshCollector collector;
749 if(fastfaces_new.size() > 0)
751 // avg 0ms (100ms spikes when loading textures the first time)
752 //TimeTaker timer2("updateMesh() mesh building");
754 video::SMaterial material;
755 material.Lighting = false;
756 material.BackfaceCulling = false;
757 material.setFlag(video::EMF_BILINEAR_FILTER, false);
758 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
759 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_SIMPLE);
760 material.setFlag(video::EMF_FOG_ENABLE, true);
762 for(u32 i=0; i<fastfaces_new.size(); i++)
764 FastFace &f = fastfaces_new[i];
766 const u16 indices[] = {0,1,2,2,3,0};
768 //video::ITexture *texture = g_irrlicht->getTexture(f.tile.spec);
769 video::ITexture *texture = f.tile.texture.atlas;
773 material.setTexture(0, texture);
775 f.tile.applyMaterialOptions(material);
777 collector.append(material, f.vertices, 4, indices, 6);
782 Add special graphics:
788 //TimeTaker timer2("updateMesh() adding special stuff");
790 // Flowing water material
791 video::SMaterial material_water1;
792 material_water1.setFlag(video::EMF_LIGHTING, false);
793 material_water1.setFlag(video::EMF_BACK_FACE_CULLING, false);
794 material_water1.setFlag(video::EMF_BILINEAR_FILTER, false);
795 material_water1.setFlag(video::EMF_FOG_ENABLE, true);
796 material_water1.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
798 //material_water1.setTexture(0, g_irrlicht->getTexture("water.png"));
799 AtlasPointer pa_water1 = g_texturesource->getTexture(
800 g_texturesource->getTextureId("water.png"));
801 material_water1.setTexture(0, pa_water1.atlas);
803 // New-style leaves material
804 video::SMaterial material_leaves1;
805 material_leaves1.setFlag(video::EMF_LIGHTING, false);
806 //material_leaves1.setFlag(video::EMF_BACK_FACE_CULLING, false);
807 material_leaves1.setFlag(video::EMF_BILINEAR_FILTER, false);
808 material_leaves1.setFlag(video::EMF_FOG_ENABLE, true);
809 material_leaves1.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
811 //material_leaves1.setTexture(0, g_irrlicht->getTexture("leaves.png"));
812 AtlasPointer pa_leaves1 = g_texturesource->getTexture(
813 g_texturesource->getTextureId("leaves.png"));
814 material_leaves1.setTexture(0, pa_leaves1.atlas);
816 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
817 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
818 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
822 MapNode &n = getNodeRef(x,y,z);
827 if(n.d == CONTENT_TORCH)
829 video::SColor c(255,255,255,255);
831 video::S3DVertex vertices[4] =
833 video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c, 0,1),
834 video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c, 1,1),
835 video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, 1,0),
836 video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c, 0,0),
839 v3s16 dir = unpackDir(n.dir);
841 for(s32 i=0; i<4; i++)
843 if(dir == v3s16(1,0,0))
844 vertices[i].Pos.rotateXZBy(0);
845 if(dir == v3s16(-1,0,0))
846 vertices[i].Pos.rotateXZBy(180);
847 if(dir == v3s16(0,0,1))
848 vertices[i].Pos.rotateXZBy(90);
849 if(dir == v3s16(0,0,-1))
850 vertices[i].Pos.rotateXZBy(-90);
851 if(dir == v3s16(0,-1,0))
852 vertices[i].Pos.rotateXZBy(45);
853 if(dir == v3s16(0,1,0))
854 vertices[i].Pos.rotateXZBy(-45);
856 vertices[i].Pos += intToFloat(p + getPosRelative());
860 video::SMaterial material;
861 material.setFlag(video::EMF_LIGHTING, false);
862 material.setFlag(video::EMF_BACK_FACE_CULLING, false);
863 material.setFlag(video::EMF_BILINEAR_FILTER, false);
864 //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
865 material.MaterialType
866 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
868 /*if(dir == v3s16(0,-1,0))
869 material.setTexture(0,
870 g_irrlicht->getTexture("torch_on_floor.png"));
871 else if(dir == v3s16(0,1,0))
872 material.setTexture(0,
873 g_irrlicht->getTexture("torch_on_ceiling.png"));
874 // For backwards compatibility
875 else if(dir == v3s16(0,0,0))
876 material.setTexture(0,
877 g_irrlicht->getTexture("torch_on_floor.png"));
879 material.setTexture(0,
880 g_irrlicht->getTexture("torch.png"));*/
882 u16 indices[] = {0,1,2,2,3,0};
883 // Add to mesh collector
884 collector.append(material, vertices, 4, indices, 6);
887 Add flowing water to mesh
889 else if(n.d == CONTENT_WATER)
891 bool top_is_water = false;
893 MapNode n = getNodeParent(v3s16(x,y+1,z));
894 if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
896 }catch(InvalidPositionException &e){}
898 u8 l = decode_light(n.getLightBlend(daynight_ratio));
899 video::SColor c(WATER_ALPHA,l,l,l);
901 // Neighbor water levels (key = relative position)
902 // Includes current node
903 core::map<v3s16, f32> neighbor_levels;
904 core::map<v3s16, u8> neighbor_contents;
905 core::map<v3s16, u8> neighbor_flags;
906 const u8 neighborflag_top_is_water = 0x01;
907 v3s16 neighbor_dirs[9] = {
918 for(u32 i=0; i<9; i++)
920 u8 content = CONTENT_AIR;
921 float level = -0.5 * BS;
925 v3s16 p2 = p + neighbor_dirs[i];
926 MapNode n2 = getNodeParent(p2);
930 if(n2.d == CONTENT_WATERSOURCE)
931 level = (-0.5+node_water_level) * BS;
932 else if(n2.d == CONTENT_WATER)
933 level = (-0.5 + ((float)n2.param2 + 0.5) / 8.0
934 * node_water_level) * BS;
936 // Check node above neighbor.
937 // NOTE: This doesn't get executed if neighbor
940 n2 = getNodeParent(p2);
941 if(n2.d == CONTENT_WATERSOURCE || n2.d == CONTENT_WATER)
942 flags |= neighborflag_top_is_water;
944 catch(InvalidPositionException &e){}
946 neighbor_levels.insert(neighbor_dirs[i], level);
947 neighbor_contents.insert(neighbor_dirs[i], content);
948 neighbor_flags.insert(neighbor_dirs[i], flags);
951 //float water_level = (-0.5 + ((float)n.param2 + 0.5) / 8.0) * BS;
952 //float water_level = neighbor_levels[v3s16(0,0,0)];
954 // Corner heights (average between four waters)
955 f32 corner_levels[4];
957 v3s16 halfdirs[4] = {
963 for(u32 i=0; i<4; i++)
965 v3s16 cornerdir = halfdirs[i];
966 float cornerlevel = 0;
968 for(u32 j=0; j<4; j++)
970 v3s16 neighbordir = cornerdir - halfdirs[j];
971 u8 content = neighbor_contents[neighbordir];
972 // Special case for source nodes
973 if(content == CONTENT_WATERSOURCE)
975 cornerlevel = (-0.5+node_water_level)*BS;
979 else if(content == CONTENT_WATER)
981 cornerlevel += neighbor_levels[neighbordir];
984 else if(content == CONTENT_AIR)
986 cornerlevel += -0.5*BS;
991 cornerlevel /= valid_count;
992 corner_levels[i] = cornerlevel;
999 v3s16 side_dirs[4] = {
1005 s16 side_corners[4][2] = {
1011 for(u32 i=0; i<4; i++)
1013 v3s16 dir = side_dirs[i];
1016 If our topside is water and neighbor's topside
1017 is water, don't draw side face
1020 neighbor_flags[dir] & neighborflag_top_is_water)
1023 u8 neighbor_content = neighbor_contents[dir];
1025 // Don't draw face if neighbor is not air or water
1026 if(neighbor_content != CONTENT_AIR
1027 && neighbor_content != CONTENT_WATER)
1030 bool neighbor_is_water = (neighbor_content == CONTENT_WATER);
1032 // Don't draw any faces if neighbor is water and top is water
1033 if(neighbor_is_water == true && top_is_water == false)
1036 video::S3DVertex vertices[4] =
1038 /*video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1),
1039 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1),
1040 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1041 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1042 video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
1043 pa_water1.x0(), pa_water1.y1()),
1044 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
1045 pa_water1.x1(), pa_water1.y1()),
1046 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1047 pa_water1.x1(), pa_water1.y0()),
1048 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1049 pa_water1.x0(), pa_water1.y0()),
1053 If our topside is water, set upper border of face
1054 at upper border of node
1058 vertices[2].Pos.Y = 0.5*BS;
1059 vertices[3].Pos.Y = 0.5*BS;
1062 Otherwise upper position of face is corner levels
1066 vertices[2].Pos.Y = corner_levels[side_corners[i][0]];
1067 vertices[3].Pos.Y = corner_levels[side_corners[i][1]];
1071 If neighbor is water, lower border of face is corner
1074 if(neighbor_is_water)
1076 vertices[0].Pos.Y = corner_levels[side_corners[i][1]];
1077 vertices[1].Pos.Y = corner_levels[side_corners[i][0]];
1080 If neighbor is not water, lower border of face is
1081 lower border of node
1085 vertices[0].Pos.Y = -0.5*BS;
1086 vertices[1].Pos.Y = -0.5*BS;
1089 for(s32 j=0; j<4; j++)
1091 if(dir == v3s16(0,0,1))
1092 vertices[j].Pos.rotateXZBy(0);
1093 if(dir == v3s16(0,0,-1))
1094 vertices[j].Pos.rotateXZBy(180);
1095 if(dir == v3s16(-1,0,0))
1096 vertices[j].Pos.rotateXZBy(90);
1097 if(dir == v3s16(1,0,-0))
1098 vertices[j].Pos.rotateXZBy(-90);
1100 vertices[j].Pos += intToFloat(p + getPosRelative());
1103 u16 indices[] = {0,1,2,2,3,0};
1104 // Add to mesh collector
1105 collector.append(material_water1, vertices, 4, indices, 6);
1109 Generate top side, if appropriate
1112 if(top_is_water == false)
1114 video::S3DVertex vertices[4] =
1116 /*video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
1117 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
1118 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1119 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1120 video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
1121 pa_water1.x0(), pa_water1.y1()),
1122 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
1123 pa_water1.x1(), pa_water1.y1()),
1124 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1125 pa_water1.x1(), pa_water1.y0()),
1126 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1127 pa_water1.x0(), pa_water1.y0()),
1130 for(s32 i=0; i<4; i++)
1132 //vertices[i].Pos.Y += water_level;
1133 //vertices[i].Pos.Y += neighbor_levels[v3s16(0,0,0)];
1134 vertices[i].Pos.Y += corner_levels[i];
1135 vertices[i].Pos += intToFloat(p + getPosRelative());
1138 u16 indices[] = {0,1,2,2,3,0};
1139 // Add to mesh collector
1140 collector.append(material_water1, vertices, 4, indices, 6);
1144 Add water sources to mesh if using new style
1146 else if(n.d == CONTENT_WATERSOURCE && new_style_water)
1148 //bool top_is_water = false;
1149 bool top_is_air = false;
1151 MapNode n = getNodeParent(v3s16(x,y+1,z));
1152 /*if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
1153 top_is_water = true;*/
1154 if(n.d == CONTENT_AIR)
1156 }catch(InvalidPositionException &e){}
1158 /*if(top_is_water == true)
1160 if(top_is_air == false)
1163 u8 l = decode_light(n.getLightBlend(daynight_ratio));
1164 video::SColor c(WATER_ALPHA,l,l,l);
1166 video::S3DVertex vertices[4] =
1168 /*video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
1169 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
1170 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1171 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1172 video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
1173 pa_water1.x0(), pa_water1.y1()),
1174 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
1175 pa_water1.x1(), pa_water1.y1()),
1176 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1177 pa_water1.x1(), pa_water1.y0()),
1178 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1179 pa_water1.x0(), pa_water1.y0()),
1182 for(s32 i=0; i<4; i++)
1184 vertices[i].Pos.Y += (-0.5+node_water_level)*BS;
1185 vertices[i].Pos += intToFloat(p + getPosRelative());
1188 u16 indices[] = {0,1,2,2,3,0};
1189 // Add to mesh collector
1190 collector.append(material_water1, vertices, 4, indices, 6);
1193 Add leaves if using new style
1195 else if(n.d == CONTENT_LEAVES && new_style_leaves)
1197 u8 l = decode_light(n.getLightBlend(daynight_ratio));
1198 video::SColor c(255,l,l,l);
1200 for(u32 j=0; j<6; j++)
1202 video::S3DVertex vertices[4] =
1204 /*video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, 0,1),
1205 video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, 1,1),
1206 video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c, 1,0),
1207 video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c, 0,0),*/
1208 video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c,
1209 pa_leaves1.x0(), pa_leaves1.y1()),
1210 video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c,
1211 pa_leaves1.x1(), pa_leaves1.y1()),
1212 video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c,
1213 pa_leaves1.x1(), pa_leaves1.y0()),
1214 video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c,
1215 pa_leaves1.x0(), pa_leaves1.y0()),
1220 for(u16 i=0; i<4; i++)
1221 vertices[i].Pos.rotateXZBy(0);
1225 for(u16 i=0; i<4; i++)
1226 vertices[i].Pos.rotateXZBy(180);
1230 for(u16 i=0; i<4; i++)
1231 vertices[i].Pos.rotateXZBy(-90);
1235 for(u16 i=0; i<4; i++)
1236 vertices[i].Pos.rotateXZBy(90);
1240 for(u16 i=0; i<4; i++)
1241 vertices[i].Pos.rotateYZBy(-90);
1245 for(u16 i=0; i<4; i++)
1246 vertices[i].Pos.rotateYZBy(90);
1249 for(u16 i=0; i<4; i++)
1251 vertices[i].Pos += intToFloat(p + getPosRelative());
1254 u16 indices[] = {0,1,2,2,3,0};
1255 // Add to mesh collector
1256 collector.append(material_leaves1, vertices, 4, indices, 6);
1262 Add stuff from collector to mesh
1265 scene::SMesh *mesh_new = NULL;
1266 mesh_new = new scene::SMesh();
1268 collector.fillMesh(mesh_new);
1271 Do some stuff to the mesh
1274 mesh_new->recalculateBoundingBox();
1277 Delete new mesh if it is empty
1280 if(mesh_new->getMeshBufferCount() == 0)
1289 // Usually 1-700 faces and 1-7 materials
1290 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
1291 <<"and uses "<<mesh_new->getMeshBufferCount()
1292 <<" materials (meshbuffers)"<<std::endl;
1295 // Use VBO for mesh (this just would set this for ever buffer)
1296 // This will lead to infinite memory usage because or irrlicht.
1297 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
1300 NOTE: If that is enabled, some kind of a queue to the main
1301 thread should be made which would call irrlicht to delete
1302 the hardware buffer and then delete the mesh
1312 //scene::SMesh *mesh_old = mesh[daynight_i];
1313 //mesh[daynight_i] = mesh_new;
1315 scene::SMesh *mesh_old = mesh;
1317 setMeshExpired(false);
1319 if(mesh_old != NULL)
1321 // Remove hardware buffers of meshbuffers of mesh
1322 // NOTE: No way, this runs in a different thread and everything
1323 /*u32 c = mesh_old->getMeshBufferCount();
1324 for(u32 i=0; i<c; i++)
1326 IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1329 /*dstream<<"mesh_old->getReferenceCount()="
1330 <<mesh_old->getReferenceCount()<<std::endl;
1331 u32 c = mesh_old->getMeshBufferCount();
1332 for(u32 i=0; i<c; i++)
1334 scene::IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1335 dstream<<"buf->getReferenceCount()="
1336 <<buf->getReferenceCount()<<std::endl;
1345 mesh_mutex.Unlock();
1347 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1350 /*void MapBlock::updateMeshes(s32 first_i)
1352 assert(first_i >= 0 && first_i <= DAYNIGHT_CACHE_COUNT);
1353 updateMesh(first_i);
1354 for(s32 i=0; i<DAYNIGHT_CACHE_COUNT; i++)
1365 Propagates sunlight down through the block.
1366 Doesn't modify nodes that are not affected by sunlight.
1368 Returns false if sunlight at bottom block is invalid
1369 Returns true if bottom block doesn't exist.
1371 If there is a block above, continues from it.
1372 If there is no block above, assumes there is sunlight, unless
1373 is_underground is set or highest node is water.
1375 At the moment, all sunlighted nodes are added to light_sources.
1376 - SUGG: This could be optimized
1378 Turns sunglighted mud into grass.
1380 if remove_light==true, sets non-sunlighted nodes black.
1382 if black_air_left!=NULL, it is set to true if non-sunlighted
1383 air is left in block.
1385 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources,
1386 bool remove_light, bool *black_air_left,
1389 // Whether the sunlight at the top of the bottom block is valid
1390 bool block_below_is_valid = true;
1392 v3s16 pos_relative = getPosRelative();
1394 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
1396 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
1399 bool no_sunlight = false;
1400 bool no_top_block = false;
1401 // Check if node above block has sunlight
1403 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1404 if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1409 catch(InvalidPositionException &e)
1411 no_top_block = true;
1413 // NOTE: This makes over-ground roofed places sunlighted
1414 // Assume sunlight, unless is_underground==true
1421 MapNode n = getNode(v3s16(x, MAP_BLOCKSIZE-1, z));
1422 if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
1427 // NOTE: As of now, this just would make everything dark.
1429 //no_sunlight = true;
1432 #if 0 // Doesn't work; nothing gets light.
1433 bool no_sunlight = true;
1434 bool no_top_block = false;
1435 // Check if node above block has sunlight
1437 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1438 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
1440 no_sunlight = false;
1443 catch(InvalidPositionException &e)
1445 no_top_block = true;
1449 /*std::cout<<"("<<x<<","<<z<<"): "
1450 <<"no_top_block="<<no_top_block
1451 <<", is_underground="<<is_underground
1452 <<", no_sunlight="<<no_sunlight
1455 s16 y = MAP_BLOCKSIZE-1;
1457 // This makes difference to diminishing in water.
1458 bool stopped_to_solid_object = false;
1460 u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
1465 MapNode &n = getNodeRef(pos);
1467 if(current_light == 0)
1471 else if(current_light == LIGHT_SUN && n.sunlight_propagates())
1473 // Do nothing: Sunlight is continued
1475 else if(n.light_propagates() == false)
1479 bool upper_is_air = false;
1482 if(getNodeParent(pos+v3s16(0,1,0)).d == CONTENT_AIR)
1483 upper_is_air = true;
1485 catch(InvalidPositionException &e)
1488 // Turn mud into grass
1489 if(upper_is_air && n.d == CONTENT_MUD
1490 && current_light == LIGHT_SUN)
1492 n.d = CONTENT_GRASS;
1496 // A solid object is on the way.
1497 stopped_to_solid_object = true;
1505 current_light = diminish_light(current_light);
1508 u8 old_light = n.getLight(LIGHTBANK_DAY);
1510 if(current_light > old_light || remove_light)
1512 n.setLight(LIGHTBANK_DAY, current_light);
1515 if(diminish_light(current_light) != 0)
1517 light_sources.insert(pos_relative + pos, true);
1520 if(current_light == 0 && stopped_to_solid_object)
1524 *black_air_left = true;
1529 // Whether or not the block below should see LIGHT_SUN
1530 bool sunlight_should_go_down = (current_light == LIGHT_SUN);
1533 If the block below hasn't already been marked invalid:
1535 Check if the node below the block has proper sunlight at top.
1536 If not, the block below is invalid.
1538 Ignore non-transparent nodes as they always have no light
1542 if(block_below_is_valid)
1544 MapNode n = getNodeParent(v3s16(x, -1, z));
1545 if(n.light_propagates())
1547 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN
1548 && sunlight_should_go_down == false)
1549 block_below_is_valid = false;
1550 else if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN
1551 && sunlight_should_go_down == true)
1552 block_below_is_valid = false;
1556 catch(InvalidPositionException &e)
1558 /*std::cout<<"InvalidBlockException for bottom block node"
1560 // Just no block below, no need to panic.
1565 return block_below_is_valid;
1568 void MapBlock::copyTo(VoxelManipulator &dst)
1570 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
1571 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
1573 // Copy from data to VoxelManipulator
1574 dst.copyFrom(data, data_area, v3s16(0,0,0),
1575 getPosRelative(), data_size);
1578 void MapBlock::copyFrom(VoxelManipulator &dst)
1580 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
1581 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
1583 // Copy from VoxelManipulator to data
1584 dst.copyTo(data, data_area, v3s16(0,0,0),
1585 getPosRelative(), data_size);
1588 void MapBlock::stepObjects(float dtime, bool server, u32 daynight_ratio)
1593 m_objects.step(dtime, server, daynight_ratio);
1596 Spawn some objects at random.
1598 Use dayNightDiffed() to approximate being near ground level
1600 if(m_spawn_timer < -999)
1604 if(dayNightDiffed() == true && getObjectCount() == 0)
1606 m_spawn_timer -= dtime;
1607 if(m_spawn_timer <= 0.0)
1609 m_spawn_timer += myrand() % 300;
1612 (myrand()%(MAP_BLOCKSIZE-1))+0,
1613 (myrand()%(MAP_BLOCKSIZE-1))+0
1616 s16 y = getGroundLevel(p2d);
1620 v3s16 p(p2d.X, y+1, p2d.Y);
1622 if(getNode(p).d == CONTENT_AIR
1623 && getNode(p).getLightBlend(daynight_ratio) <= 11)
1625 RatObject *obj = new RatObject(NULL, -1, intToFloat(p));
1636 void MapBlock::updateDayNightDiff()
1640 m_day_night_differs = false;
1644 bool differs = false;
1647 Check if any lighting value differs
1649 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1651 MapNode &n = data[i];
1652 if(n.getLight(LIGHTBANK_DAY) != n.getLight(LIGHTBANK_NIGHT))
1660 If some lighting values differ, check if the whole thing is
1661 just air. If it is, differ = false
1665 bool only_air = true;
1666 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1668 MapNode &n = data[i];
1669 if(n.d != CONTENT_AIR)
1679 // Set member variable
1680 m_day_night_differs = differs;
1683 s16 MapBlock::getGroundLevel(v2s16 p2d)
1689 s16 y = MAP_BLOCKSIZE-1;
1692 //if(is_ground_content(getNodeRef(p2d.X, y, p2d.Y).d))
1693 if(content_features(getNodeRef(p2d.X, y, p2d.Y).d).walkable)
1695 if(y == MAP_BLOCKSIZE-1)
1703 catch(InvalidPositionException &e)
1713 void MapBlock::serialize(std::ostream &os, u8 version)
1715 if(!ser_ver_supported(version))
1716 throw VersionMismatchException("ERROR: MapBlock format not supported");
1720 throw SerializationError("ERROR: Not writing dummy block.");
1723 // These have no compression
1724 if(version <= 3 || version == 5 || version == 6)
1726 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1728 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
1729 SharedBuffer<u8> dest(buflen);
1731 dest[0] = is_underground;
1732 for(u32 i=0; i<nodecount; i++)
1734 u32 s = 1 + i * MapNode::serializedLength(version);
1735 data[i].serialize(&dest[s], version);
1738 os.write((char*)*dest, dest.getSize());
1740 else if(version <= 10)
1744 Compress the materials and the params separately.
1748 os.write((char*)&is_underground, 1);
1750 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1752 // Get and compress materials
1753 SharedBuffer<u8> materialdata(nodecount);
1754 for(u32 i=0; i<nodecount; i++)
1756 materialdata[i] = data[i].d;
1758 compress(materialdata, os, version);
1760 // Get and compress lights
1761 SharedBuffer<u8> lightdata(nodecount);
1762 for(u32 i=0; i<nodecount; i++)
1764 lightdata[i] = data[i].param;
1766 compress(lightdata, os, version);
1770 // Get and compress param2
1771 SharedBuffer<u8> param2data(nodecount);
1772 for(u32 i=0; i<nodecount; i++)
1774 param2data[i] = data[i].param2;
1776 compress(param2data, os, version);
1779 // All other versions (newest)
1786 if(m_day_night_differs)
1788 if(m_lighting_expired)
1790 os.write((char*)&flags, 1);
1792 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1798 SharedBuffer<u8> databuf(nodecount*3);
1801 for(u32 i=0; i<nodecount; i++)
1803 databuf[i] = data[i].d;
1807 for(u32 i=0; i<nodecount; i++)
1809 databuf[i+nodecount] = data[i].param;
1813 for(u32 i=0; i<nodecount; i++)
1815 databuf[i+nodecount*2] = data[i].param2;
1819 Compress data to output stream
1822 compress(databuf, os, version);
1826 void MapBlock::deSerialize(std::istream &is, u8 version)
1828 if(!ser_ver_supported(version))
1829 throw VersionMismatchException("ERROR: MapBlock format not supported");
1831 // These have no compression
1832 if(version <= 3 || version == 5 || version == 6)
1834 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1837 if(is.gcount() != 1)
1838 throw SerializationError
1839 ("MapBlock::deSerialize: no enough input data");
1840 is_underground = tmp;
1841 for(u32 i=0; i<nodecount; i++)
1843 s32 len = MapNode::serializedLength(version);
1844 SharedBuffer<u8> d(len);
1845 is.read((char*)*d, len);
1846 if(is.gcount() != len)
1847 throw SerializationError
1848 ("MapBlock::deSerialize: no enough input data");
1849 data[i].deSerialize(*d, version);
1852 else if(version <= 10)
1854 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1857 is.read((char*)&t8, 1);
1858 is_underground = t8;
1861 // Uncompress and set material data
1862 std::ostringstream os(std::ios_base::binary);
1863 decompress(is, os, version);
1864 std::string s = os.str();
1865 if(s.size() != nodecount)
1866 throw SerializationError
1867 ("MapBlock::deSerialize: invalid format");
1868 for(u32 i=0; i<s.size(); i++)
1874 // Uncompress and set param data
1875 std::ostringstream os(std::ios_base::binary);
1876 decompress(is, os, version);
1877 std::string s = os.str();
1878 if(s.size() != nodecount)
1879 throw SerializationError
1880 ("MapBlock::deSerialize: invalid format");
1881 for(u32 i=0; i<s.size(); i++)
1883 data[i].param = s[i];
1889 // Uncompress and set param2 data
1890 std::ostringstream os(std::ios_base::binary);
1891 decompress(is, os, version);
1892 std::string s = os.str();
1893 if(s.size() != nodecount)
1894 throw SerializationError
1895 ("MapBlock::deSerialize: invalid format");
1896 for(u32 i=0; i<s.size(); i++)
1898 data[i].param2 = s[i];
1902 // All other versions (newest)
1905 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1908 is.read((char*)&flags, 1);
1909 is_underground = (flags & 1) ? true : false;
1910 m_day_night_differs = (flags & 2) ? true : false;
1911 m_lighting_expired = (flags & 3) ? true : false;
1914 std::ostringstream os(std::ios_base::binary);
1915 decompress(is, os, version);
1916 std::string s = os.str();
1917 if(s.size() != nodecount*3)
1918 throw SerializationError
1919 ("MapBlock::deSerialize: invalid format");
1922 for(u32 i=0; i<nodecount; i++)
1927 for(u32 i=0; i<nodecount; i++)
1929 data[i].param = s[i+nodecount];
1932 for(u32 i=0; i<nodecount; i++)
1934 data[i].param2 = s[i+nodecount*2];
1939 Translate nodes as specified in the translate_to fields of
1942 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1944 MapNode &n = data[i];
1946 MapNode *translate_to = content_features(n.d).translate_to;
1949 dstream<<"MapBlock: WARNING: Translating node "<<n.d<<" to "
1950 <<translate_to->d<<std::endl;