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);
1692 Spawn some objects at random.
1694 Use dayNightDiffed() to approximate being near ground level
1696 if(m_spawn_timer < -999)
1700 if(dayNightDiffed() == true && getObjectCount() == 0)
1702 m_spawn_timer -= dtime;
1703 if(m_spawn_timer <= 0.0)
1705 m_spawn_timer += myrand() % 300;
1708 (myrand()%(MAP_BLOCKSIZE-1))+0,
1709 (myrand()%(MAP_BLOCKSIZE-1))+0
1712 s16 y = getGroundLevel(p2d);
1716 v3s16 p(p2d.X, y+1, p2d.Y);
1718 if(getNode(p).d == CONTENT_AIR
1719 && getNode(p).getLightBlend(daynight_ratio) <= 11)
1721 RatObject *obj = new RatObject(NULL, -1, intToFloat(p, BS));
1732 void MapBlock::updateDayNightDiff()
1736 m_day_night_differs = false;
1740 bool differs = false;
1743 Check if any lighting value differs
1745 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1747 MapNode &n = data[i];
1748 if(n.getLight(LIGHTBANK_DAY) != n.getLight(LIGHTBANK_NIGHT))
1756 If some lighting values differ, check if the whole thing is
1757 just air. If it is, differ = false
1761 bool only_air = true;
1762 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1764 MapNode &n = data[i];
1765 if(n.d != CONTENT_AIR)
1775 // Set member variable
1776 m_day_night_differs = differs;
1779 s16 MapBlock::getGroundLevel(v2s16 p2d)
1785 s16 y = MAP_BLOCKSIZE-1;
1788 //if(is_ground_content(getNodeRef(p2d.X, y, p2d.Y).d))
1789 if(content_features(getNodeRef(p2d.X, y, p2d.Y).d).walkable)
1791 if(y == MAP_BLOCKSIZE-1)
1799 catch(InvalidPositionException &e)
1809 void MapBlock::serialize(std::ostream &os, u8 version)
1811 if(!ser_ver_supported(version))
1812 throw VersionMismatchException("ERROR: MapBlock format not supported");
1816 throw SerializationError("ERROR: Not writing dummy block.");
1819 // These have no compression
1820 if(version <= 3 || version == 5 || version == 6)
1822 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1824 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
1825 SharedBuffer<u8> dest(buflen);
1827 dest[0] = is_underground;
1828 for(u32 i=0; i<nodecount; i++)
1830 u32 s = 1 + i * MapNode::serializedLength(version);
1831 data[i].serialize(&dest[s], version);
1834 os.write((char*)*dest, dest.getSize());
1836 else if(version <= 10)
1840 Compress the materials and the params separately.
1844 os.write((char*)&is_underground, 1);
1846 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1848 // Get and compress materials
1849 SharedBuffer<u8> materialdata(nodecount);
1850 for(u32 i=0; i<nodecount; i++)
1852 materialdata[i] = data[i].d;
1854 compress(materialdata, os, version);
1856 // Get and compress lights
1857 SharedBuffer<u8> lightdata(nodecount);
1858 for(u32 i=0; i<nodecount; i++)
1860 lightdata[i] = data[i].param;
1862 compress(lightdata, os, version);
1866 // Get and compress param2
1867 SharedBuffer<u8> param2data(nodecount);
1868 for(u32 i=0; i<nodecount; i++)
1870 param2data[i] = data[i].param2;
1872 compress(param2data, os, version);
1875 // All other versions (newest)
1882 if(m_day_night_differs)
1884 if(m_lighting_expired)
1886 os.write((char*)&flags, 1);
1888 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1894 SharedBuffer<u8> databuf(nodecount*3);
1897 for(u32 i=0; i<nodecount; i++)
1899 databuf[i] = data[i].d;
1903 for(u32 i=0; i<nodecount; i++)
1905 databuf[i+nodecount] = data[i].param;
1909 for(u32 i=0; i<nodecount; i++)
1911 databuf[i+nodecount*2] = data[i].param2;
1915 Compress data to output stream
1918 compress(databuf, os, version);
1925 m_node_metadata.serialize(os);
1930 void MapBlock::deSerialize(std::istream &is, u8 version)
1932 if(!ser_ver_supported(version))
1933 throw VersionMismatchException("ERROR: MapBlock format not supported");
1935 // These have no compression
1936 if(version <= 3 || version == 5 || version == 6)
1938 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1941 if(is.gcount() != 1)
1942 throw SerializationError
1943 ("MapBlock::deSerialize: no enough input data");
1944 is_underground = tmp;
1945 for(u32 i=0; i<nodecount; i++)
1947 s32 len = MapNode::serializedLength(version);
1948 SharedBuffer<u8> d(len);
1949 is.read((char*)*d, len);
1950 if(is.gcount() != len)
1951 throw SerializationError
1952 ("MapBlock::deSerialize: no enough input data");
1953 data[i].deSerialize(*d, version);
1956 else if(version <= 10)
1958 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1961 is.read((char*)&t8, 1);
1962 is_underground = t8;
1965 // Uncompress and set material data
1966 std::ostringstream os(std::ios_base::binary);
1967 decompress(is, os, version);
1968 std::string s = os.str();
1969 if(s.size() != nodecount)
1970 throw SerializationError
1971 ("MapBlock::deSerialize: invalid format");
1972 for(u32 i=0; i<s.size(); i++)
1978 // Uncompress and set param data
1979 std::ostringstream os(std::ios_base::binary);
1980 decompress(is, os, version);
1981 std::string s = os.str();
1982 if(s.size() != nodecount)
1983 throw SerializationError
1984 ("MapBlock::deSerialize: invalid format");
1985 for(u32 i=0; i<s.size(); i++)
1987 data[i].param = s[i];
1993 // Uncompress and set param2 data
1994 std::ostringstream os(std::ios_base::binary);
1995 decompress(is, os, version);
1996 std::string s = os.str();
1997 if(s.size() != nodecount)
1998 throw SerializationError
1999 ("MapBlock::deSerialize: invalid format");
2000 for(u32 i=0; i<s.size(); i++)
2002 data[i].param2 = s[i];
2006 // All other versions (newest)
2009 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2012 is.read((char*)&flags, 1);
2013 is_underground = (flags & 0x01) ? true : false;
2014 m_day_night_differs = (flags & 0x02) ? true : false;
2015 m_lighting_expired = (flags & 0x04) ? true : false;
2018 std::ostringstream os(std::ios_base::binary);
2019 decompress(is, os, version);
2020 std::string s = os.str();
2021 if(s.size() != nodecount*3)
2022 throw SerializationError
2023 ("MapBlock::deSerialize: invalid format");
2026 for(u32 i=0; i<nodecount; i++)
2031 for(u32 i=0; i<nodecount; i++)
2033 data[i].param = s[i+nodecount];
2036 for(u32 i=0; i<nodecount; i++)
2038 data[i].param2 = s[i+nodecount*2];
2046 m_node_metadata.deSerialize(is);
2051 Translate nodes as specified in the translate_to fields of
2054 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
2056 MapNode &n = data[i];
2058 MapNode *translate_to = content_features(n.d).translate_to;
2061 dstream<<"MapBlock: WARNING: Translating node "<<n.d<<" to "
2062 <<translate_to->d<<std::endl;