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,
152 u8 l1 = n.getLightBlend(daynight_ratio);
153 u8 l2 = n2.getLightBlend(daynight_ratio);
159 // Make some nice difference to different sides
161 // This makes light come from a corner
162 /*if(face_dir.X == 1 || face_dir.Z == 1 || face_dir.Y == -1)
163 light = diminish_light(diminish_light(light));
164 else if(face_dir.X == -1 || face_dir.Z == -1)
165 light = diminish_light(light);*/
167 // All neighboring faces have different shade (like in minecraft)
168 if(face_dir.X == 1 || face_dir.X == -1 || face_dir.Y == -1)
169 light = diminish_light(diminish_light(light));
170 else if(face_dir.Z == 1 || face_dir.Z == -1)
171 light = diminish_light(light);
175 catch(InvalidPositionException &e)
183 void MapBlock::makeFastFace(TileSpec tile, u8 light, v3f p,
184 v3s16 dir, v3f scale, v3f posRelative_f,
185 core::array<FastFace> &dest)
189 // Position is at the center of the cube.
194 // If looking towards z+, this is the face that is behind
195 // the center point, facing towards z+.
196 vertex_pos[0] = v3f(-BS/2,-BS/2,BS/2);
197 vertex_pos[1] = v3f( BS/2,-BS/2,BS/2);
198 vertex_pos[2] = v3f( BS/2, BS/2,BS/2);
199 vertex_pos[3] = v3f(-BS/2, BS/2,BS/2);
201 if(dir == v3s16(0,0,1))
203 for(u16 i=0; i<4; i++)
204 vertex_pos[i].rotateXZBy(0);
206 else if(dir == v3s16(0,0,-1))
208 for(u16 i=0; i<4; i++)
209 vertex_pos[i].rotateXZBy(180);
211 else if(dir == v3s16(1,0,0))
213 for(u16 i=0; i<4; i++)
214 vertex_pos[i].rotateXZBy(-90);
216 else if(dir == v3s16(-1,0,0))
218 for(u16 i=0; i<4; i++)
219 vertex_pos[i].rotateXZBy(90);
221 else if(dir == v3s16(0,1,0))
223 for(u16 i=0; i<4; i++)
224 vertex_pos[i].rotateYZBy(-90);
226 else if(dir == v3s16(0,-1,0))
228 for(u16 i=0; i<4; i++)
229 vertex_pos[i].rotateYZBy(90);
232 for(u16 i=0; i<4; i++)
234 vertex_pos[i].X *= scale.X;
235 vertex_pos[i].Y *= scale.Y;
236 vertex_pos[i].Z *= scale.Z;
237 vertex_pos[i] += pos + posRelative_f;
241 if (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X;
242 else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
243 else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
245 v3f zerovector = v3f(0,0,0);
247 //u8 li = decode_light(light);
249 //u8 li = 255; //DEBUG
251 u8 alpha = tile.alpha;
253 if(tile.id == TILE_WATER)
254 alpha = WATER_ALPHA;*/
256 video::SColor c = video::SColor(alpha,li,li,li);
258 float x0 = tile.texture.pos.X;
259 float y0 = tile.texture.pos.Y;
260 float w = tile.texture.size.X;
261 float h = tile.texture.size.Y;
263 face.vertices[0] = video::S3DVertex(vertex_pos[0], v3f(0,1,0), c,
264 core::vector2d<f32>(x0+w*abs_scale, y0+h));
265 face.vertices[1] = video::S3DVertex(vertex_pos[1], v3f(0,1,0), c,
266 core::vector2d<f32>(x0, y0+h));
267 face.vertices[2] = video::S3DVertex(vertex_pos[2], v3f(0,1,0), c,
268 core::vector2d<f32>(x0, y0));
269 face.vertices[3] = video::S3DVertex(vertex_pos[3], v3f(0,1,0), c,
270 core::vector2d<f32>(x0+w*abs_scale, y0));
274 //f->tile = TILE_STONE;
276 dest.push_back(face);
281 Gets node tile from any place relative to block.
282 Returns TILE_NODE if doesn't exist or should not be drawn.
284 TileSpec MapBlock::getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir,
285 NodeModMap &temp_mods)
288 spec = mn.getTile(face_dir);
291 Check temporary modifications on this node
293 /*core::map<v3s16, NodeMod>::Node *n;
294 n = m_temp_mods.find(p);
298 struct NodeMod mod = n->getValue();*/
300 if(temp_mods.get(p, &mod))
302 if(mod.type == NODEMOD_CHANGECONTENT)
304 MapNode mn2(mod.param);
305 spec = mn2.getTile(face_dir);
307 if(mod.type == NODEMOD_CRACK)
310 Get texture id, translate it to name, append stuff to
314 // Get original texture name
315 u32 orig_id = spec.texture.id;
316 std::string orig_name = g_texturesource->getTextureName(orig_id);
318 // Create new texture name
319 std::ostringstream os;
320 os<<orig_name<<"^[crack"<<mod.param;
323 u32 new_id = g_texturesource->getTextureId(os.str());
325 /*dstream<<"MapBlock::getNodeTile(): Switching from "
326 <<orig_name<<" to "<<os.str()<<" ("
327 <<orig_id<<" to "<<new_id<<")"<<std::endl;*/
329 spec.texture = g_texturesource->getTexture(new_id);
336 u8 MapBlock::getNodeContent(v3s16 p, MapNode mn, NodeModMap &temp_mods)
339 Check temporary modifications on this node
341 /*core::map<v3s16, NodeMod>::Node *n;
342 n = m_temp_mods.find(p);
346 struct NodeMod mod = n->getValue();*/
348 if(temp_mods.get(p, &mod))
350 if(mod.type == NODEMOD_CHANGECONTENT)
355 if(mod.type == NODEMOD_CRACK)
358 Content doesn't change.
360 face_contents works just like it should, because
361 there should not be faces between differently cracked
364 If a semi-transparent node is cracked in front an
365 another one, it really doesn't matter whether there
366 is a cracked face drawn in between or not.
376 translate_dir: unit vector with only one of x, y or z
377 face_dir: unit vector with only one of x, y or z
379 void MapBlock::updateFastFaceRow(
388 core::array<FastFace> &dest,
389 NodeModMap &temp_mods)
393 u16 continuous_tiles_count = 0;
395 MapNode n0 = getNodeParentNoEx(p);
396 MapNode n1 = getNodeParentNoEx(p + face_dir);
398 u8 light = getFaceLight(daynight_ratio, n0, n1, face_dir);
400 TileSpec tile0 = getNodeTile(n0, p, face_dir, temp_mods);
401 TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir, temp_mods);
403 for(u16 j=0; j<length; j++)
405 bool next_is_different = true;
414 // If at last position, there is nothing to compare to and
415 // the face must be drawn anyway
418 p_next = p + translate_dir;
419 n0_next = getNodeParentNoEx(p_next);
420 n1_next = getNodeParentNoEx(p_next + face_dir);
421 tile0_next = getNodeTile(n0_next, p_next, face_dir, temp_mods);
422 tile1_next = getNodeTile(n1_next,p_next+face_dir,-face_dir, temp_mods);
423 light_next = getFaceLight(daynight_ratio, n0_next, n1_next, face_dir);
425 if(tile0_next == tile0
426 && tile1_next == tile1
427 && light_next == light)
429 next_is_different = false;
433 continuous_tiles_count++;
435 // This is set to true if the texture doesn't allow more tiling
436 bool end_of_texture = false;
438 If there is no texture, it can be tiled infinitely.
439 If tiled==0, it means the texture can be tiled infinitely.
440 Otherwise check tiled agains continuous_tiles_count.
442 This check has to be made for both tiles, because this is
443 a bit hackish and we know which one we're using only when
444 the decision to make the faces is made.
446 if(tile0.texture.atlas != NULL && tile0.texture.tiled != 0)
448 if(tile0.texture.tiled <= continuous_tiles_count)
449 end_of_texture = true;
451 if(tile1.texture.atlas != NULL && tile1.texture.tiled != 0)
453 if(tile1.texture.tiled <= continuous_tiles_count)
454 end_of_texture = true;
457 //end_of_texture = true; //DEBUG
459 if(next_is_different || end_of_texture)
462 Create a face if there should be one
464 //u8 mf = face_contents(tile0, tile1);
466 u8 content0 = getNodeContent(p, n0, temp_mods);
467 u8 content1 = getNodeContent(p + face_dir, n1, temp_mods);
468 u8 mf = face_contents(content0, content1);
472 // Floating point conversion of the position vector
473 v3f pf(p.X, p.Y, p.Z);
474 // Center point of face (kind of)
475 v3f sp = pf - ((f32)continuous_tiles_count / 2. - 0.5) * translate_dir_f;
477 if(translate_dir.X != 0){
478 scale.X = continuous_tiles_count;
480 if(translate_dir.Y != 0){
481 scale.Y = continuous_tiles_count;
483 if(translate_dir.Z != 0){
484 scale.Z = continuous_tiles_count;
489 // If node at sp (tile0) is more solid
492 makeFastFace(tile0, decode_light(light),
494 posRelative_f, dest);
496 // If node at sp is less solid (mf == 2)
499 makeFastFace(tile1, decode_light(light),
500 sp+face_dir_f, -face_dir, scale,
501 posRelative_f, dest);
506 continuous_tiles_count = 0;
519 This is used because CMeshBuffer::append() is very slow
523 video::SMaterial material;
524 core::array<u16> indices;
525 core::array<video::S3DVertex> vertices;
532 video::SMaterial material,
533 const video::S3DVertex* const vertices,
535 const u16* const indices,
539 PreMeshBuffer *p = NULL;
540 for(u32 i=0; i<m_prebuffers.size(); i++)
542 PreMeshBuffer &pp = m_prebuffers[i];
543 if(pp.material != material)
553 pp.material = material;
554 m_prebuffers.push_back(pp);
555 p = &m_prebuffers[m_prebuffers.size()-1];
558 u32 vertex_count = p->vertices.size();
559 for(u32 i=0; i<numIndices; i++)
561 u32 j = indices[i] + vertex_count;
564 dstream<<"FIXME: Meshbuffer ran out of indices"<<std::endl;
565 // NOTE: Fix is to just add an another MeshBuffer
567 p->indices.push_back(j);
569 for(u32 i=0; i<numVertices; i++)
571 p->vertices.push_back(vertices[i]);
575 void fillMesh(scene::SMesh *mesh)
577 /*dstream<<"Filling mesh with "<<m_prebuffers.size()
578 <<" meshbuffers"<<std::endl;*/
579 for(u32 i=0; i<m_prebuffers.size(); i++)
581 PreMeshBuffer &p = m_prebuffers[i];
583 /*dstream<<"p.vertices.size()="<<p.vertices.size()
584 <<", p.indices.size()="<<p.indices.size()
589 // This is a "Standard MeshBuffer",
590 // it's a typedeffed CMeshBuffer<video::S3DVertex>
591 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
593 buf->Material = p.material;
594 //((scene::SMeshBuffer*)buf)->Material = p.material;
596 //buf->setHardwareMappingHint(scene::EHM_STATIC);
598 mesh->addMeshBuffer(buf);
602 buf->append(p.vertices.pointer(), p.vertices.size(),
603 p.indices.pointer(), p.indices.size());
608 core::array<PreMeshBuffer> m_prebuffers;
611 void MapBlock::updateMesh(u32 daynight_ratio)
615 DEBUG: If mesh has been generated, don't generate it again
618 JMutexAutoLock meshlock(mesh_mutex);
624 // 4-21ms for MAP_BLOCKSIZE=16
625 // 24-155ms for MAP_BLOCKSIZE=32
626 //TimeTaker timer1("updateMesh()");
628 core::array<FastFace> fastfaces_new;
630 v3f posRelative_f(getPosRelative().X, getPosRelative().Y,
631 getPosRelative().Z); // floating point conversion
634 Avoid interlocks by copying m_temp_mods
636 NodeModMap temp_mods;
638 JMutexAutoLock lock(m_temp_mods_mutex);
639 m_temp_mods.copy(temp_mods);
645 bool new_style_water = g_settings.getBool("new_style_water");
646 bool new_style_leaves = g_settings.getBool("new_style_leaves");
648 float node_water_level = 1.0;
650 node_water_level = 0.85;
653 We are including the faces of the trailing edges of the block.
654 This means that when something changes, the caller must
655 also update the meshes of the blocks at the leading edges.
657 NOTE: This is the slowest part of this method.
661 // 4-23ms for MAP_BLOCKSIZE=16
662 //TimeTaker timer2("updateMesh() collect");
665 Go through every y,z and get top faces in rows of x+
667 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
668 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
669 updateFastFaceRow(daynight_ratio, posRelative_f,
670 v3s16(0,y,z), MAP_BLOCKSIZE,
673 v3s16(0,1,0), //face dir
680 Go through every x,y and get right faces in rows of z+
682 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
683 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
684 updateFastFaceRow(daynight_ratio, posRelative_f,
685 v3s16(x,y,0), MAP_BLOCKSIZE,
695 Go through every y,z and get back faces in rows of x+
697 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
698 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
699 updateFastFaceRow(daynight_ratio, posRelative_f,
700 v3s16(0,y,z), MAP_BLOCKSIZE,
714 Convert FastFaces to SMesh
717 MeshCollector collector;
719 if(fastfaces_new.size() > 0)
721 // avg 0ms (100ms spikes when loading textures the first time)
722 //TimeTaker timer2("updateMesh() mesh building");
724 video::SMaterial material;
725 material.setFlag(video::EMF_LIGHTING, false);
726 material.setFlag(video::EMF_BILINEAR_FILTER, false);
727 material.setFlag(video::EMF_FOG_ENABLE, true);
728 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
729 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_SIMPLE);
731 for(u32 i=0; i<fastfaces_new.size(); i++)
733 FastFace &f = fastfaces_new[i];
735 const u16 indices[] = {0,1,2,2,3,0};
737 //video::ITexture *texture = g_irrlicht->getTexture(f.tile.spec);
738 video::ITexture *texture = f.tile.texture.atlas;
742 material.setTexture(0, texture);
744 f.tile.applyMaterialOptions(material);
746 collector.append(material, f.vertices, 4, indices, 6);
751 Add special graphics:
757 //TimeTaker timer2("updateMesh() adding special stuff");
759 // Flowing water material
760 video::SMaterial material_water1;
761 material_water1.setFlag(video::EMF_LIGHTING, false);
762 //material_water1.setFlag(video::EMF_BACK_FACE_CULLING, false);
763 material_water1.setFlag(video::EMF_BILINEAR_FILTER, false);
764 material_water1.setFlag(video::EMF_FOG_ENABLE, true);
765 material_water1.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
767 //material_water1.setTexture(0, g_irrlicht->getTexture("water.png"));
768 AtlasPointer pa_water1 = g_texturesource->getTexture(
769 g_texturesource->getTextureId("water.png"));
770 material_water1.setTexture(0, pa_water1.atlas);
772 // New-style leaves material
773 video::SMaterial material_leaves1;
774 material_leaves1.setFlag(video::EMF_LIGHTING, false);
775 //material_leaves1.setFlag(video::EMF_BACK_FACE_CULLING, false);
776 material_leaves1.setFlag(video::EMF_BILINEAR_FILTER, false);
777 material_leaves1.setFlag(video::EMF_FOG_ENABLE, true);
778 material_leaves1.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
780 //material_leaves1.setTexture(0, g_irrlicht->getTexture("leaves.png"));
781 AtlasPointer pa_leaves1 = g_texturesource->getTexture(
782 g_texturesource->getTextureId("leaves.png"));
783 material_leaves1.setTexture(0, pa_leaves1.atlas);
785 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
786 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
787 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
791 MapNode &n = getNodeRef(x,y,z);
796 if(n.d == CONTENT_TORCH)
798 video::SColor c(255,255,255,255);
800 video::S3DVertex vertices[4] =
802 video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c, 0,1),
803 video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c, 1,1),
804 video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, 1,0),
805 video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c, 0,0),
808 v3s16 dir = unpackDir(n.dir);
810 for(s32 i=0; i<4; i++)
812 if(dir == v3s16(1,0,0))
813 vertices[i].Pos.rotateXZBy(0);
814 if(dir == v3s16(-1,0,0))
815 vertices[i].Pos.rotateXZBy(180);
816 if(dir == v3s16(0,0,1))
817 vertices[i].Pos.rotateXZBy(90);
818 if(dir == v3s16(0,0,-1))
819 vertices[i].Pos.rotateXZBy(-90);
820 if(dir == v3s16(0,-1,0))
821 vertices[i].Pos.rotateXZBy(45);
822 if(dir == v3s16(0,1,0))
823 vertices[i].Pos.rotateXZBy(-45);
825 vertices[i].Pos += intToFloat(p + getPosRelative());
829 video::SMaterial material;
830 material.setFlag(video::EMF_LIGHTING, false);
831 material.setFlag(video::EMF_BACK_FACE_CULLING, false);
832 material.setFlag(video::EMF_BILINEAR_FILTER, false);
833 //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
834 material.MaterialType
835 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
837 if(dir == v3s16(0,-1,0))
838 material.setTexture(0,
839 g_texturesource->getTextureRaw("torch_on_floor.png"));
840 else if(dir == v3s16(0,1,0))
841 material.setTexture(0,
842 g_texturesource->getTextureRaw("torch_on_ceiling.png"));
843 // For backwards compatibility
844 else if(dir == v3s16(0,0,0))
845 material.setTexture(0,
846 g_texturesource->getTextureRaw("torch_on_floor.png"));
848 material.setTexture(0,
849 g_texturesource->getTextureRaw("torch.png"));
851 u16 indices[] = {0,1,2,2,3,0};
852 // Add to mesh collector
853 collector.append(material, vertices, 4, indices, 6);
856 Add flowing water to mesh
858 else if(n.d == CONTENT_WATER)
860 bool top_is_water = false;
862 MapNode n = getNodeParent(v3s16(x,y+1,z));
863 if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
865 }catch(InvalidPositionException &e){}
867 u8 l = decode_light(n.getLightBlend(daynight_ratio));
868 video::SColor c(WATER_ALPHA,l,l,l);
870 // Neighbor water levels (key = relative position)
871 // Includes current node
872 core::map<v3s16, f32> neighbor_levels;
873 core::map<v3s16, u8> neighbor_contents;
874 core::map<v3s16, u8> neighbor_flags;
875 const u8 neighborflag_top_is_water = 0x01;
876 v3s16 neighbor_dirs[9] = {
887 for(u32 i=0; i<9; i++)
889 u8 content = CONTENT_AIR;
890 float level = -0.5 * BS;
894 v3s16 p2 = p + neighbor_dirs[i];
895 MapNode n2 = getNodeParent(p2);
899 if(n2.d == CONTENT_WATERSOURCE)
900 level = (-0.5+node_water_level) * BS;
901 else if(n2.d == CONTENT_WATER)
902 level = (-0.5 + ((float)n2.param2 + 0.5) / 8.0
903 * node_water_level) * BS;
905 // Check node above neighbor.
906 // NOTE: This doesn't get executed if neighbor
909 n2 = getNodeParent(p2);
910 if(n2.d == CONTENT_WATERSOURCE || n2.d == CONTENT_WATER)
911 flags |= neighborflag_top_is_water;
913 catch(InvalidPositionException &e){}
915 neighbor_levels.insert(neighbor_dirs[i], level);
916 neighbor_contents.insert(neighbor_dirs[i], content);
917 neighbor_flags.insert(neighbor_dirs[i], flags);
920 //float water_level = (-0.5 + ((float)n.param2 + 0.5) / 8.0) * BS;
921 //float water_level = neighbor_levels[v3s16(0,0,0)];
923 // Corner heights (average between four waters)
924 f32 corner_levels[4];
926 v3s16 halfdirs[4] = {
932 for(u32 i=0; i<4; i++)
934 v3s16 cornerdir = halfdirs[i];
935 float cornerlevel = 0;
937 for(u32 j=0; j<4; j++)
939 v3s16 neighbordir = cornerdir - halfdirs[j];
940 u8 content = neighbor_contents[neighbordir];
941 // Special case for source nodes
942 if(content == CONTENT_WATERSOURCE)
944 cornerlevel = (-0.5+node_water_level)*BS;
948 else if(content == CONTENT_WATER)
950 cornerlevel += neighbor_levels[neighbordir];
953 else if(content == CONTENT_AIR)
955 cornerlevel += -0.5*BS;
960 cornerlevel /= valid_count;
961 corner_levels[i] = cornerlevel;
968 v3s16 side_dirs[4] = {
974 s16 side_corners[4][2] = {
980 for(u32 i=0; i<4; i++)
982 v3s16 dir = side_dirs[i];
985 If our topside is water and neighbor's topside
986 is water, don't draw side face
989 neighbor_flags[dir] & neighborflag_top_is_water)
992 u8 neighbor_content = neighbor_contents[dir];
994 // Don't draw face if neighbor is not air or water
995 if(neighbor_content != CONTENT_AIR
996 && neighbor_content != CONTENT_WATER)
999 bool neighbor_is_water = (neighbor_content == CONTENT_WATER);
1001 // Don't draw any faces if neighbor is water and top is water
1002 if(neighbor_is_water == true && top_is_water == false)
1005 video::S3DVertex vertices[4] =
1007 /*video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1),
1008 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1),
1009 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1010 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1011 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1012 pa_water1.x0(), pa_water1.y1()),
1013 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1014 pa_water1.x1(), pa_water1.y1()),
1015 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1016 pa_water1.x1(), pa_water1.y0()),
1017 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1018 pa_water1.x0(), pa_water1.y0()),
1022 If our topside is water, set upper border of face
1023 at upper border of node
1027 vertices[2].Pos.Y = 0.5*BS;
1028 vertices[3].Pos.Y = 0.5*BS;
1031 Otherwise upper position of face is corner levels
1035 vertices[2].Pos.Y = corner_levels[side_corners[i][0]];
1036 vertices[3].Pos.Y = corner_levels[side_corners[i][1]];
1040 If neighbor is water, lower border of face is corner
1043 if(neighbor_is_water)
1045 vertices[0].Pos.Y = corner_levels[side_corners[i][1]];
1046 vertices[1].Pos.Y = corner_levels[side_corners[i][0]];
1049 If neighbor is not water, lower border of face is
1050 lower border of node
1054 vertices[0].Pos.Y = -0.5*BS;
1055 vertices[1].Pos.Y = -0.5*BS;
1058 for(s32 j=0; j<4; j++)
1060 if(dir == v3s16(0,0,1))
1061 vertices[j].Pos.rotateXZBy(0);
1062 if(dir == v3s16(0,0,-1))
1063 vertices[j].Pos.rotateXZBy(180);
1064 if(dir == v3s16(-1,0,0))
1065 vertices[j].Pos.rotateXZBy(90);
1066 if(dir == v3s16(1,0,-0))
1067 vertices[j].Pos.rotateXZBy(-90);
1069 vertices[j].Pos += intToFloat(p + getPosRelative());
1072 u16 indices[] = {0,1,2,2,3,0};
1073 // Add to mesh collector
1074 collector.append(material_water1, vertices, 4, indices, 6);
1078 Generate top side, if appropriate
1081 if(top_is_water == false)
1083 video::S3DVertex vertices[4] =
1085 /*video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
1086 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
1087 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1088 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1089 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1090 pa_water1.x0(), pa_water1.y1()),
1091 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1092 pa_water1.x1(), pa_water1.y1()),
1093 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
1094 pa_water1.x1(), pa_water1.y0()),
1095 video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
1096 pa_water1.x0(), pa_water1.y0()),
1099 // This fixes a strange bug
1100 s32 corner_resolve[4] = {3,2,1,0};
1102 for(s32 i=0; i<4; i++)
1104 //vertices[i].Pos.Y += water_level;
1105 //vertices[i].Pos.Y += neighbor_levels[v3s16(0,0,0)];
1106 s32 j = corner_resolve[i];
1107 vertices[i].Pos.Y += corner_levels[j];
1108 vertices[i].Pos += intToFloat(p + getPosRelative());
1111 u16 indices[] = {0,1,2,2,3,0};
1112 // Add to mesh collector
1113 collector.append(material_water1, vertices, 4, indices, 6);
1117 Add water sources to mesh if using new style
1119 else if(n.d == CONTENT_WATERSOURCE && new_style_water)
1121 //bool top_is_water = false;
1122 bool top_is_air = false;
1124 MapNode n = getNodeParent(v3s16(x,y+1,z));
1125 /*if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
1126 top_is_water = true;*/
1127 if(n.d == CONTENT_AIR)
1129 }catch(InvalidPositionException &e){}
1131 /*if(top_is_water == true)
1133 if(top_is_air == false)
1136 u8 l = decode_light(n.getLightBlend(daynight_ratio));
1137 video::SColor c(WATER_ALPHA,l,l,l);
1139 video::S3DVertex vertices[4] =
1141 /*video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
1142 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
1143 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1144 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1145 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1146 pa_water1.x0(), pa_water1.y1()),
1147 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1148 pa_water1.x1(), pa_water1.y1()),
1149 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
1150 pa_water1.x1(), pa_water1.y0()),
1151 video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
1152 pa_water1.x0(), pa_water1.y0()),
1155 for(s32 i=0; i<4; i++)
1157 vertices[i].Pos.Y += (-0.5+node_water_level)*BS;
1158 vertices[i].Pos += intToFloat(p + getPosRelative());
1161 u16 indices[] = {0,1,2,2,3,0};
1162 // Add to mesh collector
1163 collector.append(material_water1, vertices, 4, indices, 6);
1166 Add leaves if using new style
1168 else if(n.d == CONTENT_LEAVES && new_style_leaves)
1170 /*u8 l = decode_light(n.getLightBlend(daynight_ratio));*/
1171 u8 l = decode_light(undiminish_light(n.getLightBlend(daynight_ratio)));
1172 video::SColor c(255,l,l,l);
1174 for(u32 j=0; j<6; j++)
1176 video::S3DVertex vertices[4] =
1178 /*video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, 0,1),
1179 video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, 1,1),
1180 video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c, 1,0),
1181 video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c, 0,0),*/
1182 video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c,
1183 pa_leaves1.x0(), pa_leaves1.y1()),
1184 video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c,
1185 pa_leaves1.x1(), pa_leaves1.y1()),
1186 video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c,
1187 pa_leaves1.x1(), pa_leaves1.y0()),
1188 video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c,
1189 pa_leaves1.x0(), pa_leaves1.y0()),
1194 for(u16 i=0; i<4; i++)
1195 vertices[i].Pos.rotateXZBy(0);
1199 for(u16 i=0; i<4; i++)
1200 vertices[i].Pos.rotateXZBy(180);
1204 for(u16 i=0; i<4; i++)
1205 vertices[i].Pos.rotateXZBy(-90);
1209 for(u16 i=0; i<4; i++)
1210 vertices[i].Pos.rotateXZBy(90);
1214 for(u16 i=0; i<4; i++)
1215 vertices[i].Pos.rotateYZBy(-90);
1219 for(u16 i=0; i<4; i++)
1220 vertices[i].Pos.rotateYZBy(90);
1223 for(u16 i=0; i<4; i++)
1225 vertices[i].Pos += intToFloat(p + getPosRelative());
1228 u16 indices[] = {0,1,2,2,3,0};
1229 // Add to mesh collector
1230 collector.append(material_leaves1, vertices, 4, indices, 6);
1236 Add stuff from collector to mesh
1239 scene::SMesh *mesh_new = NULL;
1240 mesh_new = new scene::SMesh();
1242 collector.fillMesh(mesh_new);
1245 Do some stuff to the mesh
1248 mesh_new->recalculateBoundingBox();
1251 Delete new mesh if it is empty
1254 if(mesh_new->getMeshBufferCount() == 0)
1263 // Usually 1-700 faces and 1-7 materials
1264 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
1265 <<"and uses "<<mesh_new->getMeshBufferCount()
1266 <<" materials (meshbuffers)"<<std::endl;
1269 // Use VBO for mesh (this just would set this for ever buffer)
1270 // This will lead to infinite memory usage because or irrlicht.
1271 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
1274 NOTE: If that is enabled, some kind of a queue to the main
1275 thread should be made which would call irrlicht to delete
1276 the hardware buffer and then delete the mesh
1286 //scene::SMesh *mesh_old = mesh[daynight_i];
1287 //mesh[daynight_i] = mesh_new;
1289 scene::SMesh *mesh_old = mesh;
1291 setMeshExpired(false);
1293 if(mesh_old != NULL)
1295 // Remove hardware buffers of meshbuffers of mesh
1296 // NOTE: No way, this runs in a different thread and everything
1297 /*u32 c = mesh_old->getMeshBufferCount();
1298 for(u32 i=0; i<c; i++)
1300 IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1303 /*dstream<<"mesh_old->getReferenceCount()="
1304 <<mesh_old->getReferenceCount()<<std::endl;
1305 u32 c = mesh_old->getMeshBufferCount();
1306 for(u32 i=0; i<c; i++)
1308 scene::IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1309 dstream<<"buf->getReferenceCount()="
1310 <<buf->getReferenceCount()<<std::endl;
1319 mesh_mutex.Unlock();
1321 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1324 /*void MapBlock::updateMeshes(s32 first_i)
1326 assert(first_i >= 0 && first_i <= DAYNIGHT_CACHE_COUNT);
1327 updateMesh(first_i);
1328 for(s32 i=0; i<DAYNIGHT_CACHE_COUNT; i++)
1339 Propagates sunlight down through the block.
1340 Doesn't modify nodes that are not affected by sunlight.
1342 Returns false if sunlight at bottom block is invalid
1343 Returns true if bottom block doesn't exist.
1345 If there is a block above, continues from it.
1346 If there is no block above, assumes there is sunlight, unless
1347 is_underground is set or highest node is water.
1349 At the moment, all sunlighted nodes are added to light_sources.
1350 - SUGG: This could be optimized
1352 Turns sunglighted mud into grass.
1354 if remove_light==true, sets non-sunlighted nodes black.
1356 if black_air_left!=NULL, it is set to true if non-sunlighted
1357 air is left in block.
1359 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources,
1360 bool remove_light, bool *black_air_left,
1363 // Whether the sunlight at the top of the bottom block is valid
1364 bool block_below_is_valid = true;
1366 v3s16 pos_relative = getPosRelative();
1368 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
1370 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
1373 bool no_sunlight = false;
1374 bool no_top_block = false;
1375 // Check if node above block has sunlight
1377 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1378 if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1383 catch(InvalidPositionException &e)
1385 no_top_block = true;
1387 // NOTE: This makes over-ground roofed places sunlighted
1388 // Assume sunlight, unless is_underground==true
1395 MapNode n = getNode(v3s16(x, MAP_BLOCKSIZE-1, z));
1396 if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
1401 // NOTE: As of now, this just would make everything dark.
1403 //no_sunlight = true;
1406 #if 0 // Doesn't work; nothing gets light.
1407 bool no_sunlight = true;
1408 bool no_top_block = false;
1409 // Check if node above block has sunlight
1411 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1412 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
1414 no_sunlight = false;
1417 catch(InvalidPositionException &e)
1419 no_top_block = true;
1423 /*std::cout<<"("<<x<<","<<z<<"): "
1424 <<"no_top_block="<<no_top_block
1425 <<", is_underground="<<is_underground
1426 <<", no_sunlight="<<no_sunlight
1429 s16 y = MAP_BLOCKSIZE-1;
1431 // This makes difference to diminishing in water.
1432 bool stopped_to_solid_object = false;
1434 u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
1439 MapNode &n = getNodeRef(pos);
1441 if(current_light == 0)
1445 else if(current_light == LIGHT_SUN && n.sunlight_propagates())
1447 // Do nothing: Sunlight is continued
1449 else if(n.light_propagates() == false)
1453 bool upper_is_air = false;
1456 if(getNodeParent(pos+v3s16(0,1,0)).d == CONTENT_AIR)
1457 upper_is_air = true;
1459 catch(InvalidPositionException &e)
1462 // Turn mud into grass
1463 if(upper_is_air && n.d == CONTENT_MUD
1464 && current_light == LIGHT_SUN)
1466 n.d = CONTENT_GRASS;
1470 // A solid object is on the way.
1471 stopped_to_solid_object = true;
1479 current_light = diminish_light(current_light);
1482 u8 old_light = n.getLight(LIGHTBANK_DAY);
1484 if(current_light > old_light || remove_light)
1486 n.setLight(LIGHTBANK_DAY, current_light);
1489 if(diminish_light(current_light) != 0)
1491 light_sources.insert(pos_relative + pos, true);
1494 if(current_light == 0 && stopped_to_solid_object)
1498 *black_air_left = true;
1503 // Whether or not the block below should see LIGHT_SUN
1504 bool sunlight_should_go_down = (current_light == LIGHT_SUN);
1507 If the block below hasn't already been marked invalid:
1509 Check if the node below the block has proper sunlight at top.
1510 If not, the block below is invalid.
1512 Ignore non-transparent nodes as they always have no light
1516 if(block_below_is_valid)
1518 MapNode n = getNodeParent(v3s16(x, -1, z));
1519 if(n.light_propagates())
1521 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN
1522 && sunlight_should_go_down == false)
1523 block_below_is_valid = false;
1524 else if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN
1525 && sunlight_should_go_down == true)
1526 block_below_is_valid = false;
1530 catch(InvalidPositionException &e)
1532 /*std::cout<<"InvalidBlockException for bottom block node"
1534 // Just no block below, no need to panic.
1539 return block_below_is_valid;
1542 void MapBlock::copyTo(VoxelManipulator &dst)
1544 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
1545 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
1547 // Copy from data to VoxelManipulator
1548 dst.copyFrom(data, data_area, v3s16(0,0,0),
1549 getPosRelative(), data_size);
1552 void MapBlock::copyFrom(VoxelManipulator &dst)
1554 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
1555 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
1557 // Copy from VoxelManipulator to data
1558 dst.copyTo(data, data_area, v3s16(0,0,0),
1559 getPosRelative(), data_size);
1562 void MapBlock::stepObjects(float dtime, bool server, u32 daynight_ratio)
1567 m_objects.step(dtime, server, daynight_ratio);
1570 Spawn some objects at random.
1572 Use dayNightDiffed() to approximate being near ground level
1574 if(m_spawn_timer < -999)
1578 if(dayNightDiffed() == true && getObjectCount() == 0)
1580 m_spawn_timer -= dtime;
1581 if(m_spawn_timer <= 0.0)
1583 m_spawn_timer += myrand() % 300;
1586 (myrand()%(MAP_BLOCKSIZE-1))+0,
1587 (myrand()%(MAP_BLOCKSIZE-1))+0
1590 s16 y = getGroundLevel(p2d);
1594 v3s16 p(p2d.X, y+1, p2d.Y);
1596 if(getNode(p).d == CONTENT_AIR
1597 && getNode(p).getLightBlend(daynight_ratio) <= 11)
1599 RatObject *obj = new RatObject(NULL, -1, intToFloat(p));
1610 void MapBlock::updateDayNightDiff()
1614 m_day_night_differs = false;
1618 bool differs = false;
1621 Check if any lighting value differs
1623 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1625 MapNode &n = data[i];
1626 if(n.getLight(LIGHTBANK_DAY) != n.getLight(LIGHTBANK_NIGHT))
1634 If some lighting values differ, check if the whole thing is
1635 just air. If it is, differ = false
1639 bool only_air = true;
1640 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1642 MapNode &n = data[i];
1643 if(n.d != CONTENT_AIR)
1653 // Set member variable
1654 m_day_night_differs = differs;
1657 s16 MapBlock::getGroundLevel(v2s16 p2d)
1663 s16 y = MAP_BLOCKSIZE-1;
1666 //if(is_ground_content(getNodeRef(p2d.X, y, p2d.Y).d))
1667 if(content_features(getNodeRef(p2d.X, y, p2d.Y).d).walkable)
1669 if(y == MAP_BLOCKSIZE-1)
1677 catch(InvalidPositionException &e)
1687 void MapBlock::serialize(std::ostream &os, u8 version)
1689 if(!ser_ver_supported(version))
1690 throw VersionMismatchException("ERROR: MapBlock format not supported");
1694 throw SerializationError("ERROR: Not writing dummy block.");
1697 // These have no compression
1698 if(version <= 3 || version == 5 || version == 6)
1700 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1702 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
1703 SharedBuffer<u8> dest(buflen);
1705 dest[0] = is_underground;
1706 for(u32 i=0; i<nodecount; i++)
1708 u32 s = 1 + i * MapNode::serializedLength(version);
1709 data[i].serialize(&dest[s], version);
1712 os.write((char*)*dest, dest.getSize());
1714 else if(version <= 10)
1718 Compress the materials and the params separately.
1722 os.write((char*)&is_underground, 1);
1724 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1726 // Get and compress materials
1727 SharedBuffer<u8> materialdata(nodecount);
1728 for(u32 i=0; i<nodecount; i++)
1730 materialdata[i] = data[i].d;
1732 compress(materialdata, os, version);
1734 // Get and compress lights
1735 SharedBuffer<u8> lightdata(nodecount);
1736 for(u32 i=0; i<nodecount; i++)
1738 lightdata[i] = data[i].param;
1740 compress(lightdata, os, version);
1744 // Get and compress param2
1745 SharedBuffer<u8> param2data(nodecount);
1746 for(u32 i=0; i<nodecount; i++)
1748 param2data[i] = data[i].param2;
1750 compress(param2data, os, version);
1753 // All other versions (newest)
1760 if(m_day_night_differs)
1762 if(m_lighting_expired)
1764 os.write((char*)&flags, 1);
1766 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1772 SharedBuffer<u8> databuf(nodecount*3);
1775 for(u32 i=0; i<nodecount; i++)
1777 databuf[i] = data[i].d;
1781 for(u32 i=0; i<nodecount; i++)
1783 databuf[i+nodecount] = data[i].param;
1787 for(u32 i=0; i<nodecount; i++)
1789 databuf[i+nodecount*2] = data[i].param2;
1793 Compress data to output stream
1796 compress(databuf, os, version);
1800 void MapBlock::deSerialize(std::istream &is, u8 version)
1802 if(!ser_ver_supported(version))
1803 throw VersionMismatchException("ERROR: MapBlock format not supported");
1805 // These have no compression
1806 if(version <= 3 || version == 5 || version == 6)
1808 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1811 if(is.gcount() != 1)
1812 throw SerializationError
1813 ("MapBlock::deSerialize: no enough input data");
1814 is_underground = tmp;
1815 for(u32 i=0; i<nodecount; i++)
1817 s32 len = MapNode::serializedLength(version);
1818 SharedBuffer<u8> d(len);
1819 is.read((char*)*d, len);
1820 if(is.gcount() != len)
1821 throw SerializationError
1822 ("MapBlock::deSerialize: no enough input data");
1823 data[i].deSerialize(*d, version);
1826 else if(version <= 10)
1828 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1831 is.read((char*)&t8, 1);
1832 is_underground = t8;
1835 // Uncompress and set material data
1836 std::ostringstream os(std::ios_base::binary);
1837 decompress(is, os, version);
1838 std::string s = os.str();
1839 if(s.size() != nodecount)
1840 throw SerializationError
1841 ("MapBlock::deSerialize: invalid format");
1842 for(u32 i=0; i<s.size(); i++)
1848 // Uncompress and set param data
1849 std::ostringstream os(std::ios_base::binary);
1850 decompress(is, os, version);
1851 std::string s = os.str();
1852 if(s.size() != nodecount)
1853 throw SerializationError
1854 ("MapBlock::deSerialize: invalid format");
1855 for(u32 i=0; i<s.size(); i++)
1857 data[i].param = s[i];
1863 // Uncompress and set param2 data
1864 std::ostringstream os(std::ios_base::binary);
1865 decompress(is, os, version);
1866 std::string s = os.str();
1867 if(s.size() != nodecount)
1868 throw SerializationError
1869 ("MapBlock::deSerialize: invalid format");
1870 for(u32 i=0; i<s.size(); i++)
1872 data[i].param2 = s[i];
1876 // All other versions (newest)
1879 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1882 is.read((char*)&flags, 1);
1883 is_underground = (flags & 0x01) ? true : false;
1884 m_day_night_differs = (flags & 0x02) ? true : false;
1885 m_lighting_expired = (flags & 0x04) ? true : false;
1888 std::ostringstream os(std::ios_base::binary);
1889 decompress(is, os, version);
1890 std::string s = os.str();
1891 if(s.size() != nodecount*3)
1892 throw SerializationError
1893 ("MapBlock::deSerialize: invalid format");
1896 for(u32 i=0; i<nodecount; i++)
1901 for(u32 i=0; i<nodecount; i++)
1903 data[i].param = s[i+nodecount];
1906 for(u32 i=0; i<nodecount; i++)
1908 data[i].param2 = s[i+nodecount*2];
1913 Translate nodes as specified in the translate_to fields of
1916 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1918 MapNode &n = data[i];
1920 MapNode *translate_to = content_features(n.d).translate_to;
1923 dstream<<"MapBlock: WARNING: Translating node "<<n.d<<" to "
1924 <<translate_to->d<<std::endl;