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 face.vertices[0] = video::S3DVertex(vertex_pos[0], zerovector, c,
259 core::vector2d<f32>(abs_scale,1));
260 face.vertices[1] = video::S3DVertex(vertex_pos[1], zerovector, c,
261 core::vector2d<f32>(0,1));
262 face.vertices[2] = video::S3DVertex(vertex_pos[2], zerovector, c,
263 core::vector2d<f32>(0,0));
264 face.vertices[3] = video::S3DVertex(vertex_pos[3], zerovector, c,
265 core::vector2d<f32>(abs_scale,0));
267 /*float x0 = (float)tile.tx/256.0;
268 float y0 = (float)tile.ty/256.0;
269 float w = ((float)tile.tw + 1.0)/256.0;
270 float h = ((float)tile.th + 1.0)/256.0;*/
272 float x0 = tile.texture.pos.X;
273 float y0 = tile.texture.pos.Y;
274 float w = tile.texture.size.X;
275 float h = tile.texture.size.Y;
277 face.vertices[0] = video::S3DVertex(vertex_pos[0], zerovector, c,
278 core::vector2d<f32>(x0+w*abs_scale, y0+h));
279 face.vertices[1] = video::S3DVertex(vertex_pos[1], zerovector, c,
280 core::vector2d<f32>(x0, y0+h));
281 face.vertices[2] = video::S3DVertex(vertex_pos[2], zerovector, c,
282 core::vector2d<f32>(x0, y0));
283 face.vertices[3] = video::S3DVertex(vertex_pos[3], zerovector, c,
284 core::vector2d<f32>(x0+w*abs_scale, y0));
288 //f->tile = TILE_STONE;
290 dest.push_back(face);
295 Gets node tile from any place relative to block.
296 Returns TILE_NODE if doesn't exist or should not be drawn.
298 TileSpec MapBlock::getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir,
299 NodeModMap &temp_mods)
302 spec = mn.getTile(face_dir);
305 Check temporary modifications on this node
307 /*core::map<v3s16, NodeMod>::Node *n;
308 n = m_temp_mods.find(p);
312 struct NodeMod mod = n->getValue();*/
314 if(temp_mods.get(p, &mod))
316 if(mod.type == NODEMOD_CHANGECONTENT)
318 MapNode mn2(mod.param);
319 spec = mn2.getTile(face_dir);
321 if(mod.type == NODEMOD_CRACK)
324 Get texture id, translate it to name, append stuff to
328 // Get original texture name
329 u32 orig_id = spec.texture.id;
330 std::string orig_name = g_texturesource->getTextureName(orig_id);
332 // Create new texture name
333 std::ostringstream os;
334 os<<orig_name<<"^[crack"<<mod.param;
337 u32 new_id = g_texturesource->getTextureId(os.str());
339 /*dstream<<"MapBlock::getNodeTile(): Switching from "
340 <<orig_name<<" to "<<os.str()<<" ("
341 <<orig_id<<" to "<<new_id<<")"<<std::endl;*/
343 spec.texture = g_texturesource->getTexture(new_id);
350 u8 MapBlock::getNodeContent(v3s16 p, MapNode mn, NodeModMap &temp_mods)
353 Check temporary modifications on this node
355 /*core::map<v3s16, NodeMod>::Node *n;
356 n = m_temp_mods.find(p);
360 struct NodeMod mod = n->getValue();*/
362 if(temp_mods.get(p, &mod))
364 if(mod.type == NODEMOD_CHANGECONTENT)
369 if(mod.type == NODEMOD_CRACK)
372 Content doesn't change.
374 face_contents works just like it should, because
375 there should not be faces between differently cracked
378 If a semi-transparent node is cracked in front an
379 another one, it really doesn't matter whether there
380 is a cracked face drawn in between or not.
390 translate_dir: unit vector with only one of x, y or z
391 face_dir: unit vector with only one of x, y or z
393 void MapBlock::updateFastFaceRow(
402 core::array<FastFace> &dest,
403 NodeModMap &temp_mods)
407 u16 continuous_tiles_count = 0;
409 MapNode n0 = getNodeParentNoEx(p);
410 MapNode n1 = getNodeParentNoEx(p + face_dir);
412 u8 light = getFaceLight(daynight_ratio, n0, n1, face_dir);
414 TileSpec tile0 = getNodeTile(n0, p, face_dir, temp_mods);
415 TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir, temp_mods);
417 for(u16 j=0; j<length; j++)
419 bool next_is_different = true;
428 // If at last position, there is nothing to compare to and
429 // the face must be drawn anyway
432 p_next = p + translate_dir;
433 n0_next = getNodeParentNoEx(p_next);
434 n1_next = getNodeParentNoEx(p_next + face_dir);
435 tile0_next = getNodeTile(n0_next, p_next, face_dir, temp_mods);
436 tile1_next = getNodeTile(n1_next,p_next+face_dir,-face_dir, temp_mods);
437 light_next = getFaceLight(daynight_ratio, n0_next, n1_next, face_dir);
439 if(tile0_next == tile0
440 && tile1_next == tile1
441 && light_next == light)
443 next_is_different = false;
447 continuous_tiles_count++;
449 // This is set to true if the texture doesn't allow more tiling
450 bool end_of_texture = false;
452 If there is no texture, it can be tiled infinitely.
453 If tiled==0, it means the texture can be tiled infinitely.
454 Otherwise check tiled agains continuous_tiles_count.
456 This check has to be made for both tiles, because this is
457 a bit hackish and we know which one we're using only when
458 the decision to make the faces is made.
460 if(tile0.texture.atlas != NULL && tile0.texture.tiled != 0)
462 if(tile0.texture.tiled <= continuous_tiles_count)
463 end_of_texture = true;
465 if(tile1.texture.atlas != NULL && tile1.texture.tiled != 0)
467 if(tile1.texture.tiled <= continuous_tiles_count)
468 end_of_texture = true;
471 //end_of_texture = true; //DEBUG
473 if(next_is_different || end_of_texture)
476 Create a face if there should be one
478 //u8 mf = face_contents(tile0, tile1);
480 u8 content0 = getNodeContent(p, n0, temp_mods);
481 u8 content1 = getNodeContent(p + face_dir, n1, temp_mods);
482 u8 mf = face_contents(content0, content1);
486 // Floating point conversion of the position vector
487 v3f pf(p.X, p.Y, p.Z);
488 // Center point of face (kind of)
489 v3f sp = pf - ((f32)continuous_tiles_count / 2. - 0.5) * translate_dir_f;
491 if(translate_dir.X != 0){
492 scale.X = continuous_tiles_count;
494 if(translate_dir.Y != 0){
495 scale.Y = continuous_tiles_count;
497 if(translate_dir.Z != 0){
498 scale.Z = continuous_tiles_count;
503 // If node at sp (tile0) is more solid
506 makeFastFace(tile0, decode_light(light),
508 posRelative_f, dest);
510 // If node at sp is less solid (mf == 2)
513 makeFastFace(tile1, decode_light(light),
514 sp+face_dir_f, -face_dir, scale,
515 posRelative_f, dest);
520 continuous_tiles_count = 0;
533 This is used because CMeshBuffer::append() is very slow
537 video::SMaterial material;
538 core::array<u16> indices;
539 core::array<video::S3DVertex> vertices;
546 video::SMaterial material,
547 const video::S3DVertex* const vertices,
549 const u16* const indices,
553 PreMeshBuffer *p = NULL;
554 for(u32 i=0; i<m_prebuffers.size(); i++)
556 PreMeshBuffer &pp = m_prebuffers[i];
557 if(pp.material != material)
567 pp.material = material;
568 m_prebuffers.push_back(pp);
569 p = &m_prebuffers[m_prebuffers.size()-1];
572 u32 vertex_count = p->vertices.size();
573 for(u32 i=0; i<numIndices; i++)
575 u32 j = indices[i] + vertex_count;
578 dstream<<"FIXME: Meshbuffer ran out of indices"<<std::endl;
579 // NOTE: Fix is to just add an another MeshBuffer
581 p->indices.push_back(j);
583 for(u32 i=0; i<numVertices; i++)
585 p->vertices.push_back(vertices[i]);
589 void fillMesh(scene::SMesh *mesh)
591 /*dstream<<"Filling mesh with "<<m_prebuffers.size()
592 <<" meshbuffers"<<std::endl;*/
593 for(u32 i=0; i<m_prebuffers.size(); i++)
595 PreMeshBuffer &p = m_prebuffers[i];
597 /*dstream<<"p.vertices.size()="<<p.vertices.size()
598 <<", p.indices.size()="<<p.indices.size()
603 // This is a "Standard MeshBuffer",
604 // it's a typedeffed CMeshBuffer<video::S3DVertex>
605 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
607 buf->Material = p.material;
608 //((scene::SMeshBuffer*)buf)->Material = p.material;
610 //buf->setHardwareMappingHint(scene::EHM_STATIC);
612 mesh->addMeshBuffer(buf);
616 buf->append(p.vertices.pointer(), p.vertices.size(),
617 p.indices.pointer(), p.indices.size());
622 core::array<PreMeshBuffer> m_prebuffers;
625 void MapBlock::updateMesh(u32 daynight_ratio)
629 DEBUG: If mesh has been generated, don't generate it again
632 JMutexAutoLock meshlock(mesh_mutex);
638 // 4-21ms for MAP_BLOCKSIZE=16
639 // 24-155ms for MAP_BLOCKSIZE=32
640 //TimeTaker timer1("updateMesh()");
642 core::array<FastFace> fastfaces_new;
644 v3f posRelative_f(getPosRelative().X, getPosRelative().Y,
645 getPosRelative().Z); // floating point conversion
648 Avoid interlocks by copying m_temp_mods
650 NodeModMap temp_mods;
652 JMutexAutoLock lock(m_temp_mods_mutex);
653 m_temp_mods.copy(temp_mods);
659 bool new_style_water = g_settings.getBool("new_style_water");
660 bool new_style_leaves = g_settings.getBool("new_style_leaves");
662 float node_water_level = 1.0;
664 node_water_level = 0.85;
667 We are including the faces of the trailing edges of the block.
668 This means that when something changes, the caller must
669 also update the meshes of the blocks at the leading edges.
671 NOTE: This is the slowest part of this method.
675 // 4-23ms for MAP_BLOCKSIZE=16
676 //TimeTaker timer2("updateMesh() collect");
679 Go through every y,z and get top faces in rows of x+
681 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
682 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
683 updateFastFaceRow(daynight_ratio, posRelative_f,
684 v3s16(0,y,z), MAP_BLOCKSIZE,
687 v3s16(0,1,0), //face dir
694 Go through every x,y and get right faces in rows of z+
696 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
697 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
698 updateFastFaceRow(daynight_ratio, posRelative_f,
699 v3s16(x,y,0), MAP_BLOCKSIZE,
709 Go through every y,z and get back faces in rows of x+
711 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
712 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
713 updateFastFaceRow(daynight_ratio, posRelative_f,
714 v3s16(0,y,z), MAP_BLOCKSIZE,
728 Convert FastFaces to SMesh
731 MeshCollector collector;
733 if(fastfaces_new.size() > 0)
735 // avg 0ms (100ms spikes when loading textures the first time)
736 //TimeTaker timer2("updateMesh() mesh building");
738 video::SMaterial material;
739 material.Lighting = false;
740 //material.BackfaceCulling = false;
741 material.setFlag(video::EMF_BILINEAR_FILTER, false);
742 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
743 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_SIMPLE);
744 material.setFlag(video::EMF_FOG_ENABLE, true);
746 for(u32 i=0; i<fastfaces_new.size(); i++)
748 FastFace &f = fastfaces_new[i];
750 const u16 indices[] = {0,1,2,2,3,0};
752 //video::ITexture *texture = g_irrlicht->getTexture(f.tile.spec);
753 video::ITexture *texture = f.tile.texture.atlas;
757 material.setTexture(0, texture);
759 f.tile.applyMaterialOptions(material);
761 collector.append(material, f.vertices, 4, indices, 6);
766 Add special graphics:
772 //TimeTaker timer2("updateMesh() adding special stuff");
774 // Flowing water material
775 video::SMaterial material_water1;
776 material_water1.setFlag(video::EMF_LIGHTING, false);
777 //material_water1.setFlag(video::EMF_BACK_FACE_CULLING, false);
778 material_water1.setFlag(video::EMF_BILINEAR_FILTER, false);
779 material_water1.setFlag(video::EMF_FOG_ENABLE, true);
780 material_water1.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
782 //material_water1.setTexture(0, g_irrlicht->getTexture("water.png"));
783 AtlasPointer pa_water1 = g_texturesource->getTexture(
784 g_texturesource->getTextureId("water.png"));
785 material_water1.setTexture(0, pa_water1.atlas);
787 // New-style leaves material
788 video::SMaterial material_leaves1;
789 material_leaves1.setFlag(video::EMF_LIGHTING, false);
790 //material_leaves1.setFlag(video::EMF_BACK_FACE_CULLING, false);
791 material_leaves1.setFlag(video::EMF_BILINEAR_FILTER, false);
792 material_leaves1.setFlag(video::EMF_FOG_ENABLE, true);
793 material_leaves1.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
795 //material_leaves1.setTexture(0, g_irrlicht->getTexture("leaves.png"));
796 AtlasPointer pa_leaves1 = g_texturesource->getTexture(
797 g_texturesource->getTextureId("leaves.png"));
798 material_leaves1.setTexture(0, pa_leaves1.atlas);
800 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
801 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
802 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
806 MapNode &n = getNodeRef(x,y,z);
811 if(n.d == CONTENT_TORCH)
813 video::SColor c(255,255,255,255);
815 video::S3DVertex vertices[4] =
817 video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c, 0,1),
818 video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c, 1,1),
819 video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, 1,0),
820 video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c, 0,0),
823 v3s16 dir = unpackDir(n.dir);
825 for(s32 i=0; i<4; i++)
827 if(dir == v3s16(1,0,0))
828 vertices[i].Pos.rotateXZBy(0);
829 if(dir == v3s16(-1,0,0))
830 vertices[i].Pos.rotateXZBy(180);
831 if(dir == v3s16(0,0,1))
832 vertices[i].Pos.rotateXZBy(90);
833 if(dir == v3s16(0,0,-1))
834 vertices[i].Pos.rotateXZBy(-90);
835 if(dir == v3s16(0,-1,0))
836 vertices[i].Pos.rotateXZBy(45);
837 if(dir == v3s16(0,1,0))
838 vertices[i].Pos.rotateXZBy(-45);
840 vertices[i].Pos += intToFloat(p + getPosRelative());
844 video::SMaterial material;
845 material.setFlag(video::EMF_LIGHTING, false);
846 material.setFlag(video::EMF_BACK_FACE_CULLING, false);
847 material.setFlag(video::EMF_BILINEAR_FILTER, false);
848 //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
849 material.MaterialType
850 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
852 if(dir == v3s16(0,-1,0))
853 material.setTexture(0,
854 g_texturesource->getTextureRaw("torch_on_floor.png"));
855 else if(dir == v3s16(0,1,0))
856 material.setTexture(0,
857 g_texturesource->getTextureRaw("torch_on_ceiling.png"));
858 // For backwards compatibility
859 else if(dir == v3s16(0,0,0))
860 material.setTexture(0,
861 g_texturesource->getTextureRaw("torch_on_floor.png"));
863 material.setTexture(0,
864 g_texturesource->getTextureRaw("torch.png"));
866 u16 indices[] = {0,1,2,2,3,0};
867 // Add to mesh collector
868 collector.append(material, vertices, 4, indices, 6);
871 Add flowing water to mesh
873 else if(n.d == CONTENT_WATER)
875 bool top_is_water = false;
877 MapNode n = getNodeParent(v3s16(x,y+1,z));
878 if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
880 }catch(InvalidPositionException &e){}
882 u8 l = decode_light(n.getLightBlend(daynight_ratio));
883 video::SColor c(WATER_ALPHA,l,l,l);
885 // Neighbor water levels (key = relative position)
886 // Includes current node
887 core::map<v3s16, f32> neighbor_levels;
888 core::map<v3s16, u8> neighbor_contents;
889 core::map<v3s16, u8> neighbor_flags;
890 const u8 neighborflag_top_is_water = 0x01;
891 v3s16 neighbor_dirs[9] = {
902 for(u32 i=0; i<9; i++)
904 u8 content = CONTENT_AIR;
905 float level = -0.5 * BS;
909 v3s16 p2 = p + neighbor_dirs[i];
910 MapNode n2 = getNodeParent(p2);
914 if(n2.d == CONTENT_WATERSOURCE)
915 level = (-0.5+node_water_level) * BS;
916 else if(n2.d == CONTENT_WATER)
917 level = (-0.5 + ((float)n2.param2 + 0.5) / 8.0
918 * node_water_level) * BS;
920 // Check node above neighbor.
921 // NOTE: This doesn't get executed if neighbor
924 n2 = getNodeParent(p2);
925 if(n2.d == CONTENT_WATERSOURCE || n2.d == CONTENT_WATER)
926 flags |= neighborflag_top_is_water;
928 catch(InvalidPositionException &e){}
930 neighbor_levels.insert(neighbor_dirs[i], level);
931 neighbor_contents.insert(neighbor_dirs[i], content);
932 neighbor_flags.insert(neighbor_dirs[i], flags);
935 //float water_level = (-0.5 + ((float)n.param2 + 0.5) / 8.0) * BS;
936 //float water_level = neighbor_levels[v3s16(0,0,0)];
938 // Corner heights (average between four waters)
939 f32 corner_levels[4];
941 v3s16 halfdirs[4] = {
947 for(u32 i=0; i<4; i++)
949 v3s16 cornerdir = halfdirs[i];
950 float cornerlevel = 0;
952 for(u32 j=0; j<4; j++)
954 v3s16 neighbordir = cornerdir - halfdirs[j];
955 u8 content = neighbor_contents[neighbordir];
956 // Special case for source nodes
957 if(content == CONTENT_WATERSOURCE)
959 cornerlevel = (-0.5+node_water_level)*BS;
963 else if(content == CONTENT_WATER)
965 cornerlevel += neighbor_levels[neighbordir];
968 else if(content == CONTENT_AIR)
970 cornerlevel += -0.5*BS;
975 cornerlevel /= valid_count;
976 corner_levels[i] = cornerlevel;
983 v3s16 side_dirs[4] = {
989 s16 side_corners[4][2] = {
995 for(u32 i=0; i<4; i++)
997 v3s16 dir = side_dirs[i];
1000 If our topside is water and neighbor's topside
1001 is water, don't draw side face
1004 neighbor_flags[dir] & neighborflag_top_is_water)
1007 u8 neighbor_content = neighbor_contents[dir];
1009 // Don't draw face if neighbor is not air or water
1010 if(neighbor_content != CONTENT_AIR
1011 && neighbor_content != CONTENT_WATER)
1014 bool neighbor_is_water = (neighbor_content == CONTENT_WATER);
1016 // Don't draw any faces if neighbor is water and top is water
1017 if(neighbor_is_water == true && top_is_water == false)
1020 video::S3DVertex vertices[4] =
1022 /*video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1),
1023 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1),
1024 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1025 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1026 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1027 pa_water1.x0(), pa_water1.y1()),
1028 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1029 pa_water1.x1(), pa_water1.y1()),
1030 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1031 pa_water1.x1(), pa_water1.y0()),
1032 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1033 pa_water1.x0(), pa_water1.y0()),
1037 If our topside is water, set upper border of face
1038 at upper border of node
1042 vertices[2].Pos.Y = 0.5*BS;
1043 vertices[3].Pos.Y = 0.5*BS;
1046 Otherwise upper position of face is corner levels
1050 vertices[2].Pos.Y = corner_levels[side_corners[i][0]];
1051 vertices[3].Pos.Y = corner_levels[side_corners[i][1]];
1055 If neighbor is water, lower border of face is corner
1058 if(neighbor_is_water)
1060 vertices[0].Pos.Y = corner_levels[side_corners[i][1]];
1061 vertices[1].Pos.Y = corner_levels[side_corners[i][0]];
1064 If neighbor is not water, lower border of face is
1065 lower border of node
1069 vertices[0].Pos.Y = -0.5*BS;
1070 vertices[1].Pos.Y = -0.5*BS;
1073 for(s32 j=0; j<4; j++)
1075 if(dir == v3s16(0,0,1))
1076 vertices[j].Pos.rotateXZBy(0);
1077 if(dir == v3s16(0,0,-1))
1078 vertices[j].Pos.rotateXZBy(180);
1079 if(dir == v3s16(-1,0,0))
1080 vertices[j].Pos.rotateXZBy(90);
1081 if(dir == v3s16(1,0,-0))
1082 vertices[j].Pos.rotateXZBy(-90);
1084 vertices[j].Pos += intToFloat(p + getPosRelative());
1087 u16 indices[] = {0,1,2,2,3,0};
1088 // Add to mesh collector
1089 collector.append(material_water1, vertices, 4, indices, 6);
1093 Generate top side, if appropriate
1096 if(top_is_water == false)
1098 video::S3DVertex vertices[4] =
1100 /*video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
1101 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
1102 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1103 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1104 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1105 pa_water1.x0(), pa_water1.y1()),
1106 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1107 pa_water1.x1(), pa_water1.y1()),
1108 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
1109 pa_water1.x1(), pa_water1.y0()),
1110 video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
1111 pa_water1.x0(), pa_water1.y0()),
1114 // This fixes a strange bug
1115 s32 corner_resolve[4] = {3,2,1,0};
1117 for(s32 i=0; i<4; i++)
1119 //vertices[i].Pos.Y += water_level;
1120 //vertices[i].Pos.Y += neighbor_levels[v3s16(0,0,0)];
1121 s32 j = corner_resolve[i];
1122 vertices[i].Pos.Y += corner_levels[j];
1123 vertices[i].Pos += intToFloat(p + getPosRelative());
1126 u16 indices[] = {0,1,2,2,3,0};
1127 // Add to mesh collector
1128 collector.append(material_water1, vertices, 4, indices, 6);
1132 Add water sources to mesh if using new style
1134 else if(n.d == CONTENT_WATERSOURCE && new_style_water)
1136 //bool top_is_water = false;
1137 bool top_is_air = false;
1139 MapNode n = getNodeParent(v3s16(x,y+1,z));
1140 /*if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
1141 top_is_water = true;*/
1142 if(n.d == CONTENT_AIR)
1144 }catch(InvalidPositionException &e){}
1146 /*if(top_is_water == true)
1148 if(top_is_air == false)
1151 u8 l = decode_light(n.getLightBlend(daynight_ratio));
1152 video::SColor c(WATER_ALPHA,l,l,l);
1154 video::S3DVertex vertices[4] =
1156 /*video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
1157 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
1158 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1159 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1160 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1161 pa_water1.x0(), pa_water1.y1()),
1162 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1163 pa_water1.x1(), pa_water1.y1()),
1164 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
1165 pa_water1.x1(), pa_water1.y0()),
1166 video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
1167 pa_water1.x0(), pa_water1.y0()),
1170 for(s32 i=0; i<4; i++)
1172 vertices[i].Pos.Y += (-0.5+node_water_level)*BS;
1173 vertices[i].Pos += intToFloat(p + getPosRelative());
1176 u16 indices[] = {0,1,2,2,3,0};
1177 // Add to mesh collector
1178 collector.append(material_water1, vertices, 4, indices, 6);
1181 Add leaves if using new style
1183 else if(n.d == CONTENT_LEAVES && new_style_leaves)
1185 /*u8 l = decode_light(n.getLightBlend(daynight_ratio));*/
1186 u8 l = decode_light(undiminish_light(n.getLightBlend(daynight_ratio)));
1187 video::SColor c(255,l,l,l);
1189 for(u32 j=0; j<6; j++)
1191 video::S3DVertex vertices[4] =
1193 /*video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, 0,1),
1194 video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, 1,1),
1195 video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c, 1,0),
1196 video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c, 0,0),*/
1197 video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c,
1198 pa_leaves1.x0(), pa_leaves1.y1()),
1199 video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c,
1200 pa_leaves1.x1(), pa_leaves1.y1()),
1201 video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c,
1202 pa_leaves1.x1(), pa_leaves1.y0()),
1203 video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c,
1204 pa_leaves1.x0(), pa_leaves1.y0()),
1209 for(u16 i=0; i<4; i++)
1210 vertices[i].Pos.rotateXZBy(0);
1214 for(u16 i=0; i<4; i++)
1215 vertices[i].Pos.rotateXZBy(180);
1219 for(u16 i=0; i<4; i++)
1220 vertices[i].Pos.rotateXZBy(-90);
1224 for(u16 i=0; i<4; i++)
1225 vertices[i].Pos.rotateXZBy(90);
1229 for(u16 i=0; i<4; i++)
1230 vertices[i].Pos.rotateYZBy(-90);
1234 for(u16 i=0; i<4; i++)
1235 vertices[i].Pos.rotateYZBy(90);
1238 for(u16 i=0; i<4; i++)
1240 vertices[i].Pos += intToFloat(p + getPosRelative());
1243 u16 indices[] = {0,1,2,2,3,0};
1244 // Add to mesh collector
1245 collector.append(material_leaves1, vertices, 4, indices, 6);
1251 Add stuff from collector to mesh
1254 scene::SMesh *mesh_new = NULL;
1255 mesh_new = new scene::SMesh();
1257 collector.fillMesh(mesh_new);
1260 Do some stuff to the mesh
1263 mesh_new->recalculateBoundingBox();
1266 Delete new mesh if it is empty
1269 if(mesh_new->getMeshBufferCount() == 0)
1278 // Usually 1-700 faces and 1-7 materials
1279 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
1280 <<"and uses "<<mesh_new->getMeshBufferCount()
1281 <<" materials (meshbuffers)"<<std::endl;
1284 // Use VBO for mesh (this just would set this for ever buffer)
1285 // This will lead to infinite memory usage because or irrlicht.
1286 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
1289 NOTE: If that is enabled, some kind of a queue to the main
1290 thread should be made which would call irrlicht to delete
1291 the hardware buffer and then delete the mesh
1301 //scene::SMesh *mesh_old = mesh[daynight_i];
1302 //mesh[daynight_i] = mesh_new;
1304 scene::SMesh *mesh_old = mesh;
1306 setMeshExpired(false);
1308 if(mesh_old != NULL)
1310 // Remove hardware buffers of meshbuffers of mesh
1311 // NOTE: No way, this runs in a different thread and everything
1312 /*u32 c = mesh_old->getMeshBufferCount();
1313 for(u32 i=0; i<c; i++)
1315 IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1318 /*dstream<<"mesh_old->getReferenceCount()="
1319 <<mesh_old->getReferenceCount()<<std::endl;
1320 u32 c = mesh_old->getMeshBufferCount();
1321 for(u32 i=0; i<c; i++)
1323 scene::IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1324 dstream<<"buf->getReferenceCount()="
1325 <<buf->getReferenceCount()<<std::endl;
1334 mesh_mutex.Unlock();
1336 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1339 /*void MapBlock::updateMeshes(s32 first_i)
1341 assert(first_i >= 0 && first_i <= DAYNIGHT_CACHE_COUNT);
1342 updateMesh(first_i);
1343 for(s32 i=0; i<DAYNIGHT_CACHE_COUNT; i++)
1354 Propagates sunlight down through the block.
1355 Doesn't modify nodes that are not affected by sunlight.
1357 Returns false if sunlight at bottom block is invalid
1358 Returns true if bottom block doesn't exist.
1360 If there is a block above, continues from it.
1361 If there is no block above, assumes there is sunlight, unless
1362 is_underground is set or highest node is water.
1364 At the moment, all sunlighted nodes are added to light_sources.
1365 - SUGG: This could be optimized
1367 Turns sunglighted mud into grass.
1369 if remove_light==true, sets non-sunlighted nodes black.
1371 if black_air_left!=NULL, it is set to true if non-sunlighted
1372 air is left in block.
1374 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources,
1375 bool remove_light, bool *black_air_left,
1378 // Whether the sunlight at the top of the bottom block is valid
1379 bool block_below_is_valid = true;
1381 v3s16 pos_relative = getPosRelative();
1383 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
1385 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
1388 bool no_sunlight = false;
1389 bool no_top_block = false;
1390 // Check if node above block has sunlight
1392 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1393 if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1398 catch(InvalidPositionException &e)
1400 no_top_block = true;
1402 // NOTE: This makes over-ground roofed places sunlighted
1403 // Assume sunlight, unless is_underground==true
1410 MapNode n = getNode(v3s16(x, MAP_BLOCKSIZE-1, z));
1411 if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
1416 // NOTE: As of now, this just would make everything dark.
1418 //no_sunlight = true;
1421 #if 0 // Doesn't work; nothing gets light.
1422 bool no_sunlight = true;
1423 bool no_top_block = false;
1424 // Check if node above block has sunlight
1426 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1427 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
1429 no_sunlight = false;
1432 catch(InvalidPositionException &e)
1434 no_top_block = true;
1438 /*std::cout<<"("<<x<<","<<z<<"): "
1439 <<"no_top_block="<<no_top_block
1440 <<", is_underground="<<is_underground
1441 <<", no_sunlight="<<no_sunlight
1444 s16 y = MAP_BLOCKSIZE-1;
1446 // This makes difference to diminishing in water.
1447 bool stopped_to_solid_object = false;
1449 u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
1454 MapNode &n = getNodeRef(pos);
1456 if(current_light == 0)
1460 else if(current_light == LIGHT_SUN && n.sunlight_propagates())
1462 // Do nothing: Sunlight is continued
1464 else if(n.light_propagates() == false)
1468 bool upper_is_air = false;
1471 if(getNodeParent(pos+v3s16(0,1,0)).d == CONTENT_AIR)
1472 upper_is_air = true;
1474 catch(InvalidPositionException &e)
1477 // Turn mud into grass
1478 if(upper_is_air && n.d == CONTENT_MUD
1479 && current_light == LIGHT_SUN)
1481 n.d = CONTENT_GRASS;
1485 // A solid object is on the way.
1486 stopped_to_solid_object = true;
1494 current_light = diminish_light(current_light);
1497 u8 old_light = n.getLight(LIGHTBANK_DAY);
1499 if(current_light > old_light || remove_light)
1501 n.setLight(LIGHTBANK_DAY, current_light);
1504 if(diminish_light(current_light) != 0)
1506 light_sources.insert(pos_relative + pos, true);
1509 if(current_light == 0 && stopped_to_solid_object)
1513 *black_air_left = true;
1518 // Whether or not the block below should see LIGHT_SUN
1519 bool sunlight_should_go_down = (current_light == LIGHT_SUN);
1522 If the block below hasn't already been marked invalid:
1524 Check if the node below the block has proper sunlight at top.
1525 If not, the block below is invalid.
1527 Ignore non-transparent nodes as they always have no light
1531 if(block_below_is_valid)
1533 MapNode n = getNodeParent(v3s16(x, -1, z));
1534 if(n.light_propagates())
1536 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN
1537 && sunlight_should_go_down == false)
1538 block_below_is_valid = false;
1539 else if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN
1540 && sunlight_should_go_down == true)
1541 block_below_is_valid = false;
1545 catch(InvalidPositionException &e)
1547 /*std::cout<<"InvalidBlockException for bottom block node"
1549 // Just no block below, no need to panic.
1554 return block_below_is_valid;
1557 void MapBlock::copyTo(VoxelManipulator &dst)
1559 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
1560 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
1562 // Copy from data to VoxelManipulator
1563 dst.copyFrom(data, data_area, v3s16(0,0,0),
1564 getPosRelative(), data_size);
1567 void MapBlock::copyFrom(VoxelManipulator &dst)
1569 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
1570 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
1572 // Copy from VoxelManipulator to data
1573 dst.copyTo(data, data_area, v3s16(0,0,0),
1574 getPosRelative(), data_size);
1577 void MapBlock::stepObjects(float dtime, bool server, u32 daynight_ratio)
1582 m_objects.step(dtime, server, daynight_ratio);
1585 Spawn some objects at random.
1587 Use dayNightDiffed() to approximate being near ground level
1589 if(m_spawn_timer < -999)
1593 if(dayNightDiffed() == true && getObjectCount() == 0)
1595 m_spawn_timer -= dtime;
1596 if(m_spawn_timer <= 0.0)
1598 m_spawn_timer += myrand() % 300;
1601 (myrand()%(MAP_BLOCKSIZE-1))+0,
1602 (myrand()%(MAP_BLOCKSIZE-1))+0
1605 s16 y = getGroundLevel(p2d);
1609 v3s16 p(p2d.X, y+1, p2d.Y);
1611 if(getNode(p).d == CONTENT_AIR
1612 && getNode(p).getLightBlend(daynight_ratio) <= 11)
1614 RatObject *obj = new RatObject(NULL, -1, intToFloat(p));
1625 void MapBlock::updateDayNightDiff()
1629 m_day_night_differs = false;
1633 bool differs = false;
1636 Check if any lighting value differs
1638 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1640 MapNode &n = data[i];
1641 if(n.getLight(LIGHTBANK_DAY) != n.getLight(LIGHTBANK_NIGHT))
1649 If some lighting values differ, check if the whole thing is
1650 just air. If it is, differ = false
1654 bool only_air = true;
1655 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1657 MapNode &n = data[i];
1658 if(n.d != CONTENT_AIR)
1668 // Set member variable
1669 m_day_night_differs = differs;
1672 s16 MapBlock::getGroundLevel(v2s16 p2d)
1678 s16 y = MAP_BLOCKSIZE-1;
1681 //if(is_ground_content(getNodeRef(p2d.X, y, p2d.Y).d))
1682 if(content_features(getNodeRef(p2d.X, y, p2d.Y).d).walkable)
1684 if(y == MAP_BLOCKSIZE-1)
1692 catch(InvalidPositionException &e)
1702 void MapBlock::serialize(std::ostream &os, u8 version)
1704 if(!ser_ver_supported(version))
1705 throw VersionMismatchException("ERROR: MapBlock format not supported");
1709 throw SerializationError("ERROR: Not writing dummy block.");
1712 // These have no compression
1713 if(version <= 3 || version == 5 || version == 6)
1715 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1717 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
1718 SharedBuffer<u8> dest(buflen);
1720 dest[0] = is_underground;
1721 for(u32 i=0; i<nodecount; i++)
1723 u32 s = 1 + i * MapNode::serializedLength(version);
1724 data[i].serialize(&dest[s], version);
1727 os.write((char*)*dest, dest.getSize());
1729 else if(version <= 10)
1733 Compress the materials and the params separately.
1737 os.write((char*)&is_underground, 1);
1739 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1741 // Get and compress materials
1742 SharedBuffer<u8> materialdata(nodecount);
1743 for(u32 i=0; i<nodecount; i++)
1745 materialdata[i] = data[i].d;
1747 compress(materialdata, os, version);
1749 // Get and compress lights
1750 SharedBuffer<u8> lightdata(nodecount);
1751 for(u32 i=0; i<nodecount; i++)
1753 lightdata[i] = data[i].param;
1755 compress(lightdata, os, version);
1759 // Get and compress param2
1760 SharedBuffer<u8> param2data(nodecount);
1761 for(u32 i=0; i<nodecount; i++)
1763 param2data[i] = data[i].param2;
1765 compress(param2data, os, version);
1768 // All other versions (newest)
1775 if(m_day_night_differs)
1777 if(m_lighting_expired)
1779 os.write((char*)&flags, 1);
1781 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1787 SharedBuffer<u8> databuf(nodecount*3);
1790 for(u32 i=0; i<nodecount; i++)
1792 databuf[i] = data[i].d;
1796 for(u32 i=0; i<nodecount; i++)
1798 databuf[i+nodecount] = data[i].param;
1802 for(u32 i=0; i<nodecount; i++)
1804 databuf[i+nodecount*2] = data[i].param2;
1808 Compress data to output stream
1811 compress(databuf, os, version);
1815 void MapBlock::deSerialize(std::istream &is, u8 version)
1817 if(!ser_ver_supported(version))
1818 throw VersionMismatchException("ERROR: MapBlock format not supported");
1820 // These have no compression
1821 if(version <= 3 || version == 5 || version == 6)
1823 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1826 if(is.gcount() != 1)
1827 throw SerializationError
1828 ("MapBlock::deSerialize: no enough input data");
1829 is_underground = tmp;
1830 for(u32 i=0; i<nodecount; i++)
1832 s32 len = MapNode::serializedLength(version);
1833 SharedBuffer<u8> d(len);
1834 is.read((char*)*d, len);
1835 if(is.gcount() != len)
1836 throw SerializationError
1837 ("MapBlock::deSerialize: no enough input data");
1838 data[i].deSerialize(*d, version);
1841 else if(version <= 10)
1843 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1846 is.read((char*)&t8, 1);
1847 is_underground = t8;
1850 // Uncompress and set material data
1851 std::ostringstream os(std::ios_base::binary);
1852 decompress(is, os, version);
1853 std::string s = os.str();
1854 if(s.size() != nodecount)
1855 throw SerializationError
1856 ("MapBlock::deSerialize: invalid format");
1857 for(u32 i=0; i<s.size(); i++)
1863 // Uncompress and set param 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].param = s[i];
1878 // Uncompress and set param2 data
1879 std::ostringstream os(std::ios_base::binary);
1880 decompress(is, os, version);
1881 std::string s = os.str();
1882 if(s.size() != nodecount)
1883 throw SerializationError
1884 ("MapBlock::deSerialize: invalid format");
1885 for(u32 i=0; i<s.size(); i++)
1887 data[i].param2 = s[i];
1891 // All other versions (newest)
1894 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1897 is.read((char*)&flags, 1);
1898 is_underground = (flags & 0x01) ? true : false;
1899 m_day_night_differs = (flags & 0x02) ? true : false;
1900 m_lighting_expired = (flags & 0x04) ? true : false;
1903 std::ostringstream os(std::ios_base::binary);
1904 decompress(is, os, version);
1905 std::string s = os.str();
1906 if(s.size() != nodecount*3)
1907 throw SerializationError
1908 ("MapBlock::deSerialize: invalid format");
1911 for(u32 i=0; i<nodecount; i++)
1916 for(u32 i=0; i<nodecount; i++)
1918 data[i].param = s[i+nodecount];
1921 for(u32 i=0; i<nodecount; i++)
1923 data[i].param2 = s[i+nodecount*2];
1928 Translate nodes as specified in the translate_to fields of
1931 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1933 MapNode &n = data[i];
1935 MapNode *translate_to = content_features(n.d).translate_to;
1938 dstream<<"MapBlock: WARNING: Translating node "<<n.d<<" to "
1939 <<translate_to->d<<std::endl;