3 Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 // For g_settings and g_irrlicht
32 MapBlock::MapBlock(NodeContainer *parent, v3s16 pos, bool dummy):
36 is_underground(false),
37 m_lighting_expired(true),
38 m_day_night_differs(false),
45 m_spawn_timer = -10000;
48 m_mesh_expired = false;
51 m_temp_mods_mutex.Init();
59 JMutexAutoLock lock(mesh_mutex);
73 bool MapBlock::isValidPositionParent(v3s16 p)
75 if(isValidPosition(p))
80 return m_parent->isValidPosition(getPosRelative() + p);
84 MapNode MapBlock::getNodeParent(v3s16 p)
86 if(isValidPosition(p) == false)
88 return m_parent->getNode(getPosRelative() + p);
93 throw InvalidPositionException();
94 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
98 void MapBlock::setNodeParent(v3s16 p, MapNode & n)
100 if(isValidPosition(p) == false)
102 m_parent->setNode(getPosRelative() + p, n);
107 throw InvalidPositionException();
108 data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X] = n;
112 MapNode MapBlock::getNodeParentNoEx(v3s16 p)
114 if(isValidPosition(p) == false)
117 return m_parent->getNode(getPosRelative() + p);
119 catch(InvalidPositionException &e)
121 return MapNode(CONTENT_IGNORE);
128 return MapNode(CONTENT_IGNORE);
130 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
135 Parameters must consist of air and !air.
136 Order doesn't matter.
138 If either of the nodes doesn't exist, light is 0.
141 daynight_ratio: 0...1000
143 n2: getNodeParent(p + face_dir)
144 face_dir: axis oriented unit vector from p to p2
146 returns encoded light value.
148 u8 MapBlock::getFaceLight(u32 daynight_ratio, MapNode n, MapNode n2,
153 u8 l1 = n.getLightBlend(daynight_ratio);
154 u8 l2 = n2.getLightBlend(daynight_ratio);
160 // Make some nice difference to different sides
162 // This makes light come from a corner
163 /*if(face_dir.X == 1 || face_dir.Z == 1 || face_dir.Y == -1)
164 light = diminish_light(diminish_light(light));
165 else if(face_dir.X == -1 || face_dir.Z == -1)
166 light = diminish_light(light);*/
168 // All neighboring faces have different shade (like in minecraft)
169 if(face_dir.X == 1 || face_dir.X == -1 || face_dir.Y == -1)
170 light = diminish_light(diminish_light(light));
171 else if(face_dir.Z == 1 || face_dir.Z == -1)
172 light = diminish_light(light);
176 catch(InvalidPositionException &e)
184 void MapBlock::makeFastFace(TileSpec tile, u8 light, v3f p,
185 v3s16 dir, v3f scale, v3f posRelative_f,
186 core::array<FastFace> &dest)
190 // Position is at the center of the cube.
195 // If looking towards z+, this is the face that is behind
196 // the center point, facing towards z+.
197 vertex_pos[0] = v3f(-BS/2,-BS/2,BS/2);
198 vertex_pos[1] = v3f( BS/2,-BS/2,BS/2);
199 vertex_pos[2] = v3f( BS/2, BS/2,BS/2);
200 vertex_pos[3] = v3f(-BS/2, BS/2,BS/2);
202 if(dir == v3s16(0,0,1))
204 for(u16 i=0; i<4; i++)
205 vertex_pos[i].rotateXZBy(0);
207 else if(dir == v3s16(0,0,-1))
209 for(u16 i=0; i<4; i++)
210 vertex_pos[i].rotateXZBy(180);
212 else if(dir == v3s16(1,0,0))
214 for(u16 i=0; i<4; i++)
215 vertex_pos[i].rotateXZBy(-90);
217 else if(dir == v3s16(-1,0,0))
219 for(u16 i=0; i<4; i++)
220 vertex_pos[i].rotateXZBy(90);
222 else if(dir == v3s16(0,1,0))
224 for(u16 i=0; i<4; i++)
225 vertex_pos[i].rotateYZBy(-90);
227 else if(dir == v3s16(0,-1,0))
229 for(u16 i=0; i<4; i++)
230 vertex_pos[i].rotateYZBy(90);
233 for(u16 i=0; i<4; i++)
235 vertex_pos[i].X *= scale.X;
236 vertex_pos[i].Y *= scale.Y;
237 vertex_pos[i].Z *= scale.Z;
238 vertex_pos[i] += pos + posRelative_f;
242 if (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X;
243 else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
244 else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
246 v3f zerovector = v3f(0,0,0);
248 //u8 li = decode_light(light);
250 //u8 li = 255; //DEBUG
252 u8 alpha = tile.alpha;
254 if(tile.id == TILE_WATER)
255 alpha = WATER_ALPHA;*/
257 video::SColor c = video::SColor(alpha,li,li,li);
259 float x0 = tile.texture.pos.X;
260 float y0 = tile.texture.pos.Y;
261 float w = tile.texture.size.X;
262 float h = tile.texture.size.Y;
264 face.vertices[0] = video::S3DVertex(vertex_pos[0], v3f(0,1,0), c,
265 core::vector2d<f32>(x0+w*abs_scale, y0+h));
266 face.vertices[1] = video::S3DVertex(vertex_pos[1], v3f(0,1,0), c,
267 core::vector2d<f32>(x0, y0+h));
268 face.vertices[2] = video::S3DVertex(vertex_pos[2], v3f(0,1,0), c,
269 core::vector2d<f32>(x0, y0));
270 face.vertices[3] = video::S3DVertex(vertex_pos[3], v3f(0,1,0), c,
271 core::vector2d<f32>(x0+w*abs_scale, y0));
275 //f->tile = TILE_STONE;
277 dest.push_back(face);
282 Gets node tile from any place relative to block.
283 Returns TILE_NODE if doesn't exist or should not be drawn.
285 TileSpec MapBlock::getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir,
286 NodeModMap &temp_mods)
289 spec = mn.getTile(face_dir);
292 Check temporary modifications on this node
294 /*core::map<v3s16, NodeMod>::Node *n;
295 n = m_temp_mods.find(p);
299 struct NodeMod mod = n->getValue();*/
301 if(temp_mods.get(p, &mod))
303 if(mod.type == NODEMOD_CHANGECONTENT)
305 MapNode mn2(mod.param);
306 spec = mn2.getTile(face_dir);
308 if(mod.type == NODEMOD_CRACK)
311 Get texture id, translate it to name, append stuff to
315 // Get original texture name
316 u32 orig_id = spec.texture.id;
317 std::string orig_name = g_texturesource->getTextureName(orig_id);
319 // Create new texture name
320 std::ostringstream os;
321 os<<orig_name<<"^[crack"<<mod.param;
324 u32 new_id = g_texturesource->getTextureId(os.str());
326 /*dstream<<"MapBlock::getNodeTile(): Switching from "
327 <<orig_name<<" to "<<os.str()<<" ("
328 <<orig_id<<" to "<<new_id<<")"<<std::endl;*/
330 spec.texture = g_texturesource->getTexture(new_id);
337 u8 MapBlock::getNodeContent(v3s16 p, MapNode mn, NodeModMap &temp_mods)
340 Check temporary modifications on this node
342 /*core::map<v3s16, NodeMod>::Node *n;
343 n = m_temp_mods.find(p);
347 struct NodeMod mod = n->getValue();*/
349 if(temp_mods.get(p, &mod))
351 if(mod.type == NODEMOD_CHANGECONTENT)
356 if(mod.type == NODEMOD_CRACK)
359 Content doesn't change.
361 face_contents works just like it should, because
362 there should not be faces between differently cracked
365 If a semi-transparent node is cracked in front an
366 another one, it really doesn't matter whether there
367 is a cracked face drawn in between or not.
377 translate_dir: unit vector with only one of x, y or z
378 face_dir: unit vector with only one of x, y or z
380 void MapBlock::updateFastFaceRow(
389 core::array<FastFace> &dest,
390 NodeModMap &temp_mods)
394 u16 continuous_tiles_count = 0;
396 MapNode n0 = getNodeParentNoEx(p);
397 MapNode n1 = getNodeParentNoEx(p + face_dir);
399 u8 light = getFaceLight(daynight_ratio, n0, n1, face_dir);
401 TileSpec tile0 = getNodeTile(n0, p, face_dir, temp_mods);
402 TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir, temp_mods);
404 for(u16 j=0; j<length; j++)
406 bool next_is_different = true;
415 // If at last position, there is nothing to compare to and
416 // the face must be drawn anyway
419 p_next = p + translate_dir;
420 n0_next = getNodeParentNoEx(p_next);
421 n1_next = getNodeParentNoEx(p_next + face_dir);
422 tile0_next = getNodeTile(n0_next, p_next, face_dir, temp_mods);
423 tile1_next = getNodeTile(n1_next,p_next+face_dir,-face_dir, temp_mods);
424 light_next = getFaceLight(daynight_ratio, n0_next, n1_next, face_dir);
426 if(tile0_next == tile0
427 && tile1_next == tile1
428 && light_next == light)
430 next_is_different = false;
434 continuous_tiles_count++;
436 // This is set to true if the texture doesn't allow more tiling
437 bool end_of_texture = false;
439 If there is no texture, it can be tiled infinitely.
440 If tiled==0, it means the texture can be tiled infinitely.
441 Otherwise check tiled agains continuous_tiles_count.
443 This check has to be made for both tiles, because this is
444 a bit hackish and we know which one we're using only when
445 the decision to make the faces is made.
447 if(tile0.texture.atlas != NULL && tile0.texture.tiled != 0)
449 if(tile0.texture.tiled <= continuous_tiles_count)
450 end_of_texture = true;
452 if(tile1.texture.atlas != NULL && tile1.texture.tiled != 0)
454 if(tile1.texture.tiled <= continuous_tiles_count)
455 end_of_texture = true;
458 //end_of_texture = true; //DEBUG
460 if(next_is_different || end_of_texture)
463 Create a face if there should be one
465 //u8 mf = face_contents(tile0, tile1);
467 u8 content0 = getNodeContent(p, n0, temp_mods);
468 u8 content1 = getNodeContent(p + face_dir, n1, temp_mods);
469 u8 mf = face_contents(content0, content1);
473 // Floating point conversion of the position vector
474 v3f pf(p.X, p.Y, p.Z);
475 // Center point of face (kind of)
476 v3f sp = pf - ((f32)continuous_tiles_count / 2. - 0.5) * translate_dir_f;
478 if(translate_dir.X != 0){
479 scale.X = continuous_tiles_count;
481 if(translate_dir.Y != 0){
482 scale.Y = continuous_tiles_count;
484 if(translate_dir.Z != 0){
485 scale.Z = continuous_tiles_count;
490 // If node at sp (tile0) is more solid
493 makeFastFace(tile0, decode_light(light),
495 posRelative_f, dest);
497 // If node at sp is less solid (mf == 2)
500 makeFastFace(tile1, decode_light(light),
501 sp+face_dir_f, -face_dir, scale,
502 posRelative_f, dest);
507 continuous_tiles_count = 0;
520 This is used because CMeshBuffer::append() is very slow
524 video::SMaterial material;
525 core::array<u16> indices;
526 core::array<video::S3DVertex> vertices;
533 video::SMaterial material,
534 const video::S3DVertex* const vertices,
536 const u16* const indices,
540 PreMeshBuffer *p = NULL;
541 for(u32 i=0; i<m_prebuffers.size(); i++)
543 PreMeshBuffer &pp = m_prebuffers[i];
544 if(pp.material != material)
554 pp.material = material;
555 m_prebuffers.push_back(pp);
556 p = &m_prebuffers[m_prebuffers.size()-1];
559 u32 vertex_count = p->vertices.size();
560 for(u32 i=0; i<numIndices; i++)
562 u32 j = indices[i] + vertex_count;
565 dstream<<"FIXME: Meshbuffer ran out of indices"<<std::endl;
566 // NOTE: Fix is to just add an another MeshBuffer
568 p->indices.push_back(j);
570 for(u32 i=0; i<numVertices; i++)
572 p->vertices.push_back(vertices[i]);
576 void fillMesh(scene::SMesh *mesh)
578 /*dstream<<"Filling mesh with "<<m_prebuffers.size()
579 <<" meshbuffers"<<std::endl;*/
580 for(u32 i=0; i<m_prebuffers.size(); i++)
582 PreMeshBuffer &p = m_prebuffers[i];
584 /*dstream<<"p.vertices.size()="<<p.vertices.size()
585 <<", p.indices.size()="<<p.indices.size()
590 // This is a "Standard MeshBuffer",
591 // it's a typedeffed CMeshBuffer<video::S3DVertex>
592 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
594 buf->Material = p.material;
595 //((scene::SMeshBuffer*)buf)->Material = p.material;
597 //buf->setHardwareMappingHint(scene::EHM_STATIC);
599 mesh->addMeshBuffer(buf);
603 buf->append(p.vertices.pointer(), p.vertices.size(),
604 p.indices.pointer(), p.indices.size());
609 core::array<PreMeshBuffer> m_prebuffers;
612 void MapBlock::updateMesh(u32 daynight_ratio)
616 DEBUG: If mesh has been generated, don't generate it again
619 JMutexAutoLock meshlock(mesh_mutex);
625 // 4-21ms for MAP_BLOCKSIZE=16
626 // 24-155ms for MAP_BLOCKSIZE=32
627 //TimeTaker timer1("updateMesh()");
629 core::array<FastFace> fastfaces_new;
631 v3f posRelative_f(getPosRelative().X, getPosRelative().Y,
632 getPosRelative().Z); // floating point conversion
635 Avoid interlocks by copying m_temp_mods
637 NodeModMap temp_mods;
639 JMutexAutoLock lock(m_temp_mods_mutex);
640 m_temp_mods.copy(temp_mods);
646 bool new_style_water = g_settings.getBool("new_style_water");
647 bool new_style_leaves = g_settings.getBool("new_style_leaves");
649 float node_water_level = 1.0;
651 node_water_level = 0.85;
654 We are including the faces of the trailing edges of the block.
655 This means that when something changes, the caller must
656 also update the meshes of the blocks at the leading edges.
658 NOTE: This is the slowest part of this method.
662 // 4-23ms for MAP_BLOCKSIZE=16
663 //TimeTaker timer2("updateMesh() collect");
666 Go through every y,z and get top faces in rows of x+
668 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
669 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
670 updateFastFaceRow(daynight_ratio, posRelative_f,
671 v3s16(0,y,z), MAP_BLOCKSIZE,
674 v3s16(0,1,0), //face dir
681 Go through every x,y and get right faces in rows of z+
683 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
684 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
685 updateFastFaceRow(daynight_ratio, posRelative_f,
686 v3s16(x,y,0), MAP_BLOCKSIZE,
696 Go through every y,z and get back faces in rows of x+
698 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
699 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
700 updateFastFaceRow(daynight_ratio, posRelative_f,
701 v3s16(0,y,z), MAP_BLOCKSIZE,
715 Convert FastFaces to SMesh
718 MeshCollector collector;
720 if(fastfaces_new.size() > 0)
722 // avg 0ms (100ms spikes when loading textures the first time)
723 //TimeTaker timer2("updateMesh() mesh building");
725 video::SMaterial material;
726 material.setFlag(video::EMF_LIGHTING, false);
727 material.setFlag(video::EMF_BILINEAR_FILTER, false);
728 material.setFlag(video::EMF_FOG_ENABLE, true);
729 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
730 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_SIMPLE);
732 for(u32 i=0; i<fastfaces_new.size(); i++)
734 FastFace &f = fastfaces_new[i];
736 const u16 indices[] = {0,1,2,2,3,0};
738 //video::ITexture *texture = g_irrlicht->getTexture(f.tile.spec);
739 video::ITexture *texture = f.tile.texture.atlas;
743 material.setTexture(0, texture);
745 f.tile.applyMaterialOptions(material);
747 collector.append(material, f.vertices, 4, indices, 6);
752 Add special graphics:
758 //TimeTaker timer2("updateMesh() adding special stuff");
760 // Flowing water material
761 video::SMaterial material_water1;
762 material_water1.setFlag(video::EMF_LIGHTING, false);
763 //material_water1.setFlag(video::EMF_BACK_FACE_CULLING, false);
764 material_water1.setFlag(video::EMF_BILINEAR_FILTER, false);
765 material_water1.setFlag(video::EMF_FOG_ENABLE, true);
766 material_water1.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
768 //material_water1.setTexture(0, g_irrlicht->getTexture("water.png"));
769 AtlasPointer pa_water1 = g_texturesource->getTexture(
770 g_texturesource->getTextureId("water.png"));
771 material_water1.setTexture(0, pa_water1.atlas);
773 // New-style leaves material
774 video::SMaterial material_leaves1;
775 material_leaves1.setFlag(video::EMF_LIGHTING, false);
776 //material_leaves1.setFlag(video::EMF_BACK_FACE_CULLING, false);
777 material_leaves1.setFlag(video::EMF_BILINEAR_FILTER, false);
778 material_leaves1.setFlag(video::EMF_FOG_ENABLE, true);
779 material_leaves1.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
781 //material_leaves1.setTexture(0, g_irrlicht->getTexture("leaves.png"));
782 AtlasPointer pa_leaves1 = g_texturesource->getTexture(
783 g_texturesource->getTextureId("leaves.png"));
784 material_leaves1.setTexture(0, pa_leaves1.atlas);
786 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
787 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
788 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
792 MapNode &n = getNodeRef(x,y,z);
797 if(n.d == CONTENT_TORCH)
799 video::SColor c(255,255,255,255);
801 video::S3DVertex vertices[4] =
803 video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c, 0,1),
804 video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c, 1,1),
805 video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, 1,0),
806 video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c, 0,0),
809 v3s16 dir = unpackDir(n.dir);
811 for(s32 i=0; i<4; i++)
813 if(dir == v3s16(1,0,0))
814 vertices[i].Pos.rotateXZBy(0);
815 if(dir == v3s16(-1,0,0))
816 vertices[i].Pos.rotateXZBy(180);
817 if(dir == v3s16(0,0,1))
818 vertices[i].Pos.rotateXZBy(90);
819 if(dir == v3s16(0,0,-1))
820 vertices[i].Pos.rotateXZBy(-90);
821 if(dir == v3s16(0,-1,0))
822 vertices[i].Pos.rotateXZBy(45);
823 if(dir == v3s16(0,1,0))
824 vertices[i].Pos.rotateXZBy(-45);
826 vertices[i].Pos += intToFloat(p + getPosRelative(), BS);
830 video::SMaterial material;
831 material.setFlag(video::EMF_LIGHTING, false);
832 material.setFlag(video::EMF_BACK_FACE_CULLING, false);
833 material.setFlag(video::EMF_BILINEAR_FILTER, false);
834 //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
835 material.MaterialType
836 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
838 if(dir == v3s16(0,-1,0))
839 material.setTexture(0,
840 g_texturesource->getTextureRaw("torch_on_floor.png"));
841 else if(dir == v3s16(0,1,0))
842 material.setTexture(0,
843 g_texturesource->getTextureRaw("torch_on_ceiling.png"));
844 // For backwards compatibility
845 else if(dir == v3s16(0,0,0))
846 material.setTexture(0,
847 g_texturesource->getTextureRaw("torch_on_floor.png"));
849 material.setTexture(0,
850 g_texturesource->getTextureRaw("torch.png"));
852 u16 indices[] = {0,1,2,2,3,0};
853 // Add to mesh collector
854 collector.append(material, vertices, 4, indices, 6);
857 Add flowing water to mesh
859 else if(n.d == CONTENT_WATER)
861 bool top_is_water = false;
863 MapNode n = getNodeParent(v3s16(x,y+1,z));
864 if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
866 }catch(InvalidPositionException &e){}
868 u8 l = decode_light(n.getLightBlend(daynight_ratio));
869 video::SColor c(WATER_ALPHA,l,l,l);
871 // Neighbor water levels (key = relative position)
872 // Includes current node
873 core::map<v3s16, f32> neighbor_levels;
874 core::map<v3s16, u8> neighbor_contents;
875 core::map<v3s16, u8> neighbor_flags;
876 const u8 neighborflag_top_is_water = 0x01;
877 v3s16 neighbor_dirs[9] = {
888 for(u32 i=0; i<9; i++)
890 u8 content = CONTENT_AIR;
891 float level = -0.5 * BS;
895 v3s16 p2 = p + neighbor_dirs[i];
896 MapNode n2 = getNodeParent(p2);
900 if(n2.d == CONTENT_WATERSOURCE)
901 level = (-0.5+node_water_level) * BS;
902 else if(n2.d == CONTENT_WATER)
903 level = (-0.5 + ((float)n2.param2 + 0.5) / 8.0
904 * node_water_level) * BS;
906 // Check node above neighbor.
907 // NOTE: This doesn't get executed if neighbor
910 n2 = getNodeParent(p2);
911 if(n2.d == CONTENT_WATERSOURCE || n2.d == CONTENT_WATER)
912 flags |= neighborflag_top_is_water;
914 catch(InvalidPositionException &e){}
916 neighbor_levels.insert(neighbor_dirs[i], level);
917 neighbor_contents.insert(neighbor_dirs[i], content);
918 neighbor_flags.insert(neighbor_dirs[i], flags);
921 //float water_level = (-0.5 + ((float)n.param2 + 0.5) / 8.0) * BS;
922 //float water_level = neighbor_levels[v3s16(0,0,0)];
924 // Corner heights (average between four waters)
925 f32 corner_levels[4];
927 v3s16 halfdirs[4] = {
933 for(u32 i=0; i<4; i++)
935 v3s16 cornerdir = halfdirs[i];
936 float cornerlevel = 0;
938 for(u32 j=0; j<4; j++)
940 v3s16 neighbordir = cornerdir - halfdirs[j];
941 u8 content = neighbor_contents[neighbordir];
942 // Special case for source nodes
943 if(content == CONTENT_WATERSOURCE)
945 cornerlevel = (-0.5+node_water_level)*BS;
949 else if(content == CONTENT_WATER)
951 cornerlevel += neighbor_levels[neighbordir];
954 else if(content == CONTENT_AIR)
956 cornerlevel += -0.5*BS;
961 cornerlevel /= valid_count;
962 corner_levels[i] = cornerlevel;
969 v3s16 side_dirs[4] = {
975 s16 side_corners[4][2] = {
981 for(u32 i=0; i<4; i++)
983 v3s16 dir = side_dirs[i];
986 If our topside is water and neighbor's topside
987 is water, don't draw side face
990 neighbor_flags[dir] & neighborflag_top_is_water)
993 u8 neighbor_content = neighbor_contents[dir];
995 // Don't draw face if neighbor is not air or water
996 if(neighbor_content != CONTENT_AIR
997 && neighbor_content != CONTENT_WATER)
1000 bool neighbor_is_water = (neighbor_content == CONTENT_WATER);
1002 // Don't draw any faces if neighbor is water and top is water
1003 if(neighbor_is_water == true && top_is_water == false)
1006 video::S3DVertex vertices[4] =
1008 /*video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1),
1009 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1),
1010 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1011 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1012 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1013 pa_water1.x0(), pa_water1.y1()),
1014 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1015 pa_water1.x1(), pa_water1.y1()),
1016 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1017 pa_water1.x1(), pa_water1.y0()),
1018 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1019 pa_water1.x0(), pa_water1.y0()),
1023 If our topside is water, set upper border of face
1024 at upper border of node
1028 vertices[2].Pos.Y = 0.5*BS;
1029 vertices[3].Pos.Y = 0.5*BS;
1032 Otherwise upper position of face is corner levels
1036 vertices[2].Pos.Y = corner_levels[side_corners[i][0]];
1037 vertices[3].Pos.Y = corner_levels[side_corners[i][1]];
1041 If neighbor is water, lower border of face is corner
1044 if(neighbor_is_water)
1046 vertices[0].Pos.Y = corner_levels[side_corners[i][1]];
1047 vertices[1].Pos.Y = corner_levels[side_corners[i][0]];
1050 If neighbor is not water, lower border of face is
1051 lower border of node
1055 vertices[0].Pos.Y = -0.5*BS;
1056 vertices[1].Pos.Y = -0.5*BS;
1059 for(s32 j=0; j<4; j++)
1061 if(dir == v3s16(0,0,1))
1062 vertices[j].Pos.rotateXZBy(0);
1063 if(dir == v3s16(0,0,-1))
1064 vertices[j].Pos.rotateXZBy(180);
1065 if(dir == v3s16(-1,0,0))
1066 vertices[j].Pos.rotateXZBy(90);
1067 if(dir == v3s16(1,0,-0))
1068 vertices[j].Pos.rotateXZBy(-90);
1070 vertices[j].Pos += intToFloat(p + getPosRelative(), BS);
1073 u16 indices[] = {0,1,2,2,3,0};
1074 // Add to mesh collector
1075 collector.append(material_water1, vertices, 4, indices, 6);
1079 Generate top side, if appropriate
1082 if(top_is_water == false)
1084 video::S3DVertex vertices[4] =
1086 /*video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
1087 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
1088 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1089 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1090 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1091 pa_water1.x0(), pa_water1.y1()),
1092 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1093 pa_water1.x1(), pa_water1.y1()),
1094 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
1095 pa_water1.x1(), pa_water1.y0()),
1096 video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
1097 pa_water1.x0(), pa_water1.y0()),
1100 // This fixes a strange bug
1101 s32 corner_resolve[4] = {3,2,1,0};
1103 for(s32 i=0; i<4; i++)
1105 //vertices[i].Pos.Y += water_level;
1106 //vertices[i].Pos.Y += neighbor_levels[v3s16(0,0,0)];
1107 s32 j = corner_resolve[i];
1108 vertices[i].Pos.Y += corner_levels[j];
1109 vertices[i].Pos += intToFloat(p + getPosRelative(), BS);
1112 u16 indices[] = {0,1,2,2,3,0};
1113 // Add to mesh collector
1114 collector.append(material_water1, vertices, 4, indices, 6);
1118 Add water sources to mesh if using new style
1120 else if(n.d == CONTENT_WATERSOURCE && new_style_water)
1122 //bool top_is_water = false;
1123 bool top_is_air = false;
1125 MapNode n = getNodeParent(v3s16(x,y+1,z));
1126 /*if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
1127 top_is_water = true;*/
1128 if(n.d == CONTENT_AIR)
1130 }catch(InvalidPositionException &e){}
1132 /*if(top_is_water == true)
1134 if(top_is_air == false)
1137 u8 l = decode_light(n.getLightBlend(daynight_ratio));
1138 video::SColor c(WATER_ALPHA,l,l,l);
1140 video::S3DVertex vertices[4] =
1142 /*video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
1143 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
1144 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1145 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1146 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1147 pa_water1.x0(), pa_water1.y1()),
1148 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1149 pa_water1.x1(), pa_water1.y1()),
1150 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
1151 pa_water1.x1(), pa_water1.y0()),
1152 video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
1153 pa_water1.x0(), pa_water1.y0()),
1156 for(s32 i=0; i<4; i++)
1158 vertices[i].Pos.Y += (-0.5+node_water_level)*BS;
1159 vertices[i].Pos += intToFloat(p + getPosRelative(), BS);
1162 u16 indices[] = {0,1,2,2,3,0};
1163 // Add to mesh collector
1164 collector.append(material_water1, vertices, 4, indices, 6);
1167 Add leaves if using new style
1169 else if(n.d == CONTENT_LEAVES && new_style_leaves)
1171 /*u8 l = decode_light(n.getLightBlend(daynight_ratio));*/
1172 u8 l = decode_light(undiminish_light(n.getLightBlend(daynight_ratio)));
1173 video::SColor c(255,l,l,l);
1175 for(u32 j=0; j<6; j++)
1177 video::S3DVertex vertices[4] =
1179 /*video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, 0,1),
1180 video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, 1,1),
1181 video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c, 1,0),
1182 video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c, 0,0),*/
1183 video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c,
1184 pa_leaves1.x0(), pa_leaves1.y1()),
1185 video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c,
1186 pa_leaves1.x1(), pa_leaves1.y1()),
1187 video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c,
1188 pa_leaves1.x1(), pa_leaves1.y0()),
1189 video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c,
1190 pa_leaves1.x0(), pa_leaves1.y0()),
1195 for(u16 i=0; i<4; i++)
1196 vertices[i].Pos.rotateXZBy(0);
1200 for(u16 i=0; i<4; i++)
1201 vertices[i].Pos.rotateXZBy(180);
1205 for(u16 i=0; i<4; i++)
1206 vertices[i].Pos.rotateXZBy(-90);
1210 for(u16 i=0; i<4; i++)
1211 vertices[i].Pos.rotateXZBy(90);
1215 for(u16 i=0; i<4; i++)
1216 vertices[i].Pos.rotateYZBy(-90);
1220 for(u16 i=0; i<4; i++)
1221 vertices[i].Pos.rotateYZBy(90);
1224 for(u16 i=0; i<4; i++)
1226 vertices[i].Pos += intToFloat(p + getPosRelative(), BS);
1229 u16 indices[] = {0,1,2,2,3,0};
1230 // Add to mesh collector
1231 collector.append(material_leaves1, vertices, 4, indices, 6);
1237 Add stuff from collector to mesh
1240 scene::SMesh *mesh_new = NULL;
1241 mesh_new = new scene::SMesh();
1243 collector.fillMesh(mesh_new);
1246 Do some stuff to the mesh
1249 mesh_new->recalculateBoundingBox();
1252 Delete new mesh if it is empty
1255 if(mesh_new->getMeshBufferCount() == 0)
1264 // Usually 1-700 faces and 1-7 materials
1265 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
1266 <<"and uses "<<mesh_new->getMeshBufferCount()
1267 <<" materials (meshbuffers)"<<std::endl;
1270 // Use VBO for mesh (this just would set this for ever buffer)
1271 // This will lead to infinite memory usage because or irrlicht.
1272 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
1275 NOTE: If that is enabled, some kind of a queue to the main
1276 thread should be made which would call irrlicht to delete
1277 the hardware buffer and then delete the mesh
1287 //scene::SMesh *mesh_old = mesh[daynight_i];
1288 //mesh[daynight_i] = mesh_new;
1290 scene::SMesh *mesh_old = mesh;
1292 setMeshExpired(false);
1294 if(mesh_old != NULL)
1296 // Remove hardware buffers of meshbuffers of mesh
1297 // NOTE: No way, this runs in a different thread and everything
1298 /*u32 c = mesh_old->getMeshBufferCount();
1299 for(u32 i=0; i<c; i++)
1301 IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1304 /*dstream<<"mesh_old->getReferenceCount()="
1305 <<mesh_old->getReferenceCount()<<std::endl;
1306 u32 c = mesh_old->getMeshBufferCount();
1307 for(u32 i=0; i<c; i++)
1309 scene::IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1310 dstream<<"buf->getReferenceCount()="
1311 <<buf->getReferenceCount()<<std::endl;
1320 mesh_mutex.Unlock();
1322 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1325 /*void MapBlock::updateMeshes(s32 first_i)
1327 assert(first_i >= 0 && first_i <= DAYNIGHT_CACHE_COUNT);
1328 updateMesh(first_i);
1329 for(s32 i=0; i<DAYNIGHT_CACHE_COUNT; i++)
1340 Propagates sunlight down through the block.
1341 Doesn't modify nodes that are not affected by sunlight.
1343 Returns false if sunlight at bottom block is invalid
1344 Returns true if bottom block doesn't exist.
1346 If there is a block above, continues from it.
1347 If there is no block above, assumes there is sunlight, unless
1348 is_underground is set or highest node is water.
1350 At the moment, all sunlighted nodes are added to light_sources.
1351 - SUGG: This could be optimized
1353 Turns sunglighted mud into grass.
1355 if remove_light==true, sets non-sunlighted nodes black.
1357 if black_air_left!=NULL, it is set to true if non-sunlighted
1358 air is left in block.
1360 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources,
1361 bool remove_light, bool *black_air_left,
1364 // Whether the sunlight at the top of the bottom block is valid
1365 bool block_below_is_valid = true;
1367 v3s16 pos_relative = getPosRelative();
1369 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
1371 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
1374 bool no_sunlight = false;
1375 bool no_top_block = false;
1376 // Check if node above block has sunlight
1378 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1379 if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1384 catch(InvalidPositionException &e)
1386 no_top_block = true;
1388 // NOTE: This makes over-ground roofed places sunlighted
1389 // Assume sunlight, unless is_underground==true
1396 MapNode n = getNode(v3s16(x, MAP_BLOCKSIZE-1, z));
1397 if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
1402 // NOTE: As of now, this just would make everything dark.
1404 //no_sunlight = true;
1407 #if 0 // Doesn't work; nothing gets light.
1408 bool no_sunlight = true;
1409 bool no_top_block = false;
1410 // Check if node above block has sunlight
1412 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1413 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
1415 no_sunlight = false;
1418 catch(InvalidPositionException &e)
1420 no_top_block = true;
1424 /*std::cout<<"("<<x<<","<<z<<"): "
1425 <<"no_top_block="<<no_top_block
1426 <<", is_underground="<<is_underground
1427 <<", no_sunlight="<<no_sunlight
1430 s16 y = MAP_BLOCKSIZE-1;
1432 // This makes difference to diminishing in water.
1433 bool stopped_to_solid_object = false;
1435 u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
1440 MapNode &n = getNodeRef(pos);
1442 if(current_light == 0)
1446 else if(current_light == LIGHT_SUN && n.sunlight_propagates())
1448 // Do nothing: Sunlight is continued
1450 else if(n.light_propagates() == false)
1454 bool upper_is_air = false;
1457 if(getNodeParent(pos+v3s16(0,1,0)).d == CONTENT_AIR)
1458 upper_is_air = true;
1460 catch(InvalidPositionException &e)
1463 // Turn mud into grass
1464 if(upper_is_air && n.d == CONTENT_MUD
1465 && current_light == LIGHT_SUN)
1467 n.d = CONTENT_GRASS;
1471 // A solid object is on the way.
1472 stopped_to_solid_object = true;
1480 current_light = diminish_light(current_light);
1483 u8 old_light = n.getLight(LIGHTBANK_DAY);
1485 if(current_light > old_light || remove_light)
1487 n.setLight(LIGHTBANK_DAY, current_light);
1490 if(diminish_light(current_light) != 0)
1492 light_sources.insert(pos_relative + pos, true);
1495 if(current_light == 0 && stopped_to_solid_object)
1499 *black_air_left = true;
1504 // Whether or not the block below should see LIGHT_SUN
1505 bool sunlight_should_go_down = (current_light == LIGHT_SUN);
1508 If the block below hasn't already been marked invalid:
1510 Check if the node below the block has proper sunlight at top.
1511 If not, the block below is invalid.
1513 Ignore non-transparent nodes as they always have no light
1517 if(block_below_is_valid)
1519 MapNode n = getNodeParent(v3s16(x, -1, z));
1520 if(n.light_propagates())
1522 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN
1523 && sunlight_should_go_down == false)
1524 block_below_is_valid = false;
1525 else if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN
1526 && sunlight_should_go_down == true)
1527 block_below_is_valid = false;
1531 catch(InvalidPositionException &e)
1533 /*std::cout<<"InvalidBlockException for bottom block node"
1535 // Just no block below, no need to panic.
1540 return block_below_is_valid;
1543 void MapBlock::copyTo(VoxelManipulator &dst)
1545 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
1546 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
1548 // Copy from data to VoxelManipulator
1549 dst.copyFrom(data, data_area, v3s16(0,0,0),
1550 getPosRelative(), data_size);
1553 void MapBlock::copyFrom(VoxelManipulator &dst)
1555 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
1556 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
1558 // Copy from VoxelManipulator to data
1559 dst.copyTo(data, data_area, v3s16(0,0,0),
1560 getPosRelative(), data_size);
1563 void MapBlock::stepObjects(float dtime, bool server, u32 daynight_ratio)
1568 m_objects.step(dtime, server, daynight_ratio);
1571 Spawn some objects at random.
1573 Use dayNightDiffed() to approximate being near ground level
1575 if(m_spawn_timer < -999)
1579 if(dayNightDiffed() == true && getObjectCount() == 0)
1581 m_spawn_timer -= dtime;
1582 if(m_spawn_timer <= 0.0)
1584 m_spawn_timer += myrand() % 300;
1587 (myrand()%(MAP_BLOCKSIZE-1))+0,
1588 (myrand()%(MAP_BLOCKSIZE-1))+0
1591 s16 y = getGroundLevel(p2d);
1595 v3s16 p(p2d.X, y+1, p2d.Y);
1597 if(getNode(p).d == CONTENT_AIR
1598 && getNode(p).getLightBlend(daynight_ratio) <= 11)
1600 RatObject *obj = new RatObject(NULL, -1, intToFloat(p, BS));
1611 void MapBlock::updateDayNightDiff()
1615 m_day_night_differs = false;
1619 bool differs = false;
1622 Check if any lighting value differs
1624 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1626 MapNode &n = data[i];
1627 if(n.getLight(LIGHTBANK_DAY) != n.getLight(LIGHTBANK_NIGHT))
1635 If some lighting values differ, check if the whole thing is
1636 just air. If it is, differ = false
1640 bool only_air = true;
1641 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1643 MapNode &n = data[i];
1644 if(n.d != CONTENT_AIR)
1654 // Set member variable
1655 m_day_night_differs = differs;
1658 s16 MapBlock::getGroundLevel(v2s16 p2d)
1664 s16 y = MAP_BLOCKSIZE-1;
1667 //if(is_ground_content(getNodeRef(p2d.X, y, p2d.Y).d))
1668 if(content_features(getNodeRef(p2d.X, y, p2d.Y).d).walkable)
1670 if(y == MAP_BLOCKSIZE-1)
1678 catch(InvalidPositionException &e)
1688 void MapBlock::serialize(std::ostream &os, u8 version)
1690 if(!ser_ver_supported(version))
1691 throw VersionMismatchException("ERROR: MapBlock format not supported");
1695 throw SerializationError("ERROR: Not writing dummy block.");
1698 // These have no compression
1699 if(version <= 3 || version == 5 || version == 6)
1701 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1703 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
1704 SharedBuffer<u8> dest(buflen);
1706 dest[0] = is_underground;
1707 for(u32 i=0; i<nodecount; i++)
1709 u32 s = 1 + i * MapNode::serializedLength(version);
1710 data[i].serialize(&dest[s], version);
1713 os.write((char*)*dest, dest.getSize());
1715 else if(version <= 10)
1719 Compress the materials and the params separately.
1723 os.write((char*)&is_underground, 1);
1725 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1727 // Get and compress materials
1728 SharedBuffer<u8> materialdata(nodecount);
1729 for(u32 i=0; i<nodecount; i++)
1731 materialdata[i] = data[i].d;
1733 compress(materialdata, os, version);
1735 // Get and compress lights
1736 SharedBuffer<u8> lightdata(nodecount);
1737 for(u32 i=0; i<nodecount; i++)
1739 lightdata[i] = data[i].param;
1741 compress(lightdata, os, version);
1745 // Get and compress param2
1746 SharedBuffer<u8> param2data(nodecount);
1747 for(u32 i=0; i<nodecount; i++)
1749 param2data[i] = data[i].param2;
1751 compress(param2data, os, version);
1754 // All other versions (newest)
1761 if(m_day_night_differs)
1763 if(m_lighting_expired)
1765 os.write((char*)&flags, 1);
1767 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1773 SharedBuffer<u8> databuf(nodecount*3);
1776 for(u32 i=0; i<nodecount; i++)
1778 databuf[i] = data[i].d;
1782 for(u32 i=0; i<nodecount; i++)
1784 databuf[i+nodecount] = data[i].param;
1788 for(u32 i=0; i<nodecount; i++)
1790 databuf[i+nodecount*2] = data[i].param2;
1794 Compress data to output stream
1797 compress(databuf, os, version);
1801 void MapBlock::deSerialize(std::istream &is, u8 version)
1803 if(!ser_ver_supported(version))
1804 throw VersionMismatchException("ERROR: MapBlock format not supported");
1806 // These have no compression
1807 if(version <= 3 || version == 5 || version == 6)
1809 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1812 if(is.gcount() != 1)
1813 throw SerializationError
1814 ("MapBlock::deSerialize: no enough input data");
1815 is_underground = tmp;
1816 for(u32 i=0; i<nodecount; i++)
1818 s32 len = MapNode::serializedLength(version);
1819 SharedBuffer<u8> d(len);
1820 is.read((char*)*d, len);
1821 if(is.gcount() != len)
1822 throw SerializationError
1823 ("MapBlock::deSerialize: no enough input data");
1824 data[i].deSerialize(*d, version);
1827 else if(version <= 10)
1829 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1832 is.read((char*)&t8, 1);
1833 is_underground = t8;
1836 // Uncompress and set material data
1837 std::ostringstream os(std::ios_base::binary);
1838 decompress(is, os, version);
1839 std::string s = os.str();
1840 if(s.size() != nodecount)
1841 throw SerializationError
1842 ("MapBlock::deSerialize: invalid format");
1843 for(u32 i=0; i<s.size(); i++)
1849 // Uncompress and set param data
1850 std::ostringstream os(std::ios_base::binary);
1851 decompress(is, os, version);
1852 std::string s = os.str();
1853 if(s.size() != nodecount)
1854 throw SerializationError
1855 ("MapBlock::deSerialize: invalid format");
1856 for(u32 i=0; i<s.size(); i++)
1858 data[i].param = s[i];
1864 // Uncompress and set param2 data
1865 std::ostringstream os(std::ios_base::binary);
1866 decompress(is, os, version);
1867 std::string s = os.str();
1868 if(s.size() != nodecount)
1869 throw SerializationError
1870 ("MapBlock::deSerialize: invalid format");
1871 for(u32 i=0; i<s.size(); i++)
1873 data[i].param2 = s[i];
1877 // All other versions (newest)
1880 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1883 is.read((char*)&flags, 1);
1884 is_underground = (flags & 0x01) ? true : false;
1885 m_day_night_differs = (flags & 0x02) ? true : false;
1886 m_lighting_expired = (flags & 0x04) ? true : false;
1889 std::ostringstream os(std::ios_base::binary);
1890 decompress(is, os, version);
1891 std::string s = os.str();
1892 if(s.size() != nodecount*3)
1893 throw SerializationError
1894 ("MapBlock::deSerialize: invalid format");
1897 for(u32 i=0; i<nodecount; i++)
1902 for(u32 i=0; i<nodecount; i++)
1904 data[i].param = s[i+nodecount];
1907 for(u32 i=0; i<nodecount; i++)
1909 data[i].param2 = s[i+nodecount*2];
1914 Translate nodes as specified in the translate_to fields of
1917 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1919 MapNode &n = data[i];
1921 MapNode *translate_to = content_features(n.d).translate_to;
1924 dstream<<"MapBlock: WARNING: Translating node "<<n.d<<" to "
1925 <<translate_to->d<<std::endl;