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
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 = g_irrlicht->getTexture(f.tile.spec);
680 video::ITexture *texture = f.tile.texture.atlas;
684 material.setTexture(0, texture);
686 f.tile.applyMaterialOptions(material);
688 collector.append(material, f.vertices, 4, indices, 6);
693 Add special graphics:
699 //TimeTaker timer2("updateMesh() adding special stuff");
701 // Flowing water material
702 video::SMaterial material_water1;
703 material_water1.setFlag(video::EMF_LIGHTING, false);
704 //material_water1.setFlag(video::EMF_BACK_FACE_CULLING, false);
705 material_water1.setFlag(video::EMF_BILINEAR_FILTER, false);
706 material_water1.setFlag(video::EMF_FOG_ENABLE, true);
707 material_water1.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
709 //material_water1.setTexture(0, g_irrlicht->getTexture("water.png"));
710 AtlasPointer pa_water1 = g_texturesource->getTexture(
711 g_texturesource->getTextureId("water.png"));
712 material_water1.setTexture(0, pa_water1.atlas);
714 // New-style leaves material
715 video::SMaterial material_leaves1;
716 material_leaves1.setFlag(video::EMF_LIGHTING, false);
717 //material_leaves1.setFlag(video::EMF_BACK_FACE_CULLING, false);
718 material_leaves1.setFlag(video::EMF_BILINEAR_FILTER, false);
719 material_leaves1.setFlag(video::EMF_FOG_ENABLE, true);
720 material_leaves1.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
722 //material_leaves1.setTexture(0, g_irrlicht->getTexture("leaves.png"));
723 AtlasPointer pa_leaves1 = g_texturesource->getTexture(
724 g_texturesource->getTextureId("leaves.png"));
725 material_leaves1.setTexture(0, pa_leaves1.atlas);
727 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
728 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
729 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
733 MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes+p);
738 if(n.d == CONTENT_TORCH)
740 video::SColor c(255,255,255,255);
742 // Wall at X+ of node
743 video::S3DVertex vertices[4] =
745 video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c, 0,1),
746 video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c, 1,1),
747 video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, 1,0),
748 video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c, 0,0),
751 v3s16 dir = unpackDir(n.dir);
753 for(s32 i=0; i<4; i++)
755 if(dir == v3s16(1,0,0))
756 vertices[i].Pos.rotateXZBy(0);
757 if(dir == v3s16(-1,0,0))
758 vertices[i].Pos.rotateXZBy(180);
759 if(dir == v3s16(0,0,1))
760 vertices[i].Pos.rotateXZBy(90);
761 if(dir == v3s16(0,0,-1))
762 vertices[i].Pos.rotateXZBy(-90);
763 if(dir == v3s16(0,-1,0))
764 vertices[i].Pos.rotateXZBy(45);
765 if(dir == v3s16(0,1,0))
766 vertices[i].Pos.rotateXZBy(-45);
768 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
772 video::SMaterial material;
773 material.setFlag(video::EMF_LIGHTING, false);
774 material.setFlag(video::EMF_BACK_FACE_CULLING, false);
775 material.setFlag(video::EMF_BILINEAR_FILTER, false);
776 //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
777 material.MaterialType
778 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
780 if(dir == v3s16(0,-1,0))
781 material.setTexture(0,
782 g_texturesource->getTextureRaw("torch_on_floor.png"));
783 else if(dir == v3s16(0,1,0))
784 material.setTexture(0,
785 g_texturesource->getTextureRaw("torch_on_ceiling.png"));
786 // For backwards compatibility
787 else if(dir == v3s16(0,0,0))
788 material.setTexture(0,
789 g_texturesource->getTextureRaw("torch_on_floor.png"));
791 material.setTexture(0,
792 g_texturesource->getTextureRaw("torch.png"));
794 u16 indices[] = {0,1,2,2,3,0};
795 // Add to mesh collector
796 collector.append(material, vertices, 4, indices, 6);
801 if(n.d == CONTENT_SIGN_WALL)
803 u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
804 video::SColor c(255,l,l,l);
806 float d = (float)BS/16;
807 // Wall at X+ of node
808 video::S3DVertex vertices[4] =
810 video::S3DVertex(BS/2-d,-BS/2,-BS/2, 0,0,0, c, 0,1),
811 video::S3DVertex(BS/2-d,-BS/2,BS/2, 0,0,0, c, 1,1),
812 video::S3DVertex(BS/2-d,BS/2,BS/2, 0,0,0, c, 1,0),
813 video::S3DVertex(BS/2-d,BS/2,-BS/2, 0,0,0, c, 0,0),
816 v3s16 dir = unpackDir(n.dir);
818 for(s32 i=0; i<4; i++)
820 if(dir == v3s16(1,0,0))
821 vertices[i].Pos.rotateXZBy(0);
822 if(dir == v3s16(-1,0,0))
823 vertices[i].Pos.rotateXZBy(180);
824 if(dir == v3s16(0,0,1))
825 vertices[i].Pos.rotateXZBy(90);
826 if(dir == v3s16(0,0,-1))
827 vertices[i].Pos.rotateXZBy(-90);
828 if(dir == v3s16(0,-1,0))
829 vertices[i].Pos.rotateXYBy(-90);
830 if(dir == v3s16(0,1,0))
831 vertices[i].Pos.rotateXYBy(90);
833 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
837 video::SMaterial material;
838 material.setFlag(video::EMF_LIGHTING, false);
839 material.setFlag(video::EMF_BACK_FACE_CULLING, false);
840 material.setFlag(video::EMF_BILINEAR_FILTER, false);
841 material.setFlag(video::EMF_FOG_ENABLE, true);
842 //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
843 material.MaterialType
844 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
846 material.setTexture(0,
847 g_texturesource->getTextureRaw("sign_wall.png"));
849 u16 indices[] = {0,1,2,2,3,0};
850 // Add to mesh collector
851 collector.append(material, vertices, 4, indices, 6);
854 Add flowing water to mesh
856 else if(n.d == CONTENT_WATER)
858 bool top_is_water = false;
859 MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
860 if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
863 u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
864 video::SColor c(WATER_ALPHA,l,l,l);
866 // Neighbor water levels (key = relative position)
867 // Includes current node
868 core::map<v3s16, f32> neighbor_levels;
869 core::map<v3s16, u8> neighbor_contents;
870 core::map<v3s16, u8> neighbor_flags;
871 const u8 neighborflag_top_is_water = 0x01;
872 v3s16 neighbor_dirs[9] = {
883 for(u32 i=0; i<9; i++)
885 u8 content = CONTENT_AIR;
886 float level = -0.5 * BS;
889 v3s16 p2 = p + neighbor_dirs[i];
890 MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
891 if(n2.d != CONTENT_IGNORE)
895 if(n2.d == CONTENT_WATERSOURCE)
896 level = (-0.5+node_water_level) * BS;
897 else if(n2.d == CONTENT_WATER)
898 level = (-0.5 + ((float)n2.param2 + 0.5) / 8.0
899 * node_water_level) * BS;
901 // Check node above neighbor.
902 // NOTE: This doesn't get executed if neighbor
905 n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
906 if(n2.d == CONTENT_WATERSOURCE || n2.d == CONTENT_WATER)
907 flags |= neighborflag_top_is_water;
910 neighbor_levels.insert(neighbor_dirs[i], level);
911 neighbor_contents.insert(neighbor_dirs[i], content);
912 neighbor_flags.insert(neighbor_dirs[i], flags);
915 //float water_level = (-0.5 + ((float)n.param2 + 0.5) / 8.0) * BS;
916 //float water_level = neighbor_levels[v3s16(0,0,0)];
918 // Corner heights (average between four waters)
919 f32 corner_levels[4];
921 v3s16 halfdirs[4] = {
927 for(u32 i=0; i<4; i++)
929 v3s16 cornerdir = halfdirs[i];
930 float cornerlevel = 0;
932 for(u32 j=0; j<4; j++)
934 v3s16 neighbordir = cornerdir - halfdirs[j];
935 u8 content = neighbor_contents[neighbordir];
936 // Special case for source nodes
937 if(content == CONTENT_WATERSOURCE)
939 cornerlevel = (-0.5+node_water_level)*BS;
943 else if(content == CONTENT_WATER)
945 cornerlevel += neighbor_levels[neighbordir];
948 else if(content == CONTENT_AIR)
950 cornerlevel += -0.5*BS;
955 cornerlevel /= valid_count;
956 corner_levels[i] = cornerlevel;
963 v3s16 side_dirs[4] = {
969 s16 side_corners[4][2] = {
975 for(u32 i=0; i<4; i++)
977 v3s16 dir = side_dirs[i];
980 If our topside is water and neighbor's topside
981 is water, don't draw side face
984 neighbor_flags[dir] & neighborflag_top_is_water)
987 u8 neighbor_content = neighbor_contents[dir];
989 // Don't draw face if neighbor is not air or water
990 if(neighbor_content != CONTENT_AIR
991 && neighbor_content != CONTENT_WATER)
994 bool neighbor_is_water = (neighbor_content == CONTENT_WATER);
996 // Don't draw any faces if neighbor is water and top is water
997 if(neighbor_is_water == true && top_is_water == false)
1000 video::S3DVertex vertices[4] =
1002 /*video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1),
1003 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1),
1004 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1005 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1006 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1007 pa_water1.x0(), pa_water1.y1()),
1008 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1009 pa_water1.x1(), pa_water1.y1()),
1010 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1011 pa_water1.x1(), pa_water1.y0()),
1012 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1013 pa_water1.x0(), pa_water1.y0()),
1017 If our topside is water, set upper border of face
1018 at upper border of node
1022 vertices[2].Pos.Y = 0.5*BS;
1023 vertices[3].Pos.Y = 0.5*BS;
1026 Otherwise upper position of face is corner levels
1030 vertices[2].Pos.Y = corner_levels[side_corners[i][0]];
1031 vertices[3].Pos.Y = corner_levels[side_corners[i][1]];
1035 If neighbor is water, lower border of face is corner
1038 if(neighbor_is_water)
1040 vertices[0].Pos.Y = corner_levels[side_corners[i][1]];
1041 vertices[1].Pos.Y = corner_levels[side_corners[i][0]];
1044 If neighbor is not water, lower border of face is
1045 lower border of node
1049 vertices[0].Pos.Y = -0.5*BS;
1050 vertices[1].Pos.Y = -0.5*BS;
1053 for(s32 j=0; j<4; j++)
1055 if(dir == v3s16(0,0,1))
1056 vertices[j].Pos.rotateXZBy(0);
1057 if(dir == v3s16(0,0,-1))
1058 vertices[j].Pos.rotateXZBy(180);
1059 if(dir == v3s16(-1,0,0))
1060 vertices[j].Pos.rotateXZBy(90);
1061 if(dir == v3s16(1,0,-0))
1062 vertices[j].Pos.rotateXZBy(-90);
1064 vertices[j].Pos += intToFloat(p + blockpos_nodes, BS);
1067 u16 indices[] = {0,1,2,2,3,0};
1068 // Add to mesh collector
1069 collector.append(material_water1, vertices, 4, indices, 6);
1073 Generate top side, if appropriate
1076 if(top_is_water == false)
1078 video::S3DVertex vertices[4] =
1080 /*video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
1081 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
1082 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1083 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1084 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1085 pa_water1.x0(), pa_water1.y1()),
1086 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1087 pa_water1.x1(), pa_water1.y1()),
1088 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
1089 pa_water1.x1(), pa_water1.y0()),
1090 video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
1091 pa_water1.x0(), pa_water1.y0()),
1094 // This fixes a strange bug
1095 s32 corner_resolve[4] = {3,2,1,0};
1097 for(s32 i=0; i<4; i++)
1099 //vertices[i].Pos.Y += water_level;
1100 //vertices[i].Pos.Y += neighbor_levels[v3s16(0,0,0)];
1101 s32 j = corner_resolve[i];
1102 vertices[i].Pos.Y += corner_levels[j];
1103 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1106 u16 indices[] = {0,1,2,2,3,0};
1107 // Add to mesh collector
1108 collector.append(material_water1, vertices, 4, indices, 6);
1112 Add water sources to mesh if using new style
1114 else if(n.d == CONTENT_WATERSOURCE && new_style_water)
1116 //bool top_is_water = false;
1117 bool top_is_air = false;
1118 MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
1119 /*if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
1120 top_is_water = true;*/
1121 if(n.d == CONTENT_AIR)
1124 /*if(top_is_water == true)
1126 if(top_is_air == false)
1129 u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
1130 video::SColor c(WATER_ALPHA,l,l,l);
1132 video::S3DVertex vertices[4] =
1134 /*video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
1135 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
1136 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1137 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1138 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1139 pa_water1.x0(), pa_water1.y1()),
1140 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1141 pa_water1.x1(), pa_water1.y1()),
1142 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
1143 pa_water1.x1(), pa_water1.y0()),
1144 video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
1145 pa_water1.x0(), pa_water1.y0()),
1148 for(s32 i=0; i<4; i++)
1150 vertices[i].Pos.Y += (-0.5+node_water_level)*BS;
1151 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1154 u16 indices[] = {0,1,2,2,3,0};
1155 // Add to mesh collector
1156 collector.append(material_water1, vertices, 4, indices, 6);
1159 Add leaves if using new style
1161 else if(n.d == CONTENT_LEAVES && new_style_leaves)
1163 /*u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));*/
1164 u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio)));
1165 video::SColor c(255,l,l,l);
1167 for(u32 j=0; j<6; j++)
1169 video::S3DVertex vertices[4] =
1171 /*video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, 0,1),
1172 video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, 1,1),
1173 video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c, 1,0),
1174 video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c, 0,0),*/
1175 video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c,
1176 pa_leaves1.x0(), pa_leaves1.y1()),
1177 video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c,
1178 pa_leaves1.x1(), pa_leaves1.y1()),
1179 video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c,
1180 pa_leaves1.x1(), pa_leaves1.y0()),
1181 video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c,
1182 pa_leaves1.x0(), pa_leaves1.y0()),
1187 for(u16 i=0; i<4; i++)
1188 vertices[i].Pos.rotateXZBy(0);
1192 for(u16 i=0; i<4; i++)
1193 vertices[i].Pos.rotateXZBy(180);
1197 for(u16 i=0; i<4; i++)
1198 vertices[i].Pos.rotateXZBy(-90);
1202 for(u16 i=0; i<4; i++)
1203 vertices[i].Pos.rotateXZBy(90);
1207 for(u16 i=0; i<4; i++)
1208 vertices[i].Pos.rotateYZBy(-90);
1212 for(u16 i=0; i<4; i++)
1213 vertices[i].Pos.rotateYZBy(90);
1216 for(u16 i=0; i<4; i++)
1218 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1221 u16 indices[] = {0,1,2,2,3,0};
1222 // Add to mesh collector
1223 collector.append(material_leaves1, vertices, 4, indices, 6);
1229 Add stuff from collector to mesh
1232 scene::SMesh *mesh_new = NULL;
1233 mesh_new = new scene::SMesh();
1235 collector.fillMesh(mesh_new);
1238 Do some stuff to the mesh
1241 mesh_new->recalculateBoundingBox();
1244 Delete new mesh if it is empty
1247 if(mesh_new->getMeshBufferCount() == 0)
1256 // Usually 1-700 faces and 1-7 materials
1257 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
1258 <<"and uses "<<mesh_new->getMeshBufferCount()
1259 <<" materials (meshbuffers)"<<std::endl;
1262 // Use VBO for mesh (this just would set this for ever buffer)
1263 // This will lead to infinite memory usage because or irrlicht.
1264 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
1267 NOTE: If that is enabled, some kind of a queue to the main
1268 thread should be made which would call irrlicht to delete
1269 the hardware buffer and then delete the mesh
1275 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1284 MapBlock::MapBlock(NodeContainer *parent, v3s16 pos, bool dummy):
1288 is_underground(false),
1289 m_lighting_expired(true),
1290 m_day_night_differs(false),
1291 //m_not_fully_generated(false),
1298 //m_spawn_timer = -10000;
1301 m_mesh_expired = false;
1304 m_temp_mods_mutex.Init();
1308 MapBlock::~MapBlock()
1312 JMutexAutoLock lock(mesh_mutex);
1326 bool MapBlock::isValidPositionParent(v3s16 p)
1328 if(isValidPosition(p))
1333 return m_parent->isValidPosition(getPosRelative() + p);
1337 MapNode MapBlock::getNodeParent(v3s16 p)
1339 if(isValidPosition(p) == false)
1341 return m_parent->getNode(getPosRelative() + p);
1346 throw InvalidPositionException();
1347 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
1351 void MapBlock::setNodeParent(v3s16 p, MapNode & n)
1353 if(isValidPosition(p) == false)
1355 m_parent->setNode(getPosRelative() + p, n);
1360 throw InvalidPositionException();
1361 data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X] = n;
1365 MapNode MapBlock::getNodeParentNoEx(v3s16 p)
1367 if(isValidPosition(p) == false)
1370 return m_parent->getNode(getPosRelative() + p);
1372 catch(InvalidPositionException &e)
1374 return MapNode(CONTENT_IGNORE);
1381 return MapNode(CONTENT_IGNORE);
1383 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
1390 void MapBlock::updateMesh(u32 daynight_ratio)
1394 DEBUG: If mesh has been generated, don't generate it again
1397 JMutexAutoLock meshlock(mesh_mutex);
1404 data.fill(daynight_ratio, this);
1406 scene::SMesh *mesh_new = makeMapBlockMesh(&data);
1412 replaceMesh(mesh_new);
1417 void MapBlock::replaceMesh(scene::SMesh *mesh_new)
1421 //scene::SMesh *mesh_old = mesh[daynight_i];
1422 //mesh[daynight_i] = mesh_new;
1424 scene::SMesh *mesh_old = mesh;
1426 setMeshExpired(false);
1428 if(mesh_old != NULL)
1430 // Remove hardware buffers of meshbuffers of mesh
1431 // NOTE: No way, this runs in a different thread and everything
1432 /*u32 c = mesh_old->getMeshBufferCount();
1433 for(u32 i=0; i<c; i++)
1435 IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1438 /*dstream<<"mesh_old->getReferenceCount()="
1439 <<mesh_old->getReferenceCount()<<std::endl;
1440 u32 c = mesh_old->getMeshBufferCount();
1441 for(u32 i=0; i<c; i++)
1443 scene::IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1444 dstream<<"buf->getReferenceCount()="
1445 <<buf->getReferenceCount()<<std::endl;
1454 mesh_mutex.Unlock();
1460 Propagates sunlight down through the block.
1461 Doesn't modify nodes that are not affected by sunlight.
1463 Returns false if sunlight at bottom block is invalid
1464 Returns true if bottom block doesn't exist.
1466 If there is a block above, continues from it.
1467 If there is no block above, assumes there is sunlight, unless
1468 is_underground is set or highest node is water.
1470 At the moment, all sunlighted nodes are added to light_sources.
1471 - SUGG: This could be optimized
1473 Turns sunglighted mud into grass.
1475 if remove_light==true, sets non-sunlighted nodes black.
1477 if black_air_left!=NULL, it is set to true if non-sunlighted
1478 air is left in block.
1480 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources,
1481 bool remove_light, bool *black_air_left,
1484 // Whether the sunlight at the top of the bottom block is valid
1485 bool block_below_is_valid = true;
1487 v3s16 pos_relative = getPosRelative();
1489 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
1491 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
1494 bool no_sunlight = false;
1495 bool no_top_block = false;
1496 // Check if node above block has sunlight
1498 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1499 if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1504 catch(InvalidPositionException &e)
1506 no_top_block = true;
1508 // NOTE: This makes over-ground roofed places sunlighted
1509 // Assume sunlight, unless is_underground==true
1516 MapNode n = getNode(v3s16(x, MAP_BLOCKSIZE-1, z));
1517 if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
1522 // NOTE: As of now, this just would make everything dark.
1524 //no_sunlight = true;
1527 #if 0 // Doesn't work; nothing gets light.
1528 bool no_sunlight = true;
1529 bool no_top_block = false;
1530 // Check if node above block has sunlight
1532 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1533 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
1535 no_sunlight = false;
1538 catch(InvalidPositionException &e)
1540 no_top_block = true;
1544 /*std::cout<<"("<<x<<","<<z<<"): "
1545 <<"no_top_block="<<no_top_block
1546 <<", is_underground="<<is_underground
1547 <<", no_sunlight="<<no_sunlight
1550 s16 y = MAP_BLOCKSIZE-1;
1552 // This makes difference to diminishing in water.
1553 bool stopped_to_solid_object = false;
1555 u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
1560 MapNode &n = getNodeRef(pos);
1562 if(current_light == 0)
1566 else if(current_light == LIGHT_SUN && n.sunlight_propagates())
1568 // Do nothing: Sunlight is continued
1570 else if(n.light_propagates() == false)
1574 bool upper_is_air = false;
1577 if(getNodeParent(pos+v3s16(0,1,0)).d == CONTENT_AIR)
1578 upper_is_air = true;
1580 catch(InvalidPositionException &e)
1583 // Turn mud into grass
1584 if(upper_is_air && n.d == CONTENT_MUD
1585 && current_light == LIGHT_SUN)
1587 n.d = CONTENT_GRASS;
1591 // A solid object is on the way.
1592 stopped_to_solid_object = true;
1600 current_light = diminish_light(current_light);
1603 u8 old_light = n.getLight(LIGHTBANK_DAY);
1605 if(current_light > old_light || remove_light)
1607 n.setLight(LIGHTBANK_DAY, current_light);
1610 if(diminish_light(current_light) != 0)
1612 light_sources.insert(pos_relative + pos, true);
1615 if(current_light == 0 && stopped_to_solid_object)
1619 *black_air_left = true;
1624 // Whether or not the block below should see LIGHT_SUN
1625 bool sunlight_should_go_down = (current_light == LIGHT_SUN);
1628 If the block below hasn't already been marked invalid:
1630 Check if the node below the block has proper sunlight at top.
1631 If not, the block below is invalid.
1633 Ignore non-transparent nodes as they always have no light
1637 if(block_below_is_valid)
1639 MapNode n = getNodeParent(v3s16(x, -1, z));
1640 if(n.light_propagates())
1642 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN
1643 && sunlight_should_go_down == false)
1644 block_below_is_valid = false;
1645 else if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN
1646 && sunlight_should_go_down == true)
1647 block_below_is_valid = false;
1651 catch(InvalidPositionException &e)
1653 /*std::cout<<"InvalidBlockException for bottom block node"
1655 // Just no block below, no need to panic.
1660 return block_below_is_valid;
1664 void MapBlock::copyTo(VoxelManipulator &dst)
1666 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
1667 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
1669 // Copy from data to VoxelManipulator
1670 dst.copyFrom(data, data_area, v3s16(0,0,0),
1671 getPosRelative(), data_size);
1674 void MapBlock::copyFrom(VoxelManipulator &dst)
1676 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
1677 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
1679 // Copy from VoxelManipulator to data
1680 dst.copyTo(data, data_area, v3s16(0,0,0),
1681 getPosRelative(), data_size);
1684 void MapBlock::stepObjects(float dtime, bool server, u32 daynight_ratio)
1689 m_objects.step(dtime, server, daynight_ratio);
1693 Spawn some objects at random.
1695 Use dayNightDiffed() to approximate being near ground level
1697 if(m_spawn_timer < -999)
1701 if(dayNightDiffed() == true && getObjectCount() == 0)
1703 m_spawn_timer -= dtime;
1704 if(m_spawn_timer <= 0.0)
1706 m_spawn_timer += myrand() % 300;
1709 (myrand()%(MAP_BLOCKSIZE-1))+0,
1710 (myrand()%(MAP_BLOCKSIZE-1))+0
1713 s16 y = getGroundLevel(p2d);
1717 v3s16 p(p2d.X, y+1, p2d.Y);
1719 if(getNode(p).d == CONTENT_AIR
1720 && getNode(p).getLightBlend(daynight_ratio) <= 11)
1722 RatObject *obj = new RatObject(NULL, -1, intToFloat(p, BS));
1734 void MapBlock::updateDayNightDiff()
1738 m_day_night_differs = false;
1742 bool differs = false;
1745 Check if any lighting value differs
1747 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1749 MapNode &n = data[i];
1750 if(n.getLight(LIGHTBANK_DAY) != n.getLight(LIGHTBANK_NIGHT))
1758 If some lighting values differ, check if the whole thing is
1759 just air. If it is, differ = false
1763 bool only_air = true;
1764 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1766 MapNode &n = data[i];
1767 if(n.d != CONTENT_AIR)
1777 // Set member variable
1778 m_day_night_differs = differs;
1781 s16 MapBlock::getGroundLevel(v2s16 p2d)
1787 s16 y = MAP_BLOCKSIZE-1;
1790 //if(is_ground_content(getNodeRef(p2d.X, y, p2d.Y).d))
1791 if(content_features(getNodeRef(p2d.X, y, p2d.Y).d).walkable)
1793 if(y == MAP_BLOCKSIZE-1)
1801 catch(InvalidPositionException &e)
1811 void MapBlock::serialize(std::ostream &os, u8 version)
1813 if(!ser_ver_supported(version))
1814 throw VersionMismatchException("ERROR: MapBlock format not supported");
1818 throw SerializationError("ERROR: Not writing dummy block.");
1821 // These have no compression
1822 if(version <= 3 || version == 5 || version == 6)
1824 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1826 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
1827 SharedBuffer<u8> dest(buflen);
1829 dest[0] = is_underground;
1830 for(u32 i=0; i<nodecount; i++)
1832 u32 s = 1 + i * MapNode::serializedLength(version);
1833 data[i].serialize(&dest[s], version);
1836 os.write((char*)*dest, dest.getSize());
1838 else if(version <= 10)
1842 Compress the materials and the params separately.
1846 os.write((char*)&is_underground, 1);
1848 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1850 // Get and compress materials
1851 SharedBuffer<u8> materialdata(nodecount);
1852 for(u32 i=0; i<nodecount; i++)
1854 materialdata[i] = data[i].d;
1856 compress(materialdata, os, version);
1858 // Get and compress lights
1859 SharedBuffer<u8> lightdata(nodecount);
1860 for(u32 i=0; i<nodecount; i++)
1862 lightdata[i] = data[i].param;
1864 compress(lightdata, os, version);
1868 // Get and compress param2
1869 SharedBuffer<u8> param2data(nodecount);
1870 for(u32 i=0; i<nodecount; i++)
1872 param2data[i] = data[i].param2;
1874 compress(param2data, os, version);
1877 // All other versions (newest)
1884 if(m_day_night_differs)
1886 if(m_lighting_expired)
1888 os.write((char*)&flags, 1);
1890 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1896 SharedBuffer<u8> databuf(nodecount*3);
1899 for(u32 i=0; i<nodecount; i++)
1901 databuf[i] = data[i].d;
1905 for(u32 i=0; i<nodecount; i++)
1907 databuf[i+nodecount] = data[i].param;
1911 for(u32 i=0; i<nodecount; i++)
1913 databuf[i+nodecount*2] = data[i].param2;
1917 Compress data to output stream
1920 compress(databuf, os, version);
1927 std::ostringstream oss(std::ios_base::binary);
1928 m_node_metadata.serialize(oss);
1929 os<<serializeString(oss.str());
1934 void MapBlock::deSerialize(std::istream &is, u8 version)
1936 if(!ser_ver_supported(version))
1937 throw VersionMismatchException("ERROR: MapBlock format not supported");
1939 // These have no lighting info
1942 setLightingExpired(true);
1945 // These have no compression
1946 if(version <= 3 || version == 5 || version == 6)
1948 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1951 if(is.gcount() != 1)
1952 throw SerializationError
1953 ("MapBlock::deSerialize: no enough input data");
1954 is_underground = tmp;
1955 for(u32 i=0; i<nodecount; i++)
1957 s32 len = MapNode::serializedLength(version);
1958 SharedBuffer<u8> d(len);
1959 is.read((char*)*d, len);
1960 if(is.gcount() != len)
1961 throw SerializationError
1962 ("MapBlock::deSerialize: no enough input data");
1963 data[i].deSerialize(*d, version);
1966 else if(version <= 10)
1968 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1971 is.read((char*)&t8, 1);
1972 is_underground = t8;
1975 // Uncompress and set material data
1976 std::ostringstream os(std::ios_base::binary);
1977 decompress(is, os, version);
1978 std::string s = os.str();
1979 if(s.size() != nodecount)
1980 throw SerializationError
1981 ("MapBlock::deSerialize: invalid format");
1982 for(u32 i=0; i<s.size(); i++)
1988 // Uncompress and set param data
1989 std::ostringstream os(std::ios_base::binary);
1990 decompress(is, os, version);
1991 std::string s = os.str();
1992 if(s.size() != nodecount)
1993 throw SerializationError
1994 ("MapBlock::deSerialize: invalid format");
1995 for(u32 i=0; i<s.size(); i++)
1997 data[i].param = s[i];
2003 // Uncompress and set param2 data
2004 std::ostringstream os(std::ios_base::binary);
2005 decompress(is, os, version);
2006 std::string s = os.str();
2007 if(s.size() != nodecount)
2008 throw SerializationError
2009 ("MapBlock::deSerialize: invalid format");
2010 for(u32 i=0; i<s.size(); i++)
2012 data[i].param2 = s[i];
2016 // All other versions (newest)
2019 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2022 is.read((char*)&flags, 1);
2023 is_underground = (flags & 0x01) ? true : false;
2024 m_day_night_differs = (flags & 0x02) ? true : false;
2025 m_lighting_expired = (flags & 0x04) ? true : false;
2028 std::ostringstream os(std::ios_base::binary);
2029 decompress(is, os, version);
2030 std::string s = os.str();
2031 if(s.size() != nodecount*3)
2032 throw SerializationError
2033 ("MapBlock::deSerialize: invalid format");
2036 for(u32 i=0; i<nodecount; i++)
2041 for(u32 i=0; i<nodecount; i++)
2043 data[i].param = s[i+nodecount];
2046 for(u32 i=0; i<nodecount; i++)
2048 data[i].param2 = s[i+nodecount*2];
2058 std::string data = deSerializeString(is);
2059 std::istringstream iss(data, std::ios_base::binary);
2060 m_node_metadata.deSerialize(iss);
2062 catch(SerializationError &e)
2064 dstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
2065 <<" while deserializing node metadata"<<std::endl;
2071 Translate nodes as specified in the translate_to fields of
2074 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
2076 MapNode &n = data[i];
2078 MapNode *translate_to = content_features(n.d).translate_to;
2081 dstream<<"MapBlock: WARNING: Translating node "<<n.d<<" to "
2082 <<translate_to->d<<std::endl;