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 video::SColor c(255,l,l,l);
1188 for(u32 j=0; j<6; j++)
1190 video::S3DVertex vertices[4] =
1192 /*video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, 0,1),
1193 video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, 1,1),
1194 video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c, 1,0),
1195 video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c, 0,0),*/
1196 video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c,
1197 pa_leaves1.x0(), pa_leaves1.y1()),
1198 video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c,
1199 pa_leaves1.x1(), pa_leaves1.y1()),
1200 video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c,
1201 pa_leaves1.x1(), pa_leaves1.y0()),
1202 video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c,
1203 pa_leaves1.x0(), pa_leaves1.y0()),
1208 for(u16 i=0; i<4; i++)
1209 vertices[i].Pos.rotateXZBy(0);
1213 for(u16 i=0; i<4; i++)
1214 vertices[i].Pos.rotateXZBy(180);
1218 for(u16 i=0; i<4; i++)
1219 vertices[i].Pos.rotateXZBy(-90);
1223 for(u16 i=0; i<4; i++)
1224 vertices[i].Pos.rotateXZBy(90);
1228 for(u16 i=0; i<4; i++)
1229 vertices[i].Pos.rotateYZBy(-90);
1233 for(u16 i=0; i<4; i++)
1234 vertices[i].Pos.rotateYZBy(90);
1237 for(u16 i=0; i<4; i++)
1239 vertices[i].Pos += intToFloat(p + getPosRelative());
1242 u16 indices[] = {0,1,2,2,3,0};
1243 // Add to mesh collector
1244 collector.append(material_leaves1, vertices, 4, indices, 6);
1250 Add stuff from collector to mesh
1253 scene::SMesh *mesh_new = NULL;
1254 mesh_new = new scene::SMesh();
1256 collector.fillMesh(mesh_new);
1259 Do some stuff to the mesh
1262 mesh_new->recalculateBoundingBox();
1265 Delete new mesh if it is empty
1268 if(mesh_new->getMeshBufferCount() == 0)
1277 // Usually 1-700 faces and 1-7 materials
1278 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
1279 <<"and uses "<<mesh_new->getMeshBufferCount()
1280 <<" materials (meshbuffers)"<<std::endl;
1283 // Use VBO for mesh (this just would set this for ever buffer)
1284 // This will lead to infinite memory usage because or irrlicht.
1285 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
1288 NOTE: If that is enabled, some kind of a queue to the main
1289 thread should be made which would call irrlicht to delete
1290 the hardware buffer and then delete the mesh
1300 //scene::SMesh *mesh_old = mesh[daynight_i];
1301 //mesh[daynight_i] = mesh_new;
1303 scene::SMesh *mesh_old = mesh;
1305 setMeshExpired(false);
1307 if(mesh_old != NULL)
1309 // Remove hardware buffers of meshbuffers of mesh
1310 // NOTE: No way, this runs in a different thread and everything
1311 /*u32 c = mesh_old->getMeshBufferCount();
1312 for(u32 i=0; i<c; i++)
1314 IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1317 /*dstream<<"mesh_old->getReferenceCount()="
1318 <<mesh_old->getReferenceCount()<<std::endl;
1319 u32 c = mesh_old->getMeshBufferCount();
1320 for(u32 i=0; i<c; i++)
1322 scene::IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1323 dstream<<"buf->getReferenceCount()="
1324 <<buf->getReferenceCount()<<std::endl;
1333 mesh_mutex.Unlock();
1335 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1338 /*void MapBlock::updateMeshes(s32 first_i)
1340 assert(first_i >= 0 && first_i <= DAYNIGHT_CACHE_COUNT);
1341 updateMesh(first_i);
1342 for(s32 i=0; i<DAYNIGHT_CACHE_COUNT; i++)
1353 Propagates sunlight down through the block.
1354 Doesn't modify nodes that are not affected by sunlight.
1356 Returns false if sunlight at bottom block is invalid
1357 Returns true if bottom block doesn't exist.
1359 If there is a block above, continues from it.
1360 If there is no block above, assumes there is sunlight, unless
1361 is_underground is set or highest node is water.
1363 At the moment, all sunlighted nodes are added to light_sources.
1364 - SUGG: This could be optimized
1366 Turns sunglighted mud into grass.
1368 if remove_light==true, sets non-sunlighted nodes black.
1370 if black_air_left!=NULL, it is set to true if non-sunlighted
1371 air is left in block.
1373 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources,
1374 bool remove_light, bool *black_air_left,
1377 // Whether the sunlight at the top of the bottom block is valid
1378 bool block_below_is_valid = true;
1380 v3s16 pos_relative = getPosRelative();
1382 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
1384 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
1387 bool no_sunlight = false;
1388 bool no_top_block = false;
1389 // Check if node above block has sunlight
1391 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1392 if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1397 catch(InvalidPositionException &e)
1399 no_top_block = true;
1401 // NOTE: This makes over-ground roofed places sunlighted
1402 // Assume sunlight, unless is_underground==true
1409 MapNode n = getNode(v3s16(x, MAP_BLOCKSIZE-1, z));
1410 if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
1415 // NOTE: As of now, this just would make everything dark.
1417 //no_sunlight = true;
1420 #if 0 // Doesn't work; nothing gets light.
1421 bool no_sunlight = true;
1422 bool no_top_block = false;
1423 // Check if node above block has sunlight
1425 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1426 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
1428 no_sunlight = false;
1431 catch(InvalidPositionException &e)
1433 no_top_block = true;
1437 /*std::cout<<"("<<x<<","<<z<<"): "
1438 <<"no_top_block="<<no_top_block
1439 <<", is_underground="<<is_underground
1440 <<", no_sunlight="<<no_sunlight
1443 s16 y = MAP_BLOCKSIZE-1;
1445 // This makes difference to diminishing in water.
1446 bool stopped_to_solid_object = false;
1448 u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
1453 MapNode &n = getNodeRef(pos);
1455 if(current_light == 0)
1459 else if(current_light == LIGHT_SUN && n.sunlight_propagates())
1461 // Do nothing: Sunlight is continued
1463 else if(n.light_propagates() == false)
1467 bool upper_is_air = false;
1470 if(getNodeParent(pos+v3s16(0,1,0)).d == CONTENT_AIR)
1471 upper_is_air = true;
1473 catch(InvalidPositionException &e)
1476 // Turn mud into grass
1477 if(upper_is_air && n.d == CONTENT_MUD
1478 && current_light == LIGHT_SUN)
1480 n.d = CONTENT_GRASS;
1484 // A solid object is on the way.
1485 stopped_to_solid_object = true;
1493 current_light = diminish_light(current_light);
1496 u8 old_light = n.getLight(LIGHTBANK_DAY);
1498 if(current_light > old_light || remove_light)
1500 n.setLight(LIGHTBANK_DAY, current_light);
1503 if(diminish_light(current_light) != 0)
1505 light_sources.insert(pos_relative + pos, true);
1508 if(current_light == 0 && stopped_to_solid_object)
1512 *black_air_left = true;
1517 // Whether or not the block below should see LIGHT_SUN
1518 bool sunlight_should_go_down = (current_light == LIGHT_SUN);
1521 If the block below hasn't already been marked invalid:
1523 Check if the node below the block has proper sunlight at top.
1524 If not, the block below is invalid.
1526 Ignore non-transparent nodes as they always have no light
1530 if(block_below_is_valid)
1532 MapNode n = getNodeParent(v3s16(x, -1, z));
1533 if(n.light_propagates())
1535 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN
1536 && sunlight_should_go_down == false)
1537 block_below_is_valid = false;
1538 else if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN
1539 && sunlight_should_go_down == true)
1540 block_below_is_valid = false;
1544 catch(InvalidPositionException &e)
1546 /*std::cout<<"InvalidBlockException for bottom block node"
1548 // Just no block below, no need to panic.
1553 return block_below_is_valid;
1556 void MapBlock::copyTo(VoxelManipulator &dst)
1558 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
1559 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
1561 // Copy from data to VoxelManipulator
1562 dst.copyFrom(data, data_area, v3s16(0,0,0),
1563 getPosRelative(), data_size);
1566 void MapBlock::copyFrom(VoxelManipulator &dst)
1568 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
1569 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
1571 // Copy from VoxelManipulator to data
1572 dst.copyTo(data, data_area, v3s16(0,0,0),
1573 getPosRelative(), data_size);
1576 void MapBlock::stepObjects(float dtime, bool server, u32 daynight_ratio)
1581 m_objects.step(dtime, server, daynight_ratio);
1584 Spawn some objects at random.
1586 Use dayNightDiffed() to approximate being near ground level
1588 if(m_spawn_timer < -999)
1592 if(dayNightDiffed() == true && getObjectCount() == 0)
1594 m_spawn_timer -= dtime;
1595 if(m_spawn_timer <= 0.0)
1597 m_spawn_timer += myrand() % 300;
1600 (myrand()%(MAP_BLOCKSIZE-1))+0,
1601 (myrand()%(MAP_BLOCKSIZE-1))+0
1604 s16 y = getGroundLevel(p2d);
1608 v3s16 p(p2d.X, y+1, p2d.Y);
1610 if(getNode(p).d == CONTENT_AIR
1611 && getNode(p).getLightBlend(daynight_ratio) <= 11)
1613 RatObject *obj = new RatObject(NULL, -1, intToFloat(p));
1624 void MapBlock::updateDayNightDiff()
1628 m_day_night_differs = false;
1632 bool differs = false;
1635 Check if any lighting value differs
1637 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1639 MapNode &n = data[i];
1640 if(n.getLight(LIGHTBANK_DAY) != n.getLight(LIGHTBANK_NIGHT))
1648 If some lighting values differ, check if the whole thing is
1649 just air. If it is, differ = false
1653 bool only_air = true;
1654 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1656 MapNode &n = data[i];
1657 if(n.d != CONTENT_AIR)
1667 // Set member variable
1668 m_day_night_differs = differs;
1671 s16 MapBlock::getGroundLevel(v2s16 p2d)
1677 s16 y = MAP_BLOCKSIZE-1;
1680 //if(is_ground_content(getNodeRef(p2d.X, y, p2d.Y).d))
1681 if(content_features(getNodeRef(p2d.X, y, p2d.Y).d).walkable)
1683 if(y == MAP_BLOCKSIZE-1)
1691 catch(InvalidPositionException &e)
1701 void MapBlock::serialize(std::ostream &os, u8 version)
1703 if(!ser_ver_supported(version))
1704 throw VersionMismatchException("ERROR: MapBlock format not supported");
1708 throw SerializationError("ERROR: Not writing dummy block.");
1711 // These have no compression
1712 if(version <= 3 || version == 5 || version == 6)
1714 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1716 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
1717 SharedBuffer<u8> dest(buflen);
1719 dest[0] = is_underground;
1720 for(u32 i=0; i<nodecount; i++)
1722 u32 s = 1 + i * MapNode::serializedLength(version);
1723 data[i].serialize(&dest[s], version);
1726 os.write((char*)*dest, dest.getSize());
1728 else if(version <= 10)
1732 Compress the materials and the params separately.
1736 os.write((char*)&is_underground, 1);
1738 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1740 // Get and compress materials
1741 SharedBuffer<u8> materialdata(nodecount);
1742 for(u32 i=0; i<nodecount; i++)
1744 materialdata[i] = data[i].d;
1746 compress(materialdata, os, version);
1748 // Get and compress lights
1749 SharedBuffer<u8> lightdata(nodecount);
1750 for(u32 i=0; i<nodecount; i++)
1752 lightdata[i] = data[i].param;
1754 compress(lightdata, os, version);
1758 // Get and compress param2
1759 SharedBuffer<u8> param2data(nodecount);
1760 for(u32 i=0; i<nodecount; i++)
1762 param2data[i] = data[i].param2;
1764 compress(param2data, os, version);
1767 // All other versions (newest)
1774 if(m_day_night_differs)
1776 if(m_lighting_expired)
1778 os.write((char*)&flags, 1);
1780 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1786 SharedBuffer<u8> databuf(nodecount*3);
1789 for(u32 i=0; i<nodecount; i++)
1791 databuf[i] = data[i].d;
1795 for(u32 i=0; i<nodecount; i++)
1797 databuf[i+nodecount] = data[i].param;
1801 for(u32 i=0; i<nodecount; i++)
1803 databuf[i+nodecount*2] = data[i].param2;
1807 Compress data to output stream
1810 compress(databuf, os, version);
1814 void MapBlock::deSerialize(std::istream &is, u8 version)
1816 if(!ser_ver_supported(version))
1817 throw VersionMismatchException("ERROR: MapBlock format not supported");
1819 // These have no compression
1820 if(version <= 3 || version == 5 || version == 6)
1822 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1825 if(is.gcount() != 1)
1826 throw SerializationError
1827 ("MapBlock::deSerialize: no enough input data");
1828 is_underground = tmp;
1829 for(u32 i=0; i<nodecount; i++)
1831 s32 len = MapNode::serializedLength(version);
1832 SharedBuffer<u8> d(len);
1833 is.read((char*)*d, len);
1834 if(is.gcount() != len)
1835 throw SerializationError
1836 ("MapBlock::deSerialize: no enough input data");
1837 data[i].deSerialize(*d, version);
1840 else if(version <= 10)
1842 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1845 is.read((char*)&t8, 1);
1846 is_underground = t8;
1849 // Uncompress and set material data
1850 std::ostringstream os(std::ios_base::binary);
1851 decompress(is, os, version);
1852 std::string s = os.str();
1853 if(s.size() != nodecount)
1854 throw SerializationError
1855 ("MapBlock::deSerialize: invalid format");
1856 for(u32 i=0; i<s.size(); i++)
1862 // Uncompress and set param data
1863 std::ostringstream os(std::ios_base::binary);
1864 decompress(is, os, version);
1865 std::string s = os.str();
1866 if(s.size() != nodecount)
1867 throw SerializationError
1868 ("MapBlock::deSerialize: invalid format");
1869 for(u32 i=0; i<s.size(); i++)
1871 data[i].param = s[i];
1877 // Uncompress and set param2 data
1878 std::ostringstream os(std::ios_base::binary);
1879 decompress(is, os, version);
1880 std::string s = os.str();
1881 if(s.size() != nodecount)
1882 throw SerializationError
1883 ("MapBlock::deSerialize: invalid format");
1884 for(u32 i=0; i<s.size(); i++)
1886 data[i].param2 = s[i];
1890 // All other versions (newest)
1893 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1896 is.read((char*)&flags, 1);
1897 is_underground = (flags & 1) ? true : false;
1898 m_day_night_differs = (flags & 2) ? true : false;
1899 m_lighting_expired = (flags & 3) ? true : false;
1902 std::ostringstream os(std::ios_base::binary);
1903 decompress(is, os, version);
1904 std::string s = os.str();
1905 if(s.size() != nodecount*3)
1906 throw SerializationError
1907 ("MapBlock::deSerialize: invalid format");
1910 for(u32 i=0; i<nodecount; i++)
1915 for(u32 i=0; i<nodecount; i++)
1917 data[i].param = s[i+nodecount];
1920 for(u32 i=0; i<nodecount; i++)
1922 data[i].param2 = s[i+nodecount*2];
1927 Translate nodes as specified in the translate_to fields of
1930 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1932 MapNode &n = data[i];
1934 MapNode *translate_to = content_features(n.d).translate_to;
1937 dstream<<"MapBlock: WARNING: Translating node "<<n.d<<" to "
1938 <<translate_to->d<<std::endl;