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.
28 void MeshMakeData::fill(u32 daynight_ratio, MapBlock *block)
30 m_daynight_ratio = daynight_ratio;
31 m_blockpos = block->getPos();
33 v3s16 blockpos_nodes = m_blockpos*MAP_BLOCKSIZE;
36 There is no harm not copying the TempMods of the neighbors
37 because they are already copied to this block
40 block->copyTempMods(m_temp_mods);
46 // Allocate this block + neighbors
48 m_vmanip.addArea(VoxelArea(blockpos_nodes-v3s16(1,1,1)*MAP_BLOCKSIZE,
49 blockpos_nodes+v3s16(1,1,1)*MAP_BLOCKSIZE*2-v3s16(1,1,1)));
52 //TimeTaker timer("copy central block data");
56 block->copyTo(m_vmanip);
59 //TimeTaker timer("copy neighbor block data");
63 Copy neighbors. This is lightning fast.
64 Copying only the borders would be *very* slow.
68 NodeContainer *parentcontainer = block->getParent();
69 // This will only work if the parent is the map
70 assert(parentcontainer->nodeContainerId() == NODECONTAINER_ID_MAP);
71 // OK, we have the map!
72 Map *map = (Map*)parentcontainer;
74 for(u16 i=0; i<6; i++)
76 const v3s16 &dir = g_6dirs[i];
77 v3s16 bp = m_blockpos + dir;
78 MapBlock *b = map->getBlockNoCreateNoEx(bp);
87 Parameters must consist of air and !air.
90 If either of the nodes doesn't exist, light is 0.
93 daynight_ratio: 0...1000
95 n2: getNodeParent(p + face_dir)
96 face_dir: axis oriented unit vector from p to p2
98 returns encoded light value.
100 u8 getFaceLight(u32 daynight_ratio, MapNode n, MapNode n2,
105 u8 l1 = n.getLightBlend(daynight_ratio);
106 u8 l2 = n2.getLightBlend(daynight_ratio);
112 // Make some nice difference to different sides
114 // This makes light come from a corner
115 /*if(face_dir.X == 1 || face_dir.Z == 1 || face_dir.Y == -1)
116 light = diminish_light(diminish_light(light));
117 else if(face_dir.X == -1 || face_dir.Z == -1)
118 light = diminish_light(light);*/
120 // All neighboring faces have different shade (like in minecraft)
121 if(face_dir.X == 1 || face_dir.X == -1 || face_dir.Y == -1)
122 light = diminish_light(diminish_light(light));
123 else if(face_dir.Z == 1 || face_dir.Z == -1)
124 light = diminish_light(light);
128 catch(InvalidPositionException &e)
136 void makeFastFace(TileSpec tile, u8 light, v3f p,
137 v3s16 dir, v3f scale, v3f posRelative_f,
138 core::array<FastFace> &dest)
142 // Position is at the center of the cube.
147 // If looking towards z+, this is the face that is behind
148 // the center point, facing towards z+.
149 vertex_pos[0] = v3f(-BS/2,-BS/2,BS/2);
150 vertex_pos[1] = v3f( BS/2,-BS/2,BS/2);
151 vertex_pos[2] = v3f( BS/2, BS/2,BS/2);
152 vertex_pos[3] = v3f(-BS/2, BS/2,BS/2);
154 if(dir == v3s16(0,0,1))
156 for(u16 i=0; i<4; i++)
157 vertex_pos[i].rotateXZBy(0);
159 else if(dir == v3s16(0,0,-1))
161 for(u16 i=0; i<4; i++)
162 vertex_pos[i].rotateXZBy(180);
164 else if(dir == v3s16(1,0,0))
166 for(u16 i=0; i<4; i++)
167 vertex_pos[i].rotateXZBy(-90);
169 else if(dir == v3s16(-1,0,0))
171 for(u16 i=0; i<4; i++)
172 vertex_pos[i].rotateXZBy(90);
174 else if(dir == v3s16(0,1,0))
176 for(u16 i=0; i<4; i++)
177 vertex_pos[i].rotateYZBy(-90);
179 else if(dir == v3s16(0,-1,0))
181 for(u16 i=0; i<4; i++)
182 vertex_pos[i].rotateYZBy(90);
185 for(u16 i=0; i<4; i++)
187 vertex_pos[i].X *= scale.X;
188 vertex_pos[i].Y *= scale.Y;
189 vertex_pos[i].Z *= scale.Z;
190 vertex_pos[i] += pos + posRelative_f;
194 if (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X;
195 else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
196 else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
198 v3f zerovector = v3f(0,0,0);
200 //u8 li = decode_light(light);
202 //u8 li = 255; //DEBUG
204 u8 alpha = tile.alpha;
206 if(tile.id == TILE_WATER)
207 alpha = WATER_ALPHA;*/
209 video::SColor c = video::SColor(alpha,li,li,li);
211 float x0 = tile.texture.pos.X;
212 float y0 = tile.texture.pos.Y;
213 float w = tile.texture.size.X;
214 float h = tile.texture.size.Y;
216 face.vertices[0] = video::S3DVertex(vertex_pos[0], v3f(0,1,0), c,
217 core::vector2d<f32>(x0+w*abs_scale, y0+h));
218 face.vertices[1] = video::S3DVertex(vertex_pos[1], v3f(0,1,0), c,
219 core::vector2d<f32>(x0, y0+h));
220 face.vertices[2] = video::S3DVertex(vertex_pos[2], v3f(0,1,0), c,
221 core::vector2d<f32>(x0, y0));
222 face.vertices[3] = video::S3DVertex(vertex_pos[3], v3f(0,1,0), c,
223 core::vector2d<f32>(x0+w*abs_scale, y0));
227 //f->tile = TILE_STONE;
229 dest.push_back(face);
234 Gets node tile from any place relative to block.
235 Returns TILE_NODE if doesn't exist or should not be drawn.
237 TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir,
238 NodeModMap &temp_mods)
241 spec = mn.getTile(face_dir);
244 Check temporary modifications on this node
246 /*core::map<v3s16, NodeMod>::Node *n;
247 n = m_temp_mods.find(p);
251 struct NodeMod mod = n->getValue();*/
253 if(temp_mods.get(p, &mod))
255 if(mod.type == NODEMOD_CHANGECONTENT)
257 MapNode mn2(mod.param);
258 spec = mn2.getTile(face_dir);
260 if(mod.type == NODEMOD_CRACK)
263 Get texture id, translate it to name, append stuff to
267 // Get original texture name
268 u32 orig_id = spec.texture.id;
269 std::string orig_name = g_texturesource->getTextureName(orig_id);
271 // Create new texture name
272 std::ostringstream os;
273 os<<orig_name<<"^[crack"<<mod.param;
276 u32 new_id = g_texturesource->getTextureId(os.str());
278 /*dstream<<"MapBlock::getNodeTile(): Switching from "
279 <<orig_name<<" to "<<os.str()<<" ("
280 <<orig_id<<" to "<<new_id<<")"<<std::endl;*/
282 spec.texture = g_texturesource->getTexture(new_id);
289 u8 getNodeContent(v3s16 p, MapNode mn, NodeModMap &temp_mods)
292 Check temporary modifications on this node
294 /*core::map<v3s16, NodeMod>::Node *n;
295 n = m_temp_mods.find(p);
299 struct NodeMod mod = n->getValue();*/
301 if(temp_mods.get(p, &mod))
303 if(mod.type == NODEMOD_CHANGECONTENT)
308 if(mod.type == NODEMOD_CRACK)
311 Content doesn't change.
313 face_contents works just like it should, because
314 there should not be faces between differently cracked
317 If a semi-transparent node is cracked in front an
318 another one, it really doesn't matter whether there
319 is a cracked face drawn in between or not.
329 translate_dir: unit vector with only one of x, y or z
330 face_dir: unit vector with only one of x, y or z
332 void updateFastFaceRow(
341 core::array<FastFace> &dest,
342 NodeModMap &temp_mods,
343 VoxelManipulator &vmanip,
344 v3s16 blockpos_nodes)
348 u16 continuous_tiles_count = 0;
350 MapNode n0 = vmanip.getNodeNoEx(blockpos_nodes + p);
351 MapNode n1 = vmanip.getNodeNoEx(blockpos_nodes + p + face_dir);
352 TileSpec tile0 = getNodeTile(n0, p, face_dir, temp_mods);
353 TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir, temp_mods);
354 u8 light = getFaceLight(daynight_ratio, n0, n1, face_dir);
356 for(u16 j=0; j<length; j++)
358 bool next_is_different = true;
367 // If at last position, there is nothing to compare to and
368 // the face must be drawn anyway
371 p_next = p + translate_dir;
373 n0_next = vmanip.getNodeNoEx(blockpos_nodes + p_next);
374 n1_next = vmanip.getNodeNoEx(blockpos_nodes + p_next + face_dir);
375 tile0_next = getNodeTile(n0_next, p_next, face_dir, temp_mods);
376 tile1_next = getNodeTile(n1_next,p_next+face_dir,-face_dir, temp_mods);
377 light_next = getFaceLight(daynight_ratio, n0_next, n1_next, face_dir);
379 if(tile0_next == tile0
380 && tile1_next == tile1
381 && light_next == light)
383 next_is_different = false;
387 continuous_tiles_count++;
389 // This is set to true if the texture doesn't allow more tiling
390 bool end_of_texture = false;
392 If there is no texture, it can be tiled infinitely.
393 If tiled==0, it means the texture can be tiled infinitely.
394 Otherwise check tiled agains continuous_tiles_count.
396 This check has to be made for both tiles, because this is
397 a bit hackish and we know which one we're using only when
398 the decision to make the faces is made.
400 if(tile0.texture.atlas != NULL && tile0.texture.tiled != 0)
402 if(tile0.texture.tiled <= continuous_tiles_count)
403 end_of_texture = true;
405 if(tile1.texture.atlas != NULL && tile1.texture.tiled != 0)
407 if(tile1.texture.tiled <= continuous_tiles_count)
408 end_of_texture = true;
411 //end_of_texture = true; //DEBUG
413 if(next_is_different || end_of_texture)
416 Create a face if there should be one
418 //u8 mf = face_contents(tile0, tile1);
420 u8 content0 = getNodeContent(p, n0, temp_mods);
421 u8 content1 = getNodeContent(p + face_dir, n1, temp_mods);
422 u8 mf = face_contents(content0, content1);
426 // Floating point conversion of the position vector
427 v3f pf(p.X, p.Y, p.Z);
428 // Center point of face (kind of)
429 v3f sp = pf - ((f32)continuous_tiles_count / 2. - 0.5) * translate_dir_f;
431 if(translate_dir.X != 0){
432 scale.X = continuous_tiles_count;
434 if(translate_dir.Y != 0){
435 scale.Y = continuous_tiles_count;
437 if(translate_dir.Z != 0){
438 scale.Z = continuous_tiles_count;
443 // If node at sp (tile0) is more solid
446 makeFastFace(tile0, decode_light(light),
448 posRelative_f, dest);
450 // If node at sp is less solid (mf == 2)
453 makeFastFace(tile1, decode_light(light),
454 sp+face_dir_f, -face_dir, scale,
455 posRelative_f, dest);
460 continuous_tiles_count = 0;
473 This is used because CMeshBuffer::append() is very slow
477 video::SMaterial material;
478 core::array<u16> indices;
479 core::array<video::S3DVertex> vertices;
486 video::SMaterial material,
487 const video::S3DVertex* const vertices,
489 const u16* const indices,
493 PreMeshBuffer *p = NULL;
494 for(u32 i=0; i<m_prebuffers.size(); i++)
496 PreMeshBuffer &pp = m_prebuffers[i];
497 if(pp.material != material)
507 pp.material = material;
508 m_prebuffers.push_back(pp);
509 p = &m_prebuffers[m_prebuffers.size()-1];
512 u32 vertex_count = p->vertices.size();
513 for(u32 i=0; i<numIndices; i++)
515 u32 j = indices[i] + vertex_count;
518 dstream<<"FIXME: Meshbuffer ran out of indices"<<std::endl;
519 // NOTE: Fix is to just add an another MeshBuffer
521 p->indices.push_back(j);
523 for(u32 i=0; i<numVertices; i++)
525 p->vertices.push_back(vertices[i]);
529 void fillMesh(scene::SMesh *mesh)
531 /*dstream<<"Filling mesh with "<<m_prebuffers.size()
532 <<" meshbuffers"<<std::endl;*/
533 for(u32 i=0; i<m_prebuffers.size(); i++)
535 PreMeshBuffer &p = m_prebuffers[i];
537 /*dstream<<"p.vertices.size()="<<p.vertices.size()
538 <<", p.indices.size()="<<p.indices.size()
543 // This is a "Standard MeshBuffer",
544 // it's a typedeffed CMeshBuffer<video::S3DVertex>
545 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
547 buf->Material = p.material;
548 //((scene::SMeshBuffer*)buf)->Material = p.material;
550 //buf->setHardwareMappingHint(scene::EHM_STATIC);
552 mesh->addMeshBuffer(buf);
556 buf->append(p.vertices.pointer(), p.vertices.size(),
557 p.indices.pointer(), p.indices.size());
562 core::array<PreMeshBuffer> m_prebuffers;
565 scene::SMesh* makeMapBlockMesh(MeshMakeData *data)
567 // 4-21ms for MAP_BLOCKSIZE=16
568 // 24-155ms for MAP_BLOCKSIZE=32
569 //TimeTaker timer1("makeMapBlockMesh()");
571 core::array<FastFace> fastfaces_new;
573 v3s16 blockpos_nodes = data->m_blockpos*MAP_BLOCKSIZE;
575 // floating point conversion
576 v3f posRelative_f(blockpos_nodes.X, blockpos_nodes.Y, blockpos_nodes.Z);
581 bool new_style_water = g_settings.getBool("new_style_water");
582 bool new_style_leaves = g_settings.getBool("new_style_leaves");
584 float node_water_level = 1.0;
586 node_water_level = 0.85;
589 We are including the faces of the trailing edges of the block.
590 This means that when something changes, the caller must
591 also update the meshes of the blocks at the leading edges.
593 NOTE: This is the slowest part of this method.
597 // 4-23ms for MAP_BLOCKSIZE=16
598 //TimeTaker timer2("updateMesh() collect");
601 Go through every y,z and get top faces in rows of x+
603 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
604 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
605 updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
606 v3s16(0,y,z), MAP_BLOCKSIZE,
609 v3s16(0,1,0), //face dir
618 Go through every x,y and get right faces in rows of z+
620 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
621 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
622 updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
623 v3s16(x,y,0), MAP_BLOCKSIZE,
635 Go through every y,z and get back faces in rows of x+
637 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
638 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
639 updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
640 v3s16(0,y,z), MAP_BLOCKSIZE,
656 Convert FastFaces to SMesh
659 MeshCollector collector;
661 if(fastfaces_new.size() > 0)
663 // avg 0ms (100ms spikes when loading textures the first time)
664 //TimeTaker timer2("updateMesh() mesh building");
666 video::SMaterial material;
667 material.setFlag(video::EMF_LIGHTING, false);
668 material.setFlag(video::EMF_BILINEAR_FILTER, false);
669 material.setFlag(video::EMF_FOG_ENABLE, true);
670 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
671 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_SIMPLE);
673 for(u32 i=0; i<fastfaces_new.size(); i++)
675 FastFace &f = fastfaces_new[i];
677 const u16 indices[] = {0,1,2,2,3,0};
679 video::ITexture *texture = f.tile.texture.atlas;
683 material.setTexture(0, texture);
685 f.tile.applyMaterialOptions(material);
687 collector.append(material, f.vertices, 4, indices, 6);
692 Add special graphics:
698 //TimeTaker timer2("updateMesh() adding special stuff");
700 // Flowing water material
701 video::SMaterial material_water1;
702 material_water1.setFlag(video::EMF_LIGHTING, false);
703 material_water1.setFlag(video::EMF_BACK_FACE_CULLING, false);
704 material_water1.setFlag(video::EMF_BILINEAR_FILTER, false);
705 material_water1.setFlag(video::EMF_FOG_ENABLE, true);
706 material_water1.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
707 AtlasPointer pa_water1 = g_texturesource->getTexture(
708 g_texturesource->getTextureId("water.png"));
709 material_water1.setTexture(0, pa_water1.atlas);
711 // New-style leaves material
712 video::SMaterial material_leaves1;
713 material_leaves1.setFlag(video::EMF_LIGHTING, false);
714 //material_leaves1.setFlag(video::EMF_BACK_FACE_CULLING, false);
715 material_leaves1.setFlag(video::EMF_BILINEAR_FILTER, false);
716 material_leaves1.setFlag(video::EMF_FOG_ENABLE, true);
717 material_leaves1.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
718 AtlasPointer pa_leaves1 = g_texturesource->getTexture(
719 g_texturesource->getTextureId("leaves.png"));
720 material_leaves1.setTexture(0, pa_leaves1.atlas);
722 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
723 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
724 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
728 MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes+p);
733 if(n.d == CONTENT_TORCH)
735 video::SColor c(255,255,255,255);
737 // Wall at X+ of node
738 video::S3DVertex vertices[4] =
740 video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c, 0,1),
741 video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c, 1,1),
742 video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, 1,0),
743 video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c, 0,0),
746 v3s16 dir = unpackDir(n.dir);
748 for(s32 i=0; i<4; i++)
750 if(dir == v3s16(1,0,0))
751 vertices[i].Pos.rotateXZBy(0);
752 if(dir == v3s16(-1,0,0))
753 vertices[i].Pos.rotateXZBy(180);
754 if(dir == v3s16(0,0,1))
755 vertices[i].Pos.rotateXZBy(90);
756 if(dir == v3s16(0,0,-1))
757 vertices[i].Pos.rotateXZBy(-90);
758 if(dir == v3s16(0,-1,0))
759 vertices[i].Pos.rotateXZBy(45);
760 if(dir == v3s16(0,1,0))
761 vertices[i].Pos.rotateXZBy(-45);
763 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
767 video::SMaterial material;
768 material.setFlag(video::EMF_LIGHTING, false);
769 material.setFlag(video::EMF_BACK_FACE_CULLING, false);
770 material.setFlag(video::EMF_BILINEAR_FILTER, false);
771 //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
772 material.MaterialType
773 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
775 if(dir == v3s16(0,-1,0))
776 material.setTexture(0,
777 g_texturesource->getTextureRaw("torch_on_floor.png"));
778 else if(dir == v3s16(0,1,0))
779 material.setTexture(0,
780 g_texturesource->getTextureRaw("torch_on_ceiling.png"));
781 // For backwards compatibility
782 else if(dir == v3s16(0,0,0))
783 material.setTexture(0,
784 g_texturesource->getTextureRaw("torch_on_floor.png"));
786 material.setTexture(0,
787 g_texturesource->getTextureRaw("torch.png"));
789 u16 indices[] = {0,1,2,2,3,0};
790 // Add to mesh collector
791 collector.append(material, vertices, 4, indices, 6);
796 if(n.d == CONTENT_SIGN_WALL)
798 u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
799 video::SColor c(255,l,l,l);
801 float d = (float)BS/16;
802 // Wall at X+ of node
803 video::S3DVertex vertices[4] =
805 video::S3DVertex(BS/2-d,-BS/2,-BS/2, 0,0,0, c, 0,1),
806 video::S3DVertex(BS/2-d,-BS/2,BS/2, 0,0,0, c, 1,1),
807 video::S3DVertex(BS/2-d,BS/2,BS/2, 0,0,0, c, 1,0),
808 video::S3DVertex(BS/2-d,BS/2,-BS/2, 0,0,0, c, 0,0),
811 v3s16 dir = unpackDir(n.dir);
813 for(s32 i=0; i<4; i++)
815 if(dir == v3s16(1,0,0))
816 vertices[i].Pos.rotateXZBy(0);
817 if(dir == v3s16(-1,0,0))
818 vertices[i].Pos.rotateXZBy(180);
819 if(dir == v3s16(0,0,1))
820 vertices[i].Pos.rotateXZBy(90);
821 if(dir == v3s16(0,0,-1))
822 vertices[i].Pos.rotateXZBy(-90);
823 if(dir == v3s16(0,-1,0))
824 vertices[i].Pos.rotateXYBy(-90);
825 if(dir == v3s16(0,1,0))
826 vertices[i].Pos.rotateXYBy(90);
828 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
832 video::SMaterial material;
833 material.setFlag(video::EMF_LIGHTING, false);
834 material.setFlag(video::EMF_BACK_FACE_CULLING, false);
835 material.setFlag(video::EMF_BILINEAR_FILTER, false);
836 material.setFlag(video::EMF_FOG_ENABLE, true);
837 //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
838 material.MaterialType
839 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
841 material.setTexture(0,
842 g_texturesource->getTextureRaw("sign_wall.png"));
844 u16 indices[] = {0,1,2,2,3,0};
845 // Add to mesh collector
846 collector.append(material, vertices, 4, indices, 6);
849 Add flowing water to mesh
851 else if(n.d == CONTENT_WATER)
853 bool top_is_water = false;
854 MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
855 if(ntop.d == CONTENT_WATER || ntop.d == CONTENT_WATERSOURCE)
859 // Use the light of the node on top if possible
860 if(content_features(ntop.d).param_type == CPT_LIGHT)
861 l = decode_light(ntop.getLightBlend(data->m_daynight_ratio));
862 // Otherwise use the light of this node (the water)
864 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
865 video::SColor c(WATER_ALPHA,l,l,l);
867 // Neighbor water levels (key = relative position)
868 // Includes current node
869 core::map<v3s16, f32> neighbor_levels;
870 core::map<v3s16, u8> neighbor_contents;
871 core::map<v3s16, u8> neighbor_flags;
872 const u8 neighborflag_top_is_water = 0x01;
873 v3s16 neighbor_dirs[9] = {
884 for(u32 i=0; i<9; i++)
886 u8 content = CONTENT_AIR;
887 float level = -0.5 * BS;
890 v3s16 p2 = p + neighbor_dirs[i];
891 MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
892 if(n2.d != CONTENT_IGNORE)
896 if(n2.d == CONTENT_WATERSOURCE)
897 level = (-0.5+node_water_level) * BS;
898 else if(n2.d == CONTENT_WATER)
899 level = (-0.5 + ((float)n2.param2 + 0.5) / 8.0
900 * node_water_level) * BS;
902 // Check node above neighbor.
903 // NOTE: This doesn't get executed if neighbor
906 n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
907 if(n2.d == CONTENT_WATERSOURCE || n2.d == CONTENT_WATER)
908 flags |= neighborflag_top_is_water;
911 neighbor_levels.insert(neighbor_dirs[i], level);
912 neighbor_contents.insert(neighbor_dirs[i], content);
913 neighbor_flags.insert(neighbor_dirs[i], flags);
916 //float water_level = (-0.5 + ((float)n.param2 + 0.5) / 8.0) * BS;
917 //float water_level = neighbor_levels[v3s16(0,0,0)];
919 // Corner heights (average between four waters)
920 f32 corner_levels[4];
922 v3s16 halfdirs[4] = {
928 for(u32 i=0; i<4; i++)
930 v3s16 cornerdir = halfdirs[i];
931 float cornerlevel = 0;
933 for(u32 j=0; j<4; j++)
935 v3s16 neighbordir = cornerdir - halfdirs[j];
936 u8 content = neighbor_contents[neighbordir];
937 // Special case for source nodes
938 if(content == CONTENT_WATERSOURCE)
940 cornerlevel = (-0.5+node_water_level)*BS;
944 else if(content == CONTENT_WATER)
946 cornerlevel += neighbor_levels[neighbordir];
949 else if(content == CONTENT_AIR)
951 cornerlevel += -0.5*BS;
956 cornerlevel /= valid_count;
957 corner_levels[i] = cornerlevel;
964 v3s16 side_dirs[4] = {
970 s16 side_corners[4][2] = {
976 for(u32 i=0; i<4; i++)
978 v3s16 dir = side_dirs[i];
981 If our topside is water and neighbor's topside
982 is water, don't draw side face
985 neighbor_flags[dir] & neighborflag_top_is_water)
988 u8 neighbor_content = neighbor_contents[dir];
990 // Don't draw face if neighbor is not air or water
991 if(neighbor_content != CONTENT_AIR
992 && neighbor_content != CONTENT_WATER)
995 bool neighbor_is_water = (neighbor_content == CONTENT_WATER);
997 // Don't draw any faces if neighbor is water and top is water
998 if(neighbor_is_water == true && top_is_water == false)
1001 video::S3DVertex vertices[4] =
1003 /*video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1),
1004 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1),
1005 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1006 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1007 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1008 pa_water1.x0(), pa_water1.y1()),
1009 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1010 pa_water1.x1(), pa_water1.y1()),
1011 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1012 pa_water1.x1(), pa_water1.y0()),
1013 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1014 pa_water1.x0(), pa_water1.y0()),
1018 If our topside is water, set upper border of face
1019 at upper border of node
1023 vertices[2].Pos.Y = 0.5*BS;
1024 vertices[3].Pos.Y = 0.5*BS;
1027 Otherwise upper position of face is corner levels
1031 vertices[2].Pos.Y = corner_levels[side_corners[i][0]];
1032 vertices[3].Pos.Y = corner_levels[side_corners[i][1]];
1036 If neighbor is water, lower border of face is corner
1039 if(neighbor_is_water)
1041 vertices[0].Pos.Y = corner_levels[side_corners[i][1]];
1042 vertices[1].Pos.Y = corner_levels[side_corners[i][0]];
1045 If neighbor is not water, lower border of face is
1046 lower border of node
1050 vertices[0].Pos.Y = -0.5*BS;
1051 vertices[1].Pos.Y = -0.5*BS;
1054 for(s32 j=0; j<4; j++)
1056 if(dir == v3s16(0,0,1))
1057 vertices[j].Pos.rotateXZBy(0);
1058 if(dir == v3s16(0,0,-1))
1059 vertices[j].Pos.rotateXZBy(180);
1060 if(dir == v3s16(-1,0,0))
1061 vertices[j].Pos.rotateXZBy(90);
1062 if(dir == v3s16(1,0,-0))
1063 vertices[j].Pos.rotateXZBy(-90);
1065 vertices[j].Pos += intToFloat(p + blockpos_nodes, BS);
1068 u16 indices[] = {0,1,2,2,3,0};
1069 // Add to mesh collector
1070 collector.append(material_water1, vertices, 4, indices, 6);
1074 Generate top side, if appropriate
1077 if(top_is_water == false)
1079 video::S3DVertex vertices[4] =
1081 /*video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
1082 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
1083 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1084 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1085 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1086 pa_water1.x0(), pa_water1.y1()),
1087 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1088 pa_water1.x1(), pa_water1.y1()),
1089 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
1090 pa_water1.x1(), pa_water1.y0()),
1091 video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
1092 pa_water1.x0(), pa_water1.y0()),
1095 // This fixes a strange bug
1096 s32 corner_resolve[4] = {3,2,1,0};
1098 for(s32 i=0; i<4; i++)
1100 //vertices[i].Pos.Y += water_level;
1101 //vertices[i].Pos.Y += neighbor_levels[v3s16(0,0,0)];
1102 s32 j = corner_resolve[i];
1103 vertices[i].Pos.Y += corner_levels[j];
1104 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1107 u16 indices[] = {0,1,2,2,3,0};
1108 // Add to mesh collector
1109 collector.append(material_water1, vertices, 4, indices, 6);
1113 Add water sources to mesh if using new style
1115 else if(n.d == CONTENT_WATERSOURCE && new_style_water)
1117 //bool top_is_water = false;
1118 bool top_is_air = false;
1119 MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
1120 /*if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
1121 top_is_water = true;*/
1122 if(n.d == CONTENT_AIR)
1125 /*if(top_is_water == true)
1127 if(top_is_air == false)
1130 u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
1131 video::SColor c(WATER_ALPHA,l,l,l);
1133 video::S3DVertex vertices[4] =
1135 /*video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
1136 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
1137 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1138 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1139 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1140 pa_water1.x0(), pa_water1.y1()),
1141 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1142 pa_water1.x1(), pa_water1.y1()),
1143 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
1144 pa_water1.x1(), pa_water1.y0()),
1145 video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
1146 pa_water1.x0(), pa_water1.y0()),
1149 for(s32 i=0; i<4; i++)
1151 vertices[i].Pos.Y += (-0.5+node_water_level)*BS;
1152 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1155 u16 indices[] = {0,1,2,2,3,0};
1156 // Add to mesh collector
1157 collector.append(material_water1, vertices, 4, indices, 6);
1160 Add leaves if using new style
1162 else if(n.d == CONTENT_LEAVES && new_style_leaves)
1164 /*u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));*/
1165 u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio)));
1166 video::SColor c(255,l,l,l);
1168 for(u32 j=0; j<6; j++)
1170 video::S3DVertex vertices[4] =
1172 /*video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, 0,1),
1173 video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, 1,1),
1174 video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c, 1,0),
1175 video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c, 0,0),*/
1176 video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c,
1177 pa_leaves1.x0(), pa_leaves1.y1()),
1178 video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c,
1179 pa_leaves1.x1(), pa_leaves1.y1()),
1180 video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c,
1181 pa_leaves1.x1(), pa_leaves1.y0()),
1182 video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c,
1183 pa_leaves1.x0(), pa_leaves1.y0()),
1188 for(u16 i=0; i<4; i++)
1189 vertices[i].Pos.rotateXZBy(0);
1193 for(u16 i=0; i<4; i++)
1194 vertices[i].Pos.rotateXZBy(180);
1198 for(u16 i=0; i<4; i++)
1199 vertices[i].Pos.rotateXZBy(-90);
1203 for(u16 i=0; i<4; i++)
1204 vertices[i].Pos.rotateXZBy(90);
1208 for(u16 i=0; i<4; i++)
1209 vertices[i].Pos.rotateYZBy(-90);
1213 for(u16 i=0; i<4; i++)
1214 vertices[i].Pos.rotateYZBy(90);
1217 for(u16 i=0; i<4; i++)
1219 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1222 u16 indices[] = {0,1,2,2,3,0};
1223 // Add to mesh collector
1224 collector.append(material_leaves1, vertices, 4, indices, 6);
1230 Add stuff from collector to mesh
1233 scene::SMesh *mesh_new = NULL;
1234 mesh_new = new scene::SMesh();
1236 collector.fillMesh(mesh_new);
1239 Do some stuff to the mesh
1242 mesh_new->recalculateBoundingBox();
1245 Delete new mesh if it is empty
1248 if(mesh_new->getMeshBufferCount() == 0)
1257 // Usually 1-700 faces and 1-7 materials
1258 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
1259 <<"and uses "<<mesh_new->getMeshBufferCount()
1260 <<" materials (meshbuffers)"<<std::endl;
1263 // Use VBO for mesh (this just would set this for ever buffer)
1264 // This will lead to infinite memory usage because or irrlicht.
1265 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
1268 NOTE: If that is enabled, some kind of a queue to the main
1269 thread should be made which would call irrlicht to delete
1270 the hardware buffer and then delete the mesh
1276 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1285 MapBlock::MapBlock(NodeContainer *parent, v3s16 pos, bool dummy):
1289 is_underground(false),
1290 m_lighting_expired(true),
1291 m_day_night_differs(false),
1292 //m_not_fully_generated(false),
1299 //m_spawn_timer = -10000;
1302 m_mesh_expired = false;
1305 m_temp_mods_mutex.Init();
1309 MapBlock::~MapBlock()
1313 JMutexAutoLock lock(mesh_mutex);
1327 bool MapBlock::isValidPositionParent(v3s16 p)
1329 if(isValidPosition(p))
1334 return m_parent->isValidPosition(getPosRelative() + p);
1338 MapNode MapBlock::getNodeParent(v3s16 p)
1340 if(isValidPosition(p) == false)
1342 return m_parent->getNode(getPosRelative() + p);
1347 throw InvalidPositionException();
1348 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
1352 void MapBlock::setNodeParent(v3s16 p, MapNode & n)
1354 if(isValidPosition(p) == false)
1356 m_parent->setNode(getPosRelative() + p, n);
1361 throw InvalidPositionException();
1362 data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X] = n;
1366 MapNode MapBlock::getNodeParentNoEx(v3s16 p)
1368 if(isValidPosition(p) == false)
1371 return m_parent->getNode(getPosRelative() + p);
1373 catch(InvalidPositionException &e)
1375 return MapNode(CONTENT_IGNORE);
1382 return MapNode(CONTENT_IGNORE);
1384 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
1391 void MapBlock::updateMesh(u32 daynight_ratio)
1395 DEBUG: If mesh has been generated, don't generate it again
1398 JMutexAutoLock meshlock(mesh_mutex);
1405 data.fill(daynight_ratio, this);
1407 scene::SMesh *mesh_new = makeMapBlockMesh(&data);
1413 replaceMesh(mesh_new);
1418 void MapBlock::replaceMesh(scene::SMesh *mesh_new)
1422 //scene::SMesh *mesh_old = mesh[daynight_i];
1423 //mesh[daynight_i] = mesh_new;
1425 scene::SMesh *mesh_old = mesh;
1427 setMeshExpired(false);
1429 if(mesh_old != NULL)
1431 // Remove hardware buffers of meshbuffers of mesh
1432 // NOTE: No way, this runs in a different thread and everything
1433 /*u32 c = mesh_old->getMeshBufferCount();
1434 for(u32 i=0; i<c; i++)
1436 IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1439 /*dstream<<"mesh_old->getReferenceCount()="
1440 <<mesh_old->getReferenceCount()<<std::endl;
1441 u32 c = mesh_old->getMeshBufferCount();
1442 for(u32 i=0; i<c; i++)
1444 scene::IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1445 dstream<<"buf->getReferenceCount()="
1446 <<buf->getReferenceCount()<<std::endl;
1455 mesh_mutex.Unlock();
1461 Propagates sunlight down through the block.
1462 Doesn't modify nodes that are not affected by sunlight.
1464 Returns false if sunlight at bottom block is invalid
1465 Returns true if bottom block doesn't exist.
1467 If there is a block above, continues from it.
1468 If there is no block above, assumes there is sunlight, unless
1469 is_underground is set or highest node is water.
1471 At the moment, all sunlighted nodes are added to light_sources.
1472 - SUGG: This could be optimized
1474 Turns sunglighted mud into grass.
1476 if remove_light==true, sets non-sunlighted nodes black.
1478 if black_air_left!=NULL, it is set to true if non-sunlighted
1479 air is left in block.
1481 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources,
1482 bool remove_light, bool *black_air_left,
1485 // Whether the sunlight at the top of the bottom block is valid
1486 bool block_below_is_valid = true;
1488 v3s16 pos_relative = getPosRelative();
1490 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
1492 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
1495 bool no_sunlight = false;
1496 bool no_top_block = false;
1497 // Check if node above block has sunlight
1499 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1500 if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1505 catch(InvalidPositionException &e)
1507 no_top_block = true;
1509 // NOTE: This makes over-ground roofed places sunlighted
1510 // Assume sunlight, unless is_underground==true
1517 MapNode n = getNode(v3s16(x, MAP_BLOCKSIZE-1, z));
1518 if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
1523 // NOTE: As of now, this just would make everything dark.
1525 //no_sunlight = true;
1528 #if 0 // Doesn't work; nothing gets light.
1529 bool no_sunlight = true;
1530 bool no_top_block = false;
1531 // Check if node above block has sunlight
1533 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1534 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
1536 no_sunlight = false;
1539 catch(InvalidPositionException &e)
1541 no_top_block = true;
1545 /*std::cout<<"("<<x<<","<<z<<"): "
1546 <<"no_top_block="<<no_top_block
1547 <<", is_underground="<<is_underground
1548 <<", no_sunlight="<<no_sunlight
1551 s16 y = MAP_BLOCKSIZE-1;
1553 // This makes difference to diminishing in water.
1554 bool stopped_to_solid_object = false;
1556 u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
1561 MapNode &n = getNodeRef(pos);
1563 if(current_light == 0)
1567 else if(current_light == LIGHT_SUN && n.sunlight_propagates())
1569 // Do nothing: Sunlight is continued
1571 else if(n.light_propagates() == false)
1575 bool upper_is_air = false;
1578 if(getNodeParent(pos+v3s16(0,1,0)).d == CONTENT_AIR)
1579 upper_is_air = true;
1581 catch(InvalidPositionException &e)
1584 // Turn mud into grass
1585 if(upper_is_air && n.d == CONTENT_MUD
1586 && current_light == LIGHT_SUN)
1588 n.d = CONTENT_GRASS;
1592 // A solid object is on the way.
1593 stopped_to_solid_object = true;
1601 current_light = diminish_light(current_light);
1604 u8 old_light = n.getLight(LIGHTBANK_DAY);
1606 if(current_light > old_light || remove_light)
1608 n.setLight(LIGHTBANK_DAY, current_light);
1611 if(diminish_light(current_light) != 0)
1613 light_sources.insert(pos_relative + pos, true);
1616 if(current_light == 0 && stopped_to_solid_object)
1620 *black_air_left = true;
1625 // Whether or not the block below should see LIGHT_SUN
1626 bool sunlight_should_go_down = (current_light == LIGHT_SUN);
1629 If the block below hasn't already been marked invalid:
1631 Check if the node below the block has proper sunlight at top.
1632 If not, the block below is invalid.
1634 Ignore non-transparent nodes as they always have no light
1638 if(block_below_is_valid)
1640 MapNode n = getNodeParent(v3s16(x, -1, z));
1641 if(n.light_propagates())
1643 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN
1644 && sunlight_should_go_down == false)
1645 block_below_is_valid = false;
1646 else if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN
1647 && sunlight_should_go_down == true)
1648 block_below_is_valid = false;
1652 catch(InvalidPositionException &e)
1654 /*std::cout<<"InvalidBlockException for bottom block node"
1656 // Just no block below, no need to panic.
1661 return block_below_is_valid;
1665 void MapBlock::copyTo(VoxelManipulator &dst)
1667 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
1668 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
1670 // Copy from data to VoxelManipulator
1671 dst.copyFrom(data, data_area, v3s16(0,0,0),
1672 getPosRelative(), data_size);
1675 void MapBlock::copyFrom(VoxelManipulator &dst)
1677 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
1678 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
1680 // Copy from VoxelManipulator to data
1681 dst.copyTo(data, data_area, v3s16(0,0,0),
1682 getPosRelative(), data_size);
1685 void MapBlock::stepObjects(float dtime, bool server, u32 daynight_ratio)
1690 m_objects.step(dtime, server, daynight_ratio);
1694 Spawn some objects at random.
1696 Use dayNightDiffed() to approximate being near ground level
1698 if(m_spawn_timer < -999)
1702 if(dayNightDiffed() == true && getObjectCount() == 0)
1704 m_spawn_timer -= dtime;
1705 if(m_spawn_timer <= 0.0)
1707 m_spawn_timer += myrand() % 300;
1710 (myrand()%(MAP_BLOCKSIZE-1))+0,
1711 (myrand()%(MAP_BLOCKSIZE-1))+0
1714 s16 y = getGroundLevel(p2d);
1718 v3s16 p(p2d.X, y+1, p2d.Y);
1720 if(getNode(p).d == CONTENT_AIR
1721 && getNode(p).getLightBlend(daynight_ratio) <= 11)
1723 RatObject *obj = new RatObject(NULL, -1, intToFloat(p, BS));
1735 void MapBlock::updateDayNightDiff()
1739 m_day_night_differs = false;
1743 bool differs = false;
1746 Check if any lighting value differs
1748 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1750 MapNode &n = data[i];
1751 if(n.getLight(LIGHTBANK_DAY) != n.getLight(LIGHTBANK_NIGHT))
1759 If some lighting values differ, check if the whole thing is
1760 just air. If it is, differ = false
1764 bool only_air = true;
1765 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1767 MapNode &n = data[i];
1768 if(n.d != CONTENT_AIR)
1778 // Set member variable
1779 m_day_night_differs = differs;
1782 s16 MapBlock::getGroundLevel(v2s16 p2d)
1788 s16 y = MAP_BLOCKSIZE-1;
1791 //if(is_ground_content(getNodeRef(p2d.X, y, p2d.Y).d))
1792 if(content_features(getNodeRef(p2d.X, y, p2d.Y).d).walkable)
1794 if(y == MAP_BLOCKSIZE-1)
1802 catch(InvalidPositionException &e)
1812 void MapBlock::serialize(std::ostream &os, u8 version)
1814 if(!ser_ver_supported(version))
1815 throw VersionMismatchException("ERROR: MapBlock format not supported");
1819 throw SerializationError("ERROR: Not writing dummy block.");
1822 // These have no compression
1823 if(version <= 3 || version == 5 || version == 6)
1825 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1827 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
1828 SharedBuffer<u8> dest(buflen);
1830 dest[0] = is_underground;
1831 for(u32 i=0; i<nodecount; i++)
1833 u32 s = 1 + i * MapNode::serializedLength(version);
1834 data[i].serialize(&dest[s], version);
1837 os.write((char*)*dest, dest.getSize());
1839 else if(version <= 10)
1843 Compress the materials and the params separately.
1847 os.write((char*)&is_underground, 1);
1849 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1851 // Get and compress materials
1852 SharedBuffer<u8> materialdata(nodecount);
1853 for(u32 i=0; i<nodecount; i++)
1855 materialdata[i] = data[i].d;
1857 compress(materialdata, os, version);
1859 // Get and compress lights
1860 SharedBuffer<u8> lightdata(nodecount);
1861 for(u32 i=0; i<nodecount; i++)
1863 lightdata[i] = data[i].param;
1865 compress(lightdata, os, version);
1869 // Get and compress param2
1870 SharedBuffer<u8> param2data(nodecount);
1871 for(u32 i=0; i<nodecount; i++)
1873 param2data[i] = data[i].param2;
1875 compress(param2data, os, version);
1878 // All other versions (newest)
1885 if(m_day_night_differs)
1887 if(m_lighting_expired)
1889 os.write((char*)&flags, 1);
1891 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1897 SharedBuffer<u8> databuf(nodecount*3);
1900 for(u32 i=0; i<nodecount; i++)
1902 databuf[i] = data[i].d;
1906 for(u32 i=0; i<nodecount; i++)
1908 databuf[i+nodecount] = data[i].param;
1912 for(u32 i=0; i<nodecount; i++)
1914 databuf[i+nodecount*2] = data[i].param2;
1918 Compress data to output stream
1921 compress(databuf, os, version);
1930 std::ostringstream oss(std::ios_base::binary);
1931 m_node_metadata.serialize(oss);
1932 os<<serializeString(oss.str());
1936 std::ostringstream oss(std::ios_base::binary);
1937 m_node_metadata.serialize(oss);
1938 compressZlib(oss.str(), os);
1939 //os<<serializeLongString(oss.str());
1945 void MapBlock::deSerialize(std::istream &is, u8 version)
1947 if(!ser_ver_supported(version))
1948 throw VersionMismatchException("ERROR: MapBlock format not supported");
1950 // These have no lighting info
1953 setLightingExpired(true);
1956 // These have no compression
1957 if(version <= 3 || version == 5 || version == 6)
1959 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1962 if(is.gcount() != 1)
1963 throw SerializationError
1964 ("MapBlock::deSerialize: no enough input data");
1965 is_underground = tmp;
1966 for(u32 i=0; i<nodecount; i++)
1968 s32 len = MapNode::serializedLength(version);
1969 SharedBuffer<u8> d(len);
1970 is.read((char*)*d, len);
1971 if(is.gcount() != len)
1972 throw SerializationError
1973 ("MapBlock::deSerialize: no enough input data");
1974 data[i].deSerialize(*d, version);
1977 else if(version <= 10)
1979 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1982 is.read((char*)&t8, 1);
1983 is_underground = t8;
1986 // Uncompress and set material data
1987 std::ostringstream os(std::ios_base::binary);
1988 decompress(is, os, version);
1989 std::string s = os.str();
1990 if(s.size() != nodecount)
1991 throw SerializationError
1992 ("MapBlock::deSerialize: invalid format");
1993 for(u32 i=0; i<s.size(); i++)
1999 // Uncompress and set param data
2000 std::ostringstream os(std::ios_base::binary);
2001 decompress(is, os, version);
2002 std::string s = os.str();
2003 if(s.size() != nodecount)
2004 throw SerializationError
2005 ("MapBlock::deSerialize: invalid format");
2006 for(u32 i=0; i<s.size(); i++)
2008 data[i].param = s[i];
2014 // Uncompress and set param2 data
2015 std::ostringstream os(std::ios_base::binary);
2016 decompress(is, os, version);
2017 std::string s = os.str();
2018 if(s.size() != nodecount)
2019 throw SerializationError
2020 ("MapBlock::deSerialize: invalid format");
2021 for(u32 i=0; i<s.size(); i++)
2023 data[i].param2 = s[i];
2027 // All other versions (newest)
2030 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2033 is.read((char*)&flags, 1);
2034 is_underground = (flags & 0x01) ? true : false;
2035 m_day_night_differs = (flags & 0x02) ? true : false;
2036 m_lighting_expired = (flags & 0x04) ? true : false;
2039 std::ostringstream os(std::ios_base::binary);
2040 decompress(is, os, version);
2041 std::string s = os.str();
2042 if(s.size() != nodecount*3)
2043 throw SerializationError
2044 ("MapBlock::deSerialize: invalid format");
2047 for(u32 i=0; i<nodecount; i++)
2052 for(u32 i=0; i<nodecount; i++)
2054 data[i].param = s[i+nodecount];
2057 for(u32 i=0; i<nodecount; i++)
2059 data[i].param2 = s[i+nodecount*2];
2071 std::string data = deSerializeString(is);
2072 std::istringstream iss(data, std::ios_base::binary);
2073 m_node_metadata.deSerialize(iss);
2077 //std::string data = deSerializeLongString(is);
2078 std::ostringstream oss(std::ios_base::binary);
2079 decompressZlib(is, oss);
2080 std::istringstream iss(oss.str(), std::ios_base::binary);
2081 m_node_metadata.deSerialize(iss);
2084 catch(SerializationError &e)
2086 dstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
2087 <<" while deserializing node metadata"<<std::endl;
2093 Translate nodes as specified in the translate_to fields of
2096 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
2098 MapNode &n = data[i];
2100 MapNode *translate_to = content_features(n.d).translate_to;
2103 dstream<<"MapBlock: WARNING: Translating node "<<n.d<<" to "
2104 <<translate_to->d<<std::endl;