38c081eec9d58ea3dc9d77ae750931eff58a0d77
[oweals/minetest.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 and g_irrlicht
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 = g_irrlicht->getTexture(f.tile.spec);
680                         video::ITexture *texture = f.tile.texture.atlas;
681                         if(texture == NULL)
682                                 continue;
683
684                         material.setTexture(0, texture);
685                         
686                         f.tile.applyMaterialOptions(material);
687                         
688                         collector.append(material, f.vertices, 4, indices, 6);
689                 }
690         }
691
692         /*
693                 Add special graphics:
694                 - torches
695                 - flowing water
696         */
697
698         // 0ms
699         //TimeTaker timer2("updateMesh() adding special stuff");
700
701         // Flowing water material
702         video::SMaterial material_water1;
703         material_water1.setFlag(video::EMF_LIGHTING, false);
704         //material_water1.setFlag(video::EMF_BACK_FACE_CULLING, false);
705         material_water1.setFlag(video::EMF_BILINEAR_FILTER, false);
706         material_water1.setFlag(video::EMF_FOG_ENABLE, true);
707         material_water1.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
708         //TODO
709         //material_water1.setTexture(0, g_irrlicht->getTexture("water.png"));
710         AtlasPointer pa_water1 = g_texturesource->getTexture(
711                         g_texturesource->getTextureId("water.png"));
712         material_water1.setTexture(0, pa_water1.atlas);
713
714         // New-style leaves material
715         video::SMaterial material_leaves1;
716         material_leaves1.setFlag(video::EMF_LIGHTING, false);
717         //material_leaves1.setFlag(video::EMF_BACK_FACE_CULLING, false);
718         material_leaves1.setFlag(video::EMF_BILINEAR_FILTER, false);
719         material_leaves1.setFlag(video::EMF_FOG_ENABLE, true);
720         material_leaves1.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
721         //TODO
722         //material_leaves1.setTexture(0, g_irrlicht->getTexture("leaves.png"));
723         AtlasPointer pa_leaves1 = g_texturesource->getTexture(
724                         g_texturesource->getTextureId("leaves.png"));
725         material_leaves1.setTexture(0, pa_leaves1.atlas);
726
727         for(s16 z=0; z<MAP_BLOCKSIZE; z++)
728         for(s16 y=0; y<MAP_BLOCKSIZE; y++)
729         for(s16 x=0; x<MAP_BLOCKSIZE; x++)
730         {
731                 v3s16 p(x,y,z);
732
733                 MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes+p);
734                 
735                 /*
736                         Add torches to mesh
737                 */
738                 if(n.d == CONTENT_TORCH)
739                 {
740                         video::SColor c(255,255,255,255);
741
742                         // Wall at X+ of node
743                         video::S3DVertex vertices[4] =
744                         {
745                                 video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c, 0,1),
746                                 video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c, 1,1),
747                                 video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, 1,0),
748                                 video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c, 0,0),
749                         };
750
751                         v3s16 dir = unpackDir(n.dir);
752
753                         for(s32 i=0; i<4; i++)
754                         {
755                                 if(dir == v3s16(1,0,0))
756                                         vertices[i].Pos.rotateXZBy(0);
757                                 if(dir == v3s16(-1,0,0))
758                                         vertices[i].Pos.rotateXZBy(180);
759                                 if(dir == v3s16(0,0,1))
760                                         vertices[i].Pos.rotateXZBy(90);
761                                 if(dir == v3s16(0,0,-1))
762                                         vertices[i].Pos.rotateXZBy(-90);
763                                 if(dir == v3s16(0,-1,0))
764                                         vertices[i].Pos.rotateXZBy(45);
765                                 if(dir == v3s16(0,1,0))
766                                         vertices[i].Pos.rotateXZBy(-45);
767
768                                 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
769                         }
770
771                         // Set material
772                         video::SMaterial material;
773                         material.setFlag(video::EMF_LIGHTING, false);
774                         material.setFlag(video::EMF_BACK_FACE_CULLING, false);
775                         material.setFlag(video::EMF_BILINEAR_FILTER, false);
776                         //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
777                         material.MaterialType
778                                         = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
779
780                         if(dir == v3s16(0,-1,0))
781                                 material.setTexture(0,
782                                                 g_texturesource->getTextureRaw("torch_on_floor.png"));
783                         else if(dir == v3s16(0,1,0))
784                                 material.setTexture(0,
785                                                 g_texturesource->getTextureRaw("torch_on_ceiling.png"));
786                         // For backwards compatibility
787                         else if(dir == v3s16(0,0,0))
788                                 material.setTexture(0,
789                                                 g_texturesource->getTextureRaw("torch_on_floor.png"));
790                         else
791                                 material.setTexture(0, 
792                                                 g_texturesource->getTextureRaw("torch.png"));
793
794                         u16 indices[] = {0,1,2,2,3,0};
795                         // Add to mesh collector
796                         collector.append(material, vertices, 4, indices, 6);
797                 }
798                 /*
799                         Signs on walls
800                 */
801                 if(n.d == CONTENT_SIGN_WALL)
802                 {
803                         u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
804                         video::SColor c(255,l,l,l);
805                                 
806                         float d = (float)BS/16;
807                         // Wall at X+ of node
808                         video::S3DVertex vertices[4] =
809                         {
810                                 video::S3DVertex(BS/2-d,-BS/2,-BS/2, 0,0,0, c, 0,1),
811                                 video::S3DVertex(BS/2-d,-BS/2,BS/2, 0,0,0, c, 1,1),
812                                 video::S3DVertex(BS/2-d,BS/2,BS/2, 0,0,0, c, 1,0),
813                                 video::S3DVertex(BS/2-d,BS/2,-BS/2, 0,0,0, c, 0,0),
814                         };
815
816                         v3s16 dir = unpackDir(n.dir);
817
818                         for(s32 i=0; i<4; i++)
819                         {
820                                 if(dir == v3s16(1,0,0))
821                                         vertices[i].Pos.rotateXZBy(0);
822                                 if(dir == v3s16(-1,0,0))
823                                         vertices[i].Pos.rotateXZBy(180);
824                                 if(dir == v3s16(0,0,1))
825                                         vertices[i].Pos.rotateXZBy(90);
826                                 if(dir == v3s16(0,0,-1))
827                                         vertices[i].Pos.rotateXZBy(-90);
828                                 if(dir == v3s16(0,-1,0))
829                                         vertices[i].Pos.rotateXYBy(-90);
830                                 if(dir == v3s16(0,1,0))
831                                         vertices[i].Pos.rotateXYBy(90);
832
833                                 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
834                         }
835
836                         // Set material
837                         video::SMaterial material;
838                         material.setFlag(video::EMF_LIGHTING, false);
839                         material.setFlag(video::EMF_BACK_FACE_CULLING, false);
840                         material.setFlag(video::EMF_BILINEAR_FILTER, false);
841                         material.setFlag(video::EMF_FOG_ENABLE, true);
842                         //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
843                         material.MaterialType
844                                         = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
845
846                         material.setTexture(0, 
847                                         g_texturesource->getTextureRaw("sign_wall.png"));
848
849                         u16 indices[] = {0,1,2,2,3,0};
850                         // Add to mesh collector
851                         collector.append(material, vertices, 4, indices, 6);
852                 }
853                 /*
854                         Add flowing water to mesh
855                 */
856                 else if(n.d == CONTENT_WATER)
857                 {
858                         bool top_is_water = false;
859                         MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
860                         if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
861                                 top_is_water = true;
862                         
863                         u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
864                         video::SColor c(WATER_ALPHA,l,l,l);
865                         
866                         // Neighbor water levels (key = relative position)
867                         // Includes current node
868                         core::map<v3s16, f32> neighbor_levels;
869                         core::map<v3s16, u8> neighbor_contents;
870                         core::map<v3s16, u8> neighbor_flags;
871                         const u8 neighborflag_top_is_water = 0x01;
872                         v3s16 neighbor_dirs[9] = {
873                                 v3s16(0,0,0),
874                                 v3s16(0,0,1),
875                                 v3s16(0,0,-1),
876                                 v3s16(1,0,0),
877                                 v3s16(-1,0,0),
878                                 v3s16(1,0,1),
879                                 v3s16(-1,0,-1),
880                                 v3s16(1,0,-1),
881                                 v3s16(-1,0,1),
882                         };
883                         for(u32 i=0; i<9; i++)
884                         {
885                                 u8 content = CONTENT_AIR;
886                                 float level = -0.5 * BS;
887                                 u8 flags = 0;
888                                 // Check neighbor
889                                 v3s16 p2 = p + neighbor_dirs[i];
890                                 MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
891                                 if(n2.d != CONTENT_IGNORE)
892                                 {
893                                         content = n2.d;
894
895                                         if(n2.d == CONTENT_WATERSOURCE)
896                                                 level = (-0.5+node_water_level) * BS;
897                                         else if(n2.d == CONTENT_WATER)
898                                                 level = (-0.5 + ((float)n2.param2 + 0.5) / 8.0
899                                                                 * node_water_level) * BS;
900
901                                         // Check node above neighbor.
902                                         // NOTE: This doesn't get executed if neighbor
903                                         //       doesn't exist
904                                         p2.Y += 1;
905                                         n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
906                                         if(n2.d == CONTENT_WATERSOURCE || n2.d == CONTENT_WATER)
907                                                 flags |= neighborflag_top_is_water;
908                                 }
909                                 
910                                 neighbor_levels.insert(neighbor_dirs[i], level);
911                                 neighbor_contents.insert(neighbor_dirs[i], content);
912                                 neighbor_flags.insert(neighbor_dirs[i], flags);
913                         }
914
915                         //float water_level = (-0.5 + ((float)n.param2 + 0.5) / 8.0) * BS;
916                         //float water_level = neighbor_levels[v3s16(0,0,0)];
917
918                         // Corner heights (average between four waters)
919                         f32 corner_levels[4];
920                         
921                         v3s16 halfdirs[4] = {
922                                 v3s16(0,0,0),
923                                 v3s16(1,0,0),
924                                 v3s16(1,0,1),
925                                 v3s16(0,0,1),
926                         };
927                         for(u32 i=0; i<4; i++)
928                         {
929                                 v3s16 cornerdir = halfdirs[i];
930                                 float cornerlevel = 0;
931                                 u32 valid_count = 0;
932                                 for(u32 j=0; j<4; j++)
933                                 {
934                                         v3s16 neighbordir = cornerdir - halfdirs[j];
935                                         u8 content = neighbor_contents[neighbordir];
936                                         // Special case for source nodes
937                                         if(content == CONTENT_WATERSOURCE)
938                                         {
939                                                 cornerlevel = (-0.5+node_water_level)*BS;
940                                                 valid_count = 1;
941                                                 break;
942                                         }
943                                         else if(content == CONTENT_WATER)
944                                         {
945                                                 cornerlevel += neighbor_levels[neighbordir];
946                                                 valid_count++;
947                                         }
948                                         else if(content == CONTENT_AIR)
949                                         {
950                                                 cornerlevel += -0.5*BS;
951                                                 valid_count++;
952                                         }
953                                 }
954                                 if(valid_count > 0)
955                                         cornerlevel /= valid_count;
956                                 corner_levels[i] = cornerlevel;
957                         }
958
959                         /*
960                                 Generate sides
961                         */
962
963                         v3s16 side_dirs[4] = {
964                                 v3s16(1,0,0),
965                                 v3s16(-1,0,0),
966                                 v3s16(0,0,1),
967                                 v3s16(0,0,-1),
968                         };
969                         s16 side_corners[4][2] = {
970                                 {1, 2},
971                                 {3, 0},
972                                 {2, 3},
973                                 {0, 1},
974                         };
975                         for(u32 i=0; i<4; i++)
976                         {
977                                 v3s16 dir = side_dirs[i];
978
979                                 /*
980                                         If our topside is water and neighbor's topside
981                                         is water, don't draw side face
982                                 */
983                                 if(top_is_water &&
984                                                 neighbor_flags[dir] & neighborflag_top_is_water)
985                                         continue;
986
987                                 u8 neighbor_content = neighbor_contents[dir];
988                                 
989                                 // Don't draw face if neighbor is not air or water
990                                 if(neighbor_content != CONTENT_AIR
991                                                 && neighbor_content != CONTENT_WATER)
992                                         continue;
993                                 
994                                 bool neighbor_is_water = (neighbor_content == CONTENT_WATER);
995                                 
996                                 // Don't draw any faces if neighbor is water and top is water
997                                 if(neighbor_is_water == true && top_is_water == false)
998                                         continue;
999                                 
1000                                 video::S3DVertex vertices[4] =
1001                                 {
1002                                         /*video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1),
1003                                         video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1),
1004                                         video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1005                                         video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1006                                         video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1007                                                         pa_water1.x0(), pa_water1.y1()),
1008                                         video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1009                                                         pa_water1.x1(), pa_water1.y1()),
1010                                         video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1011                                                         pa_water1.x1(), pa_water1.y0()),
1012                                         video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1013                                                         pa_water1.x0(), pa_water1.y0()),
1014                                 };
1015                                 
1016                                 /*
1017                                         If our topside is water, set upper border of face
1018                                         at upper border of node
1019                                 */
1020                                 if(top_is_water)
1021                                 {
1022                                         vertices[2].Pos.Y = 0.5*BS;
1023                                         vertices[3].Pos.Y = 0.5*BS;
1024                                 }
1025                                 /*
1026                                         Otherwise upper position of face is corner levels
1027                                 */
1028                                 else
1029                                 {
1030                                         vertices[2].Pos.Y = corner_levels[side_corners[i][0]];
1031                                         vertices[3].Pos.Y = corner_levels[side_corners[i][1]];
1032                                 }
1033                                 
1034                                 /*
1035                                         If neighbor is water, lower border of face is corner
1036                                         water levels
1037                                 */
1038                                 if(neighbor_is_water)
1039                                 {
1040                                         vertices[0].Pos.Y = corner_levels[side_corners[i][1]];
1041                                         vertices[1].Pos.Y = corner_levels[side_corners[i][0]];
1042                                 }
1043                                 /*
1044                                         If neighbor is not water, lower border of face is
1045                                         lower border of node
1046                                 */
1047                                 else
1048                                 {
1049                                         vertices[0].Pos.Y = -0.5*BS;
1050                                         vertices[1].Pos.Y = -0.5*BS;
1051                                 }
1052                                 
1053                                 for(s32 j=0; j<4; j++)
1054                                 {
1055                                         if(dir == v3s16(0,0,1))
1056                                                 vertices[j].Pos.rotateXZBy(0);
1057                                         if(dir == v3s16(0,0,-1))
1058                                                 vertices[j].Pos.rotateXZBy(180);
1059                                         if(dir == v3s16(-1,0,0))
1060                                                 vertices[j].Pos.rotateXZBy(90);
1061                                         if(dir == v3s16(1,0,-0))
1062                                                 vertices[j].Pos.rotateXZBy(-90);
1063
1064                                         vertices[j].Pos += intToFloat(p + blockpos_nodes, BS);
1065                                 }
1066
1067                                 u16 indices[] = {0,1,2,2,3,0};
1068                                 // Add to mesh collector
1069                                 collector.append(material_water1, vertices, 4, indices, 6);
1070                         }
1071                         
1072                         /*
1073                                 Generate top side, if appropriate
1074                         */
1075                         
1076                         if(top_is_water == false)
1077                         {
1078                                 video::S3DVertex vertices[4] =
1079                                 {
1080                                         /*video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
1081                                         video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
1082                                         video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1083                                         video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1084                                         video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1085                                                         pa_water1.x0(), pa_water1.y1()),
1086                                         video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1087                                                         pa_water1.x1(), pa_water1.y1()),
1088                                         video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
1089                                                         pa_water1.x1(), pa_water1.y0()),
1090                                         video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
1091                                                         pa_water1.x0(), pa_water1.y0()),
1092                                 };
1093                                 
1094                                 // This fixes a strange bug
1095                                 s32 corner_resolve[4] = {3,2,1,0};
1096
1097                                 for(s32 i=0; i<4; i++)
1098                                 {
1099                                         //vertices[i].Pos.Y += water_level;
1100                                         //vertices[i].Pos.Y += neighbor_levels[v3s16(0,0,0)];
1101                                         s32 j = corner_resolve[i];
1102                                         vertices[i].Pos.Y += corner_levels[j];
1103                                         vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1104                                 }
1105
1106                                 u16 indices[] = {0,1,2,2,3,0};
1107                                 // Add to mesh collector
1108                                 collector.append(material_water1, vertices, 4, indices, 6);
1109                         }
1110                 }
1111                 /*
1112                         Add water sources to mesh if using new style
1113                 */
1114                 else if(n.d == CONTENT_WATERSOURCE && new_style_water)
1115                 {
1116                         //bool top_is_water = false;
1117                         bool top_is_air = false;
1118                         MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
1119                         /*if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
1120                                 top_is_water = true;*/
1121                         if(n.d == CONTENT_AIR)
1122                                 top_is_air = true;
1123                         
1124                         /*if(top_is_water == true)
1125                                 continue;*/
1126                         if(top_is_air == false)
1127                                 continue;
1128
1129                         u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
1130                         video::SColor c(WATER_ALPHA,l,l,l);
1131                         
1132                         video::S3DVertex vertices[4] =
1133                         {
1134                                 /*video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
1135                                 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
1136                                 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1137                                 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1138                                 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1139                                                 pa_water1.x0(), pa_water1.y1()),
1140                                 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1141                                                 pa_water1.x1(), pa_water1.y1()),
1142                                 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
1143                                                 pa_water1.x1(), pa_water1.y0()),
1144                                 video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
1145                                                 pa_water1.x0(), pa_water1.y0()),
1146                         };
1147
1148                         for(s32 i=0; i<4; i++)
1149                         {
1150                                 vertices[i].Pos.Y += (-0.5+node_water_level)*BS;
1151                                 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1152                         }
1153
1154                         u16 indices[] = {0,1,2,2,3,0};
1155                         // Add to mesh collector
1156                         collector.append(material_water1, vertices, 4, indices, 6);
1157                 }
1158                 /*
1159                         Add leaves if using new style
1160                 */
1161                 else if(n.d == CONTENT_LEAVES && new_style_leaves)
1162                 {
1163                         /*u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));*/
1164                         u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio)));
1165                         video::SColor c(255,l,l,l);
1166
1167                         for(u32 j=0; j<6; j++)
1168                         {
1169                                 video::S3DVertex vertices[4] =
1170                                 {
1171                                         /*video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, 0,1),
1172                                         video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, 1,1),
1173                                         video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c, 1,0),
1174                                         video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c, 0,0),*/
1175                                         video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c,
1176                                                 pa_leaves1.x0(), pa_leaves1.y1()),
1177                                         video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c,
1178                                                 pa_leaves1.x1(), pa_leaves1.y1()),
1179                                         video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c,
1180                                                 pa_leaves1.x1(), pa_leaves1.y0()),
1181                                         video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c,
1182                                                 pa_leaves1.x0(), pa_leaves1.y0()),
1183                                 };
1184
1185                                 if(j == 0)
1186                                 {
1187                                         for(u16 i=0; i<4; i++)
1188                                                 vertices[i].Pos.rotateXZBy(0);
1189                                 }
1190                                 else if(j == 1)
1191                                 {
1192                                         for(u16 i=0; i<4; i++)
1193                                                 vertices[i].Pos.rotateXZBy(180);
1194                                 }
1195                                 else if(j == 2)
1196                                 {
1197                                         for(u16 i=0; i<4; i++)
1198                                                 vertices[i].Pos.rotateXZBy(-90);
1199                                 }
1200                                 else if(j == 3)
1201                                 {
1202                                         for(u16 i=0; i<4; i++)
1203                                                 vertices[i].Pos.rotateXZBy(90);
1204                                 }
1205                                 else if(j == 4)
1206                                 {
1207                                         for(u16 i=0; i<4; i++)
1208                                                 vertices[i].Pos.rotateYZBy(-90);
1209                                 }
1210                                 else if(j == 5)
1211                                 {
1212                                         for(u16 i=0; i<4; i++)
1213                                                 vertices[i].Pos.rotateYZBy(90);
1214                                 }
1215
1216                                 for(u16 i=0; i<4; i++)
1217                                 {
1218                                         vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1219                                 }
1220
1221                                 u16 indices[] = {0,1,2,2,3,0};
1222                                 // Add to mesh collector
1223                                 collector.append(material_leaves1, vertices, 4, indices, 6);
1224                         }
1225                 }
1226         }
1227
1228         /*
1229                 Add stuff from collector to mesh
1230         */
1231         
1232         scene::SMesh *mesh_new = NULL;
1233         mesh_new = new scene::SMesh();
1234         
1235         collector.fillMesh(mesh_new);
1236
1237         /*
1238                 Do some stuff to the mesh
1239         */
1240
1241         mesh_new->recalculateBoundingBox();
1242
1243         /*
1244                 Delete new mesh if it is empty
1245         */
1246
1247         if(mesh_new->getMeshBufferCount() == 0)
1248         {
1249                 mesh_new->drop();
1250                 mesh_new = NULL;
1251         }
1252
1253         if(mesh_new)
1254         {
1255 #if 0
1256                 // Usually 1-700 faces and 1-7 materials
1257                 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
1258                                 <<"and uses "<<mesh_new->getMeshBufferCount()
1259                                 <<" materials (meshbuffers)"<<std::endl;
1260 #endif
1261
1262                 // Use VBO for mesh (this just would set this for ever buffer)
1263                 // This will lead to infinite memory usage because or irrlicht.
1264                 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
1265
1266                 /*
1267                         NOTE: If that is enabled, some kind of a queue to the main
1268                         thread should be made which would call irrlicht to delete
1269                         the hardware buffer and then delete the mesh
1270                 */
1271         }
1272
1273         return mesh_new;
1274         
1275         //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1276 }
1277
1278 #endif // !SERVER
1279
1280 /*
1281         MapBlock
1282 */
1283
1284 MapBlock::MapBlock(NodeContainer *parent, v3s16 pos, bool dummy):
1285                 m_parent(parent),
1286                 m_pos(pos),
1287                 changed(true),
1288                 is_underground(false),
1289                 m_lighting_expired(true),
1290                 m_day_night_differs(false),
1291                 //m_not_fully_generated(false),
1292                 m_objects(this)
1293 {
1294         data = NULL;
1295         if(dummy == false)
1296                 reallocate();
1297         
1298         //m_spawn_timer = -10000;
1299
1300 #ifndef SERVER
1301         m_mesh_expired = false;
1302         mesh_mutex.Init();
1303         mesh = NULL;
1304         m_temp_mods_mutex.Init();
1305 #endif
1306 }
1307
1308 MapBlock::~MapBlock()
1309 {
1310 #ifndef SERVER
1311         {
1312                 JMutexAutoLock lock(mesh_mutex);
1313                 
1314                 if(mesh)
1315                 {
1316                         mesh->drop();
1317                         mesh = NULL;
1318                 }
1319         }
1320 #endif
1321
1322         if(data)
1323                 delete[] data;
1324 }
1325
1326 bool MapBlock::isValidPositionParent(v3s16 p)
1327 {
1328         if(isValidPosition(p))
1329         {
1330                 return true;
1331         }
1332         else{
1333                 return m_parent->isValidPosition(getPosRelative() + p);
1334         }
1335 }
1336
1337 MapNode MapBlock::getNodeParent(v3s16 p)
1338 {
1339         if(isValidPosition(p) == false)
1340         {
1341                 return m_parent->getNode(getPosRelative() + p);
1342         }
1343         else
1344         {
1345                 if(data == NULL)
1346                         throw InvalidPositionException();
1347                 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
1348         }
1349 }
1350
1351 void MapBlock::setNodeParent(v3s16 p, MapNode & n)
1352 {
1353         if(isValidPosition(p) == false)
1354         {
1355                 m_parent->setNode(getPosRelative() + p, n);
1356         }
1357         else
1358         {
1359                 if(data == NULL)
1360                         throw InvalidPositionException();
1361                 data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X] = n;
1362         }
1363 }
1364
1365 MapNode MapBlock::getNodeParentNoEx(v3s16 p)
1366 {
1367         if(isValidPosition(p) == false)
1368         {
1369                 try{
1370                         return m_parent->getNode(getPosRelative() + p);
1371                 }
1372                 catch(InvalidPositionException &e)
1373                 {
1374                         return MapNode(CONTENT_IGNORE);
1375                 }
1376         }
1377         else
1378         {
1379                 if(data == NULL)
1380                 {
1381                         return MapNode(CONTENT_IGNORE);
1382                 }
1383                 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
1384         }
1385 }
1386
1387 #ifndef SERVER
1388
1389 #if 1
1390 void MapBlock::updateMesh(u32 daynight_ratio)
1391 {
1392 #if 0
1393         /*
1394                 DEBUG: If mesh has been generated, don't generate it again
1395         */
1396         {
1397                 JMutexAutoLock meshlock(mesh_mutex);
1398                 if(mesh != NULL)
1399                         return;
1400         }
1401 #endif
1402
1403         MeshMakeData data;
1404         data.fill(daynight_ratio, this);
1405         
1406         scene::SMesh *mesh_new = makeMapBlockMesh(&data);
1407         
1408         /*
1409                 Replace the mesh
1410         */
1411
1412         replaceMesh(mesh_new);
1413
1414 }
1415 #endif
1416
1417 void MapBlock::replaceMesh(scene::SMesh *mesh_new)
1418 {
1419         mesh_mutex.Lock();
1420
1421         //scene::SMesh *mesh_old = mesh[daynight_i];
1422         //mesh[daynight_i] = mesh_new;
1423
1424         scene::SMesh *mesh_old = mesh;
1425         mesh = mesh_new;
1426         setMeshExpired(false);
1427         
1428         if(mesh_old != NULL)
1429         {
1430                 // Remove hardware buffers of meshbuffers of mesh
1431                 // NOTE: No way, this runs in a different thread and everything
1432                 /*u32 c = mesh_old->getMeshBufferCount();
1433                 for(u32 i=0; i<c; i++)
1434                 {
1435                         IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1436                 }*/
1437                 
1438                 /*dstream<<"mesh_old->getReferenceCount()="
1439                                 <<mesh_old->getReferenceCount()<<std::endl;
1440                 u32 c = mesh_old->getMeshBufferCount();
1441                 for(u32 i=0; i<c; i++)
1442                 {
1443                         scene::IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1444                         dstream<<"buf->getReferenceCount()="
1445                                         <<buf->getReferenceCount()<<std::endl;
1446                 }*/
1447
1448                 // Drop the mesh
1449                 mesh_old->drop();
1450
1451                 //delete mesh_old;
1452         }
1453
1454         mesh_mutex.Unlock();
1455 }
1456         
1457 #endif // !SERVER
1458
1459 /*
1460         Propagates sunlight down through the block.
1461         Doesn't modify nodes that are not affected by sunlight.
1462         
1463         Returns false if sunlight at bottom block is invalid
1464         Returns true if bottom block doesn't exist.
1465
1466         If there is a block above, continues from it.
1467         If there is no block above, assumes there is sunlight, unless
1468         is_underground is set or highest node is water.
1469
1470         At the moment, all sunlighted nodes are added to light_sources.
1471         - SUGG: This could be optimized
1472
1473         Turns sunglighted mud into grass.
1474
1475         if remove_light==true, sets non-sunlighted nodes black.
1476
1477         if black_air_left!=NULL, it is set to true if non-sunlighted
1478         air is left in block.
1479 */
1480 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources,
1481                 bool remove_light, bool *black_air_left,
1482                 bool grow_grass)
1483 {
1484         // Whether the sunlight at the top of the bottom block is valid
1485         bool block_below_is_valid = true;
1486         
1487         v3s16 pos_relative = getPosRelative();
1488         
1489         for(s16 x=0; x<MAP_BLOCKSIZE; x++)
1490         {
1491                 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
1492                 {
1493 #if 1
1494                         bool no_sunlight = false;
1495                         bool no_top_block = false;
1496                         // Check if node above block has sunlight
1497                         try{
1498                                 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1499                                 if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1500                                 {
1501                                         no_sunlight = true;
1502                                 }
1503                         }
1504                         catch(InvalidPositionException &e)
1505                         {
1506                                 no_top_block = true;
1507                                 
1508                                 // NOTE: This makes over-ground roofed places sunlighted
1509                                 // Assume sunlight, unless is_underground==true
1510                                 if(is_underground)
1511                                 {
1512                                         no_sunlight = true;
1513                                 }
1514                                 else
1515                                 {
1516                                         MapNode n = getNode(v3s16(x, MAP_BLOCKSIZE-1, z));
1517                                         if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
1518                                         {
1519                                                 no_sunlight = true;
1520                                         }
1521                                 }
1522                                 // NOTE: As of now, this just would make everything dark.
1523                                 // No sunlight here
1524                                 //no_sunlight = true;
1525                         }
1526 #endif
1527 #if 0 // Doesn't work; nothing gets light.
1528                         bool no_sunlight = true;
1529                         bool no_top_block = false;
1530                         // Check if node above block has sunlight
1531                         try{
1532                                 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1533                                 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
1534                                 {
1535                                         no_sunlight = false;
1536                                 }
1537                         }
1538                         catch(InvalidPositionException &e)
1539                         {
1540                                 no_top_block = true;
1541                         }
1542 #endif
1543
1544                         /*std::cout<<"("<<x<<","<<z<<"): "
1545                                         <<"no_top_block="<<no_top_block
1546                                         <<", is_underground="<<is_underground
1547                                         <<", no_sunlight="<<no_sunlight
1548                                         <<std::endl;*/
1549                 
1550                         s16 y = MAP_BLOCKSIZE-1;
1551                         
1552                         // This makes difference to diminishing in water.
1553                         bool stopped_to_solid_object = false;
1554                         
1555                         u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
1556
1557                         for(; y >= 0; y--)
1558                         {
1559                                 v3s16 pos(x, y, z);
1560                                 MapNode &n = getNodeRef(pos);
1561                                 
1562                                 if(current_light == 0)
1563                                 {
1564                                         // Do nothing
1565                                 }
1566                                 else if(current_light == LIGHT_SUN && n.sunlight_propagates())
1567                                 {
1568                                         // Do nothing: Sunlight is continued
1569                                 }
1570                                 else if(n.light_propagates() == false)
1571                                 {
1572                                         if(grow_grass)
1573                                         {
1574                                                 bool upper_is_air = false;
1575                                                 try
1576                                                 {
1577                                                         if(getNodeParent(pos+v3s16(0,1,0)).d == CONTENT_AIR)
1578                                                                 upper_is_air = true;
1579                                                 }
1580                                                 catch(InvalidPositionException &e)
1581                                                 {
1582                                                 }
1583                                                 // Turn mud into grass
1584                                                 if(upper_is_air && n.d == CONTENT_MUD
1585                                                                 && current_light == LIGHT_SUN)
1586                                                 {
1587                                                         n.d = CONTENT_GRASS;
1588                                                 }
1589                                         }
1590
1591                                         // A solid object is on the way.
1592                                         stopped_to_solid_object = true;
1593                                         
1594                                         // Light stops.
1595                                         current_light = 0;
1596                                 }
1597                                 else
1598                                 {
1599                                         // Diminish light
1600                                         current_light = diminish_light(current_light);
1601                                 }
1602
1603                                 u8 old_light = n.getLight(LIGHTBANK_DAY);
1604
1605                                 if(current_light > old_light || remove_light)
1606                                 {
1607                                         n.setLight(LIGHTBANK_DAY, current_light);
1608                                 }
1609                                 
1610                                 if(diminish_light(current_light) != 0)
1611                                 {
1612                                         light_sources.insert(pos_relative + pos, true);
1613                                 }
1614
1615                                 if(current_light == 0 && stopped_to_solid_object)
1616                                 {
1617                                         if(black_air_left)
1618                                         {
1619                                                 *black_air_left = true;
1620                                         }
1621                                 }
1622                         }
1623
1624                         // Whether or not the block below should see LIGHT_SUN
1625                         bool sunlight_should_go_down = (current_light == LIGHT_SUN);
1626
1627                         /*
1628                                 If the block below hasn't already been marked invalid:
1629
1630                                 Check if the node below the block has proper sunlight at top.
1631                                 If not, the block below is invalid.
1632                                 
1633                                 Ignore non-transparent nodes as they always have no light
1634                         */
1635                         try
1636                         {
1637                         if(block_below_is_valid)
1638                         {
1639                                 MapNode n = getNodeParent(v3s16(x, -1, z));
1640                                 if(n.light_propagates())
1641                                 {
1642                                         if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN
1643                                                         && sunlight_should_go_down == false)
1644                                                 block_below_is_valid = false;
1645                                         else if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN
1646                                                         && sunlight_should_go_down == true)
1647                                                 block_below_is_valid = false;
1648                                 }
1649                         }//if
1650                         }//try
1651                         catch(InvalidPositionException &e)
1652                         {
1653                                 /*std::cout<<"InvalidBlockException for bottom block node"
1654                                                 <<std::endl;*/
1655                                 // Just no block below, no need to panic.
1656                         }
1657                 }
1658         }
1659
1660         return block_below_is_valid;
1661 }
1662
1663
1664 void MapBlock::copyTo(VoxelManipulator &dst)
1665 {
1666         v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
1667         VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
1668         
1669         // Copy from data to VoxelManipulator
1670         dst.copyFrom(data, data_area, v3s16(0,0,0),
1671                         getPosRelative(), data_size);
1672 }
1673
1674 void MapBlock::copyFrom(VoxelManipulator &dst)
1675 {
1676         v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
1677         VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
1678         
1679         // Copy from VoxelManipulator to data
1680         dst.copyTo(data, data_area, v3s16(0,0,0),
1681                         getPosRelative(), data_size);
1682 }
1683
1684 void MapBlock::stepObjects(float dtime, bool server, u32 daynight_ratio)
1685 {
1686         /*
1687                 Step objects
1688         */
1689         m_objects.step(dtime, server, daynight_ratio);
1690
1691 #if 0
1692         /*
1693                 Spawn some objects at random.
1694
1695                 Use dayNightDiffed() to approximate being near ground level
1696         */
1697         if(m_spawn_timer < -999)
1698         {
1699                 m_spawn_timer = 60;
1700         }
1701         if(dayNightDiffed() == true && getObjectCount() == 0)
1702         {
1703                 m_spawn_timer -= dtime;
1704                 if(m_spawn_timer <= 0.0)
1705                 {
1706                         m_spawn_timer += myrand() % 300;
1707                         
1708                         v2s16 p2d(
1709                                 (myrand()%(MAP_BLOCKSIZE-1))+0,
1710                                 (myrand()%(MAP_BLOCKSIZE-1))+0
1711                         );
1712
1713                         s16 y = getGroundLevel(p2d);
1714                         
1715                         if(y >= 0)
1716                         {
1717                                 v3s16 p(p2d.X, y+1, p2d.Y);
1718
1719                                 if(getNode(p).d == CONTENT_AIR
1720                                                 && getNode(p).getLightBlend(daynight_ratio) <= 11)
1721                                 {
1722                                         RatObject *obj = new RatObject(NULL, -1, intToFloat(p, BS));
1723                                         addObject(obj);
1724                                 }
1725                         }
1726                 }
1727         }
1728 #endif
1729
1730         setChangedFlag();
1731 }
1732
1733
1734 void MapBlock::updateDayNightDiff()
1735 {
1736         if(data == NULL)
1737         {
1738                 m_day_night_differs = false;
1739                 return;
1740         }
1741
1742         bool differs = false;
1743
1744         /*
1745                 Check if any lighting value differs
1746         */
1747         for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1748         {
1749                 MapNode &n = data[i];
1750                 if(n.getLight(LIGHTBANK_DAY) != n.getLight(LIGHTBANK_NIGHT))
1751                 {
1752                         differs = true;
1753                         break;
1754                 }
1755         }
1756
1757         /*
1758                 If some lighting values differ, check if the whole thing is
1759                 just air. If it is, differ = false
1760         */
1761         if(differs)
1762         {
1763                 bool only_air = true;
1764                 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1765                 {
1766                         MapNode &n = data[i];
1767                         if(n.d != CONTENT_AIR)
1768                         {
1769                                 only_air = false;
1770                                 break;
1771                         }
1772                 }
1773                 if(only_air)
1774                         differs = false;
1775         }
1776
1777         // Set member variable
1778         m_day_night_differs = differs;
1779 }
1780
1781 s16 MapBlock::getGroundLevel(v2s16 p2d)
1782 {
1783         if(isDummy())
1784                 return -3;
1785         try
1786         {
1787                 s16 y = MAP_BLOCKSIZE-1;
1788                 for(; y>=0; y--)
1789                 {
1790                         //if(is_ground_content(getNodeRef(p2d.X, y, p2d.Y).d))
1791                         if(content_features(getNodeRef(p2d.X, y, p2d.Y).d).walkable)
1792                         {
1793                                 if(y == MAP_BLOCKSIZE-1)
1794                                         return -2;
1795                                 else
1796                                         return y;
1797                         }
1798                 }
1799                 return -1;
1800         }
1801         catch(InvalidPositionException &e)
1802         {
1803                 return -3;
1804         }
1805 }
1806
1807 /*
1808         Serialization
1809 */
1810
1811 void MapBlock::serialize(std::ostream &os, u8 version)
1812 {
1813         if(!ser_ver_supported(version))
1814                 throw VersionMismatchException("ERROR: MapBlock format not supported");
1815         
1816         if(data == NULL)
1817         {
1818                 throw SerializationError("ERROR: Not writing dummy block.");
1819         }
1820         
1821         // These have no compression
1822         if(version <= 3 || version == 5 || version == 6)
1823         {
1824                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1825                 
1826                 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
1827                 SharedBuffer<u8> dest(buflen);
1828
1829                 dest[0] = is_underground;
1830                 for(u32 i=0; i<nodecount; i++)
1831                 {
1832                         u32 s = 1 + i * MapNode::serializedLength(version);
1833                         data[i].serialize(&dest[s], version);
1834                 }
1835                 
1836                 os.write((char*)*dest, dest.getSize());
1837         }
1838         else if(version <= 10)
1839         {
1840                 /*
1841                         With compression.
1842                         Compress the materials and the params separately.
1843                 */
1844                 
1845                 // First byte
1846                 os.write((char*)&is_underground, 1);
1847
1848                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1849
1850                 // Get and compress materials
1851                 SharedBuffer<u8> materialdata(nodecount);
1852                 for(u32 i=0; i<nodecount; i++)
1853                 {
1854                         materialdata[i] = data[i].d;
1855                 }
1856                 compress(materialdata, os, version);
1857
1858                 // Get and compress lights
1859                 SharedBuffer<u8> lightdata(nodecount);
1860                 for(u32 i=0; i<nodecount; i++)
1861                 {
1862                         lightdata[i] = data[i].param;
1863                 }
1864                 compress(lightdata, os, version);
1865                 
1866                 if(version >= 10)
1867                 {
1868                         // Get and compress param2
1869                         SharedBuffer<u8> param2data(nodecount);
1870                         for(u32 i=0; i<nodecount; i++)
1871                         {
1872                                 param2data[i] = data[i].param2;
1873                         }
1874                         compress(param2data, os, version);
1875                 }
1876         }
1877         // All other versions (newest)
1878         else
1879         {
1880                 // First byte
1881                 u8 flags = 0;
1882                 if(is_underground)
1883                         flags |= 0x01;
1884                 if(m_day_night_differs)
1885                         flags |= 0x02;
1886                 if(m_lighting_expired)
1887                         flags |= 0x04;
1888                 os.write((char*)&flags, 1);
1889
1890                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1891
1892                 /*
1893                         Get data
1894                 */
1895
1896                 SharedBuffer<u8> databuf(nodecount*3);
1897
1898                 // Get contents
1899                 for(u32 i=0; i<nodecount; i++)
1900                 {
1901                         databuf[i] = data[i].d;
1902                 }
1903
1904                 // Get params
1905                 for(u32 i=0; i<nodecount; i++)
1906                 {
1907                         databuf[i+nodecount] = data[i].param;
1908                 }
1909
1910                 // Get param2
1911                 for(u32 i=0; i<nodecount; i++)
1912                 {
1913                         databuf[i+nodecount*2] = data[i].param2;
1914                 }
1915
1916                 /*
1917                         Compress data to output stream
1918                 */
1919
1920                 compress(databuf, os, version);
1921                 
1922                 /*
1923                         NodeMetadata
1924                 */
1925                 if(version >= 14)
1926                 {
1927                         std::ostringstream oss(std::ios_base::binary);
1928                         m_node_metadata.serialize(oss);
1929                         os<<serializeString(oss.str());
1930                 }
1931         }
1932 }
1933
1934 void MapBlock::deSerialize(std::istream &is, u8 version)
1935 {
1936         if(!ser_ver_supported(version))
1937                 throw VersionMismatchException("ERROR: MapBlock format not supported");
1938
1939         // These have no lighting info
1940         if(version <= 1)
1941         {
1942                 setLightingExpired(true);
1943         }
1944
1945         // These have no compression
1946         if(version <= 3 || version == 5 || version == 6)
1947         {
1948                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1949                 char tmp;
1950                 is.read(&tmp, 1);
1951                 if(is.gcount() != 1)
1952                         throw SerializationError
1953                                         ("MapBlock::deSerialize: no enough input data");
1954                 is_underground = tmp;
1955                 for(u32 i=0; i<nodecount; i++)
1956                 {
1957                         s32 len = MapNode::serializedLength(version);
1958                         SharedBuffer<u8> d(len);
1959                         is.read((char*)*d, len);
1960                         if(is.gcount() != len)
1961                                 throw SerializationError
1962                                                 ("MapBlock::deSerialize: no enough input data");
1963                         data[i].deSerialize(*d, version);
1964                 }
1965         }
1966         else if(version <= 10)
1967         {
1968                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1969
1970                 u8 t8;
1971                 is.read((char*)&t8, 1);
1972                 is_underground = t8;
1973
1974                 {
1975                         // Uncompress and set material data
1976                         std::ostringstream os(std::ios_base::binary);
1977                         decompress(is, os, version);
1978                         std::string s = os.str();
1979                         if(s.size() != nodecount)
1980                                 throw SerializationError
1981                                                 ("MapBlock::deSerialize: invalid format");
1982                         for(u32 i=0; i<s.size(); i++)
1983                         {
1984                                 data[i].d = s[i];
1985                         }
1986                 }
1987                 {
1988                         // Uncompress and set param data
1989                         std::ostringstream os(std::ios_base::binary);
1990                         decompress(is, os, version);
1991                         std::string s = os.str();
1992                         if(s.size() != nodecount)
1993                                 throw SerializationError
1994                                                 ("MapBlock::deSerialize: invalid format");
1995                         for(u32 i=0; i<s.size(); i++)
1996                         {
1997                                 data[i].param = s[i];
1998                         }
1999                 }
2000         
2001                 if(version >= 10)
2002                 {
2003                         // Uncompress and set param2 data
2004                         std::ostringstream os(std::ios_base::binary);
2005                         decompress(is, os, version);
2006                         std::string s = os.str();
2007                         if(s.size() != nodecount)
2008                                 throw SerializationError
2009                                                 ("MapBlock::deSerialize: invalid format");
2010                         for(u32 i=0; i<s.size(); i++)
2011                         {
2012                                 data[i].param2 = s[i];
2013                         }
2014                 }
2015         }
2016         // All other versions (newest)
2017         else
2018         {
2019                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2020
2021                 u8 flags;
2022                 is.read((char*)&flags, 1);
2023                 is_underground = (flags & 0x01) ? true : false;
2024                 m_day_night_differs = (flags & 0x02) ? true : false;
2025                 m_lighting_expired = (flags & 0x04) ? true : false;
2026
2027                 // Uncompress data
2028                 std::ostringstream os(std::ios_base::binary);
2029                 decompress(is, os, version);
2030                 std::string s = os.str();
2031                 if(s.size() != nodecount*3)
2032                         throw SerializationError
2033                                         ("MapBlock::deSerialize: invalid format");
2034
2035                 // Set contents
2036                 for(u32 i=0; i<nodecount; i++)
2037                 {
2038                         data[i].d = s[i];
2039                 }
2040                 // Set params
2041                 for(u32 i=0; i<nodecount; i++)
2042                 {
2043                         data[i].param = s[i+nodecount];
2044                 }
2045                 // Set param2
2046                 for(u32 i=0; i<nodecount; i++)
2047                 {
2048                         data[i].param2 = s[i+nodecount*2];
2049                 }
2050                 
2051                 /*
2052                         NodeMetadata
2053                 */
2054                 if(version >= 14)
2055                 {
2056                         // Ignore errors
2057                         try{
2058                                 std::string data = deSerializeString(is);
2059                                 std::istringstream iss(data, std::ios_base::binary);
2060                                 m_node_metadata.deSerialize(iss);
2061                         }
2062                         catch(SerializationError &e)
2063                         {
2064                                 dstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
2065                                                 <<" while deserializing node metadata"<<std::endl;
2066                         }
2067                 }
2068         }
2069         
2070         /*
2071                 Translate nodes as specified in the translate_to fields of
2072                 node features
2073         */
2074         for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
2075         {
2076                 MapNode &n = data[i];
2077
2078                 MapNode *translate_to = content_features(n.d).translate_to;
2079                 if(translate_to)
2080                 {
2081                         dstream<<"MapBlock: WARNING: Translating node "<<n.d<<" to "
2082                                         <<translate_to->d<<std::endl;
2083                         n = *translate_to;
2084                 }
2085         }
2086 }
2087
2088
2089 //END