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;
57 JMutexAutoLock lock(mesh_mutex);
71 bool MapBlock::isValidPositionParent(v3s16 p)
73 if(isValidPosition(p))
78 return m_parent->isValidPosition(getPosRelative() + p);
82 MapNode MapBlock::getNodeParent(v3s16 p)
84 if(isValidPosition(p) == false)
86 return m_parent->getNode(getPosRelative() + p);
91 throw InvalidPositionException();
92 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
96 void MapBlock::setNodeParent(v3s16 p, MapNode & n)
98 if(isValidPosition(p) == false)
100 m_parent->setNode(getPosRelative() + p, n);
105 throw InvalidPositionException();
106 data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X] = n;
110 MapNode MapBlock::getNodeParentNoEx(v3s16 p)
112 if(isValidPosition(p) == false)
115 return m_parent->getNode(getPosRelative() + p);
117 catch(InvalidPositionException &e)
119 return MapNode(CONTENT_IGNORE);
126 return MapNode(CONTENT_IGNORE);
128 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
133 Parameters must consist of air and !air.
134 Order doesn't matter.
136 If either of the nodes doesn't exist, light is 0.
139 daynight_ratio: 0...1000
141 n2: getNodeParent(p + face_dir)
142 face_dir: axis oriented unit vector from p to p2
144 u8 MapBlock::getFaceLight(u32 daynight_ratio, MapNode n, MapNode n2,
149 u8 l1 = n.getLightBlend(daynight_ratio);
150 u8 l2 = n2.getLightBlend(daynight_ratio);
156 // Make some nice difference to different sides
158 /*if(face_dir.X == 1 || face_dir.Z == 1 || face_dir.Y == -1)
159 light = diminish_light(diminish_light(light));
160 else if(face_dir.X == -1 || face_dir.Z == -1)
161 light = diminish_light(light);*/
163 if(face_dir.X == 1 || face_dir.X == -1 || face_dir.Y == -1)
164 light = diminish_light(diminish_light(light));
165 else if(face_dir.Z == 1 || face_dir.Z == -1)
166 light = diminish_light(light);
170 catch(InvalidPositionException &e)
178 void MapBlock::makeFastFace(TileSpec tile, u8 light, v3f p,
179 v3s16 dir, v3f scale, v3f posRelative_f,
180 core::array<FastFace> &dest)
184 // Position is at the center of the cube.
189 // If looking towards z+, this is the face that is behind
190 // the center point, facing towards z+.
191 vertex_pos[0] = v3f( BS/2,-BS/2,BS/2);
192 vertex_pos[1] = v3f(-BS/2,-BS/2,BS/2);
193 vertex_pos[2] = v3f(-BS/2, BS/2,BS/2);
194 vertex_pos[3] = v3f( BS/2, BS/2,BS/2);
196 if(dir == v3s16(0,0,1))
198 for(u16 i=0; i<4; i++)
199 vertex_pos[i].rotateXZBy(0);
201 else if(dir == v3s16(0,0,-1))
203 for(u16 i=0; i<4; i++)
204 vertex_pos[i].rotateXZBy(180);
206 else if(dir == v3s16(1,0,0))
208 for(u16 i=0; i<4; i++)
209 vertex_pos[i].rotateXZBy(-90);
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(0,1,0))
218 for(u16 i=0; i<4; i++)
219 vertex_pos[i].rotateYZBy(-90);
221 else if(dir == v3s16(0,-1,0))
223 for(u16 i=0; i<4; i++)
224 vertex_pos[i].rotateYZBy(90);
227 for(u16 i=0; i<4; i++)
229 vertex_pos[i].X *= scale.X;
230 vertex_pos[i].Y *= scale.Y;
231 vertex_pos[i].Z *= scale.Z;
232 vertex_pos[i] += pos + posRelative_f;
236 if (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X;
237 else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
238 else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
240 v3f zerovector = v3f(0,0,0);
242 //u8 li = decode_light(light);
247 if(tile.id == TILE_WATER)
252 video::SColor c = video::SColor(alpha,li,li,li);
254 face.vertices[0] = video::S3DVertex(vertex_pos[0], zerovector, c,
255 core::vector2d<f32>(0,1));
256 face.vertices[1] = video::S3DVertex(vertex_pos[1], zerovector, c,
257 core::vector2d<f32>(abs_scale,1));
258 face.vertices[2] = video::S3DVertex(vertex_pos[2], zerovector, c,
259 core::vector2d<f32>(abs_scale,0));
260 face.vertices[3] = video::S3DVertex(vertex_pos[3], zerovector, c,
261 core::vector2d<f32>(0,0));
265 //f->tile = TILE_STONE;
267 dest.push_back(face);
272 Gets node tile from any place relative to block.
273 Returns TILE_NODE if doesn't exist or should not be drawn.
275 TileSpec MapBlock::getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir)
281 spec.id = TILE_STONE;
285 spec.feature = TILEFEAT_NONE;
286 //spec.id = TILE_STONE;
287 spec.id = mn.getTile(face_dir);
290 Check temporary modifications on this node
292 core::map<v3s16, NodeMod>::Node *n;
293 n = m_temp_mods.find(p);
298 struct NodeMod mod = n->getValue();
299 if(mod.type == NODEMOD_CHANGECONTENT)
301 spec.id = content_tile(mod.param, face_dir);
303 if(mod.type == NODEMOD_CRACK)
305 spec.feature = TILEFEAT_CRACK;
306 spec.param.crack.progression = mod.param;
313 u8 MapBlock::getNodeContent(v3s16 p, MapNode mn)
316 Check temporary modifications on this node
318 core::map<v3s16, NodeMod>::Node *n;
319 n = m_temp_mods.find(p);
324 struct NodeMod mod = n->getValue();
325 if(mod.type == NODEMOD_CHANGECONTENT)
330 if(mod.type == NODEMOD_CRACK)
333 Content doesn't change.
335 face_contents works just like it should, because
336 there should not be faces between differently cracked
339 If a semi-transparent node is cracked in front an
340 another one, it really doesn't matter whether there
341 is a cracked face drawn in between or not.
351 translate_dir: unit vector with only one of x, y or z
352 face_dir: unit vector with only one of x, y or z
354 void MapBlock::updateFastFaceRow(
363 core::array<FastFace> &dest)
367 u16 continuous_tiles_count = 0;
369 MapNode n0 = getNodeParentNoEx(p);
370 MapNode n1 = getNodeParentNoEx(p + face_dir);
372 u8 light = getFaceLight(daynight_ratio, n0, n1, face_dir);
374 TileSpec tile0 = getNodeTile(n0, p, face_dir);
375 TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir);
377 for(u16 j=0; j<length; j++)
379 bool next_is_different = true;
390 p_next = p + translate_dir;
391 n0_next = getNodeParentNoEx(p_next);
392 n1_next = getNodeParentNoEx(p_next + face_dir);
393 tile0_next = getNodeTile(n0_next, p_next, face_dir);
394 tile1_next = getNodeTile(n1_next, p_next + face_dir, -face_dir);
395 light_next = getFaceLight(daynight_ratio, n0_next, n1_next, face_dir);
397 if(tile0_next == tile0
398 && tile1_next == tile1
399 && light_next == light)
401 next_is_different = false;
405 continuous_tiles_count++;
407 if(next_is_different)
410 Create a face if there should be one
412 //u8 mf = face_contents(tile0, tile1);
414 u8 content0 = getNodeContent(p, n0);
415 u8 content1 = getNodeContent(p + face_dir, n1);
416 u8 mf = face_contents(content0, content1);
420 // Floating point conversion of the position vector
421 v3f pf(p.X, p.Y, p.Z);
422 // Center point of face (kind of)
423 v3f sp = pf - ((f32)continuous_tiles_count / 2. - 0.5) * translate_dir_f;
425 if(translate_dir.X != 0){
426 scale.X = continuous_tiles_count;
428 if(translate_dir.Y != 0){
429 scale.Y = continuous_tiles_count;
431 if(translate_dir.Z != 0){
432 scale.Z = continuous_tiles_count;
437 // If node at sp (tile0) is more solid
440 makeFastFace(tile0, decode_light(light),
442 posRelative_f, dest);
444 // If node at sp is less solid (mf == 2)
447 makeFastFace(tile1, decode_light(light),
448 sp+face_dir_f, -face_dir, scale,
449 posRelative_f, dest);
454 continuous_tiles_count = 0;
467 This is used because CMeshBuffer::append() is very slow
471 video::SMaterial material;
472 core::array<u16> indices;
473 core::array<video::S3DVertex> vertices;
480 video::SMaterial material,
481 const video::S3DVertex* const vertices,
483 const u16* const indices,
487 PreMeshBuffer *p = NULL;
488 for(u32 i=0; i<m_prebuffers.size(); i++)
490 PreMeshBuffer &pp = m_prebuffers[i];
491 if(pp.material != material)
501 pp.material = material;
502 m_prebuffers.push_back(pp);
503 p = &m_prebuffers[m_prebuffers.size()-1];
506 u32 vertex_count = p->vertices.size();
507 for(u32 i=0; i<numIndices; i++)
509 u32 j = indices[i] + vertex_count;
512 dstream<<"FIXME: Meshbuffer ran out of indices"<<std::endl;
513 // NOTE: Fix is to just add an another MeshBuffer
515 p->indices.push_back(j);
517 for(u32 i=0; i<numVertices; i++)
519 p->vertices.push_back(vertices[i]);
523 void fillMesh(scene::SMesh *mesh)
525 /*dstream<<"Filling mesh with "<<m_prebuffers.size()
526 <<" meshbuffers"<<std::endl;*/
527 for(u32 i=0; i<m_prebuffers.size(); i++)
529 PreMeshBuffer &p = m_prebuffers[i];
531 /*dstream<<"p.vertices.size()="<<p.vertices.size()
532 <<", p.indices.size()="<<p.indices.size()
537 // This is a "Standard MeshBuffer",
538 // it's a typedeffed CMeshBuffer<video::S3DVertex>
539 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
541 buf->Material = p.material;
542 //((scene::SMeshBuffer*)buf)->Material = p.material;
544 //buf->setHardwareMappingHint(scene::EHM_STATIC);
546 mesh->addMeshBuffer(buf);
550 buf->append(p.vertices.pointer(), p.vertices.size(),
551 p.indices.pointer(), p.indices.size());
556 core::array<PreMeshBuffer> m_prebuffers;
559 void MapBlock::updateMesh(u32 daynight_ratio)
563 DEBUG: If mesh has been generated, don't generate it again
566 JMutexAutoLock meshlock(mesh_mutex);
573 //TimeTaker timer1("updateMesh()", g_device);
575 core::array<FastFace> fastfaces_new;
577 v3f posRelative_f(getPosRelative().X, getPosRelative().Y,
578 getPosRelative().Z); // floating point conversion
581 We are including the faces of the trailing edges of the block.
582 This means that when something changes, the caller must
583 also update the meshes of the blocks at the leading edges.
585 NOTE: This is the slowest part of this method.
589 Go through every y,z and get top faces in rows of x+
591 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
592 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
593 updateFastFaceRow(daynight_ratio, posRelative_f,
594 v3s16(0,y,z), MAP_BLOCKSIZE,
597 v3s16(0,1,0), //face dir
603 Go through every x,y and get right faces in rows of z+
605 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
606 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
607 updateFastFaceRow(daynight_ratio, posRelative_f,
608 v3s16(x,y,0), MAP_BLOCKSIZE,
617 Go through every y,z and get back faces in rows of x+
619 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
620 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
621 updateFastFaceRow(daynight_ratio, posRelative_f,
622 v3s16(0,y,z), MAP_BLOCKSIZE,
634 Convert FastFaces to SMesh
637 scene::SMesh *mesh_new = NULL;
639 mesh_new = new scene::SMesh();
641 if(fastfaces_new.size() > 0)
643 MeshCollector collector;
645 for(u32 i=0; i<fastfaces_new.size(); i++)
647 FastFace &f = fastfaces_new[i];
649 const u16 indices[] = {0,1,2,2,3,0};
651 if(f.tile.feature == TILEFEAT_NONE)
653 collector.append(tile_material_get(f.tile.id), f.vertices, 4,
656 else if(f.tile.feature == TILEFEAT_CRACK)
658 const char *path = tile_texture_path_get(f.tile.id);
660 u16 progression = f.tile.param.crack.progression;
662 std::string name = (std::string)path + "_cracked_"
663 + (char)('0' + progression);
665 TextureMod *mod = new CrackTextureMod(progression);
667 video::ITexture *texture = g_irrlicht->getTexture(
668 TextureSpec(name, path, mod));
670 video::SMaterial material = tile_material_get(f.tile.id);
671 material.setTexture(0, texture);
673 collector.append(material, f.vertices, 4, indices, 6);
682 collector.fillMesh(mesh_new);
684 // Use VBO for mesh (this just would set this for ever buffer)
685 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
687 /*std::cout<<"MapBlock has "<<fastfaces_new->getSize()<<" faces "
688 <<"and uses "<<mesh_new->getMeshBufferCount()
689 <<" materials (meshbuffers)"<<std::endl;*/
693 Add special graphics:
696 TODO: Optimize by using same meshbuffer for same textures
699 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
700 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
701 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
705 MapNode &n = getNodeRef(x,y,z);
707 if(n.d == CONTENT_TORCH)
709 //scene::IMeshBuffer *buf = new scene::SMeshBuffer();
710 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
711 video::SColor c(255,255,255,255);
713 video::S3DVertex vertices[4] =
715 video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c, 0,1),
716 video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c, 1,1),
717 video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, 1,0),
718 video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c, 0,0),
721 v3s16 dir = unpackDir(n.dir);
723 for(s32 i=0; i<4; i++)
725 if(dir == v3s16(1,0,0))
726 vertices[i].Pos.rotateXZBy(0);
727 if(dir == v3s16(-1,0,0))
728 vertices[i].Pos.rotateXZBy(180);
729 if(dir == v3s16(0,0,1))
730 vertices[i].Pos.rotateXZBy(90);
731 if(dir == v3s16(0,0,-1))
732 vertices[i].Pos.rotateXZBy(-90);
733 if(dir == v3s16(0,-1,0))
734 vertices[i].Pos.rotateXZBy(45);
735 if(dir == v3s16(0,1,0))
736 vertices[i].Pos.rotateXZBy(-45);
738 vertices[i].Pos += intToFloat(p + getPosRelative());
741 u16 indices[] = {0,1,2,2,3,0};
742 buf->append(vertices, 4, indices, 6);
745 buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
746 buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false);
747 buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
748 //buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
749 buf->getMaterial().MaterialType
750 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
751 if(dir == v3s16(0,-1,0))
752 buf->getMaterial().setTexture(0,
753 g_irrlicht->getTexture("../data/torch_on_floor.png"));
754 else if(dir == v3s16(0,1,0))
755 buf->getMaterial().setTexture(0,
756 g_irrlicht->getTexture("../data/torch_on_ceiling.png"));
757 // For backwards compatibility
758 else if(dir == v3s16(0,0,0))
759 buf->getMaterial().setTexture(0,
760 g_irrlicht->getTexture("../data/torch_on_floor.png"));
762 buf->getMaterial().setTexture(0,
763 g_irrlicht->getTexture("../data/torch.png"));
766 mesh_new->addMeshBuffer(buf);
772 Do some stuff to the mesh
775 mesh_new->recalculateBoundingBox();
778 Delete new mesh if it is empty
781 if(mesh_new->getMeshBufferCount() == 0)
793 //scene::SMesh *mesh_old = mesh[daynight_i];
794 //mesh[daynight_i] = mesh_new;
796 scene::SMesh *mesh_old = mesh;
798 setMeshExpired(false);
802 // Remove hardware buffers of meshbuffers of mesh
803 // NOTE: No way, this runs in a different thread and everything
804 /*u32 c = mesh_old->getMeshBufferCount();
805 for(u32 i=0; i<c; i++)
807 IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
810 /*dstream<<"mesh_old->getReferenceCount()="
811 <<mesh_old->getReferenceCount()<<std::endl;
812 u32 c = mesh_old->getMeshBufferCount();
813 for(u32 i=0; i<c; i++)
815 scene::IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
816 dstream<<"buf->getReferenceCount()="
817 <<buf->getReferenceCount()<<std::endl;
828 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
831 /*void MapBlock::updateMeshes(s32 first_i)
833 assert(first_i >= 0 && first_i <= DAYNIGHT_CACHE_COUNT);
835 for(s32 i=0; i<DAYNIGHT_CACHE_COUNT; i++)
846 Propagates sunlight down through the block.
847 Doesn't modify nodes that are not affected by sunlight.
849 Returns false if sunlight at bottom block is invalid
850 Returns true if bottom block doesn't exist.
852 If there is a block above, continues from it.
853 If there is no block above, assumes there is sunlight, unless
854 is_underground is set.
856 At the moment, all sunlighted nodes are added to light_sources.
857 TODO: This could be optimized.
859 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources)
861 // Whether the sunlight at the top of the bottom block is valid
862 bool block_below_is_valid = true;
864 v3s16 pos_relative = getPosRelative();
866 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
868 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
870 bool no_sunlight = false;
871 bool no_top_block = false;
872 // Check if node above block has sunlight
874 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
875 if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
884 catch(InvalidPositionException &e)
888 // TODO: This makes over-ground roofed places sunlighted
889 // Assume sunlight, unless is_underground==true
895 // TODO: There has to be some way to allow this behaviour
896 // As of now, it just makes everything dark.
898 //no_sunlight = true;
901 /*std::cout<<"("<<x<<","<<z<<"): "
902 <<"no_top_block="<<no_top_block
903 <<", is_underground="<<is_underground
904 <<", no_sunlight="<<no_sunlight
907 s16 y = MAP_BLOCKSIZE-1;
909 if(no_sunlight == false)
911 // Continue spreading sunlight downwards through transparent
917 MapNode &n = getNodeRef(pos);
919 if(n.sunlight_propagates())
921 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
923 light_sources.insert(pos_relative + pos, true);
931 bool sunlight_should_go_down = (y==-1);
933 // Fill rest with black (only transparent ones)
937 MapNode &n = getNodeRef(pos);
939 if(n.light_propagates())
941 n.setLight(LIGHTBANK_DAY, 0);
949 If the block below hasn't already been marked invalid:
951 Check if the node below the block has proper sunlight at top.
952 If not, the block below is invalid.
954 Ignore non-transparent nodes as they always have no light
958 if(block_below_is_valid)
960 MapNode n = getNodeParent(v3s16(x, -1, z));
961 if(n.light_propagates())
963 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN
964 && sunlight_should_go_down == false)
965 block_below_is_valid = false;
966 else if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN
967 && sunlight_should_go_down == true)
968 block_below_is_valid = false;
972 catch(InvalidPositionException &e)
974 /*std::cout<<"InvalidBlockException for bottom block node"
976 // Just no block below, no need to panic.
981 return block_below_is_valid;
984 void MapBlock::copyTo(VoxelManipulator &dst)
986 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
987 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
989 dst.copyFrom(data, data_area, v3s16(0,0,0),
990 getPosRelative(), data_size);
993 /*void getPseudoObjects(v3f origin, f32 max_d,
994 core::array<DistanceSortedObject> &dest)
997 void MapBlock::stepObjects(float dtime, bool server, u32 daynight_ratio)
1002 m_objects.step(dtime, server, daynight_ratio);
1005 Spawn some objects at random.
1007 Use dayNightDiffed() to approximate being near ground level
1009 if(m_spawn_timer < -999)
1013 if(dayNightDiffed() == true && getObjectCount() == 0)
1015 m_spawn_timer -= dtime;
1016 if(m_spawn_timer <= 0.0)
1018 m_spawn_timer += rand() % 300;
1021 (rand()%(MAP_BLOCKSIZE-1))+0,
1022 (rand()%(MAP_BLOCKSIZE-1))+0
1025 s16 y = getGroundLevel(p2d);
1029 v3s16 p(p2d.X, y+1, p2d.Y);
1031 if(getNode(p).d == CONTENT_AIR
1032 && getNode(p).getLightBlend(daynight_ratio) <= 11)
1034 RatObject *obj = new RatObject(NULL, -1, intToFloat(p));
1045 void MapBlock::updateDayNightDiff()
1049 m_day_night_differs = false;
1053 bool differs = false;
1056 Check if any lighting value differs
1058 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1060 MapNode &n = data[i];
1061 if(n.getLight(LIGHTBANK_DAY) != n.getLight(LIGHTBANK_NIGHT))
1069 If some lighting values differ, check if the whole thing is
1070 just air. If it is, differ = false
1074 bool only_air = true;
1075 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1077 MapNode &n = data[i];
1078 if(n.d != CONTENT_AIR)
1088 // Set member variable
1089 m_day_night_differs = differs;
1092 s16 MapBlock::getGroundLevel(v2s16 p2d)
1098 s16 y = MAP_BLOCKSIZE-1;
1101 if(is_ground_content(getNodeRef(p2d.X, y, p2d.Y).d))
1103 if(y == MAP_BLOCKSIZE-1)
1111 catch(InvalidPositionException &e)
1121 void MapBlock::serialize(std::ostream &os, u8 version)
1123 if(!ser_ver_supported(version))
1124 throw VersionMismatchException("ERROR: MapBlock format not supported");
1128 throw SerializationError("ERROR: Not writing dummy block.");
1131 // These have no compression
1132 if(version <= 3 || version == 5 || version == 6)
1134 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1136 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
1137 SharedBuffer<u8> dest(buflen);
1139 dest[0] = is_underground;
1140 for(u32 i=0; i<nodecount; i++)
1142 u32 s = 1 + i * MapNode::serializedLength(version);
1143 data[i].serialize(&dest[s], version);
1146 os.write((char*)*dest, dest.getSize());
1148 else if(version <= 10)
1152 Compress the materials and the params separately.
1156 os.write((char*)&is_underground, 1);
1158 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1160 // Get and compress materials
1161 SharedBuffer<u8> materialdata(nodecount);
1162 for(u32 i=0; i<nodecount; i++)
1164 materialdata[i] = data[i].d;
1166 compress(materialdata, os, version);
1168 // Get and compress lights
1169 SharedBuffer<u8> lightdata(nodecount);
1170 for(u32 i=0; i<nodecount; i++)
1172 lightdata[i] = data[i].param;
1174 compress(lightdata, os, version);
1178 // Get and compress pressure
1179 SharedBuffer<u8> pressuredata(nodecount);
1180 for(u32 i=0; i<nodecount; i++)
1182 pressuredata[i] = data[i].pressure;
1184 compress(pressuredata, os, version);
1187 // All other versions (newest)
1194 if(m_day_night_differs)
1196 os.write((char*)&flags, 1);
1198 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1204 SharedBuffer<u8> databuf(nodecount*3);
1207 for(u32 i=0; i<nodecount; i++)
1209 databuf[i] = data[i].d;
1213 for(u32 i=0; i<nodecount; i++)
1215 databuf[i+nodecount] = data[i].param;
1219 for(u32 i=0; i<nodecount; i++)
1221 databuf[i+nodecount*2] = data[i].pressure;
1225 Compress data to output stream
1228 compress(databuf, os, version);
1232 void MapBlock::deSerialize(std::istream &is, u8 version)
1234 if(!ser_ver_supported(version))
1235 throw VersionMismatchException("ERROR: MapBlock format not supported");
1237 // These have no compression
1238 if(version <= 3 || version == 5 || version == 6)
1240 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1243 if(is.gcount() != 1)
1244 throw SerializationError
1245 ("MapBlock::deSerialize: no enough input data");
1246 is_underground = tmp;
1247 for(u32 i=0; i<nodecount; i++)
1249 s32 len = MapNode::serializedLength(version);
1250 SharedBuffer<u8> d(len);
1251 is.read((char*)*d, len);
1252 if(is.gcount() != len)
1253 throw SerializationError
1254 ("MapBlock::deSerialize: no enough input data");
1255 data[i].deSerialize(*d, version);
1258 else if(version <= 10)
1260 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1263 is.read((char*)&t8, 1);
1264 is_underground = t8;
1267 // Uncompress and set material data
1268 std::ostringstream os(std::ios_base::binary);
1269 decompress(is, os, version);
1270 std::string s = os.str();
1271 if(s.size() != nodecount)
1272 throw SerializationError
1273 ("MapBlock::deSerialize: invalid format");
1274 for(u32 i=0; i<s.size(); i++)
1280 // Uncompress and set param data
1281 std::ostringstream os(std::ios_base::binary);
1282 decompress(is, os, version);
1283 std::string s = os.str();
1284 if(s.size() != nodecount)
1285 throw SerializationError
1286 ("MapBlock::deSerialize: invalid format");
1287 for(u32 i=0; i<s.size(); i++)
1289 data[i].param = s[i];
1295 // Uncompress and set pressure data
1296 std::ostringstream os(std::ios_base::binary);
1297 decompress(is, os, version);
1298 std::string s = os.str();
1299 if(s.size() != nodecount)
1300 throw SerializationError
1301 ("MapBlock::deSerialize: invalid format");
1302 for(u32 i=0; i<s.size(); i++)
1304 data[i].pressure = s[i];
1308 // All other versions (newest)
1311 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1314 is.read((char*)&flags, 1);
1315 is_underground = (flags & 1) ? true : false;
1316 m_day_night_differs = (flags & 2) ? true : false;
1319 std::ostringstream os(std::ios_base::binary);
1320 decompress(is, os, version);
1321 std::string s = os.str();
1322 if(s.size() != nodecount*3)
1323 throw SerializationError
1324 ("MapBlock::deSerialize: invalid format");
1327 for(u32 i=0; i<nodecount; i++)
1332 for(u32 i=0; i<nodecount; i++)
1334 data[i].param = s[i+nodecount];
1337 for(u32 i=0; i<nodecount; i++)
1339 data[i].pressure = s[i+nodecount*2];