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 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
693 /*std::cout<<"MapBlock has "<<fastfaces_new->getSize()<<" faces "
694 <<"and uses "<<mesh_new->getMeshBufferCount()
695 <<" materials (meshbuffers)"<<std::endl;*/
699 Add special graphics:
702 TODO: Optimize by using same meshbuffer for same textures
705 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
706 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
707 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
711 MapNode &n = getNodeRef(x,y,z);
713 if(n.d == CONTENT_TORCH)
715 //scene::IMeshBuffer *buf = new scene::SMeshBuffer();
716 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
717 video::SColor c(255,255,255,255);
719 video::S3DVertex vertices[4] =
721 video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c, 0,1),
722 video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c, 1,1),
723 video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, 1,0),
724 video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c, 0,0),
727 v3s16 dir = unpackDir(n.dir);
729 for(s32 i=0; i<4; i++)
731 if(dir == v3s16(1,0,0))
732 vertices[i].Pos.rotateXZBy(0);
733 if(dir == v3s16(-1,0,0))
734 vertices[i].Pos.rotateXZBy(180);
735 if(dir == v3s16(0,0,1))
736 vertices[i].Pos.rotateXZBy(90);
737 if(dir == v3s16(0,0,-1))
738 vertices[i].Pos.rotateXZBy(-90);
739 if(dir == v3s16(0,-1,0))
740 vertices[i].Pos.rotateXZBy(45);
741 if(dir == v3s16(0,1,0))
742 vertices[i].Pos.rotateXZBy(-45);
744 vertices[i].Pos += intToFloat(p + getPosRelative());
747 u16 indices[] = {0,1,2,2,3,0};
748 buf->append(vertices, 4, indices, 6);
751 buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
752 buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false);
753 buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
754 //buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
755 buf->getMaterial().MaterialType
756 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
757 if(dir == v3s16(0,-1,0))
758 buf->getMaterial().setTexture(0,
759 g_irrlicht->getTexture("../data/torch_on_floor.png"));
760 else if(dir == v3s16(0,1,0))
761 buf->getMaterial().setTexture(0,
762 g_irrlicht->getTexture("../data/torch_on_ceiling.png"));
763 // For backwards compatibility
764 else if(dir == v3s16(0,0,0))
765 buf->getMaterial().setTexture(0,
766 g_irrlicht->getTexture("../data/torch_on_floor.png"));
768 buf->getMaterial().setTexture(0,
769 g_irrlicht->getTexture("../data/torch.png"));
772 mesh_new->addMeshBuffer(buf);
778 Do some stuff to the mesh
781 mesh_new->recalculateBoundingBox();
784 Delete new mesh if it is empty
787 if(mesh_new->getMeshBufferCount() == 0)
799 //scene::SMesh *mesh_old = mesh[daynight_i];
800 //mesh[daynight_i] = mesh_new;
802 scene::SMesh *mesh_old = mesh;
804 setMeshExpired(false);
808 // Remove hardware buffers of meshbuffers of mesh
809 // NOTE: No way, this runs in a different thread and everything
810 /*u32 c = mesh_old->getMeshBufferCount();
811 for(u32 i=0; i<c; i++)
813 IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
816 /*dstream<<"mesh_old->getReferenceCount()="
817 <<mesh_old->getReferenceCount()<<std::endl;
818 u32 c = mesh_old->getMeshBufferCount();
819 for(u32 i=0; i<c; i++)
821 scene::IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
822 dstream<<"buf->getReferenceCount()="
823 <<buf->getReferenceCount()<<std::endl;
834 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
837 /*void MapBlock::updateMeshes(s32 first_i)
839 assert(first_i >= 0 && first_i <= DAYNIGHT_CACHE_COUNT);
841 for(s32 i=0; i<DAYNIGHT_CACHE_COUNT; i++)
852 Propagates sunlight down through the block.
853 Doesn't modify nodes that are not affected by sunlight.
855 Returns false if sunlight at bottom block is invalid
856 Returns true if bottom block doesn't exist.
858 If there is a block above, continues from it.
859 If there is no block above, assumes there is sunlight, unless
860 is_underground is set.
862 At the moment, all sunlighted nodes are added to light_sources.
863 TODO: This could be optimized.
865 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources)
867 // Whether the sunlight at the top of the bottom block is valid
868 bool block_below_is_valid = true;
870 v3s16 pos_relative = getPosRelative();
872 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
874 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
876 bool no_sunlight = false;
877 bool no_top_block = false;
878 // Check if node above block has sunlight
880 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
881 if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
890 catch(InvalidPositionException &e)
894 // TODO: This makes over-ground roofed places sunlighted
895 // Assume sunlight, unless is_underground==true
901 // TODO: There has to be some way to allow this behaviour
902 // As of now, it just makes everything dark.
904 //no_sunlight = true;
907 /*std::cout<<"("<<x<<","<<z<<"): "
908 <<"no_top_block="<<no_top_block
909 <<", is_underground="<<is_underground
910 <<", no_sunlight="<<no_sunlight
913 s16 y = MAP_BLOCKSIZE-1;
915 if(no_sunlight == false)
917 // Continue spreading sunlight downwards through transparent
923 MapNode &n = getNodeRef(pos);
925 if(n.sunlight_propagates())
927 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
929 light_sources.insert(pos_relative + pos, true);
937 bool sunlight_should_go_down = (y==-1);
939 // Fill rest with black (only transparent ones)
943 MapNode &n = getNodeRef(pos);
945 if(n.light_propagates())
947 n.setLight(LIGHTBANK_DAY, 0);
955 If the block below hasn't already been marked invalid:
957 Check if the node below the block has proper sunlight at top.
958 If not, the block below is invalid.
960 Ignore non-transparent nodes as they always have no light
964 if(block_below_is_valid)
966 MapNode n = getNodeParent(v3s16(x, -1, z));
967 if(n.light_propagates())
969 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN
970 && sunlight_should_go_down == false)
971 block_below_is_valid = false;
972 else if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN
973 && sunlight_should_go_down == true)
974 block_below_is_valid = false;
978 catch(InvalidPositionException &e)
980 /*std::cout<<"InvalidBlockException for bottom block node"
982 // Just no block below, no need to panic.
987 return block_below_is_valid;
990 void MapBlock::copyTo(VoxelManipulator &dst)
992 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
993 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
995 dst.copyFrom(data, data_area, v3s16(0,0,0),
996 getPosRelative(), data_size);
999 /*void getPseudoObjects(v3f origin, f32 max_d,
1000 core::array<DistanceSortedObject> &dest)
1003 void MapBlock::stepObjects(float dtime, bool server, u32 daynight_ratio)
1008 m_objects.step(dtime, server, daynight_ratio);
1011 Spawn some objects at random.
1013 Use dayNightDiffed() to approximate being near ground level
1015 if(m_spawn_timer < -999)
1019 if(dayNightDiffed() == true && getObjectCount() == 0)
1021 m_spawn_timer -= dtime;
1022 if(m_spawn_timer <= 0.0)
1024 m_spawn_timer += myrand() % 300;
1027 (myrand()%(MAP_BLOCKSIZE-1))+0,
1028 (myrand()%(MAP_BLOCKSIZE-1))+0
1031 s16 y = getGroundLevel(p2d);
1035 v3s16 p(p2d.X, y+1, p2d.Y);
1037 if(getNode(p).d == CONTENT_AIR
1038 && getNode(p).getLightBlend(daynight_ratio) <= 11)
1040 RatObject *obj = new RatObject(NULL, -1, intToFloat(p));
1051 void MapBlock::updateDayNightDiff()
1055 m_day_night_differs = false;
1059 bool differs = false;
1062 Check if any lighting value differs
1064 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1066 MapNode &n = data[i];
1067 if(n.getLight(LIGHTBANK_DAY) != n.getLight(LIGHTBANK_NIGHT))
1075 If some lighting values differ, check if the whole thing is
1076 just air. If it is, differ = false
1080 bool only_air = true;
1081 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1083 MapNode &n = data[i];
1084 if(n.d != CONTENT_AIR)
1094 // Set member variable
1095 m_day_night_differs = differs;
1098 s16 MapBlock::getGroundLevel(v2s16 p2d)
1104 s16 y = MAP_BLOCKSIZE-1;
1107 if(is_ground_content(getNodeRef(p2d.X, y, p2d.Y).d))
1109 if(y == MAP_BLOCKSIZE-1)
1117 catch(InvalidPositionException &e)
1127 void MapBlock::serialize(std::ostream &os, u8 version)
1129 if(!ser_ver_supported(version))
1130 throw VersionMismatchException("ERROR: MapBlock format not supported");
1134 throw SerializationError("ERROR: Not writing dummy block.");
1137 // These have no compression
1138 if(version <= 3 || version == 5 || version == 6)
1140 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1142 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
1143 SharedBuffer<u8> dest(buflen);
1145 dest[0] = is_underground;
1146 for(u32 i=0; i<nodecount; i++)
1148 u32 s = 1 + i * MapNode::serializedLength(version);
1149 data[i].serialize(&dest[s], version);
1152 os.write((char*)*dest, dest.getSize());
1154 else if(version <= 10)
1158 Compress the materials and the params separately.
1162 os.write((char*)&is_underground, 1);
1164 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1166 // Get and compress materials
1167 SharedBuffer<u8> materialdata(nodecount);
1168 for(u32 i=0; i<nodecount; i++)
1170 materialdata[i] = data[i].d;
1172 compress(materialdata, os, version);
1174 // Get and compress lights
1175 SharedBuffer<u8> lightdata(nodecount);
1176 for(u32 i=0; i<nodecount; i++)
1178 lightdata[i] = data[i].param;
1180 compress(lightdata, os, version);
1184 // Get and compress pressure
1185 SharedBuffer<u8> pressuredata(nodecount);
1186 for(u32 i=0; i<nodecount; i++)
1188 pressuredata[i] = data[i].pressure;
1190 compress(pressuredata, os, version);
1193 // All other versions (newest)
1200 if(m_day_night_differs)
1202 os.write((char*)&flags, 1);
1204 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1210 SharedBuffer<u8> databuf(nodecount*3);
1213 for(u32 i=0; i<nodecount; i++)
1215 databuf[i] = data[i].d;
1219 for(u32 i=0; i<nodecount; i++)
1221 databuf[i+nodecount] = data[i].param;
1225 for(u32 i=0; i<nodecount; i++)
1227 databuf[i+nodecount*2] = data[i].pressure;
1231 Compress data to output stream
1234 compress(databuf, os, version);
1238 void MapBlock::deSerialize(std::istream &is, u8 version)
1240 if(!ser_ver_supported(version))
1241 throw VersionMismatchException("ERROR: MapBlock format not supported");
1243 // These have no compression
1244 if(version <= 3 || version == 5 || version == 6)
1246 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1249 if(is.gcount() != 1)
1250 throw SerializationError
1251 ("MapBlock::deSerialize: no enough input data");
1252 is_underground = tmp;
1253 for(u32 i=0; i<nodecount; i++)
1255 s32 len = MapNode::serializedLength(version);
1256 SharedBuffer<u8> d(len);
1257 is.read((char*)*d, len);
1258 if(is.gcount() != len)
1259 throw SerializationError
1260 ("MapBlock::deSerialize: no enough input data");
1261 data[i].deSerialize(*d, version);
1264 else if(version <= 10)
1266 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1269 is.read((char*)&t8, 1);
1270 is_underground = t8;
1273 // Uncompress and set material data
1274 std::ostringstream os(std::ios_base::binary);
1275 decompress(is, os, version);
1276 std::string s = os.str();
1277 if(s.size() != nodecount)
1278 throw SerializationError
1279 ("MapBlock::deSerialize: invalid format");
1280 for(u32 i=0; i<s.size(); i++)
1286 // Uncompress and set param data
1287 std::ostringstream os(std::ios_base::binary);
1288 decompress(is, os, version);
1289 std::string s = os.str();
1290 if(s.size() != nodecount)
1291 throw SerializationError
1292 ("MapBlock::deSerialize: invalid format");
1293 for(u32 i=0; i<s.size(); i++)
1295 data[i].param = s[i];
1301 // Uncompress and set pressure data
1302 std::ostringstream os(std::ios_base::binary);
1303 decompress(is, os, version);
1304 std::string s = os.str();
1305 if(s.size() != nodecount)
1306 throw SerializationError
1307 ("MapBlock::deSerialize: invalid format");
1308 for(u32 i=0; i<s.size(); i++)
1310 data[i].pressure = s[i];
1314 // All other versions (newest)
1317 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1320 is.read((char*)&flags, 1);
1321 is_underground = (flags & 1) ? true : false;
1322 m_day_night_differs = (flags & 2) ? true : false;
1325 std::ostringstream os(std::ios_base::binary);
1326 decompress(is, os, version);
1327 std::string s = os.str();
1328 if(s.size() != nodecount*3)
1329 throw SerializationError
1330 ("MapBlock::deSerialize: invalid format");
1333 for(u32 i=0; i<nodecount; i++)
1338 for(u32 i=0; i<nodecount; i++)
1340 data[i].param = s[i+nodecount];
1343 for(u32 i=0; i<nodecount; i++)
1345 data[i].pressure = s[i+nodecount*2];