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