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
27 void MeshMakeData::fill(u32 daynight_ratio, MapBlock *block)
29 m_daynight_ratio = daynight_ratio;
30 m_blockpos = block->getPos();
32 v3s16 blockpos_nodes = m_blockpos*MAP_BLOCKSIZE;
35 There is no harm not copying the TempMods of the neighbors
36 because they are already copied to this block
39 block->copyTempMods(m_temp_mods);
45 // Allocate this block + neighbors
47 m_vmanip.addArea(VoxelArea(blockpos_nodes-v3s16(1,1,1)*MAP_BLOCKSIZE,
48 blockpos_nodes+v3s16(1,1,1)*MAP_BLOCKSIZE*2-v3s16(1,1,1)));
51 //TimeTaker timer("copy central block data");
55 block->copyTo(m_vmanip);
58 //TimeTaker timer("copy neighbor block data");
62 Copy neighbors. This is lightning fast.
63 Copying only the borders would be *very* slow.
67 NodeContainer *parentcontainer = block->getParent();
68 // This will only work if the parent is the map
69 assert(parentcontainer->nodeContainerId() == NODECONTAINER_ID_MAP);
70 // OK, we have the map!
71 Map *map = (Map*)parentcontainer;
73 for(u16 i=0; i<6; i++)
75 const v3s16 &dir = g_6dirs[i];
76 v3s16 bp = m_blockpos + dir;
77 MapBlock *b = map->getBlockNoCreateNoEx(bp);
85 Parameters must consist of air and !air.
88 If either of the nodes doesn't exist, light is 0.
91 daynight_ratio: 0...1000
93 n2: getNodeParent(p + face_dir)
94 face_dir: axis oriented unit vector from p to p2
96 returns encoded light value.
98 u8 getFaceLight(u32 daynight_ratio, MapNode n, MapNode n2,
103 u8 l1 = n.getLightBlend(daynight_ratio);
104 u8 l2 = n2.getLightBlend(daynight_ratio);
110 // Make some nice difference to different sides
112 // This makes light come from a corner
113 /*if(face_dir.X == 1 || face_dir.Z == 1 || face_dir.Y == -1)
114 light = diminish_light(diminish_light(light));
115 else if(face_dir.X == -1 || face_dir.Z == -1)
116 light = diminish_light(light);*/
118 // All neighboring faces have different shade (like in minecraft)
119 if(face_dir.X == 1 || face_dir.X == -1 || face_dir.Y == -1)
120 light = diminish_light(diminish_light(light));
121 else if(face_dir.Z == 1 || face_dir.Z == -1)
122 light = diminish_light(light);
126 catch(InvalidPositionException &e)
134 void makeFastFace(TileSpec tile, u8 light, v3f p,
135 v3s16 dir, v3f scale, v3f posRelative_f,
136 core::array<FastFace> &dest)
140 // Position is at the center of the cube.
145 // If looking towards z+, this is the face that is behind
146 // the center point, facing towards z+.
147 vertex_pos[0] = v3f(-BS/2,-BS/2,BS/2);
148 vertex_pos[1] = v3f( BS/2,-BS/2,BS/2);
149 vertex_pos[2] = v3f( BS/2, BS/2,BS/2);
150 vertex_pos[3] = v3f(-BS/2, BS/2,BS/2);
152 if(dir == v3s16(0,0,1))
154 for(u16 i=0; i<4; i++)
155 vertex_pos[i].rotateXZBy(0);
157 else if(dir == v3s16(0,0,-1))
159 for(u16 i=0; i<4; i++)
160 vertex_pos[i].rotateXZBy(180);
162 else if(dir == v3s16(1,0,0))
164 for(u16 i=0; i<4; i++)
165 vertex_pos[i].rotateXZBy(-90);
167 else if(dir == v3s16(-1,0,0))
169 for(u16 i=0; i<4; i++)
170 vertex_pos[i].rotateXZBy(90);
172 else if(dir == v3s16(0,1,0))
174 for(u16 i=0; i<4; i++)
175 vertex_pos[i].rotateYZBy(-90);
177 else if(dir == v3s16(0,-1,0))
179 for(u16 i=0; i<4; i++)
180 vertex_pos[i].rotateYZBy(90);
183 for(u16 i=0; i<4; i++)
185 vertex_pos[i].X *= scale.X;
186 vertex_pos[i].Y *= scale.Y;
187 vertex_pos[i].Z *= scale.Z;
188 vertex_pos[i] += pos + posRelative_f;
192 if (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X;
193 else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
194 else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
196 v3f zerovector = v3f(0,0,0);
198 //u8 li = decode_light(light);
200 //u8 li = 255; //DEBUG
202 u8 alpha = tile.alpha;
204 if(tile.id == TILE_WATER)
205 alpha = WATER_ALPHA;*/
207 video::SColor c = video::SColor(alpha,li,li,li);
209 float x0 = tile.texture.pos.X;
210 float y0 = tile.texture.pos.Y;
211 float w = tile.texture.size.X;
212 float h = tile.texture.size.Y;
214 face.vertices[0] = video::S3DVertex(vertex_pos[0], v3f(0,1,0), c,
215 core::vector2d<f32>(x0+w*abs_scale, y0+h));
216 face.vertices[1] = video::S3DVertex(vertex_pos[1], v3f(0,1,0), c,
217 core::vector2d<f32>(x0, y0+h));
218 face.vertices[2] = video::S3DVertex(vertex_pos[2], v3f(0,1,0), c,
219 core::vector2d<f32>(x0, y0));
220 face.vertices[3] = video::S3DVertex(vertex_pos[3], v3f(0,1,0), c,
221 core::vector2d<f32>(x0+w*abs_scale, y0));
225 //f->tile = TILE_STONE;
227 dest.push_back(face);
232 Gets node tile from any place relative to block.
233 Returns TILE_NODE if doesn't exist or should not be drawn.
235 TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir,
236 NodeModMap &temp_mods)
239 spec = mn.getTile(face_dir);
242 Check temporary modifications on this node
244 /*core::map<v3s16, NodeMod>::Node *n;
245 n = m_temp_mods.find(p);
249 struct NodeMod mod = n->getValue();*/
251 if(temp_mods.get(p, &mod))
253 if(mod.type == NODEMOD_CHANGECONTENT)
255 MapNode mn2(mod.param);
256 spec = mn2.getTile(face_dir);
258 if(mod.type == NODEMOD_CRACK)
261 Get texture id, translate it to name, append stuff to
265 // Get original texture name
266 u32 orig_id = spec.texture.id;
267 std::string orig_name = g_texturesource->getTextureName(orig_id);
269 // Create new texture name
270 std::ostringstream os;
271 os<<orig_name<<"^[crack"<<mod.param;
274 u32 new_id = g_texturesource->getTextureId(os.str());
276 /*dstream<<"MapBlock::getNodeTile(): Switching from "
277 <<orig_name<<" to "<<os.str()<<" ("
278 <<orig_id<<" to "<<new_id<<")"<<std::endl;*/
280 spec.texture = g_texturesource->getTexture(new_id);
287 u8 getNodeContent(v3s16 p, MapNode mn, NodeModMap &temp_mods)
290 Check temporary modifications on this node
292 /*core::map<v3s16, NodeMod>::Node *n;
293 n = m_temp_mods.find(p);
297 struct NodeMod mod = n->getValue();*/
299 if(temp_mods.get(p, &mod))
301 if(mod.type == NODEMOD_CHANGECONTENT)
306 if(mod.type == NODEMOD_CRACK)
309 Content doesn't change.
311 face_contents works just like it should, because
312 there should not be faces between differently cracked
315 If a semi-transparent node is cracked in front an
316 another one, it really doesn't matter whether there
317 is a cracked face drawn in between or not.
327 translate_dir: unit vector with only one of x, y or z
328 face_dir: unit vector with only one of x, y or z
330 void updateFastFaceRow(
339 core::array<FastFace> &dest,
340 NodeModMap &temp_mods,
341 VoxelManipulator &vmanip,
342 v3s16 blockpos_nodes)
346 u16 continuous_tiles_count = 0;
348 MapNode n0 = vmanip.getNodeNoEx(blockpos_nodes + p);
349 MapNode n1 = vmanip.getNodeNoEx(blockpos_nodes + p + face_dir);
350 TileSpec tile0 = getNodeTile(n0, p, face_dir, temp_mods);
351 TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir, temp_mods);
352 u8 light = getFaceLight(daynight_ratio, n0, n1, face_dir);
354 for(u16 j=0; j<length; j++)
356 bool next_is_different = true;
365 // If at last position, there is nothing to compare to and
366 // the face must be drawn anyway
369 p_next = p + translate_dir;
371 n0_next = vmanip.getNodeNoEx(blockpos_nodes + p_next);
372 n1_next = vmanip.getNodeNoEx(blockpos_nodes + p_next + face_dir);
373 tile0_next = getNodeTile(n0_next, p_next, face_dir, temp_mods);
374 tile1_next = getNodeTile(n1_next,p_next+face_dir,-face_dir, temp_mods);
375 light_next = getFaceLight(daynight_ratio, n0_next, n1_next, face_dir);
377 if(tile0_next == tile0
378 && tile1_next == tile1
379 && light_next == light)
381 next_is_different = false;
385 continuous_tiles_count++;
387 // This is set to true if the texture doesn't allow more tiling
388 bool end_of_texture = false;
390 If there is no texture, it can be tiled infinitely.
391 If tiled==0, it means the texture can be tiled infinitely.
392 Otherwise check tiled agains continuous_tiles_count.
394 This check has to be made for both tiles, because this is
395 a bit hackish and we know which one we're using only when
396 the decision to make the faces is made.
398 if(tile0.texture.atlas != NULL && tile0.texture.tiled != 0)
400 if(tile0.texture.tiled <= continuous_tiles_count)
401 end_of_texture = true;
403 if(tile1.texture.atlas != NULL && tile1.texture.tiled != 0)
405 if(tile1.texture.tiled <= continuous_tiles_count)
406 end_of_texture = true;
409 //end_of_texture = true; //DEBUG
411 if(next_is_different || end_of_texture)
414 Create a face if there should be one
416 //u8 mf = face_contents(tile0, tile1);
418 u8 content0 = getNodeContent(p, n0, temp_mods);
419 u8 content1 = getNodeContent(p + face_dir, n1, temp_mods);
420 u8 mf = face_contents(content0, content1);
424 // Floating point conversion of the position vector
425 v3f pf(p.X, p.Y, p.Z);
426 // Center point of face (kind of)
427 v3f sp = pf - ((f32)continuous_tiles_count / 2. - 0.5) * translate_dir_f;
429 if(translate_dir.X != 0){
430 scale.X = continuous_tiles_count;
432 if(translate_dir.Y != 0){
433 scale.Y = continuous_tiles_count;
435 if(translate_dir.Z != 0){
436 scale.Z = continuous_tiles_count;
441 // If node at sp (tile0) is more solid
444 makeFastFace(tile0, decode_light(light),
446 posRelative_f, dest);
448 // If node at sp is less solid (mf == 2)
451 makeFastFace(tile1, decode_light(light),
452 sp+face_dir_f, -face_dir, scale,
453 posRelative_f, dest);
458 continuous_tiles_count = 0;
471 This is used because CMeshBuffer::append() is very slow
475 video::SMaterial material;
476 core::array<u16> indices;
477 core::array<video::S3DVertex> vertices;
484 video::SMaterial material,
485 const video::S3DVertex* const vertices,
487 const u16* const indices,
491 PreMeshBuffer *p = NULL;
492 for(u32 i=0; i<m_prebuffers.size(); i++)
494 PreMeshBuffer &pp = m_prebuffers[i];
495 if(pp.material != material)
505 pp.material = material;
506 m_prebuffers.push_back(pp);
507 p = &m_prebuffers[m_prebuffers.size()-1];
510 u32 vertex_count = p->vertices.size();
511 for(u32 i=0; i<numIndices; i++)
513 u32 j = indices[i] + vertex_count;
516 dstream<<"FIXME: Meshbuffer ran out of indices"<<std::endl;
517 // NOTE: Fix is to just add an another MeshBuffer
519 p->indices.push_back(j);
521 for(u32 i=0; i<numVertices; i++)
523 p->vertices.push_back(vertices[i]);
527 void fillMesh(scene::SMesh *mesh)
529 /*dstream<<"Filling mesh with "<<m_prebuffers.size()
530 <<" meshbuffers"<<std::endl;*/
531 for(u32 i=0; i<m_prebuffers.size(); i++)
533 PreMeshBuffer &p = m_prebuffers[i];
535 /*dstream<<"p.vertices.size()="<<p.vertices.size()
536 <<", p.indices.size()="<<p.indices.size()
541 // This is a "Standard MeshBuffer",
542 // it's a typedeffed CMeshBuffer<video::S3DVertex>
543 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
545 buf->Material = p.material;
546 //((scene::SMeshBuffer*)buf)->Material = p.material;
548 //buf->setHardwareMappingHint(scene::EHM_STATIC);
550 mesh->addMeshBuffer(buf);
554 buf->append(p.vertices.pointer(), p.vertices.size(),
555 p.indices.pointer(), p.indices.size());
560 core::array<PreMeshBuffer> m_prebuffers;
563 scene::SMesh* makeMapBlockMesh(MeshMakeData *data)
565 // 4-21ms for MAP_BLOCKSIZE=16
566 // 24-155ms for MAP_BLOCKSIZE=32
567 //TimeTaker timer1("makeMapBlockMesh()");
569 core::array<FastFace> fastfaces_new;
571 v3s16 blockpos_nodes = data->m_blockpos*MAP_BLOCKSIZE;
573 // floating point conversion
574 v3f posRelative_f(blockpos_nodes.X, blockpos_nodes.Y, blockpos_nodes.Z);
579 bool new_style_water = g_settings.getBool("new_style_water");
580 bool new_style_leaves = g_settings.getBool("new_style_leaves");
582 float node_water_level = 1.0;
584 node_water_level = 0.85;
587 We are including the faces of the trailing edges of the block.
588 This means that when something changes, the caller must
589 also update the meshes of the blocks at the leading edges.
591 NOTE: This is the slowest part of this method.
595 // 4-23ms for MAP_BLOCKSIZE=16
596 //TimeTaker timer2("updateMesh() collect");
599 Go through every y,z and get top faces in rows of x+
601 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
602 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
603 updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
604 v3s16(0,y,z), MAP_BLOCKSIZE,
607 v3s16(0,1,0), //face dir
616 Go through every x,y and get right faces in rows of z+
618 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
619 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
620 updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
621 v3s16(x,y,0), MAP_BLOCKSIZE,
633 Go through every y,z and get back faces in rows of x+
635 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
636 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
637 updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
638 v3s16(0,y,z), MAP_BLOCKSIZE,
654 Convert FastFaces to SMesh
657 MeshCollector collector;
659 if(fastfaces_new.size() > 0)
661 // avg 0ms (100ms spikes when loading textures the first time)
662 //TimeTaker timer2("updateMesh() mesh building");
664 video::SMaterial material;
665 material.setFlag(video::EMF_LIGHTING, false);
666 material.setFlag(video::EMF_BILINEAR_FILTER, false);
667 material.setFlag(video::EMF_FOG_ENABLE, true);
668 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
669 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_SIMPLE);
671 for(u32 i=0; i<fastfaces_new.size(); i++)
673 FastFace &f = fastfaces_new[i];
675 const u16 indices[] = {0,1,2,2,3,0};
677 //video::ITexture *texture = g_irrlicht->getTexture(f.tile.spec);
678 video::ITexture *texture = f.tile.texture.atlas;
682 material.setTexture(0, texture);
684 f.tile.applyMaterialOptions(material);
686 collector.append(material, f.vertices, 4, indices, 6);
691 Add special graphics:
697 //TimeTaker timer2("updateMesh() adding special stuff");
699 // Flowing water material
700 video::SMaterial material_water1;
701 material_water1.setFlag(video::EMF_LIGHTING, false);
702 //material_water1.setFlag(video::EMF_BACK_FACE_CULLING, false);
703 material_water1.setFlag(video::EMF_BILINEAR_FILTER, false);
704 material_water1.setFlag(video::EMF_FOG_ENABLE, true);
705 material_water1.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
707 //material_water1.setTexture(0, g_irrlicht->getTexture("water.png"));
708 AtlasPointer pa_water1 = g_texturesource->getTexture(
709 g_texturesource->getTextureId("water.png"));
710 material_water1.setTexture(0, pa_water1.atlas);
712 // New-style leaves material
713 video::SMaterial material_leaves1;
714 material_leaves1.setFlag(video::EMF_LIGHTING, false);
715 //material_leaves1.setFlag(video::EMF_BACK_FACE_CULLING, false);
716 material_leaves1.setFlag(video::EMF_BILINEAR_FILTER, false);
717 material_leaves1.setFlag(video::EMF_FOG_ENABLE, true);
718 material_leaves1.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
720 //material_leaves1.setTexture(0, g_irrlicht->getTexture("leaves.png"));
721 AtlasPointer pa_leaves1 = g_texturesource->getTexture(
722 g_texturesource->getTextureId("leaves.png"));
723 material_leaves1.setTexture(0, pa_leaves1.atlas);
725 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
726 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
727 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
731 MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes+p);
736 if(n.d == CONTENT_TORCH)
738 video::SColor c(255,255,255,255);
740 // Wall at X+ of node
741 video::S3DVertex vertices[4] =
743 video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c, 0,1),
744 video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c, 1,1),
745 video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, 1,0),
746 video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c, 0,0),
749 v3s16 dir = unpackDir(n.dir);
751 for(s32 i=0; i<4; i++)
753 if(dir == v3s16(1,0,0))
754 vertices[i].Pos.rotateXZBy(0);
755 if(dir == v3s16(-1,0,0))
756 vertices[i].Pos.rotateXZBy(180);
757 if(dir == v3s16(0,0,1))
758 vertices[i].Pos.rotateXZBy(90);
759 if(dir == v3s16(0,0,-1))
760 vertices[i].Pos.rotateXZBy(-90);
761 if(dir == v3s16(0,-1,0))
762 vertices[i].Pos.rotateXZBy(45);
763 if(dir == v3s16(0,1,0))
764 vertices[i].Pos.rotateXZBy(-45);
766 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
770 video::SMaterial material;
771 material.setFlag(video::EMF_LIGHTING, false);
772 material.setFlag(video::EMF_BACK_FACE_CULLING, false);
773 material.setFlag(video::EMF_BILINEAR_FILTER, false);
774 //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
775 material.MaterialType
776 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
778 if(dir == v3s16(0,-1,0))
779 material.setTexture(0,
780 g_texturesource->getTextureRaw("torch_on_floor.png"));
781 else if(dir == v3s16(0,1,0))
782 material.setTexture(0,
783 g_texturesource->getTextureRaw("torch_on_ceiling.png"));
784 // For backwards compatibility
785 else if(dir == v3s16(0,0,0))
786 material.setTexture(0,
787 g_texturesource->getTextureRaw("torch_on_floor.png"));
789 material.setTexture(0,
790 g_texturesource->getTextureRaw("torch.png"));
792 u16 indices[] = {0,1,2,2,3,0};
793 // Add to mesh collector
794 collector.append(material, vertices, 4, indices, 6);
799 if(n.d == CONTENT_SIGN_WALL)
801 u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
802 video::SColor c(255,l,l,l);
804 float d = (float)BS/16;
805 // Wall at X+ of node
806 video::S3DVertex vertices[4] =
808 video::S3DVertex(BS/2-d,-BS/2,-BS/2, 0,0,0, c, 0,1),
809 video::S3DVertex(BS/2-d,-BS/2,BS/2, 0,0,0, c, 1,1),
810 video::S3DVertex(BS/2-d,BS/2,BS/2, 0,0,0, c, 1,0),
811 video::S3DVertex(BS/2-d,BS/2,-BS/2, 0,0,0, c, 0,0),
814 v3s16 dir = unpackDir(n.dir);
816 for(s32 i=0; i<4; i++)
818 if(dir == v3s16(1,0,0))
819 vertices[i].Pos.rotateXZBy(0);
820 if(dir == v3s16(-1,0,0))
821 vertices[i].Pos.rotateXZBy(180);
822 if(dir == v3s16(0,0,1))
823 vertices[i].Pos.rotateXZBy(90);
824 if(dir == v3s16(0,0,-1))
825 vertices[i].Pos.rotateXZBy(-90);
826 if(dir == v3s16(0,-1,0))
827 vertices[i].Pos.rotateXYBy(-90);
828 if(dir == v3s16(0,1,0))
829 vertices[i].Pos.rotateXYBy(90);
831 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
835 video::SMaterial material;
836 material.setFlag(video::EMF_LIGHTING, false);
837 material.setFlag(video::EMF_BACK_FACE_CULLING, false);
838 material.setFlag(video::EMF_BILINEAR_FILTER, false);
839 material.setFlag(video::EMF_FOG_ENABLE, true);
840 //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
841 material.MaterialType
842 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
844 material.setTexture(0,
845 g_texturesource->getTextureRaw("sign_wall.png"));
847 u16 indices[] = {0,1,2,2,3,0};
848 // Add to mesh collector
849 collector.append(material, vertices, 4, indices, 6);
852 Add flowing water to mesh
854 else if(n.d == CONTENT_WATER)
856 bool top_is_water = false;
857 MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
858 if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
861 u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
862 video::SColor c(WATER_ALPHA,l,l,l);
864 // Neighbor water levels (key = relative position)
865 // Includes current node
866 core::map<v3s16, f32> neighbor_levels;
867 core::map<v3s16, u8> neighbor_contents;
868 core::map<v3s16, u8> neighbor_flags;
869 const u8 neighborflag_top_is_water = 0x01;
870 v3s16 neighbor_dirs[9] = {
881 for(u32 i=0; i<9; i++)
883 u8 content = CONTENT_AIR;
884 float level = -0.5 * BS;
887 v3s16 p2 = p + neighbor_dirs[i];
888 MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
889 if(n2.d != CONTENT_IGNORE)
893 if(n2.d == CONTENT_WATERSOURCE)
894 level = (-0.5+node_water_level) * BS;
895 else if(n2.d == CONTENT_WATER)
896 level = (-0.5 + ((float)n2.param2 + 0.5) / 8.0
897 * node_water_level) * BS;
899 // Check node above neighbor.
900 // NOTE: This doesn't get executed if neighbor
903 n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
904 if(n2.d == CONTENT_WATERSOURCE || n2.d == CONTENT_WATER)
905 flags |= neighborflag_top_is_water;
908 neighbor_levels.insert(neighbor_dirs[i], level);
909 neighbor_contents.insert(neighbor_dirs[i], content);
910 neighbor_flags.insert(neighbor_dirs[i], flags);
913 //float water_level = (-0.5 + ((float)n.param2 + 0.5) / 8.0) * BS;
914 //float water_level = neighbor_levels[v3s16(0,0,0)];
916 // Corner heights (average between four waters)
917 f32 corner_levels[4];
919 v3s16 halfdirs[4] = {
925 for(u32 i=0; i<4; i++)
927 v3s16 cornerdir = halfdirs[i];
928 float cornerlevel = 0;
930 for(u32 j=0; j<4; j++)
932 v3s16 neighbordir = cornerdir - halfdirs[j];
933 u8 content = neighbor_contents[neighbordir];
934 // Special case for source nodes
935 if(content == CONTENT_WATERSOURCE)
937 cornerlevel = (-0.5+node_water_level)*BS;
941 else if(content == CONTENT_WATER)
943 cornerlevel += neighbor_levels[neighbordir];
946 else if(content == CONTENT_AIR)
948 cornerlevel += -0.5*BS;
953 cornerlevel /= valid_count;
954 corner_levels[i] = cornerlevel;
961 v3s16 side_dirs[4] = {
967 s16 side_corners[4][2] = {
973 for(u32 i=0; i<4; i++)
975 v3s16 dir = side_dirs[i];
978 If our topside is water and neighbor's topside
979 is water, don't draw side face
982 neighbor_flags[dir] & neighborflag_top_is_water)
985 u8 neighbor_content = neighbor_contents[dir];
987 // Don't draw face if neighbor is not air or water
988 if(neighbor_content != CONTENT_AIR
989 && neighbor_content != CONTENT_WATER)
992 bool neighbor_is_water = (neighbor_content == CONTENT_WATER);
994 // Don't draw any faces if neighbor is water and top is water
995 if(neighbor_is_water == true && top_is_water == false)
998 video::S3DVertex vertices[4] =
1000 /*video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1),
1001 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1),
1002 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1003 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1004 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1005 pa_water1.x0(), pa_water1.y1()),
1006 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1007 pa_water1.x1(), pa_water1.y1()),
1008 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1009 pa_water1.x1(), pa_water1.y0()),
1010 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1011 pa_water1.x0(), pa_water1.y0()),
1015 If our topside is water, set upper border of face
1016 at upper border of node
1020 vertices[2].Pos.Y = 0.5*BS;
1021 vertices[3].Pos.Y = 0.5*BS;
1024 Otherwise upper position of face is corner levels
1028 vertices[2].Pos.Y = corner_levels[side_corners[i][0]];
1029 vertices[3].Pos.Y = corner_levels[side_corners[i][1]];
1033 If neighbor is water, lower border of face is corner
1036 if(neighbor_is_water)
1038 vertices[0].Pos.Y = corner_levels[side_corners[i][1]];
1039 vertices[1].Pos.Y = corner_levels[side_corners[i][0]];
1042 If neighbor is not water, lower border of face is
1043 lower border of node
1047 vertices[0].Pos.Y = -0.5*BS;
1048 vertices[1].Pos.Y = -0.5*BS;
1051 for(s32 j=0; j<4; j++)
1053 if(dir == v3s16(0,0,1))
1054 vertices[j].Pos.rotateXZBy(0);
1055 if(dir == v3s16(0,0,-1))
1056 vertices[j].Pos.rotateXZBy(180);
1057 if(dir == v3s16(-1,0,0))
1058 vertices[j].Pos.rotateXZBy(90);
1059 if(dir == v3s16(1,0,-0))
1060 vertices[j].Pos.rotateXZBy(-90);
1062 vertices[j].Pos += intToFloat(p + blockpos_nodes, BS);
1065 u16 indices[] = {0,1,2,2,3,0};
1066 // Add to mesh collector
1067 collector.append(material_water1, vertices, 4, indices, 6);
1071 Generate top side, if appropriate
1074 if(top_is_water == false)
1076 video::S3DVertex vertices[4] =
1078 /*video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
1079 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
1080 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1081 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1082 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1083 pa_water1.x0(), pa_water1.y1()),
1084 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1085 pa_water1.x1(), pa_water1.y1()),
1086 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
1087 pa_water1.x1(), pa_water1.y0()),
1088 video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
1089 pa_water1.x0(), pa_water1.y0()),
1092 // This fixes a strange bug
1093 s32 corner_resolve[4] = {3,2,1,0};
1095 for(s32 i=0; i<4; i++)
1097 //vertices[i].Pos.Y += water_level;
1098 //vertices[i].Pos.Y += neighbor_levels[v3s16(0,0,0)];
1099 s32 j = corner_resolve[i];
1100 vertices[i].Pos.Y += corner_levels[j];
1101 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1104 u16 indices[] = {0,1,2,2,3,0};
1105 // Add to mesh collector
1106 collector.append(material_water1, vertices, 4, indices, 6);
1110 Add water sources to mesh if using new style
1112 else if(n.d == CONTENT_WATERSOURCE && new_style_water)
1114 //bool top_is_water = false;
1115 bool top_is_air = false;
1116 MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
1117 /*if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
1118 top_is_water = true;*/
1119 if(n.d == CONTENT_AIR)
1122 /*if(top_is_water == true)
1124 if(top_is_air == false)
1127 u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
1128 video::SColor c(WATER_ALPHA,l,l,l);
1130 video::S3DVertex vertices[4] =
1132 /*video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
1133 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
1134 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1135 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1136 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1137 pa_water1.x0(), pa_water1.y1()),
1138 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1139 pa_water1.x1(), pa_water1.y1()),
1140 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
1141 pa_water1.x1(), pa_water1.y0()),
1142 video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
1143 pa_water1.x0(), pa_water1.y0()),
1146 for(s32 i=0; i<4; i++)
1148 vertices[i].Pos.Y += (-0.5+node_water_level)*BS;
1149 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1152 u16 indices[] = {0,1,2,2,3,0};
1153 // Add to mesh collector
1154 collector.append(material_water1, vertices, 4, indices, 6);
1157 Add leaves if using new style
1159 else if(n.d == CONTENT_LEAVES && new_style_leaves)
1161 /*u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));*/
1162 u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio)));
1163 video::SColor c(255,l,l,l);
1165 for(u32 j=0; j<6; j++)
1167 video::S3DVertex vertices[4] =
1169 /*video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, 0,1),
1170 video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, 1,1),
1171 video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c, 1,0),
1172 video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c, 0,0),*/
1173 video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c,
1174 pa_leaves1.x0(), pa_leaves1.y1()),
1175 video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c,
1176 pa_leaves1.x1(), pa_leaves1.y1()),
1177 video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c,
1178 pa_leaves1.x1(), pa_leaves1.y0()),
1179 video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c,
1180 pa_leaves1.x0(), pa_leaves1.y0()),
1185 for(u16 i=0; i<4; i++)
1186 vertices[i].Pos.rotateXZBy(0);
1190 for(u16 i=0; i<4; i++)
1191 vertices[i].Pos.rotateXZBy(180);
1195 for(u16 i=0; i<4; i++)
1196 vertices[i].Pos.rotateXZBy(-90);
1200 for(u16 i=0; i<4; i++)
1201 vertices[i].Pos.rotateXZBy(90);
1205 for(u16 i=0; i<4; i++)
1206 vertices[i].Pos.rotateYZBy(-90);
1210 for(u16 i=0; i<4; i++)
1211 vertices[i].Pos.rotateYZBy(90);
1214 for(u16 i=0; i<4; i++)
1216 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1219 u16 indices[] = {0,1,2,2,3,0};
1220 // Add to mesh collector
1221 collector.append(material_leaves1, vertices, 4, indices, 6);
1227 Add stuff from collector to mesh
1230 scene::SMesh *mesh_new = NULL;
1231 mesh_new = new scene::SMesh();
1233 collector.fillMesh(mesh_new);
1236 Do some stuff to the mesh
1239 mesh_new->recalculateBoundingBox();
1242 Delete new mesh if it is empty
1245 if(mesh_new->getMeshBufferCount() == 0)
1254 // Usually 1-700 faces and 1-7 materials
1255 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
1256 <<"and uses "<<mesh_new->getMeshBufferCount()
1257 <<" materials (meshbuffers)"<<std::endl;
1260 // Use VBO for mesh (this just would set this for ever buffer)
1261 // This will lead to infinite memory usage because or irrlicht.
1262 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
1265 NOTE: If that is enabled, some kind of a queue to the main
1266 thread should be made which would call irrlicht to delete
1267 the hardware buffer and then delete the mesh
1273 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1281 MapBlock::MapBlock(NodeContainer *parent, v3s16 pos, bool dummy):
1285 is_underground(false),
1286 m_lighting_expired(true),
1287 m_day_night_differs(false),
1288 //m_not_fully_generated(false),
1295 m_spawn_timer = -10000;
1298 m_mesh_expired = false;
1301 m_temp_mods_mutex.Init();
1305 MapBlock::~MapBlock()
1309 JMutexAutoLock lock(mesh_mutex);
1323 bool MapBlock::isValidPositionParent(v3s16 p)
1325 if(isValidPosition(p))
1330 return m_parent->isValidPosition(getPosRelative() + p);
1334 MapNode MapBlock::getNodeParent(v3s16 p)
1336 if(isValidPosition(p) == false)
1338 return m_parent->getNode(getPosRelative() + p);
1343 throw InvalidPositionException();
1344 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
1348 void MapBlock::setNodeParent(v3s16 p, MapNode & n)
1350 if(isValidPosition(p) == false)
1352 m_parent->setNode(getPosRelative() + p, n);
1357 throw InvalidPositionException();
1358 data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X] = n;
1362 MapNode MapBlock::getNodeParentNoEx(v3s16 p)
1364 if(isValidPosition(p) == false)
1367 return m_parent->getNode(getPosRelative() + p);
1369 catch(InvalidPositionException &e)
1371 return MapNode(CONTENT_IGNORE);
1378 return MapNode(CONTENT_IGNORE);
1380 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
1385 void MapBlock::updateMesh(u32 daynight_ratio)
1389 DEBUG: If mesh has been generated, don't generate it again
1392 JMutexAutoLock meshlock(mesh_mutex);
1399 data.fill(daynight_ratio, this);
1401 scene::SMesh *mesh_new = makeMapBlockMesh(&data);
1407 replaceMesh(mesh_new);
1412 void MapBlock::replaceMesh(scene::SMesh *mesh_new)
1416 //scene::SMesh *mesh_old = mesh[daynight_i];
1417 //mesh[daynight_i] = mesh_new;
1419 scene::SMesh *mesh_old = mesh;
1421 setMeshExpired(false);
1423 if(mesh_old != NULL)
1425 // Remove hardware buffers of meshbuffers of mesh
1426 // NOTE: No way, this runs in a different thread and everything
1427 /*u32 c = mesh_old->getMeshBufferCount();
1428 for(u32 i=0; i<c; i++)
1430 IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1433 /*dstream<<"mesh_old->getReferenceCount()="
1434 <<mesh_old->getReferenceCount()<<std::endl;
1435 u32 c = mesh_old->getMeshBufferCount();
1436 for(u32 i=0; i<c; i++)
1438 scene::IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1439 dstream<<"buf->getReferenceCount()="
1440 <<buf->getReferenceCount()<<std::endl;
1449 mesh_mutex.Unlock();
1455 Propagates sunlight down through the block.
1456 Doesn't modify nodes that are not affected by sunlight.
1458 Returns false if sunlight at bottom block is invalid
1459 Returns true if bottom block doesn't exist.
1461 If there is a block above, continues from it.
1462 If there is no block above, assumes there is sunlight, unless
1463 is_underground is set or highest node is water.
1465 At the moment, all sunlighted nodes are added to light_sources.
1466 - SUGG: This could be optimized
1468 Turns sunglighted mud into grass.
1470 if remove_light==true, sets non-sunlighted nodes black.
1472 if black_air_left!=NULL, it is set to true if non-sunlighted
1473 air is left in block.
1475 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources,
1476 bool remove_light, bool *black_air_left,
1479 // Whether the sunlight at the top of the bottom block is valid
1480 bool block_below_is_valid = true;
1482 v3s16 pos_relative = getPosRelative();
1484 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
1486 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
1489 bool no_sunlight = false;
1490 bool no_top_block = false;
1491 // Check if node above block has sunlight
1493 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1494 if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1499 catch(InvalidPositionException &e)
1501 no_top_block = true;
1503 // NOTE: This makes over-ground roofed places sunlighted
1504 // Assume sunlight, unless is_underground==true
1511 MapNode n = getNode(v3s16(x, MAP_BLOCKSIZE-1, z));
1512 if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
1517 // NOTE: As of now, this just would make everything dark.
1519 //no_sunlight = true;
1522 #if 0 // Doesn't work; nothing gets light.
1523 bool no_sunlight = true;
1524 bool no_top_block = false;
1525 // Check if node above block has sunlight
1527 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1528 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
1530 no_sunlight = false;
1533 catch(InvalidPositionException &e)
1535 no_top_block = true;
1539 /*std::cout<<"("<<x<<","<<z<<"): "
1540 <<"no_top_block="<<no_top_block
1541 <<", is_underground="<<is_underground
1542 <<", no_sunlight="<<no_sunlight
1545 s16 y = MAP_BLOCKSIZE-1;
1547 // This makes difference to diminishing in water.
1548 bool stopped_to_solid_object = false;
1550 u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
1555 MapNode &n = getNodeRef(pos);
1557 if(current_light == 0)
1561 else if(current_light == LIGHT_SUN && n.sunlight_propagates())
1563 // Do nothing: Sunlight is continued
1565 else if(n.light_propagates() == false)
1569 bool upper_is_air = false;
1572 if(getNodeParent(pos+v3s16(0,1,0)).d == CONTENT_AIR)
1573 upper_is_air = true;
1575 catch(InvalidPositionException &e)
1578 // Turn mud into grass
1579 if(upper_is_air && n.d == CONTENT_MUD
1580 && current_light == LIGHT_SUN)
1582 n.d = CONTENT_GRASS;
1586 // A solid object is on the way.
1587 stopped_to_solid_object = true;
1595 current_light = diminish_light(current_light);
1598 u8 old_light = n.getLight(LIGHTBANK_DAY);
1600 if(current_light > old_light || remove_light)
1602 n.setLight(LIGHTBANK_DAY, current_light);
1605 if(diminish_light(current_light) != 0)
1607 light_sources.insert(pos_relative + pos, true);
1610 if(current_light == 0 && stopped_to_solid_object)
1614 *black_air_left = true;
1619 // Whether or not the block below should see LIGHT_SUN
1620 bool sunlight_should_go_down = (current_light == LIGHT_SUN);
1623 If the block below hasn't already been marked invalid:
1625 Check if the node below the block has proper sunlight at top.
1626 If not, the block below is invalid.
1628 Ignore non-transparent nodes as they always have no light
1632 if(block_below_is_valid)
1634 MapNode n = getNodeParent(v3s16(x, -1, z));
1635 if(n.light_propagates())
1637 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN
1638 && sunlight_should_go_down == false)
1639 block_below_is_valid = false;
1640 else if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN
1641 && sunlight_should_go_down == true)
1642 block_below_is_valid = false;
1646 catch(InvalidPositionException &e)
1648 /*std::cout<<"InvalidBlockException for bottom block node"
1650 // Just no block below, no need to panic.
1655 return block_below_is_valid;
1659 void MapBlock::copyTo(VoxelManipulator &dst)
1661 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
1662 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
1664 // Copy from data to VoxelManipulator
1665 dst.copyFrom(data, data_area, v3s16(0,0,0),
1666 getPosRelative(), data_size);
1669 void MapBlock::copyFrom(VoxelManipulator &dst)
1671 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
1672 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
1674 // Copy from VoxelManipulator to data
1675 dst.copyTo(data, data_area, v3s16(0,0,0),
1676 getPosRelative(), data_size);
1679 void MapBlock::stepObjects(float dtime, bool server, u32 daynight_ratio)
1684 m_objects.step(dtime, server, daynight_ratio);
1687 Spawn some objects at random.
1689 Use dayNightDiffed() to approximate being near ground level
1691 if(m_spawn_timer < -999)
1695 if(dayNightDiffed() == true && getObjectCount() == 0)
1697 m_spawn_timer -= dtime;
1698 if(m_spawn_timer <= 0.0)
1700 m_spawn_timer += myrand() % 300;
1703 (myrand()%(MAP_BLOCKSIZE-1))+0,
1704 (myrand()%(MAP_BLOCKSIZE-1))+0
1707 s16 y = getGroundLevel(p2d);
1711 v3s16 p(p2d.X, y+1, p2d.Y);
1713 if(getNode(p).d == CONTENT_AIR
1714 && getNode(p).getLightBlend(daynight_ratio) <= 11)
1716 RatObject *obj = new RatObject(NULL, -1, intToFloat(p, BS));
1727 void MapBlock::updateDayNightDiff()
1731 m_day_night_differs = false;
1735 bool differs = false;
1738 Check if any lighting value differs
1740 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1742 MapNode &n = data[i];
1743 if(n.getLight(LIGHTBANK_DAY) != n.getLight(LIGHTBANK_NIGHT))
1751 If some lighting values differ, check if the whole thing is
1752 just air. If it is, differ = false
1756 bool only_air = true;
1757 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1759 MapNode &n = data[i];
1760 if(n.d != CONTENT_AIR)
1770 // Set member variable
1771 m_day_night_differs = differs;
1774 s16 MapBlock::getGroundLevel(v2s16 p2d)
1780 s16 y = MAP_BLOCKSIZE-1;
1783 //if(is_ground_content(getNodeRef(p2d.X, y, p2d.Y).d))
1784 if(content_features(getNodeRef(p2d.X, y, p2d.Y).d).walkable)
1786 if(y == MAP_BLOCKSIZE-1)
1794 catch(InvalidPositionException &e)
1804 void MapBlock::serialize(std::ostream &os, u8 version)
1806 if(!ser_ver_supported(version))
1807 throw VersionMismatchException("ERROR: MapBlock format not supported");
1811 throw SerializationError("ERROR: Not writing dummy block.");
1814 // These have no compression
1815 if(version <= 3 || version == 5 || version == 6)
1817 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1819 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
1820 SharedBuffer<u8> dest(buflen);
1822 dest[0] = is_underground;
1823 for(u32 i=0; i<nodecount; i++)
1825 u32 s = 1 + i * MapNode::serializedLength(version);
1826 data[i].serialize(&dest[s], version);
1829 os.write((char*)*dest, dest.getSize());
1831 else if(version <= 10)
1835 Compress the materials and the params separately.
1839 os.write((char*)&is_underground, 1);
1841 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1843 // Get and compress materials
1844 SharedBuffer<u8> materialdata(nodecount);
1845 for(u32 i=0; i<nodecount; i++)
1847 materialdata[i] = data[i].d;
1849 compress(materialdata, os, version);
1851 // Get and compress lights
1852 SharedBuffer<u8> lightdata(nodecount);
1853 for(u32 i=0; i<nodecount; i++)
1855 lightdata[i] = data[i].param;
1857 compress(lightdata, os, version);
1861 // Get and compress param2
1862 SharedBuffer<u8> param2data(nodecount);
1863 for(u32 i=0; i<nodecount; i++)
1865 param2data[i] = data[i].param2;
1867 compress(param2data, os, version);
1870 // All other versions (newest)
1877 if(m_day_night_differs)
1879 if(m_lighting_expired)
1881 /*if(m_not_fully_generated)
1883 os.write((char*)&flags, 1);
1885 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1891 SharedBuffer<u8> databuf(nodecount*3);
1894 for(u32 i=0; i<nodecount; i++)
1896 databuf[i] = data[i].d;
1900 for(u32 i=0; i<nodecount; i++)
1902 databuf[i+nodecount] = data[i].param;
1906 for(u32 i=0; i<nodecount; i++)
1908 databuf[i+nodecount*2] = data[i].param2;
1912 Compress data to output stream
1915 compress(databuf, os, version);
1919 void MapBlock::deSerialize(std::istream &is, u8 version)
1921 if(!ser_ver_supported(version))
1922 throw VersionMismatchException("ERROR: MapBlock format not supported");
1924 // These have no compression
1925 if(version <= 3 || version == 5 || version == 6)
1927 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1930 if(is.gcount() != 1)
1931 throw SerializationError
1932 ("MapBlock::deSerialize: no enough input data");
1933 is_underground = tmp;
1934 for(u32 i=0; i<nodecount; i++)
1936 s32 len = MapNode::serializedLength(version);
1937 SharedBuffer<u8> d(len);
1938 is.read((char*)*d, len);
1939 if(is.gcount() != len)
1940 throw SerializationError
1941 ("MapBlock::deSerialize: no enough input data");
1942 data[i].deSerialize(*d, version);
1945 else if(version <= 10)
1947 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1950 is.read((char*)&t8, 1);
1951 is_underground = t8;
1954 // Uncompress and set material data
1955 std::ostringstream os(std::ios_base::binary);
1956 decompress(is, os, version);
1957 std::string s = os.str();
1958 if(s.size() != nodecount)
1959 throw SerializationError
1960 ("MapBlock::deSerialize: invalid format");
1961 for(u32 i=0; i<s.size(); i++)
1967 // Uncompress and set param data
1968 std::ostringstream os(std::ios_base::binary);
1969 decompress(is, os, version);
1970 std::string s = os.str();
1971 if(s.size() != nodecount)
1972 throw SerializationError
1973 ("MapBlock::deSerialize: invalid format");
1974 for(u32 i=0; i<s.size(); i++)
1976 data[i].param = s[i];
1982 // Uncompress and set param2 data
1983 std::ostringstream os(std::ios_base::binary);
1984 decompress(is, os, version);
1985 std::string s = os.str();
1986 if(s.size() != nodecount)
1987 throw SerializationError
1988 ("MapBlock::deSerialize: invalid format");
1989 for(u32 i=0; i<s.size(); i++)
1991 data[i].param2 = s[i];
1995 // All other versions (newest)
1998 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2001 is.read((char*)&flags, 1);
2002 is_underground = (flags & 0x01) ? true : false;
2003 m_day_night_differs = (flags & 0x02) ? true : false;
2004 m_lighting_expired = (flags & 0x04) ? true : false;
2005 //m_not_fully_generated = (flags & 0x08) ? true : false;
2008 std::ostringstream os(std::ios_base::binary);
2009 decompress(is, os, version);
2010 std::string s = os.str();
2011 if(s.size() != nodecount*3)
2012 throw SerializationError
2013 ("MapBlock::deSerialize: invalid format");
2016 for(u32 i=0; i<nodecount; i++)
2021 for(u32 i=0; i<nodecount; i++)
2023 data[i].param = s[i+nodecount];
2026 for(u32 i=0; i<nodecount; i++)
2028 data[i].param2 = s[i+nodecount*2];
2033 Translate nodes as specified in the translate_to fields of
2036 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
2038 MapNode &n = data[i];
2040 MapNode *translate_to = content_features(n.d).translate_to;
2043 dstream<<"MapBlock: WARNING: Translating node "<<n.d<<" to "
2044 <<translate_to->d<<std::endl;