]> git.lizzy.rs Git - dragonfireclient.git/blob - src/mapblock.cpp
6cf816dbe2ee3e5ecd9f03e3dd55033010397e3b
[dragonfireclient.git] / src / mapblock.cpp
1 /*
2 Minetest-c55
3 Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
4
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.
9
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.
14
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.
18 */
19
20 #include "mapblock.h"
21 #include "map.h"
22 // For g_settings
23 #include "main.h"
24 #include "light.h"
25 #include <sstream>
26
27 #ifndef SERVER
28 void MeshMakeData::fill(u32 daynight_ratio, MapBlock *block)
29 {
30         m_daynight_ratio = daynight_ratio;
31         m_blockpos = block->getPos();
32
33         v3s16 blockpos_nodes = m_blockpos*MAP_BLOCKSIZE;
34         
35         /*
36                 There is no harm not copying the TempMods of the neighbors
37                 because they are already copied to this block
38         */
39         m_temp_mods.clear();
40         block->copyTempMods(m_temp_mods);
41         
42         /*
43                 Copy data
44         */
45
46         // Allocate this block + neighbors
47         m_vmanip.clear();
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)));
50
51         {
52                 //TimeTaker timer("copy central block data");
53                 // 0ms
54
55                 // Copy our data
56                 block->copyTo(m_vmanip);
57         }
58         {
59                 //TimeTaker timer("copy neighbor block data");
60                 // 0ms
61
62                 /*
63                         Copy neighbors. This is lightning fast.
64                         Copying only the borders would be *very* slow.
65                 */
66                 
67                 // Get map
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;
73
74                 for(u16 i=0; i<6; i++)
75                 {
76                         const v3s16 &dir = g_6dirs[i];
77                         v3s16 bp = m_blockpos + dir;
78                         MapBlock *b = map->getBlockNoCreateNoEx(bp);
79                         if(b)
80                                 b->copyTo(m_vmanip);
81                 }
82         }
83 }
84 #endif
85
86 /*
87         Parameters must consist of air and !air.
88         Order doesn't matter.
89
90         If either of the nodes doesn't exist, light is 0.
91         
92         parameters:
93                 daynight_ratio: 0...1000
94                 n: getNodeParent(p)
95                 n2: getNodeParent(p + face_dir)
96                 face_dir: axis oriented unit vector from p to p2
97         
98         returns encoded light value.
99 */
100 u8 getFaceLight(u32 daynight_ratio, MapNode n, MapNode n2,
101                 v3s16 face_dir)
102 {
103         try{
104                 u8 light;
105                 u8 l1 = n.getLightBlend(daynight_ratio);
106                 u8 l2 = n2.getLightBlend(daynight_ratio);
107                 if(l1 > l2)
108                         light = l1;
109                 else
110                         light = l2;
111
112                 // Make some nice difference to different sides
113
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);*/
119                 
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);
125
126                 return light;
127         }
128         catch(InvalidPositionException &e)
129         {
130                 return 0;
131         }
132 }
133
134 #ifndef SERVER
135
136 void makeFastFace(TileSpec tile, u8 light, v3f p,
137                 v3s16 dir, v3f scale, v3f posRelative_f,
138                 core::array<FastFace> &dest)
139 {
140         FastFace face;
141         
142         // Position is at the center of the cube.
143         v3f pos = p * BS;
144         posRelative_f *= BS;
145
146         v3f vertex_pos[4];
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);
153         
154         if(dir == v3s16(0,0,1))
155         {
156                 for(u16 i=0; i<4; i++)
157                         vertex_pos[i].rotateXZBy(0);
158         }
159         else if(dir == v3s16(0,0,-1))
160         {
161                 for(u16 i=0; i<4; i++)
162                         vertex_pos[i].rotateXZBy(180);
163         }
164         else if(dir == v3s16(1,0,0))
165         {
166                 for(u16 i=0; i<4; i++)
167                         vertex_pos[i].rotateXZBy(-90);
168         }
169         else if(dir == v3s16(-1,0,0))
170         {
171                 for(u16 i=0; i<4; i++)
172                         vertex_pos[i].rotateXZBy(90);
173         }
174         else if(dir == v3s16(0,1,0))
175         {
176                 for(u16 i=0; i<4; i++)
177                         vertex_pos[i].rotateYZBy(-90);
178         }
179         else if(dir == v3s16(0,-1,0))
180         {
181                 for(u16 i=0; i<4; i++)
182                         vertex_pos[i].rotateYZBy(90);
183         }
184
185         for(u16 i=0; i<4; i++)
186         {
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;
191         }
192
193         f32 abs_scale = 1.;
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;
197
198         v3f zerovector = v3f(0,0,0);
199         
200         //u8 li = decode_light(light);
201         u8 li = light;
202         //u8 li = 255; //DEBUG
203
204         u8 alpha = tile.alpha;
205         /*u8 alpha = 255;
206         if(tile.id == TILE_WATER)
207                 alpha = WATER_ALPHA;*/
208
209         video::SColor c = video::SColor(alpha,li,li,li);
210
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;
215
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));
224
225         face.tile = tile;
226         //DEBUG
227         //f->tile = TILE_STONE;
228         
229         dest.push_back(face);
230         //return f;
231 }
232         
233 /*
234         Gets node tile from any place relative to block.
235         Returns TILE_NODE if doesn't exist or should not be drawn.
236 */
237 TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir,
238                 NodeModMap &temp_mods)
239 {
240         TileSpec spec;
241         spec = mn.getTile(face_dir);
242         
243         /*
244                 Check temporary modifications on this node
245         */
246         /*core::map<v3s16, NodeMod>::Node *n;
247         n = m_temp_mods.find(p);
248         // If modified
249         if(n != NULL)
250         {
251                 struct NodeMod mod = n->getValue();*/
252         NodeMod mod;
253         if(temp_mods.get(p, &mod))
254         {
255                 if(mod.type == NODEMOD_CHANGECONTENT)
256                 {
257                         MapNode mn2(mod.param);
258                         spec = mn2.getTile(face_dir);
259                 }
260                 if(mod.type == NODEMOD_CRACK)
261                 {
262                         /*
263                                 Get texture id, translate it to name, append stuff to
264                                 name, get texture id
265                         */
266
267                         // Get original texture name
268                         u32 orig_id = spec.texture.id;
269                         std::string orig_name = g_texturesource->getTextureName(orig_id);
270
271                         // Create new texture name
272                         std::ostringstream os;
273                         os<<orig_name<<"^[crack"<<mod.param;
274
275                         // Get new texture
276                         u32 new_id = g_texturesource->getTextureId(os.str());
277                         
278                         /*dstream<<"MapBlock::getNodeTile(): Switching from "
279                                         <<orig_name<<" to "<<os.str()<<" ("
280                                         <<orig_id<<" to "<<new_id<<")"<<std::endl;*/
281                         
282                         spec.texture = g_texturesource->getTexture(new_id);
283                 }
284         }
285         
286         return spec;
287 }
288
289 u8 getNodeContent(v3s16 p, MapNode mn, NodeModMap &temp_mods)
290 {
291         /*
292                 Check temporary modifications on this node
293         */
294         /*core::map<v3s16, NodeMod>::Node *n;
295         n = m_temp_mods.find(p);
296         // If modified
297         if(n != NULL)
298         {
299                 struct NodeMod mod = n->getValue();*/
300         NodeMod mod;
301         if(temp_mods.get(p, &mod))
302         {
303                 if(mod.type == NODEMOD_CHANGECONTENT)
304                 {
305                         // Overrides content
306                         return mod.param;
307                 }
308                 if(mod.type == NODEMOD_CRACK)
309                 {
310                         /*
311                                 Content doesn't change.
312                                 
313                                 face_contents works just like it should, because
314                                 there should not be faces between differently cracked
315                                 nodes.
316
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.
320                         */
321                 }
322         }
323
324         return mn.d;
325 }
326
327 /*
328         startpos:
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
331 */
332 void updateFastFaceRow(
333                 u32 daynight_ratio,
334                 v3f posRelative_f,
335                 v3s16 startpos,
336                 u16 length,
337                 v3s16 translate_dir,
338                 v3f translate_dir_f,
339                 v3s16 face_dir,
340                 v3f face_dir_f,
341                 core::array<FastFace> &dest,
342                 NodeModMap &temp_mods,
343                 VoxelManipulator &vmanip,
344                 v3s16 blockpos_nodes)
345 {
346         v3s16 p = startpos;
347         
348         u16 continuous_tiles_count = 0;
349         
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);
355
356         for(u16 j=0; j<length; j++)
357         {
358                 bool next_is_different = true;
359                 
360                 v3s16 p_next;
361                 MapNode n0_next;
362                 MapNode n1_next;
363                 TileSpec tile0_next;
364                 TileSpec tile1_next;
365                 u8 light_next = 0;
366                 
367                 // If at last position, there is nothing to compare to and
368                 // the face must be drawn anyway
369                 if(j != length - 1)
370                 {
371                         p_next = p + translate_dir;
372                         
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);
378
379                         if(tile0_next == tile0
380                                         && tile1_next == tile1
381                                         && light_next == light)
382                         {
383                                 next_is_different = false;
384                         }
385                 }
386
387                 continuous_tiles_count++;
388                 
389                 // This is set to true if the texture doesn't allow more tiling
390                 bool end_of_texture = false;
391                 /*
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.
395
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.
399                 */
400                 if(tile0.texture.atlas != NULL && tile0.texture.tiled != 0)
401                 {
402                         if(tile0.texture.tiled <= continuous_tiles_count)
403                                 end_of_texture = true;
404                 }
405                 if(tile1.texture.atlas != NULL && tile1.texture.tiled != 0)
406                 {
407                         if(tile1.texture.tiled <= continuous_tiles_count)
408                                 end_of_texture = true;
409                 }
410                 
411                 //end_of_texture = true; //DEBUG
412                 
413                 if(next_is_different || end_of_texture)
414                 {
415                         /*
416                                 Create a face if there should be one
417                         */
418                         //u8 mf = face_contents(tile0, tile1);
419                         // This is hackish
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);
423                         
424                         if(mf != 0)
425                         {
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;
430                                 v3f scale(1,1,1);
431                                 if(translate_dir.X != 0){
432                                         scale.X = continuous_tiles_count;
433                                 }
434                                 if(translate_dir.Y != 0){
435                                         scale.Y = continuous_tiles_count;
436                                 }
437                                 if(translate_dir.Z != 0){
438                                         scale.Z = continuous_tiles_count;
439                                 }
440                                 
441                                 //FastFace *f;
442
443                                 // If node at sp (tile0) is more solid
444                                 if(mf == 1)
445                                 {
446                                         makeFastFace(tile0, decode_light(light),
447                                                         sp, face_dir, scale,
448                                                         posRelative_f, dest);
449                                 }
450                                 // If node at sp is less solid (mf == 2)
451                                 else
452                                 {
453                                         makeFastFace(tile1, decode_light(light),
454                                                         sp+face_dir_f, -face_dir, scale,
455                                                         posRelative_f, dest);
456                                 }
457                                 //dest.push_back(f);
458                         }
459
460                         continuous_tiles_count = 0;
461                         n0 = n0_next;
462                         n1 = n1_next;
463                         tile0 = tile0_next;
464                         tile1 = tile1_next;
465                         light = light_next;
466                 }
467                 
468                 p = p_next;
469         }
470 }
471
472 /*
473         This is used because CMeshBuffer::append() is very slow
474 */
475 struct PreMeshBuffer
476 {
477         video::SMaterial material;
478         core::array<u16> indices;
479         core::array<video::S3DVertex> vertices;
480 };
481
482 class MeshCollector
483 {
484 public:
485         void append(
486                         video::SMaterial material,
487                         const video::S3DVertex* const vertices,
488                         u32 numVertices,
489                         const u16* const indices,
490                         u32 numIndices
491                 )
492         {
493                 PreMeshBuffer *p = NULL;
494                 for(u32 i=0; i<m_prebuffers.size(); i++)
495                 {
496                         PreMeshBuffer &pp = m_prebuffers[i];
497                         if(pp.material != material)
498                                 continue;
499
500                         p = &pp;
501                         break;
502                 }
503
504                 if(p == NULL)
505                 {
506                         PreMeshBuffer pp;
507                         pp.material = material;
508                         m_prebuffers.push_back(pp);
509                         p = &m_prebuffers[m_prebuffers.size()-1];
510                 }
511
512                 u32 vertex_count = p->vertices.size();
513                 for(u32 i=0; i<numIndices; i++)
514                 {
515                         u32 j = indices[i] + vertex_count;
516                         if(j > 65535)
517                         {
518                                 dstream<<"FIXME: Meshbuffer ran out of indices"<<std::endl;
519                                 // NOTE: Fix is to just add an another MeshBuffer
520                         }
521                         p->indices.push_back(j);
522                 }
523                 for(u32 i=0; i<numVertices; i++)
524                 {
525                         p->vertices.push_back(vertices[i]);
526                 }
527         }
528
529         void fillMesh(scene::SMesh *mesh)
530         {
531                 /*dstream<<"Filling mesh with "<<m_prebuffers.size()
532                                 <<" meshbuffers"<<std::endl;*/
533                 for(u32 i=0; i<m_prebuffers.size(); i++)
534                 {
535                         PreMeshBuffer &p = m_prebuffers[i];
536
537                         /*dstream<<"p.vertices.size()="<<p.vertices.size()
538                                         <<", p.indices.size()="<<p.indices.size()
539                                         <<std::endl;*/
540                         
541                         // Create meshbuffer
542                         
543                         // This is a "Standard MeshBuffer",
544                         // it's a typedeffed CMeshBuffer<video::S3DVertex>
545                         scene::SMeshBuffer *buf = new scene::SMeshBuffer();
546                         // Set material
547                         buf->Material = p.material;
548                         //((scene::SMeshBuffer*)buf)->Material = p.material;
549                         // Use VBO
550                         //buf->setHardwareMappingHint(scene::EHM_STATIC);
551                         // Add to mesh
552                         mesh->addMeshBuffer(buf);
553                         // Mesh grabbed it
554                         buf->drop();
555
556                         buf->append(p.vertices.pointer(), p.vertices.size(),
557                                         p.indices.pointer(), p.indices.size());
558                 }
559         }
560
561 private:
562         core::array<PreMeshBuffer> m_prebuffers;
563 };
564
565 scene::SMesh* makeMapBlockMesh(MeshMakeData *data)
566 {
567         // 4-21ms for MAP_BLOCKSIZE=16
568         // 24-155ms for MAP_BLOCKSIZE=32
569         //TimeTaker timer1("makeMapBlockMesh()");
570
571         core::array<FastFace> fastfaces_new;
572
573         v3s16 blockpos_nodes = data->m_blockpos*MAP_BLOCKSIZE;
574         
575         // floating point conversion
576         v3f posRelative_f(blockpos_nodes.X, blockpos_nodes.Y, blockpos_nodes.Z);
577         
578         /*
579                 Some settings
580         */
581         bool new_style_water = g_settings.getBool("new_style_water");
582         bool new_style_leaves = g_settings.getBool("new_style_leaves");
583         
584         float node_water_level = 1.0;
585         if(new_style_water)
586                 node_water_level = 0.85;
587         
588         /*
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.
592
593                 NOTE: This is the slowest part of this method.
594         */
595         
596         {
597                 // 4-23ms for MAP_BLOCKSIZE=16
598                 //TimeTaker timer2("updateMesh() collect");
599
600                 /*
601                         Go through every y,z and get top faces in rows of x+
602                 */
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,
607                                                 v3s16(1,0,0), //dir
608                                                 v3f  (1,0,0),
609                                                 v3s16(0,1,0), //face dir
610                                                 v3f  (0,1,0),
611                                                 fastfaces_new,
612                                                 data->m_temp_mods,
613                                                 data->m_vmanip,
614                                                 blockpos_nodes);
615                         }
616                 }
617                 /*
618                         Go through every x,y and get right faces in rows of z+
619                 */
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,
624                                                 v3s16(0,0,1),
625                                                 v3f  (0,0,1),
626                                                 v3s16(1,0,0),
627                                                 v3f  (1,0,0),
628                                                 fastfaces_new,
629                                                 data->m_temp_mods,
630                                                 data->m_vmanip,
631                                                 blockpos_nodes);
632                         }
633                 }
634                 /*
635                         Go through every y,z and get back faces in rows of x+
636                 */
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,
641                                                 v3s16(1,0,0),
642                                                 v3f  (1,0,0),
643                                                 v3s16(0,0,1),
644                                                 v3f  (0,0,1),
645                                                 fastfaces_new,
646                                                 data->m_temp_mods,
647                                                 data->m_vmanip,
648                                                 blockpos_nodes);
649                         }
650                 }
651         }
652
653         // End of slow part
654
655         /*
656                 Convert FastFaces to SMesh
657         */
658
659         MeshCollector collector;
660
661         if(fastfaces_new.size() > 0)
662         {
663                 // avg 0ms (100ms spikes when loading textures the first time)
664                 //TimeTaker timer2("updateMesh() mesh building");
665
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);
672
673                 for(u32 i=0; i<fastfaces_new.size(); i++)
674                 {
675                         FastFace &f = fastfaces_new[i];
676
677                         const u16 indices[] = {0,1,2,2,3,0};
678                         
679                         video::ITexture *texture = f.tile.texture.atlas;
680                         if(texture == NULL)
681                                 continue;
682
683                         material.setTexture(0, texture);
684                         
685                         f.tile.applyMaterialOptions(material);
686                         
687                         collector.append(material, f.vertices, 4, indices, 6);
688                 }
689         }
690
691         /*
692                 Add special graphics:
693                 - torches
694                 - flowing water
695         */
696
697         // 0ms
698         //TimeTaker timer2("updateMesh() adding special stuff");
699
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);
710
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);
721
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++)
725         {
726                 v3s16 p(x,y,z);
727
728                 MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes+p);
729                 
730                 /*
731                         Add torches to mesh
732                 */
733                 if(n.d == CONTENT_TORCH)
734                 {
735                         video::SColor c(255,255,255,255);
736
737                         // Wall at X+ of node
738                         video::S3DVertex vertices[4] =
739                         {
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),
744                         };
745
746                         v3s16 dir = unpackDir(n.dir);
747
748                         for(s32 i=0; i<4; i++)
749                         {
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);
762
763                                 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
764                         }
765
766                         // Set material
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;
774
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"));
785                         else
786                                 material.setTexture(0, 
787                                                 g_texturesource->getTextureRaw("torch.png"));
788
789                         u16 indices[] = {0,1,2,2,3,0};
790                         // Add to mesh collector
791                         collector.append(material, vertices, 4, indices, 6);
792                 }
793                 /*
794                         Signs on walls
795                 */
796                 if(n.d == CONTENT_SIGN_WALL)
797                 {
798                         u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
799                         video::SColor c(255,l,l,l);
800                                 
801                         float d = (float)BS/16;
802                         // Wall at X+ of node
803                         video::S3DVertex vertices[4] =
804                         {
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),
809                         };
810
811                         v3s16 dir = unpackDir(n.dir);
812
813                         for(s32 i=0; i<4; i++)
814                         {
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);
827
828                                 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
829                         }
830
831                         // Set material
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;
840
841                         material.setTexture(0, 
842                                         g_texturesource->getTextureRaw("sign_wall.png"));
843
844                         u16 indices[] = {0,1,2,2,3,0};
845                         // Add to mesh collector
846                         collector.append(material, vertices, 4, indices, 6);
847                 }
848                 /*
849                         Add flowing water to mesh
850                 */
851                 else if(n.d == CONTENT_WATER)
852                 {
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)
856                                 top_is_water = true;
857                         
858                         u8 l = 0;
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)
863                         else
864                                 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
865                         video::SColor c(WATER_ALPHA,l,l,l);
866                         
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] = {
874                                 v3s16(0,0,0),
875                                 v3s16(0,0,1),
876                                 v3s16(0,0,-1),
877                                 v3s16(1,0,0),
878                                 v3s16(-1,0,0),
879                                 v3s16(1,0,1),
880                                 v3s16(-1,0,-1),
881                                 v3s16(1,0,-1),
882                                 v3s16(-1,0,1),
883                         };
884                         for(u32 i=0; i<9; i++)
885                         {
886                                 u8 content = CONTENT_AIR;
887                                 float level = -0.5 * BS;
888                                 u8 flags = 0;
889                                 // Check neighbor
890                                 v3s16 p2 = p + neighbor_dirs[i];
891                                 MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
892                                 if(n2.d != CONTENT_IGNORE)
893                                 {
894                                         content = n2.d;
895
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;
901
902                                         // Check node above neighbor.
903                                         // NOTE: This doesn't get executed if neighbor
904                                         //       doesn't exist
905                                         p2.Y += 1;
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;
909                                 }
910                                 
911                                 neighbor_levels.insert(neighbor_dirs[i], level);
912                                 neighbor_contents.insert(neighbor_dirs[i], content);
913                                 neighbor_flags.insert(neighbor_dirs[i], flags);
914                         }
915
916                         //float water_level = (-0.5 + ((float)n.param2 + 0.5) / 8.0) * BS;
917                         //float water_level = neighbor_levels[v3s16(0,0,0)];
918
919                         // Corner heights (average between four waters)
920                         f32 corner_levels[4];
921                         
922                         v3s16 halfdirs[4] = {
923                                 v3s16(0,0,0),
924                                 v3s16(1,0,0),
925                                 v3s16(1,0,1),
926                                 v3s16(0,0,1),
927                         };
928                         for(u32 i=0; i<4; i++)
929                         {
930                                 v3s16 cornerdir = halfdirs[i];
931                                 float cornerlevel = 0;
932                                 u32 valid_count = 0;
933                                 for(u32 j=0; j<4; j++)
934                                 {
935                                         v3s16 neighbordir = cornerdir - halfdirs[j];
936                                         u8 content = neighbor_contents[neighbordir];
937                                         // Special case for source nodes
938                                         if(content == CONTENT_WATERSOURCE)
939                                         {
940                                                 cornerlevel = (-0.5+node_water_level)*BS;
941                                                 valid_count = 1;
942                                                 break;
943                                         }
944                                         else if(content == CONTENT_WATER)
945                                         {
946                                                 cornerlevel += neighbor_levels[neighbordir];
947                                                 valid_count++;
948                                         }
949                                         else if(content == CONTENT_AIR)
950                                         {
951                                                 cornerlevel += -0.5*BS;
952                                                 valid_count++;
953                                         }
954                                 }
955                                 if(valid_count > 0)
956                                         cornerlevel /= valid_count;
957                                 corner_levels[i] = cornerlevel;
958                         }
959
960                         /*
961                                 Generate sides
962                         */
963
964                         v3s16 side_dirs[4] = {
965                                 v3s16(1,0,0),
966                                 v3s16(-1,0,0),
967                                 v3s16(0,0,1),
968                                 v3s16(0,0,-1),
969                         };
970                         s16 side_corners[4][2] = {
971                                 {1, 2},
972                                 {3, 0},
973                                 {2, 3},
974                                 {0, 1},
975                         };
976                         for(u32 i=0; i<4; i++)
977                         {
978                                 v3s16 dir = side_dirs[i];
979
980                                 /*
981                                         If our topside is water and neighbor's topside
982                                         is water, don't draw side face
983                                 */
984                                 if(top_is_water &&
985                                                 neighbor_flags[dir] & neighborflag_top_is_water)
986                                         continue;
987
988                                 u8 neighbor_content = neighbor_contents[dir];
989                                 
990                                 // Don't draw face if neighbor is not air or water
991                                 if(neighbor_content != CONTENT_AIR
992                                                 && neighbor_content != CONTENT_WATER)
993                                         continue;
994                                 
995                                 bool neighbor_is_water = (neighbor_content == CONTENT_WATER);
996                                 
997                                 // Don't draw any faces if neighbor is water and top is water
998                                 if(neighbor_is_water == true && top_is_water == false)
999                                         continue;
1000                                 
1001                                 video::S3DVertex vertices[4] =
1002                                 {
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()),
1015                                 };
1016                                 
1017                                 /*
1018                                         If our topside is water, set upper border of face
1019                                         at upper border of node
1020                                 */
1021                                 if(top_is_water)
1022                                 {
1023                                         vertices[2].Pos.Y = 0.5*BS;
1024                                         vertices[3].Pos.Y = 0.5*BS;
1025                                 }
1026                                 /*
1027                                         Otherwise upper position of face is corner levels
1028                                 */
1029                                 else
1030                                 {
1031                                         vertices[2].Pos.Y = corner_levels[side_corners[i][0]];
1032                                         vertices[3].Pos.Y = corner_levels[side_corners[i][1]];
1033                                 }
1034                                 
1035                                 /*
1036                                         If neighbor is water, lower border of face is corner
1037                                         water levels
1038                                 */
1039                                 if(neighbor_is_water)
1040                                 {
1041                                         vertices[0].Pos.Y = corner_levels[side_corners[i][1]];
1042                                         vertices[1].Pos.Y = corner_levels[side_corners[i][0]];
1043                                 }
1044                                 /*
1045                                         If neighbor is not water, lower border of face is
1046                                         lower border of node
1047                                 */
1048                                 else
1049                                 {
1050                                         vertices[0].Pos.Y = -0.5*BS;
1051                                         vertices[1].Pos.Y = -0.5*BS;
1052                                 }
1053                                 
1054                                 for(s32 j=0; j<4; j++)
1055                                 {
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);
1064
1065                                         vertices[j].Pos += intToFloat(p + blockpos_nodes, BS);
1066                                 }
1067
1068                                 u16 indices[] = {0,1,2,2,3,0};
1069                                 // Add to mesh collector
1070                                 collector.append(material_water1, vertices, 4, indices, 6);
1071                         }
1072                         
1073                         /*
1074                                 Generate top side, if appropriate
1075                         */
1076                         
1077                         if(top_is_water == false)
1078                         {
1079                                 video::S3DVertex vertices[4] =
1080                                 {
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()),
1093                                 };
1094                                 
1095                                 // This fixes a strange bug
1096                                 s32 corner_resolve[4] = {3,2,1,0};
1097
1098                                 for(s32 i=0; i<4; i++)
1099                                 {
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);
1105                                 }
1106
1107                                 u16 indices[] = {0,1,2,2,3,0};
1108                                 // Add to mesh collector
1109                                 collector.append(material_water1, vertices, 4, indices, 6);
1110                         }
1111                 }
1112                 /*
1113                         Add water sources to mesh if using new style
1114                 */
1115                 else if(n.d == CONTENT_WATERSOURCE && new_style_water)
1116                 {
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)
1123                                 top_is_air = true;
1124                         
1125                         /*if(top_is_water == true)
1126                                 continue;*/
1127                         if(top_is_air == false)
1128                                 continue;
1129
1130                         u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
1131                         video::SColor c(WATER_ALPHA,l,l,l);
1132                         
1133                         video::S3DVertex vertices[4] =
1134                         {
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()),
1147                         };
1148
1149                         for(s32 i=0; i<4; i++)
1150                         {
1151                                 vertices[i].Pos.Y += (-0.5+node_water_level)*BS;
1152                                 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1153                         }
1154
1155                         u16 indices[] = {0,1,2,2,3,0};
1156                         // Add to mesh collector
1157                         collector.append(material_water1, vertices, 4, indices, 6);
1158                 }
1159                 /*
1160                         Add leaves if using new style
1161                 */
1162                 else if(n.d == CONTENT_LEAVES && new_style_leaves)
1163                 {
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);
1167
1168                         for(u32 j=0; j<6; j++)
1169                         {
1170                                 video::S3DVertex vertices[4] =
1171                                 {
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()),
1184                                 };
1185
1186                                 if(j == 0)
1187                                 {
1188                                         for(u16 i=0; i<4; i++)
1189                                                 vertices[i].Pos.rotateXZBy(0);
1190                                 }
1191                                 else if(j == 1)
1192                                 {
1193                                         for(u16 i=0; i<4; i++)
1194                                                 vertices[i].Pos.rotateXZBy(180);
1195                                 }
1196                                 else if(j == 2)
1197                                 {
1198                                         for(u16 i=0; i<4; i++)
1199                                                 vertices[i].Pos.rotateXZBy(-90);
1200                                 }
1201                                 else if(j == 3)
1202                                 {
1203                                         for(u16 i=0; i<4; i++)
1204                                                 vertices[i].Pos.rotateXZBy(90);
1205                                 }
1206                                 else if(j == 4)
1207                                 {
1208                                         for(u16 i=0; i<4; i++)
1209                                                 vertices[i].Pos.rotateYZBy(-90);
1210                                 }
1211                                 else if(j == 5)
1212                                 {
1213                                         for(u16 i=0; i<4; i++)
1214                                                 vertices[i].Pos.rotateYZBy(90);
1215                                 }
1216
1217                                 for(u16 i=0; i<4; i++)
1218                                 {
1219                                         vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1220                                 }
1221
1222                                 u16 indices[] = {0,1,2,2,3,0};
1223                                 // Add to mesh collector
1224                                 collector.append(material_leaves1, vertices, 4, indices, 6);
1225                         }
1226                 }
1227         }
1228
1229         /*
1230                 Add stuff from collector to mesh
1231         */
1232         
1233         scene::SMesh *mesh_new = NULL;
1234         mesh_new = new scene::SMesh();
1235         
1236         collector.fillMesh(mesh_new);
1237
1238         /*
1239                 Do some stuff to the mesh
1240         */
1241
1242         mesh_new->recalculateBoundingBox();
1243
1244         /*
1245                 Delete new mesh if it is empty
1246         */
1247
1248         if(mesh_new->getMeshBufferCount() == 0)
1249         {
1250                 mesh_new->drop();
1251                 mesh_new = NULL;
1252         }
1253
1254         if(mesh_new)
1255         {
1256 #if 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;
1261 #endif
1262
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);
1266
1267                 /*
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
1271                 */
1272         }
1273
1274         return mesh_new;
1275         
1276         //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1277 }
1278
1279 #endif // !SERVER
1280
1281 /*
1282         MapBlock
1283 */
1284
1285 MapBlock::MapBlock(NodeContainer *parent, v3s16 pos, bool dummy):
1286                 m_parent(parent),
1287                 m_pos(pos),
1288                 changed(true),
1289                 is_underground(false),
1290                 m_lighting_expired(true),
1291                 m_day_night_differs(false),
1292                 //m_not_fully_generated(false),
1293                 m_objects(this)
1294 {
1295         data = NULL;
1296         if(dummy == false)
1297                 reallocate();
1298         
1299         //m_spawn_timer = -10000;
1300
1301 #ifndef SERVER
1302         m_mesh_expired = false;
1303         mesh_mutex.Init();
1304         mesh = NULL;
1305         m_temp_mods_mutex.Init();
1306 #endif
1307 }
1308
1309 MapBlock::~MapBlock()
1310 {
1311 #ifndef SERVER
1312         {
1313                 JMutexAutoLock lock(mesh_mutex);
1314                 
1315                 if(mesh)
1316                 {
1317                         mesh->drop();
1318                         mesh = NULL;
1319                 }
1320         }
1321 #endif
1322
1323         if(data)
1324                 delete[] data;
1325 }
1326
1327 bool MapBlock::isValidPositionParent(v3s16 p)
1328 {
1329         if(isValidPosition(p))
1330         {
1331                 return true;
1332         }
1333         else{
1334                 return m_parent->isValidPosition(getPosRelative() + p);
1335         }
1336 }
1337
1338 MapNode MapBlock::getNodeParent(v3s16 p)
1339 {
1340         if(isValidPosition(p) == false)
1341         {
1342                 return m_parent->getNode(getPosRelative() + p);
1343         }
1344         else
1345         {
1346                 if(data == NULL)
1347                         throw InvalidPositionException();
1348                 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
1349         }
1350 }
1351
1352 void MapBlock::setNodeParent(v3s16 p, MapNode & n)
1353 {
1354         if(isValidPosition(p) == false)
1355         {
1356                 m_parent->setNode(getPosRelative() + p, n);
1357         }
1358         else
1359         {
1360                 if(data == NULL)
1361                         throw InvalidPositionException();
1362                 data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X] = n;
1363         }
1364 }
1365
1366 MapNode MapBlock::getNodeParentNoEx(v3s16 p)
1367 {
1368         if(isValidPosition(p) == false)
1369         {
1370                 try{
1371                         return m_parent->getNode(getPosRelative() + p);
1372                 }
1373                 catch(InvalidPositionException &e)
1374                 {
1375                         return MapNode(CONTENT_IGNORE);
1376                 }
1377         }
1378         else
1379         {
1380                 if(data == NULL)
1381                 {
1382                         return MapNode(CONTENT_IGNORE);
1383                 }
1384                 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
1385         }
1386 }
1387
1388 #ifndef SERVER
1389
1390 #if 1
1391 void MapBlock::updateMesh(u32 daynight_ratio)
1392 {
1393 #if 0
1394         /*
1395                 DEBUG: If mesh has been generated, don't generate it again
1396         */
1397         {
1398                 JMutexAutoLock meshlock(mesh_mutex);
1399                 if(mesh != NULL)
1400                         return;
1401         }
1402 #endif
1403
1404         MeshMakeData data;
1405         data.fill(daynight_ratio, this);
1406         
1407         scene::SMesh *mesh_new = makeMapBlockMesh(&data);
1408         
1409         /*
1410                 Replace the mesh
1411         */
1412
1413         replaceMesh(mesh_new);
1414
1415 }
1416 #endif
1417
1418 void MapBlock::replaceMesh(scene::SMesh *mesh_new)
1419 {
1420         mesh_mutex.Lock();
1421
1422         //scene::SMesh *mesh_old = mesh[daynight_i];
1423         //mesh[daynight_i] = mesh_new;
1424
1425         scene::SMesh *mesh_old = mesh;
1426         mesh = mesh_new;
1427         setMeshExpired(false);
1428         
1429         if(mesh_old != NULL)
1430         {
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++)
1435                 {
1436                         IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1437                 }*/
1438                 
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++)
1443                 {
1444                         scene::IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1445                         dstream<<"buf->getReferenceCount()="
1446                                         <<buf->getReferenceCount()<<std::endl;
1447                 }*/
1448
1449                 // Drop the mesh
1450                 mesh_old->drop();
1451
1452                 //delete mesh_old;
1453         }
1454
1455         mesh_mutex.Unlock();
1456 }
1457         
1458 #endif // !SERVER
1459
1460 /*
1461         Propagates sunlight down through the block.
1462         Doesn't modify nodes that are not affected by sunlight.
1463         
1464         Returns false if sunlight at bottom block is invalid
1465         Returns true if bottom block doesn't exist.
1466
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.
1470
1471         At the moment, all sunlighted nodes are added to light_sources.
1472         - SUGG: This could be optimized
1473
1474         Turns sunglighted mud into grass.
1475
1476         if remove_light==true, sets non-sunlighted nodes black.
1477
1478         if black_air_left!=NULL, it is set to true if non-sunlighted
1479         air is left in block.
1480 */
1481 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources,
1482                 bool remove_light, bool *black_air_left,
1483                 bool grow_grass)
1484 {
1485         // Whether the sunlight at the top of the bottom block is valid
1486         bool block_below_is_valid = true;
1487         
1488         v3s16 pos_relative = getPosRelative();
1489         
1490         for(s16 x=0; x<MAP_BLOCKSIZE; x++)
1491         {
1492                 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
1493                 {
1494 #if 1
1495                         bool no_sunlight = false;
1496                         bool no_top_block = false;
1497                         // Check if node above block has sunlight
1498                         try{
1499                                 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1500                                 if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1501                                 {
1502                                         no_sunlight = true;
1503                                 }
1504                         }
1505                         catch(InvalidPositionException &e)
1506                         {
1507                                 no_top_block = true;
1508                                 
1509                                 // NOTE: This makes over-ground roofed places sunlighted
1510                                 // Assume sunlight, unless is_underground==true
1511                                 if(is_underground)
1512                                 {
1513                                         no_sunlight = true;
1514                                 }
1515                                 else
1516                                 {
1517                                         MapNode n = getNode(v3s16(x, MAP_BLOCKSIZE-1, z));
1518                                         if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
1519                                         {
1520                                                 no_sunlight = true;
1521                                         }
1522                                 }
1523                                 // NOTE: As of now, this just would make everything dark.
1524                                 // No sunlight here
1525                                 //no_sunlight = true;
1526                         }
1527 #endif
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
1532                         try{
1533                                 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1534                                 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
1535                                 {
1536                                         no_sunlight = false;
1537                                 }
1538                         }
1539                         catch(InvalidPositionException &e)
1540                         {
1541                                 no_top_block = true;
1542                         }
1543 #endif
1544
1545                         /*std::cout<<"("<<x<<","<<z<<"): "
1546                                         <<"no_top_block="<<no_top_block
1547                                         <<", is_underground="<<is_underground
1548                                         <<", no_sunlight="<<no_sunlight
1549                                         <<std::endl;*/
1550                 
1551                         s16 y = MAP_BLOCKSIZE-1;
1552                         
1553                         // This makes difference to diminishing in water.
1554                         bool stopped_to_solid_object = false;
1555                         
1556                         u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
1557
1558                         for(; y >= 0; y--)
1559                         {
1560                                 v3s16 pos(x, y, z);
1561                                 MapNode &n = getNodeRef(pos);
1562                                 
1563                                 if(current_light == 0)
1564                                 {
1565                                         // Do nothing
1566                                 }
1567                                 else if(current_light == LIGHT_SUN && n.sunlight_propagates())
1568                                 {
1569                                         // Do nothing: Sunlight is continued
1570                                 }
1571                                 else if(n.light_propagates() == false)
1572                                 {
1573                                         if(grow_grass)
1574                                         {
1575                                                 bool upper_is_air = false;
1576                                                 try
1577                                                 {
1578                                                         if(getNodeParent(pos+v3s16(0,1,0)).d == CONTENT_AIR)
1579                                                                 upper_is_air = true;
1580                                                 }
1581                                                 catch(InvalidPositionException &e)
1582                                                 {
1583                                                 }
1584                                                 // Turn mud into grass
1585                                                 if(upper_is_air && n.d == CONTENT_MUD
1586                                                                 && current_light == LIGHT_SUN)
1587                                                 {
1588                                                         n.d = CONTENT_GRASS;
1589                                                 }
1590                                         }
1591
1592                                         // A solid object is on the way.
1593                                         stopped_to_solid_object = true;
1594                                         
1595                                         // Light stops.
1596                                         current_light = 0;
1597                                 }
1598                                 else
1599                                 {
1600                                         // Diminish light
1601                                         current_light = diminish_light(current_light);
1602                                 }
1603
1604                                 u8 old_light = n.getLight(LIGHTBANK_DAY);
1605
1606                                 if(current_light > old_light || remove_light)
1607                                 {
1608                                         n.setLight(LIGHTBANK_DAY, current_light);
1609                                 }
1610                                 
1611                                 if(diminish_light(current_light) != 0)
1612                                 {
1613                                         light_sources.insert(pos_relative + pos, true);
1614                                 }
1615
1616                                 if(current_light == 0 && stopped_to_solid_object)
1617                                 {
1618                                         if(black_air_left)
1619                                         {
1620                                                 *black_air_left = true;
1621                                         }
1622                                 }
1623                         }
1624
1625                         // Whether or not the block below should see LIGHT_SUN
1626                         bool sunlight_should_go_down = (current_light == LIGHT_SUN);
1627
1628                         /*
1629                                 If the block below hasn't already been marked invalid:
1630
1631                                 Check if the node below the block has proper sunlight at top.
1632                                 If not, the block below is invalid.
1633                                 
1634                                 Ignore non-transparent nodes as they always have no light
1635                         */
1636                         try
1637                         {
1638                         if(block_below_is_valid)
1639                         {
1640                                 MapNode n = getNodeParent(v3s16(x, -1, z));
1641                                 if(n.light_propagates())
1642                                 {
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;
1649                                 }
1650                         }//if
1651                         }//try
1652                         catch(InvalidPositionException &e)
1653                         {
1654                                 /*std::cout<<"InvalidBlockException for bottom block node"
1655                                                 <<std::endl;*/
1656                                 // Just no block below, no need to panic.
1657                         }
1658                 }
1659         }
1660
1661         return block_below_is_valid;
1662 }
1663
1664
1665 void MapBlock::copyTo(VoxelManipulator &dst)
1666 {
1667         v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
1668         VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
1669         
1670         // Copy from data to VoxelManipulator
1671         dst.copyFrom(data, data_area, v3s16(0,0,0),
1672                         getPosRelative(), data_size);
1673 }
1674
1675 void MapBlock::copyFrom(VoxelManipulator &dst)
1676 {
1677         v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
1678         VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
1679         
1680         // Copy from VoxelManipulator to data
1681         dst.copyTo(data, data_area, v3s16(0,0,0),
1682                         getPosRelative(), data_size);
1683 }
1684
1685 void MapBlock::stepObjects(float dtime, bool server, u32 daynight_ratio)
1686 {
1687         /*
1688                 Step objects
1689         */
1690         m_objects.step(dtime, server, daynight_ratio);
1691
1692 #if 0
1693         /*
1694                 Spawn some objects at random.
1695
1696                 Use dayNightDiffed() to approximate being near ground level
1697         */
1698         if(m_spawn_timer < -999)
1699         {
1700                 m_spawn_timer = 60;
1701         }
1702         if(dayNightDiffed() == true && getObjectCount() == 0)
1703         {
1704                 m_spawn_timer -= dtime;
1705                 if(m_spawn_timer <= 0.0)
1706                 {
1707                         m_spawn_timer += myrand() % 300;
1708                         
1709                         v2s16 p2d(
1710                                 (myrand()%(MAP_BLOCKSIZE-1))+0,
1711                                 (myrand()%(MAP_BLOCKSIZE-1))+0
1712                         );
1713
1714                         s16 y = getGroundLevel(p2d);
1715                         
1716                         if(y >= 0)
1717                         {
1718                                 v3s16 p(p2d.X, y+1, p2d.Y);
1719
1720                                 if(getNode(p).d == CONTENT_AIR
1721                                                 && getNode(p).getLightBlend(daynight_ratio) <= 11)
1722                                 {
1723                                         RatObject *obj = new RatObject(NULL, -1, intToFloat(p, BS));
1724                                         addObject(obj);
1725                                 }
1726                         }
1727                 }
1728         }
1729 #endif
1730
1731         setChangedFlag();
1732 }
1733
1734
1735 void MapBlock::updateDayNightDiff()
1736 {
1737         if(data == NULL)
1738         {
1739                 m_day_night_differs = false;
1740                 return;
1741         }
1742
1743         bool differs = false;
1744
1745         /*
1746                 Check if any lighting value differs
1747         */
1748         for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1749         {
1750                 MapNode &n = data[i];
1751                 if(n.getLight(LIGHTBANK_DAY) != n.getLight(LIGHTBANK_NIGHT))
1752                 {
1753                         differs = true;
1754                         break;
1755                 }
1756         }
1757
1758         /*
1759                 If some lighting values differ, check if the whole thing is
1760                 just air. If it is, differ = false
1761         */
1762         if(differs)
1763         {
1764                 bool only_air = true;
1765                 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1766                 {
1767                         MapNode &n = data[i];
1768                         if(n.d != CONTENT_AIR)
1769                         {
1770                                 only_air = false;
1771                                 break;
1772                         }
1773                 }
1774                 if(only_air)
1775                         differs = false;
1776         }
1777
1778         // Set member variable
1779         m_day_night_differs = differs;
1780 }
1781
1782 s16 MapBlock::getGroundLevel(v2s16 p2d)
1783 {
1784         if(isDummy())
1785                 return -3;
1786         try
1787         {
1788                 s16 y = MAP_BLOCKSIZE-1;
1789                 for(; y>=0; y--)
1790                 {
1791                         //if(is_ground_content(getNodeRef(p2d.X, y, p2d.Y).d))
1792                         if(content_features(getNodeRef(p2d.X, y, p2d.Y).d).walkable)
1793                         {
1794                                 if(y == MAP_BLOCKSIZE-1)
1795                                         return -2;
1796                                 else
1797                                         return y;
1798                         }
1799                 }
1800                 return -1;
1801         }
1802         catch(InvalidPositionException &e)
1803         {
1804                 return -3;
1805         }
1806 }
1807
1808 /*
1809         Serialization
1810 */
1811
1812 void MapBlock::serialize(std::ostream &os, u8 version)
1813 {
1814         if(!ser_ver_supported(version))
1815                 throw VersionMismatchException("ERROR: MapBlock format not supported");
1816         
1817         if(data == NULL)
1818         {
1819                 throw SerializationError("ERROR: Not writing dummy block.");
1820         }
1821         
1822         // These have no compression
1823         if(version <= 3 || version == 5 || version == 6)
1824         {
1825                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1826                 
1827                 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
1828                 SharedBuffer<u8> dest(buflen);
1829
1830                 dest[0] = is_underground;
1831                 for(u32 i=0; i<nodecount; i++)
1832                 {
1833                         u32 s = 1 + i * MapNode::serializedLength(version);
1834                         data[i].serialize(&dest[s], version);
1835                 }
1836                 
1837                 os.write((char*)*dest, dest.getSize());
1838         }
1839         else if(version <= 10)
1840         {
1841                 /*
1842                         With compression.
1843                         Compress the materials and the params separately.
1844                 */
1845                 
1846                 // First byte
1847                 os.write((char*)&is_underground, 1);
1848
1849                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1850
1851                 // Get and compress materials
1852                 SharedBuffer<u8> materialdata(nodecount);
1853                 for(u32 i=0; i<nodecount; i++)
1854                 {
1855                         materialdata[i] = data[i].d;
1856                 }
1857                 compress(materialdata, os, version);
1858
1859                 // Get and compress lights
1860                 SharedBuffer<u8> lightdata(nodecount);
1861                 for(u32 i=0; i<nodecount; i++)
1862                 {
1863                         lightdata[i] = data[i].param;
1864                 }
1865                 compress(lightdata, os, version);
1866                 
1867                 if(version >= 10)
1868                 {
1869                         // Get and compress param2
1870                         SharedBuffer<u8> param2data(nodecount);
1871                         for(u32 i=0; i<nodecount; i++)
1872                         {
1873                                 param2data[i] = data[i].param2;
1874                         }
1875                         compress(param2data, os, version);
1876                 }
1877         }
1878         // All other versions (newest)
1879         else
1880         {
1881                 // First byte
1882                 u8 flags = 0;
1883                 if(is_underground)
1884                         flags |= 0x01;
1885                 if(m_day_night_differs)
1886                         flags |= 0x02;
1887                 if(m_lighting_expired)
1888                         flags |= 0x04;
1889                 os.write((char*)&flags, 1);
1890
1891                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1892
1893                 /*
1894                         Get data
1895                 */
1896
1897                 SharedBuffer<u8> databuf(nodecount*3);
1898
1899                 // Get contents
1900                 for(u32 i=0; i<nodecount; i++)
1901                 {
1902                         databuf[i] = data[i].d;
1903                 }
1904
1905                 // Get params
1906                 for(u32 i=0; i<nodecount; i++)
1907                 {
1908                         databuf[i+nodecount] = data[i].param;
1909                 }
1910
1911                 // Get param2
1912                 for(u32 i=0; i<nodecount; i++)
1913                 {
1914                         databuf[i+nodecount*2] = data[i].param2;
1915                 }
1916
1917                 /*
1918                         Compress data to output stream
1919                 */
1920
1921                 compress(databuf, os, version);
1922                 
1923                 /*
1924                         NodeMetadata
1925                 */
1926                 if(version >= 14)
1927                 {
1928                         if(version <= 15)
1929                         {
1930                                 std::ostringstream oss(std::ios_base::binary);
1931                                 m_node_metadata.serialize(oss);
1932                                 os<<serializeString(oss.str());
1933                         }
1934                         else
1935                         {
1936                                 std::ostringstream oss(std::ios_base::binary);
1937                                 m_node_metadata.serialize(oss);
1938                                 compressZlib(oss.str(), os);
1939                                 //os<<serializeLongString(oss.str());
1940                         }
1941                 }
1942         }
1943 }
1944
1945 void MapBlock::deSerialize(std::istream &is, u8 version)
1946 {
1947         if(!ser_ver_supported(version))
1948                 throw VersionMismatchException("ERROR: MapBlock format not supported");
1949
1950         // These have no lighting info
1951         if(version <= 1)
1952         {
1953                 setLightingExpired(true);
1954         }
1955
1956         // These have no compression
1957         if(version <= 3 || version == 5 || version == 6)
1958         {
1959                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1960                 char tmp;
1961                 is.read(&tmp, 1);
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++)
1967                 {
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);
1975                 }
1976         }
1977         else if(version <= 10)
1978         {
1979                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1980
1981                 u8 t8;
1982                 is.read((char*)&t8, 1);
1983                 is_underground = t8;
1984
1985                 {
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++)
1994                         {
1995                                 data[i].d = s[i];
1996                         }
1997                 }
1998                 {
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++)
2007                         {
2008                                 data[i].param = s[i];
2009                         }
2010                 }
2011         
2012                 if(version >= 10)
2013                 {
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++)
2022                         {
2023                                 data[i].param2 = s[i];
2024                         }
2025                 }
2026         }
2027         // All other versions (newest)
2028         else
2029         {
2030                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2031
2032                 u8 flags;
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;
2037
2038                 // Uncompress data
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");
2045
2046                 // Set contents
2047                 for(u32 i=0; i<nodecount; i++)
2048                 {
2049                         data[i].d = s[i];
2050                 }
2051                 // Set params
2052                 for(u32 i=0; i<nodecount; i++)
2053                 {
2054                         data[i].param = s[i+nodecount];
2055                 }
2056                 // Set param2
2057                 for(u32 i=0; i<nodecount; i++)
2058                 {
2059                         data[i].param2 = s[i+nodecount*2];
2060                 }
2061                 
2062                 /*
2063                         NodeMetadata
2064                 */
2065                 if(version >= 14)
2066                 {
2067                         // Ignore errors
2068                         try{
2069                                 if(version <= 15)
2070                                 {
2071                                         std::string data = deSerializeString(is);
2072                                         std::istringstream iss(data, std::ios_base::binary);
2073                                         m_node_metadata.deSerialize(iss);
2074                                 }
2075                                 else
2076                                 {
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);
2082                                 }
2083                         }
2084                         catch(SerializationError &e)
2085                         {
2086                                 dstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
2087                                                 <<" while deserializing node metadata"<<std::endl;
2088                         }
2089                 }
2090         }
2091         
2092         /*
2093                 Translate nodes as specified in the translate_to fields of
2094                 node features
2095         */
2096         for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
2097         {
2098                 MapNode &n = data[i];
2099
2100                 MapNode *translate_to = content_features(n.d).translate_to;
2101                 if(translate_to)
2102                 {
2103                         dstream<<"MapBlock: WARNING: Translating node "<<n.d<<" to "
2104                                         <<translate_to->d<<std::endl;
2105                         n = *translate_to;
2106                 }
2107         }
2108 }
2109
2110
2111 //END