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.
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 u8 MapBlock::getFaceLight(u32 daynight_ratio, MapNode n, MapNode n2,
150 u8 l1 = n.getLightBlend(daynight_ratio);
151 u8 l2 = n2.getLightBlend(daynight_ratio);
157 // Make some nice difference to different sides
159 /*if(face_dir.X == 1 || face_dir.Z == 1 || face_dir.Y == -1)
160 light = diminish_light(diminish_light(light));
161 else if(face_dir.X == -1 || face_dir.Z == -1)
162 light = diminish_light(light);*/
164 if(face_dir.X == 1 || face_dir.X == -1 || face_dir.Y == -1)
165 light = diminish_light(diminish_light(light));
166 else if(face_dir.Z == 1 || face_dir.Z == -1)
167 light = diminish_light(light);
171 catch(InvalidPositionException &e)
179 void MapBlock::makeFastFace(TileSpec tile, u8 light, v3f p,
180 v3s16 dir, v3f scale, v3f posRelative_f,
181 core::array<FastFace> &dest)
185 // Position is at the center of the cube.
190 // If looking towards z+, this is the face that is behind
191 // the center point, facing towards z+.
192 vertex_pos[0] = v3f( BS/2,-BS/2,BS/2);
193 vertex_pos[1] = v3f(-BS/2,-BS/2,BS/2);
194 vertex_pos[2] = v3f(-BS/2, BS/2,BS/2);
195 vertex_pos[3] = v3f( BS/2, BS/2,BS/2);
197 if(dir == v3s16(0,0,1))
199 for(u16 i=0; i<4; i++)
200 vertex_pos[i].rotateXZBy(0);
202 else if(dir == v3s16(0,0,-1))
204 for(u16 i=0; i<4; i++)
205 vertex_pos[i].rotateXZBy(180);
207 else if(dir == v3s16(1,0,0))
209 for(u16 i=0; i<4; i++)
210 vertex_pos[i].rotateXZBy(-90);
212 else if(dir == v3s16(-1,0,0))
214 for(u16 i=0; i<4; i++)
215 vertex_pos[i].rotateXZBy(90);
217 else if(dir == v3s16(0,1,0))
219 for(u16 i=0; i<4; i++)
220 vertex_pos[i].rotateYZBy(-90);
222 else if(dir == v3s16(0,-1,0))
224 for(u16 i=0; i<4; i++)
225 vertex_pos[i].rotateYZBy(90);
228 for(u16 i=0; i<4; i++)
230 vertex_pos[i].X *= scale.X;
231 vertex_pos[i].Y *= scale.Y;
232 vertex_pos[i].Z *= scale.Z;
233 vertex_pos[i] += pos + posRelative_f;
237 if (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X;
238 else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
239 else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
241 v3f zerovector = v3f(0,0,0);
243 //u8 li = decode_light(light);
248 if(tile.id == TILE_WATER)
253 video::SColor c = video::SColor(alpha,li,li,li);
255 face.vertices[0] = video::S3DVertex(vertex_pos[0], zerovector, c,
256 core::vector2d<f32>(0,1));
257 face.vertices[1] = video::S3DVertex(vertex_pos[1], zerovector, c,
258 core::vector2d<f32>(abs_scale,1));
259 face.vertices[2] = video::S3DVertex(vertex_pos[2], zerovector, c,
260 core::vector2d<f32>(abs_scale,0));
261 face.vertices[3] = video::S3DVertex(vertex_pos[3], zerovector, c,
262 core::vector2d<f32>(0,0));
266 //f->tile = TILE_STONE;
268 dest.push_back(face);
273 Gets node tile from any place relative to block.
274 Returns TILE_NODE if doesn't exist or should not be drawn.
276 TileSpec MapBlock::getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir)
282 spec.id = TILE_STONE;
286 spec.feature = TILEFEAT_NONE;
287 //spec.id = TILE_STONE;
288 spec.id = mn.getTile(face_dir);
291 Check temporary modifications on this node
293 core::map<v3s16, NodeMod>::Node *n;
294 n = m_temp_mods.find(p);
299 struct NodeMod mod = n->getValue();
300 if(mod.type == NODEMOD_CHANGECONTENT)
302 spec.id = content_tile(mod.param, face_dir);
304 if(mod.type == NODEMOD_CRACK)
306 spec.feature = TILEFEAT_CRACK;
307 spec.param.crack.progression = mod.param;
314 u8 MapBlock::getNodeContent(v3s16 p, MapNode mn)
317 Check temporary modifications on this node
319 core::map<v3s16, NodeMod>::Node *n;
320 n = m_temp_mods.find(p);
325 struct NodeMod mod = n->getValue();
326 if(mod.type == NODEMOD_CHANGECONTENT)
331 if(mod.type == NODEMOD_CRACK)
334 Content doesn't change.
336 face_contents works just like it should, because
337 there should not be faces between differently cracked
340 If a semi-transparent node is cracked in front an
341 another one, it really doesn't matter whether there
342 is a cracked face drawn in between or not.
352 translate_dir: unit vector with only one of x, y or z
353 face_dir: unit vector with only one of x, y or z
355 void MapBlock::updateFastFaceRow(
364 core::array<FastFace> &dest)
368 u16 continuous_tiles_count = 0;
370 MapNode n0 = getNodeParentNoEx(p);
371 MapNode n1 = getNodeParentNoEx(p + face_dir);
373 u8 light = getFaceLight(daynight_ratio, n0, n1, face_dir);
375 TileSpec tile0 = getNodeTile(n0, p, face_dir);
376 TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir);
378 for(u16 j=0; j<length; j++)
380 bool next_is_different = true;
391 p_next = p + translate_dir;
392 n0_next = getNodeParentNoEx(p_next);
393 n1_next = getNodeParentNoEx(p_next + face_dir);
394 tile0_next = getNodeTile(n0_next, p_next, face_dir);
395 tile1_next = getNodeTile(n1_next, p_next + face_dir, -face_dir);
396 light_next = getFaceLight(daynight_ratio, n0_next, n1_next, face_dir);
398 if(tile0_next == tile0
399 && tile1_next == tile1
400 && light_next == light)
402 next_is_different = false;
406 continuous_tiles_count++;
408 if(next_is_different)
411 Create a face if there should be one
413 //u8 mf = face_contents(tile0, tile1);
415 u8 content0 = getNodeContent(p, n0);
416 u8 content1 = getNodeContent(p + face_dir, n1);
417 u8 mf = face_contents(content0, content1);
421 // Floating point conversion of the position vector
422 v3f pf(p.X, p.Y, p.Z);
423 // Center point of face (kind of)
424 v3f sp = pf - ((f32)continuous_tiles_count / 2. - 0.5) * translate_dir_f;
426 if(translate_dir.X != 0){
427 scale.X = continuous_tiles_count;
429 if(translate_dir.Y != 0){
430 scale.Y = continuous_tiles_count;
432 if(translate_dir.Z != 0){
433 scale.Z = continuous_tiles_count;
438 // If node at sp (tile0) is more solid
441 makeFastFace(tile0, decode_light(light),
443 posRelative_f, dest);
445 // If node at sp is less solid (mf == 2)
448 makeFastFace(tile1, decode_light(light),
449 sp+face_dir_f, -face_dir, scale,
450 posRelative_f, dest);
455 continuous_tiles_count = 0;
468 This is used because CMeshBuffer::append() is very slow
472 video::SMaterial material;
473 core::array<u16> indices;
474 core::array<video::S3DVertex> vertices;
481 video::SMaterial material,
482 const video::S3DVertex* const vertices,
484 const u16* const indices,
488 PreMeshBuffer *p = NULL;
489 for(u32 i=0; i<m_prebuffers.size(); i++)
491 PreMeshBuffer &pp = m_prebuffers[i];
492 if(pp.material != material)
502 pp.material = material;
503 m_prebuffers.push_back(pp);
504 p = &m_prebuffers[m_prebuffers.size()-1];
507 u32 vertex_count = p->vertices.size();
508 for(u32 i=0; i<numIndices; i++)
510 u32 j = indices[i] + vertex_count;
513 dstream<<"FIXME: Meshbuffer ran out of indices"<<std::endl;
514 // NOTE: Fix is to just add an another MeshBuffer
516 p->indices.push_back(j);
518 for(u32 i=0; i<numVertices; i++)
520 p->vertices.push_back(vertices[i]);
524 void fillMesh(scene::SMesh *mesh)
526 /*dstream<<"Filling mesh with "<<m_prebuffers.size()
527 <<" meshbuffers"<<std::endl;*/
528 for(u32 i=0; i<m_prebuffers.size(); i++)
530 PreMeshBuffer &p = m_prebuffers[i];
532 /*dstream<<"p.vertices.size()="<<p.vertices.size()
533 <<", p.indices.size()="<<p.indices.size()
538 // This is a "Standard MeshBuffer",
539 // it's a typedeffed CMeshBuffer<video::S3DVertex>
540 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
542 buf->Material = p.material;
543 //((scene::SMeshBuffer*)buf)->Material = p.material;
545 //buf->setHardwareMappingHint(scene::EHM_STATIC);
547 mesh->addMeshBuffer(buf);
551 buf->append(p.vertices.pointer(), p.vertices.size(),
552 p.indices.pointer(), p.indices.size());
557 core::array<PreMeshBuffer> m_prebuffers;
560 void MapBlock::updateMesh(u32 daynight_ratio)
564 DEBUG: If mesh has been generated, don't generate it again
567 JMutexAutoLock meshlock(mesh_mutex);
574 //TimeTaker timer1("updateMesh()", g_device);
576 core::array<FastFace> fastfaces_new;
578 v3f posRelative_f(getPosRelative().X, getPosRelative().Y,
579 getPosRelative().Z); // floating point conversion
582 We are including the faces of the trailing edges of the block.
583 This means that when something changes, the caller must
584 also update the meshes of the blocks at the leading edges.
586 NOTE: This is the slowest part of this method.
590 // Lock this, as m_temp_mods will be used directly
591 JMutexAutoLock lock(m_temp_mods_mutex);
594 Go through every y,z and get top faces in rows of x+
596 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
597 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
598 updateFastFaceRow(daynight_ratio, posRelative_f,
599 v3s16(0,y,z), MAP_BLOCKSIZE,
602 v3s16(0,1,0), //face dir
608 Go through every x,y and get right faces in rows of z+
610 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
611 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
612 updateFastFaceRow(daynight_ratio, posRelative_f,
613 v3s16(x,y,0), MAP_BLOCKSIZE,
622 Go through every y,z and get back faces in rows of x+
624 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
625 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
626 updateFastFaceRow(daynight_ratio, posRelative_f,
627 v3s16(0,y,z), MAP_BLOCKSIZE,
640 Convert FastFaces to SMesh
643 scene::SMesh *mesh_new = NULL;
645 mesh_new = new scene::SMesh();
647 if(fastfaces_new.size() > 0)
649 MeshCollector collector;
651 for(u32 i=0; i<fastfaces_new.size(); i++)
653 FastFace &f = fastfaces_new[i];
655 const u16 indices[] = {0,1,2,2,3,0};
657 if(f.tile.feature == TILEFEAT_NONE)
659 collector.append(tile_material_get(f.tile.id), f.vertices, 4,
662 else if(f.tile.feature == TILEFEAT_CRACK)
664 const char *path = tile_texture_path_get(f.tile.id);
666 u16 progression = f.tile.param.crack.progression;
668 std::string name = (std::string)path + "_cracked_"
669 + (char)('0' + progression);
671 TextureMod *mod = new CrackTextureMod(progression);
673 video::ITexture *texture = g_irrlicht->getTexture(
674 TextureSpec(name, path, mod));
676 video::SMaterial material = tile_material_get(f.tile.id);
677 material.setTexture(0, texture);
679 collector.append(material, f.vertices, 4, indices, 6);
688 collector.fillMesh(mesh_new);
690 // Use VBO for mesh (this just would set this for ever buffer)
691 // This will lead to infinite memory usage because or irrlicht.
692 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
694 /*std::cout<<"MapBlock has "<<fastfaces_new->getSize()<<" faces "
695 <<"and uses "<<mesh_new->getMeshBufferCount()
696 <<" materials (meshbuffers)"<<std::endl;*/
700 Add special graphics:
703 TODO: Optimize by using same meshbuffer for same textures
706 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
707 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
708 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
712 MapNode &n = getNodeRef(x,y,z);
714 if(n.d == CONTENT_TORCH)
716 //scene::IMeshBuffer *buf = new scene::SMeshBuffer();
717 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
718 video::SColor c(255,255,255,255);
720 video::S3DVertex vertices[4] =
722 video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c, 0,1),
723 video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c, 1,1),
724 video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, 1,0),
725 video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c, 0,0),
728 v3s16 dir = unpackDir(n.dir);
730 for(s32 i=0; i<4; i++)
732 if(dir == v3s16(1,0,0))
733 vertices[i].Pos.rotateXZBy(0);
734 if(dir == v3s16(-1,0,0))
735 vertices[i].Pos.rotateXZBy(180);
736 if(dir == v3s16(0,0,1))
737 vertices[i].Pos.rotateXZBy(90);
738 if(dir == v3s16(0,0,-1))
739 vertices[i].Pos.rotateXZBy(-90);
740 if(dir == v3s16(0,-1,0))
741 vertices[i].Pos.rotateXZBy(45);
742 if(dir == v3s16(0,1,0))
743 vertices[i].Pos.rotateXZBy(-45);
745 vertices[i].Pos += intToFloat(p + getPosRelative());
748 u16 indices[] = {0,1,2,2,3,0};
749 buf->append(vertices, 4, indices, 6);
752 buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
753 buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false);
754 buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
755 //buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
756 buf->getMaterial().MaterialType
757 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
758 if(dir == v3s16(0,-1,0))
759 buf->getMaterial().setTexture(0,
760 g_irrlicht->getTexture(porting::getDataPath("torch_on_floor.png").c_str()));
761 else if(dir == v3s16(0,1,0))
762 buf->getMaterial().setTexture(0,
763 g_irrlicht->getTexture(porting::getDataPath("torch_on_ceiling.png").c_str()));
764 // For backwards compatibility
765 else if(dir == v3s16(0,0,0))
766 buf->getMaterial().setTexture(0,
767 g_irrlicht->getTexture(porting::getDataPath("torch_on_floor.png").c_str()));
769 buf->getMaterial().setTexture(0,
770 g_irrlicht->getTexture(porting::getDataPath("torch.png").c_str()));
773 mesh_new->addMeshBuffer(buf);
779 Do some stuff to the mesh
782 mesh_new->recalculateBoundingBox();
785 Delete new mesh if it is empty
788 if(mesh_new->getMeshBufferCount() == 0)
800 //scene::SMesh *mesh_old = mesh[daynight_i];
801 //mesh[daynight_i] = mesh_new;
803 scene::SMesh *mesh_old = mesh;
805 setMeshExpired(false);
809 // Remove hardware buffers of meshbuffers of mesh
810 // NOTE: No way, this runs in a different thread and everything
811 /*u32 c = mesh_old->getMeshBufferCount();
812 for(u32 i=0; i<c; i++)
814 IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
817 /*dstream<<"mesh_old->getReferenceCount()="
818 <<mesh_old->getReferenceCount()<<std::endl;
819 u32 c = mesh_old->getMeshBufferCount();
820 for(u32 i=0; i<c; i++)
822 scene::IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
823 dstream<<"buf->getReferenceCount()="
824 <<buf->getReferenceCount()<<std::endl;
835 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
838 /*void MapBlock::updateMeshes(s32 first_i)
840 assert(first_i >= 0 && first_i <= DAYNIGHT_CACHE_COUNT);
842 for(s32 i=0; i<DAYNIGHT_CACHE_COUNT; i++)
853 Propagates sunlight down through the block.
854 Doesn't modify nodes that are not affected by sunlight.
856 Returns false if sunlight at bottom block is invalid
857 Returns true if bottom block doesn't exist.
859 If there is a block above, continues from it.
860 If there is no block above, assumes there is sunlight, unless
861 is_underground is set.
863 At the moment, all sunlighted nodes are added to light_sources.
864 - SUGG: This could be optimized
866 Turns sunglighted mud into grass.
868 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources)
870 // Whether the sunlight at the top of the bottom block is valid
871 bool block_below_is_valid = true;
873 v3s16 pos_relative = getPosRelative();
875 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
877 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
879 bool no_sunlight = false;
880 bool no_top_block = false;
881 // Check if node above block has sunlight
883 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
884 if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
889 catch(InvalidPositionException &e)
893 // NOTE: This makes over-ground roofed places sunlighted
894 // Assume sunlight, unless is_underground==true
900 // NOTE: As of now, it just would make everything dark.
902 //no_sunlight = true;
905 /*std::cout<<"("<<x<<","<<z<<"): "
906 <<"no_top_block="<<no_top_block
907 <<", is_underground="<<is_underground
908 <<", no_sunlight="<<no_sunlight
911 s16 y = MAP_BLOCKSIZE-1;
913 if(no_sunlight == false)
915 // Continue spreading sunlight downwards through transparent
921 MapNode &n = getNodeRef(pos);
923 if(n.sunlight_propagates())
925 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
927 light_sources.insert(pos_relative + pos, true);
931 // Turn mud into grass
932 if(n.d == CONTENT_MUD)
937 // Sunlight goes no further
943 bool sunlight_should_go_down = (y==-1);
945 // Fill rest with black (only transparent ones)
949 MapNode &n = getNodeRef(pos);
951 if(n.light_propagates())
953 n.setLight(LIGHTBANK_DAY, 0);
961 If the block below hasn't already been marked invalid:
963 Check if the node below the block has proper sunlight at top.
964 If not, the block below is invalid.
966 Ignore non-transparent nodes as they always have no light
970 if(block_below_is_valid)
972 MapNode n = getNodeParent(v3s16(x, -1, z));
973 if(n.light_propagates())
975 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN
976 && sunlight_should_go_down == false)
977 block_below_is_valid = false;
978 else if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN
979 && sunlight_should_go_down == true)
980 block_below_is_valid = false;
984 catch(InvalidPositionException &e)
986 /*std::cout<<"InvalidBlockException for bottom block node"
988 // Just no block below, no need to panic.
993 return block_below_is_valid;
996 void MapBlock::copyTo(VoxelManipulator &dst)
998 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
999 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
1001 dst.copyFrom(data, data_area, v3s16(0,0,0),
1002 getPosRelative(), data_size);
1005 /*void getPseudoObjects(v3f origin, f32 max_d,
1006 core::array<DistanceSortedObject> &dest)
1009 void MapBlock::stepObjects(float dtime, bool server, u32 daynight_ratio)
1014 m_objects.step(dtime, server, daynight_ratio);
1017 Spawn some objects at random.
1019 Use dayNightDiffed() to approximate being near ground level
1021 if(m_spawn_timer < -999)
1025 if(dayNightDiffed() == true && getObjectCount() == 0)
1027 m_spawn_timer -= dtime;
1028 if(m_spawn_timer <= 0.0)
1030 m_spawn_timer += myrand() % 300;
1033 (myrand()%(MAP_BLOCKSIZE-1))+0,
1034 (myrand()%(MAP_BLOCKSIZE-1))+0
1037 s16 y = getGroundLevel(p2d);
1041 v3s16 p(p2d.X, y+1, p2d.Y);
1043 if(getNode(p).d == CONTENT_AIR
1044 && getNode(p).getLightBlend(daynight_ratio) <= 11)
1046 RatObject *obj = new RatObject(NULL, -1, intToFloat(p));
1057 void MapBlock::updateDayNightDiff()
1061 m_day_night_differs = false;
1065 bool differs = false;
1068 Check if any lighting value differs
1070 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1072 MapNode &n = data[i];
1073 if(n.getLight(LIGHTBANK_DAY) != n.getLight(LIGHTBANK_NIGHT))
1081 If some lighting values differ, check if the whole thing is
1082 just air. If it is, differ = false
1086 bool only_air = true;
1087 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1089 MapNode &n = data[i];
1090 if(n.d != CONTENT_AIR)
1100 // Set member variable
1101 m_day_night_differs = differs;
1104 s16 MapBlock::getGroundLevel(v2s16 p2d)
1110 s16 y = MAP_BLOCKSIZE-1;
1113 if(is_ground_content(getNodeRef(p2d.X, y, p2d.Y).d))
1115 if(y == MAP_BLOCKSIZE-1)
1123 catch(InvalidPositionException &e)
1133 void MapBlock::serialize(std::ostream &os, u8 version)
1135 if(!ser_ver_supported(version))
1136 throw VersionMismatchException("ERROR: MapBlock format not supported");
1140 throw SerializationError("ERROR: Not writing dummy block.");
1143 // These have no compression
1144 if(version <= 3 || version == 5 || version == 6)
1146 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1148 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
1149 SharedBuffer<u8> dest(buflen);
1151 dest[0] = is_underground;
1152 for(u32 i=0; i<nodecount; i++)
1154 u32 s = 1 + i * MapNode::serializedLength(version);
1155 data[i].serialize(&dest[s], version);
1158 os.write((char*)*dest, dest.getSize());
1160 else if(version <= 10)
1164 Compress the materials and the params separately.
1168 os.write((char*)&is_underground, 1);
1170 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1172 // Get and compress materials
1173 SharedBuffer<u8> materialdata(nodecount);
1174 for(u32 i=0; i<nodecount; i++)
1176 materialdata[i] = data[i].d;
1178 compress(materialdata, os, version);
1180 // Get and compress lights
1181 SharedBuffer<u8> lightdata(nodecount);
1182 for(u32 i=0; i<nodecount; i++)
1184 lightdata[i] = data[i].param;
1186 compress(lightdata, os, version);
1190 // Get and compress pressure
1191 SharedBuffer<u8> pressuredata(nodecount);
1192 for(u32 i=0; i<nodecount; i++)
1194 pressuredata[i] = data[i].pressure;
1196 compress(pressuredata, os, version);
1199 // All other versions (newest)
1206 if(m_day_night_differs)
1208 os.write((char*)&flags, 1);
1210 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1216 SharedBuffer<u8> databuf(nodecount*3);
1219 for(u32 i=0; i<nodecount; i++)
1221 databuf[i] = data[i].d;
1225 for(u32 i=0; i<nodecount; i++)
1227 databuf[i+nodecount] = data[i].param;
1231 for(u32 i=0; i<nodecount; i++)
1233 databuf[i+nodecount*2] = data[i].pressure;
1237 Compress data to output stream
1240 compress(databuf, os, version);
1244 void MapBlock::deSerialize(std::istream &is, u8 version)
1246 if(!ser_ver_supported(version))
1247 throw VersionMismatchException("ERROR: MapBlock format not supported");
1249 // These have no compression
1250 if(version <= 3 || version == 5 || version == 6)
1252 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1255 if(is.gcount() != 1)
1256 throw SerializationError
1257 ("MapBlock::deSerialize: no enough input data");
1258 is_underground = tmp;
1259 for(u32 i=0; i<nodecount; i++)
1261 s32 len = MapNode::serializedLength(version);
1262 SharedBuffer<u8> d(len);
1263 is.read((char*)*d, len);
1264 if(is.gcount() != len)
1265 throw SerializationError
1266 ("MapBlock::deSerialize: no enough input data");
1267 data[i].deSerialize(*d, version);
1270 else if(version <= 10)
1272 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1275 is.read((char*)&t8, 1);
1276 is_underground = t8;
1279 // Uncompress and set material data
1280 std::ostringstream os(std::ios_base::binary);
1281 decompress(is, os, version);
1282 std::string s = os.str();
1283 if(s.size() != nodecount)
1284 throw SerializationError
1285 ("MapBlock::deSerialize: invalid format");
1286 for(u32 i=0; i<s.size(); i++)
1292 // Uncompress and set param data
1293 std::ostringstream os(std::ios_base::binary);
1294 decompress(is, os, version);
1295 std::string s = os.str();
1296 if(s.size() != nodecount)
1297 throw SerializationError
1298 ("MapBlock::deSerialize: invalid format");
1299 for(u32 i=0; i<s.size(); i++)
1301 data[i].param = s[i];
1307 // Uncompress and set pressure data
1308 std::ostringstream os(std::ios_base::binary);
1309 decompress(is, os, version);
1310 std::string s = os.str();
1311 if(s.size() != nodecount)
1312 throw SerializationError
1313 ("MapBlock::deSerialize: invalid format");
1314 for(u32 i=0; i<s.size(); i++)
1316 data[i].pressure = s[i];
1320 // All other versions (newest)
1323 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1326 is.read((char*)&flags, 1);
1327 is_underground = (flags & 1) ? true : false;
1328 m_day_night_differs = (flags & 2) ? true : false;
1331 std::ostringstream os(std::ios_base::binary);
1332 decompress(is, os, version);
1333 std::string s = os.str();
1334 if(s.size() != nodecount*3)
1335 throw SerializationError
1336 ("MapBlock::deSerialize: invalid format");
1339 for(u32 i=0; i<nodecount; i++)
1344 for(u32 i=0; i<nodecount; i++)
1346 data[i].param = s[i+nodecount];
1349 for(u32 i=0; i<nodecount; i++)
1351 data[i].pressure = s[i+nodecount*2];