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),
39 //m_not_fully_generated(false),
46 m_spawn_timer = -10000;
49 m_mesh_expired = false;
52 m_temp_mods_mutex.Init();
60 JMutexAutoLock lock(mesh_mutex);
74 bool MapBlock::isValidPositionParent(v3s16 p)
76 if(isValidPosition(p))
81 return m_parent->isValidPosition(getPosRelative() + p);
85 MapNode MapBlock::getNodeParent(v3s16 p)
87 if(isValidPosition(p) == false)
89 return m_parent->getNode(getPosRelative() + p);
94 throw InvalidPositionException();
95 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
99 void MapBlock::setNodeParent(v3s16 p, MapNode & n)
101 if(isValidPosition(p) == false)
103 m_parent->setNode(getPosRelative() + p, n);
108 throw InvalidPositionException();
109 data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X] = n;
113 MapNode MapBlock::getNodeParentNoEx(v3s16 p)
115 if(isValidPosition(p) == false)
118 return m_parent->getNode(getPosRelative() + p);
120 catch(InvalidPositionException &e)
122 return MapNode(CONTENT_IGNORE);
129 return MapNode(CONTENT_IGNORE);
131 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
136 Parameters must consist of air and !air.
137 Order doesn't matter.
139 If either of the nodes doesn't exist, light is 0.
142 daynight_ratio: 0...1000
144 n2: getNodeParent(p + face_dir)
145 face_dir: axis oriented unit vector from p to p2
147 returns encoded light value.
149 u8 MapBlock::getFaceLight(u32 daynight_ratio, MapNode n, MapNode n2,
154 u8 l1 = n.getLightBlend(daynight_ratio);
155 u8 l2 = n2.getLightBlend(daynight_ratio);
161 // Make some nice difference to different sides
163 // This makes light come from a corner
164 /*if(face_dir.X == 1 || face_dir.Z == 1 || face_dir.Y == -1)
165 light = diminish_light(diminish_light(light));
166 else if(face_dir.X == -1 || face_dir.Z == -1)
167 light = diminish_light(light);*/
169 // All neighboring faces have different shade (like in minecraft)
170 if(face_dir.X == 1 || face_dir.X == -1 || face_dir.Y == -1)
171 light = diminish_light(diminish_light(light));
172 else if(face_dir.Z == 1 || face_dir.Z == -1)
173 light = diminish_light(light);
177 catch(InvalidPositionException &e)
185 void MapBlock::makeFastFace(TileSpec tile, u8 light, v3f p,
186 v3s16 dir, v3f scale, v3f posRelative_f,
187 core::array<FastFace> &dest)
191 // Position is at the center of the cube.
196 // If looking towards z+, this is the face that is behind
197 // the center point, facing towards z+.
198 vertex_pos[0] = v3f(-BS/2,-BS/2,BS/2);
199 vertex_pos[1] = v3f( BS/2,-BS/2,BS/2);
200 vertex_pos[2] = v3f( BS/2, BS/2,BS/2);
201 vertex_pos[3] = v3f(-BS/2, BS/2,BS/2);
203 if(dir == v3s16(0,0,1))
205 for(u16 i=0; i<4; i++)
206 vertex_pos[i].rotateXZBy(0);
208 else if(dir == v3s16(0,0,-1))
210 for(u16 i=0; i<4; i++)
211 vertex_pos[i].rotateXZBy(180);
213 else if(dir == v3s16(1,0,0))
215 for(u16 i=0; i<4; i++)
216 vertex_pos[i].rotateXZBy(-90);
218 else if(dir == v3s16(-1,0,0))
220 for(u16 i=0; i<4; i++)
221 vertex_pos[i].rotateXZBy(90);
223 else if(dir == v3s16(0,1,0))
225 for(u16 i=0; i<4; i++)
226 vertex_pos[i].rotateYZBy(-90);
228 else if(dir == v3s16(0,-1,0))
230 for(u16 i=0; i<4; i++)
231 vertex_pos[i].rotateYZBy(90);
234 for(u16 i=0; i<4; i++)
236 vertex_pos[i].X *= scale.X;
237 vertex_pos[i].Y *= scale.Y;
238 vertex_pos[i].Z *= scale.Z;
239 vertex_pos[i] += pos + posRelative_f;
243 if (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X;
244 else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
245 else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
247 v3f zerovector = v3f(0,0,0);
249 //u8 li = decode_light(light);
251 //u8 li = 255; //DEBUG
253 u8 alpha = tile.alpha;
255 if(tile.id == TILE_WATER)
256 alpha = WATER_ALPHA;*/
258 video::SColor c = video::SColor(alpha,li,li,li);
260 float x0 = tile.texture.pos.X;
261 float y0 = tile.texture.pos.Y;
262 float w = tile.texture.size.X;
263 float h = tile.texture.size.Y;
265 face.vertices[0] = video::S3DVertex(vertex_pos[0], v3f(0,1,0), c,
266 core::vector2d<f32>(x0+w*abs_scale, y0+h));
267 face.vertices[1] = video::S3DVertex(vertex_pos[1], v3f(0,1,0), c,
268 core::vector2d<f32>(x0, y0+h));
269 face.vertices[2] = video::S3DVertex(vertex_pos[2], v3f(0,1,0), c,
270 core::vector2d<f32>(x0, y0));
271 face.vertices[3] = video::S3DVertex(vertex_pos[3], v3f(0,1,0), c,
272 core::vector2d<f32>(x0+w*abs_scale, y0));
276 //f->tile = TILE_STONE;
278 dest.push_back(face);
283 Gets node tile from any place relative to block.
284 Returns TILE_NODE if doesn't exist or should not be drawn.
286 TileSpec MapBlock::getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir,
287 NodeModMap &temp_mods)
290 spec = mn.getTile(face_dir);
293 Check temporary modifications on this node
295 /*core::map<v3s16, NodeMod>::Node *n;
296 n = m_temp_mods.find(p);
300 struct NodeMod mod = n->getValue();*/
302 if(temp_mods.get(p, &mod))
304 if(mod.type == NODEMOD_CHANGECONTENT)
306 MapNode mn2(mod.param);
307 spec = mn2.getTile(face_dir);
309 if(mod.type == NODEMOD_CRACK)
312 Get texture id, translate it to name, append stuff to
316 // Get original texture name
317 u32 orig_id = spec.texture.id;
318 std::string orig_name = g_texturesource->getTextureName(orig_id);
320 // Create new texture name
321 std::ostringstream os;
322 os<<orig_name<<"^[crack"<<mod.param;
325 u32 new_id = g_texturesource->getTextureId(os.str());
327 /*dstream<<"MapBlock::getNodeTile(): Switching from "
328 <<orig_name<<" to "<<os.str()<<" ("
329 <<orig_id<<" to "<<new_id<<")"<<std::endl;*/
331 spec.texture = g_texturesource->getTexture(new_id);
338 u8 MapBlock::getNodeContent(v3s16 p, MapNode mn, NodeModMap &temp_mods)
341 Check temporary modifications on this node
343 /*core::map<v3s16, NodeMod>::Node *n;
344 n = m_temp_mods.find(p);
348 struct NodeMod mod = n->getValue();*/
350 if(temp_mods.get(p, &mod))
352 if(mod.type == NODEMOD_CHANGECONTENT)
357 if(mod.type == NODEMOD_CRACK)
360 Content doesn't change.
362 face_contents works just like it should, because
363 there should not be faces between differently cracked
366 If a semi-transparent node is cracked in front an
367 another one, it really doesn't matter whether there
368 is a cracked face drawn in between or not.
378 translate_dir: unit vector with only one of x, y or z
379 face_dir: unit vector with only one of x, y or z
381 void MapBlock::updateFastFaceRow(
390 core::array<FastFace> &dest,
391 NodeModMap &temp_mods)
395 u16 continuous_tiles_count = 0;
397 MapNode n0 = getNodeParentNoEx(p);
398 MapNode n1 = getNodeParentNoEx(p + face_dir);
400 u8 light = getFaceLight(daynight_ratio, n0, n1, face_dir);
402 TileSpec tile0 = getNodeTile(n0, p, face_dir, temp_mods);
403 TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir, temp_mods);
405 for(u16 j=0; j<length; j++)
407 bool next_is_different = true;
416 // If at last position, there is nothing to compare to and
417 // the face must be drawn anyway
420 p_next = p + translate_dir;
421 n0_next = getNodeParentNoEx(p_next);
422 n1_next = getNodeParentNoEx(p_next + face_dir);
423 tile0_next = getNodeTile(n0_next, p_next, face_dir, temp_mods);
424 tile1_next = getNodeTile(n1_next,p_next+face_dir,-face_dir, temp_mods);
425 light_next = getFaceLight(daynight_ratio, n0_next, n1_next, face_dir);
427 if(tile0_next == tile0
428 && tile1_next == tile1
429 && light_next == light)
431 next_is_different = false;
435 continuous_tiles_count++;
437 // This is set to true if the texture doesn't allow more tiling
438 bool end_of_texture = false;
440 If there is no texture, it can be tiled infinitely.
441 If tiled==0, it means the texture can be tiled infinitely.
442 Otherwise check tiled agains continuous_tiles_count.
444 This check has to be made for both tiles, because this is
445 a bit hackish and we know which one we're using only when
446 the decision to make the faces is made.
448 if(tile0.texture.atlas != NULL && tile0.texture.tiled != 0)
450 if(tile0.texture.tiled <= continuous_tiles_count)
451 end_of_texture = true;
453 if(tile1.texture.atlas != NULL && tile1.texture.tiled != 0)
455 if(tile1.texture.tiled <= continuous_tiles_count)
456 end_of_texture = true;
459 //end_of_texture = true; //DEBUG
461 if(next_is_different || end_of_texture)
464 Create a face if there should be one
466 //u8 mf = face_contents(tile0, tile1);
468 u8 content0 = getNodeContent(p, n0, temp_mods);
469 u8 content1 = getNodeContent(p + face_dir, n1, temp_mods);
470 u8 mf = face_contents(content0, content1);
474 // Floating point conversion of the position vector
475 v3f pf(p.X, p.Y, p.Z);
476 // Center point of face (kind of)
477 v3f sp = pf - ((f32)continuous_tiles_count / 2. - 0.5) * translate_dir_f;
479 if(translate_dir.X != 0){
480 scale.X = continuous_tiles_count;
482 if(translate_dir.Y != 0){
483 scale.Y = continuous_tiles_count;
485 if(translate_dir.Z != 0){
486 scale.Z = continuous_tiles_count;
491 // If node at sp (tile0) is more solid
494 makeFastFace(tile0, decode_light(light),
496 posRelative_f, dest);
498 // If node at sp is less solid (mf == 2)
501 makeFastFace(tile1, decode_light(light),
502 sp+face_dir_f, -face_dir, scale,
503 posRelative_f, dest);
508 continuous_tiles_count = 0;
521 This is used because CMeshBuffer::append() is very slow
525 video::SMaterial material;
526 core::array<u16> indices;
527 core::array<video::S3DVertex> vertices;
534 video::SMaterial material,
535 const video::S3DVertex* const vertices,
537 const u16* const indices,
541 PreMeshBuffer *p = NULL;
542 for(u32 i=0; i<m_prebuffers.size(); i++)
544 PreMeshBuffer &pp = m_prebuffers[i];
545 if(pp.material != material)
555 pp.material = material;
556 m_prebuffers.push_back(pp);
557 p = &m_prebuffers[m_prebuffers.size()-1];
560 u32 vertex_count = p->vertices.size();
561 for(u32 i=0; i<numIndices; i++)
563 u32 j = indices[i] + vertex_count;
566 dstream<<"FIXME: Meshbuffer ran out of indices"<<std::endl;
567 // NOTE: Fix is to just add an another MeshBuffer
569 p->indices.push_back(j);
571 for(u32 i=0; i<numVertices; i++)
573 p->vertices.push_back(vertices[i]);
577 void fillMesh(scene::SMesh *mesh)
579 /*dstream<<"Filling mesh with "<<m_prebuffers.size()
580 <<" meshbuffers"<<std::endl;*/
581 for(u32 i=0; i<m_prebuffers.size(); i++)
583 PreMeshBuffer &p = m_prebuffers[i];
585 /*dstream<<"p.vertices.size()="<<p.vertices.size()
586 <<", p.indices.size()="<<p.indices.size()
591 // This is a "Standard MeshBuffer",
592 // it's a typedeffed CMeshBuffer<video::S3DVertex>
593 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
595 buf->Material = p.material;
596 //((scene::SMeshBuffer*)buf)->Material = p.material;
598 //buf->setHardwareMappingHint(scene::EHM_STATIC);
600 mesh->addMeshBuffer(buf);
604 buf->append(p.vertices.pointer(), p.vertices.size(),
605 p.indices.pointer(), p.indices.size());
610 core::array<PreMeshBuffer> m_prebuffers;
613 void MapBlock::updateMesh(u32 daynight_ratio)
617 DEBUG: If mesh has been generated, don't generate it again
620 JMutexAutoLock meshlock(mesh_mutex);
626 // 4-21ms for MAP_BLOCKSIZE=16
627 // 24-155ms for MAP_BLOCKSIZE=32
628 //TimeTaker timer1("updateMesh()");
630 core::array<FastFace> fastfaces_new;
632 v3f posRelative_f(getPosRelative().X, getPosRelative().Y,
633 getPosRelative().Z); // floating point conversion
636 Avoid interlocks by copying m_temp_mods
638 NodeModMap temp_mods;
640 JMutexAutoLock lock(m_temp_mods_mutex);
641 m_temp_mods.copy(temp_mods);
647 bool new_style_water = g_settings.getBool("new_style_water");
648 bool new_style_leaves = g_settings.getBool("new_style_leaves");
650 float node_water_level = 1.0;
652 node_water_level = 0.85;
655 We are including the faces of the trailing edges of the block.
656 This means that when something changes, the caller must
657 also update the meshes of the blocks at the leading edges.
659 NOTE: This is the slowest part of this method.
663 // 4-23ms for MAP_BLOCKSIZE=16
664 //TimeTaker timer2("updateMesh() collect");
667 Go through every y,z and get top faces in rows of x+
669 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
670 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
671 updateFastFaceRow(daynight_ratio, posRelative_f,
672 v3s16(0,y,z), MAP_BLOCKSIZE,
675 v3s16(0,1,0), //face dir
682 Go through every x,y and get right faces in rows of z+
684 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
685 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
686 updateFastFaceRow(daynight_ratio, posRelative_f,
687 v3s16(x,y,0), MAP_BLOCKSIZE,
697 Go through every y,z and get back faces in rows of x+
699 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
700 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
701 updateFastFaceRow(daynight_ratio, posRelative_f,
702 v3s16(0,y,z), MAP_BLOCKSIZE,
716 Convert FastFaces to SMesh
719 MeshCollector collector;
721 if(fastfaces_new.size() > 0)
723 // avg 0ms (100ms spikes when loading textures the first time)
724 //TimeTaker timer2("updateMesh() mesh building");
726 video::SMaterial material;
727 material.setFlag(video::EMF_LIGHTING, false);
728 material.setFlag(video::EMF_BILINEAR_FILTER, false);
729 material.setFlag(video::EMF_FOG_ENABLE, true);
730 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
731 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_SIMPLE);
733 for(u32 i=0; i<fastfaces_new.size(); i++)
735 FastFace &f = fastfaces_new[i];
737 const u16 indices[] = {0,1,2,2,3,0};
739 //video::ITexture *texture = g_irrlicht->getTexture(f.tile.spec);
740 video::ITexture *texture = f.tile.texture.atlas;
744 material.setTexture(0, texture);
746 f.tile.applyMaterialOptions(material);
748 collector.append(material, f.vertices, 4, indices, 6);
753 Add special graphics:
759 //TimeTaker timer2("updateMesh() adding special stuff");
761 // Flowing water material
762 video::SMaterial material_water1;
763 material_water1.setFlag(video::EMF_LIGHTING, false);
764 //material_water1.setFlag(video::EMF_BACK_FACE_CULLING, false);
765 material_water1.setFlag(video::EMF_BILINEAR_FILTER, false);
766 material_water1.setFlag(video::EMF_FOG_ENABLE, true);
767 material_water1.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
769 //material_water1.setTexture(0, g_irrlicht->getTexture("water.png"));
770 AtlasPointer pa_water1 = g_texturesource->getTexture(
771 g_texturesource->getTextureId("water.png"));
772 material_water1.setTexture(0, pa_water1.atlas);
774 // New-style leaves material
775 video::SMaterial material_leaves1;
776 material_leaves1.setFlag(video::EMF_LIGHTING, false);
777 //material_leaves1.setFlag(video::EMF_BACK_FACE_CULLING, false);
778 material_leaves1.setFlag(video::EMF_BILINEAR_FILTER, false);
779 material_leaves1.setFlag(video::EMF_FOG_ENABLE, true);
780 material_leaves1.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
782 //material_leaves1.setTexture(0, g_irrlicht->getTexture("leaves.png"));
783 AtlasPointer pa_leaves1 = g_texturesource->getTexture(
784 g_texturesource->getTextureId("leaves.png"));
785 material_leaves1.setTexture(0, pa_leaves1.atlas);
787 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
788 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
789 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
793 MapNode &n = getNodeRef(x,y,z);
798 if(n.d == CONTENT_TORCH)
800 video::SColor c(255,255,255,255);
802 // Wall at X+ of node
803 video::S3DVertex vertices[4] =
805 video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c, 0,1),
806 video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c, 1,1),
807 video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, 1,0),
808 video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c, 0,0),
811 v3s16 dir = unpackDir(n.dir);
813 for(s32 i=0; i<4; i++)
815 if(dir == v3s16(1,0,0))
816 vertices[i].Pos.rotateXZBy(0);
817 if(dir == v3s16(-1,0,0))
818 vertices[i].Pos.rotateXZBy(180);
819 if(dir == v3s16(0,0,1))
820 vertices[i].Pos.rotateXZBy(90);
821 if(dir == v3s16(0,0,-1))
822 vertices[i].Pos.rotateXZBy(-90);
823 if(dir == v3s16(0,-1,0))
824 vertices[i].Pos.rotateXZBy(45);
825 if(dir == v3s16(0,1,0))
826 vertices[i].Pos.rotateXZBy(-45);
828 vertices[i].Pos += intToFloat(p + getPosRelative(), BS);
832 video::SMaterial material;
833 material.setFlag(video::EMF_LIGHTING, false);
834 material.setFlag(video::EMF_BACK_FACE_CULLING, false);
835 material.setFlag(video::EMF_BILINEAR_FILTER, false);
836 //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
837 material.MaterialType
838 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
840 if(dir == v3s16(0,-1,0))
841 material.setTexture(0,
842 g_texturesource->getTextureRaw("torch_on_floor.png"));
843 else if(dir == v3s16(0,1,0))
844 material.setTexture(0,
845 g_texturesource->getTextureRaw("torch_on_ceiling.png"));
846 // For backwards compatibility
847 else if(dir == v3s16(0,0,0))
848 material.setTexture(0,
849 g_texturesource->getTextureRaw("torch_on_floor.png"));
851 material.setTexture(0,
852 g_texturesource->getTextureRaw("torch.png"));
854 u16 indices[] = {0,1,2,2,3,0};
855 // Add to mesh collector
856 collector.append(material, vertices, 4, indices, 6);
861 if(n.d == CONTENT_SIGN_WALL)
863 u8 l = decode_light(n.getLightBlend(daynight_ratio));
864 video::SColor c(255,l,l,l);
866 float d = (float)BS/16;
867 // Wall at X+ of node
868 video::S3DVertex vertices[4] =
870 video::S3DVertex(BS/2-d,-BS/2,-BS/2, 0,0,0, c, 0,1),
871 video::S3DVertex(BS/2-d,-BS/2,BS/2, 0,0,0, c, 1,1),
872 video::S3DVertex(BS/2-d,BS/2,BS/2, 0,0,0, c, 1,0),
873 video::S3DVertex(BS/2-d,BS/2,-BS/2, 0,0,0, c, 0,0),
876 v3s16 dir = unpackDir(n.dir);
878 for(s32 i=0; i<4; i++)
880 if(dir == v3s16(1,0,0))
881 vertices[i].Pos.rotateXZBy(0);
882 if(dir == v3s16(-1,0,0))
883 vertices[i].Pos.rotateXZBy(180);
884 if(dir == v3s16(0,0,1))
885 vertices[i].Pos.rotateXZBy(90);
886 if(dir == v3s16(0,0,-1))
887 vertices[i].Pos.rotateXZBy(-90);
888 if(dir == v3s16(0,-1,0))
889 vertices[i].Pos.rotateXYBy(-90);
890 if(dir == v3s16(0,1,0))
891 vertices[i].Pos.rotateXYBy(90);
893 vertices[i].Pos += intToFloat(p + getPosRelative(), BS);
897 video::SMaterial material;
898 material.setFlag(video::EMF_LIGHTING, false);
899 material.setFlag(video::EMF_BACK_FACE_CULLING, false);
900 material.setFlag(video::EMF_BILINEAR_FILTER, false);
901 material.setFlag(video::EMF_FOG_ENABLE, true);
902 //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
903 material.MaterialType
904 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
906 material.setTexture(0,
907 g_texturesource->getTextureRaw("sign_wall.png"));
909 u16 indices[] = {0,1,2,2,3,0};
910 // Add to mesh collector
911 collector.append(material, vertices, 4, indices, 6);
914 Add flowing water to mesh
916 else if(n.d == CONTENT_WATER)
918 bool top_is_water = false;
920 MapNode n = getNodeParent(v3s16(x,y+1,z));
921 if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
923 }catch(InvalidPositionException &e){}
925 u8 l = decode_light(n.getLightBlend(daynight_ratio));
926 video::SColor c(WATER_ALPHA,l,l,l);
928 // Neighbor water levels (key = relative position)
929 // Includes current node
930 core::map<v3s16, f32> neighbor_levels;
931 core::map<v3s16, u8> neighbor_contents;
932 core::map<v3s16, u8> neighbor_flags;
933 const u8 neighborflag_top_is_water = 0x01;
934 v3s16 neighbor_dirs[9] = {
945 for(u32 i=0; i<9; i++)
947 u8 content = CONTENT_AIR;
948 float level = -0.5 * BS;
952 v3s16 p2 = p + neighbor_dirs[i];
953 MapNode n2 = getNodeParent(p2);
957 if(n2.d == CONTENT_WATERSOURCE)
958 level = (-0.5+node_water_level) * BS;
959 else if(n2.d == CONTENT_WATER)
960 level = (-0.5 + ((float)n2.param2 + 0.5) / 8.0
961 * node_water_level) * BS;
963 // Check node above neighbor.
964 // NOTE: This doesn't get executed if neighbor
967 n2 = getNodeParent(p2);
968 if(n2.d == CONTENT_WATERSOURCE || n2.d == CONTENT_WATER)
969 flags |= neighborflag_top_is_water;
971 catch(InvalidPositionException &e){}
973 neighbor_levels.insert(neighbor_dirs[i], level);
974 neighbor_contents.insert(neighbor_dirs[i], content);
975 neighbor_flags.insert(neighbor_dirs[i], flags);
978 //float water_level = (-0.5 + ((float)n.param2 + 0.5) / 8.0) * BS;
979 //float water_level = neighbor_levels[v3s16(0,0,0)];
981 // Corner heights (average between four waters)
982 f32 corner_levels[4];
984 v3s16 halfdirs[4] = {
990 for(u32 i=0; i<4; i++)
992 v3s16 cornerdir = halfdirs[i];
993 float cornerlevel = 0;
995 for(u32 j=0; j<4; j++)
997 v3s16 neighbordir = cornerdir - halfdirs[j];
998 u8 content = neighbor_contents[neighbordir];
999 // Special case for source nodes
1000 if(content == CONTENT_WATERSOURCE)
1002 cornerlevel = (-0.5+node_water_level)*BS;
1006 else if(content == CONTENT_WATER)
1008 cornerlevel += neighbor_levels[neighbordir];
1011 else if(content == CONTENT_AIR)
1013 cornerlevel += -0.5*BS;
1018 cornerlevel /= valid_count;
1019 corner_levels[i] = cornerlevel;
1026 v3s16 side_dirs[4] = {
1032 s16 side_corners[4][2] = {
1038 for(u32 i=0; i<4; i++)
1040 v3s16 dir = side_dirs[i];
1043 If our topside is water and neighbor's topside
1044 is water, don't draw side face
1047 neighbor_flags[dir] & neighborflag_top_is_water)
1050 u8 neighbor_content = neighbor_contents[dir];
1052 // Don't draw face if neighbor is not air or water
1053 if(neighbor_content != CONTENT_AIR
1054 && neighbor_content != CONTENT_WATER)
1057 bool neighbor_is_water = (neighbor_content == CONTENT_WATER);
1059 // Don't draw any faces if neighbor is water and top is water
1060 if(neighbor_is_water == true && top_is_water == false)
1063 video::S3DVertex vertices[4] =
1065 /*video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1),
1066 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1),
1067 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1068 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1069 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1070 pa_water1.x0(), pa_water1.y1()),
1071 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1072 pa_water1.x1(), pa_water1.y1()),
1073 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1074 pa_water1.x1(), pa_water1.y0()),
1075 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1076 pa_water1.x0(), pa_water1.y0()),
1080 If our topside is water, set upper border of face
1081 at upper border of node
1085 vertices[2].Pos.Y = 0.5*BS;
1086 vertices[3].Pos.Y = 0.5*BS;
1089 Otherwise upper position of face is corner levels
1093 vertices[2].Pos.Y = corner_levels[side_corners[i][0]];
1094 vertices[3].Pos.Y = corner_levels[side_corners[i][1]];
1098 If neighbor is water, lower border of face is corner
1101 if(neighbor_is_water)
1103 vertices[0].Pos.Y = corner_levels[side_corners[i][1]];
1104 vertices[1].Pos.Y = corner_levels[side_corners[i][0]];
1107 If neighbor is not water, lower border of face is
1108 lower border of node
1112 vertices[0].Pos.Y = -0.5*BS;
1113 vertices[1].Pos.Y = -0.5*BS;
1116 for(s32 j=0; j<4; j++)
1118 if(dir == v3s16(0,0,1))
1119 vertices[j].Pos.rotateXZBy(0);
1120 if(dir == v3s16(0,0,-1))
1121 vertices[j].Pos.rotateXZBy(180);
1122 if(dir == v3s16(-1,0,0))
1123 vertices[j].Pos.rotateXZBy(90);
1124 if(dir == v3s16(1,0,-0))
1125 vertices[j].Pos.rotateXZBy(-90);
1127 vertices[j].Pos += intToFloat(p + getPosRelative(), BS);
1130 u16 indices[] = {0,1,2,2,3,0};
1131 // Add to mesh collector
1132 collector.append(material_water1, vertices, 4, indices, 6);
1136 Generate top side, if appropriate
1139 if(top_is_water == false)
1141 video::S3DVertex vertices[4] =
1143 /*video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
1144 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
1145 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1146 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1147 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1148 pa_water1.x0(), pa_water1.y1()),
1149 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1150 pa_water1.x1(), pa_water1.y1()),
1151 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
1152 pa_water1.x1(), pa_water1.y0()),
1153 video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
1154 pa_water1.x0(), pa_water1.y0()),
1157 // This fixes a strange bug
1158 s32 corner_resolve[4] = {3,2,1,0};
1160 for(s32 i=0; i<4; i++)
1162 //vertices[i].Pos.Y += water_level;
1163 //vertices[i].Pos.Y += neighbor_levels[v3s16(0,0,0)];
1164 s32 j = corner_resolve[i];
1165 vertices[i].Pos.Y += corner_levels[j];
1166 vertices[i].Pos += intToFloat(p + getPosRelative(), BS);
1169 u16 indices[] = {0,1,2,2,3,0};
1170 // Add to mesh collector
1171 collector.append(material_water1, vertices, 4, indices, 6);
1175 Add water sources to mesh if using new style
1177 else if(n.d == CONTENT_WATERSOURCE && new_style_water)
1179 //bool top_is_water = false;
1180 bool top_is_air = false;
1182 MapNode n = getNodeParent(v3s16(x,y+1,z));
1183 /*if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
1184 top_is_water = true;*/
1185 if(n.d == CONTENT_AIR)
1187 }catch(InvalidPositionException &e){}
1189 /*if(top_is_water == true)
1191 if(top_is_air == false)
1194 u8 l = decode_light(n.getLightBlend(daynight_ratio));
1195 video::SColor c(WATER_ALPHA,l,l,l);
1197 video::S3DVertex vertices[4] =
1199 /*video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
1200 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
1201 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1202 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1203 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1204 pa_water1.x0(), pa_water1.y1()),
1205 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1206 pa_water1.x1(), pa_water1.y1()),
1207 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
1208 pa_water1.x1(), pa_water1.y0()),
1209 video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
1210 pa_water1.x0(), pa_water1.y0()),
1213 for(s32 i=0; i<4; i++)
1215 vertices[i].Pos.Y += (-0.5+node_water_level)*BS;
1216 vertices[i].Pos += intToFloat(p + getPosRelative(), BS);
1219 u16 indices[] = {0,1,2,2,3,0};
1220 // Add to mesh collector
1221 collector.append(material_water1, vertices, 4, indices, 6);
1224 Add leaves if using new style
1226 else if(n.d == CONTENT_LEAVES && new_style_leaves)
1228 /*u8 l = decode_light(n.getLightBlend(daynight_ratio));*/
1229 u8 l = decode_light(undiminish_light(n.getLightBlend(daynight_ratio)));
1230 video::SColor c(255,l,l,l);
1232 for(u32 j=0; j<6; j++)
1234 video::S3DVertex vertices[4] =
1236 /*video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, 0,1),
1237 video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, 1,1),
1238 video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c, 1,0),
1239 video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c, 0,0),*/
1240 video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c,
1241 pa_leaves1.x0(), pa_leaves1.y1()),
1242 video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c,
1243 pa_leaves1.x1(), pa_leaves1.y1()),
1244 video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c,
1245 pa_leaves1.x1(), pa_leaves1.y0()),
1246 video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c,
1247 pa_leaves1.x0(), pa_leaves1.y0()),
1252 for(u16 i=0; i<4; i++)
1253 vertices[i].Pos.rotateXZBy(0);
1257 for(u16 i=0; i<4; i++)
1258 vertices[i].Pos.rotateXZBy(180);
1262 for(u16 i=0; i<4; i++)
1263 vertices[i].Pos.rotateXZBy(-90);
1267 for(u16 i=0; i<4; i++)
1268 vertices[i].Pos.rotateXZBy(90);
1272 for(u16 i=0; i<4; i++)
1273 vertices[i].Pos.rotateYZBy(-90);
1277 for(u16 i=0; i<4; i++)
1278 vertices[i].Pos.rotateYZBy(90);
1281 for(u16 i=0; i<4; i++)
1283 vertices[i].Pos += intToFloat(p + getPosRelative(), BS);
1286 u16 indices[] = {0,1,2,2,3,0};
1287 // Add to mesh collector
1288 collector.append(material_leaves1, vertices, 4, indices, 6);
1294 Add stuff from collector to mesh
1297 scene::SMesh *mesh_new = NULL;
1298 mesh_new = new scene::SMesh();
1300 collector.fillMesh(mesh_new);
1303 Do some stuff to the mesh
1306 mesh_new->recalculateBoundingBox();
1309 Delete new mesh if it is empty
1312 if(mesh_new->getMeshBufferCount() == 0)
1321 // Usually 1-700 faces and 1-7 materials
1322 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
1323 <<"and uses "<<mesh_new->getMeshBufferCount()
1324 <<" materials (meshbuffers)"<<std::endl;
1327 // Use VBO for mesh (this just would set this for ever buffer)
1328 // This will lead to infinite memory usage because or irrlicht.
1329 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
1332 NOTE: If that is enabled, some kind of a queue to the main
1333 thread should be made which would call irrlicht to delete
1334 the hardware buffer and then delete the mesh
1344 //scene::SMesh *mesh_old = mesh[daynight_i];
1345 //mesh[daynight_i] = mesh_new;
1347 scene::SMesh *mesh_old = mesh;
1349 setMeshExpired(false);
1351 if(mesh_old != NULL)
1353 // Remove hardware buffers of meshbuffers of mesh
1354 // NOTE: No way, this runs in a different thread and everything
1355 /*u32 c = mesh_old->getMeshBufferCount();
1356 for(u32 i=0; i<c; i++)
1358 IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1361 /*dstream<<"mesh_old->getReferenceCount()="
1362 <<mesh_old->getReferenceCount()<<std::endl;
1363 u32 c = mesh_old->getMeshBufferCount();
1364 for(u32 i=0; i<c; i++)
1366 scene::IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1367 dstream<<"buf->getReferenceCount()="
1368 <<buf->getReferenceCount()<<std::endl;
1377 mesh_mutex.Unlock();
1379 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1382 /*void MapBlock::updateMeshes(s32 first_i)
1384 assert(first_i >= 0 && first_i <= DAYNIGHT_CACHE_COUNT);
1385 updateMesh(first_i);
1386 for(s32 i=0; i<DAYNIGHT_CACHE_COUNT; i++)
1397 Propagates sunlight down through the block.
1398 Doesn't modify nodes that are not affected by sunlight.
1400 Returns false if sunlight at bottom block is invalid
1401 Returns true if bottom block doesn't exist.
1403 If there is a block above, continues from it.
1404 If there is no block above, assumes there is sunlight, unless
1405 is_underground is set or highest node is water.
1407 At the moment, all sunlighted nodes are added to light_sources.
1408 - SUGG: This could be optimized
1410 Turns sunglighted mud into grass.
1412 if remove_light==true, sets non-sunlighted nodes black.
1414 if black_air_left!=NULL, it is set to true if non-sunlighted
1415 air is left in block.
1417 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources,
1418 bool remove_light, bool *black_air_left,
1421 // Whether the sunlight at the top of the bottom block is valid
1422 bool block_below_is_valid = true;
1424 v3s16 pos_relative = getPosRelative();
1426 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
1428 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
1431 bool no_sunlight = false;
1432 bool no_top_block = false;
1433 // Check if node above block has sunlight
1435 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1436 if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1441 catch(InvalidPositionException &e)
1443 no_top_block = true;
1445 // NOTE: This makes over-ground roofed places sunlighted
1446 // Assume sunlight, unless is_underground==true
1453 MapNode n = getNode(v3s16(x, MAP_BLOCKSIZE-1, z));
1454 if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
1459 // NOTE: As of now, this just would make everything dark.
1461 //no_sunlight = true;
1464 #if 0 // Doesn't work; nothing gets light.
1465 bool no_sunlight = true;
1466 bool no_top_block = false;
1467 // Check if node above block has sunlight
1469 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1470 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
1472 no_sunlight = false;
1475 catch(InvalidPositionException &e)
1477 no_top_block = true;
1481 /*std::cout<<"("<<x<<","<<z<<"): "
1482 <<"no_top_block="<<no_top_block
1483 <<", is_underground="<<is_underground
1484 <<", no_sunlight="<<no_sunlight
1487 s16 y = MAP_BLOCKSIZE-1;
1489 // This makes difference to diminishing in water.
1490 bool stopped_to_solid_object = false;
1492 u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
1497 MapNode &n = getNodeRef(pos);
1499 if(current_light == 0)
1503 else if(current_light == LIGHT_SUN && n.sunlight_propagates())
1505 // Do nothing: Sunlight is continued
1507 else if(n.light_propagates() == false)
1511 bool upper_is_air = false;
1514 if(getNodeParent(pos+v3s16(0,1,0)).d == CONTENT_AIR)
1515 upper_is_air = true;
1517 catch(InvalidPositionException &e)
1520 // Turn mud into grass
1521 if(upper_is_air && n.d == CONTENT_MUD
1522 && current_light == LIGHT_SUN)
1524 n.d = CONTENT_GRASS;
1528 // A solid object is on the way.
1529 stopped_to_solid_object = true;
1537 current_light = diminish_light(current_light);
1540 u8 old_light = n.getLight(LIGHTBANK_DAY);
1542 if(current_light > old_light || remove_light)
1544 n.setLight(LIGHTBANK_DAY, current_light);
1547 if(diminish_light(current_light) != 0)
1549 light_sources.insert(pos_relative + pos, true);
1552 if(current_light == 0 && stopped_to_solid_object)
1556 *black_air_left = true;
1561 // Whether or not the block below should see LIGHT_SUN
1562 bool sunlight_should_go_down = (current_light == LIGHT_SUN);
1565 If the block below hasn't already been marked invalid:
1567 Check if the node below the block has proper sunlight at top.
1568 If not, the block below is invalid.
1570 Ignore non-transparent nodes as they always have no light
1574 if(block_below_is_valid)
1576 MapNode n = getNodeParent(v3s16(x, -1, z));
1577 if(n.light_propagates())
1579 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN
1580 && sunlight_should_go_down == false)
1581 block_below_is_valid = false;
1582 else if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN
1583 && sunlight_should_go_down == true)
1584 block_below_is_valid = false;
1588 catch(InvalidPositionException &e)
1590 /*std::cout<<"InvalidBlockException for bottom block node"
1592 // Just no block below, no need to panic.
1597 return block_below_is_valid;
1600 void MapBlock::copyTo(VoxelManipulator &dst)
1602 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
1603 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
1605 // Copy from data to VoxelManipulator
1606 dst.copyFrom(data, data_area, v3s16(0,0,0),
1607 getPosRelative(), data_size);
1610 void MapBlock::copyFrom(VoxelManipulator &dst)
1612 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
1613 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
1615 // Copy from VoxelManipulator to data
1616 dst.copyTo(data, data_area, v3s16(0,0,0),
1617 getPosRelative(), data_size);
1620 void MapBlock::stepObjects(float dtime, bool server, u32 daynight_ratio)
1625 m_objects.step(dtime, server, daynight_ratio);
1628 Spawn some objects at random.
1630 Use dayNightDiffed() to approximate being near ground level
1632 if(m_spawn_timer < -999)
1636 if(dayNightDiffed() == true && getObjectCount() == 0)
1638 m_spawn_timer -= dtime;
1639 if(m_spawn_timer <= 0.0)
1641 m_spawn_timer += myrand() % 300;
1644 (myrand()%(MAP_BLOCKSIZE-1))+0,
1645 (myrand()%(MAP_BLOCKSIZE-1))+0
1648 s16 y = getGroundLevel(p2d);
1652 v3s16 p(p2d.X, y+1, p2d.Y);
1654 if(getNode(p).d == CONTENT_AIR
1655 && getNode(p).getLightBlend(daynight_ratio) <= 11)
1657 RatObject *obj = new RatObject(NULL, -1, intToFloat(p, BS));
1668 void MapBlock::updateDayNightDiff()
1672 m_day_night_differs = false;
1676 bool differs = false;
1679 Check if any lighting value differs
1681 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1683 MapNode &n = data[i];
1684 if(n.getLight(LIGHTBANK_DAY) != n.getLight(LIGHTBANK_NIGHT))
1692 If some lighting values differ, check if the whole thing is
1693 just air. If it is, differ = false
1697 bool only_air = true;
1698 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1700 MapNode &n = data[i];
1701 if(n.d != CONTENT_AIR)
1711 // Set member variable
1712 m_day_night_differs = differs;
1715 s16 MapBlock::getGroundLevel(v2s16 p2d)
1721 s16 y = MAP_BLOCKSIZE-1;
1724 //if(is_ground_content(getNodeRef(p2d.X, y, p2d.Y).d))
1725 if(content_features(getNodeRef(p2d.X, y, p2d.Y).d).walkable)
1727 if(y == MAP_BLOCKSIZE-1)
1735 catch(InvalidPositionException &e)
1745 void MapBlock::serialize(std::ostream &os, u8 version)
1747 if(!ser_ver_supported(version))
1748 throw VersionMismatchException("ERROR: MapBlock format not supported");
1752 throw SerializationError("ERROR: Not writing dummy block.");
1755 // These have no compression
1756 if(version <= 3 || version == 5 || version == 6)
1758 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1760 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
1761 SharedBuffer<u8> dest(buflen);
1763 dest[0] = is_underground;
1764 for(u32 i=0; i<nodecount; i++)
1766 u32 s = 1 + i * MapNode::serializedLength(version);
1767 data[i].serialize(&dest[s], version);
1770 os.write((char*)*dest, dest.getSize());
1772 else if(version <= 10)
1776 Compress the materials and the params separately.
1780 os.write((char*)&is_underground, 1);
1782 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1784 // Get and compress materials
1785 SharedBuffer<u8> materialdata(nodecount);
1786 for(u32 i=0; i<nodecount; i++)
1788 materialdata[i] = data[i].d;
1790 compress(materialdata, os, version);
1792 // Get and compress lights
1793 SharedBuffer<u8> lightdata(nodecount);
1794 for(u32 i=0; i<nodecount; i++)
1796 lightdata[i] = data[i].param;
1798 compress(lightdata, os, version);
1802 // Get and compress param2
1803 SharedBuffer<u8> param2data(nodecount);
1804 for(u32 i=0; i<nodecount; i++)
1806 param2data[i] = data[i].param2;
1808 compress(param2data, os, version);
1811 // All other versions (newest)
1818 if(m_day_night_differs)
1820 if(m_lighting_expired)
1822 /*if(m_not_fully_generated)
1824 os.write((char*)&flags, 1);
1826 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1832 SharedBuffer<u8> databuf(nodecount*3);
1835 for(u32 i=0; i<nodecount; i++)
1837 databuf[i] = data[i].d;
1841 for(u32 i=0; i<nodecount; i++)
1843 databuf[i+nodecount] = data[i].param;
1847 for(u32 i=0; i<nodecount; i++)
1849 databuf[i+nodecount*2] = data[i].param2;
1853 Compress data to output stream
1856 compress(databuf, os, version);
1860 void MapBlock::deSerialize(std::istream &is, u8 version)
1862 if(!ser_ver_supported(version))
1863 throw VersionMismatchException("ERROR: MapBlock format not supported");
1865 // These have no compression
1866 if(version <= 3 || version == 5 || version == 6)
1868 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1871 if(is.gcount() != 1)
1872 throw SerializationError
1873 ("MapBlock::deSerialize: no enough input data");
1874 is_underground = tmp;
1875 for(u32 i=0; i<nodecount; i++)
1877 s32 len = MapNode::serializedLength(version);
1878 SharedBuffer<u8> d(len);
1879 is.read((char*)*d, len);
1880 if(is.gcount() != len)
1881 throw SerializationError
1882 ("MapBlock::deSerialize: no enough input data");
1883 data[i].deSerialize(*d, version);
1886 else if(version <= 10)
1888 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1891 is.read((char*)&t8, 1);
1892 is_underground = t8;
1895 // Uncompress and set material data
1896 std::ostringstream os(std::ios_base::binary);
1897 decompress(is, os, version);
1898 std::string s = os.str();
1899 if(s.size() != nodecount)
1900 throw SerializationError
1901 ("MapBlock::deSerialize: invalid format");
1902 for(u32 i=0; i<s.size(); i++)
1908 // Uncompress and set param data
1909 std::ostringstream os(std::ios_base::binary);
1910 decompress(is, os, version);
1911 std::string s = os.str();
1912 if(s.size() != nodecount)
1913 throw SerializationError
1914 ("MapBlock::deSerialize: invalid format");
1915 for(u32 i=0; i<s.size(); i++)
1917 data[i].param = s[i];
1923 // Uncompress and set param2 data
1924 std::ostringstream os(std::ios_base::binary);
1925 decompress(is, os, version);
1926 std::string s = os.str();
1927 if(s.size() != nodecount)
1928 throw SerializationError
1929 ("MapBlock::deSerialize: invalid format");
1930 for(u32 i=0; i<s.size(); i++)
1932 data[i].param2 = s[i];
1936 // All other versions (newest)
1939 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1942 is.read((char*)&flags, 1);
1943 is_underground = (flags & 0x01) ? true : false;
1944 m_day_night_differs = (flags & 0x02) ? true : false;
1945 m_lighting_expired = (flags & 0x04) ? true : false;
1946 //m_not_fully_generated = (flags & 0x08) ? true : false;
1949 std::ostringstream os(std::ios_base::binary);
1950 decompress(is, os, version);
1951 std::string s = os.str();
1952 if(s.size() != nodecount*3)
1953 throw SerializationError
1954 ("MapBlock::deSerialize: invalid format");
1957 for(u32 i=0; i<nodecount; i++)
1962 for(u32 i=0; i<nodecount; i++)
1964 data[i].param = s[i+nodecount];
1967 for(u32 i=0; i<nodecount; i++)
1969 data[i].param2 = s[i+nodecount*2];
1974 Translate nodes as specified in the translate_to fields of
1977 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1979 MapNode &n = data[i];
1981 MapNode *translate_to = content_features(n.d).translate_to;
1984 dstream<<"MapBlock: WARNING: Translating node "<<n.d<<" to "
1985 <<translate_to->d<<std::endl;