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 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 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 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 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 updateFastFaceRow(
390 core::array<FastFace> &dest,
391 NodeModMap &temp_mods,
392 VoxelManipulator &vmanip,
393 v3s16 blockpos_nodes)
397 u16 continuous_tiles_count = 0;
399 MapNode n0 = vmanip.getNodeNoEx(blockpos_nodes + p);
400 MapNode n1 = vmanip.getNodeNoEx(blockpos_nodes + p + face_dir);
401 TileSpec tile0 = getNodeTile(n0, p, face_dir, temp_mods);
402 TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir, temp_mods);
403 u8 light = getFaceLight(daynight_ratio, n0, n1, face_dir);
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;
422 n0_next = vmanip.getNodeNoEx(blockpos_nodes + p_next);
423 n1_next = vmanip.getNodeNoEx(blockpos_nodes + p_next + face_dir);
424 tile0_next = getNodeTile(n0_next, p_next, face_dir, temp_mods);
425 tile1_next = getNodeTile(n1_next,p_next+face_dir,-face_dir, temp_mods);
426 light_next = getFaceLight(daynight_ratio, n0_next, n1_next, face_dir);
428 if(tile0_next == tile0
429 && tile1_next == tile1
430 && light_next == light)
432 next_is_different = false;
436 continuous_tiles_count++;
438 // This is set to true if the texture doesn't allow more tiling
439 bool end_of_texture = false;
441 If there is no texture, it can be tiled infinitely.
442 If tiled==0, it means the texture can be tiled infinitely.
443 Otherwise check tiled agains continuous_tiles_count.
445 This check has to be made for both tiles, because this is
446 a bit hackish and we know which one we're using only when
447 the decision to make the faces is made.
449 if(tile0.texture.atlas != NULL && tile0.texture.tiled != 0)
451 if(tile0.texture.tiled <= continuous_tiles_count)
452 end_of_texture = true;
454 if(tile1.texture.atlas != NULL && tile1.texture.tiled != 0)
456 if(tile1.texture.tiled <= continuous_tiles_count)
457 end_of_texture = true;
460 //end_of_texture = true; //DEBUG
462 if(next_is_different || end_of_texture)
465 Create a face if there should be one
467 //u8 mf = face_contents(tile0, tile1);
469 u8 content0 = getNodeContent(p, n0, temp_mods);
470 u8 content1 = getNodeContent(p + face_dir, n1, temp_mods);
471 u8 mf = face_contents(content0, content1);
475 // Floating point conversion of the position vector
476 v3f pf(p.X, p.Y, p.Z);
477 // Center point of face (kind of)
478 v3f sp = pf - ((f32)continuous_tiles_count / 2. - 0.5) * translate_dir_f;
480 if(translate_dir.X != 0){
481 scale.X = continuous_tiles_count;
483 if(translate_dir.Y != 0){
484 scale.Y = continuous_tiles_count;
486 if(translate_dir.Z != 0){
487 scale.Z = continuous_tiles_count;
492 // If node at sp (tile0) is more solid
495 makeFastFace(tile0, decode_light(light),
497 posRelative_f, dest);
499 // If node at sp is less solid (mf == 2)
502 makeFastFace(tile1, decode_light(light),
503 sp+face_dir_f, -face_dir, scale,
504 posRelative_f, dest);
509 continuous_tiles_count = 0;
522 This is used because CMeshBuffer::append() is very slow
526 video::SMaterial material;
527 core::array<u16> indices;
528 core::array<video::S3DVertex> vertices;
535 video::SMaterial material,
536 const video::S3DVertex* const vertices,
538 const u16* const indices,
542 PreMeshBuffer *p = NULL;
543 for(u32 i=0; i<m_prebuffers.size(); i++)
545 PreMeshBuffer &pp = m_prebuffers[i];
546 if(pp.material != material)
556 pp.material = material;
557 m_prebuffers.push_back(pp);
558 p = &m_prebuffers[m_prebuffers.size()-1];
561 u32 vertex_count = p->vertices.size();
562 for(u32 i=0; i<numIndices; i++)
564 u32 j = indices[i] + vertex_count;
567 dstream<<"FIXME: Meshbuffer ran out of indices"<<std::endl;
568 // NOTE: Fix is to just add an another MeshBuffer
570 p->indices.push_back(j);
572 for(u32 i=0; i<numVertices; i++)
574 p->vertices.push_back(vertices[i]);
578 void fillMesh(scene::SMesh *mesh)
580 /*dstream<<"Filling mesh with "<<m_prebuffers.size()
581 <<" meshbuffers"<<std::endl;*/
582 for(u32 i=0; i<m_prebuffers.size(); i++)
584 PreMeshBuffer &p = m_prebuffers[i];
586 /*dstream<<"p.vertices.size()="<<p.vertices.size()
587 <<", p.indices.size()="<<p.indices.size()
592 // This is a "Standard MeshBuffer",
593 // it's a typedeffed CMeshBuffer<video::S3DVertex>
594 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
596 buf->Material = p.material;
597 //((scene::SMeshBuffer*)buf)->Material = p.material;
599 //buf->setHardwareMappingHint(scene::EHM_STATIC);
601 mesh->addMeshBuffer(buf);
605 buf->append(p.vertices.pointer(), p.vertices.size(),
606 p.indices.pointer(), p.indices.size());
611 core::array<PreMeshBuffer> m_prebuffers;
614 scene::SMesh* makeMapBlockMesh(
616 NodeModMap &temp_mods,
617 VoxelManipulator &vmanip,
618 v3s16 blockpos_nodes)
620 // 4-21ms for MAP_BLOCKSIZE=16
621 // 24-155ms for MAP_BLOCKSIZE=32
622 //TimeTaker timer1("makeMapBlockMesh()");
624 core::array<FastFace> fastfaces_new;
626 // floating point conversion
627 v3f posRelative_f(blockpos_nodes.X, blockpos_nodes.Y, blockpos_nodes.Z);
632 bool new_style_water = g_settings.getBool("new_style_water");
633 bool new_style_leaves = g_settings.getBool("new_style_leaves");
635 float node_water_level = 1.0;
637 node_water_level = 0.85;
640 We are including the faces of the trailing edges of the block.
641 This means that when something changes, the caller must
642 also update the meshes of the blocks at the leading edges.
644 NOTE: This is the slowest part of this method.
648 // 4-23ms for MAP_BLOCKSIZE=16
649 //TimeTaker timer2("updateMesh() collect");
652 Go through every y,z and get top faces in rows of x+
654 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
655 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
656 updateFastFaceRow(daynight_ratio, posRelative_f,
657 v3s16(0,y,z), MAP_BLOCKSIZE,
660 v3s16(0,1,0), //face dir
669 Go through every x,y and get right faces in rows of z+
671 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
672 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
673 updateFastFaceRow(daynight_ratio, posRelative_f,
674 v3s16(x,y,0), MAP_BLOCKSIZE,
686 Go through every y,z and get back faces in rows of x+
688 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
689 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
690 updateFastFaceRow(daynight_ratio, posRelative_f,
691 v3s16(0,y,z), MAP_BLOCKSIZE,
707 Convert FastFaces to SMesh
710 MeshCollector collector;
712 if(fastfaces_new.size() > 0)
714 // avg 0ms (100ms spikes when loading textures the first time)
715 //TimeTaker timer2("updateMesh() mesh building");
717 video::SMaterial material;
718 material.setFlag(video::EMF_LIGHTING, false);
719 material.setFlag(video::EMF_BILINEAR_FILTER, false);
720 material.setFlag(video::EMF_FOG_ENABLE, true);
721 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
722 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_SIMPLE);
724 for(u32 i=0; i<fastfaces_new.size(); i++)
726 FastFace &f = fastfaces_new[i];
728 const u16 indices[] = {0,1,2,2,3,0};
730 //video::ITexture *texture = g_irrlicht->getTexture(f.tile.spec);
731 video::ITexture *texture = f.tile.texture.atlas;
735 material.setTexture(0, texture);
737 f.tile.applyMaterialOptions(material);
739 collector.append(material, f.vertices, 4, indices, 6);
744 Add special graphics:
750 //TimeTaker timer2("updateMesh() adding special stuff");
752 // Flowing water material
753 video::SMaterial material_water1;
754 material_water1.setFlag(video::EMF_LIGHTING, false);
755 //material_water1.setFlag(video::EMF_BACK_FACE_CULLING, false);
756 material_water1.setFlag(video::EMF_BILINEAR_FILTER, false);
757 material_water1.setFlag(video::EMF_FOG_ENABLE, true);
758 material_water1.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
760 //material_water1.setTexture(0, g_irrlicht->getTexture("water.png"));
761 AtlasPointer pa_water1 = g_texturesource->getTexture(
762 g_texturesource->getTextureId("water.png"));
763 material_water1.setTexture(0, pa_water1.atlas);
765 // New-style leaves material
766 video::SMaterial material_leaves1;
767 material_leaves1.setFlag(video::EMF_LIGHTING, false);
768 //material_leaves1.setFlag(video::EMF_BACK_FACE_CULLING, false);
769 material_leaves1.setFlag(video::EMF_BILINEAR_FILTER, false);
770 material_leaves1.setFlag(video::EMF_FOG_ENABLE, true);
771 material_leaves1.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
773 //material_leaves1.setTexture(0, g_irrlicht->getTexture("leaves.png"));
774 AtlasPointer pa_leaves1 = g_texturesource->getTexture(
775 g_texturesource->getTextureId("leaves.png"));
776 material_leaves1.setTexture(0, pa_leaves1.atlas);
778 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
779 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
780 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
784 MapNode &n = vmanip.getNodeRef(blockpos_nodes+p);
789 if(n.d == CONTENT_TORCH)
791 video::SColor c(255,255,255,255);
793 // Wall at X+ of node
794 video::S3DVertex vertices[4] =
796 video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c, 0,1),
797 video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c, 1,1),
798 video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, 1,0),
799 video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c, 0,0),
802 v3s16 dir = unpackDir(n.dir);
804 for(s32 i=0; i<4; i++)
806 if(dir == v3s16(1,0,0))
807 vertices[i].Pos.rotateXZBy(0);
808 if(dir == v3s16(-1,0,0))
809 vertices[i].Pos.rotateXZBy(180);
810 if(dir == v3s16(0,0,1))
811 vertices[i].Pos.rotateXZBy(90);
812 if(dir == v3s16(0,0,-1))
813 vertices[i].Pos.rotateXZBy(-90);
814 if(dir == v3s16(0,-1,0))
815 vertices[i].Pos.rotateXZBy(45);
816 if(dir == v3s16(0,1,0))
817 vertices[i].Pos.rotateXZBy(-45);
819 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
823 video::SMaterial material;
824 material.setFlag(video::EMF_LIGHTING, false);
825 material.setFlag(video::EMF_BACK_FACE_CULLING, false);
826 material.setFlag(video::EMF_BILINEAR_FILTER, false);
827 //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
828 material.MaterialType
829 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
831 if(dir == v3s16(0,-1,0))
832 material.setTexture(0,
833 g_texturesource->getTextureRaw("torch_on_floor.png"));
834 else if(dir == v3s16(0,1,0))
835 material.setTexture(0,
836 g_texturesource->getTextureRaw("torch_on_ceiling.png"));
837 // For backwards compatibility
838 else if(dir == v3s16(0,0,0))
839 material.setTexture(0,
840 g_texturesource->getTextureRaw("torch_on_floor.png"));
842 material.setTexture(0,
843 g_texturesource->getTextureRaw("torch.png"));
845 u16 indices[] = {0,1,2,2,3,0};
846 // Add to mesh collector
847 collector.append(material, vertices, 4, indices, 6);
852 if(n.d == CONTENT_SIGN_WALL)
854 u8 l = decode_light(n.getLightBlend(daynight_ratio));
855 video::SColor c(255,l,l,l);
857 float d = (float)BS/16;
858 // Wall at X+ of node
859 video::S3DVertex vertices[4] =
861 video::S3DVertex(BS/2-d,-BS/2,-BS/2, 0,0,0, c, 0,1),
862 video::S3DVertex(BS/2-d,-BS/2,BS/2, 0,0,0, c, 1,1),
863 video::S3DVertex(BS/2-d,BS/2,BS/2, 0,0,0, c, 1,0),
864 video::S3DVertex(BS/2-d,BS/2,-BS/2, 0,0,0, c, 0,0),
867 v3s16 dir = unpackDir(n.dir);
869 for(s32 i=0; i<4; i++)
871 if(dir == v3s16(1,0,0))
872 vertices[i].Pos.rotateXZBy(0);
873 if(dir == v3s16(-1,0,0))
874 vertices[i].Pos.rotateXZBy(180);
875 if(dir == v3s16(0,0,1))
876 vertices[i].Pos.rotateXZBy(90);
877 if(dir == v3s16(0,0,-1))
878 vertices[i].Pos.rotateXZBy(-90);
879 if(dir == v3s16(0,-1,0))
880 vertices[i].Pos.rotateXYBy(-90);
881 if(dir == v3s16(0,1,0))
882 vertices[i].Pos.rotateXYBy(90);
884 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
888 video::SMaterial material;
889 material.setFlag(video::EMF_LIGHTING, false);
890 material.setFlag(video::EMF_BACK_FACE_CULLING, false);
891 material.setFlag(video::EMF_BILINEAR_FILTER, false);
892 material.setFlag(video::EMF_FOG_ENABLE, true);
893 //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
894 material.MaterialType
895 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
897 material.setTexture(0,
898 g_texturesource->getTextureRaw("sign_wall.png"));
900 u16 indices[] = {0,1,2,2,3,0};
901 // Add to mesh collector
902 collector.append(material, vertices, 4, indices, 6);
905 Add flowing water to mesh
907 else if(n.d == CONTENT_WATER)
909 bool top_is_water = false;
911 MapNode n = vmanip.getNode(blockpos_nodes + v3s16(x,y+1,z));
912 if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
914 }catch(InvalidPositionException &e){}
916 u8 l = decode_light(n.getLightBlend(daynight_ratio));
917 video::SColor c(WATER_ALPHA,l,l,l);
919 // Neighbor water levels (key = relative position)
920 // Includes current node
921 core::map<v3s16, f32> neighbor_levels;
922 core::map<v3s16, u8> neighbor_contents;
923 core::map<v3s16, u8> neighbor_flags;
924 const u8 neighborflag_top_is_water = 0x01;
925 v3s16 neighbor_dirs[9] = {
936 for(u32 i=0; i<9; i++)
938 u8 content = CONTENT_AIR;
939 float level = -0.5 * BS;
943 v3s16 p2 = p + neighbor_dirs[i];
944 MapNode n2 = vmanip.getNode(blockpos_nodes + p2);
948 if(n2.d == CONTENT_WATERSOURCE)
949 level = (-0.5+node_water_level) * BS;
950 else if(n2.d == CONTENT_WATER)
951 level = (-0.5 + ((float)n2.param2 + 0.5) / 8.0
952 * node_water_level) * BS;
954 // Check node above neighbor.
955 // NOTE: This doesn't get executed if neighbor
958 n2 = vmanip.getNode(blockpos_nodes + p2);
959 if(n2.d == CONTENT_WATERSOURCE || n2.d == CONTENT_WATER)
960 flags |= neighborflag_top_is_water;
962 catch(InvalidPositionException &e){}
964 neighbor_levels.insert(neighbor_dirs[i], level);
965 neighbor_contents.insert(neighbor_dirs[i], content);
966 neighbor_flags.insert(neighbor_dirs[i], flags);
969 //float water_level = (-0.5 + ((float)n.param2 + 0.5) / 8.0) * BS;
970 //float water_level = neighbor_levels[v3s16(0,0,0)];
972 // Corner heights (average between four waters)
973 f32 corner_levels[4];
975 v3s16 halfdirs[4] = {
981 for(u32 i=0; i<4; i++)
983 v3s16 cornerdir = halfdirs[i];
984 float cornerlevel = 0;
986 for(u32 j=0; j<4; j++)
988 v3s16 neighbordir = cornerdir - halfdirs[j];
989 u8 content = neighbor_contents[neighbordir];
990 // Special case for source nodes
991 if(content == CONTENT_WATERSOURCE)
993 cornerlevel = (-0.5+node_water_level)*BS;
997 else if(content == CONTENT_WATER)
999 cornerlevel += neighbor_levels[neighbordir];
1002 else if(content == CONTENT_AIR)
1004 cornerlevel += -0.5*BS;
1009 cornerlevel /= valid_count;
1010 corner_levels[i] = cornerlevel;
1017 v3s16 side_dirs[4] = {
1023 s16 side_corners[4][2] = {
1029 for(u32 i=0; i<4; i++)
1031 v3s16 dir = side_dirs[i];
1034 If our topside is water and neighbor's topside
1035 is water, don't draw side face
1038 neighbor_flags[dir] & neighborflag_top_is_water)
1041 u8 neighbor_content = neighbor_contents[dir];
1043 // Don't draw face if neighbor is not air or water
1044 if(neighbor_content != CONTENT_AIR
1045 && neighbor_content != CONTENT_WATER)
1048 bool neighbor_is_water = (neighbor_content == CONTENT_WATER);
1050 // Don't draw any faces if neighbor is water and top is water
1051 if(neighbor_is_water == true && top_is_water == false)
1054 video::S3DVertex vertices[4] =
1056 /*video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1),
1057 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1),
1058 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1059 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1060 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1061 pa_water1.x0(), pa_water1.y1()),
1062 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1063 pa_water1.x1(), pa_water1.y1()),
1064 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1065 pa_water1.x1(), pa_water1.y0()),
1066 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1067 pa_water1.x0(), pa_water1.y0()),
1071 If our topside is water, set upper border of face
1072 at upper border of node
1076 vertices[2].Pos.Y = 0.5*BS;
1077 vertices[3].Pos.Y = 0.5*BS;
1080 Otherwise upper position of face is corner levels
1084 vertices[2].Pos.Y = corner_levels[side_corners[i][0]];
1085 vertices[3].Pos.Y = corner_levels[side_corners[i][1]];
1089 If neighbor is water, lower border of face is corner
1092 if(neighbor_is_water)
1094 vertices[0].Pos.Y = corner_levels[side_corners[i][1]];
1095 vertices[1].Pos.Y = corner_levels[side_corners[i][0]];
1098 If neighbor is not water, lower border of face is
1099 lower border of node
1103 vertices[0].Pos.Y = -0.5*BS;
1104 vertices[1].Pos.Y = -0.5*BS;
1107 for(s32 j=0; j<4; j++)
1109 if(dir == v3s16(0,0,1))
1110 vertices[j].Pos.rotateXZBy(0);
1111 if(dir == v3s16(0,0,-1))
1112 vertices[j].Pos.rotateXZBy(180);
1113 if(dir == v3s16(-1,0,0))
1114 vertices[j].Pos.rotateXZBy(90);
1115 if(dir == v3s16(1,0,-0))
1116 vertices[j].Pos.rotateXZBy(-90);
1118 vertices[j].Pos += intToFloat(p + blockpos_nodes, BS);
1121 u16 indices[] = {0,1,2,2,3,0};
1122 // Add to mesh collector
1123 collector.append(material_water1, vertices, 4, indices, 6);
1127 Generate top side, if appropriate
1130 if(top_is_water == false)
1132 video::S3DVertex vertices[4] =
1134 /*video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
1135 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
1136 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1137 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1138 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1139 pa_water1.x0(), pa_water1.y1()),
1140 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1141 pa_water1.x1(), pa_water1.y1()),
1142 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
1143 pa_water1.x1(), pa_water1.y0()),
1144 video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
1145 pa_water1.x0(), pa_water1.y0()),
1148 // This fixes a strange bug
1149 s32 corner_resolve[4] = {3,2,1,0};
1151 for(s32 i=0; i<4; i++)
1153 //vertices[i].Pos.Y += water_level;
1154 //vertices[i].Pos.Y += neighbor_levels[v3s16(0,0,0)];
1155 s32 j = corner_resolve[i];
1156 vertices[i].Pos.Y += corner_levels[j];
1157 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1160 u16 indices[] = {0,1,2,2,3,0};
1161 // Add to mesh collector
1162 collector.append(material_water1, vertices, 4, indices, 6);
1166 Add water sources to mesh if using new style
1168 else if(n.d == CONTENT_WATERSOURCE && new_style_water)
1170 //bool top_is_water = false;
1171 bool top_is_air = false;
1173 MapNode n = vmanip.getNode(blockpos_nodes + v3s16(x,y+1,z));
1174 /*if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
1175 top_is_water = true;*/
1176 if(n.d == CONTENT_AIR)
1178 }catch(InvalidPositionException &e){}
1180 /*if(top_is_water == true)
1182 if(top_is_air == false)
1185 u8 l = decode_light(n.getLightBlend(daynight_ratio));
1186 video::SColor c(WATER_ALPHA,l,l,l);
1188 video::S3DVertex vertices[4] =
1190 /*video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
1191 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
1192 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1193 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1194 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1195 pa_water1.x0(), pa_water1.y1()),
1196 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1197 pa_water1.x1(), pa_water1.y1()),
1198 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
1199 pa_water1.x1(), pa_water1.y0()),
1200 video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
1201 pa_water1.x0(), pa_water1.y0()),
1204 for(s32 i=0; i<4; i++)
1206 vertices[i].Pos.Y += (-0.5+node_water_level)*BS;
1207 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1210 u16 indices[] = {0,1,2,2,3,0};
1211 // Add to mesh collector
1212 collector.append(material_water1, vertices, 4, indices, 6);
1215 Add leaves if using new style
1217 else if(n.d == CONTENT_LEAVES && new_style_leaves)
1219 /*u8 l = decode_light(n.getLightBlend(daynight_ratio));*/
1220 u8 l = decode_light(undiminish_light(n.getLightBlend(daynight_ratio)));
1221 video::SColor c(255,l,l,l);
1223 for(u32 j=0; j<6; j++)
1225 video::S3DVertex vertices[4] =
1227 /*video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, 0,1),
1228 video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, 1,1),
1229 video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c, 1,0),
1230 video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c, 0,0),*/
1231 video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c,
1232 pa_leaves1.x0(), pa_leaves1.y1()),
1233 video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c,
1234 pa_leaves1.x1(), pa_leaves1.y1()),
1235 video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c,
1236 pa_leaves1.x1(), pa_leaves1.y0()),
1237 video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c,
1238 pa_leaves1.x0(), pa_leaves1.y0()),
1243 for(u16 i=0; i<4; i++)
1244 vertices[i].Pos.rotateXZBy(0);
1248 for(u16 i=0; i<4; i++)
1249 vertices[i].Pos.rotateXZBy(180);
1253 for(u16 i=0; i<4; i++)
1254 vertices[i].Pos.rotateXZBy(-90);
1258 for(u16 i=0; i<4; i++)
1259 vertices[i].Pos.rotateXZBy(90);
1263 for(u16 i=0; i<4; i++)
1264 vertices[i].Pos.rotateYZBy(-90);
1268 for(u16 i=0; i<4; i++)
1269 vertices[i].Pos.rotateYZBy(90);
1272 for(u16 i=0; i<4; i++)
1274 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1277 u16 indices[] = {0,1,2,2,3,0};
1278 // Add to mesh collector
1279 collector.append(material_leaves1, vertices, 4, indices, 6);
1285 Add stuff from collector to mesh
1288 scene::SMesh *mesh_new = NULL;
1289 mesh_new = new scene::SMesh();
1291 collector.fillMesh(mesh_new);
1294 Do some stuff to the mesh
1297 mesh_new->recalculateBoundingBox();
1300 Delete new mesh if it is empty
1303 if(mesh_new->getMeshBufferCount() == 0)
1312 // Usually 1-700 faces and 1-7 materials
1313 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
1314 <<"and uses "<<mesh_new->getMeshBufferCount()
1315 <<" materials (meshbuffers)"<<std::endl;
1318 // Use VBO for mesh (this just would set this for ever buffer)
1319 // This will lead to infinite memory usage because or irrlicht.
1320 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
1323 NOTE: If that is enabled, some kind of a queue to the main
1324 thread should be made which would call irrlicht to delete
1325 the hardware buffer and then delete the mesh
1331 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1334 /*scene::SMesh* makeMapBlockMesh(
1336 NodeModMap &temp_mods,
1337 VoxelManipulator &vmanip,
1338 v3s16 blockpos_nodes)
1343 void MapBlock::updateMesh(u32 daynight_ratio)
1347 DEBUG: If mesh has been generated, don't generate it again
1350 JMutexAutoLock meshlock(mesh_mutex);
1357 Avoid interlocks by copying m_temp_mods
1359 NodeModMap temp_mods;
1360 copyTempMods(temp_mods);
1362 v3s16 blockpos_nodes = getPosRelative();
1364 VoxelManipulator vmanip;
1365 // Allocate this block + borders
1366 vmanip.addArea(VoxelArea(blockpos_nodes-v3s16(1,1,1),
1367 blockpos_nodes+v3s16(1,1,1)*MAP_BLOCKSIZE));
1370 // Copy borders from map
1372 for(s16 x=-1; x<=MAP_BLOCKSIZE; x++)
1373 for(s16 y=-1; y<=MAP_BLOCKSIZE; y++)
1374 for(s16 z=-1; z<=MAP_BLOCKSIZE; z+=MAP_BLOCKSIZE+1)
1377 vmanip.setNodeNoRef(blockpos_nodes+p, getNodeParentNoEx(p));
1380 for(s16 x=-1; x<=MAP_BLOCKSIZE; x++)
1381 for(s16 y=-1; y<=MAP_BLOCKSIZE; y+=MAP_BLOCKSIZE+1)
1382 for(s16 z=-1; z<=MAP_BLOCKSIZE; z++)
1385 vmanip.setNodeNoRef(blockpos_nodes+p, getNodeParentNoEx(p));
1388 for(s16 x=-1; x<=MAP_BLOCKSIZE; x+=MAP_BLOCKSIZE+1)
1389 for(s16 y=-1; y<=MAP_BLOCKSIZE; y++)
1390 for(s16 z=-1; z<=MAP_BLOCKSIZE; z++)
1393 vmanip.setNodeNoRef(blockpos_nodes+p, getNodeParentNoEx(p));
1396 scene::SMesh *mesh_new = makeMapBlockMesh(
1407 replaceMesh(mesh_new);
1412 void MapBlock::replaceMesh(scene::SMesh *mesh_new)
1416 //scene::SMesh *mesh_old = mesh[daynight_i];
1417 //mesh[daynight_i] = mesh_new;
1419 scene::SMesh *mesh_old = mesh;
1421 setMeshExpired(false);
1423 if(mesh_old != NULL)
1425 // Remove hardware buffers of meshbuffers of mesh
1426 // NOTE: No way, this runs in a different thread and everything
1427 /*u32 c = mesh_old->getMeshBufferCount();
1428 for(u32 i=0; i<c; i++)
1430 IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1433 /*dstream<<"mesh_old->getReferenceCount()="
1434 <<mesh_old->getReferenceCount()<<std::endl;
1435 u32 c = mesh_old->getMeshBufferCount();
1436 for(u32 i=0; i<c; i++)
1438 scene::IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1439 dstream<<"buf->getReferenceCount()="
1440 <<buf->getReferenceCount()<<std::endl;
1449 mesh_mutex.Unlock();
1455 Propagates sunlight down through the block.
1456 Doesn't modify nodes that are not affected by sunlight.
1458 Returns false if sunlight at bottom block is invalid
1459 Returns true if bottom block doesn't exist.
1461 If there is a block above, continues from it.
1462 If there is no block above, assumes there is sunlight, unless
1463 is_underground is set or highest node is water.
1465 At the moment, all sunlighted nodes are added to light_sources.
1466 - SUGG: This could be optimized
1468 Turns sunglighted mud into grass.
1470 if remove_light==true, sets non-sunlighted nodes black.
1472 if black_air_left!=NULL, it is set to true if non-sunlighted
1473 air is left in block.
1475 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources,
1476 bool remove_light, bool *black_air_left,
1479 // Whether the sunlight at the top of the bottom block is valid
1480 bool block_below_is_valid = true;
1482 v3s16 pos_relative = getPosRelative();
1484 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
1486 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
1489 bool no_sunlight = false;
1490 bool no_top_block = false;
1491 // Check if node above block has sunlight
1493 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1494 if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1499 catch(InvalidPositionException &e)
1501 no_top_block = true;
1503 // NOTE: This makes over-ground roofed places sunlighted
1504 // Assume sunlight, unless is_underground==true
1511 MapNode n = getNode(v3s16(x, MAP_BLOCKSIZE-1, z));
1512 if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
1517 // NOTE: As of now, this just would make everything dark.
1519 //no_sunlight = true;
1522 #if 0 // Doesn't work; nothing gets light.
1523 bool no_sunlight = true;
1524 bool no_top_block = false;
1525 // Check if node above block has sunlight
1527 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1528 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
1530 no_sunlight = false;
1533 catch(InvalidPositionException &e)
1535 no_top_block = true;
1539 /*std::cout<<"("<<x<<","<<z<<"): "
1540 <<"no_top_block="<<no_top_block
1541 <<", is_underground="<<is_underground
1542 <<", no_sunlight="<<no_sunlight
1545 s16 y = MAP_BLOCKSIZE-1;
1547 // This makes difference to diminishing in water.
1548 bool stopped_to_solid_object = false;
1550 u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
1555 MapNode &n = getNodeRef(pos);
1557 if(current_light == 0)
1561 else if(current_light == LIGHT_SUN && n.sunlight_propagates())
1563 // Do nothing: Sunlight is continued
1565 else if(n.light_propagates() == false)
1569 bool upper_is_air = false;
1572 if(getNodeParent(pos+v3s16(0,1,0)).d == CONTENT_AIR)
1573 upper_is_air = true;
1575 catch(InvalidPositionException &e)
1578 // Turn mud into grass
1579 if(upper_is_air && n.d == CONTENT_MUD
1580 && current_light == LIGHT_SUN)
1582 n.d = CONTENT_GRASS;
1586 // A solid object is on the way.
1587 stopped_to_solid_object = true;
1595 current_light = diminish_light(current_light);
1598 u8 old_light = n.getLight(LIGHTBANK_DAY);
1600 if(current_light > old_light || remove_light)
1602 n.setLight(LIGHTBANK_DAY, current_light);
1605 if(diminish_light(current_light) != 0)
1607 light_sources.insert(pos_relative + pos, true);
1610 if(current_light == 0 && stopped_to_solid_object)
1614 *black_air_left = true;
1619 // Whether or not the block below should see LIGHT_SUN
1620 bool sunlight_should_go_down = (current_light == LIGHT_SUN);
1623 If the block below hasn't already been marked invalid:
1625 Check if the node below the block has proper sunlight at top.
1626 If not, the block below is invalid.
1628 Ignore non-transparent nodes as they always have no light
1632 if(block_below_is_valid)
1634 MapNode n = getNodeParent(v3s16(x, -1, z));
1635 if(n.light_propagates())
1637 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN
1638 && sunlight_should_go_down == false)
1639 block_below_is_valid = false;
1640 else if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN
1641 && sunlight_should_go_down == true)
1642 block_below_is_valid = false;
1646 catch(InvalidPositionException &e)
1648 /*std::cout<<"InvalidBlockException for bottom block node"
1650 // Just no block below, no need to panic.
1655 return block_below_is_valid;
1659 void MapBlock::copyTo(VoxelManipulator &dst)
1661 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
1662 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
1664 // Copy from data to VoxelManipulator
1665 dst.copyFrom(data, data_area, v3s16(0,0,0),
1666 getPosRelative(), data_size);
1669 void MapBlock::copyFrom(VoxelManipulator &dst)
1671 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
1672 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
1674 // Copy from VoxelManipulator to data
1675 dst.copyTo(data, data_area, v3s16(0,0,0),
1676 getPosRelative(), data_size);
1679 void MapBlock::stepObjects(float dtime, bool server, u32 daynight_ratio)
1684 m_objects.step(dtime, server, daynight_ratio);
1687 Spawn some objects at random.
1689 Use dayNightDiffed() to approximate being near ground level
1691 if(m_spawn_timer < -999)
1695 if(dayNightDiffed() == true && getObjectCount() == 0)
1697 m_spawn_timer -= dtime;
1698 if(m_spawn_timer <= 0.0)
1700 m_spawn_timer += myrand() % 300;
1703 (myrand()%(MAP_BLOCKSIZE-1))+0,
1704 (myrand()%(MAP_BLOCKSIZE-1))+0
1707 s16 y = getGroundLevel(p2d);
1711 v3s16 p(p2d.X, y+1, p2d.Y);
1713 if(getNode(p).d == CONTENT_AIR
1714 && getNode(p).getLightBlend(daynight_ratio) <= 11)
1716 RatObject *obj = new RatObject(NULL, -1, intToFloat(p, BS));
1727 void MapBlock::updateDayNightDiff()
1731 m_day_night_differs = false;
1735 bool differs = false;
1738 Check if any lighting value differs
1740 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1742 MapNode &n = data[i];
1743 if(n.getLight(LIGHTBANK_DAY) != n.getLight(LIGHTBANK_NIGHT))
1751 If some lighting values differ, check if the whole thing is
1752 just air. If it is, differ = false
1756 bool only_air = true;
1757 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1759 MapNode &n = data[i];
1760 if(n.d != CONTENT_AIR)
1770 // Set member variable
1771 m_day_night_differs = differs;
1774 s16 MapBlock::getGroundLevel(v2s16 p2d)
1780 s16 y = MAP_BLOCKSIZE-1;
1783 //if(is_ground_content(getNodeRef(p2d.X, y, p2d.Y).d))
1784 if(content_features(getNodeRef(p2d.X, y, p2d.Y).d).walkable)
1786 if(y == MAP_BLOCKSIZE-1)
1794 catch(InvalidPositionException &e)
1804 void MapBlock::serialize(std::ostream &os, u8 version)
1806 if(!ser_ver_supported(version))
1807 throw VersionMismatchException("ERROR: MapBlock format not supported");
1811 throw SerializationError("ERROR: Not writing dummy block.");
1814 // These have no compression
1815 if(version <= 3 || version == 5 || version == 6)
1817 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1819 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
1820 SharedBuffer<u8> dest(buflen);
1822 dest[0] = is_underground;
1823 for(u32 i=0; i<nodecount; i++)
1825 u32 s = 1 + i * MapNode::serializedLength(version);
1826 data[i].serialize(&dest[s], version);
1829 os.write((char*)*dest, dest.getSize());
1831 else if(version <= 10)
1835 Compress the materials and the params separately.
1839 os.write((char*)&is_underground, 1);
1841 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1843 // Get and compress materials
1844 SharedBuffer<u8> materialdata(nodecount);
1845 for(u32 i=0; i<nodecount; i++)
1847 materialdata[i] = data[i].d;
1849 compress(materialdata, os, version);
1851 // Get and compress lights
1852 SharedBuffer<u8> lightdata(nodecount);
1853 for(u32 i=0; i<nodecount; i++)
1855 lightdata[i] = data[i].param;
1857 compress(lightdata, os, version);
1861 // Get and compress param2
1862 SharedBuffer<u8> param2data(nodecount);
1863 for(u32 i=0; i<nodecount; i++)
1865 param2data[i] = data[i].param2;
1867 compress(param2data, os, version);
1870 // All other versions (newest)
1877 if(m_day_night_differs)
1879 if(m_lighting_expired)
1881 /*if(m_not_fully_generated)
1883 os.write((char*)&flags, 1);
1885 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1891 SharedBuffer<u8> databuf(nodecount*3);
1894 for(u32 i=0; i<nodecount; i++)
1896 databuf[i] = data[i].d;
1900 for(u32 i=0; i<nodecount; i++)
1902 databuf[i+nodecount] = data[i].param;
1906 for(u32 i=0; i<nodecount; i++)
1908 databuf[i+nodecount*2] = data[i].param2;
1912 Compress data to output stream
1915 compress(databuf, os, version);
1919 void MapBlock::deSerialize(std::istream &is, u8 version)
1921 if(!ser_ver_supported(version))
1922 throw VersionMismatchException("ERROR: MapBlock format not supported");
1924 // These have no compression
1925 if(version <= 3 || version == 5 || version == 6)
1927 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1930 if(is.gcount() != 1)
1931 throw SerializationError
1932 ("MapBlock::deSerialize: no enough input data");
1933 is_underground = tmp;
1934 for(u32 i=0; i<nodecount; i++)
1936 s32 len = MapNode::serializedLength(version);
1937 SharedBuffer<u8> d(len);
1938 is.read((char*)*d, len);
1939 if(is.gcount() != len)
1940 throw SerializationError
1941 ("MapBlock::deSerialize: no enough input data");
1942 data[i].deSerialize(*d, version);
1945 else if(version <= 10)
1947 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1950 is.read((char*)&t8, 1);
1951 is_underground = t8;
1954 // Uncompress and set material data
1955 std::ostringstream os(std::ios_base::binary);
1956 decompress(is, os, version);
1957 std::string s = os.str();
1958 if(s.size() != nodecount)
1959 throw SerializationError
1960 ("MapBlock::deSerialize: invalid format");
1961 for(u32 i=0; i<s.size(); i++)
1967 // Uncompress and set param data
1968 std::ostringstream os(std::ios_base::binary);
1969 decompress(is, os, version);
1970 std::string s = os.str();
1971 if(s.size() != nodecount)
1972 throw SerializationError
1973 ("MapBlock::deSerialize: invalid format");
1974 for(u32 i=0; i<s.size(); i++)
1976 data[i].param = s[i];
1982 // Uncompress and set param2 data
1983 std::ostringstream os(std::ios_base::binary);
1984 decompress(is, os, version);
1985 std::string s = os.str();
1986 if(s.size() != nodecount)
1987 throw SerializationError
1988 ("MapBlock::deSerialize: invalid format");
1989 for(u32 i=0; i<s.size(); i++)
1991 data[i].param2 = s[i];
1995 // All other versions (newest)
1998 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2001 is.read((char*)&flags, 1);
2002 is_underground = (flags & 0x01) ? true : false;
2003 m_day_night_differs = (flags & 0x02) ? true : false;
2004 m_lighting_expired = (flags & 0x04) ? true : false;
2005 //m_not_fully_generated = (flags & 0x08) ? true : false;
2008 std::ostringstream os(std::ios_base::binary);
2009 decompress(is, os, version);
2010 std::string s = os.str();
2011 if(s.size() != nodecount*3)
2012 throw SerializationError
2013 ("MapBlock::deSerialize: invalid format");
2016 for(u32 i=0; i<nodecount; i++)
2021 for(u32 i=0; i<nodecount; i++)
2023 data[i].param = s[i+nodecount];
2026 for(u32 i=0; i<nodecount; i++)
2028 data[i].param2 = s[i+nodecount*2];
2033 Translate nodes as specified in the translate_to fields of
2036 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
2038 MapNode &n = data[i];
2040 MapNode *translate_to = content_features(n.d).translate_to;
2043 dstream<<"MapBlock: WARNING: Translating node "<<n.d<<" to "
2044 <<translate_to->d<<std::endl;